Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

struct storage size error not reported by GCC with optimization turned on

In a file struct_test.c I write:

static struct x x; 

and I compile with GCC (4.8.2, Ubuntu)

gcc -c -O0 struct_test.c

I get the understandable error:

error: storage size of 'x' isn't known

Then I recompile, this time with optimization turned on

gcc -c -O struct_test.c

and the file is compiled without error.

I can kind of understand why turning on optimization removed the error but can someone help me to get the actual compiler option which is instructing gcc to ignore the undefined tag 'x'?

like image 741
fsheikh Avatar asked Sep 28 '22 01:09

fsheikh


1 Answers

When it appears at file scope, this code:

static struct x x;

declares x as an object of type struct x and having internal linkage. The type struct x need not have previously been declared:

If a type specifier of the form struct-or-union identifier occurs other than [in a struct type declaration], and no other declaration of the identifier as a tag is visible, then it declares an incomplete structure or union type, and declares the identifier as the tag of that type.

(C99 6.7.2.3/8)

A structure or union type that is incomplete when first encountered in a translation unit can nevertheless be completed later in that translation unit:

A structure or union type of unknown content [...] is an incomplete type. It is completed, for all declarations of that type, by declaring the same structure or union tag with its defining content later in the same scope.

(C99 6.2.5/22)

C discriminates between "declarations" generally, which specify the type of an object, and "definitions", which both specify the type and cause storage to be reserved. In a given translation unit, declarations that are not definitions may have incomplete type that is never completed within that unit.

Section 6.9.2 of C99 specifies which file-scope object declarations are definitions:

  • a declaration with file scope and an initializer is a definition (specifically, an "external definition", even though the object it defines may have internal linkage)
  • declaration with file scope and no initializer, and having static storage class or no storage class specifier is a "tentative definition". If the translation unit contains no external definition of the same object, then behavior is as if there were an external definition having initializer 0.

This section also specifies that "If the declaration of an identifier for an object is a tentative definition and has internal linkage, the declared type shall not be an incomplete type." That applies here, but since a type that is incomplete at one point in the translation unit can be completed by a later declaration, it does not inherently invalidate the line of code in question. However, it does invalidate a translation unit containing only that line.

So what's up with GCC?

Inasmuch as the code given, taken as a complete translation unit, violates a "shall not" language constraint, it yields undefined behavior. GCC is not obligated to reject the code or produce any kind of diagnostic, though it is permitted to do either or both. As it turns out, regardless of optimization level, gcc does issue a warning about the code if the -Wall or -pedantic flag is turned on. Any way around, gcc explicitly disclaims being a tool for validating standard compliance of your code.

This particular violation is relatively benign, however. Because the object is never referenced, gcc can pretend it was never even declared without risking wrong or unintended behavior.

The -fdce (dead code elimintation) option seems to be one that affects this particular behavior. Oddly, although -fdce is enough to suppress the error at optimization level O0, -fno-dce does not bring it back at higher optimization levels.

like image 109
John Bollinger Avatar answered Oct 13 '22 00:10

John Bollinger