mirror of
https://github.com/dart-lang/sdk
synced 2024-07-05 09:20:04 +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.
|
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
|
### Tools
|
||||||
|
|
||||||
#### Dart command line
|
#### Dart command line
|
||||||
|
|
|
@ -368,8 +368,11 @@ class ExpressionInfo<Variable extends Object, Type extends Object> {
|
||||||
abstract class FlowAnalysis<Node extends Object, Statement extends Node,
|
abstract class FlowAnalysis<Node extends Object, Statement extends Node,
|
||||||
Expression extends Object, Variable extends Object, Type extends Object> {
|
Expression extends Object, Variable extends Object, Type extends Object> {
|
||||||
factory FlowAnalysis(TypeOperations<Variable, Type> typeOperations,
|
factory FlowAnalysis(TypeOperations<Variable, Type> typeOperations,
|
||||||
AssignedVariables<Node, Variable> assignedVariables) {
|
AssignedVariables<Node, Variable> assignedVariables,
|
||||||
return new _FlowAnalysisImpl(typeOperations, assignedVariables);
|
{required bool respectImplicitlyTypedVarInitializers}) {
|
||||||
|
return new _FlowAnalysisImpl(typeOperations, assignedVariables,
|
||||||
|
respectImplicitlyTypedVarInitializers:
|
||||||
|
respectImplicitlyTypedVarInitializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory FlowAnalysis.legacy(TypeOperations<Variable, Type> typeOperations,
|
factory FlowAnalysis.legacy(TypeOperations<Variable, Type> typeOperations,
|
||||||
|
@ -983,10 +986,13 @@ class FlowAnalysisDebug<Node extends Object, Statement extends Node,
|
||||||
bool _exceptionOccurred = false;
|
bool _exceptionOccurred = false;
|
||||||
|
|
||||||
factory FlowAnalysisDebug(TypeOperations<Variable, Type> typeOperations,
|
factory FlowAnalysisDebug(TypeOperations<Variable, Type> typeOperations,
|
||||||
AssignedVariables<Node, Variable> assignedVariables) {
|
AssignedVariables<Node, Variable> assignedVariables,
|
||||||
|
{required bool respectImplicitlyTypedVarInitializers}) {
|
||||||
print('FlowAnalysisDebug()');
|
print('FlowAnalysisDebug()');
|
||||||
return new FlowAnalysisDebug._(
|
return new FlowAnalysisDebug._(new _FlowAnalysisImpl(
|
||||||
new _FlowAnalysisImpl(typeOperations, assignedVariables));
|
typeOperations, assignedVariables,
|
||||||
|
respectImplicitlyTypedVarInitializers:
|
||||||
|
respectImplicitlyTypedVarInitializers));
|
||||||
}
|
}
|
||||||
|
|
||||||
factory FlowAnalysisDebug.legacy(
|
factory FlowAnalysisDebug.legacy(
|
||||||
|
@ -3454,7 +3460,15 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
|
||||||
|
|
||||||
final AssignedVariables<Node, Variable> _assignedVariables;
|
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
|
@override
|
||||||
bool get isReachable => _current.reachable.overallReachable;
|
bool get isReachable => _current.reachable.overallReachable;
|
||||||
|
@ -3810,10 +3824,11 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
|
||||||
if (isLate) {
|
if (isLate) {
|
||||||
// Don't get expression info for late variables, since we don't know when
|
// Don't get expression info for late variables, since we don't know when
|
||||||
// they'll be initialized.
|
// they'll be initialized.
|
||||||
} else if (isImplicitlyTyped) {
|
} else if (isImplicitlyTyped && !respectImplicitlyTypedVarInitializers) {
|
||||||
// We don't get expression info for implicitly typed variables yet (bug
|
// If the language version is too old, SSA analysis has to ignore
|
||||||
// https://github.com/dart-lang/language/issues/1785).
|
// initializer expressions for implicitly typed variables, in order to
|
||||||
// TODO(paulberry): fix this.
|
// preserve the buggy behavior of
|
||||||
|
// https://github.com/dart-lang/language/issues/1785.
|
||||||
} else {
|
} else {
|
||||||
expressionInfo = _getExpressionInfo(initializerExpression);
|
expressionInfo = _getExpressionInfo(initializerExpression);
|
||||||
}
|
}
|
||||||
|
|
|
@ -594,7 +594,8 @@ main() {
|
||||||
var e = expr('Null');
|
var e = expr('Null');
|
||||||
var s = if_(e, []);
|
var s = if_(e, []);
|
||||||
var flow = FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
var flow = FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
||||||
h, AssignedVariables<Node, Var>());
|
h, AssignedVariables<Node, Var>(),
|
||||||
|
respectImplicitlyTypedVarInitializers: true);
|
||||||
flow.ifStatement_conditionBegin();
|
flow.ifStatement_conditionBegin();
|
||||||
flow.ifStatement_thenBegin(e, s);
|
flow.ifStatement_thenBegin(e, s);
|
||||||
expect(() => flow.finish(), _asserts);
|
expect(() => flow.finish(), _asserts);
|
||||||
|
@ -1407,9 +1408,10 @@ main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initialize() does not store expressionInfo for implicitly typed vars',
|
test(
|
||||||
() {
|
'initialize() does not store expressionInfo for implicitly typed '
|
||||||
var h = Harness();
|
'vars, pre-bug fix', () {
|
||||||
|
var h = Harness(respectImplicitlyTypedVarInitializers: false);
|
||||||
var x = Var('x', 'Object', isImplicitlyTyped: true);
|
var x = Var('x', 'Object', isImplicitlyTyped: true);
|
||||||
var y = Var('y', 'int?');
|
var y = Var('y', 'int?');
|
||||||
h.run([
|
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',
|
test('initialize() does not store expressionInfo for trivial expressions',
|
||||||
() {
|
() {
|
||||||
var h = Harness();
|
var h = Harness();
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// @dart=2.13
|
||||||
|
|
||||||
finalLocalBool(int? x) {
|
finalLocalBool(int? x) {
|
||||||
final bool b = x == null;
|
final bool b = x == null;
|
||||||
if (!b) {
|
if (!b) {
|
||||||
|
|
|
@ -388,7 +388,17 @@ class Harness extends TypeOperations<Var, Type> {
|
||||||
|
|
||||||
late final _typeAnalyzer = _MiniAstTypeAnalyzer(this);
|
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);
|
: thisType = thisType == null ? null : Type(thisType);
|
||||||
|
|
||||||
MiniIrBuilder get _irBuilder => _typeAnalyzer._irBuilder;
|
MiniIrBuilder get _irBuilder => _typeAnalyzer._irBuilder;
|
||||||
|
@ -483,7 +493,9 @@ class Harness extends TypeOperations<Var, Type> {
|
||||||
? FlowAnalysis<Node, Statement, Expression, Var, Type>.legacy(
|
? FlowAnalysis<Node, Statement, Expression, Var, Type>.legacy(
|
||||||
this, assignedVariables)
|
this, assignedVariables)
|
||||||
: FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
: FlowAnalysis<Node, Statement, Expression, Var, Type>(
|
||||||
this, assignedVariables);
|
this, assignedVariables,
|
||||||
|
respectImplicitlyTypedVarInitializers:
|
||||||
|
respectImplicitlyTypedVarInitializers);
|
||||||
_typeAnalyzer.dispatchStatement(b);
|
_typeAnalyzer.dispatchStatement(b);
|
||||||
_typeAnalyzer.finish();
|
_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.dart';
|
||||||
import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
|
import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
|
||||||
import 'package:analyzer/src/generated/migration.dart';
|
import 'package:analyzer/src/generated/migration.dart';
|
||||||
|
import 'package:analyzer/src/generated/resolver.dart';
|
||||||
import 'package:analyzer/src/generated/variable_type_provider.dart';
|
import 'package:analyzer/src/generated/variable_type_provider.dart';
|
||||||
|
|
||||||
/// Data gathered by flow analysis, retained for testing purposes.
|
/// Data gathered by flow analysis, retained for testing purposes.
|
||||||
|
@ -222,7 +223,8 @@ class FlowAnalysisHelper {
|
||||||
flow!.labeledStatement_end();
|
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}) {
|
{void Function(AstVisitor<Object?> visitor)? visit}) {
|
||||||
assert(flow == null);
|
assert(flow == null);
|
||||||
assignedVariables = computeAssignedVariables(node, parameters,
|
assignedVariables = computeAssignedVariables(node, parameters,
|
||||||
|
@ -233,7 +235,9 @@ class FlowAnalysisHelper {
|
||||||
}
|
}
|
||||||
flow = isNonNullableByDefault
|
flow = isNonNullableByDefault
|
||||||
? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
||||||
DartType>(_typeOperations, assignedVariables!)
|
DartType>(_typeOperations, assignedVariables!,
|
||||||
|
respectImplicitlyTypedVarInitializers:
|
||||||
|
resolver.isConstructorTearoffsEnabled)
|
||||||
: FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
: FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
|
||||||
DartType>.legacy(_typeOperations, assignedVariables!);
|
DartType>.legacy(_typeOperations, assignedVariables!);
|
||||||
}
|
}
|
||||||
|
@ -349,9 +353,10 @@ class FlowAnalysisHelperForMigration extends FlowAnalysisHelper {
|
||||||
: super(typeSystem, false, isNonNullableByDefault);
|
: super(typeSystem, false, isNonNullableByDefault);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void topLevelDeclaration_enter(AstNode node, FormalParameterList? parameters,
|
void topLevelDeclaration_enter(
|
||||||
|
ResolverVisitor resolver, AstNode node, FormalParameterList? parameters,
|
||||||
{void Function(AstVisitor<Object?> visitor)? visit}) {
|
{void Function(AstVisitor<Object?> visitor)? visit}) {
|
||||||
super.topLevelDeclaration_enter(node, parameters, visit: visit);
|
super.topLevelDeclaration_enter(resolver, node, parameters, visit: visit);
|
||||||
migrationResolutionHooks.setFlowAnalysis(flow);
|
migrationResolutionHooks.setFlowAnalysis(flow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class VariableDeclarationResolver {
|
||||||
|
|
||||||
InferenceContext.setTypeFromNode(initializer, node);
|
InferenceContext.setTypeFromNode(initializer, node);
|
||||||
if (isTopLevel) {
|
if (isTopLevel) {
|
||||||
_resolver.flowAnalysis.topLevelDeclaration_enter(node, null);
|
_resolver.flowAnalysis.topLevelDeclaration_enter(_resolver, node, null);
|
||||||
} else if (element.isLate) {
|
} else if (element.isLate) {
|
||||||
_resolver.flowAnalysis.flow?.lateInitializer_begin(node);
|
_resolver.flowAnalysis.flow?.lateInitializer_begin(node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1185,7 +1185,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||||
flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
|
flowAnalysis.topLevelDeclaration_enter(this, node, node.parameters);
|
||||||
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
||||||
|
|
||||||
var returnType = node.declaredElement!.type.returnType;
|
var returnType = node.declaredElement!.type.returnType;
|
||||||
|
@ -1409,7 +1409,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
||||||
flowAnalysis.flow!.functionExpression_begin(node);
|
flowAnalysis.flow!.functionExpression_begin(node);
|
||||||
} else {
|
} else {
|
||||||
flowAnalysis.topLevelDeclaration_enter(
|
flowAnalysis.topLevelDeclaration_enter(
|
||||||
node, node.functionExpression.parameters);
|
this, node, node.functionExpression.parameters);
|
||||||
}
|
}
|
||||||
flowAnalysis.executableDeclaration_enter(
|
flowAnalysis.executableDeclaration_enter(
|
||||||
node,
|
node,
|
||||||
|
@ -1638,7 +1638,7 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void visitMethodDeclaration(MethodDeclaration node) {
|
void visitMethodDeclaration(MethodDeclaration node) {
|
||||||
flowAnalysis.topLevelDeclaration_enter(node, node.parameters);
|
flowAnalysis.topLevelDeclaration_enter(this, node, node.parameters);
|
||||||
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
flowAnalysis.executableDeclaration_enter(node, node.parameters, false);
|
||||||
|
|
||||||
DartType returnType = node.declaredElement!.returnType;
|
DartType returnType = node.declaredElement!.returnType;
|
||||||
|
|
|
@ -58,7 +58,7 @@ class AstResolver {
|
||||||
node.accept(_resolutionVisitor);
|
node.accept(_resolutionVisitor);
|
||||||
node.accept(_scopeResolverVisitor);
|
node.accept(_scopeResolverVisitor);
|
||||||
_prepareEnclosingDeclarations();
|
_prepareEnclosingDeclarations();
|
||||||
_flowAnalysis.topLevelDeclaration_enter(node, null);
|
_flowAnalysis.topLevelDeclaration_enter(_resolverVisitor, node, null);
|
||||||
node.accept(_resolverVisitor);
|
node.accept(_resolverVisitor);
|
||||||
_flowAnalysis.topLevelDeclaration_exit();
|
_flowAnalysis.topLevelDeclaration_exit();
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,8 @@ class AstResolver {
|
||||||
visit(_scopeResolverVisitor);
|
visit(_scopeResolverVisitor);
|
||||||
|
|
||||||
_prepareEnclosingDeclarations();
|
_prepareEnclosingDeclarations();
|
||||||
_flowAnalysis.topLevelDeclaration_enter(node, node.parameters,
|
_flowAnalysis.topLevelDeclaration_enter(
|
||||||
|
_resolverVisitor, node, node.parameters,
|
||||||
visit: visit);
|
visit: visit);
|
||||||
visit(_resolverVisitor);
|
visit(_resolverVisitor);
|
||||||
_flowAnalysis.topLevelDeclaration_exit();
|
_flowAnalysis.topLevelDeclaration_exit();
|
||||||
|
@ -95,7 +96,8 @@ class AstResolver {
|
||||||
node.accept(_scopeResolverVisitor);
|
node.accept(_scopeResolverVisitor);
|
||||||
}
|
}
|
||||||
_prepareEnclosingDeclarations();
|
_prepareEnclosingDeclarations();
|
||||||
_flowAnalysis.topLevelDeclaration_enter(node.parent!, null);
|
_flowAnalysis.topLevelDeclaration_enter(
|
||||||
|
_resolverVisitor, node.parent!, null);
|
||||||
node.accept(_resolverVisitor);
|
node.accept(_resolverVisitor);
|
||||||
_flowAnalysis.topLevelDeclaration_exit();
|
_flowAnalysis.topLevelDeclaration_exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,12 +79,14 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
test_ifElementWithElse_mayBeConst() async {
|
test_ifElementWithElse_mayBeConst() async {
|
||||||
await assertNoErrorsInCode('''
|
await assertErrorsInCode('''
|
||||||
void main() {
|
void main() {
|
||||||
const isTrue = true;
|
const isTrue = true;
|
||||||
const {1: null, if (isTrue) null: null else null: null};
|
const {1: null, if (isTrue) null: null else null: null};
|
||||||
}
|
}
|
||||||
''');
|
''', [
|
||||||
|
error(HintCode.DEAD_CODE, 83, 12),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
test_spreadElement_mayBeConst() async {
|
test_spreadElement_mayBeConst() async {
|
||||||
|
|
|
@ -224,7 +224,9 @@ class TypeInferrerImpl implements TypeInferrer {
|
||||||
flowAnalysis = library.isNonNullableByDefault
|
flowAnalysis = library.isNonNullableByDefault
|
||||||
? new FlowAnalysis(
|
? new FlowAnalysis(
|
||||||
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
||||||
assignedVariables)
|
assignedVariables,
|
||||||
|
respectImplicitlyTypedVarInitializers:
|
||||||
|
library.enableConstructorTearOffsInLibrary)
|
||||||
: new FlowAnalysis.legacy(
|
: new FlowAnalysis.legacy(
|
||||||
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
new TypeOperationsCfe(engine.typeSchemaEnvironment),
|
||||||
assignedVariables) {}
|
assignedVariables) {}
|
||||||
|
|
|
@ -142,6 +142,7 @@ bs
|
||||||
bsd
|
bsd
|
||||||
bslash
|
bslash
|
||||||
buffered
|
buffered
|
||||||
|
buggy
|
||||||
builder`s
|
builder`s
|
||||||
bulk
|
bulk
|
||||||
bump
|
bump
|
||||||
|
@ -559,6 +560,7 @@ i'll
|
||||||
i2b
|
i2b
|
||||||
ic
|
ic
|
||||||
id
|
id
|
||||||
|
ideal
|
||||||
identifies
|
identifies
|
||||||
identifying
|
identifying
|
||||||
ideographic
|
ideographic
|
||||||
|
|
|
@ -377,10 +377,14 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
||||||
var previousAssignedVariables = _assignedVariables;
|
var previousAssignedVariables = _assignedVariables;
|
||||||
if (_flowAnalysis == null) {
|
if (_flowAnalysis == null) {
|
||||||
_assignedVariables = AssignedVariables();
|
_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,
|
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
|
||||||
PromotableElement, DecoratedType>(
|
PromotableElement, DecoratedType>(
|
||||||
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
||||||
_assignedVariables!);
|
_assignedVariables!,
|
||||||
|
respectImplicitlyTypedVarInitializers: true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
_dispatch(node.name);
|
_dispatch(node.name);
|
||||||
|
@ -2110,10 +2114,14 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
|
||||||
assert(_assignedVariables == null);
|
assert(_assignedVariables == null);
|
||||||
_assignedVariables =
|
_assignedVariables =
|
||||||
FlowAnalysisHelper.computeAssignedVariables(node, parameters);
|
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,
|
_flowAnalysis = FlowAnalysis<AstNode, Statement, Expression,
|
||||||
PromotableElement, DecoratedType>(
|
PromotableElement, DecoratedType>(
|
||||||
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
DecoratedTypeOperations(_typeSystem, _variables, _graph),
|
||||||
_assignedVariables!);
|
_assignedVariables!,
|
||||||
|
respectImplicitlyTypedVarInitializers: true);
|
||||||
if (parameters != null) {
|
if (parameters != null) {
|
||||||
for (var parameter in parameters.parameters) {
|
for (var parameter in parameters.parameters) {
|
||||||
_flowAnalysis!.declare(parameter.declaredElement!, true);
|
_flowAnalysis!.declare(parameter.declaredElement!, true);
|
||||||
|
|
|
@ -315,9 +315,15 @@ class AnnotateKernel extends RecursiveVisitor {
|
||||||
Constant? constantValue;
|
Constant? constantValue;
|
||||||
bool isInt = false;
|
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) {
|
if (nullable) {
|
||||||
type = (type as NullableType).baseType;
|
type = type.baseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullable && type == const EmptyType()) {
|
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 New Issue
Block a user