Проблема понимания вывода типа С#, как описано в спецификации языка

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

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

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

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

Первая фаза

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

    ⇒ не действует, потому что выражение лямбда не имеет явных типов параметров. Правильно?

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

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

Вторая фаза

  • Все незафиксированные переменные типа Xi, не зависящие от (п. 7.5.5.5), фиксированы (X7.2.2.10).

    T нефиксирован; T не зависит от чего-либо... так что T должно быть исправлено, правильно?

§7.5.2.11 Фиксация

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

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

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

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

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

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


Итак, где я ошибаюсь?

Ответ 1

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

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

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

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

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

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

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

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

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

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

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