Skip to content
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

Try to instantiate type variables in tryInsertImplicitOnQualifier #13884

Merged
merged 1 commit into from Nov 15, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -3130,21 +3136,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 @@ -67,6 +67,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