| // 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. |
| |
| // Tests that field promotion works with cascades. |
| |
| import 'package:expect/static_type_helper.dart'; |
| |
| class C { |
| final Object? _field; |
| C([this._field]); |
| void f([_]) {} |
| } |
| |
| void cascadedAccessReceivesTheBenefitOfPromotion(C c) { |
| // If a field of an object is promoted prior to its use in a cascade, accesses |
| // to the field within cascade sections retain the promotion. |
| c._field as int; |
| c._field.expectStaticType<Exactly<int>>(); |
| c |
| .._field.expectStaticType<Exactly<int>>() |
| .._field.expectStaticType<Exactly<int>>(); |
| // And the promotion remains on later accesses to the same variable. |
| c._field.expectStaticType<Exactly<int>>(); |
| } |
| |
| void fieldAccessOnACascadeExpressionRetainsPromotion(C c) { |
| // If a field of an object is promoted prior to its use in a cascade, accesses |
| // to the field from outside the cascade retain the promotion. |
| c._field as int; |
| c._field.expectStaticType<Exactly<int>>(); |
| (c..f())._field.expectStaticType<Exactly<int>>(); |
| // And the promotion remains on later accesses to the same variable. |
| c._field.expectStaticType<Exactly<int>>(); |
| } |
| |
| void fieldsPromotableWithinCascade(C c) { |
| // Within a cascade, a field can be promoted using `!`. |
| c |
| .._field.expectStaticType<Exactly<Object?>>() |
| .._field!.expectStaticType<Exactly<Object>>() |
| .._field.expectStaticType<Exactly<Object>>(); |
| // And after the cascade, the promotion is retained. |
| c._field.expectStaticType<Exactly<Object>>(); |
| } |
| |
| void cascadeExpressionIsNotPromotable(Object? o) { |
| // However, null-checking, casting, or type checking the result of a cascade |
| // expression does not promote the target of the cascade. (It could, in |
| // principle, but it would be extra work to implement, and it seems unlikely |
| // that it would be of much benefit). |
| (o..toString())!; |
| o.expectStaticType<Exactly<Object?>>(); |
| (o..toString()) as Object; |
| o.expectStaticType<Exactly<Object?>>(); |
| if ((o..toString()) is Object) { |
| o.expectStaticType<Exactly<Object?>>(); |
| } |
| } |
| |
| void ephemeralValueFieldsArePromotable(C Function() getC) { |
| // Fields of an ephemeral value (one that is not explicitly stored in a |
| // variable) can still be promoted in one cascade section, and the results of |
| // the promotion can be seen in later cascade sections. |
| getC() |
| .._field.expectStaticType<Exactly<Object?>>() |
| .._field!.expectStaticType<Exactly<Object>>() |
| .._field.expectStaticType<Exactly<Object>>(); |
| // But they won't be seen if a fresh value is created. |
| getC()._field.expectStaticType<Exactly<Object?>>(); |
| } |
| |
| void writeCapturedValueFieldsArePromotable(C c) { |
| // Fields of a write-captured variable can still be promoted in one cascade |
| // section, and the results of the promotion can be seen in later cascade |
| // sections. This is because the target of the cascade is stored in an |
| // implicit temporary variable, separate from the write-captured variable. |
| f() { |
| c = C(null); |
| } |
| |
| c |
| .._field.expectStaticType<Exactly<Object?>>() |
| .._field!.expectStaticType<Exactly<Object>>() |
| .._field.expectStaticType<Exactly<Object>>(); |
| // But fields of the write-captured variable itself aren't promoted. |
| c._field.expectStaticType<Exactly<Object?>>(); |
| } |
| |
| void writeDefeatsLaterAccessesButNotCascadeTarget(C c) { |
| // If a write to a variable happens during a cascade, any promotions based on |
| // that variable are invalidated, but the target of the cascade remains |
| // promoted, since it's stored in an implicit temporary variable that's |
| // unaffected by the write. |
| c._field as C; |
| c._field.expectStaticType<Exactly<C>>(); |
| c |
| .._field.f([c = C(C()), c._field.expectStaticType<Exactly<Object?>>()]) |
| .._field.expectStaticType<Exactly<C>>(); |
| c._field.expectStaticType<Exactly<Object?>>(); |
| } |
| |
| void cascadedInvocationsPermitted(C c) { |
| // A promoted field may be invoked inside a cascade. |
| c._field as int Function(); |
| c._field.expectStaticType<Exactly<int Function()>>(); |
| c.._field().expectStaticType<Exactly<int>>(); |
| } |
| |
| main() { |
| cascadedAccessReceivesTheBenefitOfPromotion(C(0)); |
| fieldAccessOnACascadeExpressionRetainsPromotion(C(0)); |
| fieldsPromotableWithinCascade(C(0)); |
| cascadeExpressionIsNotPromotable(0); |
| ephemeralValueFieldsArePromotable(() => C(0)); |
| writeCapturedValueFieldsArePromotable(C(0)); |
| writeDefeatsLaterAccessesButNotCascadeTarget(C(C())); |
| int f() => 0; |
| cascadedInvocationsPermitted(C(f)); |
| } |