Skip to content

Commit 5d7aabc

Browse files
committed
Desugaring and basic infrastructure for opaque types
Desugaring, entering in Namer, and basic navigation operations for opaque types. Establishes opaque companions.
1 parent 4bb7ba4 commit 5d7aabc

File tree

11 files changed

+175
-27
lines changed

11 files changed

+175
-27
lines changed

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,46 @@ object desugar {
674674
}
675675
}
676676

677+
/** Expand
678+
*
679+
* <mods> opaque type T = [Xs] => R
680+
*
681+
* to
682+
*
683+
* <mods> opaque type T = T.T
684+
* synthetic object T {
685+
* synthetic opaque type T >: [Xs] => R
686+
* }
687+
*
688+
* The generated companion object will later (in Namer) be merged with the user-defined
689+
* companion object, and the synthetic opaque type member will go into the self type.
690+
*/
691+
def opaqueAlias(tdef: TypeDef)(implicit ctx: Context): Tree =
692+
if (tdef.rhs.isInstanceOf[TypeBoundsTree]) {
693+
ctx.error(em"opaque type ${tdef.name} must be an alias type", tdef.pos)
694+
tdef.withFlags(tdef.mods.flags &~ Opaque)
695+
}
696+
else {
697+
def completeForwarder(fwd: Tree) = tdef.rhs match {
698+
case LambdaTypeTree(tparams, tpt) =>
699+
val tparams1 =
700+
for (tparam <- tparams)
701+
yield tparam.withMods(tparam.mods | Synthetic)
702+
lambdaAbstract(tparams1,
703+
AppliedTypeTree(fwd, tparams.map(tparam => Ident(tparam.name))))
704+
case _ =>
705+
fwd
706+
}
707+
val moduleName = tdef.name.toTermName
708+
val aliasType = cpy.TypeDef(tdef)(
709+
rhs = completeForwarder(Select(Ident(moduleName), tdef.name)))
710+
val localType = tdef.withFlags(Synthetic | Opaque)
711+
val companions = moduleDef(ModuleDef(
712+
moduleName, Template(emptyConstructor, Nil, EmptyValDef, localType :: Nil))
713+
.withFlags(Synthetic | Opaque))
714+
Thicket(aliasType :: companions.toList)
715+
}
716+
677717
/** The name of `mdef`, after checking that it does not redefine a Scala core class.
678718
* If it does redefine, issue an error and return a mangled name instead of the original one.
679719
*/
@@ -769,7 +809,10 @@ object desugar {
769809

770810
def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
771811
case tree: ValDef => valDef(tree)
772-
case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree
812+
case tree: TypeDef =>
813+
if (tree.isClassDef) classDef(tree)
814+
else if (tree.mods.is(Opaque, butNot = Synthetic)) opaqueAlias(tree)
815+
else tree
773816
case tree: DefDef =>
774817
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
775818
else defDef(tree)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,8 @@ object Trees {
350350
asInstanceOf[ThisTree[Untyped]]
351351
}
352352

353-
protected def setMods(mods: untpd.Modifiers): Unit = myMods = mods
353+
/** Destructively update modifiers. To be used with care. */
354+
def setMods(mods: untpd.Modifiers): Unit = myMods = mods
354355

355356
/** The position of the name defined by this definition.
356357
* This is a point position if the definition is synthetic, or a range position

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,9 +1102,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11021102
private val InlinedCalls = new Property.Key[List[Tree]]
11031103

11041104
/** Record an enclosing inlined call.
1105-
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
1106-
* We assume parameters are never nested inside parameters.
1107-
*/
1105+
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
1106+
* We assume parameters are never nested inside parameters.
1107+
*/
11081108
override def inlineContext(call: Tree)(implicit ctx: Context): Context = {
11091109
// We assume enclosingInlineds is already normalized, and only process the new call with the head.
11101110
val oldIC = enclosingInlineds
@@ -1118,7 +1118,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11181118
}
11191119

11201120
/** All enclosing calls that are currently inlined, from innermost to outermost.
1121-
*/
1121+
*/
11221122
def enclosingInlineds(implicit ctx: Context): List[Tree] =
11231123
ctx.property(InlinedCalls).getOrElse(Nil)
11241124

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,13 @@ object Flags {
526526
Accessor | AbsOverride | Stable | Captured | Synchronized | Erased
527527

528528
/** Flags that can apply to a module class */
529-
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | ImplClass | Enum
529+
final val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags |
530+
ImplClass | Enum | Opaque
531+
532+
/** Flags that are copied from a synthetic companion to a user-defined one
533+
* when the two are merged. See: Namer.mergeCompanionDefs
534+
*/
535+
final val RetainedSyntheticCompanionFlags: FlagSet = Opaque
530536

531537
/** Packages and package classes always have these flags set */
532538
final val PackageCreationFlags: FlagSet =
@@ -639,6 +645,9 @@ object Flags {
639645
/** A Java companion object */
640646
final val JavaModule: FlagConjunction = allOf(JavaDefined, Module)
641647

648+
/** An opaque companion object */
649+
final val OpaqueModule: FlagConjunction = allOf(Opaque, Module)
650+
642651
/** A Java companion object */
643652
final val JavaProtected: FlagConjunction = allOf(JavaDefined, Protected)
644653

@@ -675,15 +684,18 @@ object Flags {
675684
/** Java symbol which is `protected` and `static` */
676685
final val StaticProtected: FlagConjunction = allOf(JavaDefined, Protected, JavaStatic)
677686

687+
final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)
688+
678689
final val AbstractFinal: FlagConjunction = allOf(Abstract, Final)
679690
final val AbstractSealed: FlagConjunction = allOf(Abstract, Sealed)
691+
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)
692+
680693
final val SyntheticArtifact: FlagConjunction = allOf(Synthetic, Artifact)
681694
final val SyntheticModule: FlagConjunction = allOf(Synthetic, Module)
682695
final val SyntheticTermParam: FlagConjunction = allOf(Synthetic, TermParam)
683696
final val SyntheticTypeParam: FlagConjunction = allOf(Synthetic, TypeParam)
684697
final val SyntheticCase: FlagConjunction = allOf(Synthetic, Case)
685-
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)
686-
final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)
698+
final val SyntheticOpaque: FlagConjunction = allOf(Synthetic, Opaque)
687699

688700
implicit def conjToFlagSet(conj: FlagConjunction): FlagSet =
689701
FlagSet(conj.bits)

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

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,23 @@ object SymDenotations {
365365
case _ => unforcedDecls.openForMutations
366366
}
367367

368+
/** If this is a synthetic opaque type alias, mark it as Deferred with empty bounds
369+
*/
370+
final def normalizeOpaque()(implicit ctx: Context) = {
371+
def abstractRHS(tp: Type): Type = tp match {
372+
case tp: HKTypeLambda => tp.derivedLambdaType(resType = abstractRHS(tp.resType))
373+
case _ => defn.AnyType
374+
}
375+
if (isOpaqueHelper) {
376+
info match {
377+
case TypeAlias(alias) =>
378+
info = TypeBounds(defn.NothingType, abstractRHS(alias))
379+
setFlag(Deferred)
380+
case _ =>
381+
}
382+
}
383+
}
384+
368385
// ------ Names ----------------------------------------------
369386

370387
/** The expanded name of this denotation. */
@@ -517,10 +534,19 @@ object SymDenotations {
517534
/** Is this symbol an abstract type or type parameter? */
518535
final def isAbstractOrParamType(implicit ctx: Context): Boolean = this is DeferredOrTypeParam
519536

537+
/** Is this symbol a user-defined opaque alias type? */
538+
def isOpaqueAlias(implicit ctx: Context): Boolean = is(Opaque, butNot = Synthetic)
539+
540+
/** Is this symbol the companion of an opaque alias type? */
541+
def isOpaqueCompanion(implicit ctx: Context): Boolean = is(OpaqueModule)
542+
543+
/** Is this symbol a synthetic opaque type inside an opaque companion object? */
544+
def isOpaqueHelper(implicit ctx: Context): Boolean = is(SyntheticOpaque, butNot = Module)
545+
520546
/** Can this symbol have a companion module?
521547
* This is the case if it is a class or an opaque type alias.
522548
*/
523-
final def canHaveCompanion(implicit ctx: Context) = isClass
549+
final def canHaveCompanion(implicit ctx: Context) = isClass || isOpaqueAlias
524550

525551
/** Is this the denotation of a self symbol of some class?
526552
* This is the case if one of two conditions holds:
@@ -830,10 +856,12 @@ object SymDenotations {
830856
/** The module implemented by this module class, NoSymbol if not applicable. */
831857
final def sourceModule(implicit ctx: Context): Symbol = myInfo match {
832858
case ClassInfo(_, _, _, _, selfType) if this is ModuleClass =>
833-
selfType match {
834-
case selfType: TermRef => selfType.symbol
835-
case selfType: Symbol => selfType.info.asInstanceOf[TermRef].symbol
859+
def sourceOfSelf(tp: Any): Symbol = tp match {
860+
case tp: TermRef => tp.symbol
861+
case tp: Symbol => sourceOfSelf(tp.info)
862+
case tp: RefinedType => sourceOfSelf(tp.parent)
836863
}
864+
sourceOfSelf(selfType)
837865
case info: LazyType =>
838866
info.sourceModule
839867
case _ =>
@@ -954,6 +982,10 @@ object SymDenotations {
954982
*/
955983
final def companionModule(implicit ctx: Context): Symbol =
956984
if (is(Module)) sourceModule
985+
else if (isOpaqueAlias)
986+
info match {
987+
case TypeAlias(TypeRef(TermRef(prefix, _), _)) => prefix.termSymbol
988+
}
957989
else registeredCompanion.sourceModule
958990

959991
private def companionType(implicit ctx: Context): Symbol =
@@ -968,6 +1000,13 @@ object SymDenotations {
9681000
final def companionClass(implicit ctx: Context): Symbol =
9691001
companionType.suchThat(_.isClass).symbol
9701002

1003+
/** The opaque type with the same (type-) name as this module or module class,
1004+
* and which is also defined in the same scope and compilation unit.
1005+
* NoSymbol if this type does not exist.
1006+
*/
1007+
final def companionOpaqueType(implicit ctx: Context): Symbol =
1008+
companionType.suchThat(_.isOpaqueAlias).symbol
1009+
9711010
final def scalacLinkedClass(implicit ctx: Context): Symbol =
9721011
if (this is ModuleClass) companionNamed(effectiveName.toTypeName)
9731012
else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass
@@ -1017,6 +1056,24 @@ object SymDenotations {
10171056
final def enclosingSubClass(implicit ctx: Context): Symbol =
10181057
ctx.owner.ownersIterator.findSymbol(_.isSubClass(symbol))
10191058

1059+
/** The alias of a synthetic opaque type that's stored in the self type of the
1060+
* containing object.
1061+
*/
1062+
def opaqueAlias(implicit ctx: Context): Type = {
1063+
if (isOpaqueHelper) {
1064+
owner.asClass.classInfo.selfType match {
1065+
case RefinedType(_, _, TypeBounds(lo, _)) =>
1066+
def extractAlias(tp: Type): Type = tp match {
1067+
case OrType(alias, _) => alias
1068+
case HKTypeLambda(tparams, tp) =>
1069+
HKTypeLambda(tparams.map(_.paramInfo), extractAlias(tp))
1070+
}
1071+
extractAlias(lo)
1072+
}
1073+
}
1074+
else NoType
1075+
}
1076+
10201077
/** The non-private symbol whose name and type matches the type of this symbol
10211078
* in the given class.
10221079
* @param inClass The class containing the result symbol's definition
@@ -1653,15 +1710,16 @@ object SymDenotations {
16531710

16541711
def computeTypeRef = {
16551712
btrCache.put(tp, NoPrefix)
1656-
tp.symbol.denot match {
1713+
val tpSym = tp.symbol
1714+
tpSym.denot match {
16571715
case clsd: ClassDenotation =>
16581716
def isOwnThis = prefix match {
16591717
case prefix: ThisType => prefix.cls `eq` clsd.owner
16601718
case NoPrefix => true
16611719
case _ => false
16621720
}
16631721
val baseTp =
1664-
if (tp.symbol eq symbol)
1722+
if (tpSym eq symbol)
16651723
tp
16661724
else if (isOwnThis)
16671725
if (clsd.baseClassSet.contains(symbol))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3870,7 +3870,7 @@ object Types {
38703870
extends CachedClassInfo(prefix, cls, Nil, decls, selfInfo) {
38713871

38723872
/** Install classinfo with known parents in `denot` s */
3873-
def finalize(denot: SymDenotation, parents: List[Type])(implicit ctx: Context): Unit =
3873+
def finalize(denot: SymDenotation, parents: List[Type], selfInfo: TypeOrSymbol)(implicit ctx: Context): Unit =
38743874
denot.info = ClassInfo(prefix, cls, parents, decls, selfInfo)
38753875

38763876
override def derivedClassInfo(prefix: Type)(implicit ctx: Context): ClassInfo =

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@ class TreeUnpickler(reader: TastyReader,
813813
case _ => rhs.tpe.toBounds
814814
}
815815
sym.resetFlag(Provisional)
816+
sym.normalizeOpaque()
816817
TypeDef(rhs)
817818
}
818819
case PARAM =>

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ object Scala2Unpickler {
128128
else
129129
registerCompanionPair(scalacCompanion, denot.classSymbol)
130130

131-
tempInfo.finalize(denot, normalizedParents) // install final info, except possibly for typeparams ordering
131+
tempInfo.finalize(denot, normalizedParents, ost) // install final info, except possibly for typeparams ordering
132132
denot.ensureTypeParamsInCorrectOrder()
133133
}
134134
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ object Checking {
412412
checkNoConflict(Lazy, ParamAccessor, s"parameter may not be `lazy`")
413413
if (sym.is(Inline)) checkApplicable(Inline, sym.isTerm && !sym.is(Mutable | Module))
414414
if (sym.is(Lazy)) checkApplicable(Lazy, !sym.is(Method | Mutable))
415+
if (sym.is(Opaque, butNot = (Synthetic | Module))) checkApplicable(Opaque, sym.isAliasType)
415416
if (sym.isType && !sym.is(Deferred))
416417
for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) {
417418
fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden))

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

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@ class Namer { typer: Typer =>
502502
case _ =>
503503
}
504504

505-
506505
def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context): Unit = tree match {
507506
case t: MemberDef if t.rawComment.isDefined =>
508507
ctx.docCtx.foreach(_.addDocstring(sym, t.rawComment))
@@ -558,6 +557,7 @@ class Namer { typer: Typer =>
558557
if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) {
559558
removeInExpanded(fromStat, fromCls)
560559
val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body)
560+
mcls.setMods(toCls.mods | (fromCls.mods.flags & RetainedSyntheticCompanionFlags))
561561
moduleClsDef(fromCls.name) = (toStat, mcls)
562562
}
563563

@@ -617,12 +617,11 @@ class Namer { typer: Typer =>
617617
val classDef = mutable.Map[TypeName, TypeDef]()
618618
val moduleDef = mutable.Map[TypeName, TypeDef]()
619619

620-
def updateCache(cdef: TypeDef): Unit = {
621-
if (!cdef.isClassDef || cdef.mods.is(Package)) return
622-
623-
if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
624-
else classDef(cdef.name) = cdef
625-
}
620+
def updateCache(cdef: TypeDef): Unit =
621+
if (cdef.isClassDef && !cdef.mods.is(Package) || cdef.mods.is(Opaque, butNot = Synthetic)) {
622+
if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
623+
else classDef(cdef.name) = cdef
624+
}
626625

627626
for (stat <- stats)
628627
expanded(stat) match {
@@ -909,7 +908,7 @@ class Namer { typer: Typer =>
909908

910909
addAnnotations(denot.symbol)
911910

912-
val selfInfo =
911+
val selfInfo: TypeOrSymbol =
913912
if (self.isEmpty) NoType
914913
else if (cls.is(Module)) {
915914
val moduleType = cls.owner.thisType select sourceModule
@@ -937,7 +936,35 @@ class Namer { typer: Typer =>
937936
ensureFirstIsClass(parents.map(checkedParentType(_)), cls.pos))
938937
typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %")
939938

940-
tempInfo.finalize(denot, parentTypes)
939+
val finalSelfInfo: TypeOrSymbol =
940+
if (cls.isOpaqueCompanion) {
941+
// The self type of an opaque companion is refined with the type-alias of the original opaque type
942+
def refineOpaqueCompanionSelfType(mt: Type, stats: List[Tree]): RefinedType = (stats: @unchecked) match {
943+
case (td @ TypeDef(localName, rhs)) :: _
944+
if td.mods.is(SyntheticOpaque) && localName == name.stripModuleClassSuffix =>
945+
// create a context owned by the current opaque helper symbol,
946+
// but otherwise corresponding to the context enclosing the opaque
947+
// companion object, since that's where the rhs was defined.
948+
val aliasCtx = ctx.outer.fresh.setOwner(symbolOfTree(td))
949+
val alias = typedAheadType(rhs)(aliasCtx).tpe
950+
val original = cls.companionOpaqueType.typeRef
951+
val cmp = ctx.typeComparer
952+
val bounds = TypeBounds(cmp.orType(alias, original), cmp.andType(alias, original))
953+
RefinedType(mt, localName, bounds)
954+
case _ :: stats1 =>
955+
refineOpaqueCompanionSelfType(mt, stats1)
956+
}
957+
selfInfo match {
958+
case self: Type =>
959+
refineOpaqueCompanionSelfType(self, rest)
960+
case self: Symbol =>
961+
self.info = refineOpaqueCompanionSelfType(self.info, rest)
962+
self
963+
}
964+
}
965+
else selfInfo
966+
967+
tempInfo.finalize(denot, parentTypes, finalSelfInfo)
941968

942969
Checking.checkWellFormed(cls)
943970
if (isDerivedValueClass(cls)) cls.setFlag(Final)
@@ -1236,6 +1263,7 @@ class Namer { typer: Typer =>
12361263
tref.recomputeDenot()
12371264
case _ =>
12381265
}
1266+
sym.normalizeOpaque()
12391267
ensureUpToDate(sym.typeRef, dummyInfo)
12401268
ensureUpToDate(sym.typeRef.appliedTo(tparamSyms.map(_.typeRef)), TypeBounds.empty)
12411269
sym.info

0 commit comments

Comments
 (0)