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..255e5c897681 --- /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.ClassToExpr[T] + | + | But given instance ClassToExpr 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