Submitter: Greg Davis, Green Hills Software
Submission Date: 2003-08-14
Source: INCITS
Reference Document:
Version: 1.1
Date: 2004-09-28
Subject: Technical question on C99
restrict keyword
Summary
I have a question on section 6.7.3.1 of the C99 spec.Paragraph 4 is where this seems to get complicated.
Question 1)
"...Every other lvalue used to access the value of X shall also have its address based on P".
Consider the following example:
#include <stdlib.h> char * restrict a, * restrict b;
void copy(char * restrict dest, char * restrict source) { int i; for (i = 0; i < 10; i++) dest[i] = source[i]; }
int main() { a = calloc(10); b = calloc(10); copy(a, b); return 0; }
Is this a legal program? If so, could you explain the following? From the standpoint of main(), the memory in a is modified through the call to copy(). However, it seems to me that based on the definition of based on, the writes that modify *a are not based on the pointer a, but instead they're based on dest. Doesn't this violate the guarantee above?
Question 2)
"Every access that modifies X shall be considered also to modify P, for the purposes of this subclause." - Why is this necessary?
Question 3)
The same question for the rules on the copying of restrict pointers ("If P is assigned the value of a pointer expression E that is based on another restrict pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment."). Why is this necessary?
Committee Response
Question 1)
Yes, the program conforms to all requirements in the specification for the restrict qualifier (though the call to function calloc should have two arguments).
Some interpretation of this question is provided to clarify the response. The response answers the interpretation.
The interpretation of this question is: I can see how the rules are followed for the restrict qualifier on dest, relative to the execution of function copy, but I don't see how the rules are followed for the restrict qualifier on a, relative to the execution of main.
Here is a spelling out of all the requirements in the specification of the restrict qualifier for this example. The following identify definitions in the specification of the restrict qualifier.
The requirements in the specification are then:
Contrary to what is implied in the question, an lvalue can have its address based on more than one restricted pointer, provided each is associated with a different (activation of a) block. In the example, the address of lvalue dest[i] in copy() is based not only on dest but also on a (because dest receives the value of a when the call is made).
[Note that there would be undefined behavior if there were also a reference to a[i] within the body of copy(), because the address of that lvalue would not be based on dest.]
Question 2)
This is necessary for the effectiveness of the restrict qualifier for multiple levels of indirection. Consider the example:
void reverse(char * restrict * restrict dest, char * restrict * restrict source) { int i,j; for (i = 0; i < 10; i++) for (j = 0; j < 10; j++) dest[i][j] = source[i][9-j]; }
Although the objects dest[i][j] are modified by the assignment statements, the pointer objects dest[i] are not. Without the clause quoted above, the top-level restrict qualifiers in the declarations of dest would have no effect, and a call of the form reverse(x,x) would have defined behavior if the elements of x point to 10 disjoint arrays of 10 chars. With the clause, the top-level qualifiers have the same effect as if those pointer objects were modified, so the iterated assignments are asserted to be fully free of aliasing for the modified objects, and a call of the form reverse(x,x) does have undefined behavior.
Question 3)
This is necessary to allow a translator to assume that two restricted pointers declared in the same scope cannot be used to alias the same object. Consider an extreme example for file scope pointers:
char * restrict p; char * restrict q; void foo () { p = q; q = p; }
After the assignments in a call to foo(), each of the two pointers is based on the other. Without the rules quoted above, this would be allowed and would effectively enable aliasing despite the qualifiers. This possibility would, in turn, generally undermine the benefit of the restrict qualifier, because a translator would have to prove that there were no such assignments before taking advantage of restrict qualifiers.
[The assignments that are allowed are necessary to allow pointer values based on restricted pointers to be used in argument and return expressions.]