Skip to content

Commit c1b4c94

Browse files
committed
Don't inherit convenience inits if a designated init is missing.
(which can happen if an imported class has un-importable initializers) Our initializer model guarantees that it's safe to inherit convenience initializers when a subclass has implemented all designated initializers, since each convenience initializer will be implemented by calling one of the designated initializers. If one of the designated initializers /can't/ be implemented in Swift, however, then inheriting the convenience initializer would not be safe. This is potentially a source-breaking change, so the importer will only actually record that it failed to import something in when compiling in Swift 4 mode. rdar://problem/31563662
1 parent 71987b9 commit c1b4c94

File tree

6 files changed

+155
-4
lines changed

6 files changed

+155
-4
lines changed

include/swift/AST/Decl.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,9 +3186,6 @@ class ClassDecl : public NominalTypeDecl {
31863186
SourceLoc ClassLoc;
31873187
ObjCMethodLookupTable *ObjCMethodLookup = nullptr;
31883188

3189-
/// Whether the class has @objc ancestry.
3190-
unsigned ObjCKind : 3;
3191-
31923189
/// Create the Objective-C member lookup table.
31933190
void createObjCMethodLookup();
31943191

@@ -3198,6 +3195,11 @@ class ClassDecl : public NominalTypeDecl {
31983195
llvm::PointerIntPair<Type, 1, bool> Superclass;
31993196
} LazySemanticInfo;
32003197

3198+
/// Whether the class has @objc ancestry.
3199+
unsigned ObjCKind : 3;
3200+
3201+
unsigned HasMissingDesignatedInitializers : 1;
3202+
32013203
friend class IterativeTypeChecker;
32023204

32033205
public:
@@ -3282,6 +3284,17 @@ class ClassDecl : public NominalTypeDecl {
32823284
return getForeignClassKind() != ForeignKind::Normal;
32833285
}
32843286

3287+
/// Returns true if the class has designated initializers that are not listed
3288+
/// in its members.
3289+
///
3290+
/// This can occur, for example, if the class is an Objective-C class with
3291+
/// initializers that cannot be represented in Swift.
3292+
bool hasMissingDesignatedInitializers() const;
3293+
3294+
void setHasMissingDesignatedInitializers(bool newValue = true) {
3295+
HasMissingDesignatedInitializers = newValue;
3296+
}
3297+
32853298
/// Find a method of a class that overrides a given method.
32863299
/// Return nullptr, if no such method exists.
32873300
AbstractFunctionDecl *findOverridingDecl(

lib/AST/Decl.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,6 +2443,7 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
24432443
ClassDeclBits.RawForeignKind = 0;
24442444
ClassDeclBits.HasDestructorDecl = 0;
24452445
ObjCKind = 0;
2446+
HasMissingDesignatedInitializers = 0;
24462447
}
24472448

24482449
DestructorDecl *ClassDecl::getDestructor() {
@@ -2453,6 +2454,13 @@ DestructorDecl *ClassDecl::getDestructor() {
24532454
return cast<DestructorDecl>(results.front());
24542455
}
24552456

2457+
bool ClassDecl::hasMissingDesignatedInitializers() const {
2458+
auto *mutableThis = const_cast<ClassDecl *>(this);
2459+
(void)mutableThis->lookupDirect(getASTContext().Id_init,
2460+
/*ignoreNewExtensions*/true);
2461+
return HasMissingDesignatedInitializers;
2462+
}
2463+
24562464
bool ClassDecl::inheritsSuperclassInitializers(LazyResolver *resolver) {
24572465
// Get a resolver from the ASTContext if we don't have one already.
24582466
if (resolver == nullptr)
@@ -2481,6 +2489,14 @@ bool ClassDecl::inheritsSuperclassInitializers(LazyResolver *resolver) {
24812489
return false;
24822490
}
24832491

2492+
// If the superclass has known-missing designated initializers, inheriting
2493+
// is unsafe.
2494+
if (superclassDecl->hasMissingDesignatedInitializers()) {
2495+
ClassDeclBits.InheritsSuperclassInits
2496+
= static_cast<unsigned>(StoredInheritsSuperclassInits::NotInherited);
2497+
return false;
2498+
}
2499+
24842500
// Look at all of the initializers of the subclass to gather the initializers
24852501
// they override from the superclass.
24862502
auto &ctx = getASTContext();

lib/ClangImporter/ImportDecl.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6908,7 +6908,8 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
69086908
}
69096909
if (!Result && version == CurrentVersion) {
69106910
// If we couldn't import this Objective-C entity, determine
6911-
// whether it was a required member of a protocol.
6911+
// whether it was a required member of a protocol, or a designated
6912+
// initializer of a class.
69126913
bool hasMissingRequiredMember = false;
69136914
if (auto clangProto
69146915
= dyn_cast<clang::ObjCProtocolDecl>(ClangDecl->getDeclContext())) {
@@ -6930,6 +6931,21 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
69306931
}
69316932
}
69326933
}
6934+
if (auto method = dyn_cast<clang::ObjCMethodDecl>(ClangDecl)) {
6935+
if (!SwiftContext.LangOpts.isSwiftVersion3() &&
6936+
method->isDesignatedInitializerForTheInterface()) {
6937+
const clang::ObjCInterfaceDecl *theClass = method->getClassInterface();
6938+
assert(theClass && "cannot be a protocol method here");
6939+
// Only allow this to affect declarations in the same top-level module
6940+
// as the original class.
6941+
if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) {
6942+
if (auto swiftClass = cast_or_null<ClassDecl>(
6943+
importDecl(theClass, CurrentVersion))) {
6944+
swiftClass->setHasMissingDesignatedInitializers();
6945+
}
6946+
}
6947+
}
6948+
}
69336949

69346950
return nullptr;
69356951
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
__attribute__((objc_root_class))
2+
@interface Base
3+
- (instancetype)init;
4+
@end
5+
6+
@interface IncompleteDesignatedInitializers : Base
7+
- (instancetype)initFirst:(long)x __attribute__((objc_designated_initializer));
8+
- (instancetype)initSecond:(long)x __attribute__((objc_designated_initializer));
9+
- (instancetype)initMissing:(long)x, ... __attribute__((objc_designated_initializer));
10+
- (instancetype)initConveniently:(long)x;
11+
@end
12+
@interface IncompleteDesignatedInitializers (CategoryConvenience)
13+
- (instancetype)initCategory:(long)x;
14+
@end
15+
16+
@interface IncompleteConvenienceInitializers : Base
17+
- (instancetype)initFirst:(long)x __attribute__((objc_designated_initializer));
18+
- (instancetype)initSecond:(long)x __attribute__((objc_designated_initializer));
19+
- (instancetype)initMissing:(long)x, ...;
20+
- (instancetype)initConveniently:(long)x;
21+
@end
22+
@interface IncompleteConvenienceInitializers (CategoryConvenience)
23+
- (instancetype)initCategory:(long)x;
24+
@end
25+
26+
@interface IncompleteUnknownInitializers : Base
27+
- (instancetype)initFirst:(long)x;
28+
- (instancetype)initSecond:(long)x;
29+
- (instancetype)initMissing:(long)x, ...;
30+
- (instancetype)initConveniently:(long)x;
31+
@end
32+
@interface IncompleteUnknownInitializers (CategoryConvenience)
33+
- (instancetype)initCategory:(long)x;
34+
@end

test/ClangImporter/Inputs/custom-modules/module.map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ module TypeAndValue {
138138
export *
139139
}
140140

