diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5dfbf153903f..59b151e604e2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2348,7 +2348,7 @@ class Typer extends Namer def dummyArg(tp: Type) = untpd.Ident(nme.???).withTypeUnchecked(tp) def addImplicitArgs(implicit ctx: Context) = { - def implicitArgs(formals: List[Type]): List[Tree] = formals match { + def implicitArgs(formals: List[Type], argIndex: Int): List[Tree] = formals match { case Nil => Nil case formal :: formals1 => val arg = inferImplicitArg(formal, tree.pos.endPos) @@ -2361,10 +2361,17 @@ class Typer extends Namer // If there are none, we have to propagate the ambiguity to the caller. arg :: formals1.map(dummyArg) case _ => - arg :: implicitArgs(formals1) + // If the implicit parameter list is dependent we must propagate inferred + // types through the remainder of the parameter list similarly to how it's + // done for non-implicit parameter lists in Applications#matchArgs#addTyped. + val formals2 = + if (wtp.isParamDependent && arg.tpe.exists) + formals1.mapconserve(f1 => safeSubstParam(f1, wtp.paramRefs(argIndex), arg.tpe)) + else formals1 + arg :: implicitArgs(formals2, argIndex + 1) } } - val args = implicitArgs(wtp.paramInfos) + val args = implicitArgs(wtp.paramInfos, 0) def propagatedFailure(args: List[Tree]): Type = args match { case arg :: args1 => diff --git a/tests/neg/i5427.scala b/tests/neg/i5427.scala new file mode 100644 index 000000000000..807b6898fde9 --- /dev/null +++ b/tests/neg/i5427.scala @@ -0,0 +1,26 @@ +trait Foo[In] { type Out } + +object Test { + def fooInt: Foo[Int] { type Out = String } = ??? + implicit def str: String = ??? + + def test1[A](f1: Foo[A])(implicit f2: f1.Out) = ??? + def test2[A](implicit f1: Foo[A], f2: f1.Out) = ??? + + test1(fooInt) // OK + test2 // error +} + +object Test2 { + implicit def fooInt: Foo[Int] { type Out = String } = ??? + implicit def fooString: Foo[String] { type Out = Boolean } = ??? + implicit def fooBoolean: Foo[Boolean] { type Out = Double } = ??? + + def test3[A](f1: Foo[A], f2: Foo[f1.Out])(implicit f3: Foo[f2.Out]): f3.Out = ??? + def test4[A](implicit f1: Foo[A], f2: Foo[f1.Out], f3: Foo[f2.Out]): f3.Out = ??? + + val t3 = test3(fooInt, fooString) + t3: Double + val t4 = test4 // error + t4: Double +} diff --git a/tests/pos/i5427.scala b/tests/pos/i5427.scala new file mode 100644 index 000000000000..b73f1eee9d3a --- /dev/null +++ b/tests/pos/i5427.scala @@ -0,0 +1,46 @@ +trait Foo[In] { type Out } + +object Test { + implicit def fooInt: Foo[Int] { type Out = String } = ??? + implicit def str: String = ??? + + def test1[A](f1: Foo[A])(implicit f2: f1.Out) = ??? + def test2[A](implicit f1: Foo[A], f2: f1.Out) = ??? + + test1(fooInt) // OK + test2 // OK +} + +object Test2 { + implicit def fooInt: Foo[Int] { type Out = String } = ??? + implicit def fooString: Foo[String] { type Out = Boolean } = ??? + implicit def fooBoolean: Foo[Boolean] { type Out = Double } = ??? + + def test3[A](f1: Foo[A], f2: Foo[f1.Out])(implicit f3: Foo[f2.Out]): f3.Out = ??? + def test4[A](implicit f1: Foo[A], f2: Foo[f1.Out], f3: Foo[f2.Out]): f3.Out = ??? + + val t3 = test3(fooInt, fooString) + t3: Double + val t4 = test4[Int] + t4: Double +} + +object Test3 { + def fooInt: Foo[Int] { type Out = String } = ??? + implicit def str: String = ??? + + def test5[A](implicit f1: Foo[A] = fooInt, f2: f1.Out) = f2 + + val t5 = test5 + t5: String +} + +object Test4 { + implicit def fooInt: Foo[Int] { type Out = String } = ??? + def str: String = ??? + + def test6[A](implicit f1: Foo[A], f2: f1.Out = str) = f2 + + val t6 = test6 + t6: String +}