Commit 09a57583a4
Changed files (35)
lib
std
test
cases
compile_errors
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;
+ },
};
}
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);
- },
- .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 try gz.addUnNode(.elem_type, ptr_ty, node);
},
- }
+ };
+ }
- 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);
- },
- .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);
- },
- .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);
- return rvalue(gz, ri, result, node);
+ const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
+ return gz.addUnTok(.ref, result, tree.firstToken(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);
- }
+ .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 => |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);
},
.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,104 +1789,83 @@ 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);
-}
-
-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);
+ return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
}
-fn structInitExprRlPtrInner(
+/// 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,
- result_ptr: Zir.Inst.Ref,
+ ty_inst: Zir.Inst.Ref,
+ is_ref: bool,
) 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),
+ const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
+ .fields_len = @intCast(struct_init.ast.fields.len),
});
- var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
+ const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
+ var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
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,
+ const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
+ .container_type = ty_inst,
+ .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);
+ setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
+ .field_type = refToIndex(field_ty_inst).?,
+ .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
+ });
+ extra_index += field_size;
}
- _ = try gz.addPlNodePayloadIndex(.validate_struct_init, node, payload_index);
- return Zir.Inst.Ref.void_value;
+ const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
+ return gz.addPlNodePayloadIndex(tag, node, payload_index);
}
-fn structInitExprRlTy(
+/// A struct initialization expression using field pointers.
+fn structInitExprPtr(
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 {
+ ptr_inst: Zir.Inst.Ref,
+) InnerError!void {
const astgen = gz.astgen;
const tree = astgen.tree;
- const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
- .fields_len = @intCast(struct_init.ast.fields.len),
+ 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),
});
- const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
- var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
+ 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_ty_inst = try gz.addPlNode(.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),
+ const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
+ .lhs = struct_ptr_inst,
+ .field_name_start = str_index,
});
- extra_index += field_size;
+ astgen.extra.items[extra_index] = refToIndex(field_ptr).?;
+ extra_index += 1;
+ _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
}
- return try gz.addPlNodePayloadIndex(tag, node, payload_index);
+ _ = 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.*);
},
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 {
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,
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);
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_struct_init_ty => {
+ try sema.zirValidateStructInitTy(block, inst, false);
+ i += 1;
+ continue;
+ },
+ .validate_struct_init_result_ty => {
+ try sema.zirValidateStructInitTy(block, inst, true);
+ i += 1;
+ continue;
+ },
.validate_array_init_ty => {
- try sema.zirValidateArrayInitTy(block, inst);
+ try sema.zirValidateArrayInitTy(block, inst, false);
i += 1;
continue;
},
- .validate_struct_init_ty => {
- try sema.zirValidateStructInitTy(block, inst);
+ .validate_array_init_result_ty => {
+ try sema.zirValidateArrayInitTy(block, inst, true);
i += 1;
continue;
},
- .validate_struct_init => {
- try sema.zirValidateStructInit(block, inst);
+ .validate_ptr_struct_init => {
+ try sema.zirValidatePtrStructInit(block, inst);
i += 1;
continue;
},
- .validate_array_init => {
- try sema.zirValidateArrayInit(block, inst);
+ .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,21 @@ 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();
+
+ 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);
+ 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();
@@ -10094,7 +9992,15 @@ 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);
+ 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", .{});
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 };
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,
+ /// 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,
/// Finalizes a typed struct or union initialization, performs validation, and returns the
- /// struct or union value.
+ /// 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.
+ /// 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,
- /// Struct initialization without a 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.
+ /// 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,
};
};
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);
+}
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();
+}
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);
+}
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);
+}
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
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'
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
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
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'
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
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'
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
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
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
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
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'
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'
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'
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
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
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
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
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'
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
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
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
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",