Macro paradise: now a compiler plugin for 2.10.x, features quasiquotes and macro annotations

1,517 views
Skip to first unread message

Eugene Burmako

unread,
Aug 5, 2013, 9:53:16 AM8/5/13
to scala-l...@googlegroups.com
Hi folks,

I've just released a new version of macro paradise, which represents a significant revamp and introduces new features. You can read the full text of the announcement at http://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html.

Short summary:
* Now a compiler plugin that works with Scala 2.10.2
* Backports quasiquotes from 2.11.0-M4
* Also provides freshly implemented macro annotations

Your feedback is very welcome!

Cheers,
Eugene

Alois Cochard

unread,
Aug 5, 2013, 10:09:42 AM8/5/13
to scala-l...@googlegroups.com
Congratulations for the hard work Eugene!

Haoyi Li

unread,
Aug 5, 2013, 10:18:52 AM8/5/13
to scala-l...@googlegroups.com
Looks awesome =) Can't wait to play with macro annotations.

Question: I remember reading this link (http://www.infoq.com/news/2012/07/scala-macros) a while ago, about using macros to inject implicits into a scope without having to keep typing `implicit blah =>` everywhere. I had always thought untyped macros would be the obvious solution for this; is that still going to happen?

-Haoyi


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Rodrigo Cano

unread,
Aug 5, 2013, 4:02:36 PM8/5/13
to scala-l...@googlegroups.com
Wow, it just keeps getting better.

Rodrigo Cano

unread,
Aug 5, 2013, 4:50:29 PM8/5/13
to scala-l...@googlegroups.com
One thing I can't find in the docs: published artifact and an example to instruct sbt to use that plugin.

Thanks.

Eugene Burmako

unread,
Aug 5, 2013, 4:52:35 PM8/5/13
to scala-l...@googlegroups.com

Rodrigo Cano

unread,
Aug 5, 2013, 4:53:51 PM8/5/13
to scala-l...@googlegroups.com
Oh crap, it was in an obvious location. My bad. Thanks!

Eugene Burmako

unread,
Aug 5, 2013, 5:09:21 PM8/5/13
to scala-l...@googlegroups.com
Currently there exists certain skepticism wrt untyped macros on expression level.

Untyped macros are perceived as being too powerful, being something that we would like to better control but don't yet know how. A popular theme now is providing a way for programs to typecheck without triggering macro expansions, so that programs can be better understood by humans and computers alike (that's what led to the blackbox vs whitebox separation covered in my ScalaDays talk: http://scalamacros.org/news/2013/05/18/half-a-year-in-macro-paradise.html), and untyped macros don't quite fit into this scheme.

It is for that reason I'm wary of adding untyped macro to the new paradise. Exploring new avenues is of course important, but having production Scala as the ultimate target is at the foundation of the Scala macros project. Towards that goal I can motivate quasiquotes, macro annotations and the upcoming fundep materializers to the SIP committee, but I'm not yet sure about untyped macros.

All's not lost however. I'm very interested in the notion of untyped macros, because that's how they do a lot of things in LISP (and, in fact, quasiquotes have successfully brought the notion of untypedness into our previously reify-based world). It's just that I didn't yet have time to dig deep to provide personal involvement.

I think if we could come up with something that provides information about expansions of untyped macros and maybe a killer use case (like materialization for implicit macros), things can very well change. Look at macro annotations. They are also not blackbox, but they cover an important scenario of global code generation, so people are much more favorable towards them.

Haoyi Li

unread,
Aug 6, 2013, 12:36:48 AM8/6/13
to scala-l...@googlegroups.com
My use case for untyped macros would be essentially turning this:

run{ implicit i =>
    ... contextualOperation() ...
}

and this:

run{ i =>
    import i._
    ... contextualOperation() ...
}

into

run{ ... contextualOperation() ... } 

Essentially, I would like to abstract away the act of injecting a context into a scope (whether via import ._ or via an implicit). This would be nice for scala.rx (which is my original interest), but it's an identical problem that Akka actors' sender-ref, SLICK's sessions, scala-STM's transactions, and Scala.React's continuation-based DSL all face. 

You have a choice of using an implicit/explicit parameter to inject context, or using a threadlocal/mutable variable: the former gets extremely verbose/tedious after a few dozen uses, and the latter faces subtle scoping problems related to mutable state and lexical closure which really does cause problems/confusion (see all the questions regarding Akka's sender and Future{}). 

I guess the exact capability that all these things would need is the ability to inject either a single implicit or a single import ._ into the scope, so it's not so much completely untyped as it is typed-with-additional-local-bindings, presumably taken from the members of some type. I think an import ._ would be preferable since that means your contextualOperation won't need to exist around outside the run{...} block unnecessarily clogging up those scopes.

Simon Ochsenreither

unread,
Aug 6, 2013, 9:01:11 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,

the work you have done is just incredibly impressive!

Here are a few points from the Enum perspective:

I'm not sure whether macro annotations bring us any closer to having good enum support in Scala, I see at least two big issues which both are deal-breakers from my point of view.
  • It is not possible to emit static members
  • The syntactic options given by macro annotations are very limited and I don't think it is possible to get close to a syntax which I would consider acceptable for mainstream use.
Please take this with a grain of salt, I'm aware that enum support is a very different use-case to the usual macro use-cases and I don't expect any changes to make this use-case easier, especially not in favor of more important and popular use-cases of macros.

I'm not sure how untyped macros would look like in the macro annotation context. I always thought they were closely coupled to the type macro approach. Could you show some example code?

Thank a lot,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 9:07:07 AM8/6/13
to scala-l...@googlegroups.com
Hi Simon,

With macro annotations it is possible to create members in a companion object. Probably you could try to annotate them with STATIC, and probably that might work - didn't have time to check myself though.

Speaking of syntactic options, what exactly are you interested in?

Finally I didn't get the question about untyped macros. Could you elaborate?

Simon Ochsenreither

unread,
Aug 6, 2013, 9:33:27 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,



With macro annotations it is possible to create members in a companion object. Probably you could try to annotate them with STATIC, and probably that might work - didn't have time to check myself though.

Yes, I have seen that. I'm not sure though that this will work. The JVM looks for some static members in the enum class. Having members, even static members, in a different class (the companion object) won't cut it from a JVM perspective. (I tried setting the STATIC flag in the past and couldn't see any difference, too.)
The only thing which might save us are static forwarders. I know that scalac emits those, but I'm not sure whether they are emitted for those occurrences I'm interested in.
 
Speaking of syntactic options, what exactly are you interested in?

Using type macros and untyped macros, it's possible to this syntax:

class Days extends Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)

Finally I didn't get the question about untyped macros. Could you elaborate?

As far as I understand with macro annotations the best thing possible would be something along the lines of

@Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
class Days


... assuming untyped macros work this way (in the sense that the untyped parts which looked like arguments to the super constructor with type macros + untyped macros are now inside the annotation).
This syntax just won't scale (user-defined constructors, user-defined methods, user-defined overridden methods in individual enum items) and will look quite foreign.

Bye,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 9:37:29 AM8/6/13
to scala-l...@googlegroups.com
Trees that get into a macro annotation expansion don't get typechecked, so you can write whatever you want inside the definition of the class and, as long as it's syntactically valid, you'll get a chance to read that whatever, analyzer it and emit whatever you want instead. Would that bring enough flexibility to support your use case?


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:

Simon Ochsenreither

unread,
Aug 6, 2013, 10:28:07 AM8/6/13
to scala-l...@googlegroups.com


Trees that get into a macro annotation expansion don't get typechecked, so you can write whatever you want inside the definition of the class and, as long as it's syntactically valid, you'll get a chance to read that whatever, analyzer it and emit whatever you want instead. Would that bring enough flexibility to support your use case?

Does this mean that while something like ...

@Enum

class Days extends Enum(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)


... won't be possible anymore, something like ...

@Enum
class Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }


might work?

Eugene Burmako

unread,
Aug 6, 2013, 10:46:09 AM8/6/13
to scala-l...@googlegroups.com
It would work if it were valid Scala syntax :)


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:
>
>

Simon Ochsenreither

unread,
Aug 6, 2013, 11:41:16 AM8/6/13
to scala-l...@googlegroups.com

It would work if it were valid Scala syntax :)

Ok ... maybe I'm not getting then what untyped macros are about.
From my POV, being able to pass arbitrary trees to the macro is the important part of untyped macros ... without it, what's left?

Thorpe, Michael

unread,
Aug 6, 2013, 11:44:12 AM8/6/13
to <scala-language@googlegroups.com>
The arbitrary trees still need to be constructed somehow. The scalac parser cannot construct trees for things that there aren't production rules for.

Untyped =/= new syntax 

On 6 Aug 2013, at 16:41, Simon Ochsenreither <simon.och...@gmail.com>
 wrote:


It would work if it were valid Scala syntax :)

Ok ... maybe I'm not getting then what untyped macros are about.
From my POV, being able to pass arbitrary trees to the macro is the important part of untyped macros ... without it, what's left?

Rex Kerr

unread,
Aug 6, 2013, 11:45:00 AM8/6/13
to scala-l...@googlegroups.com
What you wrote wasn't even a tree, though.  This is:

@Enum class Days {
  Sunday; Monday; Tuesday; Wednesday; Thursday; Friday; Saturday
}

This too:

@Enum class Days {
  (Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
}

