From e4ef18db8f7ee393a871a12c782e25a3224833d0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 12 Feb 2025 10:47:47 -0800 Subject: [PATCH] Fail if deferred given impl is recursive --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 13 +++++++++++-- tests/neg/i22589.check | 7 +++++++ tests/neg/i22589.scala | 17 +++++++++++++++++ tests/neg/i22589b.check | 8 ++++++++ tests/neg/i22589b.scala | 16 ++++++++++++++++ tests/neg/i22589c.check | 7 +++++++ tests/neg/i22589c.scala | 10 ++++++++++ tests/pos/i22589.scala | 17 +++++++++++++++++ 8 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/neg/i22589.check create mode 100644 tests/neg/i22589.scala create mode 100644 tests/neg/i22589b.check create mode 100644 tests/neg/i22589b.scala create mode 100644 tests/neg/i22589c.check create mode 100644 tests/neg/i22589c.scala create mode 100644 tests/pos/i22589.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fc71c0e43034..04d89c50b09a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3277,7 +3277,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val superCls = cls.superClass superCls.exists && superCls.asClass.baseClasses.contains(m.symbol.owner) - def givenImpl(mbr: TermRef): ValDef = + def givenImpl(mbr: TermRef): ValDef | EmptyTree.type = val dcl = mbr.symbol val target = dcl.info.asSeenFrom(cls.thisType, dcl.owner) val constr = cls.primaryConstructor @@ -3315,7 +3315,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cpy.Select(id)(This(cls), id.name) case _ => super.transform(tree) - ValDef(impl, anchorParams.transform(rhs)).withSpan(impl.span.endPos) + + rhs.tpe match + case tp: NamedType if tp.prefix.typeSymbol == cls && tp.name == mbr.name && !tp.typeSymbol.is(Method) => + report.error( + em"""Inferred implementation of the deferred ${dcl.showLocated} is self-recursive. + |An implementing given needs to be written explicitly.""", + cdef.srcPos) + EmptyTree + case _ => + ValDef(impl, anchorParams.transform(rhs)).withSpan(impl.span.endPos) end givenImpl val givenImpls = diff --git a/tests/neg/i22589.check b/tests/neg/i22589.check new file mode 100644 index 000000000000..2f7571e8aa76 --- /dev/null +++ b/tests/neg/i22589.check @@ -0,0 +1,7 @@ +-- Error: tests/neg/i22589.scala:15:7 ---------------------------------------------------------------------------------- +15 |object Person extends CompanionEssentials[Person]: // error + |^ + |Inferred implementation of the deferred given instance given_MyCodec_E in trait CompanionEssentials is self-recursive. + |An implementing given needs to be written explicitly. +16 | //override final lazy given given_MyCodec_E: MyCodec[Person] = Person.given_MyCodec_E +17 | override def toString = "" diff --git a/tests/neg/i22589.scala b/tests/neg/i22589.scala new file mode 100644 index 000000000000..df989dbf1800 --- /dev/null +++ b/tests/neg/i22589.scala @@ -0,0 +1,17 @@ + +//> using options -Wsafe-init -Ysafe-init-global + +import scala.compiletime.deferred + +trait MyCodec[E] + +object auto: + trait CompanionEssentials[E]: + given MyCodec[E] = deferred + +import auto.CompanionEssentials + +case class Person(name: String, age: Int) +object Person extends CompanionEssentials[Person]: // error + //override final lazy given given_MyCodec_E: MyCodec[Person] = Person.given_MyCodec_E + override def toString = "" diff --git a/tests/neg/i22589b.check b/tests/neg/i22589b.check new file mode 100644 index 000000000000..bb2e25174e6a --- /dev/null +++ b/tests/neg/i22589b.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/i22589b.scala:13:7 --------------------------------------------------------------------------------- +13 |object Person extends CompanionEssentials[Person]: // error + |^ + |Inferred implementation of the deferred given instance myc in trait CompanionEssentials is self-recursive. + |An implementing given needs to be written explicitly. +14 | given String = "hw" +15 | given myc(using String): MyCodec[Person] = new MyCodec[Person] {} +16 | override def toString = "" diff --git a/tests/neg/i22589b.scala b/tests/neg/i22589b.scala new file mode 100644 index 000000000000..da428d0e6470 --- /dev/null +++ b/tests/neg/i22589b.scala @@ -0,0 +1,16 @@ + +import scala.compiletime.deferred + +trait MyCodec[E] + +object auto: + trait CompanionEssentials[E]: + given myc: MyCodec[E] = deferred + +import auto.CompanionEssentials + +case class Person(name: String, age: Int) +object Person extends CompanionEssentials[Person]: // error + given String = "hw" + given myc(using String): MyCodec[Person] = new MyCodec[Person] {} + override def toString = "" diff --git a/tests/neg/i22589c.check b/tests/neg/i22589c.check new file mode 100644 index 000000000000..d02e69bc6dff --- /dev/null +++ b/tests/neg/i22589c.check @@ -0,0 +1,7 @@ +-- Error: tests/neg/i22589c.scala:8:7 ---------------------------------------------------------------------------------- + 8 |object A extends Base[A.P] { // error + |^ + |Inferred implementation of the deferred given instance given_TC_T in trait Base is self-recursive. + |An implementing given needs to be written explicitly. + 9 | case class P() +10 |} diff --git a/tests/neg/i22589c.scala b/tests/neg/i22589c.scala new file mode 100644 index 000000000000..723fb239b89f --- /dev/null +++ b/tests/neg/i22589c.scala @@ -0,0 +1,10 @@ + +trait TC[T] + +trait Base[T] { + given TC[T] = scala.compiletime.deferred +} + +object A extends Base[A.P] { // error + case class P() +} diff --git a/tests/pos/i22589.scala b/tests/pos/i22589.scala new file mode 100644 index 000000000000..cd5a7f179180 --- /dev/null +++ b/tests/pos/i22589.scala @@ -0,0 +1,17 @@ + +import scala.compiletime.deferred + +trait MyCodec[E] + +object auto: + trait CompanionEssentials[E]: + //given [E] => MyCodec[E] = deferred + given MyCodec[E] = deferred + +import auto.CompanionEssentials + +case class Person(name: String, age: Int) +object Person extends CompanionEssentials[Person]: + //given something: [E] => MyCodec[E] = new MyCodec[E] {} + given something: MyCodec[Person] = new MyCodec[Person] {} + override def toString = ""