diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 26fd52fb7138..116845fc5843 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -198,7 +198,7 @@ class InlineReducer(inliner: Inliner)(using Context): val evTyper = new Typer(ctx.nestingLevel + 1) val evCtx = ctx.fresh.setTyper(evTyper) inContext(evCtx) { - val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span) + val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span, ignored = Set.empty) evidence.tpe match { case fail: Implicits.AmbiguousImplicits => report.error(evTyper.missingArgMsg(evidence, tpt.tpe, ""), tpt.srcPos) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index aeecd9c376e3..c31152e329d5 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -441,7 +441,7 @@ object Inlines: val evTyper = new Typer(ctx.nestingLevel + 1) val evCtx = ctx.fresh.setTyper(evTyper) inContext(evCtx) { - val evidence = evTyper.inferImplicitArg(tpe, callTypeArgs.head.span) + val evidence = evTyper.inferImplicitArg(tpe, callTypeArgs.head.span, ignored = Set.empty) evidence.tpe match case fail: Implicits.SearchFailureType => errorTree(call, evTyper.missingArgMsg(evidence, tpe, "")) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 7a0a19552f48..bf6777a665af 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -662,7 +662,7 @@ object Completion: */ private def implicitConversionTargets(qual: tpd.Tree)(using Context): Set[SearchSuccess] = { val typer = ctx.typer - val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits + val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span, Set.empty).allImplicits interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %") conversions diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index a73f884fbac9..1f012271d9e9 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -86,7 +86,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { */ protected def tryHeal(tp: TypeRef): Type = { val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) - val tag = ctx.typer.inferImplicitArg(reqType, pos.span) + val tag = ctx.typer.inferImplicitArg(reqType, pos.span, ignored = Set.empty) tag.tpe match case tp: TermRef => ctx.typer.checkStable(tp, pos, "type witness") diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0727c83d8469..1d16b0914e58 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -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): 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") @@ -1684,7 +1684,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) { @@ -1693,6 +1693,8 @@ trait Implicits: else ctxImplicits.eligible(wildProto) else implicitScope(wildProto).eligible + val preEligible = + prePreEligible.filter(candidate => !ignored.contains(candidate.implicitRef.underlyingRef.symbol)) /** 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 diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 59993a69797d..89532b200ffa 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -45,7 +45,7 @@ trait QuotesAndSplices { report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } - val quotes = inferImplicitArg(defn.QuotesClass.typeRef, tree.span) + val quotes = inferImplicitArg(defn.QuotesClass.typeRef, tree.span, ignored = Set.empty) if quotes.tpe.isInstanceOf[SearchFailureType] then report.error(missingArgMsg(quotes, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span)) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index c935e8d6b3cf..4f0d9f238bce 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -45,7 +45,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case arg :: Nil => instArg(arg) match case defn.ArrayOf(elemTp) => - val etag = typer.inferImplicitArg(defn.ClassTagClass.typeRef.appliedTo(elemTp), span) + val etag = typer.inferImplicitArg(defn.ClassTagClass.typeRef.appliedTo(elemTp), span, Set.empty) if etag.tpe.isError then EmptyTree else etag.select(nme.wrap) case tp if hasStableErasure(tp) && !tp.isBottomTypeAfterErasure => val sym = tp.typeSymbol @@ -148,7 +148,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): /** Is there an `CanEqual[T, T]` instance, assuming -strictEquality? */ def hasEq(tp: Type)(using Context): Boolean = - val inst = typer.inferImplicitArg(defn.CanEqualClass.typeRef.appliedTo(tp, tp), span) + val inst = typer.inferImplicitArg(defn.CanEqualClass.typeRef.appliedTo(tp, tp), span, ignored = Set.empty) !inst.isEmpty && !inst.tpe.isError /** Can we assume the canEqualAny instance for `tp1`, `tp2`? diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 817e7baf1c8c..e5d23e21e077 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1098,7 +1098,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case Decimal => defn.FromDigits_DecimalClass case Floating => defn.FromDigits_FloatingClass } - inferImplicit(fromDigitsCls.typeRef.appliedTo(target), EmptyTree, tree.span) match { + inferImplicit(fromDigitsCls.typeRef.appliedTo(target), EmptyTree, tree.span, ignored = Set.empty) match { case SearchSuccess(arg, _, _, _) => val fromDigits = untpd.Select(untpd.TypedSplice(arg), nme.fromDigits).withSpan(tree.span) val firstArg = Literal(Constant(digits)) @@ -1271,7 +1271,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def withTag(tpe: Type): Option[Tree] = { require(ctx.mode.is(Mode.Pattern)) withoutMode(Mode.Pattern)( - inferImplicit(tpe, EmptyTree, tree.tpt.span) + inferImplicit(tpe, EmptyTree, tree.tpt.span, ignored = Set.empty) ) match case SearchSuccess(clsTag, _, _, _) => withMode(Mode.InTypeTest) { @@ -4173,7 +4173,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else formals1 implicitArgs(formals2, argIndex + 1, pt) - val arg = inferImplicitArg(formal, tree.span.endPos) + val arg = inferImplicitArg(formal, tree.span.endPos, ignored = Set.empty) arg.tpe match case failed: AmbiguousImplicits => val pt1 = pt.deepenProtoTrans diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index ef2eacf42225..96283db3cc11 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2527,7 +2527,15 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Implicits extends ImplicitsModule: def search(tpe: TypeRepr): ImplicitSearchResult = import tpd.TreeOps - val implicitTree = ctx.typer.inferImplicitArg(tpe, Position.ofMacroExpansion.span) + val implicitTree = ctx.typer.inferImplicitArg(tpe, Position.ofMacroExpansion.span, ignored = Set.empty) + // Make sure that we do not have any uninstantiated type variables. + // See tests/pos-macros/i16636. + // See tests/pos-macros/exprSummonWithTypeVar with -Xcheck-macros. + dotc.typer.Inferencing.fullyDefinedType(implicitTree.tpe, "", implicitTree) + implicitTree + def searchIgnoring(tpe: TypeRepr)(ignored: Symbol*): ImplicitSearchResult = + import tpd.TreeOps + val implicitTree = ctx.typer.inferImplicitArg(tpe, Position.ofMacroExpansion.span, ignored.toSet) // Make sure that we do not have any uninstantiated type variables. // See tests/pos-macros/i16636. // See tests/pos-macros/exprSummonWithTypeVar with -Xcheck-macros. diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index d1385a0193d6..5a687006979a 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -280,4 +280,12 @@ object Expr { } } + @scala.annotation.experimental def summonIgnoring[T](using Type[T])(using quotes: Quotes)(ignored: quotes.reflect.Symbol*): Option[Expr[T]] = { + import quotes.reflect._ + Implicits.searchIgnoring(TypeRepr.of[T])(ignored*) match { + case iss: ImplicitSearchSuccess => Some(iss.tree.asExpr.asInstanceOf[Expr[T]]) + case isf: ImplicitSearchFailure => None + } + } + } diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 7a98d6f6f761..2794bedf8fdb 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3705,6 +3705,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param tpe type of the implicit parameter */ def search(tpe: TypeRepr): ImplicitSearchResult + + @experimental def searchIgnoring(tpe: TypeRepr)(ignored: Symbol*): ImplicitSearchResult } /** Result of a given instance search */ diff --git a/tests/run-macros/summonIgnoring.check b/tests/run-macros/summonIgnoring.check new file mode 100644 index 000000000000..5369c42c4888 --- /dev/null +++ b/tests/run-macros/summonIgnoring.check @@ -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 diff --git a/tests/run-macros/summonIgnoring/Macro_1.scala b/tests/run-macros/summonIgnoring/Macro_1.scala new file mode 100644 index 000000000000..e4771588ce4e --- /dev/null +++ b/tests/run-macros/summonIgnoring/Macro_1.scala @@ -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]") + } + } + } + +} diff --git a/tests/run-macros/summonIgnoring/Test_2.scala b/tests/run-macros/summonIgnoring/Test_2.scala new file mode 100644 index 000000000000..9855ff9dbea5 --- /dev/null +++ b/tests/run-macros/summonIgnoring/Test_2.scala @@ -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() + } +} \ No newline at end of file