Skip to content

Commit 5960536

Browse files
Add NO_INIT middle-declarator test and re-add declarator_no_init with typedef guard
Co-authored-by: michael-schwarz <13812333+michael-schwarz@users.noreply.github.com>
1 parent 363114d commit 5960536

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

src/frontc/cparser.mly

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ let smooth_expression lst =
7878

7979
let currentFunctionName = ref "<outside any function>"
8080

81+
(* Set to true while parsing a typedef declaration's declarator list, so that
82+
declarator_no_init knows not to call add_identifier for typedef names. *)
83+
let is_typedef_decl = ref false
84+
8185
let announceFunctionName ((n, decl, _, _):name) =
8286
!Lexerhack.add_identifier n;
8387
(* Start a context that includes the parameter names and the whole body.
@@ -115,6 +119,7 @@ let applyPointer (ptspecs: attribute list list) (dt: decl_type)
115119
loop ptspecs
116120

117121
let doDeclaration (loc: cabsloc) (specs: spec_elem list) (nl: init_name list) : definition =
122+
is_typedef_decl := false;
118123
if isTypedef specs then begin
119124
(* Tell the lexer about the new type names *)
120125
List.iter (fun ((n, _, _, _), _) -> !Lexerhack.add_type n) nl;
@@ -123,14 +128,8 @@ let doDeclaration (loc: cabsloc) (specs: spec_elem list) (nl: init_name list) :
123128
if nl = [] then
124129
ONLYTYPEDEF (specs, loc)
125130
else begin
126-
(* For NO_INIT declarators, add_identifier is called here after parsing.
127-
For initialized declarators, it was already called in declarator_init_start
128-
before the initializer was parsed (C11 6.2.1.7). *)
129-
List.iter (fun ((n, _, _, _), init) ->
130-
match init with
131-
| NO_INIT -> !Lexerhack.add_identifier n
132-
| _ -> ()
133-
) nl;
131+
(* add_identifier is called in declarator_no_init / declarator_init_start as each
132+
declarator is parsed (C11 6.2.1.7), so nothing to do here. *)
134133
DECDEF ((specs, nl), loc)
135134
end
136135

@@ -394,7 +393,7 @@ let transformOffsetOf (speclist, dtype) member =
394393

395394
%type <Cabs.init_name> init_declarator
396395
%type <Cabs.init_name list> init_declarator_list
397-
%type <Cabs.name> declarator declarator_init_start
396+
%type <Cabs.name> declarator declarator_no_init declarator_init_start
398397
%type <Cabs.name * expression option> field_decl
399398
%type <(Cabs.name * expression option) list> field_decl_list
400399
%type <string * Cabs.decl_type> direct_decl
@@ -1042,11 +1041,23 @@ init_declarator_attr:
10421041

10431042
;
10441043
init_declarator: /* ISO 6.7 */
1045-
declarator { ($1, NO_INIT) }
1044+
declarator_no_init { ($1, NO_INIT) }
10461045
| declarator_init_start init_expression location
10471046
{ let (n, d, a, l) = $1 in ((n, d, a, joinLoc l $3), $2) }
10481047
;
10491048

1049+
/* (* Parses "declarator" (without initializer) and immediately adds the declared name
1050+
as a variable identifier in the lexer hack, for non-typedef declarations only,
1051+
so that subsequent declarators in the same declaration see the name as an
1052+
identifier, not as a type (C11 6.2.1.7: scope begins just after the completion
1053+
of its declarator). For typedef declarations (is_typedef_decl = true), the name
1054+
is registered later via add_type in doDeclaration. *) */
1055+
declarator_no_init:
1056+
declarator { let (n, _, _, _) = $1 in
1057+
if not !is_typedef_decl then !Lexerhack.add_identifier n;
1058+
$1 }
1059+
;
1060+
10501061
/* (* Parses "declarator =" and adds the declared name as a variable identifier
10511062
in the lexer hack, so that in the initializer the name shadows any typedef
10521063
with the same name (C11 6.2.1.7: scope begins just after the completion of the
@@ -1057,7 +1068,7 @@ declarator_init_start:
10571068

10581069
decl_spec_list_common: /* ISO 6.7 */
10591070
/* ISO 6.7.1 */
1060-
| TYPEDEF decl_spec_list_opt { SpecTypedef :: $2, $1 }
1071+
| TYPEDEF decl_spec_list_opt { is_typedef_decl := true; SpecTypedef :: $2, $1 }
10611072
| EXTERN decl_spec_list_opt { SpecStorage EXTERN :: $2, $1 }
10621073
| STATIC decl_spec_list_opt { SpecStorage STATIC :: $2, $1 }
10631074
| AUTO decl_spec_list_opt { SpecStorage AUTO :: $2, $1 }

test/small1/typedef_varname.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
A variable with the same name as a typedef should shadow it in its initializer,
33
per C11 6.2.1.7: "Any other identifier has scope that begins just after the
44
completion of its declarator." */
5+
#include "testharness.h"
56
#include <stdint.h>
67
#include <stdlib.h>
78

@@ -19,7 +20,24 @@ typedef struct rax {
1920
uint64_t numnodes;
2021
} rax;
2122

23+
typedef int mytype;
24+
2225
int main() {
2326
rax *rax = malloc(sizeof(*rax)); /* variable rax shadows typedef rax in initializer */
24-
return rax == 0;
27+
if (rax == 0) E(1);
28+
free(rax);
29+
30+
/* NO_INIT variable in the middle of a declaration list shadows a typedef.
31+
After "mytype a = 1, mytype", the name "mytype" is an identifier (variable).
32+
So "b = sizeof mytype" (without parens) is only valid when mytype is a variable
33+
(types require parens: "sizeof(type)"). This fails to parse without the fix. */
34+
{
35+
mytype a = 1, mytype, b = sizeof mytype;
36+
mytype = 5;
37+
if (a != 1) E(2);
38+
if (mytype != 5) E(3);
39+
if (b != sizeof(int)) E(4);
40+
}
41+
42+
SUCCESS;
2543
}

0 commit comments

Comments
 (0)