1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

genksyms: fix 6 shift/reduce conflicts and 5 reduce/reduce conflicts

The genksyms parser has ambiguities in its grammar, which are currently
suppressed by a workaround in scripts/genksyms/Makefile.

Building genksyms with W=1 generates the following warnings:

    YACC    scripts/genksyms/parse.tab.[ch]
  scripts/genksyms/parse.y: warning: 9 shift/reduce conflicts [-Wconflicts-sr]
  scripts/genksyms/parse.y: warning: 5 reduce/reduce conflicts [-Wconflicts-rr]
  scripts/genksyms/parse.y: note: rerun with option '-Wcounterexamples' to generate conflict counterexamples

The comment in the parser describes the current problem:

    /* This wasn't really a typedef name but an identifier that
       shadows one.  */

Consider the following simple C code:

    typedef int foo;
    void my_func(foo foo) {}

In the function parameter list (foo foo), the first 'foo' is a type
specifier (typedef'ed as 'int'), while the second 'foo' is an identifier.

However, the lexer cannot distinguish between the two. Since 'foo' is
already typedef'ed, the lexer returns TYPE for both instances, instead
of returning IDENT for the second one.

To support shadowed identifiers, TYPE can be reduced to either a
simple_type_specifier or a direct_abstract_declarator, which creates
a grammatical ambiguity.

Without analyzing the grammar context, it is very difficult to resolve
this correctly.

This commit introduces a flag, dont_want_type_specifier, which allows
the parser to inform the lexer whether an identifier is expected. When
dont_want_type_specifier is true, the type lookup is suppressed, and
the lexer returns IDENT regardless of any preceding typedef.

After this commit, only 3 shift/reduce conflicts will remain.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Acked-by: Nicolas Schier <n.schier@avm.de>
This commit is contained in:
Masahiro Yamada 2025-01-14 00:00:42 +09:00
parent bc3a812b75
commit 3ccda63a3a
3 changed files with 26 additions and 23 deletions

View file

@ -12,6 +12,7 @@
#ifndef MODUTILS_GENKSYMS_H
#define MODUTILS_GENKSYMS_H 1
#include <stdbool.h>
#include <stdio.h>
#include <list_types.h>
@ -66,6 +67,8 @@ struct string_list *copy_list_range(struct string_list *start,
int yylex(void);
int yyparse(void);
extern bool dont_want_type_specifier;
void error_with_pos(const char *, ...) __attribute__ ((format(printf, 1, 2)));
/*----------------------------------------------------------------------*/

View file

@ -12,6 +12,7 @@
%{
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
@ -113,6 +114,12 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
/* The second stage lexer. Here we incorporate knowledge of the state
of the parser to tailor the tokens that are returned. */
/*
* The lexer cannot distinguish whether a typedef'ed string is a TYPE or an
* IDENT. We need a hint from the parser to handle this accurately.
*/
bool dont_want_type_specifier;
int
yylex(void)
{
@ -207,7 +214,7 @@ repeat:
goto repeat;
}
}
if (!suppress_type_lookup)
if (!suppress_type_lookup && !dont_want_type_specifier)
{
if (find_symbol(yytext, SYM_TYPEDEF, 1))
token = TYPE;

View file

@ -12,6 +12,7 @@
%{
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "genksyms.h"
@ -148,6 +149,7 @@ simple_declaration:
current_name = NULL;
}
$$ = $3;
dont_want_type_specifier = false;
}
;
@ -169,6 +171,7 @@ init_declarator_list:
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
current_name = NULL;
$$ = $1;
dont_want_type_specifier = true;
}
| init_declarator_list ',' init_declarator
{ struct string_list *decl = *$3;
@ -184,6 +187,7 @@ init_declarator_list:
is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
current_name = NULL;
$$ = $3;
dont_want_type_specifier = true;
}
;
@ -210,7 +214,7 @@ decl_specifier:
remove_node($1);
$$ = $1;
}
| type_specifier
| type_specifier { dont_want_type_specifier = true; $$ = $1; }
| type_qualifier
;
@ -307,15 +311,7 @@ direct_declarator:
current_name = (*$1)->string;
$$ = $1;
}
}
| TYPE
{ if (current_name != NULL) {
error_with_pos("unexpected second declaration name");
YYERROR;
} else {
current_name = (*$1)->string;
$$ = $1;
}
dont_want_type_specifier = false;
}
| direct_declarator '(' parameter_declaration_clause ')'
{ $$ = $4; }
@ -335,8 +331,7 @@ nested_declarator:
;
direct_nested_declarator:
IDENT
| TYPE
IDENT { $$ = $1; dont_want_type_specifier = false; }
| direct_nested_declarator '(' parameter_declaration_clause ')'
{ $$ = $4; }
| direct_nested_declarator '(' error ')'
@ -362,8 +357,9 @@ parameter_declaration_list_opt:
parameter_declaration_list:
parameter_declaration
{ $$ = $1; dont_want_type_specifier = false; }
| parameter_declaration_list ',' parameter_declaration
{ $$ = $3; }
{ $$ = $3; dont_want_type_specifier = false; }
;
parameter_declaration:
@ -375,6 +371,7 @@ abstract_declarator:
ptr_operator abstract_declarator
{ $$ = $2 ? $2 : $1; }
| direct_abstract_declarator
{ $$ = $1; dont_want_type_specifier = false; }
;
direct_abstract_declarator:
@ -385,12 +382,6 @@ direct_abstract_declarator:
remove_node($1);
$$ = $1;
}
/* This wasn't really a typedef name but an identifier that
shadows one. */
| TYPE
{ remove_node($1);
$$ = $1;
}
| direct_abstract_declarator '(' parameter_declaration_clause ')'
{ $$ = $4; }
| direct_abstract_declarator '(' error ')'
@ -440,9 +431,9 @@ member_specification:
member_declaration:
decl_specifier_seq_opt member_declarator_list_opt ';'
{ $$ = $3; }
{ $$ = $3; dont_want_type_specifier = false; }
| error ';'
{ $$ = $2; }
{ $$ = $2; dont_want_type_specifier = false; }
;
member_declarator_list_opt:
@ -452,7 +443,9 @@ member_declarator_list_opt:
member_declarator_list:
member_declarator
| member_declarator_list ',' member_declarator { $$ = $3; }
{ $$ = $1; dont_want_type_specifier = true; }
| member_declarator_list ',' member_declarator
{ $$ = $3; dont_want_type_specifier = true; }
;
member_declarator: