Skip to content

Commit 4f04a80

Browse files
committed
Redo ObjC Override Checking
The general problem with this approach is that the clang importer is part of the cache-fill for name lookup. This means that qualified lookups it runs will be re-entrant and result in validation-order-dependent behaviors. The next commit will restore the expected ordering behavior here.
1 parent 16b9611 commit 4f04a80

File tree

3 files changed

+66
-33
lines changed

3 files changed

+66
-33
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,6 +2174,13 @@ namespace {
21742174
return getVersion() == getActiveSwiftVersion();
21752175
}
21762176

2177+
template <typename T>
2178+
T *recordMemberInContext(DeclContext *dc, T *member) {
2179+
assert(member && "Attempted to record null member!");
2180+
Impl.MembersForNominal[dc->getSelfNominalTypeDecl()].push_back(member);
2181+
return member;
2182+
}
2183+
21772184
/// Import the name of the given entity.
21782185
///
21792186
/// This version of importFullName introduces any context-specific
@@ -3734,7 +3741,7 @@ namespace {
37343741
if (correctSwiftName)
37353742
markAsVariant(result, *correctSwiftName);
37363743

3737-
return result;
3744+
return recordMemberInContext(dc, result);
37383745
}
37393746

37403747
void finishFuncDecl(const clang::FunctionDecl *decl,
@@ -4344,7 +4351,7 @@ namespace {
43444351
}
43454352
}
43464353

4347-
return result;
4354+
return recordMemberInContext(dc, result);
43484355
}
43494356

