Skip to content

Commit c9b1723

Browse files
authored
Merge pull request #29561 from CodaFi/an-air-of-refinement
[ClangImporter] Redeclarations as Refinements
2 parents aaed720 + e825ca1 commit c9b1723

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2208,6 +2208,50 @@ namespace {
22082208
return {nullptr, foundMethod};
22092209
}
22102210

2211+
// Attempt to identify the redeclaration of a property.
2212+
//
2213+
// Note that this function does not perform any additional member loading and
2214+
// is therefore subject to the relativistic effects of module import order.
2215+
// That is, suppose that a Clang Module and an Overlay module are in play.
2216+
// Depending on which module loads members first, a redeclaration point may
2217+
// or may not be identifiable.
2218+
VarDecl *
2219+
identifyPropertyRedeclarationPoint(ClangImporter::Implementation &Impl,
2220+
const clang::ObjCPropertyDecl *decl,
2221+
ClassDecl *subject, Identifier name) {
2222+
llvm::SetVector<Decl *> lookup;
2223+
// First, pull in all available members of the base class so we can catch
2224+
// redeclarations of APIs that are refined for Swift.
2225+
auto currentMembers = subject->getCurrentMembersWithoutLoading();
2226+
lookup.insert(currentMembers.begin(), currentMembers.end());
2227+
2228+
// Now pull in any just-imported members from the overrides table.
2229+
auto foundNames = Impl.MembersForNominal.find(subject);
2230+
if (foundNames != Impl.MembersForNominal.end()) {
2231+
auto foundDecls = foundNames->second.find(name);
2232+
if (foundDecls != foundNames->second.end()) {
2233+
lookup.insert(foundDecls->second.begin(), foundDecls->second.end());
2234+
}
2235+
}
2236+
2237+
for (auto *result : lookup) {
2238+
auto *var = dyn_cast<VarDecl>(result);
2239+
if (!var)
2240+
continue;
2241+
2242+
if (var->isInstanceMember() != decl->isInstanceProperty())
2243+
continue;
2244+
2245+
// If the selectors of the getter match in Objective-C, we have a
2246+
// redeclaration.
2247+
if (var->getObjCGetterSelector() ==
2248+
Impl.importSelector(decl->getGetterName())) {
2249+
return var;
2250+
}
2251+
}
2252+
return nullptr;
2253+
}
2254+
22112255
/// Convert Clang declarations into the corresponding Swift
22122256
/// declarations.
22132257
class SwiftDeclConverter
@@ -5094,12 +5138,21 @@ namespace {
50945138
overrideContext->getSelfNominalTypeDecl()
50955139
== dc->getSelfNominalTypeDecl()) {
50965140
// We've encountered a redeclaration of the property.
5097-
// HACK: Just update the original declaration instead of importing a
5098-
// second property.
50995141
handlePropertyRedeclaration(overridden, decl);
51005142
return nullptr;
51015143
}
51025144
}
5145+
5146+
// Try searching the class for a property redeclaration. We can use
5147+
// the redeclaration to refine the already-imported property with a
5148+
// setter and also cut off any double-importing behavior.
5149+
auto *redecl
5150+
= identifyPropertyRedeclarationPoint(Impl, decl,
5151+
dc->getSelfClassDecl(), name);
5152+
if (redecl) {
5153+
handlePropertyRedeclaration(redecl, decl);
5154+
return nullptr;
5155+
}
51035156
}
51045157

51055158
auto importedType = Impl.importPropertyType(decl, isInSystemModule(dc));

test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/Headers/CategoryOverrides.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,19 @@ typedef enum {
3232
@interface Refinery : Base
3333
@property (nonatomic, readonly) RefinedSugar sugar /*NS_REFINED_FOR_SWIFT*/ __attribute__((swift_private));
3434
@end
35+
36+
@protocol NullableProtocol
37+
@property (nonatomic, readonly, nullable) Base *requirement;
38+
@end
39+
40+
@protocol NonNullProtocol <NullableProtocol>
41+
@property (nonatomic, readonly, nonnull) Base *requirement;
42+
@end
43+
44+
@protocol ReadonlyProtocol
45+
@property (nonatomic, readonly) int answer;
46+
@end
47+
48+
@protocol ReadwriteProtocol <ReadonlyProtocol>
49+
@property (nonatomic, readwrite) int answer;
50+
@end

test/ClangImporter/Inputs/frameworks/CategoryOverrides.framework/PrivateHeaders/Private.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@
1818
@interface Refinery ()
1919
@property (nonatomic, readwrite) RefinedSugar sugar;
2020
@end
21+
22+
@interface MyBaseClass () <NonNullProtocol>
23+
@end
24+
25+
@interface MyDerivedClass () <ReadwriteProtocol>
26+
@end

test/ClangImporter/objc_redeclared_properties_categories.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import CategoryOverrides
1111

1212
// Nail down some emergent behaviors of the Clang Importer's override checking:
1313

14-
1514
// A category declared in a (private) header can happen to double-import a property
1615
// and a function with the same name - both before and after omit-needless-words -
1716
// as long as they have different contextual types.
@@ -78,3 +77,20 @@ func takesARefinery(_ x: Refinery) {
7877
// CHECK: cannot assign to property: 'sugar' is a get-only property
7978
x.sugar = .caster
8079
}
80+
81+
func nullabilityRefinementProto(_ x: MyBaseClass) {
82+
// CHECK-PUBLIC: has no member 'requirement'
83+
// CHECK-PRIVATE-NOT: has no member 'requirement'
84+
// CHECK-PRIVATE-NOT: value of optional type 'Base?'
85+
let _ : Base = x.requirement
86+
}
87+
88+
func readwriteRefinementProto(_ x: MyDerivedClass) {
89+
// CHECK-PUBLIC: has no member 'answer'
90+
// CHECK-PRIVATE-NOT: has no member 'answer'
91+
if x.answer == 0 {
92+
// CHECK-PUBLIC: has no member 'answer'
93+
// CHECK-PRIVATE-NOT: has no member 'answer'
94+
x.answer = 42
95+
}
96+
}

0 commit comments

Comments
 (0)