Kotlin компилятор смешивает параметры уточненного типа - PullRequest
4 голосов
/ 27 января 2020

Я думаю, что, возможно, я столкнулся с ошибкой компилятора в отношении параметров уточненного типа. Вероятно, это проще всего объяснить с помощью примера кода:

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)

Как видно, при вызове через прокси-функцию вывод отличается, , но только если параметры типа reified имеют одинаковое имя . Значение P прокси-функции каким-то образом оказалось в Z прямой функции. Однако это происходит только внутри лямбда-функции , печать имени класса в теле обычной функции приводит к ожидаемому результату.

Я в полной растерянности. Я не могу найти другого объяснения этому, кроме того, что это ошибка компилятора. Я действительно столкнулся с какой-то неясной ошибкой, или я как-то здесь что-то пропустил?

Редактировать: я открыл отчет об ошибке для этого здесь .

1 Ответ

2 голосов
/ 27 января 2020

Если вы посмотрите на декомпилированный байт-код, то увидите, что proxyDiff(), похоже, относится к параметру c generi *, а 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(); 

Итак, да, я бы определенно сказал, что это ошибка.

...