summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-10-02 17:22:07 +0200
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-11-12 18:40:01 -0800
commit0d5c2f76ea30c6a45471dac635f035e931075453 (patch)
tree15f477eb256a65f2d260b940f1a3c9bed113a3db
parent6038bac3513a834e67ab4074c2c7b03aac11b1b3 (diff)
downloadscala-0d5c2f76ea30c6a45471dac635f035e931075453.tar.gz
scala-0d5c2f76ea30c6a45471dac635f035e931075453.tar.bz2
scala-0d5c2f76ea30c6a45471dac635f035e931075453.zip
blackbox restriction #3: can't affect implicit search
When an application of a blackbox macro is used as an implicit candidate, no expansion is performed until the macro is selected as the result of the implicit search. This makes it impossible to dynamically calculate availability of implicit macros.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala41
-rw-r--r--src/reflect/scala/reflect/macros/Enclosures.scala12
-rw-r--r--src/reflect/scala/reflect/macros/Typers.scala21
-rw-r--r--src/reflect/scala/reflect/macros/WhiteboxContext.scala33
-rw-r--r--test/files/neg/macro-blackbox-dynamic-materialization.check4
-rw-r--r--test/files/neg/macro-blackbox-dynamic-materialization/Macros_1.scala25
-rw-r--r--test/files/neg/macro-blackbox-dynamic-materialization/Test_2.scala4
-rw-r--r--test/files/neg/macro-blackbox-fundep-materialization.check6
-rw-r--r--test/files/neg/macro-divergence-controlled/Impls_Macros_1.scala4
-rw-r--r--test/files/run/macro-sip19-revised/Impls_Macros_1.scala4
-rw-r--r--test/files/run/macro-sip19/Impls_Macros_1.scala4
-rw-r--r--test/files/run/macro-whitebox-dynamic-materialization.check2
-rw-r--r--test/files/run/macro-whitebox-dynamic-materialization/Macros_1.scala25
-rw-r--r--test/files/run/macro-whitebox-dynamic-materialization/Test_2.scala4
14 files changed, 125 insertions, 64 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index c9dbdb93ff..01acbb8cc2 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("<argument>") setType approximate(arg1)))),
+ atPos(itree0.pos)(Apply(itree1, List(Ident("<argument>") 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 c3d019ccf3..31905c4739 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 ec90ee8fe6..29c1af110b 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 76bc17746c..9d65a5c16e 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 0000000000..f6c73f7edb
--- /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 0000000000..a00d195005
--- /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 0000000000..bf19209ab7
--- /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 a5a9b9a206..3c03064a2d 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 23fcdd6445..9fb374800b 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 870930c7e5..3acc52dbe0 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 72a3c2568d..f830d2af0d 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 0000000000..ccec8e5b25
--- /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 0000000000..87cd310b09
--- /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 0000000000..bf19209ab7
--- /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