Skip to content

Commit b0bc379

Browse files
committed
Establish opaque companions
Open opaque types as gadts in opaque companion modules.
1 parent e3689bd commit b0bc379

File tree

10 files changed

+115
-34
lines changed

10 files changed

+115
-34
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,6 +1316,7 @@ object Trees {
13161316
Stats.record("TreeAccumulator.foldOver total")
13171317
def localCtx =
13181318
if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
1319+
def templateCtx = ctx.maybeInOpaqueCompanionContext(ctx.owner)
13191320
tree match {
13201321
case Ident(name) =>
13211322
x
@@ -1398,6 +1399,7 @@ object Trees {
13981399
implicit val ctx = localCtx
13991400
this(x, rhs)
14001401
case tree @ Template(constr, parents, self, _) =>
1402+
implicit val ctx = templateCtx
14011403
this(this(this(this(x, constr), parents), self), tree.body)
14021404
case Import(expr, selectors) =>
14031405
this(x, expr)

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

Lines changed: 7 additions & 5 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
@@ -1101,11 +1101,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
11011101
ts2
11021102
case _ => call :: oldIC
11031103
}
1104-
ctx.fresh.setProperty(InlinedCalls, newIC)
1104+
ctx.fresh
1105+
.setProperty(InlinedCalls, newIC)
1106+
.inOpaqueCompanionsAround(call.symbol)
11051107
}
11061108

11071109
/** All enclosing calls that are currently inlined, from innermost to outermost.
1108-
*/
1110+
*/
11091111
def enclosingInlineds(implicit ctx: Context): List[Tree] =
11101112
ctx.property(InlinedCalls).getOrElse(Nil)
11111113

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,24 @@ object Contexts {
379379
ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt))
380380
}
381381

