Why force people to use implicitly[ClassTag[T]].runtimeClass for T : ClassTag instead of classOf[T]?

4,377 views
Skip to first unread message

Simon Ochsenreither

unread,
Apr 30, 2013, 9:10:24 AM4/30/13
to scala-i...@googlegroups.com
Original question: https://groups.google.com/d/topic/scala-user/uMhtktoRnmg/discussion

Consider this code:

import reflect.ClassTag

class Foo[T : ClassTag] {
  def clazz = // get the class value //
}


At the moment, one has to use

implicitly[ClassTag[E]].runtimeClass

to get the class value.

Is there a reason why we make classOf fail, although we guarantee that all the necessary information is there, one line above?

Can we make classOf[T] work as expected, if T has the appropriate tag?

I think this would decrease the mental overhead, because people wouldn't need to learn implicitly nor the ClassTag API for something simple as getting the class value for some type.

Paul Phillips

unread,
Apr 30, 2013, 9:18:21 AM4/30/13
to scala-i...@googlegroups.com

On Tue, Apr 30, 2013 at 6:10 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
Is there a reason why we make classOf fail, although we guarantee that all the necessary information is there, one line above?

Yes. classOf is static, the other is not. classOf[Foo] is a constant, the other is a method call. It isn't enough for the information to be there eventually. classOf means the information is there now. This is going to be particularly relevant with annotations, although I forget all the details. Compare the literal versions, and this is the best case for implicitly; if it's "classOf[T]" you know nothing statically. But classOf's whole purpose is static.

class A {
  def f1 = classOf[java.lang.String]
  def f2 = implicitly[reflect.ClassTag[java.lang.String]].runtimeClass
}

// public java.lang.Class<java.lang.String> f1();
//        0: ldc           #12                 // class java/lang/String
//        2: areturn

// public java.lang.Class<?> f2();
//        0: getstatic     #21                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
//        3: getstatic     #26                 // Field scala/reflect/ClassTag$.MODULE$:Lscala/reflect/ClassTag$;
//        6: ldc           #12                 // class java/lang/String
//        8: invokevirtual #30                 // Method scala/reflect/ClassTag$.apply:(Ljava/lang/Class;)Lscala/reflect/ClassTag;
//       11: invokevirtual #34                 // Method scala/Predef$.implicitly:(Ljava/lang/Object;)Ljava/lang/Object;
//       14: checkcast     #36                 // class scala/reflect/ClassTag
//       17: invokeinterface #39,  1           // InterfaceMethod scala/reflect/ClassTag.runtimeClass:()Ljava/lang/Class;
//       22: areturn

Simon Ochsenreither

unread,
Apr 30, 2013, 10:04:37 AM4/30/13
to scala-i...@googlegroups.com
I'm not advocating to change the static nature for those cases which are currently supported, I think adding support for the dynamic, currently unsupported cases would make sense, because it bridges a gap which shouldn't exist for users of the language. (If annotations have stricter requirements, it makes sense to disallow the non-supported cases for annotations only.)

I don't think there is a reason why we should bother users with implementation details, in fact I just checked it in C# and that's the way it works:

    class Foo<T> {
        public Type stringClass  = typeof(String);
        public Type runtimeClass = typeof(T);
    }

    var foo = new Foo<int>();
    Console.WriteLine(foo.stringClass);
    Console.WriteLine(foo.runtimeClass);


Output:
System.String
System.Int32

Eugene Burmako

unread,
Apr 30, 2013, 10:35:37 AM4/30/13
to scala-i...@googlegroups.com
Btw in C# both typeofs are mapped to a single bytecode instruction.


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

Simon Ochsenreither

unread,
Apr 30, 2013, 10:46:36 AM4/30/13
to scala-i...@googlegroups.com

Btw in C# both typeofs are mapped to a single bytecode instruction.

Yes, reified generics and stuff.
I guess we'll have to wait for Avian to save us here. :-)

Anyway, I think the distinction between statically-/dynamically-known is the same when creating a new array and in fact, new Array[T] works if T has a tag available.
I argue it is exactly the same thing for classOf[T].

Paul Phillips

unread,
Apr 30, 2013, 10:56:56 AM4/30/13
to scala-i...@googlegroups.com
On Tue, Apr 30, 2013 at 7:46 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
in fact, new Array[T] works if T has a tag available.

You think? That's why class tags were devised!

