Assertion failed: tq == QUALNONE (expr.c: decay: 101)


I found this strange bug while trying to build muon with cproc. You can easily reproduce it there. You'll need to disable bestline support because cproc doesn't like it at all(probably bug too).

OK, I investigated the issue a bit and found out that reverting b82a231 allowed the build to proceed. The build will fail later at linking step though, but that's a muon bug.

Full build log: https://termbin.com/r39e


Assigned to
1 year, 9 months ago
12 days ago
No labels applied.

~mcf 1 year, 9 months ago

I noticed this issue also during bootstrap on OpenBSD (https://builds.sr.ht/~mcf/job/757621).

The issue is related to qualifiers on array types. The C standard says that when an array type is qualified, the qualifiers apply to the element type, not the array type itself. Although this is handled partially at the end of decl.c:declspecs(), there are still a few cases where we incorrectly applied qualifiers to an array type:

  1. If you qualify a nested array type, e.g. typedef int T[1][1]; const T x;. declspecs only passes the qualifiers through one layer of array, so x ends up as "array of const array of int" instead of "array of array of const int".
  2. If you access an array member of a struct or union, the qualifiers of the struct/union get applied to the member type, but don't get passed through to the element type of the array member.

Commit b82a231 fixed a bug where an expression with array type would decay into a pointer with the qualifiers of the array type (which should always be empty as per above) and ignore the qualifiers of the array element type. However, due to 1 and 2 above, there are cases where we might end up with a qualified array type, which now trigger the assertion. I think you are hitting case 2 above (removing the const from repl_cmds is a workaround).

As for a solution, I have two ideas:

  1. Add a function that applies qualifiers to an array type by walking down until it finds a non-array type, rebuilding it as it goes and applying the qualifiers to that non-array type. Then change declspecs and postfixexpr to use that.
  2. Previously, things mostly ended up working despite this bug since the qualifiers were applied down to the element type during the implicit conversion of an array to a pointer to first element. Perhaps it would be sufficient to combine the qualifiers in decay() (i.e. t->qual | tq), and then somehow relax typecompatible() to treat "qualified array of type" and "array of qualified type" the as equivalent.

I still need to think more about it to determine the best way forward. I have some plans to change up how types are represented, which I think should make this easier to deal with. So even though it's still not quite correct, perhaps the best thing to do for now is the diff below.

diff --git a/expr.c b/expr.c
index 67976b8..532ad60 100644
--- a/expr.c
+++ b/expr.c
@@ -98,9 +98,14 @@ decay(struct expr *e)
 	tq = e->qual;
 	switch (t->kind) {
+		/*
+		TODO: qualifiers should be applied to the element
+		type of array types, not the array type itself
 		assert(tq == QUALNONE);
+		*/
 		e = mkunaryexpr(TBAND, e);
-		e->type = mkpointertype(t->base, t->qual);
+		e->type = mkpointertype(t->base, t->qual | tq);
 		e->decayed = true;
 	case TYPEFUNC:

~lattis 1 year, 9 months ago*

In muon's case, this bug is surfaced by code like:

        const struct { int a[1]; } b[1];
        return b[0].a[0];

Another workaround is to remove const.

~phoebos 11 months ago

I've also stumbled across this bug, in the following context:

struct dirent {
    char d_name[256];

int sel(const struct dirent *d) {
    return d->d_name[0] != '.';
Assertion failed: tq == QUALNONE (expr.c: decay: 101)
cproc: compile: process signaled: Aborted

Removing const works.

~mcf 12 days ago

Michael Forney referenced this ticket in commit a4a041b.

Register here or Log in to comment, or comment via email.