Skip to content

Commit 9bc16e3

Browse files
authored
Merge pull request #83402 from egorzhdan/egorzhdan/honor-non-copyable-attr
[cxx-interop] Avoid trying to instantiate copy constructors of explicitly non-copyable structs
2 parents e27f9dd + a07da0f commit 9bc16e3

8 files changed

+67
-5
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8009,7 +8009,7 @@ bool importer::hasIteratorAPIAttr(const clang::Decl *decl) {
80098009
return hasSwiftAttribute(decl, "import_iterator");
80108010
}
80118011

8012-
static bool hasNonCopyableAttr(const clang::RecordDecl *decl) {
8012+
bool importer::hasNonCopyableAttr(const clang::RecordDecl *decl) {
80138013
return hasSwiftAttribute(decl, "~Copyable");
80148014
}
80158015

lib/ClangImporter/ImportDecl.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3005,10 +3005,14 @@ namespace {
30053005
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
30063006
ctor);
30073007
}
3008+
// If the C++ struct is annotated as non-copyable, we should not try to
3009+
// instantiate its copy constructor.
3010+
bool isExplicitlyNonCopyable = hasNonCopyableAttr(decl);
3011+
30083012
clang::CXXConstructorDecl *copyCtor = nullptr;
30093013
clang::CXXConstructorDecl *moveCtor = nullptr;
30103014
clang::CXXConstructorDecl *defaultCtor = nullptr;
3011-
if (decl->needsImplicitCopyConstructor()) {
3015+
if (decl->needsImplicitCopyConstructor() && !isExplicitlyNonCopyable) {
30123016
copyCtor = clangSema.DeclareImplicitCopyConstructor(
30133017
const_cast<clang::CXXRecordDecl *>(decl));
30143018
}
@@ -3031,7 +3035,7 @@ namespace {
30313035
// that's what "DefineImplicitCopyConstructor" checks.
30323036
!declCtor->doesThisDeclarationHaveABody()) {
30333037
if (declCtor->isCopyConstructor()) {
3034-
if (!copyCtor)
3038+
if (!copyCtor && !isExplicitlyNonCopyable)
30353039
copyCtor = declCtor;
30363040
} else if (declCtor->isMoveConstructor()) {
30373041
if (!moveCtor)
@@ -3043,7 +3047,7 @@ namespace {
30433047
}
30443048
}
30453049
}
3046-
if (copyCtor) {
3050+
if (copyCtor && !isExplicitlyNonCopyable) {
30473051
clangSema.DefineImplicitCopyConstructor(clang::SourceLocation(),
30483052
copyCtor);
30493053
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,7 @@ bool hasUnsafeAPIAttr(const clang::Decl *decl);
21372137
bool hasIteratorAPIAttr(const clang::Decl *decl);
21382138

21392139
bool hasNonEscapableAttr(const clang::RecordDecl *decl);
2140-
2140+
bool hasNonCopyableAttr(const clang::RecordDecl *decl);
21412141
bool hasEscapableAttr(const clang::RecordDecl *decl);
21422142

21432143
bool isViewType(const clang::CXXRecordDecl *decl);

test/Interop/Cxx/templates/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,8 @@ module ManySpecializations {
172172
header "many-specializations.h"
173173
requires cplusplus
174174
}
175+
176+
module UninstantiatableSpecialMembers {
177+
header "uninstantiatable-special-members.h"
178+
requires cplusplus
179+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
template <class T>
2+
struct __attribute__((swift_attr("~Copyable"))) HasUninstantiatableCopyConstructor {
3+
template <class U>
4+
void scaryPoison(U u) {
5+
U::doesNotExist(u);
6+
}
7+
8+
int value;
9+
HasUninstantiatableCopyConstructor(int value) : value(value) {}
10+
HasUninstantiatableCopyConstructor(
11+
const HasUninstantiatableCopyConstructor &other) {
12+
scaryPoison(other);
13+
}
14+
HasUninstantiatableCopyConstructor(
15+
HasUninstantiatableCopyConstructor &&other) = default;
16+
};
17+
18+
typedef HasUninstantiatableCopyConstructor<int> NonCopyableInst;
19+
20+
template <class T>
21+
struct __attribute__((swift_attr("~Copyable"))) DerivedUninstantiatableCopyConstructor : HasUninstantiatableCopyConstructor<T> {
22+
DerivedUninstantiatableCopyConstructor(int value) : HasUninstantiatableCopyConstructor<T>(value) {}
23+
DerivedUninstantiatableCopyConstructor(const DerivedUninstantiatableCopyConstructor &other) = default;
24+
DerivedUninstantiatableCopyConstructor(DerivedUninstantiatableCopyConstructor &&other) = default;
25+
};
26+
27+
typedef DerivedUninstantiatableCopyConstructor<int> DerivedNonCopyableInst;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: %target-swift-emit-irgen %s -cxx-interoperability-mode=default -I %S/Inputs | %FileCheck %s
2+
3+
import UninstantiatableSpecialMembers
4+
5+
let nonCopyableValue = NonCopyableInst(123)
6+
let nonCopyableDerived = DerivedNonCopyableInst(567)
7+
8+
// CHECK-NOT: scaryPoison
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=UninstantiatableSpecialMembers -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
2+
3+
// CHECK: struct HasUninstantiatableCopyConstructor<CInt> {
4+
// CHECK: }
5+
// CHECK: typealias NonCopyableInst = HasUninstantiatableCopyConstructor<CInt>
6+
7+
// CHECK: struct DerivedUninstantiatableCopyConstructor<CInt> {
8+
// CHECK: }
9+
// CHECK: typealias DerivedNonCopyableInst = DerivedUninstantiatableCopyConstructor<CInt>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-typecheck-verify-swift -cxx-interoperability-mode=default -I %S/Inputs
2+
3+
import UninstantiatableSpecialMembers
4+
5+
let nonCopyableValue = NonCopyableInst(123)
6+
let copy1 = copy nonCopyableValue // expected-error {{'copy' cannot be applied to noncopyable types}}
7+
8+
let nonCopyableDerived = DerivedNonCopyableInst(567)
9+
let copy2 = copy nonCopyableDerived // expected-error {{'copy' cannot be applied to noncopyable types}}

0 commit comments

Comments
 (0)