Skip to content

Commit d1b9a01

Browse files
dwijnandSethTisue
andcommitted
Handle type alias in capturing wildcards
We extend capturing wildcards so that it operates on the type after dealiasing. However, in line with, for example, derivedRefinedType, we want to return the original type if nothing was captured, because it's on that basis that adapt decides whether it needs to cast and re-adapt to the new type with the type boxes. Co-authored-by: Seth Tisue <[email protected]>
1 parent 7d702a3 commit d1b9a01

File tree

4 files changed

+52
-16
lines changed

4 files changed

+52
-16
lines changed

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

+17-15
Original file line numberDiff line numberDiff line change
@@ -499,28 +499,30 @@ object Inferencing {
499499
propagate(accu(SimpleIdentityMap.empty, tp))
500500
}
501501

502+
/** Run the transformation after dealiasing but return the original type if it was a no-op. */
503+
private def derivedOnDealias(tp: Type)(transform: Type => Type)(using Context) = {
504+
val dealiased = tp.dealias
505+
val transformed = transform(dealiased)
506+
if transformed eq dealiased then tp // return the original type, not the result of dealiasing
507+
else transformed
508+
}
509+
502510
/** Replace every top-level occurrence of a wildcard type argument by
503511
* a fresh skolem type. The skolem types are of the form $i.CAP, where
504512
* $i is a skolem of type `scala.internal.TypeBox`, and `CAP` is its
505513
* type member. See the documentation of `TypeBox` for a rationale why we do this.
506514
*/
507-
def captureWildcards(tp: Type)(using Context): Type = tp match {
515+
def captureWildcards(tp: Type)(using Context): Type = derivedOnDealias(tp) {
508516
case tp @ AppliedType(tycon, args) if tp.hasWildcardArg =>
509-
tycon.typeParams match {
510-
case tparams @ ((_: Symbol) :: _) =>
511-
val boundss = tparams.map(_.paramInfo.substApprox(tparams.asInstanceOf[List[TypeSymbol]], args))
512-
val args1 = args.zipWithConserve(boundss) { (arg, bounds) =>
513-
arg match {
514-
case TypeBounds(lo, hi) =>
515-
val skolem = SkolemType(defn.TypeBoxClass.typeRef.appliedTo(lo | bounds.loBound, hi & bounds.hiBound))
516-
TypeRef(skolem, defn.TypeBox_CAP)
517-
case arg => arg
518-
}
519-
}
520-
tp.derivedAppliedType(tycon, args1)
521-
case _ =>
522-
tp
517+
val tparams = tycon.typeParamSymbols
518+
val args1 = args.zipWithConserve(tparams.map(_.paramInfo.substApprox(tparams, args))) {
519+
case (TypeBounds(lo, hi), bounds) =>
520+
val skolem = SkolemType(defn.TypeBoxClass.typeRef.appliedTo(lo | bounds.loBound, hi & bounds.hiBound))
521+
TypeRef(skolem, defn.TypeBox_CAP)
522+
case (arg, _) =>
523+
arg
523524
}
525+
tp.derivedAppliedType(tycon, args1)
524526
case tp: AndOrType => tp.derivedAndOrType(captureWildcards(tp.tp1), captureWildcards(tp.tp2))
525527
case tp: RefinedType => tp.derivedRefinedType(captureWildcards(tp.parent), tp.refinedName, tp.refinedInfo)
526528
case tp: RecType => tp.derivedRecType(captureWildcards(tp.parent))

compiler/src/dotty/tools/dotc/typer/Typer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3160,7 +3160,7 @@ class Typer extends Namer
31603160
*/
31613161
def adapt(tree: Tree, pt: Type, locked: TypeVars, tryGadtHealing: Boolean = true)(using Context): Tree =
31623162
try
3163-
trace(i"adapting $tree to $pt ${if (tryGadtHealing) "" else "(tryGadtHealing=false)" }\n", typr, show = true) {
3163+
trace(i"adapting $tree to $pt ${if (tryGadtHealing) "" else "(tryGadtHealing=false)" }", typr, show = true) {
31643164
record("adapt")
31653165
adapt1(tree, pt, locked, tryGadtHealing)
31663166
}

tests/pos/i12739-fallout.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// This is a minimisation of the fallout that the original fix caused on Shapeless 3.
2+
3+
type Foo = { type Bar }
4+
5+
extension (foo: Foo)
6+
def toBar(): foo.Bar = ???
7+
8+
def test(foo: Foo): foo.Bar = foo.toBar()

tests/pos/i12739.scala

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
object X {
2+
3+
class CA[A]
4+
type C = CA[_]
5+
val c: C = ???
6+
def f[A](r: CA[A]) = ()
7+
def g(): CA[_] = CA()
8+
def h(): C = ???
9+
10+
// works
11+
f(c)
12+
13+
// works
14+
val x = c.asInstanceOf[C]
15+
f(x)
16+
17+
// was: error
18+
f(c.asInstanceOf[C])
19+
20+
// works, error in Scala 2
21+
f(c.asInstanceOf[c.type])
22+
23+
f(c.asInstanceOf[CA[_]])
24+
f(g())
25+
f(h())
26+
}

0 commit comments

Comments
 (0)