Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upproposal: Go 2: allow goto to jump across variable declarations #27165
Comments
griesemer
added
the
Proposal
label
Aug 23, 2018
gopherbot
added this to the Proposal milestone
Aug 23, 2018
ianlancetaylor
changed the title from
Proposal: Allow variables to jump across variable declarations (in many circumstances)
to
proposal: Go 2: allow variables to jump across variable declarations (in many circumstances)
Aug 23, 2018
ianlancetaylor
added
LanguageChange
Go2
labels
Aug 23, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
I like this. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
magical
Aug 23, 2018
Contributor
@griesemer Should the title say "allow goto" instead of "allow variables"?
|
@griesemer Should the title say "allow goto" instead of "allow variables"? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
magical
Aug 23, 2018
Contributor
I
Would it be valid to redeclare a variable after a goto? For example,
func _() {
goto L
var x int
L:
var x int
}
That is, are the lines between goto L and L: a new scope, or some sort of pseudo-scope? In other words, is the above code equivalent to
func _() {
goto L
{
var x int
}
L:
var x int
}
II
Does this interact with garbage collection at all? If i understand things correctly, currently the garbage collector is allowed to reclaim variables as soon as they are no longer used, regardless of whether they are officially "out of scope", so nothing would really change under this proposal. Just checking.
|
I Would it be valid to redeclare a variable after a goto? For example,
That is, are the lines between
II Does this interact with garbage collection at all? If i understand things correctly, currently the garbage collector is allowed to reclaim variables as soon as they are no longer used, regardless of whether they are officially "out of scope", so nothing would really change under this proposal. Just checking. |
griesemer
changed the title from
proposal: Go 2: allow variables to jump across variable declarations (in many circumstances)
to
proposal: Go 2: allow goto to jump across variable declarations (in many circumstances)
Aug 23, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
griesemer
Aug 23, 2018
Contributor
@magical Thanks for catching the typo in the title. Fixed.
No, I think it would be a mistake to introduce what you call "pseudo scopes". That would make this proposal much more complicated. It would also lead to hard to understand code, because one could have what looks like double declarations of variables (per your example) which are in fact different variables even though they appear textually in the same scope. That seems like a bad idea.
This proposal is strictly about restricting the scope access of variables whose declarations are being jumped over by gotos. Anything beyond that will make this more complicated and likely is not worthwhile the trouble.
It's important to keep in mind that using labels and goto's is not really a recommended coding style. But in some limited situations, using a goto is exactly the right thing. And in those cases it would be nice if we wouldn't have to compromise the quality of the code further by being forced to move variable declarations from the preferred locations to before a goto. This is what this proposal is all about. A nice side benefit is that the change is backward-compatible, simple to implement (I believe), and easy to explain in the spec.
Regarding garbage collection: The question is not so much about garbage collection, but about the layout of variables in an activation frame. As long as we make sure that variable locations are properly initialized at all times for garbage collection to work, or the compiler can provide the correct used/unused information at each GC point, I think there shouldn't be a problem here. For all I know there is nothing to do here, but @randall77 or @aclements will be able to give the authoritative answer.
|
@magical Thanks for catching the typo in the title. Fixed. No, I think it would be a mistake to introduce what you call "pseudo scopes". That would make this proposal much more complicated. It would also lead to hard to understand code, because one could have what looks like double declarations of variables (per your example) which are in fact different variables even though they appear textually in the same scope. That seems like a bad idea. This proposal is strictly about restricting the It's important to keep in mind that using labels and goto's is not really a recommended coding style. But in some limited situations, using a goto is exactly the right thing. And in those cases it would be nice if we wouldn't have to compromise the quality of the code further by being forced to move variable declarations from the preferred locations to before a Regarding garbage collection: The question is not so much about garbage collection, but about the layout of variables in an activation frame. As long as we make sure that variable locations are properly initialized at all times for garbage collection to work, or the compiler can provide the correct used/unused information at each GC point, I think there shouldn't be a problem here. For all I know there is nothing to do here, but @randall77 or @aclements will be able to give the authoritative answer. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
magical
Aug 23, 2018
Contributor
@griesemer Thanks for the clarification. I agree that it would be overly confusing, so I'm glad that it wouldn't be allowed.
|
@griesemer Thanks for the clarification. I agree that it would be overly confusing, so I'm glad that it wouldn't be allowed. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
FMNSSun
Aug 23, 2018
I like this but you don't have to declare the variable before the goto if you use
func main() {
cond1 := false
cond2 := true
cond3 := false
if !cond1 {
goto exit
}
if !cond2 {
goto exit
}
{
i := 5
if i == 5 && !cond3 {
goto exit
}
}
exit:
fmt.Println("exit")
}
FMNSSun
commented
Aug 23, 2018
|
I like this but you don't have to declare the variable before the
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
aclements
Aug 23, 2018
Member
For all I know there is nothing to do here, but @randall77 or @aclements will be able to give the authoritative answer.
I'm not 100% positive, but I believe you're right that this doesn't have any particular interaction with the garbage collector. Variables affected by this proposal must already be dead at the label, so it won't matter whether or not they're initialized in the frame.
It's possible this would have some interaction with ambiguously live variables, for example if you jump from a location before an ambiguously live variable is declared to a location where it is ambiguously live. This may require zeroing the ambiguously live slot before the jump. I suspect @randall77's stack tracing change will make this a moot point anyway.
I'm not 100% positive, but I believe you're right that this doesn't have any particular interaction with the garbage collector. Variables affected by this proposal must already be dead at the label, so it won't matter whether or not they're initialized in the frame. It's possible this would have some interaction with ambiguously live variables, for example if you jump from a location before an ambiguously live variable is declared to a location where it is ambiguously live. This may require zeroing the ambiguously live slot before the jump. I suspect @randall77's stack tracing change will make this a moot point anyway. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
griesemer
Aug 23, 2018
Contributor
I've edited the proposal per the discussion in #26058 (comment) .
Also, the original proposal text suggested that variables that must not be accessed after a label should be removed from scope to achieve that effect. But that would permit another variable with the same name to be declared in the original scope. That seems like a bad idea. In #27165 (comment) I had explained as much without noticing the difference from the earlier proposal text. I've added a further example to the original proposal text.
|
I've edited the proposal per the discussion in #26058 (comment) . Also, the original proposal text suggested that variables that must not be accessed after a label should be removed from scope to achieve that effect. But that would permit another variable with the same name to be declared in the original scope. That seems like a bad idea. In #27165 (comment) I had explained as much without noticing the difference from the earlier proposal text. I've added a further example to the original proposal text. |
griesemer
changed the title from
proposal: Go 2: allow goto to jump across variable declarations (in many circumstances)
to
proposal: Go 2: allow goto to jump across variable declarations
Aug 23, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
griesemer
Aug 24, 2018
Contributor
Possible refinement: Instead of disallowing variable access after a label if a goto jumped over the variable's declaration, it may be sufficient to disallow variable "reads". (A similar refinement may apply to #26058).
|
Possible refinement: Instead of disallowing variable access after a label if a |
networkimprov
referenced this issue
Sep 7, 2018
Open
proposal: Go 2: The #id/catch error model, a rethink of check/handle #27519
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
sdwarwick
Sep 9, 2018
Wrapping code between the goto and label with a {} block seems to work now and provides some explicit indication of intent regarding scope of declared variables in that range. Putting gotos inside the block to jump out also seems to work now.
Just worried about impacts to one of golang's greatest features.. clarity of intent is very explicit.
sdwarwick
commented
Sep 9, 2018
|
Wrapping code between the goto and label with a {} block seems to work now and provides some explicit indication of intent regarding scope of declared variables in that range. Putting gotos inside the block to jump out also seems to work now. Just worried about impacts to one of golang's greatest features.. clarity of intent is very explicit. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ianlancetaylor
Oct 2, 2018
Contributor
Closing in favor of #26058, which is the same idea expressed in a slightly different way. Both issues accept the same set of programs; the difference is whether the error is reported on the use of the variable or on the goto statement. The (small) advantage of #26058 is that it does not change variable scope. It just relaxes, slightly, the existing constraint on goto over a variable declaration.
|
Closing in favor of #26058, which is the same idea expressed in a slightly different way. Both issues accept the same set of programs; the difference is whether the error is reported on the use of the variable or on the |
griesemer commentedAug 23, 2018
•
edited
Problem statement
The current rules for the
gotostatement state:In short, a
gotostatement is not allowed to jump across variable declarations (jumping across nested scopes that declare variables is ok). The underlying reason for that rule is that such variable declarations will not be executed when thegotois taken and thus the respective variables may be in an undefined state (not initialized, not heap-allocated, etc.) at the target label.This is unfortunate because many times when a
gotois used at all, we don't actually care about the values of such variables. A perhaps typical example is the use ofgototo factor out error handling to the end of a function:Other common scenarios are uses of
goto's for early exit where there is no error handling but some finishing up code (e.g., look forgoto done,goto out,goto skipped, etc. in the std library).If the code above needs to use a new variable in the code following the
gotoand before theErrorlabel, that variable cannot conveniently be introduced close to where it is used, but instead will need to be declared before thegoto. Often this also means that one cannot use a short variable declaration because the program state is not what is needs to be for an initialization expression.In summary, using a
gotosometimes requires that otherwise unaffected code will need to move variable declarations before the use of thegotosince thegotowould not be permitted otherwise. This is at best annoying, and at worst may lead to less readable and maintainable code due to bulk variable declarations, without initialization expressions, sometimes much before they are actually needed.Observation
Obviously, if code accesses a variable at a target label of a
goto, that variable must be declared before thegotoas otherwise its value may be unknown. But the values of variables that are not accessed at the target label don't matter exactly because the code doesn't use those values.This observation leads directly to the proposal.
Proposal
We propose to remove the
gotorestrictions on variable declarations in favor of a new rule for variable accesses (edited per discussion):Examples
The code fragment:
currently is invalid and leads to the compiler error:
To make it valid, we would need to write something like:
With the proposed new rule, the
gotoerror will disappear, making the original code fragment valid. On the other hand, if the code were to make use ofxat the labelLas well:the compiler might report instead (edited per discussion):
The error reported currently with the
gotostatement could be used literally as explanation in the error reporting the invalid variable access at the target label.It is important that the variable remains in scope to make it impossible that another variable with the same name is declared in what used to be the variable's scope if there was no goto. For instance, the following code remains invalid with this proposal:
Implementation
The implementation is expected to be straight-forward: The new rule shifts an existing error, reported with a
goto, to a specific access of the variable which caused thegotoerror. More detailed:Whenever the compiler's analysis detects that a
gotojumps over a variable declaration, instead of reporting that as an error, the compiler remembers that variable as inaccessible at the target label.At the target label, all such inaccessible variables are removed from scope. This will in turn make it impossible to access such a variable in the code. (A more user-friendly implementation might keep the variable in scope but upon access would also check if it was marked inaccessible. Such an approach could provide a better error message).If the variable is accessed lexically after the target label, the compiler reports an error.Alternatives
#26058 proposes an alternative approach to achieve a similar outcome: Instead of disallowing variable access if a
gotojumped over the variable's declaration, #26058 disallows agotoif it jumps over a variable declaration of a variable that is used after the target label. In other words, #26058 makes the existing restriction ongoto's slightly less onerous while this proposal removes that restriction altogether but introduces a new one for variables.Summary
This proposal removes a restriction on uses of
goto's in favor of a new rule for variables: Instead of making it invalid for agototo jump over a variable declaration, it will become invalid to access a variable (after a label) whose declaration was jumped over. This is an improvement over the existing situation because the current rules always disallow variable declarations that are being jumped over, while the new rules only lead to an error if such variables are used at the target label, which is a less common scenario.Because the proposal removes an existing restriction, this is a backward-compatible language change.
#26058 achieves a similar outcome through a slightly different approach. It does not seem obviously clear which approach is better.