Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Inconsistent behavior when returning instances of an anonymous class from a macro #6992

Closed
scabug opened this issue Jan 18, 2013 · 6 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Jan 18, 2013

In Scala 2.10.0 you can write a macro that will define an anonymous class with a type member and return an instance that will be statically typed as a structural type with that type member:

object MacroExample extends ReflectionUtils {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def foo(name: String): Any = macro foo_impl
  def foo_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._

    val Literal(Constant(lit: String)) = name.tree
    val anon = newTypeName(c.fresh)

    c.Expr(Block(
      ClassDef(
        Modifiers(Flag.FINAL), anon, Nil, Template(
          Nil, emptyValDef, List(
            constructor(c.universe),
            TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
          )
        )
      ),
      Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
    ))
  }
}

(Where ReflectionUtils just defines a constructor method for convenience.)

And now:

scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6

But if we try the same thing with a method instead of a type member, it doesn't work:

def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(Flag.FINAL), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
  ))
}

We just don't end up with a structural type:

scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492

But for some reason we can add an extra class that extends the anonymous class and instantiate that instead:

def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)
  val wrapper = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    ClassDef(
      Modifiers(Flag.FINAL), wrapper, Nil,
      Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
    ),
    Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
  ))
}

And it works:

scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834

See this Stack Overflow question for more discussion.

@scabug
Copy link
Author

scabug commented Jan 18, 2013

Imported From: https://issues.scala-lang.org/browse/SI-6992?orig=1
Reporter: Travis Brown (travisbrown)
Affected Versions: 2.10.0

@scabug
Copy link
Author

scabug commented Sep 7, 2013

@scabug
Copy link
Author

scabug commented Dec 8, 2013

@xeno-by said:
Related: #8048

@scabug
Copy link
Author

scabug commented Dec 8, 2013

@xeno-by said:
scala/scala#3236

@scabug
Copy link
Author

scabug commented Jan 14, 2014

@adriaanm said:
was this fixed by the PR? I see no mention of the ticket number there.

@scabug
Copy link
Author

scabug commented Jan 14, 2014

@xeno-by said:
Yes, it was. Sorry, my oversight.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants