Skip to content

Commit 6f6797e

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Add NullSafetyUnderstandingFlag, update APIs to use it.
DartType.== implementations ignore nullability if the flag is not true. TypeSystem APIs eliminate nullability from types before doing anything. Internally analyzer continues using actual nullability for types. Package nnbd_migration opted into NullSafetyUnderstandingFlag. Bug: #40500 Change-Id: Ifeea28c01adf1dc59ed2da675b4a62c6334d529a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134865 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 8d9cbaf commit 6f6797e

38 files changed

+398
-232
lines changed

pkg/analyzer/lib/dart/element/element.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,14 @@ abstract class LibraryElement implements Element {
13741374
/// Return the class defined in this library that has the given [name], or
13751375
/// `null` if this library does not define a class with the given name.
13761376
ClassElement getType(String className);
1377+
1378+
/// If a legacy library, return the legacy view on the [element].
1379+
/// Otherwise, return the original element.
1380+
T toLegacyElementIfOptOut<T extends Element>(T element);
1381+
1382+
/// If a legacy library, return the legacy version of the [type].
1383+
/// Otherwise, return the original type.
1384+
DartType toLegacyTypeIfOptOut(DartType type);
13771385
}
13781386

13791387
/// An element that can be (but is not required to be) defined within a method
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:analyzer/dart/element/element.dart';
8+
import 'package:analyzer/dart/element/type.dart';
9+
10+
/// Every [DartType] has the nullability suffix, elements declared in legacy
11+
/// libraries have types with star suffixes, and types in migrated libraries
12+
/// have none, question mark, or star suffixes. Analyzer itself can handle
13+
/// mixtures of nullabilities with legacy and migrated libraries.
14+
///
15+
/// However analyzer clients saw only star suffixes so far. Exposing other
16+
/// nullabilities is a breaking change, because types types with different
17+
/// nullabilities are not equal, `null` cannot be used where a non-nullable
18+
/// type is expected, etc. When accessing elements and types that come from
19+
/// migrated libraries, while analyzing a legacy library, nullabilities must
20+
/// be erased, using [LibraryElement.toLegacyElementIfOptOut] and
21+
/// [LibraryElement.toLegacyTypeIfOptOut]. The client must explicitly do
22+
/// this, and explicitly specify that it knows how to handle nullabilities
23+
/// by setting this flag to `true`.
24+
///
25+
/// When this flag is `false` (by default), all types will return their
26+
/// nullability as star. So, type equality and subtype checks will work
27+
/// as they worked before some libraries migrated. Note, that during
28+
/// analysis (building element models, and resolving ASTs), analyzer will use
29+
/// actual nullabilities, according to the language specification, so report
30+
/// all corresponding errors, and perform necessary type operations. It is
31+
/// only when the client later views on the types, they will look as legacy.
32+
class NullSafetyUnderstandingFlag {
33+
static final _zoneKey = Object();
34+
35+
static bool get isEnabled {
36+
return Zone.current[_zoneKey] ?? false;
37+
}
38+
39+
/// Code that understands nullability should be run using this method,
40+
/// otherwise all type operations will treat all nullabilities as star.
41+
static R enableNullSafetyTypes<R>(R body()) {
42+
return runZoned<R>(body, zoneValues: {_zoneKey: true});
43+
}
44+
}

