dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.09k stars 1.56k forks source link

Establish documentation links for non-promotion reasons #44900

Open stereotype441 opened 3 years ago

stereotype441 commented 3 years ago

(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:

class C {
  int? i;                  // (1)
  f() {
    if (i == null) return;
    i.isEven;              // (2)
  }
}

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.

stereotype441 commented 3 years ago

I'm not sure who to assign this to: perhaps @munificent or @kwalrath?

kwalrath commented 3 years ago

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.

bwilkerson commented 3 years ago

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.

kwalrath commented 3 years ago

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?)

stereotype441 commented 3 years ago

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

kwalrath commented 3 years ago

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 dart.dev/tools/<filename>, where <filename> might be non-promotion-reasons. (This is parallel to https://dart.dev/tools/diagnostic-messages.)

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:

bwilkerson commented 3 years ago

I can get y'all a complete list of the possible type promotion failure reasons in the next day or two.

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.

stereotype441 commented 3 years ago

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.

Similar situations can occur between try and finally blocks, and between catch and finally blocks.

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.

stereotype441 commented 3 years ago

I can get y'all a complete list of the possible type promotion failure reasons in the next day or two.

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.

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

bwilkerson commented 3 years ago

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.

stereotype441 commented 3 years ago

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 ResolverVisitor.computeWhyNotPromotedMessages-- the return value should always be passed fairly promptly to an ErrorReporter method, so it should be fairly easy to see which error codes might be affected.

stereotype441 commented 3 years ago

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 dart.dev/tools/<filename>, where <filename> might be non-promotion-reasons. (This is parallel to https://dart.dev/tools/diagnostic-messages.)

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:

  • dart.dev/go/non-promotion
  • dart.dev/go/non-promo, dart.dev/go/non-promo-field, dart.dev/go/non-promo-write-captured, ...
  • dart.dev/go/non-promo, dart.dev/go/non-promo-f, dart.dev/go/non-promo-wc, ...
  • dart.dev/go/non-promo, dart.dev/go/non-promo-1, dart.dev/go/non-promo-2, ...

@kwalrath Distilling down my colossal comment above, how about we reserve these go links for now:

stereotype441 commented 3 years ago

@kwalrath as of c25e74852c09e1498c12fe2ce1f212efebe9f0c6, 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.

kwalrath commented 3 years ago

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.

stereotype441 commented 3 years ago

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 2.13.0-192.0.dev.

stereotype441 commented 3 years ago

I can get y'all a complete list of the possible type promotion failure reasons in the next day or two.

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.

@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 94162a28126443d4c3f1a7c9267ec1d69d38b330 (the beta branch for the 2.13 stable release):

kwalrath commented 3 years ago

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

happy-san commented 3 years ago

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 i is declared final. So I expected it to be promoted at (2).

From the comment on a similar example,

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 this (even though it would be sound to do so; there were some technical reasons that this would have been difficult and we didn't think it would be worth it)

Is this the reason why i is not promoted in this case?

NVM, found #1188

eernstg commented 3 years ago

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