Open FedericoStra opened 3 years ago
It's undefined and the compiler is allowed to do anything if you did that. For each specific version (or even a wide range of julia versions) it'll of course have a specific behavior and the compiler will usually no go out of its way to break your code. However, anything can still happen.
May I ask then why not make it an error instead? Having it be UB implies that anything following it (actually, even preceding) is completely meaningless. In this particular instance, this is avoidable and it does not seem useful at all, but rather only dangerous instead.
I hope you realize what are the extreme consequences of having it be UB, but still letting users do it nonetheless. I would like to be sure that there is a clear unanimous consensus among the core team that this is indeed the intended semantics of const
. And if it is so then I feel that it should be stated more clearly in the docs.
There are several viable alternatives to having it be UB, so even if it currently is, maybe it is beneficial to discuss whether it could/should be relaxed to a less dramatic "unpredictable behavior" (quotation from the docs).
Just to name a few alternatives, from stronger to weaker guarantees, when we reassign to const c
:
const c = some_value
could mean that, from the moment of this reassignment onward, any past and future use of the global variable c
can resolve to any value that the variable c
has ever had up to this point, including some_value
, and every access can be resolved with different values. Basically, every time c
is resolved, it can take any value that was assigned up to this point. This seems to be the actual behavior of the current implementation, at least.
const c = ...
could mean that, from the moment of this reassignment onward, any past and future use of the global variable c
can resolve to any value which valid for the type of c
. This is akin to "unspecified value" from the C standard:
3.19.31 unspecified value valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance
const c = ...
could mean that, from the moment of this reassignment onward, any past and future use of the global variable c
can resolve to any value which is valid for the type of c
or even to invalid values. This is akin to "indeterminate value" from the C standard:
3.19.21 indeterminate value either an unspecified value or a trap representation (an object representation that need not represent a value of the object type)
The quotes of the C standard are taken from this draft, because I don't have 200 $ to buy the standard.
why not make it an error instead?
That would make sense, especially if it was optional (like --depwarn
), perhaps defaulting to error
for non-interactive use.
If reassigning to a const
is truly meant to be undefined behavior, and this is not subject to change, then I have a proposal (#38588) for a new feature.
This is genuine undefined behavior. We allow it because it's useful and usually doesn't cause too many problems. Most of the time all that happens is that old definitions give answers that are inconsistent with the new value, but it's entirely possible that some optimization could cause worse things to happen. It would, however, be a lot of effort and fairly pointless to try to limit how bad things can get when you redefine a constant. The error message is already quite alarming (intentionally). This should only be done while working interactively: do not redefine constants in any final working code; if you redefine a constant and your program does something weird, restart Julia. In general, your final working programs should never emit warnings.
That's completely fine for me. It makes sense and I agree that this is how things should be. I still feel that the documentation is a bit lacking on this point. I'll try to come up with a PR for improving the docs as soon as I find the time to write it properly.
Great, thanks. Clarification would certainly be helpful.
I'm sorry to come back without a finished PR, but while working on it I'm still really confused by the meaning of current docs Scope of Variables » Constants because they seem highly contradictory with what is claimed in this issue. Stripping out the examples, they write:
Additionally when one tries to assign a value to a variable that is declared constant the following scenarios are possible:
- if a new value has a different type than the type of the constant then an error is thrown: [...]
- if a new value has the same type as the constant then a warning is printed: [...]
- if an assignment would not result in the change of variable value no message is given: [...]
The last rule applies for immutable objects even if the variable binding would change, e.g.: [...]. However, for mutable objects the warning is printed as expected: [...]
In particular, it appears to me that they are prescribing a very well defined behavior.
Is this section prescribing the meaning of the abstract language or is it merely describing the current implementation-specific behavior? Is scenario 1 guaranteed to throw an error? This would be well defined behavior and the error could be caught. Is scenario 3 guaranteed to be a valid code?
I guess every choice would lead to a valid specification of the language, I just don't know what is your intention because it is not clear from the docs, hence I don't know how to fix them.
To exemplify my doubt, is the following code intended to be undefined behavior or is it guaranteed to print "hi"
?
const c = 1
try
c = "different type" # scenario 1: must throw an error
catch
print("hi")
end
Maybe the "correct" choice is the following?
That seems reasonable to me at least.
Yes, I think only the middle case can cause problems. Would be good for @JeffBezanson to confirm.
I think that the semantics of
const
is not specified in the documentation clearly enough. In both Scope of Variables » Constants and Base » Essentials » Keywords » const the word "undefined" does not appear a single time.What is the behavior of the following code?
Is it really undefined behavior?
I see two major answers.
1. It is UB
Then the program above is allowed to do whatever it wants, including printing
0
, printing1
, printing2
, printing"hello"
, not printing anything, crashing, hanging indefinitely, ...According to the docs:
However, if it is UB, then redefining a
const
is of no use whatsoever because there is no guarantee that it will do anything meaningful at all. Even during an interactive session, what follows has completely no sense because the whole behavior is undefined.If this is the case, I think this point should be made more clear in the docs.
2. It is not UB, just "unspecified value"
Another possible interpretation is that when doing
const c = some_new_value
, from this moment on every reference to the namec
can resolve to any value that the "constant"c
has ever had, includingsome_new_value
. This mean that the "value" ofc
may be unpredictable, maybe even unspecified, but the behavior of the program as a whole is not undefined in the strict sense.Within this interpretation the following code is allowed to compute the surprising result
(1, 0)
, but is not allowed to crash, hang, return42
, etc...Question
My personal interpretation from reading the docs is that the intent leans more towards option 2 (unspecified value), but I'm asking here to be sure (and possibly improve the documentation).
What is the correct interpretation of the meaning of
const
?