diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 17d22d23803c..b041a69e171c 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1118,8 +1118,31 @@ object Denotations { 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)) + 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): Type = tp match { + case RefinedType(parent, rname, rinfo) => + if (rname == name) rinfo else findRefined(parent, name) + case _ => + symbol.info + } + derived(findRefined(pre.underlying, symbol.name)) + case _ => + if (!owner.membersNeedAsSeenFrom(pre) || symbol.is(NonMember)) this + else derived(symbol.info) + } } /** Does this denotation have all the `required` flags but none of the `excluded` flags? diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 06765608430e..a245eb33648d 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 @@ -1244,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 @@ -1428,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 @@ -1728,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`. */ @@ -1884,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) } 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 => 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 = { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 310c93befda9..a2a529d9b05c 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) @@ -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 => @@ -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) => @@ -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) @@ -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 => { 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/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 new file mode 100644 index 000000000000..ca4524066f0b --- /dev/null +++ b/tests/pos/implicit-scope.scala @@ -0,0 +1,30 @@ +object A { + + object opaques { + opaque type FlagSet = Long + def FlagSet(bits: Long): FlagSet = bits + 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) + def (xs: FlagSet) | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) + } +} + +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 + + 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 } }