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:
Konstantin Shcheglov 2020-04-21 03:01:54 +00:00 committed by commit-bot@chromium.org
parent 7e4673f497
commit 61c8ac8b46
9 changed files with 93 additions and 19 deletions

View file

@ -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) {

View file

@ -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,

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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'.";

View file

@ -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++) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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;