-
Notifications
You must be signed in to change notification settings - Fork 213
Legacy code interaction with Records #2426
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
Comments
An aspect of legacy code is the lack of sound null safety which causes an asymmetry between Does this asymmetry push down into structural type checks on records dynamic r = (null, null);
r as (int, int); // throws or accepts? This test will most likely occur in parametric covariance checks, like that for the argument of List<(int,int)> spans = [];
void addSpan(int start, int end) { spans.add((start, end)); } For dart2js and DDC
If I hope we can make
|
I'd generally say that we should be pushing down casts. An That said, I'd still be fine with not having backwards compatible unsound checks, though. It's fine to reject I'd also expect per-field implicit downcasts, so Or, to be precise, I expect the assignment Could your testing stubs on the representation of a record type recursively call the testing stubs of the field types on the field values of the incoming record (if it is a record, otherwise it's definitely rejected)? Or would you want it to do The record won't be carrying around type information separate from the objects anyway, so you can't check the type of a record without checking the types of its elements. (Well, it can carry extra information around, but that could be unnecessarily expensive.) |
So should the error message say
instead of
?
We can't rely on one general 'recursive' stub because it will be too inefficient. There is a lot of polymorphism at the JavaScript level for a general stub.
Yes. I want a specialized
that is possibly further specialized for some cases, e.g.
et cetera. The If we must push down 'legacy-
or (2) have another stub that is almost like I really want to avoid having to do all this specialization twice. |
I'd be fine with the
message, if it's easier to achieve. It might be harder to point to a specific place in the code. If you have type 'bool' is not a subtype of type 'int' of `pair.$1` (line, column of "pair") I think it's readable enough. Also fine with not pushing down legacy- |
It might depend on the textual representation of the record type. If it's a |
Nooo. While the mechanics of the subtype testing might be to recurse into the fields, from the user's perspective, a record type is a type, and not a transparent bag of types that can be piecewise compared to other transparent bags of types.
This is another very good reason not to show the error only as a piecewise failure. I do think it might be nice to report the error like:
|
Proposed specification here. |
Completed by #2538. |
tl;dr: I don't think there is any problem with this, it should just work. Writing this to give a place to write concerns, if we come up with some.
If we introduce records in Dart 2.x.0, existing pre-2.x.0 code will may up interacting with record types and record values.
(Some of it pre-2.12.0 code too.)
What we've done with type system changes so far (aka: the null safety update) is to have a shared type system that both legacy and new code interacts with, but the legacy code has none of the new syntaxes available.
Legacy code seeing record values or types.
Record values can flow into legacy code typed as
Object
.Opaque record types can flow into legacy code as type parameters, or type arguments of instances of generic classes (like
<(int, int)>
orList<(int, int)>
. The static type of those in the legacy code will likely not contain record type.Concrete record types can be introduced in legacy code by referencing type aliases from new code, or by having record types be filled in by type inference for legacy code referencing new code. (At the extreme, a legacy class extending a new-code class can inherit the parameters and return type of a method, which can contain record types.)
All of these should probably just work.
Since we make
Record
a subtype ofObject
, the legacy code won't need to do anything special, just treat the record types and objects as types and instances of types declared in other libraries that it hasn't seen.(It actually has seen at least one of them, since
Record
will be indart:core
, unless we hide that declaration from pre 2.x.0 code. We probably don't need to hide it.)There probably shouldn't be any problems with that. Legacy code treats the record types as any other foreign type.
Member access
I have suggested that member access on records could be "extension getter-like" so that it doesn't work with dynamic invocations. (Also means you cannot pass
(x: 1, y: 2)
to something which expects aPoint
, but does dynamic member access instead of typed member access.)If we do that (EDIT: We don't! Members are normal instance getters), a legacy library can only destructure a record using typed access to field getters,
r.$0
andr.fieldName
, since it cannot use pattern matching, cannot cast the record to a static record type (unless it uses a type alias from another library), and cannot access field getters, even through dynamic invocations.If member access uses normal virtual methods, a dynamic invocation of
(o as dynamic).$0
or(o as dynamic).fieldName
would also be able to destructure the record.Legacy code deliberately using record types, by using type aliases from new-code libraries, is probably going to be rare. Might as well just migrate. If the code is pre-2.12 too, you should really prioritize null safety migration over using records.
I don't think legacy code not being able to interact with records, other than by shuffling them around, is a problem. Might even be seen as an advantage, and any legacy code that actually works on concrete record types may be worth warning about.
(At least upgrade the SDK min requirement, and add a
//@dart=2.x-1
marker, since the code definitely depends on a 2.x.0 SDK.)The text was updated successfully, but these errors were encountered: