aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-04-18 19:47:33 +0200
committerlemon <lsof@mailbox.org>2026-04-18 19:47:33 +0200
commite243a262720a19224a4ae4a99466808711fb0acd (patch)
treee6693130705647c16337de5615a169ea5478ae77 /src
parent2e1e513d0cfd3686abb1634824a1503f93f413de (diff)
frontend: GNU statement expressions
Diffstat (limited to 'src')
-rw-r--r--src/c.c88
-rw-r--r--src/c.h3
2 files changed, 58 insertions, 33 deletions
diff --git a/src/c.c b/src/c.c
index c45d1e6..8e99be1 100644
--- a/src/c.c
+++ b/src/c.c
@@ -1002,13 +1002,16 @@ tkprec(int tt)
return ((uint)tt < countof(bintab)) ? bintab[tt].prec : 0;
}
-static Expr initializer(CComp *cm, Type *ty, enum evalmode ev,
- bool globl, enum qualifier qual, internstr name);
+static Expr initializer(CComp *, Type *ty, enum evalmode ev,
+ bool globl, enum qualifier qual, internstr name);
+
+static void block(CComp *, Ref *stmtexprval, Type *stmtexprty);
static internstr istr__func__, istr_main, istr_memset;
static internstr mkhiddensym(const char *fnname, const char *name, int id);
+
/* parse an expression with the given operator precedence */
/* param ident is a kludge to support block labels without backtracking or extra lookahead
* see stmt() */
@@ -1062,7 +1065,15 @@ Unary:
/* might be unary op (cast) or primary expr */
case '(':
- if (!isdecltok(cm)) { /* (expr) */
+ if (match(cm, &tk, '{')) { /* ({ GNU statement expression }) */
+ Span span = tk.span;
+ Ref val;
+ ex.t = EIRVALUE;
+ block(cm, &val, &ex.ty);
+ ex.irref.bits = val.bits;
+ if (expect(cm, ')', NULL)) joinspan(&span.ex, tk.span.ex);
+ ex.span = span;
+ } else if (!isdecltok(cm)) { /* (expr) */
Span span = tk.span;
ex = commaexpr(cm);
joinspan(&span.ex, ex.span.ex);
@@ -3030,7 +3041,7 @@ expraddr(Function *fn, const Expr *ex)
assert(decl->id >= 0);
return mkref(RTMP, decl->id);
case SCEXTERN: case SCNONE: case SCSTATIC:
- return mksymref(decl->sym, (SFUNC & -(decl->ty.t == TYFUNC)) | (SLOCAL & -(decl->scls == SCSTATIC || decl->isdef)));
+ return mksymref(decl->sym, (SFUNC & -(decl->ty.t == TYFUNC)) | (SLOCAL & -(decl->scls == SCSTATIC || (decl->isdef && !decl->inlin))));
default:
assert(0);
}
@@ -3938,6 +3949,8 @@ compileexpr(Function *fn, const Expr *ex, bool discard)
case ESEQ:
expreffects(fn, &sub[0]);
return compileexpr(fn, &sub[1], discard);
+ case EIRVALUE:
+ return (Ref){.bits = ex->irref.bits};
default: assert(!"nyi expr");
}
}
@@ -4219,9 +4232,8 @@ stmtterm(CComp *cm)
expect(cm, ';', "to terminate previous statement");
}
-static void block(CComp *cm, Function *fn);
-static bool stmt(CComp *cm, Function *fn);
-static void localdecl(CComp *cm, Function *fn, bool forinit);
+static bool stmt(CComp *, Ref *stmtexprval, Type *stmtexprty);
+static void localdecl(CComp *, bool forinit);
typedef struct Label Label;
struct Label {
@@ -4244,8 +4256,9 @@ findlabel(CComp *cm, internstr name)
}
static void
-deflabel(CComp *cm, Function *fn, const Span *span, internstr name)
+deflabel(CComp *cm, const Span *span, internstr name)
{
+ Function *fn = cm->fn;
Label *label = findlabel(cm, name);
if (label && label->usespan.ex.len == 0) {
error(span, "redefinition of label '%s'", name);
@@ -4279,7 +4292,7 @@ deflabel(CComp *cm, Function *fn, const Span *span, internstr name)
}
static bool
-loopbody(CComp *cm, Function *fn, Block *brk, Block *cont)
+loopbody(CComp *cm, Block *brk, Block *cont)
{
Block *save[2];
bool terminates = 0;
@@ -4288,7 +4301,7 @@ loopbody(CComp *cm, Function *fn, Block *brk, Block *cont)
cm->breakto = brk, cm->loopcont = cont;
++cm->loopdepth;
- terminates = stmt(cm, fn);
+ terminates = stmt(cm, NULL, NULL);
--cm->loopdepth;
cm->breakto = save[0], cm->loopcont = save[1];
@@ -4326,9 +4339,10 @@ swsortcases(SwitchCase *cs, uint n)
}
static bool
-genswitch(CComp *cm, Function *fn, const Expr *ex)
+genswitch(CComp *cm, const Expr *ex)
{
Ref sel;
+ Function *fn = cm->fn;
bool doemit = fn->curblk;
Block *begin = NULL, *end = NULL, *breaksave = cm->breakto;
SwitchStmt *stsave = cm->switchstmt, st = {.condtype = ex->ty};
@@ -4347,7 +4361,7 @@ genswitch(CComp *cm, Function *fn, const Expr *ex)
begin = fn->curblk;
fn->curblk = NULL;
++cm->switchdepth;
- stmt(cm, fn);
+ stmt(cm, NULL, NULL);
--cm->switchdepth;
doemit = fn->curblk;
cm->switchstmt = stsave;
@@ -4396,8 +4410,9 @@ genswitch(CComp *cm, Function *fn, const Expr *ex)
}
static bool /* return 1 if stmt is terminating (ends with a jump) */
-stmt(CComp *cm, Function *fn)
+stmt(CComp *cm, Ref *stmtexprval, Type *stmtexprty)
{
+ Function *fn = cm->fn;
Block *tr, *fl, *end, *begin;
union {
Arena a;
@@ -4449,14 +4464,12 @@ stmt(CComp *cm, Function *fn)
}
} else if (tk.t == TKIDENT && match(cm, NULL, ':')) {
/* <label> ':' */
- deflabel(cm, fn, &tk.span, tk.name);
+ deflabel(cm, &tk.span, tk.name);
} else {
assert(tk.t == TKIDENT);
/* kludge for no backtracking and no lookahead */
ex = exprparse(cm, 1, &tk, EFROMSTMT);
- stmtterm(cm);
- EMITS expreffects(fn, &ex);
- return fn->curblk == NULL;
+ goto ExprStmt;
}
doemit = 1;
}
@@ -4465,7 +4478,7 @@ stmt(CComp *cm, Function *fn)
case '{':
lex(cm, NULL);
envdown(cm, &e);
- block(cm, fn);
+ block(cm, NULL, NULL);
envup(cm);
break;
case ';':
@@ -4485,7 +4498,7 @@ stmt(CComp *cm, Function *fn)
condjump(fn, &ex, tr, fl);
useblk(fn, tr);
}
- terminates = stmt(cm, fn);
+ terminates = stmt(cm, NULL, NULL);
if (!match(cm, NULL, TKWelse)) {
end = fl;
EMITS if (!terminates) putbranch(fn, end);
@@ -4496,7 +4509,7 @@ stmt(CComp *cm, Function *fn)
if (!terminates) putbranch(fn, end);
useblk(fn, fl);
}
- terminates &= stmt(cm, fn);
+ terminates &= stmt(cm, NULL, NULL);
EMITS {
if (fn->curblk) putbranch(fn, end);
}
@@ -4533,7 +4546,7 @@ stmt(CComp *cm, Function *fn)
condjump(fn, &ex, tr = newblk(fn), end = newblk(fn));
useblk(fn, tr);
}
- terminates = loopbody(cm, fn, end, begin);
+ terminates = loopbody(cm, end, begin);
EMITS {
if (fn->curblk) putbranch(fn, begin);
useblk(fn, end);
@@ -4560,7 +4573,7 @@ stmt(CComp *cm, Function *fn)
tr = newblk(fn);
end = newblk(fn);
}
- terminates = loopbody(cm, fn, end, tr);
+ terminates = loopbody(cm, end, tr);
expect(cm, TKWwhile, NULL);
expect(cm, '(', NULL);
ex = commaexpr(cm);
@@ -4600,7 +4613,7 @@ stmt(CComp *cm, Function *fn)
envdown(cm, &e);
if (!match(cm, NULL, ';')) { /* init */
if (isdecltok(cm)) {
- localdecl(cm, fn, 1);
+ localdecl(cm, 1);
} else {
ex = commaexpr(cm);
EMITS expreffects(fn, &ex);
@@ -4641,7 +4654,7 @@ stmt(CComp *cm, Function *fn)
expect(cm, ')', NULL);
}
- terminates = loopbody(cm, fn, fl, end);
+ terminates = loopbody(cm, fl, end);
EMITS {
if (end != begin) { /* have iter */
@@ -4667,7 +4680,7 @@ stmt(CComp *cm, Function *fn)
expect(cm, ')', NULL);
if (!isint(ex.ty))
error(&ex.span, "'switch' value is not an integer: '%ty'", ex.ty);
- terminates = genswitch(cm, fn, &ex);
+ terminates = genswitch(cm, &ex);
break;
case TKWbreak:
lex(cm, &tk);
@@ -4745,8 +4758,11 @@ stmt(CComp *cm, Function *fn)
break;
default:
ex = exprparse(cm, 1, NULL, EFROMSTMT);
+ ExprStmt:
stmtterm(cm);
- EMITS expreffects(fn, &ex);
+ if (stmtexprty) *stmtexprty = ex.ty;
+ EMITS r = compileexpr(fn, &ex, stmtexprval == NULL);
+ if (stmtexprval) *stmtexprval = r;
break;
}
freearena(&cm->exarena);
@@ -4756,18 +4772,19 @@ stmt(CComp *cm, Function *fn)
/* parse and compile a function-local declaration */
static void
-localdecl(CComp *cm, Function *fn, bool forini)
+localdecl(CComp *cm, bool forini)
{
Expr ini;
Token tk;
+ Function *fn = cm->fn;
const bool doemit = fn->curblk;
DeclState st = { DFUNCVAR };
if (!forini && match(cm, &tk, TKIDENT)) {
if (match(cm, NULL, ':')) {
/* <label> ':' */
- deflabel(cm, fn, &tk.span, tk.name);
- stmt(cm, fn);
+ deflabel(cm, &tk.span, tk.name);
+ stmt(cm, NULL, NULL);
return;
}
/* finddecl() -> non null because localdecl() is called when isdecltok() */
@@ -4893,15 +4910,17 @@ localdecl(CComp *cm, Function *fn, bool forini)
}
static void
-block(CComp *cm, Function *fn)
+block(CComp *cm, Ref *stexval, Type *stexty)
{
Token tk;
+ if (stexty) *stexty = mktype(TYVOID);
while (!match(cm, &tk, '}')) {
+ if (stexty) *stexty = mktype(TYVOID);
if (isdecltok(cm))
- localdecl(cm, fn, 0);
+ localdecl(cm, 0);
else
- stmt(cm, fn);
+ stmt(cm, stexval, stexty);
}
cm->fnblkspan = tk.span;
}
@@ -4911,6 +4930,8 @@ function(CComp *cm, Function *fn, internstr *pnames, const Span *pspans, uchar *
{
const TypeData *td = &typedata[fn->fnty.dat];
const bool doemit = fn->curblk;
+ Function *prevfn = cm->fn;
+ cm->fn = fn;
Env e;
Token tk;
envdown(cm, &e);
@@ -4957,7 +4978,7 @@ function(CComp *cm, Function *fn, internstr *pnames, const Span *pspans, uchar *
useblk(fn, blk);
}
cm->labels = NULL;
- block(cm, fn);
+ block(cm, NULL, NULL);
envup(cm);
for (Label *l = cm->labels; l; l = l->link) {
if (l->usespan.ex.len) {
@@ -4979,6 +5000,7 @@ function(CComp *cm, Function *fn, internstr *pnames, const Span *pspans, uchar *
putreturn(fn, NOREF, NOREF);
}
}
+ cm->fn = prevfn;
}
/* top-level declaration */
diff --git a/src/c.h b/src/c.h
index a0142e7..4e4c7a7 100644
--- a/src/c.h
+++ b/src/c.h
@@ -14,6 +14,7 @@ enum exprkind {
EEQU, ENEQ, ELTH, EGTH, ELTE, EGTE,
ESET, ESETADD, ESETSUB, ESETMUL, ESETDIV, ESETREM, ESETAND, ESETIOR, ESETXOR, ESETSHL, ESETSHR,
ESEQ,
+ EIRVALUE,
};
#define isunop(t) in_range(t, EPLUS, EPOSTDEC)
#define isbinop(t) in_range(t, EADD, ESEQ)
@@ -55,6 +56,7 @@ struct Expr {
bool func : 1, local : 1;
} ssym; /* ESSYMREF (static symbol addr + off) */
Init *init; /* EINIT */
+ struct { uint bits; } irref; /* EIRVALUE */
};
};
@@ -81,6 +83,7 @@ typedef struct {
struct Block *breakto, *loopcont;
struct SwitchStmt *switchstmt;
struct Label *labels;
+ struct Function *fn;
} CComp;
enum storageclass {