Skip to content

Commit 323c6a7

Browse files
authored
Merge pull request #6567 from dotty-staging/add-opaque-bounds
Improve opaque types
2 parents c46553a + 44b9562 commit 323c6a7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+715
-381
lines changed

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

+43-52
Original file line numberDiff line numberDiff line change
@@ -818,47 +818,6 @@ object desugar {
818818
}
819819
}
820820

821-
/** Expand
822-
*
823-
* <mods> opaque type T = [Xs] =>> R
824-
*
825-
* to
826-
*
827-
* <mods> opaque type T = T.T
828-
* synthetic object T {
829-
* synthetic opaque type T >: [Xs] =>> R
830-
* }
831-
*
832-
* The generated companion object will later (in Namer) be merged with the user-defined
833-
* companion object, and the synthetic opaque type member will go into the self type.
834-
*/
835-
def opaqueAlias(tdef: TypeDef)(implicit ctx: Context): Tree =
836-
if (lacksDefinition(tdef)) {
837-
ctx.error(em"opaque type ${tdef.name} must be an alias type", tdef.sourcePos)
838-
tdef.withFlags(tdef.mods.flags &~ Opaque)
839-
}
840-
else {
841-
def completeForwarder(fwd: Tree) = tdef.rhs match {
842-
case LambdaTypeTree(tparams, tpt) =>
843-
val tparams1 =
844-
for (tparam <- tparams)
845-
yield tparam.withMods(tparam.mods | Synthetic)
846-
lambdaAbstract(tparams1,
847-
AppliedTypeTree(fwd, tparams.map(tparam => Ident(tparam.name))))
848-
case _ =>
849-
fwd
850-
}
851-
val moduleName = tdef.name.toTermName
852-
val localRef = Select(Ident(moduleName), tdef.name).withAttachment(SuppressAccessCheck, ())
853-
val aliasType = cpy.TypeDef(tdef)(rhs = completeForwarder(localRef)).withSpan(tdef.span.startPos)
854-
val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name))
855-
856-
val companions = moduleDef(ModuleDef(
857-
moduleName, Template(emptyConstructor, Nil, Nil, EmptyValDef, localType :: Nil))
858-
.withFlags(Synthetic | Opaque))
859-
Thicket(aliasType :: companions.toList)
860-
}
861-
862821
/** The normalized name of `mdef`. This means
863822
* 1. Check that the name does not redefine a Scala core class.
864823
* If it does redefine, issue an error and return a mangled name instead of the original one.
@@ -1035,19 +994,51 @@ object desugar {
1035994
Bind(name, Ident(nme.WILDCARD)).withSpan(tree.span)
1036995
}
1037996

1038-
def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
1039-
case tree: ValDef => valDef(tree)
1040-
case tree: TypeDef =>
1041-
if (tree.isClassDef) classDef(tree)
1042-
else if (tree.mods.is(Opaque, butNot = Synthetic)) opaqueAlias(tree)
1043-
else tree
1044-
case tree: DefDef =>
1045-
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
1046-
else defDef(tree)
1047-
case tree: ModuleDef => moduleDef(tree)
1048-
case tree: PatDef => patDef(tree)
997+
/** The type of tests that check whether a MemberDef is OK for some flag.
998+
* The test succeeds if the partial function is defined and returns true.
999+
*/
1000+
type MemberDefTest = PartialFunction[MemberDef, Boolean]
1001+
1002+
val legalOpaque: MemberDefTest = {
1003+
case TypeDef(_, rhs) =>
1004+
def rhsOK(tree: Tree): Boolean = tree match {
1005+
case _: TypeBoundsTree | _: Template => false
1006+
case LambdaTypeTree(_, body) => rhsOK(body)
1007+
case _ => true
1008+
}
1009+
rhsOK(rhs)
10491010
}
10501011

1012+
/** Check that modifiers are legal for the definition `tree`.
1013+
* Right now, we only check for `opaque`. TODO: Move other modifier checks here.
1014+
*/
1015+
def checkModifiers(tree: Tree)(implicit ctx: Context): Tree = tree match {
1016+
case tree: MemberDef =>
1017+
var tested: MemberDef = tree
1018+
def fail(msg: String) = ctx.error(msg, tree.sourcePos)
1019+
def checkApplicable(flag: FlagSet, test: MemberDefTest): Unit =
1020+
if (tested.mods.is(flag) && !test.applyOrElse(tree, _ => false)) {
1021+
fail(i"modifier `$flag` is not allowed for this definition")
1022+
tested = tested.withMods(tested.mods.withoutFlags(flag))
1023+
}
1024+
checkApplicable(Opaque, legalOpaque)
1025+
tested
1026+
case _ =>
1027+
tree
1028+
}
1029+
1030+
def defTree(tree: Tree)(implicit ctx: Context): Tree =
1031+
checkModifiers(tree) match {
1032+
case tree: ValDef => valDef(tree)
1033+
case tree: TypeDef =>
1034+
if (tree.isClassDef) classDef(tree) else tree
1035+
case tree: DefDef =>
1036+
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
1037+
else defDef(tree)
1038+
case tree: ModuleDef => moduleDef(tree)
1039+
case tree: PatDef => patDef(tree)
1040+
}
1041+
10511042
/** { stats; <empty > }
10521043
* ==>
10531044
* { stats; () }

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

+2
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,8 @@ object Trees {
818818
extends ProxyTree[T] {
819819
type ThisTree[-T >: Untyped] = Annotated[T]
820820
def forwardTo: Tree[T] = arg
821+
override def disableOverlapChecks = true
822+
// disable overlaps checks since the WithBounds annotation swaps type and annotation.
821823
}
822824

823825
trait WithoutTypeOrPos[-T >: Untyped] extends Tree[T] {

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

+6-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
206206
def DefDef(sym: TermSymbol, tparams: List[TypeSymbol], vparamss: List[List[TermSymbol]],
207207
resultType: Type, rhs: Tree)(implicit ctx: Context): DefDef =
208208
ta.assignType(
209-
untpd.DefDef(sym.name, tparams map TypeDef, vparamss.nestedMap(ValDef(_)),
210-
TypeTree(resultType), rhs),
209+
untpd.DefDef(
210+
sym.name,
211+
tparams.map(tparam => TypeDef(tparam).withSpan(tparam.span)),
212+
vparamss.nestedMap(vparam => ValDef(vparam).withSpan(vparam.span)),
213+
TypeTree(resultType),
214+
rhs),
211215
sym)
212216

213217
def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =

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

+5
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
198198
if (this.flags == flags) this
199199
else copy(flags = flags)
200200

201+
def withoutFlags(flags: FlagSet): Modifiers =
202+
if (this.is(flags))
203+
Modifiers(this.flags &~ flags, this.privateWithin, this.annotations, this.mods.filterNot(_.flags.is(flags)))
204+
else this
205+
201206
def withAddedMod(mod: Mod): Modifiers =
202207
if (mods.exists(_ eq mod)) this
203208
else withMods(mods :+ mod)

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,27 @@ object Annotations {
171171

172172
def unapply(ann: Annotation)(implicit ctx: Context): Option[Symbol] =
173173
if (ann.symbol == defn.ChildAnnot) {
174-
val AppliedType(tycon, (arg: NamedType) :: Nil) = ann.tree.tpe
174+
val AppliedType(_, (arg: NamedType) :: Nil) = ann.tree.tpe
175175
Some(arg.symbol)
176176
}
177177
else None
178178
}
179179

180+
/** Extractor for WithBounds[T] annotations */
181+
object WithBounds {
182+
def unapply(ann: Annotation)(implicit ctx: Context): Option[TypeBounds] =
183+
if (ann.symbol == defn.WithBoundsAnnot) {
184+
// We need to extract the type of the type tree in the New itself.
185+
// The annotation's type has been simplified as the type of an expression,
186+
// which means that `&` or `|` might have been lost.
187+
// Test in pos/reference/opaque.scala
188+
val Apply(TypeApply(Select(New(tpt), nme.CONSTRUCTOR), _), Nil) = ann.tree
189+
val AppliedType(_, lo :: hi :: Nil) = tpt.tpe
190+
Some(TypeBounds(lo, hi))
191+
}
192+
else None
193+
}
194+
180195
def makeSourceFile(path: String)(implicit ctx: Context): Annotation =
181196
apply(defn.SourceFileAnnot, Literal(Constant(path)))
182197
}

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

