mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:01:20 +00:00
Fix handling of nested typedefs (for real this time).
This is a re-fix of dartbug.com/21912, which I previously fixed incorrectly. Previously, our approach to avoiding infinite loops when comparing types was to maintain a set of typedefs being expanded on the stack, and prune the comparison whenever an attempt was made to expand a typedef that was already being expanded. However, this was too strict, since there are legal (non-circular) types which invole expanding a given typedef in reentrant fashion; we can't prune these types without producing incorrect semantics. An example (from the bug report) is the type of f in the code below: typedef T Function2<S, T>(S z); Function2<Function2<A, B>, Function2<B, A>> f; The solution is to maintain the list of typedefs being expanded inside each FunctionTypeImpl object (and InterfaceTypeImpl object) rather than on the stack during the comparison; this allows us to distinguish the situations where we need to prune (those having to do exclusively with expansion of a typedef) from the situations where we shouldn't prune (those having to do with substitution of a type parameter). A beneficial side effect of this change is that code that interacts with types no longer needs to worry about typedef circularities, since the circularities will automatically be pruned while exploring the type definitions. This simplifies the implementation of isAssignableTo, isSubtypeOf, operator==, and hashCode. (Note, however, that code still needs to cope with circularities in the inheritance hierarchy). BUG=dartbug.com/21912 R=brianwilkerson@google.com Review URL: https://codereview.chromium.org//1143003007
This commit is contained in:
parent
21f6f1aaca
commit
62f7ac8c5d
File diff suppressed because it is too large
Load diff
|
@ -55,7 +55,7 @@ class ElementFactory {
|
|||
new List<TypeParameterTypeImpl>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
TypeParameterElementImpl typeParameter =
|
||||
new TypeParameterElementImpl(parameterNames[i], 0);
|
||||
typeParameterElement(parameterNames[i]);
|
||||
typeParameters[i] = typeParameter;
|
||||
typeParameterTypes[i] = new TypeParameterTypeImpl(typeParameter);
|
||||
typeParameter.type = typeParameterTypes[i];
|
||||
|
@ -346,6 +346,14 @@ class ElementFactory {
|
|||
return functionElement;
|
||||
}
|
||||
|
||||
static FunctionTypeAliasElementImpl functionTypeAliasElement(String name) {
|
||||
FunctionTypeAliasElementImpl functionTypeAliasElement =
|
||||
new FunctionTypeAliasElementImpl(name, -1);
|
||||
functionTypeAliasElement.type =
|
||||
new FunctionTypeImpl.forTypedef(functionTypeAliasElement);
|
||||
return functionTypeAliasElement;
|
||||
}
|
||||
|
||||
static PropertyAccessorElementImpl getterElement(
|
||||
String name, bool isStatic, DartType type) {
|
||||
FieldElementImpl field = new FieldElementImpl(name, -1);
|
||||
|
@ -537,4 +545,7 @@ class ElementFactory {
|
|||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
static TypeParameterElementImpl typeParameterElement(String name) =>
|
||||
new TypeParameterElementImpl(name, 0);
|
||||
}
|
||||
|
|
|
@ -1258,6 +1258,24 @@ class FunctionTypeImplTest extends EngineTestCase {
|
|||
isNotNull);
|
||||
}
|
||||
|
||||
void test_equality_recursive() {
|
||||
FunctionTypeAliasElementImpl s =
|
||||
ElementFactory.functionTypeAliasElement('s');
|
||||
FunctionTypeAliasElementImpl t =
|
||||
ElementFactory.functionTypeAliasElement('t');
|
||||
FunctionTypeAliasElementImpl u =
|
||||
ElementFactory.functionTypeAliasElement('u');
|
||||
FunctionTypeAliasElementImpl v =
|
||||
ElementFactory.functionTypeAliasElement('v');
|
||||
s.returnType = t.type;
|
||||
t.returnType = s.type;
|
||||
u.returnType = v.type;
|
||||
v.returnType = u.type;
|
||||
// We don't care whether the types compare equal or not. We just need the
|
||||
// computation to terminate.
|
||||
expect(s.type == u.type, new isInstanceOf<bool>());
|
||||
}
|
||||
|
||||
void test_getElement() {
|
||||
FunctionElementImpl typeElement =
|
||||
new FunctionElementImpl.forNode(AstFactory.identifier3("f"));
|
||||
|
@ -1307,6 +1325,18 @@ class FunctionTypeImplTest extends EngineTestCase {
|
|||
type.hashCode;
|
||||
}
|
||||
|
||||
void test_hashCode_recursive() {
|
||||
FunctionTypeAliasElementImpl s =
|
||||
ElementFactory.functionTypeAliasElement('s');
|
||||
FunctionTypeAliasElementImpl t =
|
||||
ElementFactory.functionTypeAliasElement('t');
|
||||
s.returnType = t.type;
|
||||
t.returnType = s.type;
|
||||
// We don't care what the hash code is. We just need its computation to
|
||||
// terminate.
|
||||
expect(t.type.hashCode, new isInstanceOf<int>());
|
||||
}
|
||||
|
||||
void test_isAssignableTo_normalAndPositionalArgs() {
|
||||
// ([a]) -> void <: (a) -> void
|
||||
ClassElement a = ElementFactory.classElement2("A");
|
||||
|
@ -1691,6 +1721,141 @@ class FunctionTypeImplTest extends EngineTestCase {
|
|||
expect(s.isSubtypeOf(t), isFalse);
|
||||
}
|
||||
|
||||
void test_namedParameterTypes_pruned_no_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.parameters = [ElementFactory.namedParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.namedParameterTypes['x'];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_namedParameterTypes_pruned_with_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.typeParameters = [ElementFactory.typeParameterElement('T')];
|
||||
f.parameters = [ElementFactory.namedParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.namedParameterTypes['x'];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_newPrune_no_previous_prune() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeImpl type = f.type;
|
||||
List<FunctionTypeAliasElement> pruneList = type.newPrune;
|
||||
expect(pruneList, hasLength(1));
|
||||
expect(pruneList[0], same(f));
|
||||
}
|
||||
|
||||
void test_newPrune_non_typedef() {
|
||||
// No pruning needs to be done for function types that aren't associated
|
||||
// with typedefs because those types can't be directly referred to by the
|
||||
// user (and hence can't participate in circularities).
|
||||
FunctionElementImpl f = ElementFactory.functionElement('f');
|
||||
FunctionTypeImpl type = f.type;
|
||||
expect(type.newPrune, isNull);
|
||||
}
|
||||
|
||||
void test_newPrune_synthetic_typedef() {
|
||||
// No pruning needs to be done for function types that are associated with
|
||||
// synthetic typedefs because those types are only created for
|
||||
// function-typed formal parameters, which can't be directly referred to by
|
||||
// the user (and hence can't participate in circularities).
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
f.synthetic = true;
|
||||
FunctionTypeImpl type = f.type;
|
||||
expect(type.newPrune, isNull);
|
||||
}
|
||||
|
||||
void test_newPrune_with_previous_prune() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
FunctionTypeImpl type = f.type;
|
||||
FunctionTypeImpl prunedType = type.pruned([g]);
|
||||
List<FunctionTypeAliasElement> pruneList = prunedType.newPrune;
|
||||
expect(pruneList, hasLength(2));
|
||||
expect(pruneList, contains(f));
|
||||
expect(pruneList, contains(g));
|
||||
}
|
||||
|
||||
void test_normalParameterTypes_pruned_no_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.parameters = [ElementFactory.requiredParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.normalParameterTypes[0];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_normalParameterTypes_pruned_with_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.typeParameters = [ElementFactory.typeParameterElement('T')];
|
||||
f.parameters = [ElementFactory.requiredParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.normalParameterTypes[0];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_optionalParameterTypes_pruned_no_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.parameters = [ElementFactory.positionalParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.optionalParameterTypes[0];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_optionalParameterTypes_pruned_with_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.typeParameters = [ElementFactory.typeParameterElement('T')];
|
||||
f.parameters = [ElementFactory.positionalParameter2('x', g.type)];
|
||||
FunctionTypeImpl paramType = f.type.optionalParameterTypes[0];
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_returnType_pruned_no_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.returnType = g.type;
|
||||
FunctionTypeImpl paramType = f.type.returnType;
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_returnType_pruned_with_type_arguments() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
FunctionTypeAliasElementImpl g =
|
||||
ElementFactory.functionTypeAliasElement('g');
|
||||
f.typeParameters = [ElementFactory.typeParameterElement('T')];
|
||||
f.returnType = g.type;
|
||||
FunctionTypeImpl paramType = f.type.returnType;
|
||||
expect(paramType.prunedTypedefs, hasLength(1));
|
||||
expect(paramType.prunedTypedefs[0], same(f));
|
||||
}
|
||||
|
||||
void test_setTypeArguments() {
|
||||
ClassElementImpl enclosingClass = ElementFactory.classElement2("C", ["E"]);
|
||||
MethodElementImpl methodElement =
|
||||
|
@ -1773,12 +1938,22 @@ class FunctionTypeImplTest extends EngineTestCase {
|
|||
}
|
||||
|
||||
void test_toString_recursive() {
|
||||
FunctionElementImpl t = ElementFactory.functionElement("t");
|
||||
FunctionElementImpl s = ElementFactory.functionElement("s");
|
||||
FunctionTypeAliasElementImpl t =
|
||||
ElementFactory.functionTypeAliasElement("t");
|
||||
FunctionTypeAliasElementImpl s =
|
||||
ElementFactory.functionTypeAliasElement("s");
|
||||
t.returnType = s.type;
|
||||
s.returnType = t.type;
|
||||
expect(t.type.toString(), '() \u2192 () \u2192 ...');
|
||||
}
|
||||
|
||||
void test_toString_recursive_via_interface_type() {
|
||||
FunctionTypeAliasElementImpl f =
|
||||
ElementFactory.functionTypeAliasElement('f');
|
||||
ClassElementImpl c = ElementFactory.classElement2('C', ['T']);
|
||||
f.returnType = c.type.substitute4([f.type]);
|
||||
expect(f.type.toString(), '() \u2192 C<...>');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
|
|
|
@ -237,6 +237,40 @@ f(A a) {
|
|||
verify([source]);
|
||||
}
|
||||
|
||||
void test_assignability_function_expr_rettype_from_typedef_cls() {
|
||||
// In the code below, the type of (() => f()) has a return type which is
|
||||
// a class, and that class is inferred from the return type of the typedef
|
||||
// F.
|
||||
Source source = addSource('''
|
||||
class C {}
|
||||
typedef C F();
|
||||
F f;
|
||||
main() {
|
||||
F f2 = (() => f());
|
||||
}
|
||||
''');
|
||||
resolve(source);
|
||||
assertNoErrors(source);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
void test_assignability_function_expr_rettype_from_typedef_typedef() {
|
||||
// In the code below, the type of (() => f()) has a return type which is
|
||||
// a typedef, and that typedef is inferred from the return type of the
|
||||
// typedef F.
|
||||
Source source = addSource('''
|
||||
typedef G F();
|
||||
typedef G();
|
||||
F f;
|
||||
main() {
|
||||
F f2 = (() => f());
|
||||
}
|
||||
''');
|
||||
resolve(source);
|
||||
assertNoErrors(source);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
void test_assignmentToFinal_prefixNegate() {
|
||||
Source source = addSource(r'''
|
||||
f() {
|
||||
|
|
|
@ -80,6 +80,35 @@ f() async {
|
|||
verify([source]);
|
||||
}
|
||||
|
||||
void test_bug21912() {
|
||||
Source source = addSource('''
|
||||
class A {}
|
||||
class B extends A {}
|
||||
|
||||
typedef T Function2<S, T>(S z);
|
||||
typedef B AToB(A x);
|
||||
typedef A BToA(B x);
|
||||
|
||||
void main() {
|
||||
{
|
||||
Function2<Function2<A, B>, Function2<B, A>> t1;
|
||||
Function2<AToB, BToA> t2;
|
||||
|
||||
Function2<Function2<int, double>, Function2<int, double>> left;
|
||||
|
||||
left = t1;
|
||||
left = t2;
|
||||
}
|
||||
}
|
||||
''');
|
||||
resolve(source);
|
||||
assertErrors(source, [
|
||||
StaticTypeWarningCode.INVALID_ASSIGNMENT,
|
||||
StaticTypeWarningCode.INVALID_ASSIGNMENT
|
||||
]);
|
||||
verify([source]);
|
||||
}
|
||||
|
||||
void test_expectedOneListTypeArgument() {
|
||||
Source source = addSource(r'''
|
||||
main() {
|
||||
|
|
|
@ -16,7 +16,7 @@ void main() {
|
|||
Function2<Function2<A, B>, Function2<B, A>> t1;
|
||||
Function2<AToB, BToA> t2;
|
||||
Function2<Function2<int, double>, Function2<int, double>> left;
|
||||
left = t1; /// 01: ok
|
||||
left = t2; /// 02: ok
|
||||
left = t1; /// 01: static type warning
|
||||
left = t2; /// 02: static type warning
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue