Skip to content

Commit e1f46b5

Browse files
authored
[ClangImporter] Handle subscript getters redeclared in subclasses (#13470)
Objective-C subscripts don't have special declarations like properties; they're just specially-named methods (or method pairs), and we have to make an independent SubscriptDecl in Swift. This means that when a subclass wants to make a subscript settable, they just add the appropriately-named setter method. Swift handled this by detecting when the getter and setter weren't declared in the same type, and assuming this meant it was a subclass adding a setter. Unfortunately, the same condition /also/ picked up the case where the getter (and only the getter) is /redeclared/ in a subclass (perhaps to add an attribute), and the new subscript was getting added to the base class instead of the subclass. The fix relies on the fact that the original decl we provide is what we use to look up the other accessor. If the getter and setter are in different types, whichever one we started with must be the more-derived one. So the final change is just "did we start with the setter?" rather than "is there a setter at all?". I'm not sure why this is only just now causing problems, given that we seem to have been getting this wrong for years, but it definitely /was/ wrong and now it's not. rdar://problem/36033356
1 parent 40e9499 commit e1f46b5

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6345,8 +6345,6 @@ SwiftDeclConverter::importSubscript(Decl *decl,
63456345
->getAsNominalTypeOrNominalTypeExtensionContext() ==
63466346
setter->getDeclContext()
63476347
->getAsNominalTypeOrNominalTypeExtensionContext());
6348-
// TODO: Possible that getter and setter are different instantiations
6349-
// of the same objc generic type?
63506348

63516349
// Whether we can update the types involved in the subscript
63526350
// operation.
@@ -6401,8 +6399,16 @@ SwiftDeclConverter::importSubscript(Decl *decl,
64016399
}
64026400
}
64036401

6404-
// The context into which the subscript should go.
6405-
bool associateWithSetter = setter && !getterAndSetterInSameType;
6402+
// The context into which the subscript should go. We prefer wherever the
6403+
// getter is declared unless the two accessors are in different types and the
6404+
// one we started with is the setter. This happens when:
6405+
// - A read-only subscript is made read/write is a subclass.
6406+
// - A setter is redeclared in a subclass, but not the getter.
6407+
// And not when:
6408+
// - A getter is redeclared in a subclass, but not the setter.
6409+
// - The getter and setter are part of the same type.
6410+
// - There is no setter.
6411+
bool associateWithSetter = !getterAndSetterInSameType && setter == decl;
64066412
DeclContext *dc =
64076413
associateWithSetter ? setter->getDeclContext() : getter->getDeclContext();
64086414

test/ClangImporter/Inputs/custom-modules/ObjCSubscripts.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,34 @@
2929
- (NSString *)objectForKeyedSubscript:(NSString *)subscript;
3030
- (void)setObject:(NSString *)object forKeyedSubscript:(NSString *)key;
3131
@end
32+
33+
34+
// rdar://problem/36033356 failed specifically when the base class was never
35+
// subscripted, so please don't mention this class in the .swift file.
36+
@interface KeySubscriptBase
37+
- (id)objectForKeyedSubscript:(NSString *)subscript;
38+
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
39+
@end
40+
41+
@interface KeySubscriptOverrideGetter : KeySubscriptBase
42+
- (id)objectForKeyedSubscript:(NSString *)subscript;
43+
@end
44+
45+
@interface KeySubscriptOverrideSetter : KeySubscriptBase
46+
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
47+
@end
48+
49+
// rdar://problem/36033356 failed specifically when the base class was never
50+
// subscripted, so please don't mention this class in the .swift file.
51+
@interface KeySubscriptReversedBase
52+
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
53+
- (id)objectForKeyedSubscript:(NSString *)subscript;
54+
@end
55+
56+
@interface KeySubscriptReversedOverrideGetter : KeySubscriptReversedBase
57+
- (id)objectForKeyedSubscript:(NSString *)subscript;
58+
@end
59+
60+
@interface KeySubscriptReversedOverrideSetter : KeySubscriptReversedBase
61+
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key;
62+
@end

test/ClangImporter/objc_subscript.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,24 @@ class ConformsToKeySubscriptProto2 : KeySubscriptProto2 {
4848
set { }
4949
}
5050
}
51+
52+
func testOverridesWithoutBase(
53+
o1: KeySubscriptOverrideGetter,
54+
o2: KeySubscriptOverrideSetter,
55+
o3: KeySubscriptReversedOverrideGetter,
56+
o4: KeySubscriptReversedOverrideSetter
57+
) {
58+
// rdar://problem/36033356 failed specifically when the base class was never
59+
// subscripted, so please don't mention the base classes here.
60+
_ = o1["abc"]
61+
o1["abc"] = "xyz"
62+
63+
_ = o2["abc"]
64+
o2["abc"] = "xyz"
65+
66+
_ = o3["abc"]
67+
o3["abc"] = "xyz"
68+
69+
_ = o4["abc"]
70+
o4["abc"] = "xyz"
71+
}

0 commit comments

Comments
 (0)