diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig index eb31d1cae3..1b96be472a 100644 --- a/lib/std/array_hash_map.zig +++ b/lib/std/array_hash_map.zig @@ -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, diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 2510973692..24098a01f6 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -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 { diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 1c8a5d78af..7d7b2739c3 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -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; diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index 61e256d290..b760c9d85d 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -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 {}; diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 9c27d1e036..6a1cdb910a 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -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) }, } } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index af4ddaad6a..7b71b20780 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -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, diff --git a/src/Sema.zig b/src/Sema.zig index a72c749f2e..57a4809f95 100644 --- a/src/Sema.zig +++ b/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; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index a70618a394..1934cee7f5 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -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 }); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 8d70af5f3c..7910da080c 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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, diff --git a/test/behavior.zig b/test/behavior.zig index 430c34bcc6..eca6cee5af 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig index 9faa123a62..10bb445ca7 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.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; diff --git a/test/behavior/decl_literals.zig b/test/behavior/decl_literals.zig new file mode 100644 index 0000000000..7956689122 --- /dev/null +++ b/test/behavior/decl_literals.zig @@ -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); +} diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 44f035f46f..4694758e19 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -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, diff --git a/test/behavior/try.zig b/test/behavior/try.zig index 53fdc48934..f17133fabe 100644 --- a/test/behavior/try.zig +++ b/test/behavior/try.zig @@ -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)); +} diff --git a/test/behavior/while.zig b/test/behavior/while.zig index 532bac258d..7f4d025d63 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -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 { diff --git a/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig b/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig index bd89dc402a..2275e9dc92 100644 --- a/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig +++ b/test/cases/compile_errors/cast_enum_literal_to_enum_but_it_doesnt_match.zig @@ -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 diff --git a/test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig b/test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig index efc3f556a9..e9f356ca78 100644 --- a/test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig +++ b/test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig @@ -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