Is there any practical difference between the following prototypes?
void f(const int *p);
void f(const int *restrict p);
void f(const int *volatile p);
Assuming a definition of f lacking the restrict qualifier, the code should be well-defined. C11 (n1570) 6.5.2.2 (Function calls) p7 [emph. mine, identical wording in C99 TC3 (n1256)]
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type.
The function f is called with unqualified arguments (and thus, with arguments of the correct types), and all its declarations are of compatible type (as per the quote in the question): The function call is well-defined. (If there isn't anything in the standard making it explicitly undefined. I don't think there is.)