Skip to content

Commit 3183820

Browse files
committed
Try to instantiate type variables in tryInsertImplicitOnQualifier
Fixes #13842 What happened in #13842 was that we were not hitting this case in `tryWiden`, which is a method in ApproximatingTypeMap that tries to dealias or widen before propagating a Range outwards: ```scala case info: SingletonType => // if H#x: y.type, then for any x in L..H, x.type =:= y.type, // hence we can replace with y.type under all variances reapply(info) ``` The actual info here was a TypeVar with a singleton type as lower bound. Instantiating the TypeVar produces a SingletonType and case applies. We cannot really instantiate TypeVars here since this is very low-level code. For instance we might be in a state where the instantiation is provisional and the TyperState is thrown away. But AsSeenFrom results are cached so we'd have the wrong type in the caches. What we do instead is add an additional case in `tryInsertImplicitOnQualifier` which is the last resort when an application fails. Here, if we can instantiate some type variables in the qualifier, we try again.
1 parent 483bf2c commit 3183820

File tree

4 files changed

+53
-16
lines changed

4 files changed

+53
-16
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,12 @@ object Inferencing {
9696
* their instantiation could uncover new type members. However that search is best
9797
* effort only. It might miss type variables that appear in structures involving
9898
* alias types and type projections.
99+
* @param applied Test is done in a `tryInsertImplicitOnQualifier` application.
100+
* In this case, we always try to instantiate TypeVars in type arguments.
101+
* If `applied` is false, we only try that in arguments that may affect
102+
* the result type.
99103
*/
100-
def couldInstantiateTypeVar(tp: Type)(using Context): Boolean = tp.dealias match
104+
def couldInstantiateTypeVar(tp: Type, applied: Boolean = false)(using Context): Boolean = tp.dealias match
101105
case tvar: TypeVar
102106
if !tvar.isInstantiated
103107
&& ctx.typerState.constraint.contains(tvar)
@@ -120,14 +124,14 @@ object Inferencing {
120124
case _ => Nil
121125
case _ => Nil
122126
case _ => Nil
123-
couldInstantiateTypeVar(tycon)
124-
|| argsInResult.exists(couldInstantiateTypeVar)
127+
couldInstantiateTypeVar(tycon, applied)
128+
|| (if applied then args else argsInResult).exists(couldInstantiateTypeVar(_, applied))
125129
case RefinedType(parent, _, _) =>
126-
couldInstantiateTypeVar(parent)
130+
couldInstantiateTypeVar(parent, applied)
127131
case tp: AndOrType =>
128-
couldInstantiateTypeVar(tp.tp1) || couldInstantiateTypeVar(tp.tp2)
132+
couldInstantiateTypeVar(tp.tp1, applied) || couldInstantiateTypeVar(tp.tp2, applied)
129133
case AnnotatedType(tp, _) =>
130-
couldInstantiateTypeVar(tp)
134+
couldInstantiateTypeVar(tp, applied)
131135
case _ =>
132136
false
133137

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ object Typer {
8181
*/
8282
private val InsertedApply = new Property.Key[Unit]
8383

84+
/** An attachment on a result of an implicit conversion or extension method
85+
* that was added by tryInsertImplicitOnQualifier. Needed to prevent infinite
86+
* expansions in error cases (e.g. in fuzzy/i9293.scala).
87+
*/
88+
private val InsertedImplicitOnQualifier = new Property.Key[Unit]
89+
8490
/** An attachment on a tree `t` occurring as part of a `t()` where
8591
* the `()` was dropped by the Typer.
8692
*/
@@ -3130,21 +3136,30 @@ class Typer extends Namer
31303136
}
31313137

31323138
/** If this tree is a select node `qual.name` (possibly applied to type variables)
3133-
* that does not conform to `pt`, try to insert an implicit conversion `c` around
3134-
* `qual` so that `c(qual).name` conforms to `pt`.
3139+
* that does not conform to `pt`, try two mitigations:
3140+
* 1. Instantiate any TypeVars in the widened type of `tree` with their lower bounds.
3141+
* 2. Try to insert an implicit conversion `c` around `qual` so that
3142+
* `c(qual).name` conforms to `pt`.
31353143
*/
31363144
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type, locked: TypeVars)(using Context): Option[Tree] = trace(i"try insert impl on qualifier $tree $pt") {
31373145
tree match
31383146
case tree @ Select(qual, name) if name != nme.CONSTRUCTOR =>
3139-
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
3140-
if selProto.isMatchedBy(qual.tpe) then None
3147+
if couldInstantiateTypeVar(qual.tpe.widen, applied = true)
3148+
then
3149+
Some(adapt(tree, pt, locked))
31413150
else
3142-
tryEither {
3143-
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, inSelect = false)
3144-
if tree1.isEmpty then None
3145-
else Some(adapt(tree1, pt, locked))
3146-
} { (_, _) => None
3147-
}
3151+
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
3152+
if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then
3153+
None
3154+
else
3155+
tryEither {
3156+
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, inSelect = false)
3157+
if tree1.isEmpty then None
3158+
else
3159+
tree1.putAttachment(InsertedImplicitOnQualifier, ())
3160+
Some(adapt(tree1, pt, locked))
3161+
} { (_, _) => None
3162+
}
31483163
case TypeApply(fn, args) if args.forall(_.isInstanceOf[untpd.InferredTypeTree]) =>
31493164
tryInsertImplicitOnQualifier(fn, pt, locked)
31503165
case _ => None

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ i8182.scala
6767
# local lifted value in annotation argument has different position after pickling
6868
i2797a
6969

70+
# Late instantiation of type variable in tryInsertImplicitOnQualifier
71+
# allows to simplify a type that was already computed
72+
i13842.scala
73+
7074
# GADT cast applied to singleton type difference
7175
i4176-gadt.scala
7276

tests/pos/i13842.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Parent { class E }
2+
3+
object ChildA extends Parent
4+
5+
object ChildB extends Parent
6+
7+
class Printer[C <: Parent](val child: C):
8+
def print22(e: child.E): String = ""
9+
10+
def test =
11+
Printer(ChildA).print22(new ChildA.E) // does not work
12+
13+
//Printer[ChildA.type](ChildA).print22(new ChildA.E) // works
14+
//val p = Printer(ChildA); p.print22(new ChildA.E) // works

0 commit comments

Comments
 (0)