Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

SI-8321 bundles can't be whitebox #3571

Merged
merged 7 commits into from

3 participants

@xeno-by
Owner

review @retronym

xeno-by added some commits
@xeno-by xeno-by minor code cleanup in the macro engine 6ce573b
@xeno-by xeno-by SI-8321 whitebox bundles are now recognized as such
whitebox.Context <: blackbox.Context, so in order to check for blackboxity
it's not enough to check whether the context used is <: blackbox.Context.
4203170
@xeno-by xeno-by added this to the 2.11.0-RC1 milestone
@retronym retronym was assigned by xeno-by
@xeno-by xeno-by bundles now reject invalid context types
Vanilla macros only allow blackbox.Context, whitebox.Context and
PrefixType refinements thereof. Bundles should behave in the same way.
31b52ed
xeno-by added some commits
@xeno-by xeno-by prohibits polymorphic bundles
It's not like they were inducing bugs, but I can't see how polymorphism
can be useful for macro bundles, hence imho it's better to reduce the
number of degrees of freedom of the system.
da1032c
@xeno-by xeno-by more helpful bundle error messages
At the moment, bundle selection mechanism is pretty picky. If a candidate
bundle's parameter isn't either blackbox.Context, whitebox.Context or
PrefixType refinement thereof, then it's not a bundle and the user
will get a generic error.

However we can be a bit more helpful and admit classes that are almost
like bundles (looksLikeMacroBundleType), have them fail isMacroBundleType,
and then emit a much prettier error message to the user that would tell them
that bundles must be monomorphic and their sole parameter should not just
be any subtype of blackbox.Context or whitebox.Context.
64edb44
@xeno-by xeno-by more tests for macro bundles
Given the recent glaring oversight in macro bundles, I have to have more
tests in order to make sure that things are going to work as they should.
fb0c25c
@xeno-by xeno-by more clean up in the macro engine
Now when SI-7507 is fixed in starr, we can actually remove a workaround
and make a 10 line reduction in the size of Resolvers.scala.
dded01b
@retronym
Owner

LGTM. Well tested and presented. Macro bundles are themselves a new feature, so the surface area for regressions is low.

@adriaanm I recommend this one for RC1, but I'll let you make the call on that as its hard to argue that this is a blocker, other than noting that by not fixing these quirks now, we're probably locked into them for 2.11.N

@xeno-by
Owner

Hey guys! Everything is green, so how about we merge? Please, let this thing in - I would be very ashamed if macro bundles will be released like that.

@adriaanm
Owner

What Jason said. Merging.

@adriaanm adriaanm merged commit efa99c0 into from
@xeno-by
Owner

Oh yeah! This made my weekend!!

@xeno-by xeno-by deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 21, 2014
  1. @xeno-by
  2. @xeno-by

    SI-8321 whitebox bundles are now recognized as such

    xeno-by authored
    whitebox.Context <: blackbox.Context, so in order to check for blackboxity
    it's not enough to check whether the context used is <: blackbox.Context.
  3. @xeno-by

    bundles now reject invalid context types

    xeno-by authored
    Vanilla macros only allow blackbox.Context, whitebox.Context and
    PrefixType refinements thereof. Bundles should behave in the same way.
  4. @xeno-by

    prohibits polymorphic bundles

    xeno-by authored
    It's not like they were inducing bugs, but I can't see how polymorphism
    can be useful for macro bundles, hence imho it's better to reduce the
    number of degrees of freedom of the system.
  5. @xeno-by

    more helpful bundle error messages

    xeno-by authored
    At the moment, bundle selection mechanism is pretty picky. If a candidate
    bundle's parameter isn't either blackbox.Context, whitebox.Context or
    PrefixType refinement thereof, then it's not a bundle and the user
    will get a generic error.
    
    However we can be a bit more helpful and admit classes that are almost
    like bundles (looksLikeMacroBundleType), have them fail isMacroBundleType,
    and then emit a much prettier error message to the user that would tell them
    that bundles must be monomorphic and their sole parameter should not just
    be any subtype of blackbox.Context or whitebox.Context.
  6. @xeno-by

    more tests for macro bundles

    xeno-by authored
    Given the recent glaring oversight in macro bundles, I have to have more
    tests in order to make sure that things are going to work as they should.
  7. @xeno-by

    more clean up in the macro engine

    xeno-by authored
    Now when SI-7507 is fixed in starr, we can actually remove a workaround
    and make a 10 line reduction in the size of Resolvers.scala.
This page is out of date. Refresh to see the latest.
Showing with 844 additions and 22 deletions.
  1. +1 −1  src/compiler/scala/reflect/macros/compiler/Errors.scala
  2. +3 −14 src/compiler/scala/reflect/macros/compiler/Resolvers.scala
  3. +1 −1  src/compiler/scala/reflect/macros/compiler/Validators.scala
  4. +9 −4 src/reflect/scala/reflect/internal/Definitions.scala
  5. +1 −1  test/files/neg/macro-bundle-abstract.check
  6. +4 −0 test/files/neg/macro-bundle-need-qualifier.check
  7. +11 −0 test/files/neg/macro-bundle-need-qualifier.scala
  8. +4 −0 test/files/neg/macro-bundle-nonpublic-c.check
  9. +11 −0 test/files/neg/macro-bundle-nonpublic-c.scala
  10. +4 −0 test/files/neg/macro-bundle-nonpublic-impl.check
  11. +11 −0 test/files/neg/macro-bundle-nonpublic-impl.scala
  12. +13 −0 test/files/neg/macro-bundle-nonstatic.check
  13. +36 −0 test/files/neg/macro-bundle-nonstatic.scala
  14. +1 −1  test/files/neg/macro-bundle-overloaded.check
  15. +19 −0 test/files/neg/macro-bundle-polymorphic.check
  16. +43 −0 test/files/neg/macro-bundle-polymorphic.scala
  17. +17 −0 test/files/neg/macro-bundle-whitebox-use-raw.check
  18. +108 −0 test/files/neg/macro-bundle-whitebox-use-raw/Macros_1.scala
  19. +19 −0 test/files/neg/macro-bundle-whitebox-use-raw/Test_2.scala
  20. +17 −0 test/files/neg/macro-bundle-whitebox-use-refined.check
  21. +108 −0 test/files/neg/macro-bundle-whitebox-use-refined/Macros_1.scala
  22. +19 −0 test/files/neg/macro-bundle-whitebox-use-refined/Test_2.scala
  23. +4 −0 test/files/neg/macro-bundle-wrongcontext-a.check
  24. +13 −0 test/files/neg/macro-bundle-wrongcontext-a.scala
  25. +4 −0 test/files/neg/macro-bundle-wrongcontext-b.check
  26. +11 −0 test/files/neg/macro-bundle-wrongcontext-b.scala
  27. +4 −0 test/files/run/macro-bundle-context-alias.check
  28. +38 −0 test/files/run/macro-bundle-context-alias/Macros_1.scala
  29. +6 −0 test/files/run/macro-bundle-context-alias/Test_2.scala
  30. +2 −0  test/files/run/macro-bundle-context-refinement.check
  31. +19 −0 test/files/run/macro-bundle-context-refinement/Macros_1.scala
  32. +4 −0 test/files/run/macro-bundle-context-refinement/Test_2.scala
  33. 0  test/files/run/{macro-bundle-whitebox.check → macro-bundle-whitebox-decl.check}
  34. 0  test/files/run/{macro-bundle-whitebox → macro-bundle-whitebox-decl}/Impls_Macros_1.scala
  35. 0  test/files/run/{macro-bundle-whitebox → macro-bundle-whitebox-decl}/Test_2.scala
  36. +5 −0 test/files/run/macro-bundle-whitebox-use-raw.check
  37. +108 −0 test/files/run/macro-bundle-whitebox-use-raw/Macros_1.scala
  38. +19 −0 test/files/run/macro-bundle-whitebox-use-raw/Test_2.scala
  39. +5 −0 test/files/run/macro-bundle-whitebox-use-refined.check
  40. +108 −0 test/files/run/macro-bundle-whitebox-use-refined/Macros_1.scala
  41. +19 −0 test/files/run/macro-bundle-whitebox-use-refined/Test_2.scala
  42. +1 −0  test/files/run/t8321.check
  43. +11 −0 test/files/run/t8321/Macros_1.scala
  44. +3 −0  test/files/run/t8321/Test_2.scala
