diff options
author | Leo Tenenbaum <pommicket@gmail.com> | 2019-11-16 14:20:19 -0500 |
---|---|---|
committer | Leo Tenenbaum <pommicket@gmail.com> | 2019-11-16 14:20:19 -0500 |
commit | 09af32ffade52ca84943d047db9e2dcecc4ca495 (patch) | |
tree | a1d2670f4d36fc37184096fdb16ca0186ab88f91 | |
parent | 54f1d5bc80117063a1b67f834b65dbb488ddf1c8 (diff) |
:@
-rw-r--r-- | cgen.c | 71 | ||||
-rw-r--r-- | decls_cgen.c | 44 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | parse.c | 64 | ||||
-rw-r--r-- | test.toc | 24 | ||||
-rw-r--r-- | typedefs_cgen.c | 3 | ||||
-rw-r--r-- | types.c | 60 | ||||
-rw-r--r-- | types.h | 25 |
8 files changed, 224 insertions, 69 deletions
@@ -412,8 +412,8 @@ static inline void cgen_fn_name(CGenerator *g, FnExpr *f) { } } -/* unless f needs multiple instances, instance can be set to 0 */ -static bool cgen_fn_header(CGenerator *g, FnExpr *f, Location where, I64 instance) { +/* unless f has const/semi-const args, instance and which_are_const can be set to 0 */ +static bool cgen_fn_header(CGenerator *g, FnExpr *f, Location where, I64 instance, U64 which_are_const) { bool out_param = cgen_uses_ptr(&f->ret_type); bool any_params = false; if (!f->c.name) /* anonymous fn */ @@ -432,8 +432,10 @@ static bool cgen_fn_header(CGenerator *g, FnExpr *f, Location where, I64 instanc if (!cgen_type_post(g, &f->ret_type, where)) return false; } cgen_write(g, "("); + int semi_const_idx = 0; arr_foreach(f->params, Declaration, d) { - if (!(d->flags & DECL_IS_CONST)) { + if (!(d->flags & DECL_IS_CONST) && !((d->flags & DECL_SEMI_CONST) + && (which_are_const & (((U64)1) << semi_const_idx++)))) { long idx = 0; arr_foreach(d->idents, Identifier, i) { if (d != f->params || i != d->idents) @@ -591,10 +593,10 @@ static bool cgen_set_tuple(CGenerator *g, Expression *exprs, Identifier *idents, cgen_write(g, "%"PRId64, to->call.c.instance); cgen_write(g, "("); bool any_args = false; - bool *constant = to->call.fn->type.fn.constant; + Constness *constness = to->call.fn->type.fn.constness; int i = 0; arr_foreach(to->call.arg_exprs, Expression, arg) { - if (!constant || !constant[i]) { + if (!constness || !arg_is_const(arg, constness[i])) { if (any_args) cgen_write(g, ", "); any_args = true; @@ -938,9 +940,9 @@ static bool cgen_expr_pre(CGenerator *g, Expression *e) { case EXPR_CALL: { if (!cgen_expr_pre(g, e->call.fn)) return false; int i = 0; - bool *constant = e->call.fn->type.fn.constant; + Constness *constness = e->call.fn->type.fn.constness; arr_foreach(e->call.arg_exprs, Expression, arg) { - if (!constant || !constant[i]) { + if (!constness || !arg_is_const(arg, constness[i])) { if (!cgen_expr_pre(g, arg)) return false; } i++; @@ -961,7 +963,7 @@ static bool cgen_expr_pre(CGenerator *g, Expression *e) { bool any_args = false; i = 0; arr_foreach(e->call.arg_exprs, Expression, arg) { - if (!constant || !constant[i]) { + if (!constness || !arg_is_const(arg, constness[i])) { if (any_args) cgen_write(g, ", "); any_args = true; if (!cgen_expr(g, arg)) @@ -1107,10 +1109,21 @@ static bool cgen_expr(CGenerator *g, Expression *e) { IdentDecl *idecl = ident_decl(e->ident); if (idecl && idecl->kind == IDECL_DECL) { Declaration *d = idecl->decl; - Value fn_val = d->val; - FnExpr *fn = fn_val.fn; - cgen_fn_name(g, fn); - handled = true; + if (d->flags & DECL_IS_CONST) { + int index = decl_ident_index(d, e->ident); + Value fn_val = *decl_val_at_index(d, index); + FnExpr *fn = fn_val.fn; + Expression fn_expr; + + fn_expr.kind = EXPR_FN; + fn_expr.fn = *fn; + fn_expr.flags = EXPR_FOUND_TYPE; + fn_expr.type = *decl_type_at_index(d, index); + + if (!cgen_expr(g, &fn_expr)) + return false; + handled = true; + } } } if (!handled) { @@ -1307,9 +1320,9 @@ static bool cgen_expr(CGenerator *g, Expression *e) { } cgen_write(g, "("); bool first_arg = true; - size_t i = 0; + int i = 0; arr_foreach(e->call.arg_exprs, Expression, arg) { - if (!fn_type->constant || !fn_type->constant[i]) { + if (!fn_type->constness || !arg_is_const(arg, fn_type->constness[i])) { if (!first_arg) cgen_write(g, ", "); first_arg = false; @@ -1363,9 +1376,10 @@ static bool cgen_expr(CGenerator *g, Expression *e) { case EXPR_TYPE: assert(0); break; - case EXPR_FN: - cgen_fn_name(g, &e->fn); - break; + case EXPR_FN: { + FnExpr *f = &e->fn; + cgen_fn_name(g, f); + } break; case EXPR_SLICE: cgen_ident_id(g, e->slice.c.id); break; @@ -1445,8 +1459,9 @@ static bool cgen_fn(CGenerator *g, FnExpr *f, Location where, I64 instance, Valu /* see also cgen_defs_expr */ FnExpr *prev_fn = g->fn; Block *prev_block = g->block; + U64 which_are_const = compile_time_args ? compile_time_args->u64 : 0; fn_enter(f, 0); - if (!cgen_fn_header(g, f, where, instance)) + if (!cgen_fn_header(g, f, where, instance, which_are_const)) return false; g->fn = f; cgen_write(g, " {"); @@ -1457,8 +1472,12 @@ static bool cgen_fn(CGenerator *g, FnExpr *f, Location where, I64 instance, Valu } if (compile_time_args) { int carg_idx = 0; + compile_time_args++; /* move past which_are_const */ + int semi_const_idx = 0; arr_foreach(f->params, Declaration, param) { - if (param->flags & DECL_IS_CONST) { + if ((param->flags & DECL_IS_CONST) + || ((param->flags & DECL_SEMI_CONST) + && (which_are_const & (((U64)1) << semi_const_idx++)))) { int i = 0; arr_foreach(param->idents, Identifier, ident) { Type *type = param->type.kind == TYPE_TUPLE ? ¶m->type.tuple[i] @@ -1784,8 +1803,15 @@ static bool cgen_stmt(CGenerator *g, Statement *s) { static bool cgen_defs_expr(CGenerator *g, Expression *e) { if (e->kind == EXPR_FN) { FnExpr *f = &e->fn; - - if (e->type.fn.constant) { + FnType *fn_type = &e->type.fn; + bool any_const = false; + if (fn_type->constness) { + for (size_t i = 0; i < arr_len(fn_type->types)-1; i++) { + if (fn_type->constness[i] == CONSTNESS_YES) + any_const = true; + } + } + if (fn_type->constness) { HashTable *instances = f->c.instances; if (instances) { /* generate each instance */ @@ -1798,7 +1824,8 @@ static bool cgen_defs_expr(CGenerator *g, Expression *e) { } } } - } else { + } + if (!any_const) { if (!cgen_fn(g, &e->fn, e->where, 0, NULL)) return false; } diff --git a/decls_cgen.c b/decls_cgen.c index 0e9fff3..6b52291 100644 --- a/decls_cgen.c +++ b/decls_cgen.c @@ -8,7 +8,7 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) { e->call.c.instance = 0; assert(e->call.fn->type.kind == TYPE_FN); FnType *fn_type = &e->call.fn->type.fn; - if (fn_type->constant) { + if (fn_type->constness) { Value fval; /* e->call.fn had better be a compile-time constant if it has compile-time arguments */ if (!eval_expr(g->evalr, e->call.fn, &fval)) @@ -20,9 +20,25 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) { Value *compile_time_args = NULL; Type *tuple_types = NULL; size_t nparams = arr_len(fn_type->types)-1; + Value *which_are_const_val = arr_add(&compile_time_args); + U64 *which_are_const = &which_are_const_val->u64; + Type *u64t = arr_add(&tuple_types); + u64t->kind = TYPE_BUILTIN; + u64t->flags = TYPE_IS_RESOLVED; + u64t->builtin = BUILTIN_U64; + *which_are_const = 0; + int semi_const_arg_index = 0; for (size_t i = 0; i < nparams; i++) { - if (fn_type->constant[i]) { - Expression *arg = &e->call.arg_exprs[i]; + Expression *arg = &e->call.arg_exprs[i]; + if (arg_is_const(arg, fn_type->constness[i])) { + if (fn_type->constness[i] == CONSTNESS_SEMI) { + if (semi_const_arg_index >= 64) { + err_print(e->where, "You can't have more than 64 semi-constant parameters in a function at the moment."); + return false; + } + *which_are_const |= ((U64)1) << semi_const_arg_index; + semi_const_arg_index++; + } assert(arg->kind == EXPR_VAL); /* should have been evaluated by types.c */ *(Value *)arr_adda(&compile_time_args, g->allocr) = arg->val; *(Type *)arr_add(&tuple_types) = arg->type; @@ -44,7 +60,7 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) { bool already_generated_decl = val_hash_table_adda(g->allocr, f->c.instances, tuple, &tuple_type, &instance_number); if (!already_generated_decl) { /* generate a copy of this function */ - if (!cgen_fn_header(g, f, e->where, instance_number)) + if (!cgen_fn_header(g, f, e->where, instance_number, *which_are_const)) return false; cgen_write(g, ";"); cgen_nl(g); @@ -58,9 +74,18 @@ static bool cgen_decls_expr(CGenerator *g, Expression *e) { e->fn.c.name = NULL; if (!e->fn.c.id) e->fn.c.id = g->ident_counter++; - if (!e->type.fn.constant) { + bool any_const = false; + FnType *fn_type = &e->type.fn; + if (fn_type->constness) { + for (size_t i = 0; i < arr_len(fn_type->types)-1; i++) { + if (fn_type->constness[i] == CONSTNESS_YES) + any_const = true; + } + } + + if (!any_const) { fn_enter(&e->fn, 0); - if (!cgen_fn_header(g, &e->fn, e->where, 0)) + if (!cgen_fn_header(g, &e->fn, e->where, 0, 0)) return false; cgen_write(g, ";"); cgen_nl(g); @@ -79,17 +104,18 @@ static bool cgen_decls_block(CGenerator *g, Block *b) { if (!cgen_block_enter(g, b)) return false; arr_foreach(b->stmts, Statement, s) - cgen_decls_stmt(g, s); + if (!cgen_decls_stmt(g, s)) + return false; cgen_block_exit(g, prev); return true; } static bool cgen_decls_decl(CGenerator *g, Declaration *d) { if (cgen_fn_is_direct(g, d)) { + d->expr.fn.c.name = d->idents[0]; if (!fn_has_any_const_params(&d->expr.fn)) { - d->expr.fn.c.name = d->idents[0]; fn_enter(&d->expr.fn, 0); - if (!cgen_fn_header(g, &d->expr.fn, d->where, 0)) + if (!cgen_fn_header(g, &d->expr.fn, d->where, 0, 0)) return false; cgen_write(g, ";"); cgen_nl(g); @@ -1,7 +1,5 @@ /* TODO: -:@ -don't allow pointers to functions with compile-time arguments type parameters (e.g. fn(foo @ type) {x: foo;}) struct parameters @@ -1,6 +1,7 @@ static bool parse_expr(Parser *p, Expression *e, Token *end); static bool parse_stmt(Parser *p, Statement *s); #define PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR 0x01 +#define PARSE_DECL_ALLOW_SEMI_CONST 0x02 static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, uint16_t flags); static bool is_decl(Tokenizer *t); @@ -174,6 +175,17 @@ static size_t type_to_str_(Type *t, char *buffer, size_t bufsize) { for (size_t i = 0; i < nparams; i++) { if (i > 0) written += str_copy(buffer + written, bufsize - written, ", "); + if (t->fn.constness) { + switch (t->fn.constness[i]) { + case CONSTNESS_NO: break; + case CONSTNESS_SEMI: + written += str_copy(buffer + written, bufsize - written, ":@"); + break; + case CONSTNESS_YES: + written += str_copy(buffer + written, bufsize - written, "@"); + break; + } + } written += type_to_str_(¶m_types[i], buffer + written, bufsize - written); } written += str_copy(buffer + written, bufsize - written, ")"); @@ -382,6 +394,7 @@ static bool parse_type(Parser *p, Type *type) { /* function type */ type->kind = TYPE_FN; type->fn.types = NULL; + type->fn.constness = NULL; t->token++; if (!token_is_kw(t->token, KW_LPAREN)) { tokr_err(t, "Expected ( to follow fn."); @@ -763,7 +776,7 @@ static bool parse_decl_list(Parser *p, Declaration **decls, DeclEndKind decl_end !token_is_kw(t->token - 1, KW_LBRACE)))) { first = false; Declaration *decl = parser_arr_add(p, decls); - if (!parse_decl(p, decl, decl_end, PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR)) { + if (!parse_decl(p, decl, decl_end, PARSE_DECL_ALLOW_CONST_WITH_NO_EXPR | PARSE_DECL_ALLOW_SEMI_CONST)) { ret = false; /* skip to end of list */ while (t->token->kind != TOKEN_EOF && !ends_decl(t->token, decl_end)) @@ -808,7 +821,7 @@ static bool parse_fn_expr(Parser *p, FnExpr *f) { if (!parse_decl_list(p, &f->ret_decls, DECL_END_LBRACE_COMMA)) return false; arr_foreach(f->ret_decls, Declaration, d) { - if (d->flags & DECL_IS_CONST) { + if ((d->flags & DECL_IS_CONST) || (d->flags & DECL_SEMI_CONST)) { err_print(d->where, "Named return values cannot be constant."); return false; } @@ -1691,7 +1704,7 @@ static inline bool ends_decl(Token *t, DeclEndKind ends_with) { } } -static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, uint16_t flags) { +static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, U16 flags) { d->where = p->tokr->token->where; d->idents = NULL; Tokenizer *t = p->tokr; @@ -1711,6 +1724,10 @@ static bool parse_decl(Parser *p, Declaration *d, DeclEndKind ends_with, uint16_ } if (token_is_kw(t->token, KW_COLON)) { t->token++; + if (token_is_kw(t->token, KW_AT) && (flags & PARSE_DECL_ALLOW_SEMI_CONST)) { + t->token++; + d->flags |= DECL_SEMI_CONST; + } break; } if (token_is_kw(t->token, KW_AT)) { @@ -2188,3 +2205,44 @@ static inline Type *decl_type_at_index(Declaration *d, int i) { return d->type.kind == TYPE_TUPLE ? &d->type.tuple[i] : &d->type; } + +static bool expr_is_definitely_const(Expression *e) { + switch (e->kind) { + case EXPR_LITERAL_FLOAT: + case EXPR_LITERAL_INT: + case EXPR_LITERAL_CHAR: + case EXPR_LITERAL_STR: + case EXPR_LITERAL_BOOL: + case EXPR_DSIZEOF: + case EXPR_DALIGNOF: + case EXPR_TYPE: + case EXPR_VAL: + return true; + case EXPR_IF: + case EXPR_WHILE: + case EXPR_C: + case EXPR_NEW: + case EXPR_CAST: + case EXPR_CALL: + case EXPR_BLOCK: + case EXPR_TUPLE: + case EXPR_EACH: + case EXPR_FN: + return false; + case EXPR_UNARY_OP: + return expr_is_definitely_const(e->unary.of); + case EXPR_BINARY_OP: + return expr_is_definitely_const(e->binary.lhs) + && expr_is_definitely_const(e->binary.rhs); + case EXPR_SLICE: + return expr_is_definitely_const(e->slice.of); + case EXPR_IDENT: { + IdentDecl *idecl = ident_decl(e->ident); + assert(idecl); + return idecl->kind == IDECL_DECL + && (idecl->decl->flags & DECL_IS_CONST); + } + } + assert(0); + return false; +} @@ -2,19 +2,17 @@ puti @= fn(x: int) { #C("printf(\"%ld\\n\", (long)x); "); }; -putf @= fn(x: float) { - #C("printf(\"%f\\n\", (double)x); -"); -}; -x,z @= fn(x: int, y @ int) int { - x+y -}; +// putf @= fn(x: float) { + // #C("printf(\"%f\\n\", (double)x); +// "); +// }; +// f@= fn(x: int, y :@ int) int { +// x+y +// }; + +asdf @= fn(x :@= 18) int { x }; main @= fn() { - puti(x(10, 30)); - a @= x(20, 40); - puti(a); - puti(z(10, 30)); - b @= z(20, 40); - puti(b); + something := asdf; + puti(something(100)); }; diff --git a/typedefs_cgen.c b/typedefs_cgen.c index a6453de..77ea7f5 100644 --- a/typedefs_cgen.c +++ b/typedefs_cgen.c @@ -5,7 +5,8 @@ static bool typedefs_block(CGenerator *g, Block *b) { if (!cgen_block_enter(g, b)) return false; arr_foreach(b->stmts, Statement, s) - typedefs_stmt(g, s); + if (!typedefs_stmt(g, s)) + return false; cgen_block_exit(g, prev); return true; } @@ -55,7 +55,16 @@ static bool type_eq(Type *a, Type *b) { case TYPE_FN: { if (arr_len(a->fn.types) != arr_len(b->fn.types)) return false; Type *a_types = a->fn.types, *b_types = b->fn.types; + Constness *a_constness = a->fn.constness, *b_constness = b->fn.constness; for (size_t i = 0; i < arr_len(a->fn.types); i++) { + Constness const_a = CONSTNESS_NO, const_b = CONSTNESS_NO; + if (a_constness) + const_a = a_constness[i]; + if (b_constness) + const_b = b_constness[i]; + if ((const_a == CONSTNESS_NO && const_b == CONSTNESS_YES) + || (const_a == CONSTNESS_YES && const_b == CONSTNESS_NO)) + return false; if (!type_eq(&a_types[i], &b_types[i])) return false; @@ -177,7 +186,7 @@ static bool type_of_fn(Typer *tr, Expression *e, Type *t) { FnExpr *f = &e->fn; t->kind = TYPE_FN; t->fn.types = NULL; - t->fn.constant = NULL; /* OPTIM: constant doesn't need to be a dynamic array */ + t->fn.constness = NULL; /* OPTIM: constant doesn't need to be a dynamic array */ bool has_constant_params = false; Type *ret_type = typer_arr_add(tr, &t->fn.types); if (f->ret_decls && f->ret_type.kind == TYPE_VOID /* haven't found return type yet */) { @@ -215,12 +224,12 @@ static bool type_of_fn(Typer *tr, Expression *e, Type *t) { if (!types_decl(tr, decl)) return false; if (!type_resolve(tr, &decl->type, e->where)) return false; - unsigned is_const = decl->flags & DECL_IS_CONST; - if (is_const) { - if (!t->fn.constant) { + U32 is_at_all_const = decl->flags & (DECL_IS_CONST | DECL_SEMI_CONST); + if (is_at_all_const) { + if (!t->fn.constness) { has_constant_params = true; for (size_t i = 0; i < idx; i++) { - *(bool *)typer_arr_add(tr, &t->fn.constant) = false; + *(Constness *)typer_arr_add(tr, &t->fn.constness) = CONSTNESS_NO; } } } @@ -239,7 +248,15 @@ static bool type_of_fn(Typer *tr, Expression *e, Type *t) { Type *param_type = typer_arr_add(tr, &t->fn.types); *param_type = decl->type; if (has_constant_params) { - *(bool *)typer_arr_add(tr, &t->fn.constant) = is_const != 0; + Constness constn; + if (decl->flags & DECL_IS_CONST) { + constn = CONSTNESS_YES; + } else if (decl->flags & DECL_SEMI_CONST) { + constn = CONSTNESS_SEMI; + } else { + constn = CONSTNESS_NO; + } + *(Constness *)typer_arr_add(tr, &t->fn.constness) = constn; } idx++; } @@ -563,6 +580,17 @@ static Status type_cast_status(Type *from, Type *to) { return STATUS_ERR; } +static bool arg_is_const(Expression *arg, Constness constness) { + switch (constness) { + case CONSTNESS_NO: return false; + case CONSTNESS_SEMI: return expr_is_definitely_const(arg); + case CONSTNESS_YES: return true; + } + assert(0); + return false; +} + + static bool types_expr(Typer *tr, Expression *e) { if (e->flags & EXPR_FOUND_TYPE) return true; Type *t = &e->type; @@ -999,11 +1027,8 @@ static bool types_expr(Typer *tr, Expression *e) { } if (!ret) return false; - bool any_const = false; FnType *fn_type = &f->type.fn; for (size_t i = 0; i < nparams; i++) { - if (fn_type->constant && fn_type->constant[i]) - any_const = true; if (!params_set[i]) { size_t index = 0; assert(fn_decl); /* we can only miss an arg if we're using named/optional args */ @@ -1036,10 +1061,12 @@ static bool types_expr(Typer *tr, Expression *e) { } } } - if (any_const) { + if (fn_type->constness) { /* evaluate compile-time arguments */ for (size_t i = 0; i < arr_len(fn_type->types)-1; i++) { - if (fn_type->constant[i]) { + bool should_be_evald = arg_is_const(&new_args[i], fn_type->constness[i]); + + if (should_be_evald) { Value arg_val; if (!eval_expr(tr->evalr, &new_args[i], &arg_val)) { if (tr->evalr->enabled) { @@ -1048,6 +1075,7 @@ static bool types_expr(Typer *tr, Expression *e) { return false; } new_args[i].kind = EXPR_VAL; + new_args[i].flags = 0; new_args[i].val = arg_val; i++; } @@ -1544,6 +1572,16 @@ static bool types_decl(Typer *tr, Declaration *d) { success = false; goto ret; } + } else if (!(d->flags & DECL_IS_CONST) && t->kind == TYPE_FN && t->fn.constness) { + for (size_t p = 0; p < arr_len(t->fn.types)-1; p++) { + if (t->fn.constness[p] == CONSTNESS_YES) { + err_print(d->where, "You can't have a pointer to a function with constant parameters."); + success = false; + goto ret; + } + } + /* make constness NULL, so that semi-constant parameters turn into non-constant arguments */ + t->fn.constness = NULL; } } @@ -313,9 +313,15 @@ typedef struct { #define TYPE_IS_RESOLVED 0x02 #define TYPE_STRUCT_FOUND_OFFSETS 0x04 +typedef U8 Constness; + +#define CONSTNESS_NO ((Constness)0) +#define CONSTNESS_SEMI ((Constness)1) +#define CONSTNESS_YES ((Constness)2) + typedef struct { struct Type *types; /* dynamic array [0] = ret_type, [1:] = param_types */ - bool *constant; /* [i] = is param #i constant? if NULL, none are constant. don't use it as a dynamic array, because eventually it might not be. */ + Constness *constness; /* [i] = constness of param #i. iff no parameters are constant, this is NULL. don't use it as a dynamic array, because eventually it might not be. */ } FnType; typedef struct Type { @@ -588,13 +594,16 @@ typedef struct Argument { Expression val; } Argument; -#define DECL_ANNOTATES_TYPE 0x01 -#define DECL_IS_CONST 0x02 -#define DECL_HAS_EXPR 0x04 -#define DECL_FOUND_TYPE 0x08 -#define DECL_ERRORED_ABOUT_SELF_REFERENCE 0x10 /* has there been an error about this decl referencing itself? */ -#define DECL_FOUND_VAL 0x20 - +enum { + DECL_ANNOTATES_TYPE = 0x01, + DECL_IS_CONST = 0x02, + DECL_SEMI_CONST = 0x04, + DECL_HAS_EXPR = 0x08, + DECL_FOUND_TYPE = 0x10, + DECL_ERRORED_ABOUT_SELF_REFERENCE = 0x20, /* has there been an error about this decl referencing itself? */ + DECL_FOUND_VAL = 0x40, +}; + /* OPTIM: Instead of using dynamic arrays, do two passes. */ typedef struct Declaration { Location where; |