Компилятор Dart2JS не может скомпилировать код. Это ошибка, функция или ограничение?
Я использую компилятор Dart2JS версии 1.0.0_r30798 (STABLE).
Пример кода (только для ознакомления с проблемой):
Настоящий код здесь (теперь исправлено поведение dart2js): https://github.com/mezoni/queries/blob/master/lib/src/queries/lookup.dart
Это часть коллекции Queryable для языка дартс.
class ILookup<TKey, TElement> implements IEnumerable<IGrouping<TKey, TElement>> {
}
class Lookup<TKey, TElement> extends Object with Enumerable implements ILookup<TKey, TElement> {
}
class IEnumerable<T> implements HasIterator<T> {
}
class HasIterator<T> {
}
class IGrouping<TKey, TElement> implements IEnumerable<TKey> {
}
class Enumerable<T> implements IEnumerable<T> {
}
void main() {
var obj = new Lookup();
print(obj);
}
Этот код генерирует следующую ошибку Google Dart dart2js Compiler:
Internal Error: Inheritance of the same class with different type arguments is not
supported: Both HasIterator<dynamic> and HasIterator<IGrouping<TKey, TElement>> are
supertypes of Lookup<TKey, TElement>.
class Lookup<TKey, TElement> extends Object with Enumerable implements ILookup<TKey,
TElement> {
^^^^^
Error: Compilation failed.
Это dart2js
Компилятор не может скомпилировать этот код.
Итак, я не могу понять: "Это ошибка, функция или ограничение?".
2 ответа
Ответ от Dart Team очень хороший.
"Поведение виртуальной машины правильное, и dart2js пока не реализует ее".
https://code.google.com/p/dart/issues/detail?id=16047
Также ответ от Гилада Брача.
"FWIW, спецификация не имеет такого ограничения" (RE: класс реализует интерфейс с двумя различными параметрами типа).
https://code.google.com/p/dart/issues/detail?id=14729
Также очень хорошо упомянуто:
"К сожалению, на данный момент это намеренное ограничение в dart2js. Реализация одного и того же интерфейса с аргументами разных типов никогда не работала, поэтому мы чувствовали себя очень неловко, когда люди зависели от нарушенного поведения".
https://code.google.com/p/dart/issues/detail?id=14729
Этот ответ полностью соответствует тому, что пример кода в оригинальном вопросе верен, и в настоящее время его нельзя скомпилировать с помощью dart2js.
PS
Мои мысли (мои прыгуны):
Я думаю, что эта проблема может быть решена в компиляторе Dart2JS с помощью лучшего тестирования совместимости типов, но не только путем проверки равенства классов.
Я думаю, что в этом случае HasIterator<dynamic>
а также HasIterator<IGrouping<TKey, TElement>>
это не одни и те же типы (даже те же классы), потому что они оба неявно задают нижнюю и верхнюю границы TElement
параметр HasIterator<TElement>
,
На практике это более сложно, что я могу объяснить здесь, но я могу добавить следующее.
Они не одного типа, потому что это выражение верно:
HasIterator<dynamic> != HasIterator<IGrouping<TKey, TElement>>
Они не конфликтуют (но неявно задают нижнюю и верхнюю границы), поскольку одно из следующих выражений является истинным.
HasIterator<dynamic> is HasIterator<IGrouping<TKey, TElement>>
HasIterator<IGrouping<TKey, TElement>> is HasIterator<dynamic>
Является ли наш случай (неявный) нижней границей dynamic
и (неявный) верхний bound
является <IGrouping<TKey, TElement>
,
implicit
термин означает только resolved at compile time
,
Это означает, что один из них является подтипом другого, и компилятор должен разрешить их обоих в объявлении. А в аннотациях типов компилятор должен тестировать параметры на совместимость с ними обоими (включая другие суперинтерфейсы).
Если Dart2JS будет тестировать супертипы более тщательно, это может обойти эту проблему.
Я не хочу здесь приводить пример, как это возможно, но я думаю, что разработчики знают, как решить эту проблему.
Во-первых, dart2js НЕ является компилятором Dart VM, к которому применима языковая спецификация. Поскольку Dart VM и Javascript - это разные языки, в очень абстрактных угловых случаях может быть разное поведение или ограничения. Они не должны, но они делают.
При этом я не понимаю, почему этот код в первую очередь может работать на ВМ. Согласно всему, что я знаю о наследовании и миксинах в Dart, ваше определение Lookup
должен выглядеть так:
class Lookup<TKey, TElement> extends Object with Enumerable<IGrouping<TKey, TElement>> implements ILookup<TKey, TElement>`
Потому что в противном случае, как говорится в сообщении об ошибке, вы бы наследовали HasIterator два раза с разными параметрами типа, что, конечно, является небольшой проблемой - если вы наконец добавите методы в HasIterator, какой из этих двух должен быть вызван? method1(dynamic)
или же method1(IGrouping<TKey, TElement>)
?