Fixing SI-7685: value classes crippled by nesting restriction

114 views
Skip to first unread message

Dale Wijnand

unread,
May 9, 2016, 9:38:27 AM5/9/16
to scala-internals
Hi,

Feeling crippled for too long by value classes not being able to nest I've ventured into the compiler codebase to see if I can lift this restriction.

For reference, the ticket I'm targeting is https://issues.scala-lang.org/browse/SI-7685 .

But also closely related is https://issues.scala-lang.org/browse/SI-6337 where the restriction was imposed.

I've no prior experience in the Scala compiler or compilers in general, but I've started working on this, see my branch https://github.com/dwijnand/scala/tree/wip/7685

I'm looking for advice and help, currently I'm trying to get this to run (test/files/run/t7685-class-simple.scala):

object Test {
  final class Foo(val x: String) extends AnyVal { override def toString = "" + x }
  final class Bar(val f: Foo)    extends AnyVal { override def toString = "" + f }
  def main(args: Array[String]) = {
    val x = "abc"
    val f = new Foo(x)
    val b = new Bar(f)
    println(b)
  }
}

 but it's failing in the constructor of Bar:

java.lang.ClassCastException: Test$Foo cannot be cast to java.lang.String
at Test$.main(t7685-class-simple.scala:7)
at Test.main(t7685-class-simple.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


I can get a little more insight from decompiling in the REPL - compared to Foo, creating a Bar has an additional checkcast call which I assume is causing the ClassCastException:

[info] Running scala.tools.nsc.MainGenericRunner -usejavacp
Welcome to Scala 2.12.0-20160509-125434-7d0738b (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_74).
Type in expressions for evaluation. Or try :help.

scala> :pa -raw
// Entering paste mode (ctrl-D to finish)

final class Foo(val x: String) extends AnyVal
final class Bar(val f: Foo)    extends AnyVal
object Create {
  def foo = new Foo("abc")
  def bar = new Bar(new Foo("abc"))
}

// Exiting paste mode, now interpreting.


scala> :javap -c Create$
Compiled from "<pastie>"
public final class Create$ {
  public static final Create$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class Create$
       3: invokespecial #12                 // Method "<init>":()V
       6: return

  public java.lang.String foo();
    Code:
       0: ldc           #16                 // String abc
       2: areturn

  public java.lang.String bar();
    Code:
       0: new           #20                 // class Foo
       3: dup
       4: ldc           #16                 // String abc
       6: invokespecial #23                 // Method Foo."<init>":(Ljava/lang/String;)V
       9: checkcast     #25                 // class java/lang/String
      12: areturn
}

Where and how can I change the bytecode that is emitted? What's a good technique for TDD/iterating my way to something that seems to work?

In truth, any advice is very welcome, I've no idea what I'm doing :)

Thanks,

Dale 

martin odersky

unread,
May 9, 2016, 9:45:57 AM5/9/16
to scala-internals, Vladimir Nikolaev
It's been fixed in dotty and as far as I heard it was pretty easy to
do so. @vladimirnik should have the details.

Cheers

- Martin
> --
> 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/d/optout.



--

Martin Odersky
EPFL and Lightbend

Dale Wijnand

unread,
May 9, 2016, 10:15:35 AM5/9/16
to scala-i...@googlegroups.com, Vladimir Nikolaev, Guillaume Martres
I think it was actually fixed by @smarter, but within the same pull request that implements value classes: https://github.com/lampepfl/dotty/pull/411

... which makes it difficult for me to understand what was required to correctly allow for nesting.

Dale

You received this message because you are subscribed to a topic in the Google Groups "scala-internals" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scala-internals/lSl3bRo1bZY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scala-interna...@googlegroups.com.

Vladimir Nikolaev