382+
/** If `owner` is a companion object of an opaque type, record the alias in the GADT bounds */
383+
def maybeInOpaqueCompanionContext(owner: Symbol): Context = {
384+
if (owner.is(Flags.Module, butNot = Flags.Package)) {
385+
val opaq = owner.companionOpaqueType
386+
val alias = opaq.opaqueAlias
387+
if (alias.exists) {
388+
val result = fresh.setFreshGADTBounds
389+
result.gadt.setBounds(opaq, TypeAlias(alias))
390+
result
391+
}
392+
else this
393+
}
394+
else this
395+
}
396+
397+
def inOpaqueCompanionsAround(sym: Symbol) =
398+
(this /: sym.ownersIterator.takeWhile(!_.is(Flags.Package)))(_.maybeInOpaqueCompanionContext(_))
399+
382400
/** The current source file; will be derived from current
383401
* compilation unit.
384402
*/

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ object SymDenotations {
543543
/** Can this symbol have a companion module?
544544
* This is the case if it is a class or an opaque type alias.
545545
*/
546-
final def canHaveCompanion(implicit ctx: Context) = isClass
546+
final def canHaveCompanion(implicit ctx: Context) = isClass || is(Opaque)
547547

548548
/** Is this the denotation of a self symbol of some class?
549549
* This is the case if one of two conditions holds:
@@ -979,6 +979,16 @@ object SymDenotations {
979979
*/
980980
final def companionModule(implicit ctx: Context): Symbol =
981981
if (is(Module)) sourceModule
982+
else if (is(Opaque))
983+
getAnnotation(defn.OpaqueAliasAnnot) match {
984+
case Some(ann) =>
985+
val AppliedType(_, arg :: Nil) = ann.tree.tpe
986+
arg match {
987+
case RefinedType(tp, _, ref) => ref.termSymbol
988+
case _ => NoSymbol
989+
}
990+
case None => NoSymbol
991+
}
982992
else registeredCompanion.sourceModule
983993

984994
private def companionType(implicit ctx: Context): Symbol =
@@ -993,6 +1003,13 @@ object SymDenotations {
9931003
final def companionClass(implicit ctx: Context): Symbol =
9941004
companionType.suchThat(_.isClass).symbol
9951005

1006+
/** The opaque type with the same (type-) name as this module or module class,
1007+
* and which is also defined in the same scope and compilation unit.
1008+
* NoSymbol if this type does not exist.
1009+
*/
1010+
final def companionOpaqueType(implicit ctx: Context): Symbol =
1011+
companionType.suchThat(_.is(Opaque)).symbol
1012+
9961013
final def scalacLinkedClass(implicit ctx: Context): Symbol =
9971014
if (this is ModuleClass) companionNamed(effectiveName.toTypeName)
9981015
else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass
@@ -1690,15 +1707,16 @@ object SymDenotations {
16901707

16911708
def computeTypeRef = {
16921709
btrCache.put(tp, NoPrefix)
1693-
tp.symbol.denot match {
1710+
val tpSym = tp.symbol
1711+
tpSym.denot match {
16941712
case clsd: ClassDenotation =>
16951713
def isOwnThis = prefix match {
16961714
case prefix: ThisType => prefix.cls `eq` clsd.owner
16971715
case NoPrefix => true
16981716
case _ => false
16991717
}
17001718
val baseTp =
1701-
if (tp.symbol eq symbol)
1719+
if (tpSym eq symbol)
17021720
tp
17031721
else if (isOwnThis)
17041722
if (clsd.baseClassSet.contains(symbol))
@@ -1712,7 +1730,7 @@ object SymDenotations {
17121730
case _ =>
17131731
val superTp = tp.superType
17141732
val baseTp = recur(superTp)
1715-
if (inCache(superTp) && tp.symbol.maybeOwner.isType)
1733+
if (inCache(superTp) && tpSym.maybeOwner.isType && !tpSym.is(Opaque))
17161734
record(tp, baseTp) // typeref cannot be a GADT, so cache is stable
17171735
else
17181736
btrCache.remove(tp)

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -830,9 +830,9 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
830830
* tp1 <:< tp2 using fourthTry (this might instantiate params in tp1)
831831
* tp1 <:< app2 using isSubType (this might instantiate params in tp2)
832832
*/
833-
def compareLower(tycon2bounds: TypeBounds, tyconIsTypeRef: Boolean): Boolean =
833+
def compareLower(tycon2bounds: TypeBounds, followSuperType: Boolean): Boolean =
834834
if ((tycon2bounds.lo `eq` tycon2bounds.hi) && !tycon2bounds.isInstanceOf[MatchAlias])
835-
if (tyconIsTypeRef) recur(tp1, tp2.superType)
835+
if (followSuperType) recur(tp1, tp2.superType)
836836
else isSubApproxHi(tp1, tycon2bounds.lo.applyIfParameterized(args2))
837837
else
838838
fallback(tycon2bounds.lo)
@@ -841,13 +841,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
841841
case param2: TypeParamRef =>
842842
isMatchingApply(tp1) ||
843843
canConstrain(param2) && canInstantiate(param2) ||
844-
compareLower(bounds(param2), tyconIsTypeRef = false)
844+
compareLower(bounds(param2), followSuperType = false)
845845
case tycon2: TypeRef =>
846846
isMatchingApply(tp1) ||
847847
defn.isTypelevel_S(tycon2.symbol) && compareS(tp2, tp1, fromBelow = true) || {
848848
tycon2.info match {
849849
case info2: TypeBounds =>
850-
compareLower(info2, tyconIsTypeRef = true)
850+
val gbounds2 = ctx.gadt.bounds(tycon2.symbol)
851+
if (gbounds2 == null) compareLower(info2, followSuperType = true)
852+
else compareLower(gbounds2 & info2, followSuperType = false)
851853
case info2: ClassInfo =>
852854
tycon2.name.toString.startsWith("Tuple") &&
853855
defn.isTupleType(tp2) && isSubType(tp1, tp2.toNestedPairs) ||
@@ -883,8 +885,11 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
883885
case tycon1: TypeRef =>
884886
val sym = tycon1.symbol
885887
!sym.isClass && (
886-
defn.isTypelevel_S(sym) && compareS(tp1, tp2, fromBelow = false) ||
887-
recur(tp1.superType, tp2))
888+
defn.isTypelevel_S(sym) && compareS(tp1, tp2, fromBelow = false) || {
889+
val gbounds1 = ctx.gadt.bounds(tycon1.symbol)
890+
if (gbounds1 == null) recur(tp1.superType, tp2)
891+
else recur((gbounds1.hi & tycon1.info.bounds.hi).applyIfParameterized(args1), tp2)
892+
})
888893
case tycon1: TypeProxy =>
889894
recur(tp1.superType, tp2)
890895
case _ =>

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ object Types {
548548
case tp: TypeRef =>
549549
tp.denot match {
550550
case d: ClassDenotation => d.findMember(name, pre, excluded)
551-
case d => go(d.info)
551+
case d => goTypeRef(d)
552552
}
553553
case tp: AppliedType =>
554554
tp.tycon match {
@@ -563,7 +563,7 @@ object Types {
563563
case _ =>
564564
go(tp.superType)
565565
}
566-
case tp: ThisType => // ??? inline
566+
case tp: ThisType =>
567567
goThis(tp)
568568
case tp: RefinedType =>
569569
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
@@ -595,6 +595,11 @@ object Types {
595595
case _ =>
596596
NoDenotation
597597
}
598+
def goTypeRef(d: Denotation) = {
599+
val mbr = go(d.info)
600+
if (mbr.exists) mbr
601+
else followOpaqueGADT.findMember(name, pre, excluded)
602+
}
598603
def goRec(tp: RecType) =
599604
if (tp.parent == null) NoDenotation
600605
else {
@@ -1367,6 +1372,21 @@ object Types {
13671372
*/
13681373
def deepenProto(implicit ctx: Context): Type = this
13691374

1375+
/** If this is a TypeRef or an Application of a GADT-bound type, replace the
1376+
* GADT reference by its upper GADT bound. Otherwise NoType.
1377+
*/
1378+
def followOpaqueGADT(implicit ctx: Context): Type = widenDealias match {
1379+
case site: TypeRef if site.symbol.is(Opaque) =>
1380+
val bounds = ctx.gadt.bounds(site.symbol)
1381+
if (bounds != 0) bounds.hi else NoType
1382+
case AppliedType(tycon, args) =>
1383+
val tycon1 = tycon.followOpaqueGADT
1384+
if (tycon1.exists) tycon1.appliedTo(args)
1385+
else NoType
1386+
case _ =>
1387+
NoType
1388+
}
1389+
13701390
// ----- Substitutions -----------------------------------------------------
13711391

13721392
/** Substitute all types that refer in their symbol attribute to

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,12 @@ class TreeUnpickler(reader: TastyReader,
12561256
class LazyReader[T <: AnyRef](reader: TreeReader, owner: Symbol, mode: Mode, op: TreeReader => Context => T) extends Trees.Lazy[T] {
12571257
def complete(implicit ctx: Context): T = {
12581258
pickling.println(i"starting to read at ${reader.reader.currentAddr} with owner $owner")
1259-
op(reader)(ctx.withPhaseNoLater(ctx.picklerPhase).withOwner(owner).withModeBits(mode))
1259+
val ctx1 = ctx
1260+
.withPhaseNoLater(ctx.picklerPhase)
1261+
.withOwner(owner)
1262+
.withModeBits(mode)
1263+
.inOpaqueCompanionsAround(owner)
1264+
op(reader)(ctx1)
12601265
}
12611266
}
12621267

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
247247
// be duplicated
248248
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
249249
// would trigger an assertion when pickling).
250-
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
250+
val symTrace =
251+
if (call.symbol.owner.companionOpaqueType.exists) call.symbol.owner
252+
else call.symbol.topLevelClass
253+
val callTrace =
254+
Ident(symTrace.typeRef).withPos(call.pos)
251255
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call)))
252256
case tree: Template =>
253257
withNoCheckNews(tree.parents.flatMap(newPart)) {

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -421,15 +421,15 @@ trait ImplicitRunInfo { self: Run =>
421421
override implicit protected val ctx: Context = liftingCtx
422422
override def stopAtStatic = true
423423
def apply(tp: Type) = tp match {
424-
case tp: TypeRef if tp.symbol.isAbstractOrAliasType =>
424+
case tp: TypeRef if !tp.symbol.canHaveCompanion =>
425425
val pre = tp.prefix
426426
def joinClass(tp: Type, cls: ClassSymbol) =
427427
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
428428
val lead = if (tp.prefix eq NoPrefix) defn.AnyType else apply(tp.prefix)
429429
(lead /: tp.classSymbols)(joinClass)
430430
case tp: TypeVar =>
431431
apply(tp.underlying)
432-
case tp: AppliedType if !tp.tycon.typeSymbol.isClass =>
432+
case tp: AppliedType if !tp.tycon.typeSymbol.canHaveCompanion =>
433433
def applyArg(arg: Type) = arg match {
434434
case TypeBounds(lo, hi) => AndType.make(lo, hi)
435435
case WildcardType(TypeBounds(lo, hi)) => AndType.make(lo, hi)
@@ -467,21 +467,24 @@ trait ImplicitRunInfo { self: Run =>
467467
case tp: NamedType =>
468468
val pre = tp.prefix
469469
comps ++= iscopeRefs(pre)
470-
def addClassScope(cls: ClassSymbol): Unit = {
471-
def addRef(companion: TermRef): Unit = {
472-
val compSym = companion.symbol
473-
if (compSym is Package)
474-
addRef(companion.select(nme.PACKAGE))
475-
else if (compSym.exists)
476-
comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef]
477-
}
478-
def addParentScope(parent: Type): Unit =
479-
iscopeRefs(tp.baseType(parent.classSymbol)) foreach addRef
480-
val companion = cls.companionModule
470+
def addRef(companion: TermRef): Unit = {
471+
val compSym = companion.symbol
472+
if (compSym is Package)
473+
addRef(companion.select(nme.PACKAGE))
474+
else if (compSym.exists)
475+
comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef]
476+
}
477+
def addCompanionOf(sym: Symbol) = {
478+
val companion = sym.companionModule
481479
if (companion.exists) addRef(companion.termRef)
482-
cls.classParents foreach addParentScope
483480
}
484-
tp.classSymbols(liftingCtx) foreach addClassScope
481+
def addClassScope(cls: ClassSymbol): Unit = {
482+
addCompanionOf(cls)
483+
for (parent <- cls.classParents; ref <- iscopeRefs(tp.baseType(parent.classSymbol)))
484+
addRef(ref)
485+
}
486+
if (tp.widen.typeSymbol.is(Opaque)) addCompanionOf(tp.widen.typeSymbol)
487+
else tp.classSymbols(liftingCtx).foreach(addClassScope)
485488
case _ =>
486489
for (part <- tp.namedPartsWith(_.isType)) comps ++= iscopeRefs(part)
487490
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import core._
66
import ast._
77
import Contexts._, Types._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
88
import NameKinds.DepParamName
9+
import SymDenotations.NoDenotation
910
import Trees._
1011
import Constants._
1112
import util.{Stats, SimpleIdentityMap}
@@ -135,6 +136,9 @@ object ProtoTypes {
135136
// Note: can't use `m.info` here because if `m` is a method, `m.info`
136137
// loses knowledge about `m`'s default arguments.
137138
mbr match { // hasAltWith inlined for performance
139+
case NoDenotation =>
140+
val tp2 = tp1.followOpaqueGADT
141+
tp2.exists && isMatchedBy(tp2)
138142
case mbr: SingleDenotation => mbr.exists && qualifies(mbr)
139143
case _ => mbr hasAltWith qualifies
140144
}

0 commit comments

Comments
 (0)