First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…
Pointers Are Complicated II, or: We need better language specs
21–30 of 135 posts
Re: Pointers Are Complicated II, or: We need better language specs
#22First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…
Why would it do anything special here? This is not really different from something like this:
struct a { struct a *p; };
struct a x = { &a };
The only real difference is that the void version involves a cast, because the type is otherwise impossible to express in C.Re: Pointers Are Complicated II, or: We need better language specs
#23First of all: fantastic article . In-depth, insightful, and the examples are absolutely top-notch. On the razor's edge between accessible and profound. Hats off to the author. I will say that the problems seem to lie in a few interesting interlanguage quirks, and not so much on language specs . For example, LLVM and C have different definitions of "undefined behavior"[1] -- this is pointed out when looking at the `po…
As usual; if a compiler knows about undefined behavior I would much rather it throw an error rather than optimize something the programmer didn't intend based on the compiler out-smarting a human's ability to be specific.
int factorial(int n) {
int r = 1;
for (int i = 1; i
Since there are two instances of possible UB in the above function, what error messages would you like the compiler to produce?First of all, the compiler can assume that the loop terminates. This is a reasonable assumption from a human perspective—the loop is almost certainly intended to run N times. However, if n = INT_MAX, then there’s UB… and there’s no real way to know that n ≠ INT_MAX.
You could argue that you want -fsanitize=undefined, where the compiler inserts checks at runtime.
I don’t think this proposal is well formulated enough to be viable.
Or consider something like this:
void puts_safe(const char *s) {
puts(s == NULL ? "(null)" : s)
}
void puts_with_len(const char *s) {
printf("len = %zu\n", strlen(s));
puts_safe(s);
}
Do you want something like, Warning: puts_safe replaced with puts, because s != NULL, because strlen(NULL) is undefined
I suspect there would be a mountain of false positives, and I would rather just use a static analyzer.Re: Pointers Are Complicated II, or: We need better language specs
#24I'm sorry but I don't fully understand the problem and that 'provenance' thing. For me the third optimization is the wrong one, for the same reason as this char i,j='0'; *(&i+1)='1'; cout can't be optimized to cout even though the j variable is also never overwritten directly. This reminds me of paralelization of nested loops with pragmas, where you need to specifically say that two pointers will never point to the s…
https://c.godbolt.org/z/qe3f4K
`*(&i+1)` dereferences the pointer "one past the end" which is UB.
Re: Pointers Are Complicated II, or: We need better language specs
#25I'm sorry but I don't fully understand the problem and that 'provenance' thing. For me the third optimization is the wrong one, for the same reason as this char i,j='0'; *(&i+1)='1'; cout can't be optimized to cout even though the j variable is also never overwritten directly. This reminds me of paralelization of nested loops with pragmas, where you need to specifically say that two pointers will never point to the s…
Your comparison with parallel loops is interesting because in the case of big arrays, all the pointers refer to the same object, so the compiler can’t use provenance or strict aliasing to prove that they don’t overlap. So you have to use the pragmas to give the compiler permission to treat them as non-aliasing.
Re: Pointers Are Complicated II, or: We need better language specs
#26I'm sorry but I don't fully understand the problem and that 'provenance' thing. For me the third optimization is the wrong one, for the same reason as this char i,j='0'; *(&i+1)='1'; cout can't be optimized to cout even though the j variable is also never overwritten directly. This reminds me of paralelization of nested loops with pragmas, where you need to specifically say that two pointers will never point to the s…
Your block of code can be (and absolutely is) optimized to output zero directly (in c, because the assembly is easier to read): https://c.godbolt.org/z/qe3f4K `*(&i+1)` dereferences the pointer "one past the end" which is UB.
But then, why such a long article for a code whose second line is a UB ('p+1' is undefined)?
Re: Pointers Are Complicated II, or: We need better language specs
#27Earlier quoted context omitted.
Your block of code can be (and absolutely is) optimized to output zero directly (in c, because the assembly is easier to read): https://c.godbolt.org/z/qe3f4K `*(&i+1)` dereferences the pointer "one past the end" which is UB.
And I agree, with an UB the original code is undefined and so any optimization is not right nor wrong, simply keep that UB. But then, why such a long article for a code whose second line is a UB ('p+1' is undefined)?
uintptr_t ip = (uintptr_t)(p+1);
Is perfectly defined. What is undefined is converting ip back to a pointer and writing to it, which the original program never does.Re: Pointers Are Complicated II, or: We need better language specs
#28Earlier quoted context omitted.
Your block of code can be (and absolutely is) optimized to output zero directly (in c, because the assembly is easier to read): https://c.godbolt.org/z/qe3f4K `*(&i+1)` dereferences the pointer "one past the end" which is UB.
And I agree, with an UB the original code is undefined and so any optimization is not right nor wrong, simply keep that UB. But then, why such a long article for a code whose second line is a UB ('p+1' is undefined)?
It constructs a pointer to the "one past the end" element, but that is fine, and the original program never dereferences that pointer. Again: there is no UB in the original program.
Re: Pointers Are Complicated II, or: We need better language specs
#29It just seems to me there's too much risk in "overoptimizing" especially in a weakly defined language like C with trigger happy optimizers and even more trigger happy "UB means let's go crazy"
I take it as principle that no compilers should "throw their hands up" when detecting UB. Crash the program, don't just take that part out.
Re: Pointers Are Complicated II, or: We need better language specs
#30Earlier quoted context omitted.
Your block of code can be (and absolutely is) optimized to output zero directly (in c, because the assembly is easier to read): https://c.godbolt.org/z/qe3f4K `*(&i+1)` dereferences the pointer "one past the end" which is UB.
And I agree, with an UB the original code is undefined and so any optimization is not right nor wrong, simply keep that UB. But then, why such a long article for a code whose second line is a UB ('p+1' is undefined)?