Skip to content

Commit e825ca1

Browse files
committed
[ClangImporter] Reject *all* attempts to redeclare properties
Add an algorithm to go search the override tables *and* the existing loaded members of a class for a redeclaration point. The idea is that both mirrored protocol members and categories offer a way to convince the Clang Importer to import a property twice. We support a limited form of this multiple-imports behavior today by allowing the redeclared property to refine a readonly property into a readwrite property. To maintain both the refinement behavior and to disambiguate any remaining cases of ambiguity caused by extensions, attempt to identify a redeclaration point if we can't identify an override point. Then, decline to import the redeclared member if we're successful. Note that the algorithm as presented is subject to import ordering. That is, if a framework declares both an overlay and a clang module unit, if the overlay is not loaded or members from the overlay are not installed in the class by the time we see the declaration we may fail to identify an redeclaration point. The regression tests are for rdar://59044692, rdar://59075988, and rdar://59125907.
1 parent 353f396 commit e825ca1

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)