diff --git a/fec/src/check.c b/fec/src/check.c index f2e291d..3e014db 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -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;iinstance_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;itypes.param_count && ibinds[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); diff --git a/fec/src/check.h b/fec/src/check.h index 2dda600..59bde60 100644 --- a/fec/src/check.h +++ b/fec/src/check.h @@ -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, diff --git a/fec/src/lower.c b/fec/src/lower.c index cf9ff73..e344e82 100644 --- a/fec/src/lower.c +++ b/fec/src/lower.c @@ -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; } diff --git a/fec/tests/exec/generic.fe b/fec/tests/exec/generic.fe new file mode 100644 index 0000000..a817005 --- /dev/null +++ b/fec/tests/exec/generic.fe @@ -0,0 +1,21 @@ +// EXIT:39 +unit generic; + +struct Box(T) { + value: T, + + pub fn new(v: T) -> Self { return Self{ value: v }; } + pub fn get(self: &Self) -> T { return self.value; } +} + +fn id(comptime T: type, v: T) -> T { return v; } + +fn twice(comptime T: type, v: T) -> T { return v + v; } + +fn main() -> i32 { + let a: i32 = id(i32, 7); + let b: u8 = id(u8, 9 as u8); + let c: i32 = twice(i32, 10); + let box: Box(i32) = Box(i32).new(3); + return a + (b as i32) + c + box.get(); +}