Skip to content

Tags inside constant expressions are handled incorrectly #601

Description

@dranov
struct P { int x; int y; };

int main(void) {
  char a[sizeof(struct P { int y; int x; })];   /* declares a new struct P in this block */
  struct P v = {1, 2};                           /* must name the inner P: v.x == 2, v.y == 1 */
  return v.x * 10 + v.y;                         /* 21 */
}
exit code
CompCert 3.18 (74cbdbf0), -std=c11 -Wall 12, no warning
CompCert 3.18, -interp 12 ("program terminated (exit code = 12)")
GCC 16.2, -std=c11 -pedantic -Wall 21
Clang 22.1.8, -std=c11 -pedantic -Wall 21

CompCert binds struct P v to the file-scope struct P, which is incorrect.

A similar issue seems to have been fixed for initializers in commit 0375c183, tested in CompCert-small-tests/regression/decl2.c

Here is a more comprehensive suite of similar problems:

/* Declarations inside constant expressions must stay in scope.

   C11 lets a type specifier declare a tag, and an enumerator list declare
   enumeration constants, anywhere a type name or constant expression may
   appear.  Their scope is the enclosing block (6.2.1p4, 6.7.2.3p6), whether
   or not the expression is evaluated.  In every test below such a
   declaration hides a same-spelled outer one, so a later use must see the
   inner declaration:

     - tag tests declare `struct P { int y; int x; }` over the file-scope
       `struct P { int x; int y; }`; `struct P v = {1, 2}` then gives
       x = 2, y = 1 and the test returns 21 (12 means the outer P was used);
     - constant tests declare `enum { K = 5 }` over the file-scope
       `enum { K = 1 }`, and return K: 5 (1 means the outer K was used).

   Build: cc -std=c11 compcert_scope_bugs.c && ./a.out   (gcc, clang, ccomp) */

#include <stdio.h>

struct P { int x; int y; };
enum { K = 1 };

#define USE_P  struct P v = {1, 2}; return v.x * 10 + v.y

/* (A) An array bound, in any declarator or type name. */
static int a1_object_bound(void)   { char a[sizeof(struct P { int y; int x; })]; (void)a; USE_P; }
static int a2_typedef_bound(void)  { typedef char B[sizeof(struct P { int y; int x; })]; USE_P; }
static int a3_member_bound(void)   { struct O { char b[sizeof(struct P { int y; int x; })]; }; USE_P; }
static int a4_cast_type_name(void) { (void)(char (*)[sizeof(struct P { int y; int x; })])0; USE_P; }
static int a5_sizeof_type_name(void) { (void)sizeof(char[sizeof(struct P { int y; int x; })]); USE_P; }
static int a6_compound_literal(void) { (void)(char[sizeof(struct P { int y; int x; })]){0}; USE_P; }
static int a7_parameter_bound(char a[sizeof(struct P { int y; int x; })]) { (void)a; USE_P; }
static int a8_bound_constant(void) { char a[sizeof(enum { K = 5 })]; (void)a; return K; }
static int a9_generic_association(void) {
  (void)_Generic(0, char (*)[sizeof(struct P { int y; int x; })]: 1, default: 2);
  USE_P;
}
static int a10_return_type_bound(void) { char (*g(void))[sizeof(struct P { int y; int x; })]; (void)g; USE_P; }

/* (B) An enumerator's value. */
static int b1_enumerator_tag(void)      { enum { J = sizeof(struct P { int y; int x; }) }; (void)J; USE_P; }
static int b2_enumerator_constant(void) { enum { J = sizeof(enum { K = 5 }) }; (void)J; return K; }

/* (C) A static assertion, in a block or in a member list. */
static int c1_static_assert_tag(void) { _Static_assert(sizeof(struct P { int y; int x; }) > 0, ""); USE_P; }
static int c2_member_static_assert(void) {
  struct O { int m; _Static_assert(sizeof(struct P { int y; int x; }) > 0, ""); };
  USE_P;
}
static int c3_static_assert_constant(void) { _Static_assert(sizeof(enum { K = 5 }) > 0, ""); return K; }

/* (D) An alignment specifier, with a type name or an expression. */
static int d1_alignas_type(void)       { _Alignas(struct P { int y; int x; }) char c; (void)c; USE_P; }
static int d2_alignas_expression(void) { _Alignas(sizeof(struct P { int y; int x; })) char c; (void)c; USE_P; }

/* (E) An array designator in an initializer. */
static int e1_designator_tag(void) {
  int a[2] = { [sizeof(struct P { int y; int x; }) > 0] = 1 };
  (void)a;
  USE_P;
}
static int e2_designator_constant(void) { int a[2] = { [sizeof(enum { K = 5 }) > 0] = 1 }; (void)a; return K; }

/* (F) An initializer (fixed in CompCert master by commit 0375c183). */
static int f1_initializer(void) { unsigned long n = sizeof(struct P { int y; int x; }); (void)n; USE_P; }

static int failures = 0;

static void check(const char *name, int value, int expected) {
  printf("%-28s %3d  %s\n", name, value, value == expected ? "ok" : "WRONG");
  if (value != expected)
    failures++;
}

int main(void) {
  check("A1 object array bound", a1_object_bound(), 21);
  check("A2 typedef array bound", a2_typedef_bound(), 21);
  check("A3 member array bound", a3_member_bound(), 21);
  check("A4 bound in a cast", a4_cast_type_name(), 21);
  check("A5 bound in sizeof(type)", a5_sizeof_type_name(), 21);
  check("A6 bound in compound literal", a6_compound_literal(), 21);
  check("A7 parameter array bound", a7_parameter_bound(0), 21);
  check("A8 bound, enum constant", a8_bound_constant(), 5);
  check("A9 bound in _Generic type", a9_generic_association(), 21);
  check("A10 bound in return type", a10_return_type_bound(), 21);
  check("B1 enumerator value, tag", b1_enumerator_tag(), 21);
  check("B2 enumerator value, const", b2_enumerator_constant(), 5);
  check("C1 _Static_assert, tag", c1_static_assert_tag(), 21);
  check("C2 member _Static_assert", c2_member_static_assert(), 21);
  check("C3 _Static_assert, const", c3_static_assert_constant(), 5);
  check("D1 _Alignas(type)", d1_alignas_type(), 21);
  check("D2 _Alignas(expression)", d2_alignas_expression(), 21);
  check("E1 designator, tag", e1_designator_tag(), 21);
  check("E2 designator, const", e2_designator_constant(), 5);
  check("F1 initializer", f1_initializer(), 21);
  return failures != 0;
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions