Skip to content

Commit d1efc80

Browse files
committed
[Import Decl] Don’t import as init using omit needless words
Previously, for an Objective-C class method declaration that could be imported as init, we were making 4 decls: 1) The Swift 2 init 2) The Swift 2 class method decl (suppressing init formation) 3) The Swift 3 init (omitting needless words) 4) The Swift 3 class method decl (suppressing init formation and omitting needless words) Decls 1), 2), and 4) exist for diagnostics and redirect the user at 3). But, 4) does not correspond to any actual Swift version name and producing it correctly would require the user to understand how omit-needless-words and other importer magic operates. It provides very limited value and more importantly gets in the way of future Clang importer refactoring. We’d like to turn Decl importing into something that is simpler and language-version parameterized, but there is no real Swift version to correspond to decl 4). Therefore we will be making the following decls: 1) The "raw" decl, the name as it would appear to the user if they copy-pasted Objective-C code 2) The name as it appeared in Swift 2 (which could be an init) 3) The name as it appeared in Swift 3 (which could be an init and omit needless words) This aligns with the language versions we want to import as in the future: raw, swift2, swift3, …, and current. Note that swift-ide-test prunes decls that are unavailable in the current Swift version, so the Swift 2 non-init decls are not printed out, though they are still present. Tests were updated and expanded to ensure this was still the case.
1 parent 2d5b727 commit d1efc80

File tree

12 files changed

+232
-322
lines changed

12 files changed

