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

Remove CHECK from GetCurrentReturnSlot #4688

Merged
merged 3 commits into from
Dec 17, 2024

Conversation

geoffromer
Copy link
Contributor

GetCurrentReturnSlot is sometimes called when there is no return slot while checking incorrect Carbon code. This change also updates fail_returned_var_no_return_type.carbon to cover one such case.

`GetCurrentReturnSlot` is sometimes called when there is no return slot while checking incorrect Carbon code. This change also updates `fail_returned_var_no_return_type.carbon` to cover one such case.
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

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

It looks like you're trying to test the behavior for BuildReturnVar. What is the effect on BuildReturnWithExpr, which appears to have no handling for Invalid?

@@ -16,7 +16,7 @@ fn Procedure() {
// CHECK:STDERR: fn Procedure() {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
returned var v: () = ();
return;
return var;
Copy link
Contributor

@jonmeow jonmeow Dec 16, 2024

Choose a reason for hiding this comment

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

Why is this removing the test of return;? Should that be retained?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My original rationale was that this code has two distinct errors (forgetting the return type and forgetting to return the returned var), and it would be more orthogonal to have only one. But on reflection, arguably it's only one error (accidentally adding a returned var declaration), so it's probably worth keeping.

We do also need a test for the "forgot return type" case, because that's what would fail the CHECK that's being removed here, so I've added that below.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks, I must be missing the change. Can you please link to it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see it now.

Would it make sense to use split files (// --- ...) in order to force each failure to be independently verified?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, done.

Copy link
Contributor Author

@geoffromer geoffromer left a comment

Choose a reason for hiding this comment

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

It looks like you're trying to test the behavior for BuildReturnVar. What is the effect on BuildReturnWithExpr, which appears to have no handling for Invalid?

If you're asking whether BuildReturnWithExpr could ever benefit from the CHECK being removed here, I believe the answer is "not as it's currently written", because it only calls GetCurrentReturnSlot under conditions that ensure it will return a valid result. It also looks like it may implicitly handle invalid values, because there are other paths through that function that seem to leave return_slot_id invalid.

If that doesn't answer your question, can you rephrase it?

@@ -16,7 +16,7 @@ fn Procedure() {
// CHECK:STDERR: fn Procedure() {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
returned var v: () = ();
return;
return var;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

My original rationale was that this code has two distinct errors (forgetting the return type and forgetting to return the returned var), and it would be more orthogonal to have only one. But on reflection, arguably it's only one error (accidentally adding a returned var declaration), so it's probably worth keeping.

We do also need a test for the "forgot return type" case, because that's what would fail the CHECK that's being removed here, so I've added that below.

Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

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

It looks like you're trying to test the behavior for BuildReturnVar. What is the effect on BuildReturnWithExpr, which appears to have no handling for Invalid?

If you're asking whether BuildReturnWithExpr could ever benefit from the CHECK being removed here, I believe the answer is "not as it's currently written", because it only calls GetCurrentReturnSlot under conditions that ensure it will return a valid result. It also looks like it may implicitly handle invalid values, because there are other paths through that function that seem to leave return_slot_id invalid.

If that doesn't answer your question, can you rephrase it?

Rather than assuming BuildReturnWithExpr would work, my leaning here would be to either:

  1. Add a "valid" CHECK to the BuildReturnWithExpr code, to maintain the current behavior (essentially forcing a change/test if it's actually possible)
  2. Add a test that BuildReturnWithExpr correctly handles invalid, to test that the new behavior doesn't lead to a bug.

Thoughts?

@@ -16,7 +16,7 @@ fn Procedure() {
// CHECK:STDERR: fn Procedure() {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
returned var v: () = ();
return;
return var;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks, I must be missing the change. Can you please link to it?

Copy link
Contributor Author

@geoffromer geoffromer left a comment

Choose a reason for hiding this comment

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

Rather than assuming BuildReturnWithExpr would work, my leaning here would be to either:

  1. Add a "valid" CHECK to the BuildReturnWithExpr code, to maintain the current behavior (essentially forcing a change/test if it's actually possible)
  2. Add a test that BuildReturnWithExpr correctly handles invalid, to test that the new behavior doesn't lead to a bug.

Thoughts?

#2 is infeasible, because AFAIK there's no input that can cause BuildReturnWithExpr to get an invalid result from its GetCurrentReturnSlot call. I've implemented #1 now, but it feels a bit redundant: in the space of three lines the code checks that there's a return slot to get, gets the return slot, and then checks that the result is valid.

@@ -16,7 +16,7 @@ fn Procedure() {
// CHECK:STDERR: fn Procedure() {
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
returned var v: () = ();
return;
return var;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, done.

@jonmeow jonmeow added this pull request to the merge queue Dec 16, 2024
@jonmeow jonmeow removed this pull request from the merge queue due to a manual request Dec 16, 2024
@jonmeow
Copy link
Contributor

jonmeow commented Dec 16, 2024

Rather than assuming BuildReturnWithExpr would work, my leaning here would be to either:

  1. Add a "valid" CHECK to the BuildReturnWithExpr code, to maintain the current behavior (essentially forcing a change/test if it's actually possible)
  2. Add a test that BuildReturnWithExpr correctly handles invalid, to test that the new behavior doesn't lead to a bug.

Thoughts?

#2 is infeasible, because AFAIK there's no input that can cause BuildReturnWithExpr to get an invalid result from its GetCurrentReturnSlot call. I've implemented #1 now, but it feels a bit redundant: in the space of three lines the code checks that there's a return slot to get, gets the return slot, and then checks that the result is valid.

Fair enough. My own thought here though is that it's getting the information in a different path, and assuming they'll match. But maybe this is an indicator that there's a cleaner fix? Had you considered changing BuildReturnVar to do an equivalent check before getting the return slot:

  auto return_slot_id = GetCurrentReturnSlot(context);
  if (!SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function)
           .has_return_slot()) {
    // If we don't have a return slot, we're returning by value. Convert to a
    // value expression.
    returned_var_id = ConvertToValueExpr(context, returned_var_id);
    return_slot_id = SemIR::InstId::Invalid;
  }

->

  auto return_slot_id = SemIR::InstId::Invalid;
  if (SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function)
           .has_return_slot()) {
    return_slot_id = GetCurrentReturnSlot(context);
  } else {
    // If we don't have a return slot, we're returning by value. Convert to a
    // value expression.
    returned_var_id = ConvertToValueExpr(context, returned_var_id);
  }

i.e., instead of the GetCurrentReturnSlot change. Note, approved either way.

@jonmeow
Copy link
Contributor

jonmeow commented Dec 16, 2024

Actually, maybe that approach is better since it avoids a name lookup in the case? Maybe the CHECK in GetCurrentReturnSlot is correct on that basis?

@geoffromer
Copy link
Contributor Author

That's a good idea, and I tried it, but it turns out not to work, because GetCurrentReturnSlot can return a valid value even if ReturnTypeInfo::has_return_slot would return false (although not vice-versa). Specifically, GetCurrentReturnSlot will succeed if the function has a declared return type, but ReturnTypeInfo::has_return_slot only returns true if the return type has an in-place initializing representation, i.e. the return slot should actually be used.

(We really ought to get rid of that distinction; it's so confusing.)

I do think the logic in this whole area could be improved, but I think that's out of scope for this PR, so I'm merging this as-is.

@geoffromer geoffromer added this pull request to the merge queue Dec 17, 2024
Merged via the queue into carbon-language:trunk with commit 557c9b0 Dec 17, 2024
8 checks passed
@geoffromer geoffromer deleted the return-slot-bug branch December 17, 2024 19:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants