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.