Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate implicit search errors from implicit macros #16840

Merged
merged 1 commit into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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