Skip to content

Commit c404b10

Browse files
committed
Introduce capture wildcard adaptation
If tree is not a subtype of expected type, try to capture wildcard types in its type with fresh skolems.
1 parent 424d5ea commit c404b10

File tree

5 files changed

+55
-3
lines changed

5 files changed

+55
-3
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,10 @@ class Definitions {
755755

756756
def Eql_eqlAny(implicit ctx: Context): TermSymbol = EqlModule.requiredMethod(nme.eqlAny)
757757

758+
lazy val TypeBoxType: TypeRef = ctx.requiredClassRef("scala.internal.TypeBox")
759+
760+
lazy val TypeBox_CAP: TypeSymbol = TypeBoxType.symbol.requiredType(tpnme.CAP)
761+
758762
lazy val NotType: TypeRef = ctx.requiredClassRef("scala.implicits.Not")
759763
def NotClass(implicit ctx: Context): ClassSymbol = NotType.symbol.asClass
760764
def NotModule(implicit ctx: Context): Symbol = NotClass.companionModule

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ object StdNames {
326326
val AnnotatedType: N = "AnnotatedType"
327327
val AppliedTypeTree: N = "AppliedTypeTree"
328328
val ArrayAnnotArg: N = "ArrayAnnotArg"
329+
val CAP: N = "CAP"
329330
val Constant: N = "Constant"
330331
val ConstantType: N = "ConstantType"
331332
val doubleHash: N = "doubleHash"

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3609,8 +3609,12 @@ object Types {
36093609

36103610
// ----- Skolem types -----------------------------------------------
36113611

3612-
/** A skolem type reference with underlying type `binder`. */
3613-
case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType {
3612+
/** A skolem type reference with underlying type `info`.
3613+
* Note: `info` is a var, since this allows one to create a number of skolem types
3614+
* and fill in their infos with types that refers to the skolem types recursively.
3615+
* This is used in `captureWildcards` in Typer`.
3616+
*/
3617+
case class SkolemType(var info: Type) extends UncachedProxyType with ValueType with SingletonType {
36143618
override def underlying(implicit ctx: Context): Type = info
36153619
def derivedSkolemType(info: Type)(implicit ctx: Context): SkolemType =
36163620
if (info eq this.info) this else SkolemType(info)

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ object Typer {
7979
* search was tried on a tree. This will in some cases be reported in error messages
8080
*/
8181
private[typer] val HiddenSearchFailure = new Property.Key[SearchFailure]
82+
83+
val isBounds: Type => Boolean = _.isInstanceOf[TypeBounds]
8284
}
8385

8486
class Typer extends Namer
@@ -1917,7 +1919,7 @@ class Typer extends Namer
19171919
val elemTpes = (elems, pts).zipped.map((elem, pt) =>
19181920
ctx.typeComparer.widenInferred(elem.tpe, pt))
19191921
val resTpe = (elemTpes :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _))
1920-
app1.asInstance(resTpe)
1922+
app1.cast(resTpe)
19211923
}
19221924
}
19231925
}
@@ -2697,10 +2699,47 @@ class Typer extends Namer
26972699
case tree: Closure => cpy.Closure(tree)(tpt = TypeTree(pt)).withType(pt)
26982700
}
26992701

2702+
/** Replace every top-level occurrence of a wildcard type argument by
2703+
* a skolem type
2704+
*/
2705+
def captureWildcards(tp: Type)(implicit ctx: Context): Type = tp match {
2706+
case tp: AndOrType => tp.derivedAndOrType(captureWildcards(tp.tp1), captureWildcards(tp.tp2))
2707+
case tp: RefinedType => tp.derivedRefinedType(captureWildcards(tp.parent), tp.refinedName, tp.refinedInfo)
2708+
case tp: RecType => tp.derivedRecType(captureWildcards(tp.parent))
2709+
case tp: LazyRef => captureWildcards(tp.ref)
2710+
case tp: AnnotatedType => tp.derivedAnnotatedType(captureWildcards(tp.parent), tp.annot)
2711+
case tp @ AppliedType(tycon, args) if args.exists(isBounds) =>
2712+
tycon.typeParams match {
2713+
case tparams @ ((_: Symbol) :: _) =>
2714+
val args1 = args.map {
2715+
case TypeBounds(lo, hi) =>
2716+
val skolem = SkolemType(defn.TypeBoxType.appliedTo(lo, hi))
2717+
TypeRef(skolem, defn.TypeBox_CAP)
2718+
case arg => arg
2719+
}
2720+
val boundss = tparams.map(_.paramInfo.subst(tparams.asInstanceOf[List[TypeSymbol]], args1))
2721+
for ((newArg, oldArg, bounds) <- (args1, args, boundss).zipped)
2722+
if (newArg `ne` oldArg) {
2723+
val TypeRef(skolem @ SkolemType(app @ AppliedType(typeBox, lo :: hi :: Nil)), _) = newArg
2724+
skolem.info = app.derivedAppliedType(
2725+
typeBox, (lo | bounds.loBound) :: (hi & bounds.hiBound) :: Nil)
2726+
}
2727+
tp.derivedAppliedType(tycon, args1)
2728+
case _ =>
2729+
tp
2730+
}
2731+
case _ => tp
2732+
}
2733+
27002734
def adaptToSubType(wtp: Type): Tree = {
27012735
// try converting a constant to the target type
27022736
val folded = ConstFold(tree, pt)
27032737
if (folded ne tree) return adaptConstant(folded, folded.tpe.asInstanceOf[ConstantType])
2738+
2739+
// Try to capture wildcards in type
2740+
val captured = captureWildcards(wtp)
2741+
if (captured `ne` wtp) return readapt(tree.cast(captured))
2742+
27042743
// drop type if prototype is Unit
27052744
if (pt isRef defn.UnitClass) {
27062745
// local adaptation makes sure every adapted tree conforms to its pt
@@ -2709,6 +2748,7 @@ class Typer extends Namer
27092748
checkStatementPurity(tree1)(tree, ctx.owner)
27102749
return tpd.Block(tree1 :: Nil, Literal(Constant(())))
27112750
}
2751+
27122752
// convert function literal to SAM closure
27132753
tree match {
27142754
case closure(Nil, id @ Ident(nme.ANON_FUN), _)
@@ -2725,6 +2765,7 @@ class Typer extends Namer
27252765
}
27262766
case _ =>
27272767
}
2768+
27282769
// try an extension method in scope
27292770
pt match {
27302771
case SelectionProto(name, mbrType, _, _) =>
@@ -2749,6 +2790,7 @@ class Typer extends Namer
27492790
}
27502791
case _ =>
27512792
}
2793+
27522794
// try an implicit conversion
27532795
val prevConstraint = ctx.typerState.constraint
27542796
def recover(failure: SearchFailureType) =

tests/pos/i3598.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
class Foo[A] {
22
def baz(foo: Foo[_]): Unit = bar(foo)
3+
def bam(foo: => Foo[_]) = bar(foo)
34
def bar[A](foo: Foo[A]): A = ???
45
}

0 commit comments

Comments
 (0)