diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index c9dbdb93ff58..01acbb8cc28c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -596,7 +596,7 @@ trait Implicits { // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints val isScalaDoc = context.tree == EmptyTree - val itree = atPos(pos.focus) { + val itree0 = atPos(pos.focus) { if (isLocal && !isScalaDoc) { // SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope, // rather than an attributed Select, to detect shadowing. @@ -608,15 +608,16 @@ trait Implicits { Select(gen.mkAttributedQualifier(info.pre), implicitMemberName) } } - typingLog("considering", typeDebug.ptTree(itree)) + val itree1 = if (isBlackbox(info.sym)) suppressMacroExpansion(itree0) else itree0 + typingLog("considering", typeDebug.ptTree(itree1)) - def fail(reason: String): SearchResult = failure(itree, reason) - def fallback = typed1(itree, EXPRmode, wildPt) + def fail(reason: String): SearchResult = failure(itree0, reason) + def fallback = typed1(itree1, EXPRmode, wildPt) try { - val itree1 = if (!isView) fallback else pt match { + val itree2 = if (!isView) fallback else pt match { case Function1(arg1, arg2) => typed1( - atPos(itree.pos)(Apply(itree, List(Ident("") setType approximate(arg1)))), + atPos(itree0.pos)(Apply(itree1, List(Ident("") setType approximate(arg1)))), EXPRmode, approximate(arg2) ) match { @@ -647,10 +648,10 @@ trait Implicits { if (Statistics.canEnable) Statistics.incCounter(typedImplicits) - val itree2 = if (isView) treeInfo.dissectApplied(itree1).callee - else adapt(itree1, EXPRmode, wildPt) + val itree3 = if (isView) treeInfo.dissectApplied(itree2).callee + else adapt(itree2, EXPRmode, wildPt) - typingStack.showAdapt(itree, itree2, pt, context) + typingStack.showAdapt(itree0, itree3, pt, context) def hasMatchingSymbol(tree: Tree): Boolean = (tree.symbol == info.sym) || { tree match { @@ -663,21 +664,21 @@ trait Implicits { if (context.hasErrors) fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) - else if (isLocal && !hasMatchingSymbol(itree1)) + else if (isLocal && !hasMatchingSymbol(itree2)) fail("candidate implicit %s is shadowed by %s".format( - info.sym.fullLocationString, itree1.symbol.fullLocationString)) + info.sym.fullLocationString, itree2.symbol.fullLocationString)) else { val tvars = undetParams map freshVar def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars) - if (matchesPt(itree2.tpe, ptInstantiated, undetParams)) { + if (matchesPt(itree3.tpe, ptInstantiated, undetParams)) { if (tvars.nonEmpty) typingLog("solve", ptLine("tvars" -> tvars, "tvars.constr" -> tvars.map(_.constr))) - val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), upper = false, lubDepth(itree2.tpe :: pt :: Nil)) + val targs = solvedTypes(tvars, undetParams, undetParams map varianceInType(pt), upper = false, lubDepth(itree3.tpe :: pt :: Nil)) // #2421: check that we correctly instantiated type parameters outside of the implicit tree: - checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") + checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ") context.firstError match { case Some(err) => return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) @@ -693,7 +694,7 @@ trait Implicits { if (okParams.isEmpty) EmptyTreeTypeSubstituter else { val subst = new TreeTypeSubstituter(okParams, okArgs) - subst traverse itree2 + subst traverse itree3 notifyUndetparamsInferred(okParams, okArgs) subst } @@ -711,9 +712,9 @@ trait Implicits { // This is just called for the side effect of error detection, // see SI-6966 to see what goes wrong if we use the result of this // as the SearchResult. - itree2 match { - case TypeApply(fun, args) => typedTypeApply(itree2, EXPRmode, fun, args) - case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c + itree3 match { + case TypeApply(fun, args) => typedTypeApply(itree3, EXPRmode, fun, args) + case Apply(TypeApply(fun, args), _) => typedTypeApply(itree3, EXPRmode, fun, args) // t2421c case t => t } @@ -721,13 +722,13 @@ trait Implicits { case Some(err) => fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => - val result = new SearchResult(itree2, subst) + val result = new SearchResult(unsuppressMacroExpansion(itree3), subst) if (Statistics.canEnable) Statistics.incCounter(foundImplicits) typingLog("success", s"inferred value of type $ptInstantiated is $result") result } } - else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) + else fail("incompatible: %s does not match expected type %s".format(itree3.tpe, ptInstantiated)) } } catch { diff --git a/src/reflect/scala/reflect/macros/Enclosures.scala b/src/reflect/scala/reflect/macros/Enclosures.scala index c3d019ccf355..31905c473969 100644 --- a/src/reflect/scala/reflect/macros/Enclosures.scala +++ b/src/reflect/scala/reflect/macros/Enclosures.scala @@ -45,18 +45,6 @@ trait Enclosures { */ def enclosingMacros: List[BlackboxContext] - /** Information about one of the currently considered implicit candidates. - * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, - * hence implicit searches can recursively trigger other implicit searches. - * - * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. - * If we're in an implicit macro being expanded, it's included in this list. - * - * Unlike `openImplicits`, this is a val, which means that it gets initialized when the context is created - * and always stays the same regardless of whatever happens during macro expansion. - */ - def enclosingImplicits: List[ImplicitCandidate] - /** Tries to guess a position for the enclosing application. * But that is simple, right? Just dereference `pos` of `macroApplication`? Not really. * If we're in a synthetic macro expansion (no positions), we must do our best to infer the position of something that triggerd this expansion. diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala index ec90ee8fe6d4..29c1af110bc4 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -23,27 +23,6 @@ trait Typers { */ def openMacros: List[BlackboxContext] - /** Information about one of the currently considered implicit candidates. - * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, - * hence implicit searches can recursively trigger other implicit searches. - * - * `pre` and `sym` provide information about the candidate itself. - * `pt` and `tree` store the parameters of the implicit search the candidate is participating in. - */ - case class ImplicitCandidate(pre: Type, sym: Symbol, pt: Type, tree: Tree) - - /** Information about one of the currently considered implicit candidates. - * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, - * hence implicit searches can recursively trigger other implicit searches. - * - * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. - * If we're in an implicit macro being expanded, it's included in this list. - * - * Unlike `enclosingImplicits`, this is a def, which means that it gets recalculated on every invocation, - * so it might change depending on what is going on during macro expansion. - */ - def openImplicits: List[ImplicitCandidate] - /** Typechecks the provided tree against the expected type `pt` in the macro callsite context. * * If `silent` is false, `TypecheckException` will be thrown in case of a typecheck error. diff --git a/src/reflect/scala/reflect/macros/WhiteboxContext.scala b/src/reflect/scala/reflect/macros/WhiteboxContext.scala index 76bc17746c96..9d65a5c16ea9 100644 --- a/src/reflect/scala/reflect/macros/WhiteboxContext.scala +++ b/src/reflect/scala/reflect/macros/WhiteboxContext.scala @@ -40,4 +40,37 @@ trait WhiteboxContext extends BlackboxContext { /** @inheritdoc */ def enclosingMacros: List[WhiteboxContext] + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * `pre` and `sym` provide information about the candidate itself. + * `pt` and `tree` store the parameters of the implicit search the candidate is participating in. + */ + case class ImplicitCandidate(pre: Type, sym: Symbol, pt: Type, tree: Tree) + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * If we're in an implicit macro being expanded, it's included in this list. + * + * Unlike `enclosingImplicits`, this is a def, which means that it gets recalculated on every invocation, + * so it might change depending on what is going on during macro expansion. + */ + def openImplicits: List[ImplicitCandidate] + + /** Information about one of the currently considered implicit candidates. + * Candidates are used in plural form, because implicit parameters may themselves have implicit parameters, + * hence implicit searches can recursively trigger other implicit searches. + * + * Can be useful to get information about an application with an implicit parameter that is materialized during current macro expansion. + * If we're in an implicit macro being expanded, it's included in this list. + * + * Unlike `openImplicits`, this is a val, which means that it gets initialized when the context is created + * and always stays the same regardless of whatever happens during macro expansion. + */ + def enclosingImplicits: List[ImplicitCandidate] } \ No newline at end of file diff --git a/test/files/neg/macro-blackbox-dynamic-materialization.check b/test/files/neg/macro-blackbox-dynamic-materialization.check new file mode 100644 index 000000000000..f6c73f7edb69 --- /dev/null +++ b/test/files/neg/macro-blackbox-dynamic-materialization.check @@ -0,0 +1,4 @@ +Test_2.scala:2: error: I don't like classes that contain integers + println(implicitly[Foo[C1]]) + ^ +one error found diff --git a/test/files/neg/macro-blackbox-dynamic-materialization/Macros_1.scala b/test/files/neg/macro-blackbox-dynamic-materialization/Macros_1.scala new file mode 100644 index 000000000000..a00d1950054a --- /dev/null +++ b/test/files/neg/macro-blackbox-dynamic-materialization/Macros_1.scala @@ -0,0 +1,25 @@ +import scala.reflect.macros.BlackboxContext +import scala.language.experimental.macros + +trait Foo[T] + +class C1(val x: Int) +class C2(val x: String) + +trait LowPriority { + implicit def lessSpecific[T]: Foo[T] = null +} + +object Foo extends LowPriority { + implicit def moreSpecific[T]: Foo[T] = macro Macros.impl[T] +} + +object Macros { + def impl[T: c.WeakTypeTag](c: BlackboxContext) = { + import c.universe._ + val tpe = weakTypeOf[T] + if (tpe.members.exists(_.typeSignature =:= typeOf[Int])) + c.abort(c.enclosingPosition, "I don't like classes that contain integers") + q"new Foo[$tpe]{ override def toString = ${tpe.toString} }" + } +} \ No newline at end of file diff --git a/test/files/neg/macro-blackbox-dynamic-materialization/Test_2.scala b/test/files/neg/macro-blackbox-dynamic-materialization/Test_2.scala new file mode 100644 index 000000000000..bf19209ab7e8 --- /dev/null +++ b/test/files/neg/macro-blackbox-dynamic-materialization/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + println(implicitly[Foo[C1]]) + println(implicitly[Foo[C2]]) +} \ No newline at end of file diff --git a/test/files/neg/macro-blackbox-fundep-materialization.check b/test/files/neg/macro-blackbox-fundep-materialization.check index a5a9b9a2060c..3c03064a2ddc 100644 --- a/test/files/neg/macro-blackbox-fundep-materialization.check +++ b/test/files/neg/macro-blackbox-fundep-materialization.check @@ -1,12 +1,8 @@ -Test_2.scala:7: Iso.materializeIso is not a valid implicit value for Iso[Test.Foo,L] because: -hasMatchingSymbol reported error: type mismatch; +Test_2.scala:7: error: type mismatch; found : Iso[Test.Foo,(Int, String, Boolean)] required: Iso[Test.Foo,Nothing] Note: (Int, String, Boolean) >: Nothing, but trait Iso is invariant in type U. You may wish to define U as -U instead. (SLS 4.5) val equiv = foo(Foo(23, "foo", true)) ^ -Test_2.scala:7: error: could not find implicit value for parameter iso: Iso[Test.Foo,L] - val equiv = foo(Foo(23, "foo", true)) - ^ one error found diff --git a/test/files/neg/macro-divergence-controlled/Impls_Macros_1.scala b/test/files/neg/macro-divergence-controlled/Impls_Macros_1.scala index 23fcdd6445a8..9fb374800bf4 100644 --- a/test/files/neg/macro-divergence-controlled/Impls_Macros_1.scala +++ b/test/files/neg/macro-divergence-controlled/Impls_Macros_1.scala @@ -1,4 +1,4 @@ -import scala.reflect.macros.BlackboxContext +import scala.reflect.macros.WhiteboxContext import language.experimental.macros trait Complex[T] @@ -6,7 +6,7 @@ trait Complex[T] class Foo(val foo: Foo) object Complex { - def impl[T: c.WeakTypeTag](c: BlackboxContext): c.Expr[Complex[T]] = { + def impl[T: c.WeakTypeTag](c: WhiteboxContext): c.Expr[Complex[T]] = { import c.universe._ val tpe = weakTypeOf[T] for (f <- tpe.declarations.collect{case f: TermSymbol if f.isParamAccessor && !f.isMethod => f}) { diff --git a/test/files/run/macro-sip19-revised/Impls_Macros_1.scala b/test/files/run/macro-sip19-revised/Impls_Macros_1.scala index 870930c7e5f5..3acc52dbe0bf 100644 --- a/test/files/run/macro-sip19-revised/Impls_Macros_1.scala +++ b/test/files/run/macro-sip19-revised/Impls_Macros_1.scala @@ -1,7 +1,7 @@ -import scala.reflect.macros.BlackboxContext +import scala.reflect.macros.WhiteboxContext object Macros { - def impl(c: BlackboxContext) = { + def impl(c: WhiteboxContext) = { import c.universe._ val inscope = c.inferImplicitValue(c.mirror.staticClass("SourceLocation").toType) diff --git a/test/files/run/macro-sip19/Impls_Macros_1.scala b/test/files/run/macro-sip19/Impls_Macros_1.scala index 72a3c2568da9..f830d2af0d2d 100644 --- a/test/files/run/macro-sip19/Impls_Macros_1.scala +++ b/test/files/run/macro-sip19/Impls_Macros_1.scala @@ -1,7 +1,7 @@ -import scala.reflect.macros.BlackboxContext +import scala.reflect.macros.WhiteboxContext object Macros { - def impl(c: BlackboxContext) = { + def impl(c: WhiteboxContext) = { import c.universe._ val Apply(fun, args) = c.enclosingImplicits(0).tree val fileName = fun.pos.source.file.file.getName diff --git a/test/files/run/macro-whitebox-dynamic-materialization.check b/test/files/run/macro-whitebox-dynamic-materialization.check new file mode 100644 index 000000000000..ccec8e5b25f5 --- /dev/null +++ b/test/files/run/macro-whitebox-dynamic-materialization.check @@ -0,0 +1,2 @@ +null +C2 diff --git a/test/files/run/macro-whitebox-dynamic-materialization/Macros_1.scala b/test/files/run/macro-whitebox-dynamic-materialization/Macros_1.scala new file mode 100644 index 000000000000..87cd310b09d4 --- /dev/null +++ b/test/files/run/macro-whitebox-dynamic-materialization/Macros_1.scala @@ -0,0 +1,25 @@ +import scala.reflect.macros.WhiteboxContext +import scala.language.experimental.macros + +trait Foo[T] + +class C1(val x: Int) +class C2(val x: String) + +trait LowPriority { + implicit def lessSpecific[T]: Foo[T] = null +} + +object Foo extends LowPriority { + implicit def moreSpecific[T]: Foo[T] = macro Macros.impl[T] +} + +object Macros { + def impl[T: c.WeakTypeTag](c: WhiteboxContext) = { + import c.universe._ + val tpe = weakTypeOf[T] + if (tpe.members.exists(_.typeSignature =:= typeOf[Int])) + c.abort(c.enclosingPosition, "I don't like classes that contain integers") + q"new Foo[$tpe]{ override def toString = ${tpe.toString} }" + } +} \ No newline at end of file diff --git a/test/files/run/macro-whitebox-dynamic-materialization/Test_2.scala b/test/files/run/macro-whitebox-dynamic-materialization/Test_2.scala new file mode 100644 index 000000000000..bf19209ab7e8 --- /dev/null +++ b/test/files/run/macro-whitebox-dynamic-materialization/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + println(implicitly[Foo[C1]]) + println(implicitly[Foo[C2]]) +} \ No newline at end of file