-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Implement summonIgnoring
#22417
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
Implement summonIgnoring
#22417
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -901,7 +901,7 @@ trait Implicits: | |
} | ||
|
||
try | ||
val inferred = inferImplicit(adjust(to), from, from.span) | ||
val inferred = inferImplicit(adjust(to), from, from.span, ignored = Set.empty) | ||
|
||
inferred match { | ||
case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) => | ||
|
@@ -928,8 +928,8 @@ trait Implicits: | |
/** Find an implicit argument for parameter `formal`. | ||
* Return a failure as a SearchFailureType in the type of the returned tree. | ||
*/ | ||
def inferImplicitArg(formal: Type, span: Span)(using Context): Tree = | ||
inferImplicit(formal, EmptyTree, span) match | ||
def inferImplicitArg(formal: Type, span: Span, ignored: Set[Symbol])(using Context): Tree = | ||
inferImplicit(formal, EmptyTree, span, ignored) match | ||
case SearchSuccess(arg, _, _, _) => arg | ||
case fail @ SearchFailure(failed) => | ||
if fail.isAmbiguous then failed | ||
|
@@ -944,7 +944,7 @@ trait Implicits: | |
|
||
/** Search an implicit argument and report error if not found */ | ||
def implicitArgTree(formal: Type, span: Span, where: => String = "")(using Context): Tree = { | ||
val arg = inferImplicitArg(formal, span) | ||
val arg = inferImplicitArg(formal, span, ignored = Set.empty) | ||
if (arg.tpe.isInstanceOf[SearchFailureType]) | ||
report.error(missingArgMsg(arg, formal, where), ctx.source.atSpan(span)) | ||
arg | ||
|
@@ -968,7 +968,7 @@ trait Implicits: | |
def ignoredInstanceNormalImport = arg.tpe match | ||
case fail: SearchFailureType => | ||
if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then | ||
inferImplicit(fail.expectedType, fail.argument, arg.span)( | ||
inferImplicit(fail.expectedType, fail.argument, arg.span, Set.empty)( | ||
using findHiddenImplicitsCtx(ctx)) match { | ||
case s: SearchSuccess => Some(s) | ||
case f: SearchFailure => | ||
|
@@ -1082,7 +1082,7 @@ trait Implicits: | |
* it should be applied, EmptyTree otherwise. | ||
* @param span The position where errors should be reported. | ||
*/ | ||
def inferImplicit(pt: Type, argument: Tree, span: Span)(using Context): SearchResult = ctx.profiler.onImplicitSearch(pt): | ||
def inferImplicit(pt: Type, argument: Tree, span: Span, ignored: Set[Symbol])(using Context): SearchResult = ctx.profiler.onImplicitSearch(pt): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also use a default argument here. |
||
trace(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) { | ||
record("inferImplicit") | ||
assert(ctx.phase.allowsImplicitSearch, | ||
|
@@ -1110,7 +1110,7 @@ trait Implicits: | |
else i"conversion from ${argument.tpe} to $pt" | ||
|
||
CyclicReference.trace(i"searching for an implicit $searchStr"): | ||
try ImplicitSearch(pt, argument, span)(using searchCtx).bestImplicit | ||
try ImplicitSearch(pt, argument, span, ignored)(using searchCtx).bestImplicit | ||
catch case ce: CyclicReference => | ||
ce.inImplicitSearch = true | ||
throw ce | ||
|
@@ -1130,9 +1130,9 @@ trait Implicits: | |
result | ||
case result: SearchFailure if result.isAmbiguous => | ||
val deepPt = pt.deepenProto | ||
if (deepPt ne pt) inferImplicit(deepPt, argument, span) | ||
if (deepPt ne pt) inferImplicit(deepPt, argument, span, ignored) | ||
else if (migrateTo3 && !ctx.mode.is(Mode.OldImplicitResolution)) | ||
withMode(Mode.OldImplicitResolution)(inferImplicit(pt, argument, span)) match { | ||
withMode(Mode.OldImplicitResolution)(inferImplicit(pt, argument, span, ignored)) match { | ||
case altResult: SearchSuccess => | ||
report.migrationWarning( | ||
result.reason.msg | ||
|
@@ -1243,7 +1243,7 @@ trait Implicits: | |
} | ||
|
||
/** An implicit search; parameters as in `inferImplicit` */ | ||
class ImplicitSearch(protected val pt: Type, protected val argument: Tree, span: Span)(using Context): | ||
class ImplicitSearch(protected val pt: Type, protected val argument: Tree, span: Span, ignored: Set[Symbol])(using Context): | ||
assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType], | ||
em"found: $argument: ${argument.tpe}, expected: $pt") | ||
|
||
|
@@ -1670,7 +1670,7 @@ trait Implicits: | |
SearchFailure(TooUnspecific(pt), span) | ||
else | ||
val contextual = ctxImplicits != null | ||
val preEligible = // the eligible candidates, ignoring positions | ||
val prePreEligible = // the eligible candidates, ignoring positions | ||
if ctxImplicits != null then | ||
if ctx.gadt.isNarrowing then | ||
withoutMode(Mode.ImplicitsEnabled) { | ||
|
@@ -1679,6 +1679,8 @@ trait Implicits: | |
else ctxImplicits.eligible(wildProto) | ||
else implicitScope(wildProto).eligible | ||
|
||
val preEligible = | ||
prePreEligible.filter(candidate => !ignored.contains(candidate.implicitRef.underlyingRef.symbol)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will copy each list returned by eligible, so it's a significant memory churn to cater for an edge case. An improvement would use var preEligible = ... // rhs of prePreEligible
if !ignored.isEmpty then
preEligible = preEligible.filter(...) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, good point. Thank you! |
||
/** Does candidate `cand` come too late for it to be considered as an | ||
* eligible candidate? This is the case if `cand` appears in the same | ||
* scope as a given definition of the form `given ... = ...` that | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
TC[C2] generated in macro using: | ||
TC2[_] generated in macro using: | ||
TC[C1] generated in macro |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
//> using options -experimental | ||
import scala.quoted._ | ||
class C1 | ||
trait TC[T] { | ||
def print(): Unit | ||
} | ||
|
||
object TC { | ||
implicit transparent inline def auto[T]: TC[T] = ${autoImpl[T]} | ||
def autoImpl[T: Type](using Quotes): Expr[TC[T]] = | ||
import quotes.reflect._ | ||
if (TypeRepr.of[T].typeSymbol == Symbol.classSymbol("C1")){ | ||
'{ | ||
new TC[T] { | ||
def print() = { | ||
println("TC[C1] generated in macro") | ||
} | ||
} | ||
} | ||
} else { | ||
Expr.summonIgnoring[TC2[C1]](Symbol.classSymbol("TC").companionModule.methodMember("auto")*) match | ||
case Some(a) => | ||
'{ | ||
new TC[T] { | ||
def print(): Unit = | ||
println(s"TC[${${Expr(TypeRepr.of[T].show)}}] generated in macro using:") | ||
$a.print() | ||
} | ||
} | ||
case None => | ||
'{ | ||
new TC[T]{ | ||
def print(): Unit = | ||
println(s"TC[${${Expr(TypeRepr.of[T].show)}}] generated in macro without TC2[_]") | ||
} | ||
} | ||
} | ||
} | ||
|
||
trait TC2[T] { | ||
def print(): Unit | ||
} | ||
|
||
object TC2 { | ||
implicit def auto2[T](using tc: TC[T]): TC2[T] = new TC2[T] { | ||
def print(): Unit = | ||
println(s"TC2[_] generated in macro using:") | ||
tc.print() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//> using options -experimental | ||
|
||
@main def Test(): Unit = { | ||
class C2 | ||
summon[TC[C2]].print() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
No given in scope: | ||
TC[C2] generated in macro without TC[C1] | ||
Given in scope: | ||
TC[C2] generated in macro using: | ||
TC[C1] defined by a user |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//> using options -experimental | ||
import scala.quoted._ | ||
class C1 | ||
trait TC[T] { | ||
def print(): Unit | ||
} | ||
object TC { | ||
implicit transparent inline def auto[T]: TC[T] = ${autoImpl[T]} | ||
def autoImpl[T: Type](using Quotes): Expr[TC[T]] = | ||
import quotes.reflect._ | ||
if(TypeRepr.of[T].typeSymbol == Symbol.classSymbol("C1")){ | ||
'{ | ||
new TC[T] { | ||
def print() = { | ||
println("TC[C1] generated in macro") | ||
} | ||
} | ||
} | ||
} else { | ||
Expr.summonIgnoring[TC[C1]](Symbol.classSymbol("TC").companionModule.methodMember("auto")*) match | ||
case Some(a) => | ||
'{ | ||
new TC[T] { | ||
def print(): Unit = | ||
println(s"TC[${${Expr(TypeRepr.of[T].show)}}] generated in macro using:") | ||
$a.print() | ||
} | ||
} | ||
case None => | ||
'{ | ||
new TC[T]{ | ||
def print(): Unit = | ||
println(s"TC[${${Expr(TypeRepr.of[T].show)}}] generated in macro without TC[C1]") | ||
} | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//> using options -experimental | ||
|
||
@main def Test(): Unit = { | ||
class C2 | ||
println("No given in scope:") | ||
summon[TC[C2]].print() | ||
|
||
{ | ||
println("Given in scope:") | ||
given TC[C1] = new TC[C1] { | ||
def print() = println("TC[C1] defined by a user") | ||
} | ||
summon[TC[C2]].print() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this case it's overall simpler to use a default
= Set.empty
forignored
.