Dart2JS Compiler cannot compile code. Is this bug, feature or limitation?

226 views Asked by At

I use Dart2JS Compiler version 1.0.0_r30798 (STABLE).

Sample code (only for introducing problem):

The real code here (now corrected for dart2js behavior): https://github.com/mezoni/queries/blob/master/lib/src/queries/lookup.dart

This is a part of 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);
}

This code generates the following error of 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.

That is, the dart2js compiler cannot compile this code.

So, I cannot understand: " Is this bug, feature or limitation?".

2

There are 2 answers

2
mezoni On BEST ANSWER

Answer from Dart Team is very well.

"The VM behavior is correct, and dart2js isn't implementing it yet."

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

Also answer from Gilad Bracha.

"FWIW, the spec does not have such a restriction" (RE: class implements an interface with two different type parameters).

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

Also very well mentioned:

"Unfortunately this is an intentional restriction in dart2js for now. Implementing the same interface with different type arguments has never really worked, so we felt very uncomfortable having people depend on the broken behavior."

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

This answer fully fits in that the sample code in original question correct and it cannot be compiled currently via dart2js.

P.S.

My thoughts (my jumpers):

I think this problem can be solved in the Dart2JS compiler via better testing of type compatibility but not only via testing the equality of classes.

I think that in this case HasIterator<dynamic> and HasIterator<IGrouping<TKey, TElement>> is not the same types (even they the same classes) because they both just implicitly specifies lower and upper bounds of TElement parameter of HasIterator<TElement>.

In practice this is more complex that I can explain here but I can add the following.

They not the same types because this expression is true:

HasIterator<dynamic> != HasIterator<IGrouping<TKey, TElement>>

They not conflicts (but implicitly specifies lower and upper bounds) because one from the following expressions is true.

HasIterator<dynamic> is HasIterator<IGrouping<TKey, TElement>>
HasIterator<IGrouping<TKey, TElement>> is HasIterator<dynamic>

Is our case (implicit) lower bound is dynamic and (implicit) upper bound is <IGrouping<TKey, TElement>.

The implicit term means only resolved at compile time.

This means that one from them is a subtype of another and compiler must allow both of them in declaration. And in type annotations the compiler must tests parameters on compatibility with both of them (including other super interfaces).

If Dart2JS will test supertypes more thoroughly it can bypass around this problem.

I not want here give sample how this is possible but I think developers know how to solve this problem.

6
MarioP On

First off, dart2js is NOT the Dart VM Compiler, to which the language specification applies. Because the Dart VM and Javascript are different languages, differing behaviour or restrictions in very abstract corner cases can happen. They shouldn't, but they do.

That being said, I don't understand why this code is able to run in the VM in the first place. According to everything I know about inheritance and mixins in Dart, your definition of Lookup should look like this:

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

Because otherwise, as the error message says, you would inherit HasIterator two times with different type parameters, which of course is a little bit of a problem - if you finally add methods to HasIterator, which of those two should be called? method1(dynamic) or method1(IGrouping<TKey, TElement>)?