c175d48343
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>
210 lines
5.2 KiB
Dart
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()));
|
|
}
|