diff options
author | Michael Forney <mforney@mforney.org> | 2021-10-24 22:47:44 -0700 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2024-03-24 02:41:18 -0700 |
commit | 731e7a2f3bf18baa4d4ce966006637ee13c5651a (patch) | |
tree | 8bc4457c7c663b544ccc0386dbabcccd7259a45a | |
parent | 7585cbb758b7bc2169cba42b7665d46f0c57db93 (diff) |
Add support for C23 attribute syntax
Currently, all attributes are ignored.
References: https://todo.sr.ht/~mcf/cproc/68
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | attr.c | 90 | ||||
-rw-r--r-- | cc.h | 12 | ||||
-rwxr-xr-x | configure | 3 | ||||
-rw-r--r-- | decl.c | 29 | ||||
-rw-r--r-- | stmt.c | 2 | ||||
-rw-r--r-- | test/attribute-syntax.c | 79 | ||||
-rw-r--r-- | test/attribute-syntax.qbe | 22 |
8 files changed, 237 insertions, 2 deletions
@@ -23,6 +23,7 @@ $(objdir)/cproc: $(DRIVER_OBJ) $(CC) $(LDFLAGS) -o $@ $(DRIVER_OBJ) SRC=\ + attr.c\ decl.c\ eval.c\ expr.c\ @@ -46,6 +47,7 @@ OBJ=$(SRC:%.c=$(objdir)/%.o) $(objdir)/cproc-qbe: $(OBJ) $(CC) $(LDFLAGS) -o $@ $(OBJ) +$(objdir)/attr.o : attr.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ attr.c $(objdir)/decl.o : decl.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ decl.c $(objdir)/driver.o : driver.c util.h config.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ driver.c $(objdir)/eval.o : eval.c util.h cc.h $(stagedeps) ; $(CC) $(CFLAGS) -c -o $@ eval.c @@ -0,0 +1,90 @@ +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include "util.h" +#include "cc.h" + +enum attrprefix { + PREFIXNONE = 1, /* standard attribute */ + PREFIXGNU, +}; + +static char * +strip(char *name) +{ + size_t len; + + len = strlen(name); + if (len >= 4 && name[0] == '_' && name[1] == '_' && name[len - 2] == '_' && name[len - 1] == '_') { + name[len - 2] = '\0'; + name += 2; + } + return name; +} + +static bool +parseattr(struct attr *a, enum attrkind allowed, enum attrprefix prefix) +{ + char *name, *prefixname = ""; + enum attrkind kind; + int paren; + + if (tok.kind != TIDENT) + return false; + name = strip(tok.lit); + next(); + if (!prefix) { + if (consume(TCOLONCOLON)) { + if (strcmp(name, "gnu") == 0) + prefix = PREFIXGNU; + else + prefix = 0; + name = strip(expect(TIDENT, "after attribute prefix")); + } else { + prefix = PREFIXNONE; + } + } + kind = 0; + switch (prefix) { + case PREFIXGNU: + prefixname = "GNU "; + break; + } + if (kind) { + if (!(kind & allowed)) + error(&tok.loc, "%sattribute '%s' is not supported here", prefixname, name); + if (a) + a->kind |= kind; + } else if (consume(TLPAREN)) { + /* skip arguments */ + for (paren = 1; paren > 0; next()) { + switch (tok.kind) { + case TLPAREN: ++paren; break; + case TRPAREN: --paren; break; + } + } + } + return true; +} + +static bool +attrspec(struct attr *a, enum attrkind allowed) +{ + if (tok.kind != TLBRACK || !peek(TLBRACK)) + return false; + while (parseattr(a, allowed, 0) || consume(TCOMMA)) + ; + expect(TRBRACK, "to end attribute specifier"); + expect(TRBRACK, "to end attribute specifier"); + return true; +} + +bool +attr(struct attr *a, enum attrkind allowed) +{ + if (!attrspec(a, allowed)) + return false; + while (attrspec(a, allowed)) + ; + return true; +} @@ -470,6 +470,18 @@ extern const struct target *targ; void targinit(const char *); +/* attr */ + +enum attrkind { + ATTRNONE, +}; + +struct attr { + enum attrkind kind; +}; + +_Bool attr(struct attr *, enum attrkind); + /* decl */ struct decl *mkdecl(enum declkind, struct type *, enum typequal, enum linkage); @@ -154,8 +154,7 @@ static const char *const preprocesscmd[] = { /* we don't generate position-independent code */ "-U", "__PIC__", - /* ignore attributes and extension markers */ - "-D", "__attribute__(x)=", + /* ignore extension markers */ "-D", "__extension__=", $defines}; static const char *const codegencmd[] = {"$DEFAULT_QBE"}; @@ -170,6 +170,7 @@ tagspec(struct scope *s) default: fatal("internal error: unknown tag kind"); } next(); + attr(NULL, 0); tag = NULL; t = NULL; et = NULL; @@ -236,6 +237,7 @@ tagspec(struct scope *s) for (value = 0; tok.kind == TIDENT; ++value) { name = tok.lit; next(); + attr(NULL, 0); if (consume(TASSIGN)) { e = evalexpr(s); if (e->kind != EXPRCONST || !(e->type->prop & PROPINT)) @@ -497,6 +499,12 @@ done: t = mkarraytype(t->base, t->qual | tq, t->u.array.length); tq = QUALNONE; } + /* + TODO: consider delaying attribute parsing to declarator(), + so we can tell the difference between the start of an + attribute and an array declarator. + */ + attr(NULL, 0); return (struct qualtype){t, tq}; } @@ -528,8 +536,10 @@ declaratortypes(struct scope *s, struct list *result, char **name, bool allowabs struct expr *e; unsigned long long i; enum typequal tq; + bool allowattr; while (consume(TMUL)) { + attr(NULL, 0); tq = QUALNONE; while (typequal(&tq)) ; @@ -552,21 +562,25 @@ declaratortypes(struct scope *s, struct list *result, char **name, bool allowabs break; /* fallthrough */ default: + allowattr = true; goto func; } } declaratortypes(s, result, name, allowabstract); expect(TRPAREN, "after parenthesized declarator"); + allowattr = false; break; case TIDENT: if (!name) error(&tok.loc, "identifier not allowed in abstract declarator"); *name = tok.lit; next(); + allowattr = true; break; default: if (!allowabstract) error(&tok.loc, "expected '(' or identifier"); + allowattr = true; } for (;;) { switch (tok.kind) { @@ -615,8 +629,11 @@ declaratortypes(struct scope *s, struct list *result, char **name, bool allowabs expect(TRPAREN, "to close function declarator"); t->u.func.paraminfo = t->u.func.isprototype || t->u.func.params || tok.kind == TLBRACE; listinsert(ptr->prev, &t->link); + allowattr = true; break; case TLBRACK: /* array declarator */ + if (allowattr && attr(NULL, 0)) + goto attr; next(); tq = QUALNONE; while (consume(TSTATIC) || typequal(&tq)) @@ -637,6 +654,14 @@ declaratortypes(struct scope *s, struct list *result, char **name, bool allowabs } expect(TRBRACK, "after array length"); listinsert(ptr->prev, &t->link); + allowattr = true; + break; + case T__ATTRIBUTE__: + if (!allowattr) + error(&tok.loc, "attribute not allowed after parenthesized declarator"); + /* attribute applies to identifier if ptr->prev == result, otherwise type ptr->prev */ + attr(NULL, 0); + attr: break; default: return; @@ -688,6 +713,7 @@ parameter(struct scope *s) struct qualtype t; enum storageclass sc; + attr(NULL, 0); t = declspecs(s, &sc, NULL, NULL); if (!t.type) error(&tok.loc, "no type in parameter declaration"); @@ -844,6 +870,7 @@ structdecl(struct scope *s, struct structbuilder *b) if (staticassert(s)) return; + attr(NULL, 0); base = declspecs(s, NULL, NULL, &align); if (!base.type) error(&tok.loc, "no type in struct member declaration"); @@ -966,6 +993,8 @@ decl(struct scope *s, struct func *f) if (staticassert(s)) return true; + if (attr(NULL, 0) && consume(TSEMICOLON)) + return true; base = declspecs(s, &sc, &fs, &align); if (!base.type) return false; @@ -16,6 +16,7 @@ label(struct func *f, struct scope *s) struct block *b; unsigned long long i; + attr(NULL, 0); switch (tok.kind) { case TCASE: next(); @@ -70,6 +71,7 @@ stmt(struct func *f, struct scope *s) struct block *b[4]; struct switchcases swtch; + attr(NULL, 0); switch (tok.kind) { /* 6.8.2 Compound statement */ case TLBRACE: diff --git a/test/attribute-syntax.c b/test/attribute-syntax.c new file mode 100644 index 0000000..1be4260 --- /dev/null +++ b/test/attribute-syntax.c @@ -0,0 +1,79 @@ +/* applies to a and b */ +[[dummy]] int a, b; + +/* applies only to d */ +int c, d [[dummy]]; + +/* applies to type int in this instance */ +int [[dummy]] x; + +/* applies to array type */ +int array[8] [[dummy]]; + +/* applies to function type */ +int function(void) [[dummy]]; + +/* applies to pointer type */ +int *[[dummy]]const pointer; + +/* standalone attribute is implementation-defined */ +[[dummy]]; + +/* applies to type `struct s`, here and elsewhere */ +struct [[dummy]] s { + /* applies to i */ + [[dummy]] int i; + + /* applies to type float in this instance */ + float [[dummy]] f; +}; + +/* applies to type `union u`, here and elsewhere */ +union [[dummy]] u { + /* applies to i */ + [[dummy]] int i; + + /* applies to type float in this instance */ + float [[dummy]] f; +}; + +/* applies to type `enum e`, here and elsewhere */ +enum [[dummy]] e { + /* applies to E1 */ + E1 [[dummy]], + + /* applies to E2 */ + E2 [[dummy]] = 123, +}; + +/* applies to function */ +[[dummy]] void f([[dummy]] int p1 /* applies to p1 */, int [[dummy]] p2 /* applies to type int */) { + /* applies to type int */ + sizeof(int [[dummy]]); + + /* applies to array type */ + sizeof(int[4] [[dummy]]); + + /* applies to function type */ + sizeof(int (*)(float) [[dummy]]); + + /* applies to pointer type */ + sizeof(int *[[dummy]]); + + /* applies to statement */ + [[dummy]] + if (0) + ; + + /* applies to label */ + [[dummy]] + L1: 0; + + /* applies to statement */ + L2: [[dummy]]0; +} + +[[dummy(attribute with parameters [123 456 ({+ -} if else)])]]; +[[dummy1,dummy2,,dummy3]]; +[[]]; +[[dummy1]] [[dummy2]]; diff --git a/test/attribute-syntax.qbe b/test/attribute-syntax.qbe new file mode 100644 index 0000000..ad8f4cc --- /dev/null +++ b/test/attribute-syntax.qbe @@ -0,0 +1,22 @@ +export +function $f(w %.1, w %.3) { +@start.1 + %.2 =l alloc4 4 + storew %.1, %.2 + %.4 =l alloc4 4 + storew %.3, %.4 +@body.2 + jnz 0, @if_true.3, @if_false.4 +@if_true.3 +@if_false.4 +@L1.5 +@L2.6 + ret +} +export data $a = align 4 { z 4 } +export data $b = align 4 { z 4 } +export data $c = align 4 { z 4 } +export data $d = align 4 { z 4 } +export data $x = align 4 { z 4 } +export data $array = align 4 { z 32 } +export data $pointer = align 8 { z 8 } |