I argue it is exactly the same thing for classOf[T].

You argue incorrectly.

There's nothing stopping you from merging them somewhere, it's just that the somewhere isn't classOf.

Simon Ochsenreither

unread,
Apr 30, 2013, 11:13:12 AM4/30/13
to scala-i...@googlegroups.com

You argue incorrectly.

I'd love to understand where my reasoning took the wrong path!
Both methods require more information about a type than what is normally known at runtime.
Supplying that information should work with consistently with both.

 
There's nothing stopping you from merging them somewhere, it's just that the somewhere isn't classOf.

So what would be the benefit of having both classOf and betterClassOf, if the second is a more consistent superset of the first? I think the added confusion would not be worth the benefits.

If we look at it in a more organized way:

                  static       dynamic
Java
  new Array        YES             NO
  classOf          YES             NO
C#
  new Array        YES            YES
  classOf          YES            YES
Scala
  new Array        YES            YES
  classOf          YES             NO


What's Scala's improvement on Java's or C#'s approach here?

Paul Phillips

unread,
Apr 30, 2013, 11:26:45 AM4/30/13
to scala-i...@googlegroups.com
There's a bunch of stuff that expects classOf to be statically resolved. The products of classOf are Constants with a capital C. You're not going to handle all the breakage which would arise from changing that, and I'm not volunteering. That's as much as I have to say.

nafg

unread,
May 1, 2013, 6:15:34 PM5/1/13
to scala-i...@googlegroups.com

On Tuesday, April 30, 2013 11:26:45 AM UTC-4, Paul Phillips wrote:
There's a bunch of stuff that expects classOf to be statically resolved. The products of classOf are Constants with a capital C. You're not going to handle all the breakage which would arise from changing that, and I'm not volunteering. That's as much as I have to say.


By the way, was classOf ever made stable with regard to pattern matching (e.g., c match { case classOf[Int] => ... })?

Paul Phillips

unread,
May 2, 2013, 3:44:26 PM5/2/13
to scala-i...@googlegroups.com

On Wed, May 1, 2013 at 3:15 PM, nafg <nafto...@gmail.com> wrote:
By the way, was classOf ever made stable with regard to pattern matching (e.g., c match { case classOf[Int] => ... })?

No, but I implemented an adjunct to that (type parameter inference for stable identifier patterns) not that it's in trunk:


Enabling the syntax being the easier but unimplemented part.

Simon Ochsenreither

unread,
Jan 8, 2014, 10:13:46 AM1/8/14
to scala-i...@googlegroups.com
Hi remaining people,

I started to work on this again, and given the comment here https://github.com/scala/scala/blob/master/src/compiler/scala/tools/nsc/transform/Erasure.scala#L337 I'd like to know what representation for constant classOfs would be preferred. Something like ConstantClass(Constant(clazz)) or just transforming classOf to ClassClass earlier in the process? (Hey, we could do that in the parser, right? :-D)

I modified Predef.classOf like this

  def classOf[T : scala.reflect.ClassTag]: Class[T] = scala.reflect.classTag[T].runtimeClass

and plan to erase to the existing Constant representation where T is a concrete class as usual and do the actual call above only in the now fixed cases of generic Ts.

Opinions?

Bye,

Simon

Jason Zaugg

unread,
Jan 8, 2014, 10:50:54 AM1/8/14
to scala-i...@googlegroups.com
I suspect that erasure is likely too late.

I think a cleaner approach is to make `classOf` a macro. Paul got most of the way there in https://github.com/scala/scala/pull/1642.

You could omit the context bound and do a sneaky implicit search inside the macro for ClassTag[T] if the type parameter isn't a class type.

I'm still on the fence as to whether or not this is a good idea. I appreciate the utility but want us to make sure we think carefully about undesirable side effects.

One (somewhat contrived) example: we'll have to make sure that error messages for code like the following are still meaningful:

   class T[A: ClassTag] {
      @JavaAnnotation(classOf[A])
   } 

-jason

Simon Ochsenreither

unread,
Jan 8, 2014, 11:01:39 AM1/8/14
to scala-i...@googlegroups.com
Hi Jason!


I think a cleaner approach is to make `classOf` a macro. Paul got most of the way there in https://github.com/scala/scala/pull/1642.

Thanks, I'll look into that. Wouldn't making classOf a macro create a dependency on scala-reflect?
 
