Dart2JS编译器无法编译代码。这个bug,功能还是限制?

时间:2014-01-12 16:49:50

标签: parsing compiler-construction dart standards-compliance maturity

我使用的是Dart2JS编译器版本1.0.0_r30798(STABLE)。

示例代码(仅用于介绍问题):

此处的实际代码(现已针对dart2js行为进行了更正):https://github.com/mezoni/queries/blob/master/lib/src/queries/lookup.dart

这是其中的一部分 Queryable collections for Dart language

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编译器错误:

 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 个答案:

答案 0 :(得分:3)

首先,dart2js不是适用于语言规范的Dart VM编译器。由于Dart VM和Javascript是不同的语言,因此可能会出现非常抽象的极端情况中的不同行为或限制。他们不应该,但他们确实如此。

话虽如此,我不明白为什么这个代码首先能够在VM中运行。根据我对Dart中的继承和mixin的了解,您对Lookup的定义应如下所示:

class Lookup<TKey, TElement> extends Object with Enumerable<IGrouping<TKey, TElement>> implements ILookup<TKey, TElement>`

因为否则,正如错误消息所示,您将使用不同的类型参数继承HasIterator两次,这当然有点问题 - 如果您最后向HasIterator添加方法,那么应该调用哪两个? method1(dynamic)method1(IGrouping<TKey, TElement>)

答案 1 :(得分:1)

Dart Team的答案非常好。

“VM行为正确,dart2js尚未实现它。”

https://code.google.com/p/dart/issues/detail?id=16047#c5

Gilad Bracha也回答。

“FWIW,规范没有这样的限制”(RE:class实现了一个带有两个不同类型参数的接口)。

https://code.google.com/p/dart/issues/detail?id=14729#c2

也很好地提到:

“不幸的是,这是dart2js中的故意限制。使用不同类型参数实现相同的接口从未真正起作用,因此我们感到非常不舒服让人们依赖于破坏的行为。”

https://code.google.com/p/dart/issues/detail?id=14729#c3

这个答案完全符合原始问题中的示例代码是正确的,目前无法通过dart2js进行编译。

P.S。

我的想法(我的跳投):

我认为这个问题可以通过更好的类型兼容性测试在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会更彻底地测试超类型,它可以绕过这个问题。

我不想在这里举例说明这是可能的,但我认为开发人员知道如何解决这个问题。