3

Why is it not allowed to discard restrict qualifier in this case?

void func(double * const *q) {
    q[0][0] = 10.0;
}

int main(void) {
    double a = 5.0;
    double * restrict p = &a;
    double * restrict const *q = &p;

    // GCC: warning: passing argument 1 of 'func' discards 'restrict' qualifier
    // from pointer target type [-Werror=discarded-qualifiers]
    func(q);

    return 0;
}

The error seems unintuitive as a restrict pointer can be casted to a non-restricted, and here with a const I make sure that func cannot replace p with a non-restricted pointer.

ikicic
  • 98
  • 1
  • 7
  • 1
    The warning says that `func` *is allowed* to modify the parameter. That it might not do that *right now* doesn't change the fact that it could. – Bo Persson Feb 22 '18 at 11:44
  • The `restrict` qualifier instruct the compiler that you will use only this pointer to access the pointed memory, but can't check if you really do so (in the main code or in a function). From the function side you are declaring a `const` pointer that can be copied in a different pointer inside the function allowing a non restrict access, in this case the compiler couldn't generate warnings while compiling the function. Passing the pointer without an explicit cast is then signaled as a possible source of problems inside the function eventually not voluntary.. – Frankie_C Feb 22 '18 at 12:19
  • Can't the same argument about making copies be used when sending `double * restrict` to some function taking only `double *`? Then again you duplicate the pointer and get non-restrict access. – ikicic Feb 22 '18 at 20:37

1 Answers1

0

The general rule for all type qualifiers is that you can safely and implicitly convert from pointer-to-type to qualified-pointer-to-type. This is guaranteed by 6.3.2.3 as well as the rules of simple assignment 6.5.16.1 (which apply during parameter passing). The same applies to all qualifiers: const, volatile, restrict and _Atomic.

This doesn't mean that you can go the other way around. You can never drop a qualifier implicitly, you have to use an explicit cast. And when you do so, you invoke implementation-defined behavior.

In this case the cast is perfectly safe: func((double*const *)q);.

Notably, restrict makes the most sense when used on function parameters and you call those functions from another translation unit. To use restrict within a local scope is likely not very meaningful, as the compiler can easily deduce if the variables involved in an expression point at different addresses or the same one.

Overall, it is a "contract" between the programmer and the compiler, where the programmer promises not to access the pointed-at data through any other variable. The programmer can however easily break this contract by casting away the restrict, and then anything can happen.

Lundin
  • 195,001
  • 40
  • 254
  • 396