unread,
May 9, 2016, 10:39:18 AM5/9/16
to scala-internals, vladimir....@gmail.com, sma...@ubuntu.com
As far as I know the support of wrapped value classes (VC with underlying type that is value class) was added to Dotty in the first implementation (https://github.com/lampepfl/dotty/pull/411/commits). 

Its introduction affects several phases including Erasure (for example, recursive ErasedValueType introduction - https://github.com/lampepfl/dotty/blob/master/src/dotty/tools/dotc/core/TypeErasure.scala#L414).

понедельник, 9 мая 2016 г., 16:15:35 UTC+2 пользователь Dale Wijnand написал:

Guillaume Martres

unread,
May 9, 2016, 10:44:40 AM5/9/16
to scala-i...@googlegroups.com
It basically "just worked" in Dotty without doing anything special :).

Dale: Are you familiar with the value class SIP: http://docs.scala-lang.org/
sips/completed/value-classes.html ? It describes the transform in some
details.
To figure out what is going on, try to see what phases are involved in the
value class transform in scalac (if I remember correctly, it's only
ExtensionMethods, Erasure and PostErasure), then stare at their output using -
Xprint.

Dale Wijnand

unread,
May 9, 2016, 10:48:48 AM5/9/16
to scala-i...@googlegroups.com
Alright, thanks Guillaume.

Guillaume Martres

unread,
May 9, 2016, 11:00:00 AM5/9/16
to scala-i...@googlegroups.com
Note that what SIP-15 calls "Foo$unboxed", scalac (and dotty) calls
"ErasedValueType(Foo, underlyingValueOfFoo)".

Also, not sure how helpful that will be, but here's a link to my Master thesis
which describes the value class transform in Dotty in details:
http://guillaume.martres.me/master_thesis.pdf

On lundi 9 mai 2016 14:48:35 CEST Dale Wijnand wrote:
> Alright, thanks Guillaume.
>
> On Mon, 9 May 2016 at 15:44 Guillaume Martres <smar...@gmail.com> wrote:
> > It basically "just worked" in Dotty without doing anything special :).
> >
> > Dale: Are you familiar with the value class SIP:
> > http://docs.scala-lang.org/
> > sips/completed/value-classes.html
> > <http://docs.scala-lang.org/sips/completed/value-classes.html> ? It

Dale Wijnand

unread,
May 9, 2016, 11:30:17 AM5/9/16
to scala-i...@googlegroups.com
Ah thank you, a more verbose, step-by-step explanation (even if Dotty-specific) helps.

I think I've identified the cause of the failure in my specific test case:

after extmethods, main looks like this:

    def main(args: Array[String]): Unit = {
      val x: String = "abc";
      val f: Test.Foo = new Test.this.Foo(x);
      val b: Test.Bar = new Test.this.Bar(f);
      scala.Predef.println(b)
    }

and after erasure it looks like this:

    def main(args: Array[String]): Unit = {
      val x: String = "abc";
      val f: ErasedValueType(class Foo, String) = x;
      val b: ErasedValueType(class Bar, String) = new Test.Foo(f.$asInstanceOf[String]()).$asInstanceOf[String]();
      scala.Predef.println(new Test.Bar(b.$asInstanceOf[ErasedValueType(class Foo, String)]()))
    }

I think val b should also have value x (and no asInstanceOf).

Jason Zaugg

unread,
May 10, 2016, 2:25:07 AM5/10/16
to scala-i...@googlegroups.com

Dale Wijnand

unread,
May 12, 2016, 8:02:54 PM5/12/16
to scala-i...@googlegroups.com
Hi again,

I was wondering if anyone could explain to me why ErasedValueClass is erased during erasure phase in typed1 before calling typed, rather than being erased in posterasure: https://github.com/scala/scala/blob/f66a230f909858bf39ffdb154727157bc7cd54a5/src/compiler/scala/tools/nsc/transform/Erasure.scala#L693

I ask because I feel that that is getting in the way of TypeAdaptingTransformer's adaptToType working correctly, which is currently issuing a cast which makes the code fail with a ClassCastException: https://github.com/scala/scala/blob/f66a230f909858bf39ffdb154727157bc7cd54a5/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala#L130

If anyone wants to look at my WIP, I've pushed up what I've been dabbling with, with Jason's suggestion (thank you): https://github.com/scala/scala/compare/2.12.x...dwijnand:wip/7685

Thanks,
Dale
Reply all
Reply to author
Forward
0 new messages