렉서 다음은 파서다. 노드는 한 배열에 살고 자식은 그 안의 인덱스다 -- 노드는 ^Node 를 들 수 없고(여럿이며 한 번씩 소유하지 않는다) &Node 도 들 수 없다(R4). 인덱스는 둘 다 아니다. 소스도 필드가 아니라 매 단계에 같이 다닌다. unit demo / fn answer @2 / let n = (+ 1 (* 2 3)) / return n / balanced 전위 표기로 다시 찍는 것이 시험의 요점이다. 1 + 2 * 3 이 어떻게 묶였는지는 그렇게만 보인다. 쓰면서 나온 컴파일러 버그 넷: 1. 다른 유닛의 타입을 필드로 쓰면 그 필드 타입이 영영 UNKNOWN 이었다. 필드 해석이 유닛마다 선언 직후에 돌아서, 아직 선언되지 않은 유닛의 타입을 찾다 실패하고 그 답을 굳혔다. 이제 모든 유닛이 선언을 마친 뒤에 한 번 푼다. 2. 그리고 그 해석은 타입을 선언한 유닛에서 해야 한다. 필드 타입은 그 유닛의 import 로 쓰였는데 아무 유닛에서나 풀고 있었다. 타입 계층에 enter/leave 콜백을 두고 체커가 그 자리로 데려간다. 3. cycle_state 를 재귀 검사와 크기 계산이 같이 썼다. 첫 번째가 보는 중인 구조체 가 두 번째에게는 다 끝난 것으로 보여서, 필드가 하나뿐인 것처럼 1 바이트로 자리를 잡았다 -- Parser 가 그래서 자기 토큰을 밟았다. layout_state 로 나눴다. 4. 다른 유닛의 상수(ast.NONE)를 lowering 이 필드 접근으로 봤다. 체커가 이미 링크 이름을 붙여두었으니 그것이 있으면 전역이다. 그리고 R1 을 실제로 지키게 했다: 소유자를 놓으면 그것이 가진 것도 놓는다. 전에는 자기 drop 이 있거나 자기가 owned 일 때만이어서, drop 을 가진 타입을 필드로 담은 구조체는 그것을 놓을 방법이 없었다(drop 은 손으로 못 부른다). 이제 release_at 이 drop 을 부르고 필드로 내려간다. 그 덕에 List/Arena/Map 의 drop 이 전부 필요 없어져서 지웠다 -- 버퍼가 owned 이니 R1 이 알아서 한다. 221/221, 29/29.
185 lines
6.9 KiB
C
185 lines
6.9 KiB
C
#include "checkpri.h"
|
|
|
|
int m7_ast_reference_storage(FeNode *type)
|
|
{
|
|
if (!type || !type->text) return 0;
|
|
if (strcmp(type->text,"&")==0 || strcmp(type->text,"&mut")==0 ||
|
|
(strcmp(type->text,"[")==0 && !type->a) ||
|
|
strcmp(type->text,"str")==0)
|
|
return 1;
|
|
if (strcmp(type->text,"?")==0)
|
|
return m7_ast_reference_storage(type->a);
|
|
if (strcmp(type->text,"^")==0) return 0;
|
|
return 0;
|
|
}
|
|
|
|
void m7_check_storage(FeCheck *c, FeNode *decl)
|
|
{
|
|
FeNode *m;
|
|
if (!decl) return;
|
|
if (decl->kind==FE_N_STRUCT || decl->kind==FE_N_ENUM) {
|
|
/* This pass exists for the shapes the other one cannot see, such as a
|
|
reference behind an optional. A plain `&T` field is seen by both, so
|
|
leave that one to check_reference_storage below. */
|
|
for (m=decl->children;m;m=m->next)
|
|
if (m->kind==FE_N_FIELD && m7_ast_reference_storage(m->a) &&
|
|
!own_ast_reference_type(m->a) &&
|
|
!own_ast_pointer_to_reference(m->a))
|
|
err(c,m->loc,"reference type is not allowed in aggregate storage");
|
|
}
|
|
check_reference_storage(c,decl);
|
|
}
|
|
|
|
void m7_validate_error_decl(FeCheck *c, FeNode *decl)
|
|
{
|
|
FeNode *a;
|
|
FeNode *b;
|
|
unsigned long code;
|
|
unsigned long other;
|
|
if (!decl || decl->kind!=FE_N_ERROR_DECL) return;
|
|
for (a=decl->children;a;a=a->next) {
|
|
if (!a->a || a->a->kind!=FE_N_LITERAL || !a->a->text) continue;
|
|
code=strtoul(a->a->text,0,0);
|
|
if (code==0UL)
|
|
err(c,a->loc,"error code 0 is reserved for success");
|
|
for (b=decl->children;b && b!=a;b=b->next) {
|
|
if (a->text && b->text && strcmp(a->text,b->text)==0) {
|
|
err(c,a->loc,"duplicate error member name");
|
|
break;
|
|
}
|
|
if (b->a && b->a->kind==FE_N_LITERAL && b->a->text) {
|
|
other=strtoul(b->a->text,0,0);
|
|
if (other==code) {
|
|
err(c,a->loc,"duplicate error numeric code");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Everything a unit declares, before any body anywhere is looked at. */
|
|
void declare_unit(FeCheck *c)
|
|
{
|
|
FeNode *n;
|
|
/* A generic declaration is not a type; only its instances are. */
|
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
|
|
if (n->kind==FE_N_STRUCT && !decl_is_generic(n))
|
|
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) {
|
|
FeNode *m;
|
|
m7_check_storage(c,n);
|
|
if (n->kind==FE_N_ERROR_DECL) m7_validate_error_decl(c,n);
|
|
check_generic_params(c,n);
|
|
for (m=n->kind==FE_N_STRUCT ? n->children : 0;m;m=m->next)
|
|
if (m->kind==FE_N_FN) check_generic_params(c,m);
|
|
}
|
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
|
|
if (n->kind==FE_N_ENUM && !decl_is_generic(n))
|
|
fe_type_declare_enum(&c->types,n);
|
|
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);
|
|
}
|
|
|
|
/* 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. */
|
|
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) {
|
|
if (n->kind==FE_N_STRUCT) {
|
|
for (m=n->children;m;m=m->next) if (m->kind==FE_N_FN) {
|
|
sprintf(method_name,"%s_%s",n->text ? n->text : "Type",
|
|
m->text ? m->text : "method");
|
|
m->cname=unit_cname(c,method_name);
|
|
}
|
|
}
|
|
if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) {
|
|
t=n->a ? node_type(c,n->a) : unknown(c);
|
|
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);
|
|
}
|
|
}
|
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
|
|
if (n->kind==FE_N_FN) {
|
|
t=fe_type_intern(&c->types,"<fn>");
|
|
/* `extern "c"` means the linker already knows this name, so it is
|
|
not decorated with the unit it was declared in. */
|
|
add_symbol(s,globals,n->text,t,n,0,1,
|
|
(n->flags & FE_NODE_EXTERN) && n->text ? n->text :
|
|
unit_cname(c,n->text ? n->text : "fn"),n);
|
|
}
|
|
return globals;
|
|
}
|
|
|
|
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)
|
|
if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) {
|
|
sym=find_current(s->globals,n->text ? n->text : "");
|
|
if (n->kind==FE_N_CONST && const_names_type(s,n)) continue;
|
|
if (n->b) {
|
|
iv=m7_check_expected(s,n->b,sym ? sym->type : 0);
|
|
if (sym && sym->type->kind==FE_TYPE_UNKNOWN) {
|
|
sym->type=iv;
|
|
n->sem_type=iv;
|
|
} else if (sym && !fe_type_equal(sym->type,iv) &&
|
|
!m7_actual_compatible(sym->type,iv,n->b))
|
|
err(c,n->loc,"global initializer type mismatch");
|
|
}
|
|
}
|
|
/* A generic body means nothing until its parameters are bound, so it is
|
|
checked once per instance and not here. */
|
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
|
|
if (n->kind==FE_N_FN && !decl_is_generic(n)) check_fn(c,n,s->globals);
|
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next)
|
|
if (n->kind==FE_N_STRUCT && !decl_is_generic(n)) {
|
|
t=fe_type_intern(&c->types,n->text);
|
|
for (m=n->children;m;m=m->next)
|
|
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); }
|
|
/* Only now: a field may name a type in a unit that had not declared it
|
|
yet, and resolving it early would freeze the wrong answer in place. */
|
|
for (u=0;u<c->build->count;++u) { enter_unit(c,u); check_type_cycles(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);
|
|
return c->diags->errors==0;
|
|
}
|