clanguage-lawyerrestrict-qualifier

Is the C restrict qualifier transitive through pointers?


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?


Solution

  • 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 object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.

    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.