blob: cc56148edd475cb7ad45daad4f6cf441ae2e5dbe [file] [log] [blame]
// 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.
/// @assertion At runtime, a pattern is matched against a value. This determines
/// whether or not the match fails and the pattern refutes the value. If the
/// match succeeds, the pattern may also destructure data from the object or
/// bind variables.
///
/// Refutable patterns usually occur in a context where match refutation causes
/// execution to skip over the body of code where any variables bound by the
/// pattern are in scope. If a pattern match failure occurs in an irrefutable
/// context, a runtime error is thrown.
///
/// To match a pattern p against a value v:
/// ...
/// Map:
/// i. If the runtime type of v is not a subtype of the required type of p then
/// the match fails.
/// ii. For each entry in p, in source order:
/// a. Evaluate the key expression to k.
/// b. Evaluate v[k] to r.
/// c. If r != null || (null is V) && v.containsKey(k) evaluates to false then
/// the map does not match.
/// Note:
/// - When v[k] returns a non-null value, we know the key is present and
/// we short-circuit the containsKey() call.
/// - If V is known to be a non-nullable type, then null is V is always
/// false and the expression simplifies to:
/// r != null
/// - Conversely, if V is known to be a nullable type, then null is V is
/// always true and the expression simplifies to:
/// r != null || v.containsKey(k)
/// - When V is a potentially nullable type, the null is V test must be
/// performed but can be hoisted out and shared across all entries since
/// it doesn't depend on k.
/// - If v is a poorly behaved Map whose v[k] and containsKey(k) results
/// don't agree (i.e. a non-null v[k] and false containsKey(k) or vice
/// versa) we do not detect that mismatch. Since badly behaved maps are
/// rare, this is allowed. Even if v is poorly behaved, a null value
/// will only be passed to the subpattern if null is V, so soundness is
/// preserved.
/// d. Else, match r against this entry's value subpattern. If it does not
/// match, the map does not match.
/// iii. The match succeeds if all entry subpatterns match.
///
/// @description Checks that match succeeds if all entry subpatterns match.
/// @author sgrekhov22@gmail.com
// Requirements=nnbd-strong
import "patterns_collections_lib.dart";
import "../../Utils/expect.dart";
String test1(Object o) {
switch (o) {
case <String, int?>{"key1": null}:
return "match";
default:
return "no match";
}
}
String test2(Object o) {
if (o case <String, int?>{"key1": null}) {
return "match";
}
return "no match";
}
String test3(Object o) {
return switch (o) {
<String, int?>{"key1": null} => "match",
_ => "no match"
};
}
main() {
final map = MyMap<String, int?>({"key1": null, "key2": 42});
Expect.equals("match", test1(map));
Expect.equals("[key1];containsKey(key1);", map.log);
map.clearLog();
Expect.equals("match", test2(map));
Expect.equals("[key1];containsKey(key1);", map.log);
map.clearLog();
Expect.equals("match", test3(map));
Expect.equals("[key1];containsKey(key1);", map.log);
map.clearLog();
}