authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-18 00:12:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-18 00:12:22-07:00
logf3f5a5d05b7056aceb408b701613242d053019ab
treeacb912950d734b0c25b38c1ae82a8558d1313057
parent69d78bdae4a5d0ec5877add5936ae8e9daf09140

stage2: improve `@typeName`

* make it always return a fully qualified name. stage1 is inconsistent about this. * AstGen: fix anon_name_strategy to correctly be `func` when anon type creation happens in the operand of the return expression. * Sema: implement type names for the "function" naming strategy. * Put "enum", "union", "opaque", or "struct" in place of "anon" when creating respective anonymous Decl names. * std.testing: add `expectStringStartsWith`. Didn't end up using it after all. Also this enables the real test runner for stage2 LLVM backend (sans wasm32) since it works now.

11 files changed, 369 insertions(+), 87 deletions(-)

doc/langref.html.in+2-1
...@@ -9702,8 +9702,9 @@ test "integer truncation" {...@@ -9702,8 +9702,9 @@ test "integer truncation" {
9702 <p>9702 <p>
9703 This function returns the string representation of a type, as9703 This function returns the string representation of a type, as
9704 an array. It is equivalent to a string literal of the type name.9704 an array. It is equivalent to a string literal of the type name.
9705 The returned type name is fully qualified with the parent namespace included
9706 as part of the type name with a series of dots.
9705 </p>9707 </p>
9706
9707 {#header_close#}9708 {#header_close#}
97089709
9709 {#header_open|@TypeOf#}9710 {#header_open|@TypeOf#}
lib/std/special/test_runner.zig+3-1
...@@ -23,7 +23,9 @@ fn processArgs() void {...@@ -23,7 +23,9 @@ fn processArgs() void {
23}23}
2424
25pub fn main() void {25pub fn main() void {
26 if (builtin.zig_backend != .stage1) {26 if (builtin.zig_backend != .stage1 and
27 (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32))
28 {
27 return main2() catch @panic("test failure");29 return main2() catch @panic("test failure");
28 }30 }
29 if (builtin.zig_backend == .stage1) processArgs();31 if (builtin.zig_backend == .stage1) processArgs();
lib/std/testing.zig+20
...@@ -445,6 +445,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {...@@ -445,6 +445,26 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
445 }445 }
446}446}
447447
448pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const u8) !void {
449 if (std.mem.startsWith(u8, actual, expected_starts_with))
450 return;
451
452 const shortened_actual = if (actual.len >= expected_starts_with.len)
453 actual[0..expected_starts_with.len]
454 else
455 actual;
456
457 print("\n====== expected to start with: =========\n", .{});
458 printWithVisibleNewlines(expected_starts_with);
459 print("\n====== instead ended with: ===========\n", .{});
460 printWithVisibleNewlines(shortened_actual);
461 print("\n========= full output: ==============\n", .{});
462 printWithVisibleNewlines(actual);
463 print("\n======================================\n", .{});
464
465 return error.TestExpectedStartsWith;
466}
467
448pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void {468pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void {
449 if (std.mem.endsWith(u8, actual, expected_ends_with))469 if (std.mem.endsWith(u8, actual, expected_ends_with))
450 return;470 return;
src/AstGen.zig+3
...@@ -6293,7 +6293,10 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6293,7 +6293,10 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6293 } else .{6293 } else .{
6294 .ty = try gz.addNodeExtended(.ret_type, node),6294 .ty = try gz.addNodeExtended(.ret_type, node),
6295 };6295 };
6296 const prev_anon_name_strategy = gz.anon_name_strategy;
6297 gz.anon_name_strategy = .func;
6296 const operand = try reachableExpr(gz, scope, rl, operand_node, node);6298 const operand = try reachableExpr(gz, scope, rl, operand_node, node);
6299 gz.anon_name_strategy = prev_anon_name_strategy;
62976300
6298 switch (nodeMayEvalToError(tree, operand_node)) {6301 switch (nodeMayEvalToError(tree, operand_node)) {
6299 .never => {6302 .never => {
src/Sema.zig+47-21
...@@ -1767,7 +1767,7 @@ fn zirStructDecl(...@@ -1767,7 +1767,7 @@ fn zirStructDecl(
1767 const struct_obj = try new_decl_arena_allocator.create(Module.Struct);1767 const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
1768 const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);1768 const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
1769 const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);1769 const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
1770 const type_name = try sema.createTypeName(block, small.name_strategy);1770 const type_name = try sema.createTypeName(block, small.name_strategy, "struct");
1771 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{1771 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
1772 .ty = Type.type,1772 .ty = Type.type,
1773 .val = struct_val,1773 .val = struct_val,
...@@ -1796,28 +1796,54 @@ fn zirStructDecl(...@@ -1796,28 +1796,54 @@ fn zirStructDecl(
1796 return sema.analyzeDeclVal(block, src, new_decl);1796 return sema.analyzeDeclVal(block, src, new_decl);
1797}1797}
17981798
1799fn createTypeName(sema: *Sema, block: *Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 {1799fn createTypeName(
1800 sema: *Sema,
1801 block: *Block,
1802 name_strategy: Zir.Inst.NameStrategy,
1803 anon_prefix: []const u8,
1804) ![:0]u8 {
1800 switch (name_strategy) {1805 switch (name_strategy) {
1801 .anon => {1806 .anon => {
1802 // It would be neat to have "struct:line:column" but this name has1807 // It would be neat to have "struct:line:column" but this name has
1803 // to survive incremental updates, where it may have been shifted down1808 // to survive incremental updates, where it may have been shifted down
1804 // or up to a different line, but unchanged, and thus not unnecessarily1809 // or up to a different line, but unchanged, and thus not unnecessarily
1805 // semantically analyzed.1810 // semantically analyzed.
1811 // This name is also used as the key in the parent namespace so it cannot be
1812 // renamed.
1806 const name_index = sema.mod.getNextAnonNameIndex();1813 const name_index = sema.mod.getNextAnonNameIndex();
1807 return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{1814 return std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{
1808 block.src_decl.name, name_index,1815 block.src_decl.name, anon_prefix, name_index,
1809 });1816 });
1810 },1817 },
1811 .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)),1818 .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)),
1812 .func => {1819 .func => {
1813 const name_index = sema.mod.getNextAnonNameIndex();1820 const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst);
1814 const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{1821 const zir_tags = sema.code.instructions.items(.tag);
1815 block.src_decl.name, name_index,1822
1816 });1823 var buf = std.ArrayList(u8).init(sema.gpa);
1817 log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{1824 defer buf.deinit();
1818 name,1825 try buf.appendSlice(mem.sliceTo(block.src_decl.name, 0));
1819 });1826 try buf.appendSlice("(");
1820 return name;1827
1828 var arg_i: usize = 0;
1829 for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) {
1830 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
1831 const arg = sema.inst_map.get(zir_inst).?;
1832 // The comptime call code in analyzeCall already did this, so we're
1833 // just repeating it here and it's guaranteed to work.
1834 const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable;
1835
1836 if (arg_i != 0) try buf.appendSlice(",");
1837 try buf.writer().print("{}", .{arg_val});
1838
1839 arg_i += 1;
1840 continue;
1841 },
1842 else => continue,
1843 };
1844
1845 try buf.appendSlice(")");
1846 return buf.toOwnedSliceSentinel(0);
1821 },1847 },
1822 }1848 }
1823}1849}
...@@ -1877,7 +1903,7 @@ fn zirEnumDecl(...@@ -1877,7 +1903,7 @@ fn zirEnumDecl(
1877 };1903 };
1878 const enum_ty = Type.initPayload(&enum_ty_payload.base);1904 const enum_ty = Type.initPayload(&enum_ty_payload.base);
1879 const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);1905 const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
1880 const type_name = try sema.createTypeName(block, small.name_strategy);1906 const type_name = try sema.createTypeName(block, small.name_strategy, "enum");
1881 const new_decl = try mod.createAnonymousDeclNamed(block, .{1907 const new_decl = try mod.createAnonymousDeclNamed(block, .{
1882 .ty = Type.type,1908 .ty = Type.type,
1883 .val = enum_val,1909 .val = enum_val,
...@@ -2088,7 +2114,7 @@ fn zirUnionDecl(...@@ -2088,7 +2114,7 @@ fn zirUnionDecl(
2088 };2114 };
2089 const union_ty = Type.initPayload(&union_payload.base);2115 const union_ty = Type.initPayload(&union_payload.base);
2090 const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);2116 const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
2091 const type_name = try sema.createTypeName(block, small.name_strategy);2117 const type_name = try sema.createTypeName(block, small.name_strategy, "union");
2092 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{2118 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
2093 .ty = Type.type,2119 .ty = Type.type,
2094 .val = union_val,2120 .val = union_val,
...@@ -2156,7 +2182,7 @@ fn zirOpaqueDecl(...@@ -2156,7 +2182,7 @@ fn zirOpaqueDecl(
2156 };2182 };
2157 const opaque_ty = Type.initPayload(&opaque_ty_payload.base);2183 const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
2158 const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);2184 const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
2159 const type_name = try sema.createTypeName(block, small.name_strategy);2185 const type_name = try sema.createTypeName(block, small.name_strategy, "opaque");
2160 const new_decl = try mod.createAnonymousDeclNamed(block, .{2186 const new_decl = try mod.createAnonymousDeclNamed(block, .{
2161 .ty = Type.type,2187 .ty = Type.type,
2162 .val = opaque_val,2188 .val = opaque_val,
...@@ -2204,7 +2230,7 @@ fn zirErrorSetDecl(...@@ -2204,7 +2230,7 @@ fn zirErrorSetDecl(
2204 const error_set = try new_decl_arena_allocator.create(Module.ErrorSet);2230 const error_set = try new_decl_arena_allocator.create(Module.ErrorSet);
2205 const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set);2231 const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set);
2206 const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty);2232 const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty);
2207 const type_name = try sema.createTypeName(block, name_strategy);2233 const type_name = try sema.createTypeName(block, name_strategy, "error");
2208 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{2234 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
2209 .ty = Type.type,2235 .ty = Type.type,
2210 .val = error_set_val,2236 .val = error_set_val,
...@@ -12697,7 +12723,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12697,7 +12723,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12697 };12723 };
12698 const enum_ty = Type.initPayload(&enum_ty_payload.base);12724 const enum_ty = Type.initPayload(&enum_ty_payload.base);
12699 const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);12725 const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
12700 const type_name = try sema.createTypeName(block, .anon);12726 const type_name = try sema.createTypeName(block, .anon, "enum");
12701 const new_decl = try mod.createAnonymousDeclNamed(block, .{12727 const new_decl = try mod.createAnonymousDeclNamed(block, .{
12702 .ty = Type.type,12728 .ty = Type.type,
12703 .val = enum_val,12729 .val = enum_val,
...@@ -12707,7 +12733,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12707,7 +12733,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1270712733
12708 enum_obj.* = .{12734 enum_obj.* = .{
12709 .owner_decl = new_decl,12735 .owner_decl = new_decl,
12710 .tag_ty = Type.initTag(.@"null"),12736 .tag_ty = Type.@"null",
12711 .tag_ty_inferred = true,12737 .tag_ty_inferred = true,
12712 .fields = .{},12738 .fields = .{},
12713 .values = .{},12739 .values = .{},
...@@ -12785,7 +12811,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12785,7 +12811,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12785 };12811 };
12786 const opaque_ty = Type.initPayload(&opaque_ty_payload.base);12812 const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
12787 const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);12813 const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
12788 const type_name = try sema.createTypeName(block, .anon);12814 const type_name = try sema.createTypeName(block, .anon, "opaque");
12789 const new_decl = try mod.createAnonymousDeclNamed(block, .{12815 const new_decl = try mod.createAnonymousDeclNamed(block, .{
12790 .ty = Type.type,12816 .ty = Type.type,
12791 .val = opaque_val,12817 .val = opaque_val,
...@@ -12836,7 +12862,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12836,7 +12862,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12836 };12862 };
12837 const union_ty = Type.initPayload(&union_payload.base);12863 const union_ty = Type.initPayload(&union_payload.base);
12838 const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);12864 const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
12839 const type_name = try sema.createTypeName(block, .anon);12865 const type_name = try sema.createTypeName(block, .anon, "union");
12840 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{12866 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
12841 .ty = Type.type,12867 .ty = Type.type,
12842 .val = new_union_val,12868 .val = new_union_val,
...@@ -13001,7 +13027,7 @@ fn reifyStruct(...@@ -13001,7 +13027,7 @@ fn reifyStruct(
13001 const struct_obj = try new_decl_arena_allocator.create(Module.Struct);13027 const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
13002 const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);13028 const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
13003 const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);13029 const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
13004 const type_name = try sema.createTypeName(block, .anon);13030 const type_name = try sema.createTypeName(block, .anon, "struct");
13005 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{13031 const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
13006 .ty = Type.type,13032 .ty = Type.type,
13007 .val = new_struct_val,13033 .val = new_struct_val,
src/type.zig+102-24
...@@ -1793,7 +1793,7 @@ pub const Type = extern union {...@@ -1793,7 +1793,7 @@ pub const Type = extern union {
1793 },1793 },
1794 .error_set_inferred => {1794 .error_set_inferred => {
1795 const func = ty.castTag(.error_set_inferred).?.data.func;1795 const func = ty.castTag(.error_set_inferred).?.data.func;
1796 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});1796 return writer.print("@typeInfo(@typeInfo(@TypeOf({s})).Fn.return_type.?).ErrorUnion.error_set", .{func.owner_decl.name});
1797 },1797 },
1798 .error_set_merged => {1798 .error_set_merged => {
1799 const names = ty.castTag(.error_set_merged).?.data.keys();1799 const names = ty.castTag(.error_set_merged).?.data.keys();
...@@ -1836,6 +1836,21 @@ pub const Type = extern union {...@@ -1836,6 +1836,21 @@ pub const Type = extern union {
1836 .inferred_alloc_const => unreachable,1836 .inferred_alloc_const => unreachable,
1837 .inferred_alloc_mut => unreachable,1837 .inferred_alloc_mut => unreachable,
1838 .generic_poison => unreachable,1838 .generic_poison => unreachable,
1839 .var_args_param => unreachable,
1840 .bound_fn => unreachable,
1841
1842 // TODO get rid of these Type.Tag values.
1843 .atomic_order => unreachable,
1844 .atomic_rmw_op => unreachable,
1845 .calling_convention => unreachable,
1846 .address_space => unreachable,
1847 .float_mode => unreachable,
1848 .reduce_op => unreachable,
1849 .call_options => unreachable,
1850 .prefetch_options => unreachable,
1851 .export_options => unreachable,
1852 .extern_options => unreachable,
1853 .type_info => unreachable,
18391854
1840 .u1,1855 .u1,
1841 .u8,1856 .u8,
...@@ -1873,39 +1888,44 @@ pub const Type = extern union {...@@ -1873,39 +1888,44 @@ pub const Type = extern union {
1873 .comptime_int,1888 .comptime_int,
1874 .comptime_float,1889 .comptime_float,
1875 .noreturn,1890 .noreturn,
1876 .var_args_param,
1877 .bound_fn,
1878 => return maybeDupe(@tagName(t), ally, is_arena),1891 => return maybeDupe(@tagName(t), ally, is_arena),
18791892
1880 .enum_literal => return maybeDupe("@Type(.EnumLiteral)", ally, is_arena),1893 .enum_literal => return maybeDupe("@TypeOf(.enum_literal)", ally, is_arena),
1881 .@"null" => return maybeDupe("@Type(.Null)", ally, is_arena),1894 .@"null" => return maybeDupe("@TypeOf(null)", ally, is_arena),
1882 .@"undefined" => return maybeDupe("@Type(.Undefined)", ally, is_arena),1895 .@"undefined" => return maybeDupe("@TypeOf(undefined)", ally, is_arena),
1896 .empty_struct_literal => return maybeDupe("@TypeOf(.{})", ally, is_arena),
18831897
1884 .empty_struct, .empty_struct_literal => return maybeDupe("struct {}", ally, is_arena),1898 .empty_struct => {
1899 const namespace = ty.castTag(.empty_struct).?.data;
1900 var buffer = std.ArrayList(u8).init(ally);
1901 defer buffer.deinit();
1902 try namespace.renderFullyQualifiedName("", buffer.writer());
1903 return buffer.toOwnedSliceSentinel(0);
1904 },
18851905
1886 .@"struct" => {1906 .@"struct" => {
1887 const struct_obj = ty.castTag(.@"struct").?.data;1907 const struct_obj = ty.castTag(.@"struct").?.data;
1888 return try ally.dupeZ(u8, std.mem.sliceTo(struct_obj.owner_decl.name, 0));1908 return try struct_obj.owner_decl.getFullyQualifiedName(ally);
1889 },1909 },
1890 .@"union", .union_tagged => {1910 .@"union", .union_tagged => {
1891 const union_obj = ty.cast(Payload.Union).?.data;1911 const union_obj = ty.cast(Payload.Union).?.data;
1892 return try ally.dupeZ(u8, std.mem.sliceTo(union_obj.owner_decl.name, 0));1912 return try union_obj.owner_decl.getFullyQualifiedName(ally);
1893 },1913 },
1894 .enum_full, .enum_nonexhaustive => {1914 .enum_full, .enum_nonexhaustive => {
1895 const enum_full = ty.cast(Payload.EnumFull).?.data;1915 const enum_full = ty.cast(Payload.EnumFull).?.data;
1896 return try ally.dupeZ(u8, std.mem.sliceTo(enum_full.owner_decl.name, 0));1916 return try enum_full.owner_decl.getFullyQualifiedName(ally);
1897 },1917 },
1898 .enum_simple => {1918 .enum_simple => {
1899 const enum_simple = ty.castTag(.enum_simple).?.data;1919 const enum_simple = ty.castTag(.enum_simple).?.data;
1900 return try ally.dupeZ(u8, std.mem.sliceTo(enum_simple.owner_decl.name, 0));1920 return try enum_simple.owner_decl.getFullyQualifiedName(ally);
1901 },1921 },
1902 .enum_numbered => {1922 .enum_numbered => {
1903 const enum_numbered = ty.castTag(.enum_numbered).?.data;1923 const enum_numbered = ty.castTag(.enum_numbered).?.data;
1904 return try ally.dupeZ(u8, std.mem.sliceTo(enum_numbered.owner_decl.name, 0));1924 return try enum_numbered.owner_decl.getFullyQualifiedName(ally);
1905 },1925 },
1906 .@"opaque" => {1926 .@"opaque" => {
1907 const opaque_obj = ty.cast(Payload.Opaque).?.data;1927 const opaque_obj = ty.cast(Payload.Opaque).?.data;
1908 return try ally.dupeZ(u8, std.mem.sliceTo(opaque_obj.owner_decl.name, 0));1928 return try opaque_obj.owner_decl.getFullyQualifiedName(ally);
1909 },1929 },
19101930
1911 .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena),1931 .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena),
...@@ -1919,21 +1939,79 @@ pub const Type = extern union {...@@ -1919,21 +1939,79 @@ pub const Type = extern union {
1919 .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena),1939 .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena),
1920 .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena),1940 .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena),
1921 .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena),1941 .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena),
1922 .atomic_order => return maybeDupe("AtomicOrder", ally, is_arena),1942
1923 .atomic_rmw_op => return maybeDupe("AtomicRmwOp", ally, is_arena),1943 .error_set_inferred => {
1924 .calling_convention => return maybeDupe("CallingConvention", ally, is_arena),1944 const func = ty.castTag(.error_set_inferred).?.data.func;
1925 .address_space => return maybeDupe("AddressSpace", ally, is_arena),1945
1926 .float_mode => return maybeDupe("FloatMode", ally, is_arena),1946 var buf = std.ArrayList(u8).init(ally);
1927 .reduce_op => return maybeDupe("ReduceOp", ally, is_arena),1947 defer buf.deinit();
1928 .call_options => return maybeDupe("CallOptions", ally, is_arena),1948 try buf.appendSlice("@typeInfo(@typeInfo(@TypeOf(");
1929 .prefetch_options => return maybeDupe("PrefetchOptions", ally, is_arena),1949 try func.owner_decl.renderFullyQualifiedName(buf.writer());
1930 .export_options => return maybeDupe("ExportOptions", ally, is_arena),1950 try buf.appendSlice(")).Fn.return_type.?).ErrorUnion.error_set");
1931 .extern_options => return maybeDupe("ExternOptions", ally, is_arena),1951 return try buf.toOwnedSliceSentinel(0);
1932 .type_info => return maybeDupe("Type", ally, is_arena),1952 },
1953
1954 .function => {
1955 const fn_info = ty.fnInfo();
1956 var buf = std.ArrayList(u8).init(ally);
1957 defer buf.deinit();
1958 try buf.appendSlice("fn(");
1959 for (fn_info.param_types) |param_type, i| {
1960 if (i != 0) try buf.appendSlice(", ");
1961 const param_name = try param_type.nameAllocAdvanced(ally, is_arena);
1962 defer if (!is_arena) ally.free(param_name);
1963 try buf.appendSlice(param_name);
1964 }
1965 if (fn_info.is_var_args) {
1966 if (fn_info.param_types.len != 0) {
1967 try buf.appendSlice(", ");
1968 }
1969 try buf.appendSlice("...");
1970 }
1971 try buf.appendSlice(") ");
1972 if (fn_info.cc != .Unspecified) {
1973 try buf.appendSlice("callconv(.");
1974 try buf.appendSlice(@tagName(fn_info.cc));
1975 try buf.appendSlice(") ");
1976 }
1977 if (fn_info.alignment != 0) {
1978 try buf.writer().print("align({d}) ", .{fn_info.alignment});
1979 }
1980 {
1981 const ret_ty_name = try fn_info.return_type.nameAllocAdvanced(ally, is_arena);
1982 defer if (!is_arena) ally.free(ret_ty_name);
1983 try buf.appendSlice(ret_ty_name);
1984 }
1985 return try buf.toOwnedSliceSentinel(0);
1986 },
1987
1988 .error_union => {
1989 const error_union = ty.castTag(.error_union).?.data;
1990
1991 var buf = std.ArrayList(u8).init(ally);
1992 defer buf.deinit();
1993
1994 {
1995 const err_set_ty_name = try error_union.error_set.nameAllocAdvanced(ally, is_arena);
1996 defer if (!is_arena) ally.free(err_set_ty_name);
1997 try buf.appendSlice(err_set_ty_name);
1998 }
1999
2000 try buf.appendSlice("!");
2001
2002 {
2003 const payload_ty_name = try error_union.payload.nameAllocAdvanced(ally, is_arena);
2004 defer if (!is_arena) ally.free(payload_ty_name);
2005 try buf.appendSlice(payload_ty_name);
2006 }
2007
2008 return try buf.toOwnedSliceSentinel(0);
2009 },
19332010
1934 else => {2011 else => {
1935 // TODO this is wasteful and also an incorrect implementation of `@typeName`2012 // TODO this is wasteful and also an incorrect implementation of `@typeName`
1936 var buf = std.ArrayList(u8).init(ally);2013 var buf = std.ArrayList(u8).init(ally);
2014 defer buf.deinit();
1937 try buf.writer().print("{}", .{ty});2015 try buf.writer().print("{}", .{ty});
1938 return try buf.toOwnedSliceSentinel(0);2016 return try buf.toOwnedSliceSentinel(0);
1939 },2017 },
test/behavior.zig+1-1
...@@ -120,6 +120,7 @@ test {...@@ -120,6 +120,7 @@ test {
120 _ = @import("behavior/tuple.zig");120 _ = @import("behavior/tuple.zig");
121 _ = @import("behavior/type.zig");121 _ = @import("behavior/type.zig");
122 _ = @import("behavior/type_info.zig");122 _ = @import("behavior/type_info.zig");
123 _ = @import("behavior/typename.zig");
123 _ = @import("behavior/undefined.zig");124 _ = @import("behavior/undefined.zig");
124 _ = @import("behavior/underscore.zig");125 _ = @import("behavior/underscore.zig");
125 _ = @import("behavior/union.zig");126 _ = @import("behavior/union.zig");
...@@ -179,7 +180,6 @@ test {...@@ -179,7 +180,6 @@ test {
179 _ = @import("behavior/bugs/7027.zig");180 _ = @import("behavior/bugs/7027.zig");
180 _ = @import("behavior/select.zig");181 _ = @import("behavior/select.zig");
181 _ = @import("behavior/struct_contains_slice_of_itself.zig");182 _ = @import("behavior/struct_contains_slice_of_itself.zig");
182 _ = @import("behavior/typename.zig");
183 }183 }
184 }184 }
185 }185 }
test/behavior/basic.zig+11-4
...@@ -199,11 +199,18 @@ const OpaqueA = opaque {};...@@ -199,11 +199,18 @@ const OpaqueA = opaque {};
199const OpaqueB = opaque {};199const OpaqueB = opaque {};
200200
201test "opaque types" {201test "opaque types" {
202 try expect(*OpaqueA != *OpaqueB);202 if (builtin.zig_backend == .stage1) {
203 if (builtin.zig_backend == .stage1) { // TODO make this pass for stage2203 // stage1 gets the type names wrong
204 try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA"));204 return error.SkipZigTest;
205 try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB"));
206 }205 }
206
207 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
208 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
209
210 try expect(*OpaqueA != *OpaqueB);
211
212 try expect(mem.eql(u8, @typeName(OpaqueA), "behavior.basic.OpaqueA"));
213 try expect(mem.eql(u8, @typeName(OpaqueB), "behavior.basic.OpaqueB"));
207}214}
208215
209const global_a: i32 = 1234;216const global_a: i32 = 1234;
test/behavior/bugs/3779.zig+3-2
...@@ -28,9 +28,10 @@ const type_name = @typeName(TestType);...@@ -28,9 +28,10 @@ const type_name = @typeName(TestType);
28const ptr_type_name: [*:0]const u8 = type_name;28const ptr_type_name: [*:0]const u8 = type_name;
2929
30test "@typeName() returns a string literal" {30test "@typeName() returns a string literal" {
31 if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong
31 try std.testing.expectEqual(*const [type_name.len:0]u8, @TypeOf(type_name));32 try std.testing.expectEqual(*const [type_name.len:0]u8, @TypeOf(type_name));
32 try std.testing.expectEqualStrings("TestType", type_name);33 try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", type_name);
33 try std.testing.expectEqualStrings("TestType", ptr_type_name[0..type_name.len]);34 try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]);
34}35}
3536
36const actual_contents = @embedFile("3779_file_to_embed.txt");37const actual_contents = @embedFile("3779_file_to_embed.txt");
test/behavior/typename.zig+172-32
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1const builtin = @import("builtin");
1const std = @import("std");2const std = @import("std");
2const expect = std.testing.expect;3const expect = std.testing.expect;
3const expectEqualSlices = std.testing.expectEqualSlices;4const expectEqualStrings = std.testing.expectEqualStrings;
5const expectStringStartsWith = std.testing.expectStringStartsWith;
46
5// Most tests here can be comptime but use runtime so that a stacktrace7// Most tests here can be comptime but use runtime so that a stacktrace
6// can show failure location.8// can show failure location.
...@@ -9,50 +11,124 @@ const expectEqualSlices = std.testing.expectEqualSlices;...@@ -9,50 +11,124 @@ const expectEqualSlices = std.testing.expectEqualSlices;
9// root file. Running a test against this file as root will result in11// root file. Running a test against this file as root will result in
10// failures.12// failures.
1113
12// CAUTION: this test is source-location sensitive.14test "anon fn param" {
13test "anon fn param - source-location sensitive" {15 if (builtin.zig_backend == .stage1) {
16 // stage1 uses line/column for the names but we're moving away from that for
17 // incremental compilation purposes.
18 return error.SkipZigTest;
19 }
20
21 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
22 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
26
14 // https://github.com/ziglang/zig/issues/933927 // https://github.com/ziglang/zig/issues/9339
15 try expectEqualSlices(u8, @typeName(TypeFromFn(struct {})), "behavior.typename.TypeFromFn(behavior.typename.struct:15:52)");28 try expectEqualStringsIgnoreDigits(
16 try expectEqualSlices(u8, @typeName(TypeFromFn(union { unused: u8 })), "behavior.typename.TypeFromFn(behavior.typename.union:16:52)");29 "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__struct_0)",
17 try expectEqualSlices(u8, @typeName(TypeFromFn(enum { unused })), "behavior.typename.TypeFromFn(behavior.typename.enum:17:52)");30 @typeName(TypeFromFn(struct {})),
31 );
32 try expectEqualStringsIgnoreDigits(
33 "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__union_0)",
34 @typeName(TypeFromFn(union { unused: u8 })),
35 );
36 try expectEqualStringsIgnoreDigits(
37 "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__enum_0)",
38 @typeName(TypeFromFn(enum { unused })),
39 );
1840
19 try expectEqualSlices(41 try expectEqualStringsIgnoreDigits(
20 u8,42 "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)",
21 @typeName(TypeFromFn3(struct {}, union { unused: u8 }, enum { unused })),43 @typeName(TypeFromFnB(struct {}, union { unused: u8 }, enum { unused })),
22 "behavior.typename.TypeFromFn3(behavior.typename.struct:21:31,behavior.typename.union:21:42,behavior.typename.enum:21:64)",
23 );44 );
24}45}
2546
26// CAUTION: this test is source-location sensitive.
27test "anon field init" {47test "anon field init" {
48 if (builtin.zig_backend == .stage1) {
49 // stage1 uses line/column for the names but we're moving away from that for
50 // incremental compilation purposes.
51 return error.SkipZigTest;
52 }
53
54 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
55 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
56 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
59
28 const Foo = .{60 const Foo = .{
29 .T1 = struct {},61 .T1 = struct {},
30 .T2 = union { unused: u8 },62 .T2 = union { unused: u8 },
31 .T3 = enum { unused },63 .T3 = enum { unused },
32 };64 };
3365
34 try expectEqualSlices(u8, @typeName(Foo.T1), "behavior.typename.struct:29:15");66 try expectEqualStringsIgnoreDigits(
35 try expectEqualSlices(u8, @typeName(Foo.T2), "behavior.typename.union:30:15");67 "behavior.typename.test.anon field init__struct_0",
36 try expectEqualSlices(u8, @typeName(Foo.T3), "behavior.typename.enum:31:15");68 @typeName(Foo.T1),
69 );
70 try expectEqualStringsIgnoreDigits(
71 "behavior.typename.test.anon field init__union_0",
72 @typeName(Foo.T2),
73 );
74 try expectEqualStringsIgnoreDigits(
75 "behavior.typename.test.anon field init__enum_0",
76 @typeName(Foo.T3),
77 );
37}78}
3879
39test "basic" {80test "basic" {
40 try expectEqualSlices(u8, @typeName(i64), "i64");81 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
41 try expectEqualSlices(u8, @typeName(*usize), "*usize");82 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
42 try expectEqualSlices(u8, @typeName([]u8), "[]u8");83 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
84 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
85 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
86
87 try expectEqualStrings(@typeName(i64), "i64");
88 try expectEqualStrings(@typeName(*usize), "*usize");
89 try expectEqualStrings(@typeName([]u8), "[]u8");
43}90}
4491
45test "top level decl" {92test "top level decl" {
46 try expectEqualSlices(u8, @typeName(A_Struct), "A_Struct");93 if (builtin.zig_backend == .stage1) {
47 try expectEqualSlices(u8, @typeName(A_Union), "A_Union");94 // stage1 fails to return fully qualified namespaces.
48 try expectEqualSlices(u8, @typeName(A_Enum), "A_Enum");95 return error.SkipZigTest;
96 }
97
98 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
100 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
102 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
103
104 try expectEqualStrings(
105 "behavior.typename.A_Struct",
106 @typeName(A_Struct),
107 );
108 try expectEqualStrings(
109 "behavior.typename.A_Union",
110 @typeName(A_Union),
111 );
112 try expectEqualStrings(
113 "behavior.typename.A_Enum",
114 @typeName(A_Enum),
115 );
49116
50 // regular fn, without error117 // regular fn, without error
51 try expectEqualSlices(u8, @typeName(@TypeOf(regular)), "fn() void");118 try expectEqualStrings(
119 "fn() void",
120 @typeName(@TypeOf(regular)),
121 );
52 // regular fn inside struct, with error122 // regular fn inside struct, with error
53 try expectEqualSlices(u8, @typeName(@TypeOf(B.doTest)), "fn() @typeInfo(@typeInfo(@TypeOf(behavior.typename.B.doTest)).Fn.return_type.?).ErrorUnion.error_set!void");123 try expectEqualStrings(
124 "fn() @typeInfo(@typeInfo(@TypeOf(behavior.typename.B.doTest)).Fn.return_type.?).ErrorUnion.error_set!void",
125 @typeName(@TypeOf(B.doTest)),
126 );
54 // generic fn127 // generic fn
55 try expectEqualSlices(u8, @typeName(@TypeOf(TypeFromFn)), "fn(type) anytype");128 try expectEqualStrings(
129 "fn(type) type",
130 @typeName(@TypeOf(TypeFromFn)),
131 );
56}132}
57133
58const A_Struct = struct {};134const A_Struct = struct {};
...@@ -66,6 +142,17 @@ const A_Enum = enum {...@@ -66,6 +142,17 @@ const A_Enum = enum {
66fn regular() void {}142fn regular() void {}
67143
68test "fn body decl" {144test "fn body decl" {
145 if (builtin.zig_backend == .stage1) {
146 // stage1 fails to return fully qualified namespaces.
147 return error.SkipZigTest;
148 }
149
150 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
151 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
152 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
153 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
155
69 try B.doTest();156 try B.doTest();
70}157}
71158
...@@ -79,20 +166,50 @@ const B = struct {...@@ -79,20 +166,50 @@ const B = struct {
79 unused,166 unused,
80 };167 };
81168
82 try expectEqualSlices(u8, @typeName(B_Struct), "B_Struct");169 try expectEqualStringsIgnoreDigits(
83 try expectEqualSlices(u8, @typeName(B_Union), "B_Union");170 "behavior.typename.B.doTest__struct_0",
84 try expectEqualSlices(u8, @typeName(B_Enum), "B_Enum");171 @typeName(B_Struct),
172 );
173 try expectEqualStringsIgnoreDigits(
174 "behavior.typename.B.doTest__union_0",
175 @typeName(B_Union),
176 );
177 try expectEqualStringsIgnoreDigits(
178 "behavior.typename.B.doTest__enum_0",
179 @typeName(B_Enum),
180 );
85 }181 }
86};182};
87183
88test "fn param" {184test "fn param" {
185 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
186 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
187 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
188 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
189 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
190
89 // https://github.com/ziglang/zig/issues/675191 // https://github.com/ziglang/zig/issues/675
90 try expectEqualSlices(u8, @typeName(TypeFromFn(u8)), "behavior.typename.TypeFromFn(u8)");192 try expectEqualStrings(
91 try expectEqualSlices(u8, @typeName(TypeFromFn(A_Struct)), "behavior.typename.TypeFromFn(behavior.typename.A_Struct)");193 "behavior.typename.TypeFromFn(u8)",
92 try expectEqualSlices(u8, @typeName(TypeFromFn(A_Union)), "behavior.typename.TypeFromFn(behavior.typename.A_Union)");194 @typeName(TypeFromFn(u8)),
93 try expectEqualSlices(u8, @typeName(TypeFromFn(A_Enum)), "behavior.typename.TypeFromFn(behavior.typename.A_Enum)");195 );
196 try expectEqualStrings(
197 "behavior.typename.TypeFromFn(behavior.typename.A_Struct)",
198 @typeName(TypeFromFn(A_Struct)),
199 );
200 try expectEqualStrings(
201 "behavior.typename.TypeFromFn(behavior.typename.A_Union)",
202 @typeName(TypeFromFn(A_Union)),
203 );
204 try expectEqualStrings(
205 "behavior.typename.TypeFromFn(behavior.typename.A_Enum)",
206 @typeName(TypeFromFn(A_Enum)),
207 );
94208
95 try expectEqualSlices(u8, @typeName(TypeFromFn2(u8, bool)), "behavior.typename.TypeFromFn2(u8,bool)");209 try expectEqualStrings(
210 "behavior.typename.TypeFromFn2(u8,bool)",
211 @typeName(TypeFromFn2(u8, bool)),
212 );
96}213}
97214
98fn TypeFromFn(comptime T: type) type {215fn TypeFromFn(comptime T: type) type {
...@@ -106,9 +223,32 @@ fn TypeFromFn2(comptime T1: type, comptime T2: type) type {...@@ -106,9 +223,32 @@ fn TypeFromFn2(comptime T1: type, comptime T2: type) type {
106 return struct {};223 return struct {};
107}224}
108225
109fn TypeFromFn3(comptime T1: type, comptime T2: type, comptime T3: type) type {226fn TypeFromFnB(comptime T1: type, comptime T2: type, comptime T3: type) type {
110 _ = T1;227 _ = T1;
111 _ = T2;228 _ = T2;
112 _ = T3;229 _ = T3;
113 return struct {};230 return struct {};
114}231}
232
233/// Replaces integers in `actual` with '0' before doing the test.
234pub fn expectEqualStringsIgnoreDigits(expected: []const u8, actual: []const u8) !void {
235 var actual_buf: [1024]u8 = undefined;
236 var actual_i: usize = 0;
237 var last_digit = false;
238 for (actual) |byte| {
239 switch (byte) {
240 '0'...'9' => {
241 if (last_digit) continue;
242 last_digit = true;
243 actual_buf[actual_i] = '0';
244 actual_i += 1;
245 },
246 else => {
247 last_digit = false;
248 actual_buf[actual_i] = byte;
249 actual_i += 1;
250 },
251 }
252 }
253 return expectEqualStrings(expected, actual_buf[0..actual_i]);
254}
test/behavior/undefined.zig+5-1
...@@ -77,8 +77,12 @@ test "assign undefined to struct with method" {...@@ -77,8 +77,12 @@ test "assign undefined to struct with method" {
77}77}
7878
79test "type name of undefined" {79test "type name of undefined" {
80 if (builtin.zig_backend == .stage1) {
81 // stage1 gets the type name wrong
82 return error.SkipZigTest;
83 }
80 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;84 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8185
82 const x = undefined;86 const x = undefined;
83 try expect(mem.eql(u8, @typeName(@TypeOf(x)), "@Type(.Undefined)"));87 try expect(mem.eql(u8, @typeName(@TypeOf(x)), "@TypeOf(undefined)"));
84}88}