diff --git a/fec/src/ast.h b/fec/src/ast.h index 2118872..e623588 100644 --- a/fec/src/ast.h +++ b/fec/src/ast.h @@ -42,6 +42,7 @@ struct FeNode { #define FE_NODE_STATIC 0x2U #define FE_NODE_SHARED 0x4U #define FE_NODE_PUB 0x8U +#define FE_NODE_COMPTIME 0x10U typedef struct FeAst { FeArena arena; diff --git a/fec/src/check.c b/fec/src/check.c index 8d0d3e7..a2d1dbd 100644 --- a/fec/src/check.c +++ b/fec/src/check.c @@ -59,6 +59,12 @@ static void err(FeCheck *c, FeLoc loc, const char *msg) fe_diag_error(c->diags, loc, msg); } +/* Only numbers and characters have an order (SPEC 6.2). */ +static int ordered_type(const FeType *t) +{ + return t && (t->kind==FE_TYPE_INT || t->kind==FE_TYPE_CHAR); +} + static int known(FeType *t) { return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR; @@ -279,6 +285,14 @@ static void enter_unit(FeCheck *c, unsigned index) fe_diags_source(c->diags, u->source, u->size); } +/* The type bindings in force, saved across a nested instantiation. */ +typedef struct FeBindSave { + FeTypeBind params[FE_TYPE_PARAM_MAX]; + unsigned count; +} FeBindSave; + +static FeType *instantiate_type_node(void *owner, const FeNode *node); + void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags, unsigned pointer_bits, int no_checks) { @@ -294,6 +308,12 @@ void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags, c->no_checks = no_checks; fe_types_init(&c->types, &c->arena, pointer_bits); c->types.unit_name = "unit"; + c->types.instantiate = instantiate_type_node; + c->types.instantiate_owner = c; + c->instances = (FeInstance *)fe_arena_alloc(&c->arena, + (unsigned long)FE_GENERIC_INSTANCE_MAX * sizeof(FeInstance)); + c->instance_count = 0; + c->instance_depth = 0; } void fe_check_destroy(FeCheck *c) @@ -376,6 +396,12 @@ static FeNode *find_method(FeCheck *c, FeType *owner, const char *name) FeNode *decl; FeNode *method; if(!owner || !name) return 0; + if(owner->decl_node) { + for(method=owner->decl_node->children; method; method=method->next) + if(method->kind==FE_N_FN && method->text && + strcmp(method->text,name)==0) return method; + return 0; + } for(decl=c->ast->root ? c->ast->root->children : 0; decl; decl=decl->next) if(decl->kind==FE_N_STRUCT && decl->text && strcmp(decl->text,owner->name)==0) @@ -403,9 +429,37 @@ static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read, FeType *base_in); static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read); static FeType *check_call(FeCheckerState *s, FeNode *n); -static FeType *check_call_fn(FeCheckerState *s, FeNode *n, FeSym *sym, - const char *home); +static FeType *check_call_args(FeCheckerState *s, FeNode *n, FeSym *sym, + const char *home, unsigned skip); static int is_error_set_member(FeCheckerState *s, FeNode *n); +static void check_fn(FeCheck *c, FeNode *n, FeScope *globals); +static void check_method(FeCheck *c, FeNode *n, FeScope *globals, FeType *owner); +static char *unit_cname(FeCheck *c, const char *name); +static unsigned unit_index(FeCheck *c, const FeUnit *u); +static FeNode *unit_type_decl(FeCheck *c, FeUnit *u, const char *name); +static FeType *check_generic_call(FeCheckerState *s, FeNode *n, FeSym *sym, + FeUnit *home); +static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok); +static FeUnit *current_unit(FeCheck *c); +static int decl_is_generic(const FeNode *decl); +static void check_generic_params(FeCheck *c, FeNode *decl); +static int comptime_condition(FeCheckerState *s, FeNode *n, int *out); +static FeType *check_static_method_call(FeCheckerState *s, FeNode *n, + FeType *owner, FeNode *method); +static FeNode *type_method(FeType *t, const char *name); +static int method_is_static(const FeNode *method); +static int const_names_type(FeCheckerState *s, FeNode *n); +static void push_instance_bindings(FeCheck *c, FeBindSave *save, FeType *t); +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_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); typedef struct FeFlowSlot { FeSym *sym; @@ -972,6 +1026,10 @@ static FeType *check_identifier(FeCheckerState *s, FeNode *n) if (!sym) { FeType *named=fe_type_intern(&s->c->types,n->text ? n->text : ""); if(named->kind==FE_TYPE_STRUCT || named->kind==FE_TYPE_ENUM) { n->sem_type=named; return named; } + if(named->kind!=FE_TYPE_UNKNOWN) { + err(s->c, n->loc, "a type is not a value here"); + return unknown(s->c); + } err(s->c, n->loc, "unknown name"); return unknown(s->c); } @@ -1074,6 +1132,10 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n) if (known(a) && known(b) && !fe_type_equal(a, b) && !compatible(a, b, n->b) && !compatible(b, a, n->a)) err(c, n->loc, "comparison operands have different types"); + else if (strcmp(op,"==")!=0 && strcmp(op,"!=")!=0 && + ((known(a) && !ordered_type(a)) || + (known(b) && !ordered_type(b)))) + err(c, n->loc, "ordering requires integer or char operands"); a = fe_type_intern(&c->types, "bool"); } else { if ((known(a) && !fe_type_is_integer(a)) || @@ -1187,17 +1249,43 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n) for (x=n->children;x;x=x->next) check_expr(s,x); return unknown(c); } - return check_call_fn(s,n,fsym,home->name); + if (decl_is_generic(fsym->fn)) + return check_generic_call(s,n,fsym,home); + return check_call_args(s,n,fsym,home->name,0); + } + { + int names_type=0; + FeType *owner_type=type_from_expr(s,n->a->a,&names_type); + if (names_type && owner_type && + owner_type->kind==FE_TYPE_STRUCT) { + FeNode *m=type_method(owner_type, + n->a->b ? n->a->b->text : ""); + if (!m) { err(c,n->a->loc,"unknown method"); return unknown(c); } + if (!method_is_static(m)) { + err(c,n->loc,"method requires a receiver"); + return unknown(c); + } + return check_static_method_call(s,n,owner_type,m); + } } et=check_expr(s,n->a->a); method=et && et->kind==FE_TYPE_STRUCT ? find_method(c,et,n->a->b ? n->a->b->text : "") : 0; if(method) { + FeBindSave msave; + int bound=0; self_param=method->a ? method->a->children : 0; if(!self_param) { err(c,n->loc,"method requires self parameter"); return unknown(c); } + /* A method of a generic instance reads its signature with that + instance's arguments bound. */ + if (et->bind_count) { + push_instance_bindings(c,&msave,et); + bind_self(c,et); + bound=1; + } a=method_type(c,self_param->a,et); if(a->kind==FE_TYPE_REF && a->ref_mut && !lvalue_writable(s,n->a->a)) @@ -1218,6 +1306,10 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n) n->sem_decl=method; n->sem_type=method->b ? method_type(c,method->b,et) : fe_type_intern(&c->types,"void"); + if (bound) { + pop_bindings(c,&msave); + check_instance_method(s,et,method,n->loc); + } return n->sem_type; } if (et && (et->kind==FE_TYPE_SLICE || et->kind==FE_TYPE_STR) && @@ -1243,7 +1335,9 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n) err(c, n->loc, "unknown function"); return unknown(c); } - return check_call_fn(s, n, sym, 0); + if (decl_is_generic(sym->fn)) + return check_generic_call(s,n,sym,current_unit(c)); + return check_call_args(s, n, sym, 0, 0); } for (x = n->children; x; x = x->next) check_expr(s, x); return unknown(c); @@ -1997,6 +2091,537 @@ static int m7_place_is_projection(FeNode *n) return n && (n->kind==FE_N_MEMBER || n->kind==FE_N_INDEX); } +/* ------------------------------------------------------------------------- * + * Generics (SPEC 9) + * + * A generic declaration is checked once per distinct list of type arguments. + * Those arguments are bound as types for the length of that check, so a name + * that is a type parameter simply is its argument -- in the body, in field + * types and in the signature alike. An instance is identified by its declaring + * unit, its declaration and the spelling of its arguments, so asking twice + * asks for the same instance, and a chain of new ones is bounded. + * ------------------------------------------------------------------------- */ + +#define FE_GENERIC_DEPTH_MAX 32 + +static unsigned decl_type_param_count(const FeNode *decl) +{ + FeNode *p; + unsigned n=0; + if (!decl) return 0; + if (decl->kind==FE_N_FN) { + for (p=decl->a?decl->a->children:0;p;p=p->next) + if (p->flags & FE_NODE_COMPTIME) ++n; + return n; + } + if (decl->kind==FE_N_STRUCT || decl->kind==FE_N_ENUM) + for (p=decl->a?decl->a->children:0;p;p=p->next) ++n; + return n; +} + +static FeNode *decl_type_param(const FeNode *decl, unsigned i) +{ + FeNode *p; + unsigned n=0; + if (!decl) return 0; + if (decl->kind==FE_N_FN) { + for (p=decl->a?decl->a->children:0;p;p=p->next) + if (p->flags & FE_NODE_COMPTIME) { if (n==i) return p; ++n; } + return 0; + } + for (p=decl->a?decl->a->children:0;p;p=p->next) { if (n==i) return p; ++n; } + return 0; +} + +static int decl_is_generic(const FeNode *decl) +{ + return decl_type_param_count(decl)!=0; +} + +/* SPEC 9: v0.1 has comptime type parameters and no other kind. */ +static void check_generic_params(FeCheck *c, FeNode *decl) +{ + FeNode *p; + if (!decl || decl->kind!=FE_N_FN) return; + for (p=decl->a?decl->a->children:0;p;p=p->next) { + if (!(p->flags & FE_NODE_COMPTIME)) continue; + if (!p->a || p->a->kind!=FE_N_TYPE || !p->a->text || + strcmp(p->a->text,"type")!=0) + err(c,p->loc,"a comptime parameter must be a type parameter"); + } +} + +static void push_bindings(FeCheck *c, FeBindSave *save, FeNode *decl, + FeType **args, unsigned count) +{ + unsigned i; + save->count=c->types.param_count; + for (i=0;iparams[i]=c->types.params[i]; + c->types.param_count=0; + for (i=0;itypes.params[i].name=p && p->text ? p->text : "?"; + c->types.params[i].type=args[i]; + ++c->types.param_count; + } +} + +/* Restore the bindings recorded on an instance, so a method sees exactly the + environment its type was built with. */ +static void push_instance_bindings(FeCheck *c, FeBindSave *save, FeType *t) +{ + unsigned i; + save->count=c->types.param_count; + for (i=0;iparams[i]=c->types.params[i]; + c->types.param_count=0; + for (i=0;ibind_count && itypes.params[c->types.param_count++]=t->binds[i]; +} + +static void bind_self(FeCheck *c, FeType *owner) +{ + if (c->types.param_count>=FE_TYPE_PARAM_MAX) return; + c->types.params[c->types.param_count].name="Self"; + c->types.params[c->types.param_count].type=owner; + ++c->types.param_count; +} + +static void pop_bindings(FeCheck *c, const FeBindSave *save) +{ + unsigned i; + for (i=0;itypes.params[i]=save->params[i]; + c->types.param_count=save->count; +} + +/* `unit.Name(arg,arg)` -- the canonical identity of one instance. + Nesting makes the readable spelling grow without bound, and a spelling that + got cut off would make two different instances look like the same one, so + past a length the arguments are written as serial numbers instead. Those are + unique, so identity stays exact even where the spelling stops being + readable. */ +#define FE_GENERIC_NAME_READABLE 200 + +static void instance_key(char *out, const char *unit, const char *name, + FeType **args, unsigned count) +{ + unsigned i; + unsigned long n=0; + unsigned long cap=(unsigned long)FE_GENERIC_NAME_READABLE; + const char *p; + char number[24]; + int readable=1; + for (p=unit?unit:"";*p;++p) { if (nname[0] ? args[i]->name : "?";*p;++p) { + if (nserial : 0U); + for (p=number;*p && ninstance_count;++i) + if (strcmp(c->instances[i].key,key)==0) return 1; + return 0; +} + +static int instance_record(FeCheck *c, const char *key, FeLoc loc) +{ + 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); + ++c->instance_count; + return 1; +} + +/* One step further down a chain of instantiations. Chains that keep producing + new instances are the ones that never end, so the limit counts nesting. */ +static int instance_descend(FeCheck *c, FeLoc loc) +{ + if (c->instance_depth>=FE_GENERIC_DEPTH_MAX) { + err(c,loc,"generic instantiation depth exceeded"); + return 0; + } + ++c->instance_depth; + return 1; +} + +static FeUnit *current_unit(FeCheck *c) +{ + unsigned u; + for (u=0;ubuild->count;++u) + if (strcmp(c->build->units[u].name,c->types.unit_name)==0) + return &c->build->units[u]; + return c->unit; +} + +/* Build `Box(i32)`: the declaration's fields with the parameters bound, under + a name that records which arguments made it. */ +static FeType *build_struct_instance(FeCheck *c, FeUnit *home, FeNode *decl, + const char *key, FeType **args, + unsigned count) +{ + FeBindSave save; + FeType *t; + FeNode *f; + unsigned fields=0; + unsigned i=0; + t=fe_type_intern_unit(&c->types,home->name,key); + if (!t || t->kind!=FE_TYPE_UNKNOWN) return t; + t->kind=FE_TYPE_STRUCT; + t->packed=(decl->flags & FE_NODE_PACKED)!=0; + t->decl_node=decl; + t->bind_count=0; + for (i=0;ibinds[t->bind_count].name=p && p->text ? p->text : "?"; + t->binds[t->bind_count].type=args[i]; + ++t->bind_count; + } + t->cname=unit_cname(c,key); + for (f=decl->children;f;f=f->next) + if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0) + t->has_drop=1; + for (f=decl->children;f;f=f->next) if (f->kind==FE_N_FIELD) ++fields; + t->field_count=fields; + if (fields) { + t->fields=(FeFieldType *)fe_arena_alloc(&c->arena, + fields*sizeof(FeFieldType)); + if (!t->fields) { t->field_count=0; return t; } + push_instance_bindings(c,&save,t); + bind_self(c,t); + i=0; + for (f=decl->children;f;f=f->next) if (f->kind==FE_N_FIELD) { + t->fields[i].name=f->text; + t->fields[i].type=node_type(c,f->a); + t->fields[i].offset=0; + t->fields[i].ast_node=f; + ++i; + } + pop_bindings(c,&save); + } + fe_type_layout_all(&c->types); + return t; +} + +static FeType *instantiate_struct(FeCheck *c, FeUnit *home, const char *name, + FeType **args, unsigned count, FeLoc loc) +{ + FeNode *decl=unit_type_decl(c,home,name); + char key[FE_GENERIC_KEY_MAX]; + if (!decl || !decl_is_generic(decl)) { + err(c,loc,"type does not take generic arguments"); + return unknown(c); + } + if (decl->kind!=FE_N_STRUCT) { + err(c,loc,"only a generic struct can be instantiated"); + return unknown(c); + } + if (count!=decl_type_param_count(decl)) { + err(c,loc,"wrong number of generic arguments"); + return unknown(c); + } + instance_key(key,home->name,name,args,count); + if (instance_record(c,key,loc)<0) return unknown(c); + return build_struct_instance(c,home,decl,key,args,count); +} + +/* `Name(args...)` written in type position. */ +static FeType *instantiate_type_node(void *owner, const FeNode *node) +{ + FeCheck *c=(FeCheck *)owner; + FeUnit *home=current_unit(c); + FeNode *arg; + FeType *args[FE_TYPE_PARAM_MAX]; + unsigned count=0; + FeType *result; + if (!node->children) { + /* A generic declaration is not a type until it has its arguments. */ + FeNode *decl=unit_type_decl(c,home,node->text ? node->text : ""); + if (decl && decl_is_generic(decl)) { + err(c,node->loc,"generic type requires type arguments"); + return unknown(c); + } + return fe_type_intern(&c->types,node->text); + } + if (!instance_descend(c,node->loc)) return unknown(c); + for (arg=node->children;arg;arg=arg->next) { + if (counttypes,arg); + ++count; + } + if (count>FE_TYPE_PARAM_MAX) { + err(c,node->loc,"wrong number of generic arguments"); + --c->instance_depth; + return unknown(c); + } + result=instantiate_struct(c,home,node->text ? node->text : "",args,count, + node->loc); + --c->instance_depth; + return result; +} + +/* A type written where an expression is: `i32`, `Box(i32)`. Only a comptime + argument position accepts one. */ +static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok) +{ + FeCheck *c=s->c; + FeType *t; + unsigned i; + *ok=0; + if (!n) return unknown(c); + if (n->kind==FE_N_IDENT && n->text) { + for (i=0;itypes.param_count;++i) + if (strcmp(c->types.params[i].name,n->text)==0) { + *ok=1; + return c->types.params[i].type; + } + if (find_symbol(s->scope,n->text)) { + /* A const alias of a type is that type (SPEC 4.7). */ + FeSym *sym=find_symbol(s->scope,n->text); + if (sym && sym->decl && sym->decl->kind==FE_N_CONST && + sym->decl->b && sym->decl->b->kind==FE_N_IDENT) + return type_from_expr(s,sym->decl->b,ok); + return unknown(c); + } + t=fe_type_intern(&c->types,n->text); + if (t && t->kind!=FE_TYPE_UNKNOWN) { *ok=1; return t; } + return unknown(c); + } + if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_IDENT && n->a->text) { + FeType *args[FE_TYPE_PARAM_MAX]; + unsigned count=0; + FeNode *arg; + FeType *result; + FeUnit *home=current_unit(c); + if (!unit_type_decl(c,home,n->a->text)) return unknown(c); + if (!instance_descend(c,n->loc)) { *ok=1; return unknown(c); } + for (arg=n->children;arg;arg=arg->next) { + int inner=0; + if (countinstance_depth; return unknown(c); } + ++count; + } + if (count>FE_TYPE_PARAM_MAX) { --c->instance_depth; return unknown(c); } + result=instantiate_struct(c,home,n->a->text,args,count,n->loc); + --c->instance_depth; + *ok=1; + return result; + } + return unknown(c); +} + +/* A `comptime if` condition. Only the forms SPEC 9 allows: type equality and + the type predicates. Anything else is not decidable here. */ +static int comptime_condition(FeCheckerState *s, FeNode *n, int *out) +{ + FeType *a; + FeType *b; + int ok=0; + int eq; + if (!n) return 0; + if (n->kind==FE_N_BINARY && n->text && + (strcmp(n->text,"==")==0 || strcmp(n->text,"!=")==0)) { + a=type_from_expr(s,n->a,&ok); + if (!ok) return 0; + b=type_from_expr(s,n->b,&ok); + if (!ok) return 0; + eq=fe_type_equal(a,b); + *out=strcmp(n->text,"==")==0 ? eq : !eq; + return 1; + } + if (n->kind==FE_N_CALL && n->text && + (strcmp(n->text,"@is_int")==0 || strcmp(n->text,"@is_ptr")==0)) { + a=type_from_expr(s,n->children,&ok); + if (!ok) return 0; + *out=strcmp(n->text,"@is_int")==0 ? fe_type_is_integer(a) : + (a && (a->kind==FE_TYPE_OWNED || a->kind==FE_TYPE_REF)); + return 1; + } + return 0; +} + +/* Check a generic body once, in the unit that declared it and with the + instance's arguments bound. Errors land on the operation that is wrong; the + call site gets a note, because the call is context and not the defect. */ +static void instantiate_body(FeCheck *c, FeUnit *home, FeNode *decl, + FeType *owner, FeBindSave *bindings, FeLoc site) +{ + FeAst *save_ast=c->ast; + FeUnit *save_unit=c->unit; + const char *save_name=c->types.unit_name; + unsigned before=c->diags->errors; + (void)bindings; + c->ast=&home->ast; + c->unit=home; + c->types.unit_name=home->name; + fe_diags_source(c->diags,home->source,home->size); + if (owner) check_method(c,decl,c->unit_scope[unit_index(c,home)],owner); + else check_fn(c,decl,c->unit_scope[unit_index(c,home)]); + c->ast=save_ast; + c->unit=save_unit; + c->types.unit_name=save_name; + if (save_unit) fe_diags_source(c->diags,save_unit->source,save_unit->size); + if (c->diags->errors>before) + fe_diag_note_src(c->diags,site,"instantiated here"); +} + +/* A call to a generic function: read the type arguments, check the value + arguments against the bound signature, then check the body once. */ +static FeType *check_generic_call(FeCheckerState *s, FeNode *n, FeSym *sym, + FeUnit *home) +{ + FeCheck *c=s->c; + FeNode *decl=sym->fn; + unsigned want=decl_type_param_count(decl); + FeType *args[FE_TYPE_PARAM_MAX]; + FeNode *arg=n->children; + unsigned i; + char key[FE_GENERIC_KEY_MAX]; + FeBindSave save; + FeType *result; + int fresh; + if (want>FE_TYPE_PARAM_MAX) { + err(c,n->loc,"too many generic parameters"); + return unknown(c); + } + for (i=0;iloc,"generic call requires explicit type arguments"); + return unknown(c); + } + args[i]=type_from_expr(s,arg,&ok); + if (!ok) { + err(c,arg->loc,"a comptime type argument must name a type"); + return unknown(c); + } + arg=arg->next; + } + 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); + pop_bindings(c,&save); + fresh=instance_record(c,key,n->loc); + if (fresh>0) { + if (!instance_descend(c,n->loc)) return result; + push_bindings(c,&save,decl,args,want); + instantiate_body(c,home,decl,0,&save,n->loc); + pop_bindings(c,&save); + --c->instance_depth; + } + return result; +} + +/* `Type.method(...)` where Type is a generic instance and the method takes no + self parameter. */ +static FeType *check_static_method_call(FeCheckerState *s, FeNode *n, + FeType *owner, FeNode *method) +{ + FeCheck *c=s->c; + FeUnit *home=current_unit(c); + FeBindSave save; + FeType *result; + char key[FE_GENERIC_KEY_MAX]; + FeType *self_args[1]; + int fresh; + FeSym fake; + self_args[0]=owner; + instance_key(key,home->name,method->text,self_args,1); + memset(&fake,0,sizeof fake); + fake.name=method->text; + fake.cname=method->cname; + fake.fn=method; + fake.decl=method; + push_instance_bindings(c,&save,owner); + bind_self(c,owner); + result=check_call_args(s,n,&fake,home->name,0); + pop_bindings(c,&save); + fresh=instance_record(c,key,n->loc); + if (fresh>0) { + if (!instance_descend(c,n->loc)) return result; + push_instance_bindings(c,&save,owner); + bind_self(c,owner); + instantiate_body(c,home,method,owner,&save,n->loc); + pop_bindings(c,&save); + --c->instance_depth; + } + return result; +} + +/* 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) +{ + FeCheck *c=s->c; + FeUnit *home=current_unit(c); + FeBindSave save; + char key[FE_GENERIC_KEY_MAX]; + 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; + if (!instance_descend(c,site)) return; + push_instance_bindings(c,&save,owner); + bind_self(c,owner); + instantiate_body(c,home,method,owner,&save,site); + pop_bindings(c,&save); + --c->instance_depth; +} + +/* SPEC 4.7: `const Word = i32;` is another spelling of a type, not a value. + It has no initializer to check and no storage. */ +static int const_names_type(FeCheckerState *s, FeNode *n) +{ + FeType *t; + if (!n->b || n->b->kind!=FE_N_IDENT || !n->b->text) return 0; + if (n->a) return 0; + if (find_symbol(s->globals,n->b->text)) return 0; + t=fe_type_intern(&s->c->types,n->b->text); + return t && t->kind!=FE_TYPE_UNKNOWN; +} + +static FeNode *type_method(FeType *t, const char *name) +{ + FeNode *m; + if (!t || !t->decl_node || !name) return 0; + for (m=t->decl_node->children;m;m=m->next) + if (m->kind==FE_N_FN && m->text && strcmp(m->text,name)==0) return m; + return 0; +} + +static int method_is_static(const FeNode *method) +{ + FeNode *first=method && method->a ? method->a->children : 0; + return !first || !first->text || strcmp(first->text,"self")!=0; +} + /* A call to a named function. `home` is the unit the signature was written in, null when that is the unit being checked: parameter and return types have to be read where they were written or a name would mean the caller's type. */ @@ -2030,14 +2655,17 @@ static FeType *cross_unit_value(FeCheckerState *s, FeNode *n, int *handled) return sym->type; } -static FeType *check_call_fn(FeCheckerState *s, FeNode *n, FeSym *sym, - const char *home) +/* `skip` leading parameters and arguments have already been consumed as + comptime type arguments. */ +static FeType *check_call_args(FeCheckerState *s, FeNode *n, FeSym *sym, + const char *home, unsigned skip) { FeCheck *c=s->c; FeNode *param; FeNode *arg; FeType *a; FeType *b; + unsigned k; if (n->a) n->a->cname = sym->cname; n->sem_decl = sym->fn; if (!sym->fn) { @@ -2046,6 +2674,10 @@ static FeType *check_call_fn(FeCheckerState *s, FeNode *n, FeSym *sym, } param = sym->fn->a ? sym->fn->a->children : 0; arg = n->children; + for (k=0;knext; + if (arg) arg=arg->next; + } while (param && arg) { a = check_expr(s, arg); b = node_type_in(c, home, param->a); @@ -2137,6 +2769,8 @@ static FeType *check_call(FeCheckerState *s, FeNode *n) n->sem_type=unknown(c); return n->sem_type; } + if (decl_is_generic(sym->fn)) + return check_generic_call(s,n,sym,current_unit(c)); n->a->cname=sym->cname; n->sem_decl=sym->fn; param=sym->fn->a ? sym->fn->a->children : 0; @@ -2424,6 +3058,11 @@ static FeType *check_expr(FeCheckerState *s, FeNode *n) !m7_actual_compatible(a,b,n->b) && !m7_actual_compatible(b,a,n->a)) err(s->c,n->loc,"comparison operands have different types"); + /* Only numbers and characters have an order. */ + else if (strcmp(op,"==")!=0 && strcmp(op,"!=")!=0 && + ((known(a) && !ordered_type(a)) || + (known(b) && !ordered_type(b)))) + err(s->c,n->loc,"ordering requires integer or char operands"); n->sem_type=fe_type_intern(&s->c->types,"bool"); return n->sem_type; } @@ -2825,6 +3464,18 @@ static void check_stmt(FeCheckerState *s, FeNode *n) --s->defer_depth; break; case FE_N_IF: + if (n->text && strcmp(n->text,"comptime if")==0) { + int taken=0; + if (!comptime_condition(s,n->a,&taken)) { + err(s->c,n->a?n->a->loc:n->loc, + "comptime condition must be decidable at compile time"); + break; + } + /* SPEC 9: the branch that is not taken is parsed and nothing more. */ + if (taken) check_stmt(s,n->b); + else if (n->c) check_stmt(s,n->c); + break; + } if (n->text && strcmp(n->text,"if let")==0) m7_check_if_let(s,n); else { @@ -2988,15 +3639,21 @@ static void m7_validate_error_decl(FeCheck *c, FeNode *decl) static 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) + 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) fe_type_declare_enum(&c->types,n); + 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); check_type_cycles(c); @@ -3047,6 +3704,7 @@ static void check_unit_bodies(FeCheck *c, FeCheckerState *s) 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) { @@ -3057,10 +3715,12 @@ static void check_unit_bodies(FeCheck *c, FeCheckerState *s) 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) check_fn(c,n,s->globals); + 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) { + 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); diff --git a/fec/src/check.h b/fec/src/check.h index dcb4da9..2dda600 100644 --- a/fec/src/check.h +++ b/fec/src/check.h @@ -7,6 +7,15 @@ typedef struct FeScope FeScope; +/* One generic instance, identified by declaring unit, declaration and the + spelling of its type arguments (SPEC 9). The table both deduplicates + requests and bounds how long a chain of new ones can get. */ +#define FE_GENERIC_KEY_MAX 320 +#define FE_GENERIC_INSTANCE_MAX 512 +typedef struct FeInstance { + char key[FE_GENERIC_KEY_MAX]; +} FeInstance; + /* 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 @@ -24,6 +33,9 @@ typedef struct FeCheck { unsigned pointer_bits; unsigned local_serial; int no_checks; + FeInstance *instances; + unsigned instance_count; + unsigned instance_depth; } FeCheck; void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags, diff --git a/fec/src/parser.c b/fec/src/parser.c index 4fcc251..1fa3537 100644 --- a/fec/src/parser.c +++ b/fec/src/parser.c @@ -185,9 +185,9 @@ static FeNode *params(FeParser *p) { FeNode *list=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"params",6); want(p,FE_TOK_LPAREN,"expected '(' after function name"); - while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)) { FeToken t=p->current; FeNode *q; - if(eat(p,FE_TOK_COMPTIME)) t=p->previous; - if(!is_name(p)){error(p,"expected parameter name");recover(p);break;} q=toknode(p,FE_N_PARAM,t);next(p);want(p,FE_TOK_COLON,"expected ':' in parameter");q->a=type(p);fe_node_add(list,q);if(!eat(p,FE_TOK_COMMA))break; + while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)) { FeToken t=p->current; FeNode *q; int ct=0; + if(eat(p,FE_TOK_COMPTIME)) { t=p->previous; ct=1; } + if(!is_name(p)){error(p,"expected parameter name");recover(p);break;} q=toknode(p,FE_N_PARAM,p->current);if(ct){q->flags|=FE_NODE_COMPTIME;q->loc=t.loc;}next(p);want(p,FE_TOK_COLON,"expected ':' in parameter");q->a=type(p);fe_node_add(list,q);if(!eat(p,FE_TOK_COMMA))break; } want(p,FE_TOK_RPAREN,"expected ')' after parameters"); return list; } @@ -214,8 +214,8 @@ static FeNode *decl(FeParser *p) 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(eat(p,FE_TOK_PACKED)) t=p->previous; - 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(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_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)){n->a=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"generics",8);while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n->a,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(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");if(eat(p,FE_TOK_LPAREN)){n->a=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"generics",8);while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(n->a,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 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(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(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; } diff --git a/fec/src/types.c b/fec/src/types.c index 290f756..2ec7b77 100644 --- a/fec/src/types.c +++ b/fec/src/types.c @@ -41,6 +41,9 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind) t->field_count = 0; t->variants = 0; t->variant_count = 0; + t->serial = ctx->generated_serial++; + t->decl_node = 0; + t->bind_count = 0; t->next = ctx->types; t->emit_state = 0; t->cycle_state = 0; @@ -55,6 +58,9 @@ void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits) ctx->pointer_bits = pointer_bits; ctx->unit_name = "unit"; ctx->generated_serial = 0; + ctx->param_count = 0; + ctx->instantiate = 0; + ctx->instantiate_owner = 0; } /* Does this type answer to `name` for someone checking `unit`? A type with no @@ -85,7 +91,11 @@ FeType *fe_type_intern(FeTypeCtx *ctx, const char *name) unsigned bits = 0; int uns = 0; FeTypeKind kind = FE_TYPE_UNKNOWN; + unsigned i; if (!name) name = ""; + /* A bound type parameter is its argument, and shadows everything. */ + for (i = 0; i < ctx->param_count; ++i) + if (strcmp(ctx->params[i].name, name) == 0) return ctx->params[i].type; for (t = ctx->types; t; t = t->next) if (type_visible_as(t, ctx->unit_name, name)) return t; if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID; @@ -152,7 +162,7 @@ static char *generated_name(FeTypeCtx *ctx, const char *prefix, FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem) { - char key[96]; + char key[320]; FeType *t; sprintf(key, "[%lu]%s", length, elem ? elem->name : "?"); t = fe_type_intern(ctx, key); @@ -173,7 +183,7 @@ FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem) FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem) { - char key[96]; + char key[320]; FeType *t; sprintf(key, "[]%s", elem ? elem->name : "?"); t = fe_type_intern(ctx, key); @@ -192,7 +202,7 @@ FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem) FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem) { - char key[96]; + char key[320]; FeType *t; sprintf(key, "[]mut %s", elem ? elem->name : "?"); t = fe_type_intern(ctx, key); @@ -212,7 +222,7 @@ FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem) FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable) { - char key[128]; + char key[320]; FeType *t; sprintf(key,"%s%s",mutable ? "&mut " : "&",elem ? elem->name : "?"); t=fe_type_intern(ctx,key); @@ -226,7 +236,7 @@ FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable) FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem) { - char key[128]; + char key[320]; FeType *t; sprintf(key,"^%s",elem ? elem->name : "?"); t=fe_type_intern(ctx,key); @@ -243,7 +253,7 @@ FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem) FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value) { - char key[128]; + char key[320]; FeType *t; sprintf(key,"!%s",value ? value->name : "?"); t=fe_type_intern(ctx,key); @@ -280,6 +290,7 @@ FeType *fe_type_declare_struct(FeTypeCtx *ctx, const FeNode *node, int packed) if (t->kind == FE_TYPE_STRUCT) return t; t->kind = FE_TYPE_STRUCT; t->packed = packed; + t->decl_node = node; for (f = node->children; f; f = f->next) if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0) t->has_drop=1; @@ -324,6 +335,7 @@ FeType *fe_type_declare_enum(FeTypeCtx *ctx, const FeNode *node) if (t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ENUM) return t; if (t->kind == FE_TYPE_ENUM) return t; t->kind = FE_TYPE_ENUM; + t->decl_node = node; cname = (char *)fe_arena_alloc(ctx->arena, (unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) + strlen(node->text) + 2UL); @@ -592,6 +604,11 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node) return fe_type_intern(ctx, ""); if (node->text && strcmp(node->text, "fn") == 0) return fe_type_intern(ctx, ""); + /* A plain named type may be a generic declaration -- with arguments it is + an instance, without them it is a mistake -- and only the checker knows + the declarations, so it decides. */ + if (ctx->instantiate) + return ctx->instantiate(ctx->instantiate_owner, node); return fe_type_intern(ctx, node->text); } diff --git a/fec/src/types.h b/fec/src/types.h index c49d706..4afabe4 100644 --- a/fec/src/types.h +++ b/fec/src/types.h @@ -11,6 +11,14 @@ typedef enum FeTypeKind { } FeTypeKind; typedef struct FeFieldType FeFieldType; + +/* A type parameter bound to an argument while an instance is checked. */ +#define FE_TYPE_PARAM_MAX 8 +typedef struct FeTypeBind { + const char *name; + FeType *type; +} FeTypeBind; + typedef struct FeVariantType FeVariantType; struct FeFieldType { @@ -31,7 +39,9 @@ struct FeVariantType { struct FeType { FeTypeKind kind; - char name[64]; + /* Long enough for a nested instance spelling such as + `Box(Box(Box(i32)))` at the depth limit. */ + char name[256]; /* 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. */ @@ -65,6 +75,15 @@ struct FeType { unsigned field_count; FeVariantType *variants; unsigned variant_count; + /* The declaration this type came from, and the bindings that made it if + it is a generic instance. A method has to be checked with the same + bindings the instance was built with. */ + /* A small unique number, used to name an instance whose readable + spelling would be too long to keep distinct. */ + unsigned serial; + const FeNode *decl_node; + FeTypeBind binds[FE_TYPE_PARAM_MAX]; + unsigned bind_count; FeType *next; int emit_state; int cycle_state; @@ -76,6 +95,14 @@ typedef struct FeTypeCtx { unsigned pointer_bits; const char *unit_name; unsigned generated_serial; + /* Bindings in force right now. A name that is a bound parameter is + that argument's type and nothing else. */ + FeTypeBind params[FE_TYPE_PARAM_MAX]; + unsigned param_count; + /* Instantiate `Name(args...)`. Only the checker knows the declarations, + so it installs this and the type layer calls back into it. */ + FeType *(*instantiate)(void *owner, const FeNode *node); + void *instantiate_owner; } FeTypeCtx; void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits); diff --git a/fec/tests/generic/badbody.fe b/fec/tests/generic/badbody.fe index c909bd0..51c613b 100644 --- a/fec/tests/generic/badbody.fe +++ b/fec/tests/generic/badbody.fe @@ -1,4 +1,6 @@ -// ERROR:9:instantiation +// ERROR:7:arithmetic operands +// SPEC 9: the defect is the operation in the body, so that is the primary +// error; the call gets an `instantiated here` note. unit badbody; fn add(comptime T: type, a: T, b: T) -> T { diff --git a/fec/tests/generic/badop.fe b/fec/tests/generic/badop.fe index 872989d..1c1b473 100644 --- a/fec/tests/generic/badop.fe +++ b/fec/tests/generic/badop.fe @@ -1,4 +1,6 @@ -// ERROR:16:instantiation +// ERROR:11:ordering requires +// SPEC 9: the primary error is the comparison in the body. The call at the +// bottom gets an `instantiated here` note. unit badop; struct Token { diff --git a/fec/tests/generic/okscope/lib.fe b/fec/tests/generic/badscope/lib.fe similarity index 100% rename from fec/tests/generic/okscope/lib.fe rename to fec/tests/generic/badscope/lib.fe diff --git a/fec/tests/generic/badscope/main.fe b/fec/tests/generic/badscope/main.fe new file mode 100644 index 0000000..d840235 --- /dev/null +++ b/fec/tests/generic/badscope/main.fe @@ -0,0 +1,12 @@ +// ERROR:unknown function +// SPEC 9: a generic body resolves its names in the unit that declared it, +// so lib.call cannot see this unit's `helper`. The mirror of defscope/, +// where the definition unit's own private helper is reachable. +unit main; +import lib; + +fn helper(v: i32) -> i32 { return v + 10; } + +fn main() -> i32 { + return lib.call(i32, 1); +} diff --git a/fec/tests/generic/okscope/main.fe b/fec/tests/generic/okscope/main.fe deleted file mode 100644 index e7dd676..0000000 --- a/fec/tests/generic/okscope/main.fe +++ /dev/null @@ -1,8 +0,0 @@ -unit main; -import lib; - -fn helper(v: i32) -> i32 { return v + 10; } - -fn main() -> i32 { - return lib.call(i32, 1); -}