141+
module UnimportableMembers {
142+
header "UnimportableMembers.h"
143+
}
144+
141145
module UsesSubmodule {
142146
header "UsesSubmodule.h"
143147
export *
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -swift-version 4 -verify
2+
3+
// REQUIRES: objc_interop
4+
5+
import UnimportableMembers
6+
7+
class IncompleteInitSubclassImplicit : IncompleteDesignatedInitializers {
8+
var myOneNewMember = 1
9+
}
10+
11+
class IncompleteInitSubclass : IncompleteDesignatedInitializers {
12+
override init(first: Int) {}
13+
override init(second: Int) {}
14+
}
15+
16+
class IncompleteConvenienceInitSubclass : IncompleteConvenienceInitializers {}
17+
18+
class IncompleteUnknownInitSubclass : IncompleteUnknownInitializers {}
19+
20+
func testBaseClassesBehaveAsExpected() {
21+
_ = IncompleteDesignatedInitializers(first: 0) // okay
22+
_ = IncompleteDesignatedInitializers(second: 0) // okay
23+
_ = IncompleteDesignatedInitializers(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
24+
_ = IncompleteDesignatedInitializers(conveniently: 0) // okay
25+
_ = IncompleteDesignatedInitializers(category: 0) // okay
26+
27+
_ = IncompleteConvenienceInitializers(first: 0) // okay
28+
_ = IncompleteConvenienceInitializers(second: 0) // okay
29+
_ = IncompleteConvenienceInitializers(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
30+
_ = IncompleteConvenienceInitializers(conveniently: 0) // okay
31+
_ = IncompleteConvenienceInitializers(category: 0) // okay
32+
33+
_ = IncompleteUnknownInitializers(first: 0) // okay
34+
_ = IncompleteUnknownInitializers(second: 0) // okay
35+
_ = IncompleteUnknownInitializers(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
36+
_ = IncompleteUnknownInitializers(conveniently: 0) // okay
37+
_ = IncompleteUnknownInitializers(category: 0) // okay
38+
}
39+
40+
func testSubclasses() {
41+
_ = IncompleteInitSubclass(first: 0) // okay
42+
_ = IncompleteInitSubclass(second: 0) // okay
43+
_ = IncompleteInitSubclass(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
44+
_ = IncompleteInitSubclass(conveniently: 0) // expected-error {{argument labels '(conveniently:)' do not match any available overloads}} expected-note {{overloads}}
45+
_ = IncompleteInitSubclass(category: 0) // expected-error {{argument labels '(category:)' do not match any available overloads}} expected-note {{overloads}}
46+
47+
_ = IncompleteInitSubclassImplicit(first: 0) // okay
48+
_ = IncompleteInitSubclassImplicit(second: 0) // okay
49+
_ = IncompleteInitSubclassImplicit(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
50+
_ = IncompleteInitSubclassImplicit(conveniently: 0) // expected-error {{argument labels '(conveniently:)' do not match any available overloads}} expected-note {{overloads}}
51+
_ = IncompleteInitSubclassImplicit(category: 0) // expected-error {{argument labels '(category:)' do not match any available overloads}} expected-note {{overloads}}
52+
53+
_ = IncompleteConvenienceInitSubclass(first: 0) // okay
54+
_ = IncompleteConvenienceInitSubclass(second: 0) // okay
55+
_ = IncompleteConvenienceInitSubclass(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
56+
_ = IncompleteConvenienceInitSubclass(conveniently: 0) // okay
57+
_ = IncompleteConvenienceInitSubclass(category: 0) // okay
58+
59+
_ = IncompleteUnknownInitSubclass(first: 0) // okay
60+
_ = IncompleteUnknownInitSubclass(second: 0) // okay
61+
_ = IncompleteUnknownInitSubclass(missing: 0) // expected-error {{argument labels '(missing:)' do not match any available overloads}} expected-note {{overloads}}
62+
_ = IncompleteUnknownInitSubclass(conveniently: 0) // okay
63+
64+
// FIXME: This initializer isn't being inherited for some reason, unrelated
65+
// to the non-importable -initMissing:.
66+
// https://bugs.swift.org/browse/SR-4566
67+
_ = IncompleteUnknownInitSubclass(category: 0) // expected-error {{argument labels '(category:)' do not match any available overloads}} expected-note {{overloads}}
68+
}

0 commit comments

Comments
 (0)