Commit 2be347a2c8

Ian Johnson <ian@ianjohnson.dev>
2023-04-23 02:44:57
Sema: allow method calls on optional pointers
1 parent c75e11b
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -23738,7 +23738,6 @@ fn fieldCallBind(
                     {
                         const first_param_type = decl_type.fnParamType(0);
                         const first_param_tag = first_param_type.tag();
-                        var opt_buf: Type.Payload.ElemType = undefined;
                         // zig fmt: off
                         if (first_param_tag == .var_args_param or
                             first_param_tag == .generic_poison or (
@@ -23764,17 +23763,29 @@ fn fieldCallBind(
                                 .arg0_inst = deref,
                             });
                             return sema.addConstant(ty, value);
-                        } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .Optional and
-                            first_param_type.optionalChild(&opt_buf).eql(concrete_ty, sema.mod))
-                        {
-                            const deref = try sema.analyzeLoad(block, src, object_ptr, src);
-                            const ty = Type.Tag.bound_fn.init();
-                            const value = try Value.Tag.bound_fn.create(arena, .{
-                                .func_inst = decl_val,
-                                .arg0_inst = deref,
-                            });
-                            return sema.addConstant(ty, value);
-                        } else if (first_param_tag != .generic_poison and first_param_type.zigTypeTag() == .ErrorUnion and
+                        } else if (first_param_type.zigTypeTag() == .Optional) {
+                            var opt_buf: Type.Payload.ElemType = undefined;
+                            const child = first_param_type.optionalChild(&opt_buf);
+                            if (child.eql(concrete_ty, sema.mod)) {
+                                const deref = try sema.analyzeLoad(block, src, object_ptr, src);
+                                const ty = Type.Tag.bound_fn.init();
+                                const value = try Value.Tag.bound_fn.create(arena, .{
+                                    .func_inst = decl_val,
+                                    .arg0_inst = deref,
+                                });
+                                return sema.addConstant(ty, value);
+                            } else if (child.zigTypeTag() == .Pointer and
+                                child.ptrSize() == .One and
+                                child.childType().eql(concrete_ty, sema.mod))
+                            {
+                                const ty = Type.Tag.bound_fn.init();
+                                const value = try Value.Tag.bound_fn.create(arena, .{
+                                    .func_inst = decl_val,
+                                    .arg0_inst = object_ptr,
+                                });
+                                return sema.addConstant(ty, value);
+                            }
+                        } else if (first_param_type.zigTypeTag() == .ErrorUnion and
                             first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod))
                         {
                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
test/behavior/fn.zig
@@ -455,6 +455,23 @@ test "method call with optional and error union first param" {
     try s.errUnion();
 }
 
+test "method call with optional pointer first param" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        x: i32 = 1234,
+
+        fn method(s: ?*@This()) !void {
+            try expect(s.?.x == 1234);
+        }
+    };
+    var s: S = .{};
+    try s.method();
+    const s_ptr = &s;
+    try s_ptr.method();
+}
+
 test "using @ptrCast on function pointers" {
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO