~mcf/cproc

Type checking in exprconvert (fixes #39) v1 PROPOSED

Min4Builder
Min4Builder: 1
 Type checking in exprconvert (fixes #39)

 3 files changed, 74 insertions(+), 7 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mcf/cproc/patches/10231/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH] Type checking in exprconvert (fixes #39) Export this patch

Min4Builder
From: lieks <lieks124@gmail.com>

This is a fix for issue #39.

I hit this bug while messing around with the rules of the language. In
order to test my brand new typing rules, I had to fix it. So here am I,
writing my first email patch. I hope it's useful :)

Now, code like:
	int a[37] = (struct {float f;}) { 500.0 };
should be correctly rejected.

It allows any integer to be implicitly cast to a pointer, not just the
constant 0. This may or may not be worth fixing.

---
 cc.h   |  1 +
 expr.c | 22 +++++++++++++++++-----
 type.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/cc.h b/cc.h
index b9df86c..73f9de4 100644
--- a/cc.h
+++ b/cc.h
@@ -408,6 +408,7 @@ struct type *mkpointertype(struct type *, enum typequal);
struct type *mkarraytype(struct type *, enum typequal, uint64_t);

_Bool typecompatible(struct type *, struct type *);
_Bool typeconvertible(struct type *, struct type *);
_Bool typesame(struct type *, struct type *);
struct type *typecomposite(struct type *, struct type *);
struct type *typeunqual(struct type *, enum typequal *);
diff --git a/expr.c b/expr.c
index 8b10bf4..e4ff6d7 100644
--- a/expr.c
+++ b/expr.c
@@ -284,11 +284,17 @@ mkbinaryexpr(struct location *loc, enum tokenkind op, struct expr *l, struct exp
		} else {
			if (!typecompatible(l->type->base, r->type->base))
				error(&tok.loc, "pointer operands to '-' are to incompatible types");
			t = l->type->base;
			e = l;
			l = mkexpr(EXPRCAST, &typelong);
			l->base = e;
			e = r;
			r = mkexpr(EXPRCAST, &typelong);
			r->base = e;
			l = mkbinaryexpr(loc, TSUB, l, r);
			r = mkconstexpr(&typelong, t->size);
			op = TDIV;
			t = &typelong;
			e = mkbinaryexpr(loc, TSUB, exprconvert(l, &typelong), exprconvert(r, &typelong));
			r = mkconstexpr(&typelong, l->type->base->size);
			l = e;
		}
		break;
	case TMOD:
@@ -782,13 +788,17 @@ postfixexpr(struct scope *s, struct expr *r)
			if (tok.kind != TIDENT)
				error(&tok.loc, "expected identifier after '%s' operator", tokstr[op]);
			lvalue = op == TARROW || r->base->lvalue;
			r = exprconvert(r, mkpointertype(&typechar, QUALNONE));
			tmp = r;
			r = mkexpr(EXPRCAST, mkpointertype(&typechar, QUALNONE));
			r->base = tmp;
			offset = 0;
			m = typemember(t, tok.lit, &offset);
			if (!m)
				error(&tok.loc, "struct/union has no member named '%s'", tok.lit);
			r = mkbinaryexpr(&tok.loc, TADD, r, mkconstexpr(&typeulong, offset));
			r = exprconvert(r, mkpointertype(m->type, tq | m->qual));
			tmp = r;
			r = mkexpr(EXPRCAST, mkpointertype(m->type, tq | m->qual));
			r->base = tmp;
			r = mkunaryexpr(TMUL, r);
			r->lvalue = lvalue;
			if (m->bits.before || m->bits.after) {
@@ -1155,6 +1165,8 @@ exprconvert(struct expr *e, struct type *t)

	if (typecompatible(e->type, t))
		return e;
	if (!typeconvertible(e->type, t))
		error(&tok.loc, "illegal implicit conversion");
	cast = mkexpr(EXPRCAST, t);
	cast->base = e;

diff --git a/type.c b/type.c
index 87f8094..24deea1 100644
--- a/type.c
+++ b/type.c
@@ -166,11 +166,65 @@ typecompatible(struct type *t1, struct type *t2)
	return false;
}

bool
typeconvertible(struct type *t1, struct type *t2)
{
	if (t1->prop & PROPSCALAR && t2->kind == TYPEBOOL)
		return true;
	if (t1->prop & (PROPINT | PROPFLOAT) && t2->prop & (PROPINT | PROPFLOAT))
		return true;
	if (t1->kind == TYPEPOINTER && t2->kind == TYPEPOINTER) {
		if (t1->base->kind == TYPEVOID || t2->base->kind == TYPEVOID)
			return true;
		if ((t1->qual & t2->qual) == t1->qual && typesame(t1->base, t2->base))
			return true;
	}
	if (t1->prop & PROPINT && t2->kind == TYPEPOINTER)
		return true;
	if (t1->kind == TYPEARRAY && t2->kind == TYPEPOINTER && typesame(t1->base, t2->base))
		return true;
	if (t2->kind == TYPEVOID)
		return true;
	return false;
}

bool
typesame(struct type *t1, struct type *t2)
{
	// XXX: implement
	return typecompatible(t1, t2);
	struct param *p1, *p2;

	if (t1 == t2)
		return true;
	if (t1->kind != t2->kind || t1->size != t2->size)
		return false;
	if (t1->prop & PROPINT)
		return t1->basic.issigned == t2->basic.issigned;
	if (t1->qual != t2->qual)
		return false;

	switch (t1->kind) {
	case TYPEPOINTER:
		return typesame(t1->base, t2->base);
	case TYPEARRAY:
		return t1->array.length == t2->array.length
			&& typesame(t1->base, t2->base);
	case TYPEFUNC:
		if (t1->func.isprototype != t2->func.isprototype ||
		    t1->func.isvararg != t2->func.isvararg ||
		    t1->func.isnoreturn != t2->func.isnoreturn ||
		    t1->func.paraminfo != t2->func.paraminfo)
			return false;
		if (!t1->func.paraminfo)
			return typesame(t1->base, t2->base);
		for (p1 = t1->func.params, p2 = t2->func.params; p1 && p2; p1 = p1->next, p2 = p2->next) {
			if (p1->qual != p2->qual || !typesame(p1->type, p2->type))
				return false;
		}
		if (p1 || p2)
			return false;
		return typesame(t1->base, t2->base);
	}
	return false;
}

struct type *
-- 
2.26.1