Kotlin 编译器混淆了具体化的类型参数
Kotlin compiler mixing up reified type parameters
我想我可能遇到了关于具体化类型参数的编译器错误。用代码示例可能最容易解释:
fun main() {
println("Same:");
proxySame<Int>();
directSame<String, Int>();
println("\nDifferent:");
proxyDiff<Int>();
directDiff<String, Int>();
}
inline fun <reified P> proxySame() = directSame<String, P>();
inline fun <reified P, reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
inline fun <reified P> proxyDiff() = directDiff<String, P>();
inline fun <reified Q, reified Z> directDiff(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
这个例子显然不是真实场景,但它是我能找到的最简单的程序,它仍然表现出意外的行为。
在此代码中有 2 种类型的函数:directX
和 proxyX
,其中 X
是 Same
或 Diff
。 Same
和 Diff
函数之间的唯一区别是 directX
函数的第一个类型参数的名称 。对于Same
,它与proxyX
函数相同,对于Diff
,它是不同的。
无论是否通过代理函数调用 directX
,人们都希望输出相同。不幸的是,情况显然并非总是如此:
Same:
String (proxy, wrong)
Integer (direct, correct)
Different:
Integer (proxy, correct)
Integer (direct, correct)
正如您所看到的,通过代理函数调用时输出是不同的,但前提是具体化类型参数具有相同的名称。
代理函数 P 的值不知何故落在了直接函数的 Z 中。但是,这只发生在 lambda 函数内部,在常规函数体中打印 class 名称会导致预期结果。
我在这里完全不知所措。除了编译器错误外,我找不到任何其他解释。我真的 运行 遇到了一些晦涩的错误,还是我在这里遗漏了什么?
编辑:我已经为此 here 打开了一个错误报告。
如果您查看反编译的字节码,您会发现 proxyDiff()
似乎考虑了通用参数 P
而 proxySame()
没有。
public final void proxyDiff() {
// ...
Intrinsics.reifiedOperationMarker(4, "P");
String var5 = Object.class.getSimpleName();
// ...
}
public final void proxySame() {
// ...
String var5 = String.class.getSimpleName();
// ...
}
我的猜测是它在内联期间被优化掉了。
编译器看到 P
没有在 directSame()
中使用,因此推断它也没有在 proxySame()
中使用。所以这个:
inline fun <reified P> proxySame() = directSame<String, P>();
inline fun <reified P, reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
在引擎盖下变成了这个:
inline fun proxySame() = directSame<String>();
inline fun <reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
所以,是的,我肯定会说这是一个错误。
我想我可能遇到了关于具体化类型参数的编译器错误。用代码示例可能最容易解释:
fun main() {
println("Same:");
proxySame<Int>();
directSame<String, Int>();
println("\nDifferent:");
proxyDiff<Int>();
directDiff<String, Int>();
}
inline fun <reified P> proxySame() = directSame<String, P>();
inline fun <reified P, reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
inline fun <reified P> proxyDiff() = directDiff<String, P>();
inline fun <reified Q, reified Z> directDiff(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
这个例子显然不是真实场景,但它是我能找到的最简单的程序,它仍然表现出意外的行为。
在此代码中有 2 种类型的函数:directX
和 proxyX
,其中 X
是 Same
或 Diff
。 Same
和 Diff
函数之间的唯一区别是 directX
函数的第一个类型参数的名称 。对于Same
,它与proxyX
函数相同,对于Diff
,它是不同的。
无论是否通过代理函数调用 directX
,人们都希望输出相同。不幸的是,情况显然并非总是如此:
Same:
String (proxy, wrong)
Integer (direct, correct)
Different:
Integer (proxy, correct)
Integer (direct, correct)
正如您所看到的,通过代理函数调用时输出是不同的,但前提是具体化类型参数具有相同的名称。 代理函数 P 的值不知何故落在了直接函数的 Z 中。但是,这只发生在 lambda 函数内部,在常规函数体中打印 class 名称会导致预期结果。
我在这里完全不知所措。除了编译器错误外,我找不到任何其他解释。我真的 运行 遇到了一些晦涩的错误,还是我在这里遗漏了什么?
编辑:我已经为此 here 打开了一个错误报告。
如果您查看反编译的字节码,您会发现 proxyDiff()
似乎考虑了通用参数 P
而 proxySame()
没有。
public final void proxyDiff() {
// ...
Intrinsics.reifiedOperationMarker(4, "P");
String var5 = Object.class.getSimpleName();
// ...
}
public final void proxySame() {
// ...
String var5 = String.class.getSimpleName();
// ...
}
我的猜测是它在内联期间被优化掉了。
编译器看到 P
没有在 directSame()
中使用,因此推断它也没有在 proxySame()
中使用。所以这个:
inline fun <reified P> proxySame() = directSame<String, P>();
inline fun <reified P, reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
在引擎盖下变成了这个:
inline fun proxySame() = directSame<String>();
inline fun <reified Z> directSame(
func: () -> Unit = {
println(Z::class.java.simpleName)
}
) = func();
所以,是的,我肯定会说这是一个错误。