Al Viro wrote: > There's an unpleasant case in conditional operator we are getting > wrong. > int *p; > const void *v; > int n; > > n ? p : (const void *)0 > > According to C standard, the type of that expression is const void *. Note > that > n ? p : (void *)0 > is an entirely different story - it's int *. That much actually makes sense to me. You can convert from (int *) to (const void *), but not from (const void *) to (int *), so I'd expect the behavior of the first case. You *can* convert bidirectionally between (void *) and (int *), so I expect the behavior of the second case as well. > What's going on here is pretty simple: there are two degenerate cases of > conditional operator: pointer vs. null pointer constant and pointer vs. > possibly qualified pointer to void. Look at these cases: > n ? p : NULL => should be the same type as p > n ? p : v => clearly const void * - pointer to void with union of > qualifiers; in this case we obviously lose any information about the type > of object being pointed to. I didn't actually know about the special case for a null pointer constant. > The tricky part comes from definition of what null pointer constant _is_. > C allows two variants - integer constant expression with value 0 (we accept > it, but warn about bad taste) and the same cast to void * (we also accept > that, of course). Right. > Note that this is specific type - pointer to void. Without any qualifiers. > We are guaranteed that we can convert it to any pointer type and get > a pointer distinct from address of any object. So (const void *)0 is the same > thing as (const void *)(void *)0 and it is the null pointer to const void. > *HOWEVER*, it is not a null pointer constant. The standard is clear here and > frankly, it's reasonable. If you cast to anything other than void *, then > you presumably mean it and want the conversion rules as for any pointer > of that type. Think of something like > #ifdef FOO > const void *f(int n); > #else > #define f(n) ((const void *)NULL) > #endif > You don't want to have types suddenly change under you depending on FOO. Definitely not. I don't want qualifiers to disappear just because I applied them to NULL. > sparse is more liberal than standard C in what it accepts as null pointer > constant. It almost never matters; however, in case of conditional operator > we end up with a different type for an expression both sparse and any > C compiler will accept as valid. > > I'm fixing other fun stuff in that area (e.g. we ought to take a union of > qualifiers, ought _not_ to mix different structs or unions, etc.), so > unless there are serious objections I'd rather go with standard behaviour > in that case. What will change: > > int n; > int *p; > > n ? p : (const void *)NULL int * => const void * > n ? p : (const void *)0 ditto > n ? p : (char *)0 int * => a warning on mixing int * with char * > n ? p : (char *)NULL ditto > n ? p : (void *)NULL int * => void * > n ? p : (void *)0 unchanged > n ? p : NULL unchanged > > Objections? Thanks for the clear explanation. I think following the standard seems like a good idea here, though I find some of the cases somewhat unintuitive and potentially error-prone. In particular: > n ? p : (void *)NULL int * => void * Shouldn't this have type int * just like n ? p : NULL ? - Josh Triplett
Attachment:
signature.asc
Description: OpenPGP digital signature