Is the C restrict
keyword/qualifier transitive through multiple levels of pointer indirection?
For instance, given the following snippet:
struct Bar {
int b;
};
struct Foo
{
struct Bar* bar;
};
int examineFoos(struct Foo* restrict foo1, struct Foo* restrict foo2)
{
foo1->bar->b = 1;
return foo2->bar->b;
}
int main(void)
{
struct Bar bar = { 0 };
struct Foo foo1 = { .bar = &bar };
struct Foo foo2 = { .bar = &bar };
int val = examineFoos(&foo1, &foo2);
return val;
}
Does this invoke undefined behaviour and can val == 0
because it violates the restrict
when accessing bar
, or is it valid code and we always get val == 1
because the restrict
is not applied transitively through the bar
pointer?
The restrict
qualifications of foo1
and foo2
do not impose any limitations on use of the structures that foo1->bar
and foo2->bar
point to, as demonstrated below.
The requirements imposed upon a program by restrict
are specified in C 2018 6.7.3.1 4. These requirements apply only “If L
is used to access the value of the object X
that it designates, and X
is also modified (by any means),” where L
is an lvalue for which &L
is based upon a P
that has been designated as a restrict-qualified pointer to type T
, during execution of an associated block B
. Based is specified in 6.7.3.1 3:
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
.
The restrict-qualified pointers are foo1
and foo2
. Consider whether either foo1->bar
or foo2->bar
can be based upon either foo1
or foo2
. In other words, if E
is foo1->bar
or foo2->bar
, will its value change if the modification described above is made?
Suppose at various points in the body of examineFoos
(the block B
), we make a copy of the structures that foo1
and foo2
point to and then change foo1
and foo2
to point to the respective copies. Will the value of foo1->bar
be the same after this intervention as it is before? Yes, because the value of foo1->bar
is determined by the bytes that represent it, and those bytes have been copied. Similarly, foo2->bar
will have the same value.
Therefore, foo1->bar
and foo2->bar
are not based upon foo1
or foo2
, so the specification of restrict
does not impose any requirements in regard to using them to access the objects they point to.