Skip to content

Commit ef5d96a

Browse files
committed
Diagnose and forbid invalid Swift names on inits
Initializers should always have Swift names that have the special `DeclBaseName::createConstructor()` base name. Although there is an assertion to this effect in the constructor for ConstructorDecl, ClangImporter did not actually reject custom Swift names for initializers that violated this rule. This meant that asserts compilers would crash if they encountered code with an invalid `swift_name` attribute, while release compilers would silently accept them (while excluding these decls from certain checks since lookups that were supposed to find all initializers didn’t find them). Modify ClangImporter to diagnose this condition and ignore the custom Swift name.
1 parent e689ad3 commit ef5d96a

File tree

7 files changed

+51
-4
lines changed

7 files changed

+51
-4
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
66
## Swift 6.1
77

8+
* Previous versions of Swift would incorrectly allow Objective-C `-init...`
9+
methods with custom Swift names to be imported as initializers, but with base
10+
names other than `init`. The compiler now diagnoses these attributes and
11+
infers a name for the initializer as though they are not present.
12+
813
* Projected value initializers are now correctly injected into calls when
914
an argument exactly matches a parameter with an external property wrapper.
1015

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ ERROR(dump_pcm_error,Fatal,
5454
WARNING(invalid_swift_name_method,none,
5555
"too %select{few|many}0 parameters in swift_name attribute (expected %1; "
5656
"got %2)", (bool, unsigned, unsigned))
57+
WARNING(invalid_swift_name_for_decl,none,
58+
"custom Swift name '%0' ignored because it is not valid for %kindonly1; "
59+
"imported as %1 instead",
60+
(StringRef, ValueDecl *))
5761

5862
NOTE(note_while_importing, none, "while importing '%0'", (StringRef))
5963
ERROR(swift_name_protocol_static, none, "swift_name cannot be used to define "

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7281,6 +7281,14 @@ ConstructorDecl *SwiftDeclConverter::importConstructor(
72817281
// If this constructor overrides another constructor, mark it as such.
72827282
recordObjCOverride(result);
72837283

7284+
// If we ignored a custom Swift name because it wasn't suitable for an init,
7285+
// diagnose that now.
7286+
if (importedName.hasInvalidCustomName() && isActiveSwiftVersion()) {
7287+
if (auto customName = NameImporter::findCustomName(objcMethod, version)) {
7288+
result->diagnose(diag::invalid_swift_name_for_decl, *customName, result);
7289+
}
7290+
}
7291+
72847292
return result;
72857293
}
72867294

lib/ClangImporter/ImportName.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,15 @@ static StringRef renameUnsafeMethod(ASTContext &ctx,
15011501
return name;
15021502
}
15031503

1504+
std::optional<StringRef>
1505+
NameImporter::findCustomName(const clang::Decl *decl,
1506+
ImportNameVersion version) {
1507+
if (auto nameAttr = findSwiftNameAttr(decl, version)) {
1508+
return nameAttr->name;
1509+
}
1510+
return std::nullopt;
1511+
}
1512+
15041513
ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15051514
ImportNameVersion version,
15061515
clang::DeclarationName givenName) {
@@ -1676,6 +1685,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
16761685
// Note that this is an initializer.
16771686
isInitializer = true;
16781687
}
1688+
} else if (shouldImportAsInitializer(method, version, initPrefixLength)) {
1689+
// This is an initializer, but its custom name is ill-formed. Ignore
1690+
// the swift_name attribute.
1691+
skipCustomName = true;
1692+
result.info.hasInvalidCustomName = true;
16791693
}
16801694
}
16811695

lib/ClangImporter/ImportName.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,16 @@ class ImportedName {
230230

231231
unsigned hasAsyncAlternateInfo: 1;
232232

233+
/// Whether this declaration had a custom name that was ignored because it
234+
/// was invalid.
235+
unsigned hasInvalidCustomName : 1;
236+
233237
Info()
234238
: errorInfo(), selfIndex(), initKind(CtorInitializerKind::Designated),
235239
accessorKind(ImportedAccessorKind::None), hasCustomName(false),
236240
droppedVariadic(false), importAsMember(false), hasSelfIndex(false),
237241
hasErrorInfo(false), hasAsyncInfo(false),
238-
hasAsyncAlternateInfo(false) {}
242+
hasAsyncAlternateInfo(false), hasInvalidCustomName(false) {}
239243
} info;
240244

241245
public:
@@ -313,6 +317,10 @@ class ImportedName {
313317
bool hasCustomName() const { return info.hasCustomName; }
314318
void setHasCustomName() { info.hasCustomName = true; }
315319

320+
/// Whether this declaration had a custom name that was ignored because it
321+
/// was invalid.
322+
bool hasInvalidCustomName() const { return info.hasInvalidCustomName; }
323+
316324
/// Whether this was one of a special class of Objective-C
317325
/// initializers for which we drop the variadic argument rather
318326
/// than refuse to import the initializer.
@@ -489,6 +497,10 @@ class NameImporter {
489497
importSymbolicCXXDecls = isEnabled;
490498
}
491499

500+
/// Retrieve a purported custom name even if it is invalid.
501+
static std::optional<StringRef>
502+
findCustomName(const clang::Decl *decl, ImportNameVersion version);
503+
492504
private:
493505
bool enableObjCInterop() const { return swiftCtx.LangOpts.EnableObjCInterop; }
494506

test/ClangImporter/Inputs/custom-modules/SwiftName.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ enum {
4343
#if __OBJC__
4444
@interface Foo
4545
- (instancetype)init;
46+
- (instancetype)initWithFoo SWIFT_NAME(initWithFoo()); // expected-warning {{custom Swift name 'initWithFoo()' ignored because it is not valid for initializer; imported as 'init(foo:)' instead}}
4647
@end
4748

48-
void acceptsClosure(id value, void (*fn)(void)) SWIFT_NAME(Foo.accepts(self:closure:));
49-
void acceptsClosureStatic(void (*fn)(void)) SWIFT_NAME(Foo.accepts(closure:));
49+
void acceptsClosure(id value, void (*fn)(void)) SWIFT_NAME(Foo.accepts(self:closure:)); // expected-note * {{'acceptsClosure' was obsoleted in Swift 3}}
50+
void acceptsClosureStatic(void (*fn)(void)) SWIFT_NAME(Foo.accepts(closure:)); // expected-note * {{'acceptsClosureStatic' was obsoleted in Swift 3}}
5051

5152
enum {
5253
// Note that there was specifically a crash when renaming onto an ObjC class,

test/ClangImporter/attr-swift_name_renaming-objc.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -I %S/Inputs/custom-modules -Xcc -w -typecheck -verify %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -I %S/Inputs/custom-modules -Xcc -w -typecheck -verify %s -verify-additional-file %S/Inputs/custom-modules/SwiftName.h
22

33
import SwiftName
44

@@ -22,4 +22,7 @@ func test() {
2222

2323
_ = AnonymousEnumConstantObjC // expected-error {{'AnonymousEnumConstantObjC' has been renamed to 'Foo.anonymousEnumConstant'}}
2424
_ = Foo.anonymousEnumConstant // okay
25+
26+
_ = Foo.initWithFoo() // expected-error {{type 'Foo' has no member 'initWithFoo'}}
27+
_ = Foo.init(foo: ())
2528
}

0 commit comments

Comments
 (0)