From b5119e85879da3d74a11163afc3aa5e685f66547 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 3 Jun 2026 07:45:10 +0300 Subject: [PATCH] test(ir): cover Obj-C protocol pointers in isObjcClassPointer/objcPropertyKind (A6.1 scaffolding review fix) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex review of 0012228 noted isObjcClassPointer's contract is `fcd.runtime == .objc_class or fcd.runtime == .objc_protocol`, but the new tests only exercised the class case. Test-only fix (no visibility/behavior change — still exactly the two pub widenings from the parent commit): - isObjcClassPointer: add a *NSCopying case where NSCopying is a registered .objc_protocol foreign class -> true (alongside the .objc_class *NSString case). - objcPropertyKind: add a *NSCoding protocol-pointer field -> strong default assertion, since it uses the same class/protocol object-pointer predicate. Gate: zig build, zig build test, bash tests/run_examples.sh -> 361/0. --- src/ir/lower.test.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/ir/lower.test.zig b/src/ir/lower.test.zig index 6afaa59..ecad5d1 100644 --- a/src/ir/lower.test.zig +++ b/src/ir/lower.test.zig @@ -680,6 +680,22 @@ test "lower: isObjcClassPointer recognises pointer-to-foreign-Obj-C-class" { try lowering.program_index.foreign_class_map.put("NSString", &ns_fcd); try std.testing.expect(lowering.isObjcClassPointer(ns_ptr)); + // *NSCopying where NSCopying is a registered Obj-C *protocol* → also true + // (the predicate accepts .objc_class OR .objc_protocol). + const proto_name = module.types.internString("NSCopying"); + const proto_struct = module.types.intern(.{ .@"struct" = .{ .name = proto_name, .fields = &.{} } }); + const proto_ptr = module.types.ptrTo(proto_struct); + var proto_fcd = ast.ForeignClassDecl{ + .name = "NSCopying", + .foreign_path = "NSCopying", + .runtime = .objc_protocol, + .members = &.{}, + .is_foreign = true, + .is_main = false, + }; + try lowering.program_index.foreign_class_map.put("NSCopying", &proto_fcd); + try std.testing.expect(lowering.isObjcClassPointer(proto_ptr)); + // *Plain where Plain is a non-foreign struct → false. const plain_name = module.types.internString("Plain"); const plain_struct = module.types.intern(.{ .@"struct" = .{ .name = plain_name, .fields = &.{} } }); @@ -720,6 +736,24 @@ test "lower: objcPropertyKind defaults + explicit ARC modifiers" { const obj_default = ast.ForeignFieldDecl{ .name = "title", .field_type = obj_ty, .is_property = true }; try std.testing.expect(lowering.objcPropertyKind(obj_default) == .strong); + // Protocol-pointer field → also strong by default (same object-pointer + // predicate accepts .objc_protocol). + const proto_name = module.types.internString("NSCoding"); + _ = module.types.intern(.{ .@"struct" = .{ .name = proto_name, .fields = &.{} } }); + var proto_fcd = ast.ForeignClassDecl{ + .name = "NSCoding", + .foreign_path = "NSCoding", + .runtime = .objc_protocol, + .members = &.{}, + .is_foreign = true, + .is_main = false, + }; + try lowering.program_index.foreign_class_map.put("NSCoding", &proto_fcd); + const proto_ty = typeKeyword(alloc, "*NSCoding"); + defer alloc.destroy(proto_ty); + const proto_default = ast.ForeignFieldDecl{ .name = "coder", .field_type = proto_ty, .is_property = true }; + try std.testing.expect(lowering.objcPropertyKind(proto_default) == .strong); + // Explicit modifiers on an object pointer win over the default. const weak_mods = [_][]const u8{"weak"}; try std.testing.expect(lowering.objcPropertyKind(.{ .name = "delegate", .field_type = obj_ty, .is_property = true, .property_modifiers = &weak_mods }) == .weak);