mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:49:17 +00:00
Flow analysis: account for initializers of implicitly typed variables.
Previously, initializers of implicitly typed variables did not contribute to the SSA tracking performed by flow analysis (see https://github.com/dart-lang/language/issues/1785). This change fixes the bug, however the fix is conditioned on the "constructor tearoffs" language feature to avoid compatibility issues (we don't want someone to publish a package taking advantage of the fix, without realizing that it makes their package unusable on older SDKs). TEST=standard trybots Bug: https://github.com/dart-lang/language/issues/1785 Change-Id: I1143440c7a9795b059e8f4b84e3f4125cd80732c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211306 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
1b4f067e0b
commit
1b18e8f5ac
24
CHANGELOG.md
24
CHANGELOG.md
|
@ -21,6 +21,30 @@
|
|||
|
||||
This brings the implementation behavior in line with the spec.
|
||||
|
||||
- Initializer expressions on implicitly typed condition variables can now
|
||||
contribute to type promotion. For example, this program no longer produces a
|
||||
compile-time error:
|
||||
|
||||
```dart
|
||||
f(int? i) {
|
||||
var iIsNull = i == null;
|
||||
if (!iIsNull) {
|
||||
print(i + 1); // OK, because `i` is known to be non-null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Previously, the above program had a compile-time error because type promotion
|
||||
due to a bug ([#1785][]) which prevented the initializer expression (`i ==
|
||||
null`) from being accounted for when the variable in question (`iIsNull`)
|
||||
lacked an explicit type.
|
||||
|
||||
To avoid causing problems for packages that are intended to work with older
|
||||
versions of Dart, the fix only takes effect when the "constructor tearoffs"
|
||||
language feature is enabled.
|
||||
|
||||
[#1785]: https://github.com/dart-lang/language/issues/1785
|
||||
|
||||
### Tools
|
||||
|
||||
#### Dart command line
|
||||
|
|
|
@ -368,8 +368,11 @@ class ExpressionInfo<Variable extends Object, Type extends Object> {
|
|||
abstract class FlowAnalysis<Node extends Object, Statement extends Node,
|
||||
Expression extends Object, Variable extends Object, Type extends Object> {
|
||||
factory FlowAnalysis(TypeOperations<Variable, Type> typeOperations,
|
||||
AssignedVariables<Node, Variable> assignedVariables) {
|
||||
return new _FlowAnalysisImpl(typeOperations, assignedVariables);
|
||||
AssignedVariables<Node, Variable> assignedVariables,
|
||||
{required bool respectImplicitlyTypedVarInitializers}) {
|
||||
return new _FlowAnalysisImpl(typeOperations, assignedVariables,
|
||||
respectImplicitlyTypedVarInitializers:
|
||||
respectImplicitlyTypedVarInitializers);
|
||||
}
|
||||
|
||||
factory FlowAnalysis.legacy(TypeOperations<Variable, Type> typeOperations,
|
||||
|
@ -983,10 +986,13 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
|
|||
bool _exceptionOccurred = false;
|
||||
|
||||
factory FlowAnalysisDebug(TypeOperations<Variable, Type> typeOperations,
|
||||
AssignedVariables<Node, Variable> assignedVariables) {
|
||||
AssignedVariables<Node, Variable> assignedVariables,
|
||||
{required bool respectImplicitlyTypedVarInitializers}) {
|
||||
print('FlowAnalysisDebug()');
|
||||
return new FlowAnalysisDebug._(
|
||||
new _FlowAnalysisImpl(typeOperations, assignedVariables));
|
||||
return new FlowAnalysisDebug._(new _FlowAnalysisImpl(
|
||||
typeOperations, assignedVariables,
|
||||
respectImplicitlyTypedVarInitializers:
|
||||
respectImplicitlyTypedVarInitializers));
|
||||
}
|
||||
|
||||
factory FlowAnalysisDebug.legacy(
|
||||
|
@ -3454,7 +3460,15 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
|
|||
|
||||
final AssignedVariables<Node, Variable> _assignedVariables;
|
||||
|
||||
_FlowAnalysisImpl(this.typeOperations, this._assignedVariables);
|
||||
/// Indicates whether initializers of implicitly typed variables should be
|
||||
/// accounted for by SSA analysis. (In an ideal world, they always would be,
|
||||
/// but due to https://github.com/dart-lang/language/issues/1785, they weren't
|
||||
/// always, and we need to be able to replicate the old behavior when
|
||||
/// analyzing old language versions).
|
||||
final bool respectImplicitlyTypedVarInitializers;
|
||||
|
||||
_FlowAnalysisImpl(this.typeOperations, this._assignedVariables,
|
||||
{required this.respectImplicitlyTypedVarInitializers});
|
||||
|
||||
@override
|
||||
bool get isReachable => _current.reachable.overallReachable;
|
||||
|
@ -3810,10 +3824,11 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
|
|||
if (isLate) {
|
||||
// Don't get expression info for late variables, since we don't know when
|
||||
// they'll be initialized.
|
||||
} else if (isImplicitlyTyped) {
|
||||
// We don't get expression info for implicitly typed variables yet (bug
|
||||
// https://github.com/dart-lang/language/issues/1785).
|
||||
// TODO(paulberry): fix this.
|
||||
} else if (isImplicitlyTyped && !respectImplicitlyTypedVarInitializers) {
|
||||
// If the language version is too old, SSA analysis has to ignore
|
||||
// initializer expressions for implicitly typed variables, in order to
|
||||
// preserve the buggy behavior of
|
||||
// https://github.com/dart-lang/language/issues/1785.
|
||||
} else {
|
||||
expressionInfo = _getExpressionInfo(initializerExpression);
|
||||
}
|
||||
|
|
|
@ -594,7 +594,8 @@ main() {
|
|||
var e = expr('Null');
|
||||
var s = if_(e, []);
|
||||
var flow = FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
||||
h, AssignedVariables<Node, Var>());
|
||||
h, AssignedVariables<Node, Var>(),
|
||||
respectImplicitlyTypedVarInitializers: true);
|
||||
flow.ifStatement_conditionBegin();
|
||||
flow.ifStatement_thenBegin(e, s);
|
||||
expect(() => flow.finish(), _asserts);
|
||||
|
@ -1407,9 +1408,10 @@ main() {
|
|||
]);
|
||||
});
|
||||
|
||||
test('initialize() does not store expressionInfo for implicitly typed vars',
|
||||
() {
|
||||
var h = Harness();
|
||||
test(
|
||||
'initialize() does not store expressionInfo for implicitly typed '
|
||||
'vars, pre-bug fix', () {
|
||||
var h = Harness(respectImplicitlyTypedVarInitializers: false);
|
||||
var x = Var('x', 'Object', isImplicitlyTyped: true);
|
||||
var y = Var('y', 'int?');
|
||||
h.run([
|
||||
|
@ -1420,6 +1422,34 @@ main() {
|
|||
]);
|
||||
});
|
||||
|
||||
test(
|
||||
'initialize() stores expressionInfo for implicitly typed '
|
||||
'vars, post-bug fix', () {
|
||||
var h = Harness(respectImplicitlyTypedVarInitializers: true);
|
||||
var x = Var('x', 'Object', isImplicitlyTyped: true);
|
||||
var y = Var('y', 'int?');
|
||||
h.run([
|
||||
declareInitialized(x, y.expr.eq(nullLiteral)),
|
||||
getSsaNodes((nodes) {
|
||||
expect(nodes[x]!.expressionInfo, isNotNull);
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test(
|
||||
'initialize() stores expressionInfo for explicitly typed '
|
||||
'vars, pre-bug fix', () {
|
||||
var h = Harness(respectImplicitlyTypedVarInitializers: false);
|
||||
var x = Var('x', 'Object');
|
||||
var y = Var('y', 'int?');
|
||||
h.run([
|
||||
declareInitialized(x, y.expr.eq(nullLiteral)),
|
||||
getSsaNodes((nodes) {
|
||||
expect(nodes[x]!.expressionInfo, isNotNull);
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test('initialize() does not store expressionInfo for trivial expressions',
|
||||
() {
|
||||
var h = Harness();
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// 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.
|
||||
|
||||
// @dart=2.13
|
||||
|
||||
finalLocalBool(int? x) {
|
||||
final bool b = x == null;
|
||||
if (!b) {
|
||||
|
|
|
@ -388,7 +388,17 @@ class Harness extends TypeOperations<Var, Type> {
|
|||
|
||||
late final _typeAnalyzer = _MiniAstTypeAnalyzer(this);
|
||||
|
||||
Harness({this.legacy = false, String? thisType})
|
||||
/// Indicates whether initializers of implicitly typed variables should be
|
||||
/// accounted for by SSA analysis. (In an ideal world, they always would be,
|
||||
/// but due to https://github.com/dart-lang/language/issues/1785, they weren't
|
||||
/// always, and we need to be able to replicate the old behavior when
|
||||
/// analyzing old language versions).
|
||||
final bool respectImplicitlyTypedVarInitializers;
|
||||
|
||||
Harness(
|
||||
{this.legacy = false,
|
||||
String? thisType,
|
||||
this.respectImplicitlyTypedVarInitializers = true})
|
||||
: thisType = thisType == null ? null : Type(thisType);
|
||||
|
||||
MiniIrBuilder get _irBuilder => _typeAnalyzer._irBuilder;
|
||||
|
@ -483,7 +493,9 @@ class Harness extends TypeOperations<Var, Type> {
|
|||
? FlowAnalysis<Node, Statement, Expression, Var, Type>.legacy(
|
||||
this, assignedVariables)
|
||||
: FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
||||
this, assignedVariables);
|
||||
this, assignedVariables,
|
||||
respectImplicitlyTypedVarInitializers:
|
||||
respectImplicitlyTypedVarInitializers);
|
||||
_typeAnalyzer.dispatchStatement(b);
|
||||
_typeAnalyzer.finish();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:analyzer/src/dart/ast/extensions.dart';
|
|||
import 'package:analyzer/src/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
|
||||
import 'package:analyzer/src/generated/migration.dart';
|
||||
import 'package:analyzer/src/generated/resolver.dart';
|
||||
import 'package:analyzer/src/generated/variable_type_provider.dart';
|
||||
|
||||
/// Data gathered by flow analysis, retained for testing purposes.
|
||||
|
@ -222,7 +223,8 @@ class FlowAnalysisHelper {
|
|||
flow!.labeledStatement_end();
|
||||
}
|
||||
|
||||
void topLevelDeclaration_enter(AstNode node, FormalParameterList? parameters,
|
||||
void topLevelDeclaration_enter(
|
||||
ResolverVisitor resolver, AstNode node, FormalParameterList? parameters,
|
||||
{void Function(AstVisitor<Object?> visitor)? visit}) {
|
||||
assert(flow == null);
|
||||
assignedVariables = computeAssignedVariables(node, parameters,
|
||||
|
@ -233,7 +235,9 @@ class FlowAnalysisHelper {
|
|||
}
|
||||
flow = isNonNullableByDefault
|
||||
? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
||||
DartType>(_typeOperations, assignedVariables!)
|
||||
DartType>(_typeOperations, assignedVariables!,
|
||||
respectImplicitlyTypedVarInitializers:
|
||||
resolver.isConstructorTearoffsEnabled)
|
||||
: FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
||||
DartType>.legacy(_typeOperations, assignedVariables!);
|
||||
}
|
||||
|
@ -349,9 +353,10 @@ class FlowAnalysisHelperForMigration extends FlowAnalysisHelper {
|
|||
: super(typeSystem, false, isNonNullableByDefault);
|
||||
|
||||
@override
|
||||
void topLevelDeclaration_enter(AstNode node, FormalParameterList? parameters,
|
||||
void topLevelDeclaration_enter(
|
||||
ResolverVisitor resolver, AstNode node, FormalParameterList? parameters,
|
||||
{void Function(AstVisitor<Object?> visitor)? visit}) {
|
||||
super.topLevelDeclaration_enter(node, parameters, visit: visit);
|
||||
super.topLevelDeclaration_enter(resolver, node, parameters, visit: visit);
|
||||
migrationResolutionHooks.setFlowAnalysis(flow);
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class VariableDeclarationResolver {
|
|||
|
||||
InferenceContext.setTypeFromNode(initializer, node);
|
||||
if (isTopLevel) {
|
||||
_resolver.flowAnalysis.topLevelDeclaration_enter(node, null);
|
||||
_resolver.flowAnalysis.topLevelDeclaration_enter(_resolver, node, null);
|
||||
} else if (element.isLate) {
|
||||
_resolver.flowAnalysis.flow?.lateInitializer_begin(node);
|
||||
}
|
||||
|
|
|
@ -1185,7 +1185,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
|||
|
||||
@override
|
||||
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||
flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
|
||||
flowAnalysis.topLevelDeclaration_enter(this, node, node.parameters);
|
||||
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
||||
|
||||
var returnType = node.declaredElement!.type.returnType;
|
||||
|
@ -1409,7 +1409,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
|||
flowAnalysis.flow!.functionExpression_begin(node);
|
||||
} else {
|
||||
flowAnalysis.topLevelDeclaration_enter(
|
||||
node, node.functionExpression.parameters);
|
||||
this, node, node.functionExpression.parameters);
|
||||
}
|
||||
flowAnalysis.executableDeclaration_enter(
|
||||
node,
|
||||
|
@ -1638,7 +1638,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
|||
|
||||
@override
|
||||
void visitMethodDeclaration(MethodDeclaration node) {
|
||||
flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
|
||||
flowAnalysis.topLevelDeclaration_enter(this, node, node.parameters);
|
||||
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
||||
|
||||
DartType returnType = node.declaredElement!.returnType;
|
||||
|
|
|
@ -58,7 +58,7 @@ class AstResolver {
|
|||
node.accept(_resolutionVisitor);
|
||||
node.accept(_scopeResolverVisitor);
|
||||
_prepareEnclosingDeclarations();
|
||||
_flowAnalysis.topLevelDeclaration_enter(node, null);
|
||||
_flowAnalysis.topLevelDeclaration_enter(_resolverVisitor, node, null);
|
||||
node.accept(_resolverVisitor);
|
||||
_flowAnalysis.topLevelDeclaration_exit();
|
||||
}
|
||||
|
@ -76,7 +76,8 @@ class AstResolver {
|
|||
visit(_scopeResolverVisitor);
|
||||
|
||||
_prepareEnclosingDeclarations();
|
||||
_flowAnalysis.topLevelDeclaration_enter(node, node.parameters,
|
||||
_flowAnalysis.topLevelDeclaration_enter(
|
||||
_resolverVisitor, node, node.parameters,
|
||||
visit: visit);
|
||||
visit(_resolverVisitor);
|
||||
_flowAnalysis.topLevelDeclaration_exit();
|
||||
|
@ -95,7 +96,8 @@ class AstResolver {
|
|||
node.accept(_scopeResolverVisitor);
|
||||
}
|
||||
_prepareEnclosingDeclarations();
|
||||
_flowAnalysis.topLevelDeclaration_enter(node.parent!, null);
|
||||
_flowAnalysis.topLevelDeclaration_enter(
|
||||
_resolverVisitor, node.parent!, null);
|
||||
node.accept(_resolverVisitor);
|
||||
_flowAnalysis.topLevelDeclaration_exit();
|
||||
}
|
||||
|
|
|
@ -79,12 +79,14 @@ void main() {
|
|||
}
|
||||
|
||||
test_ifElementWithElse_mayBeConst() async {
|
||||
await assertNoErrorsInCode('''
|
||||
await assertErrorsInCode('''
|
||||
void main() {
|
||||
const isTrue = true;
|
||||
const {1: null, if (isTrue) null: null else null: null};
|
||||
}
|
||||
''');
|
||||
''', [
|
||||
error(HintCode.DEAD_CODE, 83, 12),
|
||||
]);
|
||||
}
|
||||
|
||||
test_spreadElement_mayBeConst() async {
|
||||
|
|
|
@ -224,7 +224,9 @@ class TypeInferrerImpl implements TypeInferrer {
|
|||
flowAnalysis = library.isNonNullableByDefault
|
||||
? new FlowAnalysis(
|
||||
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
||||
assignedVariables)
|
||||
assignedVariables,
|
||||
respectImplicitlyTypedVarInitializers:
|
||||
library.enableConstructorTearOffsInLibrary)
|
||||
: new FlowAnalysis.legacy(
|
||||
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
||||
assignedVariables) {}
|
||||
|
|
|
@ -142,6 +142,7 @@ bs
|
|||
bsd
|
||||
bslash
|
||||
buffered
|
||||
buggy
|
||||
builder`s
|
||||
bulk
|
||||
bump
|
||||
|
@ -559,6 +560,7 @@ i'll
|
|||
i2b
|
||||
ic
|
||||
id
|
||||
ideal
|
||||
identifies
|
||||
identifying
|
||||
ideographic
|
||||
|
|
|
@ -377,10 +377,14 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
var previousAssignedVariables = _assignedVariables;
|
||||
if (_flowAnalysis == null) {
|
||||
_assignedVariables = AssignedVariables();
|
||||
// Note: we are using flow analysis to help us track true nullabilities;
|
||||
// it's not necessary to replicate old bugs. So we pass `true` for
|
||||
// `respectImplicitlyTypedVarInitializers`.
|
||||
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
|
||||
PromotableElement, DecoratedType>(
|
||||
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
||||
_assignedVariables!);
|
||||
_assignedVariables!,
|
||||
respectImplicitlyTypedVarInitializers: true);
|
||||
}
|
||||
try {
|
||||
_dispatch(node.name);
|
||||
|
@ -2110,10 +2114,14 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
|||
assert(_assignedVariables == null);
|
||||
_assignedVariables =
|
||||
FlowAnalysisHelper.computeAssignedVariables(node, parameters);
|
||||
// Note: we are using flow analysis to help us track true nullabilities;
|
||||
// it's not necessary to replicate old bugs. So we pass `true` for
|
||||
// `respectImplicitlyTypedVarInitializers`.
|
||||
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
|
||||
PromotableElement, DecoratedType>(
|
||||
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
||||
_assignedVariables!);
|
||||
_assignedVariables!,
|
||||
respectImplicitlyTypedVarInitializers: true);
|
||||
if (parameters != null) {
|
||||
for (var parameter in parameters.parameters) {
|
||||
_flowAnalysis!.declare(parameter.declaredElement!, true);
|
||||
|
|
|
@ -315,9 +315,15 @@ class AnnotateKernel extends RecursiveVisitor {
|
|||
Constant? constantValue;
|
||||
bool isInt = false;
|
||||
|
||||
final nullable = type is NullableType;
|
||||
// Note: the explicit type `bool` is needed because the checked-in version
|
||||
// of the CFE that we use for bootstrapping doesn't yet have constructor
|
||||
// tearoffs enabled, and the fix for bug
|
||||
// https://github.com/dart-lang/language/issues/1785 only takes effect when
|
||||
// constructor tearoffs are enabled. TODO(paulberry): remove the type after
|
||||
// the bootstrap CFE enables constructor tearoffs.
|
||||
final bool nullable = type is NullableType;
|
||||
if (nullable) {
|
||||
type = (type as NullableType).baseType;
|
||||
type = type.baseType;
|
||||
}
|
||||
|
||||
if (nullable && type == const EmptyType()) {
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
// SharedOptions=--enable-experiment=constructor-tearoffs
|
||||
|
||||
import '../../static_type_helper.dart';
|
||||
|
||||
// This test checks whether a local boolean variable can be used to perform type
|
||||
// promotion, if that variable is implicitly typed.
|
||||
//
|
||||
// This test confirms that once the "constructor tearoffs" language feature is
|
||||
// enabled, initializer expressions on implicitly typed variables are no longer
|
||||
// ignored for the purposes of type promotion
|
||||
// (i.e. https://github.com/dart-lang/language/issues/1785 is fixed).
|
||||
|
||||
parameterUnmodified(int? x) {
|
||||
{
|
||||
late final b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
late var b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
}
|
||||
|
||||
parameterModifiedLater(int? x, int? y) {
|
||||
x = y;
|
||||
{
|
||||
late final b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
late var b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
}
|
||||
|
||||
localVariableInitialized(int? y) {
|
||||
int? x = y;
|
||||
{
|
||||
late final b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
late var b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
}
|
||||
|
||||
localVariableModifiedLater(int? y) {
|
||||
int? x;
|
||||
x = y;
|
||||
{
|
||||
late final b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
late var b = x != null;
|
||||
// We don't promote based on the initializers of late locals anyhow, because
|
||||
// we don't know when they execute.
|
||||
if (b) x.expectStaticType<Exactly<int?>>();
|
||||
}
|
||||
{
|
||||
late var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
final b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
{
|
||||
var b;
|
||||
b = x != null;
|
||||
if (b) x.expectStaticType<Exactly<int>>();
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
parameterUnmodified(null);
|
||||
parameterUnmodified(0);
|
||||
parameterModifiedLater(null, null);
|
||||
parameterModifiedLater(null, 0);
|
||||
localVariableInitialized(null);
|
||||
localVariableInitialized(0);
|
||||
localVariableModifiedLater(null);
|
||||
localVariableModifiedLater(0);
|
||||
}
|
Loading…
Reference in a new issue