mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
Issue 40088. Promote to NonNull of the declared type on assignment.
Bug: https://github.com/dart-lang/sdk/issues/40088 Change-Id: Icc2c720e24c2e6a85994497cd12bf34957d99d4d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144040 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
7e4673f497
commit
61c8ac8b46
9 changed files with 93 additions and 19 deletions
|
@ -1423,8 +1423,9 @@ class FlowModel<Variable, Type> {
|
|||
VariableModel<Type> infoForVar = variableInfo[variable];
|
||||
if (infoForVar == null) return this;
|
||||
|
||||
Type declaredType = typeOperations.variableType(variable);
|
||||
VariableModel<Type> newInfoForVar =
|
||||
infoForVar.write(writtenType, typeOperations);
|
||||
infoForVar.write(declaredType, writtenType, typeOperations);
|
||||
if (identical(newInfoForVar, infoForVar)) return this;
|
||||
|
||||
return _updateVariableInfo(variable, newInfoForVar);
|
||||
|
@ -1756,8 +1757,8 @@ class VariableModel<Type> {
|
|||
|
||||
/// Returns a new [VariableModel] reflecting the fact that the variable was
|
||||
/// just written to.
|
||||
VariableModel<Type> write(
|
||||
Type writtenType, TypeOperations<Object, Type> typeOperations) {
|
||||
VariableModel<Type> write(Type declaredType, Type writtenType,
|
||||
TypeOperations<Object, Type> typeOperations) {
|
||||
List<Type> newPromotedTypes;
|
||||
if (promotedTypes == null) {
|
||||
newPromotedTypes = null;
|
||||
|
@ -1779,7 +1780,7 @@ class VariableModel<Type> {
|
|||
}
|
||||
}
|
||||
newPromotedTypes = _tryPromoteToTypeOfInterest(
|
||||
typeOperations, newPromotedTypes, writtenType);
|
||||
typeOperations, declaredType, newPromotedTypes, writtenType);
|
||||
if (identical(promotedTypes, newPromotedTypes) && assigned) return this;
|
||||
List<Type> newTested;
|
||||
if (newPromotedTypes == null && promotedTypes != null) {
|
||||
|
@ -1806,8 +1807,14 @@ class VariableModel<Type> {
|
|||
/// is required, a new promotion chain will be created and returned.
|
||||
List<Type> _tryPromoteToTypeOfInterest(
|
||||
TypeOperations<Object, Type> typeOperations,
|
||||
Type declaredType,
|
||||
List<Type> promotedTypes,
|
||||
Type writtenType) {
|
||||
if (writeCaptured) {
|
||||
assert(promotedTypes == null);
|
||||
return promotedTypes;
|
||||
}
|
||||
|
||||
// Figure out if we have any promotion candidates (types that are a
|
||||
// supertype of writtenType and a proper subtype of the currently-promoted
|
||||
// type). If at any point we find an exact match, we take it immediately.
|
||||
|
@ -1849,6 +1856,16 @@ class VariableModel<Type> {
|
|||
}
|
||||
}
|
||||
|
||||
// The declared type is always a type of interest, but we never promote
|
||||
// to the declared type. So, try NonNull of it.
|
||||
Type declaredTypeNonNull = typeOperations.promoteToNonNull(declaredType);
|
||||
if (!typeOperations.isSameType(declaredTypeNonNull, declaredType)) {
|
||||
handleTypeOfInterest(declaredTypeNonNull);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < tested.length; i++) {
|
||||
Type type = tested[i];
|
||||
|
||||
|
@ -1857,7 +1874,7 @@ class VariableModel<Type> {
|
|||
return result;
|
||||
}
|
||||
|
||||
var typeNonNull = typeOperations.promoteToNonNull(type);
|
||||
Type typeNonNull = typeOperations.promoteToNonNull(type);
|
||||
if (!typeOperations.isSameType(typeNonNull, type)) {
|
||||
handleTypeOfInterest(typeNonNull);
|
||||
if (result != null) {
|
||||
|
|
|
@ -1966,6 +1966,42 @@ main() {
|
|||
});
|
||||
|
||||
group('Promotes to NonNull of a type of interest', () {
|
||||
test('when declared type', () {
|
||||
var h = _Harness();
|
||||
var x = _Var('x', _Type('int?'));
|
||||
|
||||
var s1 = FlowModel<_Var, _Type>(true).declare(x, true);
|
||||
expect(s1.variableInfo, {
|
||||
x: _matchVariableModel(chain: null),
|
||||
});
|
||||
|
||||
var s2 = s1.write(x, _Type('int'), h);
|
||||
expect(s2.variableInfo, {
|
||||
x: _matchVariableModel(chain: ['int']),
|
||||
});
|
||||
});
|
||||
|
||||
test('when declared type, if write-captured', () {
|
||||
var h = _Harness();
|
||||
var x = h.addVar('x', 'int?');
|
||||
|
||||
var s1 = FlowModel<_Var, _Type>(true).declare(x, true);
|
||||
expect(s1.variableInfo, {
|
||||
x: _matchVariableModel(chain: null),
|
||||
});
|
||||
|
||||
var s2 = s1.removePromotedAll([], [x]);
|
||||
expect(s2.variableInfo, {
|
||||
x: _matchVariableModel(chain: null, writeCaptured: true),
|
||||
});
|
||||
|
||||
// 'x' is write-captured, so not promoted
|
||||
var s3 = s2.write(x, _Type('int'), h);
|
||||
expect(s3.variableInfo, {
|
||||
x: _matchVariableModel(chain: null, writeCaptured: true),
|
||||
});
|
||||
});
|
||||
|
||||
test('when promoted', () {
|
||||
var h = _Harness();
|
||||
var s1 = FlowModel<_Var, _Type>(true)
|
||||
|
@ -2053,19 +2089,27 @@ main() {
|
|||
// class A {}
|
||||
// class B extends A {}
|
||||
// class C extends B {}
|
||||
h.addSubtype(_Type('Object'), _Type('A'), false);
|
||||
h.addSubtype(_Type('Object'), _Type('A?'), false);
|
||||
h.addSubtype(_Type('Object'), _Type('B?'), false);
|
||||
h.addSubtype(_Type('A'), _Type('Object'), true);
|
||||
h.addSubtype(_Type('A'), _Type('Object?'), true);
|
||||
h.addSubtype(_Type('A'), _Type('A?'), true);
|
||||
h.addSubtype(_Type('A'), _Type('B?'), false);
|
||||
h.addSubtype(_Type('A?'), _Type('Object'), false);
|
||||
h.addSubtype(_Type('A?'), _Type('Object?'), true);
|
||||
h.addSubtype(_Type('A?'), _Type('A'), false);
|
||||
h.addSubtype(_Type('A?'), _Type('B?'), false);
|
||||
h.addSubtype(_Type('B'), _Type('Object'), true);
|
||||
h.addSubtype(_Type('B'), _Type('A'), true);
|
||||
h.addSubtype(_Type('B'), _Type('A?'), true);
|
||||
h.addSubtype(_Type('B'), _Type('B?'), true);
|
||||
h.addSubtype(_Type('B?'), _Type('Object'), false);
|
||||
h.addSubtype(_Type('B?'), _Type('Object?'), true);
|
||||
h.addSubtype(_Type('B?'), _Type('A'), false);
|
||||
h.addSubtype(_Type('B?'), _Type('A?'), true);
|
||||
h.addSubtype(_Type('B?'), _Type('B'), false);
|
||||
h.addSubtype(_Type('C'), _Type('Object'), true);
|
||||
h.addSubtype(_Type('C'), _Type('A'), true);
|
||||
h.addSubtype(_Type('C'), _Type('A?'), true);
|
||||
h.addSubtype(_Type('C'), _Type('B'), true);
|
||||
|
@ -2891,7 +2935,7 @@ main() {
|
|||
test('assigned', () {
|
||||
var h = _Harness();
|
||||
var intQModel = model([intQType]);
|
||||
var writtenModel = intQModel.write(_Type('Object?'), h);
|
||||
var writtenModel = intQModel.write(intQType, _Type('Object?'), h);
|
||||
var p1 = {x: writtenModel, y: writtenModel, z: intQModel, w: intQModel};
|
||||
var p2 = {x: writtenModel, y: intQModel, z: writtenModel, w: intQModel};
|
||||
var joined = FlowModel.joinVariableInfo(h, p1, p2);
|
||||
|
@ -3052,6 +3096,7 @@ class _Harness implements TypeOperations<_Var, _Type> {
|
|||
'int? <: int': false,
|
||||
'int? <: num': false,
|
||||
'int? <: num?': true,
|
||||
'int? <: Object': false,
|
||||
'int? <: Object?': true,
|
||||
'num <: int': false,
|
||||
'num <: Iterable': false,
|
||||
|
@ -3063,9 +3108,11 @@ class _Harness implements TypeOperations<_Var, _Type> {
|
|||
'num? <: int?': false,
|
||||
'num? <: num': false,
|
||||
'num? <: num*': true,
|
||||
'num? <: Object': false,
|
||||
'num? <: Object?': true,
|
||||
'num* <: num': true,
|
||||
'num* <: num?': true,
|
||||
'num* <: Object': true,
|
||||
'num* <: Object?': true,
|
||||
'Iterable <: int': false,
|
||||
'Iterable <: num': false,
|
||||
|
@ -3075,9 +3122,12 @@ class _Harness implements TypeOperations<_Var, _Type> {
|
|||
'List <: Iterable': true,
|
||||
'List <: Object': true,
|
||||
'Object <: int': false,
|
||||
'Object <: int?': false,
|
||||
'Object <: List': false,
|
||||
'Object <: num': false,
|
||||
'Object <: num?': false,
|
||||
'Object <: Object?': true,
|
||||
'Object? <: Object': false,
|
||||
'Object? <: int': false,
|
||||
'Object? <: int?': false,
|
||||
'String <: int': false,
|
||||
|
|
|
@ -41,9 +41,14 @@ typeOfInterest_isNot_nullable(Object? x) {
|
|||
x;
|
||||
}
|
||||
|
||||
notATypeOfInterest_nullability(Object? x) {
|
||||
typeOfInterest_declaredNullable_exact(int? x) {
|
||||
x = 1;
|
||||
x;
|
||||
/*int*/ x;
|
||||
}
|
||||
|
||||
typeOfInterest_declaredNullable_subtype(Object? x) {
|
||||
x = 1;
|
||||
/*Object*/ x;
|
||||
}
|
||||
|
||||
typeOfInterest_notEqualNull(Object? x) {
|
||||
|
|
|
@ -29,7 +29,8 @@ forInInt(List<int> list) {
|
|||
for (var e in
|
||||
/*cfe.List<int>*/
|
||||
/*cfe:nnbd.List<int!>!*/ list) {
|
||||
/*int*/
|
||||
/*cfe.int*/
|
||||
/*cfe:nnbd.int!*/
|
||||
e;
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +73,8 @@ asyncForInInt(Stream<int> stream) async {
|
|||
/*cfe.Stream<int>*/
|
||||
/*cfe:nnbd.Stream<int!>!*/
|
||||
stream) {
|
||||
/*int*/
|
||||
/*cfe.int*/
|
||||
/*cfe:nnbd.int!*/
|
||||
e;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ String? _argumentErrors(FunctionType type, List actuals, namedActuals) {
|
|||
var requiredNamed = type.requiredNamed;
|
||||
if (namedActuals != null) {
|
||||
names = getOwnPropertyNames(namedActuals);
|
||||
for (var name in names!) {
|
||||
for (var name in names) {
|
||||
if (!JS<bool>('!', '(#.hasOwnProperty(#) || #.hasOwnProperty(#))', named,
|
||||
name, requiredNamed, name)) {
|
||||
return "Dynamic call with unexpected named argument '$name'.";
|
||||
|
|
|
@ -2898,7 +2898,7 @@ bool _isInterfaceSubtype(
|
|||
if (JS_GET_FLAG("VARIANCE")) {
|
||||
sVariances = _Universe.findTypeParameterVariances(universe, sName);
|
||||
hasVariances = sVariances != null;
|
||||
assert(!hasVariances! || length == _Utils.arrayLength(sVariances));
|
||||
assert(!hasVariances || length == _Utils.arrayLength(sVariances));
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
|
|
|
@ -206,7 +206,7 @@ class _NativeSynchronousSocket extends _NativeSynchronousSocketNativeWrapper {
|
|||
if (end == start) {
|
||||
return 0;
|
||||
}
|
||||
var result = _nativeReadInto(buffer, start, end! - start);
|
||||
var result = _nativeReadInto(buffer, start, end - start);
|
||||
if (result is OSError) {
|
||||
throw new SocketException("readIntoSync failed", osError: result);
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ class _NativeSynchronousSocket extends _NativeSynchronousSocketNativeWrapper {
|
|||
return;
|
||||
}
|
||||
_BufferAndStart bufferAndStart =
|
||||
_ensureFastAndSerializableByteData(buffer, start, end!);
|
||||
_ensureFastAndSerializableByteData(buffer, start, end);
|
||||
var result = _nativeWrite(bufferAndStart.buffer, bufferAndStart.start,
|
||||
end - (start - bufferAndStart.start));
|
||||
if (result is OSError) {
|
||||
|
|
|
@ -770,7 +770,7 @@ class _RandomAccessFile implements RandomAccessFile {
|
|||
if (end == start) {
|
||||
return new Future.value(0);
|
||||
}
|
||||
int length = end! - start;
|
||||
int length = end - start;
|
||||
return _dispatch(_IOService.fileReadInto, [null, length]).then((response) {
|
||||
if (_isErrorResponse(response)) {
|
||||
throw _exceptionFromResponse(response, "readInto failed", path);
|
||||
|
@ -791,7 +791,7 @@ class _RandomAccessFile implements RandomAccessFile {
|
|||
if (end == start) {
|
||||
return 0;
|
||||
}
|
||||
var result = _ops.readInto(buffer, start, end!);
|
||||
var result = _ops.readInto(buffer, start, end);
|
||||
if (result is OSError) {
|
||||
throw new FileSystemException("readInto failed", path, result);
|
||||
}
|
||||
|
@ -834,7 +834,7 @@ class _RandomAccessFile implements RandomAccessFile {
|
|||
}
|
||||
_BufferAndStart result;
|
||||
try {
|
||||
result = _ensureFastAndSerializableByteData(buffer, start, end!);
|
||||
result = _ensureFastAndSerializableByteData(buffer, start, end);
|
||||
} catch (e) {
|
||||
return new Future.error(e);
|
||||
}
|
||||
|
@ -863,7 +863,7 @@ class _RandomAccessFile implements RandomAccessFile {
|
|||
return;
|
||||
}
|
||||
_BufferAndStart bufferAndStart =
|
||||
_ensureFastAndSerializableByteData(buffer, start, end!);
|
||||
_ensureFastAndSerializableByteData(buffer, start, end);
|
||||
var result = _ops.writeFrom(bufferAndStart.buffer, bufferAndStart.start,
|
||||
end - (start - bufferAndStart.start));
|
||||
if (result is OSError) {
|
||||
|
|
|
@ -32,7 +32,7 @@ testReadWrite(key, value, check,
|
|||
db = await html.window.indexedDB!
|
||||
.open(dbName, version: version, onUpgradeNeeded: createObjectStore);
|
||||
|
||||
idb.Transaction transaction = db!.transactionList([storeName], 'readwrite');
|
||||
idb.Transaction transaction = db.transactionList([storeName], 'readwrite');
|
||||
transaction.objectStore(storeName).put(value, key);
|
||||
|
||||
db = await transaction.completed;
|
||||
|
|
Loading…
Reference in a new issue