From cc0041b7c22166cd6acc6a91abfdd76ebe99c20e Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 3 Feb 2024 16:41:30 +0100 Subject: [PATCH] Refine handling of StaleSymbol type errors Regular StaleSymbol can be caught and masked in some situations. Stale symbol type errors need to allow the same. Fixes #19604 [Cherry-picked fbffe7b3be0dada9cf35e2d5aa5779889ce5fb61] --- .../dotty/tools/dotc/core/Denotations.scala | 20 +++++++--- tests/pos/i19604/ZSet.scala | 11 ++++++ tests/pos/i19604/core.scala | 37 +++++++++++++++++++ tests/pos/i19604/macro.scala | 8 ++++ tests/pos/i19604/prelude.scala | 8 ++++ 5 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i19604/ZSet.scala create mode 100644 tests/pos/i19604/core.scala create mode 100644 tests/pos/i19604/macro.scala create mode 100644 tests/pos/i19604/prelude.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f568117c72a0..6170bc97d3df 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -22,6 +22,7 @@ import config.Config import config.Printers.overload import util.common.* import typer.ProtoTypes.NoViewsAllowed +import reporting.Message import collection.mutable.ListBuffer /** Denotations represent the meaning of symbols and named types. @@ -945,10 +946,9 @@ object Denotations { } def staleSymbolError(using Context): Nothing = - if symbol.isPackageObject && ctx.run != null && ctx.run.nn.isCompilingSuspended then - throw TypeError(em"Cyclic macro dependency; macro refers to a toplevel symbol in ${symbol.source} from which the macro is called") - else - throw new StaleSymbol(staleSymbolMsg) + if symbol.isPackageObject && ctx.run != null && ctx.run.nn.isCompilingSuspended + then throw StaleSymbolTypeError(symbol) + else throw StaleSymbolException(staleSymbolMsg) def staleSymbolMsg(using Context): String = { def ownerMsg = this match { @@ -1356,9 +1356,19 @@ object Denotations { else NoSymbol + trait StaleSymbol extends Exception + /** An exception for accessing symbols that are no longer valid in current run */ - class StaleSymbol(msg: => String) extends Exception { + class StaleSymbolException(msg: => String) extends Exception, StaleSymbol { util.Stats.record("stale symbol") override def getMessage(): String = msg } + + /** An exception that is at the same type a StaleSymbol and a TypeError. + * Sine it is a TypeError it can be reported as a nroaml error instead of crashing + * the compiler. + */ + class StaleSymbolTypeError(symbol: Symbol)(using Context) extends TypeError, StaleSymbol: + def toMessage(using Context) = + em"Cyclic macro dependency; macro refers to a toplevel symbol in ${symbol.source} from which the macro is called" } diff --git a/tests/pos/i19604/ZSet.scala b/tests/pos/i19604/ZSet.scala new file mode 100644 index 000000000000..b27c6a939f6c --- /dev/null +++ b/tests/pos/i19604/ZSet.scala @@ -0,0 +1,11 @@ +// ZSet.scala +// moving it to core.scala would lead to Recursion limit exceeded: find-member prelude.ZSetSyntax +package prelude + +import prelude.newtypes._ + +class ZSet[+A, +B] +object ZSet +trait ZSetSyntax { + implicit final class ZSetMapOps[+A](self: Map[A, Natural]) +} \ No newline at end of file diff --git a/tests/pos/i19604/core.scala b/tests/pos/i19604/core.scala new file mode 100644 index 000000000000..26b56fb0f8f2 --- /dev/null +++ b/tests/pos/i19604/core.scala @@ -0,0 +1,37 @@ +// core.scala +package prelude + +sealed trait Assertion[-A] +object Assertion: + def greaterThanOrEqualTo[A](value: A): Assertion[A] = ??? + +sealed trait AssertionError + +abstract class NewtypeCustom[A] { + type Type + protected inline def validateInline(inline value: A): Unit + + inline def apply(inline a1: A): Type = { + validateInline(a1) + a1.asInstanceOf[Type] + } +} +abstract class Newtype[A] extends NewtypeCustom[A] { + def assertion: Assertion[A] = ??? + protected inline def validateInline(inline value: A): Unit = ${ + Macros.validateInlineImpl[A]('assertion, 'value) + } +} + +abstract class Subtype[A] extends Newtype[A] { + type Type <: A +} + + +package object newtypes { + type Natural = Natural.Type + object Natural extends Subtype[Int] { + override inline def assertion = Assertion.greaterThanOrEqualTo(0) + val one: Natural = Natural(1) // triggers macro + } +} \ No newline at end of file diff --git a/tests/pos/i19604/macro.scala b/tests/pos/i19604/macro.scala new file mode 100644 index 000000000000..fa1faba10512 --- /dev/null +++ b/tests/pos/i19604/macro.scala @@ -0,0 +1,8 @@ +// macro.scala +package prelude +import scala.quoted.* + +object Macros { + def validateInlineImpl[A: Type](assertionExpr: Expr[Assertion[A]], a: Expr[A])(using Quotes): Expr[Unit] = + '{ () } +} diff --git a/tests/pos/i19604/prelude.scala b/tests/pos/i19604/prelude.scala new file mode 100644 index 000000000000..3a8292d3d2ec --- /dev/null +++ b/tests/pos/i19604/prelude.scala @@ -0,0 +1,8 @@ +// prelude.scala + +import prelude.newtypes.Natural + +package object prelude extends ZSetSyntax { + type MultiSet[+A] = ZSet[A, Natural] + val MultiSet: ZSet.type = ZSet +} \ No newline at end of file