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",