authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-09 00:10:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-09 00:10:53-07:00
log97019bc56d27349e0aeb44faa9d3f738887abe7f
tree4c9ceb095fa8885a21a77030108d8cae90173e3e
parentf4fa32a63219917e8fb26f43cbd2d97b17e0aeee

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`).

4 files changed, 73 insertions(+), 22 deletions(-)

src/Air.zig+2-1
......@@ -521,7 +521,8 @@ pub const Inst = struct {
521521 /// Some of the elements may be comptime-known.
522522 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
523523 /// is a `Ref`. Length of the array is given by the vector type.
524 /// TODO rename this to `array_init` and make it support array values too.
524 /// TODO rename this to `aggregate_init` and make it support array values and
525 /// struct values too.
525526 vector_init,
526527
527528 /// Communicates an intent to load memory.
src/Module.zig+2-2
......@@ -3547,7 +3547,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
35473547 .code = file.zir,
35483548 .owner_decl = new_decl,
35493549 .func = null,
3550 .fn_ret_ty = Type.initTag(.void),
3550 .fn_ret_ty = Type.void,
35513551 .owner_func = null,
35523552 };
35533553 defer sema.deinit();
......@@ -3628,7 +3628,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
36283628 .code = zir,
36293629 .owner_decl = decl,
36303630 .func = null,
3631 .fn_ret_ty = Type.initTag(.void),
3631 .fn_ret_ty = Type.void,
36323632 .owner_func = null,
36333633 };
36343634 defer sema.deinit();
src/Sema.zig+67-18
......@@ -3187,12 +3187,32 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
31873187 const tracy = trace(@src());
31883188 defer tracy.end();
31893189
3190 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
3190 const zir_tags = sema.code.instructions.items(.tag);
3191 const zir_datas = sema.code.instructions.items(.data);
3192 const inst_data = zir_datas[inst].pl_node;
31913193 const src = inst_data.src();
31923194 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
31933195 const ptr = sema.resolveInst(extra.lhs);
3194 const value = sema.resolveInst(extra.rhs);
3195 return sema.storePtr(block, src, ptr, value);
3196 const operand = sema.resolveInst(extra.rhs);
3197
3198 // Check for the possibility of this pattern:
3199 // %a = ret_ptr
3200 // %b = store(%a, %c)
3201 // Where %c is an error union. In such case we need to add to the current function's
3202 // inferred error set, if any.
3203 if (sema.typeOf(operand).zigTypeTag() == .ErrorUnion and
3204 sema.fn_ret_ty.zigTypeTag() == .ErrorUnion)
3205 {
3206 if (Zir.refToIndex(extra.lhs)) |ptr_index| {
3207 if (zir_tags[ptr_index] == .extended and
3208 zir_datas[ptr_index].extended.opcode == .ret_ptr)
3209 {
3210 try sema.addToInferredErrorSet(operand);
3211 }
3212 }
3213 }
3214
3215 return sema.storePtr(block, src, ptr, operand);
31963216}
31973217
31983218fn 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
1040010420 return always_noreturn;
1040110421}
1040210422
10423fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
10424 assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
10425
10426 if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
10427 const op_ty = sema.typeOf(uncasted_operand);
10428 switch (op_ty.zigTypeTag()) {
10429 .ErrorSet => {
10430 try payload.data.addErrorSet(sema.gpa, op_ty);
10431 },
10432 .ErrorUnion => {
10433 try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
10434 },
10435 else => {},
10436 }
10437 }
10438}
10439
1040310440fn analyzeRet(
1040410441 sema: *Sema,
1040510442 block: *Block,
......@@ -10410,18 +10447,7 @@ fn analyzeRet(
1041010447 // add the error tag to the inferred error set of the in-scope function, so
1041110448 // that the coercion below works correctly.
1041210449 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
10413 if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
10414 const op_ty = sema.typeOf(uncasted_operand);
10415 switch (op_ty.zigTypeTag()) {
10416 .ErrorSet => {
10417 try payload.data.addErrorSet(sema.gpa, op_ty);
10418 },
10419 .ErrorUnion => {
10420 try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
10421 },
10422 else => {},
10423 }
10424 }
10450 try sema.addToInferredErrorSet(uncasted_operand);
1042510451 }
1042610452 const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
1042710453
......@@ -14355,9 +14381,32 @@ fn coerce(
1435514381 },
1435614382 else => {},
1435714383 },
14358 .ErrorUnion => {
14359 // T to E!T or E to E!T
14360 return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
14384 .ErrorUnion => switch (inst_ty.zigTypeTag()) {
14385 .ErrorUnion => {
14386 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14387 switch (inst_val.tag()) {
14388 .undef => return sema.addConstUndef(dest_ty),
14389 .eu_payload => {
14390 const payload = try sema.addConstant(
14391 inst_ty.errorUnionPayload(),
14392 inst_val.castTag(.eu_payload).?.data,
14393 );
14394 return sema.wrapErrorUnion(block, dest_ty, payload, inst_src);
14395 },
14396 else => {
14397 const error_set = try sema.addConstant(
14398 inst_ty.errorUnionSet(),
14399 inst_val,
14400 );
14401 return sema.wrapErrorUnion(block, dest_ty, error_set, inst_src);
14402 },
14403 }
14404 }
14405 },
14406 else => {
14407 // T to E!T or E to E!T
14408 return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
14409 },
1436114410 },
1436214411 .Union => switch (inst_ty.zigTypeTag()) {
1436314412 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
src/type.zig+2-1
......@@ -3013,7 +3013,7 @@ pub const Type = extern union {
30133013 }
30143014 }
30153015
3016 /// Asserts the type is an array or vector.
3016 /// Asserts the type is an array or vector or struct.
30173017 pub fn arrayLen(ty: Type) u64 {
30183018 return switch (ty.tag()) {
30193019 .vector => ty.castTag(.vector).?.data.len,
......@@ -3022,6 +3022,7 @@ pub const Type = extern union {
30223022 .array_u8 => ty.castTag(.array_u8).?.data,
30233023 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
30243024 .tuple => ty.castTag(.tuple).?.data.types.len,
3025 .@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
30253026
30263027 else => unreachable,
30273028 };