Commit ed174b7386

Michael Dusan <michael.dusan@gmail.com>
2021-07-19 08:35:17
stage1: fix anon struct naming in certain cases
- generic "struct:L:C" naming if rloc is NodeTypeStructValueField - generic "struct:L:C" naming if rloc is NodeTypeFnCallExpr - move some tests from test/behavior/misc to test/behavior/typename closes #4330 closes #9339
1 parent 15bec20
Changed files (5)
src/stage1/astgen.cpp
@@ -3454,7 +3454,7 @@ static Stage1ZirInst *astgen_merge_err_sets(Stage1AstGen *ag, Scope *scope, AstN
 
     // TODO only pass type_name when the || operator is the top level AST node in the var decl expr
     Buf bare_name = BUF_INIT;
-    Buf *type_name = get_anon_type_name(ag->codegen, ag->exec, "error", scope, node, &bare_name);
+    Buf *type_name = get_anon_type_name(ag->codegen, ag->exec, "error", scope, node, &bare_name, nullptr);
 
     return ir_build_merge_err_sets(ag, scope, node, op1, op2, type_name);
 }
@@ -7588,42 +7588,73 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o
 }
 
 Buf *get_anon_type_name(CodeGen *codegen, Stage1Zir *exec, const char *kind_name,
-        Scope *scope, AstNode *source_node, Buf *out_bare_name)
-{
-    if (exec != nullptr && exec->name) {
-        ZigType *import = get_scope_import(scope);
-        Buf *namespace_name = buf_alloc();
-        append_namespace_qualification(codegen, namespace_name, import);
-        buf_append_buf(namespace_name, exec->name);
-        buf_init_from_buf(out_bare_name, exec->name);
-        return namespace_name;
-    } else if (exec != nullptr && exec->name_fn != nullptr) {
-        Buf *name = buf_alloc();
-        buf_append_buf(name, &exec->name_fn->symbol_name);
-        buf_appendf(name, "(");
-        render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
-        buf_appendf(name, ")");
-        buf_init_from_buf(out_bare_name, name);
-        return name;
-    } else {
-        ZigType *import = get_scope_import(scope);
-        Buf *namespace_name = buf_alloc();
-        append_namespace_qualification(codegen, namespace_name, import);
-        RootStruct *root_struct = source_node->owner->data.structure.root_struct;
-        TokenLoc tok_loc = root_struct->token_locs[source_node->main_token];
-        buf_appendf(namespace_name, "%s:%u:%u", kind_name,
-                tok_loc.line + 1, tok_loc.column + 1);
-        buf_init_from_buf(out_bare_name, namespace_name);
-        return namespace_name;
+        Scope *scope, AstNode *source_node, Buf *out_bare_name, ResultLoc *result_loc)
+{
+    // See https://ziglang.org/documentation/master/#Struct-Naming .
+    bool force_generic = false;
+    if (result_loc != nullptr
+        && result_loc->source_instruction != nullptr
+        && result_loc->source_instruction->source_node != nullptr
+    ) {
+        switch (result_loc->source_instruction->source_node->type) {
+            case NodeTypeVariableDeclaration: {
+                    ZigType *import = get_scope_import(scope);
+                    Buf *name = buf_alloc();
+                    append_namespace_qualification(codegen, name, import);
+                    const auto &basename = result_loc->source_instruction->source_node->data.variable_declaration.symbol;
+                    buf_append_buf(name, basename);
+                    buf_init_from_buf(out_bare_name, basename);
+                    return name;
+                }
+            case NodeTypeFnCallExpr:
+            case NodeTypeStructValueField:
+                force_generic = true;
+                break;
+            default:
+                break;
+        }
     }
+
+    if (!force_generic) {
+        if (exec != nullptr && exec->name != nullptr) {
+            ZigType *import = get_scope_import(scope);
+            Buf *namespace_name = buf_alloc();
+            append_namespace_qualification(codegen, namespace_name, import);
+            buf_append_buf(namespace_name, exec->name);
+            buf_init_from_buf(out_bare_name, exec->name);
+            return namespace_name;
+        }
+        if (exec != nullptr && exec->name_fn != nullptr) {
+            Buf *name = buf_alloc();
+            buf_append_buf(name, &exec->name_fn->symbol_name);
+            buf_appendf(name, "(");
+            render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
+            buf_appendf(name, ")");
+            buf_init_from_buf(out_bare_name, name);
+            return name;
+        }
+    }
+
+    ZigType *import = get_scope_import(scope);
+    Buf *namespace_name = buf_alloc();
+    append_namespace_qualification(codegen, namespace_name, import);
+    RootStruct *root_struct = source_node->owner->data.structure.root_struct;
+    TokenLoc tok_loc = root_struct->token_locs[source_node->main_token];
+    buf_appendf(namespace_name, "%s:%u:%u", kind_name,
+            tok_loc.line + 1, tok_loc.column + 1);
+    buf_init_from_buf(out_bare_name, namespace_name);
+    return namespace_name;
 }
 
-static Stage1ZirInst *astgen_container_decl(Stage1AstGen *ag, Scope *parent_scope, AstNode *node) {
+static Stage1ZirInst *astgen_container_decl(Stage1AstGen *ag, Scope *parent_scope,
+        AstNode *node, ResultLoc *result_loc)
+{
     assert(node->type == NodeTypeContainerDecl);
 
     ContainerKind kind = node->data.container_decl.kind;
     Buf *bare_name = buf_alloc();
-    Buf *name = get_anon_type_name(ag->codegen, ag->exec, container_string(kind), parent_scope, node, bare_name);
+    Buf *name = get_anon_type_name(ag->codegen,
+        ag->exec, container_string(kind), parent_scope, node, bare_name, result_loc);
 
     ContainerLayout layout = node->data.container_decl.layout;
     ZigType *container_type = get_partial_container_type(ag->codegen, parent_scope,
@@ -7653,7 +7684,7 @@ static Stage1ZirInst *astgen_err_set_decl(Stage1AstGen *ag, Scope *parent_scope,
     uint32_t err_count = node->data.err_set_decl.decls.length;
 
     Buf bare_name = BUF_INIT;
-    Buf *type_name = get_anon_type_name(ag->codegen, ag->exec, "error", parent_scope, node, &bare_name);
+    Buf *type_name = get_anon_type_name(ag->codegen, ag->exec, "error", parent_scope, node, &bare_name, nullptr);
     ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
     buf_init_from_buf(&err_set_type->name, type_name);
     err_set_type->data.error_set.err_count = err_count;
@@ -7963,7 +7994,7 @@ static Stage1ZirInst *astgen_node_raw(Stage1AstGen *ag, AstNode *node, Scope *sc
         case NodeTypeCatchExpr:
             return astgen_catch(ag, scope, node, lval, result_loc);
         case NodeTypeContainerDecl:
-            return ir_lval_wrap(ag, scope, astgen_container_decl(ag, scope, node), lval, result_loc);
+            return ir_lval_wrap(ag, scope, astgen_container_decl(ag, scope, node, result_loc), lval, result_loc);
         case NodeTypeFnProto:
             return ir_lval_wrap(ag, scope, astgen_fn_proto(ag, scope, node), lval, result_loc);
         case NodeTypeErrorSetDecl:
src/stage1/astgen.hpp
@@ -32,6 +32,6 @@ void destroy_instruction_src(Stage1ZirInst *inst);
 
 bool ir_should_inline(Stage1Zir *exec, Scope *scope);
 Buf *get_anon_type_name(CodeGen *codegen, Stage1Zir *exec, const char *kind_name,
-        Scope *scope, AstNode *source_node, Buf *out_bare_name);
+        Scope *scope, AstNode *source_node, Buf *out_bare_name, ResultLoc *result_loc);
 
 #endif
src/stage1/ir.cpp
@@ -10423,7 +10423,7 @@ static Stage1AirInst *ir_analyze_tuple_cat(IrAnalyze *ira, Scope *scope, AstNode
 
     Buf *bare_name = buf_alloc();
     Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
-            scope, source_node, bare_name);
+            scope, source_node, bare_name, nullptr);
     ZigType *new_type = get_partial_container_type(ira->codegen, scope,
         ContainerKindStruct, source_node, buf_ptr(name), bare_name, ContainerLayoutAuto);
     new_type->data.structure.special = StructSpecialInferredTuple;
@@ -10755,7 +10755,7 @@ static Stage1AirInst *ir_analyze_tuple_mult(IrAnalyze *ira, Scope *scope, AstNod
 
     Buf *bare_name = buf_alloc();
     Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
-        scope, source_node, bare_name);
+        scope, source_node, bare_name, nullptr);
     ZigType *new_type = get_partial_container_type(ira->codegen, scope,
         ContainerKindStruct, source_node, buf_ptr(name), bare_name, ContainerLayoutAuto);
     new_type->data.structure.special = StructSpecialInferredTuple;
@@ -12277,7 +12277,7 @@ static Stage1AirInst *ir_analyze_instruction_resolve_result(IrAnalyze *ira, Stag
         if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) {
             Buf *bare_name = buf_alloc();
             Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
-                    instruction->base.scope, instruction->base.source_node, bare_name);
+                    instruction->base.scope, instruction->base.source_node, bare_name, nullptr);
 
             StructSpecial struct_special = StructSpecialInferredStruct;
             if (instruction->base.source_node->type == NodeTypeContainerInitExpr &&
@@ -18898,7 +18898,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
 
             Buf *bare_name = buf_alloc();
             Buf *full_name = get_anon_type_name(ira->codegen,
-                ira->zir, "opaque", scope, source_node, bare_name);
+                ira->zir, "opaque", scope, source_node, bare_name, nullptr);
             return get_opaque_type(ira->codegen,
                 scope, source_node, buf_ptr(full_name), bare_name);
         }
@@ -18945,7 +18945,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
             assert(is_slice(slice->type));
             ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
             Buf bare_name = BUF_INIT;
-            buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->zir, "error", scope, source_node, &bare_name));
+            buf_init_from_buf(&err_set_type->name,
+                get_anon_type_name(ira->codegen, ira->zir, "error", scope, source_node, &bare_name, nullptr));
             err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
             err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
             err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
