Skip to content

Commit 89cdb01

Browse files
committed
[cxx-interop] Prevent usage in Swift of C++ move constructor with default args
1 parent 51b7ac3 commit 89cdb01

File tree

10 files changed

+72
-11
lines changed

10 files changed

+72
-11
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ NOTE(record_not_automatically_importable, none, "record '%0' is not "
170170
"does this type have reference "
171171
"semantics?",
172172
(StringRef, StringRef))
173+
NOTE(record_unsupported_default_args, none,
174+
"copy constructors and move constructors with more than one parameter are "
175+
"unavailable in Swift",
176+
())
173177

174178
NOTE(projection_value_not_imported, none, "C++ method '%0' that returns a value "
175179
"of type '%1' is unavailable",

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,10 @@ enum class CxxRecordSemanticsKind {
333333
MoveOnly,
334334
Reference,
335335
Iterator,
336-
// A record that is either not copyable or not destructible.
336+
// A record that is either not copyable/movable or not destructible.
337337
MissingLifetimeOperation,
338+
// A record that has no copy and no move operations
339+
UnavailableConstructors,
338340
// A C++ record that represents a Swift class type exposed to C++ from Swift.
339341
SwiftClassType
340342
};

lib/ClangImporter/ClangImporter.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7855,6 +7855,7 @@ static bool hasNonFirstDefaultArg(const clang::CXXConstructorDecl *ctor) {
78557855
if (ctor->getNumParams() < 2)
78567856
return false;
78577857

7858+
// TODO i could just assume that if numParams > 1, they have default args
78587859
auto lastParam = ctor->parameters().back();
78597860
return lastParam->hasDefaultArg();
78607861
}
@@ -7891,7 +7892,7 @@ static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
78917892
return false;
78927893

78937894
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
7894-
return ctor->isMoveConstructor();
7895+
return ctor->isMoveConstructor() && !hasNonFirstDefaultArg(ctor);
78957896
});
78967897
}
78977898

@@ -7910,6 +7911,14 @@ static bool hasCustomCopyOrMoveConstructor(const clang::CXXRecordDecl *decl) {
79107911
decl->hasUserDeclaredMoveConstructor();
79117912
}
79127913

7914+
static bool
7915+
hasConstructorWithUnsupportedDefaultArgs(const clang::CXXRecordDecl *decl) {
7916+
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
7917+
return (ctor->isCopyConstructor() || ctor->isMoveConstructor()) &&
7918+
hasNonFirstDefaultArg(ctor);
7919+
});
7920+
}
7921+
79137922
static bool isSwiftClassType(const clang::CXXRecordDecl *decl) {
79147923
// Swift type must be annotated with external_source_symbol attribute.
79157924
auto essAttr = decl->getAttr<clang::ExternalSourceSymbolAttr>();
@@ -7964,6 +7973,9 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
79647973
"import_iterator", decl->getNameAsString());
79657974
}
79667975

7976+
if (hasConstructorWithUnsupportedDefaultArgs(cxxDecl))
7977+
return CxxRecordSemanticsKind::UnavailableConstructors;
7978+
79677979
return CxxRecordSemanticsKind::MissingLifetimeOperation;
79687980
}
79697981

lib/ClangImporter/ImportDecl.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,15 +2927,23 @@ namespace {
29272927
auto semanticsKind =
29282928
evaluateOrDefault(Impl.SwiftContext.evaluator,
29292929
CxxRecordSemantics({decl, Impl.SwiftContext}), {});
2930-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation &&
2930+
if ((semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation ||
2931+
semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) &&
29312932
// Let un-specialized class templates through. We'll sort out their
2932-
// members once they're instranciated.
2933+
// members once they're instantiated.
29332934
!Impl.importSymbolicCXXDecls) {
2935+
2936+
if (semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) {
2937+
Impl.addImportDiagnostic(
2938+
decl, Diagnostic(diag::record_unsupported_default_args),
2939+
decl->getLocation());
2940+
}
2941+
29342942
Impl.addImportDiagnostic(
29352943
decl,
29362944
Diagnostic(diag::record_not_automatically_importable,
29372945
Impl.SwiftContext.AllocateCopy(decl->getNameAsString()),
2938-
"does not have a copy constructor or destructor"),
2946+
"it must have a copy/move constructor and a destructor"),
29392947
decl->getLocation());
29402948
return nullptr;
29412949
}

