-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Defer cannot affect return value #6186
Comments
I actually thought it wouldn't change them. And I'm not entirely convinced its a great idea that they should change. Because the return in my mind acts a bit like an assignment.
Plus if you are using this it seems like the return statement is kinda a sham and you have to scroll around to see if any defers are lurking around waiting to mutate it. Also consider this: Should it mutate here? fn bar() u32 {
var i: u32 = 5;
defer i += 5;
return i+0;
} Or here? fn bar() u32 {
var i: u32 = 5;
defer i += 5;
return (&i).*;
} Or here? pub var win: bool = true;
fn bar() u32 {
var i: u32 = 5;
defer i += 5;
return if (win) i else i;
} Treating it as an assignment expression of some return value, the answer is clear in all three cases: no. Proposal: Defer semantically happens after "return value assignment" but before control flow returns. only items returned by reference may be modified by the defer statememt. |
Another example where the current behavior is desired: const MyList = struct {
items: []u32,
capacity: usize,
fn init(...) @This() { ... }
fn deinit(self: *@This()) void {
// ... free array ...
self.* = undefined;
}
};
fn foo() usize {
var list = MyList.init(...);
defer list.deinit();
// do stuff
return list.items.len;
} If defers run before the return statement here is evaluated, foo() will unexpectedly return undefined. |
I disagree with the logic of this, primarily the reference to deiniting a struct. Deiniting a struct can only lead to mutation of a "returned" variable when pointers are involved. For example, you had a pointer to the struct and are now deiniting it, or you returned a pointer you obtained through some transitive reference that you now happen to modify in a We should not write code that mutates a function's locally scoped variable as if it were the result location. We are not prioritizing the reader of our code if we define a local variable and expect modifying it to have external side effects. The result location is external. Currently it is simply a copy elision optimization, and relying on it like you propose is just asking for undefined behavior to bite you later. When we find a way to expose result location explicitly, as #2765 sets out to do, you'll see that it's indeed external: it's an out ptr. Mutating that after return should have external side effects. Not mutating a local variable that happens to be copy-elided into the result location. |
It is possible to modify local variables via pointers. For example if SpexGuy's I'm OK with either way, I was just curious about the expected behavior is since I thought my example would return |
I would assume the mutated
i
to be returned since it is possible to mutate the returned variables in other ways, for exampledeinit
ing a struct.The text was updated successfully, but these errors were encountered: