Commit 7b00bef6bf

Andrew Kelley <andrew@ziglang.org>
2021-10-18 06:53:59
Sema: resolveTypeFields before accessing type fields
1 parent 0ef2e25
Changed files (4)
src/Sema.zig
@@ -6413,21 +6413,33 @@ fn validateSwitchNoRange(
 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
-    const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
-    const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
-    const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
-    const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs);
-
-    const has_field = switch (container_type.zigTypeTag()) {
-        .Struct => container_type.structFields().contains(field_name),
-        .Union => container_type.unionFields().contains(field_name),
-        .Enum => container_type.enumFields().contains(field_name),
-        else => return sema.fail(block, lhs_src, "expected struct, enum, or union, found '{}'", .{container_type}),
+    const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs);
+    const field_name = try sema.resolveConstString(block, name_src, extra.rhs);
+    const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty);
+
+    const has_field = hf: {
+        if (ty.isSlice()) {
+            if (mem.eql(u8, field_name, "ptr")) break :hf true;
+            if (mem.eql(u8, field_name, "len")) break :hf true;
+            break :hf false;
+        }
+        break :hf switch (ty.zigTypeTag()) {
+            .Struct => ty.structFields().contains(field_name),
+            .Union => ty.unionFields().contains(field_name),
+            .Enum => ty.enumFields().contains(field_name),
+            .Array => mem.eql(u8, field_name, "len"),
+            else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{
+                ty,
+            }),
+        };
     };
     if (has_field) {
         return Air.Inst.Ref.bool_true;
+    } else {
+        return Air.Inst.Ref.bool_false;
     }
-    return Air.Inst.Ref.bool_false;
 }
 
 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
test/behavior/hasfield.zig
@@ -2,6 +2,28 @@ const expect = @import("std").testing.expect;
 const builtin = @import("builtin");
 
 test "@hasField" {
+    const struc = struct {
+        a: i32,
+        b: []u8,
+
+        pub const nope = 1;
+    };
+    try expect(@hasField(struc, "a") == true);
+    try expect(@hasField(struc, "b") == true);
+    try expect(@hasField(struc, "non-existant") == false);
+    try expect(@hasField(struc, "nope") == false);
+
+    const unin = union {
+        a: u64,
+        b: []u16,
+
+        pub const nope = 1;
+    };
+    try expect(@hasField(unin, "a") == true);
+    try expect(@hasField(unin, "b") == true);
+    try expect(@hasField(unin, "non-existant") == false);
+    try expect(@hasField(unin, "nope") == false);
+
     const enm = enum {
         a,
         b,
test/behavior/hasfield_stage1.zig
@@ -1,26 +0,0 @@
-const expect = @import("std").testing.expect;
-const builtin = @import("builtin");
-
-test "@hasField" {
-    const struc = struct {
-        a: i32,
-        b: []const u8,
-
-        pub const nope = 1;
-    };
-    try expect(@hasField(struc, "a") == true);
-    try expect(@hasField(struc, "b") == true);
-    try expect(@hasField(struc, "non-existant") == false);
-    try expect(@hasField(struc, "nope") == false);
-
-    const unin = union {
-        a: u64,
-        b: []const u16,
-
-        pub const nope = 1;
-    };
-    try expect(@hasField(unin, "a") == true);
-    try expect(@hasField(unin, "b") == true);
-    try expect(@hasField(unin, "non-existant") == false);
-    try expect(@hasField(unin, "nope") == false);
-}
test/behavior.zig
@@ -128,7 +128,6 @@ test {
         _ = @import("behavior/fn_in_struct_in_comptime.zig");
         _ = @import("behavior/for_stage1.zig");
         _ = @import("behavior/generics_stage1.zig");
-        _ = @import("behavior/hasfield_stage1.zig");
         _ = @import("behavior/if_stage1.zig");
         _ = @import("behavior/import.zig");
         _ = @import("behavior/incomplete_struct_param_tld.zig");