+232
-322
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 72 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,19 +1667,17 @@ namespace {
16671667
///
16681668
/// Note: Use this rather than calling Impl.importFullName directly!
16691669
ImportedName importFullName(const clang::NamedDecl *D,
1670-
Optional<ImportedName> &swift3Name,
1671-
ImportNameOptions options = None) {
1670+
Optional<ImportedName> &swift3Name) {
16721671
// Special handling when we import using the Swift 2 name.
16731672
if (useSwift2Name) {
16741673
// First, import based on the Swift 3 name. If that fails, we won't
16751674
// do anything.
1676-
swift3Name = Impl.importFullName(D, options);
1675+
swift3Name = Impl.importFullName(D, None);
16771676
if (!*swift3Name) return *swift3Name;
16781677

16791678
// Import using the Swift 2 name. If that fails, or if it's identical
16801679
// to the Swift name, we won't introduce a Swift 2 stub declaration.
1681-
auto swift2Name =
1682-
Impl.importFullName(D, options | ImportNameFlags::Swift2Name);
1680+
auto swift2Name = Impl.importFullName(D, ImportNameFlags::Swift2Name);
16831681
if (!swift2Name || swift2Name.Imported == swift3Name->Imported)
16841682
return ImportedName();
16851683

@@ -1689,7 +1687,7 @@ namespace {
16891687

16901688
// Just import the Swift 2 name.
16911689
swift3Name = None;
1692-
return Impl.importFullName(D, options);
1690+
return Impl.importFullName(D, None);
16931691
}
16941692

16951693
/// \brief Create a declaration name for anonymous enums, unions and
@@ -1756,6 +1754,12 @@ namespace {
17561754
return ImportedName();
17571755
}
17581756

1757+
bool isFactoryInit(ImportedName &name) {
1758+
return name && name.Imported.getBaseName() == Impl.SwiftContext.Id_init &&
1759+
(name.InitKind == CtorInitializerKind::Factory ||
1760+
name.InitKind == CtorInitializerKind::ConvenienceFactory);
1761+
}
1762+
17591763
public:
17601764
explicit SwiftDeclConverter(ClangImporter::Implementation &impl,
17611765
bool useSwift2Name)
@@ -3162,13 +3166,6 @@ namespace {
31623166
return result;
31633167
}
31643168

3165-
/// If the given method is a factory method, import it as a constructor
3166-
Optional<ConstructorDecl *>
3167-
importFactoryMethodAsConstructor(Decl *member,
3168-
const clang::ObjCMethodDecl *decl,
3169-
ObjCSelector selector,
3170-
DeclContext *dc);
3171-
31723169
Decl *VisitObjCMethodDecl(const clang::ObjCMethodDecl *decl,
31733170
DeclContext *dc) {
31743171
return VisitObjCMethodDecl(decl, dc, false);
@@ -3206,13 +3203,71 @@ namespace {
32063203
if (!useSwift2Name && methodAlreadyImported(selector, isInstance, dc))
32073204
return nullptr;
32083205

3206+
3207+
ImportedName importedName;
32093208
Optional<ImportedName> swift3Name;
3210-
auto importedName
3211-
= importFullName(decl, swift3Name,
3212-
ImportNameFlags::SuppressFactoryMethodAsInit);
3209+
importedName = importFullName(decl, swift3Name);
32133210
if (!importedName)
32143211
return nullptr;
32153212

3213+
// Normal case applies when we're importing an older name, or when we're
3214+
// not an init
3215+
if (useSwift2Name || !isFactoryInit(importedName)) {
3216+
auto result = importNonInitObjCMethodDecl(decl, dc, importedName,
3217+
selector, forceClassMethod);
3218+
if (useSwift2Name && result)
3219+
markAsSwift2Variant(result, *swift3Name);
3220+
return result;
3221+
}
3222+
3223+
// We don't want to suppress init formation in Swift 3 names. Instead, we
3224+
// want the normal Swift 3 name, and a "raw" name for diagnostics. The
3225+
// "raw" name will be imported as unavailable with a more helpful and
3226+
// specific message.
3227+
++NumFactoryMethodsAsInitializers;
3228+
bool redundant = false;
3229+
auto result =
3230+
importConstructor(decl, dc, false, importedName.InitKind,
3231+
/*required=*/false, selector, importedName,
3232+
{decl->param_begin(), decl->param_size()},
3233+
decl->isVariadic(), redundant);
3234+
3235+
// Directly ask the NameImporter for the non-init variant of the Swift 2
3236+
// name.
3237+
auto rawOptions =
3238+
ImportNameOptions(ImportNameFlags::SuppressFactoryMethodAsInit) |
3239+
ImportNameFlags::Swift2Name;
3240+
auto rawName = Impl.importFullName(decl, rawOptions);
3241+
if (!rawName)
3242+
return result;
3243+
3244+
auto rawDecl = importNonInitObjCMethodDecl(decl, dc, rawName, selector,
3245+
forceClassMethod);
3246+
if (!rawDecl)
3247+
return result;
3248+
3249+
// Mark the raw imported class method "unavailable", with a useful error
3250+
// message.
3251+
llvm::SmallString<64> message;
3252+
llvm::raw_svector_ostream os(message);
3253+
os << "use object construction '" << decl->getClassInterface()->getName()
3254+
<< "(";
3255+
for (auto arg : importedName.Imported.getArgumentNames()) {
3256+
os << arg << ":";
3257+
}
3258+
os << ")'";
3259+
rawDecl->getAttrs().add(AvailableAttr::createPlatformAgnostic(
3260+
Impl.SwiftContext, Impl.SwiftContext.AllocateCopy(os.str())));
3261+
markAsSwift2Variant(rawDecl, importedName);
3262+
Impl.addAlternateDecl(result, cast<ValueDecl>(rawDecl));
3263+
return result;
3264+
}
3265+
3266+
Decl *importNonInitObjCMethodDecl(const clang::ObjCMethodDecl *decl,
3267+
DeclContext *dc,
3268+
ImportedName importedName,
3269+
ObjCSelector selector,
3270+
bool forceClassMethod) {
32163271
assert(dc->getDeclaredTypeOfContext() && "Method in non-type context?");
32173272
assert(isa<ClangModuleUnit>(dc->getModuleScopeContext()) &&
32183273
"Clang method in Swift context?");
@@ -3224,6 +3279,7 @@ namespace {
32243279
return nullptr;
32253280

32263281
// Add the implicit 'self' parameter patterns.
3282+
bool isInstance = decl->isInstanceMethod() && !forceClassMethod;
32273283
SmallVector<ParameterList *, 4> bodyParams;
32283284
auto selfVar =
32293285
ParamDecl::createSelf(SourceLoc(), dc, /*isStatic*/!isInstance);
@@ -3361,20 +3417,9 @@ namespace {
33613417
if (auto imported = VisitObjCMethodDecl(decl, dc,
33623418
/*forceClassMethod=*/true))
33633419
Impl.addAlternateDecl(result, cast<ValueDecl>(imported));
3364-
} else if (auto factory = importFactoryMethodAsConstructor(
3365-
result, decl, selector, dc)) {
3366-
// We imported the factory method as an initializer, so
3367-
// record it as an alternate declaration.
3368-
if (*factory)
3369-
Impl.addAlternateDecl(result, *factory);
33703420
}
3371-
33723421
}
33733422

3374-
// If this is a Swift 2 stub, mark it as such.
3375-
if (swift3Name)
3376-
markAsSwift2Variant(result, *swift3Name);
3377-
33783423
return result;
33793424
}
33803425

@@ -4976,60 +5021,6 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
49765021
return property;
49775022
}
49785023

4979-
Optional<ConstructorDecl *>
4980-
SwiftDeclConverter::importFactoryMethodAsConstructor(
4981-
Decl *member, const clang::ObjCMethodDecl *decl, ObjCSelector selector,
4982-
DeclContext *dc) {
4983-
// Import the full name of the method.
4984-
Optional<ImportedName> swift3Name;
4985-
auto importedName = importFullName(decl, swift3Name);
4986-
4987-
// Check that we imported an initializer name.
4988-
DeclName initName = importedName;
4989-
if (initName.getBaseName() != Impl.SwiftContext.Id_init)
4990-
return None;
4991-
4992-
// ... that came from a factory method.
4993-
if (importedName.InitKind != CtorInitializerKind::Factory &&
4994-
importedName.InitKind != CtorInitializerKind::ConvenienceFactory)
4995-
return None;
4996-
4997-
bool redundant = false;
4998-
auto result = importConstructor(decl, dc, false, importedName.InitKind,
4999-
/*required=*/false, selector, importedName,
5000-
{decl->param_begin(), decl->param_size()},
5001-
decl->isVariadic(), redundant);
5002-
5003-
if ((result || redundant) && member) {
5004-
++NumFactoryMethodsAsInitializers;
5005-
5006-
// Mark the imported class method "unavailable", with a useful error
5007-
// message.
5008-
// TODO: Could add a replacement string?
5009-
llvm::SmallString<64> message;
5010-
llvm::raw_svector_ostream os(message);
5011-
os << "use object construction '" << decl->getClassInterface()->getName()
5012-
<< "(";
5013-
for (auto arg : initName.getArgumentNames()) {
5014-
os << arg << ":";
5015-
}
5016-
os << ")'";
5017-
member->getAttrs().add(AvailableAttr::createPlatformAgnostic(
5018-
Impl.SwiftContext, Impl.SwiftContext.AllocateCopy(os.str())));
5019-
}
5020-
5021-
/// Record the initializer as an alternative declaration for the
5022-
/// member.
5023-
if (result) {
5024-
Impl.addAlternateDecl(member, result);
5025-
5026-
if (swift3Name)
5027-
markAsSwift2Variant(result, *swift3Name);
5028-
}
5029-
5030-
return result;
5031-
}
5032-
50335024
ConstructorDecl *SwiftDeclConverter::importConstructor(
50345025
const clang::ObjCMethodDecl *objcMethod, DeclContext *dc, bool implicit,
50355026
Optional<CtorInitializerKind> kind, bool required) {

lib/ClangImporter/ImportName.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,18 @@ bool NameImporter::hasErrorMethodNameCollision(
10841084
enableObjCInterop());
10851085
}
10861086

1087+
/// Whether we should suppress this factory method being imported as an
1088+
/// initializer. We want to do this when explicitly directed to, or when
1089+
/// importing a property accessor.
1090+
static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method,
1091+
ImportNameOptions options,
1092+
CtorInitializerKind initKind) {
1093+
return (method->isPropertyAccessor() ||
1094+
options.contains(ImportNameFlags::SuppressFactoryMethodAsInit)) &&
1095+
(initKind == CtorInitializerKind::Factory ||
1096+
initKind == CtorInitializerKind::ConvenienceFactory);
1097+
}
1098+
10871099
ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
10881100
ImportNameOptions options) {
10891101
ImportedName result;
@@ -1198,9 +1210,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
11981210
// If this swift_name attribute maps a factory method to an
11991211
// initializer and we were asked not to do so, ignore the
12001212
// custom name.
1201-
if (options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) &&
1202-
(result.InitKind == CtorInitializerKind::Factory ||
1203-
result.InitKind == CtorInitializerKind::ConvenienceFactory)) {
1213+
if (suppressFactoryMethodAsInit(method, options, result.InitKind)) {
12041214
skipCustomName = true;
12051215
} else {
12061216
// Note that this is an initializer.
@@ -1331,9 +1341,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
13311341
// If we would import a factory method as an initializer but were
13321342
// asked not to, don't consider this as an initializer.
13331343
if (isInitializer &&
1334-
options.contains(ImportNameFlags::SuppressFactoryMethodAsInit) &&
1335-
(result.InitKind == CtorInitializerKind::Factory ||
1336-
result.InitKind == CtorInitializerKind::ConvenienceFactory)) {
1344+
suppressFactoryMethodAsInit(objcMethod, options, result.InitKind)) {
13371345
isInitializer = false;
13381346
}
13391347

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
447447

448448
/// Retrieve the alternative declaration for the given imported
449449
/// Swift declaration.
450-
TinyPtrVector<ValueDecl *> getAlternateDecls(Decl *decl) {
450+
ArrayRef<ValueDecl *> getAlternateDecls(Decl *decl) {
451451
auto known = AlternateDecls.find(decl);
452452
if (known == AlternateDecls.end()) return {};
453453
return known->second;

test/ClangImporter/Inputs/SwiftPrivateAttr.txt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,10 @@ class Foo : NSObject, __PrivProto {
88
func __noArgs()
99
func __oneArg(_ arg: Int32)
1010
func __twoArgs(_ arg: Int32, other arg2: Int32)
11-
class func __foo() -> Self!
1211
class func __withNoArgs() -> Self!
1312
convenience init!(__oneArg arg: Int32)
14-
@available(*, unavailable, message: "use object construction 'Foo(__oneArg:)'")
15-
class func __withOneArg(_ arg: Int32) -> Self!
1613
convenience init!(__twoArgs arg: Int32, other arg2: Int32)
17-
@available(*, unavailable, message: "use object construction 'Foo(__twoArgs:other:)'")
18-
class func __withTwoArgs(_ arg: Int32, other arg2: Int32) -> Self!
1914
convenience init!(__ arg: Int32)
20-
@available(*, unavailable, message: "use object construction 'Foo(__:)'")
21-
class func __foo(_ arg: Int32) -> Self!
2215
func objectForKeyedSubscript(_ index: Any!) -> Any!
2316
func __setObject(_ object: Any!, forKeyedSubscript index: Any!)
2417
func __objectAtIndexedSubscript(_ index: Int32) -> Any!

test/ClangImporter/attr-swift_private.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,10 @@ func testCF(_ a: __PrivCFType, b: __PrivCFSub, c: __PrivInt) {
132132
extension __PrivCFType {}
133133
extension __PrivCFSub {}
134134
_ = 1 as __PrivInt
135+
136+
#if !IRGEN
137+
func testRawNames() {
138+
let _ = Foo.__fooWithOneArg(0) // expected-error {{'__fooWithOneArg' is unavailable: use object construction 'Foo(__oneArg:)'}}
139+
let _ = Foo.__foo // expected-error{{'__foo' is unavailable: use object construction 'Foo(__:)'}}
140+
}
141+
#endif

test/ClangImporter/objc_factory_method.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func testNonInstanceTypeFactoryMethod(_ s: String) {
8484
}
8585

8686
func testUseOfFactoryMethod(_ queen: Bee) {
87-
_ = Hive.withQueen(queen) // expected-error{{'withQueen' is unavailable: use object construction 'Hive(queen:)'}}
87+
_ = Hive.hiveWithQueen(queen) // expected-error{{'hiveWithQueen' is unavailable: use object construction 'Hive(queen:)'}}
8888
}
8989

9090
func testNonsplittableFactoryMethod() {
@@ -97,16 +97,16 @@ func testFactoryMethodBlacklist() {
9797

9898
func test17261609() {
9999
_ = NSDecimalNumber(mantissa:1, exponent:1, isNegative:true)
100-
_ = NSDecimalNumber.withMantissa(1, exponent:1, isNegative:true) // expected-error{{'withMantissa(_:exponent:isNegative:)' is unavailable: use object construction 'NSDecimalNumber(mantissa:exponent:isNegative:)'}}
100+
_ = NSDecimalNumber.decimalNumberWithMantissa(1, exponent:1, isNegative:true) // expected-error{{'decimalNumberWithMantissa(_:exponent:isNegative:)' is unavailable: use object construction 'NSDecimalNumber(mantissa:exponent:isNegative:)'}}
101101
}
102102

103103
func testURL() {
104104
let url = NSURL(string: "http://www.llvm.org")!
105-
_ = NSURL.withString("http://www.llvm.org") // expected-error{{'withString' is unavailable: use object construction 'NSURL(string:)'}}
105+
_ = NSURL.URLWithString("http://www.llvm.org") // expected-error{{'URLWithString' is unavailable: use object construction 'NSURL(string:)'}}
106106

107107
NSURLRequest(string: "http://www.llvm.org") // expected-warning{{unused}}
108108
NSURLRequest(url: url as URL) // expected-warning{{unused}}
109109

110-
_ = NSURLRequest.withString("http://www.llvm.org") // expected-error{{'withString' is unavailable: use object construction 'NSURLRequest(string:)'}}
111-
_ = NSURLRequest.withURL(url as URL) // expected-error{{'withURL' is unavailable: use object construction 'NSURLRequest(url:)'}}
110+
_ = NSURLRequest.requestWithString("http://www.llvm.org") // expected-error{{'requestWithString' is unavailable: use object construction 'NSURLRequest(string:)'}}
111+
_ = NSURLRequest.URLRequestWithURL(url as URL) // expected-error{{'URLRequestWithURL' is unavailable: use object construction 'NSURLRequest(url:)'}}
112112
}

test/ClangImporter/objc_implicit_with.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func testNonInstanceTypeFactoryMethod(_ s: String) {
5050
}
5151

5252
func testUseOfFactoryMethod(_ queen: Bee) {
53-
_ = Hive.withQueen(queen) // expected-error{{'withQueen' is unavailable: use object construction 'Hive(queen:)'}}
53+
_ = Hive.hiveWithQueen(queen) // expected-error{{'hiveWithQueen' is unavailable: use object construction 'Hive(queen:)'}}
5454
}
5555

5656
func testNonsplittableFactoryMethod() {

test/ClangImporter/objc_init.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,10 @@ class DesignedInitSubSub : DesignatedInitSub {
164164
init(double: Double) { super.init(int: 0) } // okay
165165
init(string: String) { super.init() } // expected-error {{must call a designated initializer of the superclass 'DesignatedInitSub'}}
166166
}
167+
168+
// Make sure that our magic doesn't think the class property with the type name is an init
169+
func classPropertiesAreNotInit() -> ProcessInfo {
170+
var procInfo = NSProcessInfo.processInfo // expected-error{{'NSProcessInfo' has been renamed to 'ProcessInfo'}}
171+
procInfo = ProcessInfo.processInfo // okay
172+
return procInfo
173+
}

0 commit comments

Comments
 (0)