refactor: 빌드 전체를 하나의 검사기로 본다

유닛마다 FeCheck 를 새로 만들면 타입 문맥도 유닛마다 따로 생겨서 유닛 경계를
넘는 이름을 볼 수가 없었다. 검사기가 빌드 전체를 맡고, 모든 유닛의 선언을
등록한 뒤에 어느 본문이든 보기 시작한다.

스코프와 심볼과 타입은 현재 유닛보다 오래 살아야 하므로 AST 아레나가 아니라
검사기 자신의 아레나에서 잡는다. 이름이 같아도 유닛이 다르면 다른 타입이므로
nominal 타입은 선언한 유닛으로도 구분한다.

pub 은 파서가 버리고 있었다. 이제 FE_NODE_PUB 으로 남긴다.
This commit is contained in:
2026-08-17 05:00:31 +09:00
parent de02b8bbbe
commit 96e05aba5b
7 changed files with 161 additions and 63 deletions
+6
View File
@@ -37,6 +37,12 @@ struct FeNode {
unsigned flags; unsigned flags;
}; };
/* Bits in FeNode.flags. 0x100 and above belong to own.h. */
#define FE_NODE_PACKED 0x1U
#define FE_NODE_STATIC 0x2U
#define FE_NODE_SHARED 0x4U
#define FE_NODE_PUB 0x8U
typedef struct FeAst { typedef struct FeAst {
FeArena arena; FeArena arena;
FeNode *root; FeNode *root;
+94 -49
View File
@@ -8,7 +8,7 @@
#include <stdio.h> #include <stdio.h>
typedef struct FeSym FeSym; typedef struct FeSym FeSym;
typedef struct FeScope FeScope; /* FeScope is forward declared in check.h. */
struct FeSym { struct FeSym {
const char *name; const char *name;
@@ -156,7 +156,7 @@ static char *unit_cname(FeCheck *c, const char *name)
u = c->ast->root && c->ast->root->text ? c->ast->root->text : "unit"; u = c->ast->root && c->ast->root->text ? c->ast->root->text : "unit";
n = (unsigned long)strlen("fe_") + (unsigned long)strlen(u) + n = (unsigned long)strlen("fe_") + (unsigned long)strlen(u) +
(unsigned long)strlen(name ? name : "name") + 2UL; (unsigned long)strlen(name ? name : "name") + 2UL;
p = (char *)fe_arena_alloc(&c->ast->arena, n); p = (char *)fe_arena_alloc(&c->arena, n);
if (!p) return 0; if (!p) return 0;
strcpy(p, "fe_"); strcpy(p, "fe_");
strcat(p, u); strcat(p, u);
@@ -173,7 +173,7 @@ static char *local_cname(FeCheck *c, const char *name)
sprintf(number, "%u", c->local_serial++); sprintf(number, "%u", c->local_serial++);
n = (unsigned long)strlen("fe_l_") + (unsigned long)strlen(name) + n = (unsigned long)strlen("fe_l_") + (unsigned long)strlen(name) +
(unsigned long)strlen(number) + 2UL; (unsigned long)strlen(number) + 2UL;
p = (char *)fe_arena_alloc(&c->ast->arena, n); p = (char *)fe_arena_alloc(&c->arena, n);
if (!p) return 0; if (!p) return 0;
strcpy(p, "fe_l_"); strcpy(p, "fe_l_");
strcat(p, name ? name : "local"); strcat(p, name ? name : "local");
@@ -185,7 +185,7 @@ static char *local_cname(FeCheck *c, const char *name)
static FeScope *scope_new(FeCheckerState *s, FeScope *parent) static FeScope *scope_new(FeCheckerState *s, FeScope *parent)
{ {
FeScope *scope; FeScope *scope;
scope = (FeScope *)fe_arena_alloc(&s->c->ast->arena, sizeof(FeScope)); scope = (FeScope *)fe_arena_alloc(&s->c->arena, sizeof(FeScope));
if (!scope) { if (!scope) {
err(s->c, s->c->ast->root->loc, "out of memory creating scope"); err(s->c, s->c->ast->root->loc, "out of memory creating scope");
return parent; return parent;
@@ -234,7 +234,7 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
} }
if (scope->count == scope->capacity) { if (scope->count == scope->capacity) {
capacity = scope->capacity ? scope->capacity * 2U : 8U; capacity = scope->capacity ? scope->capacity * 2U : 8U;
items = (FeSym *)fe_arena_alloc(&s->c->ast->arena, items = (FeSym *)fe_arena_alloc(&s->c->arena,
capacity * sizeof(FeSym)); capacity * sizeof(FeSym));
if (!items) { if (!items) {
err(s->c, decl ? decl->loc : s->c->ast->root->loc, err(s->c, decl ? decl->loc : s->c->ast->root->loc,
@@ -267,16 +267,38 @@ static FeSym *add_symbol(FeCheckerState *s, FeScope *scope,
return sym; return sym;
} }
void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, /* Make `unit` the one being checked. Types intern against its name, cnames
are built from it, and diagnostics quote its source rather than whichever
file happened to be parsed last. */
static void enter_unit(FeCheck *c, unsigned index)
{
FeUnit *u = &c->build->units[index];
c->unit = u;
c->ast = &u->ast;
c->types.unit_name = u->name[0] ? u->name : "unit";
fe_diags_source(c->diags, u->source, u->size);
}
void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags,
unsigned pointer_bits, int no_checks) unsigned pointer_bits, int no_checks)
{ {
c->ast = ast; unsigned i;
fe_arena_init(&c->arena, 16384);
c->build = build;
c->unit = 0;
c->ast = build->count ? &build->units[0].ast : 0;
for (i = 0; i < FE_BUILD_UNIT_MAX; ++i) c->unit_scope[i] = 0;
c->diags = diags; c->diags = diags;
c->pointer_bits = pointer_bits; c->pointer_bits = pointer_bits;
c->local_serial = 0; c->local_serial = 0;
c->no_checks = no_checks; c->no_checks = no_checks;
fe_types_init(&c->types, &ast->arena, pointer_bits); fe_types_init(&c->types, &c->arena, pointer_bits);
c->types.unit_name = ast->root && ast->root->text ? ast->root->text : "unit"; c->types.unit_name = "unit";
}
void fe_check_destroy(FeCheck *c)
{
fe_arena_destroy(&c->arena);
} }
static FeType *check_expr(FeCheckerState *s, FeNode *n); static FeType *check_expr(FeCheckerState *s, FeNode *n);
@@ -517,7 +539,7 @@ static void own_release_after_stmt(FeCheckerState *s, FeScope *scope,
static FeOwnState *flow_own_new(FeCheckerState *s, unsigned count) static FeOwnState *flow_own_new(FeCheckerState *s, unsigned count)
{ {
if (!s || !count) return 0; if (!s || !count) return 0;
return (FeOwnState *)fe_arena_alloc(&s->c->ast->arena, return (FeOwnState *)fe_arena_alloc(&s->c->arena,
count*sizeof(FeOwnState)); count*sizeof(FeOwnState));
} }
@@ -554,7 +576,7 @@ typedef struct FeFlowBorrow {
static FeFlowBorrow *flow_borrow_new(FeCheckerState *s, unsigned count) static FeFlowBorrow *flow_borrow_new(FeCheckerState *s, unsigned count)
{ {
if (!s || !count) return 0; if (!s || !count) return 0;
return (FeFlowBorrow *)fe_arena_alloc(&s->c->ast->arena, return (FeFlowBorrow *)fe_arena_alloc(&s->c->arena,
count*sizeof(FeFlowBorrow)); count*sizeof(FeFlowBorrow));
} }
@@ -1737,7 +1759,7 @@ static void check_fn(FeCheck *c, FeNode *fn, FeScope *globals)
s.loop_depth=0; s.loop_depth=0;
s.defer_depth=0; s.defer_depth=0;
s.fn_node=fn; s.fn_node=fn;
fe_own_liveness_init(&s.liveness,&c->ast->arena); fe_own_liveness_init(&s.liveness,&c->arena);
fe_own_collect_last_uses(&s.liveness,fn); fe_own_collect_last_uses(&s.liveness,fn);
fn->sem_type = s.ret; fn->sem_type = s.ret;
for (x = fn->a ? fn->a->children : 0; x; x = x->next) { for (x = fn->a ? fn->a->children : 0; x; x = x->next) {
@@ -1765,7 +1787,7 @@ static void check_method(FeCheck *c, FeNode *fn, FeScope *globals,
s.loop_depth=0; s.loop_depth=0;
s.defer_depth=0; s.defer_depth=0;
s.fn_node=fn; s.fn_node=fn;
fe_own_liveness_init(&s.liveness,&c->ast->arena); fe_own_liveness_init(&s.liveness,&c->arena);
fe_own_collect_last_uses(&s.liveness,fn); fe_own_collect_last_uses(&s.liveness,fn);
fn->sem_type=s.ret; fn->sem_type=s.ret;
for(x=fn->a ? fn->a->children : 0; x; x=x->next) { for(x=fn->a ? fn->a->children : 0; x; x=x->next) {
@@ -2776,26 +2798,13 @@ static void m7_validate_error_decl(FeCheck *c, FeNode *decl)
} }
} }
int fe_check_program(FeCheck *c) /* Everything a unit declares, before any body anywhere is looked at. */
static void declare_unit(FeCheck *c)
{ {
FeCheckerState s;
FeNode *n; FeNode *n;
FeNode *m;
FeSym *sym;
FeType *t;
FeType *iv;
char method_name[128];
s.c=c;
s.scope=scope_new(&s,0);
s.globals=s.scope;
s.ret=fe_type_intern(&c->types,"void");
s.loop_depth=0;
s.defer_depth=0;
s.fn_node=0;
fe_own_liveness_init(&s.liveness,&c->ast->arena);
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_STRUCT) if (n->kind==FE_N_STRUCT)
fe_type_declare_struct(&c->types,n,(n->flags & 1U)!=0); fe_type_declare_struct(&c->types,n,(n->flags & FE_NODE_PACKED)!=0);
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) { for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) {
m7_check_storage(c,n); m7_check_storage(c,n);
if (n->kind==FE_N_ERROR_DECL) m7_validate_error_decl(c,n); if (n->kind==FE_N_ERROR_DECL) m7_validate_error_decl(c,n);
@@ -2805,7 +2814,20 @@ int fe_check_program(FeCheck *c)
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_ERROR_DECL) fe_type_declare_error(&c->types,n); if (n->kind==FE_N_ERROR_DECL) fe_type_declare_error(&c->types,n);
check_type_cycles(c); check_type_cycles(c);
fe_type_layout_all(&c->types); }
/* The unit's top-level names, in a scope of their own so that another unit
can look into it later without inheriting anything else. */
static FeScope *declare_unit_scope(FeCheck *c, FeCheckerState *s)
{
FeNode *n;
FeNode *m;
FeType *t;
FeScope *globals;
char method_name[128];
globals=scope_new(s,0);
s->scope=globals;
s->globals=globals;
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) { for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) {
if (n->kind==FE_N_STRUCT) { if (n->kind==FE_N_STRUCT) {
for (m=n->children;m;m=m->next) if (m->kind==FE_N_FN) { for (m=n->children;m;m=m->next) if (m->kind==FE_N_FN) {
@@ -2816,21 +2838,31 @@ int fe_check_program(FeCheck *c)
} }
if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) {
t=n->a ? node_type(c,n->a) : unknown(c); t=n->a ? node_type(c,n->a) : unknown(c);
add_symbol(&s,s.globals,n->text,t,0,n->kind==FE_N_GLOBAL, add_symbol(s,globals,n->text,t,0,n->kind==FE_N_GLOBAL,
n->b!=0,unit_cname(c,n->text ? n->text : "global"),n); n->b!=0,unit_cname(c,n->text ? n->text : "global"),n);
} }
} }
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_FN) { if (n->kind==FE_N_FN) {
t=fe_type_intern(&c->types,"<fn>"); t=fe_type_intern(&c->types,"<fn>");
add_symbol(&s,s.globals,n->text,t,n,0,1, add_symbol(s,globals,n->text,t,n,0,1,
unit_cname(c,n->text ? n->text : "fn"),n); unit_cname(c,n->text ? n->text : "fn"),n);
} }
return globals;
}
static void check_unit_bodies(FeCheck *c, FeCheckerState *s)
{
FeNode *n;
FeNode *m;
FeSym *sym;
FeType *t;
FeType *iv;
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) {
sym=find_current(s.globals,n->text ? n->text : ""); sym=find_current(s->globals,n->text ? n->text : "");
if (n->b) { if (n->b) {
iv=m7_check_expected(&s,n->b,sym ? sym->type : 0); iv=m7_check_expected(s,n->b,sym ? sym->type : 0);
if (sym && sym->type->kind==FE_TYPE_UNKNOWN) { if (sym && sym->type->kind==FE_TYPE_UNKNOWN) {
sym->type=iv; sym->type=iv;
n->sem_type=iv; n->sem_type=iv;
@@ -2840,27 +2872,40 @@ int fe_check_program(FeCheck *c)
} }
} }
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_FN) check_fn(c,n,s.globals); if (n->kind==FE_N_FN) check_fn(c,n,s->globals);
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
if (n->kind==FE_N_STRUCT) { if (n->kind==FE_N_STRUCT) {
t=fe_type_intern(&c->types,n->text); t=fe_type_intern(&c->types,n->text);
for (m=n->children;m;m=m->next) for (m=n->children;m;m=m->next)
if (m->kind==FE_N_FN) check_method(c,m,s.globals,t); if (m->kind==FE_N_FN) check_method(c,m,s->globals,t);
}
}
int fe_check_program(FeCheck *c)
{
FeCheckerState s;
unsigned u;
s.c=c;
s.scope=0;
s.globals=0;
s.ret=fe_type_intern(&c->types,"void");
s.loop_depth=0;
s.defer_depth=0;
s.fn_node=0;
fe_own_liveness_init(&s.liveness,&c->arena);
for (u=0;u<c->build->count;++u) { enter_unit(c,u); declare_unit(c); }
fe_type_layout_all(&c->types);
for (u=0;u<c->build->count;++u) {
enter_unit(c,u);
c->unit_scope[u]=declare_unit_scope(c,&s);
}
for (u=0;u<c->build->count;++u) {
enter_unit(c,u);
s.scope=c->unit_scope[u];
s.globals=c->unit_scope[u];
check_unit_bodies(c,&s);
} }
fe_type_layout_all(&c->types); fe_type_layout_all(&c->types);
return c->diags->errors==0; return c->diags->errors==0;
} }
FeType *fe_check_expr_type(FeCheck *c, FeNode *n)
{
FeCheckerState s;
s.c=c;
s.scope=scope_new(&s,0);
s.globals=s.scope;
s.ret=fe_type_intern(&c->types,"void");
s.loop_depth=0;
s.defer_depth=0;
s.fn_node=0;
fe_own_liveness_init(&s.liveness,&c->ast->arena);
return check_expr(&s,n);
}
+16 -3
View File
@@ -3,9 +3,22 @@
#include "types.h" #include "types.h"
#include "diag.h" #include "diag.h"
#include "resolve.h"
typedef struct FeScope FeScope;
/* The checker spans a whole build, not one file. Names cross unit boundaries,
so every unit's declarations have to exist before any unit's bodies are
looked at, and they all have to be interned in one type context or the same
spelling in two units would not be the same type. */
typedef struct FeCheck { typedef struct FeCheck {
FeAst *ast; /* Scopes, symbols and types outlive whichever unit is current, so they
come from the checker's own arena rather than from an AST's. */
FeArena arena;
FeBuild *build;
FeAst *ast; /* the unit being checked now */
FeUnit *unit; /* its entry in the build */
FeScope *unit_scope[FE_BUILD_UNIT_MAX];
FeTypeCtx types; FeTypeCtx types;
FeDiags *diags; FeDiags *diags;
unsigned pointer_bits; unsigned pointer_bits;
@@ -13,9 +26,9 @@ typedef struct FeCheck {
int no_checks; int no_checks;
} FeCheck; } FeCheck;
void fe_check_init(FeCheck *c, FeAst *ast, FeDiags *diags, void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags,
unsigned pointer_bits, int no_checks); unsigned pointer_bits, int no_checks);
void fe_check_destroy(FeCheck *c);
int fe_check_program(FeCheck *c); int fe_check_program(FeCheck *c);
FeType *fe_check_expr_type(FeCheck *c, FeNode *n);
#endif #endif
+3 -5
View File
@@ -90,13 +90,11 @@ int main(int argc, char **argv)
every unit's AST. */ every unit's AST. */
{ {
FeBuild build; FeBuild build;
unsigned u;
int ok=fe_build_load(&build,file,&d); int ok=fe_build_load(&build,file,&d);
for(u=0;ok && u<build.count;u++){ if(ok){
FeUnit *unit=&build.units[u]; fe_check_init(&check,&build,&d,pointer_bits,no_checks);
fe_diags_source(&d,unit->source,unit->size);
fe_check_init(&check,&unit->ast,&d,pointer_bits,no_checks);
if(!fe_check_program(&check)) ok=0; if(!fe_check_program(&check)) ok=0;
fe_check_destroy(&check);
} }
fe_build_destroy(&build); fe_build_destroy(&build);
/* Semantic analysis is the last pass there is. A code generator /* Semantic analysis is the last pass there is. A code generator
+8 -8
View File
@@ -194,14 +194,14 @@ static FeNode *params(FeParser *p)
static FeNode *fn_decl(FeParser *p, int pub, int external, int interrupt, int interrupt_safe) static FeNode *fn_decl(FeParser *p, int pub, int external, int interrupt, int interrupt_safe)
{ {
FeToken t=p->current, name; FeNode *n; FeToken t=p->current, name; FeNode *n;
(void)pub; (void)external; (void)interrupt; (void)interrupt_safe; (void)external; (void)interrupt; (void)interrupt_safe;
want(p,FE_TOK_FN,"expected 'fn'"); if(!is_name(p)){error(p,"expected function name");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"fn",2);} want(p,FE_TOK_FN,"expected 'fn'"); if(!is_name(p)){error(p,"expected function name");return fe_node(p->ast,FE_N_ERROR_NODE,t.loc,"fn",2);}
name=p->current; n=toknode(p,FE_N_FN,t); n->text=fe_arena_strdup(&p->ast->arena,name.begin,name.length); next(p); n->a=params(p); if(eat(p,FE_TOK_ARROW)) n->b=type(p); if(eat(p,FE_TOK_SEMI)) return n; n->c=block(p); return n; name=p->current; n=toknode(p,FE_N_FN,t); if(pub) n->flags|=FE_NODE_PUB; n->text=fe_arena_strdup(&p->ast->arena,name.begin,name.length); next(p); n->a=params(p); if(eat(p,FE_TOK_ARROW)) n->b=type(p); if(eat(p,FE_TOK_SEMI)) return n; n->c=block(p); return n;
} }
static FeNode *field(FeParser *p) static FeNode *field(FeParser *p, int pub)
{ {
FeToken t=p->current; FeNode *n; FeToken t=p->current; FeNode *n;
if(!is_name(p)){error(p,"expected field name");recover(p);return 0;} next(p);n=toknode(p,FE_N_FIELD,t);want(p,FE_TOK_COLON,"expected ':' after field");n->a=type(p);if(!eat(p,FE_TOK_COMMA) && !is(p,FE_TOK_RBRACE)) error(p,"expected ',' after field");return n; if(!is_name(p)){error(p,"expected field name");recover(p);return 0;} next(p);n=toknode(p,FE_N_FIELD,t);if(pub)n->flags|=FE_NODE_PUB;want(p,FE_TOK_COLON,"expected ':' after field");n->a=type(p);if(!eat(p,FE_TOK_COMMA) && !is(p,FE_TOK_RBRACE)) error(p,"expected ',' after field");return n;
} }
static FeNode *decl(FeParser *p) static FeNode *decl(FeParser *p)
{ {
@@ -214,11 +214,11 @@ static FeNode *decl(FeParser *p)
if(!is(p,FE_TOK_PACKED)) t=p->current; if(!is(p,FE_TOK_PACKED)) t=p->current;
if(is(p,FE_TOK_FN)) return fn_decl(p,pub,external,interrupt,interrupt_safe); if(is(p,FE_TOK_FN)) return fn_decl(p,pub,external,interrupt,interrupt_safe);
if(eat(p,FE_TOK_PACKED)) t=p->previous; if(eat(p,FE_TOK_PACKED)) t=p->previous;
if(eat(p,FE_TOK_STRUCT)) { n=toknode(p,FE_N_STRUCT,t);if(t.kind==FE_TOK_PACKED)n->flags|=1U;if(!is_name(p)){error(p,"expected struct name");return n;}next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after generic parameters");}want(p,FE_TOK_LBRACE,"expected '{' in struct");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){if(is(p,FE_TOK_PUB))next(p);if(is(p,FE_TOK_FN))fe_node_add(n,fn_decl(p,0,0,0,0));else fe_node_add(n,field(p));}want(p,FE_TOK_RBRACE,"expected '}' after struct");return n; } if(eat(p,FE_TOK_STRUCT)) { n=toknode(p,FE_N_STRUCT,t);if(pub)n->flags|=FE_NODE_PUB;if(t.kind==FE_TOK_PACKED)n->flags|=FE_NODE_PACKED;if(!is_name(p)){error(p,"expected struct name");return n;}next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);if(eat(p,FE_TOK_LPAREN)){while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n,type(p));if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RPAREN,"expected ')' after generic parameters");}want(p,FE_TOK_LBRACE,"expected '{' in struct");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){int mpub=eat(p,FE_TOK_PUB);if(is(p,FE_TOK_FN))fe_node_add(n,fn_decl(p,mpub,0,0,0));else fe_node_add(n,field(p,mpub));}want(p,FE_TOK_RBRACE,"expected '}' after struct");return n; }
if(eat(p,FE_TOK_ENUM)) { n=toknode(p,FE_N_ENUM,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected enum name");want(p,FE_TOK_LBRACE,"expected '{' in enum");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected variant name");recover(p);break;}if(eat(p,FE_TOK_LPAREN)){v->a=type(p);want(p,FE_TOK_RPAREN,"expected ')' in variant");}else if(eat(p,FE_TOK_LBRACE)){while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF))fe_node_add(v,field(p));want(p,FE_TOK_RBRACE,"expected '}' in variant");}fe_node_add(n,v);if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RBRACE,"expected '}' after enum");return n; } if(eat(p,FE_TOK_ENUM)) { n=toknode(p,FE_N_ENUM,t);if(pub)n->flags|=FE_NODE_PUB;if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected enum name");want(p,FE_TOK_LBRACE,"expected '{' in enum");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected variant name");recover(p);break;}if(eat(p,FE_TOK_LPAREN)){v->a=type(p);want(p,FE_TOK_RPAREN,"expected ')' in variant");}else if(eat(p,FE_TOK_LBRACE)){while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF))fe_node_add(v,field(p,1));want(p,FE_TOK_RBRACE,"expected '}' in variant");}fe_node_add(n,v);if(!eat(p,FE_TOK_COMMA))break;}want(p,FE_TOK_RBRACE,"expected '}' after enum");return n; }
if(eat(p,FE_TOK_ERROR_KW)) { n=toknode(p,FE_N_ERROR_DECL,t);if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected error name");want(p,FE_TOK_LBRACE,"expected '{' in error declaration");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected error member");recover(p);break;}want(p,FE_TOK_EQ,"expected '=' in error member");v->a=expr(p,0);want(p,FE_TOK_COMMA,"expected ',' in error declaration");fe_node_add(n,v);}want(p,FE_TOK_RBRACE,"expected '}' after error");return n; } if(eat(p,FE_TOK_ERROR_KW)) { n=toknode(p,FE_N_ERROR_DECL,t);if(pub)n->flags|=FE_NODE_PUB;if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected error name");want(p,FE_TOK_LBRACE,"expected '{' in error declaration");while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)){FeNode *v=toknode(p,FE_N_VARIANT,p->current);if(is_name(p))next(p);else{error(p,"expected error member");recover(p);break;}want(p,FE_TOK_EQ,"expected '=' in error member");v->a=expr(p,0);want(p,FE_TOK_COMMA,"expected ',' in error declaration");fe_node_add(n,v);}want(p,FE_TOK_RBRACE,"expected '}' after error");return n; }
if(eat(p,FE_TOK_SHARED)) { shared=1; if(eat(p,FE_TOK_ATOMIC)) atomic=1; if(!is(p,FE_TOK_VAR)) error(p,"expected 'var' after shared"); } if(eat(p,FE_TOK_SHARED)) { shared=1; if(eat(p,FE_TOK_ATOMIC)) atomic=1; if(!is(p,FE_TOK_VAR)) error(p,"expected 'var' after shared"); }
if(is(p,FE_TOK_CONST)||is(p,FE_TOK_STATIC)||is(p,FE_TOK_VAR)) { FeTokKind kk=p->current.kind;next(p);n=toknode(p,kk==FE_TOK_CONST?FE_N_CONST:FE_N_GLOBAL,t);if(kk==FE_TOK_STATIC)n->flags|=2U;if(shared)n->flags|=4U;if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected declaration name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in declaration");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after declaration");return n; } if(is(p,FE_TOK_CONST)||is(p,FE_TOK_STATIC)||is(p,FE_TOK_VAR)) { FeTokKind kk=p->current.kind;next(p);n=toknode(p,kk==FE_TOK_CONST?FE_N_CONST:FE_N_GLOBAL,t);if(pub)n->flags|=FE_NODE_PUB;if(kk==FE_TOK_STATIC)n->flags|=FE_NODE_STATIC;if(shared)n->flags|=FE_NODE_SHARED;if(is_name(p)){next(p);n->text=fe_arena_strdup(&p->ast->arena,p->previous.begin,p->previous.length);}else error(p,"expected declaration name");if(eat(p,FE_TOK_COLON))n->a=type(p);want(p,FE_TOK_EQ,"expected '=' in declaration");n->b=expr(p,0);want(p,FE_TOK_SEMI,"expected ';' after declaration");return n; }
error(p,"expected declaration"); before=p->current.kind; recover(p); error(p,"expected declaration"); before=p->current.kind; recover(p);
if (p->current.kind==before && p->current.kind!=FE_TOK_EOF) next(p); if (p->current.kind==before && p->current.kind!=FE_TOK_EOF) next(p);
return 0; return 0;
+32 -4
View File
@@ -14,6 +14,7 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind)
t->name[i] = name[i]; t->name[i] = name[i];
t->name[i] = '\0'; t->name[i] = '\0';
t->kind = kind; t->kind = kind;
t->unit = 0;
t->cname = 0; t->cname = 0;
t->maker = 0; t->maker = 0;
t->none_cname = 0; t->none_cname = 0;
@@ -56,6 +57,28 @@ void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits)
ctx->generated_serial = 0; ctx->generated_serial = 0;
} }
/* Does this type answer to `name` for someone checking `unit`? A type with no
unit is shared by everyone; one with a unit answers only inside it. */
static int type_visible_as(const FeType *t, const char *unit, const char *name)
{
if (strcmp(t->name, name) != 0) return 0;
if (!t->unit) return 1;
return unit && strcmp(t->unit, unit) == 0;
}
FeType *fe_type_intern_unit(FeTypeCtx *ctx, const char *unit, const char *name)
{
FeType *t;
if (!name) name = "<unknown>";
if (!unit) return fe_type_intern(ctx, name);
for (t = ctx->types; t; t = t->next)
if (t->unit && strcmp(t->name, name) == 0 &&
strcmp(t->unit, unit) == 0) return t;
t = new_type(ctx, name, FE_TYPE_UNKNOWN);
if (t) t->unit = unit;
return t;
}
FeType *fe_type_intern(FeTypeCtx *ctx, const char *name) FeType *fe_type_intern(FeTypeCtx *ctx, const char *name)
{ {
FeType *t; FeType *t;
@@ -64,7 +87,7 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name)
FeTypeKind kind = FE_TYPE_UNKNOWN; FeTypeKind kind = FE_TYPE_UNKNOWN;
if (!name) name = "<unknown>"; if (!name) name = "<unknown>";
for (t = ctx->types; t; t = t->next) for (t = ctx->types; t; t = t->next)
if (strcmp(t->name, name) == 0) return t; if (type_visible_as(t, ctx->unit_name, name)) return t;
if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID; if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID;
else if (strcmp(name, "bool") == 0) kind = FE_TYPE_BOOL; else if (strcmp(name, "bool") == 0) kind = FE_TYPE_BOOL;
else if (strcmp(name, "char") == 0) kind = FE_TYPE_CHAR; else if (strcmp(name, "char") == 0) kind = FE_TYPE_CHAR;
@@ -246,7 +269,7 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed)
unsigned i = 0; unsigned i = 0;
char *cname; char *cname;
if (!node || !node->text) return 0; if (!node || !node->text) return 0;
t = fe_type_intern(ctx, node->text); t = fe_type_intern_unit(ctx, ctx->unit_name, node->text);
if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_STRUCT) return t; if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_STRUCT) return t;
if (t->kind == FE_TYPE_STRUCT) return t; if (t->kind == FE_TYPE_STRUCT) return t;
t->kind = FE_TYPE_STRUCT; t->kind = FE_TYPE_STRUCT;
@@ -291,7 +314,7 @@ FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node)
unsigned i = 0; unsigned i = 0;
char *cname; char *cname;
if (!node || !node->text) return 0; if (!node || !node->text) return 0;
t = fe_type_intern(ctx, node->text); t = fe_type_intern_unit(ctx, ctx->unit_name, node->text);
if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ENUM) return t; if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ENUM) return t;
if (t->kind == FE_TYPE_ENUM) return t; if (t->kind == FE_TYPE_ENUM) return t;
t->kind = FE_TYPE_ENUM; t->kind = FE_TYPE_ENUM;
@@ -568,7 +591,12 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
int fe_type_equal(const FeType *a, const FeType *b) int fe_type_equal(const FeType *a, const FeType *b)
{ {
return a == b || (a && b && strcmp(a->name, b->name) == 0); if (a == b) return 1;
if (!a || !b) return 0;
if (strcmp(a->name, b->name) != 0) return 0;
/* The same spelling is not the same type across a unit boundary. */
if (!a->unit || !b->unit) return a->unit == b->unit;
return strcmp(a->unit, b->unit) == 0;
} }
int fe_type_is_integer(const FeType *t) int fe_type_is_integer(const FeType *t)
+8
View File
@@ -32,6 +32,10 @@ struct FeVariantType {
struct FeType { struct FeType {
FeTypeKind kind; FeTypeKind kind;
char name[64]; char name[64];
/* The unit that declared this type, for the nominal kinds. NULL for
builtins and for structural types like `[]u8`, which every unit
shares. Two units declaring the same name declare two types. */
const char *unit;
char *cname; char *cname;
char *maker; char *maker;
char *none_cname; char *none_cname;
@@ -76,6 +80,10 @@ typedef struct FeTypeCtx {
void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits); void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits);
FeType *fe_type_intern(FeTypeCtx *ctx, const char *name); FeType *fe_type_intern(FeTypeCtx *ctx, const char *name);
/* Intern a nominal type belonging to `unit` rather than to whichever unit
is being checked. Used to name a type across a unit boundary. */
FeType *fe_type_intern_unit(FeTypeCtx *ctx, const char *unit,
const char *name);
FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node); FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node);
FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem); FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem);
FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem); FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem);