Search code examples
cc99restrict-qualifier

Calling function with restricted arguments that are already restricted in current scope


I have trouble understanding what restrict means in terms with calling functions with already restricted variables.

Wikipedia tells me:

The restrict keyword is a declaration of intent given by the programmer to the compiler. It says that for the lifetime of the pointer, only it or a value directly derived from it (such as pointer + 1) will be used to access the object to which it points.

I have these three example functions:

void a(int const *p1, int const *p2) {
    ...
}

void b(int *restrict p1, int *restrict p2) {
    ...
}

void c(int *p1, int *p2) {
    ...
}

and I would call them each from a function

foo(int *restrict p1, int *restrict p2) {
    a(p1, p2);
    b(p1, p2);
    c(p1, p1+1);
}

which of them would keep the restrict promise that function foo declaration makes?

The three cases would be:

  1. Function a doesn't modify anything, so that would surely hold.

  2. How about b, are the parameters to it "directly derived" from the foo's pointers? Am I braking the promise I'm giving in foo declaration if I modify the p1 or p2 in b?

  3. Does the situation change in c from the previous scenario if the parameters aren't restricted in any way, and I edit for example p2 in c?


Solution

  • Here are the promises you are making:

    void foo(int *restrict p1, int *restrict p2) {
        // a() can't modify p1[...] or p2[...]
        a(p1, p2);
        // b() CAN modify p1[...] or p2[...]
        // Note that you are still making this promise if you don't use
        // "restrict" in the declaration of b()
        b(p1, p2);
        // c() CAN modify p1[...] but not p2[...]
        c(p1, p1+1);
    }
    

    You can't be sure that the promises you are making are correct unless you know what the functions do and how they are called.

    For example, this is wrong:

    int global;
    void a(int const *p1, int const *p2) {
        // Since p1 == &global, we can break the promise here
        // by accessing *p1 through the name "global"...
        // Even though this function is perfectly okay by itself!
        global = 5;
    }
    void foo(int *restrict p1, int *restrict p2) {
        // We have a promise that a() won't modify p1[...]
        // BECAUSE: "restrict" promises that all p1 modifications
        // go through p1, since p1 is passed "const" a() is not
        // supposed to modify *p1, but p1 = &global, and a() modifies
        // global... BOOM!
        // Even though this function is perfectly okay by itself...
        a(p1, p2);
    }
    int main() {
        int y;
        // Illegal!  Once you pass &global to foo(), BOOM!
        foo(&global, &y);
    }
    

    This is why restrict is a bit tricky. You can't figure out if it is correct or not based on the function signatures alone.