dart / sdk.git / refs/tags/2.12.0-239.0.dev / . / tests / language / nnbd / inference / variable_initialization_promotion_test.dart

// Copyright (c) 2020, 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 '../../static_type_helper.dart'; | |

/// Test that the type of a local variable is treated as a "type of interest" | |

/// for the variable, and that for non-final variables, the initialization (if | |

/// any) is treated as an assignment for the purposes of promotion. | |

/// Verify that the declared type of a local variable is a type of interest. | |

void declaredTypeIsATypeOfInterest() { | |

// Check that a variable declared with a non-nullable type can be assignment | |

// demoted back to its declared type after being promoted. | |

{ | |

num x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a variable declared with a nullable type can be assignment | |

// promoted to the non-nullable variant of its type, and demoted back to both | |

// the non-nullable variant and the declared type after being promoted. | |

{ | |

num? x = (3 as num?); | |

x.expectStaticType<Exactly<num?>>(); | |

// This should promote to num, since num and num? should both be types of | |

// interest. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that demotion back to num? works | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

// Check that a late variable declared with a non-nullable type can be | |

// assignment demoted back to its declared type after being promoted. | |

{ | |

late num x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a late variable declared with a nullable type can be assignment | |

// promoted to the non-nullable variant of its type, and demoted back to both | |

// the non-nullable variant and the declared type after being promoted. | |

{ | |

late num? x = (3 as num?); | |

x.expectStaticType<Exactly<num?>>(); | |

// This should promote to num, since num and num? should both be types of | |

// interest. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that demotion back to num? works | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

// Final variables are not re-assignable, but can still be subject to | |

// to assignment based promotion and demotion in a few situations. | |

// Check that a late final variable declared with a non-nullable type can be | |

// assignment demoted back to its declared type after being promoted. | |

{ | |

late final num x; | |

// Make x potentially assigned, and initialize it | |

if (num == num) { | |

x = 3.5; | |

} | |

// Branch will not be taken to avoid a double initialization error | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

x = 3.5; // Demote to num. | |

x.expectStaticType<Exactly<num>>(); | |

} | |

} | |

// Check that a final variable declared with a non-nullable type can be | |

// assignment promoted to the non-nullable variant of its type. | |

{ | |

final num? x; | |

// Promote to num, since num is a type of interest | |

x = 3; | |

// Verify that we have promoted to num | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a late final variable declared with a non-nullable type can be | |

// assignment promoted to the non-nullable variant of its type. | |

{ | |

late final num? x; | |

// Promote to num, since num is a type of interest | |

x = 3; | |

// Verify that we have promoted to num | |

x.expectStaticType<Exactly<num>>(); | |

} | |

} | |

/// Verify that the inferred type of a local variable is a type of interest. | |

void inferredTypeIsATypeOfInterest() { | |

// Check that a variable inferred with a non-nullable type can be | |

// assignment demoted back to its declared type after being promoted. | |

{ | |

var x = (3 as num); | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a variable inferred to have a nullable type can be assignment | |

// promoted to the non-nullable variant of its type, and demoted back to both | |

// the non-nullable variant and the declared type after being promoted. | |

{ | |

var x = (3 as num?); | |

x.expectStaticType<Exactly<num?>>(); | |

// This should promote to num, since num and num? should both be types of | |

// interest. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that demotion back to num? works | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

// Check that a variable inferred with a non-nullable type can be | |

// assignment demoted back to its declared type after being promoted. | |

{ | |

late var x = (3 as num); | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a late variable inferred to have a nullable type can be | |

// assignment promoted to the non-nullable variant of its type, and demoted | |

// back to both the non-nullable variant and the declared type after being | |

// promoted. | |

{ | |

late var x = (3 as num?); | |

x.expectStaticType<Exactly<num?>>(); | |

// This should promote to num, since num and num? should both be types of | |

// interest. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

// Promote x to int | |

if (x is int) { | |

x.expectStaticType<Exactly<int>>(); | |

// Verify that demotion back to num works | |

x = 3.5; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that demotion back to num? works | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

} | |

/// Verify that the initializer on a mutable variable is treated as if it were | |

/// an assignment for the purposes of promotion, and therefore assigning a | |

/// non-nullable value can promote to a non-nullable variant of the declared | |

/// type. | |

void initializersOnVarPromote() { | |

// Check that a variable declared to have a nullable type can be promoted on | |

// initialization to the non-nullable variant of its type, demoted back to the | |

// declared type, and assignment promoted to the non-nullable variant of the | |

// declared type. | |

{ | |

num? x = 3; | |

// Verify that we have promoted to num | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that num? is a type of interest by demoting to it | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

// Verify that num is a type of interest by promoting to it. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

// Check that a late variable declared to have a nullable type can be promoted | |

// on initialization to the non-nullable variant of its type, demoted back to | |

// the declared type, and assignment promoted to the non-nullable variant of | |

// the declared type. | |

{ | |

late num? x = 3; | |

// Verify that we have promoted to num | |

x.expectStaticType<Exactly<num>>(); | |

// Verify that num? is a type of interest by demoting to it | |

x = null; | |

x.expectStaticType<Exactly<num?>>(); | |

// Verify that num is a type of interest by promoting to it. | |

x = 3; | |

x.expectStaticType<Exactly<num>>(); | |

} | |

} | |

/// Verify that the initializer on a final variable is not treated as if it were | |

/// an assignment for the purposes of promotion, and therefore assigning a | |

/// non-nullable value does not promote to a non-nullable variant of the | |

/// declared type. | |

void initializersOnFinalDoNotPromote() { | |

// Check that a final nullable variable initialized with a non-nullable value | |

// does not get promoted by the initializer to the non-nullable variant of the | |

// type. | |

{ | |

final num? x = 3; | |

// Verify that we have not promoted to num | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

// Check that a late final nullable variable initialized with a non-nullable | |

// value does not get promoted by the initializer to the non-nullable variant | |

// of the type. | |

{ | |

late final num? x = 3; | |

// Verify that we have not promoted to num | |

x.expectStaticType<Exactly<num?>>(); | |

} | |

} | |

/// Check that when an initializer is a promoted type variable `X & T`, the | |

/// inferred type of the variable is `X`, but that the variable is immediately | |

/// promoted to `X & T`. | |

void typeVariableTypedVariableInferencePromotes<T>(T x0, T x1, bool b) { | |

if (x0 is num) { | |

// Promote `x0` to T & num | |

{ | |

// Declare y, which should have inferred type T, and be promoted to T & | |

// num | |

var y = x0; | |

// Check that y is assignable to z, and hence that y is still promoted to | |

// T & num | |

num z = y; | |

// Check that y can be demoted to T, but do it conditionally so that T & | |

// num remains a type of interest. | |

if (b) y = x1; | |

// T & num is still a type of interest, and hence this assignment should | |

// promote to T & num. | |

y = x0; | |

// Check that y is assignable to z, and hence that y has been promoted T & | |

// num | |

z = y; | |

} | |

{ | |

// Declare y, which should have inferred type T, and be promoted to T & | |

// num | |

late var y = x0; | |

// Check that y is assignable to z, and hence that y is still promoted to | |

// T & num | |

num z = y; | |

// Check that y can be demoted to T, but do it conditionally so that T & | |

// num remains a type of interest. | |

if (b) y = x1; | |

// T & num is still a type of interest, and hence this assignment should | |

// promote to T & num. | |

y = x0; | |

// Check that y is assignable to z, and hence that y has been promoted T & | |

// num | |

z = y; | |

} | |

{ | |

// Declare y, which should have inferred type T, and be promoted to T & | |

// num | |

final y = x0; | |

// Check that y is assignable to z, and hence that y is still promoted to | |

// T & num | |

num z = y; | |

} | |

{ | |

// Declare y, which should have inferred type T, and be promoted to T & | |

// num | |

late final y = x0; | |

// Check that y is assignable to z, and hence that y is still promoted to | |

// T & num | |

num z = y; | |

} | |

} | |

} | |

void main() { | |

declaredTypeIsATypeOfInterest(); | |

inferredTypeIsATypeOfInterest(); | |

initializersOnVarPromote(); | |

initializersOnFinalDoNotPromote(); | |

typeVariableTypedVariableInferencePromotes<num>(3, 3.5, true); | |

} |