From 59943ebf3711b2c91911d2bbef6e2c19aba270b6 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 10 Sep 2025 15:07:43 -0700 Subject: [PATCH] Derived members succeed other givens in cycle test Sibling givens are always eligible for derived members, which are appended to the enclosing template. --- .../dotty/tools/dotc/typer/Implicits.scala | 5 ++- tests/pos/i23897.scala | 14 +++++++++ tests/run/i23897.scala | 31 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i23897.scala create mode 100644 tests/run/i23897.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 4a25e3f38dec..f0569d850cb1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1718,7 +1718,10 @@ trait Implicits: def candSucceedsGiven(sym: Symbol): Boolean = val owner = sym.owner if owner == candSym.owner then - sym.is(GivenVal) && sym.span.exists && sym.span.start <= candSym.span.start + sym.is(GivenVal) + && sym.span.exists && sym.span.start <= candSym.span.start + && (sym.span.isSourceDerived || !sym.name.startsWith("derived$", 0)) + // logically a synthetic injected at the end of the body else if owner.isClass then false else candSucceedsGiven(owner) diff --git a/tests/pos/i23897.scala b/tests/pos/i23897.scala new file mode 100644 index 000000000000..0b42867dc3f3 --- /dev/null +++ b/tests/pos/i23897.scala @@ -0,0 +1,14 @@ + +import scala.deriving.Mirror + +class Test[A] +object Test: + def derived[A](using m: Mirror.Of[A], t: Test[Int]): Test[A] = new Test[A] + +case class Foo(i: Int) derives Test +object Foo: + given i: Test[Int] = new Test[Int] + +case class Bar(i: Int) derives Test +object Bar: + given Test[Int]() diff --git a/tests/run/i23897.scala b/tests/run/i23897.scala new file mode 100644 index 000000000000..343adee1cf73 --- /dev/null +++ b/tests/run/i23897.scala @@ -0,0 +1,31 @@ + +import scala.deriving.Mirror + +trait Semigroup[A] { + def combine(x: A, y: A): A +} +object Semigroup { + given semigroupInt: Semigroup[Int] = _ + _ + + given emptyTuple: Semigroup[EmptyTuple] = (x, _) => x + + given tupleN[H, T <: Tuple](using h: Semigroup[H], t: Semigroup[T]): Semigroup[H *: T] = + (x, y) => h.combine(x.head, y.head) *: t.combine(x.tail, y.tail) + + def derived[A <: Product](using m: Mirror.ProductOf[A], s: Semigroup[m.MirroredElemTypes]): Semigroup[A] = + (x, y) => m.fromTuple(s.combine(Tuple.fromProductTyped(x), Tuple.fromProductTyped(y))) +} + +case class Foo(i: Int) derives Semigroup +object Foo { + given overrideSemigroupInt: Semigroup[Int] = _ * _ +} + +@main def Test = + assert: + summon[Semigroup[Foo]].combine(Foo(2), Foo(3)) == Foo(6) + +// Scala 3.3 and 3.4 +//summon[Semigroup[Foo]].combine(Foo(2), Foo(3)) // => Foo(6) +// Scala 3.5+ +//summon[Semigroup[Foo]].combine(Foo(2), Foo(3)) // => Foo(5)