Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I discovered recently that when we implemented the "patterns" feature, we got the pattern type schema for cast patterns wrong--it was specified to be `_`, but what was actually implemented was `Object?`. I've filed a breaking change request to address that: #54640. In the course of prototyping a fix, I was surprised to find that no language tests or co19 tests were affected; it appears that we don't have adequate test coverage of pattern type schemas. This CL addresses the lack of test coverage by adding a language test. The test case for cast patterns is currently commented out, pending resolution of the breaking change request; after the breaking change lands, I will update the test accordingly. Change-Id: I0d27fd8ac65f16d21a8597586e6f76fd9a5ba86e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/346621 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Bob Nystrom <rnystrom@google.com>
- Loading branch information
1 parent
4b273b4
commit c5fb584
Showing
1 changed file
with
173 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
// Copyright (c) 2024, 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. | ||
|
||
// Verifies that pattern type schemas produced by the implementation correspond | ||
// to the specification in | ||
// https://github.com/dart-lang/language/blob/main/accepted/3.0/patterns/feature-specification.md#pattern-context-type-schema. | ||
|
||
import '../static_type_helper.dart'; | ||
|
||
test() { | ||
// The context type schema for a pattern `p` is: | ||
|
||
// - Logical-and: The greatest lower bound of the context type schemas of the | ||
// branches. | ||
{ | ||
var (void Function(int) x && void Function(double) y) = | ||
contextType((n) => n.expectStaticType<Exactly<num>>()) | ||
..expectStaticType<Exactly<void Function(num)>>(); | ||
} | ||
|
||
// - Null-assert: A context type schema `E?` where `E` is the context type | ||
// schema of the inner pattern. | ||
{ | ||
var ((int i)!) = contextType(1)..expectStaticType<Exactly<int?>>(); | ||
} | ||
|
||
// - Variable: | ||
// | ||
// i. If `p` has a type annotation, the context type schema is the annotated | ||
// type. | ||
{ | ||
var (int i) = contextType(1)..expectStaticType<Exactly<int>>(); | ||
} | ||
|
||
// ii. Else the context type schema is _. | ||
// | ||
// This rule is actually never used, because: | ||
// - Pattern type schemas are only computed for pattern variable | ||
// declarations and pattern assignments. | ||
// - It is a compile-time error if a variable pattern appears in an | ||
// assignment context. So the context type schema for a variable pattern | ||
// only matters if it appears in a declaration context. | ||
// - It is a compile-time error if a variable pattern in a declaration | ||
// context is marked with `var` or `final`. So a variable pattern inside a | ||
// pattern variable declaration must have a type annotation. | ||
|
||
// - Identifier: | ||
// | ||
// i. In an assignment context, the context type schema is the static type | ||
// of the variable that `p` resolves to. | ||
{ | ||
int i; | ||
(i) = contextType(1)..expectStaticType<Exactly<int>>(); | ||
} | ||
|
||
// ii. Else the context type schema is `_`. | ||
{ | ||
var [x] = [1]..expectStaticType<Exactly<List<int>>>(); | ||
var [_] = [1]..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// - Cast: The context type schema is `_`. | ||
{ | ||
// TODO(paulberry): uncomment this after | ||
// https://github.com/dart-lang/sdk/issues/54640 is addressed. | ||
// var [_ as Object] = [1]..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// - Parenthesized: The context type schema of the inner subpattern. | ||
{ | ||
var (<int>[]) = contextType(<int>[]) | ||
..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// - List: A context type schema `List<E>` where: | ||
// | ||
// i. If `p` has a type argument, then `E` is the type argument. | ||
{ | ||
var <int>[] = contextType(<int>[])..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// ii. Else if `p` has no elements then `E` is `_`. | ||
{ | ||
var [] = [1] | ||
..expectStaticType<Exactly<List<int>>>() | ||
..removeLast(); | ||
} | ||
|
||
// iii. Else, infer the type schema from the elements: | ||
// | ||
// a. Let `es` be an empty list of type schemas. | ||
// | ||
// b. For each element `e` in `p`: | ||
// | ||
// a. If `e` is a matching rest element with subpattern `s` and the | ||
// context type schema of `s` is an `Iterable<T>` for some type | ||
// schema `T`, then add `T` to es. | ||
{ | ||
var [ | ||
...Iterable<int> x | ||
] = contextType(<int>[])..expectStaticType<Exactly<List<int>>>(); | ||
var [ | ||
...List<int> y | ||
] = contextType(<int>[])..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// b. Else if `e` is not a rest element, add the context type schema | ||
// of `e` to `es`. | ||
{ | ||
var [int x] = contextType(<int>[0])..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// c. If `es` is empty, then `E` is `_`. | ||
{ | ||
var [...] = [1]..expectStaticType<Exactly<List<int>>>(); | ||
var [...Object x] = [1]..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// d. Else `E` is the greatest lower bound of the type schemas in `es`. | ||
{ | ||
var [void Function(int) x, void Function(double) y] = contextType([ | ||
(n) => n.expectStaticType<Exactly<num>>(), | ||
(n) => n.expectStaticType<Exactly<num>>() | ||
]) | ||
..expectStaticType<Exactly<List<void Function(num)>>>(); | ||
} | ||
|
||
// - Map: A type schema `Map<K, V>` where: | ||
// | ||
// i. If `p` has type arguments then `K`, and `V` are those type arguments. | ||
{ | ||
var <int, String>{0: _} = contextType(<int, String>{0: 'x'}) | ||
..expectStaticType<Exactly<Map<int, String>>>(); | ||
} | ||
|
||
// ii. Else `K` is `_` and `V` is the greatest lower bound of the context | ||
// type schemas of all value subpatterns. | ||
{ | ||
var {1: void Function(int) x, 2: void Function(double) y} = { | ||
(1 as num): (n) => n.expectStaticType<Exactly<num>>(), | ||
(2 as num): (n) => n.expectStaticType<Exactly<num>>() | ||
}..expectStaticType<Exactly<Map<num, void Function(num)>>>(); | ||
} | ||
|
||
// Record: A record type schema with positional and named fields corresponding | ||
// to the type schemas of the corresponding field subpatterns. | ||
{ | ||
var (int _, y: String _) = contextType((1, y: 'y')) | ||
..expectStaticType<Exactly<(int, {String y})>>(); | ||
} | ||
|
||
// Object: The type the object name resolves to. | ||
{ | ||
var List<int>() = contextType(<int>[]) | ||
..expectStaticType<Exactly<List<int>>>(); | ||
} | ||
|
||
// If the type the object name resolves to is generic, and no type arguments | ||
// are specified, then instantiate to bounds is used to fill in provisional | ||
// type arguments for the purpose of determining the context type schema. | ||
{ | ||
var List( | ||
first: x | ||
) = contextType([1])..expectStaticType<Exactly<List<dynamic>>>(); | ||
var List(first: y) = <int>[1]; | ||
y.expectStaticType<Exactly<int>>(); | ||
} | ||
} | ||
|
||
main() { | ||
test(); | ||
} |