diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 6f65d81c1635..7654b98995ff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -499,28 +499,30 @@ object Inferencing { propagate(accu(SimpleIdentityMap.empty, tp)) } + /** Run the transformation after dealiasing but return the original type if it was a no-op. */ + private def derivedOnDealias(tp: Type)(transform: Type => Type)(using Context) = { + val dealiased = tp.dealias + val transformed = transform(dealiased) + if transformed eq dealiased then tp // return the original type, not the result of dealiasing + else transformed + } + /** Replace every top-level occurrence of a wildcard type argument by * a fresh skolem type. The skolem types are of the form $i.CAP, where * $i is a skolem of type `scala.internal.TypeBox`, and `CAP` is its * type member. See the documentation of `TypeBox` for a rationale why we do this. */ - def captureWildcards(tp: Type)(using Context): Type = tp match { + def captureWildcards(tp: Type)(using Context): Type = derivedOnDealias(tp) { case tp @ AppliedType(tycon, args) if tp.hasWildcardArg => - tycon.typeParams match { - case tparams @ ((_: Symbol) :: _) => - val boundss = tparams.map(_.paramInfo.substApprox(tparams.asInstanceOf[List[TypeSymbol]], args)) - val args1 = args.zipWithConserve(boundss) { (arg, bounds) => - arg match { - case TypeBounds(lo, hi) => - val skolem = SkolemType(defn.TypeBoxClass.typeRef.appliedTo(lo | bounds.loBound, hi & bounds.hiBound)) - TypeRef(skolem, defn.TypeBox_CAP) - case arg => arg - } - } - tp.derivedAppliedType(tycon, args1) - case _ => - tp + val tparams = tycon.typeParamSymbols + val args1 = args.zipWithConserve(tparams.map(_.paramInfo.substApprox(tparams, args))) { + case (TypeBounds(lo, hi), bounds) => + val skolem = SkolemType(defn.TypeBoxClass.typeRef.appliedTo(lo | bounds.loBound, hi & bounds.hiBound)) + TypeRef(skolem, defn.TypeBox_CAP) + case (arg, _) => + arg } + if tparams.isEmpty then tp else tp.derivedAppliedType(tycon, args1) case tp: AndOrType => tp.derivedAndOrType(captureWildcards(tp.tp1), captureWildcards(tp.tp2)) case tp: RefinedType => tp.derivedRefinedType(captureWildcards(tp.parent), tp.refinedName, tp.refinedInfo) case tp: RecType => tp.derivedRecType(captureWildcards(tp.parent)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6728cab6f3d0..556287da5221 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3160,7 +3160,7 @@ class Typer extends Namer */ def adapt(tree: Tree, pt: Type, locked: TypeVars, tryGadtHealing: Boolean = true)(using Context): Tree = try - trace(i"adapting $tree to $pt ${if (tryGadtHealing) "" else "(tryGadtHealing=false)" }\n", typr, show = true) { + trace(i"adapting $tree to $pt ${if (tryGadtHealing) "" else "(tryGadtHealing=false)" }", typr, show = true) { record("adapt") adapt1(tree, pt, locked, tryGadtHealing) } diff --git a/tests/pos/i12739-fallout.scala b/tests/pos/i12739-fallout.scala new file mode 100644 index 000000000000..7f685b3c1a0f --- /dev/null +++ b/tests/pos/i12739-fallout.scala @@ -0,0 +1,8 @@ +// This is a minimisation of the fallout that the original fix caused on Shapeless 3. + +type Foo = { type Bar } + +extension (foo: Foo) + def toBar(): foo.Bar = ??? + +def test(foo: Foo): foo.Bar = foo.toBar() diff --git a/tests/pos/i12739.scala b/tests/pos/i12739.scala new file mode 100644 index 000000000000..baf7a7ae2698 --- /dev/null +++ b/tests/pos/i12739.scala @@ -0,0 +1,26 @@ +object X { + + class CA[A] + type C = CA[_] + val c: C = ??? + def f[A](r: CA[A]) = () + def g(): CA[_] = CA() + def h(): C = ??? + + // works + f(c) + + // works + val x = c.asInstanceOf[C] + f(x) + + // was: error + f(c.asInstanceOf[C]) + + // works, error in Scala 2 + f(c.asInstanceOf[c.type]) + + f(c.asInstanceOf[CA[_]]) + f(g()) + f(h()) +}