fix #26965, allow promotion from type param upper bound in strong mode

Also updates the changelog with this, and adds notes on a few other missing strong mode features.

R=leafp@google.com

Review URL: https://codereview.chromium.org/2214833002 .
This commit is contained in:
John Messerly 2016-08-05 08:51:44 -07:00
parent cf962d4cc7
commit efe9f1140d
3 changed files with 81 additions and 4 deletions

View file

@ -7,9 +7,19 @@
on Mac. Was already non-blocking on all other platforms.
* Report a better error when a bind fails because of a bad source address.
### Analyzer
### Strong Mode
* Strong mode breaking change - infer generic type arguments from the
* New feature - an option to disable implicit casts
(SDK issue [26583](https://github.com/dart-lang/sdk/issues/26583)),
see the [documentation](https://github.com/dart-lang/dev_compiler/blob/master/doc/STATIC_SAFETY.md#disable-implicit-casts)
for usage instructions and examples.
* New feature - an option to disable implicit dynamic
(SDK issue [25573](https://github.com/dart-lang/sdk/issues/25573)),
see the [documentation](https://github.com/dart-lang/dev_compiler/blob/master/doc/STATIC_SAFETY.md#disable-implicit-dynamic)
for usage instructions and examples.
* Breaking change - infer generic type arguments from the
constructor invocation arguments
(SDK issue [25220](https://github.com/dart-lang/sdk/issues/25220))
@ -20,7 +30,7 @@
var otherMap = new Map.from(map);
```
* Strong mode breaking change - infer local function return type
* Breaking change - infer local function return type
(SDK issue [26414](https://github.com/dart-lang/sdk/issues/26414))
```dart
@ -32,6 +42,19 @@
}
```
* Breaking change - allow type promotion from a generic type parameter
(SDK issue [26414](https://github.com/dart-lang/sdk/issues/26965))
```dart
void fn/*<T>*/(/*=T*/ object) {
if (object is String) {
// Treat `object` as `String` inside this block.
// But it will require a cast to pass it to something that expects `T`.
print(object.substring(1));
}
}
```
### Tool Changes
* `dartfmt` - upgraded to v0.2.9

View file

@ -45,7 +45,38 @@ class StrongTypeSystemImpl extends TypeSystem {
}
@override
bool canPromoteToType(DartType to, DartType from) => isSubtypeOf(to, from);
bool canPromoteToType(DartType to, DartType from) {
// Allow promoting to a subtype, for example:
//
// f(Base b) {
// if (b is SubTypeOfBase) {
// // promote `b` to SubTypeOfBase for this block
// }
// }
//
// This allows the variable to be used wherever the supertype (here `Base`)
// is expected, while gaining a more precise type.
if (isSubtypeOf(to, from)) {
return true;
}
// For a type parameter `T extends U`, allow promoting from the upper bound
// `U` to `S` where `S <: U`.
//
// This does restrict the variable, because `S </: T`, it can no longer be
// used as a `T` without another cast.
//
// However the members you could access from a variable of type `T`, were
// already those on the upper bound `U`. So all members on `U` will be
// accessible, as well as those on `S`. Pragmatically this feels like a
// useful enough trade-off to allow promotion.
//
// (In general we would need union types to support this feature precisely.)
if (from is TypeParameterType) {
return isSubtypeOf(to, from.resolveToBound(DynamicTypeImpl.instance));
}
return false;
}
@override
FunctionType functionTypeToConcreteType(

View file

@ -3422,6 +3422,29 @@ g() {
''');
}
void test_typePromotionFromTypeParameter() {
// Regression test for https://github.com/dart-lang/sdk/issues/26965
checkFile(r'''
void f/*<T>*/(/*=T*/ object) {
if (object is String) print(object.substring(1));
}
void g/*<T extends num>*/(/*=T*/ object) {
if (object is int) print(object.isEven);
if (object is String) print(/*info:DYNAMIC_INVOKE*/object.substring(1));
}
class Clonable<T> {}
class SubClonable<T> extends Clonable<T> {
T m(T t) => t;
}
void h/*<T extends Clonable<T>>*/(/*=T*/ object) {
if (/*info:NON_GROUND_TYPE_CHECK_INFO*/object is SubClonable/*<T>*/) {
// Note we need to cast back to T, because promotion lost that type info.
print(object.m(object as dynamic/*=T*/));
}
}
''');
}
void test_typeSubtyping_assigningClass() {
checkFile('''
class A {}