refactor: check.c 와 lower.c 를 사람이 머리에 담을 크기로 나눈다
check.c 3,937 줄, lower.c 1,913 줄이었다. 가장 큰 파일이 978 줄이 됐다.
check.c 679 스코프·심볼·흐름·소유권 접착
checkexp.c 739 포매팅 검사와 표현식
checkstm.c 635 문장, 함수, 메서드
checkgen.c 618 제네릭 실체화
checkcal.c 978 유닛 경계 호출과 옵셔널/에러 유니온
checkpro.c 184 선언 패스와 프로그램
lower.c 567 타입·슬롯·지역·블록·mem.*
lowerprn.c 238 포매팅 빌트인 전개
lowerexp.c 468 표현식
lowerstm.c 543 문장·함수·프로그램
줄 범위로 잘랐다. 주제별로 묶는 것보다 정확한데, 한 줄도 잃거나 겹치지
않기 때문이다. 파일 순서가 이미 단계를 따라가서 경계가 실제 이음매에 떨어진다.
모든 정의가 static 을 잃고 비공개 헤더에 프로토타입을 갖는다. 대안 --
static 을 유지하고 #include 로 텍스트만 나누는 것 -- 은 결합을 보여주는 대신
숨긴다.
두 스위트 그대로: 209/209, 21/21.
This commit is contained in:
@@ -0,0 +1,468 @@
|
||||
#include "lowerpri.h"
|
||||
|
||||
Slot lower_expr_core(Lower *L, FeNode *n)
|
||||
{
|
||||
FeType *t;
|
||||
FeIrType it;
|
||||
if (!n || L->failed) return slot_void();
|
||||
t = n->sem_type;
|
||||
it = ir_type(t);
|
||||
switch (n->kind) {
|
||||
case FE_N_LITERAL:
|
||||
if (n->text && n->text[0] == '"') {
|
||||
/* The bytes live in the image; the value is a pointer to them and
|
||||
how many there are. The lexer keeps the quotes and the escapes,
|
||||
so this is where `
|
||||
` becomes one byte. */
|
||||
char text[1024];
|
||||
unsigned long raw = strlen(n->text);
|
||||
unsigned long len = 0;
|
||||
unsigned long i;
|
||||
const char *label;
|
||||
unsigned local;
|
||||
unsigned p;
|
||||
unsigned c;
|
||||
if (raw >= 2) raw -= 2;
|
||||
for (i = 0; i < raw && len + 1 < sizeof text; ++i) {
|
||||
char ch = n->text[1 + i];
|
||||
if (ch == 92 && i + 1 < raw) { /* a backslash */
|
||||
++i;
|
||||
switch (n->text[1 + i]) {
|
||||
case 'n': ch = 10; break;
|
||||
case 't': ch = 9; break;
|
||||
case 'r': ch = 13; break;
|
||||
case '0': ch = 0; break;
|
||||
default: ch = n->text[1 + i]; break;
|
||||
}
|
||||
}
|
||||
text[len++] = ch;
|
||||
}
|
||||
label = fe_ir_string(L->m, text, len);
|
||||
if (!label) { fail(L, "a string literal", n); return slot_void(); }
|
||||
local = scratch(L, t, "text");
|
||||
p = fe_ir_addr(L->m, L->b, fe_ir_at_global(label, 0));
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, SLICE_PTR_OFFSET), p,
|
||||
FE_IR_PTR);
|
||||
c = fe_ir_const(L->m, L->b, FE_IR_I32, (long)len);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, SLICE_LEN_OFFSET), c,
|
||||
FE_IR_I32);
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
return slot_value(fe_ir_const(L->m, L->b,
|
||||
it == FE_IR_VOID ? FE_IR_I32 : it,
|
||||
literal_value(n)),
|
||||
it == FE_IR_VOID ? FE_IR_I32 : it);
|
||||
case FE_N_IDENT: {
|
||||
LowerVar *var = find_var(L, n->cname);
|
||||
if (var) {
|
||||
if (var->by_address) {
|
||||
unsigned p = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(var->local, 0));
|
||||
return slot_place(fe_ir_at_temp(p, 0), it, ir_size(t));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(var->local, 0), it, ir_size(t));
|
||||
}
|
||||
if (n->cname)
|
||||
return slot_place(fe_ir_at_global(n->cname, 0), it, ir_size(t));
|
||||
fail(L, "an unresolved name", n);
|
||||
return slot_void();
|
||||
}
|
||||
case FE_N_BINARY: {
|
||||
int is_cmp = 0;
|
||||
FeIrOp op;
|
||||
unsigned a;
|
||||
unsigned b;
|
||||
FeIrType operand;
|
||||
if (n->text && !strcmp(n->text, "orelse")) return lower_lazy(L, n, 0);
|
||||
if (n->text && !strcmp(n->text, "catch")) return lower_lazy(L, n, 1);
|
||||
if (n->text && (!strcmp(n->text, "and") || !strcmp(n->text, "or")))
|
||||
return lower_logical(L, n, !strcmp(n->text, "and"));
|
||||
op = binary_op(n->text, &is_cmp);
|
||||
operand = ir_type(n->a ? n->a->sem_type : 0);
|
||||
if (operand == FE_IR_VOID || operand == FE_IR_MEM) operand = FE_IR_I32;
|
||||
a = as_value(L, lower_expr(L, n->a), n->a);
|
||||
b = as_value(L, lower_expr(L, n->b), n->b);
|
||||
return slot_value(fe_ir_binary(L->m, L->b, op, operand, a, b,
|
||||
type_is_unsigned(n->a ? n->a->sem_type
|
||||
: 0)),
|
||||
is_cmp ? FE_IR_I8 : operand);
|
||||
}
|
||||
case FE_N_UNARY:
|
||||
if (n->text && !strcmp(n->text, "try")) return lower_try(L, n);
|
||||
if (n->text && !strcmp(n->text, "-")) {
|
||||
unsigned zero = fe_ir_const(L->m, L->b, it, 0);
|
||||
unsigned v = as_value(L, lower_expr(L, n->a), n->a);
|
||||
return slot_value(fe_ir_binary(L->m, L->b, FE_IR_SUB, it, zero, v,
|
||||
0), it);
|
||||
}
|
||||
if (n->text && !strcmp(n->text, "not")) {
|
||||
unsigned zero = fe_ir_const(L->m, L->b, FE_IR_I8, 0);
|
||||
unsigned v = as_value(L, lower_expr(L, n->a), n->a);
|
||||
return slot_value(fe_ir_binary(L->m, L->b, FE_IR_EQ, FE_IR_I8, v,
|
||||
zero, 0), FE_IR_I8);
|
||||
}
|
||||
if (n->text && (!strcmp(n->text, "&") || !strcmp(n->text, "&mut"))) {
|
||||
Slot inner = lower_expr(L, n->a);
|
||||
return slot_value(as_address(L, inner, n->a), FE_IR_PTR);
|
||||
}
|
||||
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) {
|
||||
FeVariantType *v = fe_type_variant(t, n->b->text);
|
||||
if (v)
|
||||
return slot_value(fe_ir_const(L->m, L->b, ir_type(t),
|
||||
(long)v->tag), ir_type(t));
|
||||
}
|
||||
/* `error.Name` is a member of the open default set: a code, and
|
||||
nothing to look up. */
|
||||
if (n->a && n->a->kind == FE_N_IDENT && n->a->text &&
|
||||
!strcmp(n->a->text, "error") && n->b && n->b->text)
|
||||
return slot_value(fe_ir_const(L->m, L->b, FE_IR_I16,
|
||||
error_code(L, n->b->text)),
|
||||
FE_IR_I16);
|
||||
/* `.?` is the payload of an optional the checker already proved is
|
||||
there. */
|
||||
if (n->text && !strcmp(n->text, ".?")) {
|
||||
FeType *bt = n->a ? n->a->sem_type : 0;
|
||||
return wrapper_payload(L, lower_expr(L, n->a), bt);
|
||||
}
|
||||
/* `p.^` reads through a pointer -- except for an owned slice, whose
|
||||
pointer and length are the value itself, so there is nothing to
|
||||
step through. */
|
||||
if (n->text && !strcmp(n->text, ".^")) {
|
||||
Slot base = lower_expr(L, n->a);
|
||||
unsigned p;
|
||||
if (base.type == FE_IR_MEM)
|
||||
return slot_place(base.place, it, ir_size(t));
|
||||
p = as_value(L, base, n->a);
|
||||
return slot_place(fe_ir_at_temp(p, 0), it, ir_size(t));
|
||||
}
|
||||
/* `.n` is how many elements there are, which an array knows at
|
||||
compile time and a slice carries beside its pointer. */
|
||||
if (n->b && n->b->text && !strcmp(n->b->text, "n")) {
|
||||
FeType *bt = n->a ? n->a->sem_type : 0;
|
||||
Slot base;
|
||||
if (bt && bt->kind == FE_TYPE_ARRAY)
|
||||
return slot_value(fe_ir_const(L->m, L->b, FE_IR_I32,
|
||||
(long)bt->length), FE_IR_I32);
|
||||
base = lower_expr(L, n->a);
|
||||
if (!base.is_place) { fail(L, "a length of a temporary", n); return slot_void(); }
|
||||
base.place.offset += SLICE_LEN_OFFSET;
|
||||
return slot_place(base.place, FE_IR_I32, 4);
|
||||
}
|
||||
/* A field is a constant offset from the base. */
|
||||
{
|
||||
FeType *base = n->a ? n->a->sem_type : 0;
|
||||
FeFieldType *field;
|
||||
Slot b;
|
||||
if (base && (base->kind == FE_TYPE_REF ||
|
||||
base->kind == FE_TYPE_OWNED)) base = base->elem;
|
||||
field = fe_type_field(base, n->b && n->b->text ? n->b->text : "");
|
||||
if (!field) { fail(L, "an unresolved field", n); return slot_void(); }
|
||||
b = lower_expr(L, n->a);
|
||||
if (n->a->sem_type && (n->a->sem_type->kind == FE_TYPE_REF ||
|
||||
n->a->sem_type->kind == FE_TYPE_OWNED)) {
|
||||
unsigned p = as_value(L, b, n->a);
|
||||
return slot_place(fe_ir_at_temp(p, (long)field->offset), it,
|
||||
ir_size(t));
|
||||
}
|
||||
if (!b.is_place) { fail(L, "a field of a temporary", n); return slot_void(); }
|
||||
b.place.offset += (long)field->offset;
|
||||
return slot_place(b.place, it, ir_size(t));
|
||||
}
|
||||
case FE_N_INDEX: {
|
||||
FeType *bt = n->a ? n->a->sem_type : 0;
|
||||
FeType *elem = bt ? bt->elem : 0;
|
||||
Slot base;
|
||||
unsigned data;
|
||||
unsigned length;
|
||||
unsigned index;
|
||||
unsigned scale;
|
||||
unsigned offset;
|
||||
unsigned addr;
|
||||
if (n->flags & FE_NODE_SLICE) return lower_slice(L, n);
|
||||
base = lower_expr(L, n->a);
|
||||
indexable_parts(L, base, bt, &data, &length, n);
|
||||
index = as_value(L, lower_expr(L, n->b), n->b);
|
||||
if (!L->c->no_checks) {
|
||||
unsigned ok = fe_ir_binary(L->m, L->b, FE_IR_LT, FE_IR_I32,
|
||||
index, length, 1);
|
||||
guard(L, ok, FE_TRAP_BOUNDS, n->loc.line);
|
||||
}
|
||||
scale = fe_ir_const(L->m, L->b, FE_IR_I32, (long)ir_size(elem));
|
||||
offset = fe_ir_binary(L->m, L->b, FE_IR_MUL, FE_IR_I32, index, scale, 1);
|
||||
addr = fe_ir_binary(L->m, L->b, FE_IR_ADD, FE_IR_PTR, data, offset, 1);
|
||||
return slot_place(fe_ir_at_temp(addr, 0), ir_type(elem), ir_size(elem));
|
||||
}
|
||||
case FE_N_ARRAY_INIT: {
|
||||
unsigned local = scratch(L, t, "array");
|
||||
FeType *elem = t ? t->elem : 0;
|
||||
unsigned long step = ir_size(elem);
|
||||
long at = 0;
|
||||
FeNode *x;
|
||||
for (x = n->children; x; x = x->next) {
|
||||
Slot v = lower_expr(L, x);
|
||||
store_into(L, fe_ir_at_local(local, at), v, x, step);
|
||||
at += (long)step;
|
||||
}
|
||||
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");
|
||||
FeNode *f;
|
||||
for (f = n->children; f; f = f->next) {
|
||||
FeFieldType *field;
|
||||
Slot v;
|
||||
if (f->kind != FE_N_FIELD) continue;
|
||||
field = fe_type_field(t, f->text);
|
||||
if (!field) { fail(L, "an unresolved field", f); return slot_void(); }
|
||||
v = lower_expr(L, f->a);
|
||||
store_into(L, fe_ir_at_local(local, (long)field->offset), v, f,
|
||||
ir_size(field->type));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
case FE_N_CALL:
|
||||
return lower_call(L, n);
|
||||
case FE_N_TYPE:
|
||||
/* `x as T`: the operand is `a` and the target type is the node's own.
|
||||
Between integers this only changes how wide the value is and whether
|
||||
the top bits repeat the sign. */
|
||||
if (n->a) {
|
||||
FeType *from = n->a->sem_type;
|
||||
unsigned v = as_value(L, lower_expr(L, n->a), n->a);
|
||||
if (ir_type(from) == it) return slot_value(v, it);
|
||||
return slot_value(fe_ir_cast(L->m, L->b, ir_type(from), it, v,
|
||||
type_is_unsigned(from)), it);
|
||||
}
|
||||
fail(L, "this type expression", n);
|
||||
return slot_void();
|
||||
case FE_N_EXPR:
|
||||
return lower_expr(L, n->a);
|
||||
default:
|
||||
fail(L, "this expression", n);
|
||||
return slot_void();
|
||||
}
|
||||
}
|
||||
|
||||
/* The link name of the `drop` method for this type, found through the instance
|
||||
the checker recorded. */
|
||||
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. */
|
||||
void run_deferred(Lower *L, unsigned from)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = L->owed_count; i > from; --i) {
|
||||
if (L->owed[i - 1].block) {
|
||||
lower_stmt(L, L->owed[i - 1].block);
|
||||
continue;
|
||||
}
|
||||
{
|
||||
/* Release only where the value is still here. */
|
||||
unsigned live = fe_ir_load(L->m, L->b, FE_IR_I8,
|
||||
fe_ir_at_local(L->owed[i - 1].flag, 0));
|
||||
FeIrBlock *doit = new_block(L);
|
||||
FeIrBlock *skip = new_block(L);
|
||||
unsigned args[1];
|
||||
FeType *t = L->owed[i - 1].type;
|
||||
fe_ir_br(L->b, live, doit->id, skip->id);
|
||||
L->b = doit;
|
||||
if (t && t->kind == FE_TYPE_OWNED && t->elem &&
|
||||
t->elem->kind == FE_TYPE_SLICE) {
|
||||
FeIrPlace at = fe_ir_at_local(L->owed[i - 1].local,
|
||||
SLICE_PTR_OFFSET);
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR, at);
|
||||
} else {
|
||||
args[0] = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(L->owed[i - 1].local, 0));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------- wrappers -------- *
|
||||
* An optional is a tag and a payload; an error union is an error code and a
|
||||
* payload, where a code of zero means there is no error. Both are memory, and
|
||||
* both are built the same way: write the tag, then write the value after it.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
Slot wrap_context(Lower *L, Slot v, FeNode *n)
|
||||
{
|
||||
FeType *want = n->sem_context;
|
||||
unsigned local;
|
||||
long payload_at;
|
||||
if (!want) return v;
|
||||
local = scratch(L, want, "wrapped");
|
||||
payload_at = (long)fe_type_payload_offset(want);
|
||||
if (want->kind == FE_TYPE_OPTIONAL) {
|
||||
if (fe_m7_is_null(n)) {
|
||||
/* A payload with a spare representation uses it for "nothing"
|
||||
instead of carrying a separate tag. */
|
||||
unsigned z = fe_ir_const(L->m, L->b,
|
||||
uses_niche(want) ? FE_IR_PTR : FE_IR_I8, 0);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), z,
|
||||
uses_niche(want) ? FE_IR_PTR : FE_IR_I8);
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(want));
|
||||
}
|
||||
if (!uses_niche(want)) {
|
||||
unsigned one = fe_ir_const(L->m, L->b, FE_IR_I8, 1);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), one, FE_IR_I8);
|
||||
}
|
||||
store_into(L, fe_ir_at_local(local, payload_at), v, n,
|
||||
ir_size(want->elem));
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(want));
|
||||
}
|
||||
if (want->kind == FE_TYPE_ERROR_UNION) {
|
||||
FeType *value_type = want->error_value;
|
||||
if (n->sem_type && n->sem_type->is_error) {
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0),
|
||||
as_value(L, v, n), FE_IR_I16);
|
||||
} else {
|
||||
unsigned zero = fe_ir_const(L->m, L->b, FE_IR_I16, 0);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), zero, FE_IR_I16);
|
||||
if (value_type && value_type->kind != FE_TYPE_VOID)
|
||||
store_into(L, fe_ir_at_local(local, payload_at), v, n,
|
||||
ir_size(value_type));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(want));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/* The tag of a wrapper that is already in memory. */
|
||||
unsigned wrapper_tag(Lower *L, Slot w, const FeType *t, FeNode *n)
|
||||
{
|
||||
FeIrPlace p;
|
||||
if (!w.is_place) { fail(L, "a wrapper with no place", n); return 0; }
|
||||
p = w.place;
|
||||
if (uses_niche(t)) return fe_ir_load(L->m, L->b, FE_IR_PTR, p);
|
||||
return fe_ir_load(L->m, L->b, tag_type(t), p);
|
||||
}
|
||||
|
||||
Slot wrapper_payload(Lower *L, Slot w, const FeType *t)
|
||||
{
|
||||
FeType *payload = t ? (t->kind == FE_TYPE_ERROR_UNION ? t->error_value
|
||||
: t->elem) : 0;
|
||||
FeIrPlace p = w.place;
|
||||
(void)L;
|
||||
p.offset += (long)fe_type_payload_offset(t);
|
||||
return slot_place(p, ir_type(payload), ir_size(payload));
|
||||
}
|
||||
|
||||
/* Leave the function with this error code, after the deferred blocks. */
|
||||
void return_error(Lower *L, unsigned err, FeNode *n)
|
||||
{
|
||||
FeType *ret = L->ret_type;
|
||||
unsigned local = scratch(L, ret, "failure");
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(local, 0), err, FE_IR_I16);
|
||||
run_deferred(L, 0);
|
||||
if (L->fn->returns_by_address) {
|
||||
unsigned dst = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(L->ret_local, 0));
|
||||
fe_ir_copy(L->m, L->b, fe_ir_at_temp(dst, 0), fe_ir_at_local(local, 0),
|
||||
ir_size(ret));
|
||||
fe_ir_ret(L->b, 0, 0);
|
||||
return;
|
||||
}
|
||||
fe_ir_ret(L->b, fe_ir_load(L->m, L->b, ir_type(ret),
|
||||
fe_ir_at_local(local, 0)), 1);
|
||||
(void)n;
|
||||
}
|
||||
|
||||
/* `try e` -- if e failed, leave with its error; otherwise the value. */
|
||||
Slot lower_try(Lower *L, FeNode *n)
|
||||
{
|
||||
FeType *t = n->a ? n->a->sem_type : 0;
|
||||
Slot e = lower_expr(L, n->a);
|
||||
unsigned err = wrapper_tag(L, e, t, n);
|
||||
unsigned zero = fe_ir_const(L->m, L->b, FE_IR_I16, 0);
|
||||
unsigned ok = fe_ir_binary(L->m, L->b, FE_IR_EQ, FE_IR_I16, err, zero, 1);
|
||||
FeIrBlock *bad = new_block(L);
|
||||
FeIrBlock *good = new_block(L);
|
||||
fe_ir_br(L->b, ok, good->id, bad->id);
|
||||
L->b = bad;
|
||||
return_error(L, err, n);
|
||||
L->b = good;
|
||||
return wrapper_payload(L, e, t);
|
||||
}
|
||||
|
||||
/* `e orelse d` and `e catch d` both mean "the value, or that instead". The
|
||||
right-hand side is only evaluated when it is needed, so it is a branch. */
|
||||
Slot lower_lazy(Lower *L, FeNode *n, int is_catch)
|
||||
{
|
||||
FeType *t = n->a ? n->a->sem_type : 0;
|
||||
FeType *payload = t ? (is_catch ? t->error_value : t->elem) : 0;
|
||||
Slot e;
|
||||
unsigned tag;
|
||||
unsigned zero;
|
||||
unsigned ok;
|
||||
unsigned result;
|
||||
FeIrBlock *other;
|
||||
FeIrBlock *join;
|
||||
FeIrBlock *have;
|
||||
e = lower_expr(L, n->a);
|
||||
tag = wrapper_tag(L, e, t, n);
|
||||
zero = fe_ir_const(L->m, L->b, is_catch || uses_niche(t) ? FE_IR_PTR
|
||||
: FE_IR_I8, 0);
|
||||
/* An error union is fine when its code is zero; an optional is fine when
|
||||
its tag is not. */
|
||||
ok = fe_ir_binary(L->m, L->b, is_catch ? FE_IR_EQ : FE_IR_NE,
|
||||
is_catch ? FE_IR_I16 : (uses_niche(t) ? FE_IR_PTR
|
||||
: FE_IR_I8),
|
||||
tag, zero, 1);
|
||||
result = scratch(L, payload, "result");
|
||||
have = new_block(L);
|
||||
other = new_block(L);
|
||||
join = new_block(L);
|
||||
fe_ir_br(L->b, ok, have->id, other->id);
|
||||
L->b = have;
|
||||
store_into(L, fe_ir_at_local(result, 0), wrapper_payload(L, e, t), n,
|
||||
ir_size(payload));
|
||||
fe_ir_jmp(L->b, join->id);
|
||||
L->b = other;
|
||||
if (is_catch && n->c) {
|
||||
/* The block form handles the error and must not fall through with a
|
||||
value, so whatever it leaves behind is what the checker allowed. */
|
||||
lower_stmt(L, n->c);
|
||||
} else {
|
||||
Slot d = lower_expr(L, n->b);
|
||||
store_into(L, fe_ir_at_local(result, 0), d, n->b, ir_size(payload));
|
||||
}
|
||||
fe_ir_jmp(L->b, join->id);
|
||||
L->b = join;
|
||||
return slot_place(fe_ir_at_local(result, 0), ir_type(payload),
|
||||
ir_size(payload));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------- statements --- */
|
||||
Reference in New Issue
Block a user