implement: 제네릭 모노모피제이션 (SPEC 9)

타입 인자를 바인딩한 상태로 선언을 인스턴스마다 한 번씩 검사한다. 바인딩된
이름은 그냥 그 인자 타입이므로 본문, 필드 타입, 시그니처가 모두 같은 규칙으로
풀린다. 인스턴스 정체성은 선언 유닛 + 선언 + 인자 철자다.

찾은 버그 셋:

- 파서가 comptime 파라미터의 이름을 'comptime' 이라는 키워드에서 가져갔다.
  타입 파라미터 이름이 전부 comptime 이 되어 아무것도 바인딩되지 않았다.
- 인스턴스 이름이 중첩마다 길어져서, 깊은 사슬에서 잘린 이름끼리 충돌해
  재귀가 깊이 제한에 닿기 전에 조용히 멈췄다. 길어지면 인자를 일련번호로
  적어 정체성을 유지한다.
- 순서 비교 연산자가 피연산자 타입을 보지 않아 구조체끼리 비교해도 통과했다.
  제네릭과 무관한 기존 구멍이다.

fixture 셋이 명세와 어긋나 있어 명세를 따랐다. badbody 와 badop 은 호출 지점을
primary error 로 기대했지만 SPEC 9 는 본문의 연산이 primary 이고 호출에는
'instantiated here' note 를 붙이라고 한다. okscope 는 제네릭 본문이 호출자의
이름을 본다고 기대했지만 SPEC 9 는 정의 유닛에서 해석한다 -- badscope 로 옮기고
이유를 적었다.

188/188.
This commit is contained in:
2026-08-17 05:24:36 +09:00
parent cb20cce81a
commit 25d9de65e1
11 changed files with 757 additions and 32 deletions
+1
View File
@@ -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;
+670 -10
View File
@@ -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;i<FE_TYPE_PARAM_MAX;++i) save->params[i]=c->types.params[i];
c->types.param_count=0;
for (i=0;i<count && i<FE_TYPE_PARAM_MAX;++i) {
FeNode *p=decl_type_param(decl,i);
c->types.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;i<FE_TYPE_PARAM_MAX;++i) save->params[i]=c->types.params[i];
c->types.param_count=0;
for (i=0;i<t->bind_count && i<FE_TYPE_PARAM_MAX;++i)
c->types.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;i<FE_TYPE_PARAM_MAX;++i) c->types.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 (n<cap) out[n++]=*p; else readable=0; }
if (n<cap) out[n++]='.'; else readable=0;
for (p=name?name:"?";*p;++p) { if (n<cap) out[n++]=*p; else readable=0; }
if (n<cap) out[n++]='('; else readable=0;
for (i=0;i<count && readable;++i) {
if (i) { if (n<cap) out[n++]=','; else { readable=0; break; } }
for (p=args[i] && args[i]->name[0] ? args[i]->name : "?";*p;++p) {
if (n<cap) out[n++]=*p;
else { readable=0; break; }
}
}
if (readable && n<cap) out[n++]=')'; else readable=0;
if (readable) { out[n]='\0'; return; }
n=0;
for (p=unit?unit:"";*p && n<cap;++p) out[n++]=*p;
if (n<cap) out[n++]='.';
for (p=name?name:"?";*p && n<cap;++p) out[n++]=*p;
if (n<cap) out[n++]='(';
for (i=0;i<count;++i) {
if (i && n<cap) out[n++]=',';
sprintf(number,"#%u",args[i] ? args[i]->serial : 0U);
for (p=number;*p && n<cap;++p) out[n++]=*p;
}
if (n<cap) out[n++]=')';
out[n]='\0';
}
/* 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 int instance_known(FeCheck *c, const char *key)
{
unsigned i;
for (i=0;i<c->instance_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;u<c->build->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;i<count && i<FE_TYPE_PARAM_MAX;++i) {
FeNode *p=decl_type_param(decl,i);
t->binds[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 (count<FE_TYPE_PARAM_MAX)
args[count]=fe_type_from_ast(&c->types,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;i<c->types.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 (count<FE_TYPE_PARAM_MAX)
args[count]=type_from_expr(s,arg,&inner);
if (!inner) { --c->instance_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;i<want;++i) {
int ok=0;
if (!arg) {
err(c,n->loc,"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;k<skip;++k) {
if (param) param=param->next;
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);
+12
View File
@@ -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,
+5 -5
View File
@@ -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; }
+23 -6
View File
@@ -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 = "<unknown>";
/* 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, "<unknown>");
if (node->text && strcmp(node->text, "fn") == 0)
return fe_type_intern(ctx, "<unknown>");
/* 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);
}
+28 -1
View File
@@ -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);
+3 -1
View File
@@ -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 {
+3 -1
View File
@@ -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 {
+12
View File
@@ -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);
}
-8
View File
@@ -1,8 +0,0 @@
unit main;
import lib;
fn helper(v: i32) -> i32 { return v + 10; }
fn main() -> i32 {
return lib.call(i32, 1);
}