From 85f061755a39dba41e88ccce1ccbe2bade0e0eb6 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 2 Apr 2022 11:04:33 +0200 Subject: [PATCH 1/2] Handle situation where an inline info cannot be computed due to a cyclic reference involving a macro. Fixes #14772 --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 10 +++++++++- compiler/src/dotty/tools/dotc/typer/Namer.scala | 8 ++++++++ tests/neg/i14772.check | 15 +++++++++++++++ tests/neg/i14772.scala | 10 ++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i14772.check create mode 100644 tests/neg/i14772.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index fd8e86897f49..67bc68c4a495 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -40,6 +40,11 @@ object Inliner { private type DefBuffer = mutable.ListBuffer[ValOrDefDef] + /** An exception signalling that an inline info cannot be computed due to a + * cyclic reference. i14772.scala shows a case where this happens. + */ + private[typer] class MissingInlineInfo extends Exception + /** `sym` is an inline method with a known body to inline. */ def hasBodyToInline(sym: SymDenotation)(using Context): Boolean = @@ -154,7 +159,10 @@ object Inliner { if bindings.nonEmpty then cpy.Block(tree)(bindings.toList, inlineCall(tree1)) else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then - val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors + val body = + try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors + catch case _: MissingInlineInfo => + throw CyclicReference(ctx.owner) new Inliner(tree, body).inlined(tree.srcPos) else ctx.base.stopInlining = true diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 67f69b69fda8..232018bc7cd0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -103,6 +103,10 @@ class Namer { typer: Typer => } } + def hasDefinedSymbol(tree: Tree)(using Context): Boolean = + val xtree = expanded(tree) + xtree.hasAttachment(TypedAhead) || xtree.hasAttachment(SymOfTree) + /** The enclosing class with given name; error if none exists */ def enclosingClassNamed(name: TypeName, span: Span)(using Context): Symbol = if (name.isEmpty) NoSymbol @@ -837,6 +841,10 @@ class Namer { typer: Typer => private def addInlineInfo(sym: Symbol) = original match { case original: untpd.DefDef if sym.isInlineMethod => def rhsToInline(using Context): tpd.Tree = + if !original.symbol.exists && !hasDefinedSymbol(original) then + throw + if sym.isCompleted then Inliner.MissingInlineInfo() + else CyclicReference(sym) val mdef = typedAheadExpr(original).asInstanceOf[tpd.DefDef] PrepareInlineable.wrapRHS(original, mdef.tpt, mdef.rhs) PrepareInlineable.registerInlineInfo(sym, rhsToInline)(using localContext(sym)) diff --git a/tests/neg/i14772.check b/tests/neg/i14772.check new file mode 100644 index 000000000000..f9ad78db8614 --- /dev/null +++ b/tests/neg/i14772.check @@ -0,0 +1,15 @@ +-- [E044] Cyclic Error: tests/neg/i14772.scala:7:7 --------------------------------------------------------------------- +7 | foo(a) // error + | ^ + | Overloaded or recursive method impl needs return type + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/i14772.scala:8:12 ---------------------------------------------------------------------------------- +8 | Expr(()) // error + | ^ + | no given instance of type quoted.ToExpr[Unit] was found for parameter x$2 of method apply in object Expr. + | I found: + | + | quoted.ToExpr.IntToExpr[T] + | + | But given instance IntToExpr in object ToExpr does not match type quoted.ToExpr[Unit]. diff --git a/tests/neg/i14772.scala b/tests/neg/i14772.scala new file mode 100644 index 000000000000..29cafb8e6a48 --- /dev/null +++ b/tests/neg/i14772.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +object A { + transparent inline def foo(a: Any): Any = ${ impl('a) } + + def impl(a: Expr[Any])(using Quotes)/*: Expr[Any]*/ = { + foo(a) // error + Expr(()) // error + } +} \ No newline at end of file From aa2d3c5961ff1581ff2be5d1bd7e7bca7b6716e3 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 2 Apr 2022 16:45:23 +0200 Subject: [PATCH 2/2] Fix check file --- tests/neg/i14772.check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neg/i14772.check b/tests/neg/i14772.check index f9ad78db8614..255e5c897681 100644 --- a/tests/neg/i14772.check +++ b/tests/neg/i14772.check @@ -10,6 +10,6 @@ | no given instance of type quoted.ToExpr[Unit] was found for parameter x$2 of method apply in object Expr. | I found: | - | quoted.ToExpr.IntToExpr[T] + | quoted.ToExpr.ClassToExpr[T] | - | But given instance IntToExpr in object ToExpr does not match type quoted.ToExpr[Unit]. + | But given instance ClassToExpr in object ToExpr does not match type quoted.ToExpr[Unit].