You could omit the context bound and do a sneaky implicit search inside the macro for ClassTag[T] if the type parameter isn't a class type.

I considered that, but I felt that being more explicit like we do for Arrays is preferable and more consistent.
 
I'm still on the fence as to whether or not this is a good idea. I appreciate the utility but want us to make sure we think carefully about undesirable side effects.

Agree.
 
One (somewhat contrived) example: we'll have to make sure that error messages for code like the following are still meaningful:

   class T[A: ClassTag] {
      @JavaAnnotation(classOf[A])
   } 

Isn't that ruled out by the additional checks that annotation arguments must be constant? Of course I could be wrong, but I would assume that our current checks and error messages would catch this case already.

Thanks and bye,

Simon

Eugene Burmako

unread,
Jan 8, 2014, 11:02:57 AM1/8/14
to <scala-internals@googlegroups.com>
No it wouldn't create a dependency on scala-reflect. Take a look at how StringContext.f is implemented :)


--

Simon Ochsenreither

unread,
Jan 8, 2014, 11:11:25 AM1/8/14
to scala-i...@googlegroups.com

I think a cleaner approach is to make `classOf` a macro. Paul got most of the way there in https://github.com/scala/scala/pull/1642.

What would be the difference to doing things like this?

    def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false): Tree = {
      val isTypeParameterOrSkolem = tpt.symbol.isTypeParameterOrSkolem
      if (isTypeParameterOrSkolem && noGen) {
        // create call to scala.reflect.classTag[T].runtimeClass
      } else {
        atPos(tree.pos)(gen.mkClassOf(tpt.tpe))
      }
    }


But ... that macro code is nice ... it's like "wow, now ... where is the actual implementation?".

Eugene Burmako

unread,
Jan 8, 2014, 11:17:01 AM1/8/14
to <scala-internals@googlegroups.com>
What if tpt's an array of type parameters?


--

Jason Zaugg

unread,
Jan 8, 2014, 11:35:54 AM1/8/14
to scala-i...@googlegroups.com
There shouldn't be any real difference in what you can do in a macro classOf vs the compiler hard-coded classOf (modulo the difference between public vs internal reflection API). It's just independentally desirable to macro-ify classOf.

-jason

Simon Ochsenreither

unread,
Jan 8, 2014, 11:44:13 AM1/8/14
to scala-i...@googlegroups.com
How does this look?

scala> class Foo[T : reflect.ClassTag] { def clazz = classOf[T] }
defined class Foo

scala> new Foo[Int]
res2: Foo[Int] = Foo@11323617

scala> val foo = new Foo[Int]
foo: Foo[Int] = Foo@7e126c6f

scala> foo.clazz
res3: Class[Int] = int


:-D

Simon Ochsenreither

unread,
Jan 8, 2014, 11:46:26 AM1/8/14
to scala-i...@googlegroups.com

Simon Ochsenreither

unread,
Jan 8, 2014, 12:02:32 PM1/8/14
to scala-i...@googlegroups.com
Well, not quite there it seems:

quick.reflect:
    [mkdir] Created dir: /home/soc/Entwicklung/scala/build/quick/classes/reflect
[quick.reflect] Compiling 158 files to /home/soc/Entwicklung/scala/build/quick/classes/reflect
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Trees.scala:1868: error: recursive value ValOrDefDefTag needs type
[quick.reflect]   implicit val ValOrDefDefTag         = ClassTag[ValOrDefDef](classOf[ValOrDefDef])
[quick.reflect]                                                                      ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/FlagSets.scala:10: error: recursive value FlagSetTag needs type
[quick.reflect]   implicit val FlagSetTag = ClassTag[FlagSet](classOf[FlagSet])
[quick.reflect]                                                      ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Constants.scala:274: error: recursive value ConstantTag needs type
[quick.reflect]   implicit val ConstantTag = ClassTag[Constant](classOf[Constant])
[quick.reflect]                                                        ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Positions.scala:29: error: recursive value PositionTag needs type
[quick.reflect]   implicit val PositionTag = ClassTag[Position](classOf[Position])
[quick.reflect]                                                        ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Symbols.scala:3335: error: recursive value FreeTypeSymbolTag needs type
[quick.reflect]   implicit val FreeTypeSymbolTag = ClassTag[FreeTypeSymbol](classOf[FreeTypeSymbol])
[quick.reflect]                                                                    ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Scopes.scala:403: error: recursive value MemberScopeTag needs type
[quick.reflect]   implicit val MemberScopeTag = ClassTag[MemberScope](classOf[MemberScope])
[quick.reflect]                                                              ^
[quick.reflect] /home/soc/Entwicklung/scala/src/reflect/scala/reflect/internal/Types.scala:4622: error: recursive value TypeTagg needs type
[quick.reflect]   implicit val TypeTagg = ClassTag[Type](classOf[Type])
[quick.reflect]                                                 ^
[quick.reflect] 7 errors found


