From 42031708b25f7252fab9992fe444651f8c141e40 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 21 Feb 2014 21:25:29 +0100 Subject: 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. --- test/files/neg/macro-bundle-whitebox-use.check | 17 ++++ .../neg/macro-bundle-whitebox-use/Macros_1.scala | 108 +++++++++++++++++++++ .../neg/macro-bundle-whitebox-use/Test_2.scala | 19 ++++ test/files/run/macro-bundle-whitebox-decl.check | 6 ++ .../Impls_Macros_1.scala | 26 +++++ .../run/macro-bundle-whitebox-decl/Test_2.scala | 8 ++ test/files/run/macro-bundle-whitebox-use.check | 5 + .../run/macro-bundle-whitebox-use/Macros_1.scala | 108 +++++++++++++++++++++ .../run/macro-bundle-whitebox-use/Test_2.scala | 19 ++++ test/files/run/macro-bundle-whitebox.check | 6 -- .../run/macro-bundle-whitebox/Impls_Macros_1.scala | 26 ----- test/files/run/macro-bundle-whitebox/Test_2.scala | 8 -- test/files/run/t8321.check | 1 + test/files/run/t8321/Macros_1.scala | 11 +++ test/files/run/t8321/Test_2.scala | 3 + 15 files changed, 331 insertions(+), 40 deletions(-) create mode 100644 test/files/neg/macro-bundle-whitebox-use.check create mode 100644 test/files/neg/macro-bundle-whitebox-use/Macros_1.scala create mode 100644 test/files/neg/macro-bundle-whitebox-use/Test_2.scala create mode 100644 test/files/run/macro-bundle-whitebox-decl.check create mode 100644 test/files/run/macro-bundle-whitebox-decl/Impls_Macros_1.scala create mode 100644 test/files/run/macro-bundle-whitebox-decl/Test_2.scala create mode 100644 test/files/run/macro-bundle-whitebox-use.check create mode 100644 test/files/run/macro-bundle-whitebox-use/Macros_1.scala create mode 100644 test/files/run/macro-bundle-whitebox-use/Test_2.scala delete mode 100644 test/files/run/macro-bundle-whitebox.check delete mode 100644 test/files/run/macro-bundle-whitebox/Impls_Macros_1.scala delete mode 100644 test/files/run/macro-bundle-whitebox/Test_2.scala create mode 100644 test/files/run/t8321.check create mode 100644 test/files/run/t8321/Macros_1.scala create mode 100644 test/files/run/t8321/Test_2.scala (limited to 'test/files') diff --git a/test/files/neg/macro-bundle-whitebox-use.check b/test/files/neg/macro-bundle-whitebox-use.check new file mode 100644 index 0000000000..5792e317a6 --- /dev/null +++ b/test/files/neg/macro-bundle-whitebox-use.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 diff --git a/test/files/neg/macro-bundle-whitebox-use/Macros_1.scala b/test/files/neg/macro-bundle-whitebox-use/Macros_1.scala new file mode 100644 index 0000000000..61bf73e481 --- /dev/null +++ b/test/files/neg/macro-bundle-whitebox-use/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) + """ + } +} diff --git a/test/files/neg/macro-bundle-whitebox-use/Test_2.scala b/test/files/neg/macro-bundle-whitebox-use/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/test/files/neg/macro-bundle-whitebox-use/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) + } +} diff --git a/test/files/run/macro-bundle-whitebox-decl.check b/test/files/run/macro-bundle-whitebox-decl.check new file mode 100644 index 0000000000..37c8eaf27a --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-decl.check @@ -0,0 +1,6 @@ +() +Int +() +true +IntInt +true diff --git a/test/files/run/macro-bundle-whitebox-decl/Impls_Macros_1.scala b/test/files/run/macro-bundle-whitebox-decl/Impls_Macros_1.scala new file mode 100644 index 0000000000..5e1b11895d --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-decl/Impls_Macros_1.scala @@ -0,0 +1,26 @@ +import scala.language.experimental.macros +import scala.reflect.macros.whitebox.Context + +class Impl(val c: Context) { + def mono = { import c.universe._; c.Expr[Unit](q"()") } + def poly[T: c.WeakTypeTag] = { import c.universe._; c.Expr[String](q"${c.weakTypeOf[T].toString}") } + def weird = macro mono +} + +object Macros { + def mono = macro Impl.mono + def poly[T] = macro Impl.poly[T] +} + +package pkg { + class Impl(val c: Context) { + def mono = { import c.universe._; c.Expr[Boolean](q"true") } + def poly[T: c.WeakTypeTag] = { import c.universe._; c.Expr[String](q"${c.weakTypeOf[T].toString + c.weakTypeOf[T].toString}") } + def weird = macro mono + } + + object Macros { + def mono = macro Impl.mono + def poly[T] = macro Impl.poly[T] + } +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-whitebox-decl/Test_2.scala b/test/files/run/macro-bundle-whitebox-decl/Test_2.scala new file mode 100644 index 0000000000..195fb49262 --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-decl/Test_2.scala @@ -0,0 +1,8 @@ +object Test extends App { + println(Macros.mono) + println(Macros.poly[Int]) + println(new Impl(???).weird) + println(pkg.Macros.mono) + println(pkg.Macros.poly[Int]) + println(new pkg.Impl(???).weird) +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-whitebox-use.check b/test/files/run/macro-bundle-whitebox-use.check new file mode 100644 index 0000000000..5679c5faba --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-use.check @@ -0,0 +1,5 @@ +2 +(23,foo,true) +null +C2 +42 diff --git a/test/files/run/macro-bundle-whitebox-use/Macros_1.scala b/test/files/run/macro-bundle-whitebox-use/Macros_1.scala new file mode 100644 index 0000000000..de1863418e --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-use/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) + """ + } +} diff --git a/test/files/run/macro-bundle-whitebox-use/Test_2.scala b/test/files/run/macro-bundle-whitebox-use/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-use/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) + } +} diff --git a/test/files/run/macro-bundle-whitebox.check b/test/files/run/macro-bundle-whitebox.check deleted file mode 100644 index 37c8eaf27a..0000000000 --- a/test/files/run/macro-bundle-whitebox.check +++ /dev/null @@ -1,6 +0,0 @@ -() -Int -() -true -IntInt -true diff --git a/test/files/run/macro-bundle-whitebox/Impls_Macros_1.scala b/test/files/run/macro-bundle-whitebox/Impls_Macros_1.scala deleted file mode 100644 index 5e1b11895d..0000000000 --- a/test/files/run/macro-bundle-whitebox/Impls_Macros_1.scala +++ /dev/null @@ -1,26 +0,0 @@ -import scala.language.experimental.macros -import scala.reflect.macros.whitebox.Context - -class Impl(val c: Context) { - def mono = { import c.universe._; c.Expr[Unit](q"()") } - def poly[T: c.WeakTypeTag] = { import c.universe._; c.Expr[String](q"${c.weakTypeOf[T].toString}") } - def weird = macro mono -} - -object Macros { - def mono = macro Impl.mono - def poly[T] = macro Impl.poly[T] -} - -package pkg { - class Impl(val c: Context) { - def mono = { import c.universe._; c.Expr[Boolean](q"true") } - def poly[T: c.WeakTypeTag] = { import c.universe._; c.Expr[String](q"${c.weakTypeOf[T].toString + c.weakTypeOf[T].toString}") } - def weird = macro mono - } - - object Macros { - def mono = macro Impl.mono - def poly[T] = macro Impl.poly[T] - } -} \ No newline at end of file diff --git a/test/files/run/macro-bundle-whitebox/Test_2.scala b/test/files/run/macro-bundle-whitebox/Test_2.scala deleted file mode 100644 index 195fb49262..0000000000 --- a/test/files/run/macro-bundle-whitebox/Test_2.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test extends App { - println(Macros.mono) - println(Macros.poly[Int]) - println(new Impl(???).weird) - println(pkg.Macros.mono) - println(pkg.Macros.poly[Int]) - println(new pkg.Impl(???).weird) -} \ No newline at end of file diff --git a/test/files/run/t8321.check b/test/files/run/t8321.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t8321.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t8321/Macros_1.scala b/test/files/run/t8321/Macros_1.scala new file mode 100644 index 0000000000..70e44fc761 --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/test/files/run/t8321/Test_2.scala b/test/files/run/t8321/Test_2.scala new file mode 100644 index 0000000000..82ec32384e --- /dev/null +++ b/test/files/run/t8321/Test_2.scala @@ -0,0 +1,3 @@ +object Test extends App { + println(Macros.foo.x) +} \ No newline at end of file -- cgit v1.2.3 From 31b52ed651e58a68d478aabe41c62287b6d4d718 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 21 Feb 2014 21:48:16 +0100 Subject: 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. --- src/reflect/scala/reflect/internal/Definitions.scala | 14 +++++--------- test/files/neg/macro-bundle-wrongcontext-a.check | 4 ++++ test/files/neg/macro-bundle-wrongcontext-a.scala | 13 +++++++++++++ test/files/neg/macro-bundle-wrongcontext-b.check | 4 ++++ test/files/neg/macro-bundle-wrongcontext-b.scala | 11 +++++++++++ 5 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 test/files/neg/macro-bundle-wrongcontext-a.check create mode 100644 test/files/neg/macro-bundle-wrongcontext-a.scala create mode 100644 test/files/neg/macro-bundle-wrongcontext-b.check create mode 100644 test/files/neg/macro-bundle-wrongcontext-b.scala (limited to 'test/files') diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 20f6d51fcf..f8f7673530 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -609,12 +609,8 @@ trait Definitions extends api.StandardDefinitions { private def macroBundleParamInfo(tp: Type) = { val ctor = tp.erasure.typeSymbol.primaryConstructor ctor.paramss match { - case List(List(c)) => - val sym = c.info.typeSymbol - val isContextCompatible = sym.isNonBottomSubClass(BlackboxContextClass) || sym.isNonBottomSubClass(WhiteboxContextClass) - if (isContextCompatible) c.info else NoType - case _ => - NoType + case List(List(c)) => if (isMacroContextType(c.info)) c.info else NoType + case _ => NoType } } @@ -630,9 +626,9 @@ trait Definitions extends api.StandardDefinitions { def isBlackboxMacroBundleType(tp: Type) = { val isBundle = isMacroBundleType(tp) - val isBlackbox = (macroBundleParamInfo(tp) <:< BlackboxContextClass.tpe) - val notWhitebox = !(macroBundleParamInfo(tp) <:< WhiteboxContextClass.tpe) - isBundle && isBlackbox && notWhitebox + val unwrappedContext = MacroContextType.unapply(macroBundleParamInfo(tp)).getOrElse(NoType) + val isBlackbox = unwrappedContext =:= BlackboxContextClass.tpe + isBundle && isBlackbox } def isListType(tp: Type) = tp <:< classExistentialType(ListClass) diff --git a/test/files/neg/macro-bundle-wrongcontext-a.check b/test/files/neg/macro-bundle-wrongcontext-a.check new file mode 100644 index 0000000000..7a48dbfd3a --- /dev/null +++ b/test/files/neg/macro-bundle-wrongcontext-a.check @@ -0,0 +1,4 @@ +macro-bundle-wrongcontext-a.scala:12: error: not found: value Bundle + def foo: Any = macro Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-wrongcontext-a.scala b/test/files/neg/macro-bundle-wrongcontext-a.scala new file mode 100644 index 0000000000..ed566fd977 --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/test/files/neg/macro-bundle-wrongcontext-b.check b/test/files/neg/macro-bundle-wrongcontext-b.check new file mode 100644 index 0000000000..9c94c3bc34 --- /dev/null +++ b/test/files/neg/macro-bundle-wrongcontext-b.check @@ -0,0 +1,4 @@ +macro-bundle-wrongcontext-b.scala:10: error: not found: value Bundle + def foo: Any = macro Bundle.impl + ^ +one error found diff --git a/test/files/neg/macro-bundle-wrongcontext-b.scala b/test/files/neg/macro-bundle-wrongcontext-b.scala new file mode 100644 index 0000000000..0b4ff7e17c --- /dev/null +++ b/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 +} \ No newline at end of file -- cgit v1.2.3 From da1032caa4ee4c780b5fff3056dd816623a53737 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 21 Feb 2014 22:18:09 +0100 Subject: 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. --- .../scala/reflect/macros/compiler/Errors.scala | 2 +- .../scala/reflect/internal/Definitions.scala | 3 +- test/files/neg/macro-bundle-abstract.check | 2 +- test/files/neg/macro-bundle-overloaded.check | 2 +- test/files/neg/macro-bundle-polymorphic.check | 19 ++++++++++ test/files/neg/macro-bundle-polymorphic.scala | 43 ++++++++++++++++++++++ 6 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 test/files/neg/macro-bundle-polymorphic.check create mode 100644 test/files/neg/macro-bundle-polymorphic.scala (limited to 'test/files') diff --git a/src/compiler/scala/reflect/macros/compiler/Errors.scala b/src/compiler/scala/reflect/macros/compiler/Errors.scala index 490ab3657a..cc4508e696 100644 --- a/src/compiler/scala/reflect/macros/compiler/Errors.scala +++ b/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 => diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index f8f7673530..62e98829b7 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -618,10 +618,11 @@ trait Definitions extends api.StandardDefinitions { macroBundleParamInfo(tp) != NoType def isMacroBundleType(tp: Type) = { + val isMonomorphic = tp.typeSymbol.typeParams.isEmpty val isContextCompatible = macroBundleParamInfo(tp) != NoType val hasSingleConstructor = !tp.declaration(nme.CONSTRUCTOR).isOverloaded val nonAbstract = !tp.erasure.typeSymbol.isAbstractClass - isContextCompatible && hasSingleConstructor && nonAbstract + isMonomorphic && isContextCompatible && hasSingleConstructor && nonAbstract } def isBlackboxMacroBundleType(tp: Type) = { diff --git a/test/files/neg/macro-bundle-abstract.check b/test/files/neg/macro-bundle-abstract.check index 3afd079521..1e51a00d05 100644 --- a/test/files/neg/macro-bundle-abstract.check +++ b/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 diff --git a/test/files/neg/macro-bundle-overloaded.check b/test/files/neg/macro-bundle-overloaded.check index fc94ff0000..499068aaa8 100644 --- a/test/files/neg/macro-bundle-overloaded.check +++ b/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 diff --git a/test/files/neg/macro-bundle-polymorphic.check b/test/files/neg/macro-bundle-polymorphic.check new file mode 100644 index 0000000000..dc6f540f77 --- /dev/null +++ b/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: not found: value BlackboxBundle2 + def black2: Any = macro BlackboxBundle2.impl + ^ +macro-bundle-polymorphic.scala:38: error: not found: value BlackboxBundle3 + 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: not found: value WhiteboxBundle2 + def white2: Any = macro WhiteboxBundle2.impl + ^ +macro-bundle-polymorphic.scala:42: error: not found: value WhiteboxBundle3 + def white3: Any = macro WhiteboxBundle3.impl + ^ +6 errors found diff --git a/test/files/neg/macro-bundle-polymorphic.scala b/test/files/neg/macro-bundle-polymorphic.scala new file mode 100644 index 0000000000..2ba91aa0c5 --- /dev/null +++ b/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 +} \ No newline at end of file -- cgit v1.2.3 From 64edb44fc6a4db0ba3ecee0555212d8112a17f1a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 21 Feb 2014 22:24:21 +0100 Subject: 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. --- src/reflect/scala/reflect/internal/Definitions.scala | 10 +++++++--- test/files/neg/macro-bundle-polymorphic.check | 8 ++++---- test/files/neg/macro-bundle-wrongcontext-a.check | 2 +- test/files/neg/macro-bundle-wrongcontext-b.check | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) (limited to 'test/files') diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 62e98829b7..558e1aa611 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -609,8 +609,12 @@ trait Definitions extends api.StandardDefinitions { private def macroBundleParamInfo(tp: Type) = { val ctor = tp.erasure.typeSymbol.primaryConstructor ctor.paramss match { - case List(List(c)) => if (isMacroContextType(c.info)) c.info else NoType - case _ => NoType + case List(List(c)) => + val sym = c.info.typeSymbol + val isContextCompatible = sym.isNonBottomSubClass(BlackboxContextClass) || sym.isNonBottomSubClass(WhiteboxContextClass) + if (isContextCompatible) c.info else NoType + case _ => + NoType } } @@ -619,7 +623,7 @@ trait Definitions extends api.StandardDefinitions { def isMacroBundleType(tp: Type) = { val isMonomorphic = tp.typeSymbol.typeParams.isEmpty - val isContextCompatible = macroBundleParamInfo(tp) != NoType + val isContextCompatible = isMacroContextType(macroBundleParamInfo(tp)) val hasSingleConstructor = !tp.declaration(nme.CONSTRUCTOR).isOverloaded val nonAbstract = !tp.erasure.typeSymbol.isAbstractClass isMonomorphic && isContextCompatible && hasSingleConstructor && nonAbstract diff --git a/test/files/neg/macro-bundle-polymorphic.check b/test/files/neg/macro-bundle-polymorphic.check index dc6f540f77..60a4d59119 100644 --- a/test/files/neg/macro-bundle-polymorphic.check +++ b/test/files/neg/macro-bundle-polymorphic.check @@ -1,19 +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: not found: value BlackboxBundle2 +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: not found: value BlackboxBundle3 +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: not found: value WhiteboxBundle2 +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: not found: value WhiteboxBundle3 +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 diff --git a/test/files/neg/macro-bundle-wrongcontext-a.check b/test/files/neg/macro-bundle-wrongcontext-a.check index 7a48dbfd3a..10aadb0035 100644 --- a/test/files/neg/macro-bundle-wrongcontext-a.check +++ b/test/files/neg/macro-bundle-wrongcontext-a.check @@ -1,4 +1,4 @@ -macro-bundle-wrongcontext-a.scala:12: error: not found: value Bundle +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 diff --git a/test/files/neg/macro-bundle-wrongcontext-b.check b/test/files/neg/macro-bundle-wrongcontext-b.check index 9c94c3bc34..e9700d379e 100644 --- a/test/files/neg/macro-bundle-wrongcontext-b.check +++ b/test/files/neg/macro-bundle-wrongcontext-b.check @@ -1,4 +1,4 @@ -macro-bundle-wrongcontext-b.scala:10: error: not found: value Bundle +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 -- cgit v1.2.3 From fb0c25c7fd004ee5de1a910bdcccc4fd503da3ce Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 21 Feb 2014 21:40:12 +0100 Subject: 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. --- test/files/neg/macro-bundle-need-qualifier.check | 4 + test/files/neg/macro-bundle-need-qualifier.scala | 11 +++ test/files/neg/macro-bundle-nonpublic-c.check | 4 + test/files/neg/macro-bundle-nonpublic-c.scala | 11 +++ test/files/neg/macro-bundle-nonpublic-impl.check | 4 + test/files/neg/macro-bundle-nonpublic-impl.scala | 11 +++ test/files/neg/macro-bundle-nonstatic.check | 13 +++ test/files/neg/macro-bundle-nonstatic.scala | 36 +++++++ test/files/neg/macro-bundle-whitebox-use-raw.check | 17 ++++ .../macro-bundle-whitebox-use-raw/Macros_1.scala | 108 +++++++++++++++++++++ .../neg/macro-bundle-whitebox-use-raw/Test_2.scala | 19 ++++ .../neg/macro-bundle-whitebox-use-refined.check | 17 ++++ .../Macros_1.scala | 108 +++++++++++++++++++++ .../macro-bundle-whitebox-use-refined/Test_2.scala | 19 ++++ test/files/neg/macro-bundle-whitebox-use.check | 17 ---- .../neg/macro-bundle-whitebox-use/Macros_1.scala | 108 --------------------- .../neg/macro-bundle-whitebox-use/Test_2.scala | 19 ---- test/files/run/macro-bundle-context-alias.check | 4 + .../run/macro-bundle-context-alias/Macros_1.scala | 38 ++++++++ .../run/macro-bundle-context-alias/Test_2.scala | 6 ++ .../run/macro-bundle-context-refinement.check | 2 + .../macro-bundle-context-refinement/Macros_1.scala | 19 ++++ .../macro-bundle-context-refinement/Test_2.scala | 4 + test/files/run/macro-bundle-whitebox-use-raw.check | 5 + .../macro-bundle-whitebox-use-raw/Macros_1.scala | 108 +++++++++++++++++++++ .../run/macro-bundle-whitebox-use-raw/Test_2.scala | 19 ++++ .../run/macro-bundle-whitebox-use-refined.check | 5 + .../Macros_1.scala | 108 +++++++++++++++++++++ .../macro-bundle-whitebox-use-refined/Test_2.scala | 19 ++++ test/files/run/macro-bundle-whitebox-use.check | 5 - .../run/macro-bundle-whitebox-use/Macros_1.scala | 108 --------------------- .../run/macro-bundle-whitebox-use/Test_2.scala | 19 ---- 32 files changed, 719 insertions(+), 276 deletions(-) create mode 100644 test/files/neg/macro-bundle-need-qualifier.check create mode 100644 test/files/neg/macro-bundle-need-qualifier.scala create mode 100644 test/files/neg/macro-bundle-nonpublic-c.check create mode 100644 test/files/neg/macro-bundle-nonpublic-c.scala create mode 100644 test/files/neg/macro-bundle-nonpublic-impl.check create mode 100644 test/files/neg/macro-bundle-nonpublic-impl.scala create mode 100644 test/files/neg/macro-bundle-nonstatic.check create mode 100644 test/files/neg/macro-bundle-nonstatic.scala create mode 100644 test/files/neg/macro-bundle-whitebox-use-raw.check create mode 100644 test/files/neg/macro-bundle-whitebox-use-raw/Macros_1.scala create mode 100644 test/files/neg/macro-bundle-whitebox-use-raw/Test_2.scala create mode 100644 test/files/neg/macro-bundle-whitebox-use-refined.check create mode 100644 test/files/neg/macro-bundle-whitebox-use-refined/Macros_1.scala create mode 100644 test/files/neg/macro-bundle-whitebox-use-refined/Test_2.scala delete mode 100644 test/files/neg/macro-bundle-whitebox-use.check delete mode 100644 test/files/neg/macro-bundle-whitebox-use/Macros_1.scala delete mode 100644 test/files/neg/macro-bundle-whitebox-use/Test_2.scala create mode 100644 test/files/run/macro-bundle-context-alias.check create mode 100644 test/files/run/macro-bundle-context-alias/Macros_1.scala create mode 100644 test/files/run/macro-bundle-context-alias/Test_2.scala create mode 100644 test/files/run/macro-bundle-context-refinement.check create mode 100644 test/files/run/macro-bundle-context-refinement/Macros_1.scala create mode 100644 test/files/run/macro-bundle-context-refinement/Test_2.scala create mode 100644 test/files/run/macro-bundle-whitebox-use-raw.check create mode 100644 test/files/run/macro-bundle-whitebox-use-raw/Macros_1.scala create mode 100644 test/files/run/macro-bundle-whitebox-use-raw/Test_2.scala create mode 100644 test/files/run/macro-bundle-whitebox-use-refined.check create mode 100644 test/files/run/macro-bundle-whitebox-use-refined/Macros_1.scala create mode 100644 test/files/run/macro-bundle-whitebox-use-refined/Test_2.scala delete mode 100644 test/files/run/macro-bundle-whitebox-use.check delete mode 100644 test/files/run/macro-bundle-whitebox-use/Macros_1.scala delete mode 100644 test/files/run/macro-bundle-whitebox-use/Test_2.scala (limited to 'test/files') diff --git a/test/files/neg/macro-bundle-need-qualifier.check b/test/files/neg/macro-bundle-need-qualifier.check new file mode 100644 index 0000000000..6a74ee6aed --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-need-qualifier.scala b/test/files/neg/macro-bundle-need-qualifier.scala new file mode 100644 index 0000000000..0d021e3537 --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/test/files/neg/macro-bundle-nonpublic-c.check b/test/files/neg/macro-bundle-nonpublic-c.check new file mode 100644 index 0000000000..1dfcee58b7 --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-nonpublic-c.scala b/test/files/neg/macro-bundle-nonpublic-c.scala new file mode 100644 index 0000000000..86a2039743 --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/test/files/neg/macro-bundle-nonpublic-impl.check b/test/files/neg/macro-bundle-nonpublic-impl.check new file mode 100644 index 0000000000..7a4e1516f7 --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-nonpublic-impl.scala b/test/files/neg/macro-bundle-nonpublic-impl.scala new file mode 100644 index 0000000000..5857cc6b24 --- /dev/null +++ b/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 +} \ No newline at end of file diff --git a/test/files/neg/macro-bundle-nonstatic.check b/test/files/neg/macro-bundle-nonstatic.check new file mode 100644 index 0000000000..36bccc54db --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-nonstatic.scala b/test/files/neg/macro-bundle-nonstatic.scala new file mode 100644 index 0000000000..dfba79660a --- /dev/null +++ b/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 +} diff --git a/test/files/neg/macro-bundle-whitebox-use-raw.check b/test/files/neg/macro-bundle-whitebox-use-raw.check new file mode 100644 index 0000000000..5792e317a6 --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-whitebox-use-raw/Macros_1.scala b/test/files/neg/macro-bundle-whitebox-use-raw/Macros_1.scala new file mode 100644 index 0000000000..61bf73e481 --- /dev/null +++ b/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) + """ + } +} diff --git a/test/files/neg/macro-bundle-whitebox-use-raw/Test_2.scala b/test/files/neg/macro-bundle-whitebox-use-raw/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/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) + } +} diff --git a/test/files/neg/macro-bundle-whitebox-use-refined.check b/test/files/neg/macro-bundle-whitebox-use-refined.check new file mode 100644 index 0000000000..5792e317a6 --- /dev/null +++ b/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 diff --git a/test/files/neg/macro-bundle-whitebox-use-refined/Macros_1.scala b/test/files/neg/macro-bundle-whitebox-use-refined/Macros_1.scala new file mode 100644 index 0000000000..1866044221 --- /dev/null +++ b/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) + """ + } +} diff --git a/test/files/neg/macro-bundle-whitebox-use-refined/Test_2.scala b/test/files/neg/macro-bundle-whitebox-use-refined/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/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) + } +} diff --git a/test/files/neg/macro-bundle-whitebox-use.check b/test/files/neg/macro-bundle-whitebox-use.check deleted file mode 100644 index 5792e317a6..0000000000 --- a/test/files/neg/macro-bundle-whitebox-use.check +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/test/files/neg/macro-bundle-whitebox-use/Macros_1.scala b/test/files/neg/macro-bundle-whitebox-use/Macros_1.scala deleted file mode 100644 index 61bf73e481..0000000000 --- a/test/files/neg/macro-bundle-whitebox-use/Macros_1.scala +++ /dev/null @@ -1,108 +0,0 @@ -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) - """ - } -} diff --git a/test/files/neg/macro-bundle-whitebox-use/Test_2.scala b/test/files/neg/macro-bundle-whitebox-use/Test_2.scala deleted file mode 100644 index 3a81700251..0000000000 --- a/test/files/neg/macro-bundle-whitebox-use/Test_2.scala +++ /dev/null @@ -1,19 +0,0 @@ -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) - } -} diff --git a/test/files/run/macro-bundle-context-alias.check b/test/files/run/macro-bundle-context-alias.check new file mode 100644 index 0000000000..55e4dfcf92 --- /dev/null +++ b/test/files/run/macro-bundle-context-alias.check @@ -0,0 +1,4 @@ +C +C +C +C diff --git a/test/files/run/macro-bundle-context-alias/Macros_1.scala b/test/files/run/macro-bundle-context-alias/Macros_1.scala new file mode 100644 index 0000000000..354c5e0d92 --- /dev/null +++ b/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" +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-context-alias/Test_2.scala b/test/files/run/macro-bundle-context-alias/Test_2.scala new file mode 100644 index 0000000000..de499cc111 --- /dev/null +++ b/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) +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-context-refinement.check b/test/files/run/macro-bundle-context-refinement.check new file mode 100644 index 0000000000..10f9ebb617 --- /dev/null +++ b/test/files/run/macro-bundle-context-refinement.check @@ -0,0 +1,2 @@ +C +C diff --git a/test/files/run/macro-bundle-context-refinement/Macros_1.scala b/test/files/run/macro-bundle-context-refinement/Macros_1.scala new file mode 100644 index 0000000000..d3a5d179c6 --- /dev/null +++ b/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" +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-context-refinement/Test_2.scala b/test/files/run/macro-bundle-context-refinement/Test_2.scala new file mode 100644 index 0000000000..43d641adeb --- /dev/null +++ b/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) +} \ No newline at end of file diff --git a/test/files/run/macro-bundle-whitebox-use-raw.check b/test/files/run/macro-bundle-whitebox-use-raw.check new file mode 100644 index 0000000000..5679c5faba --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-use-raw.check @@ -0,0 +1,5 @@ +2 +(23,foo,true) +null +C2 +42 diff --git a/test/files/run/macro-bundle-whitebox-use-raw/Macros_1.scala b/test/files/run/macro-bundle-whitebox-use-raw/Macros_1.scala new file mode 100644 index 0000000000..de1863418e --- /dev/null +++ b/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) + """ + } +} diff --git a/test/files/run/macro-bundle-whitebox-use-raw/Test_2.scala b/test/files/run/macro-bundle-whitebox-use-raw/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/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) + } +} diff --git a/test/files/run/macro-bundle-whitebox-use-refined.check b/test/files/run/macro-bundle-whitebox-use-refined.check new file mode 100644 index 0000000000..5679c5faba --- /dev/null +++ b/test/files/run/macro-bundle-whitebox-use-refined.check @@ -0,0 +1,5 @@ +2 +(23,foo,true) +null +C2 +42 diff --git a/test/files/run/macro-bundle-whitebox-use-refined/Macros_1.scala b/test/files/run/macro-bundle-whitebox-use-refined/Macros_1.scala new file mode 100644 index 0000000000..de1863418e --- /dev/null +++ b/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) + """ + } +} diff --git a/test/files/run/macro-bundle-whitebox-use-refined/Test_2.scala b/test/files/run/macro-bundle-whitebox-use-refined/Test_2.scala new file mode 100644 index 0000000000..3a81700251 --- /dev/null +++ b/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) + } +} diff --git a/test/files/run/macro-bundle-whitebox-use.check b/test/files/run/macro-bundle-whitebox-use.check deleted file mode 100644 index 5679c5faba..0000000000 --- a/test/files/run/macro-bundle-whitebox-use.check +++ /dev/null @@ -1,5 +0,0 @@ -2 -(23,foo,true) -null -C2 -42 diff --git a/test/files/run/macro-bundle-whitebox-use/Macros_1.scala b/test/files/run/macro-bundle-whitebox-use/Macros_1.scala deleted file mode 100644 index de1863418e..0000000000 --- a/test/files/run/macro-bundle-whitebox-use/Macros_1.scala +++ /dev/null @@ -1,108 +0,0 @@ -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) - """ - } -} diff --git a/test/files/run/macro-bundle-whitebox-use/Test_2.scala b/test/files/run/macro-bundle-whitebox-use/Test_2.scala deleted file mode 100644 index 3a81700251..0000000000 --- a/test/files/run/macro-bundle-whitebox-use/Test_2.scala +++ /dev/null @@ -1,19 +0,0 @@ -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) - } -} -- cgit v1.2.3