aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2021-10-25 15:59:54 -0700
committerMichael Forney <mforney@mforney.org>2021-10-25 17:01:43 -0700
commite79005956c888e7bafb602df1137f881b34775ec (patch)
tree2267161bfb9b9cc5e065691cb75ef9679ca4b5ad
parent9540626e5793583b6feb36d025dc640c7cb97cc8 (diff)
eval: Fix int-to-float conversions
Also, add bounds checks for float-to-int conversions. If the integer part can't be represented in the result type, C behavior is undefined. Although this means the result is arbitrary, we need to avoid undefined behavior in cproc itself when given such a program as input.
-rw-r--r--eval.c28
-rw-r--r--test/initializer-cast-float-int.c1
-rw-r--r--test/initializer-cast-float-int.qbe1
-rw-r--r--test/initializer-cast-int-float.c1
-rw-r--r--test/initializer-cast-int-float.qbe1
5 files changed, 25 insertions, 7 deletions
diff --git a/eval.c b/eval.c
index 0c68051..871f917 100644
--- a/eval.c
+++ b/eval.c
@@ -91,7 +91,9 @@ eval(struct expr *expr, enum evalkind kind)
{
struct expr *l, *r, *c;
struct decl *d;
+ struct type *t;
+ t = expr->type;
switch (expr->kind) {
case EXPRIDENT:
if (expr->ident.decl->kind != DECLCONST)
@@ -102,7 +104,7 @@ eval(struct expr *expr, enum evalkind kind)
case EXPRCOMPOUND:
if (kind != EVALINIT)
break;
- d = mkdecl(DECLOBJECT, expr->type, expr->qual, LINKNONE);
+ d = mkdecl(DECLOBJECT, t, expr->qual, LINKNONE);
d->value = mkglobal(NULL, true);
emitdata(d, expr->compound.init);
expr->kind = EXPRIDENT;
@@ -130,12 +132,24 @@ eval(struct expr *expr, enum evalkind kind)
l = eval(expr->base, kind);
if (l->kind == EXPRCONST) {
expr->kind = EXPRCONST;
- if (l->type->prop & PROPINT && expr->type->prop & PROPFLOAT)
- expr->constant.f = l->constant.u;
- else if (l->type->prop & PROPFLOAT && expr->type->prop & PROPINT)
- expr->constant.u = l->constant.f;
- else
+ if (l->type->prop & PROPINT && t->prop & PROPFLOAT) {
+ if (l->type->basic.issigned)
+ expr->constant.f = l->constant.i;
+ else
+ expr->constant.f = l->constant.u;
+ } else if (l->type->prop & PROPFLOAT && t->prop & PROPINT) {
+ if (t->basic.issigned) {
+ if (l->constant.f < INT64_MIN || l->constant.f > INT64_MAX)
+ error(&tok.loc, "integer part of floating-point constant %g cannot be represented as signed integer", l->constant.f);
+ expr->constant.i = l->constant.f;
+ } else {
+ if (l->constant.f < 0 || l->constant.f > UINT64_MAX)
+ error(&tok.loc, "integer part of floating-point constant %g cannot be represented as unsigned integer", l->constant.f);
+ expr->constant.u = l->constant.f;
+ }
+ } else {
expr->constant = l->constant;
+ }
cast(expr);
} else if (l->type->kind == TYPEPOINTER) {
/*
@@ -144,7 +158,7 @@ eval(struct expr *expr, enum evalkind kind)
other forms of constant expressions (6.6p10), and some
programs expect this functionality.
*/
- if (expr->type->kind == TYPEPOINTER || expr->type->prop & PROPINT && expr->type->size == typelong.size)
+ if (t->kind == TYPEPOINTER || t->prop & PROPINT && t->size == typelong.size)
expr = l;
}
break;
diff --git a/test/initializer-cast-float-int.c b/test/initializer-cast-float-int.c
new file mode 100644
index 0000000..42296e9
--- /dev/null
+++ b/test/initializer-cast-float-int.c
@@ -0,0 +1 @@
+int x = -1.0;
diff --git a/test/initializer-cast-float-int.qbe b/test/initializer-cast-float-int.qbe
new file mode 100644
index 0000000..675fc96
--- /dev/null
+++ b/test/initializer-cast-float-int.qbe
@@ -0,0 +1 @@
+export data $x = align 4 { w 18446744073709551615, }
diff --git a/test/initializer-cast-int-float.c b/test/initializer-cast-int-float.c
new file mode 100644
index 0000000..e041d90
--- /dev/null
+++ b/test/initializer-cast-int-float.c
@@ -0,0 +1 @@
+double x = -1;
diff --git a/test/initializer-cast-int-float.qbe b/test/initializer-cast-int-float.qbe
new file mode 100644
index 0000000..80b905e
--- /dev/null
+++ b/test/initializer-cast-int-float.qbe
@@ -0,0 +1 @@
+export data $x = align 8 { d d_-1, }