+3-12
Original file line numberDiff line numberDiff line change
@@ -145,25 +145,16 @@ class CheckRealizable(implicit ctx: Context) {
145145
*/
146146
private def boundsRealizability(tp: Type) = {
147147

148-
def isOpaqueCompanionThis = tp match {
149-
case tp: ThisType => tp.cls.isOpaqueCompanion
150-
case _ => false
151-
}
152-
153148
val memberProblems =
154-
for {
155-
mbr <- tp.nonClassTypeMembers
156-
if !(mbr.info.loBound <:< mbr.info.hiBound) && !mbr.symbol.isOpaqueHelper
157-
}
149+
for mbr <- tp.nonClassTypeMembers if !(mbr.info.loBound <:< mbr.info.hiBound)
158150
yield new HasProblemBounds(mbr.name, mbr.info)
159151

160152
val refinementProblems =
161-
for {
153+
for
162154
name <- refinedNames(tp)
163155
if (name.isTypeName)
164156
mbr <- tp.member(name).alternatives
165-
if !(mbr.info.loBound <:< mbr.info.hiBound) && !isOpaqueCompanionThis
166-
}
157+
if !(mbr.info.loBound <:< mbr.info.hiBound)
167158
yield new HasProblemBounds(name, mbr.info)
168159

169160
def baseTypeProblems(base: Type) = base match {

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

+2
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,8 @@ class Definitions {
884884
def BodyAnnot(implicit ctx: Context): ClassSymbol = BodyAnnotType.symbol.asClass
885885
@threadUnsafe lazy val ChildAnnotType: TypeRef = ctx.requiredClassRef("scala.annotation.internal.Child")
886886
def ChildAnnot(implicit ctx: Context): ClassSymbol = ChildAnnotType.symbol.asClass
887+
@threadUnsafe lazy val WithBoundsAnnotType: TypeRef = ctx.requiredClassRef("scala.annotation.internal.WithBounds")
888+
def WithBoundsAnnot(implicit ctx: Context): ClassSymbol = WithBoundsAnnotType.symbol.asClass
887889
@threadUnsafe lazy val CovariantBetweenAnnotType: TypeRef = ctx.requiredClassRef("scala.annotation.internal.CovariantBetween")
888890
def CovariantBetweenAnnot(implicit ctx: Context): ClassSymbol = CovariantBetweenAnnotType.symbol.asClass
889891
@threadUnsafe lazy val ContravariantBetweenAnnotType: TypeRef = ctx.requiredClassRef("scala.annotation.internal.ContravariantBetween")

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

+2-12
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ object Flags {
268268
/** A mutable var */
269269
final val Mutable: FlagSet = termFlag(12, "mutable")
270270

271-
/** An opqaue type */
271+
/** An opaque type or a class containing one */
272272
final val Opaque: FlagSet = typeFlag(12, "opaque")
273273

274274
final val MutableOrOpaque: FlagSet = Mutable.toCommonFlags
@@ -550,13 +550,7 @@ object Flags {
550550
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased
551551

552552
/** Flags that can apply to a module class */
553-
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags |
554-
Enum | Opaque
555-
556-
/** Flags that are copied from a synthetic companion to a user-defined one
557-
* when the two are merged. See: Namer.mergeCompanionDefs
558-
*/
559-
final val RetainedSyntheticCompanionFlags: FlagSet = Opaque
553+
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | Enum
560554

561555
/** Packages and package classes always have these flags set */
562556
final val PackageCreationFlags: FlagSet =
@@ -687,9 +681,6 @@ object Flags {
687681
/** A Java companion object */
688682
final val JavaModule: FlagConjunction = allOf(JavaDefined, Module)
689683

690-
/** An opaque companion object */
691-
final val OpaqueModule: FlagConjunction = allOf(Opaque, Module)
692-
693684
/** A Java companion object */
694685
final val JavaProtected: FlagConjunction = allOf(JavaDefined, Protected)
695686

@@ -739,7 +730,6 @@ object Flags {
739730
final val SyntheticTermParam: FlagConjunction = allOf(Synthetic, TermParam)
740731
final val SyntheticTypeParam: FlagConjunction = allOf(Synthetic, TypeParam)
741732
final val SyntheticCase: FlagConjunction = allOf(Synthetic, Case)
742-
final val SyntheticOpaque: FlagConjunction = allOf(Synthetic, Opaque)
743733

744734
implicit def conjToFlagSet(conj: FlagConjunction): FlagSet =
745735
FlagSet(conj.bits)

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

+39-38
Original file line numberDiff line numberDiff line change
@@ -372,18 +372,38 @@ object SymDenotations {
372372
case _ => unforcedDecls.openForMutations
373373
}
374374

375-
/** If this is a synthetic opaque type alias, mark it as Deferred with empty bounds
375+
/** If this is a synthetic opaque type alias, mark it as Deferred with bounds
376+
* as given by the right hand side's `WithBounds` annotation, if one is present,
377+
* or with empty bounds of the right kind, otherwise.
378+
* At the same time, integrate the original alias as a refinement of the
379+
* self type of the enclosing class.
376380
*/
377381
final def normalizeOpaque()(implicit ctx: Context) = {
378382
def abstractRHS(tp: Type): Type = tp match {
379383
case tp: HKTypeLambda => tp.derivedLambdaType(resType = abstractRHS(tp.resType))
380384
case _ => defn.AnyType
381385
}
382-
if (isOpaqueHelper) {
386+
if (isOpaqueAlias) {
383387
info match {
384388
case TypeAlias(alias) =>
385-
info = TypeBounds(defn.NothingType, abstractRHS(alias))
389+
val (refiningAlias, bounds) = alias match {
390+
case AnnotatedType(alias1, Annotation.WithBounds(bounds)) =>
391+
(alias1, bounds)
392+
case _ =>
393+
(alias, TypeBounds(defn.NothingType, abstractRHS(alias)))
394+
}
395+
def refineSelfType(selfType: Type) =
396+
RefinedType(selfType, name, TypeAlias(refiningAlias))
397+
val enclClassInfo = owner.asClass.classInfo
398+
enclClassInfo.selfInfo match {
399+
case self: Type =>
400+
owner.info = enclClassInfo.derivedClassInfo(selfInfo = refineSelfType(self))
401+
case self: Symbol =>
402+
self.info = refineSelfType(self.info)
403+
}
404+
info = bounds
386405
setFlag(Deferred)
406+
typeRef.recomputeDenot()
387407
case _ =>
388408
}
389409
}
@@ -553,18 +573,14 @@ object SymDenotations {
553573
final def isAbstractOrParamType(implicit ctx: Context): Boolean = this is DeferredOrTypeParam
554574

555575
/** Is this symbol a user-defined opaque alias type? */
556-
def isOpaqueAlias(implicit ctx: Context): Boolean = is(Opaque, butNot = Synthetic)
557-
558-
/** Is this symbol the companion of an opaque alias type? */
559-
def isOpaqueCompanion(implicit ctx: Context): Boolean = is(OpaqueModule)
576+
def isOpaqueAlias(implicit ctx: Context): Boolean = is(Opaque) && !isClass
560577

561-
/** Is this symbol a synthetic opaque type inside an opaque companion object? */
562-
def isOpaqueHelper(implicit ctx: Context): Boolean = is(SyntheticOpaque, butNot = Module)
578+
/** Is this symbol a module that contains opaque aliases? */
579+
def containsOpaques(implicit ctx: Context): Boolean = is(Opaque) && isClass
563580

564-
/** Can this symbol have a companion module?
565-
* This is the case if it is a class or an opaque type alias.
566-
*/
567-
final def canHaveCompanion(implicit ctx: Context) = isClass || isOpaqueAlias
581+
def seesOpaques(implicit ctx: Context): Boolean =
582+
containsOpaques ||
583+
is(Module, butNot = Package) && owner.containsOpaques
568584

569585
/** Is this the denotation of a self symbol of some class?
570586
* This is the case if one of two conditions holds:
@@ -789,7 +805,7 @@ object SymDenotations {
789805
*/
790806
def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context): Boolean =
791807
!( this.isTerm
792-
|| this.isStaticOwner && !this.isOpaqueCompanion
808+
|| this.isStaticOwner && !this.seesOpaques
793809
|| ctx.erasedTypes
794810
|| (pre eq NoPrefix)
795811
|| (pre eq thisType)
@@ -1029,16 +1045,6 @@ object SymDenotations {
10291045
*/
10301046
final def companionModule(implicit ctx: Context): Symbol =
10311047
if (is(Module)) sourceModule
1032-
else if (isOpaqueAlias) {
1033-
def reference(tp: Type): Symbol = tp match {
1034-
case TypeRef(prefix: TermRef, _) => prefix.termSymbol
1035-
case tp: HKTypeLambda => reference(tp.resType)
1036-
case tp: AppliedType => reference(tp.tycon)
1037-
case tp: ErrorType => registeredCompanion.sourceModule
1038-
}
1039-
val TypeAlias(alias) = info
1040-
reference(alias)
1041-
}
10421048
else registeredCompanion.sourceModule
10431049

10441050
private def companionType(implicit ctx: Context): Symbol =
@@ -1053,13 +1059,6 @@ object SymDenotations {
10531059
final def companionClass(implicit ctx: Context): Symbol =
10541060
companionType.suchThat(_.isClass).symbol
10551061

1056-
/** The opaque type with the same (type-) name as this module or module class,
1057-
* and which is also defined in the same scope and compilation unit.
1058-
* NoSymbol if this type does not exist.
1059-
*/
1060-
final def companionOpaqueType(implicit ctx: Context): Symbol =
1061-
companionType.suchThat(_.isOpaqueAlias).symbol
1062-
10631062
final def scalacLinkedClass(implicit ctx: Context): Symbol =
10641063
if (this is ModuleClass) companionNamed(effectiveName.toTypeName)
10651064
else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass
@@ -1109,15 +1108,17 @@ object SymDenotations {
11091108
final def enclosingSubClass(implicit ctx: Context): Symbol =
11101109
ctx.owner.ownersIterator.findSymbol(_.isSubClass(symbol))
11111110

1112-
/** The alias of a synthetic opaque type that's stored in the self type of the
1111+
/** The alias of an opaque type alias that's stored in the self type of the
11131112
* containing object.
11141113
*/
11151114
def opaqueAlias(implicit ctx: Context): Type = {
1116-
if (isOpaqueHelper)
1117-
owner.asClass.classInfo.selfType match {
1118-
case RefinedType(_, _, bounds) => bounds.extractOpaqueAlias
1119-
}
1120-
else NoType
1115+
def recur(tp: Type): Type = tp match {
1116+
case RefinedType(parent, rname, TypeAlias(alias)) =>
1117+
if (rname == name) alias else recur(parent)
1118+
case _ =>
1119+
NoType
1120+
}
1121+
recur(owner.asClass.classInfo.selfType)
11211122
}
11221123

11231124
/** The non-private symbol whose name and type matches the type of this symbol
@@ -1974,7 +1975,7 @@ object SymDenotations {
19741975

19751976
/** Register companion class */
19761977
override def registerCompanion(companion: Symbol)(implicit ctx: Context) =
1977-
if (companion.canHaveCompanion && !unforcedIsAbsent && !companion.unforcedIsAbsent)
1978+
if (companion.isClass && !unforcedIsAbsent && !companion.unforcedIsAbsent)
19781979
myCompanion = companion
19791980

19801981
override def registeredCompanion(implicit ctx: Context) = { ensureCompleted(); myCompanion }

0 commit comments

Comments
 (0)