tccgen: accept array-size expressions in function paramters

Modify function parameter parser such that symbols are
put into token-table temporarily.  Benefits are:
- detects redefinitions, as with
    int foo(int a, int a);
- detects reserved symbols, as with
    int foo(int if);
- can parse expressions like
    int main(int argc, char *argv[argc + 1]);
- doesn't fix this one
    int main(int argc, char *argv[++argc]);

Also: fix unexpected "function might return no value"
with statement expression
    int f() { ({ return 0; }); }
This commit is contained in:
grischka 2022-03-01 22:00:42 +01:00
parent 917aad3bcf
commit ec5d94291c
5 changed files with 73 additions and 16 deletions

1
tcc.h
View File

@ -643,6 +643,7 @@ typedef struct DLLReference {
/* type_decl() types */
#define TYPE_ABSTRACT 1 /* type without variable */
#define TYPE_DIRECT 2 /* type with variable */
#define TYPE_PARAM 4 /* type declares function parameter */
#define IO_BUF_SIZE 8192

View File

@ -2424,7 +2424,7 @@ static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
unlink(filename);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
if (fd < 0 || (f = fdopen(fd, "wb")) == NULL) {
tcc_error_noabort("could not write or fdopen '%s'", filename);
tcc_error_noabort("could not write '%s: %s'", filename, strerror(errno));
return -1;
}
if (s1->verbose)

View File

@ -5351,40 +5351,44 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
if (tok == '(') {
/* function type, or recursive declarator (return if so) */
next();
if (td && !(td & TYPE_ABSTRACT))
if (TYPE_DIRECT == (td & (TYPE_DIRECT|TYPE_ABSTRACT)))
return 0;
if (tok == ')')
l = 0;
else if (parse_btype(&pt, &ad1))
l = FUNC_NEW;
else if (td) {
else if (td & (TYPE_DIRECT|TYPE_ABSTRACT)) {
merge_attr (ad, &ad1);
return 0;
} else
l = FUNC_OLD;
first = NULL;
plast = &first;
arg_size = 0;
++local_scope;
if (l) {
for(;;) {
/* read param name and compute offset */
if (l != FUNC_OLD) {
if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
break;
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT | TYPE_PARAM);
if ((pt.t & VT_BTYPE) == VT_VOID)
tcc_error("parameter declared as void");
if (n == 0)
n = SYM_FIELD;
} else {
n = tok;
if (n < TOK_UIDENT)
expect("identifier");
pt.t = VT_VOID; /* invalid type */
pt.ref = NULL;
next();
}
if (n < TOK_UIDENT)
expect("identifier");
convert_parameter_type(&pt);
arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
s = sym_push(n | SYM_FIELD, &pt, 0, 0);
s = sym_push(n, &pt, 0, 0);
*plast = s;
plast = &s->next;
if (tok == ')')
@ -5402,6 +5406,13 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
/* if no parameters, then old type prototype */
l = FUNC_OLD;
skip(')');
/* remove parameter symbols from token table, keep on stack */
if (first) {
sym_pop(local_stack ? &local_stack : &global_stack, first->prev, 1);
for (s = first; s; s = s->next)
s->v |= SYM_FIELD;
}
--local_scope;
/* NOTE: const is ignored in returned type as it has a special
meaning in gcc / C++ */
type->t &= ~VT_CONSTANT;
@ -5426,7 +5437,9 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
int saved_nocode_wanted = nocode_wanted;
/* array definition */
next();
while (1) {
n = -1;
t1 = 0;
if (td & TYPE_PARAM) while (1) {
/* XXX The optional type-quals and static should only be accepted
in parameter decls. The '*' as well, and then even only
in prototypes (not function defs). */
@ -5441,11 +5454,13 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
default:
break;
}
break;
}
n = -1;
t1 = 0;
if (tok != ']') {
if (tok != ']') {
nocode_wanted = 1;
gexpr(), vpop();
}
break;
} else if (tok != ']') {
if (!local_stack || (storage & VT_STATIC))
vpushi(expr_const());
else {
@ -5469,7 +5484,7 @@ static int post_type(CType *type, AttributeDef *ad, int storage, int td)
}
skip(']');
/* parse next post type */
post_type(type, ad, storage, 0);
post_type(type, ad, storage, td & ~(TYPE_DIRECT|TYPE_ABSTRACT));
if ((type->t & VT_BTYPE) == VT_FUNC)
tcc_error("declaration of an array of functions");
@ -5580,7 +5595,7 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
expect("identifier");
*v = 0;
}
post_type(post, ad, storage, 0);
post_type(post, ad, storage, td & ~(TYPE_DIRECT|TYPE_ABSTRACT));
parse_attribute(ad);
type->t |= storage;
return ret;
@ -5973,7 +5988,9 @@ ST_FUNC void unary(void)
outside, so any reactivation of code emission (from labels
or loop heads) can be disabled again after the end of it. */
block(1);
nocode_wanted = saved_nocode_wanted;
/* or'ing to keep however possible CODE_OFF() from e.g. "return 0;"
in the statement expression */
nocode_wanted |= saved_nocode_wanted;
skip(')');
} else {
gexpr();

View File

@ -416,4 +416,30 @@ void func()
fink();
}
__attribute__((stuff)) int fink() {return 0;}
#elif defined test_invalid_funcparam_1
void func(int a, int b, int a);
#elif defined test_invalid_funcparam_2
void func(int a, int if);
#elif defined test_array_funcparam
int amain(int argc, char *argv[static argc + 1])
{
int i;
int printf(const char*, ...);
for (i = 0; i < argc; ++i)
printf("arg[%d] = \"%s\"\n", i, argv[i]);
return 0;
}
int main()
{
return amain(2, (char *[]){ "X", "Y", 0 });
}
#elif defined test_return_from_statement_expr
int f() { ({ return 78; }); }
int main() { return f(); }
/******************************************************************/
#endif

View File

@ -203,3 +203,16 @@ bar : 3 ; 3
[test_switch_W4]
60_errors_and_warnings.c:416: warning: implicit declaration of function 'fink'
60_errors_and_warnings.c:418: error: 'stuff' attribute ignored
[test_invalid_funcparam_1]
60_errors_and_warnings.c:421: error: redeclaration of 'a'
[test_invalid_funcparam_2]
60_errors_and_warnings.c:424: error: identifier expected
[test_array_funcparam]
arg[0] = "X"
arg[1] = "Y"
[test_return_from_statement_expr]
[returns 78]