From af69895358d57c7273218424e57efa7e211d566d Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 5 Mar 2024 00:49:37 +0100 Subject: [PATCH] Restore pre-3.3.2 behavior of `inline implicit def` `inline implicit def` is not really a supported feature since it combines Scala 3's `inline` with Scala 2's `implicit` where the latter should eventually be deprecated. This however didn't prevent at least one project from using this combination in a way that was broken by #18249, see #19862 for the details. The issue is that when definining: implicit def foo(x: A): B = ... Then `foo` is a valid implicit search candidate when looking up an implicit `Function1[A, B]`. However, before #18249 if instead we wrote: inline implicit def foo(x: A): B = ... Then `foo` would be considered as an implicit search candidate but discarded because eta-expansion was disabled. There is no particular reason for `inline implicit def` to behave differently from `implicit def` here, but since `implicit def` is a legacy feature and since Scala 3.3 is an LTS release, we choose to restore the pre-#18249 behavior for compatibility reasons. Fixes #19862. --- compiler/src/dotty/tools/dotc/core/Flags.scala | 1 + compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 ++ tests/pos/i19862.scala | 8 ++++++++ 3 files changed, 11 insertions(+) create mode 100644 tests/pos/i19862.scala diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index be43cbc8dfcf..249940d8ff99 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -581,6 +581,7 @@ object Flags { val LazyGiven: FlagSet = Given | Lazy val InlineOrProxy: FlagSet = Inline | InlineProxy // An inline method or inline argument proxy */ val InlineMethod: FlagSet = Inline | Method + val InlineImplicitMethod: FlagSet = Implicit | InlineMethod val InlineParam: FlagSet = Inline | Param val InlineByNameProxy: FlagSet = InlineProxy | Method val JavaEnum: FlagSet = JavaDefined | Enum // A Java enum trait diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b1bb5805bdc4..47bd25e5d45e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3993,10 +3993,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Reasons NOT to eta expand: // - we reference a constructor + // - we reference an inline implicit def (see #19862) // - we are in a pattern // - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that) if arity >= 0 && !tree.symbol.isConstructor + && !tree.symbol.isAllOf(InlineImplicitMethod) && !ctx.mode.is(Mode.Pattern) && !(isSyntheticApply(tree) && !functionExpected) then diff --git a/tests/pos/i19862.scala b/tests/pos/i19862.scala new file mode 100644 index 000000000000..8467f9a65280 --- /dev/null +++ b/tests/pos/i19862.scala @@ -0,0 +1,8 @@ +import scala.language.implicitConversions + +object Test: + implicit inline def uhOh[A](value: A): A = + compiletime.error("Should not have been called") + def test = + // Compiles because `uhOh` fails to eta-expand and we fallback to `Predef.$conforms[A, A]` + summon[Function1[Int, Int]]