pkg/analyzer/lib/src/dart/analysis/driver.dart

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:analyzer/dart/analysis/results.dart';
1111
import 'package:analyzer/dart/analysis/session.dart';
1212
import 'package:analyzer/dart/ast/ast.dart';
1313
import 'package:analyzer/dart/element/element.dart' show LibraryElement;
14+
import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
1415
import 'package:analyzer/error/error.dart';
1516
import 'package:analyzer/error/listener.dart';
1617
import 'package:analyzer/exception/exception.dart';
@@ -1216,6 +1217,21 @@ class AnalysisDriver implements AnalysisDriverGeneric {
12161217
{bool withUnit = false,
12171218
bool asIsIfPartWithoutLibrary = false,
12181219
bool skipIfSameSignature = false}) {
1220+
return NullSafetyUnderstandingFlag.enableNullSafetyTypes(() {
1221+
return _computeAnalysisResult2(
1222+
path,
1223+
withUnit: withUnit,
1224+
asIsIfPartWithoutLibrary: asIsIfPartWithoutLibrary,
1225+
skipIfSameSignature: skipIfSameSignature,
1226+
);
1227+
});
1228+
}
1229+
1230+
/// Unwrapped implementation of [_computeAnalysisResult].
1231+
AnalysisResult _computeAnalysisResult2(String path,
1232+
{bool withUnit = false,
1233+
bool asIsIfPartWithoutLibrary = false,
1234+
bool skipIfSameSignature = false}) {
12191235
FileState file = _fsState.getFileForPath(path);
12201236

12211237
// Prepare the library - the file itself, or the known library.
@@ -1476,21 +1492,24 @@ class AnalysisDriver implements AnalysisDriverGeneric {
14761492
}
14771493
}
14781494

1479-
if (_libraryContext == null) {
1480-
_libraryContext = LibraryContext(
1481-
session: currentSession,
1482-
logger: _logger,
1483-
fsState: fsState,
1484-
byteStore: _byteStore,
1485-
analysisOptions: _analysisOptions,
1486-
declaredVariables: declaredVariables,
1487-
sourceFactory: _sourceFactory,
1488-
externalSummaries: _externalSummaries,
1489-
targetLibrary: library,
1490-
);
1491-
} else {
1492-
_libraryContext.load2(library);
1493-
}
1495+
NullSafetyUnderstandingFlag.enableNullSafetyTypes(() {
1496+
if (_libraryContext == null) {
1497+
_libraryContext = LibraryContext(
1498+
session: currentSession,
1499+
logger: _logger,
1500+
fsState: fsState,
1501+
byteStore: _byteStore,
1502+
analysisOptions: _analysisOptions,
1503+
declaredVariables: declaredVariables,
1504+
sourceFactory: _sourceFactory,
1505+
externalSummaries: _externalSummaries,
1506+
targetLibrary: library,
1507+
);
1508+
} else {
1509+
_libraryContext.load2(library);
1510+
}
1511+
});
1512+
14941513
return _libraryContext;
14951514
}
14961515

pkg/analyzer/lib/src/dart/constant/evaluation.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class ConstantEvaluationEngine {
6767

6868
/// The type system. This is used to guess the types of constants when their
6969
/// exact value is unknown.
70-
final TypeSystem typeSystem;
70+
final TypeSystemImpl typeSystem;
7171

7272
/// The helper for evaluating variables declared on the command line
7373
/// using '-D', and represented as [DeclaredVariables].
@@ -109,7 +109,7 @@ class ConstantEvaluationEngine {
109109
}
110110

111111
bool get _isNonNullableByDefault {
112-
return (typeSystem as TypeSystemImpl).isNonNullableByDefault;
112+
return typeSystem.isNonNullableByDefault;
113113
}
114114

115115
DartObjectImpl get _nullObject {
@@ -937,7 +937,7 @@ class ConstantEvaluationEngine {
937937
return true;
938938
}
939939
var objType = obj.type;
940-
return typeSystem.isSubtypeOf(objType, type);
940+
return typeSystem.isSubtypeOf2(objType, type);
941941
}
942942

943943
/// Determine whether the given string is a valid name for a public symbol
@@ -1039,7 +1039,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
10391039
ExperimentStatus get experimentStatus => evaluationEngine.experimentStatus;
10401040

10411041
/// Convenience getter to gain access to the [evaluationEngine]'s type system.
1042-
TypeSystem get typeSystem => evaluationEngine.typeSystem;
1042+
TypeSystemImpl get typeSystem => evaluationEngine.typeSystem;
10431043

10441044
/// Convenience getter to gain access to the [evaluationEngine]'s type
10451045
/// provider.
@@ -1211,7 +1211,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
12111211
ParameterizedType elseType = elseResult.type;
12121212
return DartObjectImpl.validWithUnknownValue(
12131213
typeSystem,
1214-
typeSystem.leastUpperBound(thenType, elseType) as ParameterizedType,
1214+
typeSystem.getLeastUpperBound(thenType, elseType) as ParameterizedType,
12151215
);
12161216
}
12171217

