Skip to content

Fix #1857: Interpolate type variables before implicit search #1859

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

Closed
wants to merge 3 commits into from
Closed
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
13 changes: 11 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ object Inferencing {
if (constraint.uninstVars exists qualifies) interpolate()
}

/** Instantiate undetermined type variables to that type `tp` is
/** Instantiate undetermined type variables so that type `tp` is
* maximized and return None. If this is not possible, because a non-variant
* typevar is not uniquely determined, return that typevar in a Some.
*/
Expand All @@ -302,7 +302,7 @@ object Inferencing {
* +1 means: only covariant occurrences
* 0 means: mixed or non-variant occurrences
*
* Note: We intentionally use a relaxed version of variance here,
* Note 1: We intentionally use a relaxed version of variance here,
* where the variance does not change under a prefix of a named type
* (the strict version makes prefixes invariant). This turns out to be
* better for type inference. In a nutshell, if a type variable occurs
Expand All @@ -311,6 +311,10 @@ object Inferencing {
* (U? >: x.type) # T
*
* we want to instantiate U to x.type right away. No need to wait further.
*
* Note 2: Parameters of implicit method types are assumed to be non-variant here.
* This is necessary to prevent them from getting interpolated before an implicit
* parameter search.
*/
private def variances(tp: Type, include: TypeVar => Boolean)(implicit ctx: Context): VarianceMap = Stats.track("variances") {
val constraint = ctx.typerState.constraint
Expand All @@ -324,6 +328,11 @@ object Inferencing {
if (v == null) vmap.updated(t, variance)
else if (v == variance || v == 0) vmap
else vmap.updated(t, 0)
case t: ImplicitMethodType =>
val saved = variance
variance = 0
val vmap1 = try foldOver(vmap, t.paramTypes) finally variance = saved
apply(vmap1, t.resultType)
case _ =>
foldOver(vmap, t)
}
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1697,8 +1697,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit

def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ {
/*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {
def shouldInterpolate(tp: Type) = tp match {
case tp: PolyType => false
case tp: MethodType => tp.isImplicit
case _ => true
}
if (tree.isDef) interpolateUndetVars(tree, tree.symbol)
else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol)
else if (shouldInterpolate(tree.tpe.widen)) interpolateUndetVars(tree, NoSymbol)
tree.overwriteType(tree.tpe.simplified)
adaptInterpolated(tree, pt, original)
}
Expand Down
34 changes: 34 additions & 0 deletions tests/pos/i1857.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
trait CommandeerDSL[Host] {
trait Operation[T]
type Op[T] <: Operation[T]
}

object CommandeerDSL {
def apply[Host, DSL <: CommandeerDSL[Host]](host: Host)(implicit dsl: DSL): DSL = dsl
}

trait Foo {
def bar(a: String, b: Int): Double
}

object Foo {
implicit val fooDSL: FooDSL = new FooDSL {}
}

trait FooDSL extends CommandeerDSL[Foo] {
sealed trait FooOperation[T] extends Operation[T]
type Op[T] = FooOperation[T]

case class Bar(a: String, b: Int) extends FooOperation[Double]
}

object RunMe {
def main(args: Array[String]): Unit = {
println("Hi Mum")

val kevin = CommandeerDSL(null.asInstanceOf[Foo])
println(s"Found DSL for Foo: $kevin")
val bar = kevin.Bar("bob", 3)
println(s"Made a bar: $bar")
}
}
10 changes: 10 additions & 0 deletions tests/pos/i1857a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
object Test {
def foo1[T](x: T)(implicit ev: T): Nothing = ???

def test1: Unit = {
implicit val ii: Int = 42
implicit val ss: String = "foo"

foo1(10) // ambiguous implicit because T=Any
}
}