Skip to content

Commit

Permalink
Implement instantiateToBounds in front_end.
Browse files Browse the repository at this point in the history
R=scheglov@google.com

Note: error reporting will be added in a follow-up CL.
Review-Url: https://codereview.chromium.org/2866543002 .
  • Loading branch information
stereotype441 committed May 4, 2017
1 parent dadb76a commit c376a45
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,48 @@ class TypeSchemaEnvironment extends TypeEnvironment {
return const DynamicType();
}

/// Given a [DartType] [type], if [type] is an uninstantiated
/// parameterized type then instantiate the parameters to their
/// bounds. See the issue for the algorithm description.
///
/// https://github.com/dart-lang/sdk/issues/27526#issuecomment-260021397
///
/// TODO(paulberry) Compute lazily and cache.
DartType instantiateToBounds(DartType type,
{Map<TypeParameter, DartType> knownTypes}) {
List<TypeParameter> typeFormals = _typeFormalsAsParameters(type);
int count = typeFormals.length;
if (count == 0) {
return type;
}
var substitution = <TypeParameter, DartType>{};
for (TypeParameter parameter in typeFormals) {
// Note: we treat class<T extends Object> as equivalent to class<T>; in
// both cases they instantiate to class<dynamic>. See dartbug.com/29561
if (_isObjectOrDynamic(parameter.bound)) {
substitution[parameter] = const DynamicType();
} else {
substitution[parameter] = parameter.bound;
}
}
if (knownTypes != null) {
type = substitute(type, knownTypes);
}
var result = substituteDeep(type, substitution);
if (result != null) return result;

// Instantiation failed due to a circularity.
// TODO(paulberry): report the error.
// Substitute `dynamic` for all parameters to try to allow compilation to
// continue. Note that [substituteDeep] is destructive of the
// [substitution] so we create a fresh one.
substitution = <TypeParameter, DartType>{};
for (TypeParameter parameter in typeFormals) {
substitution[parameter] = const DynamicType();
}
return substitute(type, substitution);
}

@override
bool isBottom(DartType t) {
if (t is UnknownType) {
Expand Down Expand Up @@ -397,6 +439,23 @@ class TypeSchemaEnvironment extends TypeEnvironment {
return hierarchy.getClassicLeastUpperBound(type1, type2);
}

bool _isObjectOrDynamic(DartType type) =>
type is DynamicType ||
(type is InterfaceType &&
identical(type.classNode, coreTypes.objectClass));

/// Given a [type], returns the [TypeParameter]s corresponding to its formal
/// type parameters (if any).
List<TypeParameter> _typeFormalsAsParameters(DartType type) {
if (type is TypedefType) {
return type.typedefNode.typeParameters;
} else if (type is InterfaceType) {
return type.classNode.typeParameters;
} else {
return const [];
}
}

DartType _typeParameterLeastUpperBound(DartType type1, DartType type2) {
// This currently just implements a simple least upper bound to
// handle some common cases. It also avoids some termination issues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,87 @@ class TypeSchemaEnvironmentTest {
expect(env.getGreatestLowerBound(A, B), same(bottomType));
}

void test_instantiateToBounds_noTypesKnown() {
// class A {}
var A = _addClass(_class('A')).rawType;
// class B<T extends int> {}
var B = _addClass(
_class('B', typeParameters: [new TypeParameter('T', intType)]))
.thisType;
// class C<T extends int, S extends B<T>> {}
var C = () {
var T = new TypeParameter('T', intType);
var S = new TypeParameter(
'S', new InterfaceType(B.classNode, [new TypeParameterType(T)]));
return _addClass(_class('C', typeParameters: [T, S])).thisType;
}();
// class D<T extends B<T>> {}
var D = () {
var T = new TypeParameter('T');
T.bound = new InterfaceType(B.classNode, [new TypeParameterType(T)]);
return _addClass(_class('D', typeParameters: [T])).thisType;
}();
// typedef T E<T extends int>();
var E = () {
var T = new TypeParameter('T', intType);
var typedefNode = new Typedef(
'E', new FunctionType([], new TypeParameterType(T)),
typeParameters: [T]);
return new TypedefType(typedefNode, [new TypeParameterType(T)]);
}();
// class F<T> {}
var F = _addClass(
_class('F', typeParameters: [new TypeParameter('T', objectType)]))
.thisType;
var env = _makeEnv();
// A => A
expect(env.instantiateToBounds(A), same(A));
// B => B<int>
expect(
env.instantiateToBounds(B), new InterfaceType(B.classNode, [intType]));
// C => C<int, A<int>>
expect(
env.instantiateToBounds(C),
new InterfaceType(C.classNode, [
intType,
new InterfaceType(B.classNode, [intType])
]));
// D => error
// However to allow analysis to continue D => D<dynamic>
// TODO(paulberry): check that an error is reported.
expect(env.instantiateToBounds(D), D.classNode.rawType);
// E => E<int> => () -> int
expect(
env.instantiateToBounds(E), new TypedefType(E.typedefNode, [intType]));
// F => F<dynamic>
expect(env.instantiateToBounds(F), F.classNode.rawType);
}

void test_instantiateToBounds_typesKnown() {
// class A<T extends num> {}
var A = _addClass(
_class('A', typeParameters: [new TypeParameter('T', numType)]))
.thisType;
// class B<T extends A<T>> {}
var B = () {
var T = new TypeParameter('T');
T.bound = new InterfaceType(A.classNode, [new TypeParameterType(T)]);
return _addClass(_class('B', typeParameters: [T])).thisType;
}();
var env = _makeEnv();
// A => A<int> (if T known to be `int`)
expect(
env.instantiateToBounds(A,
knownTypes: {A.classNode.typeParameters[0]: intType}),
new InterfaceType(A.classNode, [intType]));
// Check that known types can be used to break circularities
// B => B<int> (if T known to be `int`)
expect(
env.instantiateToBounds(B,
knownTypes: {B.classNode.typeParameters[0]: intType}),
new InterfaceType(B.classNode, [intType]));
}

void test_lub_bottom() {
var A = _addClass(_class('A')).rawType;
var env = _makeEnv();
Expand Down

0 comments on commit c376a45

Please sign in to comment.