| // 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. | |
| import "package:expect/static_type_helper.dart"; | |
| test() { | |
| { | |
| // A pattern assignment can demote an assigned variable. | |
| var x = expr<int?>(); | |
| x!; | |
| x.expectStaticType<Exactly<int>>(); | |
| (x) = expr<int?>(); | |
| x.expectStaticType<Exactly<int?>>(); | |
| } | |
| { | |
| // But it won't demote an assigned variable if it's not necessary. | |
| var x = expr<num?>(); | |
| x!; | |
| x.expectStaticType<Exactly<num>>(); | |
| (x) = expr<int>(); | |
| x.expectStaticType<Exactly<num>>(); | |
| } | |
| { | |
| // Inside a pattern assignment, the type schema of a reference to an | |
| // unpromoted variable is that variable's unpromoted type. | |
| var x = expr<int?>(); | |
| (x) = contextType(expr<int>())..expectStaticType<Exactly<int?>>(); | |
| } | |
| { | |
| // But the type schema of a reference to a promoted variable is that | |
| // variable's promoted type. | |
| var x = expr<int?>(); | |
| x!; | |
| x.expectStaticType<Exactly<int>>(); | |
| (x) = contextType(expr<int>())..expectStaticType<Exactly<int>>(); | |
| } | |
| { | |
| // A pattern assignment can promote a variable to a type of interest. | |
| var x = expr<num>(); | |
| if (x is int) {} | |
| x.expectStaticType<Exactly<num>>(); | |
| (x) = expr<int>(); | |
| x.expectStaticType<Exactly<int>>(); | |
| } | |
| { | |
| // But it won't promote a variable to a type that is not a type of interest. | |
| var x = expr<num>(); | |
| (x) = expr<int>(); | |
| x.expectStaticType<Exactly<num>>(); | |
| } | |
| { | |
| // Assignment to a variable promotes the matched value, in the same way that | |
| // matching a variable pattern promotes the matched value. In this example, | |
| // assignment to `x` promotes the matched value from `dynamic` to `int`, | |
| // therefore attempting to also assign the matched value to the double `y` | |
| // produces an error. | |
| int x; | |
| double y; | |
| (x && y) = expr<dynamic>(); | |
| // ^ | |
| // [analyzer] COMPILE_TIME_ERROR.PATTERN_TYPE_MISMATCH_IN_IRREFUTABLE_CONTEXT | |
| // [cfe] The matched value of type 'int' isn't assignable to the required type 'double'. | |
| } | |
| { | |
| // Assignment to a variable doesn't promote the RHS. | |
| int x; | |
| var y = expr<dynamic>(); | |
| (x) = y; | |
| y.expectStaticType<Exactly<dynamic>>(); | |
| } | |
| { | |
| // Assignment to a variable makes it definitely assigned. | |
| int x; | |
| x; // Definitely not assigned | |
| // [error column 5, length 1] | |
| // [analyzer] COMPILE_TIME_ERROR.NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE | |
| // [cfe] Non-nullable variable 'x' must be assigned before it can be used. | |
| (x) = expr<int>(); | |
| x; // Definitely assigned | |
| } | |
| { | |
| // A pattern assignment that assigns to a boolean variable can capture | |
| // promotions of other variables (just as an ordinary assignment can). | |
| var x = expr<int?>(); | |
| bool b; | |
| (b) = x != null; | |
| if (b) { | |
| x.expectStaticType<Exactly<int>>(); | |
| } | |
| } | |
| { | |
| // But assignment to a boolean variable in a subpattern does not capture | |
| // promotions (since this would be unsound). | |
| var x = expr<int?>(); | |
| bool b; | |
| bool(foo: b) = x != null; | |
| if (b) { | |
| x.expectStaticType<Exactly<int?>>(); | |
| } | |
| } | |
| { | |
| // Inside a record pattern, if a record's field is assigned to a variable, | |
| // and the type of the variable is a subtype of the record's field type, the | |
| // record's field type is promoted (but only within the match; the RHS is | |
| // not promoted). | |
| var x = expr<(dynamic,)>(); | |
| int y; | |
| var z = expr<Object?>(); | |
| z as (int,); | |
| z.expectStaticType<Exactly<(int,)>>(); | |
| ((y,) && z) = x; | |
| x.expectStaticType<Exactly<(dynamic,)>>(); | |
| // Assignment to `y` demonstrated that the matched value had type `int`, so | |
| // `z` was not demoted | |
| z.expectStaticType<Exactly<(int,)>>(); | |
| } | |
| { | |
| // If a record's field is assigned to a variable and the type of the | |
| // variable is a supertype of the record's field type, the record's field | |
| // type is unchanged. | |
| var x = expr<(int,)>(); | |
| num y; | |
| var z = expr<Object?>(); | |
| z as (int,); | |
| ((y,) && z) = x; | |
| x.expectStaticType<Exactly<(int,)>>(); | |
| // Assignment to `y` demonstrated that the matched value had type `int`, so | |
| // `z` was not demoted | |
| z.expectStaticType<Exactly<(int,)>>(); | |
| } | |
| { | |
| // A simple case to verify that pattern assignments don't promote the RHS. | |
| var x = expr<num>(); | |
| (_ as int) = x; | |
| x.expectStaticType<Exactly<num>>(); | |
| } | |
| } | |
| T expr<T>() => throw UnimplementedError(); | |
| extension on bool { | |
| bool get foo => throw UnimplementedError(); | |
| } | |
| main() {} |