mirror of
https://github.com/ziglang/zig.git
synced 2024-11-14 16:13:24 +00:00
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.
This commit is contained in:
parent
69d78bdae4
commit
f3f5a5d05b
@ -9702,8 +9702,9 @@ test "integer truncation" {
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@TypeOf#}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 => {
|
||||
|
68
src/Sema.zig
68
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,
|
||||
|
126
src/type.zig
126
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);
|
||||
},
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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)"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user