From ff844b5a838e930ca9b629125fd9689781809b75 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Jun 2019 22:34:45 +0200 Subject: [PATCH 1/4] Fix liftToClasses for opaque types liftToClasses used to drop an opaque types from the set of types whose companion modules are included. Opaque types do not have companion modules, but their prefixes might, so it is incorrect to drop them. --- .../dotty/tools/dotc/typer/Implicits.scala | 30 +++++++++++++------ tests/pos/implicit-scope.scala | 25 ++++++++++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 tests/pos/implicit-scope.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 310c93befda9..0547493747a3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -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) @@ -492,12 +493,21 @@ trait ImplicitRunInfo { self: Run => 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: NamedType => + tp.info match { + case TypeAlias(alias) => + apply(alias) + case TypeBounds(_, hi) => + if (tp.symbol.isOpaqueAlias) tp + else { + 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 /: hi.classSymbols)(joinClass) + } + case _ => tp + } case tp: TypeVar => apply(tp.underlying) case tp: AppliedType if !tp.tycon.typeSymbol.isClass => @@ -516,7 +526,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) => @@ -575,8 +585,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) @@ -673,7 +685,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 => { diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala new file mode 100644 index 000000000000..62636b09adbc --- /dev/null +++ b/tests/pos/implicit-scope.scala @@ -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 +} \ No newline at end of file From db2390bc08142974129d4f86060d2d9d5add32f4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 11:54:43 +0200 Subject: [PATCH 2/4] Revise liftToClasses to make CB work The difference is that in an alias type `p.A = q.B` we take `p` instead of `q` as the prefix to search. --- compiler/src/dotty/tools/dotc/core/Types.scala | 17 +++++++++++------ .../src/dotty/tools/dotc/typer/Implicits.scala | 12 ++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 06765608430e..d470cba6fd68 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0547493747a3..97a853a011d7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -492,7 +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 => + 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: NamedType => tp.info match { case TypeAlias(alias) => From fae600926605bb7e8df26334c0127ec49b26ecad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 12:28:48 +0200 Subject: [PATCH 3/4] drop redundant code --- .../src/dotty/tools/dotc/typer/Implicits.scala | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 97a853a011d7..a2a529d9b05c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -505,21 +505,6 @@ trait ImplicitRunInfo { self: Run => def isLiftTarget(sym: Symbol) = sym.isClass || sym.isOpaqueAlias (lead /: tp.parentSymbols(isLiftTarget))(joinClass) } - case tp: NamedType => - tp.info match { - case TypeAlias(alias) => - apply(alias) - case TypeBounds(_, hi) => - if (tp.symbol.isOpaqueAlias) tp - else { - 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 /: hi.classSymbols)(joinClass) - } - case _ => tp - } case tp: TypeVar => apply(tp.underlying) case tp: AppliedType if !tp.tycon.typeSymbol.isClass => @@ -598,7 +583,7 @@ trait ImplicitRunInfo { self: Run => val liftedTp = if (isLifted) tp else liftToClasses(tp) val refs = if (liftedTp ne tp) { - implicitsDetailed.println(i"lifted of $tp = $liftedTp") + implicitsDetailed.println(i"lifted of $tp = $liftedTp") iscope(liftedTp, isLifted = true).companionRefs } else From bf6b9f2d6b84522782580a2267df6738e876e8d5 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 20 Jun 2019 17:58:34 +0200 Subject: [PATCH 4/4] Update compiler/src/dotty/tools/dotc/core/Types.scala Co-Authored-By: Guillaume Martres --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d470cba6fd68..ee9ace2c7355 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -440,7 +440,7 @@ object Types { NoSymbol } - /** The least (wrt <:<) set of symbols satisfying the `include` prediacte of which this type is a subtype + /** The least (wrt <:<) set of symbols satisfying the `include` predicate of which this type is a subtype */ final def parentSymbols(include: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = this match { case tp: ClassInfo =>