Skip to content

Commit

Permalink
Merge pull request #13884 from dotty-staging/fix-13842
Browse files Browse the repository at this point in the history
Try to instantiate type variables in tryInsertImplicitOnQualifier
  • Loading branch information
smarter committed Nov 15, 2021
2 parents 4d81752 + 3183820 commit 6e3e34d
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 16 deletions.
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Expand Up @@ -96,8 +96,12 @@ object Inferencing {
* their instantiation could uncover new type members. However that search is best
* effort only. It might miss type variables that appear in structures involving
* alias types and type projections.
* @param applied Test is done in a `tryInsertImplicitOnQualifier` application.
* In this case, we always try to instantiate TypeVars in type arguments.
* If `applied` is false, we only try that in arguments that may affect
* the result type.
*/
def couldInstantiateTypeVar(tp: Type)(using Context): Boolean = tp.dealias match
def couldInstantiateTypeVar(tp: Type, applied: Boolean = false)(using Context): Boolean = tp.dealias match
case tvar: TypeVar
if !tvar.isInstantiated
&& ctx.typerState.constraint.contains(tvar)
Expand All @@ -120,14 +124,14 @@ object Inferencing {
case _ => Nil
case _ => Nil
case _ => Nil
couldInstantiateTypeVar(tycon)
|| argsInResult.exists(couldInstantiateTypeVar)
couldInstantiateTypeVar(tycon, applied)
|| (if applied then args else argsInResult).exists(couldInstantiateTypeVar(_, applied))
case RefinedType(parent, _, _) =>
couldInstantiateTypeVar(parent)
couldInstantiateTypeVar(parent, applied)
case tp: AndOrType =>
couldInstantiateTypeVar(tp.tp1) || couldInstantiateTypeVar(tp.tp2)
couldInstantiateTypeVar(tp.tp1, applied) || couldInstantiateTypeVar(tp.tp2, applied)
case AnnotatedType(tp, _) =>
couldInstantiateTypeVar(tp)
couldInstantiateTypeVar(tp, applied)
case _ =>
false

Expand Down
35 changes: 25 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Expand Up @@ -81,6 +81,12 @@ object Typer {
*/
private val InsertedApply = new Property.Key[Unit]

/** An attachment on a result of an implicit conversion or extension method
* that was added by tryInsertImplicitOnQualifier. Needed to prevent infinite
* expansions in error cases (e.g. in fuzzy/i9293.scala).
*/
private val InsertedImplicitOnQualifier = new Property.Key[Unit]

/** An attachment on a tree `t` occurring as part of a `t()` where
* the `()` was dropped by the Typer.
*/
Expand Down Expand Up @@ -3141,21 +3147,30 @@ class Typer extends Namer
}

/** If this tree is a select node `qual.name` (possibly applied to type variables)
* that does not conform to `pt`, try to insert an implicit conversion `c` around
* `qual` so that `c(qual).name` conforms to `pt`.
* that does not conform to `pt`, try two mitigations:
* 1. Instantiate any TypeVars in the widened type of `tree` with their lower bounds.
* 2. Try to insert an implicit conversion `c` around `qual` so that
* `c(qual).name` conforms to `pt`.
*/
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type, locked: TypeVars)(using Context): Option[Tree] = trace(i"try insert impl on qualifier $tree $pt") {
tree match
case tree @ Select(qual, name) if name != nme.CONSTRUCTOR =>
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
if selProto.isMatchedBy(qual.tpe) then None
if couldInstantiateTypeVar(qual.tpe.widen, applied = true)
then
Some(adapt(tree, pt, locked))
else
tryEither {
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, inSelect = false)
if tree1.isEmpty then None
else Some(adapt(tree1, pt, locked))
} { (_, _) => None
}
val selProto = SelectionProto(name, pt, NoViewsAllowed, privateOK = false)
if selProto.isMatchedBy(qual.tpe) || tree.hasAttachment(InsertedImplicitOnQualifier) then
None
else
tryEither {
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, inSelect = false)
if tree1.isEmpty then None
else
tree1.putAttachment(InsertedImplicitOnQualifier, ())
Some(adapt(tree1, pt, locked))
} { (_, _) => None
}
case TypeApply(fn, args) if args.forall(_.isInstanceOf[untpd.InferredTypeTree]) =>
tryInsertImplicitOnQualifier(fn, pt, locked)
case _ => None
Expand Down
4 changes: 4 additions & 0 deletions compiler/test/dotc/pos-test-pickling.blacklist
Expand Up @@ -68,6 +68,10 @@ i8182.scala
# local lifted value in annotation argument has different position after pickling
i2797a

# Late instantiation of type variable in tryInsertImplicitOnQualifier
# allows to simplify a type that was already computed
i13842.scala

# GADT cast applied to singleton type difference
i4176-gadt.scala

Expand Down
14 changes: 14 additions & 0 deletions tests/pos/i13842.scala
@@ -0,0 +1,14 @@
class Parent { class E }

object ChildA extends Parent

object ChildB extends Parent

class Printer[C <: Parent](val child: C):
def print22(e: child.E): String = ""

def test =
Printer(ChildA).print22(new ChildA.E) // does not work

//Printer[ChildA.type](ChildA).print22(new ChildA.E) // works
//val p = Printer(ChildA); p.print22(new ChildA.E) // works

0 comments on commit 6e3e34d

Please sign in to comment.