Skip to content

Commit 2c84a78

Browse files
authored
Merge pull request #6712 from dotty-staging/fix-liftToClasses
Fix liftToClasses for opaque types
2 parents 1c235fa + bf6b9f2 commit 2c84a78

File tree

3 files changed

+54
-15
lines changed

3 files changed

+54
-15
lines changed

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,24 +440,29 @@ object Types {
440440
NoSymbol
441441
}
442442

443-
/** The least (wrt <:<) set of class symbols of which this type is a subtype
443+
/** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype
444444
*/
445-
final def classSymbols(implicit ctx: Context): List[ClassSymbol] = this match {
445+
final def parentSymbols(include: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = this match {
446446
case tp: ClassInfo =>
447447
tp.cls :: Nil
448448
case tp: TypeRef =>
449449
val sym = tp.symbol
450-
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
450+
if (include(sym)) sym :: Nil else tp.superType.parentSymbols(include)
451451
case tp: TypeProxy =>
452-
tp.underlying.classSymbols
452+
tp.underlying.parentSymbols(include)
453453
case AndType(l, r) =>
454-
l.classSymbols | r.classSymbols
454+
l.parentSymbols(include) | r.parentSymbols(include)
455455
case OrType(l, r) =>
456-
l.classSymbols intersect r.classSymbols // TODO does not conform to spec
456+
l.parentSymbols(include) intersect r.parentSymbols(include) // TODO does not conform to spec
457457
case _ =>
458458
Nil
459459
}
460460

461+
/** The least (wrt <:<) set of class symbols of which this type is a subtype
462+
*/
463+
final def classSymbols(implicit ctx: Context): List[ClassSymbol] =
464+
parentSymbols(_.isClass).asInstanceOf
465+
461466
/** The term symbol associated with the type */
462467
@tailrec final def termSymbol(implicit ctx: Context): Symbol = this match {
463468
case tp: TermRef => tp.symbol

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ object Implicits {
232232
*/
233233
class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
234234
assert(initctx.typer != null)
235+
implicits.println(i"implicits of type $tp = ${companionRefs.toList}%, %")
235236
@threadUnsafe lazy val refs: List[ImplicitRef] = {
236237
val buf = new mutable.ListBuffer[TermRef]
237238
for (companion <- companionRefs) buf ++= companion.implicitMembers(ImplicitOrImpliedOrGiven)
@@ -491,13 +492,19 @@ trait ImplicitRunInfo { self: Run =>
491492
object liftToClasses extends TypeMap {
492493
override implicit protected val ctx: Context = liftingCtx
493494
override def stopAtStatic = true
495+
494496
def apply(tp: Type) = tp match {
495-
case tp: TypeRef if !tp.symbol.isClass =>
496-
val pre = tp.prefix
497-
def joinClass(tp: Type, cls: ClassSymbol) =
498-
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
499-
val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre)
500-
(lead /: tp.classSymbols)(joinClass)
497+
case tp: TypeRef =>
498+
val sym = tp.symbol
499+
if (sym.isClass || sym.isOpaqueAlias) tp
500+
else {
501+
val pre = tp.prefix
502+
def joinClass(tp: Type, cls: Symbol) =
503+
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
504+
val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre)
505+
def isLiftTarget(sym: Symbol) = sym.isClass || sym.isOpaqueAlias
506+
(lead /: tp.parentSymbols(isLiftTarget))(joinClass)
507+
}
501508
case tp: TypeVar =>
502509
apply(tp.underlying)
503510
case tp: AppliedType if !tp.tycon.typeSymbol.isClass =>
@@ -516,7 +523,7 @@ trait ImplicitRunInfo { self: Run =>
516523

517524
// todo: compute implicits directly, without going via companionRefs?
518525
def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") {
519-
trace(i"collectCompanions($tp)", implicits) {
526+
trace(i"collectCompanions($tp)", implicitsDetailed) {
520527

521528
def iscopeRefs(t: Type): TermRefSet = implicitScopeCache.get(t) match {
522529
case Some(is) =>
@@ -575,8 +582,10 @@ trait ImplicitRunInfo { self: Run =>
575582
def computeIScope() = {
576583
val liftedTp = if (isLifted) tp else liftToClasses(tp)
577584
val refs =
578-
if (liftedTp ne tp)
585+
if (liftedTp ne tp) {
586+
implicitsDetailed.println(i"lifted of $tp = $liftedTp")
579587
iscope(liftedTp, isLifted = true).companionRefs
588+
}
580589
else
581590
collectCompanions(tp)
582591
val result = new OfTypeImplicits(tp, refs)(ctx)
@@ -673,7 +682,7 @@ trait Implicits { self: Typer =>
673682
}
674683

675684
/** Synthesize the tree for `'[T]` for an implicit `scala.quoted.Type[T]`.
676-
* `T` is deeply dealiassed to avoid references to local type aliases.
685+
* `T` is deeply dealiased to avoid references to local type aliases.
677686
*/
678687
lazy val synthesizedTypeTag: SpecialHandler =
679688
(formal, span) => implicit ctx => {

tests/pos/implicit-scope.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
object A {
2+
3+
object opaques {
4+
opaque type FlagSet = Long
5+
def FlagSet(bits: Long): FlagSet = bits.asInstanceOf // !!!
6+
def toBits(fs: FlagSet): Long = fs
7+
}
8+
val someFlag = FlagSet(1)
9+
type FlagSet = opaques.FlagSet
10+
def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits)
11+
12+
delegate FlagOps {
13+
def (xs: FlagSet) bits: Long = opaques.toBits(xs)
14+
}
15+
}
16+
17+
object B {
18+
type Variance = A.FlagSet
19+
20+
val f: A.FlagSet = A.someFlag
21+
f.bits // OK
22+
23+
val v: Variance = A.someFlag
24+
v.bits // OK, used to fail with: value bits is not a member of B.Variance
25+
}

0 commit comments

Comments
 (0)