First some references. The C99 Standard says this about restrict
in section 6.7.3:
An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer.117) The intended use of the
restrict
qualifier (like theregister
storage class) is to promote optimization, and deleting all instances of the qualifier from all preprocessing translation units composing a conforming program does not change its meaning (i.e., observable behavior).
And then (§6.7.3.1 "Formal definition of restrict
"):
Let
D
be a declaration of an ordinary identifier that provides a means of designating an objectP
as a restrict-qualified pointer to typeT
.If
D
appears inside a block and does not have storage classextern
, letB
denote the block. IfD
appears in the list of parameter declarations of a function definition, letB
denote the associated block. Otherwise, letB
denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).In what follows, a pointer expression
E
is said to be based on objectP
if (at some sequence point in the execution ofB
prior to the evaluation ofE
) modifyingP
to point to a copy of the array object into which it formerly pointed would change the value ofE
.119) Note that ''based'' is defined only for expressions with pointer types.During each execution of
B
, letL
be any lvalue that has&L
based onP
. IfL
is used to access the value of the objectX
that it designates, andX
is also modified (by any means), then the following requirements apply:T
shall not be const-qualified. Every other lvalue used to access the value ofX
shall also have its address based onP
. Every access that modifiesX
shall be considered also to modifyP
, for the purposes of this subclause. IfP
is assigned the value of a pointer expressionE
that is based on another restricted pointer objectP2
, associated with blockB2
, then either the execution ofB2
shall begin before the execution ofB
, or the execution ofB2
shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.
As some have pointed out, this illustrates the rules (Example 4 from the standard):
{
int * restrict p1;
int * restrict q1;
p1 = q1; // undefined behavior
{
int * restrict p2 = p1; // valid
int * restrict q2 = q1; // valid
p1 = q2; // undefined behavior
p2 = q2; // undefined behavior
}
}
Now, my first question is this: why is it okay to assign from an outer restricted pointer to an inner one?
My understanding is that nothing forbids this, which has clear aliasing:
int * restricted x = /* ... */ ;
{
int * restricted y = x;
*x = 3;
printf("%d\n", *y); // 3
*y = 4;
printf("%d\n", *x); // 4
}
Of course, the set of aliases is restricted to the two pointers.
Hence my second question: what is the difference assigning from outer to inner (allowed), but not from inner to outer (forbidden, e.g. p1 = q1;
in the first example above)?
In the C programming language, restrict is a keyword that can be used in pointer declarations. By adding this type qualifier, a programmer hints to the compiler that for the lifetime of the pointer, no other pointer will be used to access the object to which it points.
I think the rules are designed to satisfy two objectives:
Allow creation of a temporary pointer similar to what would naturally be created when an argument is passed to a function call, without requiring that code using the pointer be moved into a physically separate function.
Ensure that a graph showing pointer derivation will be free of cycles (meaning that if pointer x is derived from y or anything that is directly or indirectly derived from y, y cannot be derived from x nor anything which is directly or indirectly derived from x). While the rules may be tighter than would be absolutely necessary to achieve #2, nearly all of the useful cases that would satisfy this second requirement also satisfy the rules as written, and compilers would have difficulty getting much benefit from restrict
in the cases that don't.
Unfortunately, the authors of the rules don't seem to have made any particular effort to consider all possible corner cases and ensure that the rules in the Standard could be sensibly applied to all of them. There are two usage cases where the semantics of restrict
are clear and sensible:
A function receives a restrict
-qualified pointer, in which case the qualifier should affect the pointer value passed to the function, independent of anything else that might be stored in the argument.
The definition of an automatic object with a restrict qualifier includes an initialization expression, in which case the qualifier should affect the pointer value used for the initialization, independent of anything else that might be stored in the object.
The semantics of having restrict "guard" anything other than an address stored into a pointer via one of the two means above are rather murky at best, even if one tries to add scope rules like those in the Standard. Adding restrict qualifiers in other cases would be more likely to break something as to have a useful effect, even though the most likely outcome would be that the qualifiers would have no effect whatsoever.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With