Home > Back-end >  Can the second and third operands of conditional operator have incomplete object types?
Can the second and third operands of conditional operator have incomplete object types?

Time:12-22

Per C11 (as well as per C2x) I understand that the second and third operands of conditional operator can have incomplete object types. Is that correct?

Code:

struct x y;
void f(void) { 1 ? y : y; }

Invocations:

$ clang t0.c -std=c11 -pedantic -Wall -Wextra
t0.c:2:24: warning: expression result unused [-Wunused-value]

$ gcc t0.c -std=c11 -pedantic -Wall -Wextra
t0.c: In function 'f':
t0.c:2:20: error: 'y' has an incomplete type 'struct x'
    2 | void f(void) { 1 ? y : y; }
      |                    ^
t0.c:2:24: error: 'y' has an incomplete type 'struct x'
    2 | void f(void) { 1 ? y : y; }

$ cl t0.c /std:c11
t0.c(1): error C2079: 'y' uses undefined struct 'x'

$ icc t0.c -std=c11 -pedantic -Wall -Wextra
t0.c(2): error: incomplete type is not allowed
  void f(void) { 1 ? y : y; }
                     ^
t0.c(2): error: incomplete type is not allowed
  void f(void) { 1 ? y : y; }
                         ^

CodePudding user response:

Apple Clang 11.0.0 accepts this code without complaint, with -c -Wmost -Werror -std=c18 -pedantic:

extern struct x y;
void f(void) { (void) (1 ? y : y); }

This is curious because the type of y is incomplete when f is being analyzed, so it implies the compiler analyzes the code sufficiently to determine the result of the conditional operator is not used and so the incompleteness of its operands is irrelevant.

In any case, C 2018 6.3.2.1 2 says:

Except when it is the operand of the sizeof operator, the unary & operator, the operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion… If the lvalue has an incomplete type and does not have array type, the behavior is undefined.

This is not in a constraints paragraph, so a C implementation is not required to diagnose it.

However, the rule is puzzling. Lvalue conversion nominally occurs during program execution, since it needs the values stored in the bytes of an object. However, whether a type is complete or incomplete is a translation-time property; C 2018 6.2.5 1 says “… At various points within a translation unit an object type may be incomplete (lacking sufficient information to determine the size of objects of that type) or complete (having sufficient information).” If struct x { int i; } appeared after f, would the operands of the conditional operator be complete (because the type was completed before execution and hence before lvalue conversion occurred) or incomplete (because the type was incomplete when f was analyzed)?

In any case, given that the operand type is incomplete, the behavior is not defined by the C standard. A compiler is free to compile it or to reject it. (Since the behavior is not defined, if the compiler does compile it, it is allowed to compile it into any code.)

CodePudding user response:

Not sure where you got that idea from. Like the compiler is telling you: no, you can't use incomplete types because C doesn't even allow you to use an object of incomplete type in any expression and that's not related to the ?: operator as such.

What you can do with the ?: operator however, is to use pointers to (qualified) objects of incomplete type. As long as the objects are compatible, then that is fine.

// strictly conforming C program
struct x* y;
struct x* z;
void f(void) { 1 ? y : z; }

For this to make sense, struct x must be defined somewhere though. But not necessarily in the same translation unit.

  • Related