Skip to content

Fix liftToClasses for opaque types #6712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -440,24 +440,29 @@ object Types {
NoSymbol
}

/** The least (wrt <:<) set of class symbols of which this type is a subtype
/** The least (wrt <:<) set of symbols satisfying the `include` prediacte of which this type is a subtype
*/
final def classSymbols(implicit ctx: Context): List[ClassSymbol] = this match {
final def parentSymbols(include: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = this match {
case tp: ClassInfo =>
tp.cls :: Nil
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
if (include(sym)) sym :: Nil else tp.superType.parentSymbols(include)
case tp: TypeProxy =>
tp.underlying.classSymbols
tp.underlying.parentSymbols(include)
case AndType(l, r) =>
l.classSymbols | r.classSymbols
l.parentSymbols(include) | r.parentSymbols(include)
case OrType(l, r) =>
l.classSymbols intersect r.classSymbols // TODO does not conform to spec
l.parentSymbols(include) intersect r.parentSymbols(include) // TODO does not conform to spec
case _ =>
Nil
}

/** The least (wrt <:<) set of class symbols of which this type is a subtype
*/
final def classSymbols(implicit ctx: Context): List[ClassSymbol] =
parentSymbols(_.isClass).asInstanceOf

/** The term symbol associated with the type */
@tailrec final def termSymbol(implicit ctx: Context): Symbol = this match {
case tp: TermRef => tp.symbol
Expand Down
27 changes: 18 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ object Implicits {
*/
class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
assert(initctx.typer != null)
implicits.println(i"implicits of type $tp = ${companionRefs.toList}%, %")
@threadUnsafe lazy val refs: List[ImplicitRef] = {
val buf = new mutable.ListBuffer[TermRef]
for (companion <- companionRefs) buf ++= companion.implicitMembers(ImplicitOrImpliedOrGiven)
Expand Down Expand Up @@ -491,13 +492,19 @@ trait ImplicitRunInfo { self: Run =>
object liftToClasses extends TypeMap {
override implicit protected val ctx: Context = liftingCtx
override def stopAtStatic = true

def apply(tp: Type) = tp match {
case tp: TypeRef if !tp.symbol.isClass =>
val pre = tp.prefix
def joinClass(tp: Type, cls: ClassSymbol) =
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre)
(lead /: tp.classSymbols)(joinClass)
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass || sym.isOpaqueAlias) tp
else {
val pre = tp.prefix
def joinClass(tp: Type, cls: Symbol) =
AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre)
def isLiftTarget(sym: Symbol) = sym.isClass || sym.isOpaqueAlias
(lead /: tp.parentSymbols(isLiftTarget))(joinClass)
}
case tp: TypeVar =>
apply(tp.underlying)
case tp: AppliedType if !tp.tycon.typeSymbol.isClass =>
Expand All @@ -516,7 +523,7 @@ trait ImplicitRunInfo { self: Run =>

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

def iscopeRefs(t: Type): TermRefSet = implicitScopeCache.get(t) match {
case Some(is) =>
Expand Down Expand Up @@ -575,8 +582,10 @@ trait ImplicitRunInfo { self: Run =>
def computeIScope() = {
val liftedTp = if (isLifted) tp else liftToClasses(tp)
val refs =
if (liftedTp ne tp)
if (liftedTp ne tp) {
implicitsDetailed.println(i"lifted of $tp = $liftedTp")
iscope(liftedTp, isLifted = true).companionRefs
}
else
collectCompanions(tp)
val result = new OfTypeImplicits(tp, refs)(ctx)
Expand Down Expand Up @@ -673,7 +682,7 @@ trait Implicits { self: Typer =>
}

/** Synthesize the tree for `'[T]` for an implicit `scala.quoted.Type[T]`.
* `T` is deeply dealiassed to avoid references to local type aliases.
* `T` is deeply dealiased to avoid references to local type aliases.
*/
lazy val synthesizedTypeTag: SpecialHandler =
(formal, span) => implicit ctx => {
Expand Down
25 changes: 25 additions & 0 deletions tests/pos/implicit-scope.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
object A {

object opaques {
opaque type FlagSet = Long
def FlagSet(bits: Long): FlagSet = bits.asInstanceOf // !!!
def toBits(fs: FlagSet): Long = fs
}
val someFlag = FlagSet(1)
type FlagSet = opaques.FlagSet
def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits)

delegate FlagOps {
def (xs: FlagSet) bits: Long = opaques.toBits(xs)
}
}

object B {
type Variance = A.FlagSet

val f: A.FlagSet = A.someFlag
f.bits // OK

val v: Variance = A.someFlag
v.bits // OK, used to fail with: value bits is not a member of B.Variance
}