@@ -19024,7 +19025,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
 
             ZigType *entry = new_type_table_entry(ZigTypeIdStruct);
             buf_init_from_buf(&entry->name,
-                get_anon_type_name(ira->codegen, ira->zir, "struct", scope, source_node, &entry->name));
+                get_anon_type_name(ira->codegen, ira->zir, "struct", scope, source_node, &entry->name, nullptr));
             entry->data.structure.decl_node = source_node;
             entry->data.structure.fields = alloc_type_struct_fields(fields_len);
             entry->data.structure.fields_by_name.init(fields_len);
@@ -19134,7 +19135,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
 
             ZigType *entry = new_type_table_entry(ZigTypeIdEnum);
             buf_init_from_buf(&entry->name,
-                get_anon_type_name(ira->codegen, ira->zir, "enum", scope, source_node, &entry->name));
+                get_anon_type_name(ira->codegen, ira->zir, "enum", scope, source_node, &entry->name, nullptr));
             entry->data.enumeration.decl_node = source_node;
             entry->data.enumeration.tag_int_type = tag_type;
             entry->data.enumeration.decls_scope = create_decls_scope(
@@ -19216,7 +19217,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, Scope *scope, AstNode *source_
 
             ZigType *entry = new_type_table_entry(ZigTypeIdUnion);
             buf_init_from_buf(&entry->name,
-                get_anon_type_name(ira->codegen, ira->zir, "union", scope, source_node, &entry->name));
+                get_anon_type_name(ira->codegen, ira->zir, "union", scope, source_node, &entry->name, nullptr));
             entry->data.unionation.decl_node = source_node;
             entry->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(fields_len);
             entry->data.unionation.fields_by_name.init(fields_len);
test/behavior/misc.zig
@@ -436,30 +436,6 @@ const AUnion = union {
     Two: void,
 };
 
-test "@typeName" {
-    const Struct = struct {};
-    const Union = union {
-        unused: u8,
-    };
-    const Enum = enum {
-        Unused,
-    };
-    comptime {
-        try expect(mem.eql(u8, @typeName(i64), "i64"));
-        try expect(mem.eql(u8, @typeName(*usize), "*usize"));
-        // https://github.com/ziglang/zig/issues/675
-        try expect(mem.eql(u8, "behavior.misc.TypeFromFn(u8)", @typeName(TypeFromFn(u8))));
-        try expect(mem.eql(u8, @typeName(Struct), "Struct"));
-        try expect(mem.eql(u8, @typeName(Union), "Union"));
-        try expect(mem.eql(u8, @typeName(Enum), "Enum"));
-    }
-}
-
-fn TypeFromFn(comptime T: type) type {
-    _ = T;
-    return struct {};
-}
-
 test "double implicit cast in same expression" {
     var x = @as(i32, @as(u16, nine()));
     try expect(x == 9);
test/behavior/typename.zig
@@ -2,6 +2,113 @@ const std = @import("std");
 const expect = std.testing.expect;
 const expectEqualSlices = std.testing.expectEqualSlices;
 
-test "slice" {
-    try expectEqualSlices(u8, "[]u8", @typeName([]u8));
+// Most tests here can be comptime but use runtime so that a stacktrace
+// can show failure location.
+//
+// Note certain results of `@typeName()` expect `behavior.zig` to be the
+// 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)");
+
+    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)",
+    );
+}
+
+// CAUTION: this test is source-location sensitive.
+test "anon field init" {
+    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");
+}
+
+test "basic" {
+    try expectEqualSlices(u8, @typeName(i64), "i64");
+    try expectEqualSlices(u8, @typeName(*usize), "*usize");
+    try expectEqualSlices(u8, @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");
+
+    // regular fn, without error
+    try expectEqualSlices(u8, @typeName(@TypeOf(regular)), "fn() void");
+    // 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");
+    // generic fn
+    try expectEqualSlices(u8, @typeName(@TypeOf(TypeFromFn)), "fn(type) anytype");
+}
+
+const A_Struct = struct {};
+const A_Union = union {
+    unused: u8,
+};
+const A_Enum = enum {
+    unused,
+};
+
+fn regular() void {}
+
+test "fn body decl" {
+    try B.doTest();
+}
+
+const B = struct {
+    fn doTest() !void {
+        const B_Struct = struct {};
+        const B_Union = union {
+            unused: u8,
+        };
+        const B_Enum = enum {
+            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");
+    }
+};
+
+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)");
+
+    try expectEqualSlices(u8, @typeName(TypeFromFn2(u8, bool)), "behavior.typename.TypeFromFn2(u8,bool)");
+}
+
+fn TypeFromFn(comptime T: type) type {
+    _ = T;
+    return struct {};
+}
+
+fn TypeFromFn2(comptime T1: type, comptime T2: type) type {
+    _ = T1;
+    _ = T2;
+    return struct {};
+}
+
+fn TypeFromFn3(comptime T1: type, comptime T2: type, comptime T3: type) type {
+    _ = T1;
+    _ = T2;
+    _ = T3;
+    return struct {};
 }