Skip to content

Commit

Permalink
Propagate implicit search errors from implicit macros
Browse files Browse the repository at this point in the history
Fixes partially scala#16835
  • Loading branch information
nicolasstucki committed Feb 7, 2023
1 parent a356581 commit 1f5a990
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 4 deletions.
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
*/
Expand Down
16 changes: 13 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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._
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 6 additions & 0 deletions tests/neg-macros/i16835.check
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions tests/neg-macros/i16835/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -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 }
1 change: 1 addition & 0 deletions tests/neg-macros/i16835/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def test: Unit = foo // error

0 comments on commit 1f5a990

Please sign in to comment.