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 {...@@ -521,7 +521,8 @@ pub const Inst = struct {
521 /// Some of the elements may be comptime-known.521 /// Some of the elements may be comptime-known.
522 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which522 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
523 /// is a `Ref`. Length of the array is given by the vector type.523 /// 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.
525 vector_init,526 vector_init,
526527
527 /// Communicates an intent to load memory.528 /// Communicates an intent to load memory.
src/Module.zig+2-2
...@@ -3547,7 +3547,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {...@@ -3547,7 +3547,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
3547 .code = file.zir,3547 .code = file.zir,
3548 .owner_decl = new_decl,3548 .owner_decl = new_decl,
3549 .func = null,3549 .func = null,
3550 .fn_ret_ty = Type.initTag(.void),3550 .fn_ret_ty = Type.void,
3551 .owner_func = null,3551 .owner_func = null,
3552 };3552 };
3553 defer sema.deinit();3553 defer sema.deinit();
...@@ -3628,7 +3628,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3628,7 +3628,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3628 .code = zir,3628 .code = zir,
3629 .owner_decl = decl,3629 .owner_decl = decl,
3630 .func = null,3630 .func = null,
3631 .fn_ret_ty = Type.initTag(.void),3631 .fn_ret_ty = Type.void,
3632 .owner_func = null,3632 .owner_func = null,
3633 };3633 };
3634 defer sema.deinit();3634 defer sema.deinit();
src/Sema.zig+67-18
...@@ -3187,12 +3187,32 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v...@@ -3187,12 +3187,32 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
3187 const tracy = trace(@src());3187 const tracy = trace(@src());
3188 defer tracy.end();3188 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;
3191 const src = inst_data.src();3193 const src = inst_data.src();
3192 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;3194 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
3193 const ptr = sema.resolveInst(extra.lhs);3195 const ptr = sema.resolveInst(extra.lhs);
3194 const value = sema.resolveInst(extra.rhs);3196 const operand = sema.resolveInst(extra.rhs);
3195 return sema.storePtr(block, src, ptr, value);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);
3196}3216}
31973217
3198fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {3218fn 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...@@ -10400,6 +10420,23 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
10400 return always_noreturn;10420 return always_noreturn;
10401}10421}
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
10403fn analyzeRet(10440fn analyzeRet(
10404 sema: *Sema,10441 sema: *Sema,
10405 block: *Block,10442 block: *Block,
...@@ -10410,18 +10447,7 @@ fn analyzeRet(...@@ -10410,18 +10447,7 @@ fn analyzeRet(
10410 // add the error tag to the inferred error set of the in-scope function, so10447 // add the error tag to the inferred error set of the in-scope function, so
10411 // that the coercion below works correctly.10448 // that the coercion below works correctly.
10412 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {10449 if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
10413 if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {10450 try sema.addToInferredErrorSet(uncasted_operand);
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 }
10425 }10451 }
10426 const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);10452 const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
1042710453
...@@ -14355,9 +14381,32 @@ fn coerce(...@@ -14355,9 +14381,32 @@ fn coerce(
14355 },14381 },
14356 else => {},14382 else => {},
14357 },14383 },
14358 .ErrorUnion => {14384 .ErrorUnion => switch (inst_ty.zigTypeTag()) {
14359 // T to E!T or E to E!T14385 .ErrorUnion => {
14360 return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);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 },
14361 },14410 },
14362 .Union => switch (inst_ty.zigTypeTag()) {14411 .Union => switch (inst_ty.zigTypeTag()) {
14363 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),14412 .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 {...@@ -3013,7 +3013,7 @@ pub const Type = extern union {
3013 }3013 }
3014 }3014 }
30153015
3016 /// Asserts the type is an array or vector.3016 /// Asserts the type is an array or vector or struct.
3017 pub fn arrayLen(ty: Type) u64 {3017 pub fn arrayLen(ty: Type) u64 {
3018 return switch (ty.tag()) {3018 return switch (ty.tag()) {
3019 .vector => ty.castTag(.vector).?.data.len,3019 .vector => ty.castTag(.vector).?.data.len,
...@@ -3022,6 +3022,7 @@ pub const Type = extern union {...@@ -3022,6 +3022,7 @@ pub const Type = extern union {
3022 .array_u8 => ty.castTag(.array_u8).?.data,3022 .array_u8 => ty.castTag(.array_u8).?.data,
3023 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,3023 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
3024 .tuple => ty.castTag(.tuple).?.data.types.len,3024 .tuple => ty.castTag(.tuple).?.data.types.len,
3025 .@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
30253026
3026 else => unreachable,3027 else => unreachable,
3027 };3028 };