std: str 과 list, 그리고 자동 drop 이 사용자 타입까지 닿는다
std.str 은 eq/starts_with/find/trim/parse_int 을 바이트 슬라이스 위에서 한다. std.list 는 자라는 제네릭 시퀀스다 -- 버퍼를 소유하므로 리스트를 놓으면 원소도 놓인다. 성장은 두 배씩이라 push 당 복사량이 상수로 눌린다. 찾은 버그 넷: - 메서드가 자기 타입의 유닛이 아니라 호출한 유닛에 속한 것으로 계산됐다. 다른 유닛의 제네릭 타입을 쓰면 필드가 전부 private 으로 보였다. - 참조로 도달한 메서드를 찾지 못했다. self.grow() 가 안 됐다. - 이미 참조인 수신자의 주소를 한 번 더 떠서 넘겼다. 포인터의 포인터를 받은 메서드가 그것을 구조체로 읽었다. - 유닛으로 한정된 제네릭 타입(list.List(i32))이 타입 자리에서도 식 자리에서도 해석되지 않았다. drop 을 가진 타입은 인스턴스마다 그 메서드가 존재해야 한다 -- 이름으로 부르는 사람이 없어도 스코프 정리가 부른다. 그리고 자기 drop 안에서는 필드를 꺼낼 수 있다. 객체가 사라지는 중이라 뒤에 남아 읽힐 것이 없다. run.py 207/207, exec.py 19/19.
This commit is contained in:
+92
-9
@@ -70,9 +70,27 @@ static int known(FeType *t)
|
||||
return t && t->kind != FE_TYPE_UNKNOWN && t->kind != FE_TYPE_ERROR;
|
||||
}
|
||||
|
||||
/* Is this a projection of `self` inside that type's own `drop`? */
|
||||
static int in_own_drop(FeCheckerState *s, FeNode *n)
|
||||
{
|
||||
FeNode *base;
|
||||
if (!s->fn_node || !s->fn_node->text || strcmp(s->fn_node->text,"drop")!=0)
|
||||
return 0;
|
||||
base = n ? n->a : 0;
|
||||
while (base && (base->kind==FE_N_MEMBER || base->kind==FE_N_INDEX))
|
||||
base = base->a;
|
||||
return base && base->kind==FE_N_IDENT && base->text &&
|
||||
strcmp(base->text,"self")==0;
|
||||
}
|
||||
|
||||
static void mark_moved(FeCheckerState *s, FeNode *n, FeType *t)
|
||||
{
|
||||
FeSym *sym=0;
|
||||
/* Inside a type's own `drop` the object is going away, so taking a field
|
||||
out of it leaves nothing behind that anyone could read. That is the one
|
||||
place R7 has nothing to protect. */
|
||||
if (n && (n->kind==FE_N_MEMBER || n->kind==FE_N_INDEX) && in_own_drop(s,n))
|
||||
return;
|
||||
if (n && n->kind==FE_N_IDENT)
|
||||
sym=find_symbol(s->scope,n->text ? n->text : "");
|
||||
if (s->defer_depth != 0) {
|
||||
@@ -1283,6 +1301,12 @@ static FeType *check_expr_core(FeCheckerState *s, FeNode *n)
|
||||
}
|
||||
}
|
||||
et=check_expr(s,n->a->a);
|
||||
/* A method can be reached through a reference or an owner as well
|
||||
as through the value itself. */
|
||||
if (et && (et->kind==FE_TYPE_REF || et->kind==FE_TYPE_OWNED) &&
|
||||
et->elem && et->elem->kind==FE_TYPE_STRUCT &&
|
||||
find_method(c,et->elem,n->a->b ? n->a->b->text : ""))
|
||||
et=et->elem;
|
||||
method=et && et->kind==FE_TYPE_STRUCT ?
|
||||
find_method(c,et,n->a->b ? n->a->b->text : "") : 0;
|
||||
if(method) {
|
||||
@@ -2361,6 +2385,23 @@ static FeType *build_struct_instance(FeCheck *c, FeUnit *home, FeNode *decl,
|
||||
pop_bindings(c,&save);
|
||||
}
|
||||
fe_type_layout_all(&c->types);
|
||||
/* A type that says how to let go of itself needs that method to exist for
|
||||
every instance, whether or not anyone calls it by name: scope cleanup
|
||||
will. */
|
||||
{
|
||||
FeNode *release;
|
||||
for (release=decl->children;release;release=release->next)
|
||||
if (release->kind==FE_N_FN && release->text &&
|
||||
!strcmp(release->text,"drop") && release->c) {
|
||||
FeCheckerState s;
|
||||
memset(&s,0,sizeof s);
|
||||
s.c=c;
|
||||
s.scope=c->unit_scope[unit_index(c,home)];
|
||||
s.globals=s.scope;
|
||||
check_instance_method(&s,t,release,decl->loc,0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -2391,18 +2432,30 @@ static FeType *instantiate_type_node(void *owner, const FeNode *node)
|
||||
{
|
||||
FeCheck *c=(FeCheck *)owner;
|
||||
FeUnit *home=current_unit(c);
|
||||
const char *name=node->text;
|
||||
FeNode *arg;
|
||||
FeType *args[FE_TYPE_PARAM_MAX];
|
||||
unsigned count=0;
|
||||
FeType *result;
|
||||
/* `binding.Name` names a type in another unit. The binding is not itself a
|
||||
type, so it has to be peeled off before anything is looked up. */
|
||||
if (node->a && node->a->kind==FE_N_IDENT && node->a->text && c->build &&
|
||||
c->unit) {
|
||||
FeUnit *bound=fe_build_binding(c->build,c->unit,node->text);
|
||||
if (bound) { home=bound; name=node->a->text; }
|
||||
}
|
||||
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 : "");
|
||||
FeNode *decl=unit_type_decl(c,home,name ? name : "");
|
||||
if (decl && decl_is_generic(decl)) {
|
||||
/* A generic declaration is not a type until it has arguments. */
|
||||
err(c,node->loc,"generic type requires type arguments");
|
||||
return unknown(c);
|
||||
}
|
||||
return fe_type_intern(&c->types,node->text);
|
||||
if (name!=node->text) {
|
||||
FeType *there=unit_type(c,home,name);
|
||||
if (there) return there;
|
||||
}
|
||||
return fe_type_intern(&c->types,name);
|
||||
}
|
||||
if (!instance_descend(c,node->loc)) return unknown(c);
|
||||
for (arg=node->children;arg;arg=arg->next) {
|
||||
@@ -2415,7 +2468,7 @@ static FeType *instantiate_type_node(void *owner, const FeNode *node)
|
||||
--c->instance_depth;
|
||||
return unknown(c);
|
||||
}
|
||||
result=instantiate_struct(c,home,node->text ? node->text : "",args,count,
|
||||
result=instantiate_struct(c,home,name ? name : "",args,count,
|
||||
node->loc);
|
||||
--c->instance_depth;
|
||||
return result;
|
||||
@@ -2448,13 +2501,27 @@ static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok)
|
||||
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) {
|
||||
if (n->kind==FE_N_CALL && n->a &&
|
||||
(n->a->kind==FE_N_IDENT ||
|
||||
(n->a->kind==FE_N_MEMBER && n->a->a &&
|
||||
n->a->a->kind==FE_N_IDENT && n->a->b && n->a->b->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);
|
||||
const char *want;
|
||||
/* `Name(args)` here, `binding.Name(args)` when the declaration is in
|
||||
another unit. */
|
||||
if (n->a->kind==FE_N_MEMBER) {
|
||||
FeUnit *bound=binding_unit(s,n->a->a);
|
||||
if (!bound) return unknown(c);
|
||||
home=bound;
|
||||
want=n->a->b->text;
|
||||
} else {
|
||||
want=n->a->text;
|
||||
}
|
||||
if (!want || !unit_type_decl(c,home,want)) 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;
|
||||
@@ -2464,7 +2531,7 @@ static FeType *type_from_expr(FeCheckerState *s, FeNode *n, int *ok)
|
||||
++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);
|
||||
result=instantiate_struct(c,home,want,args,count,n->loc);
|
||||
--c->instance_depth;
|
||||
*ok=1;
|
||||
return result;
|
||||
@@ -2578,17 +2645,30 @@ static FeType *check_generic_call(FeCheckerState *s, FeNode *n, FeSym *sym,
|
||||
|
||||
/* `Type.method(...)` where Type is a generic instance and the method takes no
|
||||
self parameter. */
|
||||
/* The unit a name belongs to, by name. */
|
||||
static FeUnit *unit_named(FeCheck *c, const char *name)
|
||||
{
|
||||
unsigned u;
|
||||
if (!name) return 0;
|
||||
for (u=0;u<c->build->count;++u)
|
||||
if (!strcmp(c->build->units[u].name,name)) return &c->build->units[u];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FeType *check_static_method_call(FeCheckerState *s, FeNode *n,
|
||||
FeType *owner, FeNode *method)
|
||||
{
|
||||
FeCheck *c=s->c;
|
||||
FeUnit *home=current_unit(c);
|
||||
/* A method belongs to the unit that declared its type, not to whichever
|
||||
unit happens to be calling it. */
|
||||
FeUnit *home=unit_named(c,owner ? owner->unit : 0);
|
||||
FeBindSave save;
|
||||
FeType *result;
|
||||
char key[FE_GENERIC_KEY_MAX];
|
||||
FeType *self_args[1];
|
||||
int fresh;
|
||||
FeSym fake;
|
||||
if (!home) home=current_unit(c);
|
||||
self_args[0]=owner;
|
||||
instance_key(key,home->name,method->text,self_args,1);
|
||||
memset(&fake,0,sizeof fake);
|
||||
@@ -2618,11 +2698,14 @@ static void check_instance_method(FeCheckerState *s, FeType *owner,
|
||||
FeNode *method, FeLoc site, FeNode *call)
|
||||
{
|
||||
FeCheck *c=s->c;
|
||||
FeUnit *home=current_unit(c);
|
||||
/* A method belongs to the unit that declared its type, not to whichever
|
||||
unit happens to be calling it. */
|
||||
FeUnit *home=unit_named(c,owner ? owner->unit : 0);
|
||||
FeBindSave save;
|
||||
char key[FE_GENERIC_KEY_MAX];
|
||||
FeType *self_args[1];
|
||||
self_args[0]=owner;
|
||||
if (!home) home=current_unit(c);
|
||||
instance_key(key,home->name,method->text,self_args,1);
|
||||
{
|
||||
FeBindSave probe;
|
||||
|
||||
+38
-3
@@ -687,9 +687,17 @@ static Slot lower_call(Lower *L, FeNode *n)
|
||||
if (n->a && n->a->kind == FE_N_MEMBER && n->sem_decl) {
|
||||
FeNode *first = n->sem_decl->a ? n->sem_decl->a->children : 0;
|
||||
if (first && first->text && !strcmp(first->text, "self")) {
|
||||
FeType *rt = n->a->a ? n->a->a->sem_type : 0;
|
||||
Slot recv = lower_expr(L, n->a->a);
|
||||
args[count++] = recv.is_place ? as_address(L, recv, n->a->a)
|
||||
: recv.temp;
|
||||
/* A receiver that is already a reference or an owner is a pointer
|
||||
already; taking its address would pass a pointer to the
|
||||
pointer. */
|
||||
if (rt && (rt->kind == FE_TYPE_REF ||
|
||||
(rt->kind == FE_TYPE_OWNED && ir_type(rt) == FE_IR_PTR)))
|
||||
args[count++] = as_value(L, recv, n->a->a);
|
||||
else
|
||||
args[count++] = recv.is_place ? as_address(L, recv, n->a->a)
|
||||
: recv.temp;
|
||||
}
|
||||
}
|
||||
/* A generic call passes its type arguments first. They were consumed when
|
||||
@@ -990,6 +998,24 @@ static Slot lower_expr_core(Lower *L, FeNode *n)
|
||||
}
|
||||
}
|
||||
|
||||
/* The link name of the `drop` method for this type, found through the instance
|
||||
the checker recorded. */
|
||||
static const char *drop_name(Lower *L, const FeType *t)
|
||||
{
|
||||
unsigned i;
|
||||
FeNode *method = 0;
|
||||
if (!t || !t->decl_node) return 0;
|
||||
for (method = t->decl_node->children; method; method = method->next)
|
||||
if (method->kind == FE_N_FN && method->text &&
|
||||
!strcmp(method->text, "drop")) break;
|
||||
if (!method) return 0;
|
||||
for (i = 0; i < L->c->instance_count; ++i)
|
||||
if (L->c->instances[i].decl == method &&
|
||||
L->c->instances[i].owner == t)
|
||||
return L->c->instances[i].cname;
|
||||
return method->cname;
|
||||
}
|
||||
|
||||
/* Settle what a scope owes, most recent first. A `return` in the middle of a
|
||||
function still owes everything, so every exit path calls this. */
|
||||
static void run_deferred(Lower *L, unsigned from)
|
||||
@@ -1019,7 +1045,16 @@ static void run_deferred(Lower *L, unsigned from)
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
}
|
||||
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
||||
if (t && t->has_drop) {
|
||||
/* A type that says how to let go of itself is asked to; the
|
||||
name is the one its instance was given. */
|
||||
const char *how = drop_name(L, t);
|
||||
args[0] = fe_ir_addr(L->m, L->b,
|
||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
if (how) fe_ir_call(L->m, L->b, FE_IR_VOID, how, args, 1);
|
||||
} else {
|
||||
fe_ir_call(L->m, L->b, FE_IR_VOID, "fe_rt_free", args, 1);
|
||||
}
|
||||
fe_ir_jmp(L->b, skip->id);
|
||||
L->b = skip;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user