cproc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/cproc
Log | Files | Refs | Submodules | README | LICENSE

commit 863fec184fdb150f4f8758cc68eb6a51f9a38dfe
parent 7d746860bda62c2f382bc0ac82d4d6c8cdf6c7b2
Author: Michael Forney <mforney@mforney.org>
Date:   Sun, 17 Feb 2019 00:57:33 -0800

Improve old-style function declaration support

Implement typecompatible for types created with non-prototype function
declarations.

Require a function definition with parameter declaration list after a
declaration with a non-empty identifier list.

Detect function definitions with parameter declaration lists containing
types incompatible with the promoted types, and report an error for now.

Diffstat:
Mdecl.c | 29++++++++++++++++-------------
Mqbe.c | 8++++++++
Atests/compatible-function-types.c | 17+++++++++++++++++
Atests/compatible-function-types.qbe | 16++++++++++++++++
Mtype.c | 31+++++++++++++++++++------------
Mtype.h | 2+-
6 files changed, 77 insertions(+), 26 deletions(-)

diff --git a/decl.c b/decl.c @@ -498,6 +498,7 @@ declaratortypes(struct scope *s, struct list *result, char **name, bool allowabs break; } expect(TRPAREN, "to close function declarator"); + t->func.paraminfo = t->func.isprototype || t->func.params || tok.kind == TLBRACE; listinsert(ptr->prev, &t->link); break; case TLBRACK: /* array declarator */ @@ -816,6 +817,19 @@ decl(struct scope *s, struct function *f) t->func.isnoreturn |= fs & FUNCNORETURN; if (f && sc && sc != SCEXTERN) /* 6.7.1p7 */ error(&tok.loc, "function '%s' with block scope may only have storage class 'extern'", name); + if (!t->func.isprototype && t->func.paraminfo) { + if (!allowfunc) + error(&tok.loc, "function declaration not allowed"); + /* collect type information for parameters before we check compatibility */ + while (paramdecl(s, t->func.params)) + ; + if (tok.kind != TLBRACE) + error(&tok.loc, "function declaration with identifier list is not part of definition"); + for (p = t->func.params; p; p = p->next) { + if (!p->type) + error(&tok.loc, "old-style function definition does not declare '%s'", p->name); + } + } if (d) { if (!typecompatible(t, d->type)) error(&tok.loc, "function '%s' redeclared with incompatible type", name); @@ -838,19 +852,6 @@ decl(struct scope *s, struct function *f) break; } switch (tok.kind) { - default: - if (!allowfunc || kind != DECLFUNC || t->func.isprototype) - error(&tok.loc, "expected ',' or ';' after declarator"); - /* K&R-style function definition */ - while (paramdecl(s, t->func.params)) - ; - for (p = t->func.params; p; p = p->next) { - if (!p->type) - error(&tok.loc, "old-style function definition does not declare '%s'", p->name); - } - if (tok.kind != TLBRACE) - error(&tok.loc, "expected compound statement after function declarator"); - /* fallthrough */ case TLBRACE: if (!allowfunc) error(&tok.loc, "function declaration not allowed"); @@ -870,6 +871,8 @@ decl(struct scope *s, struct function *f) case TSEMICOLON: next(); return true; + default: + error(&tok.loc, "expected ',' or ';' after declarator"); } } } diff --git a/qbe.c b/qbe.c @@ -337,6 +337,12 @@ mkglobal(char *name, bool private) return v; } +/* +XXX: If a function declared without a prototype is declared with a +parameter affected by default argument promotion, we need to emit a QBE +function with the promoted type and implicitly convert to the declared +parameter type before storing into the allocated memory for the parameter. +*/ struct function * mkfunc(char *name, struct type *t, struct scope *s) { @@ -355,6 +361,8 @@ mkfunc(char *name, struct type *t, struct scope *s) for (p = t->func.params; p; p = p->next) { if (!p->name) error(&tok.loc, "parameter name omitted in function definition"); + if (!t->func.isprototype && !typecompatible(p->type, typeargpromote(p->type))) + error(&tok.loc, "old-style function definition with parameter type incompatible with promoted type is not yet supported"); emittype(p->type); d = mkdecl(DECLOBJECT, p->type, LINKNONE); p->value = xmalloc(sizeof(*p->value)); diff --git a/tests/compatible-function-types.c b/tests/compatible-function-types.c @@ -0,0 +1,17 @@ +void f1(); +void f1(int, void *); + +int f2(); +int f2(double); + +void f3() {} +void f3(void); + +void f4(int, char *); +void f4(int, char *); + +void f5(x, y) unsigned x; double y; {} +void f5(unsigned, double); + +void f6(const char *, ...); +void f6(const char[], ...); diff --git a/tests/compatible-function-types.qbe b/tests/compatible-function-types.qbe @@ -0,0 +1,16 @@ +export +function $f3() { +@start.1 +@body.2 + ret +} +export +function $f5(w %.1, d %.3) { +@start.3 + %.2 =l alloc4 4 + storew %.1, %.2 + %.4 =l alloc8 8 + stored %.3, %.4 +@body.4 + ret +} diff --git a/type.c b/type.c @@ -153,6 +153,7 @@ typerank(struct type *t) bool typecompatible(struct type *t1, struct type *t2) { + struct type *tmp; struct parameter *p1, *p2; if (t1 == t2) @@ -166,8 +167,6 @@ typecompatible(struct type *t1, struct type *t2) return typecompatible(t1->base, t2->base); case TYPEVOID: return true; - case TYPEBASIC: - return false; case TYPEPOINTER: return typecompatible(t1->base, t2->base); case TYPEARRAY: @@ -177,20 +176,28 @@ typecompatible(struct type *t1, struct type *t2) case TYPEFUNC: if (!typecompatible(t1->base, t2->base)) return false; - if (t1->func.isprototype && t2->func.isprototype) { - if (t1->func.isvararg != t2->func.isvararg) - return false; - for (p1 = t1->func.params, p2 = t2->func.params; p1 && p2; p1 = p1->next, p2 = p2->next) { - if (!typecompatible(p1->type, p2->type)) + if (!t1->func.isprototype) { + if (!t2->func.isprototype) + return true; + tmp = t1, t1 = t2, t2 = tmp; + } + if (t1->func.isvararg != t2->func.isvararg) + return false; + if (!t2->func.paraminfo) { + for (p1 = t1->func.params; p1; p1 = p1->next) { + if (!typecompatible(p1->type, typeargpromote(p1->type))) return false; } - return !p1 && !p2; + return true; } - // XXX: handle non-prototype functions - return false; - default: - return false; + for (p1 = t1->func.params, p2 = t2->func.params; p1 && p2; p1 = p1->next, p2 = p2->next) { + tmp = t2->func.isprototype ? p2->type : typeargpromote(p2->type); + if (!typecompatible(p1->type, tmp)) + return false; + } + return !p1 && !p2; } + return false; } bool diff --git a/type.h b/type.h @@ -80,7 +80,7 @@ struct type { uint64_t length; } array; struct { - _Bool isprototype, isvararg, isnoreturn; + _Bool isprototype, isvararg, isnoreturn, paraminfo; struct parameter *params; } func; struct {