Skip to content

Commit 8a45e7d

Browse files
authored
Merge pull request #28025 from slavapestov/imported-init-kind-fix
ClangImporter: Fix init kind computation with overridden constructors
2 parents 0f53095 + 7ba2b35 commit 8a45e7d

File tree

3 files changed

+69
-31
lines changed

3 files changed

+69
-31
lines changed

lib/ClangImporter/ImportName.cpp

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ matchFactoryAsInitName(const clang::ObjCMethodDecl *method) {
518518
/// Determine the kind of initializer the given factory method could be mapped
519519
/// to, or produce \c None.
520520
static Optional<CtorInitializerKind>
521-
determineCtorInitializerKind(const clang::ObjCMethodDecl *method) {
521+
determineFactoryInitializerKind(const clang::ObjCMethodDecl *method) {
522522
// Determine whether we have a suitable return type.
523523
if (method->hasRelatedResultType()) {
524524
// When the factory method has an "instancetype" result type, we
@@ -708,7 +708,7 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
708708
}
709709

710710
// Special case: preventing a mapping to an initializer.
711-
if (matchFactoryAsInitName(method) && determineCtorInitializerKind(method))
711+
if (matchFactoryAsInitName(method) && determineFactoryInitializerKind(method))
712712
return attr;
713713

714714
return nullptr;
@@ -733,32 +733,39 @@ getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
733733
return FactoryAsInitKind::Infer;
734734
}
735735

736+
Optional<CtorInitializerKind> determineCtorInitializerKind(
737+
const clang::ObjCMethodDecl *method) {
738+
const clang::ObjCInterfaceDecl *interface = method->getClassInterface();
739+
740+
if (isInitMethod(method)) {
741+
// If the owning Objective-C class has designated initializers and this
742+
// is not one of them, treat it as a convenience initializer.
743+
if (interface && interface->hasDesignatedInitializers() &&
744+
!method->hasAttr<clang::ObjCDesignatedInitializerAttr>()) {
745+
return CtorInitializerKind::Convenience;
746+
}
747+
748+
return CtorInitializerKind::Designated;
749+
}
750+
751+
if (method->isClassMethod())
752+
return determineFactoryInitializerKind(method);
753+
754+
return None;
755+
}
756+
736757
/// Determine whether this Objective-C method should be imported as
737758
/// an initializer.
738759
///
739760
/// \param prefixLength Will be set to the length of the prefix that
740761
/// should be stripped from the first selector piece, e.g., "init"
741762
/// or the restated name of the class in a factory method.
742-
///
743-
/// \param kind Will be set to the kind of initializer being imported.
744763
static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
745764
ImportNameVersion version,
746-
unsigned &prefixLength,
747-
CtorInitializerKind &kind) {
765+
unsigned &prefixLength) {
748766
/// Is this an initializer?
749767
if (isInitMethod(method)) {
750768
prefixLength = 4;
751-
752-
// If the owning Objective-C class has designated initializers and this
753-
// is not one of them, treat it as a convenience initializer.
754-
const clang::ObjCInterfaceDecl *interface = method->getClassInterface();
755-
if (interface && interface->hasDesignatedInitializers() &&
756-
!method->hasAttr<clang::ObjCDesignatedInitializerAttr>()) {
757-
kind = CtorInitializerKind::Convenience;
758-
} else {
759-
kind = CtorInitializerKind::Designated;
760-
}
761-
762769
return true;
763770
}
764771

@@ -791,11 +798,8 @@ static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
791798
return false;
792799
}
793800

794-
// Determine what kind of initializer we're creating.
795-
if (auto initKind = determineCtorInitializerKind(method)) {
796-
kind = *initKind;
801+
if (determineFactoryInitializerKind(method))
797802
return true;
798-
}
799803

800804
// Not imported as an initializer.
801805
return false;
@@ -1246,6 +1250,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
12461250
if (overriddenNames.size() > 1)
12471251
mergeOverriddenNames(swiftCtx, method, overriddenNames);
12481252
overriddenNames[0].second.effectiveContext = result.effectiveContext;
1253+
1254+
// Compute the initializer kind from the derived method, though.
1255+
if (auto kind = determineCtorInitializerKind(method))
1256+
overriddenNames[0].second.info.initKind = *kind;
1257+
12491258
return overriddenNames[0].second;
12501259
}
12511260
} else if (auto property = dyn_cast<clang::ObjCPropertyDecl>(D)) {
@@ -1302,12 +1311,14 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
13021311
if (method) {
13031312
unsigned initPrefixLength;
13041313
if (parsedName.BaseName == "init" && parsedName.IsFunctionName) {
1305-
if (!shouldImportAsInitializer(method, version, initPrefixLength,
1306-
result.info.initKind)) {
1314+
if (!shouldImportAsInitializer(method, version, initPrefixLength)) {
13071315
// We cannot import this as an initializer anyway.
13081316
return ImportedName();
13091317
}
13101318

1319+
if (auto kind = determineCtorInitializerKind(method))
1320+
result.info.initKind = *kind;
1321+
13111322
// If this swift_name attribute maps a factory method to an
13121323
// initializer and we were asked not to do so, ignore the
13131324
// custom name.
@@ -1479,14 +1490,18 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14791490
return ImportedName();
14801491

14811492
isInitializer = shouldImportAsInitializer(objcMethod, version,
1482-
initializerPrefixLen,
1483-
result.info.initKind);
1484-
1485-
// If we would import a factory method as an initializer but were
1486-
// asked not to, don't consider this as an initializer.
1487-
if (isInitializer && suppressFactoryMethodAsInit(objcMethod, version,
1488-
result.getInitKind())) {
1489-
isInitializer = false;
1493+
initializerPrefixLen);
1494+
1495+
if (isInitializer) {
1496+
if (auto kind = determineCtorInitializerKind(objcMethod))
1497+
result.info.initKind = *kind;
1498+
1499+
// If we would import a factory method as an initializer but were
1500+
// asked not to, don't consider this as an initializer.
1501+
if (suppressFactoryMethodAsInit(objcMethod, version,
1502+
result.getInitKind())) {
1503+
isInitializer = false;
1504+
}
14901505
}
14911506

14921507
if (isInitializer)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#import <Foundation.h>
2+
3+
@interface Base : NSObject
4+
5+
- (instancetype) init;
6+
7+
- (instancetype) initWithFoo: (int) x NS_DESIGNATED_INITIALIZER;
8+
@end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -import-objc-header %S/Inputs/objc_init_override_kind.h %s
2+
3+
// REQUIRES: objc_interop
4+
5+
import Foundation
6+
7+
// rdar://problem/56674158
8+
class Derived : Base {
9+
// This is not flagged as an error, because Base.init() is a
10+
// convenience init.
11+
init() { super.init(foo: 123) }
12+
13+
required init?(coder: NSCoder) {}
14+
}
15+

0 commit comments

Comments
 (0)