0

If we have a function:

void func(int *restrict a, int *restrict b, int *restrict c) {
    *c = *a + *b;
}

In principle, this code may lead to some error:

int aa = 1;
func(&aa, &aa, &aa);

because in func, *a *b *c will be the same object. But why this code can be compiled successfully?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Yishu Fang
  • 9,448
  • 21
  • 65
  • 102

4 Answers4

4

restrict is basically a promise of the programmer to the compiler that the pointer is the only one that will be used to access the object it points to (within the scope of the pointer).

The compiler does not (generally) check that promise - it leaves that responsibility to the programmer. If the programmer gets it wrong, undefined behavior will be the punishment.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
2

Generally the detection of this type of UB is not possible. The calling function could already have receive the pointers from another compilation unit. This is why in general this is "just" UB, because as a constraint it could not be detectable at compile time.

Nothing prevents a compiler to issue a diagnostic in such cases, though. Why your compiler implementor didn't find it worth to implement such a diagnostic, you'd have to ask directly there.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
1

The whole point of restrict is to tell the compiler that it can assume that there's no overlap between the memory pointed to by a restricted pointer and any other memory accessible at that point, so that the compiler can do optimizations impossible without such an assumption. If the compiler could tell that there's no such overlap by itself, then there would be no need for the keyword. Making the code in func be an error would defeat the whole purpose of the keyword. As for the invocation of func: Since the compiler cannot in general tell whether the restriction is violated, it isn't required to note instances where it can tell. Some high quality compilers, with warning levels set appropriately, may issue a warning in circumstances such as the func call in your question.

Jim Balter
  • 16,163
  • 3
  • 43
  • 66
0

This code can compile successfully because there is nothing wrong in it.
Simply said, it is a standard compliant code. The standard demands diagnostic for only ill-formed code and this code is not.