@@ -1672,7 +1672,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> {
16721672
// TODO(brianwilkerson) Figure out why the static type is sometimes null.
16731673
DartType staticType = condition.staticType;
16741674
if (staticType == null ||
1675-
typeSystem.isAssignableTo(staticType, _typeProvider.boolType)) {
1675+
typeSystem.isAssignableTo2(staticType, _typeProvider.boolType)) {
16761676
// If the static type is not assignable, then we will have already
16771677
// reported this error.
16781678
// TODO(mfairhurst) get the FeatureSet to suppress this for nnbd too.

pkg/analyzer/lib/src/dart/constant/value.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ class DartObjectImpl implements DartObject {
262262
if (isNull) {
263263
return this;
264264
}
265-
if (!typeSystem.isSubtypeOf(type, (castType._state as TypeState)._type)) {
265+
if (!typeSystem.isSubtypeOf2(type, (castType._state as TypeState)._type)) {
266266
throw EvaluationException(
267267
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
268268
}
@@ -478,7 +478,7 @@ class DartObjectImpl implements DartObject {
478478
state = BoolState.FALSE_STATE;
479479
}
480480
} else {
481-
state = BoolState.from(typeSystem.isSubtypeOf(type, typeType));
481+
state = BoolState.from(typeSystem.isSubtypeOf2(type, typeType));
482482
}
483483
return DartObjectImpl(typeSystem, typeSystem.typeProvider.boolType, state);
484484
}

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import 'package:analyzer/src/dart/constant/compute.dart';
1616
import 'package:analyzer/src/dart/constant/evaluation.dart';
1717
import 'package:analyzer/src/dart/constant/value.dart';
1818
import 'package:analyzer/src/dart/element/display_string_builder.dart';
19+
import 'package:analyzer/src/dart/element/member.dart';
20+
import 'package:analyzer/src/dart/element/nullability_eliminator.dart';
1921
import 'package:analyzer/src/dart/element/type.dart';
2022
import 'package:analyzer/src/dart/element/type_algebra.dart';
2123
import 'package:analyzer/src/dart/element/type_provider.dart';
@@ -5662,6 +5664,18 @@ class LibraryElementImpl extends ElementImpl implements LibraryElement {
56625664
BooleanArray.set(_resolutionCapabilities, capability.index, value);
56635665
}
56645666

5667+
@override
5668+
T toLegacyElementIfOptOut<T extends Element>(T element) {
5669+
if (isNonNullableByDefault) return element;
5670+
return Member.legacy(element);
5671+
}
5672+
5673+
@override
5674+
DartType toLegacyTypeIfOptOut(DartType type) {
5675+
if (isNonNullableByDefault) return type;
5676+
return NullabilityEliminator.perform(typeProvider, type);
5677+
}
5678+
56655679
@override
56665680
void visitChildren(ElementVisitor visitor) {
56675681
super.visitChildren(visitor);

pkg/analyzer/lib/src/dart/element/top_merge.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ class TopMergeHelper {
236236
if (R_isCovariant) {
237237
var T1 = T_parameter.type;
238238
var T2 = S_parameter.type;
239-
var T1_isSubtype = typeSystem.isSubtypeOf(T1, T2);
240-
var T2_isSubtype = typeSystem.isSubtypeOf(T2, T1);
239+
var T1_isSubtype = typeSystem.isSubtypeOf2(T1, T2);
240+
var T2_isSubtype = typeSystem.isSubtypeOf2(T2, T1);
241241
if (T1_isSubtype && T2_isSubtype) {
242242
// if `T1 <: T2` and `T2 <: T1`, then the result is
243243
// `NNBD_TOP_MERGE(T1, T2)`, and it is covariant.

pkg/analyzer/lib/src/dart/element/type.dart

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:collection';
66

77
import 'package:analyzer/dart/ast/token.dart';
88
import 'package:analyzer/dart/element/element.dart';
9+
import 'package:analyzer/dart/element/null_safety_understanding_flag.dart';
910
import 'package:analyzer/dart/element/nullability_suffix.dart';
1011
import 'package:analyzer/dart/element/type.dart';
1112
import 'package:analyzer/dart/element/type_provider.dart';
@@ -241,27 +242,35 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
241242
List<TypeParameterElement> get typeParameters => const [] /*TODO(paulberry)*/;
242243

243244
@override
244-
bool operator ==(Object object) {
245-
if (object is FunctionTypeImpl) {
246-
if (typeFormals.length != object.typeFormals.length) {
245+
bool operator ==(Object other) {
246+
if (identical(other, this)) {
247+
return true;
248+
}
249+
250+
if (other is FunctionTypeImpl) {
251+
if (NullSafetyUnderstandingFlag.isEnabled) {
252+
if (other.nullabilitySuffix != nullabilitySuffix) {
253+
return false;
254+
}
255+
}
256+
257+
if (other.typeFormals.length != typeFormals.length) {
247258
return false;
248259
}
249260
// `<T>T -> T` should be equal to `<U>U -> U`
250261
// To test this, we instantiate both types with the same (unique) type
251262
// variables, and see if the result is equal.
252263
if (typeFormals.isNotEmpty) {
253264
List<DartType> freshVariables = FunctionTypeImpl.relateTypeFormals(
254-
this, object, (t, s, _, __) => t == s);
265+
this, other, (t, s, _, __) => t == s);
255266
if (freshVariables == null) {
256267
return false;
257268
}
258-
return instantiate(freshVariables) ==
259-
object.instantiate(freshVariables);
269+
return instantiate(freshVariables) == other.instantiate(freshVariables);
260270
}
261271

262-
return returnType == object.returnType &&
263-
_equalParameters(parameters, object.parameters) &&
264-
nullabilitySuffix == object.nullabilitySuffix;
272+
return other.returnType == returnType &&
273+
_equalParameters(other.parameters, parameters);
265274
}
266275
return false;
267276
}
@@ -930,14 +939,18 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
930939
(element.library.session as AnalysisSessionImpl).inheritanceManager;
931940

932941
@override
933-
bool operator ==(Object object) {
934-
if (identical(object, this)) {
942+
bool operator ==(Object other) {
943+
if (identical(other, this)) {
935944
return true;
936945
}
937-
if (object is InterfaceTypeImpl) {
938-
return element == object.element &&
939-
TypeImpl.equalArrays(typeArguments, object.typeArguments) &&
940-
nullabilitySuffix == object.nullabilitySuffix;
946+
if (other is InterfaceTypeImpl) {
947+
if (NullSafetyUnderstandingFlag.isEnabled) {
948+
if (other.nullabilitySuffix != nullabilitySuffix) {
949+
return false;
950+
}
951+
}
952+
return other.element == element &&
953+
TypeImpl.equalArrays(other.typeArguments, typeArguments);
941954
}
942955
return false;
943956
}
@@ -1553,15 +1566,15 @@ class InterfaceTypeImpl extends TypeImpl implements InterfaceType {
15531566
for (DartType type in types) {
15541567
// If any existing type in the bucket is more specific than this type,
15551568
// then we can ignore this type.
1556-
if (bucket.any((DartType t) => typeSystem.isSubtypeOf(t, type))) {
1569+
if (bucket.any((DartType t) => typeSystem.isSubtypeOf2(t, type))) {
15571570
continue;
15581571
}
15591572
// Otherwise, we need to add this type to the bucket and remove any types
15601573
// that are less specific than it.
15611574
bool added = false;
15621575
int i = 0;
15631576
while (i < bucket.length) {
1564-
if (typeSystem.isSubtypeOf(type, bucket[i])) {
1577+
if (typeSystem.isSubtypeOf2(type, bucket[i])) {
15651578
if (added) {
15661579
if (i < bucket.length - 1) {
15671580
bucket[i] = bucket.removeLast();
@@ -2057,9 +2070,13 @@ class TypeParameterTypeImpl extends TypeImpl implements TypeParameterType {
20572070
return true;
20582071
}
20592072

2060-
if (other is TypeParameterTypeImpl &&
2061-
other.nullabilitySuffix == nullabilitySuffix &&
2062-
other.element == element) {
2073+
if (other is TypeParameterTypeImpl && other.element == element) {
2074+
if (NullSafetyUnderstandingFlag.isEnabled) {
2075+
if (other.nullabilitySuffix != nullabilitySuffix) {
2076+
return false;
2077+
}
2078+
}
2079+
20632080
// If the same declaration, or the same promoted element.
20642081
if (identical(other.element, element)) {
20652082
return true;

pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class AssignmentExpressionResolver {
293293
_inferenceHelper.recordStaticType(node, type);
294294

295295
var leftWriteType = _getStaticType2(node.leftHandSide);
296-
if (!_typeSystem.isAssignableTo(type, leftWriteType)) {
296+
if (!_typeSystem.isAssignableTo2(type, leftWriteType)) {
297297
_resolver.errorReporter.reportErrorForNode(
298298
StaticTypeWarningCode.INVALID_ASSIGNMENT,
299299
node.rightHandSide,

pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class ExtensionMemberResolver {
153153
if (receiverType.isVoid) {
154154
_errorReporter.reportErrorForNode(
155155
StaticWarningCode.USE_OF_VOID_RESULT, receiverExpression);
156-
} else if (!_typeSystem.isAssignableTo(receiverType, node.extendedType)) {
156+
} else if (!_typeSystem.isAssignableTo2(receiverType, node.extendedType)) {
157157
_errorReporter.reportErrorForNode(
158158
CompileTimeErrorCode.EXTENSION_OVERRIDE_ARGUMENT_NOT_ASSIGNABLE,
159159
receiverExpression,
@@ -212,7 +212,7 @@ class ExtensionMemberResolver {
212212
var boundType = typeParameters[i].bound;
213213
if (boundType != null) {
214214
boundType = substitution.substituteType(boundType);
215-
if (!_typeSystem.isSubtypeOf(argType, boundType)) {
215+
if (!_typeSystem.isSubtypeOf2(argType, boundType)) {
216216
_errorReporter.reportErrorForNode(
217217
CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
218218
typeArgumentList.arguments[i],
@@ -429,7 +429,7 @@ class ExtensionMemberResolver {
429429

430430
/// Ask the type system for a subtype check.
431431
bool _isSubtypeOf(DartType type1, DartType type2) =>
432-
_typeSystem.isSubtypeOf(type1, type2);
432+
_typeSystem.isSubtypeOf2(type1, type2);
433433

434434
List<DartType> _listOfDynamic(List<TypeParameterElement> parameters) {
435435
return List<DartType>.filled(parameters.length, _dynamicType);

pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class TypeSystemTypeOperations
317317

318318
@override
319319
bool isSubtypeOf(DartType leftType, DartType rightType) {
320-
return typeSystem.isSubtypeOf(leftType, rightType);
320+
return typeSystem.isSubtypeOf2(leftType, rightType);
321321
}
322322

323323
@override

0 commit comments

Comments
 (0)