diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Macros.scala | 61 | ||||
-rw-r--r-- | test/files/run/t6992.check | 3 | ||||
-rw-r--r-- | test/files/run/t6992/Macros_1.scala | 75 | ||||
-rw-r--r-- | test/files/run/t6992/Test_2.scala | 12 | ||||
-rw-r--r-- | test/files/run/t8048a.check | 1 | ||||
-rw-r--r-- | test/files/run/t8048a/Macros_1.scala | 11 | ||||
-rw-r--r-- | test/files/run/t8048a/Test_2.scala | 4 | ||||
-rw-r--r-- | test/files/run/t8048b.check | 3 | ||||
-rw-r--r-- | test/files/run/t8048b/Macros_1.scala | 37 | ||||
-rw-r--r-- | test/files/run/t8048b/Test_2.scala | 5 |
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 |