mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Front end: fix type inference for object patterns that refer to typedefs.
I've also added some runtime checks to `tests/language/patterns/object_pattern_inference_test.dart` to verify that the object patterns match when they should match and don't match when they shouldn't; this helps verify that not only has the front end inferred the correct type for the object patterns, but it has also stored the correct type in the kernel representation. Fixes #51795. Bug: https://github.com/dart-lang/sdk/issues/51795 Change-Id: I73ce43e440db50d9942deb4a1eb4ee68a5c23142 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/290900 Reviewed-by: Chloe Stefantsova <cstefantsova@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
628ba2ac64
commit
c175d48343
|
@ -10925,30 +10925,45 @@ class InferenceVisitorImpl extends InferenceVisitorBase
|
||||||
}) {
|
}) {
|
||||||
DartType requiredType = pattern.requiredType;
|
DartType requiredType = pattern.requiredType;
|
||||||
if (!pattern.hasExplicitTypeArguments) {
|
if (!pattern.hasExplicitTypeArguments) {
|
||||||
if (pattern.typedef != null) {
|
Typedef? typedef = pattern.typedef;
|
||||||
// TODO(paulberry): handle typedefs properly.
|
if (typedef != null) {
|
||||||
}
|
List<TypeParameter> typedefTypeParameters = typedef.typeParameters;
|
||||||
if (requiredType is InterfaceType &&
|
if (typedefTypeParameters.isNotEmpty) {
|
||||||
requiredType.classNode.typeParameters.isNotEmpty) {
|
List<DartType> asTypeArguments =
|
||||||
|
getAsTypeArguments(typedefTypeParameters, libraryBuilder.library);
|
||||||
|
TypedefType typedefType = new TypedefType(
|
||||||
|
typedef, libraryBuilder.library.nonNullable, asTypeArguments);
|
||||||
|
DartType unaliasedTypedef = typedefType.unalias;
|
||||||
|
List<DartType> inferredTypeArguments = _inferTypeArguments(
|
||||||
|
typeParameters: typedefTypeParameters,
|
||||||
|
declaredType: unaliasedTypedef,
|
||||||
|
contextType: matchedType);
|
||||||
|
requiredType = new TypedefType(typedef,
|
||||||
|
libraryBuilder.library.nonNullable, inferredTypeArguments)
|
||||||
|
.unalias;
|
||||||
|
}
|
||||||
|
} else if (requiredType is InterfaceType) {
|
||||||
List<TypeParameter> typeParameters =
|
List<TypeParameter> typeParameters =
|
||||||
requiredType.classNode.typeParameters;
|
requiredType.classNode.typeParameters;
|
||||||
|
if (typeParameters.isNotEmpty) {
|
||||||
|
// It's possible that one of the callee type parameters might match a
|
||||||
|
// type that already exists as part of inference. This might happen,
|
||||||
|
// for instance, in the case where a method in a generic class
|
||||||
|
// contains an object pattern naming the enclosing class. To avoid
|
||||||
|
// creating invalid inference results, we need to create fresh type
|
||||||
|
// parameters.
|
||||||
|
FreshTypeParameters fresh = getFreshTypeParameters(typeParameters);
|
||||||
|
InterfaceType declaredType = new InterfaceType(requiredType.classNode,
|
||||||
|
requiredType.declaredNullability, fresh.freshTypeArguments);
|
||||||
|
typeParameters = fresh.freshTypeParameters;
|
||||||
|
|
||||||
// It's possible that one of the callee type parameters might match a
|
List<DartType> inferredTypeArguments = _inferTypeArguments(
|
||||||
// type that already exists as part of inference. This might happen,
|
typeParameters: typeParameters,
|
||||||
// for instance, in the case where a method in a generic class contains
|
declaredType: declaredType,
|
||||||
// an object pattern naming the enclosing class. To avoid creating
|
contextType: matchedType);
|
||||||
// invalid inference results, we need to create fresh type parameters.
|
requiredType = new InterfaceType(requiredType.classNode,
|
||||||
FreshTypeParameters fresh = getFreshTypeParameters(typeParameters);
|
requiredType.declaredNullability, inferredTypeArguments);
|
||||||
InterfaceType declaredType = new InterfaceType(requiredType.classNode,
|
}
|
||||||
requiredType.declaredNullability, fresh.freshTypeArguments);
|
|
||||||
typeParameters = fresh.freshTypeParameters;
|
|
||||||
|
|
||||||
List<DartType> inferredTypeArguments = _inferTypeArguments(
|
|
||||||
typeParameters: typeParameters,
|
|
||||||
declaredType: declaredType,
|
|
||||||
contextType: matchedType);
|
|
||||||
requiredType = new InterfaceType(requiredType.classNode,
|
|
||||||
requiredType.declaredNullability, inferredTypeArguments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return requiredType;
|
return requiredType;
|
||||||
|
|
|
@ -44,14 +44,15 @@ bool simpleInference(B<num> b) {
|
||||||
// No need for a `return` since the switch is exhaustive.
|
// No need for a `return` since the switch is exhaustive.
|
||||||
}
|
}
|
||||||
|
|
||||||
void inferDynamic(Object o) {
|
bool inferDynamic(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case C(listOfT: var x, t: var t):
|
case C(listOfT: var x, t: var t):
|
||||||
x.expectStaticType<Exactly<List<dynamic>>>();
|
x.expectStaticType<Exactly<List<dynamic>>>();
|
||||||
t.isEven; // Should be ok since T is `dynamic`.
|
t.isEven; // Should be ok since T is `dynamic`.
|
||||||
o.expectStaticType<Exactly<C<dynamic>>>();
|
o.expectStaticType<Exactly<C<dynamic>>>();
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Expect.fail('Match failure');
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +67,14 @@ class D<T extends num> {
|
||||||
D(this.t);
|
D(this.t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inferBound(Object o) {
|
bool inferBound(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case D(listOfT: var x):
|
case D(listOfT: var x):
|
||||||
x.expectStaticType<Exactly<List<num>>>();
|
x.expectStaticType<Exactly<List<num>>>();
|
||||||
o.expectStaticType<Exactly<D<num>>>();
|
o.expectStaticType<Exactly<D<num>>>();
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Expect.fail('Match failure');
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +112,14 @@ class F2 extends F1<F2> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fBounded(Object o) {
|
bool fBounded(Object o) {
|
||||||
switch (o) {
|
switch (o) {
|
||||||
case F1(listOfT: var x):
|
case F1(listOfT: var x):
|
||||||
x.expectStaticType<Exactly<List<F1<Object?>>>>();
|
x.expectStaticType<Exactly<List<F1<Object?>>>>();
|
||||||
o.expectStaticType<Exactly<F1<F1<Object?>>>>();
|
o.expectStaticType<Exactly<F1<F1<Object?>>>>();
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
Expect.fail('Match failure');
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,22 +139,71 @@ class G2<T, U extends Set<T>> extends G1<T> {
|
||||||
G2(super.t, this.u);
|
G2(super.t, this.u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void partialInference(G1<int> g) {
|
bool partialInference(G1<int> g) {
|
||||||
switch (g) {
|
switch (g) {
|
||||||
case G2(listOfT: var x, listOfU: var y):
|
case G2(listOfT: var x, listOfU: var y):
|
||||||
x.expectStaticType<Exactly<List<int>>>();
|
x.expectStaticType<Exactly<List<int>>>();
|
||||||
y.expectStaticType<Exactly<List<Set<int>>>>();
|
y.expectStaticType<Exactly<List<Set<int>>>>();
|
||||||
g.expectStaticType<Exactly<G2<int, Set<int>>>>();
|
g.expectStaticType<Exactly<G2<int, Set<int>>>>();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class H1<T, U> {
|
||||||
|
final T t;
|
||||||
|
final U u;
|
||||||
|
|
||||||
|
List<T> get listOfT => [t];
|
||||||
|
List<U> get listOfU => [u];
|
||||||
|
|
||||||
|
H1(this.t, this.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef H2<S extends num> = H1<S, String>;
|
||||||
|
|
||||||
|
bool typedefResolvingToInterfaceType(Object o) {
|
||||||
|
switch (o) {
|
||||||
|
case H2(listOfT: var x, listOfU: var y):
|
||||||
|
x.expectStaticType<Exactly<List<num>>>();
|
||||||
|
y.expectStaticType<Exactly<List<String>>>();
|
||||||
|
o.expectStaticType<Exactly<H2<num>>>();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef I<S extends num> = String Function(S);
|
||||||
|
|
||||||
|
bool typedefResolvingToFunctionType(Object o) {
|
||||||
|
switch (o) {
|
||||||
|
case I():
|
||||||
|
o.expectStaticType<Exactly<I<num>>>();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
explicitTypeArguments(C(0));
|
Expect.isTrue(explicitTypeArguments(C(0)));
|
||||||
simpleInference(C(0));
|
Expect.isTrue(simpleInference(C(0)));
|
||||||
inferDynamic(C(0));
|
Expect.isTrue(inferDynamic(C(0)));
|
||||||
inferBound(D(0));
|
Expect.isFalse(inferDynamic(0));
|
||||||
E<int, String>(0, '')
|
Expect.isTrue(inferBound(D(0)));
|
||||||
.inferEnclosingTypeParameters(E<Set<String>, Set<int>>({''}, {0}));
|
Expect.isFalse(inferBound(0));
|
||||||
fBounded(F2());
|
Expect.isTrue(E<int, String>(0, '')
|
||||||
partialInference(G2(0, {0}));
|
.inferEnclosingTypeParameters(E<Set<String>, Set<int>>({''}, {0})));
|
||||||
|
Expect.isTrue(fBounded(F2()));
|
||||||
|
Expect.isFalse(fBounded(0));
|
||||||
|
Expect.isTrue(partialInference(G2(0, {0})));
|
||||||
|
Expect.isFalse(partialInference(G1(0)));
|
||||||
|
Expect.isTrue(typedefResolvingToInterfaceType(H1<int, String>(0, '')));
|
||||||
|
Expect.isTrue(typedefResolvingToInterfaceType(H1<num, String>(0, '')));
|
||||||
|
Expect.isFalse(typedefResolvingToInterfaceType(H1<Object, String>(0, '')));
|
||||||
|
Expect.isTrue(typedefResolvingToFunctionType((Object o) => o.toString()));
|
||||||
|
Expect.isTrue(typedefResolvingToFunctionType((num n) => n.toString()));
|
||||||
|
Expect.isFalse(typedefResolvingToFunctionType((int i) => i.toString()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue