-
Notifications
You must be signed in to change notification settings - Fork 18k
spec: no rule about loops in constant definition #13890
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
Comments
I believe this is working as intended. From the specs: Package initialization:
According to the above, |
There is some bug somewhere if gc and gccgo disagree. |
I agree with cznic that this is a dependency cycle, so all three frontends should report it as such. Thus this is a bug report against gccgo. FWIW, a.go:6:25: cannot return ([...]float64 literal) (value of type [1]float64) as value of type [0]float64 |
Note that |
Good point; the broader problem is that the spec does not currently forbid this program:
|
I've changed this to be a bug report against the spec. |
@alandonovan, I don't think there is a dependency cycle, unless a somewhat conservative notion of dependency is introduced in the spec, according to which the type of |
@momchil-velikov: I was mistakenly reading the specification of variable initialization cycles, which treats a reference to a function as a reference to all the entities mentioned in the function body. You are right that the corresponding text for constants need not be so conservative, but currently there is no such text at all. |
I think @alandonovan's sample
is obviously invalid and should be disallowed, because it's a cycle in constant declarations. However, I think @momchil-velikov's original sample is still interesting, because it reveals that current Go type checkers are unnecessarily integrating type propagation/inference with constant evaluations. If those two steps are split into separate phases, the cycle goes away. Original:
Apply type propagation/inference:
Apply constant evaluation (including evaluating array bounds, which I expect in practice would even include evaluating the constant literal
Am I overlooking any counter examples where separating these steps would fail? I.e., programs where assigning types to names/expressions requires constant evaluation? |
Our goal should be to write the simplest possible rule, not the rule that permits the largest number of programs. That is the rule we've tried to follow for initialization loops in general. The simpler the rule, the more likely that all Go compilers will agree on the definition of a valid program. |
@mdempsky I believe it is difficult to separate type inference from constant evaluations - they are necessarily coupled: the length of an array type can be any constant expression, that value of which becomes part of that type. And a constant expression may depend on the result of a compile-time evaluated call of len(x), where the result of len(x) depends on the type of x. I agree that some type checkers (gc, go/types, even) use a somewhat ad-hoc approach at the moment to resolve cycles - in the sense that we don't necessarily have proof that we caught all cases correctly. Determining explicit dependencies first, and evaluating types (and constants) afterwards, in a controlled manner, might be the way to go. The spec doesn't say much if anything about cycles in constant expressions. For what it's worth, I don't think this is for 1.7. These are esoteric corner cases, and hardly showstoppers. |
@ianlancetaylor Agreed. I'm not saying @momchil-velikov's program has to be valid, but I think there should be a simple way to describe making it valid. I also wouldn't be surprised if allowing it is simpler than rejecting it. @griesemer I don't think either of those reasons actually force type inference and constant evaluations to happen together. In fact, I think they both already show up in @momchil-velikov's sample, that I walked through above:
Formalizing slightly, suppose for Go expression X we have Type(X) to denote X's type and Value(X) to denote X's constant value (if any). E.g., given My hypothesis is Type's definition does not depend on Value. If so, then it's sound to separate type inference from constant evaluation into separate passes. Further, if expanding Type(X) or Value(X) in turn recursively references Type(X) or Value(X) (respectively), then there's a cycle and the program is invalid. Under this formulation, disallowing @momchil-velikov's program requires an additional rule that if Type(X)'s expansion includes Type(Y) anywhere, then the program is also invalid if Value(Y)'s expansion mentions Type(X) or Value(X). (Of course this sort of pseudo-language-theory is not suitable as is for the Go spec; it's just my thought process on the topic.) |
@mdempsky I think you're right. I had a similar - albeit not quite formulated out - thought on my way home last night, that the actual length of the array is not required right away. I also agree that we should be able to make the program valid. |
Random example I stumbled upon and reminded me of this issue: gccgo accepts this code, but cmd/compile and go/types reject it reporting a constant definition loop:
|
The compiler issues error
constant definition loop
for the following program:In fact the value if
C
(resp. the type ofu
) does not depend on the type of the second return off
.The same program is compiled successfully by gccgo.
PS. Another test case
The text was updated successfully, but these errors were encountered: