Skip to content

Commit f9b8369

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 d8a6f1c commit f9b8369

File tree

11 files changed

+177
-31
lines changed

11 files changed

+177
-31
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
@@ -1089,9 +1089,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
10891089
private val InlinedCalls = new Property.Key[List[Tree]]
10901090

10911091
/** Record an enclosing inlined call.
1092-
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
1093-
* We assume parameters are never nested inside parameters.
1094-
*/
1092+
* EmptyTree calls (for parameters) cancel the next-enclosing call in the list instead of being added to it.
1093+
* We assume parameters are never nested inside parameters.
1094+
*/
10951095
override def inlineContext(call: Tree)(implicit ctx: Context): Context = {
10961096
// We assume enclosingInlineds is already normalized, and only process the new call with the head.
10971097
val oldIC = enclosingInlineds
@@ -1105,7 +1105,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11051105
}
11061106

11071107
/** All enclosing calls that are currently inlined, from innermost to outermost.
1108-
*/
1108+
*/
11091109
def enclosingInlineds(implicit ctx: Context): List[Tree] =
11101110
ctx.property(InlinedCalls).getOrElse(Nil)
11111111

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 =
@@ -642,6 +648,9 @@ object Flags {
642648
/** A Java companion object */
643649
final val JavaModule: FlagConjunction = allOf(JavaDefined, Module)
644650

651+
/** An opaque companion object */
652+
final val OpaqueModule: FlagConjunction = allOf(Opaque, Module)
653+
645654
/** A Java companion object */
646655
final val JavaProtected: FlagConjunction = allOf(JavaDefined, Protected)
647656

@@ -678,15 +687,18 @@ object Flags {
678687
/** Java symbol which is `protected` and `static` */
679688
final val StaticProtected: FlagConjunction = allOf(JavaDefined, Protected, JavaStatic)
680689

690+
final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)
691+
681692
final val AbstractFinal: FlagConjunction = allOf(Abstract, Final)
682693
final val AbstractSealed: FlagConjunction = allOf(Abstract, Sealed)
694+
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)
695+
683696
final val SyntheticArtifact: FlagConjunction = allOf(Synthetic, Artifact)
684697
final val SyntheticModule: FlagConjunction = allOf(Synthetic, Module)
685698
final val SyntheticTermParam: FlagConjunction = allOf(Synthetic, TermParam)
686699
final val SyntheticTypeParam: FlagConjunction = allOf(Synthetic, TypeParam)
687700
final val SyntheticCase: FlagConjunction = allOf(Synthetic, Case)
688-
final val AbstractAndOverride: FlagConjunction = allOf(Abstract, Override)
689-
final val Scala2Trait: FlagConjunction = allOf(Scala2x, Trait)
701+
final val SyntheticOpaque: FlagConjunction = allOf(Synthetic, Opaque)
690702

691703
implicit def conjToFlagSet(conj: FlagConjunction): FlagSet =
692704
FlagSet(conj.bits)

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

Lines changed: 66 additions & 10 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:
@@ -832,10 +858,12 @@ object SymDenotations {
832858
/** The module implemented by this module class, NoSymbol if not applicable. */
833859
final def sourceModule(implicit ctx: Context): Symbol = myInfo match {
834860
case ClassInfo(_, _, _, _, selfType) if this is ModuleClass =>
835-
selfType match {
836-
case selfType: TermRef => selfType.symbol
837-
case selfType: Symbol => selfType.info.asInstanceOf[TermRef].symbol
861+
def sourceOfSelf(tp: Any): Symbol = tp match {
862+
case tp: TermRef => tp.symbol
863+
case tp: Symbol => sourceOfSelf(tp.info)
864+
case tp: RefinedType => sourceOfSelf(tp.parent)
838865
}
866+
sourceOfSelf(selfType)
839867
case info: LazyType =>
840868
info.sourceModule
841869
case _ =>
@@ -956,6 +984,10 @@ object SymDenotations {
956984
*/
957985
final def companionModule(implicit ctx: Context): Symbol =
958986
if (is(Module)) sourceModule
987+
else if (isOpaqueAlias)
988+
info match {
989+
case TypeAlias(TypeRef(TermRef(prefix, _), _)) => prefix.termSymbol
990+
}
959991
else registeredCompanion.sourceModule
960992

961993
private def companionType(implicit ctx: Context): Symbol =
@@ -970,6 +1002,13 @@ object SymDenotations {
9701002
final def companionClass(implicit ctx: Context): Symbol =
9711003
companionType.suchThat(_.isClass).symbol
9721004

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

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

16531710
def computeTypeRef = {
16541711
btrCache.put(tp, NoPrefix)
1655-
tp.symbol.denot match {
1712+
val tpSym = tp.symbol
1713+
tpSym.denot match {
16561714
case clsd: ClassDenotation =>
16571715
def isOwnThis = prefix match {
16581716
case prefix: ThisType => prefix.cls `eq` clsd.owner
16591717
case NoPrefix => true
16601718
case _ => false
16611719
}
16621720
val baseTp =
1663-
if (tp.symbol eq symbol)
1721+
if (tpSym eq symbol)
16641722
tp
16651723
else if (isOwnThis)
16661724
if (clsd.baseClassSet.contains(symbol))
@@ -1674,10 +1732,8 @@ object SymDenotations {
16741732
case _ =>
16751733
val superTp = tp.superType
16761734
val baseTp = recur(superTp)
1677-
if (inCache(superTp) && tp.symbol.maybeOwner.isType)
1678-
record(tp, baseTp) // typeref cannot be a GADT, so cache is stable
1679-
else
1680-
btrCache.remove(tp)
1735+
if (inCache(superTp) && tpSym.maybeOwner.isType) record(tp, baseTp)
1736+
else btrCache.remove(tp)
16811737
baseTp
16821738
}
16831739
}

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

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

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

38673867
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
@@ -807,6 +807,7 @@ class TreeUnpickler(reader: TastyReader,
807807
case _ => rhs.tpe.toBounds
808808
}
809809
sym.resetFlag(Provisional)
810+
sym.normalizeOpaque()
810811
TypeDef(rhs)
811812
}
812813
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))

0 commit comments

Comments
 (0)