summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2013-12-17 03:56:52 -0800
committerEugene Burmako <xeno.by@gmail.com>2013-12-17 03:56:52 -0800
commitb5ef79f2f8bc010220d2920a890352d96ad84b45 (patch)
treefd6b0985debc9348bdc8df1f7b3629415974e5cb
parentdbe7a366c994fe359edc368bfcd8a6a35a00e0da (diff)
parentca2dbe55eb55ec0d5461a62f5783ab1f1ebb4818 (diff)
downloadscala-b5ef79f2f8bc010220d2920a890352d96ad84b45.tar.gz
scala-b5ef79f2f8bc010220d2920a890352d96ad84b45.tar.bz2
scala-b5ef79f2f8bc010220d2920a890352d96ad84b45.zip
Merge pull request #3236 from xeno-by/topic/wildbox-macros
(2.11.0-M8) whitebox macros are now first typechecked against outerPt
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala61
-rw-r--r--test/files/run/t6992.check3
-rw-r--r--test/files/run/t6992/Macros_1.scala75
-rw-r--r--test/files/run/t6992/Test_2.scala12
-rw-r--r--test/files/run/t8048a.check1
-rw-r--r--test/files/run/t8048a/Macros_1.scala11
-rw-r--r--test/files/run/t8048a/Test_2.scala4
-rw-r--r--test/files/run/t8048b.check3
-rw-r--r--test/files/run/t8048b/Macros_1.scala37
-rw-r--r--test/files/run/t8048b/Test_2.scala5
10 files changed, 185 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 27920dbd74..a18068f2e0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -600,39 +600,46 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
}
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
+ * @param outerPt Expected type that comes from enclosing context (something that's traditionally called `pt`).
+ * @param innerPt Expected type that comes from the signature of a macro def, possibly wildcarded to help type inference.
* @see MacroExpander
*/
- def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
- object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, pt) {
- override def onSuccess(expanded0: Tree) = {
- def approximate(tp: Type) = {
+ def macroExpandApply(typer: Typer, expandee: Tree, mode: Mode, outerPt: Type): Tree = {
+ object expander extends TermMacroExpander(APPLY_ROLE, typer, expandee, mode, outerPt) {
+ lazy val innerPt = {
+ val tp = if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe
+ if (isBlackbox(expandee)) tp
+ else {
// approximation is necessary for whitebox macros to guide type inference
// read more in the comments for onDelayed below
- if (isBlackbox(expandee)) tp
+ val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
+ deriveTypeWithWildcards(undetparams)(tp)
+ }
+ }
+ override def onSuccess(expanded0: Tree) = {
+ // prematurely annotate the tree with a macro expansion attachment
+ // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
+ linkExpandeeAndExpanded(expandee, expanded0)
+
+ def typecheck(label: String, tree: Tree, pt: Type): Tree = {
+ if (tree.isErrorTyped) tree
else {
- val undetparams = tp collect { case tp if tp.typeSymbol.isTypeParameter => tp.typeSymbol }
- deriveTypeWithWildcards(undetparams)(tp)
+ if (macroDebugVerbose) println(s"$label (against pt = $pt): $tree")
+ // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
+ // therefore we need to re-enable the conversions back temporarily
+ val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
+ if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
+ result
}
}
- val macroPt = approximate(if (isNullaryInvocation(expandee)) expandee.tpe.finalResultType else expandee.tpe)
- val expanded = if (isBlackbox(expandee)) atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(macroPt))) else expanded0
- // prematurely annotate the tree with a macro expansion attachment
- // so that adapt called indirectly by typer.typed knows that it needs to apply the existential fixup
- linkExpandeeAndExpanded(expandee, expanded)
-
- // `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
- // therefore we need to re-enable the conversions back temporarily
- if (macroDebugVerbose) println(s"typecheck #1 (against macroPt = $macroPt): $expanded")
- val expanded1 = typer.context.withImplicitsEnabled(typer.typed(expanded, mode, macroPt))
- if (expanded1.isErrorTyped) {
- if (macroDebugVerbose) println(s"typecheck #1 has failed: ${typer.context.reportBuffer.errors}")
- expanded1
+ if (isBlackbox(expandee)) {
+ val expanded1 = atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(innerPt)))
+ typecheck("blackbox typecheck", expanded1, outerPt)
} else {
- if (macroDebugVerbose) println(s"typecheck #2 (against pt = $pt): $expanded1")
- val expanded2 = typer.context.withImplicitsEnabled(super.onSuccess(expanded1))
- if (macroDebugVerbose && expanded2.isErrorTyped) println(s"typecheck #2 has failed: ${typer.context.reportBuffer.errors}")
- expanded2
+ val expanded1 = expanded0
+ val expanded2 = typecheck("whitebox typecheck #1", expanded1, outerPt)
+ typecheck("whitebox typecheck #2", expanded2, innerPt)
}
}
override def onDelayed(delayed: Tree) = {
@@ -686,11 +693,11 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers {
// Thanks to that the materializer can take a look at what's going on and react accordingly.
val shouldInstantiate = typer.context.undetparams.nonEmpty && !mode.inPolyMode
if (shouldInstantiate) {
- if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, pt)
+ if (isBlackbox(expandee)) typer.instantiatePossiblyExpectingUnit(delayed, mode, outerPt)
else {
forced += delayed
- typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), pt, keepNothings = false)
- macroExpandApply(typer, delayed, mode, pt)
+ typer.infer.inferExprInstance(delayed, typer.context.extractUndetparams(), outerPt, keepNothings = false)
+ macroExpandApply(typer, delayed, mode, outerPt)
}
} else delayed
}
diff --git a/test/files/run/t6992.check b/test/files/run/t6992.check
new file mode 100644
index 0000000000..1a0684c995
--- /dev/null
+++ b/test/files/run/t6992.check
@@ -0,0 +1,3 @@
+Int
+42
+42
diff --git a/test/files/run/t6992/Macros_1.scala b/test/files/run/t6992/Macros_1.scala
new file mode 100644
index 0000000000..25566dddbf
--- /dev/null
+++ b/test/files/run/t6992/Macros_1.scala
@@ -0,0 +1,75 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.Context
+
+object Macros {
+ def foo(name: String): Any = macro foo_impl
+ def foo_impl(c: Context)(name: c.Expr[String]) = {
+ import c.universe._
+
+ val Literal(Constant(lit: String)) = name.tree
+ val anon = newTypeName(c.fresh)
+
+ c.Expr(Block(
+ ClassDef(
+ Modifiers(Flag.FINAL), anon, Nil, Template(
+ Nil, noSelfType, List(
+ DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))),
+ TypeDef(Modifiers(), TypeName(lit), Nil, TypeTree(typeOf[Int]))
+ )
+ )
+ ),
+ Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
+ ))
+ }
+
+ def bar(name: String): Any = macro bar_impl
+ def bar_impl(c: Context)(name: c.Expr[String]) = {
+ import c.universe._
+
+ val Literal(Constant(lit: String)) = name.tree
+ val anon = newTypeName(c.fresh)
+
+ c.Expr(Block(
+ ClassDef(
+ Modifiers(Flag.FINAL), anon, Nil, Template(
+ Nil, noSelfType, List(
+ DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))),
+ DefDef(
+ Modifiers(), TermName(lit), Nil, Nil, TypeTree(),
+ c.literal(42).tree
+ )
+ )
+ )
+ ),
+ Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
+ ))
+ }
+
+ def baz(name: String): Any = macro baz_impl
+ def baz_impl(c: Context)(name: c.Expr[String]) = {
+ import c.universe._
+
+ val Literal(Constant(lit: String)) = name.tree
+ val anon = newTypeName(c.fresh)
+ val wrapper = newTypeName(c.fresh)
+
+ c.Expr(Block(
+ ClassDef(
+ Modifiers(), anon, Nil, Template(
+ Nil, emptyValDef, List(
+ DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))),
+ DefDef(
+ Modifiers(), TermName(lit), Nil, Nil, TypeTree(),
+ c.literal(42).tree
+ )
+ )
+ )
+ ),
+ ClassDef(
+ Modifiers(Flag.FINAL), wrapper, Nil,
+ Template(Ident(anon) :: Nil, noSelfType, DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) :: Nil)
+ ),
+ Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
+ ))
+ }
+} \ No newline at end of file
diff --git a/test/files/run/t6992/Test_2.scala b/test/files/run/t6992/Test_2.scala
new file mode 100644
index 0000000000..05282d6f5b
--- /dev/null
+++ b/test/files/run/t6992/Test_2.scala
@@ -0,0 +1,12 @@
+import scala.language.reflectiveCalls
+
+object Test extends App {
+ val foo = Macros.foo("T")
+ println(scala.reflect.runtime.universe.weakTypeOf[foo.T].typeSymbol.typeSignature)
+
+ val bar = Macros.bar("test")
+ println(bar.test)
+
+ val baz = Macros.baz("test")
+ println(baz.test)
+} \ No newline at end of file
diff --git a/test/files/run/t8048a.check b/test/files/run/t8048a.check
new file mode 100644
index 0000000000..8fb9e26e84
--- /dev/null
+++ b/test/files/run/t8048a.check
@@ -0,0 +1 @@
+Some(2)
diff --git a/test/files/run/t8048a/Macros_1.scala b/test/files/run/t8048a/Macros_1.scala
new file mode 100644
index 0000000000..f48e84f1de
--- /dev/null
+++ b/test/files/run/t8048a/Macros_1.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.WhiteboxContext
+import scala.language.experimental.macros
+
+object Macros {
+ def impl(c: WhiteboxContext) = {
+ import c.universe._
+ q"if (true) Some(2) else None"
+ }
+
+ def foo: Any = macro impl
+} \ No newline at end of file
diff --git a/test/files/run/t8048a/Test_2.scala b/test/files/run/t8048a/Test_2.scala
new file mode 100644
index 0000000000..4e1c8b16b0
--- /dev/null
+++ b/test/files/run/t8048a/Test_2.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ val x: Option[Int] = Macros.foo
+ println(x)
+} \ No newline at end of file
diff --git a/test/files/run/t8048b.check b/test/files/run/t8048b.check
new file mode 100644
index 0000000000..083edaac24
--- /dev/null
+++ b/test/files/run/t8048b.check
@@ -0,0 +1,3 @@
+2
+2
+2
diff --git a/test/files/run/t8048b/Macros_1.scala b/test/files/run/t8048b/Macros_1.scala
new file mode 100644
index 0000000000..b113af86ea
--- /dev/null
+++ b/test/files/run/t8048b/Macros_1.scala
@@ -0,0 +1,37 @@
+// see the following discussions to understand what's being tested here:
+// * https://issues.scala-lang.org/browse/SI-6992
+// * https://issues.scala-lang.org/browse/SI-8048
+// * http://stackoverflow.com/questions/14370842/getting-a-structural-type-with-an-anonymous-classs-methods-from-a-macro
+// * http://stackoverflow.com/questions/18480707/method-cannot-be-accessed-in-macro-generated-class/18485004#18485004
+// * https://groups.google.com/forum/#!topic/scala-internals/eXQt-BPm4i8
+
+import scala.language.experimental.macros
+import scala.reflect.macros.WhiteboxContext
+
+object Macros {
+ def impl1(c: WhiteboxContext) = {
+ import c.universe._
+ q"""
+ trait Foo { def x = 2 }
+ new Foo {}
+ """
+ }
+ def foo1: Any = macro impl1
+
+ def impl2(c: WhiteboxContext) = {
+ import c.universe._
+ q"""
+ class Foo { def x = 2 }
+ new Foo
+ """
+ }
+ def foo2: Any = macro impl2
+
+ def impl3(c: WhiteboxContext) = {
+ import c.universe._
+ q"""
+ new { def x = 2 }
+ """
+ }
+ def foo3: Any = macro impl3
+} \ No newline at end of file
diff --git a/test/files/run/t8048b/Test_2.scala b/test/files/run/t8048b/Test_2.scala
new file mode 100644
index 0000000000..fb410dab7a
--- /dev/null
+++ b/test/files/run/t8048b/Test_2.scala
@@ -0,0 +1,5 @@
+object Test extends App {
+ println(Macros.foo1.x)
+ println(Macros.foo2.x)
+ println(Macros.foo3.x)
+} \ No newline at end of file