  --Rex




--

Tim Underwood

unread,
Aug 6, 2013, 11:46:11 AM8/6/13
to scala-l...@googlegroups.com
Hi Eugene,

I tried adding the compiler plugin to my existing project so I could play with the new macro features but I get a compiler AssertionError (stack trace below).  I haven't tried using any of the new macro features yet and the code compiles fine using Scala 2.10.2 without the macro paradise plugin.

I'm trying to minimize the code that causes the error but haven't been successful so far.  Does the stack trace give you any hints as to what the problem might be?

Thanks,

-Tim



Stack Trace:

at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1210)
at scala.reflect.internal.Symbols$ModuleClassSymbol.implicitMembers(Symbols.scala:3095)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1073)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1113)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.companionImplicitMap(Implicits.scala:1147)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$implicitsOfExpectedType(Implicits.scala:1167)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1427)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:82)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:40)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$wrapImplicit$1(Typers.scala:202)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:209)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply$mcZ$sp(Typers.scala:105)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.reflect.internal.Types$UndoLog.undo(Types.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2.isCoercible(Typers.scala:102)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isCompatible(Infer.scala:425)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.collection.LinearSeqLike$class.corresponds(LinearSeqLike.scala:76)
at scala.collection.immutable.List.corresponds(List.scala:84)
at scala.tools.nsc.typechecker.Infer$Inferencer.isCompatibleArgs(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer.typesCompatible$1(Infer.scala:814)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:833)
at scala.tools.nsc.typechecker.Infer$Inferencer.tryTupleApply$1(Infer.scala:809)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:856)
at scala.tools.nsc.typechecker.Infer$Inferencer.isApplicableSafe(Infer.scala:877)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3146)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3135)
at scala.collection.TraversableLike$$anonfun$filter$1.apply(TraversableLike.scala:264)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:263)
at scala.collection.AbstractTraversable.filter(Traversable.scala:105)
at scala.reflect.internal.Symbols$Symbol.filter(Symbols.scala:1658)
at scala.tools.nsc.typechecker.Typers$Typer.preSelectOverloaded$1(Typers.scala:3135)
at scala.tools.nsc.typechecker.Typers$Typer.doTypedApply(Typers.scala:3163)
at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4589)
at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4621)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5526)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5683)
at scala.tools.nsc.typechecker.Typers$Typer.computeType(Typers.scala:5770)
at scala.tools.nsc.typechecker.Namers$Namer.assignTypeToTree(Namers.scala:834)
at scala.tools.nsc.typechecker.Namers$Namer.valDefSig(Namers.scala:1315)
at scala.tools.nsc.typechecker.Namers$Namer.getSig$1(Namers.scala:1456)
at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1465)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply$mcV$sp(Namers.scala:731)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer.scala$tools$nsc$typechecker$Namers$Namer$$logAndValidate(Namers.scala:1498)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:729)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.completeImpl(Namers.scala:704)
at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter$class.complete(Namers.scala:1621)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.complete(Namers.scala:702)
at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1229)
at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1365)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:39)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1919)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedTemplate(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedTemplate(Typers.scala:51)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedTemplate(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1759)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedClassDef(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedClassDef(Typers.scala:62)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedClassDef(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5545)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5263)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5549)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedPackageDef$1(Typers.scala:35)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:43)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5666)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:99)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:464)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:91)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1583)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1557)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1553)
at scala.tools.nsc.Global$Run.compile(Global.scala:1662)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:123)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:99)
at xsbt.CompilerInterface.run(CompilerInterface.scala:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:48)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply$mcV$sp(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile.sbt$compiler$AggressiveCompile$$timed(AggressiveCompile.scala:159)
at sbt.compiler.AggressiveCompile$$anonfun$3.compileScala$1(AggressiveCompile.scala:97)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:142)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:86)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:38)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:36)
at sbt.inc.Incremental$.cycle(Incremental.scala:73)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:33)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:32)
at sbt.inc.Incremental$.manageClassfiles(Incremental.scala:41)
at sbt.inc.Incremental$.compile(Incremental.scala:32)
at sbt.inc.IncrementalCompile$.apply(Compile.scala:26)
at sbt.compiler.AggressiveCompile.compile2(AggressiveCompile.scala:150)
at sbt.compiler.AggressiveCompile.compile1(AggressiveCompile.scala:70)
at sbt.compiler.AggressiveCompile.apply(AggressiveCompile.scala:45)
at sbt.Compiler$.apply(Compiler.scala:70)
at sbt.Defaults$.sbt$Defaults$$compileTaskImpl(Defaults.scala:722)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)

Eugene Burmako

unread,
Aug 6, 2013, 12:22:21 PM8/6/13
to scala-l...@googlegroups.com
Thanks a lot for the crash report! It would be great if you could minimize the reproduction. I will gladly take a look.


--

Simon Ochsenreither

unread,
Aug 6, 2013, 12:39:24 PM8/6/13
to scala-l...@googlegroups.com

The arbitrary trees still need to be constructed somehow. The scalac parser cannot construct trees for things that there aren't production rules for.

Alright, of course!

The syntax in the type macro + untyped annotation code only worked because it was declared as varargs!

I think Rex' syntax of

@Enum class Days {
  Sunday; Monday; Tuesday; Wednesday; Thursday; Friday; Saturday
}


could be bearable ... especially if it would be written like this:


@Enum class Days {
  Sunday
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
}


The remaining question is whether the syntax scales, e.g.:

@Enum
class Days(val inGerman: String) extends HasName {
  Monday("Montag")
  Tuesday("Dienstag")
  Wednesday("Mittwoch")
  Thursday("Donnerstag")
  Friday("Freitag")
  Saturday("Samstag") { override def workingDay: Boolean = false }
  Sunday("Sonntag") { override def workingDay: Boolean = false }

  def abbreviation = name take 3
  def workingDay: Boolean = true
}

trait HasName { def name: String }


I think this looks quite nice, indeed!

I'll spend a few minutes to have a look whether I can re-organize my macro accordingly. What's the recommended way currently if one needs type annotations and untyped macros? The macro paradise branch for 2.11 trunk? Are there any current published artifacts?

Thanks,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 12:42:29 PM8/6/13
to scala-l...@googlegroups.com
Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.


--

Simon Ochsenreither

unread,
Aug 6, 2013, 12:51:50 PM8/6/13
to scala-l...@googlegroups.com

Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.

So I can basically put arbitrary trees into the body of a class annotated with a macro as long as it can be parsed, without needing untyped macros?

Eugene Burmako

unread,
Aug 6, 2013, 1:04:08 PM8/6/13
to scala-l...@googlegroups.com
Yes


On Tue, Aug 6, 2013 at 6:51 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:


Currently you cannot mix macro annotations and untyped macros. What would be your use case? Looks like for enums macro annotations alone should be enough.

So I can basically put arbitrary trees into the body of a class annotated with a macro as long as it can be parsed, without needing untyped macros?

--

Simon Ochsenreither

unread,
Aug 6, 2013, 1:13:52 PM8/6/13
to scala-l...@googlegroups.com

Yes

Wow, impressive!

Sorry for not reading your explanations thoroughly enough. :-/

Now the remaining question is how to add the required type (java.lang.Enum) to the parents of the annotated class.

Eugene Burmako

unread,
Aug 6, 2013, 1:15:38 PM8/6/13
to scala-l...@googlegroups.com
It's also possible.

Macro annotations get the entire tree that represents an annottee. In our case that would be a ClassDef, and ClassDef contains a list of parents that you can freely change.


On Tuesday, 6 August 2013, Simon Ochsenreither <simon.och...@gmail.com> wrote:
>

Simon Ochsenreither

unread,
Aug 6, 2013, 1:25:02 PM8/6/13
to scala-l...@googlegroups.com

It's also possible.

Macro annotations get the entire tree that represents an annottee. In our case that would be a ClassDef, and ClassDef contains a list of parents that you can freely change.

Excellent!

I'll try to port the type-macro+untyped-macro-based enums to annotation macros and report back.

Simon Ochsenreither

unread,
Aug 6, 2013, 2:39:43 PM8/6/13
to scala-l...@googlegroups.com
In the documentation, it says:

[...] macro annotations in 2.0 are done right in the sense that they [...] allow to introduce static members (i.e. expansions of classes can affect companions). This opens a number of new possibilities in code generation land, including customizable case classes and better enums.

How would one do that?

Simon Ochsenreither

unread,
Aug 6, 2013, 3:16:08 PM8/6/13
to scala-l...@googlegroups.com
Ok, this is where I ended up: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax.scala

Something like

@Enum
class Day {

    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
}


already compiles successfully!

But as the println(show(newClassDef)) in line 171 shows, the static flag doesn't work:

class Day extends java.lang.Enum[Day] {
  private def <init>(name: String, ordinal: Int) = {
    super.<init>(name, ordinal);
    ()
  };
  private val Monday: Day = new Day("Monday", 1);
  private val Tuesday: Day = new Day("Tuesday", 2);
  private val Wednesday: Day = new Day("Wednesday", 3);
  private val Thursday: Day = new Day("Thursday", 4);
  private val Friday: Day = new Day("Friday", 5);
  private[this] val $VALUES: scala.Array[Day] = scala.Array.apply[Day]()(Predef.implicitly);
  def values: scala.Array[Day] = $VALUES.clone();
  def valueOf(name: String): Day = java.lang.Enum.valueOf(classOf[Day], name)
}

So the class is not usable, because it is missing the required static members (Monday, Tuesday, Wednesday, Thursday, Friday, $VALUES, values, valueOf should all be static).

Any ideas?

Tim Underwood

unread,
Aug 6, 2013, 4:12:26 PM8/6/13
to scala-l...@googlegroups.com

If you comment out the macro paradise plugin it compiles fine.  Also if you comment out the @BeanInfo annotation on the Bar object in Bar.scala it compiles fine.

Let me know if I can do anything else to help.

-Tim

Eugene Burmako

unread,
Aug 6, 2013, 4:41:03 PM8/6/13
to scala-l...@googlegroups.com
I meant generating members for companions. After a discussion with you I understood that it's a stretch, and I will correct it once I get back home.
--

Simon Ochsenreither

unread,
Aug 6, 2013, 5:36:18 PM8/6/13
to scala-l...@googlegroups.com
I think one could get away by emitting a companion object and profit from the fact scalac emits static forwarders for it in the companion class by default already.
What's the best way to define the class and get the object for free? I have looked into the Context API but didn't find an appropriate introduce??? method. Looks like this is 2.11 only.

Eugene Burmako

unread,
Aug 6, 2013, 5:41:38 PM8/6/13
to scala-l...@googlegroups.com
We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Please let me know whether the idea with forwarders works out. Keeping fingers crossed.


On 6 August 2013 23:36, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I think one could get away by emitting a companion object and profit from the fact scalac emits static forwarders for it in the companion class by default already.
What's the best way to define the class and get the object for free? I have looked into the Context API but didn't find an appropriate introduce??? method. Looks like this is 2.11 only.

--

Eugene Burmako

unread,
Aug 6, 2013, 5:54:03 PM8/6/13
to scala-l...@googlegroups.com
Why are you not using quasiquotes?



--

Simon Ochsenreither

unread,
Aug 6, 2013, 6:09:48 PM8/6/13
to scala-l...@googlegroups.com

Why are you not using quasiquotes?

I was just porting existing, pre-quasiquote code; the changes to move from type macros + untyped macros to macro annotations were pretty small, so I didn't bother to rework the code yet:
https://github.com/soc/enum-paradise/commit/ec64e228dc934ec59c6bf68a4592d16fbc08da41

Simon Ochsenreither

unread,
Aug 6, 2013, 6:15:40 PM8/6/13
to scala-l...@googlegroups.com

We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Mhhh, maybe I'm misunderstanding the docs, but it reads like I would need to have the companion object already declared in the code to be able to emit members for that object. Is that correct?

