Redefining structs without restart?

There is no mention of undefined behavior in the docs of const. To my understanding, const c = ... means that whenever we reference c in a function, from this moment on the compiler is free to inline this value of c , but some occurrences might have already be inlined with a previous valued, or be inlined in the future with a new value if we redefine c. This inlining may occur at an indeterminate time, including at the time of definition or at the time of specialization of the function. For instance

const c = 0
f() = c
# ... we don't redefine f or c here
c = 1
# ... we don't redefine f or c here
f() # can be 0, can be 1

I don’t see written anywhere that the previous program exhibits undefined behavior. Real UB would allow the previous program to print "banana!", but I believe this is not what is meant by “However, this can produce unpredictable behavior or corrupt the state of your program, and so should be avoided.”.

Violating the promise of const seems to allow for a very narrow type of unpredictability (because you don’t know necessarily which value of c gets used), but it is not undefined behavior (or at last it is not clear from what is written in the docs).

You can contrast the violation of const in Julia with doing the same in C here, where it is really UB.

This is pretty much the textbook case of what is called “undefined” behavior in programming: various outcomes can happen depending on accidental circumstances (eg evaluation order) and compiler internals (which can change at any time).

2 Likes

I’m sorry but this is not the definition of undefined behavior (no pun intended). From the various documentation about const one is lead to believe that only a very restricted case of consequences can happen when redefining a const (namely, which exact value gets used is difficult to predict). UB on the other hand would allow any outcome (or no outcome at all…), and in particular my previous program could print "banana!" or format the hard drive. Is this really what is meant by the docs? If so, then a clarification is in order. Otherwise, it is not undefined behavior as commonly intended when speaking about C.

I think however that the intervention of some core developer might be required to clarify the exact intention behind this, since the docs are insufficiently clear.

It is, really. Please look it up:

That does not matter — the fact that there are multiple outcomes, and a conforming program can be unpredictable, makes it undefined behavior.

This has been discussed multiple times, please search the forum. TL;DR: const is your promise to the compiler, allowing it to make various optimizations (if it chooses to do so). Violate that promise, and consequences are “undefined”. This is pretty much the bottom line, and the warning that ?const gives should be instructive:

In some cases changing the value of a const variable gives a warning instead of an error. However, this can produce unpredictable behavior or corrupt the state of your program, and so should be avoided. This feature is intended only for convenience during interactive use.

Incidentally, this terminology is very commonly used for programming languages, which may be the reason that the manual does not define these terms. Also, Julia has a single implementation, so I guess it didn’t bother with the super-fine distinctions unlike, say, Common Lisp.

2 Likes

I think there is a huge level of mutual misunderstanding going on. Let me ask you this question. What are the possible behaviors of the following program?

const c = 0
c = 1
print(c)

Is it allowed to print 42? What about "hello"? What about printing nothing? What about never halting? If you insist that redefining a const truly is UB, then these are all possible outcomes compatible with the language specification.

On the other hand, if you don’t accept these outcomes, then no, redefining const is not truly UB in the sense of C.

By reading the docs, what I understand is that redefining a const means that occurrences might still retain previous values it had before, because it was possibly inlined. If you like, you can call it “unspecified value” (even unspecified behavior if you really want), but it is not undefined behavior as doing the same thing in C is.

I might be interpreting the docs wrongly, but this is what seems to be suggested by reading them. If the intention was to make redefining const UB, then it should be clearly written so.

Edit. A nice example of the distinction between unspecified value and undefined behavior in C can be found here.

There’s defined behaviour in corner cases as you show here, but I think @Tamas_Papp means the behaviour is undefined in the general case. Example:

julia> const c = 1
1

julia> const c = 2
WARNING: redefinition of constant c. This may fail, cause incorrect answers, or produce other errors.
2

julia> const c = [1,2,3]
ERROR: invalid redefinition of constant c
Stacktrace:
 [1] top-level scope at REPL[3]:1
 [2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.2/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

julia> c
2

# New session!

julia> const c = [1,2,3]
3-element Array{Int64,1}:
 1
 2
 3

julia> const c = 2
ERROR: invalid redefinition of constant c
Stacktrace:
 [1] top-level scope at REPL[2]:1
 [2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.2/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288

So in the general case you cannot predict the outcome of redefining a constant in Julia as it depends on the current value and the value used in the attempted const update, together with the fact that no specific guarantees on the outcome for these cases is given.

Edit: expanded the last sentence

You seem to confuse “it throws an error” with “it is undefined behavior”.

Your example is consistent also with the theory that it is not UB, because you tried to assign a value of another type, which is disallowed. Basically your argument is: “See! If I do this it throws an error, therefore it must be UB”. Being UB is not something that you can detect by looking at a particular behavior; it is something which is mandated by a language specification.

If changing a const truly is UB, then even the apparently innocuous snippet below doesn’t make any sense

const c = 0
c = 1

because we cannot have any confidence that executing it will not buy 10.000 rubber ducks on amazon and deliver them at our home address.

I don’t have much time at the moment so I can’t reply to the larger posts we’ve exchanged earlier, but that analogy is a very destructive argument why some behaviour is/isn’t UB. Practically speaking, dereferencing an invalid pointer in C is UB, yet you’re very unlikely to “buy 10.000 rubber ducks on amazon” by accident. Typically, C throws a “Segmentation Fault”, which is no different to throwing an error in julia.

“Undefined Behaviour” is simply behaviour a program displays when something happens that break promises or invariants. In C you promise never to dereference invalid pointers, in julia you promise never to change const bindings.

2 Likes

That is the whole point. The language specification does not say what happens in in all cases when a const is redefined. Notice how Scope of Variables · The Julia Language says:

For instance, if a method references a constant and is already compiled before the constant is changed, then it might keep using the old value

You don’t get a guarantee what happens, the specification does not dictate it so it isn’t fixed. Undefined behaviour in most cases case is purely a semantic label, as running the same native code + same data + on same hardware + etc should always produce the same output as the processing is deterministic.

There’s plenty of APIs that only guarantee certain results for specific parameter values. Calling such an API with values outside of the guaranteed range might do one thing or the other, but you have no guarantee what will happen. And that is called undefined behaviour. Even though the actual code implementing the API is still deterministic and might not change for years, and so produce the same undefined behaviour given the same inputs, it can change at any point in time as no guarantee was given on what would be produced in the first place. And so should not be relied upon.

1 Like

Awesome, now my answers get deleted. Wonderful.

The following code might be undefined behavior in C:

unsigned int a, b;
memcpy(&a, &b, 1);
a -= a;

The following code is not undefined behavior in C:

unsigned char a, b;
memcpy(&a, &b, 1);

In this case value of a is an unspecified value:

3.19.3 unspecified value
valid value of the relevant type where this International Standard imposes no requirements on which value is chosen in any instance

This is different from UB.

This amounts to being unspecified value, not necessarily undefined behavior.

I can’t make it clearer than this. If you don’t consider this undefined behaviour then I don’t know what will. The answer printed depends on the optimization level in this case, which should never happen if the behaviour was well-defined (i.e. the opposite of undefined):

melis@juggle 18:56:~$ cat t.jl 
const c = 1
function f()
    return c
end
print("$(c), $(f())\n")

const c = 2
print("$(c), $(f())\n")

melis@juggle 18:56:~$ julia t.jl 
1, 1
WARNING: redefinition of constant c. This may fail, cause incorrect answers, or produce other errors.
2, 1

melis@juggle 18:56:~$ julia -O0 --compile=no t.jl 
1, 1
WARNING: redefinition of constant c. This may fail, cause incorrect answers, or produce other errors.
2, 2
4 Likes

You really seem to not understand the difference between undefined behavior and unspecified value. I’ll leave this conversation because it is pointless.

I agree that any further discussion is pointless…

5 Likes

Your answers have not been deleted as far as I can tell, they have been moved to a new thread because they were very much off topic to the original thread (which was asking about why/when using will become faster). For reference, the old thread is here.

6 Likes