Permalink
Browse files

[backport] no longer warns on calls to vampire macros

As eloquently elaborated and cleverly named by Travis Brown, macros
defined in structural types are useful:
http://meta.plasm.us/posts/2013/07/12/vampire-methods-for-structural-types/.

However, since such macros are on the intersection of a number of language
features, as usual, there are bugs.

This commit fixes an unwanted interaction of macros defined in structural
types with the scala.language.reflectiveCalls guard. Since macro calls
aren't going to be carried to runtime, there's no need to warn about them.
1 parent b66a396 commit db300d4d9e3eb1de4245ad3aeb686eb5302ae152 @xeno-by xeno-by committed Aug 31, 2013
@@ -4934,7 +4934,7 @@ trait Typers extends Modes with Adaptations with Tags {
if (tree.isInstanceOf[PostfixSelect])
checkFeature(tree.pos, PostfixOpsFeature, name.decode)
- if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember)
+ if (tree1.symbol != null && tree1.symbol.isOnlyRefinementMember && !tree1.symbol.isMacro)
checkFeature(tree1.pos, ReflectiveCallsFeature, tree1.symbol.toString)
if (qual1.hasSymbolWhich(_.isRootPackage)) treeCopy.Ident(tree1, name)
@@ -0,0 +1,2 @@
+2
+3
@@ -0,0 +1 @@
+-Xfatal-warnings
@@ -0,0 +1,52 @@
+// As per http://meta.plasm.us/posts/2013/08/31/feeding-our-vampires/
+
+import scala.annotation.StaticAnnotation
+import scala.reflect.macros.Context
+import scala.language.experimental.macros
+
+class body(tree: Any) extends StaticAnnotation
+
+object Macros {
+ def selFieldImpl(c: Context) = {
+ import c.universe._
+ val field = c.macroApplication.symbol
+ val bodyAnn = field.annotations.filter(_.tpe <:< typeOf[body]).head
+ c.Expr[Any](bodyAnn.scalaArgs.head)
+ }
+
+ def mkObjectImpl(c: Context)(xs: c.Expr[Any]*) = {
+ import c.universe._
+ import Flag._
+ // val kvps = xs.toList map { case q"${_}(${Literal(Constant(name: String))}).->[${_}]($value)" => name -> value }
+ val kvps = xs.map(_.tree).toList map { case Apply(TypeApply(Select(Apply(_, List(Literal(Constant(name: String)))), _), _), List(value)) => name -> value }
+ // val fields = kvps map { case (k, v) => q"@body($v) def ${TermName(k)} = macro Macros.selFieldImpl" }
+ val fields = kvps map { case (k, v) => DefDef(
+ Modifiers(MACRO, tpnme.EMPTY, List(Apply(Select(New(Ident(newTypeName("body"))), nme.CONSTRUCTOR), List(v)))),
+ newTermName(k), Nil, Nil, TypeTree(), Select(Ident(newTermName("Macros")), newTermName("selFieldImpl"))) }
+ // q"import scala.language.experimental.macros; class Workaround { ..$fields }; new Workaround{}"
+ c.Expr[Any](Block(
+ List(
+ Import(Select(Select(Ident(newTermName("scala")), newTermName("language")), newTermName("experimental")), List(ImportSelector(newTermName("macros"), 51, newTermName("macros"), 51))),
+ ClassDef(
+ NoMods, newTypeName("Workaround"), Nil,
+ Template(
+ List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef,
+ DefDef(
+ NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(),
+ Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(()))))
+ +: fields)),
+ ClassDef(
+ Modifiers(FINAL), newTypeName("$anon"), Nil,
+ Template(
+ List(Ident(newTypeName("Workaround"))), emptyValDef,
+ List(
+ DefDef(
+ NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(),
+ Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))))))),
+ Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List())))
+ }
+}
+
+object mkObject {
+ def apply(xs: Any*) = macro Macros.mkObjectImpl
+}
@@ -0,0 +1,6 @@
+object Test extends App {
+ val foo = mkObject("x" -> "2", "y" -> 3)
+ println(foo.x)
+ println(foo.y)
+ // println(foo.z) => will result in a compilation error
+}

0 comments on commit db300d4

Please sign in to comment.