Eugene Burmako

unread,
Aug 6, 2013, 6:18:02 PM8/6/13
to scala-l...@googlegroups.com
Nope, you can just expand a ClassDef into a ClassDef + ModuleDef, and the companion will be transparently created for you.


On 7 August 2013 00:15, Simon Ochsenreither <simon.och...@gmail.com> wrote:

We don't have introduceXXX anymore, and introduceTopLevel will be removed before the 2.11 release. The only way to emit a companion object is to put an annotation on a class, as described here: http://docs.scala-lang.org/overviews/macros/annotations.html.

Mhhh, maybe I'm misunderstanding the docs, but it reads like I would need to have the companion object already declared in the code to be able to emit members for that object. Is that correct?

--

Simon Ochsenreither

unread,
Aug 6, 2013, 6:18:21 PM8/6/13
to scala-l...@googlegroups.com
Ahhh, just saw your comments on GitHub. Will try it that way!

Thanks,

Simon

Eugene Burmako

unread,
Aug 6, 2013, 6:30:39 PM8/6/13
to scala-l...@googlegroups.com
Thank you very much for pointing out flaws in the documentation!

Based on your feedback I've just updated the docs: http://docs.scala-lang.org/overviews/macros/annotations.html. I'm still not satisfied with the expansion rules, but at least now the description should be helpful. What do you think?



--

Simon Ochsenreither

unread,
Aug 6, 2013, 9:35:54 PM8/6/13
to scala-l...@googlegroups.com
What do you think?

Yes, this makes the new possibilities much more explicit!

Update: https://github.com/soc/enum-paradise/commit/e19c0b2a1670fd642e759b9c08081ec470aca250

@Enum class Foo { Bar }

val bar = Foo.Bar


:-)

I think the next question now is how to set the ENUM flag for the enum class. Any ideas?

Eugene Burmako

unread,
Aug 7, 2013, 1:32:10 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Grzegorz Kossakowski
Doesn't look like we in scalac emit this flag at all. Probably you'll need to do it in a compiler plugin that injects into backend. Miguel, Greg, could you comment?



--

Grzegorz Kossakowski

unread,
Aug 7, 2013, 3:26:15 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia
On 6 August 2013 22:32, Eugene Burmako <eugene....@epfl.ch> wrote:
Doesn't look like we in scalac emit this flag at all. Probably you'll need to do it in a compiler plugin that injects into backend. Miguel, Greg, could you comment?

The ENUM is not set by Scala compiler anywhere (it's only used to represent types coming from Java) so backend doesn't support it in any way.

I wouldn't be very against adding support for it but one would have to figure out exact semantics. For the start:
  • should ENUM flag be pickled? (probably yes)
  • would class file parser be confused by the fact that there's a scala class that has an enum flag?
  • would referring to enums synthesized from scala files work?

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

Eugene Burmako

unread,
Aug 7, 2013, 4:13:33 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Oh wow, looks like we're just one step from having enums just like you wanted, Simon.

I would probably not have time to implement that myself, but if you can give it a try, it would be very cool. I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Simon Ochsenreither

unread,
Aug 7, 2013, 6:59:10 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither


Oh wow, looks like we're just one step from having enums just like you wanted, Simon.

I would probably not have time to implement that myself, but if you can give it a try, it would be very cool.

Yes, I'll have a look at it.

 
I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Ouch ... that probably means I should start to prioritize things now. Off the top of my head, I come up with ...
  • Avian support
  • JavaScript backend
  • Being able to recognize a value type at runtime
  • Deprecating procedures
  • Deprecating dangerous implicit widening conversions
  • Deprecating “warning: Adapting argument list by inserting (): this is unlikely to be what you want.”
  • Trying to get a reliable definition of what's in the default scope (instead of the current “whatever version of Java you might run” free-for-all)
  • SI-5722 - classOf[T] support with Tags
  • SI-6634 - Fix and specify Collection's remove method
  • SI-6747 - Fix Range
  • SI-6632 - Fix ListBuffer
  • SI-7379 - Fix Option(null.asInstanceOf[Boolean])

... in addition to implementing @Enum (which includes writing tests, updating the SIP, getting it accepted, deprecating scala.Enumeration and documenting the migration) which I all want to ship/fix/implement for 2.11.

Some of those might be close to the finish line, but estimating the amount of time it takes for the politics associated with it hasn't been really reliable in the past.

Simon Ochsenreither

unread,
Aug 7, 2013, 8:35:41 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia
Hi Grzegorz!



The ENUM is not set by Scala compiler anywhere (it's only used to represent types coming from Java) so backend doesn't support it in any way.

I wouldn't be very against adding support for it but one would have to figure out exact semantics. For the start:
  • should ENUM flag be pickled? (probably yes)
  • would class file parser be confused by the fact that there's a scala class that has an enum flag?
  • would referring to enums synthesized from scala files work?
These are good questions!

I think a pretty idiomatic and minimal change would look like this: https://github.com/soc/scala/compare/topic;enums?expand=1
There are two isEnum methods, which aren't strictly required, so they could also be left out.

This way, we don't have any special-cased code anywhere in the compiler, it just wires up the a modifier flag to the appropriate ACC_ENUM, just like for all the other class file bits.
Creating enums would happen completely in the macro.

What do you think?

Thanks and bye,

Simon

Rex Kerr

unread,
Aug 7, 2013, 11:53:03 AM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
On Wed, Aug 7, 2013 at 6:59 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:

 
I believe code freeze for 2.11 is just a month away, but on the other hand it's the entire month away!

Ouch ... that probably means I should start to prioritize things now. Off the top of my head, I come up with ...

I don't think all of these things are equally frozen.  Anything that's a bug or a drop-in replacement that doesn't require deprecation, interface changes, etc. should be fair game.

Maybe someone more in-the-know than I can specify which of these are subject to freeze and which are fine as post-major-feature-freeze tweaks.

Most or all of the things in JIRA look like they could be introduced post-freeze--they're randomly broken stuff that cannot be sanely used.  (Updating a negative number actually changes the SECOND thing in the list?!)

Something like deprecating procedures, in contrast, almost surely should go in.  As I'm sure too many people are aware, I dislike the change, but if the decision has been made to do it, someone please get it in before the freeze!
 
  • Avian support
  • JavaScript backend
  • Being able to recognize a value type at runtime
  • Deprecating procedures
  • Deprecating dangerous implicit widening conversions
  • Deprecating “warning: Adapting argument list by inserting (): this is unlikely to be what you want.”
  • Trying to get a reliable definition of what's in the default scope (instead of the current “whatever version of Java you might run” free-for-all)
  • SI-5722 - classOf[T] support with Tags
  • SI-6634 - Fix and specify Collection's remove method
  • SI-6747 - Fix Range
  • SI-6632 - Fix ListBuffer
  • SI-7379 - Fix Option(null.asInstanceOf[Boolean])

... in addition to implementing @Enum (which includes writing tests, updating the SIP, getting it accepted, deprecating scala.Enumeration and documenting the migration) which I all want to ship/fix/implement for 2.11.


Having a working Enum would be awesome, in my opinion.  Not quite as nice as having a for-construct that doesn't penalize you when you use it like a Java for loop, but it always irks me that Java enums in many ways are better-supported and lower-boilerplate than Scala's equivalents.

But this too should IMO really go in before the freeze.  Messing with code generation is something you want to do early in your release process.

  --Rex

Simon Ochsenreither

unread,
Aug 7, 2013, 1:06:43 PM8/7/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hi Eugene, hi Grzegorz,

thanks a lot for your support.

I have created changes for enum support against trunk and I'm not sure whether backporting them to 2.10.x is an option ... so I would probably need to port the macro code from 2.10.x to 2.11 again. Is the functionality I'm using already in 2.11 trunk (afaik interpolation is in, but macro annotations aren't yet, correct?)?

Or should I compile against some macro paradise version + my changes?

What do you think?

Thanks and bye!

Simon

Edit: Mhhhh, looks like porting the enum support to 2.10.x might be the most painfree way in the end.

Simon Ochsenreither

unread,
Aug 8, 2013, 7:15:08 AM8/8/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hey guys,

want do you think of this? :D

Enum declaration:

@Enum
class Day {
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
}


Enum usage:

  val mon = Day.Monday
  val tue = Day.Tuesday
  val days = Day.values.toList
  def monday(day: Day) = day match {
    case Day.Monday => "Monday!"
  }
  println(s"Day.Monday: \t\t\t\t $mon")
  println(s"Day.Monday.name: \t\t\t ${mon.name}")
  println(s"Day.Monday.ordinal: \t\t\t ${mon.ordinal}")
  println(s"Day.Monday.getClass: \t\t\t ${mon.getClass}")
  println(s"classOf[Day].isEnum: \t\t\t ${classOf[Day].isEnum}")
  println(s"Day.Monday  compareTo Day.Tuesday: \t ${mon compareTo tue}")
  println(s"Day.Monday  compareTo Day.Monday: \t ${mon compareTo mon}")
  println(s"Day.Tuesday compareTo Day.Monday: \t ${tue compareTo mon}")
  println(s"Day.Monday == Day.Tuesday: \t\t ${mon == tue}")
  println(s"Day.Monday == Day.Monday: \t\t ${mon == mon}")
  println(s"Day.Monday.getDeclaringClass: \t\t ${mon.getDeclaringClass}")
  println(s"Day.values: \t\t\t\t $days")
  println(s"""Day valueOf "Wednesday": \t\t ${Day valueOf "Wednesday"}""")
  println(s"""Enum.valueOf(classOf[Day], "Wednesday"): ${Enum.valueOf(classOf[Day], "Wednesday")}""")
  println(s"monday(Days.Monday): \t\t\t ${monday(mon)}")


Output:

Day.Monday:                              Monday
Day.Monday.name:                         Monday
Day.Monday.ordinal:                      1
Day.Monday.getClass:                     class scalax.Day
classOf[Day].isEnum:                     true
Day.Monday  compareTo Day.Tuesday:       -1
Day.Monday  compareTo Day.Monday:        0
Day.Tuesday compareTo Day.Monday:        1
Day.Monday == Day.Tuesday:               false
Day.Monday == Day.Monday:                true
Day.Monday.getDeclaringClass:            class scalax.Day
Day.values:                              List(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
Day valueOf "Wednesday":                 Wednesday
Enum.valueOf(classOf[Day], "Wednesday"): Wednesday
monday(Days.Monday):                     Monday!


The only limitation I found is that switch/case doesn't work in Java, because

an enum switch case label must be the unqualified name of an enumeration constant
             case Day.Monday():
                            ^


Not sure if we can fix that, considering that we basically depend on scalac's generation of static forwarders and don't have any power to tell it how it should be done. I don't think it matters that much anyway.

Thanks and bye,

Simon

Jason Zaugg

unread,
Aug 8, 2013, 7:21:30 AM8/8/13
to scala-l...@googlegroups.com
On Thu, Aug 8, 2013 at 1:15 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Hey guys,

want do you think of this? :D

Enum declaration:

@Enum
class Day {
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
}

I like the brevity, but I don't like rewriting expressions in the constructor as members.

For example, you have to know about the @Enum annotation to know that this method isn't called:

def Monday = println("!"); @Enum class Day { Monday }

-jason

Simon Ochsenreither

unread,
Aug 8, 2013, 8:19:38 AM8/8/13
to scala-l...@googlegroups.com
Hi Jason!


I like the brevity, but I don't like rewriting expressions in the constructor as members.

I would have certainly preferred the syntax of the type macro/untyped macro approach, but that's not a viable option anymore.
I'm still looking for better solutions and I'm happy about all suggestions I can get in this regard!

For example, you have to know about the @Enum annotation to know that this method isn't called:

def Monday = println("!"); @Enum class Day { Monday }

Yes, but I think this is pretty much exactly like in Java. Anyway, if we follow Java's style guide for enums, the amount of all-upercase-with-underscores method names might be quite limited in 99.99% of the code bases in the wild.

Bye,

Simon

Simon Ochsenreither

unread,
Aug 8, 2013, 8:32:07 AM8/8/13
to scala-l...@googlegroups.com

Simon Ochsenreither

unread,
Aug 8, 2013, 10:49:08 AM8/8/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither, abor...@gmail.com
> The only limitation I found is that switch/case doesn't work in Java,
> because
>
> an enum switch case label must be the unqualified name of an enumeration
> constant
>              case Day.Monday():
>                             ^
In case it is from Java, the Day. part is part of the problem. You are
not allowed to put in a case statement. (And it is usually not a method....)

Yes, that's basically the remaining issue: scalac never emits public static fields, only methods. As far as I know, there is not much I can do about it. :-(

Simon Ochsenreither

unread,
Aug 9, 2013, 5:07:37 AM8/9/13
to scala-l...@googlegroups.com, Miguel Garcia, Simon Ochsenreither
Hi Eugene!

It seems like the STATIC flag started to work, too and I have added a different approach which generates static members in the class instead of generating a companion object.

The good news is that it works from Java, The bad news is that Scala can't seem to figure out that new approach and complains that "Day is not a value", although it clearly supports that scheme with class files coming from javac.

I already tried marking the symbols as coming from Java, but without success.

Any ideas what could cause the issue or how to make scalac understand static members coming from Scala code?

Here is the code: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax/EnumMacroUsingStaticMembers.scala

And here is what scalac claims to generate:

class Day extends java.lang.Enum[Day] {
  final private def <init>(name: String, ordinal: Int) = {

    super.<init>(name, ordinal);
    ()
  };
  <static> val Monday: Day = new Day("Monday", 1);
  <static> val Tuesday: Day = new Day("Tuesday", 2);
  <static> val Wednesday: Day = new Day("Wednesday", 3);
  <static> val Thursday: Day = new Day("Thursday", 4);
  <static> val Friday: Day = new Day("Friday", 5);
  <static> val Saturday: Day = new Day("Saturday", 6);
  <static> val Sunday: Day = new Day("Sunday", 7);
  <static> private[this] val $VALUES: scala.Array[Day] = scala.Array.apply[Day](Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)(Predef.implicitly);
  <static> def values: scala.Array[Day] = $VALUES.clone();
  <static> def valueOf(name: String): Day = java.lang.Enum.valueOf(classOf[Day], name)
}


Error messages:

[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:4: not found: value Day
[error]   val mon = Day.Monday
[error]             ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:5: not found: value Day
[error]   val tue = Day.Tuesday
[error]             ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:6: not found: value Day
[error]   val days = Day.values.toList
[error]              ^
[error] /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaConsumer.scala:8: not found: value Day
[error]     case Day.Monday => "Monday!"
[error]          ^


Another issue I'm seeing is that the static fields are not initialized at all. Could it be that the initializers are put into the wrong place (e. g. into instance initialization, not into class initialization, despite STATIC being set)?

Any ideas about these issues?

Thanks!

Simon

Eugene Burmako

unread,
Aug 9, 2013, 5:11:18 AM8/9/13
to scala-l...@googlegroups.com
"Not found: value Day" means that you need to have a symbol with term name Day in scope. Since you're no longer generating a companion, you no longer have such a symbol.

Simon Ochsenreither

unread,
Aug 9, 2013, 6:36:26 AM8/9/13
to scala-l...@googlegroups.com

"Not found: value Day" means that you need to have a symbol with term name Day in scope. Since you're no longer generating a companion, you no longer have such a symbol.

But then calling a static method defined in Java wouldn't work, too, right? So scalac already knows how this is handled, I just don't find how I can trigger it for the enums.
I tried setting the JAVA flag for my class with static members, but that didn't help.

Eugene Burmako

unread,
Aug 9, 2013, 6:56:17 AM8/9/13
to scala-l...@googlegroups.com
Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.


--

Simon Ochsenreither

unread,
Aug 9, 2013, 8:04:42 AM8/9/13
to scala-l...@googlegroups.com

Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.

Thanks for the hint ... is there a way this mechanism can be leveraged for our use-case here? This way I might even get public fields to work, because they have to be represented somehow as symbols in the Scala AST.

Eugene Burmako

unread,
Aug 9, 2013, 10:54:46 AM8/9/13
to scala-l...@googlegroups.com
Not sure, needs investigation.


On 9 August 2013 14:04, Simon Ochsenreither <simon.och...@gmail.com> wrote:

Scalac sneakily converts symbols from java class files into class+module combos, pushing static parts to modules.

Thanks for the hint ... is there a way this mechanism can be leveraged for our use-case here? This way I might even get public fields to work, because they have to be represented somehow as symbols in the Scala AST.

--

Eugene Burmako

unread,
Aug 9, 2013, 1:23:50 PM8/9/13
to scala-l...@googlegroups.com
I can confirm that I can reproduce the bug. Currently looking into it.

On Tuesday, August 6, 2013 10:12:26 PM UTC+2, Tim Underwood wrote:

If you comment out the macro paradise plugin it compiles fine.  Also if you comment out the @BeanInfo annotation on the Bar object in Bar.scala it compiles fine.

Let me know if I can do anything else to help.

-Tim


On Tuesday, August 6, 2013 9:22:21 AM UTC-7, Eugene Burmako wrote:
Thanks a lot for the crash report! It would be great if you could minimize the reproduction. I will gladly take a look.


On 6 August 2013 17:46, Tim Underwood <timund...@gmail.com> wrote:
Hi Eugene,

I tried adding the compiler plugin to my existing project so I could play with the new macro features but I get a compiler AssertionError (stack trace below).  I haven't tried using any of the new macro features yet and the code compiles fine using Scala 2.10.2 without the macro paradise plugin.

I'm trying to minimize the code that causes the error but haven't been successful so far.  Does the stack trace give you any hints as to what the problem might be?

Thanks,

-Tim



Stack Trace:

at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1210)
at scala.reflect.internal.Symbols$ModuleClassSymbol.implicitMembers(Symbols.scala:3095)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1073)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1113)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$getParts$1(Implicits.scala:1120)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.companionImplicitMap(Implicits.scala:1147)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.scala$tools$nsc$typechecker$Implicits$ImplicitSearch$$implicitsOfExpectedType(Implicits.scala:1167)
at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.bestImplicit(Implicits.scala:1427)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:82)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Implicits$class.inferImplicit(Implicits.scala:40)
at org.scalalang.macroparadise.Plugin$$anon$1.inferImplicit(Plugin.scala:25)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$wrapImplicit$1(Typers.scala:202)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:209)
at scala.tools.nsc.typechecker.Typers$Typer.inferView(Typers.scala:180)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply$mcZ$sp(Typers.scala:105)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2$$anonfun$isCoercible$1.apply(Typers.scala:103)
at scala.reflect.internal.Types$UndoLog.undo(Types.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer$$anon$2.isCoercible(Typers.scala:102)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isCompatible(Infer.scala:425)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer$$anonfun$isCompatibleArgs$1.apply(Infer.scala:428)
at scala.collection.LinearSeqLike$class.corresponds(LinearSeqLike.scala:76)
at scala.collection.immutable.List.corresponds(List.scala:84)
at scala.tools.nsc.typechecker.Infer$Inferencer.isCompatibleArgs(Infer.scala:428)
at scala.tools.nsc.typechecker.Infer$Inferencer.typesCompatible$1(Infer.scala:814)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:833)
at scala.tools.nsc.typechecker.Infer$Inferencer.tryTupleApply$1(Infer.scala:809)
at scala.tools.nsc.typechecker.Infer$Inferencer.scala$tools$nsc$typechecker$Infer$Inferencer$$isApplicable(Infer.scala:856)
at scala.tools.nsc.typechecker.Infer$Inferencer.isApplicableSafe(Infer.scala:877)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3146)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$63.apply(Typers.scala:3135)
at scala.collection.TraversableLike$$anonfun$filter$1.apply(TraversableLike.scala:264)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.TraversableLike$class.filter(TraversableLike.scala:263)
at scala.collection.AbstractTraversable.filter(Traversable.scala:105)
at scala.reflect.internal.Symbols$Symbol.filter(Symbols.scala:1658)
at scala.tools.nsc.typechecker.Typers$Typer.preSelectOverloaded$1(Typers.scala:3135)
at scala.tools.nsc.typechecker.Typers$Typer.doTypedApply(Typers.scala:3163)
at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4589)
at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4621)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5526)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5683)
at scala.tools.nsc.typechecker.Typers$Typer.computeType(Typers.scala:5770)
at scala.tools.nsc.typechecker.Namers$Namer.assignTypeToTree(Namers.scala:834)
at scala.tools.nsc.typechecker.Namers$Namer.valDefSig(Namers.scala:1315)
at scala.tools.nsc.typechecker.Namers$Namer.getSig$1(Namers.scala:1456)
at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1465)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply$mcV$sp(Namers.scala:731)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1$$anonfun$apply$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer.scala$tools$nsc$typechecker$Namers$Namer$$logAndValidate(Namers.scala:1498)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:730)
at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$monoTypeCompleter$1.apply(Namers.scala:729)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.completeImpl(Namers.scala:704)
at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter$class.complete(Namers.scala:1621)
at org.scalalang.macroparadise.typechecker.Namers$$anon$3.complete(Namers.scala:702)
at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1229)
at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1365)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:39)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1919)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedTemplate(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedTemplate(Typers.scala:51)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedTemplate(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1759)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typedClassDef(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedClassDef(Typers.scala:62)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typedClassDef(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5545)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2926)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$60.apply(Typers.scala:3030)
at scala.collection.immutable.List.loop$1(List.scala:170)
at scala.collection.immutable.List.mapConserve(List.scala:186)
at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3030)
at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5263)
at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5549)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.org$scalalang$macroparadise$typechecker$Typers$ParadiseTyper$$super$typed1(Analyzer.scala:17)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedPackageDef$1(Typers.scala:35)
at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:43)
at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:17)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5604)
at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5666)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:99)
at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:464)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:91)
at scala.collection.Iterator$class.foreach(Iterator.scala:727)
at scala.collection.AbstractIterator.foreach(Iterator.scala:1157)
at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:91)
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1583)
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1557)
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1553)
at scala.tools.nsc.Global$Run.compile(Global.scala:1662)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:123)
at xsbt.CachedCompiler0.run(CompilerInterface.scala:99)
at xsbt.CompilerInterface.run(CompilerInterface.scala:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:102)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:48)
at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply$mcV$sp(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile$$anonfun$3$$anonfun$compileScala$1$1.apply(AggressiveCompile.scala:98)
at sbt.compiler.AggressiveCompile.sbt$compiler$AggressiveCompile$$timed(AggressiveCompile.scala:159)
at sbt.compiler.AggressiveCompile$$anonfun$3.compileScala$1(AggressiveCompile.scala:97)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:142)
at sbt.compiler.AggressiveCompile$$anonfun$3.apply(AggressiveCompile.scala:86)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:38)
at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:36)
at sbt.inc.Incremental$.cycle(Incremental.scala:73)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:33)
at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:32)
at sbt.inc.Incremental$.manageClassfiles(Incremental.scala:41)
at sbt.inc.Incremental$.compile(Incremental.scala:32)
at sbt.inc.IncrementalCompile$.apply(Compile.scala:26)
at sbt.compiler.AggressiveCompile.compile2(AggressiveCompile.scala:150)
at sbt.compiler.AggressiveCompile.compile1(AggressiveCompile.scala:70)
at sbt.compiler.AggressiveCompile.apply(AggressiveCompile.scala:45)
at sbt.Compiler$.apply(Compiler.scala:70)
at sbt.Defaults$.sbt$Defaults$$compileTaskImpl(Defaults.scala:722)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at sbt.Defaults$$anonfun$compileTask$1.apply(Defaults.scala:716)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:42)
at sbt.std.Transform$$anon$4.work(System.scala:64)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)


On Monday, August 5, 2013 6:53:16 AM UTC-7, Eugene Burmako wrote:
Hi folks,

I've just released a new version of macro paradise, which represents a significant revamp and introduces new features. You can read the full text of the announcement at http://scalamacros.org/news/2013/08/05/macro-paradise-2.0.0-snapshot.html.

Short summary:
* Now a compiler plugin that works with Scala 2.10.2
* Backports quasiquotes from 2.11.0-M4
* Also provides freshly implemented macro annotations

Your feedback is very welcome!

Cheers,
Eugene

Eugene Burmako

unread,
Aug 9, 2013, 2:19:58 PM8/9/13
to scala-l...@googlegroups.com
Now fixed. Patched version of the paradise plugin is already on Sonatype. Could you verify that it works for you?

Tim Underwood

unread,
Aug 9, 2013, 4:00:31 PM8/9/13
to scala-l...@googlegroups.com
Yup it works now.  Thanks!

-Tim

Simon Ochsenreither

unread,
Aug 10, 2013, 10:17:31 AM8/10/13
to scala-l...@googlegroups.com

Why are you not using quasiquotes?

I just ported my code to quasi-quotations and what Den and you have done here is just INCREDIBLY, AMAZINGLY IMPRESSIVE!

Have a look: https://github.com/soc/enum-paradise/commit/591d5b928a53d16bf2e11129c9984a84e5fac795

I have a few notes, though:
  • private def ${nme.CONSTRUCTOR}(name: String, ordinal: Int) = ... works, but private def <init>(name: String, ordinal: Int) = ... doesn't. From the error message, it seems like it thinks that it's the start of some XML fragment. Is this intended/expected? (Using nme.CONSTRUCTOR is probably better anyway, but just asking)
  • There is a lot of uglyness and bad error messages around Unit-returning methods. Is there a plan to work on that?
    (I have the "you always need to append () manually" in mind.)
  • I had to try lot of slightly different variants until I got things to compile and even now I get warning messages:
    [warn] embeddedFile--QuasiquoteCompat.scala@ca20d3eaa08c4dba9413fbeb5c05faa8:320: Adapting argument list by inserting (): leaky (Object-receiving) target makes this especially dangerous.
    [warn]         signature: ConstantExtractor.apply(value: Any): Constants.this.Constant
    [warn]   given arguments: <none>
    [warn]  after adaptation: ConstantExtractor((): Unit)
    [warn]               DefDef(NoMods, newTermName("$init$"), List(), List(List()), TypeTree(), u.Block(lvdefs, Literal(Constant())))))
    [warn]                                                                                                                       ^
    [warn] embeddedFile--QuasiquoteCompat.scala@ca20d3eaa08c4dba9413fbeb5c05faa8:329: Adapting argument list by inserting (): leaky (Object-receiving) target makes this especially dangerous.
    [warn]         signature: ConstantExtractor.apply(value: Any): Constants.this.Constant
    [warn]   given arguments: <none>
    [warn]  after adaptation: ConstantExtractor((): Unit)
    [warn]               DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), u.Block(lvdefs ::: List(superCall), Literal(Constant())))))
    [warn]                                                                                                                                      ^
    [warn] two warnings found

  • It's a bit annoying that quasi-quotes are always recompiled ... I already read that the amount of fixes one can do here is limited, but maybe some hashing for "pure" quasi-quotes might make sense? E. g. "if the qq doesn't refer to anything and the hashing the qq results in an identical value as the stored one, don't recompile".

I hope this is helpful!

Thanks,

Simon

Simon Ochsenreither

unread,
Aug 10, 2013, 5:27:27 PM8/10/13
to scala-l...@googlegroups.com
Hi!

Another issue I found:

You can write code like ...

tq"""Enum[$className]"""

... but if you want to be really sure that things don't break if people define their own Enum class in scope, one better does:

tq"""_root_.java.lang.Enum[$className]"""

... but that gets pretty verbose and tedious, so one refactors it a bit:

val Enum = "_root_.java.lang.Enum"
tq"""$Enum[$className]"""


But this causes:

[error] exception during macro expansion:
[error] java.lang.AssertionError: assertion failed: "java.lang.Enum"
[error]     at scala.reflect.internal.Trees$AppliedTypeTree.<init>(Trees.scala:481)
[error]     at scala.reflect.internal.Trees$AppliedTypeTree$.apply(Trees.scala:478)
[error]     at scala.reflect.internal.Trees$AppliedTypeTree$.apply(Trees.scala:486)


Ouch.

val Enum = newTypeName("_root_.java.lang.Enum")
tq"""$Enum[$className]"""


Now I suddenly have to care about TermNames vs. TypeNames again!

I think we have two issues here with the current situation:
  • The "easy" way is extremely fragile and one gets no warning about it
  • Doing it "correctly" (fully-qualified names, no duplication) is a lot more verbose and invalidates one of the key features of quasi-quotes: Not having to mess with TermNames vs. TypeNames.

I think it is pretty unfortunate that the wrong way is easy, readable and concise while the right way is more complicated and verbose.

Is there a way to fix this?

By the way, I think the error messages caused by the assertions could be made more useful, e. g. "java.lang.Enum is not a type" instead of "java.lang.Enum".

Thanks and bye,

Simon

Eugene Burmako

unread,
Aug 10, 2013, 5:37:30 PM8/10/13
to scala-l...@googlegroups.com
Don't worry - the easy way will eventually become the right way. In 2.11 we just lack necessary tech for that.


--

Simon Ochsenreither

unread,
Aug 10, 2013, 5:51:38 PM8/10/13
to scala-l...@googlegroups.com

val Enum = newTypeName("_root_.java.lang.Enum")
tq"""$Enum[$className]"""


This doesn't seem to work either, I need to use ...

val Enum   = Select(Select(Ident(newTermName("java")), newTermName("lang")), newTypeName("Enum"))

of course. :-/

Simon Ochsenreither

unread,
Aug 10, 2013, 5:52:38 PM8/10/13
to scala-l...@googlegroups.com
Cool! Thanks, good to know that you are aware of the issues already.
What's the battle plan here?

Eugene Burmako

unread,
Aug 10, 2013, 5:56:21 PM8/10/13
to scala-l...@googlegroups.com
You could use quasiquotes in the previous email. There's no plan. Yet :)


On Sat, Aug 10, 2013 at 11:52 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:

Cool! Thanks, good to know that you are aware of the issues already.
What's the battle plan here?

--

Eugene Burmako

unread,
Aug 10, 2013, 8:21:37 PM8/10/13
to scala-l...@googlegroups.com
Speaking of the plan, here are some preliminary thoughts on hygiene for Scala macros: https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI

Eugene Burmako

unread,
Aug 10, 2013, 8:30:15 PM8/10/13
to scala-l...@googlegroups.com
1) Denys will probably have something more to tell, but to me it looks like the nme.CONSTRUCTOR approach is the only possible option.

2) Do you mean 3, or there's something else to it?

3) Do you get these warnings just by compiling sources that contain quasiquotes, or I need to do something special to reproduce them?

4) What do you mean always recompiled?

Simon Ochsenreither

unread,
Aug 11, 2013, 4:28:02 AM8/11/13
to scala-l...@googlegroups.com

1) Denys will probably have something more to tell, but to me it looks like the nme.CONSTRUCTOR approach is the only possible option.

Ok, thanks.
By the way, I think "nme" and "tpnme" are unnecessarily cryptic for a public API ... would it be possible to name them termNames and typeNames and deprecate the old names?

 
2) Do you mean 3, or there's something else to it?

I didn't save the error message, but the error message when you don't define your constructor properly (missing (), wrong return type, etc.) is not helpful, despite that the compiler/reflection API exactly knows what was intended (due to nme.CONSTRUCTOR).


3) Do you get these warnings just by compiling sources that contain quasiquotes, or I need to do something special to reproduce them?

Just quasiquotes. I think the mandatory () at the end of the constructor bodies trigger some faulty heuristic. I guess it checks a bit like "Ok, what's the position of () in the source? ... Oh, there isn't one? (because there isn't a source position for anything, because there is no real source) ... Let's print a warning because it seems like the compiler had to insert the () itself!"


4) What do you mean always recompiled?

Eugene Burmako

unread,
Aug 11, 2013, 5:34:19 AM8/11/13
to scala-l...@googlegroups.com
1) I think that's a good idea. Mind opening an issue / submitting a pull request?

2-3) Is it reproducible in 2.11.0-M4? If yes, then I'd submit an issue to JIRA.

4) I see. So when you modify a quasiquote in a macro, the entire project that uses that macro gets recompiled?


--

Simon Ochsenreither

unread,
Aug 11, 2013, 5:51:15 AM8/11/13
to scala-l...@googlegroups.com

1) I think that's a good idea. Mind opening an issue / submitting a pull request?

I'll do as soon as the build system is in a stable state again. I don't want to get flooded by dozens of automated your-PR-is-marked-as-failed-because-Jenkins-has-gone-mad mails.

2-3) Is it reproducible in 2.11.0-M4? If yes, then I'd submit an issue to JIRA.

Yes, I'll submit a bug.

4) I see. So when you modify a quasiquote in a macro, the entire project that uses that macro gets recompiled?

I think the issue is that stuff _always_ gets recompiled, because the compiler can't tell whether the macro has changed.

Eugene Burmako

unread,
Aug 11, 2013, 5:52:36 AM8/11/13
to scala-l...@googlegroups.com
4) We'll have to address that for sure. Probably not right now, though.


--

Denys Shabalin

unread,
Aug 14, 2013, 10:58:11 AM8/14/13
to scala-l...@googlegroups.com
This should work:

val Enum = tq"_root_.java.lang.Enum" 
tq"$Enum[$className]"

Explanations regarding your previous attempts:

1. strings are neither name nor AST equivalents, they are just strings; when you try to splice them they get lifted to their corresponding representation: constant literals. This is actually expected behavior to make things like:

val s = "hello"
q"f($s)"

translate into

q""" f("hello") """

2. paths are trees too (combination of selects and idents), not names, that's why you need q"" or tq"" to refactor path out

3. names generally represent plain identifiers and have to be manually created with TermName/TypeName (they can't be magically created out of strings)

Grzegorz Kossakowski

unread,
Aug 16, 2013, 3:56:59 PM8/16/13
to scala-l...@googlegroups.com

On 7 August 2013 07:35, Simon Ochsenreither <simon.och...@gmail.com> wrote:

The ENUM is not set by Scala compiler anywhere (it's only used to represent types coming from Java) so backend doesn't support it in any way.

I wouldn't be very against adding support for it but one would have to figure out exact semantics. For the start:
  • should ENUM flag be pickled? (probably yes)
  • would class file parser be confused by the fact that there's a scala class that has an enum flag?
  • would referring to enums synthesized from scala files work?
These are good questions!

I think a pretty idiomatic and minimal change would look like this: https://github.com/soc/scala/compare/topic;enums?expand=1
There are two isEnum methods, which aren't strictly required, so they could also be left out.

This way, we don't have any special-cased code anywhere in the compiler, it just wires up the a modifier flag to the appropriate ACC_ENUM, just like for all the other class file bits.
Creating enums would happen completely in the macro.

When you add a new flag it's visible everywhere in the compiler so one must describe its contract in general terms and not only in terms of macro that happens to set that flag.

If we introduce that flag, we have to figure out if we are pickling it or not. In your case it seems like we are pickling it and I think that's the right thing to do.

Now, if we have an ENUM flag then how do we represent enums coming from Java? Should we set that flag while reading class files? I think, the answer should be yes because that's the only way to get uniform treatment of enums defined in Java or Scala.

Enums are already special-cased in Scala compiler so if you introduce a new flag you actually need to clean up that special casing or you risk introduction of bugs and inconsistencies.

I'm pointing out that there's more work to be done not to discourage you but just set the right expectations. For example, we all know that our current logic for classpath scanning is super slow. Paul reimplemented that logic and made it significantly faster. Why isn't it merged yet? Oh because we have a lot of logic depending on current structure of classpath scanning. The end result is that reimplementing classpath scanning is relatively easy, plugging it into compiler is really hard and time consuming because a lot of old logic has to be cleaned up. That's our life.

--
Grzegorz Kossakowski
Scalac hacker at Typesafe
twitter: @gkossakowski

Simon Ochsenreither

unread,
Aug 20, 2013, 4:27:17 PM8/20/13
to scala-l...@googlegroups.com
Hi Eugene,

I think I have managed to make quite some progress, but I'm getting a compiler crash currently where I'm not sure what to do about it. Could you have a look and tell me what you think?

Enum implementation: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax/EnumMacroUsingStaticMembers.scala
  • Note that I'm explicitly setting STATIC | JAVA | STABLE for the enum item fields

Enum support in 2.10.2: https://github.com/soc/scala/compare/scala:2.10.x...topic/enums-2.10?expand=1
  • Note the additional check for enum items preventing the generation of additional getters

Error message: https://gist.github.com/soc/6286665
  • I'm especially confused why all return types in the offending AST are gone, although they have all been set explicitly


Any idea what's the issue here?

Thanks,

Simon

Eugene Burmako

unread,
Aug 20, 2013, 11:02:01 PM8/20/13
to scala-l...@googlegroups.com
Looking into it now.

Some random notes:
1) I'd suggest upgrading to 2.10.3-RC1-2.0.0-SNAPSHOT (and to 2.10.3, when it's released). At the moment the only difference with 2.10.2 is the fact that Literal(Constant()) things are fixed, but later on there might be more. If there won't
2) I could reproduce the crash with your scalac fork by setting scalaHome and running "sbt compile", but everything works fine with 2.10.x, which suggests that something's wrong in your fork. Let's see whether I'll be able to figure it out before the train arrives to the airport.


--

Eugene Burmako

unread,
Aug 20, 2013, 11:03:22 PM8/20/13
to scala-l...@googlegroups.com
1) (Forgot to finish the sentence, sorry) If there aren't going to be folks particularly attached to 2.10.2, I'll only be maintaining the 2.10.3 branch of macro paradise.

Eugene Burmako

unread,
Aug 20, 2013, 11:42:45 PM8/20/13
to scala-l...@googlegroups.com
Okay, I figured it out.

