From ff844b5a838e930ca9b629125fd9689781809b75 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Jun 2019 22:34:45 +0200 Subject: [PATCH 1/9] 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/9] 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/9] 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 573b664e9760303bfd8510ba5162ab06c7bdf633 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 11:24:46 +0200 Subject: [PATCH 4/9] Ignore all kinds of ProtoTypes instead of just AppliedProtos In blocks, conditionals and matches we mapped applied prototypes to WildcardTypes in order to force eta expansions. With extension methods the same problem arises for SelectionProtos as well. Furthermore, it makes to sense to propagate a ViewProto into a local context. So the consistent thing to do is to map all kinds of ProtoTypes to WildcardTypes in these situations. This fails one line in run/implicitsFuns.scala, which, however is of dubious value. (The line requires a manually inserted `apply` to propagate a `given` argument into an if-then-else, circumvening the previous "no applies propagated" strategy. --- compiler/src/dotty/tools/dotc/core/Types.scala | 8 +++++--- compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala | 4 ---- compiler/src/dotty/tools/dotc/typer/Typer.scala | 10 +++++----- tests/pos/implicit-scope.scala | 5 +++++ tests/run/implicitFuns.scala | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d470cba6fd68..43e66c67b912 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1249,9 +1249,6 @@ object Types { case _ => if (isRepeatedParam) this.argTypesHi.head else this } - /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */ - def notApplied: Type = this - // ----- Normalizing typerefs over refined types ---------------------------- /** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed @@ -1433,6 +1430,9 @@ object Types { /** If this is an ignored proto type, its underlying type, otherwise the type itself */ def revealIgnored: Type = this + /** If this is a proto type, the ignored version, otherwise the type itself */ + def dropIfProto: Type = this + // ----- Substitutions ----------------------------------------------------- /** Substitute all types that refer in their symbol attribute to @@ -1733,6 +1733,8 @@ object Types { * captures the given context `ctx`. */ def withContext(ctx: Context): ProtoType = this + + override def dropIfProto = WildcardType } /** Implementations of this trait cache the results of `narrow`. */ diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index c69b06c872db..f96a0c761503 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -260,8 +260,6 @@ object ProtoTypes { if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this else new FunProto(args, resultType)(typer, isGivenApply) - override def notApplied: Type = WildcardType - /** @return True if all arguments have types. */ def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = @@ -453,8 +451,6 @@ object ProtoTypes { if ((targs eq this.targs) && (resType eq this.resType)) this else PolyProto(targs, resType) - override def notApplied: Type = WildcardType - def map(tm: TypeMap)(implicit ctx: Context): PolyProto = derivedPolyProto(targs, tm(resultType)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 60c074f82955..547f132d2066 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -712,7 +712,7 @@ class Typer extends Namer def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context): Tree = track("typedBlock") { val (exprCtx, stats1) = typedBlockStats(tree.stats) - val expr1 = typedExpr(tree.expr, pt.notApplied)(exprCtx) + val expr1 = typedExpr(tree.expr, pt.dropIfProto)(exprCtx) ensureNoLocalRefs( cpy.Block(tree)(stats1, expr1).withType(expr1.tpe), pt, localSyms(stats1)) } @@ -766,7 +766,7 @@ class Typer extends Namer } else { val thenp1 :: elsep1 :: Nil = harmonic(harmonize, pt)( - (tree.thenp :: tree.elsep :: Nil).map(typed(_, pt.notApplied))) + (tree.thenp :: tree.elsep :: Nil).map(typed(_, pt.dropIfProto))) assignType(cpy.If(tree)(cond1, thenp1, elsep1), thenp1, elsep1) } } @@ -1068,7 +1068,7 @@ class Typer extends Namer // Overridden in InlineTyper for inline matches def typedMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: Type)(implicit ctx: Context): Tree = { - val cases1 = harmonic(harmonize, pt)(typedCases(cases, wideSelType, pt.notApplied)) + val cases1 = harmonic(harmonize, pt)(typedCases(cases, wideSelType, pt.dropIfProto)) .asInstanceOf[List[CaseDef]] assignType(cpy.Match(tree)(sel, cases1), sel, cases1) } @@ -1193,8 +1193,8 @@ class Typer extends Namer def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") { val expr2 :: cases2x = harmonic(harmonize, pt) { - val expr1 = typed(tree.expr, pt.notApplied) - val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied) + val expr1 = typed(tree.expr, pt.dropIfProto) + val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.dropIfProto) expr1 :: cases1 } val finalizer1 = typed(tree.finalizer, defn.UnitType) diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 62636b09adbc..4c2f71b33c09 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -11,6 +11,7 @@ object A { delegate FlagOps { def (xs: FlagSet) bits: Long = opaques.toBits(xs) + def (xs: FlagSet) | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } } @@ -22,4 +23,8 @@ object B { val v: Variance = A.someFlag v.bits // OK, used to fail with: value bits is not a member of B.Variance + + A.someFlag.bits // OK + var x = 0 + (if (x > 0) A.someFlag else A.someFlag).bits // OK, used to fail with: value bits is not a member of ? } \ No newline at end of file diff --git a/tests/run/implicitFuns.scala b/tests/run/implicitFuns.scala index 508d54c31569..719d5b502847 100644 --- a/tests/run/implicitFuns.scala +++ b/tests/run/implicitFuns.scala @@ -56,7 +56,7 @@ object Test { def foo(s: String): Stringly[Int] = 42 - (if ("".isEmpty) foo("") else foo("")).apply given "" + //(if ("".isEmpty) foo("") else foo("")).apply given "" // does not typecheck } } From 70535c80b6ef8b62b87934a27100670eaaf05db2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 13:54:39 +0200 Subject: [PATCH 5/9] Fix bug in error message Used to print: does not take parameters. --- .../src/dotty/tools/dotc/reporting/diagnostic/messages.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 43c2410d5bad..51746c5c0020 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1363,7 +1363,9 @@ object messages { val msg: String = { val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" - em"${methodSymbol.showLocated} does not take$more parameters" + val meth = methodSymbol + val methStr = if (meth.exists) methodSymbol.showLocated else "expression" + em"$methStr does not take$more parameters" } val explanation: String = { From 5381a3daf62e3277900ed7b0b282288f559dc644 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 17:55:49 +0200 Subject: [PATCH 6/9] Refine printing of Export trees --- .../tools/dotc/printing/RefinedPrinter.scala | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 390ed5029122..9e7257aca9dc 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -317,6 +317,20 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => tree } + def importText(deleg: Boolean, expr: Tree, selectors: List[Tree]) = { + def selectorText(sel: Tree): Text = sel match { + case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) + case _: Ident => toTextGlobal(sel) + case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt) + } + val selectorsText: Text = selectors match { + case id :: Nil => toText(id) + case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" + } + (keywordText("delegate ") provided deleg) ~ + toTextLocal(expr) ~ "." ~ selectorsText + } + tree match { case id: Trees.BackquotedIdent[_] if !homogenizedView => "`" ~ toText(id.name) ~ "`" @@ -499,18 +513,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { typeDefText(tparamsTxt, optText(rhs)(" = " ~ _)) } recur(rhs, "") - case Import(importImplied, expr, selectors) => - def selectorText(sel: Tree): Text = sel match { - case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) - case _: Ident => toTextGlobal(sel) - case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt) - } - val selectorsText: Text = selectors match { - case id :: Nil => toText(id) - case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}" - } - keywordText("import ") ~ (keywordText("delegate ") provided importImplied) ~ - toTextLocal(expr) ~ "." ~ selectorsText + case Import(deleg, expr, selectors) => + keywordText("import ") ~ importText(deleg, expr, selectors) + case Export(deleg, expr, selectors) => + keywordText("export ") ~ importText(deleg, expr, selectors) case packageDef: PackageDef => packageDefText(packageDef) case tree: Template => From e3170845584242278e5c6980cd5436c506de89c1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 17:56:42 +0200 Subject: [PATCH 7/9] Handle opaque types in computeAsSeenFrom --- .../dotty/tools/dotc/core/Denotations.scala | 26 +++++++++++++++---- tests/pos/export-opaque.scala | 11 ++++++++ tests/pos/implicit-scope.scala | 2 +- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 tests/pos/export-opaque.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 17d22d23803c..7439d5c98bd4 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1114,12 +1114,28 @@ object Denotations { type AsSeenFromResult = SingleDenotation protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = { val symbol = this.symbol - val owner = this match { - case thisd: SymDenotation => thisd.owner - case _ => if (symbol.exists) symbol.owner else NoSymbol + def derived = { + val owner = this match { + case thisd: SymDenotation => thisd.owner + case _ => if (symbol.exists) symbol.owner else NoSymbol + } + if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this + else derivedSingleDenotation(symbol, symbol.info.asSeenFrom(pre, owner)) + } + pre match { + case pre: ThisType if symbol.isOpaqueAlias && pre.cls == symbol.owner => + symbol.normalizeOpaque() + def findRefined(tp: Type, name: Name): SingleDenotation = tp match { + case RefinedType(parent, rname, rinfo) => + if (rname == name) derivedSingleDenotation(symbol, rinfo) + else findRefined(parent, name) + case _ => + derived + } + findRefined(pre.underlying, symbol.name) + case _ => + derived } - if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this - else derivedSingleDenotation(symbol, symbol.info.asSeenFrom(pre, owner)) } /** Does this denotation have all the `required` flags but none of the `excluded` flags? diff --git a/tests/pos/export-opaque.scala b/tests/pos/export-opaque.scala new file mode 100644 index 000000000000..10258806e078 --- /dev/null +++ b/tests/pos/export-opaque.scala @@ -0,0 +1,11 @@ +object A { + + object opaques { + opaque type FlagSet = Long + def FlagSet(bits: Long): FlagSet = bits + } + //type FlagSet = opaques.FlagSet + //def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) + export opaques.FlagSet + +} diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 4c2f71b33c09..ca4524066f0b 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -2,7 +2,7 @@ object A { object opaques { opaque type FlagSet = Long - def FlagSet(bits: Long): FlagSet = bits.asInstanceOf // !!! + def FlagSet(bits: Long): FlagSet = bits def toBits(fs: FlagSet): Long = fs } val someFlag = FlagSet(1) From e4fe509c62ef3caf0eb9b292d78a00d302dc04e2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 20 Jun 2019 18:40:32 +0200 Subject: [PATCH 8/9] Drop special provision for opaque types in computeDenot It turns out we needed that only because of the hole that's now plugged in computeAsSeenFrom --- compiler/src/dotty/tools/dotc/core/Types.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 43e66c67b912..a245eb33648d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1891,10 +1891,8 @@ object Types { finish(memberDenot(symd.initial.name, allowPrivate = false)) else if (prefix.isArgPrefixOf(symd)) finish(argDenot(sym.asType)) - else if (infoDependsOnPrefix(symd, prefix)) { - if (!symd.isClass && symd.is(Opaque, butNot = Deferred)) symd.normalizeOpaque() + else if (infoDependsOnPrefix(symd, prefix)) finish(memberDenot(symd.initial.name, allowPrivate = symd.is(Private))) - } else finish(symd.current) } From 8a7522ccdb0892d569f91260f7ccc380483624ab Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 21 Jun 2019 10:24:57 +0200 Subject: [PATCH 9/9] Add explanations --- .../dotty/tools/dotc/core/Denotations.scala | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 7439d5c98bd4..b041a69e171c 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1114,27 +1114,34 @@ object Denotations { type AsSeenFromResult = SingleDenotation protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = { val symbol = this.symbol - def derived = { - val owner = this match { - case thisd: SymDenotation => thisd.owner - case _ => if (symbol.exists) symbol.owner else NoSymbol - } - if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this - else derivedSingleDenotation(symbol, symbol.info.asSeenFrom(pre, owner)) + val owner = this match { + case thisd: SymDenotation => thisd.owner + case _ => if (symbol.exists) symbol.owner else NoSymbol } + def derived(info: Type) = derivedSingleDenotation(symbol, info.asSeenFrom(pre, owner)) pre match { case pre: ThisType if symbol.isOpaqueAlias && pre.cls == symbol.owner => + // This code is necessary to compensate for a "window of vulnerability" with + // opaque types. The problematic sequence is as follows. + // 1. Type a selection `m.this.T` where `T` is an opaque type alias in `m` + // and this is the first access + // 2. `T` will normalize to an abstract type on completion. + // 3. At that time, the default logic in the second case is wrong: `T`'s new info + // is now an abstract type and running it through an asSeenFrom gives nothing. + // We fix this as follows: + // 1. Force opaque normalization as first step + // 2. Read the info from the enclosing object's refinement symbol.normalizeOpaque() - def findRefined(tp: Type, name: Name): SingleDenotation = tp match { + def findRefined(tp: Type, name: Name): Type = tp match { case RefinedType(parent, rname, rinfo) => - if (rname == name) derivedSingleDenotation(symbol, rinfo) - else findRefined(parent, name) + if (rname == name) rinfo else findRefined(parent, name) case _ => - derived + symbol.info } - findRefined(pre.underlying, symbol.name) + derived(findRefined(pre.underlying, symbol.name)) case _ => - derived + if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this + else derived(symbol.info) } }