From 09a57583a4ccce30603ffc8d0e3d7359dc3cb978 Mon Sep 17 00:00:00 2001 From: mlugg Date: Mon, 18 Sep 2023 14:49:18 +0100 Subject: [PATCH] compiler: preserve result type information through address-of operator This commit introduces the new `ref_coerced_ty` result type into AstGen. This represents a expression which we want to treat as an lvalue, and the pointer will be coerced to a given type. This change gives known result types to many expressions, in particular struct and array initializations. This allows certain casts to work which previously required explicitly specifying types via `@as`. It also eliminates our dependence on anonymous struct types for expressions of the form `&.{ ... }` - this paves the way for #16865, and also results in less Sema magic happening for such initializations, also leading to potentially better runtime code. As part of these changes, this commit also implements #17194 by disallowing RLS on explicitly-typed struct and array initializations. Apologies for linking these changes - it seemed rather pointless to try and separate them, since they both make big changes to struct and array initializations in AstGen. The rationale for this change can be found in the proposal - in essence, performing RLS whilst maintaining the semantics of the intermediary type is a very difficult problem to solve. This allowed the problematic `coerce_result_ptr` ZIR instruction to be completely eliminated, which in turn also simplified the logic for inferred allocations in Sema - thanks to this, we almost break even on line count! In doing this, the ZIR instructions surrounding these initializations have been restructured - some have been added and removed, and others renamed for clarity (and their semantics changed slightly). In order to optimize ZIR tag count, the `struct_init_anon_ref` and `array_init_anon_ref` instructions have been removed in favour of using `ref` on a standard anonymous value initialization, since these instructions are now virtually never used. Lastly, it's worth noting that this commit introduces a slightly strange source of generic poison types: in the expression `@as(*anyopaque, &x)`, the sub-expression `x` has a generic poison result type, despite no generic code being involved. This turns out to be a logical choice, because we don't know the result type for `x`, and the generic poison type represents precisely this case, providing the semantics we need. Resolves: #16512 Resolves: #17194 --- lib/std/debug.zig | 7 +- src/AstGen.zig | 631 ++++++------ src/AstRlAnnotate.zig | 44 +- src/Autodoc.zig | 37 +- src/Sema.zig | 931 +++++++++--------- src/Zir.zig | 331 ++++--- src/print_zir.zig | 80 +- src/type.zig | 11 + test/behavior/array.zig | 34 + test/behavior/cast.zig | 26 + test/behavior/pointers.zig | 18 + test/behavior/struct.zig | 15 + .../anytype_param_requires_comptime.zig | 6 +- ...th_a_function_that_returns_an_optional.zig | 6 +- ...t_result_type_due_to_anyopaque_pointer.zig | 21 + ...t_result_type_due_to_generic_parameter.zig | 16 +- .../compile_errors/for_invalid_ranges.zig | 3 +- .../invalid_store_to_comptime_field.zig | 9 +- ..._const_in_slice_with_nested_array_type.zig | 2 +- .../compile_errors/missing_else_clause.zig | 4 +- ..._when_coercing_pointer_to_anon_literal.zig | 6 +- .../reassign_to_array_parameter.zig | 2 +- .../reassign_to_struct_parameter.zig | 2 +- ...when_assigning_a_value_within_a_struct.zig | 6 +- ...ompatibility_mismatching_handle_is_ptr.zig | 6 +- ...mismatching_handle_is_ptr_generic_call.zig | 6 +- .../return_incompatible_generic_struct.zig | 1 + ...ime_assignment_to_comptime_struct_type.zig | 4 +- ...time_assignment_to_comptime_union_type.zig | 4 +- ...shift_amount_has_to_be_an_integer_type.zig | 3 +- .../slice_sentinel_mismatch-1.zig | 13 +- ...nion_init_with_none_or_multiple_fields.zig | 1 - .../union_noreturn_field_initialized.zig | 2 +- .../wrong_types_given_to_export.zig | 2 +- test/compile_errors.zig | 6 +- 35 files changed, 1261 insertions(+), 1035 deletions(-) create mode 100644 test/cases/compile_errors/cast_without_result_type_due_to_anyopaque_pointer.zig diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6de21ddd1b..38a4631711 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -514,7 +514,12 @@ pub const StackIterator = struct { return StackIterator{ .first_address = first_address, - .fp = fp orelse @frameAddress(), + // TODO: this is a workaround for #16876 + //.fp = fp orelse @frameAddress(), + .fp = fp orelse blk: { + const fa = @frameAddress(); + break :blk fa; + }, }; } diff --git a/src/AstGen.zig b/src/AstGen.zig index 07f3c54cc3..e29457bb46 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -265,14 +265,17 @@ const ResultInfo = struct { discard, /// The expression has an inferred type, and it will be evaluated as an rvalue. none, - /// The expression must generate a pointer rather than a value. For example, the left hand side - /// of an assignment uses this kind of result location. - ref, /// The expression will be coerced into this type, but it will be evaluated as an rvalue. ty: Zir.Inst.Ref, /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, /// so no `as` instruction needs to be emitted. coerced_ty: Zir.Inst.Ref, + /// The expression must generate a pointer rather than a value. For example, the left hand side + /// of an assignment uses this kind of result location. + ref, + /// The expression must generate a pointer rather than a value, and the pointer will be coerced + /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type. + ref_coerced_ty: Zir.Inst.Ref, /// The expression must store its result into this typed pointer. The result instruction /// from the expression must be ignored. ptr: PtrResultLoc, @@ -303,26 +306,30 @@ const ResultInfo = struct { /// Find the result type for a cast builtin given the result location. /// If the location does not have a known result type, emits an error on /// the given node. - fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref { - const astgen = gz.astgen; - switch (rl) { - .discard, .none, .ref, .inferred_ptr => {}, - .ty, .coerced_ty => |ty_ref| return ty_ref, + fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref { + return switch (rl) { + .discard, .none, .ref, .inferred_ptr, .destructure => null, + .ty, .coerced_ty => |ty_ref| ty_ref, + .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node), .ptr => |ptr| { const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); - return gz.addUnNode(.elem_type, ptr_ty, node); + return try gz.addUnNode(.elem_type, ptr_ty, node); }, - .destructure => |destructure| { - return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ - try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), - try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), - }); - }, - } + }; + } - return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ - try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), - }); + fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref { + const astgen = gz.astgen; + if (try rl.resultType(gz, node)) |ty| return ty; + switch (rl) { + .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ + try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}), + try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), + }), + else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ + try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}), + }), + } } }; @@ -933,7 +940,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs); _ = try gz.addUnNode(.validate_deref, lhs, node); switch (ri.rl) { - .ref => return lhs, + .ref, .ref_coerced_ty => return lhs, else => { const result = try gz.addUnNode(.load, lhs, node); return rvalue(gz, ri, result, node); @@ -941,7 +948,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE } }, .address_of => { - const result = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); + const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: { + _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node)); + break :rl .{ .ref_coerced_ty = res_ty_inst }; + } else .ref; + const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs); return rvalue(gz, ri, result, node); }, .optional_type => { @@ -950,7 +961,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .unwrap_optional => switch (ri.rl) { - .ref => { + .ref, .ref_coerced_ty => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); @@ -1001,7 +1012,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE else null; switch (ri.rl) { - .ref => return orelseCatchExpr( + .ref, .ref_coerced_ty => return orelseCatchExpr( gz, scope, ri, @@ -1028,7 +1039,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE } }, .@"orelse" => switch (ri.rl) { - .ref => return orelseCatchExpr( + .ref, .ref_coerced_ty => return orelseCatchExpr( gz, scope, ri, @@ -1432,73 +1443,75 @@ fn arrayInitExpr( break :inst .{ array_type_inst, .none }; }; + if (array_ty != .none) { + // Typed inits do not use RLS for language simplicity. + switch (ri.rl) { + .discard => { + if (elem_ty != .none) { + const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; + for (array_init.ast.elements) |elem_init| { + _ = try expr(gz, scope, elem_ri, elem_init); + } + } else { + for (array_init.ast.elements, 0..) |elem_init, i| { + const this_elem_ty = try gz.add(.{ + .tag = .array_init_elem_type, + .data = .{ .bin = .{ + .lhs = array_ty, + .rhs = @enumFromInt(i), + } }, + }); + _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); + } + } + return .void_value; + }, + .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true), + else => { + const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false); + return rvalue(gz, ri, array_inst, node); + }, + } + } + switch (ri.rl) { + .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements), .discard => { - if (elem_ty != .none) { - const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } }; - for (array_init.ast.elements) |elem_init| { - _ = try expr(gz, scope, elem_ri, elem_init); - } - } else if (array_ty != .none) { - for (array_init.ast.elements, 0..) |elem_init, i| { - const this_elem_ty = try gz.add(.{ - .tag = .elem_type_index, - .data = .{ .bin = .{ - .lhs = array_ty, - .rhs = @enumFromInt(i), - } }, - }); - _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init); - } - } else { - for (array_init.ast.elements) |elem_init| { - _ = try expr(gz, scope, .{ .rl = .discard }, elem_init); - } + for (array_init.ast.elements) |elem_init| { + _ = try expr(gz, scope, .{ .rl = .discard }, elem_init); } return Zir.Inst.Ref.void_value; }, .ref => { - const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init_ref else .array_init_anon_ref; - return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag); + const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); + return gz.addUnTok(.ref, result, tree.firstToken(node)); }, - .none => { - const tag: Zir.Inst.Tag = if (array_ty != .none) .array_init else .array_init_anon; - return arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, tag); + .ref_coerced_ty => |ptr_ty_inst| { + const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{ + .ptr_ty = ptr_ty_inst, + .elem_count = @intCast(array_init.ast.elements.len), + }); + return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true); }, - .ty, .coerced_ty => |ty_inst| { - const arr_ty = if (array_ty != .none) array_ty else blk: { - const arr_ty = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node); - _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{ - .ty = arr_ty, - .init_count = @intCast(array_init.ast.elements.len), - }); - break :blk arr_ty; - }; - const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, elem_ty, .array_init); + .ty, .coerced_ty => |result_ty_inst| { + _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{ + .ty = result_ty_inst, + .init_count = @intCast(array_init.ast.elements.len), + }); + return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false); + }, + .ptr => |ptr| { + try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst); + return .void_value; + }, + .inferred_ptr => { + // We can't get elem pointers of an untyped inferred alloc, so must perform a + // standard anonymous initialization followed by an rvalue store. + // See corresponding logic in structInitExpr. + const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements); return rvalue(gz, ri, result, node); }, - .ptr => |ptr_res| { - return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, array_ty); - }, - .inferred_ptr => |ptr_inst| { - if (array_ty == .none) { - // We treat this case differently so that we don't get a crash when - // analyzing array_base_ptr against an alloc_inferred_mut. - // See corresponding logic in structInitExpr. - const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); - return rvalue(gz, ri, result, node); - } else { - return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, array_ty); - } - }, .destructure => |destructure| { - if (array_ty != .none) { - // We have a specific type, so there may be things like default - // field values messing with us. Do this as a standard typed - // init followed by an rvalue destructure. - const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, .array_init); - return rvalue(gz, ri, result, node); - } // Untyped init - destructure directly into result pointers if (array_init.ast.elements.len != destructure.components.len) { return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{ @@ -1521,12 +1534,12 @@ fn arrayInitExpr( } } -fn arrayInitExprRlNone( +/// An array initialization expression using an `array_init_anon` instruction. +fn arrayInitExprAnon( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, elements: []const Ast.Node.Index, - tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; @@ -1540,95 +1553,84 @@ fn arrayInitExprRlNone( astgen.extra.items[extra_index] = @intFromEnum(elem_ref); extra_index += 1; } - return try gz.addPlNodePayloadIndex(tag, node, payload_index); + return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index); } -fn arrayInitExprInner( +/// An array initialization expression using an `array_init` or `array_init_ref` instruction. +fn arrayInitExprTyped( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, elements: []const Ast.Node.Index, - array_ty_inst: Zir.Inst.Ref, - elem_ty: Zir.Inst.Ref, - tag: Zir.Inst.Tag, + ty_inst: Zir.Inst.Ref, + maybe_elem_ty_inst: Zir.Inst.Ref, + is_ref: bool, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; - const len = elements.len + @intFromBool(array_ty_inst != .none); + const len = elements.len + 1; // +1 for type const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{ .operands_len = @intCast(len), }); var extra_index = try reserveExtra(astgen, len); - if (array_ty_inst != .none) { - astgen.extra.items[extra_index] = @intFromEnum(array_ty_inst); - extra_index += 1; - } + astgen.extra.items[extra_index] = @intFromEnum(ty_inst); + extra_index += 1; - for (elements, 0..) |elem_init, i| { - const ri = if (elem_ty != .none) - ResultInfo{ .rl = .{ .coerced_ty = elem_ty } } - else if (array_ty_inst != .none) ri: { - const ty_expr = try gz.add(.{ - .tag = .elem_type_index, + if (maybe_elem_ty_inst != .none) { + const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } }; + for (elements) |elem_init| { + const elem_inst = try expr(gz, scope, elem_ri, elem_init); + astgen.extra.items[extra_index] = @intFromEnum(elem_inst); + extra_index += 1; + } + } else { + for (elements, 0..) |elem_init, i| { + const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{ + .tag = .array_init_elem_type, .data = .{ .bin = .{ - .lhs = array_ty_inst, + .lhs = ty_inst, .rhs = @enumFromInt(i), } }, - }); - break :ri ResultInfo{ .rl = .{ .coerced_ty = ty_expr } }; - } else ResultInfo{ .rl = .{ .none = {} } }; + }) } }; - const elem_ref = try expr(gz, scope, ri, elem_init); - astgen.extra.items[extra_index] = @intFromEnum(elem_ref); - extra_index += 1; + const elem_inst = try expr(gz, scope, ri, elem_init); + astgen.extra.items[extra_index] = @intFromEnum(elem_inst); + extra_index += 1; + } } + const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init; return try gz.addPlNodePayloadIndex(tag, node, payload_index); } -fn arrayInitExprRlPtr( +/// An array initialization expression using element pointers. +fn arrayInitExprPtr( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, - result_ptr: Zir.Inst.Ref, elements: []const Ast.Node.Index, - array_ty: Zir.Inst.Ref, -) InnerError!Zir.Inst.Ref { - if (array_ty == .none) { - const base_ptr = try gz.addUnNode(.array_base_ptr, result_ptr, node); - return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements); - } - - const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = array_ty, .rhs = result_ptr }); - return arrayInitExprRlPtrInner(gz, scope, node, casted_ptr, elements); -} - -fn arrayInitExprRlPtrInner( - gz: *GenZir, - scope: *Scope, - node: Ast.Node.Index, - result_ptr: Zir.Inst.Ref, - elements: []const Ast.Node.Index, -) InnerError!Zir.Inst.Ref { + ptr_inst: Zir.Inst.Ref, +) InnerError!void { const astgen = gz.astgen; + const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); + const payload_index = try addExtra(astgen, Zir.Inst.Block{ .body_len = @intCast(elements.len), }); var extra_index = try reserveExtra(astgen, elements.len); for (elements, 0..) |elem_init, i| { - const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{ - .ptr = result_ptr, + const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{ + .ptr = array_ptr_inst, .index = @intCast(i), }); - astgen.extra.items[extra_index] = refToIndex(elem_ptr).?; + astgen.extra.items[extra_index] = refToIndex(elem_ptr_inst).?; extra_index += 1; - _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr } } }, elem_init); + _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init); } - _ = try gz.addPlNodePayloadIndex(.validate_array_init, node, payload_index); - return .void_value; + _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index); } fn structInitExpr( @@ -1643,7 +1645,26 @@ fn structInitExpr( if (struct_init.ast.type_expr == 0) { if (struct_init.ast.fields.len == 0) { - return rvalue(gz, ri, .empty_struct, node); + // Anonymous init with no fields. + switch (ri.rl) { + .discard => return .void_value, + .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node), + .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node), + .ptr => { + // TODO: should we modify this to use RLS for the field stores here? + const ty_inst = (try ri.rl.resultType(gz, node)).?; + const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node); + return rvalue(gz, ri, val, node); + }, + .none, .ref, .inferred_ptr => { + return rvalue(gz, ri, .empty_struct, node); + }, + .destructure => |destructure| { + return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{ + try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), + }); + }, + } } } else array: { const node_tags = tree.nodes.items(.tag); @@ -1694,86 +1715,67 @@ fn structInitExpr( } } + if (struct_init.ast.type_expr != 0) { + // Typed inits do not use RLS for language simplicity. + const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); + _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); + switch (ri.rl) { + .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true), + else => { + const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false); + return rvalue(gz, ri, struct_inst, node); + }, + } + } + switch (ri.rl) { + .none => return structInitExprAnon(gz, scope, node, struct_init), .discard => { - if (struct_init.ast.type_expr != 0) { - const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - _ = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); - } else { - _ = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); - } - return Zir.Inst.Ref.void_value; + // Even if discarding we must perform an anonymous init to check for duplicate field names. + // TODO: should duplicate field names be caught in AstGen? + _ = try structInitExprAnon(gz, scope, node, struct_init); + return .void_value; }, .ref => { - if (struct_init.ast.type_expr != 0) { - const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref); - } else { - return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref); - } + const result = try structInitExprAnon(gz, scope, node, struct_init); + return gz.addUnTok(.ref, result, tree.firstToken(node)); }, - .none => { - if (struct_init.ast.type_expr != 0) { - const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); - } else { - return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); - } + .ref_coerced_ty => |ptr_ty_inst| { + const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node); + _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); + return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true); }, - .ty, .coerced_ty => |ty_inst| { - if (struct_init.ast.type_expr == 0) { - const struct_ty_inst = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node); - _ = try gz.addUnNode(.validate_struct_init_ty, struct_ty_inst, node); - const result = try structInitExprRlTy(gz, scope, node, struct_init, struct_ty_inst, .struct_init); - return rvalue(gz, ri, result, node); - } - const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node); - const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); - return rvalue(gz, ri, result, node); + .ty, .coerced_ty => |result_ty_inst| { + _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node); + return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false); }, - .ptr => |ptr_res| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_res.inst), - .inferred_ptr => |ptr_inst| { - if (struct_init.ast.type_expr == 0) { - // We treat this case differently so that we don't get a crash when - // analyzing field_base_ptr against an alloc_inferred_mut. - // See corresponding logic in arrayInitExpr. - const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); - return rvalue(gz, ri, result, node); - } else { - return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst); - } + .ptr => |ptr| { + try structInitExprPtr(gz, scope, node, struct_init, ptr.inst); + return .void_value; + }, + .inferred_ptr => { + // We can't get field pointers of an untyped inferred alloc, so must perform a + // standard anonymous initialization followed by an rvalue store. + // See corresponding logic in arrayInitExpr. + const struct_inst = try structInitExprAnon(gz, scope, node, struct_init); + return rvalue(gz, ri, struct_inst, node); }, .destructure => |destructure| { - if (struct_init.ast.type_expr == 0) { - // This is an untyped init, so is an actual struct, which does - // not support destructuring. - return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ - try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), - }); - } - // You can init tuples using struct init syntax and numeric field - // names, but as with array inits, we could be bitten by default - // fields. Therefore, we do a normal typed init then an rvalue - // destructure. - const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - const result = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init); - return rvalue(gz, ri, result, node); + // This is an untyped init, so is an actual struct, which does + // not support destructuring. + return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{ + try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}), + }); }, } } -fn structInitExprRlNone( +/// A struct initialization expression using a `struct_init_anon` instruction. +fn structInitExprAnon( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, struct_init: Ast.full.StructInit, - ty_inst: Zir.Inst.Ref, - tag: Zir.Inst.Tag, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const tree = astgen.tree; @@ -1787,79 +1789,24 @@ fn structInitExprRlNone( for (struct_init.ast.fields) |field_init| { const name_token = tree.firstToken(field_init) - 2; const str_index = try astgen.identAsString(name_token); - const sub_ri: ResultInfo = if (ty_inst != .none) - ResultInfo{ .rl = .{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ - .container_type = ty_inst, - .name_start = str_index, - }) } } - else - .{ .rl = .none }; setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ .field_name = str_index, - .init = try expr(gz, scope, sub_ri, field_init), + .init = try expr(gz, scope, .{ .rl = .none }, field_init), }); extra_index += field_size; } - return try gz.addPlNodePayloadIndex(tag, node, payload_index); + return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index); } -fn structInitExprRlPtr( - gz: *GenZir, - scope: *Scope, - node: Ast.Node.Index, - struct_init: Ast.full.StructInit, - result_ptr: Zir.Inst.Ref, -) InnerError!Zir.Inst.Ref { - if (struct_init.ast.type_expr == 0) { - const base_ptr = try gz.addUnNode(.field_base_ptr, result_ptr, node); - return structInitExprRlPtrInner(gz, scope, node, struct_init, base_ptr); - } - const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); - _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); - - const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = ty_inst, .rhs = result_ptr }); - return structInitExprRlPtrInner(gz, scope, node, struct_init, casted_ptr); -} - -fn structInitExprRlPtrInner( - gz: *GenZir, - scope: *Scope, - node: Ast.Node.Index, - struct_init: Ast.full.StructInit, - result_ptr: Zir.Inst.Ref, -) InnerError!Zir.Inst.Ref { - const astgen = gz.astgen; - const tree = astgen.tree; - - const payload_index = try addExtra(astgen, Zir.Inst.Block{ - .body_len = @intCast(struct_init.ast.fields.len), - }); - var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len); - - for (struct_init.ast.fields) |field_init| { - const name_token = tree.firstToken(field_init) - 2; - const str_index = try astgen.identAsString(name_token); - const field_ptr = try gz.addPlNode(.field_ptr_init, field_init, Zir.Inst.Field{ - .lhs = result_ptr, - .field_name_start = str_index, - }); - astgen.extra.items[extra_index] = refToIndex(field_ptr).?; - extra_index += 1; - _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init); - } - - _ = try gz.addPlNodePayloadIndex(.validate_struct_init, node, payload_index); - return Zir.Inst.Ref.void_value; -} - -fn structInitExprRlTy( +/// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction. +fn structInitExprTyped( gz: *GenZir, scope: *Scope, node: Ast.Node.Index, struct_init: Ast.full.StructInit, ty_inst: Zir.Inst.Ref, - tag: Zir.Inst.Tag, + is_ref: bool, ) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const tree = astgen.tree; @@ -1873,18 +1820,52 @@ fn structInitExprRlTy( for (struct_init.ast.fields) |field_init| { const name_token = tree.firstToken(field_init) - 2; const str_index = try astgen.identAsString(name_token); - const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{ + const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{ .container_type = ty_inst, .name_start = str_index, }); setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{ .field_type = refToIndex(field_ty_inst).?, - .init = try expr(gz, scope, .{ .rl = .{ .ty = field_ty_inst } }, field_init), + .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init), }); extra_index += field_size; } - return try gz.addPlNodePayloadIndex(tag, node, payload_index); + const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init; + return gz.addPlNodePayloadIndex(tag, node, payload_index); +} + +/// A struct initialization expression using field pointers. +fn structInitExprPtr( + gz: *GenZir, + scope: *Scope, + node: Ast.Node.Index, + struct_init: Ast.full.StructInit, + ptr_inst: Zir.Inst.Ref, +) InnerError!void { + const astgen = gz.astgen; + const tree = astgen.tree; + + const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node); + + const payload_index = try addExtra(astgen, Zir.Inst.Block{ + .body_len = @intCast(struct_init.ast.fields.len), + }); + var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len); + + for (struct_init.ast.fields) |field_init| { + const name_token = tree.firstToken(field_init) - 2; + const str_index = try astgen.identAsString(name_token); + const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{ + .lhs = struct_ptr_inst, + .field_name_start = str_index, + }); + astgen.extra.items[extra_index] = refToIndex(field_ptr).?; + extra_index += 1; + _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init); + } + + _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index); } /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if @@ -2314,7 +2295,7 @@ fn labeledBlockExpr( const need_rl = astgen.nodes_need_rl.contains(block_node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(gz, block_node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -2504,7 +2485,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .array_mul, .array_type, .array_type_sentinel, - .elem_type_index, .elem_type, .indexable_ptr_elem_type, .vector_elem_type, @@ -2531,7 +2511,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .cmp_gte, .cmp_gt, .cmp_neq, - .coerce_result_ptr, .decl_ref, .decl_val, .load, @@ -2539,11 +2518,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .elem_ptr, .elem_val, .elem_ptr_node, - .elem_ptr_imm, .elem_val_node, .elem_val_imm, .field_ptr, - .field_ptr_init, .field_val, .field_ptr_named, .field_val_named, @@ -2599,17 +2576,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .import, .switch_block, .switch_block_ref, - .struct_init_empty, - .struct_init, - .struct_init_ref, - .struct_init_anon, - .struct_init_anon_ref, - .array_init, - .array_init_anon, - .array_init_ref, - .array_init_anon_ref, .union_init, - .field_type, .field_type_ref, .error_set_decl, .error_set_decl_anon, @@ -2680,14 +2647,27 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .@"await", .ret_err_value_code, .closure_get, - .array_base_ptr, - .field_base_ptr, .ret_ptr, .ret_type, .for_len, .@"try", .try_ptr, - .opt_eu_base_ty, + .opt_eu_base_ptr_init, + .coerce_ptr_elem_ty, + .struct_init_empty, + .struct_init_empty_result, + .struct_init_empty_ref_result, + .struct_init_anon, + .struct_init, + .struct_init_ref, + .struct_init_field_type, + .struct_init_field_ptr, + .array_init_anon, + .array_init, + .array_init_ref, + .validate_array_init_ref_ty, + .array_init_elem_type, + .array_init_elem_ptr, => break :b false, .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) { @@ -2738,18 +2718,21 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .store_node, .store_to_inferred_ptr, .resolve_inferred_alloc, - .validate_struct_init, - .validate_array_init, .set_runtime_safety, .closure_capture, .memcpy, .memset, - .validate_array_init_ty, - .validate_struct_init_ty, .validate_deref, .validate_destructure, .save_err_ret_index, .restore_err_ret_index, + .validate_struct_init_ty, + .validate_struct_init_result_ty, + .validate_ptr_struct_init, + .validate_array_init_ty, + .validate_array_init_result_ty, + .validate_ptr_array_init, + .validate_ref_ty, => break :b true, .@"defer" => unreachable, @@ -5635,7 +5618,7 @@ fn tryExpr( const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; const operand_ri: ResultInfo = switch (ri.rl) { - .ref => .{ .rl = .ref, .ctx = .error_handling_expr }, + .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = .error_handling_expr }, else => .{ .rl = .none, .ctx = .error_handling_expr }, }; // This could be a pointer or value depending on the `ri` parameter. @@ -5648,7 +5631,7 @@ fn tryExpr( defer else_scope.unstack(); const err_tag = switch (ri.rl) { - .ref => Zir.Inst.Tag.err_union_code_ptr, + .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr, else => Zir.Inst.Tag.err_union_code, }; const err_code = try else_scope.addUnNode(err_tag, operand, node); @@ -5659,7 +5642,7 @@ fn tryExpr( try else_scope.setTryBody(try_inst, operand); const result = indexToRef(try_inst); switch (ri.rl) { - .ref => return result, + .ref, .ref_coerced_ty => return result, else => return rvalue(parent_gz, ri, result, node), } } @@ -5682,7 +5665,7 @@ fn orelseCatchExpr( const need_rl = astgen.nodes_need_rl.contains(node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -5700,7 +5683,7 @@ fn orelseCatchExpr( defer block_scope.unstack(); const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { - .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, + .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, }; // This could be a pointer or value depending on the `operand_ri` parameter. @@ -5722,7 +5705,7 @@ fn orelseCatchExpr( // This could be a pointer or value depending on `unwrap_op`. const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); const then_result = switch (ri.rl) { - .ref => unwrapped_payload, + .ref, .ref_coerced_ty => unwrapped_payload, else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), }; _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); @@ -5793,7 +5776,7 @@ fn fieldAccess( node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { switch (ri.rl) { - .ref => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), + .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node), else => { const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node); return rvalue(gz, ri, access, node); @@ -5837,7 +5820,7 @@ fn arrayAccess( const tree = gz.astgen.tree; const node_datas = tree.nodes.items(.data); switch (ri.rl) { - .ref => { + .ref, .ref_coerced_ty => { const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); @@ -5951,7 +5934,7 @@ fn ifExpr( const need_rl = astgen.nodes_need_rl.contains(node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -6181,7 +6164,7 @@ fn whileExpr( const need_rl = astgen.nodes_need_rl.contains(node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -6455,7 +6438,7 @@ fn forExpr( const need_rl = astgen.nodes_need_rl.contains(node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -6773,7 +6756,7 @@ fn switchExpr( const need_rl = astgen.nodes_need_rl.contains(switch_node); const block_ri: ResultInfo = if (need_rl) ri else .{ .rl = switch (ri.rl) { - .ptr => .{ .ty = try ri.rl.resultType(parent_gz, switch_node, undefined) }, + .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, switch_node)).? }, .inferred_ptr => .none, else => ri.rl, }, @@ -7465,7 +7448,7 @@ fn localVarRef( gpa, ); - return rvalue(gz, ri, value_inst, ident); + return rvalueNoCoercePreRef(gz, ri, value_inst, ident); } s = local_val.parent; }, @@ -7498,10 +7481,10 @@ fn localVarRef( ); switch (ri.rl) { - .ref => return ptr_inst, + .ref, .ref_coerced_ty => return ptr_inst, else => { const loaded = try gz.addUnNode(.load, ptr_inst, ident); - return rvalue(gz, ri, loaded, ident); + return rvalueNoCoercePreRef(gz, ri, loaded, ident); }, } } @@ -7535,10 +7518,10 @@ fn localVarRef( // Decl references happen by name rather than ZIR index so that when unrelated // decls are modified, ZIR code containing references to them can be unmodified. switch (ri.rl) { - .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token), + .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token), else => { const result = try gz.addStrTok(.decl_val, name_str_index, ident_token); - return rvalue(gz, ri, result, ident); + return rvalueNoCoercePreRef(gz, ri, result, ident); }, } } @@ -7924,7 +7907,7 @@ fn bitCast( node: Ast.Node.Index, operand_node: Ast.Node.Index, ) InnerError!Zir.Inst.Ref { - const dest_type = try ri.rl.resultType(gz, node, "@bitCast"); + const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast"); const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node); const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{ .lhs = dest_type, @@ -8024,7 +8007,7 @@ fn ptrCast( // Full cast including result type const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node); - const result_type = try ri.rl.resultType(gz, root_node, flags.needResultTypeBuiltinName()); + const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName()); const operand = try expr(gz, scope, .{ .rl = .none }, node); try emitDbgStmt(gz, cursor); const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_i, Zir.Inst.BinNode{ @@ -8208,7 +8191,7 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .field => { - if (ri.rl == .ref) { + if (ri.rl == .ref or ri.rl == .ref_coerced_ty) { return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]), .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .slice_const_u8_type } }, params[1]), @@ -8475,7 +8458,7 @@ fn builtinCall( try emitDbgNode(gz, node); const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{ - .lhs = try ri.rl.resultType(gz, node, "@errSetCast"), + .lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"), .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), .node = gz.nodeIndexToRelative(node), }); @@ -8548,7 +8531,7 @@ fn builtinCall( }, .splat => { - const result_type = try ri.rl.resultType(gz, node, "@splat"); + const result_type = try ri.rl.resultTypeForCast(gz, node, "@splat"); const elem_type = try gz.addUnNode(.vector_elem_type, result_type, node); const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]); const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{ @@ -8810,7 +8793,7 @@ fn typeCast( builtin_name: []const u8, ) InnerError!Zir.Inst.Ref { const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const result_type = try ri.rl.resultType(gz, node, builtin_name); + const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name); const operand = try expr(gz, scope, .{ .rl = .none }, operand_node); try emitDbgStmt(gz, cursor); @@ -10069,6 +10052,29 @@ fn rvalue( ri: ResultInfo, raw_result: Zir.Inst.Ref, src_node: Ast.Node.Index, +) InnerError!Zir.Inst.Ref { + return rvalueInner(gz, ri, raw_result, src_node, true); +} + +/// Like `rvalue`, but refuses to perform coercions before taking references for +/// the `ref_coerced_ty` result type. This is used for local variables which do +/// not have `alloc`s, because we want variables to have consistent addresses, +/// i.e. we want them to act like lvalues. +fn rvalueNoCoercePreRef( + gz: *GenZir, + ri: ResultInfo, + raw_result: Zir.Inst.Ref, + src_node: Ast.Node.Index, +) InnerError!Zir.Inst.Ref { + return rvalueInner(gz, ri, raw_result, src_node, false); +} + +fn rvalueInner( + gz: *GenZir, + ri: ResultInfo, + raw_result: Zir.Inst.Ref, + src_node: Ast.Node.Index, + allow_coerce_pre_ref: bool, ) InnerError!Zir.Inst.Ref { const result = r: { if (refToIndex(raw_result)) |result_index| { @@ -10088,7 +10094,14 @@ fn rvalue( _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); return .void_value; }, - .ref => { + .ref, .ref_coerced_ty => { + const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: { + const ptr_ty = ri.rl.ref_coerced_ty; + break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{ + .lhs = ptr_ty, + .rhs = result, + }); + } else result; // We need a pointer but we have a value. // Unfortunately it's not quite as simple as directly emitting a ref // instruction here because we need subsequent address-of operator on @@ -10096,14 +10109,14 @@ fn rvalue( const astgen = gz.astgen; const tree = astgen.tree; const src_token = tree.firstToken(src_node); - const result_index = refToIndex(result) orelse - return gz.addUnTok(.ref, result, src_token); + const result_index = refToIndex(coerced_result) orelse + return gz.addUnTok(.ref, coerced_result, src_token); const zir_tags = gz.astgen.instructions.items(.tag); - if (zir_tags[result_index].isParam() or astgen.isInferred(result)) - return gz.addUnTok(.ref, result, src_token); + if (zir_tags[result_index].isParam() or astgen.isInferred(coerced_result)) + return gz.addUnTok(.ref, coerced_result, src_token); const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index); if (!gop.found_existing) { - gop.value_ptr.* = try gz.makeUnTok(.ref, result, src_token); + gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token); } return indexToRef(gop.value_ptr.*); }, diff --git a/src/AstRlAnnotate.zig b/src/AstRlAnnotate.zig index 81fb370b13..4e30aff268 100644 --- a/src/AstRlAnnotate.zig +++ b/src/AstRlAnnotate.zig @@ -669,17 +669,21 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI => { var buf: [2]Ast.Node.Index = undefined; const full = tree.fullArrayInit(&buf, node).?; - const have_type = if (full.ast.type_expr != 0) have_type: { + + if (full.ast.type_expr != 0) { + // Explicitly typed init does not participate in RLS _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none); - break :have_type true; - } else ri.have_type; - if (have_type) { - const elem_ri: ResultInfo = .{ - .have_type = true, - .have_ptr = ri.have_ptr, - }; for (full.ast.elements) |elem_init| { - _ = try astrl.expr(elem_init, block, elem_ri); + _ = try astrl.expr(elem_init, block, ResultInfo.type_only); + } + return false; + } + + if (ri.have_type) { + // Always forward type information + // If we have a result pointer, we use and forward it + for (full.ast.elements) |elem_init| { + _ = try astrl.expr(elem_init, block, ri); } return ri.have_ptr; } else { @@ -702,17 +706,21 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI => { var buf: [2]Ast.Node.Index = undefined; const full = tree.fullStructInit(&buf, node).?; - const have_type = if (full.ast.type_expr != 0) have_type: { + + if (full.ast.type_expr != 0) { + // Explicitly typed init does not participate in RLS _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none); - break :have_type true; - } else ri.have_type; - if (have_type) { - const elem_ri: ResultInfo = .{ - .have_type = true, - .have_ptr = ri.have_ptr, - }; for (full.ast.fields) |field_init| { - _ = try astrl.expr(field_init, block, elem_ri); + _ = try astrl.expr(field_init, block, ResultInfo.type_only); + } + return false; + } + + if (ri.have_type) { + // Always forward type information + // If we have a result pointer, we use and forward it + for (full.ast.fields) |field_init| { + _ = try astrl.expr(field_init, block, ri); } return ri.have_ptr; } else { diff --git a/src/Autodoc.zig b/src/Autodoc.zig index b45cee1400..55fe164767 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -2391,34 +2391,6 @@ fn walkInstruction( .expr = .{ .@"&" = expr_index }, }; }, - .array_init_anon_ref => { - const pl_node = data[inst_index].pl_node; - const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index); - const operands = file.zir.refSlice(extra.end, extra.data.operands_len); - const array_data = try self.arena.alloc(usize, operands.len); - - for (operands, 0..) |op, idx| { - const wr = try self.walkRef( - file, - parent_scope, - parent_src, - op, - false, - call_ctx, - ); - const expr_index = self.exprs.items.len; - try self.exprs.append(self.arena, wr.expr); - array_data[idx] = expr_index; - } - - const expr_index = self.exprs.items.len; - try self.exprs.append(self.arena, .{ .array = array_data }); - - return DocData.WalkResult{ - .typeRef = null, - .expr = .{ .@"&" = expr_index }, - }; - }, .float => { const float = data[inst_index].float; return DocData.WalkResult{ @@ -2709,9 +2681,7 @@ fn walkInstruction( .expr = .{ .declRef = decl_status }, }; }, - .field_val, .field_ptr, .field_type => { - // TODO: field type uses Zir.Inst.FieldType, it just happens to have the - // same layout as Zir.Inst.Field :^) + .field_val, .field_ptr => { const pl_node = data[inst_index].pl_node; const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); @@ -2730,8 +2700,7 @@ fn walkInstruction( }; if (tags[lhs] != .field_val and - tags[lhs] != .field_ptr and - tags[lhs] != .field_type) break :blk lhs_extra.data.lhs; + tags[lhs] != .field_ptr) break :blk lhs_extra.data.lhs; lhs_extra = file.zir.extraData( Zir.Inst.Field, @@ -2870,7 +2839,7 @@ fn walkInstruction( const field_name = blk: { const field_inst_index = init_extra.data.field_type; - if (tags[field_inst_index] != .field_type) unreachable; + if (tags[field_inst_index] != .struct_init_field_type) unreachable; const field_pl_node = data[field_inst_index].pl_node; const field_extra = file.zir.extraData( Zir.Inst.FieldType, diff --git a/src/Sema.zig b/src/Sema.zig index 75bff8c680..c33a1a7603 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -836,16 +836,11 @@ const LabeledBlock = struct { /// the items are contiguous in memory and thus can be passed to /// `Module.resolvePeerTypes`. const InferredAlloc = struct { - prongs: std.MultiArrayList(struct { - /// The dummy instruction used as a peer to resolve the type. - /// Although this has a redundant type with placeholder, this is - /// needed in addition because it may be a constant value, which - /// affects peer type resolution. - stored_inst: Air.Inst.Ref, - /// The bitcast instruction used as a placeholder when the - /// new result pointer type is not yet known. - placeholder: Air.Inst.Index, - }) = .{}, + /// The placeholder `store` instructions used before the result pointer type + /// is known. These should be rewritten to perform any required coercions + /// when the type is resolved. + /// Allocated from `sema.arena`. + prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .{}, }; const NeededComptimeReason = struct { @@ -1040,17 +1035,14 @@ fn analyzeBodyInner( .cmp_gte => try sema.zirCmp(block, inst, .gte), .cmp_gt => try sema.zirCmp(block, inst, .gt), .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .Optimized)), - .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), .decl_ref => try sema.zirDeclRef(block, inst), .decl_val => try sema.zirDeclVal(block, inst), .load => try sema.zirLoad(block, inst), .elem_ptr => try sema.zirElemPtr(block, inst), .elem_ptr_node => try sema.zirElemPtrNode(block, inst), - .elem_ptr_imm => try sema.zirElemPtrImm(block, inst), .elem_val => try sema.zirElemVal(block, inst), .elem_val_node => try sema.zirElemValNode(block, inst), .elem_val_imm => try sema.zirElemValImm(block, inst), - .elem_type_index => try sema.zirElemTypeIndex(block, inst), .elem_type => try sema.zirElemType(block, inst), .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), .vector_elem_type => try sema.zirVectorElemType(block, inst), @@ -1063,8 +1055,7 @@ fn analyzeBodyInner( .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), .error_union_type => try sema.zirErrorUnionType(block, inst), .error_value => try sema.zirErrorValue(block, inst), - .field_ptr => try sema.zirFieldPtr(block, inst, false), - .field_ptr_init => try sema.zirFieldPtr(block, inst, true), + .field_ptr => try sema.zirFieldPtr(block, inst), .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), .field_val => try sema.zirFieldVal(block, inst), .field_val_named => try sema.zirFieldValNamed(block, inst), @@ -1111,16 +1102,19 @@ fn analyzeBodyInner( .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), .xor => try sema.zirBitwise(block, inst, .xor), .struct_init_empty => try sema.zirStructInitEmpty(block, inst), + .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false), + .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true), + .struct_init_anon => try sema.zirStructInitAnon(block, inst), .struct_init => try sema.zirStructInit(block, inst, false), .struct_init_ref => try sema.zirStructInit(block, inst, true), - .struct_init_anon => try sema.zirStructInitAnon(block, inst, false), - .struct_init_anon_ref => try sema.zirStructInitAnon(block, inst, true), + .struct_init_field_type => try sema.zirStructInitFieldType(block, inst), + .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst), + .array_init_anon => try sema.zirArrayInitAnon(block, inst), .array_init => try sema.zirArrayInit(block, inst, false), .array_init_ref => try sema.zirArrayInit(block, inst, true), - .array_init_anon => try sema.zirArrayInitAnon(block, inst, false), - .array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true), + .array_init_elem_type => try sema.zirArrayInitElemType(block, inst), + .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst), .union_init => try sema.zirUnionInit(block, inst), - .field_type => try sema.zirFieldType(block, inst), .field_type_ref => try sema.zirFieldTypeRef(block, inst), .int_from_ptr => try sema.zirIntFromPtr(block, inst), .align_of => try sema.zirAlignOf(block, inst), @@ -1154,10 +1148,10 @@ fn analyzeBodyInner( .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), .@"resume" => try sema.zirResume(block, inst), .@"await" => try sema.zirAwait(block, inst), - .array_base_ptr => try sema.zirArrayBasePtr(block, inst), - .field_base_ptr => try sema.zirFieldBasePtr(block, inst), .for_len => try sema.zirForLen(block, inst), - .opt_eu_base_ty => try sema.zirOptEuBaseTy(block, inst), + .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), + .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), + .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), @@ -1386,23 +1380,33 @@ fn analyzeBodyInner( i += 1; continue; }, - .validate_array_init_ty => { - try sema.zirValidateArrayInitTy(block, inst); - i += 1; - continue; - }, .validate_struct_init_ty => { - try sema.zirValidateStructInitTy(block, inst); + try sema.zirValidateStructInitTy(block, inst, false); i += 1; continue; }, - .validate_struct_init => { - try sema.zirValidateStructInit(block, inst); + .validate_struct_init_result_ty => { + try sema.zirValidateStructInitTy(block, inst, true); i += 1; continue; }, - .validate_array_init => { - try sema.zirValidateArrayInit(block, inst); + .validate_array_init_ty => { + try sema.zirValidateArrayInitTy(block, inst, false); + i += 1; + continue; + }, + .validate_array_init_result_ty => { + try sema.zirValidateArrayInitTy(block, inst, true); + i += 1; + continue; + }, + .validate_ptr_struct_init => { + try sema.zirValidatePtrStructInit(block, inst); + i += 1; + continue; + }, + .validate_ptr_array_init => { + try sema.zirValidatePtrArrayInit(block, inst); i += 1; continue; }, @@ -1416,6 +1420,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .validate_ref_ty => { + try sema.zirValidateRefTy(block, inst); + i += 1; + continue; + }, .@"export" => { try sema.zirExport(block, inst); i += 1; @@ -1922,7 +1931,11 @@ fn resolveDestType( const msg = msg: { const msg = try sema.errMsg(block, src, "{s} must have a known result type", .{builtin_name}); errdefer msg.destroy(sema.gpa); - try sema.errNote(block, src, msg, "result type is unknown due to anytype parameter", .{}); + switch (sema.genericPoisonReason(zir_ref)) { + .anytype_param => |call_src| try sema.errNote(block, call_src, msg, "result type is unknown due to anytype parameter", .{}), + .anyopaque_ptr => |ptr_src| try sema.errNote(block, ptr_src, msg, "result type is unknown due to opaque pointer type", .{}), + .unknown => {}, + } try sema.errNote(block, src, msg, "use @as to provide explicit result type", .{}); break :msg msg; }; @@ -1944,6 +1957,65 @@ fn resolveDestType( return raw_ty; } +const GenericPoisonReason = union(enum) { + anytype_param: LazySrcLoc, + anyopaque_ptr: LazySrcLoc, + unknown, +}; + +/// Backtracks through ZIR instructions to determine the reason a generic poison +/// type was created. Used for error reporting. +fn genericPoisonReason(sema: *Sema, ref: Zir.Inst.Ref) GenericPoisonReason { + var cur = ref; + while (true) { + const inst = Zir.refToIndex(cur) orelse return .unknown; + switch (sema.code.instructions.items(.tag)[inst]) { + .validate_array_init_ref_ty => { + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; + cur = extra.ptr_ty; + }, + .array_init_elem_type => { + const bin = sema.code.instructions.items(.data)[inst].bin; + cur = bin.lhs; + }, + .indexable_ptr_elem_type, .vector_elem_type => { + const un_node = sema.code.instructions.items(.data)[inst].un_node; + cur = un_node.operand; + }, + .struct_init_field_type => { + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; + cur = extra.container_type; + }, + .elem_type => { + // There are two cases here: the pointer type may already have been + // generic poison, or it may have been an anyopaque pointer. + const un_node = sema.code.instructions.items(.data)[inst].un_node; + const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) { + error.GenericPoison => unreachable, // this is a type, not a value + }; + const operand_val = Air.refToInterned(operand_ref) orelse return .unknown; + if (operand_val == .generic_poison_type) { + // The pointer was generic poison - keep looking. + cur = un_node.operand; + } else { + // This must be an anyopaque pointer! + return .{ .anyopaque_ptr = un_node.src() }; + } + }, + .call, .field_call => { + // A function call can never return generic poison, so we must be + // evaluating an `anytype` function parameter. + // TODO: better source location - function decl rather than call + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + return .{ .anytype_param = pl_node.src() }; + }, + else => return .unknown, + } + } +} + fn analyzeAsType( sema: *Sema, block: *Block, @@ -2634,217 +2706,6 @@ pub fn resolveInstValue( }; } -fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const mod = sema.mod; - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const pointee_ty = try sema.resolveType(block, src, extra.lhs); - const ptr = try sema.resolveInst(extra.rhs); - const target = mod.getTarget(); - const addr_space = target_util.defaultAddressSpace(target, .local); - - if (Air.refToIndex(ptr)) |ptr_inst| { - switch (sema.air_instructions.items(.tag)[ptr_inst]) { - .inferred_alloc => { - const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; - const ia2 = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; - // Add the stored instruction to the set we will use to resolve peer types - // for the inferred allocation. - // This instruction will not make it to codegen; it is only to participate - // in the `stored_inst_list` of the `inferred_alloc`. - var trash_block = block.makeSubBlock(); - defer trash_block.instructions.deinit(sema.gpa); - const operand = try trash_block.addBitCast(pointee_ty, .void_value); - - const ptr_ty = try sema.ptrType(.{ - .child = pointee_ty.toIntern(), - .flags = .{ - .alignment = ia1.alignment, - .address_space = addr_space, - }, - }); - const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr); - - try ia2.prongs.append(sema.arena, .{ - .stored_inst = operand, - .placeholder = Air.refToIndex(bitcasted_ptr).?, - }); - - try sema.checkKnownAllocPtr(ptr, bitcasted_ptr); - return bitcasted_ptr; - }, - .inferred_alloc_comptime => { - const alignment = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.alignment; - // There will be only one coerce_result_ptr because we are running at comptime. - // The alloc will turn into a Decl. - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - const decl_index = try anon_decl.finish( - pointee_ty, - (try mod.intern(.{ .undef = pointee_ty.toIntern() })).toValue(), - alignment, - ); - sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.decl_index = decl_index; - if (alignment != .none) { - try sema.resolveTypeLayout(pointee_ty); - } - const ptr_ty = try sema.ptrType(.{ - .child = pointee_ty.toIntern(), - .flags = .{ - .alignment = alignment, - .address_space = addr_space, - }, - }); - try sema.maybeQueueFuncBodyAnalysis(decl_index); - try sema.comptime_mutable_decls.append(decl_index); - return Air.internedToRef((try mod.intern(.{ .ptr = .{ - .ty = ptr_ty.toIntern(), - .addr = .{ .mut_decl = .{ - .decl = decl_index, - .runtime_index = block.runtime_index, - } }, - } }))); - }, - else => {}, - } - } - - // Make a dummy store through the pointer to test the coercion. - // We will then use the generated instructions to decide what - // kind of transformations to make on the result pointer. - var trash_block = block.makeSubBlock(); - trash_block.is_comptime = false; - defer trash_block.instructions.deinit(sema.gpa); - - const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr)); - const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value); - const new_ptr = try sema.coerceResultPtr(block, src, ptr, dummy_ptr, dummy_operand, &trash_block); - try sema.checkKnownAllocPtr(ptr, new_ptr); - return new_ptr; -} - -fn coerceResultPtr( - sema: *Sema, - block: *Block, - src: LazySrcLoc, - ptr: Air.Inst.Ref, - dummy_ptr: Air.Inst.Ref, - dummy_operand: Air.Inst.Ref, - trash_block: *Block, -) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const target = sema.mod.getTarget(); - const addr_space = target_util.defaultAddressSpace(target, .local); - const pointee_ty = sema.typeOf(dummy_operand); - const prev_trash_len = trash_block.instructions.items.len; - - try sema.storePtr2(trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast); - - { - const air_tags = sema.air_instructions.items(.tag); - - //std.debug.print("dummy storePtr instructions:\n", .{}); - //for (trash_block.instructions.items) |item| { - // std.debug.print(" {s}\n", .{@tagName(air_tags[item])}); - //} - - // The last one is always `store`. - const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1]; - if (air_tags[trash_inst] != .store and air_tags[trash_inst] != .store_safe) { - // no store instruction is generated for zero sized types - assert((try sema.typeHasOnePossibleValue(pointee_ty)) != null); - } else { - trash_block.instructions.items.len -= 1; - assert(trash_inst == sema.air_instructions.len - 1); - sema.air_instructions.len -= 1; - } - } - - const ptr_ty = try sema.ptrType(.{ - .child = pointee_ty.toIntern(), - .flags = .{ .address_space = addr_space }, - }); - - var new_ptr = ptr; - - while (true) { - const air_tags = sema.air_instructions.items(.tag); - const air_datas = sema.air_instructions.items(.data); - - if (trash_block.instructions.items.len == prev_trash_len) { - if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { - return Air.internedToRef(ptr_val.toIntern()); - } - if (pointee_ty.eql(Type.null, sema.mod)) { - const null_inst = Air.internedToRef(Value.null.toIntern()); - _ = try block.addBinOp(.store, new_ptr, null_inst); - return .void_value; - } - return sema.bitCast(block, ptr_ty, new_ptr, src, null); - } - - const trash_inst = trash_block.instructions.pop(); - - switch (air_tags[trash_inst]) { - // Array coerced to Vector where element size is not equal but coercible. - .aggregate_init => { - const ty_pl = air_datas[trash_inst].ty_pl; - const ptr_operand_ty = try sema.ptrType(.{ - .child = (try sema.analyzeAsType(block, src, ty_pl.ty)).toIntern(), - .flags = .{ .address_space = addr_space }, - }); - - if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { - return Air.internedToRef(ptr_val.toIntern()); - } else { - return sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); - } - }, - .bitcast => { - const ty_op = air_datas[trash_inst].ty_op; - const operand_ty = sema.typeOf(ty_op.operand); - const ptr_operand_ty = try sema.ptrType(.{ - .child = operand_ty.toIntern(), - .flags = .{ .address_space = addr_space }, - }); - if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { - new_ptr = Air.internedToRef((try mod.getCoerced(ptr_val, ptr_operand_ty)).toIntern()); - } else { - new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); - } - }, - .wrap_optional => { - new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true); - }, - .wrap_errunion_err => { - return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{}); - }, - .wrap_errunion_payload => { - new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true); - }, - .array_to_slice => { - return sema.fail(block, src, "TODO coerce_result_ptr array_to_slice", .{}); - }, - .get_union_tag => { - return sema.fail(block, src, "TODO coerce_result_ptr get_union_tag", .{}); - }, - else => { - if (std.debug.runtime_safety) { - std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{ - air_tags[trash_inst], - }); - } else { - unreachable; - } - }, - } - } -} - pub fn getStructType( sema: *Sema, decl: Module.Decl.Index, @@ -4220,8 +4081,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com .inferred_alloc => { const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc; const ia2 = sema.unresolved_inferred_allocs.fetchRemove(ptr_inst).?.value; - const peer_inst_list = ia2.prongs.items(.stored_inst); - const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); + const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len); + for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| { + assert(sema.air_instructions.items(.tag)[store_inst] == .store); + const bin_op = sema.air_instructions.items(.data)[store_inst].bin_op; + peer_val.* = bin_op.rhs; + } + const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); const final_ptr_ty = try sema.ptrType(.{ .child = final_elem_ty.toIntern(), @@ -4259,55 +4125,19 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com .data = .{ .ty = final_ptr_ty }, }); - // Now we need to go back over all the coerce_result_ptr instructions, which - // previously inserted a bitcast as a placeholder, and do the logic as if + // Now we need to go back over all the store instructions, and do the logic as if // the new result ptr type was available. - const placeholders = ia2.prongs.items(.placeholder); const gpa = sema.gpa; - var trash_block = block.makeSubBlock(); - trash_block.is_comptime = false; - defer trash_block.instructions.deinit(gpa); - - const mut_final_ptr_ty = try sema.ptrType(.{ - .child = final_elem_ty.toIntern(), - .flags = .{ - .alignment = ia1.alignment, - .address_space = target_util.defaultAddressSpace(target, .local), - }, - }); - const dummy_ptr = try trash_block.addTy(.alloc, mut_final_ptr_ty); - const empty_trash_count = trash_block.instructions.items.len; - - for (peer_inst_list, placeholders) |peer_inst, placeholder_inst| { - const sub_ptr_ty = sema.typeOf(Air.indexToRef(placeholder_inst)); - - if (mut_final_ptr_ty.eql(sub_ptr_ty, mod)) { - // New result location type is the same as the old one; nothing - // to do here. - continue; - } - + for (ia2.prongs.items) |placeholder_inst| { var replacement_block = block.makeSubBlock(); defer replacement_block.instructions.deinit(gpa); - const result = switch (sema.air_instructions.items(.tag)[placeholder_inst]) { - .bitcast => result: { - trash_block.instructions.shrinkRetainingCapacity(empty_trash_count); - const sub_ptr = try sema.coerceResultPtr(&replacement_block, src, ptr, dummy_ptr, peer_inst, &trash_block); + assert(sema.air_instructions.items(.tag)[placeholder_inst] == .store); + const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op; + try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store); - assert(replacement_block.instructions.items.len > 0); - break :result sub_ptr; - }, - .store, .store_safe => result: { - const bin_op = sema.air_instructions.items(.data)[placeholder_inst].bin_op; - try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .bitcast); - break :result .void_value; - }, - else => unreachable, - }; - - // If only one instruction is produced then we can replace the bitcast + // If only one instruction is produced then we can replace the store // placeholder instruction with this instruction; no need for an entire block. if (replacement_block.instructions.items.len == 1) { const only_inst = replacement_block.instructions.items[0]; @@ -4315,13 +4145,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com continue; } - // Here we replace the placeholder bitcast instruction with a block - // that does the coerce_result_ptr logic. - _ = try replacement_block.addBr(placeholder_inst, result); - const ty_inst = if (result == .void_value) - .void_type - else - sema.air_instructions.items(.data)[placeholder_inst].ty_op.ty; + // Here we replace the placeholder store instruction with a block + // that does the actual store logic. + _ = try replacement_block.addBr(placeholder_inst, .void_value); try sema.air_extra.ensureUnusedCapacity( gpa, @typeInfo(Air.Block).Struct.fields.len + replacement_block.instructions.items.len, @@ -4329,7 +4155,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com sema.air_instructions.set(placeholder_inst, .{ .tag = .block, .data = .{ .ty_pl = .{ - .ty = ty_inst, + .ty = .void_type, .payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = @intCast(replacement_block.instructions.items.len), }), @@ -4342,64 +4168,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com } } -fn zirArrayBasePtr( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - - const start_ptr = try sema.resolveInst(inst_data.operand); - var base_ptr = start_ptr; - while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { - .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), - .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), - else => break, - }; - - const elem_ty = sema.typeOf(base_ptr).childType(mod); - switch (elem_ty.zigTypeTag(mod)) { - .Array, .Vector => return base_ptr, - .Struct => if (elem_ty.isTuple(mod)) { - // TODO validate element count - try sema.checkKnownAllocPtr(start_ptr, base_ptr); - return base_ptr; - }, - else => {}, - } - return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); -} - -fn zirFieldBasePtr( - sema: *Sema, - block: *Block, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - - const start_ptr = try sema.resolveInst(inst_data.operand); - var base_ptr = start_ptr; - while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { - .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), - .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), - else => break, - }; - - const elem_ty = sema.typeOf(base_ptr).childType(mod); - switch (elem_ty.zigTypeTag(mod)) { - .Struct, .Union => { - try sema.checkKnownAllocPtr(start_ptr, base_ptr); - return base_ptr; - }, - else => {}, - } - return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType(mod)); -} - fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; @@ -4526,34 +4294,140 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return len; } -fn zirOptEuBaseTy( +/// Given any single pointer, retrieve a pointer to the payload of any optional +/// or error union pointed to, initializing these pointers along the way. +/// Given a `*E!?T`, returns a (valid) `*T`. +/// May invalidate already-stored payload data. +fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { + const mod = sema.mod; + var base_ptr = ptr; + while (true) switch (sema.typeOf(base_ptr).childType(mod).zigTypeTag(mod)) { + .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), + .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), + else => break, + }; + try sema.checkKnownAllocPtr(ptr, base_ptr); + return base_ptr; +} + +fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const un_node = sema.code.instructions.items(.data)[inst].un_node; + const ptr = try sema.resolveInst(un_node.operand); + return sema.optEuBasePtrInit(block, ptr, un_node.src()); +} + +fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + const src = pl_node.src(); + const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; + const uncoerced_val = try sema.resolveInst(extra.rhs); + const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.lhs) catch |err| switch (err) { + error.GenericPoison => return uncoerced_val, + else => |e| return e, + }; + const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); + assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction + const elem_ty = ptr_ty.childType(mod); + switch (ptr_ty.ptrSize(mod)) { + .One => { + const uncoerced_ty = sema.typeOf(uncoerced_val); + if (elem_ty.zigTypeTag(mod) == .Array and elem_ty.childType(mod).toIntern() == uncoerced_ty.toIntern()) { + // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion. + return uncoerced_val; + } + // If the destination type is anyopaque, don't coerce - the pointer will coerce instead. + if (elem_ty.toIntern() == .anyopaque_type) { + return uncoerced_val; + } else { + return sema.coerce(block, elem_ty, uncoerced_val, src); + } + }, + .Slice, .Many => { + // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`. + const val_ty = sema.typeOf(uncoerced_val); + switch (val_ty.zigTypeTag(mod)) { + .Array, .Vector => {}, + else => if (!val_ty.isTuple(mod)) { + return sema.fail(block, src, "expected array of '{}', found '{}'", .{ elem_ty.fmt(mod), val_ty.fmt(mod) }); + }, + } + const want_ty = try mod.arrayType(.{ + .len = val_ty.arrayLen(mod), + .child = elem_ty.toIntern(), + .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, + }); + return sema.coerce(block, want_ty, uncoerced_val, src); + }, + .C => { + // There's nothing meaningful to do here, because we don't know if this is meant to be a + // single-pointer or a many-pointer. + return uncoerced_val; + }, + } +} + +fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { + const mod = sema.mod; + const un_tok = sema.code.instructions.items(.data)[inst].un_tok; + const src = un_tok.src(); + const ty_operand = sema.resolveType(block, src, un_tok.operand) catch |err| switch (err) { + error.GenericPoison => { + // We don't actually have a type, so this will be treated as an untyped address-of operator. + return; + }, + else => |e| return e, + }; + if (ty_operand.optEuBaseType(mod).zigTypeTag(mod) != .Pointer) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "expected type '{}', found pointer", .{ty_operand.fmt(mod)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, src, msg, "address-of operator always returns a pointer", .{}); + break :msg msg; + }); + } +} + +fn zirValidateArrayInitRefTy( sema: *Sema, block: *Block, inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - const inst_data = sema.code.instructions.items(.data)[inst].un_node; - var ty = sema.resolveType(block, .unneeded, inst_data.operand) catch |err| switch (err) { - // Since this is a ZIR instruction that returns a type, encountering - // generic poison should not result in a failed compilation, but the - // generic poison type. This prevents unnecessary failures when - // constructing types at compile-time. + const pl_node = sema.code.instructions.items(.data)[inst].pl_node; + const src = pl_node.src(); + const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; + const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.ptr_ty) catch |err| switch (err) { error.GenericPoison => return .generic_poison_type, else => |e| return e, }; - while (true) { - switch (ty.zigTypeTag(mod)) { - .Optional => ty = ty.optionalChild(mod), - .ErrorUnion => ty = ty.errorUnionPayload(mod), - else => return Air.internedToRef(ty.toIntern()), - } + const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); + assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction + if (ptr_ty.isSlice(mod)) { + // Use array of correct length + const arr_ty = try mod.arrayType(.{ + .len = extra.elem_count, + .child = ptr_ty.childType(mod).toIntern(), + .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, + }); + return Air.internedToRef(arr_ty.toIntern()); } + // Otherwise, we just want the pointer child type + const ret_ty = ptr_ty.childType(mod); + if (ret_ty.toIntern() == .anyopaque_type) { + // The actual array type is unknown, which we represent with a generic poison. + return .generic_poison_type; + } + const arr_ty = ret_ty.optEuBaseType(mod); + try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty); + return Air.internedToRef(ret_ty.toIntern()); } fn zirValidateArrayInitTy( sema: *Sema, block: *Block, inst: Zir.Inst.Index, + is_result_ty: bool, ) CompileError!void { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; @@ -4565,22 +4439,34 @@ fn zirValidateArrayInitTy( error.GenericPoison => return, else => |e| return e, }; + const arr_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty; + return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty); +} +fn validateArrayInitTy( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty_src: LazySrcLoc, + init_count: u32, + ty: Type, +) CompileError!void { + const mod = sema.mod; switch (ty.zigTypeTag(mod)) { .Array => { const array_len = ty.arrayLen(mod); - if (extra.init_count != array_len) { + if (init_count != array_len) { return sema.fail(block, src, "expected {d} array elements; found {d}", .{ - array_len, extra.init_count, + array_len, init_count, }); } return; }, .Vector => { const array_len = ty.arrayLen(mod); - if (extra.init_count != array_len) { + if (init_count != array_len) { return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ - array_len, extra.init_count, + array_len, init_count, }); } return; @@ -4588,9 +4474,9 @@ fn zirValidateArrayInitTy( .Struct => if (ty.isTuple(mod)) { try sema.resolveTypeFields(ty); const array_len = ty.arrayLen(mod); - if (extra.init_count > array_len) { + if (init_count > array_len) { return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ - array_len, extra.init_count, + array_len, init_count, }); } return; @@ -4604,6 +4490,7 @@ fn zirValidateStructInitTy( sema: *Sema, block: *Block, inst: Zir.Inst.Index, + is_result_ty: bool, ) CompileError!void { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; @@ -4613,15 +4500,16 @@ fn zirValidateStructInitTy( error.GenericPoison => return, else => |e| return e, }; + const struct_ty = if (is_result_ty) ty.optEuBaseType(mod) else ty; - switch (ty.zigTypeTag(mod)) { + switch (struct_ty.zigTypeTag(mod)) { .Struct, .Union => return, else => {}, } - return sema.failWithStructInitNotSupported(block, src, ty); + return sema.failWithStructInitNotSupported(block, src, struct_ty); } -fn zirValidateStructInit( +fn zirValidatePtrStructInit( sema: *Sema, block: *Block, inst: Zir.Inst.Index, @@ -4637,7 +4525,7 @@ fn zirValidateStructInit( const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); - const agg_ty = sema.typeOf(object_ptr).childType(mod); + const agg_ty = sema.typeOf(object_ptr).childType(mod).optEuBaseType(mod); switch (agg_ty.zigTypeTag(mod)) { .Struct => return sema.validateStructInit( block, @@ -4723,10 +4611,6 @@ fn validateUnionInit( // based only on the store instructions. // `first_block_index` needs to point to the `field_ptr` if it exists; // the `store` otherwise. - // - // It's also possible for there to be no store instruction, in the case - // of nested `coerce_result_ptr` instructions. If we see the `field_ptr` - // but we have not found a `store`, treat as a runtime-known field. var first_block_index = block.instructions.items.len; var block_index = block.instructions.items.len - 1; var init_val: ?Value = null; @@ -4963,10 +4847,6 @@ fn validateStructInit( // based only on the store instructions. // `first_block_index` needs to point to the `field_ptr` if it exists; // the `store` otherwise. - // - // It's also possible for there to be no store instruction, in the case - // of nested `coerce_result_ptr` instructions. If we see the `field_ptr` - // but we have not found a `store`, treat as a runtime-known field. // Possible performance enhancement: save the `block_index` between iterations // of the for loop. @@ -5115,7 +4995,7 @@ fn validateStructInit( } } -fn zirValidateArrayInit( +fn zirValidatePtrArrayInit( sema: *Sema, block: *Block, inst: Zir.Inst.Index, @@ -5128,7 +5008,7 @@ fn zirValidateArrayInit( const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr); - const array_ty = sema.typeOf(array_ptr).childType(mod); + const array_ty = sema.typeOf(array_ptr).childType(mod).optEuBaseType(mod); const array_len = array_ty.arrayLen(mod); if (instrs.len != array_len) switch (array_ty.zigTypeTag(mod)) { @@ -5227,10 +5107,6 @@ fn zirValidateArrayInit( // `first_block_index` needs to point to the `elem_ptr` if it exists; // the `store` otherwise. // - // It's also possible for there to be no store instruction, in the case - // of nested `coerce_result_ptr` instructions. If we see the `elem_ptr` - // but we have not found a `store`, treat as a runtime-known element. - // // This is nearly identical to similar logic in `validateStructInit`. // Possible performance enhancement: save the `block_index` between iterations @@ -5540,10 +5416,7 @@ fn storeToInferredAlloc( try sema.checkComptimeKnownStore(block, dummy_store); // Add the stored instruction to the set we will use to resolve peer types // for the inferred allocation. - try inferred_alloc.prongs.append(sema.arena, .{ - .stored_inst = operand, - .placeholder = Air.refToIndex(dummy_store).?, - }); + try inferred_alloc.prongs.append(sema.arena, Air.refToIndex(dummy_store).?); } fn storeToInferredAllocComptime( @@ -8314,10 +8187,10 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return Air.internedToRef(opt_type.toIntern()); } -fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const bin = sema.code.instructions.items(.data)[inst].bin; - const indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) { + const maybe_wrapped_indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) { // Since this is a ZIR instruction that returns a type, encountering // generic poison should not result in a failed compilation, but the // generic poison type. This prevents unnecessary failures when @@ -8325,6 +8198,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr error.GenericPoison => return .generic_poison_type, else => |e| return e, }; + const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(mod); try sema.resolveTypeFields(indexable_ty); assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction if (indexable_ty.zigTypeTag(mod) == .Struct) { @@ -8339,8 +8213,18 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const un_node = sema.code.instructions.items(.data)[inst].un_node; - const ptr_ty = try sema.resolveType(block, .unneeded, un_node.operand); + const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) { + error.GenericPoison => return .generic_poison_type, + else => |e| return e, + }; + const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(mod); assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction + const elem_ty = ptr_ty.childType(mod); + if (elem_ty.toIntern() == .anyopaque_type) { + // The pointer's actual child type is effectively unknown, so it makes + // sense to represent it with a generic poison. + return .generic_poison_type; + } return Air.internedToRef(ptr_ty.childType(mod).toIntern()); } @@ -10083,7 +9967,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fieldVal(block, src, object, field_name, field_name_src); } -fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: bool) CompileError!Air.Inst.Ref { +fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -10094,7 +9978,29 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index, initializing: b const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); const object_ptr = try sema.resolveInst(extra.lhs); - return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, initializing); + return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); +} + +fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const mod = sema.mod; + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); + const object_ptr = try sema.resolveInst(extra.lhs); + const struct_ty = sema.typeOf(object_ptr).childType(mod); + switch (struct_ty.zigTypeTag(mod)) { + .Struct, .Union => { + return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true); + }, + else => { + return sema.failWithStructInitNotSupported(block, src, struct_ty); + }, + } } fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -10587,15 +10493,23 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); } -fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); + const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; const array_ptr = try sema.resolveInst(extra.ptr); const elem_index = try sema.mod.intRef(Type.usize, extra.index); + const array_ty = sema.typeOf(array_ptr).childType(mod); + switch (array_ty.zigTypeTag(mod)) { + .Array, .Vector => {}, + else => if (!array_ty.isTuple(mod)) { + return sema.failWithArrayInitNotSupported(block, src, array_ty); + }, + } return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); } @@ -19213,6 +19127,52 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } +fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const mod = sema.mod; + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + const ty_operand = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) { + // Generic poison means this is an untyped anonymous empty struct init + error.GenericPoison => return .empty_struct, + else => |e| return e, + }; + const init_ty = if (is_byref) ty: { + const ptr_ty = ty_operand.optEuBaseType(mod); + assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction + if (!ptr_ty.isSlice(mod)) { + break :ty ptr_ty.childType(mod); + } + // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`. + break :ty try mod.arrayType(.{ + .len = 0, + .sentinel = if (ptr_ty.sentinel(mod)) |s| s.toIntern() else .none, + .child = ptr_ty.childType(mod).toIntern(), + }); + } else ty_operand; + const obj_ty = init_ty.optEuBaseType(mod); + + const empty_ref = switch (obj_ty.zigTypeTag(mod)) { + .Struct => try sema.structInitEmpty(block, obj_ty, src, src), + .Array, .Vector => try sema.arrayInitEmpty(block, src, obj_ty), + .Union => return sema.fail(block, src, "union initializer must initialize one field", .{}), + else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), + }; + const init_ref = try sema.coerce(block, init_ty, empty_ref, src); + + if (is_byref) { + const init_val = (try sema.resolveMaybeUndefVal(init_ref)).?; + var anon_decl = try block.startAnonDecl(); + defer anon_decl.deinit(); + const decl = try anon_decl.finish(init_ty, init_val, .none); + return sema.analyzeDeclRef(decl); + } else { + return init_ref; + } +} + fn structInitEmpty( sema: *Sema, block: *Block, @@ -19230,7 +19190,7 @@ fn structInitEmpty( defer gpa.free(field_inits); @memset(field_inits, .none); - return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, false); + return sema.finishStructInit(block, init_src, dest_src, field_inits, struct_ty, struct_ty, false); } fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { @@ -19321,13 +19281,14 @@ fn zirStructInit( const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; const first_field_type_data = zir_datas[first_item.field_type].pl_node; const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; - const resolved_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) { + const result_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) { error.GenericPoison => { // The type wasn't actually known, so treat this as an anon struct init. return sema.structInitAnon(block, src, .typed_init, extra.data, extra.end, is_ref); }, else => |e| return e, }; + const resolved_ty = result_ty.optEuBaseType(mod); try sema.resolveTypeLayout(resolved_ty); if (resolved_ty.zigTypeTag(mod) == .Struct) { @@ -19372,7 +19333,9 @@ fn zirStructInit( return sema.failWithOwnedErrorMsg(block, msg); } found_fields[field_index] = item.data.field_type; - field_inits[field_index] = try sema.resolveInst(item.data.init); + const uncoerced_init = try sema.resolveInst(item.data.init); + const field_ty = resolved_ty.structFieldType(field_index, mod); + field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); if (!is_packed) if (try resolved_ty.structFieldValueComptime(mod, field_index)) |default_value| { const init_val = (try sema.resolveMaybeUndefVal(field_inits[field_index])) orelse { return sema.failWithNeededComptime(block, field_src, .{ @@ -19386,7 +19349,7 @@ fn zirStructInit( }; } - return sema.finishStructInit(block, src, src, field_inits, resolved_ty, is_ref); + return sema.finishStructInit(block, src, src, field_inits, resolved_ty, result_ty, is_ref); } else if (resolved_ty.zigTypeTag(mod) == .Union) { if (extra.data.fields_len != 1) { return sema.fail(block, src, "union initialization expects exactly one field", .{}); @@ -19401,36 +19364,60 @@ fn zirStructInit( const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); + const field_ty = mod.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index].toType(); + + if (field_ty.zigTypeTag(mod) == .NoReturn) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{}' declared here", .{ + field_name.fmt(ip), + }); + try sema.addDeclaredHereNote(msg, resolved_ty); + break :msg msg; + }); + } + + const uncoerced_init_inst = try sema.resolveInst(item.data.init); + const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); - const init_inst = try sema.resolveInst(item.data.init); if (try sema.resolveMaybeUndefVal(init_inst)) |val| { - const field_ty = mod.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index].toType(); - return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{ + const struct_val = (try mod.intern(.{ .un = .{ .ty = resolved_ty.toIntern(), .tag = try tag_val.intern(tag_ty, mod), .val = try val.intern(field_ty, mod), - } })).toValue(), is_ref); + } })).toValue(); + const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); + const final_val = (try sema.resolveMaybeUndefVal(final_val_inst)).?; + return sema.addConstantMaybeRef(block, resolved_ty, final_val, is_ref); + } + + if (try sema.typeRequiresComptime(resolved_ty)) { + return sema.failWithNeededComptime(block, field_src, .{ + .needed_comptime_reason = "initializer of comptime only union must be comptime-known", + }); } if (is_ref) { const target = mod.getTarget(); const alloc_ty = try sema.ptrType(.{ - .child = resolved_ty.toIntern(), + .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); - const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty, true); + const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); + const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); try sema.storePtr(block, src, field_ptr, init_inst); const new_tag = Air.internedToRef(tag_val.toIntern()); - _ = try block.addBinOp(.set_union_tag, alloc, new_tag); + _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag); return sema.makePtrConst(block, alloc); } try sema.requireRuntimeBlock(block, src, null); try sema.queueFullTypeResolution(resolved_ty); - return block.addUnionInit(resolved_ty, field_index, init_inst); - } else if (resolved_ty.isAnonStruct(mod)) { - return sema.fail(block, src, "TODO anon struct init validation", .{}); + const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst); + return sema.coerce(block, result_ty, union_val, src); } unreachable; } @@ -19442,6 +19429,7 @@ fn finishStructInit( dest_src: LazySrcLoc, field_inits: []Air.Inst.Ref, struct_ty: Type, + result_ty: Type, is_ref: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; @@ -19452,8 +19440,24 @@ fn finishStructInit( switch (ip.indexToKey(struct_ty.toIntern())) { .anon_struct_type => |anon_struct| { - for (anon_struct.values.get(ip), 0..) |default_val, i| { - if (field_inits[i] != .none) continue; + // We can't get the slices, as the coercion may invalidate them. + for (0..anon_struct.types.len) |i| { + if (field_inits[i] != .none) { + // Coerce the init value to the field type. + const field_ty = anon_struct.types.get(ip)[i].toType(); + field_inits[i] = sema.coerce(block, field_ty, field_inits[i], .unneeded) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(init_src.node_offset.x, decl, i); + _ = try sema.coerce(block, field_ty, field_inits[i], field_src); + unreachable; + }, + else => |e| return e, + }; + continue; + } + + const default_val = anon_struct.values.get(ip)[i]; if (default_val == .none) { if (anon_struct.names.len == 0) { @@ -19480,7 +19484,20 @@ fn finishStructInit( }, .struct_type => |struct_type| { for (0..struct_type.field_types.len) |i| { - if (field_inits[i] != .none) continue; + if (field_inits[i] != .none) { + // Coerce the init value to the field type. + const field_ty = struct_type.field_types.get(ip)[i].toType(); + field_inits[i] = sema.coerce(block, field_ty, field_inits[i], init_src) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(init_src.node_offset.x, decl, i); + _ = try sema.coerce(block, field_ty, field_inits[i], field_src); + unreachable; + }, + else => |e| return e, + }; + continue; + } const field_init = struct_type.fieldInit(ip, i); if (field_init == .none) { @@ -19524,29 +19541,39 @@ fn finishStructInit( const runtime_index = opt_runtime_index orelse { const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); - for (elems, field_inits, 0..) |*elem, field_init, field_i| { - elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).? - .intern(struct_ty.structFieldType(field_i, mod), mod); + for (elems, field_inits) |*elem, field_init| { + elem.* = (sema.resolveMaybeUndefVal(field_init) catch unreachable).?.toIntern(); } const struct_val = try mod.intern(.{ .aggregate = .{ .ty = struct_ty.toIntern(), .storage = .{ .elems = elems }, } }); - return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref); + const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val), init_src); + const final_val = (try sema.resolveMaybeUndefVal(final_val_inst)).?; + return sema.addConstantMaybeRef(block, result_ty, final_val, is_ref); }; + if (try sema.typeRequiresComptime(struct_ty)) { + const decl = mod.declPtr(block.src_decl); + const field_src = mod.initSrc(init_src.node_offset.x, decl, runtime_index); + return sema.failWithNeededComptime(block, field_src, .{ + .needed_comptime_reason = "initializer of comptime only struct must be comptime-known", + }); + } + if (is_ref) { try sema.resolveStructLayout(struct_ty); const target = sema.mod.getTarget(); const alloc_ty = try sema.ptrType(.{ - .child = struct_ty.toIntern(), + .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); + const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); for (field_inits, 0..) |field_init, i_usize| { const i: u32 = @intCast(i_usize); const field_src = dest_src; - const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, alloc, i, field_src, struct_ty, true); + const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, field_src, struct_ty, true); try sema.storePtr(block, dest_src, field_ptr, field_init); } @@ -19563,19 +19590,19 @@ fn finishStructInit( else => |e| return e, }; try sema.queueFullTypeResolution(struct_ty); - return block.addAggregateInit(struct_ty, field_inits); + const struct_val = try block.addAggregateInit(struct_ty, field_inits); + return sema.coerce(block, result_ty, struct_val, init_src); } fn zirStructInitAnon( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - is_ref: bool, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); - return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, is_ref); + return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, false); } fn structInitAnon( @@ -19748,13 +19775,14 @@ fn zirArrayInit( const args = sema.code.refSlice(extra.end, extra.data.operands_len); assert(args.len >= 2); // array_ty + at least one element - const array_ty = sema.resolveType(block, src, args[0]) catch |err| switch (err) { + const result_ty = sema.resolveType(block, src, args[0]) catch |err| switch (err) { error.GenericPoison => { // The type wasn't actually known, so treat this as an anon array init. return sema.arrayInitAnon(block, src, args[1..], is_ref); }, else => |e| return e, }; + const array_ty = result_ty.optEuBaseType(mod); const is_tuple = array_ty.zigTypeTag(mod) == .Struct; const sentinel_val = array_ty.sentinel(mod); @@ -19810,10 +19838,12 @@ fn zirArrayInit( // We checked that all args are comptime above. val.* = try ((sema.resolveMaybeUndefVal(arg) catch unreachable).?).intern(elem_ty, mod); } - return sema.addConstantMaybeRef(block, array_ty, (try mod.intern(.{ .aggregate = .{ + const arr_val = try mod.intern(.{ .aggregate = .{ .ty = array_ty.toIntern(), .storage = .{ .elems = elem_vals }, - } })).toValue(), is_ref); + } }); + const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val), src); + return sema.addConstantMaybeRef(block, result_ty, (try sema.resolveMaybeUndefVal(result_ref)).?, is_ref); }; sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { @@ -19830,10 +19860,11 @@ fn zirArrayInit( if (is_ref) { const target = mod.getTarget(); const alloc_ty = try sema.ptrType(.{ - .child = array_ty.toIntern(), + .child = result_ty.toIntern(), .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, }); const alloc = try block.addTy(.alloc, alloc_ty); + const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); if (array_ty.isTuple(mod)) { for (resolved_args, 0..) |arg, i| { @@ -19844,7 +19875,7 @@ fn zirArrayInit( const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern()); const index = try mod.intRef(Type.usize, i); - const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); + const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } return sema.makePtrConst(block, alloc); @@ -19858,26 +19889,26 @@ fn zirArrayInit( for (resolved_args, 0..) |arg, i| { const index = try mod.intRef(Type.usize, i); - const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); + const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref); _ = try block.addBinOp(.store, elem_ptr, arg); } return sema.makePtrConst(block, alloc); } - return block.addAggregateInit(array_ty, resolved_args); + const arr_ref = try block.addAggregateInit(array_ty, resolved_args); + return sema.coerce(block, result_ty, arr_ref, src); } fn zirArrayInitAnon( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - is_ref: bool, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); const operands = sema.code.refSlice(extra.end, extra.data.operands_len); - return sema.arrayInitAnon(block, src, operands, is_ref); + return sema.arrayInitAnon(block, src, operands, false); } fn arrayInitAnon( @@ -19997,14 +20028,14 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } -fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; const ty_src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; - const aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { + const wrapped_aggregate_ty = sema.resolveType(block, ty_src, extra.container_type) catch |err| switch (err) { // Since this is a ZIR instruction that returns a type, encountering // generic poison should not result in a failed compilation, but the // generic poison type. This prevents unnecessary failures when @@ -20012,6 +20043,7 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A error.GenericPoison => return .generic_poison_type, else => |e| return e, }; + const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(mod); const zir_field_name = sema.code.nullTerminatedString(extra.name_start); const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); @@ -20033,7 +20065,10 @@ fn fieldType( switch (cur_ty.zigTypeTag(mod)) { .Struct => switch (ip.indexToKey(cur_ty.toIntern())) { .anon_struct_type => |anon_struct| { - const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); + const field_index = if (anon_struct.names.len == 0) + try sema.tupleFieldIndex(block, cur_ty, field_name, field_src) + else + try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); return Air.internedToRef(anon_struct.types.get(ip)[field_index]); }, .struct_type => |struct_type| { @@ -21620,7 +21655,8 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src); const ptr_ty = dest_ty.scalarType(mod); - try sema.checkPtrType(block, src, ptr_ty); + try sema.checkPtrType(block, src, ptr_ty, true); + const elem_ty = ptr_ty.elemType2(mod); const ptr_align = try ptr_ty.ptrAlignmentAdvanced(mod, sema); @@ -21860,7 +21896,7 @@ fn ptrCastFull( const mod = sema.mod; const operand_ty = sema.typeOf(operand); - try sema.checkPtrType(block, src, dest_ty); + try sema.checkPtrType(block, src, dest_ty, true); try sema.checkPtrOperand(block, operand_src, operand_ty); const src_info = operand_ty.ptrInfo(mod); @@ -22668,10 +22704,11 @@ fn checkPtrType( block: *Block, ty_src: LazySrcLoc, ty: Type, + allow_slice: bool, ) CompileError!void { const mod = sema.mod; switch (ty.zigTypeTag(mod)) { - .Pointer => return, + .Pointer => if (allow_slice or !ty.isSlice(mod)) return, .Fn => { const msg = msg: { const msg = try sema.errMsg( @@ -29577,13 +29614,6 @@ fn storePtr2( return; } - if (air_tag == .bitcast) { - // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr` - // to avoid calling `requireRuntimeBlock` for the dummy block. - _ = try block.addBinOp(.store, ptr, operand); - return; - } - try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(elem_ty); @@ -29719,6 +29749,7 @@ fn storePtrVal( switch (mut_kit.pointee) { .direct => |val_ptr| { if (mut_kit.mut_decl.runtime_index == .comptime_field_ptr) { + val_ptr.* = (try val_ptr.intern(operand_ty, mod)).toValue(); if (!operand_val.eql(val_ptr.*, operand_ty, mod)) { // TODO use failWithInvalidComptimeFieldStore return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{}); diff --git a/src/Zir.zig b/src/Zir.zig index c3c2ee150c..47d1053292 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -242,10 +242,9 @@ pub const Inst = struct { /// Uses the `pl_node` union field with `Bin` payload. /// lhs is length, rhs is element type. vector_type, - /// Given an indexable type, returns the type of the element at given index. - /// Uses the `bin` union field. lhs is the indexable type, rhs is the index. - elem_type_index, - /// Given a pointer type, returns its element type. + /// Given a pointer type, returns its element type. Reaches through any optional or error + /// union types wrapping the pointer. Asserts that the underlying type is a pointer type. + /// Returns generic poison if the element type is `anyopaque`. /// Uses the `un_node` field. elem_type, /// Given an indexable pointer (slice, many-ptr, single-ptr-to-array), returns its @@ -353,11 +352,6 @@ pub const Inst = struct { /// `!=` /// Uses the `pl_node` union field. Payload is `Bin`. cmp_neq, - /// Coerces a result location pointer to a new element type. It is evaluated "backwards"- - /// as type coercion from the new element type to the old element type. - /// Uses the `pl_node` union field. Payload is `Bin`. - /// LHS is destination element type, RHS is result pointer. - coerce_result_ptr, /// Conditional branch. Splits control flow based on a boolean condition value. /// Uses the `pl_node` union field. AST node is an if, while, for, etc. /// Payload is `CondBr`. @@ -419,13 +413,6 @@ pub const Inst = struct { /// Payload is `Bin`. /// No OOB safety check is emitted. elem_ptr, - /// Same as `elem_ptr_node` except the index is stored immediately rather than - /// as a reference to another ZIR instruction. - /// Uses the `pl_node` union field. AST node is an element inside array initialization - /// syntax. Payload is `ElemPtrImm`. - /// This instruction has a way to set the result type to be a - /// single-pointer or a many-pointer. - elem_ptr_imm, /// Given an array, slice, or pointer, returns the element at the provided index. /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`. elem_val_node, @@ -463,8 +450,6 @@ pub const Inst = struct { /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. field_ptr, - /// Same as `field_ptr` but used for struct init. - field_ptr_init, /// Given a struct or object that contains virtual fields, returns the named field. /// The field name is stored in string_bytes. Used by a.b syntax. /// This instruction also accepts a pointer. @@ -688,84 +673,123 @@ pub const Inst = struct { /// A switch expression. Uses the `pl_node` union field. /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer. switch_block_ref, - /// Given a - /// *A returns *A - /// *E!A returns *A - /// *?A returns *A - /// Uses the `un_node` field. - array_base_ptr, - /// Given a - /// *S returns *S - /// *E!S returns *S - /// *?S returns *S - /// Uses the `un_node` field. - field_base_ptr, - /// Given a type, strips all optional and error union types wrapping it. - /// e.g. `E!?u32` becomes `u32`, `[]u8` becomes `[]u8`. - /// Uses the `un_node` field. - opt_eu_base_ty, - /// Checks that the type supports array init syntax. - /// Returns the underlying indexable type (since the given type may be e.g. an optional). - /// Uses the `un_node` field. - validate_array_init_ty, - /// Checks that the type supports struct init syntax. - /// Returns the underlying struct type (since the given type may be e.g. an optional). - /// Uses the `un_node` field. - validate_struct_init_ty, - /// Given a set of `field_ptr` instructions, assumes they are all part of a struct - /// initialization expression, and emits compile errors for duplicate fields - /// as well as missing fields, if applicable. - /// This instruction asserts that there is at least one field_ptr instruction, - /// because it must use one of them to find out the struct type. - /// Uses the `pl_node` field. Payload is `Block`. - validate_struct_init, - /// Given a set of `elem_ptr_imm` instructions, assumes they are all part of an - /// array initialization expression, and emits a compile error if the number of - /// elements does not match the array type. - /// This instruction asserts that there is at least one `elem_ptr_imm` instruction, - /// because it must use one of them to find out the array type. - /// Uses the `pl_node` field. Payload is `Block`. - validate_array_init, /// Check that operand type supports the dereference operand (.*). /// Uses the `un_node` field. validate_deref, /// Check that the operand's type is an array or tuple with the given number of elements. /// Uses the `pl_node` field. Payload is `ValidateDestructure`. validate_destructure, - /// A struct literal with a specified type, with no fields. - /// Uses the `un_node` field. - struct_init_empty, - /// Given a struct or union, and a field name as a string index, - /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`. - field_type, /// Given a struct or union, and a field name as a Ref, /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`. field_type_ref, - /// Finalizes a typed struct or union initialization, performs validation, and returns the - /// struct or union value. - /// Uses the `pl_node` field. Payload is `StructInit`. - struct_init, - /// Struct initialization syntax, make the result a pointer. - /// Uses the `pl_node` field. Payload is `StructInit`. - struct_init_ref, - /// Struct initialization without a type. + /// Given a pointer, initializes all error unions and optionals in the pointee to payloads, + /// returning the base payload pointer. For instance, converts *E!?T into a valid *T + /// (clobbering any existing error or null value). + /// Uses the `un_node` field. + opt_eu_base_ptr_init, + /// Coerce a given value such that when a reference is taken, the resulting pointer will be + /// coercible to the given type. For instance, given a value of type 'u32' and the pointer + /// type '*u64', coerces the value to a 'u64'. Asserts that the type is a pointer type. + /// Uses the `pl_node` field. Payload is `Bin`. + /// LHS is the pointer type, RHS is the value. + coerce_ptr_elem_ty, + /// Given a type, validate that it is a pointer type suitable for return from the address-of + /// operator. Emit a compile error if not. + /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type. + validate_ref_ty, + + // The following tags all relate to struct initialization expressions. + + /// A struct literal with a specified explicit type, with no fields. + /// Uses the `un_node` field. + struct_init_empty, + /// An anonymous struct literal with a known result type, with no fields. + /// Uses the `un_node` field. + struct_init_empty_result, + /// An anonymous struct literal with no fields, returned by reference, with a known result + /// type for the pointer. Asserts that the type is a pointer. + /// Uses the `un_node` field. + struct_init_empty_ref_result, + /// Struct initialization without a type. Creates a value of an anonymous struct type. /// Uses the `pl_node` field. Payload is `StructInitAnon`. struct_init_anon, - /// Anonymous struct initialization syntax, make the result a pointer. - /// Uses the `pl_node` field. Payload is `StructInitAnon`. - struct_init_anon_ref, - /// Array initialization syntax. - /// Uses the `pl_node` field. Payload is `MultiOp`. - array_init, - /// Anonymous array initialization syntax. + /// Finalizes a typed struct or union initialization, performs validation, and returns the + /// struct or union value. The given type must be validated prior to this instruction, using + /// `validate_struct_init_ty` or `validate_struct_init_result_ty`. If the given type is + /// generic poison, this is downgraded to an anonymous initialization. + /// Uses the `pl_node` field. Payload is `StructInit`. + struct_init, + /// Struct initialization syntax, make the result a pointer. Equivalent to `struct_init` + /// followed by `ref` - this ZIR tag exists as an optimization for a common pattern. + /// Uses the `pl_node` field. Payload is `StructInit`. + struct_init_ref, + /// Checks that the type supports struct init syntax. Always returns void. + /// Uses the `un_node` field. + validate_struct_init_ty, + /// Like `validate_struct_init_ty`, but additionally accepts types which structs coerce to. + /// Used on the known result type of a struct init expression. Always returns void. + /// Uses the `un_node` field. + validate_struct_init_result_ty, + /// Given a set of `struct_init_field_ptr` instructions, assumes they are all part of a + /// struct initialization expression, and emits compile errors for duplicate fields as well + /// as missing fields, if applicable. + /// This instruction asserts that there is at least one struct_init_field_ptr instruction, + /// because it must use one of them to find out the struct type. + /// Uses the `pl_node` field. Payload is `Block`. + validate_ptr_struct_init, + /// Given a type being used for a struct initialization expression, returns the type of the + /// field with the given name. + /// Uses the `pl_node` field. Payload is `FieldType`. + struct_init_field_type, + /// Given a pointer being used as the result pointer of a struct initialization expression, + /// return a pointer to the field of the given name. + /// Uses the `pl_node` field. The AST node is the field initializer. Payload is Field. + struct_init_field_ptr, + + // The following tags all relate to array initialization expressions. + + /// Array initialization without a type. Creates a value of a tuple type. /// Uses the `pl_node` field. Payload is `MultiOp`. array_init_anon, - /// Array initialization syntax, make the result a pointer. - /// Uses the `pl_node` field. Payload is `MultiOp`. + /// Array initialization syntax with a known type. The given type must be validated prior to + /// this instruction, using some `validate_array_init_*_ty` instruction. + /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type. + array_init, + /// Array initialization syntax, make the result a pointer. Equivalent to `array_init` + /// followed by `ref`- this ZIR tag exists as an optimization for a common pattern. + /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type. array_init_ref, - /// Anonymous array initialization syntax, make the result a pointer. - /// Uses the `pl_node` field. Payload is `MultiOp`. - array_init_anon_ref, + /// Checks that the type supports array init syntax. Always returns void. + /// Uses the `pl_node` field. Payload is `ArrayInit`. + validate_array_init_ty, + /// Like `validate_array_init_ty`, but additionally accepts types which arrays coerce to. + /// Used on the known result type of an array init expression. Always returns void. + /// Uses the `pl_node` field. Payload is `ArrayInit`. + validate_array_init_result_ty, + /// Given a pointer or slice type and an element count, return the expected type of an array + /// initializer such that a pointer to the initializer has the given pointer type, checking + /// that this type supports array init syntax and emitting a compile error if not. Preserves + /// error union and optional wrappers on the array type, if any. + /// Asserts that the given type is a pointer or slice type. + /// Uses the `pl_node` field. Payload is `ArrayInitRefTy`. + validate_array_init_ref_ty, + /// Given a set of `array_init_elem_ptr` instructions, assumes they are all part of an array + /// initialization expression, and emits a compile error if the number of elements does not + /// match the array type. + /// This instruction asserts that there is at least one `array_init_elem_ptr` instruction, + /// because it must use one of them to find out the array type. + /// Uses the `pl_node` field. Payload is `Block`. + validate_ptr_array_init, + /// Given a type being used for an array initialization expression, returns the type of the + /// element at the given index. + /// Uses the `bin` union field. lhs is the indexable type, rhs is the index. + array_init_elem_type, + /// Given a pointer being used as the result pointer of an array initialization expression, + /// return a pointer to the element at the given index. + /// Uses the `pl_node` union field. AST node is an element inside array initialization + /// syntax. Payload is `ElemPtrImm`. + array_init_elem_ptr, + /// Implements the `@unionInit` builtin. /// Uses the `pl_node` field. Payload is `UnionInit`. union_init, @@ -1038,7 +1062,6 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, - .elem_type_index, .elem_type, .indexable_ptr_elem_type, .vector_elem_type, @@ -1066,7 +1089,6 @@ pub const Inst = struct { .cmp_gte, .cmp_gt, .cmp_neq, - .coerce_result_ptr, .error_set_decl, .error_set_decl_anon, .error_set_decl_func, @@ -1082,7 +1104,6 @@ pub const Inst = struct { .elem_ptr, .elem_val, .elem_ptr_node, - .elem_ptr_imm, .elem_val_node, .elem_val_imm, .ensure_result_used, @@ -1091,7 +1112,6 @@ pub const Inst = struct { .@"export", .export_value, .field_ptr, - .field_ptr_init, .field_val, .field_ptr_named, .field_val_named, @@ -1154,25 +1174,9 @@ pub const Inst = struct { .set_eval_branch_quota, .switch_block, .switch_block_ref, - .array_base_ptr, - .field_base_ptr, - .validate_array_init_ty, - .validate_struct_init_ty, - .validate_struct_init, - .validate_array_init, .validate_deref, .validate_destructure, - .struct_init_empty, - .struct_init, - .struct_init_ref, - .struct_init_anon, - .struct_init_anon_ref, - .array_init, - .array_init_anon, - .array_init_ref, - .array_init_anon_ref, .union_init, - .field_type, .field_type_ref, .enum_from_int, .int_from_enum, @@ -1254,7 +1258,29 @@ pub const Inst = struct { .save_err_ret_index, .restore_err_ret_index, .for_len, - .opt_eu_base_ty, + .opt_eu_base_ptr_init, + .coerce_ptr_elem_ty, + .struct_init_empty, + .struct_init_empty_result, + .struct_init_empty_ref_result, + .struct_init_anon, + .struct_init, + .struct_init_ref, + .validate_struct_init_ty, + .validate_struct_init_result_ty, + .validate_ptr_struct_init, + .struct_init_field_type, + .struct_init_field_ptr, + .array_init_anon, + .array_init, + .array_init_ref, + .validate_array_init_ty, + .validate_array_init_result_ty, + .validate_array_init_ref_ty, + .validate_ptr_array_init, + .array_init_elem_type, + .array_init_elem_ptr, + .validate_ref_ty, => false, .@"break", @@ -1307,10 +1333,6 @@ pub const Inst = struct { .store_node, .store_to_inferred_ptr, .resolve_inferred_alloc, - .validate_array_init_ty, - .validate_struct_init_ty, - .validate_struct_init, - .validate_array_init, .validate_deref, .validate_destructure, .@"export", @@ -1323,6 +1345,13 @@ pub const Inst = struct { .defer_err_code, .restore_err_ret_index, .save_err_ret_index, + .validate_struct_init_ty, + .validate_struct_init_result_ty, + .validate_ptr_struct_init, + .validate_array_init_ty, + .validate_array_init_result_ty, + .validate_ptr_array_init, + .validate_ref_ty, => true, .param, @@ -1346,7 +1375,6 @@ pub const Inst = struct { .array_type, .array_type_sentinel, .vector_type, - .elem_type_index, .elem_type, .indexable_ptr_elem_type, .vector_elem_type, @@ -1374,7 +1402,6 @@ pub const Inst = struct { .cmp_gte, .cmp_gt, .cmp_neq, - .coerce_result_ptr, .error_set_decl, .error_set_decl_anon, .error_set_decl_func, @@ -1385,11 +1412,9 @@ pub const Inst = struct { .elem_ptr, .elem_val, .elem_ptr_node, - .elem_ptr_imm, .elem_val_node, .elem_val_imm, .field_ptr, - .field_ptr_init, .field_val, .field_ptr_named, .field_val_named, @@ -1447,19 +1472,7 @@ pub const Inst = struct { .typeof_log2_int_type, .switch_block, .switch_block_ref, - .array_base_ptr, - .field_base_ptr, - .struct_init_empty, - .struct_init, - .struct_init_ref, - .struct_init_anon, - .struct_init_anon_ref, - .array_init, - .array_init_anon, - .array_init_ref, - .array_init_anon_ref, .union_init, - .field_type, .field_type_ref, .enum_from_int, .int_from_enum, @@ -1546,7 +1559,22 @@ pub const Inst = struct { .for_len, .@"try", .try_ptr, - .opt_eu_base_ty, + .opt_eu_base_ptr_init, + .coerce_ptr_elem_ty, + .struct_init_empty, + .struct_init_empty_result, + .struct_init_empty_ref_result, + .struct_init_anon, + .struct_init, + .struct_init_ref, + .struct_init_field_type, + .struct_init_field_ptr, + .array_init_anon, + .array_init, + .array_init_ref, + .validate_array_init_ref_ty, + .array_init_elem_type, + .array_init_elem_ptr, => false, .extended => switch (data.extended.opcode) { @@ -1580,7 +1608,6 @@ pub const Inst = struct { .array_type = .pl_node, .array_type_sentinel = .pl_node, .vector_type = .pl_node, - .elem_type_index = .bin, .elem_type = .un_node, .indexable_ptr_elem_type = .un_node, .vector_elem_type = .un_node, @@ -1612,7 +1639,6 @@ pub const Inst = struct { .cmp_gte = .pl_node, .cmp_gt = .pl_node, .cmp_neq = .pl_node, - .coerce_result_ptr = .pl_node, .condbr = .pl_node, .condbr_inline = .pl_node, .@"try" = .pl_node, @@ -1631,7 +1657,6 @@ pub const Inst = struct { .div = .pl_node, .elem_ptr = .pl_node, .elem_ptr_node = .pl_node, - .elem_ptr_imm = .pl_node, .elem_val = .pl_node, .elem_val_node = .pl_node, .elem_val_imm = .elem_val_imm, @@ -1643,7 +1668,6 @@ pub const Inst = struct { .@"export" = .pl_node, .export_value = .pl_node, .field_ptr = .pl_node, - .field_ptr_init = .pl_node, .field_val = .pl_node, .field_ptr_named = .pl_node, .field_val_named = .pl_node, @@ -1701,30 +1725,16 @@ pub const Inst = struct { .enum_literal = .str_tok, .switch_block = .pl_node, .switch_block_ref = .pl_node, - .array_base_ptr = .un_node, - .field_base_ptr = .un_node, - .opt_eu_base_ty = .un_node, - .validate_array_init_ty = .pl_node, - .validate_struct_init_ty = .un_node, - .validate_struct_init = .pl_node, - .validate_array_init = .pl_node, .validate_deref = .un_node, .validate_destructure = .pl_node, - .struct_init_empty = .un_node, - .field_type = .pl_node, .field_type_ref = .pl_node, - .struct_init = .pl_node, - .struct_init_ref = .pl_node, - .struct_init_anon = .pl_node, - .struct_init_anon_ref = .pl_node, - .array_init = .pl_node, - .array_init_anon = .pl_node, - .array_init_ref = .pl_node, - .array_init_anon_ref = .pl_node, .union_init = .pl_node, .type_info = .un_node, .size_of = .un_node, .bit_size_of = .un_node, + .opt_eu_base_ptr_init = .un_node, + .coerce_ptr_elem_ty = .pl_node, + .validate_ref_ty = .un_tok, .int_from_ptr = .un_node, .compile_error = .un_node, @@ -1826,6 +1836,27 @@ pub const Inst = struct { .save_err_ret_index = .save_err_ret_index, .restore_err_ret_index = .restore_err_ret_index, + .struct_init_empty = .un_node, + .struct_init_empty_result = .un_node, + .struct_init_empty_ref_result = .un_node, + .struct_init_anon = .pl_node, + .struct_init = .pl_node, + .struct_init_ref = .pl_node, + .validate_struct_init_ty = .un_node, + .validate_struct_init_result_ty = .un_node, + .validate_ptr_struct_init = .pl_node, + .struct_init_field_type = .pl_node, + .struct_init_field_ptr = .pl_node, + .array_init_anon = .pl_node, + .array_init = .pl_node, + .array_init_ref = .pl_node, + .validate_array_init_ty = .pl_node, + .validate_array_init_result_ty = .pl_node, + .validate_array_init_ref_ty = .pl_node, + .validate_ptr_array_init = .pl_node, + .array_init_elem_type = .bin, + .array_init_elem_ptr = .pl_node, + .extended = .extended, }); }; @@ -2771,6 +2802,11 @@ pub const Inst = struct { }; }; + pub const ArrayInitRefTy = struct { + ptr_ty: Ref, + elem_count: u32, + }; + pub const Field = struct { lhs: Ref, /// Offset into `string_bytes`. @@ -3064,9 +3100,10 @@ pub const Inst = struct { fields_len: u32, pub const Item = struct { - /// The `field_type` ZIR instruction for this field init. + /// The `struct_init_field_type` ZIR instruction for this field init. field_type: Index, - /// The field init expression to be used as the field value. + /// The field init expression to be used as the field value. This value will be coerced + /// to the field type if not already. init: Ref, }; }; diff --git a/src/print_zir.zig b/src/print_zir.zig index 07b1937771..bef5f2c815 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -205,8 +205,6 @@ const Writer = struct { .store_to_inferred_ptr, => try self.writeBin(stream, inst), - .elem_type_index => try self.writeElemTypeIndex(stream, inst), - .alloc, .alloc_mut, .alloc_comptime_mut, @@ -241,7 +239,6 @@ const Writer = struct { .is_non_err_ptr, .ret_is_non_err, .typeof, - .struct_init_empty, .type_info, .size_of, .bit_size_of, @@ -281,18 +278,16 @@ const Writer = struct { .bit_reverse, .@"resume", .@"await", - .array_base_ptr, - .field_base_ptr, - .validate_struct_init_ty, .make_ptr_const, .validate_deref, .check_comptime_control_flow, - .opt_eu_base_ty, + .opt_eu_base_ptr_init, => try self.writeUnNode(stream, inst), .ref, .ret_implicit, .closure_capture, + .validate_ref_ty, => try self.writeUnTok(stream, inst), .bool_br_and, @@ -300,7 +295,6 @@ const Writer = struct { => try self.writeBoolBr(stream, inst), .validate_destructure => try self.writeValidateDestructure(stream, inst), - .validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst), .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), .ptr_type => try self.writePtrType(stream, inst), .int => try self.writeInt(stream, inst), @@ -316,12 +310,6 @@ const Writer = struct { .@"break", .break_inline, => try self.writeBreak(stream, inst), - .array_init, - .array_init_ref, - => try self.writeArrayInit(stream, inst), - .array_init_anon, - .array_init_anon_ref, - => try self.writeArrayInitAnon(stream, inst), .slice_start => try self.writeSliceStart(stream, inst), .slice_end => try self.writeSliceEnd(stream, inst), @@ -330,10 +318,44 @@ const Writer = struct { .union_init => try self.writeUnionInit(stream, inst), + // Struct inits + + .struct_init_empty, + .struct_init_empty_result, + .struct_init_empty_ref_result, + => try self.writeUnNode(stream, inst), + + .struct_init_anon => try self.writeStructInitAnon(stream, inst), + .struct_init, .struct_init_ref, => try self.writeStructInit(stream, inst), + .validate_struct_init_ty, + .validate_struct_init_result_ty, + => try self.writeUnNode(stream, inst), + + .validate_ptr_struct_init => try self.writeBlock(stream, inst), + .struct_init_field_type => try self.writeStructInitFieldType(stream, inst), + .struct_init_field_ptr => try self.writePlNodeField(stream, inst), + + // Array inits + + .array_init_anon => try self.writeArrayInitAnon(stream, inst), + + .array_init, + .array_init_ref, + => try self.writeArrayInit(stream, inst), + + .validate_array_init_ty, + .validate_array_init_result_ty, + => try self.writeValidateArrayInitTy(stream, inst), + + .validate_array_init_ref_ty => try self.writeValidateArrayInitRefTy(stream, inst), + .validate_ptr_array_init => try self.writeBlock(stream, inst), + .array_init_elem_type => try self.writeArrayInitElemType(stream, inst), + .array_init_elem_ptr => try self.writeArrayInitElemPtr(stream, inst), + .atomic_load => try self.writeAtomicLoad(stream, inst), .atomic_store => try self.writeAtomicStore(stream, inst), .atomic_rmw => try self.writeAtomicRmw(stream, inst), @@ -342,11 +364,6 @@ const Writer = struct { .field_parent_ptr => try self.writeFieldParentPtr(stream, inst), .builtin_call => try self.writeBuiltinCall(stream, inst), - .struct_init_anon, - .struct_init_anon_ref, - => try self.writeStructInitAnon(stream, inst), - - .field_type => try self.writeFieldType(stream, inst), .field_type_ref => try self.writeFieldTypeRef(stream, inst), .add, @@ -409,16 +426,14 @@ const Writer = struct { .elem_val_node, .elem_ptr, .elem_val, - .coerce_result_ptr, .array_type, + .coerce_ptr_elem_ty, => try self.writePlNodeBin(stream, inst), .for_len => try self.writePlNodeMultiOp(stream, inst), .elem_val_imm => try self.writeElemValImm(stream, inst), - .elem_ptr_imm => try self.writeElemPtrImm(stream, inst), - .@"export" => try self.writePlNodeExport(stream, inst), .export_value => try self.writePlNodeExportValue(stream, inst), @@ -430,8 +445,6 @@ const Writer = struct { .block_inline, .suspend_block, .loop, - .validate_struct_init, - .validate_array_init, .c_import, .typeof_builtin, => try self.writeBlock(stream, inst), @@ -452,9 +465,8 @@ const Writer = struct { .switch_block_ref, => try self.writeSwitchBlock(stream, inst), - .field_ptr, - .field_ptr_init, .field_val, + .field_ptr, => try self.writePlNodeField(stream, inst), .field_ptr_named, @@ -617,7 +629,7 @@ const Writer = struct { try stream.writeByte(')'); } - fn writeElemTypeIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeArrayInitElemType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].bin; try self.writeInstRef(stream, inst_data.lhs); try stream.print(", {d})", .{@intFromEnum(inst_data.rhs)}); @@ -972,7 +984,7 @@ const Writer = struct { try stream.print(", {d})", .{inst_data.idx}); } - fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeArrayInitElemPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; @@ -1004,6 +1016,16 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeValidateArrayInitRefTy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.ArrayInitRefTy, inst_data.payload_index).data; + + try self.writeInstRef(stream, extra.ptr_ty); + try stream.writeAll(", "); + try stream.print(", {}) ", .{extra.elem_count}); + try self.writeSrc(stream, inst_data.src()); + } + fn writeStructInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); @@ -1134,7 +1156,7 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } - fn writeFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + fn writeStructInitFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; try self.writeInstRef(stream, extra.container_type); diff --git a/src/type.zig b/src/type.zig index 0ed8c394fc..a4f85ae946 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3182,6 +3182,17 @@ pub const Type = struct { }; } + /// Traverses optional child types and error union payloads until the type + /// is not a pointer. For `E!?u32`, returns `u32`; for `*u8`, returns `*u8`. + pub fn optEuBaseType(ty: Type, mod: *Module) Type { + var cur = ty; + while (true) switch (cur.zigTypeTag(mod)) { + .Optional => cur = cur.optionalChild(mod), + .ErrorUnion => cur = cur.errorUnionPayload(mod), + else => return cur, + }; + } + pub const @"u1": Type = .{ .ip_index = .u1_type }; pub const @"u8": Type = .{ .ip_index = .u8_type }; pub const @"u16": Type = .{ .ip_index = .u16_type }; diff --git a/test/behavior/array.zig b/test/behavior/array.zig index a291d3a7b7..40224e5026 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -780,3 +780,37 @@ test "runtime side-effects in comptime-known array init" { try expectEqual([4]u4{ 1, 2, 4, 8 }, init); try expectEqual(@as(u4, std.math.maxInt(u4)), side_effects); } + +test "slice initialized through reference to anonymous array init provides result types" { + var my_u32: u32 = 123; + var my_u64: u64 = 456; + const foo: []const u16 = &.{ + @intCast(my_u32), + @intCast(my_u64), + @truncate(my_u32), + @truncate(my_u64), + }; + try std.testing.expectEqualSlices(u16, &.{ 123, 456, 123, 456 }, foo); +} + +test "pointer to array initialized through reference to anonymous array init provides result types" { + var my_u32: u32 = 123; + var my_u64: u64 = 456; + const foo: *const [4]u16 = &.{ + @intCast(my_u32), + @intCast(my_u64), + @truncate(my_u32), + @truncate(my_u64), + }; + try std.testing.expectEqualSlices(u16, &.{ 123, 456, 123, 456 }, foo); +} + +test "tuple initialized through reference to anonymous array init provides result types" { + const Tuple = struct { u64, *const u32 }; + const foo: *const Tuple = &.{ + @intCast(12345), + @ptrFromInt(0x1000), + }; + try expect(foo[0] == 12345); + try expect(@intFromPtr(foo[1]) == 0x1000); +} diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index f7ab8695c0..dcd7a8b426 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -2493,3 +2493,29 @@ test "@as does not corrupt values with incompatible representations" { }); try std.testing.expectApproxEqAbs(@as(f32, 1.23), x, 0.001); } + +test "result information is preserved through many nested structures" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + const E = error{Foo}; + const T = *const ?E!struct { x: ?*const E!?u8 }; + + var val: T = &.{ .x = &@truncate(0x1234) }; + + const struct_val = val.*.? catch unreachable; + const int_val = (struct_val.x.?.* catch unreachable).?; + + try expect(int_val == 0x34); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 3122ae23d5..1cc46fcd1a 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -548,3 +548,21 @@ test "pointer to array has explicit alignment" { const casted = S.func(&bases); try expect(casted[0].a == 2); } + +test "result type preserved through multiple references" { + const S = struct { x: u32 }; + var my_u64: u64 = 12345; + const foo: *const *const *const S = &&&.{ + .x = @intCast(my_u64), + }; + try expect(foo.*.*.*.x == 12345); +} + +test "result type found through optional pointer" { + const ptr1: ?*const u32 = &@intCast(123); + const ptr2: ?[]const u8 = &.{ @intCast(123), @truncate(0xABCD) }; + try expect(ptr1.?.* == 123); + try expect(ptr2.?.len == 2); + try expect(ptr2.?[0] == 123); + try expect(ptr2.?[1] == 0xCD); +} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 1ba9e946d1..393f23ec6e 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1760,3 +1760,18 @@ test "runtime side-effects in comptime-known struct init" { try expectEqual(S{ .a = 1, .b = 2, .c = 4, .d = 8 }, init); try expectEqual(@as(u4, std.math.maxInt(u4)), side_effects); } + +test "pointer to struct initialized through reference to anonymous initializer provides result types" { + const S = struct { a: u8, b: u16, c: *const anyopaque }; + var my_u16: u16 = 0xABCD; + const s: *const S = &.{ + // intentionally out of order + .c = @ptrCast("hello"), + .b = my_u16, + .a = @truncate(my_u16), + }; + try expect(s.a == 0xCD); + try expect(s.b == 0xABCD); + const str: *const [5]u8 = @ptrCast(s.c); + try std.testing.expectEqualSlices(u8, "hello", str); +} diff --git a/test/cases/compile_errors/anytype_param_requires_comptime.zig b/test/cases/compile_errors/anytype_param_requires_comptime.zig index 6fd86b0de3..ddcb0a7b9b 100644 --- a/test/cases/compile_errors/anytype_param_requires_comptime.zig +++ b/test/cases/compile_errors/anytype_param_requires_comptime.zig @@ -16,7 +16,5 @@ pub export fn entry() void { // backend=stage2 // target=native // -// :7:14: error: runtime-known argument passed to parameter of comptime-only type -// :9:12: note: declared here -// :4:16: note: struct requires comptime because of this field -// :4:16: note: types are not available at runtime +// :7:25: error: unable to resolve comptime value +// :7:25: note: initializer of comptime only struct must be comptime-known diff --git a/test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig b/test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig index 530e5ffb74..6a7de57352 100644 --- a/test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig +++ b/test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig @@ -18,6 +18,6 @@ export fn entry() void { // backend=stage2 // target=native // -// :11:27: error: expected type 'u8', found '?u8' -// :11:27: note: cannot convert optional to payload type -// :11:27: note: consider using '.?', 'orelse', or 'if' +// :11:20: error: expected type 'u8', found '?u8' +// :11:20: note: cannot convert optional to payload type +// :11:20: note: consider using '.?', 'orelse', or 'if' diff --git a/test/cases/compile_errors/cast_without_result_type_due_to_anyopaque_pointer.zig b/test/cases/compile_errors/cast_without_result_type_due_to_anyopaque_pointer.zig new file mode 100644 index 0000000000..844444ce36 --- /dev/null +++ b/test/cases/compile_errors/cast_without_result_type_due_to_anyopaque_pointer.zig @@ -0,0 +1,21 @@ +export fn foo() void { + const x: *const anyopaque = &@intCast(123); + _ = x; +} +export fn bar() void { + const x: *const anyopaque = &.{ + .x = @intCast(123), + }; + _ = x; +} + +// error +// backend=stage2 +// target=native +// +// :2:34: error: @intCast must have a known result type +// :2:34: note: result type is unknown due to opaque pointer type +// :2:34: note: use @as to provide explicit result type +// :7:14: error: @intCast must have a known result type +// :6:35: note: result type is unknown due to opaque pointer type +// :7:14: note: use @as to provide explicit result type diff --git a/test/cases/compile_errors/cast_without_result_type_due_to_generic_parameter.zig b/test/cases/compile_errors/cast_without_result_type_due_to_generic_parameter.zig index a9006c5352..c0f8f8b90e 100644 --- a/test/cases/compile_errors/cast_without_result_type_due_to_generic_parameter.zig +++ b/test/cases/compile_errors/cast_without_result_type_due_to_generic_parameter.zig @@ -10,6 +10,11 @@ export fn c() void { export fn d() void { bar(@floatFromInt(123)); } +export fn f() void { + bar(.{ + .x = @intCast(123), + }); +} fn bar(_: anytype) void {} @@ -18,14 +23,17 @@ fn bar(_: anytype) void {} // target=native // // :2:9: error: @ptrFromInt must have a known result type -// :2:9: note: result type is unknown due to anytype parameter +// :2:8: note: result type is unknown due to anytype parameter // :2:9: note: use @as to provide explicit result type // :5:9: error: @ptrCast must have a known result type -// :5:9: note: result type is unknown due to anytype parameter +// :5:8: note: result type is unknown due to anytype parameter // :5:9: note: use @as to provide explicit result type // :8:9: error: @intCast must have a known result type -// :8:9: note: result type is unknown due to anytype parameter +// :8:8: note: result type is unknown due to anytype parameter // :8:9: note: use @as to provide explicit result type // :11:9: error: @floatFromInt must have a known result type -// :11:9: note: result type is unknown due to anytype parameter +// :11:8: note: result type is unknown due to anytype parameter // :11:9: note: use @as to provide explicit result type +// :15:14: error: @intCast must have a known result type +// :14:8: note: result type is unknown due to anytype parameter +// :15:14: note: use @as to provide explicit result type diff --git a/test/cases/compile_errors/for_invalid_ranges.zig b/test/cases/compile_errors/for_invalid_ranges.zig index bf93cb07f5..7a1506efac 100644 --- a/test/cases/compile_errors/for_invalid_ranges.zig +++ b/test/cases/compile_errors/for_invalid_ranges.zig @@ -31,5 +31,6 @@ export fn e() void { // :2:13: error: expected type 'usize', found '*const [5:0]u8' // :7:10: error: type 'usize' cannot represent integer value '-1' // :12:10: error: expected type 'usize', found '*const [5:0]u8' -// :17:13: error: expected type 'usize', found '*const struct{comptime comptime_int = 97, comptime comptime_int = 98, comptime comptime_int = 99}' +// :17:13: error: expected type 'usize', found pointer +// :17:13: note: address-of operator always returns a pointer // :22:20: error: overflow of integer type 'usize' with value '-1' diff --git a/test/cases/compile_errors/invalid_store_to_comptime_field.zig b/test/cases/compile_errors/invalid_store_to_comptime_field.zig index b2605f9158..672eea8ddc 100644 --- a/test/cases/compile_errors/invalid_store_to_comptime_field.zig +++ b/test/cases/compile_errors/invalid_store_to_comptime_field.zig @@ -71,8 +71,8 @@ pub export fn entry8() void { // target=native // backend=stage2 // -// :6:19: error: value stored in comptime field does not match the default value of the field -// :14:19: error: value stored in comptime field does not match the default value of the field +// :6:9: error: value stored in comptime field does not match the default value of the field +// :14:9: error: value stored in comptime field does not match the default value of the field // :19:38: error: value stored in comptime field does not match the default value of the field // :31:19: error: value stored in comptime field does not match the default value of the field // :25:29: note: default value set here @@ -80,5 +80,6 @@ pub export fn entry8() void { // :35:29: note: default value set here // :45:12: error: value stored in comptime field does not match the default value of the field // :53:25: error: value stored in comptime field does not match the default value of the field -// :66:43: error: value stored in comptime field does not match the default value of the field -// :59:35: error: value stored in comptime field does not match the default value of the field +// :66:36: error: value stored in comptime field does not match the default value of the field +// :59:30: error: value stored in comptime field does not match the default value of the field +// :57:29: note: default value set here diff --git a/test/cases/compile_errors/missing_const_in_slice_with_nested_array_type.zig b/test/cases/compile_errors/missing_const_in_slice_with_nested_array_type.zig index 2f596db1ed..4043f305a0 100644 --- a/test/cases/compile_errors/missing_const_in_slice_with_nested_array_type.zig +++ b/test/cases/compile_errors/missing_const_in_slice_with_nested_array_type.zig @@ -15,4 +15,4 @@ export fn entry() void { // backend=llvm // target=native // -// :4:30: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32' +// :4:26: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32' diff --git a/test/cases/compile_errors/missing_else_clause.zig b/test/cases/compile_errors/missing_else_clause.zig index 8e4d4334b6..965d8e2177 100644 --- a/test/cases/compile_errors/missing_else_clause.zig +++ b/test/cases/compile_errors/missing_else_clause.zig @@ -39,4 +39,6 @@ export fn entry() void { // :8:25: note: type 'i32' here // :16:16: error: expected type 'tmp.h.T', found 'void' // :15:15: note: struct declared here -// :22:9: error: incompatible types: 'void' and 'tmp.k.T' +// :22:13: error: incompatible types: 'void' and 'tmp.k.T' +// :22:25: note: type 'void' here +// :24:13: note: type 'tmp.k.T' here diff --git a/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig b/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig index da7f2492d1..0ef37dd8c3 100644 --- a/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig +++ b/test/cases/compile_errors/pointer_attributes_checked_when_coercing_pointer_to_anon_literal.zig @@ -16,9 +16,9 @@ comptime { // backend=stage2 // target=native // -// :2:29: error: expected type '[][]const u8', found '*const struct{comptime *const [5:0]u8 = "hello", comptime *const [5:0]u8 = "world"}' +// :2:29: error: expected type '[][]const u8', found '*const [2][]const u8' // :2:29: note: cast discards const qualifier -// :6:31: error: expected type '*[2][]const u8', found '*const struct{comptime *const [5:0]u8 = "hello", comptime *const [5:0]u8 = "world"}' +// :6:31: error: expected type '*[2][]const u8', found '*const [2][]const u8' // :6:31: note: cast discards const qualifier -// :11:19: error: expected type '*tmp.S', found '*const struct{comptime a: comptime_int = 2}' +// :11:19: error: expected type '*tmp.S', found '*const tmp.S' // :11:19: note: cast discards const qualifier diff --git a/test/cases/compile_errors/reassign_to_array_parameter.zig b/test/cases/compile_errors/reassign_to_array_parameter.zig index 380fd62154..ec7db76cbd 100644 --- a/test/cases/compile_errors/reassign_to_array_parameter.zig +++ b/test/cases/compile_errors/reassign_to_array_parameter.zig @@ -9,4 +9,4 @@ export fn entry() void { // backend=llvm // target=native // -// :2:15: error: cannot assign to constant +// :2:5: error: cannot assign to constant diff --git a/test/cases/compile_errors/reassign_to_struct_parameter.zig b/test/cases/compile_errors/reassign_to_struct_parameter.zig index 560de215b5..7840a83413 100644 --- a/test/cases/compile_errors/reassign_to_struct_parameter.zig +++ b/test/cases/compile_errors/reassign_to_struct_parameter.zig @@ -12,4 +12,4 @@ export fn entry() void { // backend=stage2 // target=native // -// :5:10: error: cannot assign to constant +// :5:5: error: cannot assign to constant diff --git a/test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig b/test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig index 1b951528bb..98a8181c62 100644 --- a/test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig +++ b/test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig @@ -18,6 +18,6 @@ export fn entry() void { // backend=stage2 // target=native // -// :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' -// :12:25: note: cannot convert error union to payload type -// :12:25: note: consider using 'try', 'catch', or 'if' +// :12:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' +// :12:15: note: cannot convert error union to payload type +// :12:15: note: consider using 'try', 'catch', or 'if' diff --git a/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig b/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig index 26c1a8d9cf..ce140b64a8 100644 --- a/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig +++ b/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig @@ -15,6 +15,6 @@ pub const Container = struct { // backend=stage2 // target=native // -// :3:36: error: expected type 'i32', found '?i32' -// :3:36: note: cannot convert optional to payload type -// :3:36: note: consider using '.?', 'orelse', or 'if' +// :3:23: error: expected type 'i32', found '?i32' +// :3:23: note: cannot convert optional to payload type +// :3:23: note: consider using '.?', 'orelse', or 'if' diff --git a/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig b/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig index 471f9cca04..e813ea1d55 100644 --- a/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig +++ b/test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig @@ -15,6 +15,6 @@ pub const Container = struct { // backend=stage2 // target=native // -// :3:36: error: expected type 'i32', found '?i32' -// :3:36: note: cannot convert optional to payload type -// :3:36: note: consider using '.?', 'orelse', or 'if' +// :3:23: error: expected type 'i32', found '?i32' +// :3:23: note: cannot convert optional to payload type +// :3:23: note: consider using '.?', 'orelse', or 'if' diff --git a/test/cases/compile_errors/return_incompatible_generic_struct.zig b/test/cases/compile_errors/return_incompatible_generic_struct.zig index f46d44d53f..acfed93479 100644 --- a/test/cases/compile_errors/return_incompatible_generic_struct.zig +++ b/test/cases/compile_errors/return_incompatible_generic_struct.zig @@ -18,3 +18,4 @@ export fn entry() void { // :8:18: error: expected type 'tmp.A(u32)', found 'tmp.B(u32)' // :5:12: note: struct declared here // :2:12: note: struct declared here +// :7:11: note: function return type declared here diff --git a/test/cases/compile_errors/runtime_assignment_to_comptime_struct_type.zig b/test/cases/compile_errors/runtime_assignment_to_comptime_struct_type.zig index 39e9662679..e99f093c95 100644 --- a/test/cases/compile_errors/runtime_assignment_to_comptime_struct_type.zig +++ b/test/cases/compile_errors/runtime_assignment_to_comptime_struct_type.zig @@ -12,5 +12,5 @@ export fn f() void { // backend=stage2 // target=native // -// :7:29: error: unable to resolve comptime value -// :7:29: note: initializer of comptime only struct must be comptime-known +// :7:23: error: unable to resolve comptime value +// :7:23: note: initializer of comptime only struct must be comptime-known diff --git a/test/cases/compile_errors/runtime_assignment_to_comptime_union_type.zig b/test/cases/compile_errors/runtime_assignment_to_comptime_union_type.zig index 71a490bc2f..b0528e17bd 100644 --- a/test/cases/compile_errors/runtime_assignment_to_comptime_union_type.zig +++ b/test/cases/compile_errors/runtime_assignment_to_comptime_union_type.zig @@ -12,5 +12,5 @@ export fn f() void { // backend=stage2 // target=native // -// :7:29: error: unable to resolve comptime value -// :7:29: note: initializer of comptime only union must be comptime-known +// :7:23: error: unable to resolve comptime value +// :7:23: note: initializer of comptime only union must be comptime-known diff --git a/test/cases/compile_errors/shift_amount_has_to_be_an_integer_type.zig b/test/cases/compile_errors/shift_amount_has_to_be_an_integer_type.zig index ed6935315b..6c5d152e28 100644 --- a/test/cases/compile_errors/shift_amount_has_to_be_an_integer_type.zig +++ b/test/cases/compile_errors/shift_amount_has_to_be_an_integer_type.zig @@ -7,4 +7,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :2:20: error: expected type 'comptime_int', found '*const u8' +// :2:20: error: expected type 'comptime_int', found pointer +// :2:20: note: address-of operator always returns a pointer diff --git a/test/cases/compile_errors/slice_sentinel_mismatch-1.zig b/test/cases/compile_errors/slice_sentinel_mismatch-1.zig index 60d8d9707a..23f45f1205 100644 --- a/test/cases/compile_errors/slice_sentinel_mismatch-1.zig +++ b/test/cases/compile_errors/slice_sentinel_mismatch-1.zig @@ -1,11 +1,18 @@ -export fn entry() void { +export fn entry1() void { const y: [:1]const u8 = &[_:2]u8{ 1, 2 }; _ = y; } +export fn entry2() void { + const x: [:2]const u8 = &.{ 1, 2 }; + const y: [:1]const u8 = x; + _ = y; +} // error // backend=stage2 // target=native // -// :2:29: error: expected type '[:1]const u8', found '*const [2:2]u8' -// :2:29: note: pointer sentinel '2' cannot cast into pointer sentinel '1' +// :2:37: error: expected type '[2:1]u8', found '[2:2]u8' +// :2:37: note: array sentinel '2' cannot cast into array sentinel '1' +// :7:29: error: expected type '[:1]const u8', found '[:2]const u8' +// :7:29: note: pointer sentinel '2' cannot cast into pointer sentinel '1' diff --git a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig index be99c394d3..b817d887d2 100644 --- a/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig +++ b/test/cases/compile_errors/union_init_with_none_or_multiple_fields.zig @@ -28,7 +28,6 @@ export fn u2m() void { // target=native // // :10:20: error: union initializer must initialize one field -// :1:12: note: union declared here // :14:20: error: cannot initialize multiple union fields at once; unions can only have one active field // :14:31: note: additional initializer here // :1:12: note: union declared here diff --git a/test/cases/compile_errors/union_noreturn_field_initialized.zig b/test/cases/compile_errors/union_noreturn_field_initialized.zig index 66304d6a74..df0a2df093 100644 --- a/test/cases/compile_errors/union_noreturn_field_initialized.zig +++ b/test/cases/compile_errors/union_noreturn_field_initialized.zig @@ -32,7 +32,7 @@ pub export fn entry3() void { // backend=stage2 // target=native // -// :11:21: error: cannot initialize 'noreturn' field of union +// :11:14: error: cannot initialize 'noreturn' field of union // :4:9: note: field 'b' declared here // :2:15: note: union declared here // :19:10: error: cannot initialize 'noreturn' field of union diff --git a/test/cases/compile_errors/wrong_types_given_to_export.zig b/test/cases/compile_errors/wrong_types_given_to_export.zig index 6e688d33d6..a7405faa92 100644 --- a/test/cases/compile_errors/wrong_types_given_to_export.zig +++ b/test/cases/compile_errors/wrong_types_given_to_export.zig @@ -7,5 +7,5 @@ comptime { // backend=stage2 // target=native // -// :3:51: error: expected type 'builtin.GlobalLinkage', found 'u32' +// :3:41: error: expected type 'builtin.GlobalLinkage', found 'u32' // :?:?: note: enum declared here diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5cc5c36f3f..5f787683c4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -207,10 +207,8 @@ pub fn addCases(ctx: *Cases) !void { ":1:38: note: declared comptime here", ":8:36: error: runtime-known argument passed to comptime parameter", ":2:41: note: declared comptime here", - ":13:29: error: runtime-known argument passed to parameter of comptime-only type", - ":3:24: note: declared here", - ":12:35: note: struct requires comptime because of this field", - ":12:35: note: types are not available at runtime", + ":13:32: error: unable to resolve comptime value", + ":13:32: note: initializer of comptime only struct must be comptime-known", }); case.addSourceFile("import.zig",