Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SI-8321 bundles can't be whitebox #3571

Merged
merged 7 commits into from Feb 23, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/macros/compiler/Errors.scala
Expand Up @@ -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 =>
Expand Down
17 changes: 3 additions & 14 deletions src/compiler/scala/reflect/macros/compiler/Resolvers.scala
Expand Up @@ -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
}
}
Expand Up @@ -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))
Expand Down
13 changes: 9 additions & 4 deletions src/reflect/scala/reflect/internal/Definitions.scala
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion 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
4 changes: 4 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions 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
}
4 changes: 4 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions 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
}
4 changes: 4 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions 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
}
13 changes: 13 additions & 0 deletions 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
36 changes: 36 additions & 0 deletions 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
}
2 changes: 1 addition & 1 deletion 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
19 changes: 19 additions & 0 deletions 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
43 changes: 43 additions & 0 deletions 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
}
17 changes: 17 additions & 0 deletions 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
108 changes: 108 additions & 0 deletions 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)
"""
}
}
19 changes: 19 additions & 0 deletions 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)
}
}