Skip to content

Commit 18418f6

Browse files
committed
Fix #3564: Allow annotated singleton types
Previously, singleton types were widened before an annotation was added. Now such a widening is no longer performed. The change necessitated a systematic generalization of what it means to be a singleton, where we now use in many places an extractor instead of a type test. This has the added benefit that we now also classify as singletons intersections involving a singleton type and aliases to a singleton type.
1 parent faa0a1b commit 18418f6

File tree

14 files changed

+59
-35
lines changed

14 files changed

+59
-35
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
338338
else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot)
339339
Ident(tp)
340340
else tp.prefix match {
341-
case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp)
341+
case SingletonType(pre) => followOuterLinks(singleton(pre)).select(tp)
342342
case pre => Select(TypeTree(pre), tp)
343343
} // no checks necessary
344344

@@ -777,7 +777,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
777777

778778
/** `tree.isInstanceOf[tp]`, with special treatment of singleton types */
779779
def isInstance(tp: Type)(implicit ctx: Context): Tree = tp match {
780-
case tp: SingletonType =>
780+
case SingletonType(tp) =>
781781
if (tp.widen.derivesFrom(defn.ObjectClass))
782782
tree.ensureConforms(defn.ObjectType).select(defn.Object_eq).appliedTo(singleton(tp))
783783
else

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,11 @@ trait ConstraintHandling {
247247
*/
248248
def instanceType(param: TypeParamRef, fromBelow: Boolean): Type = {
249249
def upperBound = constraint.fullUpperBound(param)
250-
def isSingleton(tp: Type): Boolean = tp match {
250+
def isMultiSingleton(tp: Type): Boolean = tp match {
251251
case tp: SingletonType => true
252-
case AndType(tp1, tp2) => isSingleton(tp1) | isSingleton(tp2)
253-
case OrType(tp1, tp2) => isSingleton(tp1) & isSingleton(tp2)
252+
case AndType(tp1, tp2) => isMultiSingleton(tp1) | isMultiSingleton(tp2)
253+
case OrType(tp1, tp2) => isMultiSingleton(tp1) & isMultiSingleton(tp2)
254+
case AnnotatedType(tp, _) => isMultiSingleton(tp)
254255
case _ => false
255256
}
256257
def isFullyDefined(tp: Type): Boolean = tp match {
@@ -274,7 +275,7 @@ trait ConstraintHandling {
274275
// 1. If instance is from below and is a singleton type, yet upper bound is
275276
// not a singleton type or a reference to `scala.Singleton`, widen the
276277
// instance.
277-
if (fromBelow && isSingleton(inst) && !isSingleton(upperBound)
278+
if (fromBelow && isMultiSingleton(inst) && !isMultiSingleton(upperBound)
278279
&& !upperBound.isRef(defn.SingletonClass))
279280
inst = inst.widen
280281

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ object Types {
114114
case _: SingletonType | NoPrefix => true
115115
case tp: RefinedOrRecType => tp.parent.isStable
116116
case tp: ExprType => tp.resultType.isStable
117+
case tp: AnnotatedType => tp.tpe.isStable
117118
case _ => false
118119
}
119120

@@ -247,6 +248,9 @@ object Types {
247248
case _ => NoType
248249
}
249250

251+
/** Is this type a (possibly aliased or intersected) singleton type? */
252+
def isSingleton(implicit ctx: Context) = SingletonType.unapply(this).isDefined
253+
250254
/** Is this type guaranteed not to have `null` as a value? */
251255
final def isNotNull(implicit ctx: Context): Boolean = this match {
252256
case tp: ConstantType => tp.value.value != null
@@ -354,7 +358,6 @@ object Types {
354358
@tailrec final def typeSymbol(implicit ctx: Context): Symbol = this match {
355359
case tp: TypeRef => tp.symbol
356360
case tp: ClassInfo => tp.cls
357-
// case ThisType(cls) => cls // needed?
358361
case tp: SingletonType => NoSymbol
359362
case tp: TypeProxy => tp.underlying.typeSymbol
360363
case _ => NoSymbol
@@ -918,7 +921,7 @@ object Types {
918921
/** Widen from singleton type to its underlying non-singleton
919922
* base type by applying one or more `underlying` dereferences.
920923
*/
921-
final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match {
924+
final def widenSingleton(implicit ctx: Context): Type = stripTypeVar.stripAnnots match {
922925
case tp: SingletonType if !tp.isOverloaded => tp.underlying.widenSingleton
923926
case _ => this
924927
}
@@ -1497,6 +1500,17 @@ object Types {
14971500
def isOverloaded(implicit ctx: Context) = false
14981501
}
14991502

1503+
/** An extractor that returns the underlying singleton type of a
1504+
* (possibly annotated, aliased, or intersected) type
1505+
*/
1506+
object SingletonType {
1507+
def unapply(tp: Type)(implicit ctx: Context): Option[SingletonType] = tp.dealias match {
1508+
case AndType(tp1, tp2) => unapply(tp1).orElse(unapply(tp2))
1509+
case tp: SingletonType => Some(tp)
1510+
case _ => None
1511+
}
1512+
}
1513+
15001514
/** A marker trait for types that bind other types that refer to them.
15011515
* Instances are: LambdaType, RecType.
15021516
*/
@@ -3555,16 +3569,18 @@ object Types {
35553569
// ----- Annotated and Import types -----------------------------------------------
35563570

35573571
/** An annotated type tpe @ annot */
3558-
case class AnnotatedType(tpe: Type, annot: Annotation)
3559-
extends UncachedProxyType with ValueType {
3572+
case class AnnotatedType(tpe: Type, annot: Annotation) extends UncachedProxyType with ValueType {
35603573
// todo: cache them? but this makes only sense if annotations and trees are also cached.
3574+
35613575
override def underlying(implicit ctx: Context): Type = tpe
3576+
35623577
def derivedAnnotatedType(tpe: Type, annot: Annotation) =
35633578
if ((tpe eq this.tpe) && (annot eq this.annot)) this
35643579
else AnnotatedType(tpe, annot)
35653580

35663581
override def stripTypeVar(implicit ctx: Context): Type =
35673582
derivedAnnotatedType(tpe.stripTypeVar, annot)
3583+
35683584
override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots
35693585
}
35703586

@@ -3974,7 +3990,7 @@ object Types {
39743990
// If H#T = _ >: S <: U, then for any x in L..H, S <: x.T <: U,
39753991
// hence we can replace with S..U under all variances
39763992
range(atVariance(-variance)(reapply(lo)), reapply(hi))
3977-
case info: SingletonType =>
3993+
case SingletonType(info) =>
39783994
// if H#x: y.type, then for any x in L..H, x.type =:= y.type,
39793995
// hence we can replace with y.type under all variances
39803996
reapply(info)

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,12 @@ object Erasure {
244244
* in ExtensionMethods#transform.
245245
*/
246246
def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = trace(i"cast ${tree.tpe.widen} --> $pt", show = true) {
247+
247248
def wrap(tycon: TypeRef) =
248249
ref(u2evt(tycon.typeSymbol.asClass)).appliedTo(tree)
249250
def unwrap(tycon: TypeRef) =
250251
ref(evt2u(tycon.typeSymbol.asClass)).appliedTo(tree)
251252

252-
253253
assert(!pt.isInstanceOf[SingletonType], pt)
254254
if (pt isRef defn.UnitClass) unbox(tree, pt)
255255
else (tree.tpe.widen, pt) match {

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ object PatternMatcher {
783783
// See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest`
784784
// generates an outer test based on `patType.prefix` with automatically dealises.
785785
expectedTp.dealias match {
786-
case tref @ TypeRef(pre: SingletonType, _) =>
786+
case tref @ TypeRef(SingletonType(pre), _) =>
787787
tref.symbol.isClass &&
788788
ExplicitOuter.needsOuterIfReferenced(tref.symbol.asClass)
789789
case _ =>
@@ -802,7 +802,7 @@ object PatternMatcher {
802802
}
803803

804804
expectedTp.dealias match {
805-
case expectedTp: SingletonType =>
805+
case SingletonType(expectedTp) =>
806806
scrutinee.isInstance(expectedTp) // will be translated to an equality test
807807
case _ =>
808808
val typeTest = scrutinee.select(defn.Any_typeTest).appliedToType(expectedTp)

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ object TypeTestsCasts {
133133
* with `transformIsInstanceOf`, which depends on erased type of `testType`.
134134
*/
135135
def transformTypeTest(expr: Tree, testType: Type, flagUnrelated: Boolean): Tree = testType.dealias match {
136-
case _: SingletonType =>
136+
case SingletonType(_) =>
137137
expr.isInstance(testType).withPos(tree.pos)
138138
case OrType(tp1, tp2) =>
139139
evalOnce(expr) { e =>

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,9 +579,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
579579
*/
580580
def inhabited(tpe: Type)(implicit ctx: Context): Boolean = {
581581
val emptySingletonIntersection = new ExistsAccumulator({
582-
case AndType(s: SingletonType, t) =>
582+
case AndType(SingletonType(s), t) =>
583583
!(s <:< t)
584-
case AndType(t, s: SingletonType) =>
584+
case AndType(t, SingletonType(s)) =>
585585
!(s <:< t)
586586
case x =>
587587
false

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,7 @@ trait Checking {
561561
*/
562562
def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
563563
case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) =>
564-
if (vparam.tpt.tpe.isInstanceOf[SingletonType])
565-
ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos)
564+
checkNotSingleton(vparam.tpt, " to be parameter type of an implicit conversion")
566565
case _ =>
567566
}
568567

@@ -659,11 +658,10 @@ trait Checking {
659658
else tpt
660659

661660
/** Check that `tpt` does not refer to a singleton type */
662-
def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree =
663-
if (tpt.tpe.isInstanceOf[SingletonType]) {
664-
errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where")
665-
}
666-
else tpt
661+
def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt.tpe match {
662+
case SingletonType(_) => errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where")
663+
case _ => tpt
664+
}
667665

668666
/** Verify classes extending AnyVal meet the requirements */
669667
def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) =

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,8 +352,8 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
352352
case tp: MethodType =>
353353
(tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) =>
354354
def isByName = paramtp.dealias.isInstanceOf[ExprType]
355-
paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match {
356-
case argtpe: SingletonType if isIdempotentExpr(arg) => argtpe
355+
paramBinding(name) = arg.tpe match {
356+
case SingletonType(_) if isIdempotentExpr(arg) => arg.tpe
357357
case argtpe =>
358358
val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags
359359
val (bindingFlags, bindingType) =
@@ -476,7 +476,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
476476
}
477477
case _: Ident =>
478478
paramProxy.get(tree.tpe) match {
479-
case Some(t: SingletonType) if tree.isTerm => singleton(t).withPos(tree.pos)
479+
case Some(SingletonType(t)) if tree.isTerm => singleton(t).withPos(tree.pos)
480480
case Some(t) if tree.isType => TypeTree(t).withPos(tree.pos)
481481
case None => tree
482482
}

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ trait TypeAssigner {
9494
case tp: TermRef
9595
if toAvoid(tp.symbol) || partsToAvoid(mutable.Set.empty, tp.info).nonEmpty =>
9696
tp.info.widenExpr match {
97-
case info: SingletonType => apply(info)
97+
case info @ SingletonType(_) => apply(info)
9898
case info => range(tp.info.bottomType, apply(info))
9999
}
100100
case tp: TypeRef if toAvoid(tp.symbol) =>
@@ -122,7 +122,7 @@ trait TypeAssigner {
122122
* 1. We first try a widening conversion to the type's info with
123123
* the original prefix. Since the original prefix is known to
124124
* be a subtype of the returned prefix, this can improve results.
125-
* 2. IThen, if the approximation result is a singleton reference C#x.type, we
125+
* 2. Then, if the approximation result is a singleton reference C#x.type, we
126126
* replace by the widened type, which is usually more natural.
127127
* 3. Finally, we need to handle the case where the prefix type does not have a member
128128
* named `tp.name` anymmore. In that case, we need to fall back to Bot..Top.
@@ -131,7 +131,7 @@ trait TypeAssigner {
131131
if (pre eq tp.prefix)
132132
tp
133133
else tryWiden(tp, tp.prefix).orElse {
134-
if (tp.isTerm && variance > 0 && !pre.isInstanceOf[SingletonType])
134+
if (tp.isTerm && variance > 0 && !pre.isSingleton)
135135
apply(tp.info.widenExpr)
136136
else if (upper(pre).member(tp.name).exists)
137137
super.derivedSelect(tp, pre)
@@ -536,7 +536,7 @@ trait TypeAssigner {
536536
tree.withType(sym.termRef)
537537

538538
def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(implicit ctx: Context) =
539-
tree.withType(AnnotatedType(arg.tpe.widen, Annotation(annot)))
539+
tree.withType(AnnotatedType(arg.tpe, Annotation(annot)))
540540

541541
def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) =
542542
tree.withType(pid.symbol.termRef)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15921592
if (ctx.mode is Mode.Type)
15931593
assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
15941594
else {
1595-
val tpt = TypeTree(AnnotatedType(arg1.tpe.widen, Annotation(annot1)))
1595+
val tpt = TypeTree(AnnotatedType(arg1.tpe, Annotation(annot1)))
15961596
assignType(cpy.Typed(tree)(arg1, tpt), tpt)
15971597
}
15981598
}
@@ -2267,7 +2267,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
22672267
// is tried. See strawman-contrib MapDecoratorTest.scala for an example where this happens.
22682268
err.typeMismatch(tree, pt)
22692269
}
2270-
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
2270+
case wtp: MethodType if !pt.isSingleton =>
22712271
val arity =
22722272
if (functionExpected)
22732273
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))

tests/pos/i830.scala renamed to tests/neg/i830.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ object C {
22
trait X[T]
33
implicit def u[A, B]: X[A | B] = new X[A | B] {}
44
def y[T](implicit x: X[T]): T = ???
5-
val x: 1 & 2 | 2 & 3 = y
5+
val x: 1 & 2 | 2 & 3 = y // error: singleton is not allowed in a union type
66
}

tests/pos/i3564.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
case class Foo(s: "abc")
2+
3+
object Test {
4+
5+
val x: "abc" @deprecated = "abc"
6+
7+
val y: "abc" = x
8+
9+
}

tests/pos/i864.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ object C {
66
trait X[T]
77
implicit def u[A, B]: X[A | B] = new X[A | B] {}
88
def y[T](implicit x: X[T]): T = ???
9-
val x: a.type & b.type | b.type & c.type = y
9+
// val x: a.type & b.type | b.type & c.type = y // no longer allowed, singletons cannot be in union types
1010
}

0 commit comments

Comments
 (0)