aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--decl.c29
-rw-r--r--qbe.c8
-rw-r--r--tests/compatible-function-types.c17
-rw-r--r--tests/compatible-function-types.qbe16
-rw-r--r--type.c31
-rw-r--r--type.h2
6 files changed, 77 insertions, 26 deletions
diff --git a/decl.c b/decl.c
index 3e574b2..cb2e2f4 100644
--- 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
index 6107bf8..7c01202 100644
--- 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
new file mode 100644
index 0000000..0e63a1c
--- /dev/null
+++ 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
new file mode 100644
index 0000000..27d9b32
--- /dev/null
+++ 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
index 7414f0a..74e7094 100644
--- 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
index d02df31..d985a4a 100644
--- 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 {