diff options
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | expr.c | 49 | ||||
-rw-r--r-- | test/generic.c | 6 | ||||
-rw-r--r-- | test/generic.qbe | 1 |
4 files changed, 55 insertions, 2 deletions
@@ -63,7 +63,6 @@ specified in `config.h`. - Preprocessor ([#6]). - Generation of position independent code (i.e. shared libraries, modules, PIEs). -- `_Generic` selection ([#44]). - Currently only `x86_64` is supported and tested, though QBE also supports `aarch64`, so it is possible that it works as well. @@ -322,6 +322,52 @@ unescape(char **p) return c; } +static struct expr * +generic(struct scope *s) +{ + struct expr *e, *match = NULL, *def = NULL; + struct type *t, *want; + enum typequal qual; + + next(); + expect(TLPAREN, "after '_Generic'"); + e = assignexpr(s); + expect(TCOMMA, "after generic selector expression"); + want = e->type; + delexpr(e); + do { + if (consume(TDEFAULT)) { + if (def) + error(&tok.loc, "multiple default expressions in generic association list"); + expect(TCOLON, "after 'default'"); + def = assignexpr(s); + } else { + qual = QUALNONE; + t = typename(s, &qual); + if (!t) + error(&tok.loc, "expected typename for generic association"); + expect(TCOLON, "after type name"); + e = assignexpr(s); + if (typecompatible(t, want) && qual == QUALNONE) { + if (match) + error(&tok.loc, "generic selector matches multiple associations"); + match = e; + } else { + delexpr(e); + } + } + } while (consume(TCOMMA)); + expect(TRPAREN, "after generic assocation list"); + if (!match) { + if (!def) + error(&tok.loc, "generic selector matches no associations and no default was specified"); + match = def; + } else if (def) { + delexpr(def); + } + return match; +} + /* 6.5 Expressions */ static struct expr * primaryexpr(struct scope *s) @@ -407,7 +453,8 @@ primaryexpr(struct scope *s) expect(TRPAREN, "after expression"); break; case T_GENERIC: - error(&tok.loc, "generic selection is not yet supported"); + e = generic(s); + break; default: error(&tok.loc, "expected primary expression"); } diff --git a/test/generic.c b/test/generic.c new file mode 100644 index 0000000..d04bc90 --- /dev/null +++ b/test/generic.c @@ -0,0 +1,6 @@ +int x = _Generic(123, + const int: 1, + unsigned: 2, + int: 3, + int *: 4 +); diff --git a/test/generic.qbe b/test/generic.qbe new file mode 100644 index 0000000..6b57b3e --- /dev/null +++ b/test/generic.qbe @@ -0,0 +1 @@ +export data $x = align 4 { w 3, } |