From 14ffb35d05f36e6b4f794b2e8271e596b6a85b75 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 5 Jun 2021 12:55:25 +0200 Subject: [PATCH] Insert conversions also on selections wrapped in type applications In i12708.scala, the problematic function was a selection `qual.m[tvs]` that was already applied to type variables. In that case we need to backtrack, forget the type variables and try to insert a conversion or extension method on `qual`. Fixes #12708 --- .../src/dotty/tools/dotc/typer/Typer.scala | 11 +++--- tests/pos/i12708.scala | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i12708.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bce9aff02be1..885dc86a7fd4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3045,12 +3045,12 @@ class Typer extends Namer } } - /** If this tree is a select node `qual.name` that does not conform to `pt`, - * try to insert an implicit conversion `c` around `qual` so that - * `c(qual).name` conforms to `pt`. + /** 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`. */ def tryInsertImplicitOnQualifier(tree: Tree, pt: Type, locked: TypeVars)(using Context): Option[Tree] = trace(i"try insert impl on qualifier $tree $pt") { - tree match { + 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 @@ -3061,8 +3061,9 @@ class Typer extends Namer else Some(adapt(tree1, pt, locked)) } { (_, _) => None } + case TypeApply(fn, args) if args.forall(_.isInstanceOf[TypeVarBinder[_]]) => + tryInsertImplicitOnQualifier(fn, pt, locked) case _ => None - } } /** Given a selection `qual.name`, try to convert to an extension method diff --git a/tests/pos/i12708.scala b/tests/pos/i12708.scala new file mode 100644 index 000000000000..f8149f0732d0 --- /dev/null +++ b/tests/pos/i12708.scala @@ -0,0 +1,37 @@ +import language.implicitConversions + +trait AdditiveSemigroup[A] + +final class AdditiveSemigroupOps[A](lhs: A)(implicit as: AdditiveSemigroup[A]) { + def +(rhs: A): A = ??? + def ^(rhs: A): A = ??? +} + +trait AdditiveSemigroupSyntax { + implicit def additiveSemigroupOps[A: AdditiveSemigroup](a: A): AdditiveSemigroupOps[A] = + new AdditiveSemigroupOps(a) +} + +object syntax { + object additiveSemigroup extends AdditiveSemigroupSyntax +} + +object App { + + def main(args: Array[String]): Unit = { + import syntax.additiveSemigroup._ + + implicit def IntAlgebra[A]: AdditiveSemigroup[Map[Int, A]] = ??? + + def res[A]: Map[Int, A] = { + val a: Map[Int, A] = Map.empty + val b: Map[Int, A] = Map.empty + // Calls the operator on AdditiveSemigroupOps + a ^ b + // Calls the operator + on AdditiveSemigroupOps only in Scala 2 + // In Scala 3 tries to call `+` on Map + a + b + } + } + +} \ No newline at end of file