diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Macros.scala | 409 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/TreeInfo.scala | 9 | ||||
-rw-r--r-- | test/files/run/t5940.scala | 41 |
3 files changed, 298 insertions, 161 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 57180e66d5..e48a95fab0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -11,6 +11,7 @@ import reflect.internal.util.Statistics import scala.reflect.macros.util._ import java.lang.{Class => jClass} import java.lang.reflect.{Array => jArray, Method => jMethod} +import scala.reflect.internal.util.Collections._ /** * Code to deal with macros, namely with: @@ -48,6 +49,183 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { val globalMacroCache = collection.mutable.Map[Any, Any]() val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]] + /** `MacroImplBinding` and its companion module are responsible for + * serialization/deserialization of macro def -> impl bindings. + * + * The first officially released version of macros persisted these bindings across compilation runs + * using a neat trick. The right-hand side of a macro definition (which contains a reference to a macro impl) + * was typechecked and then put verbatim into an annotation on the macro definition. + * + * This solution is very simple, but unfortunately it's also lacking. If we use it, then + * signatures of macro defs become transitively dependent on scala-reflect.jar + * (because they refer to macro impls, and macro impls refer to scala.reflect.macros.Context defined in scala-reflect.jar). + * More details can be found in comments to https://issues.scala-lang.org/browse/SI-5940. + * + * Therefore we have to avoid putting macro impls into binding pickles and come up with our own serialization format. + * Situation is further complicated by the fact that it's not enough to just pickle macro impl's class name and method name, + * because macro expansion needs some knowledge about the shape of macro impl's signature (which we can't pickle). + * Hence we precompute necessary stuff (e.g. the layout of type parameters) when compiling macro defs. + */ + + /** Represents all the information that a macro definition needs to know about its implementation. + * Includes a path to load the implementation via Java reflection, + * and various accounting information necessary when composing an argument list for the reflective invocation. + */ + private case class MacroImplBinding( + // Java class name of the class that contains the macro implementation + // is used to load the corresponding object with Java reflection + val className: String, + // method name of the macro implementation + // `className` and `methName` are all we need to reflectively invoke a macro implementation + // because macro implementations cannot be overloaded + val methName: String, + // flattens the macro impl's parameter lists having symbols replaced with metadata + // currently metadata is an index of the type parameter corresponding to that type tag (if applicable) + // f.ex. for: def impl[T: AbsTypeTag, U: AbsTypeTag, V](c: Context)(x: c.Expr[T]): (U, V) = ??? + // `signature` will be equal to List(-1, -1, 0, 1) + val signature: List[Int], + // type arguments part of a macro impl ref (the right-hand side of a macro definition) + // these trees don't refer to a macro impl, so we can pickle them as is + val targs: List[Tree]) + + /** Macro def -> macro impl bindings are serialized into a `macroImpl` annotation + * with synthetic content that carries the payload described in `MacroImplBinding`. + * + * For example, for a pair of macro definition and macro implementation: + * def impl(c: scala.reflect.macros.Context): c.Expr[Unit] = c.literalUnit; + * def foo: Unit = macro impl + * + * We will have the following annotation added on the macro definition `foo`: + * + * @scala.reflect.macros.internal.macroImpl( + * `macro`( + * "signature" = List(-1), + * "methodName" = "impl", + * "versionFormat" = 1, + * "className" = "Macros$")) + */ + private object MacroImplBinding { + val versionFormat = 1 + + def pickleAtom(obj: Any): Tree = + obj match { + case list: List[_] => Apply(Ident(ListModule), list map pickleAtom) + case s: String => Literal(Constant(s)) + case i: Int => Literal(Constant(i)) + } + + def unpickleAtom(tree: Tree): Any = + tree match { + case Apply(list @ Ident(_), args) if list.symbol == ListModule => args map unpickleAtom + case Literal(Constant(s: String)) => s + case Literal(Constant(i: Int)) => i + } + + def pickle(macroImplRef: Tree): Tree = { + val macroImpl = macroImplRef.symbol + val paramss = macroImpl.paramss + + // this logic relies on the assumptions that were valid for the old macro prototype + // namely that macro implementations can only be defined in top-level classes and modules + // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different + // for example, a macro def could be defined in a trait that is implemented by an object + // there are some more clever cases when seemingly non-static method ends up being statically accessible + // however, the code below doesn't account for these guys, because it'd take a look of time to get it right + // for now I leave it as a todo and move along to more the important stuff + // [Eugene] relies on the fact that macro implementations can only be defined in static classes + // [Martin to Eugene++] There's similar logic buried in Symbol#flatname. Maybe we can refactor? + // [Eugene] we will refactor once I get my hands on https://issues.scala-lang.org/browse/SI-5498 + def className: String = { + def loop(sym: Symbol): String = sym match { + case sym if sym.owner.isPackageClass => + val suffix = if (sym.isModuleClass) "$" else "" + sym.fullName + suffix + case sym => + val separator = if (sym.owner.isModuleClass) "" else "$" + loop(sym.owner) + separator + sym.javaSimpleName.toString + } + + val sym = macroImpl.owner + if (sym.isClass || sym.isModule) loop(sym) + else loop(sym.enclClass) + } + + def signature: List[Int] = { + val transformed = transformTypeTagEvidenceParams(paramss, (param, tparam) => tparam) + transformed.flatten map (p => if (p.isTerm) -1 else p.paramPos) + } + + val payload = List[(String, Any)]( + "versionFormat" -> versionFormat, + "className" -> className, + "methodName" -> macroImpl.name.toString, + "signature" -> signature + ) + + // the shape of the nucleus is chosen arbitrarily. it doesn't carry any payload. + // it's only necessary as a stub `fun` for an Apply node that carries metadata in its `args` + // so don't try to find a program element named "macro" that corresponds to the nucleus + // I just named it "macro", because it's macro-related, but I could as well name it "foobar" + val nucleus = Ident(newTermName("macro")) + val wrapped = Apply(nucleus, payload map { case (k, v) => Assign(pickleAtom(k), pickleAtom(v)) }) + val pickle = gen.mkTypeApply(wrapped, treeInfo.typeArguments(macroImplRef.duplicate)) + + // assign NoType to all freshly created AST nodes + // otherwise pickler will choke on tree.tpe being null + // there's another gotcha + // if you don't assign a ConstantType to a constant + // then pickling will crash + new Transformer { + override def transform(tree: Tree) = { + tree match { + case Literal(const @ Constant(x)) if tree.tpe == null => tree setType ConstantType(const) + case _ if tree.tpe == null => tree setType NoType + case _ => ; + } + super.transform(tree) + } + }.transform(pickle) + } + + def unpickle(pickle: Tree): Option[MacroImplBinding] = + try { + val (wrapped, targs) = + pickle match { + case TypeApply(wrapped, targs) => (wrapped, targs) + case wrapped => (wrapped, Nil) + } + val Apply(_, pickledPayload) = wrapped + val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap + + val pickleVersionFormat = payload("versionFormat").asInstanceOf[Int] + if (versionFormat != pickleVersionFormat) throw new Error("macro impl binding format mismatch: expected $versionFormat, actual $pickleVersionFormat") + + val className = payload("className").asInstanceOf[String] + val methodName = payload("methodName").asInstanceOf[String] + val signature = payload("signature").asInstanceOf[List[Int]] + Some(MacroImplBinding(className, methodName, signature, targs)) + } catch { + case ex: Exception => + val message = new java.io.StringWriter() + ex.printStackTrace(new java.io.PrintWriter(message)) + macroLogVerbose(s"failed to unpickle macro impl binding from ${showRaw(pickle)}:\n$message") + None + } + } + + private def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = { + val pickle = MacroImplBinding.pickle(macroImplRef) + macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(pickle), Nil) + } + + private def loadMacroImplBinding(macroDef: Symbol): Option[MacroImplBinding] = { + macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations) + macroDef.getAnnotation(MacroImplAnnotation) flatMap { + case AnnotationInfo(_, List(pickle), _) => MacroImplBinding.unpickle(pickle) + case _ => None + } + } + /** A list of compatible macro implementation signatures. * * In the example above: @@ -308,7 +486,6 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { if (typecheckedWithErrors) macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) val macroImpl = rhs1.symbol - macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil) if (!hasErrors) { if (macroImpl == null) { invalidBodyError() @@ -479,6 +656,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // hence, we now use IS_ERROR flag to serve as an indicator that given macro definition is broken if (hasErrors) { macroDef setFlag IS_ERROR + } else { + bindMacroImpl(macroDef, rhs1) } rhs1 @@ -516,17 +695,13 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // sym.paramPos is unreliable (see another case below) val tparams = macroImpl.typeParams map (_.deSkolemize) val paramPos = tparams indexOf sym.deSkolemize - val sym1 = if (paramPos == -1) sym else { - val ann = macroDef.getAnnotation(MacroImplAnnotation) - ann match { - case Some(ann) => - val TypeApply(_, implRefTargs) = ann.args(0) - val implRefTarg = implRefTargs(paramPos).tpe.typeSymbol - implRefTarg - case None => - sym - } - } + val sym1 = + if (paramPos == -1) sym + else + loadMacroImplBinding(macroDef) match { + case Some(binding) => binding.targs(paramPos).tpe.typeSymbol + case None => sym + } TypeRef(pre, sym1, args) case tpe => tpe @@ -621,103 +796,44 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { Some(fastTrack(macroDef)) } else { macroRuntimesCache.getOrElseUpdate(macroDef, { - val runtime = { - macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations) - - val ann = macroDef.getAnnotation(MacroImplAnnotation) - if (ann == None) { macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef); return None } - - val macroImpl = ann.get.args(0).symbol - if (macroImpl == NoSymbol) { macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef); return None } - macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) - if (macroImpl.isErroneous) { macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef); return None } - - // [Eugene++] I don't use Scala reflection here, because it seems to interfere with JIT magic - // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% - // I'm not sure what's the reason - for me it's pure voodoo - def loadMacroImpl(cl: ClassLoader): Option[(Object, jMethod)] = { - try { - // this logic relies on the assumptions that were valid for the old macro prototype - // namely that macro implementations can only be defined in top-level classes and modules - // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different - // for example, a macro def could be defined in a trait that is implemented by an object - // there are some more clever cases when seemingly non-static method ends up being statically accessible - // however, the code below doesn't account for these guys, because it'd take a look of time to get it right - // for now I leave it as a todo and move along to more the important stuff - - macroTraceVerbose("loading implementation class: ")(macroImpl.owner.fullName) - macroTraceVerbose("classloader is: ")(ReflectionUtils.show(cl)) - - // [Eugene] relies on the fact that macro implementations can only be defined in static classes - // [Martin to Eugene++] There's similar logic buried in Symbol#flatname. Maybe we can refactor? - def classfile(sym: Symbol): String = { - def recur(sym: Symbol): String = sym match { - case sym if sym.owner.isPackageClass => - val suffix = if (sym.isModuleClass) "$" else "" - sym.fullName + suffix - case sym => - val separator = if (sym.owner.isModuleClass) "" else "$" - recur(sym.owner) + separator + sym.javaSimpleName.toString + val runtime = + loadMacroImplBinding(macroDef) flatMap { + case binding => + val className = binding.className + val methName = binding.methName + macroLogVerbose(s"resolved implementation as $className.$methName") + + // [Eugene++] I don't use Scala reflection here, because it seems to interfere with JIT magic + // whenever you instantiate a mirror (and not do anything with in, just instantiate), performance drops by 15-20% + // I'm not sure what's the reason - for me it's pure voodoo + def loadMacroImpl(cl: ClassLoader): Option[(Object, jMethod)] = { + def fail(what: String, ex: Exception) = { + macroTraceVerbose(s"implementation $what failed to load: ")(ex.toString) + None } - if (sym.isClass || sym.isModule) recur(sym) - else recur(sym.enclClass) + try { + macroTraceVerbose("loading implementation class: ")(className) + macroTraceVerbose("classloader is: ")(ReflectionUtils.show(cl)) + val implObj = ReflectionUtils.staticSingletonInstance(cl, className) + // relies on the fact that macro impls cannot be overloaded + // so every methName can resolve to at maximum one method + val implMeths = implObj.getClass.getDeclaredMethods.filter(_.getName == methName) + val implMeth = implMeths.headOption getOrElse { throw new NoSuchMethodException(s"$className.$methName") } + macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth)) + Some((implObj, implMeth)) + } catch { + case ex: ClassNotFoundException => fail("class", ex) + case ex: NoSuchMethodException => fail("method", ex) + } } - // [Eugene++] this doesn't work for inner classes - // neither does macroImpl.owner.javaClassName, so I had to roll my own implementation - //val receiverName = macroImpl.owner.fullName - val implClassName = classfile(macroImpl.owner) - val implObj = try { - val implObjClass = jClass.forName(implClassName, true, cl) - implObjClass getField "MODULE$" get null - } catch { - case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null - case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null - case ex: ClassNotFoundException => macroTraceVerbose("exception when loading implObj: ")(ex); null + loadMacroImpl(macroClassloader) map { + case (implObj, implMeth) => + def runtime(args: List[Any]) = implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*).asInstanceOf[Any] + runtime _ } - - if (implObj == null) None - else { - // [Eugene++] doh, it seems that I need to copy/paste Scala reflection logic - // see `JavaMirrors.methodToJava` or whatever it's called now - val implMeth = { - def typeToJavaClass(tpe: Type): jClass[_] = tpe match { - case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) - case TypeRef(_, ArrayClass, List(elemtpe)) => jArray.newInstance(typeToJavaClass(elemtpe), 0).getClass - case TypeRef(_, sym: ClassSymbol, _) => jClass.forName(classfile(sym), true, cl) - case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") - } - - val paramClasses = transformedType(macroImpl).paramTypes map typeToJavaClass - try implObj.getClass getDeclaredMethod (macroImpl.name.toString, paramClasses: _*) - catch { - case ex: NoSuchMethodException => - val expandedName = - if (macroImpl.isPrivate) nme.expandedName(macroImpl.name.toTermName, macroImpl.owner).toString - else macroImpl.name.toString - implObj.getClass getDeclaredMethod (expandedName, paramClasses: _*) - } - } - macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMeth)) - Some((implObj, implMeth)) - } - } catch { - case ex: ClassNotFoundException => - macroTraceVerbose("implementation class failed to load: ")(ex.toString) - None - case ex: NoSuchMethodException => - macroTraceVerbose("implementation method failed to load: ")(ex.toString) - None } - } - - loadMacroImpl(macroClassloader) map { - case (implObj, implMeth) => - def runtime(args: List[Any]) = implMeth.invoke(implObj, (args map (_.asInstanceOf[AnyRef])): _*).asInstanceOf[Any] - runtime _ - } - } if (runtime == None) macroDef setFlag IS_ERROR runtime @@ -761,6 +877,11 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { case _ => } collectMacroArgs(expandee) + + val argcDoesntMatch = macroDef.paramss.length != exprArgs.length + val nullaryArgsEmptyParams = exprArgs.length == 0 && macroDef.paramss.length == 1 && macroDef.paramss.flatten.length == 0 + if (argcDoesntMatch && !nullaryArgsEmptyParams) { typer.TyperErrorGen.MacroPartialApplicationError(expandee); return None } + var argss: List[List[Any]] = List(context) :: exprArgs.toList macroTraceVerbose("argss: ")(argss) val rawArgss = @@ -777,33 +898,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { return None } } else { - val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) - val macroImpl = ann.args(0).symbol - var paramss = macroImpl.paramss - val tparams = macroImpl.typeParams - macroTraceVerbose("paramss: ")(paramss) - - // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations - // nullary def + nullary invocation => paramss and argss match, everything is okay - // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay - // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss - // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay - // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist - val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => NoSymbol) - val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty - val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty - if (isEmptyParamlistDef && !isEmptyArglistInvocation) { - macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") - argss = argss :+ Nil - } - - // nb! check partial application against paramss without evidences - val numParamLists = paramss_without_evidences.length - val numArgLists = argss.length - if (numParamLists != numArgLists) { - typer.TyperErrorGen.MacroPartialApplicationError(expandee) - return None - } + val binding = loadMacroImplBinding(macroDef).get + macroTraceVerbose("binding: ")(binding) // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences // consider the following example: @@ -821,43 +917,36 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom'' // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim - val resolved = collection.mutable.Map[Symbol, Type]() - paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => { - val TypeApply(_, implRefTargs) = ann.args(0) - var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol - val tpe = if (implRefTarg.isTypeParameterOrSkolem) { - if (implRefTarg.owner == macroDef) { + val tags = binding.signature filter (_ != -1) map (paramPos => { + val targ = binding.targs(paramPos).tpe.typeSymbol + val tpe = if (targ.isTypeParameterOrSkolem) { + if (targ.owner == macroDef) { // [Eugene] doesn't work when macro def is compiled separately from its usages - // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams - // val paramPos = implRefTarg.deSkolemize.paramPos - val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name) - typeArgs(paramPos).tpe + // then targ is not a skolem and isn't equal to any of macroDef.typeParams + // val argPos = targ.deSkolemize.paramPos + val argPos = macroDef.typeParams.indexWhere(_.name == targ.name) + typeArgs(argPos).tpe } else - implRefTarg.tpe.asSeenFrom( + targ.tpe.asSeenFrom( if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe, macroDef.owner) } else - implRefTarg.tpe - macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) - resolved(tparam) = tpe - param.tpe.typeSymbol match { - case definitions.AbsTypeTagClass => - // do nothing - case _ => - throw new Error("unsupported tpe: " + tpe) - } - tparam + targ.tpe + if (tpe.isConcrete) context.TypeTag(tpe) else context.AbsTypeTag(tpe) }) - val tags = paramss.last takeWhile (_.isType) map (resolved(_)) map (tpe => if (tpe.isConcrete) context.TypeTag(tpe) else context.AbsTypeTag(tpe)) - if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil - argss = argss.dropRight(1) :+ (tags ++ argss.last) // todo. add support for context bounds in argss - - assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, paramss)) - val rawArgss = for ((as, ps) <- argss zip paramss) yield { - if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) - else as + val hasImplicitParams = macroDef.paramss.flatten.lastOption map (_.isImplicit) getOrElse false + argss = if (hasImplicitParams) argss.dropRight(1) :+ (tags ++ argss.last) else argss :+ tags + + // nb! varargs can apply to any parameter section, not necessarily to the last one + for ((as, i_argss) <- argss.zipWithIndex) yield { + val i_paramss = i_argss - 1 + val mapsToParamss = 0 <= i_paramss && i_paramss < macroDef.paramss.length + if (mapsToParamss) { + val ps = macroDef.paramss(i_paramss) + if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1) + else as + } else as } - rawArgss } val rawArgs = rawArgss.flatten macroTraceVerbose("rawArgs: ")(rawArgs) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 1b4c1b2877..19f264f60e 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -236,7 +236,7 @@ abstract class TreeInfo { case _ => tree } - + /** Is tree a self or super constructor call? */ def isSelfOrSuperConstrCall(tree: Tree) = { // stripNamedApply for SI-3584: adaptToImplicitMethod in Typers creates a special context @@ -372,6 +372,13 @@ abstract class TreeInfo { case _ => EmptyTree } + /** If this tree represents a type application the type arguments. Otherwise Nil. + */ + def typeArguments(tree: Tree): List[Tree] = tree match { + case TypeApply(_, targs) => targs + case _ => Nil + } + /** If this tree has type parameters, those. Otherwise Nil. */ def typeParameters(tree: Tree): List[TypeDef] = tree match { diff --git a/test/files/run/t5940.scala b/test/files/run/t5940.scala new file mode 100644 index 0000000000..147ff38256 --- /dev/null +++ b/test/files/run/t5940.scala @@ -0,0 +1,41 @@ +import scala.tools.partest._ + +object Test extends DirectTest { + def code = ??? + + def macros_1 = """ + import scala.reflect.macros.Context + + object Impls { + def impl(c: Context) = c.literalUnit + } + + object Macros { + //import Impls._ + def impl(c: Context) = c.literalUnit + def foo = macro impl + } + """ + def compileMacros() = { + val classpath = List(sys.props("partest.lib"), sys.props("partest.reflect")) mkString sys.props("path.separator") + compileString(newCompiler("-language:experimental.macros", "-cp", classpath, "-d", testOutput.path))(macros_1) + } + + def test_2 = """ + object Test extends App { + println(Macros.foo) + } + """ + def compileTest() = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(test_2) + } + + def show(): Unit = { + log("Compiling Macros_1...") + if (compileMacros()) { + log("Compiling Test_2...") + if (compileTest()) log("Success!") else log("Failed...") + } + } +}
\ No newline at end of file |