Simon Ochsenreither

unread,
Jan 8, 2014, 12:10:48 PM1/8/14
to scala-i...@googlegroups.com
I guess the issue is that we have now the context bound even in those cases we don't need it.
Recommendations? Revert to def classOf[T] = ... and check for the need and availability of a ClassTag in the compiler?

Jason Zaugg

unread,
Jan 8, 2014, 12:13:58 PM1/8/14
to scala-i...@googlegroups.com
On Wed, Jan 8, 2014 at 6:10 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
I guess the issue is that we have now the context bound even in those cases we don't need it.
Recommendations? Revert to def classOf[T] = ... and check for the need and availability of a ClassTag in the compiler?

You might not like this answer, but my recommendation is to take a fresh look at the problem after the 2.11. This change doesn't have the right risk/reward nor cost/benefit profile for this release, IMO.

-jason

Simon Ochsenreither

unread,
Jan 8, 2014, 12:40:45 PM1/8/14
to scala-i...@googlegroups.com

You might not like this answer, but my recommendation is to take a fresh look at the problem after the 2.11. This change doesn't have the right risk/reward nor cost/benefit profile for this release, IMO.

No problem, I'm targeting 2.12 anyway. The remaining stuff I want to fix for 2.11 is blocked by un-merged PRs anyway, so I pretty much stopped working on 2.11 for now.

Simon Ochsenreither

unread,
Jan 8, 2014, 12:48:34 PM1/8/14
to scala-i...@googlegroups.com
At this stage https://github.com/soc/scala/commit/d6138a3d90bca7970936b3111de9422ada66914e (no ClassTags) it looks like I broke the constant checking for annotations:

test.osgi.comp:
    [mkdir] Created dir: /home/soc/Entwicklung/scala/build/osgi/classes
[scalacfork] Compiling 5 files to /home/soc/Entwicklung/scala/build/osgi/classes
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicLibrary.scala:21: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.junit.JUnit4TestRunner]: Class[org.ops4j.pax.exam.junit.JUnit4TestRunner])
[scalacfork] @RunWith(classOf[JUnit4TestRunner])
[scalacfork]                 ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicLibrary.scala:22: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory]: Class[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory])
[scalacfork] @ExamReactorStrategy(Array(classOf[AllConfinedStagedReactorFactory]))
[scalacfork]                                   ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicReflection.scala:39: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.junit.JUnit4TestRunner]: Class[org.ops4j.pax.exam.junit.JUnit4TestRunner])
[scalacfork] @RunWith(classOf[JUnit4TestRunner])
[scalacfork]                 ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicReflection.scala:40: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory]: Class[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory])
[scalacfork] @ExamReactorStrategy(Array(classOf[AllConfinedStagedReactorFactory]))
[scalacfork]                                   ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicTest.scala:22: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.junit.JUnit4TestRunner]: Class[org.ops4j.pax.exam.junit.JUnit4TestRunner])
[scalacfork] @RunWith(classOf[JUnit4TestRunner])
[scalacfork]                 ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/BasicTest.scala:23: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory]: Class[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory])
[scalacfork] @ExamReactorStrategy(Array(classOf[AllConfinedStagedReactorFactory]))
[scalacfork]                                   ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/ReflectionToolboxTest.scala:25: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.junit.JUnit4TestRunner]: Class[org.ops4j.pax.exam.junit.JUnit4TestRunner])
[scalacfork] @RunWith(classOf[JUnit4TestRunner])
[scalacfork]                 ^
[scalacfork] /home/soc/Entwicklung/scala/test/osgi/src/ReflectionToolboxTest.scala:26: error: annotation argument needs to be a constant; found: (classOf[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory]: Class[org.ops4j.pax.exam.spi.reactors.AllConfinedStagedReactorFactory])
[scalacfork] @ExamReactorStrategy(Array(classOf[AllConfinedStagedReactorFactory]))
[scalacfork]                                   ^
[scalacfork] two warnings found
[scalacfork] 8 errors found


