Skip to content

#3057. Add more for-statement tests #3110

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

Merged
merged 3 commits into from
Mar 19, 2025
Merged

Conversation

sgrekhov
Copy link
Contributor

It turns out that flow analysis after break statement can be not intuitive :)

@eernstg
Copy link
Member

eernstg commented Mar 18, 2025

Oh, I thought the flow analysis was always clear as day! ;-D

Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky! ;-)

I've tried to comment on a number of cases where a test has a description which claims to be about a specific formula in the spec (in particular, the conservative join), but it doesn't really seem to test that formula. It's more natural to say that it is testing some other thing (e.g., that after(B) contains a specific setting of 'assigned' for a variable, or 'captured', etc.)

test1(int? n) {
if (n != null) { // n promoted to `int`
for (; () {
late int? i = (n = x); // n demoted to `int?`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think write-captured variables care about which type we're assigning to them: They can't be promoted at any location where the write-capture has occurred (that is: the function object exists, no matter whether we "know" that it has or hasn't been called). So x could be a plain int, and it would still prevent all promotion of n.

I think this implies that the test would be slightly stronger if x were actually of type int.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the flow analysis makes no attempt to prove that n = x is dead code because we never read the value of i. All we need is the fact that n = x occurs in the function literal, no matter where.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanx! x replaced by 42.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG!

/// - Let `before(U) = merge(after(S), continue(S))`.
/// - Let `after(N) = inheritTested(join(false(C), unsplit(break(S))), after(U))`
///
/// @description Checks that `before(U) = merge(after(S), continue(S))`. Test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to merge the flow models which is after(S) plus all the flow models from locations where S contains a statement of the form continue; or continue L; which is targeting this for statement. But then we should probably have at least one test that contains continue; or continue L;.

One thing which could be tested is that a variable which may be assigned before a continue; in the body is "not definitely unassigned" when it is used in U.

This test seems to be testing something which is quite different, and I'm actually not quite sure how I would describe it...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, looking at this one more time I think I get it: The break; is needed because this test will actually run (so it should not enter an infinite loop). There is no continue; statement , but this is a case that needs to work, just like the case where we do have a continue; statement.

Copy link
Contributor Author

@sgrekhov sgrekhov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Than you for the detailed review! Updated. PTAL.

test1(int? n) {
if (n != null) { // n promoted to `int`
for (; () {
late int? i = (n = x); // n demoted to `int?`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanx! x replaced by 42.

Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

test1(int? n) {
if (n != null) { // n promoted to `int`
for (; () {
late int? i = (n = x); // n demoted to `int?`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG!

/// - Let `before(U) = merge(after(S), continue(S))`.
/// - Let `after(N) = inheritTested(join(false(C), unsplit(break(S))), after(U))`
///
/// @description Checks that `before(U) = merge(after(S), continue(S))`. Test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, looking at this one more time I think I get it: The break; is needed because this test will actually run (so it should not enter an infinite loop). There is no continue; statement , but this is a case that needs to work, just like the case where we do have a continue; statement.

try {
for (;; i) { // Possibly assigned. https://github.com/dart-lang/sdk/issues/42232#issuecomment-690681385
continue;
i = 42;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good one because it shows that the analysis is somewhat lossy: We will actually never execute line 23, but the analysis still thinks that it might have been executed when we reach U.

Of course, developers would be expected to modify their code such that there is no dead code, which means that it isn't a big problem that the analysis can be made less useful by dead code.

@eernstg eernstg merged commit 6ba3ad8 into dart-lang:master Mar 19, 2025
2 checks passed
copybara-service bot pushed a commit to dart-lang/sdk that referenced this pull request Mar 21, 2025
2025-03-21 [email protected] dart-lang/co19#3057. Update expectations for `while(false) ...` test (dart-lang/co19#3114)
2025-03-19 [email protected] dart-lang/co19#3057. Add more for-statement tests (dart-lang/co19#3110)
2025-03-17 [email protected] dart-lang/co19#2119. Fix typos in descriptions (dart-lang/co19#3109)
2025-03-17 [email protected] dart-lang/co19#3057. Add `for(;;)` statements tests (dart-lang/co19#3108)
2025-03-14 [email protected] dart-lang/co19#3057. Add while statements tests and update assignments (dart-lang/co19#3107)
2025-03-14 [email protected] dart-lang/co19#3057. Add conditional statements tests (dart-lang/co19#3106)

Cq-Include-Trybots: luci.dart.try:analyzer-linux-release-try
Change-Id: I7edea518e4d853da39d2f1b78674ab6abeadccb7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417180
Reviewed-by: Alexander Thomas <[email protected]>
Commit-Queue: Erik Ernst <[email protected]>
Reviewed-by: Erik Ernst <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants