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 */ /* type_decl() types */
#define TYPE_ABSTRACT 1 /* type without variable */ #define TYPE_ABSTRACT 1 /* type without variable */
#define TYPE_DIRECT 2 /* type with variable */ #define TYPE_DIRECT 2 /* type with variable */
#define TYPE_PARAM 4 /* type declares function parameter */
#define IO_BUF_SIZE 8192 #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); unlink(filename);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
if (fd < 0 || (f = fdopen(fd, "wb")) == NULL) { 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; return -1;
} }
if (s1->verbose) if (s1->verbose)

View file

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

View file

@ -416,4 +416,30 @@ void func()
fink(); fink();
} }
__attribute__((stuff)) int fink() {return 0;} __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 #endif

View file

@ -203,3 +203,16 @@ bar : 3 ; 3
[test_switch_W4] [test_switch_W4]
60_errors_and_warnings.c:416: warning: implicit declaration of function 'fink' 60_errors_and_warnings.c:416: warning: implicit declaration of function 'fink'
60_errors_and_warnings.c:418: error: 'stuff' attribute ignored 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]