43504357
public:
@@ -5002,44 +5009,57 @@ namespace {
50025009
return nullptr;
50035010

50045011
VarDecl *overridden = nullptr;
5005-
if (dc->getSelfClassDecl()) {
5006-
// Check whether there is a function with the same name as this
5007-
// property. If so, suppress the property; the user will have to use
5008-
// the methods directly, to avoid ambiguities.
5009-
NominalTypeDecl *lookupContext = dc->getSelfNominalTypeDecl();
5012+
// Check whether there is a function with the same name as this
5013+
// property. If so, suppress the property; the user will have to use
5014+
// the methods directly, to avoid ambiguities.
5015+
if (auto *subject = dc->getSelfClassDecl()) {
5016+
bool foundMethod = false;
50105017

50115018
if (auto *classDecl = dyn_cast<ClassDecl>(dc)) {
50125019
// If we're importing into the primary @interface for something, as
50135020
// opposed to an extension, make sure we don't try to load any
50145021
// categories...by just looking into the super type.
5015-
lookupContext = classDecl->getSuperclassDecl();
5022+
subject = classDecl->getSuperclassDecl();
50165023
}
5024+
5025+
for (; subject; (subject = subject->getSuperclassDecl())) {
5026+
llvm::SmallVector<ValueDecl *, 8> lookup;
5027+
auto found = Impl.MembersForNominal.find(subject);
5028+
if (found != Impl.MembersForNominal.end()) {
5029+
lookup.append(found->second.begin(), found->second.end());
5030+
namelookup::pruneLookupResultSet(dc, NL_QualifiedDefault, lookup);
5031+
}
5032+
5033+
for (auto &result : lookup) {
5034+
// Skip declarations that don't match the name we're looking for.
5035+
if (result->getBaseName() != name)
5036+
continue;
5037+
5038+
if (auto *fd = dyn_cast<FuncDecl>(result)) {
5039+
if (fd->isInstanceMember() != decl->isInstanceProperty())
5040+
continue;
5041+
5042+
if (fd->getFullName().getArgumentNames().empty()) {
5043+
foundMethod = true;
5044+
}
5045+
} else {
5046+
auto *var = cast<VarDecl>(result);
5047+
if (var->isInstanceMember() != decl->isInstanceProperty())
5048+
continue;
50175049

5018-
if (lookupContext) {
5019-
SmallVector<ValueDecl *, 2> lookup;
5020-
dc->lookupQualified(lookupContext, DeclNameRef(name),
5021-
NL_QualifiedDefault | NL_KnownNoDependency,
5022-
lookup);
5023-
bool foundMethod = false;
5024-
for (auto result : lookup) {
5025-
if (isa<FuncDecl>(result) &&
5026-
result->isInstanceMember() == decl->isInstanceProperty() &&
5027-
result->getFullName().getArgumentNames().empty())
5028-
foundMethod = true;
5029-
5030-
if (auto var = dyn_cast<VarDecl>(result)) {
50315050
// If the selectors of the getter match in Objective-C, we have an
50325051
// override.
5033-
if (var->isInstanceMember() == decl->isInstanceProperty() &&
5034-
var->getObjCGetterSelector() ==
5035-
Impl.importSelector(decl->getGetterName()))
5052+
if (var->getObjCGetterSelector() ==
5053+
Impl.importSelector(decl->getGetterName())) {
50365054
overridden = var;
5055+
}
50375056
}
50385057
}
5039-
if (foundMethod && !overridden)
5040-
return nullptr;
50415058
}
50425059

5060+
if (foundMethod && !overridden)
5061+
return nullptr;
5062+
50435063
if (overridden) {
50445064
const DeclContext *overrideContext = overridden->getDeclContext();
50455065
// It's okay to compare interface types directly because Objective-C
@@ -5130,7 +5150,7 @@ namespace {
51305150
if (correctSwiftName)
51315151
markAsVariant(result, *correctSwiftName);
51325152

5133-
return result;
5153+
return recordMemberInContext(dc, result);
51345154
}
51355155

51365156
Decl *

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
447447
// Mapping from imported types to their raw value types.
448448
llvm::DenseMap<const NominalTypeDecl *, Type> RawTypes;
449449

450+
/// Keep track of all member declarations that have been imported into a nominal type.
451+
llvm::DenseMap<const NominalTypeDecl *, std::vector<ValueDecl *>>
452+
MembersForNominal;
453+
450454
clang::CompilerInstance *getClangInstance() {
451455
return Instance.get();
452456
}

test/ClangImporter/objc_redeclared_properties_incompatible.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,31 +71,40 @@ func testGenericWithoutInitializer(obj: PropertiesNoInitGeneric<Base>) {
7171
// CHECK-PUBLIC: [[@LINE-1]]:7: error: cannot assign to property: 'readwriteChange' is a get-only property
7272
}
7373

74+
// N.B. We consider the category in the imported header Private.h as the
75+
// canonical declaration of these properties and drop the one in the framework.
76+
7477
func testCategoryWithInitializer() {
7578
let obj = PropertiesInitCategory()
7679

7780
let _: Int = obj.nullabilityChange
78-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
81+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
82+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'Base?' to specified type 'Int'
7983

8084
let _: Int = obj.missingGenerics
81-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass<Base>' to specified type 'Int'
85+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass<Base>' to specified type 'Int'
86+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'GenericClass<AnyObject>' to specified type 'Int'
8287

8388
let _: Int = obj.typeChange
84-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
89+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
90+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'PrivateSubclass' to specified type 'Int'
8591

8692
obj.readwriteChange = Base() // CHECK-PRIVATE-NOT: [[@LINE]]:{{.+}}: error
8793
// CHECK-PUBLIC: [[@LINE-1]]:7: error: cannot assign to property: 'readwriteChange' is a get-only property
8894
}
8995

9096
func testCategoryWithoutInitializer(obj: PropertiesNoInitCategory) {
9197
let _: Int = obj.nullabilityChange
92-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
98+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
99+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'Base?' to specified type 'Int'
93100

94101
let _: Int = obj.missingGenerics
95-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass<Base>' to specified type 'Int'
102+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'GenericClass<Base>' to specified type 'Int'
103+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'GenericClass<AnyObject>' to specified type 'Int'
96104

97105
let _: Int = obj.typeChange
98-
// CHECK: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
106+
// CHECK-PUBLIC: [[@LINE-1]]:20: error: cannot convert value of type 'Base' to specified type 'Int'
107+
// CHECK-PRIVATE: [[@LINE-2]]:20: error: cannot convert value of type 'PrivateSubclass' to specified type 'Int'
99108

100109
obj.readwriteChange = Base() // CHECK-PRIVATE-NOT: [[@LINE]]:{{.+}}: error
101110
// CHECK-PUBLIC: [[@LINE-1]]:7: error: cannot assign to property: 'readwriteChange' is a get-only property

0 commit comments

Comments
 (0)