lib/IRGen/GenStruct.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ namespace {
566566
if (!cxxRecordDecl)
567567
return nullptr;
568568
for (auto ctor : cxxRecordDecl->ctors()) {
569-
if (ctor->isMoveConstructor() &&
569+
if (ctor->isMoveConstructor() && !hasNonFirstDefaultArg(ctor) &&
570570
ctor->getAccess() == clang::AS_public && !ctor->isDeleted())
571571
return ctor;
572572
}

test/Interop/Cxx/class/Inputs/constructors.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ struct CopyAndMoveConstructor {
3737
int *ptr = nullptr;
3838
};
3939

40+
struct MoveConstructorWithOneParameterWithDefaultArg {
41+
int value;
42+
43+
MoveConstructorWithOneParameterWithDefaultArg(int value) : value(value) {}
44+
45+
MoveConstructorWithOneParameterWithDefaultArg(
46+
const MoveConstructorWithOneParameterWithDefaultArg &) = delete;
47+
48+
MoveConstructorWithOneParameterWithDefaultArg(
49+
MoveConstructorWithOneParameterWithDefaultArg &&other =
50+
MoveConstructorWithOneParameterWithDefaultArg{1})
51+
: value(other.value + 1) {}
52+
};
53+
4054
struct Base {};
4155

4256
struct ArgType {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,13 @@ struct HasCopyConstructorWithDefaultArgs {
274274
default;
275275
};
276276

277+
struct HasMoveConstructorWithDefaultArgs {
278+
int value;
279+
HasMoveConstructorWithDefaultArgs(int value) : value(value) {}
280+
281+
HasMoveConstructorWithDefaultArgs(HasMoveConstructorWithDefaultArgs &&other,
282+
int value = 1)
283+
: value(other.value + value) {}
284+
};
285+
277286
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_TYPE_CLASSIFICATION_H

test/Interop/Cxx/class/constructors-executable.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,11 @@ CxxConstructorTestSuite.test("implicit default ctor") {
6363
expectNil(instance3.ptr)
6464
}
6565

66+
CxxConstructorTestSuite.test("MoveConstructorWithOneParamWithDefaultArg") {
67+
let instance1 = MoveConstructorWithOneParameterWithDefaultArg(5)
68+
let instance2 = instance1
69+
let instance3 = MoveConstructorWithOneParameterWithDefaultArg(5)
70+
expectEqual(instance2.value, instance3.value + 1)
71+
}
72+
6673
runAllTests()

test/Interop/Cxx/class/copy-move-assignment-executable.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ CxxCopyMoveAssignTestSuite.test("NonTrivialCopyAssign") {
2525
expectEqual(0, instance.copyAssignCounter)
2626
takeValue(instance2)
2727
}
28-
// The number of construcors and destructors called for `NonTrivialCopyAssign` must be balanced.
28+
// The number of constructors and destructors called for `NonTrivialCopyAssign` must be balanced.
2929
expectEqual(0, InstanceBalanceCounter.getCounterValue())
3030
}
3131

@@ -37,7 +37,7 @@ CxxCopyMoveAssignTestSuite.test("NonTrivialMoveAssign") {
3737
// `operator=` isn't called.
3838
expectEqual(0, instance.moveAssignCounter)
3939
}
40-
// The number of construcors and destructors called for `NonTrivialCopyAssign` must be balanced.
40+
// The number of constructors and destructors called for `NonTrivialCopyAssign` must be balanced.
4141
expectEqual(0, InstanceBalanceCounter.getCounterValue())
4242
}
4343

@@ -51,7 +51,7 @@ CxxCopyMoveAssignTestSuite.test("NonTrivialCopyAndCopyMoveAssign") {
5151
expectEqual(0, instance.assignCounter)
5252
takeValue(instance2)
5353
}
54-
// The number of construcors and destructors called for `NonTrivialCopyAndCopyMoveAssign` must be balanced.
54+
// The number of constructors and destructors called for `NonTrivialCopyAndCopyMoveAssign` must be balanced.
5555
expectEqual(0, InstanceBalanceCounter.getCounterValue())
5656
}
5757

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@ func testAnnotated() {
2222
_ = v
2323
}
2424

25-
func testNonCopyable() {
25+
func testHasCopyTypeOperations() {
2626
let x = HasCopyConstructorWithDefaultArgs(5)
2727
let v = copy x // expected-error {{'copy' cannot be applied to noncopyable types}}
2828
_ = v
2929
}
3030

31+
func testHasMoveTypeOperations() {
32+
let x = HasMoveConstructorWithDefaultArgs(5) // expected-error {{cannot find 'HasMoveConstructorWithDefaultArgs' in scope}}
33+
}
34+
3135
test()
3236
testField()
3337
testAnnotated()
34-
testNonCopyable()
38+
testHasCopyTypeOperations()
39+
testHasMoveTypeOperations()

0 commit comments

Comments
 (0)