View
2  src/compiler/scala/reflect/macros/compiler/Errors.scala
@@ -36,7 +36,7 @@ trait Errors extends Traces {
def MacroBundleNonStaticError() = bundleRefError("macro bundles must be static")
- def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter")
+ def MacroBundleWrongShapeError() = bundleRefError("macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter")
trait Error {
self: MacroImplRefCompiler =>
View
17 src/compiler/scala/reflect/macros/compiler/Resolvers.scala
@@ -25,22 +25,11 @@ trait Resolvers {
typer.silent(_.typed(markMacroImplRef(core)), reportAmbiguousErrors = false).nonEmpty
}
- lazy val macroImplRef: Tree =
+ lazy val (macroImplRef, isBlackbox, macroImplOwner, macroImpl, targs) =
typer.silent(_.typed(markMacroImplRef(untypedMacroImplRef)), reportAmbiguousErrors = false) match {
- case SilentResultValue(success) => success
+ case SilentResultValue(macroImplRef @ MacroImplReference(_, isBlackbox, owner, meth, targs)) => (macroImplRef, isBlackbox, owner, meth, targs)
+ case SilentResultValue(macroImplRef) => MacroImplReferenceWrongShapeError()
case SilentTypeError(err) => abort(err.errPos, err.errMsg)
}
-
- // FIXME: cannot write this concisely because of SI-7507
- // lazy val (_, macroImplOwner, macroImpl, macroImplTargs) =
- private lazy val dissectedMacroImplRef =
- macroImplRef match {
- case MacroImplReference(isBundle, isBlackbox, owner, meth, targs) => (isBlackbox, owner, meth, targs)
- case _ => MacroImplReferenceWrongShapeError()
- }
- lazy val isImplBlackbox = dissectedMacroImplRef._1
- lazy val macroImplOwner = dissectedMacroImplRef._2
- lazy val macroImpl = dissectedMacroImplRef._3
- lazy val targs = dissectedMacroImplRef._4
}
}
View
2  src/compiler/scala/reflect/macros/compiler/Validators.scala
@@ -147,7 +147,7 @@ trait Validators {
// had to move method's body to an object because of the recursive dependencies between sigma and param
object SigGenerator {
val cache = scala.collection.mutable.Map[Symbol, Symbol]()
- val ctxTpe = if (isImplBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
+ val ctxTpe = if (isBlackbox) BlackboxContextClass.tpe else WhiteboxContextClass.tpe
val ctxPrefix =
if (isImplMethod) singleType(NoPrefix, makeParam(nme.macroContext, macroDdef.pos, ctxTpe, SYNTHETIC))
else singleType(ThisType(macroImpl.owner), macroImpl.owner.tpe.member(nme.c))
View
13 src/reflect/scala/reflect/internal/Definitions.scala
@@ -622,14 +622,19 @@ trait Definitions extends api.StandardDefinitions {
macroBundleParamInfo(tp) != NoType
def isMacroBundleType(tp: Type) = {
- val isContextCompatible = macroBundleParamInfo(tp) != NoType
+ val isMonomorphic = tp.typeSymbol.typeParams.isEmpty
+ val isContextCompatible = isMacroContextType(macroBundleParamInfo(tp))
val hasSingleConstructor = !tp.declaration(nme.CONSTRUCTOR).isOverloaded
val nonAbstract = !tp.erasure.typeSymbol.isAbstractClass
- isContextCompatible && hasSingleConstructor && nonAbstract
+ isMonomorphic && isContextCompatible && hasSingleConstructor && nonAbstract
}
- def isBlackboxMacroBundleType(tp: Type) =
- isMacroBundleType(tp) && (macroBundleParamInfo(tp) <:< BlackboxContextClass.tpe)
+ def isBlackboxMacroBundleType(tp: Type) = {
+ val isBundle = isMacroBundleType(tp)
+ val unwrappedContext = MacroContextType.unapply(macroBundleParamInfo(tp)).getOrElse(NoType)
+ val isBlackbox = unwrappedContext =:= BlackboxContextClass.tpe
+ isBundle && isBlackbox
+ }
def isListType(tp: Type) = tp <:< classExistentialType(ListClass)
def isIterableType(tp: Type) = tp <:< classExistentialType(IterableClass)
View
2  test/files/neg/macro-bundle-abstract.check
@@ -1,4 +1,4 @@
-macro-bundle-abstract.scala:10: error: macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter
+macro-bundle-abstract.scala:10: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
def foo = macro Bundle.impl
^
one error found
View
4 test/files/neg/macro-bundle-need-qualifier.check
@@ -0,0 +1,4 @@
+macro-bundle-need-qualifier.scala:10: error: not found: value impl
+ def foo: Any = macro impl
+ ^
+one error found
View
11 test/files/neg/macro-bundle-need-qualifier.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+class Macros(val c: Context) {
+ import c.universe._
+ def impl = q"()"
+}
+
+object Macros {
+ def foo: Any = macro impl
+}
View
4 test/files/neg/macro-bundle-nonpublic-c.check
@@ -0,0 +1,4 @@
+macro-bundle-nonpublic-c.scala:6: error: private value c escapes its defining scope as part of type Macros.this.c.universe.Literal
+ def impl = q"()"
+ ^
+one error found
View
11 test/files/neg/macro-bundle-nonpublic-c.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+class Macros(c: Context) {
+ import c.universe._
+ def impl = q"()"
+}
+
+object Macros {
+ def foo: Any = macro Macros.impl
+}
View
4 test/files/neg/macro-bundle-nonpublic-impl.check
@@ -0,0 +1,4 @@
+macro-bundle-nonpublic-impl.scala:10: error: bundle implementation must be public
+ def foo: Any = macro Macros.impl
+ ^
+one error found
View
11 test/files/neg/macro-bundle-nonpublic-impl.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+class Macros(val c: Context) {
+ import c.universe._
+ private def impl = q"()"
+}
+
+object Macros {
+ def foo: Any = macro Macros.impl
+}
View
13 test/files/neg/macro-bundle-nonstatic.check
@@ -0,0 +1,13 @@
+macro-bundle-nonstatic.scala:12: error: value Bundle is not a member of object Module
+ def foo1 = macro Module.Bundle.impl
+ ^
+macro-bundle-nonstatic.scala:13: error: value Bundle is not a member of Module
+ def foo2 = macro new Module().Bundle.impl
+ ^
+macro-bundle-nonstatic.scala:17: error: macro bundles must be static
+ def foo = macro Bundle.impl
+ ^
+macro-bundle-nonstatic.scala:23: error: macro bundles must be static
+ def foo = macro Bundle.impl
+ ^
+four errors found
View
36 test/files/neg/macro-bundle-nonstatic.scala
@@ -0,0 +1,36 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.whitebox.Context
+
+class Module {
+ class Bundle(val c: Context) {
+ import c.universe._
+ def impl = q"()"
+ }
+}
+
+object Macros1 {
+ def foo1 = macro Module.Bundle.impl
+ def foo2 = macro new Module().Bundle.impl
+}
+
+object Macros2 extends Module {
+ def foo = macro Bundle.impl
+}
+
+object Macros3 {
+ val module = new Module
+ import module._
+ def foo = macro Bundle.impl
+}
+
+object Module {
+ class GoodBundle(val c: Context) {
+ import c.universe._
+ def impl = q"()"
+ }
+}
+
+object Macros4 {
+ import Module._
+ def foo: Unit = macro GoodBundle.impl
+}
View
2  test/files/neg/macro-bundle-overloaded.check
@@ -1,4 +1,4 @@
-macro-bundle-overloaded.scala:11: error: macro bundles must be concrete classes having a single constructor with a `val c: Context` parameter
+macro-bundle-overloaded.scala:11: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
def foo = macro Bundle.impl
^
one error found
View
19 test/files/neg/macro-bundle-polymorphic.check
@@ -0,0 +1,19 @@
+macro-bundle-polymorphic.scala:36: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def black1: Any = macro BlackboxBundle1.impl
+ ^
+macro-bundle-polymorphic.scala:37: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def black2: Any = macro BlackboxBundle2.impl
+ ^
+macro-bundle-polymorphic.scala:38: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def black3: Any = macro BlackboxBundle3.impl
+ ^
+macro-bundle-polymorphic.scala:40: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def white1: Any = macro WhiteboxBundle1.impl
+ ^
+macro-bundle-polymorphic.scala:41: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def white2: Any = macro WhiteboxBundle2.impl
+ ^
+macro-bundle-polymorphic.scala:42: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def white3: Any = macro WhiteboxBundle3.impl
+ ^
+6 errors found
View
43 test/files/neg/macro-bundle-polymorphic.scala
@@ -0,0 +1,43 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.{Context => BlackboxContext}
+import scala.reflect.macros.whitebox.{Context => WhiteboxContext}
+
+class BlackboxBundle1[T](val c: BlackboxContext) {
+ import c.universe._
+ def impl = q"()"
+}
+
+class BlackboxBundle2[T <: BlackboxContext](val c: T) {
+ import c.universe._
+ def impl = q"()"
+}
+
+class BlackboxBundle3[T <: BlackboxContext, U <: T](val c: U) {
+ import c.universe._
+ def impl = q"()"
+}
+
+class WhiteboxBundle1[T](val c: WhiteboxContext) {
+ import c.universe._
+ def impl = q"()"
+}
+
+class WhiteboxBundle2[T <: WhiteboxContext](val c: T) {
+ import c.universe._
+ def impl = q"()"
+}
+
+class WhiteboxBundle3[T <: WhiteboxContext, U <: T](val c: U) {
+ import c.universe._
+ def impl = q"()"
+}
+
+object Macros {
+ def black1: Any = macro BlackboxBundle1.impl
+ def black2: Any = macro BlackboxBundle2.impl
+ def black3: Any = macro BlackboxBundle3.impl
+
+ def white1: Any = macro WhiteboxBundle1.impl
+ def white2: Any = macro WhiteboxBundle2.impl
+ def white3: Any = macro WhiteboxBundle3.impl
+}
View
17 test/files/neg/macro-bundle-whitebox-use-raw.check
@@ -0,0 +1,17 @@
+Test_2.scala:2: error: value x is not a member of Any
+ println(ReturnTypeRefinement.foo.x)
+ ^
+Test_2.scala:7: error: type mismatch;
+ found : FundepMaterialization[Test.Foo,(Int, String, Boolean)]
+ required: FundepMaterialization[Test.Foo,Nothing]
+Note: (Int, String, Boolean) >: Nothing, but trait FundepMaterialization 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:13: error: I don't like classes that contain integers
+ println(implicitly[DynamicMaterialization[C1]])
+ ^
+Test_2.scala:17: error: extractor macros can only be whitebox
+ case ExtractorMacro(x) => println(x)
+ ^
+four errors found
View
108 test/files/neg/macro-bundle-whitebox-use-raw/Macros_1.scala
@@ -0,0 +1,108 @@
+import scala.reflect.macros.blackbox.Context
+import scala.language.experimental.macros
+
+// whitebox use case #1: return type refinement
+
+class ReturnTypeRefinementBundle(val c: Context) {
+ import c.universe._
+ def impl = {
+ q"""
+ trait Foo {
+ def x = 2
+ }
+ new Foo {}
+ """
+ }
+}
+
+object ReturnTypeRefinement {
+ def foo: Any = macro ReturnTypeRefinementBundle.impl
+}
+
+// whitebox use case #2: fundep materialization
+
+trait FundepMaterialization[T, U] {
+ def to(t : T) : U
+ // def from(u : U) : T
+}
+
+class FundepMaterializationBundle(val c: Context) {
+ import c.universe._
+ import definitions._
+ import Flag._
+
+ def impl[T: c.WeakTypeTag, U: c.WeakTypeTag]: c.Expr[FundepMaterialization[T, U]] = {
+ val sym = c.weakTypeOf[T].typeSymbol
+ if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class")
+ val fields = sym.info.decls.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x }
+
+ def mkTpt() = {
+ val core = Ident(TupleClass(fields.length) orElse UnitClass)
+ if (fields.length == 0) core
+ else AppliedTypeTree(core, fields map (f => TypeTree(f.info)))
+ }
+
+ def mkFrom() = {
+ if (fields.length == 0) Literal(Constant(Unit))
+ else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim))))
+ }
+
+ val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(
+ List(AppliedTypeTree(Ident(newTypeName("FundepMaterialization")), List(Ident(sym), mkTpt()))),
+ emptyValDef,
+ List(
+ DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))),
+ DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom()))))
+ c.Expr[FundepMaterialization[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), termNames.CONSTRUCTOR), List())))
+ }
+}
+
+object FundepMaterialization {
+ implicit def materializeIso[T, U]: FundepMaterialization[T, U] = macro FundepMaterializationBundle.impl[T, U]
+}
+
+// whitebox use case #3: dynamic materialization
+
+trait DynamicMaterialization[T]
+
+class C1(val x: Int)
+class C2(val x: String)
+
+trait LowPriority {
+ implicit def lessSpecific[T]: DynamicMaterialization[T] = null
+}
+
+object DynamicMaterialization extends LowPriority {
+ implicit def moreSpecific[T]: DynamicMaterialization[T] = macro DynamicMaterializationBundle.impl[T]
+}
+
+class DynamicMaterializationBundle(val c: Context) {
+ import c.universe._
+ def impl[T: c.WeakTypeTag] = {
+ val tpe = weakTypeOf[T]
+ if (tpe.members.exists(_.info =:= typeOf[Int]))
+ c.abort(c.enclosingPosition, "I don't like classes that contain integers")
+ q"new DynamicMaterialization[$tpe]{ override def toString = ${tpe.toString} }"
+ }
+}
+
+// whitebox use case #4: extractor macros
+
+object ExtractorMacro {
+ def unapply(x: Int): Any = macro ExtractorBundle.unapplyImpl
+}
+
+class ExtractorBundle(val c: Context) {
+ import c.universe._
+ def unapplyImpl(x: Tree) = {
+ q"""
+ new {
+ class Match(x: Int) {
+ def isEmpty = false
+ def get = x
+ }
+ def unapply(x: Int) = new Match(x)
+ }.unapply($x)
+ """
+ }
+}
View
19 test/files/neg/macro-bundle-whitebox-use-raw/Test_2.scala
@@ -0,0 +1,19 @@
+object Test extends App {
+ println(ReturnTypeRefinement.foo.x)
+
+ case class Foo(i: Int, s: String, b: Boolean)
+ def foo[C, L](c: C)(implicit iso: FundepMaterialization[C, L]): L = iso.to(c)
+ locally {
+ val equiv = foo(Foo(23, "foo", true))
+ def typed[T](t: => T) {}
+ typed[(Int, String, Boolean)](equiv)
+ println(equiv)
+ }
+
+ println(implicitly[DynamicMaterialization[C1]])
+ println(implicitly[DynamicMaterialization[C2]])
+
+ 42 match {
+ case ExtractorMacro(x) => println(x)
+ }
+}
View
17 test/files/neg/macro-bundle-whitebox-use-refined.check
@@ -0,0 +1,17 @@
+Test_2.scala:2: error: value x is not a member of Any
+ println(ReturnTypeRefinement.foo.x)
+ ^
+Test_2.scala:7: error: type mismatch;
+ found : FundepMaterialization[Test.Foo,(Int, String, Boolean)]
+ required: FundepMaterialization[Test.Foo,Nothing]
+Note: (Int, String, Boolean) >: Nothing, but trait FundepMaterialization 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:13: error: I don't like classes that contain integers
+ println(implicitly[DynamicMaterialization[C1]])
+ ^
+Test_2.scala:17: error: extractor macros can only be whitebox
+ case ExtractorMacro(x) => println(x)
+ ^
+four errors found
View
108 test/files/neg/macro-bundle-whitebox-use-refined/Macros_1.scala
@@ -0,0 +1,108 @@
+import scala.reflect.macros.blackbox.Context
+import scala.language.experimental.macros
+
+// whitebox use case #1: return type refinement
+
+class ReturnTypeRefinementBundle(val c: Context { type PrefixType = Nothing }) {
+ import c.universe._
+ def impl = {
+ q"""
+ trait Foo {
+ def x = 2
+ }
+ new Foo {}
+ """
+ }
+}
+
+object ReturnTypeRefinement {
+ def foo: Any = macro ReturnTypeRefinementBundle.impl
+}
+
+// whitebox use case #2: fundep materialization
+
+trait FundepMaterialization[T, U] {
+ def to(t : T) : U
+ // def from(u : U) : T
+}
+
+class FundepMaterializationBundle(val c: Context { type PrefixType = Nothing }) {
+ import c.universe._
+ import definitions._
+ import Flag._
+
+ def impl[T: c.WeakTypeTag, U: c.WeakTypeTag]: c.Expr[FundepMaterialization[T, U]] = {
+ val sym = c.weakTypeOf[T].typeSymbol
+ if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class")
+ val fields = sym.info.decls.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x }
+
+ def mkTpt() = {
+ val core = Ident(TupleClass(fields.length) orElse UnitClass)
+ if (fields.length == 0) core
+ else AppliedTypeTree(core, fields map (f => TypeTree(f.info)))
+ }
+
+ def mkFrom() = {
+ if (fields.length == 0) Literal(Constant(Unit))
+ else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim))))
+ }
+
+ val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(
+ List(AppliedTypeTree(Ident(newTypeName("FundepMaterialization")), List(Ident(sym), mkTpt()))),
+ emptyValDef,
+ List(
+ DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))),
+ DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom()))))
+ c.Expr[FundepMaterialization[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), termNames.CONSTRUCTOR), List())))
+ }
+}
+
+object FundepMaterialization {
+ implicit def materializeIso[T, U]: FundepMaterialization[T, U] = macro FundepMaterializationBundle.impl[T, U]
+}
+
+// whitebox use case #3: dynamic materialization
+
+trait DynamicMaterialization[T]
+
+class C1(val x: Int)
+class C2(val x: String)
+
+trait LowPriority {
+ implicit def lessSpecific[T]: DynamicMaterialization[T] = null
+}
+
+object DynamicMaterialization extends LowPriority {
+ implicit def moreSpecific[T]: DynamicMaterialization[T] = macro DynamicMaterializationBundle.impl[T]
+}
+
+class DynamicMaterializationBundle(val c: Context { type PrefixType = Nothing }) {
+ import c.universe._
+ def impl[T: c.WeakTypeTag] = {
+ val tpe = weakTypeOf[T]
+ if (tpe.members.exists(_.info =:= typeOf[Int]))
+ c.abort(c.enclosingPosition, "I don't like classes that contain integers")
+ q"new DynamicMaterialization[$tpe]{ override def toString = ${tpe.toString} }"
+ }
+}
+
+// whitebox use case #4: extractor macros
+
+object ExtractorMacro {
+ def unapply(x: Int): Any = macro ExtractorBundle.unapplyImpl
+}
+
+class ExtractorBundle(val c: Context { type PrefixType = Nothing }) {
+ import c.universe._
+ def unapplyImpl(x: Tree) = {
+ q"""
+ new {
+ class Match(x: Int) {
+ def isEmpty = false
+ def get = x
+ }
+ def unapply(x: Int) = new Match(x)
+ }.unapply($x)
+ """
+ }
+}
View
19 test/files/neg/macro-bundle-whitebox-use-refined/Test_2.scala
@@ -0,0 +1,19 @@
+object Test extends App {
+ println(ReturnTypeRefinement.foo.x)
+
+ case class Foo(i: Int, s: String, b: Boolean)
+ def foo[C, L](c: C)(implicit iso: FundepMaterialization[C, L]): L = iso.to(c)
+ locally {
+ val equiv = foo(Foo(23, "foo", true))
+ def typed[T](t: => T) {}
+ typed[(Int, String, Boolean)](equiv)
+ println(equiv)
+ }
+
+ println(implicitly[DynamicMaterialization[C1]])
+ println(implicitly[DynamicMaterialization[C2]])
+
+ 42 match {
+ case ExtractorMacro(x) => println(x)
+ }
+}
View
4 test/files/neg/macro-bundle-wrongcontext-a.check
@@ -0,0 +1,4 @@
+macro-bundle-wrongcontext-a.scala:12: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def foo: Any = macro Bundle.impl
+ ^
+one error found
View
13 test/files/neg/macro-bundle-wrongcontext-a.scala
@@ -0,0 +1,13 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+abstract class MyContext extends Context
+
+class Bundle(val c: MyContext) {
+ import c.universe._
+ def impl = q"()"
+}
+
+object Macros {
+ def foo: Any = macro Bundle.impl
+}
View
4 test/files/neg/macro-bundle-wrongcontext-b.check
@@ -0,0 +1,4 @@
+macro-bundle-wrongcontext-b.scala:10: error: macro bundles must be concrete monomorphic classes having a single constructor with a `val c: Context` parameter
+ def foo: Any = macro Bundle.impl
+ ^
+one error found
View
11 test/files/neg/macro-bundle-wrongcontext-b.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+class Bundle(val c: Context { type Foo <: Int }) {
+ import c.universe._
+ def impl = q"()"
+}
+
+object Macros {
+ def foo: Any = macro Bundle.impl
+}
View
4 test/files/run/macro-bundle-context-alias.check
@@ -0,0 +1,4 @@
+C
+C
+C
+C
View
38 test/files/run/macro-bundle-context-alias/Macros_1.scala
@@ -0,0 +1,38 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.{Context => BlackboxContext}
+import scala.reflect.macros.whitebox.{Context => WhiteboxContext}
+
+object Module {
+ type BBC = BlackboxContext
+ type RBBC = BBC { type PrefixType = C }
+ type WBC = WhiteboxContext
+ type RWBC = WBC { type PrefixType = C }
+
+ class BlackboxBundle(val c: BBC) {
+ import c.universe._
+ def impl = q"${c.prefix}"
+ }
+
+ class RefinedBlackboxBundle(val c: RBBC) {
+ import c.universe._
+ def impl = reify(c.prefix.splice)
+ }
+
+ class WhiteboxBundle(val c: WBC) {
+ import c.universe._
+ def impl = q"${c.prefix}"
+ }
+
+ class RefinedWhiteboxBundle(val c: RWBC) {
+ import c.universe._
+ def impl = reify(c.prefix.splice)
+ }
+}
+
+class C {
+ def blackbox: C = macro Module.BlackboxBundle.impl
+ def refinedBlackbox: C = macro Module.RefinedBlackboxBundle.impl
+ def whitebox: C = macro Module.WhiteboxBundle.impl
+ def refinedWhitebox: C = macro Module.RefinedWhiteboxBundle.impl
+ override def toString = "C"
+}
View
6 test/files/run/macro-bundle-context-alias/Test_2.scala
@@ -0,0 +1,6 @@
+object Test extends App {
+ println(new C().blackbox)
+ println(new C().refinedBlackbox)
+ println(new C().whitebox)
+ println(new C().refinedWhitebox)
+}
View
2  test/files/run/macro-bundle-context-refinement.check
@@ -0,0 +1,2 @@
+C
+C
View
19 test/files/run/macro-bundle-context-refinement/Macros_1.scala
@@ -0,0 +1,19 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.{Context => BlackboxContext}
+import scala.reflect.macros.whitebox.{Context => WhiteboxContext}
+
+class BlackboxBundle(val c: BlackboxContext { type PrefixType = C }) {
+ import c.universe._
+ def impl = reify(c.prefix.splice)
+}
+
+class WhiteboxBundle(val c: WhiteboxContext { type PrefixType = C }) {
+ import c.universe._
+ def impl = reify(c.prefix.splice)
+}
+
+class C {
+ def blackbox: C = macro BlackboxBundle.impl
+ def whitebox: C = macro WhiteboxBundle.impl
+ override def toString = "C"
+}
View
4 test/files/run/macro-bundle-context-refinement/Test_2.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ println(new C().blackbox)
+ println(new C().whitebox)
+}
View
0  test/files/run/macro-bundle-whitebox.check → test/files/run/macro-bundle-whitebox-decl.check
File renamed without changes
View
0  ...es/run/macro-bundle-whitebox/Impls_Macros_1.scala → ...n/macro-bundle-whitebox-decl/Impls_Macros_1.scala
File renamed without changes
View
0  test/files/run/macro-bundle-whitebox/Test_2.scala → ...files/run/macro-bundle-whitebox-decl/Test_2.scala
File renamed without changes
View
5 test/files/run/macro-bundle-whitebox-use-raw.check
@@ -0,0 +1,5 @@
+2
+(23,foo,true)
+null
+C2
+42
View
108 test/files/run/macro-bundle-whitebox-use-raw/Macros_1.scala
@@ -0,0 +1,108 @@
+import scala.reflect.macros.whitebox.Context
+import scala.language.experimental.macros
+
+// whitebox use case #1: return type refinement
+
+class ReturnTypeRefinementBundle(val c: Context) {
+ import c.universe._
+ def impl = {
+ q"""
+ trait Foo {
+ def x = 2
+ }
+ new Foo {}
+ """
+ }
+}
+
+object ReturnTypeRefinement {
+ def foo: Any = macro ReturnTypeRefinementBundle.impl
+}
+
+// whitebox use case #2: fundep materialization
+
+trait FundepMaterialization[T, U] {
+ def to(t : T) : U
+ // def from(u : U) : T
+}
+
+class FundepMaterializationBundle(val c: Context) {
+ import c.universe._
+ import definitions._
+ import Flag._
+
+ def impl[T: c.WeakTypeTag, U: c.WeakTypeTag]: c.Expr[FundepMaterialization[T, U]] = {
+ val sym = c.weakTypeOf[T].typeSymbol
+ if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class")
+ val fields = sym.info.decls.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x }
+
+ def mkTpt() = {
+ val core = Ident(TupleClass(fields.length) orElse UnitClass)
+ if (fields.length == 0) core
+ else AppliedTypeTree(core, fields map (f => TypeTree(f.info)))
+ }
+
+ def mkFrom() = {
+ if (fields.length == 0) Literal(Constant(Unit))
+ else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim))))
+ }
+
+ val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(
+ List(AppliedTypeTree(Ident(newTypeName("FundepMaterialization")), List(Ident(sym), mkTpt()))),
+ emptyValDef,
+ List(
+ DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))),
+ DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom()))))
+ c.Expr[FundepMaterialization[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), termNames.CONSTRUCTOR), List())))
+ }
+}
+
+object FundepMaterialization {
+ implicit def materializeIso[T, U]: FundepMaterialization[T, U] = macro FundepMaterializationBundle.impl[T, U]
+}
+
+// whitebox use case #3: dynamic materialization
+
+trait DynamicMaterialization[T]
+
+class C1(val x: Int)
+class C2(val x: String)
+
+trait LowPriority {
+ implicit def lessSpecific[T]: DynamicMaterialization[T] = null
+}
+
+object DynamicMaterialization extends LowPriority {
+ implicit def moreSpecific[T]: DynamicMaterialization[T] = macro DynamicMaterializationBundle.impl[T]
+}
+
+class DynamicMaterializationBundle(val c: Context) {
+ import c.universe._
+ def impl[T: c.WeakTypeTag] = {
+ val tpe = weakTypeOf[T]
+ if (tpe.members.exists(_.info =:= typeOf[Int]))
+ c.abort(c.enclosingPosition, "I don't like classes that contain integers")
+ q"new DynamicMaterialization[$tpe]{ override def toString = ${tpe.toString} }"
+ }
+}
+
+// whitebox use case #4: extractor macros
+
+object ExtractorMacro {
+ def unapply(x: Int): Any = macro ExtractorBundle.unapplyImpl
+}
+
+class ExtractorBundle(val c: Context) {
+ import c.universe._
+ def unapplyImpl(x: Tree) = {
+ q"""
+ new {
+ class Match(x: Int) {
+ def isEmpty = false
+ def get = x
+ }
+ def unapply(x: Int) = new Match(x)
+ }.unapply($x)
+ """
+ }
+}
View
19 test/files/run/macro-bundle-whitebox-use-raw/Test_2.scala
@@ -0,0 +1,19 @@
+object Test extends App {
+ println(ReturnTypeRefinement.foo.x)
+
+ case class Foo(i: Int, s: String, b: Boolean)
+ def foo[C, L](c: C)(implicit iso: FundepMaterialization[C, L]): L = iso.to(c)
+ locally {
+ val equiv = foo(Foo(23, "foo", true))
+ def typed[T](t: => T) {}
+ typed[(Int, String, Boolean)](equiv)
+ println(equiv)
+ }
+
+ println(implicitly[DynamicMaterialization[C1]])
+ println(implicitly[DynamicMaterialization[C2]])
+
+ 42 match {
+ case ExtractorMacro(x) => println(x)
+ }
+}
View
5 test/files/run/macro-bundle-whitebox-use-refined.check
@@ -0,0 +1,5 @@
+2
+(23,foo,true)
+null
+C2
+42
View
108 test/files/run/macro-bundle-whitebox-use-refined/Macros_1.scala
@@ -0,0 +1,108 @@
+import scala.reflect.macros.whitebox.Context
+import scala.language.experimental.macros
+
+// whitebox use case #1: return type refinement
+
+class ReturnTypeRefinementBundle(val c: Context) {
+ import c.universe._
+ def impl = {
+ q"""
+ trait Foo {
+ def x = 2
+ }
+ new Foo {}
+ """
+ }
+}
+
+object ReturnTypeRefinement {
+ def foo: Any = macro ReturnTypeRefinementBundle.impl
+}
+
+// whitebox use case #2: fundep materialization
+
+trait FundepMaterialization[T, U] {
+ def to(t : T) : U
+ // def from(u : U) : T
+}
+
+class FundepMaterializationBundle(val c: Context) {
+ import c.universe._
+ import definitions._
+ import Flag._
+
+ def impl[T: c.WeakTypeTag, U: c.WeakTypeTag]: c.Expr[FundepMaterialization[T, U]] = {
+ val sym = c.weakTypeOf[T].typeSymbol
+ if (!sym.isClass || !sym.asClass.isCaseClass) c.abort(c.enclosingPosition, s"$sym is not a case class")
+ val fields = sym.info.decls.toList.collect{ case x: TermSymbol if x.isVal && x.isCaseAccessor => x }
+
+ def mkTpt() = {
+ val core = Ident(TupleClass(fields.length) orElse UnitClass)
+ if (fields.length == 0) core
+ else AppliedTypeTree(core, fields map (f => TypeTree(f.info)))
+ }
+
+ def mkFrom() = {
+ if (fields.length == 0) Literal(Constant(Unit))
+ else Apply(Ident(newTermName("Tuple" + fields.length)), fields map (f => Select(Ident(newTermName("f")), newTermName(f.name.toString.trim))))
+ }
+
+ val evidenceClass = ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(
+ List(AppliedTypeTree(Ident(newTypeName("FundepMaterialization")), List(Ident(sym), mkTpt()))),
+ emptyValDef,
+ List(
+ DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(typeNames.EMPTY), typeNames.EMPTY), termNames.CONSTRUCTOR), List())), Literal(Constant(())))),
+ DefDef(Modifiers(), newTermName("to"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("f"), Ident(sym), EmptyTree))), TypeTree(), mkFrom()))))
+ c.Expr[FundepMaterialization[T, U]](Block(List(evidenceClass), Apply(Select(New(Ident(newTypeName("$anon"))), termNames.CONSTRUCTOR), List())))
+ }
+}
+
+object FundepMaterialization {
+ implicit def materializeIso[T, U]: FundepMaterialization[T, U] = macro FundepMaterializationBundle.impl[T, U]
+}
+
+// whitebox use case #3: dynamic materialization
+
+trait DynamicMaterialization[T]
+
+class C1(val x: Int)
+class C2(val x: String)
+
+trait LowPriority {
+ implicit def lessSpecific[T]: DynamicMaterialization[T] = null
+}
+
+object DynamicMaterialization extends LowPriority {
+ implicit def moreSpecific[T]: DynamicMaterialization[T] = macro DynamicMaterializationBundle.impl[T]
+}
+
+class DynamicMaterializationBundle(val c: Context) {
+ import c.universe._
+ def impl[T: c.WeakTypeTag] = {
+ val tpe = weakTypeOf[T]
+ if (tpe.members.exists(_.info =:= typeOf[Int]))
+ c.abort(c.enclosingPosition, "I don't like classes that contain integers")
+ q"new DynamicMaterialization[$tpe]{ override def toString = ${tpe.toString} }"
+ }
+}
+
+// whitebox use case #4: extractor macros
+
+object ExtractorMacro {
+ def unapply(x: Int): Any = macro ExtractorBundle.unapplyImpl
+}
+
+class ExtractorBundle(val c: Context) {
+ import c.universe._
+ def unapplyImpl(x: Tree) = {
+ q"""
+ new {
+ class Match(x: Int) {
+ def isEmpty = false
+ def get = x
+ }
+ def unapply(x: Int) = new Match(x)
+ }.unapply($x)
+ """
+ }
+}
View
19 test/files/run/macro-bundle-whitebox-use-refined/Test_2.scala
@@ -0,0 +1,19 @@
+object Test extends App {
+ println(ReturnTypeRefinement.foo.x)
+
+ case class Foo(i: Int, s: String, b: Boolean)
+ def foo[C, L](c: C)(implicit iso: FundepMaterialization[C, L]): L = iso.to(c)
+ locally {
+ val equiv = foo(Foo(23, "foo", true))
+ def typed[T](t: => T) {}
+ typed[(Int, String, Boolean)](equiv)
+ println(equiv)
+ }
+
+ println(implicitly[DynamicMaterialization[C1]])
+ println(implicitly[DynamicMaterialization[C2]])
+
+ 42 match {
+ case ExtractorMacro(x) => println(x)
+ }
+}
View
1  test/files/run/t8321.check
@@ -0,0 +1 @@
+2
View
11 test/files/run/t8321/Macros_1.scala
@@ -0,0 +1,11 @@
+import scala.reflect.macros.whitebox._
+import scala.language.experimental.macros
+
+class Bundle(val c: Context) {
+ import c.universe._
+ def impl = q"new { val x = 2 }"
+}
+
+object Macros {
+ def foo: Any = macro Bundle.impl
+}
View
3  test/files/run/t8321/Test_2.scala
@@ -0,0 +1,3 @@
+object Test extends App {
+ println(Macros.foo.x)
+}
Something went wrong with that request. Please try again.