| author | |
| committer | |
| log | f3f5a5d05b7056aceb408b701613242d053019ab |
| tree | acb912950d734b0c25b38c1ae82a8558d1313057 |
| parent | 69d78bdae4a5d0ec5877add5936ae8e9daf09140 |
* 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 | 9702 | <p> |
| 9703 | 9703 | This function returns the string representation of a type, as |
| 9704 | 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 | 9707 | </p> |
| 9706 | ||
| 9707 | 9708 | {#header_close#} |
| 9708 | 9709 | |
| 9709 | 9710 | {#header_open|@TypeOf#} |
lib/std/special/test_runner.zig+3-1| ... | ... | @@ -23,7 +23,9 @@ fn processArgs() void { |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | pub 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 | 29 | return main2() catch @panic("test failure"); |
| 28 | 30 | } |
| 29 | 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 | 445 | } |
| 446 | 446 | } |
| 447 | 447 | |
| 448 | pub 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 | ||
| 448 | 468 | pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void { |
| 449 | 469 | if (std.mem.endsWith(u8, actual, expected_ends_with)) |
| 450 | 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 | 6293 | } else .{ |
| 6294 | 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 | 6298 | const operand = try reachableExpr(gz, scope, rl, operand_node, node); |
| 6299 | gz.anon_name_strategy = prev_anon_name_strategy; | |
| 6297 | 6300 | |
| 6298 | 6301 | switch (nodeMayEvalToError(tree, operand_node)) { |
| 6299 | 6302 | .never => { |
src/Sema.zig+47-21| ... | ... | @@ -1767,7 +1767,7 @@ fn zirStructDecl( |
| 1767 | 1767 | const struct_obj = try new_decl_arena_allocator.create(Module.Struct); |
| 1768 | 1768 | const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); |
| 1769 | 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 | 1771 | const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ |
| 1772 | 1772 | .ty = Type.type, |
| 1773 | 1773 | .val = struct_val, |
| ... | ... | @@ -1796,28 +1796,54 @@ fn zirStructDecl( |
| 1796 | 1796 | return sema.analyzeDeclVal(block, src, new_decl); |
| 1797 | 1797 | } |
| 1798 | 1798 | |
| 1799 | fn createTypeName(sema: *Sema, block: *Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 { | |
| 1799 | fn createTypeName( | |
| 1800 | sema: *Sema, | |
| 1801 | block: *Block, | |
| 1802 | name_strategy: Zir.Inst.NameStrategy, | |
| 1803 | anon_prefix: []const u8, | |
| 1804 | ) ![:0]u8 { | |
| 1800 | 1805 | switch (name_strategy) { |
| 1801 | 1806 | .anon => { |
| 1802 | 1807 | // It would be neat to have "struct:line:column" but this name has |
| 1803 | 1808 | // to survive incremental updates, where it may have been shifted down |
| 1804 | 1809 | // or up to a different line, but unchanged, and thus not unnecessarily |
| 1805 | 1810 | // semantically analyzed. |
| 1811 | // This name is also used as the key in the parent namespace so it cannot be | |
| 1812 | // renamed. | |
| 1806 | 1813 | const name_index = sema.mod.getNextAnonNameIndex(); |
| 1807 | return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ | |
| 1808 | block.src_decl.name, name_index, | |
| 1814 | return std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{ | |
| 1815 | block.src_decl.name, anon_prefix, name_index, | |
| 1809 | 1816 | }); |
| 1810 | 1817 | }, |
| 1811 | 1818 | .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)), |
| 1812 | 1819 | .func => { |
| 1813 | const name_index = sema.mod.getNextAnonNameIndex(); | |
| 1814 | const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{ | |
| 1815 | block.src_decl.name, name_index, | |
| 1816 | }); | |
| 1817 | log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{ | |
| 1818 | name, | |
| 1819 | }); | |
| 1820 | return name; | |
| 1820 | const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst); | |
| 1821 | const zir_tags = sema.code.instructions.items(.tag); | |
| 1822 | ||
| 1823 | var buf = std.ArrayList(u8).init(sema.gpa); | |
| 1824 | defer buf.deinit(); | |
| 1825 | try buf.appendSlice(mem.sliceTo(block.src_decl.name, 0)); | |
| 1826 | try buf.appendSlice("("); | |
| 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 | 1903 | }; |
| 1878 | 1904 | const enum_ty = Type.initPayload(&enum_ty_payload.base); |
| 1879 | 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 | 1907 | const new_decl = try mod.createAnonymousDeclNamed(block, .{ |
| 1882 | 1908 | .ty = Type.type, |
| 1883 | 1909 | .val = enum_val, |
| ... | ... | @@ -2088,7 +2114,7 @@ fn zirUnionDecl( |
| 2088 | 2114 | }; |
| 2089 | 2115 | const union_ty = Type.initPayload(&union_payload.base); |
| 2090 | 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 | 2118 | const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ |
| 2093 | 2119 | .ty = Type.type, |
| 2094 | 2120 | .val = union_val, |
| ... | ... | @@ -2156,7 +2182,7 @@ fn zirOpaqueDecl( |
| 2156 | 2182 | }; |
| 2157 | 2183 | const opaque_ty = Type.initPayload(&opaque_ty_payload.base); |
| 2158 | 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 | 2186 | const new_decl = try mod.createAnonymousDeclNamed(block, .{ |
| 2161 | 2187 | .ty = Type.type, |
| 2162 | 2188 | .val = opaque_val, |
| ... | ... | @@ -2204,7 +2230,7 @@ fn zirErrorSetDecl( |
| 2204 | 2230 | const error_set = try new_decl_arena_allocator.create(Module.ErrorSet); |
| 2205 | 2231 | const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set); |
| 2206 | 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 | 2234 | const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ |
| 2209 | 2235 | .ty = Type.type, |
| 2210 | 2236 | .val = error_set_val, |
| ... | ... | @@ -12697,7 +12723,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 12697 | 12723 | }; |
| 12698 | 12724 | const enum_ty = Type.initPayload(&enum_ty_payload.base); |
| 12699 | 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 | 12727 | const new_decl = try mod.createAnonymousDeclNamed(block, .{ |
| 12702 | 12728 | .ty = Type.type, |
| 12703 | 12729 | .val = enum_val, |
| ... | ... | @@ -12707,7 +12733,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 12707 | 12733 | |
| 12708 | 12734 | enum_obj.* = .{ |
| 12709 | 12735 | .owner_decl = new_decl, |
| 12710 | .tag_ty = Type.initTag(.@"null"), | |
| 12736 | .tag_ty = Type.@"null", | |
| 12711 | 12737 | .tag_ty_inferred = true, |
| 12712 | 12738 | .fields = .{}, |
| 12713 | 12739 | .values = .{}, |
| ... | ... | @@ -12785,7 +12811,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 12785 | 12811 | }; |
| 12786 | 12812 | const opaque_ty = Type.initPayload(&opaque_ty_payload.base); |
| 12787 | 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 | 12815 | const new_decl = try mod.createAnonymousDeclNamed(block, .{ |
| 12790 | 12816 | .ty = Type.type, |
| 12791 | 12817 | .val = opaque_val, |
| ... | ... | @@ -12836,7 +12862,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I |
| 12836 | 12862 | }; |
| 12837 | 12863 | const union_ty = Type.initPayload(&union_payload.base); |
| 12838 | 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 | 12866 | const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ |
| 12841 | 12867 | .ty = Type.type, |
| 12842 | 12868 | .val = new_union_val, |
| ... | ... | @@ -13001,7 +13027,7 @@ fn reifyStruct( |
| 13001 | 13027 | const struct_obj = try new_decl_arena_allocator.create(Module.Struct); |
| 13002 | 13028 | const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj); |
| 13003 | 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 | 13031 | const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ |
| 13006 | 13032 | .ty = Type.type, |
| 13007 | 13033 | .val = new_struct_val, |
src/type.zig+102-24| ... | ... | @@ -1793,7 +1793,7 @@ pub const Type = extern union { |
| 1793 | 1793 | }, |
| 1794 | 1794 | .error_set_inferred => { |
| 1795 | 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 | 1798 | .error_set_merged => { |
| 1799 | 1799 | const names = ty.castTag(.error_set_merged).?.data.keys(); |
| ... | ... | @@ -1836,6 +1836,21 @@ pub const Type = extern union { |
| 1836 | 1836 | .inferred_alloc_const => unreachable, |
| 1837 | 1837 | .inferred_alloc_mut => unreachable, |
| 1838 | 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, | |
| 1839 | 1854 | |
| 1840 | 1855 | .u1, |
| 1841 | 1856 | .u8, |
| ... | ... | @@ -1873,39 +1888,44 @@ pub const Type = extern union { |
| 1873 | 1888 | .comptime_int, |
| 1874 | 1889 | .comptime_float, |
| 1875 | 1890 | .noreturn, |
| 1876 | .var_args_param, | |
| 1877 | .bound_fn, | |
| 1878 | 1891 | => return maybeDupe(@tagName(t), ally, is_arena), |
| 1879 | 1892 | |
| 1880 | .enum_literal => return maybeDupe("@Type(.EnumLiteral)", ally, is_arena), | |
| 1881 | .@"null" => return maybeDupe("@Type(.Null)", ally, is_arena), | |
| 1882 | .@"undefined" => return maybeDupe("@Type(.Undefined)", ally, is_arena), | |
| 1893 | .enum_literal => return maybeDupe("@TypeOf(.enum_literal)", ally, is_arena), | |
| 1894 | .@"null" => return maybeDupe("@TypeOf(null)", ally, is_arena), | |
| 1895 | .@"undefined" => return maybeDupe("@TypeOf(undefined)", ally, is_arena), | |
| 1896 | .empty_struct_literal => return maybeDupe("@TypeOf(.{})", ally, is_arena), | |
| 1883 | 1897 | |
| 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 | }, | |
| 1885 | 1905 | |
| 1886 | 1906 | .@"struct" => { |
| 1887 | 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 | 1910 | .@"union", .union_tagged => { |
| 1891 | 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 | 1914 | .enum_full, .enum_nonexhaustive => { |
| 1895 | 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 | 1918 | .enum_simple => { |
| 1899 | 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 | 1922 | .enum_numbered => { |
| 1903 | 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 | 1926 | .@"opaque" => { |
| 1907 | 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 | }, |
| 1910 | 1930 | |
| 1911 | 1931 | .anyerror_void_error_union => return maybeDupe("anyerror!void", ally, is_arena), |
| ... | ... | @@ -1919,21 +1939,79 @@ pub const Type = extern union { |
| 1919 | 1939 | .manyptr_u8 => return maybeDupe("[*]u8", ally, is_arena), |
| 1920 | 1940 | .manyptr_const_u8 => return maybeDupe("[*]const u8", ally, is_arena), |
| 1921 | 1941 | .manyptr_const_u8_sentinel_0 => return maybeDupe("[*:0]const u8", ally, is_arena), |
| 1922 | .atomic_order => return maybeDupe("AtomicOrder", ally, is_arena), | |
| 1923 | .atomic_rmw_op => return maybeDupe("AtomicRmwOp", ally, is_arena), | |
| 1924 | .calling_convention => return maybeDupe("CallingConvention", ally, is_arena), | |
| 1925 | .address_space => return maybeDupe("AddressSpace", ally, is_arena), | |
| 1926 | .float_mode => return maybeDupe("FloatMode", ally, is_arena), | |
| 1927 | .reduce_op => return maybeDupe("ReduceOp", ally, is_arena), | |
| 1928 | .call_options => return maybeDupe("CallOptions", ally, is_arena), | |
| 1929 | .prefetch_options => return maybeDupe("PrefetchOptions", ally, is_arena), | |
| 1930 | .export_options => return maybeDupe("ExportOptions", ally, is_arena), | |
| 1931 | .extern_options => return maybeDupe("ExternOptions", ally, is_arena), | |
| 1932 | .type_info => return maybeDupe("Type", ally, is_arena), | |
| 1942 | ||
| 1943 | .error_set_inferred => { | |
| 1944 | const func = ty.castTag(.error_set_inferred).?.data.func; | |
| 1945 | ||
| 1946 | var buf = std.ArrayList(u8).init(ally); | |
| 1947 | defer buf.deinit(); | |
| 1948 | try buf.appendSlice("@typeInfo(@typeInfo(@TypeOf("); | |
| 1949 | try func.owner_decl.renderFullyQualifiedName(buf.writer()); | |
| 1950 | try buf.appendSlice(")).Fn.return_type.?).ErrorUnion.error_set"); | |
| 1951 | return try buf.toOwnedSliceSentinel(0); | |
| 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 | }, | |
| 1933 | 2010 | |
| 1934 | 2011 | else => { |
| 1935 | 2012 | // TODO this is wasteful and also an incorrect implementation of `@typeName` |
| 1936 | 2013 | var buf = std.ArrayList(u8).init(ally); |
| 2014 | defer buf.deinit(); | |
| 1937 | 2015 | try buf.writer().print("{}", .{ty}); |
| 1938 | 2016 | return try buf.toOwnedSliceSentinel(0); |
| 1939 | 2017 | }, |
test/behavior.zig+1-1| ... | ... | @@ -120,6 +120,7 @@ test { |
| 120 | 120 | _ = @import("behavior/tuple.zig"); |
| 121 | 121 | _ = @import("behavior/type.zig"); |
| 122 | 122 | _ = @import("behavior/type_info.zig"); |
| 123 | _ = @import("behavior/typename.zig"); | |
| 123 | 124 | _ = @import("behavior/undefined.zig"); |
| 124 | 125 | _ = @import("behavior/underscore.zig"); |
| 125 | 126 | _ = @import("behavior/union.zig"); |
| ... | ... | @@ -179,7 +180,6 @@ test { |
| 179 | 180 | _ = @import("behavior/bugs/7027.zig"); |
| 180 | 181 | _ = @import("behavior/select.zig"); |
| 181 | 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 | 199 | const OpaqueB = opaque {}; |
| 200 | 200 | |
| 201 | 201 | test "opaque types" { |
| 202 | try expect(*OpaqueA != *OpaqueB); | |
| 203 | if (builtin.zig_backend == .stage1) { // TODO make this pass for stage2 | |
| 204 | try expect(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); | |
| 205 | try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); | |
| 202 | if (builtin.zig_backend == .stage1) { | |
| 203 | // stage1 gets the type names wrong | |
| 204 | return error.SkipZigTest; | |
| 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 | } |
| 208 | 215 | |
| 209 | 216 | const global_a: i32 = 1234; |
test/behavior/bugs/3779.zig+3-2| ... | ... | @@ -28,9 +28,10 @@ const type_name = @typeName(TestType); |
| 28 | 28 | const ptr_type_name: [*:0]const u8 = type_name; |
| 29 | 29 | |
| 30 | 30 | test "@typeName() returns a string literal" { |
| 31 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; // stage1 gets the type wrong | |
| 31 | 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("TestType", ptr_type_name[0..type_name.len]); | |
| 33 | try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", type_name); | |
| 34 | try std.testing.expectEqualStrings("behavior.bugs.3779.TestType", ptr_type_name[0..type_name.len]); | |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | 37 | const actual_contents = @embedFile("3779_file_to_embed.txt"); |
test/behavior/typename.zig+172-32| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | const builtin = @import("builtin"); | |
| 1 | 2 | const std = @import("std"); |
| 2 | 3 | const expect = std.testing.expect; |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 4 | const expectEqualStrings = std.testing.expectEqualStrings; | |
| 5 | const expectStringStartsWith = std.testing.expectStringStartsWith; | |
| 4 | 6 | |
| 5 | 7 | // Most tests here can be comptime but use runtime so that a stacktrace |
| 6 | 8 | // can show failure location. |
| ... | ... | @@ -9,50 +11,124 @@ const expectEqualSlices = std.testing.expectEqualSlices; |
| 9 | 11 | // root file. Running a test against this file as root will result in |
| 10 | 12 | // failures. |
| 11 | 13 | |
| 12 | // CAUTION: this test is source-location sensitive. | |
| 13 | test "anon fn param - source-location sensitive" { | |
| 14 | test "anon fn param" { | |
| 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 | 27 | // https://github.com/ziglang/zig/issues/9339 |
| 15 | try expectEqualSlices(u8, @typeName(TypeFromFn(struct {})), "behavior.typename.TypeFromFn(behavior.typename.struct:15:52)"); | |
| 16 | try expectEqualSlices(u8, @typeName(TypeFromFn(union { unused: u8 })), "behavior.typename.TypeFromFn(behavior.typename.union:16:52)"); | |
| 17 | try expectEqualSlices(u8, @typeName(TypeFromFn(enum { unused })), "behavior.typename.TypeFromFn(behavior.typename.enum:17:52)"); | |
| 28 | try expectEqualStringsIgnoreDigits( | |
| 29 | "behavior.typename.TypeFromFn(behavior.typename.test.anon fn param__struct_0)", | |
| 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 | ); | |
| 18 | 40 | |
| 19 | try expectEqualSlices( | |
| 20 | u8, | |
| 21 | @typeName(TypeFromFn3(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)", | |
| 41 | try expectEqualStringsIgnoreDigits( | |
| 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)", | |
| 43 | @typeName(TypeFromFnB(struct {}, union { unused: u8 }, enum { unused })), | |
| 23 | 44 | ); |
| 24 | 45 | } |
| 25 | 46 | |
| 26 | // CAUTION: this test is source-location sensitive. | |
| 27 | 47 | test "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 | 60 | const Foo = .{ |
| 29 | 61 | .T1 = struct {}, |
| 30 | 62 | .T2 = union { unused: u8 }, |
| 31 | 63 | .T3 = enum { unused }, |
| 32 | 64 | }; |
| 33 | 65 | |
| 34 | try expectEqualSlices(u8, @typeName(Foo.T1), "behavior.typename.struct:29:15"); | |
| 35 | try expectEqualSlices(u8, @typeName(Foo.T2), "behavior.typename.union:30:15"); | |
| 36 | try expectEqualSlices(u8, @typeName(Foo.T3), "behavior.typename.enum:31:15"); | |
| 66 | try expectEqualStringsIgnoreDigits( | |
| 67 | "behavior.typename.test.anon field init__struct_0", | |
| 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 | } |
| 38 | 79 | |
| 39 | 80 | test "basic" { |
| 40 | try expectEqualSlices(u8, @typeName(i64), "i64"); | |
| 41 | try expectEqualSlices(u8, @typeName(*usize), "*usize"); | |
| 42 | try expectEqualSlices(u8, @typeName([]u8), "[]u8"); | |
| 81 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 82 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 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 | } |
| 44 | 91 | |
| 45 | 92 | test "top level decl" { |
| 46 | try expectEqualSlices(u8, @typeName(A_Struct), "A_Struct"); | |
| 47 | try expectEqualSlices(u8, @typeName(A_Union), "A_Union"); | |
| 48 | try expectEqualSlices(u8, @typeName(A_Enum), "A_Enum"); | |
| 93 | if (builtin.zig_backend == .stage1) { | |
| 94 | // stage1 fails to return fully qualified namespaces. | |
| 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 | ); | |
| 49 | 116 | |
| 50 | 117 | // 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 | 122 | // 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 | 127 | // 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 | } |
| 57 | 133 | |
| 58 | 134 | const A_Struct = struct {}; |
| ... | ... | @@ -66,6 +142,17 @@ const A_Enum = enum { |
| 66 | 142 | fn regular() void {} |
| 67 | 143 | |
| 68 | 144 | test "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 | 156 | try B.doTest(); |
| 70 | 157 | } |
| 71 | 158 | |
| ... | ... | @@ -79,20 +166,50 @@ const B = struct { |
| 79 | 166 | unused, |
| 80 | 167 | }; |
| 81 | 168 | |
| 82 | try expectEqualSlices(u8, @typeName(B_Struct), "B_Struct"); | |
| 83 | try expectEqualSlices(u8, @typeName(B_Union), "B_Union"); | |
| 84 | try expectEqualSlices(u8, @typeName(B_Enum), "B_Enum"); | |
| 169 | try expectEqualStringsIgnoreDigits( | |
| 170 | "behavior.typename.B.doTest__struct_0", | |
| 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 | }; |
| 87 | 183 | |
| 88 | 184 | test "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 | 191 | // https://github.com/ziglang/zig/issues/675 |
| 90 | try expectEqualSlices(u8, @typeName(TypeFromFn(u8)), "behavior.typename.TypeFromFn(u8)"); | |
| 91 | try expectEqualSlices(u8, @typeName(TypeFromFn(A_Struct)), "behavior.typename.TypeFromFn(behavior.typename.A_Struct)"); | |
| 92 | try expectEqualSlices(u8, @typeName(TypeFromFn(A_Union)), "behavior.typename.TypeFromFn(behavior.typename.A_Union)"); | |
| 93 | try expectEqualSlices(u8, @typeName(TypeFromFn(A_Enum)), "behavior.typename.TypeFromFn(behavior.typename.A_Enum)"); | |
| 192 | try expectEqualStrings( | |
| 193 | "behavior.typename.TypeFromFn(u8)", | |
| 194 | @typeName(TypeFromFn(u8)), | |
| 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 | ); | |
| 94 | 208 | |
| 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 | } |
| 97 | 214 | |
| 98 | 215 | fn TypeFromFn(comptime T: type) type { |
| ... | ... | @@ -106,9 +223,32 @@ fn TypeFromFn2(comptime T1: type, comptime T2: type) type { |
| 106 | 223 | return struct {}; |
| 107 | 224 | } |
| 108 | 225 | |
| 109 | fn TypeFromFn3(comptime T1: type, comptime T2: type, comptime T3: type) type { | |
| 226 | fn TypeFromFnB(comptime T1: type, comptime T2: type, comptime T3: type) type { | |
| 110 | 227 | _ = T1; |
| 111 | 228 | _ = T2; |
| 112 | 229 | _ = T3; |
| 113 | 230 | return struct {}; |
| 114 | 231 | } |
| 232 | ||
| 233 | /// Replaces integers in `actual` with '0' before doing the test. | |
| 234 | pub 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 | 77 | } |
| 78 | 78 | |
| 79 | 79 | test "type name of undefined" { |
| 80 | if (builtin.zig_backend == .stage1) { | |
| 81 | // stage1 gets the type name wrong | |
| 82 | return error.SkipZigTest; | |
| 83 | } | |
| 80 | 84 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 81 | 85 | |
| 82 | 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 | } |