-Wcast-qual consistency with initialization conversion and double pointer types

2024-06-15 Thread Ryan Libby via Gcc
I'm not a C language expert and I'm looking for advice on whether a
-Wcast-qual diagnostic in one situation and not another is intentional
behavior.

Here's a set of examples (same as attachment).

% cat cast-qual-example.c
#define F(name, type, qual) \
typedef type t_##name;  \
void name(void) {   \
t_##name x = 0, y, z;   \
y = *(t_##name qual *)&x;   \
z = *(t_##name qual *){&x}; \
}

F(fcc, char, const)
F(fpc, char *, const)
F(fcv, char, volatile)
F(fpv, char *, volatile)

void fpv2(void) {
char *x = 0, *y, *z;
y = *(char * volatile *)&x;
z = *(char * volatile *){&x};
}

void eg1(void) {
/* Adapted from -Wcast-qual doc */
char v0 = 'v';
char *v1 = &v0;
char **p = &v1;
/* p is char ** value.  */
char * volatile *q = (char * volatile *) p;
/* Assignment of volatile pointer to char is OK. */
char u0 = 'u';
char * volatile u1 = &u0;
*q = u1;
/* Now *q is accessed through a non-volatile-qualified pointer. */
*p = 0;
}

void eg2(void) {
char v = 'v';
char *p = &v;
/* p is char * value.  */
char volatile *q = (char volatile *) p;
/* Assignment of volatile char is OK (and also plain char). */
char volatile u = 'u';
*q = u;
/* Now *q is accessed through a non-volatile-qualified pointer. */
*p = 0;
}

% gcc13 -std=c17 -Wall -Wextra -Wcast-qual -Wno-unused -c
cast-qual-example.c -o /dev/null
cast-qual-example.c: In function 'fpv':
cast-qual-example.c:5:14: warning: to be safe all intermediate
pointers in cast from 'char **' to 'char * volatile*' must be 'const'
qualified [-Wcast-qual]
5 | y = *(t_##name qual *)&x;   \
  |  ^
cast-qual-example.c:12:1: note: in expansion of macro 'F'
   12 | F(fpv, char *, volatile)
  | ^
cast-qual-example.c: In function 'fpv2':
cast-qual-example.c:16:14: warning: to be safe all intermediate
pointers in cast from 'char **' to 'char * volatile*' must be 'const'
qualified [-Wcast-qual]
   16 | y = *(char * volatile *)&x;
  |  ^
cast-qual-example.c: In function 'eg1':
cast-qual-example.c:26:30: warning: to be safe all intermediate
pointers in cast from 'char **' to 'char * volatile*' must be 'const'
qualified [-Wcast-qual]
   26 | char * volatile *q = (char * volatile *) p;
  |  ^
% clang -std=c17 -Wall -Wextra -Wcast-qual -Wno-unused -c
cast-qual-example.c -o /dev/null
%

The macro and typedef are to illustrate the point, they aren't otherwise
needed, and fpv2 shows the same thing without them.

So, in the conversion of char ** to char * volatile *, the cast before
the assignment of y is diagnosed, but the conversion in the
initialization of the compound literal for the assignment of z is not.

First, is the cast construct actually different from the initialization
construct in terms of safety?  I would think not, but maybe I am
missing something.

I think that both assignment expressions in fpv as a whole are
ultimately safe, considering also the immediate dereference of the
temporary outer pointer value.

In eg1 and eg2 I modified examples from the -Wcast-qual documentation.
eg1 is diagnosed, eg2 is not.

I think that the *p assignment in eg1 might be undefined behavior
(6.7.3, referring to an object with volatile-qualified type (*q) through
an lvalue without volatile-qualified type (*p)).

But then I don't get why the same wouldn't be true if we take away the
inner pointer and repeat the exercise with plain char (eg1 vs eg2).

So, what's going on here?  Is the gcc behavior intentional?  Is it
consistent?  And is there a recommended way to construct a temporary
volatile pointer to an object (which may itself be a pointer) without
tripping -Wcast-qual, without just casting away type information (as in,
without intermediate casts through void *, uintptr_t, etc), and
preferably also without undefined behavior?

I have checked that the behavior is the same with current sources and
-std=c23 (gcc (GCC) 15.0.0 20240614 (experimental)).

P.s. I have seen gcc bug 84166 that advises that the -Wcast-qual warning
from the cast is intentional in that case.  I think this case is
different because in that case the qualifiers are on the innermost type.

Thank you,

Ryan Libby
#define	F(name, type, qual)		\
typedef type t_##name;			\
void name(void) {			\
	t_##name x = 0, y, z;		\
	y = *(t_##name qual *)&x;	\
	z = *(t_##name qual *){&x};	\
}

F(fcc, char, const)
F(fpc, char *, const)
F(fcv, char, volatile)
F(fpv, char *, volatile)

void fpv2(void) {
	char *x = 0, *y, *z;
	y = *(char * volatile *)&x;
	z = *(char * volatile *){&x};
}

void eg1(void) {
	/* Adapted from -Wcast-qual doc */
	char v0 = 'v';
	char *v1 = &v0;
	char **p = &v1;
	/* p is char ** value.  */
	char * volatile *q = (char * volatile *) p;
	/* Assignment of vo

gcc-14-20240615 is now available

2024-06-15 Thread GCC Administrator via Gcc
Snapshot gcc-14-20240615 is now available on
  https://gcc.gnu.org/pub/gcc/snapshots/14-20240615/
and on various mirrors, see https://gcc.gnu.org/mirrors.html for details.

This snapshot has been generated from the GCC 14 git branch
with the following options: git://gcc.gnu.org/git/gcc.git branch 
releases/gcc-14 revision 3fe255fd3f9b7f93c69d11d4bb0963793fc5edd4

You'll find:

 gcc-14-20240615.tar.xz   Complete GCC

  SHA256=e467196b1515a4fe03673b9d25299a7072e7965dac4ab65e8e6e6af65167edab
  SHA1=e9feacbc52dfdc1fd86debc95416a617ff780084

Diffs from 14-20240608 are available in the diffs/ subdirectory.

When a particular snapshot is ready for public consumption the LATEST-14
link is updated and a message is sent to the gcc list.  Please do not use
a snapshot before it has been announced that way.