Проблема понимания вывода типа C #, как описано в спецификации языка - PullRequest
62 голосов
/ 13 сентября 2010

Спецификация языка C # описывает вывод типа в разделе §7.5.2. В этом есть деталь, которую я не понимаю. Рассмотрим следующий случай:

// declaration
void Method<T>(T obj, Func<string, T> func);

// call
Method("obj", s => (object) s);

И компиляторы Microsoft, и Mono C # правильно выводят T = object, но мое понимание алгоритма в спецификации даст T = string, а затем завершится ошибкой. Вот как я это понимаю:

Первый этап

  • Если Ei является анонимной функцией, явный вывод типа параметра *1021* (§7.5.2.7) делается из Ei в Ti

    ⇒ не имеет никакого эффекта, потому что лямбда-выражение не имеет явных типов параметров. Правильно?

  • В противном случае, если Ei имеет тип U, а xi является параметром-значением, тогда нижний вывод делается из U в Ti.

    ⇒ первый параметр имеет статический тип string, поэтому это добавляет string к нижним границам для T, верно?

Второй этап

  • Все нефиксированные переменные типа Xi, которые не зависят от (§7.5.2.5), любые Xj являются фиксированными (§7.5.2.10).

    T нефиксировано; T не зависит ни от чего ... поэтому T следует исправить, верно?

§7.5.2.11 Исправление

  • Набор типов кандидатов Uj начинается как набор всех типов в наборе границ для Xi.

    ⇒ {string (нижняя граница)}

  • Затем мы рассмотрим каждую границу для Xi по очереди: [...] Для каждой нижней границы U для Xi все типы Uj, в которые нет неявного преобразования из U, удаляются из набора кандидатов. [...]

    ⇒ ничего не удаляет из набора кандидатов, верно?

  • Если среди оставшихся типов-кандидатов Uj есть уникальный тип V, из которого происходит неявное преобразование во все другие типы-кандидаты, то Xi фиксируется на V.

    ⇒ Так как существует только один тип-кандидат, это неверно, так что Xi устанавливается на string. Правильно?


Так, где я иду не так?

1 Ответ

41 голосов
/ 13 сентября 2010

ОБНОВЛЕНИЕ: Мое первое расследование в автобусе этим утром было неполным и неправильным. Текст спецификации первого этапа правильный. Реализация правильная.

Спецификация неверна в том смысле, что на втором этапе она неправильно определяет порядок событий. Мы должны указать, что мы делаем вывод типа вывода , прежде чем исправим независимые параметры.

Чувак, все это сложно. Я переписывал этот раздел спецификации больше раз, чем помню.

Я видел эту проблему раньше, и я отчетливо помню, как делал ревизии, так что неправильный термин «переменная типа» везде заменялся «параметром типа». (Параметры типа не являются местами хранения, содержимое которых может изменяться, поэтому нет смысла называть их переменными.) Я думаю, что в то же время я заметил, что порядок был неправильным. Вероятно, произошло то, что мы случайно отправили более старую версию спецификации в Интернете. Много извинений.

Я буду работать с Мадсом, чтобы обновить спецификацию в соответствии с реализацией. Я думаю, что правильная формулировка второго этапа должна выглядеть примерно так:

  • Если нефиксированных параметров типа не существует, вывод типа выполняется успешно.
  • В противном случае, если существует один или несколько аргументов Ei с соответствующий тип параметра Ti такой, что тип вывода Ei с типом Ti содержит хотя бы один незафиксированный введите параметр Xj и ни один из типов ввода Ei с типом Ti не содержит нефиксированных введите параметр Xj, затем вывод типа вывода делается из всех таких Ei в Ti.

Независимо от того, действительно ли предыдущий шаг сделал вывод, мы Теперь необходимо исправить хотя бы один параметр типа, как показано ниже:

  • Если существует один или несколько параметров типа Xi, таких что Си нефиксирован, и Си имеет непустой набор границ, и Си не зависит от любого Xj тогда каждый такой Си фиксирован. Если какая-либо операция по исправлению не удалась, то вывод типа завершается неудачей.
  • В противном случае, если существует один или несколько параметров типа Xi, таких что Си нефиксирован, и Си имеет непустой набор границ, и существует по крайней мере один параметр типа Xj, который зависит от Xi тогда каждый такой Си фиксирован. Если какая-либо операция по исправлению не удалась, то вывод типа завершается неудачей.
  • В противном случае мы не сможем добиться прогресса, и есть нефиксированные параметры. Вывод типа не выполняется.

Если вывод типа не дает сбоя и не завершается успешно, вторая фаза повторяется.

Идея в том, что мы хотим, чтобы алгоритм никогда не входил в бесконечный цикл. При каждом повторении второго этапа он либо успешно, либо неудачно, либо прогрессирует. Он не может зацикливаться больше раз, чем есть параметры типа, которые нужно исправить для типов.

Спасибо, что обратили на это мое внимание.

...