Youssef Shoaib [MOD]
07/26/2022, 12:14 PMT::class == Desired::class
doesn't seem to be evaluated at compile time. I can also do valueOfTypeDesired is T && !(valueOfSupertypeOfDesired is T)
but again this isn't evaluated at compile time. I'm interested not only because of performance but for a value-class optimization use case (can give more details if interested).Tóth István Zoltán
07/26/2022, 5:01 PMreified
is about making the type runtime-available. IMHO, it means that when you use reified
the exact type will be available during run-time, not just some generic, erased super-type. There is a reason, why reified
applies only to inline
functions. Second, all the checks you've written above are run-time checks, the only way to have a compile type check is to build a type system which enforces the type and/or use a non-inline function. Maybe your best bet is a compiler plugin, after playing around with them a bit it is quite easy to write one. (Disclaimer: I might be wrong, not an expert, ask your doctor, etc...)Youssef Shoaib [MOD]
07/27/2022, 11:39 PMvalue is ValueClass
checks and eliminates any branches that will never run as a result. This happens even in inline functions when value
is of some type argument T
. Is this optimization documented somewhere? because any other constant conditions never cause code to collapse like that. E.g.:
public inline fun <T : Any> printlnWithoutBoxing(value: T) {
// This `is` check gets inlined by the compiler if `value` is statically known to be Result
println(if(value is Result<*>) (value as Result<*>).toString() else value)
}
public fun test(){
val myResult = Result.success(42)
val myResultAny: Any = myResult
println(myResult)
println(myResultAny)
}
public fun test2(){
val myResult = Result.success(42)
val myResultAny: Any = myResult
printlnWithoutBoxing(myResult)
printlnWithoutBoxing(myResultAny)
}
The else branch disappears for the calls in test2, even in the myResultAny
case! In fact, I have to use myResultAny
as a "real" Any (i.e. pass it to a non-inline function that expects Any or a generic T
, or pass it to an inline function that uses that argument as a "real" Any from the aforementioned criteria). The behavior is surprising, but it is useful. Is this how the compiler is expected to behave? (Decompilation of example in 🧵 )public final class MainKt {
public static final void test() {
Object myResult = Result.constructor-impl((Object)42);
Result myResultAny = Result.box-impl((Object)myResult);
Result result = Result.box-impl((Object)myResult);
System.out.println(result);
System.out.println(myResultAny);
}
public static final void test2() {
Object myResult;
Object myResultAny = myResult = Result.constructor-impl((Object)42);
Object value$iv = myResult;
boolean $i$f$printlnWithoutBoxing = false;
System.out.println((Object)Result.toString-impl((Object)value$iv));
boolean $i$f$printlnWithoutBoxing2 = false;
System.out.println((Object)Result.toString-impl((Object)myResultAny));
}
public static final <T> void printlnWithoutBoxing(@NotNull T value) {
Intrinsics.checkNotNullParameter(value, (String)"value");
boolean $i$f$printlnWithoutBoxing = false;
System.out.println((Object)(value instanceof Result ? Result.toString-impl((Object)((Result)value).unbox-impl()) : value));
}
}
myResultAny
as an actual `Any`:
public fun test2(){
val myResultAny: Any = Result.success(42).id() // ensures boxing
printlnWithoutBoxing(myResultAny)
}
public fun <T> T.id(): T = this
it does indeed box, and you can see that in that case (i.e. when value isn't statically known to be a Result
) the else
branch persists:
public static final void test2() {
Result myResultAny = MainKt.id(Result.box-impl((Object)Result.constructor-impl((Object)42)));
boolean $i$f$printlnWithoutBoxing = false;
System.out.println((Object)(myResultAny instanceof Result ? Result.toString-impl((Object)myResultAny.unbox-impl()) : myResultAny));
}
shikasd
07/28/2022, 8:31 AM