-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Establish documentation links for non-promotion reasons #44900
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
I'm not sure who to assign this to: perhaps @munificent or @kwalrath? |
This reminds me of diagnostic messages, which are written by the engineer and are in the SDK repo, but are edited by the writing team and published on dart.dev. For details, ask @bwilkerson and check out the comments at the top of https://github.com/dart-lang/site-www/blob/master/src/tools/diagnostic-messages.md. |
The expectation is that someone will write a separate document describing promotion or at least the reasons why is sometimes fails. I believe that's because we expect the size of the document to be unwieldy for the diagnostic messages page. My hope is that the diagnostic will include a link to the diagnostic messages entry, not to the promotion page directly, and that we can link to the promotion page from the appropriate diagnostic messages (there will be several of them) for users that need more information than is available from the diagnostic messages page. We might need slightly different support for the CFE-based tools than for analyzer, because the diagnostics they produce in these cases might not be a 1-to-1 match to what analyzer is producing, and in either case, analyzer needs to keep the URL(s) separate from the messages so that clients can treat them as links. |
It's easy enough for us to provide a go link and/or page as a target for these explanations, but writing up the explanations in a finished way is going to take some time. (Maybe @munificent will have that time sooner than I will?) |
@munificent @kwalrath It's probably time to start working on this. At a minimum we need to figure out what the link URL should be explaining about the lack of field promotion, so that I can commit it prior to the stable branch cut (which I believe is scheduled for Apr 6). We don't necessarily have to have the content ready until the release happens. In the long term we'll want other pages (or other sections of the same page) to explain about other reasons why promotion might fail (e.g. the variable was written to between test and usage, or the variable is written to in a local function), but it's not certain how many of those reasons will be supported by the branch cut date. We should probably decide on the link URLs for all these cases now, so that I can add support as I can before the branch cut. I can get y'all a complete list of the possible type promotion failure reasons in the next day or two. |
I'd prefer to have one page rather than many, to enable in-page search and keep the website URLs manageable. If this doc lives on dart.dev, the URL would probably be something like If you expect people to type in these URLs, then we could provide one or more go links, as well. They could be either to the page or to sections within the page. E.g. we could use one of the following schemes:
|
Could you also get a list of the analyzer diagnostics with which these context messages are associated? After we've settled on a URL for this documentation I'd like to add a link to it from the documentation for each of those diagnostics. |
Ok, here's all the reasons I'm aware of why type promotion might fail. This is kind of a brain dump so sorry for the length. Note that these examples are all pretty contrived; hopefully someone can dig around some successfully migrated code for more realistic examples :) Probably not all of these are worth writing about. But I think it would be good to reserve a "go" link for each of them, so that I can point the error messages to those links as I implement them. Initially all the "go" links could resolve to a common "catch all" article about lack of type promotion, and then we could change them if/when we discover that we have more to say about a particular case.
class C {
int? i;
f() {
if (i != null) return;
print(i.isEven); // ERROR
}
} Note that it's an instance field in this example, but it could also be an instance getter, or a static field or getter, or a top level variable or getter. Also we don't allow promoting
f(bool b, int? i, int? j) {
if (i != null) return;
if (b) {
i = j; // (1)
}
if (!b) {
print(i.isEven); // (2) ERROR
}
} In this example, when flow analysis hits (1), it demotes f(bool b, int? i, int? j) {
if (i != null) return;
if (b) {
i = j;
} else {
print(i.isEven);
}
} Note that in straight-line control flow cases like these (no loops), flow analysis takes into account the right hand side of the assignment in order to decide whether to demote. That means the user could also fix this by changing the type of
f(Link? p) {
if (p != null) return;
while (true) { // (1)
print(p.value); // ERROR
var next = p.next;
if (next == null) break;
p = next; // (2)
}
} What's happening here is that when flow analysis reaches (1), it looks ahead and sees the write to f(Link? p) {
while (p != null) {
print(p.value);
p = p.next;
}
} This situation can also arise in switch statements if there's a case block that has a label, because the user can use labeled switch statements to construct loops, e.g.: f(int i, int? j, int? k) {
if (j == null) return;
switch (i) {
label:
case 0:
print(j.isEven);
j = k;
continue label;
}
}
f(int? i, int? j) {
if (i == null) return;
try {
i = j; // (1)
if (i == null) return; // (2)
} catch (...) {
print(i.isEven); // ERROR
}
} In this case, flow analysis doesn't consider Similar situations can occur between Because of a historical artifact of how the implementation was done, these try/catch/finally situations don't take into account the right-hand side of the assignment, similar to what happens in loops.
f(Object o) {
if (o is Comparable) { // (1)
if (o is Pattern) { // (2)
print(o.matchAsPrefix('foo')); // (3) ERROR
}
}
} In this example,
f(int? i, int? j) {
if (i == null) return;
var foo = () {
i = j;
};
print(i.isEven); // ERROR
} Flow analysis reasons that as soon as the definition of
f(int? i, int? j) {
if (i == null) return;
var foo = () {
print(i.isEven); // ERROR
};
i = j; // (1)
} Flow analysis reasons that there is no way of telling when f(int? i) {
i ??= 0;
var foo = () {
print(i.isEven); // ERROR
};
} In this case, a human reader can see that the promotion is safe because the only write to
f(int? i, int? j) {
var foo = () {
if (i == null) return;
print(i.isEven); // ERROR
};
var bar = () {
i = j;
};
} Flow analysis reasons that there's no way of telling what order |
@bwilkerson Ok, I can do that, but it will be an ever-expanding list as I work on the "why not promoted" logic. Is it ok if I wait until after the branch cut so that I can give you a definitive list? |
The documentation can be updated after the branch point, so yes. The other option is to tell me where to start looking for uses and I can do the searching myself. |
Sure, just look for any uses of |
@kwalrath Distilling down my colossal comment above, how about we reserve these go links for now:
|
These links are not live yet, but they will be filled in with documentation explaining subtleties of type promotion in more detail. Bug: #44900 Change-Id: I3e1865597fc5c56495a8108c8a4de98cab0c3e4b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193749 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
@kwalrath as of c25e748, the analyzer and CFE are now making use of the following links:
So we will definitely need these links to go somewhere useful by the time we ship stable. I'll keep you posted if I implement enough functionality to require any of the other links. |
I'd like to have redirects for those URLs (even if it's just a placeholder page) by the time this change gets to dev channel. |
FYI, the change will land in dev version |
@bwilkerson I believe this is a complete list of the analyzer diagnostics with which the analyzer is capable of associating these context messages, as of 94162a2 (the beta branch for the 2.13 stable release):
|
@stereotype441 & @bwilkerson, I'm about to start editing the page to add Paul's content. Please let me know if there's anything I should be aware of before I get too deep into this. |
Hey! I have the following use-case: class C {
final int? i; // (1)
C(this.i);
f() {
if (i != null) {
i.isEven; // (2)
}
}
} The instance field From the comment on a similar example,
Is this the reason why NVM, found #1188 |
Check out field-promotion. The core point is that it is unsound to promote a an instance variable because evaluation of an instance variable amounts to execution of a getter (which is generated implicitly for each instance variable), and that getter can be overridden: class A {
final int? i;
void foo() {
if (i != null) i.isEven; // Not safe!
}
A(this.i);
}
class B implements A {
get i => Random().nextBool() ? 1 : null;
} |
(Parent issue #44897)
We are currently in the process of adding functionality to the CFE and analyzer to explain type promotion failures to the user. For example in the following code:
the error message generated at (2) will contain a context message (pointing to (1)) explaining that
i
couldn't be promoted because it's a field.We'll have other context messages for other non-promotion reasons (e.g. the variable has been written to since it was promoted, the variable is write captured, etc.)
It would be nice if these context messages could contain links to documentation pages explaining the reasons for the type promotion failures in more detail. This is particularly important for field non-promotion, since it's a common user trap.
Note that this bug covers both writing the documentation and linking it up to the context messages.
The text was updated successfully, but these errors were encountered: