Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking Change Request: normalize types and use mutual subtyping for type parameter bounds #40633

Closed
sigmundch opened this issue Feb 14, 2020 · 10 comments
Assignees
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). breaking-change-request This tracks requests for feedback on breaking changes NNBD Issues related to NNBD Release

Comments

@sigmundch
Copy link
Member

sigmundch commented Feb 14, 2020

Planned change

In preparation for null safety, we'd like to roll out several minor breaking changes. Two such changes are about types in the language. These changes are:

  • Types objects will now be normalized (see specification for details)
  • Subtyping of type bounds in function types will switch from using structural subtyping to use mutual subtyping.

Expected Impact

This affects equality of type objects and some subtyping checks, but the cases where we expect differences are pretty rare and small.

Most of the normalization rules are not visible to Dart programs today (most rules concern null safety features). Only only two rules are visible:

  • FutureOr<Object> normalizes to Object (similarly with other top types like dynamic and void)
  • FutureOr<Null> normalizes to Future<Null>

This change is visible when comparing or printing .runtimeType of objects involving those two FutureOr types, for example: <FutureOr<Object>>[].runtimeType == <Object>[].runtimeType will now be true.

For subtyping of bounds, this affects checks where the bounds were structurally different but would be subtypes of each other. Again we only expect this to affect cases where FutureOr<Null> or FutureOr<Object> were used within function type bounds.

For example, consider void Function<T extends List<Object>>() and void Function<T extends List<FutureOr<Object>>>(). With structural subtyping these two function types were not comparable, but with mutual subtyping they are both considered equivalent.

Note that there is a lot of overlap between these two breaking changes. With normalization we would start indicating that the two function types above would be subtypes using structural subtyping as well. We however introduce the concept of using mutual subtyping on the bounds to prepare for the future additions that come with null safety, where normalization will no longer be sufficient.

Mitigation

We think the most visible part of this change will be in tests that rely on using .runtimeType.toString() that also mention FutureOr<Null>, FutureOr<Object>, FutureOr<dynamic>, or FutureOr<void>. If those occur, then they will need to be updated to reflect the normalization changes.

@sigmundch sigmundch added area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). breaking-change-request This tracks requests for feedback on breaking changes labels Feb 14, 2020
@sigmundch
Copy link
Member Author

@franklinyow franklinyow added the NNBD Issues related to NNBD Release label Feb 14, 2020
@sigmundch
Copy link
Member Author

@Hixie @matanlurey - let us know if you foresee any issues with this for either flutter or angular.

@sigmundch
Copy link
Member Author

@lrhn
Copy link
Member

lrhn commented Feb 16, 2020

How will this affect the "await only futures" lint?
It seems reasonable to await FutureOr<Object>, but not to await Object.

@franklinyow
Copy link
Contributor

cc @Hixie @matanlurey @dgrove @vsmenon for review and approval.

@Hixie
Copy link
Contributor

Hixie commented Feb 22, 2020

I am indifferent to things affecting FutureOr<>, it's my assumption that eventually we will be removing FutureOr from the language, adding type unions/intersections, and redefining FutureOr as:

typedef FutureOr<T> = Future<T> | T;

So long as what we're doing here is consistent with doing that on the long term, I'm happy.

@vsmenon
Copy link
Member

vsmenon commented Feb 24, 2020

lgtm

@matanlurey
Copy link
Contributor

LGTM

@franklinyow
Copy link
Contributor

Approved

dart-bot pushed a commit that referenced this issue Feb 27, 2020
Back-porting a breaking change for Null Safety to the existing SDK to
help front load any issues (we expect them to be minimal).
#40633

We are now allowing the type argument bounds in generic function
subtypes to differ if the bounds are mutual subtypes.

Change-Id: I2b093c08f772a1b866dbe1324b605e6143d3a0b4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/137289
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
@sigmundch
Copy link
Member Author

Changes implementing this in the VM, dart2js, and DDC have now landed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). breaking-change-request This tracks requests for feedback on breaking changes NNBD Issues related to NNBD Release
Projects
None yet
Development

No branches or pull requests

6 participants