-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Erasure tries to box call to label def #4563
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
Comments
Minimal example: case class Foo(str: String)
object Test {
def test(x: Any): Unit = {
val vc: Any = x match {
case Foo("") =>
42
case _ =>
10
}
}
} After erasure we get: val vc: Object =
{
case val x4: Object = x
<label> def case6(): Int = 10
if x4.isInstanceOf[Foo] then
{
case val x5: Foo = x4.asInstanceOf[Foo]
case val x6: Foo = Foo.unapply(x5)
case val x7: String = x6._1()
if "".==(x7) then scala.Int.box(42) else scala.Int.box(case6())
}
else scala.Int.box(case6())
} Notice the boxing around |
Fixing this will require special casing label defs in Erasure. In general, this also illustrates that many transformations are unsafe on label defs compared to defs. This isn't great since we expose label defs in Tasty and thus presumably in the macro API. This might be the tipping point for moving to Scala.js-like labeled blocks as advocated by @sjrd :). |
Ah ah! I will make my labeled blocks a reality. I will! 😛 |
@smarter The expected type is |
Hum, @smarter's minimal example does not seem to be a reproduction, indeed. With or without case class Foo(str: String) extends AnyVal
object Test {
def main(args: Array[String]): Unit = {
test(Foo("one"))
test(Foo(""))
test("two")
}
def test(x: Any): Unit = {
val vc: Any = x match {
case Foo("") =>
42
case _ =>
10
}
println(vc)
}
} result:
|
OK this is an actual reproduction: case class Foo(str: String) extends AnyVal
object Test {
def main(args: Array[String]): Unit = {
test(Foo("one"))
test(Foo(""))
test("two")
}
def test(x: Any): Unit = {
val vc: Any = x match {
case Foo("") =>
Foo("42")
case _ =>
Foo("10")
}
println(vc)
}
} On master:
On #4982:
|
Because case6 is a labeldef which should always be a tailcall. Looks like it doesn't cause a miscompilation here, my bad. |
A Labeled block is an expression tree of the form label[T]: { expr } where `expr` must conform to the type `T`. In addition, within `expr` (but nowhere else), return from the label is allowed: return[label] e where `e` must conform to the type `T` as well. If execution of `expr` completes normally (rather than throwing an exception or returning, etc.), then the result of evaluating the `Labeled` block is the result of `expr`. If a `return[label] e` is reached, the execution of `expr` is interrupted, and the result of evaluating the `Labeled` block is the result of evaluating the argument `e`. Implementation-wise, a `Labeled` block is represented as a `Tree` with the shape: Labeled(Bind(labelName), expr) where the `Bind` nodes holds the definition of the label symbol. That symbol is a term symbol with the flag `Label` (but not `Method`, unlike symbols for label-defs) and whose `info` is the result type `T` of the labeled block. We use those new `Labeled` blocks in `PatternMatcher`, instead of label-defs. This is the first step towards completely removing label-defs from the compiler. This commit structurally fixes a few issues: * It fixes scala#1313 through the `mergeTests` optimization. * It fixes scala#4563 because Labeled blocks are erasure-friendly. * It does a big step towards fixing the upstream test t10387: the compiler can get to the back-end on that test, but it produces too much bytecode for a single JVM method. We do add a sister test t10387b which works because optimizations can kick in.
A Labeled block is an expression tree of the form label[T]: { expr } where `expr` must conform to the type `T`. In addition, within `expr` (but nowhere else), return from the label is allowed: return[label] e where `e` must conform to the type `T` as well. If execution of `expr` completes normally (rather than throwing an exception or returning, etc.), then the result of evaluating the `Labeled` block is the result of `expr`. If a `return[label] e` is reached, the execution of `expr` is interrupted, and the result of evaluating the `Labeled` block is the result of evaluating the argument `e`. Implementation-wise, a `Labeled` block is represented as a `Tree` with the shape: Labeled(Bind(labelName), expr) where the `Bind` nodes holds the definition of the label symbol. That symbol is a term symbol with the flag `Label` (but not `Method`, unlike symbols for label-defs) and whose `info` is the result type `T` of the labeled block. We use those new `Labeled` blocks in `PatternMatcher`, instead of label-defs. This is the first step towards completely removing label-defs from the compiler. This commit structurally fixes a few issues: * It fixes scala#1313 through the `mergeTests` optimization. * It fixes scala#4563 because Labeled blocks are erasure-friendly. * It does a big step towards fixing the upstream test t10387: the compiler can get to the back-end on that test, but it produces too much bytecode for a single JVM method. We do add a sister test t10387b which works because optimizations can kick in.
A Labeled block is an expression tree of the form label[T]: { expr } where `expr` must conform to the type `T`. In addition, within `expr` (but nowhere else), return from the label is allowed: return[label] e where `e` must conform to the type `T` as well. If execution of `expr` completes normally (rather than throwing an exception or returning, etc.), then the result of evaluating the `Labeled` block is the result of `expr`. If a `return[label] e` is reached, the execution of `expr` is interrupted, and the result of evaluating the `Labeled` block is the result of evaluating the argument `e`. Implementation-wise, a `Labeled` block is represented as a `Tree` with the shape: Labeled(Bind(labelName), expr) where the `Bind` nodes holds the definition of the label symbol. That symbol is a term symbol with the flag `Label` (but not `Method`, unlike symbols for label-defs) and whose `info` is the result type `T` of the labeled block. We use those new `Labeled` blocks in `PatternMatcher`, instead of label-defs. This is the first step towards completely removing label-defs from the compiler. This commit structurally fixes a few issues: * It fixes scala#1313 through the `mergeTests` optimization. * It fixes scala#4563 because Labeled blocks are erasure-friendly. * It does a big step towards fixing the upstream test t10387: the compiler can get to the back-end on that test, but it produces too much bytecode for a single JVM method. We do add a sister test t10387b which works because optimizations can kick in.
While migrating inox to Scala 3, I ran into runtime errors stemming from the following code, that has been worked around here: epfl-lara/inox@d68120a
Running the code without the workaround leads to
The underlying problem here is that dotc emits code trying to box a call to a label:
where
case8
isI don't have a minimized test case unfortunately, but, as a starting point, this is how I reproduce it:
The text was updated successfully, but these errors were encountered: