From c69985cf440d4fcea539dd4329ee821c0dea91f3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 28 Jul 2019 15:46:35 +0200 Subject: [PATCH 1/2] Fix #6914: Improve type inference for implicit Conversions Make use of the knowlegde that a given instance used for a conversion must be an instance of `scala.Conversion`. --- .../dotty/tools/dotc/typer/Implicits.scala | 59 +++++++++++-------- tests/pos/i6914.scala | 19 ++++++ 2 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 tests/pos/i6914.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 155d7fe237f1..f69498cc97a4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1359,31 +1359,40 @@ trait Implicits { self: Typer => //println(i"search implicits $pt / ${eligible.map(_.ref)}") /** Try to typecheck an implicit reference */ - def typedImplicit(cand: Candidate, contextual: Boolean)(implicit ctx: Context): SearchResult = - trace(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { - record("typedImplicit") - val ref = cand.ref - val generated: Tree = tpd.ref(ref).withSpan(span.startPos) - val locked = ctx.typerState.ownedVars - val adapted = - if (argument.isEmpty) - adapt(generated, pt.widenExpr, locked) - else { - val untpdGenerated = untpd.TypedSplice(generated) - def tryConversion(implicit ctx: Context) = - typed( - untpd.Apply(untpdGenerated, untpd.TypedSplice(argument) :: Nil), - pt, locked) - if (cand.isExtension) { - val SelectionProto(name: TermName, mbrType, _, _) = pt - val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType) - if (!ctx.reporter.hasErrors && cand.isConversion) { - val testCtx = ctx.fresh.setExploreTyperState() - tryConversion(testCtx) - if (testCtx.reporter.hasErrors) - ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") - } - result + def typedImplicit(cand: Candidate, contextual: Boolean)(implicit ctx: Context): SearchResult = trace(i"typed implicit ${cand.ref}, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) { + record("typedImplicit") + val ref = cand.ref + val generated: Tree = tpd.ref(ref).withSpan(span.startPos) + val locked = ctx.typerState.ownedVars + val adapted = + if (argument.isEmpty) + adapt(generated, pt.widenExpr, locked) + else { + def untpdGenerated = untpd.TypedSplice(generated) + def tryConversion(implicit ctx: Context) = { + val untpdConv = + if (ref.symbol.is(Given)) + untpd.Select( + untpd.TypedSplice( + adapt(generated, + defn.ConversionClass.typeRef.appliedTo(argument.tpe.widen, pt), + locked)), + nme.apply) + else untpdGenerated + typed( + untpd.Apply(untpdConv, untpd.TypedSplice(argument) :: Nil), + pt, locked) + } + if (cand.isExtension) { + val SelectionProto(name: TermName, mbrType, _, _) = pt + val result = extMethodApply(untpd.Select(untpdGenerated, name), argument, mbrType) + if (!ctx.reporter.hasErrors && cand.isConversion) { + val testCtx = ctx.fresh.setExploreTyperState() + tryConversion(testCtx) + if (testCtx.reporter.hasErrors) + ctx.error(em"ambiguous implicit: $generated is eligible both as an implicit conversion and as an extension method container") + } + result } else tryConversion } diff --git a/tests/pos/i6914.scala b/tests/pos/i6914.scala new file mode 100644 index 000000000000..98fd1bb68d7c --- /dev/null +++ b/tests/pos/i6914.scala @@ -0,0 +1,19 @@ +trait Expr[T] +trait Liftable[T] + +/*given autoToExpr[T] as Conversion[T, Expr[T]] given Liftable[T] { + def apply(x: T): Expr[T] = ??? +} +*/ +class ToExpr[T] given Liftable[T] extends Conversion[T, Expr[T]] { + def apply(x: T): Expr[T] = ??? +} +given toExpr[T] as ToExpr[T] given Liftable[T] + +given as Liftable[Int] = ??? +given as Liftable[String] = ??? + +def x = the[ToExpr[String]] +def y = the[Conversion[String, Expr[String]]] + +def a: Expr[String] = "abc" From 4cb4f4c1614e2f3cdf221b12cd1c7993edbe66ba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 31 Jul 2019 10:19:31 +0200 Subject: [PATCH 2/2] Add test --- tests/pos/i6914.scala | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/pos/i6914.scala b/tests/pos/i6914.scala index 98fd1bb68d7c..0278721a1dcf 100644 --- a/tests/pos/i6914.scala +++ b/tests/pos/i6914.scala @@ -1,19 +1,29 @@ trait Expr[T] trait Liftable[T] -/*given autoToExpr[T] as Conversion[T, Expr[T]] given Liftable[T] { - def apply(x: T): Expr[T] = ??? -} -*/ -class ToExpr[T] given Liftable[T] extends Conversion[T, Expr[T]] { - def apply(x: T): Expr[T] = ??? +object test1 { + class ToExpr[T] given Liftable[T] extends Conversion[T, Expr[T]] { + def apply(x: T): Expr[T] = ??? + } + given toExpr[T] as ToExpr[T] given Liftable[T] + + given as Liftable[Int] = ??? + given as Liftable[String] = ??? + + def x = the[ToExpr[String]] + def y = the[Conversion[String, Expr[String]]] + + def a: Expr[String] = "abc" } -given toExpr[T] as ToExpr[T] given Liftable[T] -given as Liftable[Int] = ??? -given as Liftable[String] = ??? +object test2 { + + given autoToExpr[T] as Conversion[T, Expr[T]] given Liftable[T] { + def apply(x: T): Expr[T] = ??? + } -def x = the[ToExpr[String]] -def y = the[Conversion[String, Expr[String]]] + given as Liftable[Int] = ??? + given as Liftable[String] = ??? -def a: Expr[String] = "abc" + def a: Expr[String] = "abc" +} \ No newline at end of file