From 1faab48ecf83d31a4fd781f10f6f00acb681d2dd Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 24 Jul 2020 16:18:03 +0900 Subject: [PATCH] Add _Generic --- parse.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ test/generic.c | 12 ++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/generic.c diff --git a/parse.c b/parse.c index ea6ff50..aed31d6 100644 --- a/parse.c +++ b/parse.c @@ -2605,12 +2605,56 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) { return node; } +// generic-selection = "(" assign "," generic-assoc ("," generic-assoc)* ")" +// +// generic-assoc = type-name ":" assign +// | "default" ":" assign +static Node *generic_selection(Token **rest, Token *tok) { + Token *start = tok; + tok = skip(tok, "("); + + Node *ctrl = assign(&tok, tok); + add_type(ctrl); + + Type *t1 = ctrl->ty; + if (t1->kind == TY_FUNC) + t1 = pointer_to(t1); + else if (t1->kind == TY_ARRAY) + t1 = pointer_to(t1->base); + + Node *ret = NULL; + + while (!consume(rest, tok, ")")) { + tok = skip(tok, ","); + + if (equal(tok, "default")) { + tok = skip(tok->next, ":"); + Node *node = assign(&tok, tok); + if (!ret) + ret = node; + continue; + } + + Type *t2 = typename(&tok, tok); + tok = skip(tok, ":"); + Node *node = assign(&tok, tok); + if (is_compatible(t1, t2)) + ret = node; + } + + if (!ret) + error_tok(start, "controlling expression type not compatible with" + " any generic association type"); + return ret; +} + // primary = "(" "{" stmt+ "}" ")" // | "(" expr ")" // | "sizeof" "(" type-name ")" // | "sizeof" unary // | "_Alignof" "(" type-name ")" // | "_Alignof" unary +// | "_Generic" generic-selection // | "__builtin_types_compatible_p" "(" type-name, type-name, ")" // | "__builtin_reg_class" "(" type-name ")" // | ident @@ -2657,6 +2701,9 @@ static Node *primary(Token **rest, Token *tok) { return new_ulong(node->ty->align, tok); } + if (equal(tok, "_Generic")) + return generic_selection(rest, tok->next); + if (equal(tok, "__builtin_types_compatible_p")) { tok = skip(tok->next, "("); Type *t1 = typename(&tok, tok); diff --git a/test/generic.c b/test/generic.c new file mode 100644 index 0000000..ac1fb9c --- /dev/null +++ b/test/generic.c @@ -0,0 +1,12 @@ +#include "test.h" + +int main() { + ASSERT(1, _Generic(100.0, double: 1, int *: 2, int: 3, float: 4)); + ASSERT(2, _Generic((int *)0, double: 1, int *: 2, int: 3, float: 4)); + ASSERT(2, _Generic((int[3]){}, double: 1, int *: 2, int: 3, float: 4)); + ASSERT(3, _Generic(100, double: 1, int *: 2, int: 3, float: 4)); + ASSERT(4, _Generic(100f, double: 1, int *: 2, int: 3, float: 4)); + + printf("OK\n"); + return 0; +}