Skip to content

Commit 7b6b576

Browse files
committed
[cxx-interop] Avoid trying to instantiate copy constructor of std::optional<NonCopyable>
In libc++, `std::optional` inherits from several mixin base types. Some of those base types do not declare a deleted copy constructor for non-copyable value types, which works fine because clients are not supposed to use those base types directly from C++. Swift, however, imports all of the transitive base types when importing a C++ type. As part of this, ClangImporter will attempt to instantiate e.g. `std::__optional_copy_assign_base<NonCopyable>`, and will fail with a hard error while doing so. rdar://152718041 (cherry picked from commit 124055a)
1 parent 04ed960 commit 7b6b576

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2982,8 +2982,17 @@ namespace {
29822982
decl->getIdentifier() &&
29832983
(decl->getName() == "tzdb" || decl->getName() == "time_zone_link" ||
29842984
decl->getName() == "__compressed_pair" ||
2985+
decl->getName() == "__optional_copy_assign_base" || // libc++
2986+
decl->getName() == "__optional_move_assign_base" || // libc++
29852987
decl->getName() == "time_zone"))
29862988
return nullptr;
2989+
// Bail if this is one of the base types of std::optional. Those types are
2990+
// mixins that are not designed to be used directly.
2991+
if (decl->getDeclContext()->isNamespace() && decl->isInStdNamespace() &&
2992+
decl->getIdentifier() &&
2993+
(decl->getName() == "_Optional_payload_base" ||
2994+
decl->getName() == "_Optional_payload"))
2995+
return nullptr;
29872996

29882997
auto &clangSema = Impl.getClangSema();
29892998
// Make Clang define any implicit constructors it may need (copy,

test/Interop/Cxx/stdlib/Inputs/std-optional.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,24 @@
77
using StdOptionalInt = std::optional<int>;
88
using StdOptionalString = std::optional<std::string>;
99

10+
struct HasDeletedCopyCtor {
11+
int value;
12+
HasDeletedCopyCtor(int value) : value(value) {}
13+
HasDeletedCopyCtor(const HasDeletedCopyCtor &other) = delete;
14+
HasDeletedCopyCtor(HasDeletedCopyCtor &&other) = default;
15+
};
16+
using StdOptionalHasDeletedCopyCtor = std::optional<HasDeletedCopyCtor>;
17+
1018
inline StdOptionalInt getNonNilOptional() { return {123}; }
1119

1220
inline StdOptionalInt getNilOptional() { return {std::nullopt}; }
1321

22+
inline StdOptionalHasDeletedCopyCtor getNonNilOptionalHasDeletedCopyCtor() {
23+
return StdOptionalHasDeletedCopyCtor(HasDeletedCopyCtor(654));
24+
}
25+
1426
inline bool takesOptionalInt(std::optional<int> arg) { return (bool)arg; }
1527
inline bool takesOptionalString(std::optional<std::string> arg) { return (bool)arg; }
28+
inline bool takesOptionalHasDeletedCopyCtor(std::optional<HasDeletedCopyCtor> arg) { return (bool)arg; }
1629

1730
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_OPTIONAL_H

test/Interop/Cxx/stdlib/use-std-optional.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ StdOptionalTestSuite.test("pointee") {
2121
modifiedOpt.pointee = 777
2222
expectEqual(777, modifiedOpt.pointee)
2323
#endif
24+
25+
let nonNilOptNonCopyable = getNonNilOptionalHasDeletedCopyCtor()
26+
expectEqual(654, nonNilOptNonCopyable.pointee.value)
2427
}
2528

2629
StdOptionalTestSuite.test("std::optional => Swift.Optional") {

0 commit comments

Comments
 (0)