diff --git a/doc/langref.html.in b/doc/langref.html.in index a6241f541b..abe83d2117 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -9702,8 +9702,9 @@ test "integer truncation" {
This function returns the string representation of a type, as an array. It is equivalent to a string literal of the type name. + The returned type name is fully qualified with the parent namespace included + as part of the type name with a series of dots.
- {#header_close#} {#header_open|@TypeOf#} diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 0e2a0753ea..d71f2b9274 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -23,7 +23,9 @@ fn processArgs() void { } pub fn main() void { - if (builtin.zig_backend != .stage1) { + if (builtin.zig_backend != .stage1 and + (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32)) + { return main2() catch @panic("test failure"); } if (builtin.zig_backend == .stage1) processArgs(); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index c35de990ef..5049218c90 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -445,6 +445,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void { } } +pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const u8) !void { + if (std.mem.startsWith(u8, actual, expected_starts_with)) + return; + + const shortened_actual = if (actual.len >= expected_starts_with.len) + actual[0..expected_starts_with.len] + else + actual; + + print("\n====== expected to start with: =========\n", .{}); + printWithVisibleNewlines(expected_starts_with); + print("\n====== instead ended with: ===========\n", .{}); + printWithVisibleNewlines(shortened_actual); + print("\n========= full output: ==============\n", .{}); + printWithVisibleNewlines(actual); + print("\n======================================\n", .{}); + + return error.TestExpectedStartsWith; +} + pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void { if (std.mem.endsWith(u8, actual, expected_ends_with)) return; diff --git a/src/AstGen.zig b/src/AstGen.zig index c714b69572..85f2288413 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -6293,7 +6293,10 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref } else .{ .ty = try gz.addNodeExtended(.ret_type, node), }; + const prev_anon_name_strategy = gz.anon_name_strategy; + gz.anon_name_strategy = .func; const operand = try reachableExpr(gz, scope, rl, operand_node, node); + gz.anon_name_strategy = prev_anon_name_strategy; switch (nodeMayEvalToError(tree, operand_node)) { .never => { diff --git a/src/Sema.zig b/src/Sema.zig index 4c2fa33951..722c39cfcb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1767,7 +1767,7 @@ fn zirStructDecl( const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const type_name = try sema.createTypeName(block, small.name_strategy); + const type_name = try sema.createTypeName(block, small.name_strategy, "struct"); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = struct_val, @@ -1796,28 +1796,54 @@ fn zirStructDecl( return sema.analyzeDeclVal(block, src, new_decl); } -fn createTypeName(sema: *Sema, block: *Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 { +fn createTypeName( + sema: *Sema, + block: *Block, + name_strategy: Zir.Inst.NameStrategy, + anon_prefix: []const u8, +) ![:0]u8 { switch (name_strategy) { .anon => { // It would be neat to have "struct:line:column" but this name has // to survive incremental updates, where it may have been shifted down // or up to a different line, but unchanged, and thus not unnecessarily // semantically analyzed. + // This name is also used as the key in the parent namespace so it cannot be + // renamed. const name_index = sema.mod.getNextAnonNameIndex(); - return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ - block.src_decl.name, name_index, + return std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ + block.src_decl.name, anon_prefix, name_index, }); }, .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)), .func => { - const name_index = sema.mod.getNextAnonNameIndex(); - const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ - block.src_decl.name, name_index, - }); - log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{ - name, - }); - return name; + const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); + const zir_tags = sema.code.instructions.items(.tag); + + var buf = std.ArrayList(u8).init(sema.gpa); + defer buf.deinit(); + try buf.appendSlice(mem.sliceTo(block.src_decl.name, 0)); + try buf.appendSlice("("); + + var arg_i: usize = 0; + for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) { + .param, .param_comptime, .param_anytype, .param_anytype_comptime => { + const arg = sema.inst_map.get(zir_inst).?; + // The comptime call code in analyzeCall already did this, so we're + // just repeating it here and it's guaranteed to work. + const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; + + if (arg_i != 0) try buf.appendSlice(","); + try buf.writer().print("{}", .{arg_val}); + + arg_i += 1; + continue; + }, + else => continue, + }; + + try buf.appendSlice(")"); + return buf.toOwnedSliceSentinel(0); }, } } @@ -1877,7 +1903,7 @@ fn zirEnumDecl( }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const type_name = try sema.createTypeName(block, small.name_strategy); + const type_name = try sema.createTypeName(block, small.name_strategy, "enum"); const new_decl = try mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = enum_val, @@ -2088,7 +2114,7 @@ fn zirUnionDecl( }; const union_ty = Type.initPayload(&union_payload.base); const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const type_name = try sema.createTypeName(block, small.name_strategy); + const type_name = try sema.createTypeName(block, small.name_strategy, "union"); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = union_val, @@ -2156,7 +2182,7 @@ fn zirOpaqueDecl( }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const type_name = try sema.createTypeName(block, small.name_strategy); + const type_name = try sema.createTypeName(block, small.name_strategy, "opaque"); const new_decl = try mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = opaque_val, @@ -2204,7 +2230,7 @@ fn zirErrorSetDecl( const error_set = try new_decl_arena_allocator.create(Module.ErrorSet); const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty); - const type_name = try sema.createTypeName(block, name_strategy); + const type_name = try sema.createTypeName(block, name_strategy, "error"); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = error_set_val, @@ -12697,7 +12723,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const enum_ty = Type.initPayload(&enum_ty_payload.base); const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const type_name = try sema.createTypeName(block, .anon); + const type_name = try sema.createTypeName(block, .anon, "enum"); const new_decl = try mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = enum_val, @@ -12707,7 +12733,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I enum_obj.* = .{ .owner_decl = new_decl, - .tag_ty = Type.initTag(.@"null"), + .tag_ty = Type.@"null", .tag_ty_inferred = true, .fields = .{}, .values = .{}, @@ -12785,7 +12811,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const opaque_ty = Type.initPayload(&opaque_ty_payload.base); const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty); - const type_name = try sema.createTypeName(block, .anon); + const type_name = try sema.createTypeName(block, .anon, "opaque"); const new_decl = try mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = opaque_val, @@ -12836,7 +12862,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }; const union_ty = Type.initPayload(&union_payload.base); const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty); - const type_name = try sema.createTypeName(block, .anon); + const type_name = try sema.createTypeName(block, .anon, "union"); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = new_union_val, @@ -13001,7 +13027,7 @@ fn reifyStruct( const struct_obj = try new_decl_arena_allocator.create(Module.Struct); const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty); - const type_name = try sema.createTypeName(block, .anon); + const type_name = try sema.createTypeName(block, .anon, "struct"); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ .ty = Type.type, .val = new_struct_val, diff --git a/src/type.zig b/src/type.zig index b3edc87585..139f126d3b 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1793,7 +1793,7 @@ pub const Type = extern union { }, .error_set_inferred => { const func = ty.castTag(.error_set_inferred).?.data.func; - return writer.print("(inferred error set of {s})", .{func.owner_decl.name}); + return writer.print("@typeInfo(@typeInfo(@TypeOf({s})).Fn.return_type.?).ErrorUnion.error_set", .{func.owner_decl.name}); }, .error_set_merged => { const names = ty.castTag(.error_set_merged).?.data.keys(); @@ -1836,6 +1836,21 @@ pub const Type = extern union { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, .generic_poison => unreachable, + .var_args_param => unreachable, + .bound_fn => unreachable, + + // TODO get rid of these Type.Tag values. + .atomic_order => unreachable, + .atomic_rmw_op => unreachable, + .calling_convention => unreachable, + .address_space => unreachable, + .float_mode => unreachable, + .reduce_op => unreachable, + .call_options => unreachable, + .prefetch_options => unreachable, + .export_options => unreachable, + .extern_options => unreachable, + .type_info => unreachable, .u1, .u8, @@ -1873,39 +1888,44 @@ pub const Type = extern union { .comptime_int, .comptime_float, .noreturn, - .var_args_param, - .bound_fn, => return maybeDupe(@tagName(t), ally, is_arena), - .enum_literal => return maybeDupe("@Type(.EnumLiteral)", ally, is_arena), - .@"null" => return maybeDupe("@Type(.Null)", ally, is_arena), - .@"undefined" => return maybeDupe("@Type(.Undefined)", ally, is_arena), + .enum_literal => return maybeDupe("@TypeOf(.enum_literal)", ally, is_arena), + .@"null" => return maybeDupe("@TypeOf(null)", ally, is_arena), + .@"undefined" => return maybeDupe("@TypeOf(undefined)", ally, is_arena), + .empty_struct_literal => return maybeDupe("@TypeOf(.{})", ally, is_arena), - .empty_struct, .empty_struct_literal => return maybeDupe("struct {}", ally, is_arena), + .empty_struct => { + const namespace = ty.castTag(.empty_struct).?.data; + var buffer = std.ArrayList(u8).init(ally); + defer buffer.deinit(); + try namespace.renderFullyQualifiedName("", buffer.writer()); + return buffer.toOwnedSliceSentinel(0); + }, .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0)); + return try struct_obj.owner_decl.getFullyQualifiedName(ally); }, .@"union", .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0)); + return try union_obj.owner_decl.getFullyQualifiedName(ally); }, .enum_full, .enum_nonexhaustive => { const enum_full = ty.cast(Payload.EnumFull).?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0)); + return try enum_full.owner_decl.getFullyQualifiedName(ally); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0)); + return try enum_simple.owner_decl.getFullyQualifiedName(ally); }, .enum_numbered => { const enum_numbered = ty.castTag(.enum_numbered).?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0)); + return try enum_numbered.owner_decl.getFullyQualifiedName(ally); }, .@"opaque" => { const opaque_obj = ty.cast(Payload.Opaque).?.data; - return try ally.dupeZ(u8, std.mem.sliceTo(opaque_obj.owner_decl.name, 0)); + return try opaque_obj.owner_decl.getFullyQualifiedName(ally); }, .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena), @@ -1919,21 +1939,79 @@ pub const Type = extern union { .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena), .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena), .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena), - .atomic_order => return maybeDupe("AtomicOrder", ally, is_arena), - .atomic_rmw_op => return maybeDupe("AtomicRmwOp", ally, is_arena), - .calling_convention => return maybeDupe("CallingConvention", ally, is_arena), - .address_space => return maybeDupe("AddressSpace", ally, is_arena), - .float_mode => return maybeDupe("FloatMode", ally, is_arena), - .reduce_op => return maybeDupe("ReduceOp", ally, is_arena), - .call_options => return maybeDupe("CallOptions", ally, is_arena), - .prefetch_options => return maybeDupe("PrefetchOptions", ally, is_arena), - .export_options => return maybeDupe("ExportOptions", ally, is_arena), - .extern_options => return maybeDupe("ExternOptions", ally, is_arena), - .type_info => return maybeDupe("Type", ally, is_arena), + + .error_set_inferred => { + const func = ty.castTag(.error_set_inferred).?.data.func; + + var buf = std.ArrayList(u8).init(ally); + defer buf.deinit(); + try buf.appendSlice("@typeInfo(@typeInfo(@TypeOf("); + try func.owner_decl.renderFullyQualifiedName(buf.writer()); + try buf.appendSlice(")).Fn.return_type.?).ErrorUnion.error_set"); + return try buf.toOwnedSliceSentinel(0); + }, + + .function => { + const fn_info = ty.fnInfo(); + var buf = std.ArrayList(u8).init(ally); + defer buf.deinit(); + try buf.appendSlice("fn("); + for (fn_info.param_types) |param_type, i| { + if (i != 0) try buf.appendSlice(", "); + const param_name = try param_type.nameAllocAdvanced(ally, is_arena); + defer if (!is_arena) ally.free(param_name); + try buf.appendSlice(param_name); + } + if (fn_info.is_var_args) { + if (fn_info.param_types.len != 0) { + try buf.appendSlice(", "); + } + try buf.appendSlice("..."); + } + try buf.appendSlice(") "); + if (fn_info.cc != .Unspecified) { + try buf.appendSlice("callconv(."); + try buf.appendSlice(@tagName(fn_info.cc)); + try buf.appendSlice(") "); + } + if (fn_info.alignment != 0) { + try buf.writer().print("align({d}) ", .{fn_info.alignment}); + } + { + const ret_ty_name = try fn_info.return_type.nameAllocAdvanced(ally, is_arena); + defer if (!is_arena) ally.free(ret_ty_name); + try buf.appendSlice(ret_ty_name); + } + return try buf.toOwnedSliceSentinel(0); + }, + + .error_union => { + const error_union = ty.castTag(.error_union).?.data; + + var buf = std.ArrayList(u8).init(ally); + defer buf.deinit(); + + { + const err_set_ty_name = try error_union.error_set.nameAllocAdvanced(ally, is_arena); + defer if (!is_arena) ally.free(err_set_ty_name); + try buf.appendSlice(err_set_ty_name); + } + + try buf.appendSlice("!"); + + { + const payload_ty_name = try error_union.payload.nameAllocAdvanced(ally, is_arena); + defer if (!is_arena) ally.free(payload_ty_name); + try buf.appendSlice(payload_ty_name); + } + + return try buf.toOwnedSliceSentinel(0); + }, else => { // TODO this is wasteful and also an incorrect implementation of `@typeName` var buf = std.ArrayList(u8).init(ally); + defer buf.deinit(); try buf.writer().print("{}", .{ty}); return try buf.toOwnedSliceSentinel(0); }, diff --git a/test/behavior.zig b/test/behavior.zig index 67fd25b04d..03a02c3ea4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -120,6 +120,7 @@ test { _ = @import("behavior/tuple.zig"); _ = @import("behavior/type.zig"); _ = @import("behavior/type_info.zig"); + _ = @import("behavior/typename.zig"); _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); _ = @import("behavior/union.zig"); @@ -179,7 +180,6 @@ test { _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/select.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); - _ = @import("behavior/typename.zig"); } } } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index bb3232c01c..889519b056 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -199,11 +199,18 @@ const OpaqueA = opaque {}; const OpaqueB = opaque {}; test "opaque types" { - try expect(*OpaqueA != *OpaqueB); - if (builtin.zig_backend == .stage1) { // TODO make this pass for stage2 - try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); + if (builtin.zig_backend == .stage1) { + // stage1 gets the type names wrong + return error.SkipZigTest; } + + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + try expect(*OpaqueA != *OpaqueB); + + try expect(mem.eql(u8, @typeName(OpaqueA), "behavior.basic.OpaqueA")); + try expect(mem.eql(u8, @typeName(OpaqueB), "behavior.basic.OpaqueB")); } const global_a: i32 = 1234; diff --git a/test/behavior/bugs/3779.zig b/test/behavior/bugs/3779.zig index d33b155c2a..51b1725dd9 100644 --- a/test/behavior/bugs/3779.zig +++ b/test/behavior/bugs/3779.zig @@ -28,9 +28,10 @@ const type_name = @typeName(TestType); const ptr_type_name: [*:0]const u8 = type_name; test "@typeName() returns a string literal" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong try std.testing.expectEqual(*const [type_name.len:0]u8, @TypeOf(type_name)); - try std.testing.expectEqualStrings("TestType", type_name); - try std.testing.expectEqualStrings("TestType", ptr_type_name[0..type_name.len]); + try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", type_name); + try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]); } const actual_contents = @embedFile("3779_file_to_embed.txt"); diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index 4d87e0030c..97a6605fc2 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -1,6 +1,8 @@ +const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; -const expectEqualSlices = std.testing.expectEqualSlices; +const expectEqualStrings = std.testing.expectEqualStrings; +const expectStringStartsWith = std.testing.expectStringStartsWith; // Most tests here can be comptime but use runtime so that a stacktrace // can show failure location. @@ -9,50 +11,124 @@ const expectEqualSlices = std.testing.expectEqualSlices; // root file. Running a test against this file as root will result in // failures. -// CAUTION: this test is source-location sensitive. -test "anon fn param - source-location sensitive" { - // https://github.com/ziglang/zig/issues/9339 - try expectEqualSlices(u8, @typeName(TypeFromFn(struct {})), "behavior.typename.TypeFromFn(behavior.typename.struct:15:52)"); - try expectEqualSlices(u8, @typeName(TypeFromFn(union { unused: u8 })), "behavior.typename.TypeFromFn(behavior.typename.union:16:52)"); - try expectEqualSlices(u8, @typeName(TypeFromFn(enum { unused })), "behavior.typename.TypeFromFn(behavior.typename.enum:17:52)"); +test "anon fn param" { + if (builtin.zig_backend == .stage1) { + // stage1 uses line/column for the names but we're moving away from that for + // incremental compilation purposes. + return error.SkipZigTest; + } - try expectEqualSlices( - u8, - @typeName(TypeFromFn3(struct {}, union { unused: u8 }, enum { unused })), - "behavior.typename.TypeFromFn3(behavior.typename.struct:21:31,behavior.typename.union:21:42,behavior.typename.enum:21:64)", + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 + + // https://github.com/ziglang/zig/issues/9339 + try expectEqualStringsIgnoreDigits( + "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__struct_0)", + @typeName(TypeFromFn(struct {})), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__union_0)", + @typeName(TypeFromFn(union { unused: u8 })), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__enum_0)", + @typeName(TypeFromFn(enum { unused })), + ); + + try expectEqualStringsIgnoreDigits( + "behavior.typename.TypeFromFnB(behavior.typename.test.anon fn param__struct_0,behavior.typename.test.anon fn param__union_0,behavior.typename.test.anon fn param__enum_0)", + @typeName(TypeFromFnB(struct {}, union { unused: u8 }, enum { unused })), ); } -// CAUTION: this test is source-location sensitive. test "anon field init" { + if (builtin.zig_backend == .stage1) { + // stage1 uses line/column for the names but we're moving away from that for + // incremental compilation purposes. + return error.SkipZigTest; + } + + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 + const Foo = .{ .T1 = struct {}, .T2 = union { unused: u8 }, .T3 = enum { unused }, }; - try expectEqualSlices(u8, @typeName(Foo.T1), "behavior.typename.struct:29:15"); - try expectEqualSlices(u8, @typeName(Foo.T2), "behavior.typename.union:30:15"); - try expectEqualSlices(u8, @typeName(Foo.T3), "behavior.typename.enum:31:15"); + try expectEqualStringsIgnoreDigits( + "behavior.typename.test.anon field init__struct_0", + @typeName(Foo.T1), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.test.anon field init__union_0", + @typeName(Foo.T2), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.test.anon field init__enum_0", + @typeName(Foo.T3), + ); } test "basic" { - try expectEqualSlices(u8, @typeName(i64), "i64"); - try expectEqualSlices(u8, @typeName(*usize), "*usize"); - try expectEqualSlices(u8, @typeName([]u8), "[]u8"); + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 + + try expectEqualStrings(@typeName(i64), "i64"); + try expectEqualStrings(@typeName(*usize), "*usize"); + try expectEqualStrings(@typeName([]u8), "[]u8"); } test "top level decl" { - try expectEqualSlices(u8, @typeName(A_Struct), "A_Struct"); - try expectEqualSlices(u8, @typeName(A_Union), "A_Union"); - try expectEqualSlices(u8, @typeName(A_Enum), "A_Enum"); + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } + + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 + + try expectEqualStrings( + "behavior.typename.A_Struct", + @typeName(A_Struct), + ); + try expectEqualStrings( + "behavior.typename.A_Union", + @typeName(A_Union), + ); + try expectEqualStrings( + "behavior.typename.A_Enum", + @typeName(A_Enum), + ); // regular fn, without error - try expectEqualSlices(u8, @typeName(@TypeOf(regular)), "fn() void"); + try expectEqualStrings( + "fn() void", + @typeName(@TypeOf(regular)), + ); // regular fn inside struct, with error - try expectEqualSlices(u8, @typeName(@TypeOf(B.doTest)), "fn() @typeInfo(@typeInfo(@TypeOf(behavior.typename.B.doTest)).Fn.return_type.?).ErrorUnion.error_set!void"); + try expectEqualStrings( + "fn() @typeInfo(@typeInfo(@TypeOf(behavior.typename.B.doTest)).Fn.return_type.?).ErrorUnion.error_set!void", + @typeName(@TypeOf(B.doTest)), + ); // generic fn - try expectEqualSlices(u8, @typeName(@TypeOf(TypeFromFn)), "fn(type) anytype"); + try expectEqualStrings( + "fn(type) type", + @typeName(@TypeOf(TypeFromFn)), + ); } const A_Struct = struct {}; @@ -66,6 +142,17 @@ const A_Enum = enum { fn regular() void {} test "fn body decl" { + if (builtin.zig_backend == .stage1) { + // stage1 fails to return fully qualified namespaces. + return error.SkipZigTest; + } + + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 + try B.doTest(); } @@ -79,20 +166,50 @@ const B = struct { unused, }; - try expectEqualSlices(u8, @typeName(B_Struct), "B_Struct"); - try expectEqualSlices(u8, @typeName(B_Union), "B_Union"); - try expectEqualSlices(u8, @typeName(B_Enum), "B_Enum"); + try expectEqualStringsIgnoreDigits( + "behavior.typename.B.doTest__struct_0", + @typeName(B_Struct), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.B.doTest__union_0", + @typeName(B_Union), + ); + try expectEqualStringsIgnoreDigits( + "behavior.typename.B.doTest__enum_0", + @typeName(B_Enum), + ); } }; test "fn param" { - // https://github.com/ziglang/zig/issues/675 - try expectEqualSlices(u8, @typeName(TypeFromFn(u8)), "behavior.typename.TypeFromFn(u8)"); - try expectEqualSlices(u8, @typeName(TypeFromFn(A_Struct)), "behavior.typename.TypeFromFn(behavior.typename.A_Struct)"); - try expectEqualSlices(u8, @typeName(TypeFromFn(A_Union)), "behavior.typename.TypeFromFn(behavior.typename.A_Union)"); - try expectEqualSlices(u8, @typeName(TypeFromFn(A_Enum)), "behavior.typename.TypeFromFn(behavior.typename.A_Enum)"); + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) 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 - try expectEqualSlices(u8, @typeName(TypeFromFn2(u8, bool)), "behavior.typename.TypeFromFn2(u8,bool)"); + // https://github.com/ziglang/zig/issues/675 + try expectEqualStrings( + "behavior.typename.TypeFromFn(u8)", + @typeName(TypeFromFn(u8)), + ); + try expectEqualStrings( + "behavior.typename.TypeFromFn(behavior.typename.A_Struct)", + @typeName(TypeFromFn(A_Struct)), + ); + try expectEqualStrings( + "behavior.typename.TypeFromFn(behavior.typename.A_Union)", + @typeName(TypeFromFn(A_Union)), + ); + try expectEqualStrings( + "behavior.typename.TypeFromFn(behavior.typename.A_Enum)", + @typeName(TypeFromFn(A_Enum)), + ); + + try expectEqualStrings( + "behavior.typename.TypeFromFn2(u8,bool)", + @typeName(TypeFromFn2(u8, bool)), + ); } fn TypeFromFn(comptime T: type) type { @@ -106,9 +223,32 @@ fn TypeFromFn2(comptime T1: type, comptime T2: type) type { return struct {}; } -fn TypeFromFn3(comptime T1: type, comptime T2: type, comptime T3: type) type { +fn TypeFromFnB(comptime T1: type, comptime T2: type, comptime T3: type) type { _ = T1; _ = T2; _ = T3; return struct {}; } + +/// Replaces integers in `actual` with '0' before doing the test. +pub fn expectEqualStringsIgnoreDigits(expected: []const u8, actual: []const u8) !void { + var actual_buf: [1024]u8 = undefined; + var actual_i: usize = 0; + var last_digit = false; + for (actual) |byte| { + switch (byte) { + '0'...'9' => { + if (last_digit) continue; + last_digit = true; + actual_buf[actual_i] = '0'; + actual_i += 1; + }, + else => { + last_digit = false; + actual_buf[actual_i] = byte; + actual_i += 1; + }, + } + } + return expectEqualStrings(expected, actual_buf[0..actual_i]); +} diff --git a/test/behavior/undefined.zig b/test/behavior/undefined.zig index abfbcc23bc..52cde0057e 100644 --- a/test/behavior/undefined.zig +++ b/test/behavior/undefined.zig @@ -77,8 +77,12 @@ test "assign undefined to struct with method" { } test "type name of undefined" { + if (builtin.zig_backend == .stage1) { + // stage1 gets the type name wrong + return error.SkipZigTest; + } if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const x = undefined; - try expect(mem.eql(u8, @typeName(@TypeOf(x)), "@Type(.Undefined)")); + try expect(mem.eql(u8, @typeName(@TypeOf(x)), "@TypeOf(undefined)")); }