diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index b936afb73dc8..3d3cd1f41c84 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -119,7 +119,7 @@ object Splicer { sym.exists && !sym.is(Package) && sym.owner.ownersIterator.exists(x => x == expansionOwner || // symbol was generated within this macro expansion - x.is(Macro, butNot = Method) && x.name == nme.MACROkw // symbol was generated within another macro expansion + isMacroOwner(x) // symbol was generated within another macro expansion ) && !locals.contains(sym) // symbol is not in current scope }.traverse(tree) @@ -222,6 +222,14 @@ object Splicer { checkIfValidStaticCall(tree)(using Set.empty) } + /** Is this the dummy owner of a macro expansion */ + def isMacroOwner(sym: Symbol)(using Context): Boolean = + sym.is(Macro, butNot = Method) && sym.name == nme.MACROkw + + /** Is this the dummy owner of a macro expansion */ + def inMacroExpansion(using Context) = + ctx.owner.ownersIterator.exists(isMacroOwner) + /** Tree interpreter that evaluates the tree. * Interpreter is assumed to start at quotation level -1. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 20859b8c9d25..7d185d335218 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -31,6 +31,7 @@ import Feature.migrateTo3 import config.Printers.{implicits, implicitsDetailed} import collection.mutable import reporting._ +import transform.Splicer import annotation.tailrec import scala.annotation.internal.sharable @@ -567,6 +568,12 @@ object Implicits: def msg(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" + class MacroErrorsFailure(errors: List[Diagnostic.Error], + val expectedType: Type, + val argument: Tree) extends SearchFailureType { + def msg(using Context): Message = + em"${errors.map(_.msg).mkString("\n")}" + } end Implicits import Implicits._ @@ -1157,19 +1164,22 @@ trait Implicits: if ctx.reporter.hasErrors || !cand.ref.symbol.isAccessibleFrom(cand.ref.prefix) then - ctx.reporter.removeBufferedMessages - adapted.tpe match { + val res = adapted.tpe match { case _: SearchFailureType => SearchFailure(adapted) case error: PreviousErrorType if !adapted.symbol.isAccessibleFrom(cand.ref.prefix) => SearchFailure(adapted.withType(new NestedFailure(error.msg, pt))) - case _ => + case tpe => // Special case for `$conforms` and `<:<.refl`. Showing them to the users brings // no value, so we instead report a `NoMatchingImplicitsFailure` if (adapted.symbol == defn.Predef_conforms || adapted.symbol == defn.SubType_refl) NoMatchingImplicitsFailure + else if Splicer.inMacroExpansion && tpe <:< pt then + SearchFailure(adapted.withType(new MacroErrorsFailure(ctx.reporter.allErrors.reverse, pt, argument))) else SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } + ctx.reporter.removeBufferedMessages + res else SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) } diff --git a/tests/neg-macros/i16835.check b/tests/neg-macros/i16835.check new file mode 100644 index 000000000000..fb02f3c7f13f --- /dev/null +++ b/tests/neg-macros/i16835.check @@ -0,0 +1,6 @@ + +-- Error: tests/neg-macros/i16835/Test_2.scala:1:17 -------------------------------------------------------------------- +1 |def test: Unit = foo // error + | ^^^ + | my error + | my second error diff --git a/tests/neg-macros/i16835/Macro_1.scala b/tests/neg-macros/i16835/Macro_1.scala new file mode 100644 index 000000000000..ddee5dbecb4e --- /dev/null +++ b/tests/neg-macros/i16835/Macro_1.scala @@ -0,0 +1,21 @@ +import scala.quoted.* + +class Bar + +inline def foo: Unit = ${ fooExpr } + +def fooExpr(using Quotes): Expr[Unit] = + import quotes.reflect.* + Implicits.search(TypeRepr.of[Bar]) match + case res: ImplicitSearchSuccess => '{} + case failure: ImplicitSearchFailure => + report.errorAndAbort(failure.explanation) + + +inline given bar: Bar = ${ barExpr } + +def barExpr(using Quotes): Expr[Bar] = + import quotes.reflect.* + report.error(s"my error") + report.error(s"my second error") + '{ new Bar } diff --git a/tests/neg-macros/i16835/Test_2.scala b/tests/neg-macros/i16835/Test_2.scala new file mode 100644 index 000000000000..0dc2d39d6c3d --- /dev/null +++ b/tests/neg-macros/i16835/Test_2.scala @@ -0,0 +1 @@ +def test: Unit = foo // error