Skip to content

Commit e79281d

Browse files
committed
Nail Down The Emergent Behaviors of the ClangImporter's Override Checking
The Clang Importer allows for the creation of otherwise illegal Swift ASTs because it does not run redeclaration checking or override checking. These behaviors are also not a part of our test suite. Correct the first issue by re-instating the old emergent behavior of member loading occurring at all points in the class hierarchy before a (lazy) lookup. Correct the second issue by adding a regression test for a common failure mode in the post-re-entrant-lookup Clang Importer. We cannot stop accepting these cases, but a future compiler will warn about them. Attacks rdar://58493370
1 parent a0b0e36 commit e79281d

File tree

6 files changed

+83
-1
lines changed

6 files changed

+83
-1
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3726,6 +3726,9 @@ void ClangImporter::Implementation::lookupAllObjCMembers(
37263726
// deserialized before loading the named member of this class. This allows the
37273727
// decl members table to be warmed up and enables the correct identification of
37283728
// overrides.
3729+
//
3730+
// FIXME: Very low hanging fruit: Loading everything is extremely wasteful. We
3731+
// should be able to just load the name lazy member loading is asking for.
37293732
static void ensureSuperclassMembersAreLoaded(const ClassDecl *CD) {
37303733
if (!CD)
37313734
return;
@@ -3735,6 +3738,9 @@ static void ensureSuperclassMembersAreLoaded(const ClassDecl *CD) {
37353738
return;
37363739

37373740
CD->loadAllMembers();
3741+
3742+
for (auto *ED : const_cast<ClassDecl *>(CD)->getExtensions())
3743+
ED->loadAllMembers();
37383744
}
37393745

37403746
Optional<TinyPtrVector<ValueDecl *>>

lib/ClangImporter/ImportDecl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8443,7 +8443,7 @@ createUnavailableDecl(Identifier name, DeclContext *dc, Type type,
84438443
// deserialized before loading the members of this class. This allows the
84448444
// decl members table to be warmed up and enables the correct identification of
84458445
// overrides.
8446-
static void loadAllMembersOfSuperclassIfNeeded(const ClassDecl *CD) {
8446+
static void loadAllMembersOfSuperclassIfNeeded(ClassDecl *CD) {
84478447
if (!CD)
84488448
return;
84498449

@@ -8452,6 +8452,9 @@ static void loadAllMembersOfSuperclassIfNeeded(const ClassDecl *CD) {
84528452
return;
84538453

84548454
CD->loadAllMembers();
8455+
8456+
for (auto E : CD->getExtensions())
8457+
E->loadAllMembers();
84558458
}
84568459

84578460
void
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
__attribute__((objc_root_class))
2+
@interface Base
3+
- (nonnull instancetype)init;
4+
@end
5+
6+
@interface MyColor : Base
7+
@property (class, nonatomic, readonly) MyColor *systemRedColor;
8+
@end
9+
10+
@interface MyBaseClass : Base
11+
// @property (nonatomic, strong, nullable) Base *derivedMember;
12+
@end
13+
14+
@interface MyDerivedClass : MyBaseClass
15+
@property (nonatomic, strong, nullable) Base *derivedMember;
16+
@end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
framework module CategoryOverrides {
2+
umbrella header "CategoryOverrides.h"
3+
module * {
4+
export *
5+
}
6+
exclude header "Private.h"
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#import <CategoryOverrides/CategoryOverrides.h>
2+
3+
@interface MyBaseClass ()
4+
@property (nonatomic, strong, nullable) Base *derivedMember;
5+
@end
6+
7+
@interface MyColor ()
8+
+ (MyColor * _Null_unspecified) systemRedColor;
9+
@end
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -F %S/Inputs/frameworks %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PUBLIC %s
2+
3+
// RUN: echo '#import <CategoryOverrides/Private.h>' > %t.h
4+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h %s 2>&1 | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s --allow-empty
5+
6+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -emit-pch -F %S/Inputs/frameworks -o %t.pch %t.h
7+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s
8+
// RUNT: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -enable-objc-interop -import-objc-header %t.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck --allow-empty -check-prefix=CHECK -check-prefix=CHECK-PRIVATE %s
9+
10+
import CategoryOverrides
11+
12+
// Nail down some emergent behaviors of the Clang Importer's override checking:
13+
14+
15+
// A category declared in a (private) header can happen to double-import a property
16+
// and a function with the same name - both before and after omit-needless-words -
17+
// as long as they have different contextual types.
18+
//
19+
// This configuration appears as an undiagnosed redeclaration of a property and
20+
// function, which is illegal.
21+
func colors() {
22+
let _ : MyColor = MyColor.systemRed
23+
let _ : MyColor = MyColor.systemRed()!
24+
}
25+
26+
// A category declared in a (private) header can introduce overrides of a property
27+
// that is otherwise not declared in a base class.
28+
//
29+
// This configuration appears as an undiagnosed override in a Swift extension,
30+
// which is illegal.
31+
func takesADerivedClass(_ x: MyDerivedClass) {
32+
// CHECK-PUBLIC-NOT: has no member 'derivedMember'
33+
// CHECK-PRIVATE-NOT: has no member 'derivedMember'
34+
x.derivedMember = Base()
35+
}
36+
37+
func takesABaseClass(_ x: MyBaseClass) {
38+
// CHECK-PUBLIC: has no member 'derivedMember'
39+
// CHECK-PRIVATE-NOT: has no member 'derivedMember'
40+
x.derivedMember = Base()
41+
}

0 commit comments

Comments
 (0)