Skip to content

Commit b6c1ebb

Browse files
committed
[cxx-interop] Prevent usage in Swift of C++ move constructor with default args
1 parent 7092c0b commit b6c1ebb

11 files changed

+75
-14
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ NOTE(record_not_automatically_importable, none, "record '%0' is not "
174174
"does this type have reference "
175175
"semantics?",
176176
(StringRef, StringRef))
177+
NOTE(record_unsupported_default_args, none,
178+
"copy constructors and move constructors with more than one parameter are "
179+
"unavailable in Swift",
180+
())
177181

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

include/swift/ClangImporter/ClangImporterRequests.h

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

lib/ClangImporter/ClangImporter.cpp

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

7998+
// TODO i could just assume that if numParams > 1, they have default args
79987999
auto lastParam = ctor->parameters().back();
79998000
return lastParam->hasDefaultArg();
80008001
}
@@ -8031,7 +8032,7 @@ static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
80318032
return false;
80328033

80338034
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
8034-
return ctor->isMoveConstructor();
8035+
return ctor->isMoveConstructor() && !hasNonFirstDefaultArg(ctor);
80358036
});
80368037
}
80378038

@@ -8050,6 +8051,14 @@ static bool hasCustomCopyOrMoveConstructor(const clang::CXXRecordDecl *decl) {
80508051
decl->hasUserDeclaredMoveConstructor();
80518052
}
80528053

8054+
static bool
8055+
hasConstructorWithUnsupportedDefaultArgs(const clang::CXXRecordDecl *decl) {
8056+
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
8057+
return (ctor->isCopyConstructor() || ctor->isMoveConstructor()) &&
8058+
hasNonFirstDefaultArg(ctor);
8059+
});
8060+
}
8061+
80538062
static bool isSwiftClassType(const clang::CXXRecordDecl *decl) {
80548063
// Swift type must be annotated with external_source_symbol attribute.
80558064
auto essAttr = decl->getAttr<clang::ExternalSourceSymbolAttr>();
@@ -8105,6 +8114,9 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
81058114
"import_iterator", decl->getNameAsString());
81068115
}
81078116

8117+
if (hasConstructorWithUnsupportedDefaultArgs(cxxDecl))
8118+
return CxxRecordSemanticsKind::UnavailableConstructors;
8119+
81088120
return CxxRecordSemanticsKind::MissingLifetimeOperation;
81098121
}
81108122

lib/ClangImporter/ImportDecl.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2974,15 +2974,23 @@ namespace {
29742974
auto semanticsKind = evaluateOrDefault(
29752975
Impl.SwiftContext.evaluator,
29762976
CxxRecordSemantics({decl, Impl.SwiftContext, &Impl}), {});
2977-
if (semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation &&
2977+
if ((semanticsKind == CxxRecordSemanticsKind::MissingLifetimeOperation ||
2978+
semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) &&
29782979
// Let un-specialized class templates through. We'll sort out their
2979-
// members once they're instranciated.
2980+
// members once they're instantiated.
29802981
!Impl.importSymbolicCXXDecls) {
2982+
2983+
if (semanticsKind == CxxRecordSemanticsKind::UnavailableConstructors) {
2984+
Impl.addImportDiagnostic(
2985+
decl, Diagnostic(diag::record_unsupported_default_args),
2986+
decl->getLocation());
2987+
}
2988+
29812989
Impl.addImportDiagnostic(
29822990
decl,
29832991
Diagnostic(diag::record_not_automatically_importable,
29842992
Impl.SwiftContext.AllocateCopy(decl->getNameAsString()),
2985-
"does not have a copy constructor or destructor"),
2993+
"it must have a copy/move constructor and a destructor"),
29862994
decl->getLocation());
29872995
return nullptr;
29882996
}

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
@@ -108,4 +108,18 @@ struct HasPtrAuthMember {
108108
};
109109
#endif
110110

111+
struct MoveConstructorWithOneParameterWithDefaultArg {
112+
int value;
113+
114+
MoveConstructorWithOneParameterWithDefaultArg(int value) : value(value) {}
115+
116+
MoveConstructorWithOneParameterWithDefaultArg(
117+
const MoveConstructorWithOneParameterWithDefaultArg &) = delete;
118+
119+
MoveConstructorWithOneParameterWithDefaultArg(
120+
MoveConstructorWithOneParameterWithDefaultArg &&other =
121+
MoveConstructorWithOneParameterWithDefaultArg{1})
122+
: value(other.value + 1) {}
123+
};
124+
111125
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_CONSTRUCTORS_H

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/invalid-class-errors.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ struct Nested {
2727

2828
import Test
2929

30-
// CHECK: note: record 'A' is not automatically available: does not have a copy constructor or destructor; does this type have reference semantics?
30+
// CHECK: note: record 'A' is not automatically available: it must have a copy/move constructor and a destructor; does this type have reference semantics?
3131
// CHECK: struct A {
3232
// CHECK: ^
3333
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)
3434
public func test(x: A) { }
35-
// CHECK: note: record 'B' is not automatically available: does not have a copy constructor or destructor; does this type have reference semantics?
35+
// CHECK: note: record 'B' is not automatically available: it must have a copy/move constructor and a destructor; does this type have reference semantics?
3636
// CHECK: struct {{.*}}B {
3737
// CHECK: ^
3838
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)
3939
public func test(x: B) { }
40-
// CHECK: note: record 'Nested' is not automatically available: does not have a copy constructor or destructor; does this type have reference semantics?
40+
// CHECK: note: record 'Nested' is not automatically available: it must have a copy/move constructor and a destructor; does this type have reference semantics?
4141
// CHECK: struct Nested {
4242
// CHECK: ^
4343
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)

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)