I'll have to look into that.

Simon Ochsenreither

unread,
Jan 9, 2014, 9:32:01 AM1/9/14
to scala-i...@googlegroups.com
The type and trees at those positions are:

Typed(Literal(Constant(org.ops4j.pax.exam.junit.JUnit4TestRunner)), TypeTree())
TypeRef(SingleType(ThisType(scala), scala.Predef), TypeName("Class"), List(TypeRef(ThisType(org.ops4j.pax.exam.junit), org.ops4j.pax.exam.junit.JUnit4TestRunner, List())))
[scalacfork] @RunWith(classOf[JUnit4TestRunner])


Just guessing ... could it be that the macro wasn't expanded soon enough?

Simon Ochsenreither

unread,
Jan 24, 2014, 8:58:38 PM1/24/14
to scala-i...@googlegroups.com
Hi!

Fixed it, I was just matching on the wrong thing!

Current status is here: https://github.com/soc/scala/compare/scala:master...soc:SI-5722?expand=1

There are three remaining test cases to be resolved, but things look pretty good so far:
  • pos/annotations.scala:
    annotations.scala:108: error: annotation argument needs to be a constant; found: Test3.cls
      @Ann5(Test3.cls)
                  ^
    The tree here appears as Select(Ident(Test3), TermName("cls")), which seems pretty standard. Not sure why it wasn't constant folded earlier.

  • neg/t3222.scala:
    t3222.scala:1: error: not found: type B
    @throws(classOf[B])
                    ^
    error: null

    Not sure what's the issue here ...

  • run/t4891:
    test.generic.T1
    java.lang.NoSuchMethodError: scala.Predef$.classOf()Ljava/lang/Class;
        at scala.tools.partest.SigTest$class.isObjectMethodName(SigTest.scala:22)
        at Bug4891$.isObjectMethodName(S_1.scala:15)
        at scala.tools.partest.SigTest$$anonfun$allMethods$1.apply(SigTest.scala:37)
        at scala.tools.partest.SigTest$$anonfun$allMethods$1.apply(SigTest.scala:37)

    Looks like partest needs to be updated here.

Thanks and bye,

Simon

Eugene Burmako

unread,
Jan 25, 2014, 4:10:30 AM1/25/14
to <scala-internals@googlegroups.com>
1) I guess because it was a final val of a ConstantType.
2) Hard to say without debugging.
3) Looks like a call to Predef.classOf slips through cracks and doesn't get transformed into a class reference.


--

Simon Ochsenreither

unread,
Jan 25, 2014, 9:53:41 PM1/25/14
to scala-i...@googlegroups.com

1) I guess because it was a final val of a ConstantType.

My suspicion is that the conversion to a ConstantType fails because we get Typed(Literal(...)) ASTs back from the classOf macro (why?), not Literal(...) ASTs anymore.
 
 
3) Looks like a call to Predef.classOf slips through cracks and doesn't get transformed into a class reference.

I suspect that the partest used when running the test suite is not the one just compiled with the new encoding, but the stable one.

Eugene Burmako

unread,
Jan 26, 2014, 1:16:00 AM1/26/14
to <scala-internals@googlegroups.com>
3) Well, under normal circumstances (when partest or anything else is compiled by the current compiler), invocations of Predef.classOf are removed by the typer and don't appear in the bytecode. So I think it's something else.


--

Simon Ochsenreither

unread,
Feb 26, 2014, 3:32:04 PM2/26/14
to scala-i...@googlegroups.com
Regarding 1): I just compared the output after typer:

Before:
        final private[this] val cls: Class[String](classOf[java.lang.String]) = classOf[java.lang.String];
        final <stable> <accessor> def cls: Class[String](classOf[java.lang.String]) = classOf[java.lang.String]


After https://github.com/soc/scala/compare/scala:master...soc:SI-5722-new?expand=1:
        final private[this] val cls: Class[String] = (classOf[java.lang.String]: Class[String]);
        final <stable> <accessor> def cls: Class[String] = $iw.this.cls


It really looks like culprit is that the classOf literal isn't typed as a constant anymore because of the type ascription.

I see two options:

What do you suggest?

Eugene Burmako

unread,
Feb 26, 2014, 3:36:25 PM2/26/14
to <scala-internals@googlegroups.com>
Would it help to make classOf a whitebox macro?


