mirror of
https://github.com/ziglang/zig.git
synced 2026-02-09 19:10:48 +00:00
Sema: handle inferred error set tail call
When Sema sees a store_node instruction, it now checks for the possibility of this pattern: %a = ret_ptr %b = store(%a, %c) Where %c is an error union. In such case we need to add to the current function's inferred error set, if any. Coercion from error union to error union will be handled ideally if the operand is comptime known. In such case it does the appropriate unwrapping, then wraps again. In the future, coercion from error union to error union should do the same thing for a runtime value; emitting a runtime branch to check if the value is an error or not. `Value.arrayLen` for structs returns the number of fields. This is so that Liveness can use it for the `vector_init` instruction (soon to be renamed to `aggregate_init`).
This commit is contained in:
parent
f4fa32a632
commit
97019bc56d
@ -521,7 +521,8 @@ pub const Inst = struct {
|
||||
/// Some of the elements may be comptime-known.
|
||||
/// Uses the `ty_pl` field, payload is index of an array of elements, each of which
|
||||
/// is a `Ref`. Length of the array is given by the vector type.
|
||||
/// TODO rename this to `array_init` and make it support array values too.
|
||||
/// TODO rename this to `aggregate_init` and make it support array values and
|
||||
/// struct values too.
|
||||
vector_init,
|
||||
|
||||
/// Communicates an intent to load memory.
|
||||
|
||||
@ -3547,7 +3547,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
|
||||
.code = file.zir,
|
||||
.owner_decl = new_decl,
|
||||
.func = null,
|
||||
.fn_ret_ty = Type.initTag(.void),
|
||||
.fn_ret_ty = Type.void,
|
||||
.owner_func = null,
|
||||
};
|
||||
defer sema.deinit();
|
||||
@ -3628,7 +3628,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
|
||||
.code = zir,
|
||||
.owner_decl = decl,
|
||||
.func = null,
|
||||
.fn_ret_ty = Type.initTag(.void),
|
||||
.fn_ret_ty = Type.void,
|
||||
.owner_func = null,
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
85
src/Sema.zig
85
src/Sema.zig
@ -3187,12 +3187,32 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const zir_tags = sema.code.instructions.items(.tag);
|
||||
const zir_datas = sema.code.instructions.items(.data);
|
||||
const inst_data = zir_datas[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const ptr = sema.resolveInst(extra.lhs);
|
||||
const value = sema.resolveInst(extra.rhs);
|
||||
return sema.storePtr(block, src, ptr, value);
|
||||
const operand = sema.resolveInst(extra.rhs);
|
||||
|
||||
// Check for the possibility of this pattern:
|
||||
// %a = ret_ptr
|
||||
// %b = store(%a, %c)
|
||||
// Where %c is an error union. In such case we need to add to the current function's
|
||||
// inferred error set, if any.
|
||||
if (sema.typeOf(operand).zigTypeTag() == .ErrorUnion and
|
||||
sema.fn_ret_ty.zigTypeTag() == .ErrorUnion)
|
||||
{
|
||||
if (Zir.refToIndex(extra.lhs)) |ptr_index| {
|
||||
if (zir_tags[ptr_index] == .extended and
|
||||
zir_datas[ptr_index].extended.opcode == .ret_ptr)
|
||||
{
|
||||
try sema.addToInferredErrorSet(operand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sema.storePtr(block, src, ptr, operand);
|
||||
}
|
||||
|
||||
fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -10400,6 +10420,23 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
|
||||
return always_noreturn;
|
||||
}
|
||||
|
||||
fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
|
||||
assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
|
||||
|
||||
if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
|
||||
const op_ty = sema.typeOf(uncasted_operand);
|
||||
switch (op_ty.zigTypeTag()) {
|
||||
.ErrorSet => {
|
||||
try payload.data.addErrorSet(sema.gpa, op_ty);
|
||||
},
|
||||
.ErrorUnion => {
|
||||
try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeRet(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -10410,18 +10447,7 @@ fn analyzeRet(
|
||||
// add the error tag to the inferred error set of the in-scope function, so
|
||||
// that the coercion below works correctly.
|
||||
if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
|
||||
if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
|
||||
const op_ty = sema.typeOf(uncasted_operand);
|
||||
switch (op_ty.zigTypeTag()) {
|
||||
.ErrorSet => {
|
||||
try payload.data.addErrorSet(sema.gpa, op_ty);
|
||||
},
|
||||
.ErrorUnion => {
|
||||
try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
try sema.addToInferredErrorSet(uncasted_operand);
|
||||
}
|
||||
const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
|
||||
|
||||
@ -14355,9 +14381,32 @@ fn coerce(
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.ErrorUnion => {
|
||||
// T to E!T or E to E!T
|
||||
return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
|
||||
.ErrorUnion => switch (inst_ty.zigTypeTag()) {
|
||||
.ErrorUnion => {
|
||||
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
|
||||
switch (inst_val.tag()) {
|
||||
.undef => return sema.addConstUndef(dest_ty),
|
||||
.eu_payload => {
|
||||
const payload = try sema.addConstant(
|
||||
inst_ty.errorUnionPayload(),
|
||||
inst_val.castTag(.eu_payload).?.data,
|
||||
);
|
||||
return sema.wrapErrorUnion(block, dest_ty, payload, inst_src);
|
||||
},
|
||||
else => {
|
||||
const error_set = try sema.addConstant(
|
||||
inst_ty.errorUnionSet(),
|
||||
inst_val,
|
||||
);
|
||||
return sema.wrapErrorUnion(block, dest_ty, error_set, inst_src);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {
|
||||
// T to E!T or E to E!T
|
||||
return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
|
||||
},
|
||||
},
|
||||
.Union => switch (inst_ty.zigTypeTag()) {
|
||||
.Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
|
||||
|
||||
@ -3013,7 +3013,7 @@ pub const Type = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts the type is an array or vector.
|
||||
/// Asserts the type is an array or vector or struct.
|
||||
pub fn arrayLen(ty: Type) u64 {
|
||||
return switch (ty.tag()) {
|
||||
.vector => ty.castTag(.vector).?.data.len,
|
||||
@ -3022,6 +3022,7 @@ pub const Type = extern union {
|
||||
.array_u8 => ty.castTag(.array_u8).?.data,
|
||||
.array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
|
||||
.tuple => ty.castTag(.tuple).?.data.types.len,
|
||||
.@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user