It is same as, You are allowed to shoot yourself in the head, doesn't mean you should and if you do only you are to blame not others for not stopping you from doing so.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 1
    Perhaps by "nothing wrong" you mean "no constraint violations". It's still undefined behaviour. The same is the case for violating the constness of an object, which isn't a constraint violation, either. – autistic May 18 '13 at 07:21
  • @undefinedbehaviour: Precisely, there is a difference between code having constraint violation and undefined/unspecified/implementation behavior, former demands a compiler diagnostic latter does not. – Alok Save May 18 '13 at 07:25
  • 1
    That's not entirely true. See the example at 5.1.1.3p2. – autistic May 18 '13 at 07:30
  • 1
    In any case, the code most certainly is **not** standard compliant ... code that has undefined behavior is by definition not compliant. – Jim Balter May 18 '13 at 11:30
  • The shooting-yourself-in-the-head example is a bit off. Suicide is indeed illegal in many parts of the world. – cHao May 18 '13 at 11:35
  • @Seb: The compiler generally analyzes the caller and called function separately. The called function would be just fine if a caller were to pass it three disjoint pointers. The caller would be just fine if the called function were to arbitrarily use one of the pointers and ignore the other two, or if it didn't write to any of the pointers. While the particular combination of caller and called function might be problematical, each would be fine in isolation and a compiler would not be required to squawk at the juxtaposition. Note that in the scenario shown, a compiler that would warn if... – supercat Jul 11 '16 at 18:33
  • ...`restrict` could change the meaning of an in-lined function might be silent because a non-obtuse compiler would need to read `*a` and `*b` before writing `*c`, and would have no reason to reread them; since that order of operations would be required with or without `restrict`, there would be no reason for a non-obtuse compiler to process the code differently as a result of `restrict` (a message might be nice, to be sure, but code generation shouldn't care about it). – supercat Jul 11 '16 at 18:37
  • @supercat What alternatives have you explored for "compiler would need to read `*a` and `*b` before writing `*c`"? I do hope you're remembering that C doesn't necessarily compile to x86 code. It would be the code that accesses the objects, rather than the compiler as you believe. The compiler does indeed parse `*c` **before** it parses `*a` and `*b`. More to the point, **there is no requirement that this code compile successfully**; sometimes compilers will allow you to invoke UB, while others have more powerful analysis capable of rejecting such invalid code – autistic Jul 12 '16 at 14:03
  • @supercat ... and so the question itself is invalid. – autistic Jul 12 '16 at 14:04
  • @Seb: I meant a non-obtuse compiler would generate code which read the operands to the multiply operator before performing the multiply. The Standard doesn't forbid compiler writers from being obtuse, so from the point of view of the Standard a compiler could do anything it likes if the destination and source overlap, but since the "One Program Rule" and "As-If Rule" together mean that an obtuse compiler can do anything it likes with just about *any* program, that doesn't really mean much. – supercat Jul 12 '16 at 14:18
  • @supercat The "as if" rule you described here seems misunderstood, and the rule as it actually is is irrelevant here because of the UB. That aside, there is no requirement that a non-obscure compiler produce opcodes for an instruction set you're familiar with; this code could turn into an instruction loading `*a` into `*c` followed by an instruction multiplying `*b` into `*c`... or perhaps even a single instruction multiplying the two and storing. None of this matters, since a non-obscure compiler might reference-count `aa` during compilation and issue a diagnostic when the count exceeds 1. – autistic Jul 26 '16 at 06:20
  • @supercat Just to clarify, by "as if" I gather you're talking about the "observable behaviour" of a program. That "observable behaviour" is undefined in this case, though one might reason that a compiler could (and probably will) just optimise away all of this code if it can make the deduction that it's unused or unreachable. In that case, would the non-obtuse compiler still "need to read `*a` and `*b` before writing `*c`"? – autistic Jul 26 '16 at 06:52
  • @Seb: An obtuse compiler could say that any program other than a certain contrived source text will require 5 petabytes of stack space to run, and feeding any other source text will cause a stack overflow, thus invoking Undefined Behavior and allowing the compiler to do anything whatsoever. The authors of the Standard recognized that it made sense for some actions to be defined on some implementations but not others, but thought compiler writers would exercise good judgment in that regard. Note that they regarded the majority [they used that word] of implementations as defining behavior... – supercat Jul 26 '16 at 14:26
  • ...in some cases involving integer overflow, but did not list that in the "common extensions" annex, implying that they did not regard that as an "extension" which needed to be explicitly documented in order to exist--the fact that an implementation was running on two's-complement silent-wraparound hardware was presumably sufficient to suggest that integer overflow behavior should be defined in at least the cases described. Obtuse compilers would not be required to behave that way, but they wouldn't be required to do anything useful anyhow. – supercat Jul 26 '16 at 14:28
  • @supercat "An obtuse compiler could say that any program other than a certain contrived source text will require 5 petabytes of stack space to run, and feeding any other source text will cause a stack overflow, thus invoking Undefined Behavior and allowing the compiler to do anything whatsoever." Citation (from a standard; any C standard will do), please... Surprise me; this is rather different to what the standards I know say. Which standard/s are you referring to? By "authors of the Standard", are you referring to WG14? – autistic Jul 26 '16 at 16:35
  • "... did not list that in the "common extensions" annex, implying that they did not regard that as an "extension" which needed to be explicitly documented in order to exist"... Well of course implementations aren't required to document integer overflow behaviour; that's why it's called undefined behaviour. There are other parts of that annex (the extensions) that are technically undefined behaviour, too, such as the extension allowing modifications to a string literal. All undefined behaviour, just like this question, and that's the only way I can see to make your babble relevant. – autistic Jul 26 '16 at 16:43
  • @Seb: Read C89 2.2.4.1 (Translation Limits) and the rationale therefor. With regard to overflow, see the C89 rationale, 3.2.1.1 (Characters and Integers), starting at "Both schemes...". Would there be any relevance to the second numbered point if the authors of the Standard didn't consider the behavior defined on the majority of implementations? I don't think the majority of implementations expressly documented the effects of integer overflow, so the only way the behavior would be considered "defined" would be if the fact that an implementation targeted silent-wraparound hardware... – supercat Jul 26 '16 at 17:04
  • @supercat Why do you think the undefined behaviour of "common extensions" needs to be documented? Undefined behaviour by definition doesn't need to be documented. Have you considered that part of the reason they left signed integer overflow undefined might have been that there were other implementations that would trap, signal or saturate, rather than silently wrap? There are still implementations that use alternative choices (and document them, despite UB) in common use today; I encourage you to use one of them (perhaps gcc or clang?)... – autistic Jul 26 '16 at 17:05
  • ...suggested that the implementation would have such semantics in the indicated scenario absent documentation to the contrary. If silent wraparound semantics had been listed in Annex J2, that would have directed implementations to expressly list all the guarantees they offer beyond what the Standard would require, so programmers would have known what guarantees compilers promise to uphold in future versions and which ones they don't (e.g. a compiler might promise that in case of overflow an expression like `x*y>z` might arbitrarily yield 0 or 1, *but would have no other side-effects*). – supercat Jul 26 '16 at 17:08
  • @Seb: There definitely were and are hardware implementations that use something other than silent-wraparound semantics, and compilers targeting such hardware should only be expected to have silent-wraparound semantics if they explicitly promise it. That's why it was left undefined. The question is whether it should be considered normal for integer overflow to cause a compiler to abandon laws of time and causality *even on implementations targeting silent-wraparound hardware*. – supercat Jul 26 '16 at 17:11
  • @supercat ... so you believe undefined behaviour should be abolished, then. All undefined behaviours should be documented, even to the excruciating point of explaining how non-conforming code can lead to vulnerable programs, and how to exploit those programs, right? ... and this should all go in the compilers manual, great idea! Oh and to answer your question at the end, yes it is normal; when you tell the compiler to emit traps on signed integer overflow, you can expect traps instead... mmm hmmm, yessir, and this is documented. More reading, less guessing? Sounds good. Let's go... – autistic Jul 26 '16 at 17:24
  • @Seb: There are many situations where behavioral guarantees which aren't mandated by the Standard but which many implementations could offer at near-zero cost will allow programs to be written much more cleanly and efficiently than will otherwise be possible. It's not necessary for all possible undefined behaviors to be documented, but there are some behaviors where it would be greatly beneficial for implementations to specify what guarantees they do and do not offer; in cases where guarantees which used to be cheap become expensive, the proper remedy would be to add directives... – supercat Jul 26 '16 at 17:43
  • ...that would allow programmers to waive the guarantees they don't need, or else identify the particular places they need certain guarantees and then waive those guarantees elsewhere. In cases where a program wouldn't benefit from a guarantee, waiving it might offer a slight but non-trivial performance boost, but in cases where a program would benefit from a guarantee, the optimization loss to the compiler will often be trivial compared to the cost of working around the lack of the guarantee. – supercat Jul 26 '16 at 17:48
  • @Seb: PS--obviously if one requests traps, one should expect traps, but traps don't negate the laws of time and causality. For a lot of file-processing programs, it's would be acceptable if feeding invalid input causes the program to hang, fill the designated output files with meaningless gibverish, or core dump, but some other actions would be unacceptable. – supercat Jul 26 '16 at 17:51
  • @supercat People who guess often invoke undefined behaviour when using functions such as those you mentioned. Those who read avoid the undefined behaviour... hintedy hint – autistic Jul 26 '16 at 17:54
  • @Seb: In most cases, the amount of machine code required to ensure that a program doesn't do anything other than the above three actions when given invalid input should be minimal, and the amount of source code required should likewise be minimal, but if integer overflow negates the laws of time and causality, the amount of code required to guard against it can be comparable to the amount of code required to perform calculations in the non-overflow case. – supercat Jul 26 '16 at 17:54
  • @Seb: Did you read the rationale regarding short unsigned promotions? Why would the authors mention what the majority of then-current implementations did in cases where the result was in the range INT_MAX+1u to UINT_MAX if they did not regard the behavior defined *on those implementations*, and did not expect that future implementations would continue to define the behavior likewise? – supercat Jul 26 '16 at 17:56
  • @supercat You've drifted a long way away from unsequenced access and violating `restrict`. Would you mind explaining what useful extensions would possibly come of either of those? Is it like the extension where you dance around the maypole whilst overflowing an array to change the size of the array, or the one where Lara Croft exploits her format string to expose her most sensitive regions? – autistic Jul 26 '16 at 18:06
  • @Seb: The purpose of `restrict` is to allow compilers to perform certain kinds of optimizing transformations on the code. The natural effects of such transformations should not cause any trouble in cases where each piece of data will be written at most once, and all reads of that piece will be used in the computation of the data to be written. The Standard does not specify that `restrict` is permissible in such cases, but it doesn't provide any other means by which a programmer can invite a compiler to make such transformations. – supercat Jul 26 '16 at 18:37
  • @supercat [Please see C11 section 6.7.3.1 p7, 8 and 9](http://port70.net/~nsz/c/c11/n1570.html#6.7.3.1p7) – autistic Jul 27 '16 at 09:48
  • @supercat [3.4.3 p1, 2 and especially 3](http://port70.net/~nsz/c/c11/n1570.html#3.4.3p1) might also be interesting to you... – autistic Jul 27 '16 at 10:08
  • @Seb: And when the standard was written, the most common way of handling integer overflow would have been the second means given in p2: behaving in a documented manner characteristic of the environment. The vast majority of programs are run in an environment with documented two's-complement silent-wraparound behavior, and while there are times when it is useful for programmers to allow compilers to deviate from the environment behavior, granting total latitude is less conducive to efficiency than granting a level of latitude which would still let a program meet requirements if overflows occur. – supercat Jul 27 '16 at 14:37
  • In those days, twos complement was much less common. Additionally, there are still implementations in use today which trap (and these use a twos complement representation). If you want to write non-compliant code, go for it... just stop wasting my time. Bye. – autistic Jul 29 '16 at 07:43