diff mbox

reject decl with incomplete struct/union type in check_global_declaration()

Message ID CAAgBjMmY0n=UaVTxXe+mPCSysYhPibsxOySihxt7TLH=ai3B1g@mail.gmail.com
State New
Headers show

Commit Message

Prathamesh Kulkarni Jan. 14, 2016, 4:34 p.m. UTC
Hi,
For test-case containing only the following declaration:
static struct undefined_struct object;
gcc rejects it at -O0 in assemble_variable() with error "storage size
of <var> is unknown",
however no error is reported when compiled with -O2.
AFAIU that happens because at -O2, analyze_function() removes the
symbol "object" from symbol table and assemble_variable() has no
chance to process it.
g++ rejects it during parsing. I tried similarly in C FE by adding a
check for decl with incomplete struct/union type in finish_decl(),
however that fails to compile the following case:
typedef struct foo foo_t;
foo_t x;
struct foo { int i; };
g++ rejects the above case as well but gcc accepts it.
Do C and C++ standards differ in this regard ?

So instead of finish_decl(), I added check for incomplete struct/union
in check_global_declaration(),
which is called by analyze_function() before removing unused nodes.
The patch regresses Wcxx-compat-8.c and declspecs-1.c because the error
"storage size of <var> is unknown" shows up in these test-cases. I
modified the test-cases
to accept that error.
Does the patch look OK ?

Thank you,
Prathamesh

Comments

Joseph Myers Jan. 14, 2016, 9:57 p.m. UTC | #1
On Thu, 14 Jan 2016, Prathamesh Kulkarni wrote:

> Hi,
> For test-case containing only the following declaration:
> static struct undefined_struct object;
> gcc rejects it at -O0 in assemble_variable() with error "storage size
> of <var> is unknown",
> however no error is reported when compiled with -O2.

Cf bug 24293 (for the -fsyntax-only case) - does this patch fix that?

> g++ rejects it during parsing. I tried similarly in C FE by adding a
> check for decl with incomplete struct/union type in finish_decl(),
> however that fails to compile the following case:
> typedef struct foo foo_t;
> foo_t x;
> struct foo { int i; };
> g++ rejects the above case as well but gcc accepts it.
> Do C and C++ standards differ in this regard ?

I don't know about C++, but this sort of thing is valid C if the type is 
complete at the end of the translation unit or is an incomplete array type 
(but not in the case where the variable is static - such a case with 
static, if supported, is an extension).
Nathan Sidwell Jan. 14, 2016, 10:08 p.m. UTC | #2
On 01/14/16 16:57, Joseph Myers wrote:
> On Thu, 14 Jan 2016, Prathamesh Kulkarni wrote:

>> g++ rejects it during parsing. I tried similarly in C FE by adding a
>> check for decl with incomplete struct/union type in finish_decl(),
>> however that fails to compile the following case:
>> typedef struct foo foo_t;
>> foo_t x;
>> struct foo { int i; };
>> g++ rejects the above case as well but gcc accepts it.
>> Do C and C++ standards differ in this regard ?
>
> I don't know about C++, but this sort of thing is valid C if the type is
> complete at the end of the translation unit or is an incomplete array type
> (but not in the case where the variable is static - such a case with
> static, if supported, is an extension).

It's ill-formed C++.

7.1.1/8 allows you to use a declared but undefined struct with 'extern', but not 
  without it.

'The name of a declared but undefined class can be used in an extern 
declaration. Such a declaration can only be used in ways that do not require a 
complete class type'

(Although it doesn't seem to explicitly say that such a name can be used without 
an 'extern', the implication is that it cannot).

nathan
diff mbox

Patch

diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 950f6c5..1560a78 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -974,6 +974,12 @@  check_global_declaration (symtab_node *snode)
 		   ? OPT_Wunused_const_variable
 		   : OPT_Wunused_variable),
 		"%qD defined but not used", decl);
+
+  if (VAR_P (decl) && !DECL_EXTERNAL (decl)
+      && RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) && DECL_SIZE (decl) == 0)
+    {
+      error ("storage size of %q+D isn%'t known", decl);
+    }
 }
 
 /* Discover all functions and variables that are trivially needed, analyze
diff --git a/gcc/testsuite/gcc.dg/Wcxx-compat-8.c b/gcc/testsuite/gcc.dg/Wcxx-compat-8.c
index f7e8c55..4e9ddc1 100644
--- a/gcc/testsuite/gcc.dg/Wcxx-compat-8.c
+++ b/gcc/testsuite/gcc.dg/Wcxx-compat-8.c
@@ -33,6 +33,7 @@  enum e3
 
 __typeof__ (struct s5 { int i; }) v5; /* { dg-warning "invalid in C\[+\]\[+\]" } */
 __typeof__ (struct t5) w5; /* { dg-bogus "invalid in C\[+\]\[+\]" } */
+  /* { dg-error "storage size of 'w5' isn't known" "" { target *-*-* } 35 } */
 
 int
 f1 (struct s1 *p)
@@ -64,4 +65,4 @@  f5 ()
   return &((struct t8) { });  /* { dg-warning "invalid in C\[+\]\[+\]" } */
 }
 
-/* { dg-error "invalid use of undefined type" "" { target *-*-* } 64 } */
+/* { dg-error "invalid use of undefined type" "" { target *-*-* } 65 } */
diff --git a/gcc/testsuite/gcc.dg/declspec-1.c b/gcc/testsuite/gcc.dg/declspec-1.c
index c19f107..9113076 100644
--- a/gcc/testsuite/gcc.dg/declspec-1.c
+++ b/gcc/testsuite/gcc.dg/declspec-1.c
@@ -9,7 +9,8 @@  typedef int t;
 /* These should all be diagnosed, but only once, not for every
    identifier declared.  */
 struct s0 int x0, /* { dg-error "two or more data types" } */
-x1;
+/* { dg-error "storage size of 'x0' isn't known" "" { target *-*-* } 11 } */
+x1; /* { dg-error "storage size of 'x1' isn't known" } */ 
 
 char union u0 x2, /* { dg-error "two or more data types" } */
 x3;
diff --git a/gcc/testsuite/gcc.dg/struct-incompl-2.c b/gcc/testsuite/gcc.dg/struct-incompl-2.c
new file mode 100644
index 0000000..f3957eb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/struct-incompl-2.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+static struct foo x;  /* { dg-error "storage size of 'x' isn't known" } */
+static union bar y;  /* { dg-error "storage size of 'y' isn't known" } */
+
+typedef struct P p;
+static p p_obj;  /* { dg-error "storage size of 'p_obj' isn't known" } */
+
+extern struct undefined_object object;