Closed
Description
I wrote a test program that tries out four of defining a constant on an Object:
- val
- private val
- inline val
- inline private val
I also tried typing the constants as Int and as Byte, as well as omitting the type altogether.
Minimized code
import scala.util.Random
object ConstantTester {
val byteConstant: Byte = 0
val intConstant: Int = 1
val untypedConstant = 2
inline val inlinedByteConstant: Byte = 3
inline val inlinedIntConstant: Int = 4
inline val inlinedUntypedConstant = 5
private val privateByteConstant: Byte = 0
private val privateIntConstant: Int = 1
private val privateUntypedConstant = 2
inline private val privateInlinedByteConstant: Byte = 3
inline private val privateInlinedIntConstant: Int = 4
inline private val privateInlinedUntypedConstant = 5
def main(args: Array[String]): Unit = {
val int1 = byteConstant + intConstant + untypedConstant
val int2 = inlinedByteConstant + inlinedIntConstant + inlinedUntypedConstant
val int3 = privateByteConstant + privateIntConstant + privateUntypedConstant
val int4 = privateInlinedByteConstant + privateInlinedIntConstant + privateInlinedUntypedConstant
}
}
Output
$ ~/bin/dotty-0.23.0-RC1/bin/dotc test.scala
failure to convert Constant(true) to TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Byte)
I then ran the generated .class through Luyten decompiler. It turns out that depending on how I declared the value, dotc will do one of five things:
- Have a final field AND an accessor (e.g. byteConstant)
- Inline the value, but keep the public accessor (e.g. inlinedByteConstant)
- Inline the accessor, but still have a field (e.g. privateInlinedByteConstant)
- Properly inline the value, but declare an unused field in the constructor (privateInlinedUntypedConstant)
- Properly inline the value (privateInlinedUntypedConstant)
import java.io.*;
import scala.runtime.*;
public final class ConstantTester$ implements Serializable
{
public static final ConstantTester$ MODULE$;
private final byte byteConstant;
private final int intConstant;
private final int untypedConstant;
private final byte privateByteConstant;
private final int privateIntConstant;
private final int privateUntypedConstant;
private final byte privateInlinedByteConstant;
private final int privateInlinedIntConstant;
static {
new ConstantTester$();
}
private ConstantTester$() {
MODULE$ = this;
this.byteConstant = 0;
this.intConstant = 1;
this.untypedConstant = 2;
this.privateByteConstant = 6;
this.privateIntConstant = 7;
this.privateUntypedConstant = 8;
this.privateInlinedByteConstant = 9;
this.privateInlinedIntConstant = 10;
final int privateInlinedUntypedConstant = 11;
}
private Object writeReplace() {
return new ModuleSerializationProxy((Class)ConstantTester$.class);
}
public byte byteConstant() {
return this.byteConstant;
}
public int intConstant() {
return this.intConstant;
}
public int untypedConstant() {
return this.untypedConstant;
}
public byte inlinedByteConstant() {
return 3;
}
public int inlinedIntConstant() {
return 4;
}
public int inlinedUntypedConstant() {
return 5;
}
public void main(final String[] args) {
final int int1 = this.byteConstant() + this.intConstant() + this.untypedConstant();
final int int2 = this.inlinedByteConstant() + this.inlinedIntConstant() + 5;
final int int3 = this.privateByteConstant + this.privateIntConstant + this.privateUntypedConstant;
final int int4 = this.privateInlinedByteConstant + this.privateInlinedIntConstant + 11;
}
}
Expectation
I had a few expectations that were broken:
- I wouldn't expect "val i = 10" to differ from "val i: Int = 10", but only untyped constants ever get fully inlined
- Sometimes "inline" only inlines the accessor or inlines a constant into the accessor. My expectation would be that inlining actually gets me the raw constant in every case
- Given that these are all on an Object, I'd expect everything to be fully inlined, even if I don't specify "inline" at all