--

Simon Ochsenreither

unread,
Mar 2, 2014, 9:40:36 PM3/2/14
to scala-i...@googlegroups.com

Would it help to make classOf a whitebox macro?

Wow, this is bizarre ... I somehow didn't see your message, but decided today to change makeBlackbox to makeWhitebox, and suddenly most tests are green!

Remaining ones are:

!! 1 - neg/t3222.scala                           [compilation failed]
!! 1 - run/t4891                                 [non-zero exit code]

t3222 seems to fail because partest needs to be recompiled and t4891 looks like some kind of weird crash while trying to report an error:

Expected:

t3222.scala:1: error: not found: type B
@throws(classOf[B])
                ^
t3222.scala:4: error: not found: type D
  def foo(@throws(classOf[D]) x: Int) {}
                          ^
t3222.scala:3: error: not found: type C
  @throws(classOf[C])
                  ^
t3222.scala:6: error: not found: type E
  @throws(classOf[E])
                  ^
four errors found


Result:

Simon Ochsenreither

unread,
Mar 2, 2014, 9:43:46 PM3/2/14
to scala-i...@googlegroups.com

t3222 seems to fail because partest needs to be recompiled and t4891 looks like some kind of weird crash while trying to report an error:

Ooops. It's the other way around.

Simon Ochsenreither

unread,
Mar 2, 2014, 9:45:14 PM3/2/14
to scala-i...@googlegroups.com

t3222 seems to fail because partest needs to be recompiled and t4891 looks like some kind of weird crash while trying to report an error:

The REPL says:

java.lang.NullPointerException
    at scala.tools.nsc.typechecker.Typers$Typer.typedAnnotation(Typers.scala:3596)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$21$$anonfun$apply$18$$anonfun$apply$19.apply(Namers.scala:1499)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$21$$anonfun$apply$18$$anonfun$apply$19.apply(Namers.scala:1499)
    at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:241)
    at scala.tools.nsc.Global.enteringTyper(Global.scala:1098)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$21$$anonfun$apply$18.apply(Namers.scala:1499)
    at scala.tools.nsc.typechecker.Namers$Namer$$anonfun$21$$anonfun$apply$18.apply(Namers.scala:1499)
    at scala.reflect.internal.AnnotationInfos$LazyAnnotationInfo.forcedInfo$lzycompute(AnnotationInfos.scala:213)
    at scala.reflect.internal.AnnotationInfos$LazyAnnotationInfo.forcedInfo(AnnotationInfos.scala:213)
    at scala.reflect.internal.AnnotationInfos$LazyAnnotationInfo.completeInfo(AnnotationInfos.scala:226)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedTemplate$1.apply(Typers.scala:1841)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$typedTemplate$1.apply(Typers.scala:1841)
    at scala.collection.immutable.List.map(List.scala:274)
    at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1841)
    at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1744)
    at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5193)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5243)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5280)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5307)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5254)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5258)
    at scala.tools.nsc.interpreter.ReplGlobal$$anon$1$$anon$2.typed(ReplGlobal.scala:36)
    at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5334)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2992)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.collection.immutable.List.loop$1(List.scala:172)
    at scala.collection.immutable.List.mapConserve(List.scala:188)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1898)
    at scala.tools.nsc.typechecker.Typers$Typer.typedModuleDef(Typers.scala:1785)
    at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5194)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5243)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5280)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5307)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5254)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5258)
    at scala.tools.nsc.interpreter.ReplGlobal$$anon$1$$anon$2.typed(ReplGlobal.scala:36)
    at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5334)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2992)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.collection.immutable.List.loop$1(List.scala:172)
    at scala.collection.immutable.List.mapConserve(List.scala:188)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1898)
    at scala.tools.nsc.typechecker.Typers$Typer.typedModuleDef(Typers.scala:1785)
    at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5194)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5243)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5280)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5307)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5254)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5258)
    at scala.tools.nsc.interpreter.ReplGlobal$$anon$1$$anon$2.typed(ReplGlobal.scala:36)
    at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5334)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2992)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.collection.immutable.List.loop$1(List.scala:172)
    at scala.collection.immutable.List.mapConserve(List.scala:188)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1898)
    at scala.tools.nsc.typechecker.Typers$Typer.typedModuleDef(Typers.scala:1785)
    at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5194)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5243)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5280)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5307)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5254)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5258)
    at scala.tools.nsc.interpreter.ReplGlobal$$anon$1$$anon$2.typed(ReplGlobal.scala:36)
    at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5334)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedStat$1(Typers.scala:2992)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer$$anonfun$62.apply(Typers.scala:3096)
    at scala.collection.immutable.List.loop$1(List.scala:172)
    at scala.collection.immutable.List.mapConserve(List.scala:188)
    at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3096)
    at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:4903)
    at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5196)
    at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5243)
    at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5280)
    at scala.tools.nsc.typechecker.Typers$Typer.scala$tools$nsc$typechecker$Typers$Typer$$typedInternal(Typers.scala:5307)
    at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5254)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5258)
    at scala.tools.nsc.interpreter.ReplGlobal$$anon$1$$anon$2.typed(ReplGlobal.scala:36)
    at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5332)
    at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:102)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
    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:1624)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1609)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1604)
    at scala.tools.nsc.interpreter.IMain.compileSourcesKeepingRun(IMain.scala:388)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compileAndSaveRun(IMain.scala:804)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:763)
    at scala.tools.nsc.interpreter.IMain$Request.compile$lzycompute(IMain.scala:939)
    at scala.tools.nsc.interpreter.IMain$Request.compile(IMain.scala:934)
    at scala.tools.nsc.interpreter.IMain.compile(IMain.scala:531)
    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:519)
    at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:517)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:748)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:766)
    at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793)
    at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:660)
    at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:427)
    at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:444)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:862)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848)
    at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848)
    at scala.reflect.internal.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:95)
    at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:848)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:81)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

 

Simon Ochsenreither

unread,
Mar 5, 2014, 11:28:24 PM3/5/14
to scala-i...@googlegroups.com
Success!

scala> classOf[String]
res0: Class[String] = class java.lang.String
 
scala> class Foo[T : reflect.ClassTag] { def foo = classOf[T] }; new Foo[String].foo
defined class Foo
res1: Class[_] = class java.lang.String
 
scala> class Foo[T] { def foo = classOf[T] }; new Foo[String].foo
<console>:8: error: No ClassTag available for T
class Foo[T] { def foo = classOf[T] };;
^

Simon Ochsenreither

unread,
Mar 5, 2014, 11:45:19 PM3/5/14
to scala-i...@googlegroups.com
More precise return type:

scala> class Foo[T : reflect.ClassTag] { def foo = classOf[T] }; new Foo[String].foo
defined class Foo
res0: Class[_ <: String] = class java.lang.String

Eugene Burmako

unread,
Mar 6, 2014, 2:20:25 AM3/6/14
to <scala-internals@googlegroups.com>
How did you solve the problems that you had before?


Simon Ochsenreither

unread,
Mar 6, 2014, 12:20:09 PM3/6/14
to scala-i...@googlegroups.com

How did you solve the problems that you had before?

Additional null check. :-/

Simon Ochsenreither

unread,
Mar 6, 2014, 12:22:27 PM3/6/14
to scala-i...@googlegroups.com

Simon Ochsenreither

unread,
Mar 6, 2014, 12:34:18 PM3/6/14
to scala-i...@googlegroups.com
One thing I'm not perfectly happy about is the additional evidence parameter (which allows people to pass their own classtag evidence, imho not a good thing).

I'd prefer the way new Array[T] does it, but I haven't looked _how_ it's done for arrays yet. Any hints?

Eugene Burmako

unread,
Mar 6, 2014, 2:42:16 PM3/6/14
to <scala-internals@googlegroups.com>


On 6 March 2014 18:34, Simon Ochsenreither <simon.och...@gmail.com> wrote:
One thing I'm not perfectly happy about is the additional evidence parameter (which allows people to pass their own classtag evidence, imho not a good thing).

I'd prefer the way new Array[T] does it, but I haven't looked _how_ it's done for arrays yet. Any hints?

--

Simon Ochsenreither

unread,
Mar 6, 2014, 3:48:31 PM3/6/14
to scala-i...@googlegroups.com

Thanks a lot! Though it seems I can't use it in mkClassOf as-is because I don't have a typer available and additionally, TreeGen is in scala-relfect.jar but Tags is in scala-compiler.jar ... looks like some things might need to be moved around, but I have no clear picture of what exactly needs to be done right now ...

Simon Ochsenreither

unread,
Mar 6, 2014, 4:57:48 PM3/6/14
to scala-i...@googlegroups.com
Maybe it makes sense to emit the trees in mkClassOf with missing evidence and handle it in the appropriate typedXXX method in Typer?

Eugene Burmako

unread,
Mar 7, 2014, 8:00:08 AM3/7/14
to scala-i...@googlegroups.com
But if you don't have any evidence in mkClassOf, then what are you going to select runtimeClass from?

Simon Ochsenreither

unread,
Mar 7, 2014, 4:50:28 PM3/7/14
to scala-i...@googlegroups.com

But if you don't have any evidence in mkClassOf, then what are you going to select runtimeClass from?

Right ... I'd need some kind of AST to pass the information down to typer.
On the other hand, I guess one could implement the runtimeClass part in some method in Typers/Types and just call it from mkClassOf ...

Simon Ochsenreither

unread,
Aug 14, 2014, 10:45:57 AM8/14/14
to scala-i...@googlegroups.com
Hi Eugene,

I think I have figured it out!
There is only a remaining issue: resolveClassTag can't seem to provide a ClassTag.

E. g.
def foo[T : reflect.ClassTag] = classOf[T]
returns:
error: cannot find class tag for element type T

I have compared my usage of resolveClassTag with other usages in the compiler, but couldn't figure out why it doesn't work in my case.
Here is the code: https://github.com/soc/scala/compare/scala:2.12.x...topic/classOf-macro?expand=1 (second commit)

Anything I'm missing here?

Thanks,

Simon

Simon Ochsenreither

unread,
Mar 4, 2015, 7:23:37 AM3/4/15
to scala-i...@googlegroups.com
Hey Eugene,

I spent some time looking into this again. My current version is: https://github.com/soc/scala/commits/topic/classOf-macro
I ran scala with -Xlog-implicits and I got the following message:

build/pack/bin/scala -Xlog-implicits
Welcome to Scala version 2.12.0-20150304-113659-0edb537ae2 (OpenJDK 64-Bit Server VM, Java 1.8.0_01-internal).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def foo[T : reflect.ClassTag] = classOf[T]
<console>:7: materializing requested scala.reflect.type.ClassTag[T] using `package`.this.materializeClassTag[T]()

       def foo[T : reflect.ClassTag] = classOf[T]
                                               ^
<console>:7: materializing requested scala.reflect.type.ClassTag[T] using `package`.this.materializeClassTag[T]()

       def foo[T : reflect.ClassTag] = classOf[T]
                                               ^
<console>:7: `package`.this.materializeClassTag[T]() is not a valid implicit value for scala.reflect.ClassTag[T] because:
failed to typecheck the materialized tag:
tpe T is an unresolved spliceable type

       def foo[T : reflect.ClassTag] = classOf[T]
                                               ^
warning: No ClassTag for T!
<console>:7: error: cannot find class tag for element type T

       def foo[T : reflect.ClassTag] = classOf[T]
                                               ^
Reading the documentation for Tags#resolveClassTag, I seem to run into the second case mentioned:

EmptyTree if the result contains unresolved (i.e. not spliced) type parameters and abstract type members.

What would be the appropriate way to handle this?

Thanks and bye,

Simon

Simon Ochsenreither

unread,
Mar 4, 2015, 1:53:38 PM3/4/15
to scala-i...@googlegroups.com
Ok, I deactivated the check for concrete here: https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/reflect/reify/package.scala#L60 and after adjusting the result type of Predef.classOf to Any (doesn't matter anyway) at least the types line up correctly in both cases (type statically known, and dynamic):

scala> classOf[Int]
res0: Class[Int] = int


scala> def foo[T : reflect.ClassTag] = classOf[T]
foo: [T](implicit evidence$1: scala.reflect.ClassTag[T])Class[_]

scala> foo[Int]
res1: Class[_] = class java.lang.Object

Now it seems that the issue is that I don't get the right class instance, but always the class for java.lang.Object ...

Ideas?

Simon Ochsenreither

unread,
Mar 4, 2015, 3:55:17 PM3/4/15
to scala-i...@googlegroups.com
Ah, right, I'm running into https://github.com/scala/scala/blob/2.11.x/src/compiler/scala/reflect/reify/package.scala#L67 which of course erases everything which isn't a TypeRef to Object ...
Reply all
Reply to author
Forward
0 new messages