Stop using ConstraintVariable.always to represent the nullability of dynamic.
This is a necessary part of reworking nullability migration to stop using constraint variables entirely. Change-Id: If6c347c16882edfc2e8bdbeac761e09d8256c9bb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101622 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
d06c7b7c89
commit
df7db795dc
@@ -89,14 +89,14 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
this._source,
|
||||
this._permissive,
|
||||
this.assumptions)
|
||||
: _notNullType =
|
||||
DecoratedType(typeProvider.objectType, NullabilityNode.never),
|
||||
: _notNullType = DecoratedType(
|
||||
typeProvider.objectType, NullabilityNode.never, _graph),
|
||||
_nonNullableBoolType =
|
||||
DecoratedType(typeProvider.boolType, NullabilityNode.never),
|
||||
DecoratedType(typeProvider.boolType, NullabilityNode.never, _graph),
|
||||
_nonNullableTypeType =
|
||||
DecoratedType(typeProvider.typeType, NullabilityNode.never),
|
||||
_nullType =
|
||||
DecoratedType(typeProvider.nullType, NullabilityNode.always);
|
||||
DecoratedType(typeProvider.typeType, NullabilityNode.never, _graph),
|
||||
_nullType = DecoratedType(
|
||||
typeProvider.nullType, NullabilityNode.always, _graph);
|
||||
|
||||
/// Gets the decorated type of [element] from [_variables], performing any
|
||||
/// necessary substitutions.
|
||||
@@ -133,7 +133,7 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
throw element.runtimeType; // TODO(paulberry)
|
||||
}
|
||||
return decoratedBaseType.substitute(
|
||||
_constraints, substitution, elementType);
|
||||
_constraints, _graph, substitution, elementType);
|
||||
} else {
|
||||
return decoratedBaseType;
|
||||
}
|
||||
@@ -196,7 +196,7 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
|
||||
@override
|
||||
DecoratedType visitBooleanLiteral(BooleanLiteral node) {
|
||||
return DecoratedType(node.staticType, NullabilityNode.never);
|
||||
return DecoratedType(node.staticType, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -216,7 +216,8 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
var overallType = DecoratedType(
|
||||
node.staticType,
|
||||
NullabilityNode.forLUB(
|
||||
node, thenType.node, elseType.node, _graph, _joinNullabilities));
|
||||
node, thenType.node, elseType.node, _graph, _joinNullabilities),
|
||||
_graph);
|
||||
_variables.recordDecoratedExpressionType(node, overallType);
|
||||
return overallType;
|
||||
}
|
||||
@@ -311,7 +312,7 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
|
||||
@override
|
||||
DecoratedType visitIntegerLiteral(IntegerLiteral node) {
|
||||
return DecoratedType(node.staticType, NullabilityNode.never);
|
||||
return DecoratedType(node.staticType, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -412,24 +413,24 @@ class ConstraintGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
|
||||
@override
|
||||
DecoratedType visitStringLiteral(StringLiteral node) {
|
||||
return DecoratedType(node.staticType, NullabilityNode.never);
|
||||
return DecoratedType(node.staticType, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
@override
|
||||
DecoratedType visitThisExpression(ThisExpression node) {
|
||||
return DecoratedType(node.staticType, NullabilityNode.never);
|
||||
return DecoratedType(node.staticType, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
@override
|
||||
DecoratedType visitThrowExpression(ThrowExpression node) {
|
||||
node.expression.accept(this);
|
||||
// TODO(paulberry): do we need to check the expression type? I think not.
|
||||
return DecoratedType(node.staticType, NullabilityNode.never);
|
||||
return DecoratedType(node.staticType, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
@override
|
||||
DecoratedType visitTypeName(TypeName typeName) {
|
||||
return DecoratedType(typeName.type, NullabilityNode.never);
|
||||
return DecoratedType(typeName.type, NullabilityNode.never, _graph);
|
||||
}
|
||||
|
||||
/// Creates the necessary constraint(s) for an assignment from [sourceType] to
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'package:analysis_server/src/nullability/conditional_discard.dart';
|
||||
import 'package:analysis_server/src/nullability/decorated_type.dart';
|
||||
import 'package:analysis_server/src/nullability/expression_checks.dart';
|
||||
import 'package:analysis_server/src/nullability/nullability_graph.dart';
|
||||
import 'package:analysis_server/src/nullability/nullability_node.dart';
|
||||
import 'package:analysis_server/src/nullability/transitional_api.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
@@ -40,8 +41,10 @@ class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
|
||||
final NullabilityMigrationAssumptions assumptions;
|
||||
|
||||
ConstraintVariableGatherer(
|
||||
this._variables, this._source, this._permissive, this.assumptions);
|
||||
final NullabilityGraph _graph;
|
||||
|
||||
ConstraintVariableGatherer(this._variables, this._source, this._permissive,
|
||||
this.assumptions, this._graph);
|
||||
|
||||
/// Creates and stores a [DecoratedType] object corresponding to the given
|
||||
/// [type] AST, and returns it.
|
||||
@@ -50,8 +53,8 @@ class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
// TODO(danrubel): Return something other than this
|
||||
// to indicate that we should insert a type for the declaration
|
||||
// that is missing a type reference.
|
||||
? new DecoratedType(
|
||||
DynamicTypeImpl.instance, NullabilityNode.forInferredDynamicType())
|
||||
? new DecoratedType(DynamicTypeImpl.instance,
|
||||
NullabilityNode.forInferredDynamicType(_graph), _graph)
|
||||
: type.accept(this);
|
||||
}
|
||||
|
||||
@@ -125,8 +128,8 @@ class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
assert(node is NamedType); // TODO(paulberry)
|
||||
var type = node.type;
|
||||
if (type.isVoid) {
|
||||
return DecoratedType(
|
||||
type, NullabilityNode.forTypeAnnotation(node.end, always: true));
|
||||
return DecoratedType(type,
|
||||
NullabilityNode.forTypeAnnotation(node.end, always: true), _graph);
|
||||
}
|
||||
assert(
|
||||
type is InterfaceType || type is TypeParameterType); // TODO(paulberry)
|
||||
@@ -145,6 +148,7 @@ class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
NullabilityNode.forTypeAnnotation(node.end,
|
||||
always: node.question != null),
|
||||
node.end,
|
||||
_graph,
|
||||
typeArguments: typeArguments);
|
||||
_variables.recordDecoratedTypeAnnotation(_source, node, decoratedType);
|
||||
return decoratedType;
|
||||
@@ -161,7 +165,7 @@ class ConstraintVariableGatherer extends GeneralizingAstVisitor<DecoratedType> {
|
||||
// TODO(paulberry): test that it's correct to use `null` for the nullability
|
||||
// of the function type
|
||||
var functionType = DecoratedType(
|
||||
declaredElement.type, NullabilityNode.never,
|
||||
declaredElement.type, NullabilityNode.never, _graph,
|
||||
returnType: decoratedReturnType,
|
||||
positionalParameters: [],
|
||||
namedParameters: {});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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:analysis_server/src/nullability/nullability_graph.dart';
|
||||
import 'package:analysis_server/src/nullability/nullability_node.dart';
|
||||
import 'package:analysis_server/src/nullability/transitional_api.dart';
|
||||
import 'package:analysis_server/src/nullability/unit_propagation.dart';
|
||||
@@ -36,25 +37,27 @@ class DecoratedType {
|
||||
/// TODO(paulberry): how should we handle generic typedefs?
|
||||
final List<DecoratedType> typeArguments;
|
||||
|
||||
DecoratedType(this.type, this.node,
|
||||
DecoratedType(this.type, this.node, NullabilityGraph graph,
|
||||
{this.returnType,
|
||||
this.positionalParameters = const [],
|
||||
this.namedParameters = const {},
|
||||
this.typeArguments = const []}) {
|
||||
assert(node != null);
|
||||
// The type system doesn't have a non-nullable version of `dynamic`. So if
|
||||
// the type is `dynamic`, verify that `nullable` is `always`.
|
||||
assert(!type.isDynamic || node.isAlwaysNullable);
|
||||
// the type is `dynamic`, verify that the node is directly downstream from
|
||||
// Nullability.always.
|
||||
assert(!type.isDynamic ||
|
||||
graph.getUpstreamNodes(node).contains(NullabilityNode.always));
|
||||
}
|
||||
|
||||
/// Creates a [DecoratedType] corresponding to the given [element], which is
|
||||
/// presumed to have come from code that is already migrated.
|
||||
factory DecoratedType.forElement(Element element) {
|
||||
factory DecoratedType.forElement(Element element, NullabilityGraph graph) {
|
||||
DecoratedType decorate(DartType type) {
|
||||
assert((type as TypeImpl).nullabilitySuffix ==
|
||||
NullabilitySuffix.star); // TODO(paulberry)
|
||||
if (type is FunctionType) {
|
||||
var decoratedType = DecoratedType(type, NullabilityNode.never,
|
||||
var decoratedType = DecoratedType(type, NullabilityNode.never, graph,
|
||||
returnType: decorate(type.returnType), positionalParameters: []);
|
||||
for (var parameter in type.parameters) {
|
||||
assert(parameter.isPositional); // TODO(paulberry)
|
||||
@@ -63,7 +66,7 @@ class DecoratedType {
|
||||
return decoratedType;
|
||||
} else if (type is InterfaceType) {
|
||||
assert(type.typeParameters.isEmpty); // TODO(paulberry)
|
||||
return DecoratedType(type, NullabilityNode.never);
|
||||
return DecoratedType(type, NullabilityNode.never, graph);
|
||||
} else {
|
||||
throw type.runtimeType; // TODO(paulberry)
|
||||
}
|
||||
@@ -84,10 +87,11 @@ class DecoratedType {
|
||||
/// the normal type system.
|
||||
DecoratedType substitute(
|
||||
Constraints constraints,
|
||||
NullabilityGraph graph,
|
||||
Map<TypeParameterElement, DecoratedType> substitution,
|
||||
DartType undecoratedResult) {
|
||||
if (substitution.isEmpty) return this;
|
||||
return _substitute(constraints, substitution, undecoratedResult);
|
||||
return _substitute(constraints, graph, substitution, undecoratedResult);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -119,6 +123,7 @@ class DecoratedType {
|
||||
/// Internal implementation of [_substitute], used as a recursion target.
|
||||
DecoratedType _substitute(
|
||||
Constraints constraints,
|
||||
NullabilityGraph graph,
|
||||
Map<TypeParameterElement, DecoratedType> substitution,
|
||||
DartType undecoratedResult) {
|
||||
var type = this.type;
|
||||
@@ -132,17 +137,19 @@ class DecoratedType {
|
||||
? undecoratedResult.normalParameterTypes[i]
|
||||
: undecoratedResult
|
||||
.optionalParameterTypes[i - numRequiredParameters];
|
||||
newPositionalParameters.add(positionalParameters[i]
|
||||
._substitute(constraints, substitution, undecoratedParameterType));
|
||||
newPositionalParameters.add(positionalParameters[i]._substitute(
|
||||
constraints, graph, substitution, undecoratedParameterType));
|
||||
}
|
||||
return DecoratedType(undecoratedResult, node,
|
||||
return DecoratedType(undecoratedResult, node, graph,
|
||||
returnType: returnType._substitute(
|
||||
constraints, substitution, undecoratedResult.returnType),
|
||||
constraints, graph, substitution, undecoratedResult.returnType),
|
||||
positionalParameters: newPositionalParameters);
|
||||
} else if (type is TypeParameterType) {
|
||||
var inner = substitution[type.element];
|
||||
return DecoratedType(undecoratedResult,
|
||||
NullabilityNode.forSubstitution(constraints, inner?.node, node));
|
||||
return DecoratedType(
|
||||
undecoratedResult,
|
||||
NullabilityNode.forSubstitution(constraints, inner?.node, node),
|
||||
graph);
|
||||
} else if (type is VoidType) {
|
||||
return this;
|
||||
}
|
||||
@@ -159,10 +166,10 @@ class DecoratedTypeAnnotation extends DecoratedType
|
||||
implements PotentialModification {
|
||||
final int _offset;
|
||||
|
||||
DecoratedTypeAnnotation(
|
||||
DartType type, NullabilityNode nullabilityNode, this._offset,
|
||||
DecoratedTypeAnnotation(DartType type, NullabilityNode nullabilityNode,
|
||||
this._offset, NullabilityGraph graph,
|
||||
{List<DecoratedType> typeArguments = const []})
|
||||
: super(type, nullabilityNode, typeArguments: typeArguments);
|
||||
: super(type, nullabilityNode, graph, typeArguments: typeArguments);
|
||||
|
||||
@override
|
||||
bool get isEmpty => !node.isNullable;
|
||||
|
||||
@@ -40,7 +40,11 @@ class NullabilityNode {
|
||||
///
|
||||
/// TODO(paulberry): this should go away; we should decorate the actual
|
||||
/// inferred type rather than assuming `dynamic`.
|
||||
NullabilityNode.forInferredDynamicType() : this._(ConstraintVariable.always);
|
||||
factory NullabilityNode.forInferredDynamicType(NullabilityGraph graph) {
|
||||
var node = NullabilityNode._(TypeIsNullable(null));
|
||||
graph.connect(NullabilityNode.always, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// Creates a [NullabilityNode] representing the nullability of an
|
||||
/// expression which is nullable iff both [a] and [b] are nullable.
|
||||
|
||||
@@ -146,11 +146,11 @@ class NullabilityMigration {
|
||||
|
||||
final NullabilityMigrationAssumptions assumptions;
|
||||
|
||||
final _variables = Variables();
|
||||
final Variables _variables;
|
||||
|
||||
final _constraints = Solver();
|
||||
|
||||
final _graph = NullabilityGraph();
|
||||
final NullabilityGraph _graph;
|
||||
|
||||
/// Prepares to perform nullability migration.
|
||||
///
|
||||
@@ -160,8 +160,12 @@ class NullabilityMigration {
|
||||
/// is fully implemented.
|
||||
NullabilityMigration(
|
||||
{bool permissive: false,
|
||||
this.assumptions: const NullabilityMigrationAssumptions()})
|
||||
: _permissive = permissive;
|
||||
NullabilityMigrationAssumptions assumptions:
|
||||
const NullabilityMigrationAssumptions()})
|
||||
: this._(permissive, assumptions, NullabilityGraph());
|
||||
|
||||
NullabilityMigration._(this._permissive, this.assumptions, this._graph)
|
||||
: _variables = Variables(_graph);
|
||||
|
||||
Map<Source, List<PotentialModification>> finish() {
|
||||
_constraints.applyHeuristics();
|
||||
@@ -169,8 +173,8 @@ class NullabilityMigration {
|
||||
}
|
||||
|
||||
void prepareInput(CompilationUnit unit) {
|
||||
unit.accept(ConstraintVariableGatherer(
|
||||
_variables, unit.declaredElement.source, _permissive, assumptions));
|
||||
unit.accept(ConstraintVariableGatherer(_variables,
|
||||
unit.declaredElement.source, _permissive, assumptions, _graph));
|
||||
}
|
||||
|
||||
void processInput(CompilationUnit unit, TypeProvider typeProvider) {
|
||||
@@ -269,10 +273,14 @@ class Variables implements VariableRecorder, VariableRepository {
|
||||
|
||||
final _potentialModifications = <Source, List<PotentialModification>>{};
|
||||
|
||||
final NullabilityGraph _graph;
|
||||
|
||||
Variables(this._graph);
|
||||
|
||||
@override
|
||||
DecoratedType decoratedElementType(Element element, {bool create: false}) =>
|
||||
_decoratedElementTypes[element] ??= create
|
||||
? DecoratedType.forElement(element)
|
||||
? DecoratedType.forElement(element, _graph)
|
||||
: throw StateError('No element found');
|
||||
|
||||
Map<Source, List<PotentialModification>> getPotentialModifications() =>
|
||||
|
||||
@@ -32,9 +32,6 @@ class ConstraintGathererTest extends ConstraintsTestBase {
|
||||
@override
|
||||
final _Constraints constraints = _Constraints();
|
||||
|
||||
@override
|
||||
final graph = NullabilityGraph();
|
||||
|
||||
void assertConditional(
|
||||
NullabilityNode node, NullabilityNode left, NullabilityNode right) {
|
||||
var conditionalNode = node as NullabilityNodeForLUB;
|
||||
@@ -806,8 +803,6 @@ Type f() {
|
||||
abstract class ConstraintsTestBase extends MigrationVisitorTestBase {
|
||||
Constraints get constraints;
|
||||
|
||||
NullabilityGraph get graph;
|
||||
|
||||
/// Analyzes the given source code, producing constraint variables and
|
||||
/// constraints for it.
|
||||
@override
|
||||
@@ -851,7 +846,8 @@ void f(x) {}
|
||||
expect(decoratedFunctionType('f').positionalParameters[0],
|
||||
same(decoratedType));
|
||||
expect(decoratedType.type.isDynamic, isTrue);
|
||||
expect(decoratedType.node.nullable, same(ConstraintVariable.always));
|
||||
expect(graph.getUpstreamNodes(decoratedType.node),
|
||||
contains(NullabilityNode.always));
|
||||
}
|
||||
|
||||
test_topLevelFunction_parameterType_named_no_default() async {
|
||||
@@ -918,7 +914,8 @@ f() {}
|
||||
''');
|
||||
var decoratedType = decoratedFunctionType('f').returnType;
|
||||
expect(decoratedType.type.isDynamic, isTrue);
|
||||
expect(decoratedType.node.nullable, same(ConstraintVariable.always));
|
||||
expect(graph.getUpstreamNodes(decoratedType.node),
|
||||
contains(NullabilityNode.always));
|
||||
}
|
||||
|
||||
test_topLevelFunction_returnType_simple() async {
|
||||
@@ -932,18 +929,24 @@ int f() => 0;
|
||||
}
|
||||
|
||||
class MigrationVisitorTestBase extends AbstractSingleUnitTest {
|
||||
final _variables = _Variables();
|
||||
final _Variables _variables;
|
||||
|
||||
FindNode findNode;
|
||||
|
||||
final NullabilityGraph graph;
|
||||
|
||||
MigrationVisitorTestBase() : this._(NullabilityGraph());
|
||||
|
||||
MigrationVisitorTestBase._(this.graph) : _variables = _Variables(graph);
|
||||
|
||||
TypeProvider get typeProvider => testAnalysisResult.typeProvider;
|
||||
|
||||
Future<CompilationUnit> analyze(String code,
|
||||
{NullabilityMigrationAssumptions assumptions:
|
||||
const NullabilityMigrationAssumptions()}) async {
|
||||
await resolveTestUnit(code);
|
||||
testUnit.accept(
|
||||
ConstraintVariableGatherer(_variables, testSource, false, assumptions));
|
||||
testUnit.accept(ConstraintVariableGatherer(
|
||||
_variables, testSource, false, assumptions, graph));
|
||||
findNode = FindNode(code, testUnit);
|
||||
return testUnit;
|
||||
}
|
||||
@@ -1024,6 +1027,8 @@ class _Variables extends Variables {
|
||||
|
||||
final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
|
||||
|
||||
_Variables(NullabilityGraph graph) : super(graph);
|
||||
|
||||
/// Gets the [ExpressionChecks] associated with the given [expression].
|
||||
ExpressionChecks checkExpression(Expression expression) =>
|
||||
_expressionChecks[_normalizeExpression(expression)];
|
||||
|
||||
Reference in New Issue
Block a user