Inlining or quoting generic types causes boxing
See original GitHub issueCompiler version
3.0.1-RC1-bin-20210402-775d881-NIGHTLY
Minimized code
import scala.compiletime.*
extension [A](a: A)
transparent inline def ==*(b: A): Boolean =
inline erasedValue[A] match
case _: Int => println("COMPARING INTS"); a == b
case _ => ???
class Test:
var i = 1
var res = false
def x1 = res = 1 ==* 1
def x2 = res = 1 ==* 2
def x3 = res = 1 ==* i
def z1 = res = 1 == i
Output
> javap -c target/scala-3.0.1-RC1/classes/Test.class
Compiled from "BUG.scala"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #15 // Field i:I
9: aload_0
10: iconst_0
11: putfield #17 // Field res:Z
14: return
public int i();
Code:
0: aload_0
1: getfield #15 // Field i:I
4: ireturn
public void i_$eq(int);
Code:
0: aload_0
1: iload_1
2: putfield #15 // Field i:I
5: return
public boolean res();
Code:
0: aload_0
1: getfield #17 // Field res:Z
4: ireturn
public void res_$eq(boolean);
Code:
0: aload_0
1: iload_1
2: putfield #17 // Field res:Z
5: return
public void x1();
Code:
0: aload_0
1: getstatic #33 // Field scala/Predef$.MODULE$:Lscala/Predef$;
4: ldc #35 // String COMPARING INTS
6: invokevirtual #39 // Method scala/Predef$.println:(Ljava/lang/Object;)V
9: iconst_1
10: invokevirtual #41 // Method res_$eq:(Z)V
13: return
public void x2();
Code:
0: aload_0
1: getstatic #33 // Field scala/Predef$.MODULE$:Lscala/Predef$;
4: ldc #35 // String COMPARING INTS
6: invokevirtual #39 // Method scala/Predef$.println:(Ljava/lang/Object;)V
9: iconst_0
10: invokevirtual #41 // Method res_$eq:(Z)V
13: return
public void x3();
Code:
0: aload_0
1: aload_0
2: invokevirtual #45 // Method i:()I
5: istore_1
6: getstatic #33 // Field scala/Predef$.MODULE$:Lscala/Predef$;
9: ldc #35 // String COMPARING INTS
11: invokevirtual #39 // Method scala/Predef$.println:(Ljava/lang/Object;)V
14: iconst_1
15: invokestatic #51 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
18: iload_1
19: invokestatic #51 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
22: astore_2
23: dup
24: ifnonnull 35
27: pop
28: aload_2
29: ifnull 42
32: goto 46
35: aload_2
36: invokevirtual #55 // Method java/lang/Object.equals:(Ljava/lang/Object;)Z
39: ifeq 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #41 // Method res_$eq:(Z)V
50: return
public void z1();
Code:
0: aload_0
1: iconst_1
2: aload_0
3: invokevirtual #45 // Method i:()I
6: if_icmpne 13
9: iconst_1
10: goto 14
13: iconst_0
14: invokevirtual #41 // Method res_$eq:(Z)V
17: return
}
Expectation
We can see in the disassembly of x3
that it boxes the Ints. I was expecting that inline
would choose the appropriate implementation of ==
based on the types of its arguments. I started down the path of matching on erasedValue[Int]
to provide more type info to the inliner but that didn’t make a difference.
Workaround
The following generates code that doesn’t box but considering that I’d have to do it for every primitive, for every inline method I write, it would very quickly become an unreasonable amount of work and boilerplate.
private object Hidden:
inline def int_eq(a: Int, b: Int): Boolean = a == b
extension [A](a: A)
transparent inline def ==*(b: A): Boolean =
inline erasedValue[A] match
case _: Int => println("COMPARING INTS"); Hidden.int_eq(a.asInstanceOf[Int], b.asInstanceOf[Int])
case _ => ???
Issue Analytics
- State:
- Created 2 years ago
- Comments:19 (19 by maintainers)
Top Results From Across the Web
c# - Does a generic function implicitly cast value types to objects ...
So, while in both C# and IL, comparing a value type to null involves boxing, the C# compiler will remove such pointless cruft...
Read more >Proposal for primitive generics - Kotlin Discussions
It eliminates the need for auto-boxing / unboxing, removes the compiler type casts, enables reified types for the entire class (instead of just ......
Read more >Generic programming - Wikipedia
Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later ... (For value types like...
Read more >In C# how to avoid boxing/unboxing of the value types in ...
Hello, I'm trying to avoid boxing/unboxing in a generic method, ... and then from that force convert the type, which is causing the...
Read more >Biomechanics of the head for Olympic boxer punches to the face
Methods: Seven Olympic boxers from five weight classes delivered 18 straight punches to ... Translational and rotational head acceleration, neck responses, ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
What you refer to as “true inlining” is syntactic inlining, this kind of inlining is found in preprocessor such as
C/C++
macros. On the other hand, we have semantic inlining which provides strong guarantees about the program semantics and less surprises on the users of the method/macro.Scala 3 uses semantic inlining.
There are some cases that might be miscategorized as not type checking after inlining such as the use of
summonInline
orerror
which might fail during type checking but not due to type checking.