Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/frontc/cparser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ let transformOffsetOf (speclist, dtype) member =

%type <Cabs.init_name> init_declarator
%type <Cabs.init_name list> init_declarator_list
%type <Cabs.name> declarator
%type <Cabs.name> declarator declarator_init_start
%type <Cabs.name * expression option> field_decl
%type <(Cabs.name * expression option) list> field_decl_list
%type <string * Cabs.decl_type> direct_decl
Expand Down Expand Up @@ -1037,8 +1037,15 @@ init_declarator_attr:
;
init_declarator: /* ISO 6.7 */
declarator { ($1, NO_INIT) }
| declarator EQ init_expression location
{ let (n, d, a, l) = $1 in ((n, d, a, joinLoc l $4), $3) }
| declarator_init_start init_expression location
{ let (n, d, a, l) = $1 in ((n, d, a, joinLoc l $3), $2) }
;

/* (* Parses "declarator =" and adds the declared name as a variable identifier
in the lexer hack, so that in the initializer the name shadows any typedef
with the same name (C11 6.2.1.7: scope begins after completion of declarator). *) */
declarator_init_start:
declarator EQ { let (n, _, _, _) = $1 in !Lexerhack.add_identifier n; $1 }
Comment thread
michael-schwarz marked this conversation as resolved.
Comment on lines +1053 to +1065
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not do the lexer hack in declarator, instead of duplicating it at both new rules?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving the registration into declarator itself would fire in every context where declarator is used — including field_decl (struct members), parameter_decl (function parameters), function_def_start (function definitions), and old_pardef (K&R-style parameters). All of those must not trigger lexer registration. The two helper rules are the minimal way to isolate the registration to the init_declarator context, while also handling the semantic difference: declarator_no_init must conditionally call add_type or add_identifier (based on is_typedef_decl), whereas declarator_init_start always calls add_identifier (typedef declarations cannot have initializers).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interesting question here is: where exactly in the C standard is it stated that typedefs cannot have initializers?
Of course, I don't know what it would mean, but it'd also be strange if it isn't forbidden.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, the grammar doesn't explicitly ban it, but one can get some kind of semantic ban out of

6.7.9 (8) An initializer specifies the initial value stored in an object.

C11 draft

as typedefs do not define objects.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, there's no need for two new grammar rules. The declarator_no_init would suffice and could be reused also in the EQ case. It just needs a suitable name then.

Having declarator_init_start hardcode add_identifier is odd because the grammar doesn't forbid typedef initializers. As is, the parser accepts such a thing, but adds it to the lexer hack with the wrong kind which will screw up all the following parsing.
It seems more reasonable to me to just use the same logic for both and not try to be clever about it if the grammar was never intended to be so precise.

Instead, I would expect Cabs2cil to reject a typedef with initializer. I haven't checked if it does but surely it cannot use it for anything meaningful.
Either way, then the following lexer hack state wouldn't be affected by the strange but valid grammatical construct.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what exactly you suggest here. If you're happy with the rest of it feel free to push directly to this branch. I'll have a look at it and then we can merge.

;

decl_spec_list_common: /* ISO 6.7 */
Expand Down
25 changes: 25 additions & 0 deletions test/small1/typedef_varname.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Test for typedef and variable name conflict (issue #114).
A variable with the same name as a typedef should shadow it in its initializer,
per C11 6.2.1.7: "Any other identifier has scope that begins just after the
completion of its declarator." */
#include <stdint.h>
#include <stdlib.h>

typedef struct raxNode {
uint32_t iskey:1;
uint32_t isnull:1;
uint32_t iscompr:1;
uint32_t size:29;
unsigned char data[];
} raxNode;

typedef struct rax {
raxNode *head;
uint64_t numele;
uint64_t numnodes;
} rax;

int main() {
rax *rax = malloc(sizeof(*rax)); /* variable rax shadows typedef rax in initializer */
return rax == 0;
}
Comment thread
michael-schwarz marked this conversation as resolved.
1 change: 1 addition & 0 deletions test/testcil.pl
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ sub addToGroup {
addTest("testrun/for1 ");
addTest("testrun/void _GNUCC=1");
addTest("test/voidtypedef ");
addTest("testrun/typedef_varname ");
addTest("testrun/wrongnumargs ");
addBadComment("testrun/wrongnumargs",
"Notbug. Should fail since we don't pad argument lists");
Expand Down
Loading