aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2021-10-24 22:47:44 -0700
committerMichael Forney <mforney@mforney.org>2024-03-24 02:41:18 -0700
commit731e7a2f3bf18baa4d4ce966006637ee13c5651a (patch)
tree8bc4457c7c663b544ccc0386dbabcccd7259a45a
parent7585cbb758b7bc2169cba42b7665d46f0c57db93 (diff)
Add support for C23 attribute syntax
Currently, all attributes are ignored. References: https://todo.sr.ht/~mcf/cproc/68
-rw-r--r--Makefile2
-rw-r--r--attr.c90
-rw-r--r--cc.h12
-rwxr-xr-xconfigure3
-rw-r--r--decl.c29
-rw-r--r--stmt.c2
-rw-r--r--test/attribute-syntax.c79
-rw-r--r--test/attribute-syntax.qbe22
8 files changed, 237 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index 659181d..d52da1b 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/attr.c b/attr.c
new file mode 100644
index 0000000..75ec5ef
--- /dev/null
+++ b/attr.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;
+}
diff --git a/cc.h b/cc.h
index 4f72a68..2bf61c3 100644
--- a/cc.h
+++ b/cc.h
@@ -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);
diff --git a/configure b/configure
index fe5688a..d67a74f 100755
--- a/configure
+++ b/configure
@@ -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"};
diff --git a/decl.c b/decl.c
index 975a8d5..6e0ee37 100644
--- a/decl.c
+++ b/decl.c
@@ -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;
diff --git a/stmt.c b/stmt.c
index 843852c..e81b47b 100644
--- a/stmt.c
+++ b/stmt.c
@@ -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 }