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:
@@ -42,6 +42,7 @@ struct FeNode {
|
|||||||
#define FE_NODE_STATIC 0x2U
|
#define FE_NODE_STATIC 0x2U
|
||||||
#define FE_NODE_SHARED 0x4U
|
#define FE_NODE_SHARED 0x4U
|
||||||
#define FE_NODE_PUB 0x8U
|
#define FE_NODE_PUB 0x8U
|
||||||
|
#define FE_NODE_COMPTIME 0x10U
|
||||||
|
|
||||||
typedef struct FeAst {
|
typedef struct FeAst {
|
||||||
FeArena arena;
|
FeArena arena;
|
||||||
|
|||||||
+670
-10
@@ -59,6 +59,12 @@ static void err(FeCheck *c, FeLoc loc, const char *msg)
|
|||||||
fe_diag_error(c->diags, loc, 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)
|
static int known(FeType *t)
|
||||||
{
|
{
|
||||||
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
|
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);
|
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,
|
void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags,
|
||||||
unsigned pointer_bits, int no_checks)
|
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;
|
c->no_checks = no_checks;
|
||||||
fe_types_init(&c->types, &c->arena, pointer_bits);
|
fe_types_init(&c->types, &c->arena, pointer_bits);
|
||||||
c->types.unit_name = "unit";
|
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)
|
void fe_check_destroy(FeCheck *c)
|
||||||
@@ -376,6 +396,12 @@ static FeNode *find_method(FeCheck *c, FeType *owner, const char *name)
|
|||||||
FeNode *decl;
|
FeNode *decl;
|
||||||
FeNode *method;
|
FeNode *method;
|
||||||
if(!owner || !name) return 0;
|
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)
|
for(decl=c->ast->root ? c->ast->root->children : 0; decl; decl=decl->next)
|
||||||
if(decl->kind==FE_N_STRUCT && decl->text &&
|
if(decl->kind==FE_N_STRUCT && decl->text &&
|
||||||
strcmp(decl->text,owner->name)==0)
|
strcmp(decl->text,owner->name)==0)
|
||||||
@@ -403,9 +429,37 @@ static FeType *check_lvalue_core(FeCheckerState *s, FeNode *n, int read,
|
|||||||
FeType *base_in);
|
FeType *base_in);
|
||||||
static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read);
|
static FeType *check_lvalue(FeCheckerState *s, FeNode *n, int read);
|
||||||
static FeType *check_call(FeCheckerState *s, FeNode *n);
|
static FeType *check_call(FeCheckerState *s, FeNode *n);
|
||||||
static FeType *check_call_fn(FeCheckerState *s, FeNode *n, FeSym *sym,
|
static FeType *check_call_args(FeCheckerState *s, FeNode *n, FeSym *sym,
|
||||||
const char *home);
|
const char *home, unsigned skip);
|
||||||
static int is_error_set_member(FeCheckerState *s, FeNode *n);
|
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 {
|
typedef struct FeFlowSlot {
|
||||||
FeSym *sym;
|
FeSym *sym;
|
||||||
@@ -972,6 +1026,10 @@ static FeType *check_identifier(FeCheckerState *s, FeNode *n)
|
|||||||
if (!sym) {
|
if (!sym) {
|
||||||
FeType *named=fe_type_intern(&s->c->types,n->text ? n->text : "");
|
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_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");
|
err(s->c, n->loc, "unknown name");
|
||||||
return unknown(s->c);
|
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) &&
|
if (known(a) && known(b) && !fe_type_equal(a, b) &&
|
||||||
!compatible(a, b, n->b) && !compatible(b, a, n->a))
|
!compatible(a, b, n->b) && !compatible(b, a, n->a))
|
||||||
err(c, n->loc, "comparison operands have different types");
|
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");
|
a = fe_type_intern(&c->types, "bool");
|
||||||
} else {
|
} else {
|
||||||
if ((known(a) && !fe_type_is_integer(a)) ||
|
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);
|
for (x=n->children;x;x=x->next) check_expr(s,x);
|
||||||
return unknown(c);
|
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);
|
et=check_expr(s,n->a->a);
|
||||||
method=et && et->kind==FE_TYPE_STRUCT ?
|
method=et && et->kind==FE_TYPE_STRUCT ?
|
||||||
find_method(c,et,n->a->b ? n->a->b->text : "") : 0;
|
find_method(c,et,n->a->b ? n->a->b->text : "") : 0;
|
||||||
if(method) {
|
if(method) {
|
||||||
|
FeBindSave msave;
|
||||||
|
int bound=0;
|
||||||
self_param=method->a ? method->a->children : 0;
|
self_param=method->a ? method->a->children : 0;
|
||||||
if(!self_param) {
|
if(!self_param) {
|
||||||
err(c,n->loc,"method requires self parameter");
|
err(c,n->loc,"method requires self parameter");
|
||||||
return unknown(c);
|
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);
|
a=method_type(c,self_param->a,et);
|
||||||
if(a->kind==FE_TYPE_REF && a->ref_mut &&
|
if(a->kind==FE_TYPE_REF && a->ref_mut &&
|
||||||
!lvalue_writable(s,n->a->a))
|
!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_decl=method;
|
||||||
n->sem_type=method->b ? method_type(c,method->b,et) :
|
n->sem_type=method->b ? method_type(c,method->b,et) :
|
||||||
fe_type_intern(&c->types,"void");
|
fe_type_intern(&c->types,"void");
|
||||||
|
if (bound) {
|
||||||
|
pop_bindings(c,&msave);
|
||||||
|
check_instance_method(s,et,method,n->loc);
|
||||||
|
}
|
||||||
return n->sem_type;
|
return n->sem_type;
|
||||||
}
|
}
|
||||||
if (et && (et->kind==FE_TYPE_SLICE || et->kind==FE_TYPE_STR) &&
|
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");
|
err(c, n->loc, "unknown function");
|
||||||
return unknown(c);
|
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);
|
for (x = n->children; x; x = x->next) check_expr(s, x);
|
||||||
return unknown(c);
|
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);
|
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,
|
/* 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
|
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. */
|
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;
|
return sym->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FeType *check_call_fn(FeCheckerState *s, FeNode *n, FeSym *sym,
|
/* `skip` leading parameters and arguments have already been consumed as
|
||||||
const char *home)
|
comptime type arguments. */
|
||||||
|
static FeType *check_call_args(FeCheckerState *s, FeNode *n, FeSym *sym,
|
||||||
|
const char *home, unsigned skip)
|
||||||
{
|
{
|
||||||
FeCheck *c=s->c;
|
FeCheck *c=s->c;
|
||||||
FeNode *param;
|
FeNode *param;
|
||||||
FeNode *arg;
|
FeNode *arg;
|
||||||
FeType *a;
|
FeType *a;
|
||||||
FeType *b;
|
FeType *b;
|
||||||
|
unsigned k;
|
||||||
if (n->a) n->a->cname = sym->cname;
|
if (n->a) n->a->cname = sym->cname;
|
||||||
n->sem_decl = sym->fn;
|
n->sem_decl = sym->fn;
|
||||||
if (!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;
|
param = sym->fn->a ? sym->fn->a->children : 0;
|
||||||
arg = n->children;
|
arg = n->children;
|
||||||
|
for (k=0;k<skip;++k) {
|
||||||
|
if (param) param=param->next;
|
||||||
|
if (arg) arg=arg->next;
|
||||||
|
}
|
||||||
while (param && arg) {
|
while (param && arg) {
|
||||||
a = check_expr(s, arg);
|
a = check_expr(s, arg);
|
||||||
b = node_type_in(c, home, param->a);
|
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);
|
n->sem_type=unknown(c);
|
||||||
return n->sem_type;
|
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->a->cname=sym->cname;
|
||||||
n->sem_decl=sym->fn;
|
n->sem_decl=sym->fn;
|
||||||
param=sym->fn->a ? sym->fn->a->children : 0;
|
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(a,b,n->b) &&
|
||||||
!m7_actual_compatible(b,a,n->a))
|
!m7_actual_compatible(b,a,n->a))
|
||||||
err(s->c,n->loc,"comparison operands have different types");
|
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");
|
n->sem_type=fe_type_intern(&s->c->types,"bool");
|
||||||
return n->sem_type;
|
return n->sem_type;
|
||||||
}
|
}
|
||||||
@@ -2825,6 +3464,18 @@ static void check_stmt(FeCheckerState *s, FeNode *n)
|
|||||||
--s->defer_depth;
|
--s->defer_depth;
|
||||||
break;
|
break;
|
||||||
case FE_N_IF:
|
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)
|
if (n->text && strcmp(n->text,"if let")==0)
|
||||||
m7_check_if_let(s,n);
|
m7_check_if_let(s,n);
|
||||||
else {
|
else {
|
||||||
@@ -2988,15 +3639,21 @@ static void m7_validate_error_decl(FeCheck *c, FeNode *decl)
|
|||||||
static void declare_unit(FeCheck *c)
|
static void declare_unit(FeCheck *c)
|
||||||
{
|
{
|
||||||
FeNode *n;
|
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)
|
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);
|
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) {
|
for (n=c->ast->root ? c->ast->root->children : 0;n;n=n->next) {
|
||||||
|
FeNode *m;
|
||||||
m7_check_storage(c,n);
|
m7_check_storage(c,n);
|
||||||
if (n->kind==FE_N_ERROR_DECL) m7_validate_error_decl(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)
|
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)
|
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);
|
if (n->kind==FE_N_ERROR_DECL) fe_type_declare_error(&c->types,n);
|
||||||
check_type_cycles(c);
|
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)
|
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) {
|
if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) {
|
||||||
sym=find_current(s->globals,n->text ? n->text : "");
|
sym=find_current(s->globals,n->text ? n->text : "");
|
||||||
|
if (n->kind==FE_N_CONST && const_names_type(s,n)) continue;
|
||||||
if (n->b) {
|
if (n->b) {
|
||||||
iv=m7_check_expected(s,n->b,sym ? sym->type : 0);
|
iv=m7_check_expected(s,n->b,sym ? sym->type : 0);
|
||||||
if (sym && sym->type->kind==FE_TYPE_UNKNOWN) {
|
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");
|
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)
|
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)
|
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);
|
t=fe_type_intern(&c->types,n->text);
|
||||||
for (m=n->children;m;m=m->next)
|
for (m=n->children;m;m=m->next)
|
||||||
if (m->kind==FE_N_FN) check_method(c,m,s->globals,t);
|
if (m->kind==FE_N_FN) check_method(c,m,s->globals,t);
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
|
|
||||||
typedef struct FeScope FeScope;
|
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,
|
/* 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
|
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
|
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 pointer_bits;
|
||||||
unsigned local_serial;
|
unsigned local_serial;
|
||||||
int no_checks;
|
int no_checks;
|
||||||
|
FeInstance *instances;
|
||||||
|
unsigned instance_count;
|
||||||
|
unsigned instance_depth;
|
||||||
} FeCheck;
|
} FeCheck;
|
||||||
|
|
||||||
void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags,
|
void fe_check_init(FeCheck *c, FeBuild *build, FeDiags *diags,
|
||||||
|
|||||||
+5
-5
@@ -185,9 +185,9 @@ static FeNode *params(FeParser *p)
|
|||||||
{
|
{
|
||||||
FeNode *list=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"params",6);
|
FeNode *list=fe_node(p->ast,FE_N_BLOCK,p->current.loc,"params",6);
|
||||||
want(p,FE_TOK_LPAREN,"expected '(' after function name");
|
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;
|
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;
|
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,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;
|
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;
|
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_PACKED)) t=p->current;
|
||||||
if(is(p,FE_TOK_FN)) return fn_decl(p,pub,external,interrupt,interrupt_safe);
|
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_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_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");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_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_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(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; }
|
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
@@ -41,6 +41,9 @@ static FeType *new_type(FeTypeCtx *ctx, const char *name, FeTypeKind kind)
|
|||||||
t->field_count = 0;
|
t->field_count = 0;
|
||||||
t->variants = 0;
|
t->variants = 0;
|
||||||
t->variant_count = 0;
|
t->variant_count = 0;
|
||||||
|
t->serial = ctx->generated_serial++;
|
||||||
|
t->decl_node = 0;
|
||||||
|
t->bind_count = 0;
|
||||||
t->next = ctx->types;
|
t->next = ctx->types;
|
||||||
t->emit_state = 0;
|
t->emit_state = 0;
|
||||||
t->cycle_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->pointer_bits = pointer_bits;
|
||||||
ctx->unit_name = "unit";
|
ctx->unit_name = "unit";
|
||||||
ctx->generated_serial = 0;
|
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
|
/* 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;
|
unsigned bits = 0;
|
||||||
int uns = 0;
|
int uns = 0;
|
||||||
FeTypeKind kind = FE_TYPE_UNKNOWN;
|
FeTypeKind kind = FE_TYPE_UNKNOWN;
|
||||||
|
unsigned i;
|
||||||
if (!name) name = "<unknown>";
|
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)
|
for (t = ctx->types; t; t = t->next)
|
||||||
if (type_visible_as(t, ctx->unit_name, name)) return t;
|
if (type_visible_as(t, ctx->unit_name, name)) return t;
|
||||||
if (strcmp(name, "void") == 0) kind = FE_TYPE_VOID;
|
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)
|
FeType *fe_type_array(FeTypeCtx *ctx, unsigned long length, FeType *elem)
|
||||||
{
|
{
|
||||||
char key[96];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key, "[%lu]%s", length, elem ? elem->name : "?");
|
sprintf(key, "[%lu]%s", length, elem ? elem->name : "?");
|
||||||
t = fe_type_intern(ctx, key);
|
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)
|
FeType *fe_type_slice(FeTypeCtx *ctx, FeType *elem)
|
||||||
{
|
{
|
||||||
char key[96];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key, "[]%s", elem ? elem->name : "?");
|
sprintf(key, "[]%s", elem ? elem->name : "?");
|
||||||
t = fe_type_intern(ctx, key);
|
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)
|
FeType *fe_type_mut_slice(FeTypeCtx *ctx, FeType *elem)
|
||||||
{
|
{
|
||||||
char key[96];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key, "[]mut %s", elem ? elem->name : "?");
|
sprintf(key, "[]mut %s", elem ? elem->name : "?");
|
||||||
t = fe_type_intern(ctx, key);
|
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)
|
FeType *fe_type_ref(FeTypeCtx *ctx, FeType *elem, int mutable)
|
||||||
{
|
{
|
||||||
char key[128];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key,"%s%s",mutable ? "&mut " : "&",elem ? elem->name : "?");
|
sprintf(key,"%s%s",mutable ? "&mut " : "&",elem ? elem->name : "?");
|
||||||
t=fe_type_intern(ctx,key);
|
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)
|
FeType *fe_type_owned(FeTypeCtx *ctx, FeType *elem)
|
||||||
{
|
{
|
||||||
char key[128];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key,"^%s",elem ? elem->name : "?");
|
sprintf(key,"^%s",elem ? elem->name : "?");
|
||||||
t=fe_type_intern(ctx,key);
|
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)
|
FeType *fe_type_error_union(FeTypeCtx *ctx, FeType *value)
|
||||||
{
|
{
|
||||||
char key[128];
|
char key[320];
|
||||||
FeType *t;
|
FeType *t;
|
||||||
sprintf(key,"!%s",value ? value->name : "?");
|
sprintf(key,"!%s",value ? value->name : "?");
|
||||||
t=fe_type_intern(ctx,key);
|
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;
|
if (t->kind == FE_TYPE_STRUCT) return t;
|
||||||
t->kind = FE_TYPE_STRUCT;
|
t->kind = FE_TYPE_STRUCT;
|
||||||
t->packed = packed;
|
t->packed = packed;
|
||||||
|
t->decl_node = node;
|
||||||
for (f = node->children; f; f = f->next)
|
for (f = node->children; f; f = f->next)
|
||||||
if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0)
|
if (f->kind==FE_N_FN && f->text && strcmp(f->text,"drop")==0)
|
||||||
t->has_drop=1;
|
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_UNKNOWN && t->kind != FE_TYPE_ENUM) return t;
|
||||||
if (t->kind == FE_TYPE_ENUM) return t;
|
if (t->kind == FE_TYPE_ENUM) return t;
|
||||||
t->kind = FE_TYPE_ENUM;
|
t->kind = FE_TYPE_ENUM;
|
||||||
|
t->decl_node = node;
|
||||||
cname = (char *)fe_arena_alloc(ctx->arena,
|
cname = (char *)fe_arena_alloc(ctx->arena,
|
||||||
(unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) +
|
(unsigned long)strlen("struct fe_") + strlen(ctx->unit_name) +
|
||||||
strlen(node->text) + 2UL);
|
strlen(node->text) + 2UL);
|
||||||
@@ -592,6 +604,11 @@ FeType *fe_type_from_ast(FeTypeCtx *ctx, const FeNode *node)
|
|||||||
return fe_type_intern(ctx, "<unknown>");
|
return fe_type_intern(ctx, "<unknown>");
|
||||||
if (node->text && strcmp(node->text, "fn") == 0)
|
if (node->text && strcmp(node->text, "fn") == 0)
|
||||||
return fe_type_intern(ctx, "<unknown>");
|
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);
|
return fe_type_intern(ctx, node->text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -11,6 +11,14 @@ typedef enum FeTypeKind {
|
|||||||
} FeTypeKind;
|
} FeTypeKind;
|
||||||
|
|
||||||
typedef struct FeFieldType FeFieldType;
|
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;
|
typedef struct FeVariantType FeVariantType;
|
||||||
|
|
||||||
struct FeFieldType {
|
struct FeFieldType {
|
||||||
@@ -31,7 +39,9 @@ struct FeVariantType {
|
|||||||
|
|
||||||
struct FeType {
|
struct FeType {
|
||||||
FeTypeKind kind;
|
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
|
/* The unit that declared this type, for the nominal kinds. NULL for
|
||||||
builtins and for structural types like `[]u8`, which every unit
|
builtins and for structural types like `[]u8`, which every unit
|
||||||
shares. Two units declaring the same name declare two types. */
|
shares. Two units declaring the same name declare two types. */
|
||||||
@@ -65,6 +75,15 @@ struct FeType {
|
|||||||
unsigned field_count;
|
unsigned field_count;
|
||||||
FeVariantType *variants;
|
FeVariantType *variants;
|
||||||
unsigned variant_count;
|
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;
|
FeType *next;
|
||||||
int emit_state;
|
int emit_state;
|
||||||
int cycle_state;
|
int cycle_state;
|
||||||
@@ -76,6 +95,14 @@ typedef struct FeTypeCtx {
|
|||||||
unsigned pointer_bits;
|
unsigned pointer_bits;
|
||||||
const char *unit_name;
|
const char *unit_name;
|
||||||
unsigned generated_serial;
|
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;
|
} FeTypeCtx;
|
||||||
|
|
||||||
void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits);
|
void fe_types_init(FeTypeCtx *ctx, FeArena *arena, unsigned pointer_bits);
|
||||||
|
|||||||
@@ -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;
|
unit badbody;
|
||||||
|
|
||||||
fn add(comptime T: type, a: T, b: T) -> T {
|
fn add(comptime T: type, a: T, b: T) -> T {
|
||||||
|
|||||||
@@ -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;
|
unit badop;
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
unit main;
|
|
||||||
import lib;
|
|
||||||
|
|
||||||
fn helper(v: i32) -> i32 { return v + 10; }
|
|
||||||
|
|
||||||
fn main() -> i32 {
|
|
||||||
return lib.call(i32, 1);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user