lower: 태그드 유니온을 해체한다 -- match 페이로드와 if let
AST 도 IR 도 본질이 태그드 유니온이다. 태그 비교만 되고 안을 꺼내지 못하면
검사 없이 필드를 읽어야 하고, 그러면 안전성 이야기가 통째로 무너진다.
무언가를 담는 변이는 메모리다: 태그가 앞, 페이로드가 뒤. 태그를 읽는 것은
어느 쪽이든 같은 질문이고 자리만 다르다. 페이로드는 가리키지 않고 복사한다 --
매치한 것의 소유권을 arm 이 가져가는 것이 보통이고, 그게 허용되는지는 검사기가
이미 판단했다.
레코드 변이의 필드 오프셋을 아무도 기록하지 않고 있었다. 레이아웃이 크기는
재면서 자리는 버렸다.
Enum.Variant{...} 와 Enum.Variant(x) 와 페이로드 없는 Enum.Variant 를 모두
만든다. 마지막 것은 페이로드를 가진 enum 안에서는 태그만 든 값이다.
some 7 / none / point 3 4 / empty
exec.py 25/25.
This commit is contained in:
+44
-5
@@ -108,13 +108,23 @@ Slot lower_expr_core(Lower *L, FeNode *n)
|
||||
fail(L, "this unary operator", n);
|
||||
return slot_void();
|
||||
case FE_N_MEMBER:
|
||||
/* A payload-free variant used as a value is just its tag. */
|
||||
if (t && t->kind == FE_TYPE_ENUM && !enum_has_payload(t) &&
|
||||
n->b && n->b->text) {
|
||||
/* A variant used as a value carries nothing but its tag. When no
|
||||
variant of the enum carries anything the whole value is that tag;
|
||||
otherwise it is a tag sitting in front of an unused payload. */
|
||||
if (t && t->kind == FE_TYPE_ENUM && n->b && n->b->text) {
|
||||
FeVariantType *v = fe_type_variant(t, n->b->text);
|
||||
if (v)
|
||||
if (v && !enum_has_payload(t))
|
||||
return slot_value(fe_ir_const(L->m, L->b, ir_type(t),
|
||||
(long)v->tag), ir_type(t));
|
||||
if (v && !v->field_count) {
|
||||
unsigned local = scratch(L, t, "variant");
|
||||
unsigned tag = fe_ir_const(L->m, L->b, tag_type_of(t),
|
||||
(long)v->tag);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), tag,
|
||||
tag_type_of(t));
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM,
|
||||
ir_size(t));
|
||||
}
|
||||
}
|
||||
/* `error.Name` is a member of the open default set: a code, and
|
||||
nothing to look up. */
|
||||
@@ -211,8 +221,37 @@ Slot lower_expr_core(Lower *L, FeNode *n)
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
case FE_N_STRUCT_INIT: {
|
||||
unsigned local = scratch(L, t, "struct");
|
||||
unsigned local;
|
||||
FeNode *f;
|
||||
/* `Enum.Variant{ .. }` builds a variant, not a struct: the tag first,
|
||||
then the named fields inside the payload area. */
|
||||
if (t && t->kind == FE_TYPE_ENUM && n->a && n->a->kind == FE_N_MEMBER) {
|
||||
const FeVariantType *v = fe_type_variant(t,
|
||||
n->a->b && n->a->b->text ? n->a->b->text : "");
|
||||
long base = (long)fe_type_payload_offset(t);
|
||||
unsigned tag;
|
||||
if (!v) { fail(L, "an unknown variant", n); return slot_void(); }
|
||||
local = scratch(L, t, "variant");
|
||||
tag = fe_ir_const(L->m, L->b, tag_type_of(t), (long)v->tag);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), tag,
|
||||
tag_type_of(t));
|
||||
for (f = n->children; f; f = f->next) {
|
||||
unsigned i;
|
||||
if (f->kind != FE_N_FIELD) continue;
|
||||
for (i = 0; i < v->field_count; ++i)
|
||||
if (f->text && v->fields[i].name &&
|
||||
!strcmp(v->fields[i].name, f->text)) break;
|
||||
if (i == v->field_count) {
|
||||
fail(L, "an unknown variant field", f);
|
||||
return slot_void();
|
||||
}
|
||||
store_into(L, fe_ir_at_local(local,
|
||||
base + (long)v->fields[i].offset),
|
||||
lower_expr(L, f->a), f, ir_size(v->fields[i].type));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
local = scratch(L, t, "struct");
|
||||
for (f = n->children; f; f = f->next) {
|
||||
FeFieldType *field;
|
||||
Slot v;
|
||||
|
||||
Reference in New Issue
Block a user