Files
sdk/tests/language/patterns/object_pattern_inference_test.dart
T
Paul Berry c175d48343 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>
2023-03-24 12:20:08 +00:00

210 lines
5.2 KiB
Dart

// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SharedOptions=--enable-experiment=patterns,records
// Test that the type arguments of object patterns are properly inferred.
import "package:expect/expect.dart";
import "../static_type_helper.dart";
sealed class B<T> {}
class C<T> extends B<T> {
final T t;
// Note: we use this getter to obtain values for `expectStaticType` (rather
// than a getter returning simply `T`) to ensure that an error is reported if
// `T` gets inferred to be `dynamic`.
List<T> get listOfT => [t];
C(this.t);
}
bool explicitTypeArguments(B<int> b) {
switch (b) {
case C<num>(listOfT: var x):
x.expectStaticType<Exactly<List<num>>>();
// Since C<num> isn't a subtype of B<int>, `b` is not promoted.
b.expectStaticType<Exactly<B<int>>>();
return true;
}
// No need for a `return` since the switch is exhaustive.
}
bool simpleInference(B<num> b) {
switch (b) {
case C(listOfT: var x):
x.expectStaticType<Exactly<List<num>>>();
b.expectStaticType<Exactly<C<num>>>();
return true;
}
// No need for a `return` since the switch is exhaustive.
}
bool inferDynamic(Object o) {
switch (o) {
case C(listOfT: var x, t: var t):
x.expectStaticType<Exactly<List<dynamic>>>();
t.isEven; // Should be ok since T is `dynamic`.
o.expectStaticType<Exactly<C<dynamic>>>();
return true;
default:
return false;
}
}
class D<T extends num> {
final T t;
// Note: we use this getter to obtain values for `expectStaticType` (rather
// than a getter returning simply `T`) to ensure that an error is reported if
// `T` gets inferred to be `dynamic`.
List<T> get listOfT => [t];
D(this.t);
}
bool inferBound(Object o) {
switch (o) {
case D(listOfT: var x):
x.expectStaticType<Exactly<List<num>>>();
o.expectStaticType<Exactly<D<num>>>();
return true;
default:
return false;
}
}
class E<T, U> {
final T t;
final U u;
List<T> get listOfT => [t];
List<U> get listOfU => [u];
E(this.t, this.u);
bool inferEnclosingTypeParameters(E<Set<U>, Set<T>> e) {
// This test verifies that the inference logic properly distinguishes
// between the type parameters of the enclosing class and those that are
// part of the type of `e`.
if (e case E(listOfT: var x, listOfU: var y)) {
x.expectStaticType<Exactly<List<Set<U>>>>();
y.expectStaticType<Exactly<List<Set<T>>>>();
return true;
}
// No need for a `return` since the case fully covers the type of `e`.
}
}
class F1<T extends F1<T>> {
late final T t;
List<T> get listOfT => [t];
}
class F2 extends F1<F2> {
F2() {
t = this;
}
}
bool fBounded(Object o) {
switch (o) {
case F1(listOfT: var x):
x.expectStaticType<Exactly<List<F1<Object?>>>>();
o.expectStaticType<Exactly<F1<F1<Object?>>>>();
return true;
default:
return false;
}
}
class G1<T> {
final T t;
List<T> get listOfT => [t];
G1(this.t);
}
class G2<T, U extends Set<T>> extends G1<T> {
final U u;
List<U> get listOfU => [u];
G2(super.t, this.u);
}
bool partialInference(G1<int> g) {
switch (g) {
case G2(listOfT: var x, listOfU: var y):
x.expectStaticType<Exactly<List<int>>>();
y.expectStaticType<Exactly<List<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() {
Expect.isTrue(explicitTypeArguments(C(0)));
Expect.isTrue(simpleInference(C(0)));
Expect.isTrue(inferDynamic(C(0)));
Expect.isFalse(inferDynamic(0));
Expect.isTrue(inferBound(D(0)));
Expect.isFalse(inferBound(0));
Expect.isTrue(E<int, String>(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()));
}