Skip to content

Commit 593f320

Browse files
authored
Merge pull request #79325 from swiftlang/susmonteiro/copy-constructor-default-args
[cxx-interop] Prevent usage in Swift of C++ copy constructor with default args
2 parents f0446d0 + bc6573e commit 593f320

File tree

7 files changed

+79
-5
lines changed

7 files changed

+79
-5
lines changed

lib/ClangImporter/ClangImporter.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -8006,6 +8006,14 @@ static bool isSufficientlyTrivial(const clang::CXXRecordDecl *decl) {
80068006
return true;
80078007
}
80088008

8009+
static bool hasNonFirstDefaultArg(const clang::CXXConstructorDecl *ctor) {
8010+
if (ctor->getNumParams() < 2)
8011+
return false;
8012+
8013+
auto lastParam = ctor->parameters().back();
8014+
return lastParam->hasDefaultArg();
8015+
}
8016+
80098017
/// Checks if a record provides the required value type lifetime operations
80108018
/// (copy and destroy).
80118019
static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
@@ -8022,6 +8030,7 @@ static bool hasCopyTypeOperations(const clang::CXXRecordDecl *decl) {
80228030
// struct.
80238031
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
80248032
return ctor->isCopyConstructor() && !ctor->isDeleted() &&
8033+
!hasNonFirstDefaultArg(ctor) &&
80258034
ctor->getAccess() == clang::AccessSpecifier::AS_public;
80268035
});
80278036
}

lib/IRGen/GenStruct.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -541,12 +541,20 @@ namespace {
541541
FixedTypeInfo, ClangFieldInfo> {
542542
const clang::RecordDecl *ClangDecl;
543543

544+
bool hasNonFirstDefaultArg(const clang::CXXConstructorDecl *ctor) const {
545+
if (ctor->getNumParams() < 2)
546+
return false;
547+
548+
auto lastParam = ctor->parameters().back();
549+
return lastParam->hasDefaultArg();
550+
}
551+
544552
const clang::CXXConstructorDecl *findCopyConstructor() const {
545553
const auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(ClangDecl);
546554
if (!cxxRecordDecl)
547555
return nullptr;
548556
for (auto ctor : cxxRecordDecl->ctors()) {
549-
if (ctor->isCopyConstructor() &&
557+
if (ctor->isCopyConstructor() && !hasNonFirstDefaultArg(ctor) &&
550558
ctor->getAccess() == clang::AS_public && !ctor->isDeleted())
551559
return ctor;
552560
}

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -640,8 +640,6 @@ void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
640640
// This copy_addr [take] will perform the final deinitialization.
641641
return false;
642642
}
643-
assert(!tempObj->getType().isMoveOnly() &&
644-
"introducing copy of move-only value!?");
645643
return true;
646644
}
647645
if (auto *li = dyn_cast<LoadInst>(lastLoadInst)) {
@@ -650,8 +648,6 @@ void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
650648
// This load [take] will perform the final deinitialization.
651649
return false;
652650
}
653-
assert(!tempObj->getType().isMoveOnly() &&
654-
"introducing copy of move-only value!?");
655651
return true;
656652
}
657653
return true;

test/Interop/Cxx/class/Inputs/type-classification.h

+12
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,16 @@ struct __attribute__((swift_attr("~Copyable"))) StructCopyableMovableAnnotatedNo
262262
~StructCopyableMovableAnnotatedNonCopyable() = default;
263263
};
264264

265+
struct HasCopyConstructorWithDefaultArgs {
266+
int value;
267+
HasCopyConstructorWithDefaultArgs(int value) : value(value) {}
268+
269+
HasCopyConstructorWithDefaultArgs(
270+
const HasCopyConstructorWithDefaultArgs &other, int value = 1)
271+
: value(other.value + value) {}
272+
273+
HasCopyConstructorWithDefaultArgs(HasCopyConstructorWithDefaultArgs &&) =
274+
default;
275+
};
276+
265277
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_TYPE_CLASSIFICATION_H

test/Interop/Cxx/class/type-classification-typechecker.swift

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ func testAnnotated() {
2222
_ = v
2323
}
2424

25+
func testNonCopyable() {
26+
let x = HasCopyConstructorWithDefaultArgs(5)
27+
let v = copy x // expected-error {{'copy' cannot be applied to noncopyable types}}
28+
_ = v
29+
}
30+
2531
test()
2632
testField()
2733
testAnnotated()
34+
testNonCopyable()

test/Interop/Cxx/value-witness-table/Inputs/copy-constructors.h

+24
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ struct HasNonTrivialDefaultCopyConstructor {
2323
const HasNonTrivialDefaultCopyConstructor &) = default;
2424
};
2525

26+
struct HasCopyConstructorWithDefaultArgs {
27+
int value;
28+
HasCopyConstructorWithDefaultArgs(int value) : value(value) {}
29+
30+
HasCopyConstructorWithDefaultArgs(
31+
const HasCopyConstructorWithDefaultArgs &other, int value = 1)
32+
: value(other.value + value) {}
33+
34+
HasCopyConstructorWithDefaultArgs(HasCopyConstructorWithDefaultArgs &&) =
35+
default;
36+
};
37+
38+
struct HasCopyConstructorWithOneParameterWithDefaultArg {
39+
int numCopies;
40+
41+
HasCopyConstructorWithOneParameterWithDefaultArg(int numCopies)
42+
: numCopies(numCopies) {}
43+
44+
HasCopyConstructorWithOneParameterWithDefaultArg(
45+
const HasCopyConstructorWithOneParameterWithDefaultArg &other =
46+
HasCopyConstructorWithOneParameterWithDefaultArg{1})
47+
: numCopies(other.numCopies + 1) {}
48+
};
49+
2650
// Make sure that we don't crash on struct templates with copy-constructors.
2751
template <typename T> struct S {
2852
S(S const &) {}

test/Interop/Cxx/value-witness-table/copy-constructors-execution.swift

+18
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,22 @@ CXXCopyConstructorTestSuite.test("Default copy constructor, member with user-def
4848
expectTrue(result.0.box.numCopies + result.1.box.numCopies > 0)
4949
}
5050

51+
CXXCopyConstructorTestSuite.test("Copy constructor with default arguments") {
52+
// When in the presence of a C++ copy constructor with default args, we make the type non-copyable
53+
let originalObj = HasCopyConstructorWithDefaultArgs(5)
54+
expectEqual(originalObj.value, 5)
55+
56+
// move originalObj
57+
let newObj = originalObj
58+
expectEqual(newObj.value, 5)
59+
}
60+
61+
CXXCopyConstructorTestSuite.test("Copy constructor with one parameter that has a default argument") {
62+
// If the C++ copy constructor has exactly one param and it has a default argument, ignore the default argument and import the type as copyable to Swift
63+
64+
let original = HasCopyConstructorWithOneParameterWithDefaultArg(1)
65+
let copy = original
66+
expectTrue(original.numCopies + copy.numCopies > 1)
67+
}
68+
5169
runAllTests()

0 commit comments

Comments
 (0)