Skip to content

Handle type alias in capturing wildcards #13260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 8 additions & 0 deletions tests/pos/i12739-fallout.scala
Original file line number Diff line number Diff line change
@@ -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()
26 changes: 26 additions & 0 deletions tests/pos/i12739.scala
Original file line number Diff line number Diff line change
@@ -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())
}