You're already trying to convince namer to leave your vals alone by patching noEnterGetterSetter, but that's not enough. You also need to patch noFinishGetterSetter and probably something else as well (I'm not sure though). Nevertheless, just patching noFinishGetterSetter lets us advance by avoiding the crash and getting just a compilation error:

[error] /Users/xeno_by/Projects/enum-paradise/core/src/main/scala/scalax/Day.scala:3: type mismatch;
[error]  found   : <notype>(Monday)
[error]  required: scalax.Day
[error] @enum
[error]  ^

Now what's that? Some printlns sprinkled over the namer explain the reason. When completing the type of the enum class, Namers.templateSig receives a template which contains members like:

  <stable> <static> val Monday: Day = new Day("Monday", 1);
  <stable> <static> val Tuesday: Day = new Day("Tuesday", 2);
  <stable> <static> val Wednesday: Day = new Day("Wednesday", 3);

And then it calls enterSym on each member. Following enterSym up to enterValDef reveals an interesting hardcode that forcefully sets types of STABLE | JAVA vals to ConstantType(Constant(tree.symbol)) [1]. That's the culprit, and that needs to be worked around.

Please note that it wouldn't be enough to just remove or wrap this hardcode to exclude your vals (e.g. by additionally making sure that the file that defines the symbol is *.java), because this part of Namer is overridden by macro paradise [2], so we'll need to coordinate to propagate your changes to paradise. For now it would probably be the simplest to use a local build of paradise, and if necessary I could give you right to commit to paradise.

[1] https://github.com/scala/scala/blob/v2.10.2/src/compiler/scala/tools/nsc/typechecker/Namers.scala#L607
[2] https://github.com/scalamacros/paradise/blob/7f2daa9ab9c4473ae93a52ac3bebb2517678f3f8/plugin/src/main/scala/org/scalalang/macroparadise/typechecker/Namers.scala#L208


On 20 August 2013 22:27, Simon Ochsenreither <simon.och...@gmail.com> wrote:

--

Simon Ochsenreither

unread,
Aug 21, 2013, 10:04:25 AM8/21/13
to scala-l...@googlegroups.com
Thanks a lot!


You also need to patch noFinishGetterSetter

Ouch ... I searched only in Namers and decided that this was dead code because I saw no usage there. But it is indeed used somewhere else. :-/

And then it calls enterSym on each member. Following enterSym up to enterValDef reveals an interesting hardcode that forcefully sets types of STABLE | JAVA vals to ConstantType(Constant(tree.
symbol)) [1]. That's the culprit, and that needs to be worked around.

Yes, I saw that ... I left it in because I assumed that if it worked for enums in Java, it should work for those enums, too. Do you have any idea what is going wrong here? Is it because the class never gets actually generated?

I'll look into it, but I'm currently stuck because SBT decided to stop working: https://groups.google.com/d/topic/simple-build-tool/Dd3gEN5AS0U/discussion

Thanks a lot!

Bye,

Simon

Eugene Burmako

unread,
Aug 21, 2013, 5:03:13 PM8/21/13
to scala-l...@googlegroups.com
Tbh I've no idea how this works for Java. Maybe there's some other hardcode in that case...


--

Simon Ochsenreither

unread,
Aug 21, 2013, 8:56:12 PM8/21/13
to scala-l...@googlegroups.com
Current status:

public class JavaDayConsumer {
    public static void main(String[] args) {
    System.out.println(Day.Monday);
    }
}


Compare ...

$ javap JavaDay
Compiled from "JavaDay.java"
public final class scalax.JavaDay extends java.lang.Enum<scalax.JavaDay> {
  public static final scalax.JavaDay Monday;
  public static final scalax.JavaDay Tuesday;
  public static final scalax.JavaDay Wednesday;
  public static final scalax.JavaDay Thursday;
  public static final scalax.JavaDay Friday;
  public static final scalax.JavaDay Saturday;
  public static final scalax.JavaDay Sunday;
  public static scalax.JavaDay[] values();
  public static scalax.JavaDay valueOf(java.lang.String);
  static {};
}


... with ...

$ javap Day
Warning: Binary file Day contains scalax.Day
Compiled from "Day.scala"
public class scalax.Day extends java.lang.Enum<scalax.Day> {
  public static final scalax.Day Monday;
  public static final scalax.Day Tuesday;
  public static final scalax.Day Wednesday;
  public static final scalax.Day Thursday;
  public static final scalax.Day Friday;
  public static final scalax.Day Saturday;
  public static final scalax.Day Sunday;
  public static scalax.Day[] values();
  public static scalax.Day valueOf(java.lang.String);
}


:-D
Looks pretty good, imho!

Three remaining issues:
  • How can I refer to a static method in a class from the companion object of that class?
    (The issue is that e. g. Day.Monday works fine from Java, but not from Scala. My solution would be to emit a companion object with forwarders to the static fields of the enum class.)
  • Despite setting the ENUM flag for the fields (which seems to be required for using enums in annotations: “error: an enum annotation value must be an enum constant”), it doesn't show up in the classfile. (JavaDay.Monday has ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM, but Day.Monday has only ACC_PUBLIC, ACC_STATIC, ACC_FINAL. The weird thing is that setting the flag for the class itself works.)
  • The initialization of the static fields ends up in the constructor instead of the static initializer:
      static {};
        flags: ACC_STATIC
        Code:
          stack=4, locals=0, args_size=0
             0: new           #4                  // class scalax/JavaDay
             3: dup          
             4: ldc           #7                  // String Monday
             6: iconst_0     
             7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
            10: putstatic     #9                  // Field Monday:Lscalax/JavaDay;
      ...

    vs.
      private scalax.Day(java.lang.String, int);
        Code:
           0: aload_0      
           1: aload_1      
           2: iload_2      
           3: invokespecial #39                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
           6: new           #2                  // class scalax/Day
           9: dup          
          10: ldc           #40                 // String Monday
          12: iconst_1     
          13: invokespecial #41                 // Method "<init>":(Ljava/lang/String;I)V
          16: putstatic     #43                 // Field Monday:Lscalax/Day;
      ...
    I think this is only a matter of adding a check for STATIC somewhere, but I haven't found the right place yet.

Ideas/suggestions/opinions?

Newest code is here:

Scala: https://github.com/soc/scala/commits/topic/enums-2.10
Enums: https://github.com/soc/enum-paradise/tree/java-enums-via-macro-annotations

Please let me know what you think!

Thanks,

Simon

Simon Ochsenreither

unread,
Aug 21, 2013, 9:12:59 PM8/21/13
to scala-l...@googlegroups.com
  • The initialization of the static fields ends up in the constructor instead of the static initializer:

  • I think this is only a matter of adding a check for STATIC somewhere, but I haven't found the right place yet.

Martin Grigorov

unread,
Aug 22, 2013, 3:22:14 AM8/22/13
to scala-l...@googlegroups.com
Hi Simon,


On Thu, Aug 22, 2013 at 3:56 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Current status:

public class JavaDayConsumer {
    public static void main(String[] args) {
    System.out.println(Day.Monday);
    }
}


Compare ...

$ javap JavaDay
Compiled from "JavaDay.java"
public final class scalax.JavaDay extends java.lang.Enum<scalax.JavaDay> {
  public static final scalax.JavaDay Monday;
  public static final scalax.JavaDay Tuesday;
  public static final scalax.JavaDay Wednesday;
  public static final scalax.JavaDay Thursday;
  public static final scalax.JavaDay Friday;
  public static final scalax.JavaDay Saturday;
  public static final scalax.JavaDay Sunday;
  public static scalax.JavaDay[] values();
  public static scalax.JavaDay valueOf(java.lang.String);
  static {};
}


... with ...

$ javap Day
Warning: Binary file Day contains scalax.Day
Compiled from "Day.scala"
public class scalax.Day extends java.lang.Enum<scalax.Day> {

I see a small difference here - the class generated by Scala is not final, the one with Java is final.
 

--

Eugene Burmako

unread,
Aug 22, 2013, 2:40:57 PM8/22/13
to scala-l...@googlegroups.com, Grzegorz Kossakowski
Answering these questions requires backend knowledge which I lack unfortunately. Greg, maybe you could chime in?


--

Simon Ochsenreither

unread,
Nov 11, 2013, 2:34:41 PM11/11/13
to scala-l...@googlegroups.com, Grzegorz Kossakowski
Hi Eugene,

I have been updating and porting the code from 2.10 to 2.11 and now I'm seeing a weird error:

> compile
[info] Compiling 5 Scala sources and 3 Java sources to /home/soc/Entwicklung/enum-paradise/core/target/scala-2.11.0-SNAPSHOT/classes...
final class Day extends java.lang.Enum[Day] {
  private def <init>(nameX: String, ordinal: Int) = {
    super.<init>(nameX, ordinal);
    ()
  };

  <stable> <static> val Monday: Day = new Day("Monday", 1);
  <stable> <static> val Tuesday: Day = new Day("Tuesday", 2);
  <stable> <static> val Wednesday: Day = new Day("Wednesday", 3);
  <stable> <static> val Thursday: Day = new Day("Thursday", 4);
  <stable> <static> val Friday: Day = new Day("Friday", 5);
  <stable> <static> val Saturday: Day = new Day("Saturday", 6);
  <stable> <static> val Sunday: Day = new Day("Sunday", 7);
  <static> private[this] val $VALUES: scala.Array[Day] = scala.Array.apply[Day](Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)(Predef.implicitly);
  <static> def values: scala.Array[Day] = $VALUES.clone();
  <static> def valueOf(name: String): Day = java.lang.Enum.valueOf(classOf[Day], name)
}
object Day extends  {
  private def <init>() = {
    super.<init>();
    ()
  }
}
[trace] Stack trace suppressed: run last core/compile:compile for the full output.
[error] (core/compile:compile) java.util.NoSuchElementException: key not found: nameX
[error] Total time: 1 s, completed Nov 11, 2013 8:31:43 PM
> last core/compile:compile
[debug]
[debug] Initial source changes:
[debug]     removed:Set()
[debug]     added: Set(/home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/Day.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayMethods.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/DayAnnotation.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDayConsumer.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDay.java, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayConstructors.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayConsumer.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayMethodConsumer.scala)
[debug]     modified: Set()
[debug] Removed products: Set()
[debug] Modified external sources: Set()
[debug] Modified binary dependencies: Set()
[debug] Initial directly invalidated sources: Set(/home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/Day.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayMethods.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/DayAnnotation.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDayConsumer.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDay.java, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayConstructors.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayConsumer.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayMethodConsumer.scala)
[debug]
[debug] Sources indirectly invalidated by:
[debug]     product: Set()
[debug]     binary dep: Set()
[debug]     external source: Set()
[debug] All initially invalidated sources: Set(/home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/Day.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayMethods.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/DayAnnotation.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDayConsumer.java, /home/soc/Entwicklung/enum-paradise/core/src/main/java/scalax/JavaDay.java, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/DayConstructors.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayConsumer.scala, /home/soc/Entwicklung/enum-paradise/core/src/main/scala/scalax/ScalaDayMethodConsumer.scala)
[debug] Recompiling all 8 sources: invalidated sources (8) exceeded 50.0% of all sources
[info] Compiling 5 Scala sources and 3 Java sources to /home/soc/Entwicklung/enum-paradise/core/target/scala-2.11.0-SNAPSHOT/classes...
[debug] Getting compiler-interface from component compiler for Scala 2.11.0-SNAPSHOT
[debug] Getting compiler-interface from component compiler for Scala 2.11.0-SNAPSHOT
[debug] Running cached compiler 73e9745, interfacing (CompilerInterface) with Scala compiler version 2.11.0-20131108-232311-bf512ae916
[debug] Calling Scala compiler with arguments  (CompilerInterface):
[debug]     -deprecation
[debug]     -feature
[debug]     -Xlint
[debug]     -Xplugin:/home/soc/.ivy2/cache/org.scala-lang.plugins/macro-paradise_2.11.0-SNAPSHOT/jars/macro-paradise_2.11.0-SNAPSHOT-2.0.0-SNAPSHOT.jar
[debug]     -bootclasspath
[debug]     /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/netx.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/plugin.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/rhino.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/jfr.jar:/usr/lib/jvm/java-7-openjdk-amd64/jre/classes:/home/soc/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.11.0-SNAPSHOT.jar
[debug]     -classpath
[debug]     /home/soc/Entwicklung/enum-paradise/core/target/scala-2.11.0-SNAPSHOT/classes:/home/soc/Entwicklung/enum-paradise/macros/target/scala-2.11.0-SNAPSHOT/classes:/home/soc/.ivy2/cache/org.scala-lang/scala-compiler/jars/scala-compiler-2.11.0-SNAPSHOT.jar:/home/soc/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11.0-M5/jars/scala-xml_2.11.0-M5-1.0-RC4.jar:/home/soc/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.11.0-M5/jars/scala-parser-combinators_2.11.0-M5-1.0-RC2.jar:/home/soc/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.11.0-SNAPSHOT.jar
java.util.NoSuchElementException: key not found: nameX
    at scala.collection.MapLike$class.default(MapLike.scala:228)
    at scala.collection.AbstractMap.default(Map.scala:59)
    at scala.collection.MapLike$class.apply(MapLike.scala:141)
    at scala.collection.AbstractMap.apply(Map.scala:59)
    at org.scalalang.macroparadise.reflect.Trees$SyntacticClassDef$$anonfun$6.apply(Trees.scala:44)
    at org.scalalang.macroparadise.reflect.Trees$SyntacticClassDef$$anonfun$6.apply(Trees.scala:43)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
    at scala.collection.immutable.List.foreach(List.scala:302)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:245)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at scala.reflect.internal.util.Collections$$anonfun$mmap$1.apply(Collections.scala:36)
    at scala.reflect.internal.util.Collections$$anonfun$mmap$1.apply(Collections.scala:36)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
    at scala.collection.immutable.List.foreach(List.scala:302)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:245)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at scala.reflect.internal.util.Collections$class.mmap(Collections.scala:36)
    at scala.reflect.internal.SymbolTable.mmap(SymbolTable.scala:14)
    at org.scalalang.macroparadise.reflect.Trees$SyntacticClassDef$.unapply(Trees.scala:43)
    at org.scalalang.macroparadise.reflect.TreeInfo$ParadiseTreeInfo.org$scalalang$macroparadise$reflect$TreeInfo$ParadiseTreeInfo$$loop$1(TreeInfo.scala:23)
    at org.scalalang.macroparadise.reflect.TreeInfo$ParadiseTreeInfo.getAnnotationZippers(TreeInfo.scala:92)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$class.finishSymbol(Namers.scala:141)
    at org.scalalang.macroparadise.typechecker.Analyzer$$anon$2.finishSymbol(Analyzer.scala:15)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$class.dispatch$1(Namers.scala:28)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$class.enterSym(Namers.scala:34)
    at org.scalalang.macroparadise.typechecker.Analyzer$$anon$2.enterSym(Analyzer.scala:15)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$enterSyms$1.apply(Namers.scala:455)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$enterSyms$1.apply(Namers.scala:454)
    at scala.collection.LinearSeqOptimized$class.foldLeft(LinearSeqOptimized.scala:110)
    at scala.collection.immutable.List.foldLeft(List.scala:83)
    at scala.tools.nsc.typechecker.Namers$Namer.enterSyms(Namers.scala:454)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$$anon$1.maybeExpand(Namers.scala:338)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$MaybeExpandeeCompleter.completeImpl(Namers.scala:279)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$MaybeExpandeeCompleter.complete(Namers.scala:269)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$RichType.completeOnlyExpansions(Namers.scala:295)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$$anonfun$expandMacroAnnotations$2.apply(Namers.scala:597)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$$anonfun$expandMacroAnnotations$2.apply(Namers.scala:590)
    at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:252)
    at scala.collection.TraversableLike$$anonfun$flatMap$1.apply(TraversableLike.scala:252)
    at scala.collection.immutable.List.foreach(List.scala:302)
    at scala.collection.TraversableLike$class.flatMap(TraversableLike.scala:252)
    at scala.collection.AbstractTraversable.flatMap(Traversable.scala:104)
    at org.scalalang.macroparadise.typechecker.Namers$ParadiseNamer$class.expandMacroAnnotations(Namers.scala:590)
    at org.scalalang.macroparadise.typechecker.Analyzer$$anon$2.expandMacroAnnotations(Analyzer.scala:15)
    at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typedPackageDef$1(Typers.scala:35)
    at org.scalalang.macroparadise.typechecker.Typers$ParadiseTyper$class.typed1(Typers.scala:44)
    at org.scalalang.macroparadise.typechecker.Analyzer$$anon$1.typed1(Analyzer.scala:16)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5309)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5336)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5283)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5287)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5361)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:102)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:424)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:94)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3$$anonfun$run$1.apply(Analyzer.scala:93)
    at scala.collection.Iterator$class.foreach(Iterator.scala:743)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1174)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:93)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1603)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1588)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1583)
    at scala.tools.nsc.Global$Run.compile(Global.scala:1681)

    at xsbt.CachedCompiler0.run(CompilerInterface.scala:123)
    at xsbt.CachedCompiler0.run(CompilerInterface.scala:99)
    at xsbt.CompilerInterface.run(CompilerInterface.scala:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at java.lang.Thread.run(Thread.java:724)
[error] (core/compile:compile) java.util.NoSuchElementException: key not found: nameX


It seems to originate from the macro paradise code.
This is the macro/“nameX” in question: https://github.com/soc/enum-paradise/blob/java-enums-via-macro-annotations/macros/src/main/scala/scalax/EnumMacroUsingStaticMembers.scala#L100

Any ideas?

Eugene Burmako

unread,
Nov 12, 2013, 3:03:18 AM11/12/13
to scala-l...@googlegroups.com
Hi, I can reproduce the exception. Looking into it.


--

Eugene Burmako

unread,
Nov 12, 2013, 3:09:06 AM11/12/13
to scala-l...@googlegroups.com, Denys Shabalin, Den Shabalin
Actually, it looks like you've discovered an issue with quasiquotes in trunk (SyntacticClassDef). Denys, could you take a look?
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.

Eugene Burmako

unread,
Nov 12, 2013, 3:37:06 AM11/12/13
to scala-l...@googlegroups.com, Denys Shabalin, Den Shabalin
Allright that one was my fault, but now when I pushed the updated version of paradise, I've exposed the bug in master's BuildUtils: https://gist.github.com/xeno-by/7427539. Denys, looks like this one is all yours.

Simon Ochsenreither

unread,
Nov 12, 2013, 1:40:06 PM11/12/13
to scala-l...@googlegroups.com
Regarding macro annotations ... how does one access the parameters of the annotation? I tried stuff like

class csv(val sample: String) extends annotation.StaticAnnotation {
  def macroTransform(annottees: Any*)(sample: String) = macro CSVTypeProvider.apply
}

object CSVTypeProvider {
  def apply(c: Context)(annottees: c.Expr[Any]*)(sample: c.Expr[String]): c.Expr[Any] = {
    import c.universe._
    val List(Expr(classDef @ ClassDef(_, className, _, template))) = annottees

    c.Expr[Any](classDef)
  }
}


but this doesn't work:

[error] /home/soc/Entwicklung/typeprovider/macros/src/main/scala/de/oxnrtr/typeprovider/csv.scala:5: macro annotation has wrong shape:
[error]   required: def macroTransform(annottees: Any*) = macro ...
[error]   found   : def macroTransform(annottees: Any*)(sample: String) = macro ...
[error] class csv(val sample: String) extends annotation.StaticAnnotation {
[error]       ^


Ideas?

Thanks,

Simon

Eugene Burmako

unread,
Nov 12, 2013, 2:04:30 PM11/12/13
to scala-l...@googlegroups.com
Instantiation of a macro annotation (along with its arguments) is currently packed into c.macroApplication. 

This will most likely be improved in the future with a dedicated API, but for now it is what it is because from within a macro plugin it's difficult to introduce new APIs into the reflection/macro cake.
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Simon Ochsenreither

unread,
Nov 12, 2013, 2:20:24 PM11/12/13
to scala-l...@googlegroups.com


On Tuesday, November 12, 2013 8:04:30 PM UTC+1, Eugene Burmako wrote:
Instantiation of a macro annotation (along with its arguments) is currently packed into c.macroApplication.

Thanks!

Btw, trying to access the symbol of the class definition ...

object CSVTypeProvider {
  def apply(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {

    import c.universe._
    val List(Expr(classDef @ ClassDef(_, className, _, template))) = annottees
    println(classDef.symbol)

    c.Expr[Any](classDef)
  }
}


... crashes with ...

scala.MatchError: None (of class scala.None$)
    at scala.tools.nsc.typechecker.Macros$class.loadMacroImplBinding(Macros.scala:253)
    at org.scalalang.macroparadise.Plugin$$anon$1.loadMacroImplBinding(Plugin.scala:20)
    at scala.reflect.macros.runtime.MacroRuntimes$MacroRuntimeResolver.<init>(MacroRuntimes.scala:48)
    at scala.reflect.macros.runtime.MacroRuntimes$$anonfun$macroRuntime$3.apply(MacroRuntimes.scala:29)
    at scala.reflect.macros.runtime.MacroRuntimes$$anonfun$macroRuntime$3.apply(MacroRuntimes.scala:29)
    at scala.collection.mutable.MapLike$class.getOrElseUpdate(MapLike.scala:188)
    at scala.collection.mutable.AbstractMap.getOrElseUpdate(Map.scala:92)
    at scala.reflect.macros.runtime.MacroRuntimes$class.macroRuntime(MacroRuntimes.scala:29)
    at org.scalalang.macroparadise.Plugin$$anon$1.macroRuntime(Plugin.scala:20)
    at scala.tools.nsc.typechecker.Macros$MacroExpander$$anonfun$expand$2.apply(Macros.scala:553)
    at scala.tools.nsc.Global.withInfoLevel(Global.scala:192)
    at scala.tools.nsc.typechecker.Macros$MacroExpander.expand(Macros.scala:546)
    at scala.tools.nsc.typechecker.Macros$MacroExpander.apply(Macros.scala:533)
    at scala.tools.nsc.typechecker.Macros$class.macroExpandApply(Macros.scala:681)
    at org.scalalang.macroparadise.Plugin$$anon$1.macroExpandApply(Plugin.scala:20)
    at scala.tools.nsc.typechecker.Typers$Typer.vanillaAdapt$1(Typers.scala:1115)
    at scala.tools.nsc.typechecker.Typers$Typer.adapt(Typers.scala:1170)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5323)


Is this a bug?

Simon Ochsenreither

unread,
Nov 12, 2013, 2:25:55 PM11/12/13
to scala-l...@googlegroups.com
Weird. It's not related to symbol. Even ...

object CSVTypeProvider {
  def apply(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    ???
  }
}


... crashes now with that MatchError.

sbt reload/clean/compile didn't help.

Eugene Burmako

unread,
Nov 12, 2013, 2:35:20 PM11/12/13
to scala-l...@googlegroups.com
Weird indeed. I'll be back home in a couple hours and will see what can be done.
--

Simon Ochsenreither

unread,
Nov 12, 2013, 2:53:35 PM11/12/13
to scala-l...@googlegroups.com
I just tried my other macro-using project, too. Same issue.

I suspect that something in broke in the last SNAPSHOT version (of either Scala or the macro plugin).

Eugene Burmako

unread,
Nov 12, 2013, 4:24:35 PM11/12/13
to scala-l...@googlegroups.com
Tbh I can't reproduce your problem. If I replace the macro body in enum-paradise with ???, then I correctly get a NotImplementedException. Could it be `sbt "reboot full" or some other cache corruption?

Eugene Burmako

unread,
Nov 12, 2013, 4:55:56 PM11/12/13
to scala-l...@googlegroups.com, Den Shabalin, Denys Shabalin
Here's a workaround for updates to quasiquotes that increased their pickiness somewhere in-between M6 and now: https://github.com/soc/enum-paradise/pull/1. I'm sure Denys could suggest a better solution though, but at least this makes macro expansions compile. I got some javac errors after macro expansion, but I'm not sure whether they are intended or not.

Simon Ochsenreither

unread,
Nov 12, 2013, 5:51:32 PM11/12/13
to scala-l...@googlegroups.com
I first tried reboot full, then I also deleted the .ivy2 folder. Both without success ...

Eugene Burmako

unread,
Nov 13, 2013, 3:06:39 AM11/13/13
to scala-l...@googlegroups.com
I see. Could you publish something that doesn't work for you to github, so that I could test exactly the same set up that doesn't work for you?


On 12 November 2013 23:51, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I first tried reboot full, then I also deleted the .ivy2 folder. Both without success ...

--

Simon Ochsenreither

unread,
Nov 13, 2013, 7:58:20 AM11/13/13
to scala-l...@googlegroups.com
I figured it out!

This works:

class csv extends annotation.StaticAnnotation {
  def macroTransform(annottees: Any*) = macro CSVTypeProvider.apply
}


This doesn't:

class csv(val sample: String) extends annotation.StaticAnnotation {
  def macroTransform(annottees: Any*) = macro CSVTypeProvider.apply
}


In short: The constructor parameters make the pattern match fail!

Thanks a lot!

Simon
It is loading more messages.
0 new messages