mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #21264 from mlugg/decl-literals
compiler: implement decl literals
This commit is contained in:
commit
6d2945f1fe
@ -510,6 +510,8 @@ pub fn ArrayHashMap(
|
||||
/// `store_hash` is `false` and the number of entries in the map is less than 9,
|
||||
/// the overhead cost of using `ArrayHashMapUnmanaged` rather than `std.ArrayList` is
|
||||
/// only a single pointer-sized integer.
|
||||
///
|
||||
/// Default initialization of this struct is deprecated; use `.empty` instead.
|
||||
pub fn ArrayHashMapUnmanaged(
|
||||
comptime K: type,
|
||||
comptime V: type,
|
||||
@ -538,6 +540,12 @@ pub fn ArrayHashMapUnmanaged(
|
||||
/// Used to detect memory safety violations.
|
||||
pointer_stability: std.debug.SafetyLock = .{},
|
||||
|
||||
/// A map containing no keys or values.
|
||||
pub const empty: Self = .{
|
||||
.entries = .{},
|
||||
.index_header = null,
|
||||
};
|
||||
|
||||
/// Modifying the key is allowed only if it does not change the hash.
|
||||
/// Modifying the value is allowed.
|
||||
/// Entry pointers become invalid whenever this ArrayHashMap is modified,
|
||||
|
||||
@ -618,6 +618,8 @@ pub fn ArrayListUnmanaged(comptime T: type) type {
|
||||
/// Functions that potentially allocate memory accept an `Allocator` parameter.
|
||||
/// Initialize directly or with `initCapacity`, and deinitialize with `deinit`
|
||||
/// or use `toOwnedSlice`.
|
||||
///
|
||||
/// Default initialization of this struct is deprecated; use `.empty` instead.
|
||||
pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type {
|
||||
if (alignment) |a| {
|
||||
if (a == @alignOf(T)) {
|
||||
@ -638,6 +640,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// additional memory.
|
||||
capacity: usize = 0,
|
||||
|
||||
/// An ArrayList containing no elements.
|
||||
pub const empty: Self = .{
|
||||
.items = &.{},
|
||||
.capacity = 0,
|
||||
};
|
||||
|
||||
pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
|
||||
|
||||
pub fn SentinelSlice(comptime s: T) type {
|
||||
|
||||
@ -721,6 +721,8 @@ pub fn HashMap(
|
||||
/// the price of handling size with u32, which should be reasonable enough
|
||||
/// for almost all uses.
|
||||
/// Deletions are achieved with tombstones.
|
||||
///
|
||||
/// Default initialization of this struct is deprecated; use `.empty` instead.
|
||||
pub fn HashMapUnmanaged(
|
||||
comptime K: type,
|
||||
comptime V: type,
|
||||
@ -762,6 +764,13 @@ pub fn HashMapUnmanaged(
|
||||
/// Capacity of the first grow when bootstrapping the hashmap.
|
||||
const minimal_capacity = 8;
|
||||
|
||||
/// A map containing no keys or values.
|
||||
pub const empty: Self = .{
|
||||
.metadata = null,
|
||||
.size = 0,
|
||||
.available = 0,
|
||||
};
|
||||
|
||||
// This hashmap is specially designed for sizes that fit in a u32.
|
||||
pub const Size = u32;
|
||||
|
||||
|
||||
@ -157,6 +157,7 @@ pub const Config = struct {
|
||||
|
||||
pub const Check = enum { ok, leak };
|
||||
|
||||
/// Default initialization of this struct is deprecated; use `.init` instead.
|
||||
pub fn GeneralPurposeAllocator(comptime config: Config) type {
|
||||
return struct {
|
||||
backing_allocator: Allocator = std.heap.page_allocator,
|
||||
@ -174,6 +175,16 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
|
||||
|
||||
const Self = @This();
|
||||
|
||||
/// The initial state of a `GeneralPurposeAllocator`, containing no allocations and backed by the system page allocator.
|
||||
pub const init: Self = .{
|
||||
.backing_allocator = std.heap.page_allocator,
|
||||
.buckets = [1]Buckets{.{}} ** small_bucket_count,
|
||||
.cur_buckets = [1]?*BucketHeader{null} ** small_bucket_count,
|
||||
.large_allocations = .{},
|
||||
.empty_buckets = if (config.retain_metadata) .{} else {},
|
||||
.bucket_node_pool = .init(std.heap.page_allocator),
|
||||
};
|
||||
|
||||
const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {};
|
||||
const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {};
|
||||
|
||||
|
||||
@ -1028,7 +1028,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
||||
const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
|
||||
return blockExpr(gz, scope, ri, node, statements, .normal);
|
||||
},
|
||||
.enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
|
||||
.enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
|
||||
const str_index = try astgen.identAsString(main_tokens[node]);
|
||||
const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{
|
||||
.lhs = res_ty,
|
||||
.field_name_start = str_index,
|
||||
});
|
||||
switch (ri.rl) {
|
||||
.discard, .none, .ref => unreachable, // no result type
|
||||
.ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
|
||||
.ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node),
|
||||
}
|
||||
} else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
|
||||
.error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
|
||||
// TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
|
||||
// .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
|
||||
@ -2752,6 +2763,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.err_union_code_ptr,
|
||||
.ptr_type,
|
||||
.enum_literal,
|
||||
.decl_literal,
|
||||
.decl_literal_no_coerce,
|
||||
.merge_error_sets,
|
||||
.error_union_type,
|
||||
.bit_not,
|
||||
@ -2914,6 +2927,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.validate_array_init_result_ty,
|
||||
.validate_ptr_array_init,
|
||||
.validate_ref_ty,
|
||||
.try_operand_ty,
|
||||
.try_ref_operand_ty,
|
||||
=> break :b true,
|
||||
|
||||
.@"defer" => unreachable,
|
||||
@ -5887,13 +5902,21 @@ fn tryExpr(
|
||||
}
|
||||
const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
|
||||
|
||||
const operand_ri: ResultInfo = switch (ri.rl) {
|
||||
.ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = .error_handling_expr },
|
||||
else => .{ .rl = .none, .ctx = .error_handling_expr },
|
||||
const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
|
||||
.ref => .{ .ref, .try_ptr },
|
||||
.ref_coerced_ty => |payload_ptr_ty| .{
|
||||
.{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) },
|
||||
.try_ptr,
|
||||
},
|
||||
else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{
|
||||
// `coerced_ty` is OK due to the `rvalue` call below
|
||||
.{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) },
|
||||
.@"try",
|
||||
} else .{ .none, .@"try" },
|
||||
};
|
||||
const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
|
||||
// This could be a pointer or value depending on the `ri` parameter.
|
||||
const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
|
||||
const block_tag: Zir.Inst.Tag = if (operand_ri.rl == .ref) .try_ptr else .@"try";
|
||||
const try_inst = try parent_gz.makeBlockInst(block_tag, node);
|
||||
try parent_gz.instructions.append(astgen.gpa, try_inst);
|
||||
|
||||
@ -9905,7 +9928,7 @@ fn callExpr(
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
const astgen = gz.astgen;
|
||||
|
||||
const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
|
||||
const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr);
|
||||
const modifier: std.builtin.CallModifier = blk: {
|
||||
if (gz.is_comptime) {
|
||||
break :blk .compile_time;
|
||||
@ -10033,6 +10056,7 @@ const Callee = union(enum) {
|
||||
fn calleeExpr(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
call_rl: ResultInfo.Loc,
|
||||
node: Ast.Node.Index,
|
||||
) InnerError!Callee {
|
||||
const astgen = gz.astgen;
|
||||
@ -10059,6 +10083,19 @@ fn calleeExpr(
|
||||
.field_name_start = str_index,
|
||||
} };
|
||||
},
|
||||
.enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| {
|
||||
// Decl literal call syntax, e.g.
|
||||
// `const foo: T = .init();`
|
||||
// Look up `init` in `T`, but don't try and coerce it.
|
||||
const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]);
|
||||
const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{
|
||||
.lhs = res_ty,
|
||||
.field_name_start = str_index,
|
||||
});
|
||||
return .{ .direct = callee };
|
||||
} else {
|
||||
return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) };
|
||||
},
|
||||
else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +651,14 @@ pub const Inst = struct {
|
||||
err_union_code_ptr,
|
||||
/// An enum literal. Uses the `str_tok` union field.
|
||||
enum_literal,
|
||||
/// A decl literal. This is similar to `field`, but unwraps error unions and optionals,
|
||||
/// and coerces the result to the given type.
|
||||
/// Uses the `pl_node` union field. Payload is `Field`.
|
||||
decl_literal,
|
||||
/// The same as `decl_literal`, but the coercion is omitted. This is used for decl literal
|
||||
/// function call syntax, i.e. `.foo()`.
|
||||
/// Uses the `pl_node` union field. Payload is `Field`.
|
||||
decl_literal_no_coerce,
|
||||
/// A switch expression. Uses the `pl_node` union field.
|
||||
/// AST node is the switch, payload is `SwitchBlock`.
|
||||
switch_block,
|
||||
@ -684,6 +692,14 @@ pub const Inst = struct {
|
||||
/// operator. Emit a compile error if not.
|
||||
/// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
|
||||
validate_ref_ty,
|
||||
/// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used
|
||||
/// as the result type of a `try` operand. Generic poison is propagated.
|
||||
/// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`.
|
||||
try_operand_ty,
|
||||
/// Given a type `*T`, construct the type `*E!T`, where `E` is this function's error set, to be used
|
||||
/// as the result type of a `try` operand whose address is taken with `&`. Generic poison is propagated.
|
||||
/// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `*T`.
|
||||
try_ref_operand_ty,
|
||||
|
||||
// The following tags all relate to struct initialization expressions.
|
||||
|
||||
@ -1136,6 +1152,8 @@ pub const Inst = struct {
|
||||
.err_union_code_ptr,
|
||||
.ptr_type,
|
||||
.enum_literal,
|
||||
.decl_literal,
|
||||
.decl_literal_no_coerce,
|
||||
.merge_error_sets,
|
||||
.error_union_type,
|
||||
.bit_not,
|
||||
@ -1254,6 +1272,8 @@ pub const Inst = struct {
|
||||
.array_init_elem_type,
|
||||
.array_init_elem_ptr,
|
||||
.validate_ref_ty,
|
||||
.try_operand_ty,
|
||||
.try_ref_operand_ty,
|
||||
.restore_err_ret_index_unconditional,
|
||||
.restore_err_ret_index_fn_entry,
|
||||
=> false,
|
||||
@ -1324,6 +1344,8 @@ pub const Inst = struct {
|
||||
.validate_array_init_result_ty,
|
||||
.validate_ptr_array_init,
|
||||
.validate_ref_ty,
|
||||
.try_operand_ty,
|
||||
.try_ref_operand_ty,
|
||||
=> true,
|
||||
|
||||
.param,
|
||||
@ -1430,6 +1452,8 @@ pub const Inst = struct {
|
||||
.err_union_code_ptr,
|
||||
.ptr_type,
|
||||
.enum_literal,
|
||||
.decl_literal,
|
||||
.decl_literal_no_coerce,
|
||||
.merge_error_sets,
|
||||
.error_union_type,
|
||||
.bit_not,
|
||||
@ -1685,6 +1709,8 @@ pub const Inst = struct {
|
||||
.err_union_code = .un_node,
|
||||
.err_union_code_ptr = .un_node,
|
||||
.enum_literal = .str_tok,
|
||||
.decl_literal = .pl_node,
|
||||
.decl_literal_no_coerce = .pl_node,
|
||||
.switch_block = .pl_node,
|
||||
.switch_block_ref = .pl_node,
|
||||
.switch_block_err_union = .pl_node,
|
||||
@ -1698,6 +1724,8 @@ pub const Inst = struct {
|
||||
.opt_eu_base_ptr_init = .un_node,
|
||||
.coerce_ptr_elem_ty = .pl_node,
|
||||
.validate_ref_ty = .un_tok,
|
||||
.try_operand_ty = .un_node,
|
||||
.try_ref_operand_ty = .un_node,
|
||||
|
||||
.int_from_ptr = .un_node,
|
||||
.compile_error = .un_node,
|
||||
@ -3828,12 +3856,16 @@ fn findDeclsInner(
|
||||
.err_union_code,
|
||||
.err_union_code_ptr,
|
||||
.enum_literal,
|
||||
.decl_literal,
|
||||
.decl_literal_no_coerce,
|
||||
.validate_deref,
|
||||
.validate_destructure,
|
||||
.field_type_ref,
|
||||
.opt_eu_base_ptr_init,
|
||||
.coerce_ptr_elem_ty,
|
||||
.validate_ref_ty,
|
||||
.try_operand_ty,
|
||||
.try_ref_operand_ty,
|
||||
.struct_init_empty,
|
||||
.struct_init_empty_result,
|
||||
.struct_init_empty_ref_result,
|
||||
|
||||
121
src/Sema.zig
121
src/Sema.zig
@ -1072,6 +1072,8 @@ fn analyzeBodyInner(
|
||||
.indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
|
||||
.vector_elem_type => try sema.zirVectorElemType(block, inst),
|
||||
.enum_literal => try sema.zirEnumLiteral(block, inst),
|
||||
.decl_literal => try sema.zirDeclLiteral(block, inst, true),
|
||||
.decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
|
||||
.int_from_enum => try sema.zirIntFromEnum(block, inst),
|
||||
.enum_from_int => try sema.zirEnumFromInt(block, inst),
|
||||
.err_union_code => try sema.zirErrUnionCode(block, inst),
|
||||
@ -1177,6 +1179,8 @@ fn analyzeBodyInner(
|
||||
.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),
|
||||
.try_operand_ty => try sema.zirTryOperandTy(block, inst, false),
|
||||
.try_ref_operand_ty => try sema.zirTryOperandTy(block, inst, true),
|
||||
|
||||
.clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
|
||||
.ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
|
||||
@ -2024,6 +2028,22 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi
|
||||
const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||
cur = un_node.operand;
|
||||
},
|
||||
.try_operand_ty => {
|
||||
// Either the input type was itself poison, or it was a slice, which we cannot translate
|
||||
// to an overall result type.
|
||||
const un_node = sema.code.instructions.items(.data)[@intFromEnum(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
|
||||
};
|
||||
if (operand_ref == .generic_poison_type) {
|
||||
// The input was poison -- keep looking.
|
||||
cur = un_node.operand;
|
||||
continue;
|
||||
}
|
||||
// We got a poison because the result type was a slice. This is a tricky case -- let's just
|
||||
// not bother explaining it to the user for now...
|
||||
return .unknown;
|
||||
},
|
||||
.struct_init_field_type => {
|
||||
const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
|
||||
@ -4423,6 +4443,59 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
|
||||
}
|
||||
}
|
||||
|
||||
fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||
const src = block.nodeOffset(un_node.src_node);
|
||||
|
||||
const operand_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) {
|
||||
error.GenericPoison => return .generic_poison_type,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const payload_ty = if (is_ref) ty: {
|
||||
if (!operand_ty.isSinglePointer(zcu)) {
|
||||
return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`.
|
||||
}
|
||||
break :ty operand_ty.childType(zcu);
|
||||
} else operand_ty;
|
||||
|
||||
const err_set_ty = err_set: {
|
||||
// There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
|
||||
// until we hit an error union or set.
|
||||
var cur_ty = sema.fn_ret_ty;
|
||||
while (true) {
|
||||
switch (cur_ty.zigTypeTag(zcu)) {
|
||||
.error_set => break :err_set cur_ty,
|
||||
.error_union => break :err_set cur_ty.errorUnionSet(zcu),
|
||||
.optional => cur_ty = cur_ty.optionalChild(zcu),
|
||||
else => return sema.failWithOwnedErrorMsg(block, msg: {
|
||||
const msg = try sema.errMsg(src, "expected '{}', found error set", .{sema.fn_ret_ty.fmt(pt)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
const ret_ty_src: LazySrcLoc = .{
|
||||
.base_node_inst = sema.getOwnerFuncDeclInst(),
|
||||
.offset = .{ .node_offset_fn_type_ret_ty = 0 },
|
||||
};
|
||||
try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
|
||||
break :msg msg;
|
||||
}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty);
|
||||
|
||||
if (is_ref) {
|
||||
var ptr_info = operand_ty.ptrInfo(zcu);
|
||||
ptr_info.child = eu_ty.toIntern();
|
||||
const eu_ptr_ty = try pt.ptrTypeSema(ptr_info);
|
||||
return Air.internedToRef(eu_ptr_ty.toIntern());
|
||||
} else {
|
||||
return Air.internedToRef(eu_ty.toIntern());
|
||||
}
|
||||
}
|
||||
|
||||
fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
@ -8803,6 +8876,54 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
})));
|
||||
}
|
||||
|
||||
fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
|
||||
const src = block.nodeOffset(inst_data.src_node);
|
||||
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
|
||||
const name = try zcu.intern_pool.getOrPutString(
|
||||
sema.gpa,
|
||||
pt.tid,
|
||||
sema.code.nullTerminatedString(extra.field_name_start),
|
||||
.no_embedded_nulls,
|
||||
);
|
||||
const orig_ty = sema.resolveType(block, src, extra.lhs) catch |err| switch (err) {
|
||||
error.GenericPoison => {
|
||||
// Treat this as a normal enum literal.
|
||||
return Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
var ty = orig_ty;
|
||||
while (true) switch (ty.zigTypeTag(zcu)) {
|
||||
.error_union => ty = ty.errorUnionPayload(zcu),
|
||||
.optional => ty = ty.optionalChild(zcu),
|
||||
.enum_literal, .error_set => {
|
||||
// Treat this as a normal enum literal.
|
||||
return Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
|
||||
},
|
||||
else => break,
|
||||
};
|
||||
|
||||
const result = try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src);
|
||||
|
||||
// Decl literals cannot lookup runtime `var`s.
|
||||
if (!try sema.isComptimeKnown(result)) {
|
||||
return sema.fail(block, src, "decl literal must be comptime-known", .{});
|
||||
}
|
||||
|
||||
if (do_coerce) {
|
||||
return sema.coerce(block, orig_ty, result, src);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
|
||||
@ -3471,9 +3471,48 @@ fn airUnwrapErrPayloadPtr(func: *Func, inst: Air.Inst.Index) !void {
|
||||
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
// *(E!T) => *T
|
||||
fn airErrUnionPayloadPtrSet(func: *Func, inst: Air.Inst.Index) !void {
|
||||
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
|
||||
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement .errunion_payload_ptr_set for {}", .{func.target.cpu.arch});
|
||||
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
|
||||
const zcu = func.pt.zcu;
|
||||
const src_ty = func.typeOf(ty_op.operand);
|
||||
const src_mcv = try func.resolveInst(ty_op.operand);
|
||||
|
||||
// `src_reg` contains the pointer to the error union
|
||||
const src_reg = switch (src_mcv) {
|
||||
.register => |reg| reg,
|
||||
else => try func.copyToTmpRegister(src_ty, src_mcv),
|
||||
};
|
||||
const src_lock = func.register_manager.lockRegAssumeUnused(src_reg);
|
||||
defer func.register_manager.unlockReg(src_lock);
|
||||
|
||||
// we set the place of where the error would have been to 0
|
||||
const eu_ty = src_ty.childType(zcu);
|
||||
const pl_ty = eu_ty.errorUnionPayload(zcu);
|
||||
const err_ty = eu_ty.errorUnionSet(zcu);
|
||||
const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
|
||||
try func.genSetMem(.{ .reg = src_reg }, err_off, err_ty, .{ .immediate = 0 });
|
||||
|
||||
const dst_reg, const dst_lock = if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv))
|
||||
.{ src_reg, null }
|
||||
else
|
||||
try func.allocReg(.int);
|
||||
defer if (dst_lock) |lock| func.register_manager.unlockReg(lock);
|
||||
|
||||
// move the pointer to be at the payload
|
||||
const pl_off = errUnionPayloadOffset(pl_ty, zcu);
|
||||
try func.genBinOp(
|
||||
.add,
|
||||
.{ .register = src_reg },
|
||||
Type.u64,
|
||||
.{ .immediate = pl_off },
|
||||
Type.u64,
|
||||
dst_reg,
|
||||
);
|
||||
|
||||
break :result .{ .register = dst_reg };
|
||||
};
|
||||
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
|
||||
@ -277,6 +277,8 @@ const Writer = struct {
|
||||
.opt_eu_base_ptr_init,
|
||||
.restore_err_ret_index_unconditional,
|
||||
.restore_err_ret_index_fn_entry,
|
||||
.try_operand_ty,
|
||||
.try_ref_operand_ty,
|
||||
=> try self.writeUnNode(stream, inst),
|
||||
|
||||
.ref,
|
||||
@ -460,6 +462,8 @@ const Writer = struct {
|
||||
|
||||
.field_val,
|
||||
.field_ptr,
|
||||
.decl_literal,
|
||||
.decl_literal_no_coerce,
|
||||
=> try self.writePlNodeField(stream, inst),
|
||||
|
||||
.field_ptr_named,
|
||||
|
||||
@ -21,6 +21,7 @@ test {
|
||||
_ = @import("behavior/cast_int.zig");
|
||||
_ = @import("behavior/comptime_memory.zig");
|
||||
_ = @import("behavior/const_slice_child.zig");
|
||||
_ = @import("behavior/decl_literals.zig");
|
||||
_ = @import("behavior/decltest.zig");
|
||||
_ = @import("behavior/duplicated_test_names.zig");
|
||||
_ = @import("behavior/defer.zig");
|
||||
|
||||
@ -164,8 +164,6 @@ const Piece = packed struct {
|
||||
};
|
||||
|
||||
test "load non byte-sized optional value" {
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
// Originally reported at https://github.com/ziglang/zig/issues/14200
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
|
||||
@ -181,8 +179,6 @@ test "load non byte-sized optional value" {
|
||||
}
|
||||
|
||||
test "load non byte-sized value in struct" {
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
if (builtin.cpu.arch.endian() != .little) return error.SkipZigTest; // packed struct TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
|
||||
|
||||
38
test/behavior/decl_literals.zig
Normal file
38
test/behavior/decl_literals.zig
Normal file
@ -0,0 +1,38 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
test "decl literal" {
|
||||
const S = struct {
|
||||
x: u32,
|
||||
const foo: @This() = .{ .x = 123 };
|
||||
};
|
||||
|
||||
const val: S = .foo;
|
||||
try expect(val.x == 123);
|
||||
}
|
||||
|
||||
test "call decl literal" {
|
||||
const S = struct {
|
||||
x: u32,
|
||||
fn init() @This() {
|
||||
return .{ .x = 123 };
|
||||
}
|
||||
};
|
||||
|
||||
const val: S = .init();
|
||||
try expect(val.x == 123);
|
||||
}
|
||||
|
||||
test "call decl literal with error union" {
|
||||
const S = struct {
|
||||
x: u32,
|
||||
fn init(err: bool) !@This() {
|
||||
if (err) return error.Bad;
|
||||
return .{ .x = 123 };
|
||||
}
|
||||
};
|
||||
|
||||
const val: S = try .init(false);
|
||||
try expect(val.x == 123);
|
||||
}
|
||||
@ -1214,7 +1214,6 @@ test "anon init through error union" {
|
||||
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;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
a: u32,
|
||||
|
||||
@ -67,3 +67,22 @@ test "`try`ing an if/else expression" {
|
||||
|
||||
try std.testing.expectError(error.Test, S.getError2());
|
||||
}
|
||||
|
||||
test "try forwards result location" {
|
||||
if (builtin.zig_backend == .stage2_x86) 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_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn foo(err: bool) error{Foo}!u32 {
|
||||
const result: error{ Foo, Bar }!u32 = if (err) error.Foo else 123;
|
||||
const res_int: u32 = try @errorCast(result);
|
||||
return res_int;
|
||||
}
|
||||
};
|
||||
|
||||
try expect((S.foo(false) catch return error.TestUnexpectedResult) == 123);
|
||||
try std.testing.expectError(error.Foo, S.foo(true));
|
||||
}
|
||||
|
||||
@ -347,7 +347,6 @@ test "try terminating an infinite loop" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
|
||||
// Test coverage for https://github.com/ziglang/zig/issues/13546
|
||||
const Foo = struct {
|
||||
|
||||
@ -11,5 +11,5 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :6:21: error: no field named 'c' in enum 'tmp.Foo'
|
||||
// :6:21: error: enum 'tmp.Foo' has no member named 'c'
|
||||
// :1:13: note: enum declared here
|
||||
|
||||
@ -17,5 +17,5 @@ pub export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:28: error: no field named 'c' in enum 'meta.FieldEnum(tmp.MyStruct)'
|
||||
// :7:28: error: enum 'meta.FieldEnum(tmp.MyStruct)' has no member named 'c'
|
||||
// :?:?: note: enum declared here
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user