lower: 제네릭 인스턴스와 메서드
모노모피제이션이 실제로 코드를 만드는 자리가 여기다. 프론트엔드는 어떤 인스턴스가 존재하는지만 정했다. 검사기가 인스턴스마다 선언·바인딩·유닛·링크 이름을 기록하고, lowering 이 그 바인딩을 다시 걸고 같은 본문을 자기 이름으로 내린다. 제네릭 선언 자체는 코드가 없다. comptime 인자는 값이 아니므로 호출에서 넘기지 않고 파라미터 자리도 잡지 않는다. 메서드 호출은 도달한 대상을 첫 인자로 넘긴다 -- self: Self 든 self: &Self 든 수신자의 주소로 같다. 구조체 메서드가 아예 lowering 되지 않고 있었다. exec.py 13/13.
This commit is contained in:
+46
-10
@@ -464,12 +464,14 @@ static void pop_bindings(FeCheck *c, const FeBindSave *save);
|
||||
static void bind_self(FeCheck *c, FeType *owner);
|
||||
static void instance_key(char *out, const char *unit, const char *name,
|
||||
FeType **args, unsigned count);
|
||||
static int instance_record(FeCheck *c, const char *key, FeLoc loc);
|
||||
static int instance_record(FeCheck *c, const char *key, FeLoc loc,
|
||||
FeNode *decl, FeUnit *home, FeType *owner);
|
||||
static const char *instance_cname(FeCheck *c, const char *key);
|
||||
static int instance_descend(FeCheck *c, FeLoc loc);
|
||||
static void instantiate_body(FeCheck *c, FeUnit *home, FeNode *decl,
|
||||
FeType *owner, FeBindSave *bindings, FeLoc site);
|
||||
static void check_instance_method(FeCheckerState *s, FeType *owner,
|
||||
FeNode *method, FeLoc site);
|
||||
FeNode *method, FeLoc site, FeNode *call);
|
||||
|
||||
typedef struct FeFlowSlot {
|
||||
FeSym *sym;
|
||||
@@ -1318,7 +1320,7 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n)
|
||||
fe_type_intern(&c->types,"void");
|
||||
if (bound) {
|
||||
pop_bindings(c,&msave);
|
||||
check_instance_method(s,et,method,n->loc);
|
||||
check_instance_method(s,et,method,n->loc,n);
|
||||
}
|
||||
return n->sem_type;
|
||||
}
|
||||
@@ -2249,6 +2251,14 @@ static void instance_key(char *out, const char *unit, const char *name,
|
||||
|
||||
/* Already built, or being built right now. Re-asking for a pending instance is
|
||||
how a recursive generic terminates, so it must not look like a new one. */
|
||||
static const char *instance_cname(FeCheck *c, const char *key)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0;i<c->instance_count;++i)
|
||||
if (!strcmp(c->instances[i].key,key)) return c->instances[i].cname;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instance_known(FeCheck *c, const char *key)
|
||||
{
|
||||
unsigned i;
|
||||
@@ -2257,14 +2267,27 @@ static int instance_known(FeCheck *c, const char *key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int instance_record(FeCheck *c, const char *key, FeLoc loc)
|
||||
static int instance_record(FeCheck *c, const char *key, FeLoc loc,
|
||||
FeNode *decl, FeUnit *home, FeType *owner)
|
||||
{
|
||||
FeInstance *inst;
|
||||
unsigned i;
|
||||
if (instance_known(c,key)) return 0;
|
||||
if (c->instance_count>=FE_GENERIC_INSTANCE_MAX) {
|
||||
err(c,loc,"too many generic instances");
|
||||
return -1;
|
||||
}
|
||||
strcpy(c->instances[c->instance_count].key,key);
|
||||
inst=&c->instances[c->instance_count];
|
||||
strcpy(inst->key,key);
|
||||
inst->decl=decl;
|
||||
inst->home=home ? home->name : 0;
|
||||
inst->owner=owner;
|
||||
inst->cname=unit_cname(c,key);
|
||||
/* The bindings in force right now are the ones this instance was built
|
||||
with, and lowering has to see exactly those again. */
|
||||
inst->bind_count=c->types.param_count;
|
||||
for (i=0;i<c->types.param_count && i<FE_TYPE_PARAM_MAX;++i)
|
||||
inst->binds[i]=c->types.params[i];
|
||||
++c->instance_count;
|
||||
return 1;
|
||||
}
|
||||
@@ -2357,7 +2380,7 @@ static FeType *instantiate_struct(FeCheck *c, FeUnit *home, const char *name,
|
||||
return unknown(c);
|
||||
}
|
||||
instance_key(key,home->name,name,args,count);
|
||||
if (instance_record(c,key,loc)<0) return unknown(c);
|
||||
if (instance_record(c,key,loc,decl,home,0)<0) return unknown(c);
|
||||
return build_struct_instance(c,home,decl,key,args,count);
|
||||
}
|
||||
|
||||
@@ -2537,8 +2560,10 @@ static FeType *check_generic_call(FeCheckerState *s, FeNode *n, FeSym *sym,
|
||||
instance_key(key,home->name,decl->text,args,want);
|
||||
push_bindings(c,&save,decl,args,want);
|
||||
result=check_call_args(s,n,sym,home->name,want);
|
||||
fresh=instance_record(c,key,n->loc,decl,home,0);
|
||||
pop_bindings(c,&save);
|
||||
fresh=instance_record(c,key,n->loc);
|
||||
/* The call goes to this instance, not to the declaration it came from. */
|
||||
if (n->a) n->a->cname=(char *)instance_cname(c,key);
|
||||
if (fresh>0) {
|
||||
if (!instance_descend(c,n->loc)) return result;
|
||||
push_bindings(c,&save,decl,args,want);
|
||||
@@ -2572,8 +2597,9 @@ static FeType *check_static_method_call(FeCheckerState *s, FeNode *n,
|
||||
push_instance_bindings(c,&save,owner);
|
||||
bind_self(c,owner);
|
||||
result=check_call_args(s,n,&fake,home->name,0);
|
||||
fresh=instance_record(c,key,n->loc,method,home,owner);
|
||||
pop_bindings(c,&save);
|
||||
fresh=instance_record(c,key,n->loc);
|
||||
if (n->a) n->a->cname=(char *)instance_cname(c,key);
|
||||
if (fresh>0) {
|
||||
if (!instance_descend(c,n->loc)) return result;
|
||||
push_instance_bindings(c,&save,owner);
|
||||
@@ -2587,7 +2613,7 @@ static FeType *check_static_method_call(FeCheckerState *s, FeNode *n,
|
||||
|
||||
/* The body of a method on a generic instance, checked once per instance. */
|
||||
static void check_instance_method(FeCheckerState *s, FeType *owner,
|
||||
FeNode *method, FeLoc site)
|
||||
FeNode *method, FeLoc site, FeNode *call)
|
||||
{
|
||||
FeCheck *c=s->c;
|
||||
FeUnit *home=current_unit(c);
|
||||
@@ -2596,7 +2622,17 @@ static void check_instance_method(FeCheckerState *s, FeType *owner,
|
||||
FeType *self_args[1];
|
||||
self_args[0]=owner;
|
||||
instance_key(key,home->name,method->text,self_args,1);
|
||||
if (instance_record(c,key,site)<=0) return;
|
||||
{
|
||||
FeBindSave probe;
|
||||
int fresh;
|
||||
push_instance_bindings(c,&probe,owner);
|
||||
bind_self(c,owner);
|
||||
fresh=instance_record(c,key,site,method,home,owner);
|
||||
pop_bindings(c,&probe);
|
||||
/* The call names this instance's copy of the method. */
|
||||
if (call && call->a) call->a->cname=(char *)instance_cname(c,key);
|
||||
if (fresh<=0) return;
|
||||
}
|
||||
if (!instance_descend(c,site)) return;
|
||||
push_instance_bindings(c,&save,owner);
|
||||
bind_self(c,owner);
|
||||
|
||||
@@ -14,6 +14,15 @@ typedef struct FeScope FeScope;
|
||||
#define FE_GENERIC_INSTANCE_MAX 512
|
||||
typedef struct FeInstance {
|
||||
char key[FE_GENERIC_KEY_MAX];
|
||||
/* What lowering needs to build this instance's code: the declaration, the
|
||||
arguments bound while it was checked, the unit those names belong to,
|
||||
and the name the linker will see. */
|
||||
FeNode *decl;
|
||||
FeTypeBind binds[FE_TYPE_PARAM_MAX];
|
||||
unsigned bind_count;
|
||||
const char *home;
|
||||
const char *cname;
|
||||
FeType *owner; /* set when the instance is a method */
|
||||
} FeInstance;
|
||||
|
||||
/* The checker spans a whole build, not one file. Names cross unit boundaries,
|
||||
|
||||
+81
-9
@@ -62,6 +62,8 @@ static void lower_stmt(Lower *L, FeNode *n);
|
||||
static void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n,
|
||||
unsigned long size);
|
||||
static void lower_for(Lower *L, FeNode *n);
|
||||
static int fn_is_generic(const FeNode *fn);
|
||||
static void lower_fn_as(Lower *L, FeNode *fn, const char *name);
|
||||
static Slot lower_slice(Lower *L, FeNode *n);
|
||||
static void guard(Lower *L, unsigned ok, FeIrTrap reason, unsigned long line);
|
||||
static void lower_match(Lower *L, FeNode *n);
|
||||
@@ -450,7 +452,7 @@ static Slot lower_call(Lower *L, FeNode *n)
|
||||
{
|
||||
unsigned args[16];
|
||||
unsigned count = 0;
|
||||
FeNode *arg;
|
||||
FeNode *arg = n->children;
|
||||
FeType *ret = n->sem_type;
|
||||
FeIrType rt = ir_type(ret);
|
||||
unsigned result_local = 0;
|
||||
@@ -468,7 +470,28 @@ static Slot lower_call(Lower *L, FeNode *n)
|
||||
ir_align(ret), "result");
|
||||
args[count++] = fe_ir_addr(L->m, L->b, fe_ir_at_local(result_local, 0));
|
||||
}
|
||||
for (arg = n->children; arg; arg = arg->next) {
|
||||
/* A method call passes what it was reached through as its first argument.
|
||||
`self: Self` and `self: &Self` are the same thing here: the address of
|
||||
the receiver, because an aggregate never travels in a register. */
|
||||
if (n->a && n->a->kind == FE_N_MEMBER && n->sem_decl) {
|
||||
FeNode *first = n->sem_decl->a ? n->sem_decl->a->children : 0;
|
||||
if (first && first->text && !strcmp(first->text, "self")) {
|
||||
Slot recv = lower_expr(L, n->a->a);
|
||||
args[count++] = recv.is_place ? as_address(L, recv, n->a->a)
|
||||
: recv.temp;
|
||||
}
|
||||
}
|
||||
/* A generic call passes its type arguments first. They were consumed when
|
||||
the instance was chosen and carry no value, so they are not passed. */
|
||||
{
|
||||
FeNode *p;
|
||||
for (p = n->sem_decl && n->sem_decl->a ? n->sem_decl->a->children : 0;
|
||||
p && arg; p = p->next) {
|
||||
if (!(p->flags & FE_NODE_COMPTIME)) break;
|
||||
arg = arg->next;
|
||||
}
|
||||
}
|
||||
for (; arg; arg = arg->next) {
|
||||
Slot a = lower_expr(L, arg);
|
||||
if (count >= 16) { fail(L, "too many arguments", n); break; }
|
||||
args[count++] = a.type == FE_IR_MEM ? as_address(L, a, arg)
|
||||
@@ -1286,13 +1309,22 @@ static void lower_global(Lower *L, FeNode *n)
|
||||
fe_ir_global(L->m, n->cname, ir_type(t), size, ir_align(t), init);
|
||||
}
|
||||
|
||||
static void lower_fn(Lower *L, FeNode *fn)
|
||||
static int fn_is_generic(const FeNode *fn)
|
||||
{
|
||||
FeNode *p;
|
||||
if (!fn) return 0;
|
||||
for (p = fn->a ? fn->a->children : 0; p; p = p->next)
|
||||
if (p->flags & FE_NODE_COMPTIME) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lower_fn_as(Lower *L, FeNode *fn, const char *name)
|
||||
{
|
||||
FeNode *p;
|
||||
FeType *ret = fn->b ? fe_type_from_ast(&L->c->types, fn->b) : 0;
|
||||
FeIrFunc *f;
|
||||
if (!fn->cname) return;
|
||||
f = fe_ir_func(L->m, fn->cname, ir_type(ret), ir_size(ret));
|
||||
if (!name) return;
|
||||
f = fe_ir_func(L->m, name, ir_type(ret), ir_size(ret));
|
||||
if (!f) return;
|
||||
L->fn = f;
|
||||
L->ret_type = ret;
|
||||
@@ -1302,10 +1334,16 @@ static void lower_fn(Lower *L, FeNode *fn)
|
||||
if (f->returns_by_address)
|
||||
L->ret_local = fe_ir_local(L->m, f, FE_IR_PTR, 4, 4, "result");
|
||||
for (p = fn->a ? fn->a->children : 0; p; p = p->next) {
|
||||
FeType *pt = fe_type_from_ast(&L->c->types, p->a);
|
||||
FeType *pt;
|
||||
int by_address;
|
||||
unsigned local;
|
||||
/* A comptime parameter was consumed at compile time; it has no
|
||||
storage and takes no argument slot. */
|
||||
if (p->flags & FE_NODE_COMPTIME) continue;
|
||||
pt = fe_type_from_ast(&L->c->types, p->a);
|
||||
/* An aggregate parameter arrives as an address. */
|
||||
int by_address = ir_type(pt) == FE_IR_MEM;
|
||||
unsigned local = by_address
|
||||
by_address = ir_type(pt) == FE_IR_MEM;
|
||||
local = by_address
|
||||
? fe_ir_local(L->m, f, FE_IR_PTR, 4, 4, p->text)
|
||||
: fe_ir_local(L->m, f, ir_type(pt), ir_size(pt), ir_align(pt),
|
||||
p->text);
|
||||
@@ -1323,6 +1361,11 @@ static void lower_fn(Lower *L, FeNode *fn)
|
||||
fe_ir_ret(L->b, 0, 0);
|
||||
}
|
||||
|
||||
static void lower_fn(Lower *L, FeNode *fn)
|
||||
{
|
||||
lower_fn_as(L, fn, fn->cname);
|
||||
}
|
||||
|
||||
int fe_lower_program(FeCheck *c, FeIrModule *out)
|
||||
{
|
||||
Lower L;
|
||||
@@ -1353,12 +1396,41 @@ int fe_lower_program(FeCheck *c, FeIrModule *out)
|
||||
f = fe_ir_func(out, n->cname, ir_type(ret), ir_size(ret));
|
||||
if (f) f->is_extern = 1;
|
||||
}
|
||||
else if (n->kind == FE_N_FN && n->c) {
|
||||
else if (n->kind == FE_N_FN && n->c && !fn_is_generic(n)) {
|
||||
lower_fn(&L, n);
|
||||
/* The entry unit is the one the build was rooted at. */
|
||||
if (u == 0 && n->text && !strcmp(n->text, "main"))
|
||||
out->entry_main = n->cname;
|
||||
}
|
||||
}
|
||||
/* Each instance the checker reached is a function of its own: the same
|
||||
body, read with different types bound, under its own link name. This is
|
||||
where monomorphisation actually produces code -- the front end only
|
||||
decided which instances exist. */
|
||||
for (u = 0; u < c->instance_count && !L.failed; ++u) {
|
||||
FeInstance *inst = &c->instances[u];
|
||||
FeUnit *home;
|
||||
FeTypeBind save[FE_TYPE_PARAM_MAX];
|
||||
unsigned save_count;
|
||||
unsigned k;
|
||||
if (!inst->decl || !inst->decl->c || !inst->cname || !inst->home)
|
||||
continue;
|
||||
home = 0;
|
||||
for (k = 0; k < c->build->count; ++k)
|
||||
if (!strcmp(c->build->units[k].name, inst->home))
|
||||
home = &c->build->units[k];
|
||||
if (!home) continue;
|
||||
c->ast = &home->ast;
|
||||
c->unit = home;
|
||||
c->types.unit_name = home->name;
|
||||
save_count = c->types.param_count;
|
||||
for (k = 0; k < FE_TYPE_PARAM_MAX; ++k) save[k] = c->types.params[k];
|
||||
c->types.param_count = inst->bind_count;
|
||||
for (k = 0; k < inst->bind_count && k < FE_TYPE_PARAM_MAX; ++k)
|
||||
c->types.params[k] = inst->binds[k];
|
||||
lower_fn_as(&L, inst->decl, inst->cname);
|
||||
c->types.param_count = save_count;
|
||||
for (k = 0; k < FE_TYPE_PARAM_MAX; ++k) c->types.params[k] = save[k];
|
||||
}
|
||||
return !L.failed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user