Skip to content

Commit 83a5968

Browse files
committed
[cxx-interop] Import STL types as copyable, conditionally
1 parent 2bf0702 commit 83a5968

11 files changed

+148
-13
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5195,7 +5195,7 @@ TinyPtrVector<ValueDecl *> CXXNamespaceMemberLookup::evaluate(
51955195
return result;
51965196
}
51975197

5198-
static const llvm::StringMap<std::vector<int>> STLConditionalEscapableParams{
5198+
static const llvm::StringMap<std::vector<int>> STLConditionalParams{
51995199
{"basic_string", {0}},
52005200
{"vector", {0}},
52015201
{"array", {0}},
@@ -5271,10 +5271,10 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator,
52715271
return CxxEscapability::Escapable;
52725272
auto injectedStlAnnotation =
52735273
recordDecl->isInStdNamespace()
5274-
? STLConditionalEscapableParams.find(recordDecl->getName())
5275-
: STLConditionalEscapableParams.end();
5274+
? STLConditionalParams.find(recordDecl->getName())
5275+
: STLConditionalParams.end();
52765276
bool hasInjectedSTLAnnotation =
5277-
injectedStlAnnotation != STLConditionalEscapableParams.end();
5277+
injectedStlAnnotation != STLConditionalParams.end();
52785278
auto conditionalParams = getConditionalEscapableAttrParams(recordDecl);
52795279
if (!conditionalParams.empty() || hasInjectedSTLAnnotation) {
52805280
auto specDecl = cast<clang::ClassTemplateSpecializationDecl>(recordDecl);
@@ -8227,6 +8227,7 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
82278227
CxxValueSemanticsDescriptor desc) const {
82288228

82298229
const auto *type = desc.type;
8230+
auto *importerImpl = desc.importerImpl;
82308231

82318232
auto desugared = type->getUnqualifiedDesugaredType();
82328233
const auto *recordType = desugared->getAs<clang::RecordType>();
@@ -8238,7 +8239,7 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
82388239
// When a reference type is copied, the pointer’s value is copied rather than
82398240
// the object’s storage. This means reference types can be imported as
82408241
// copyable to Swift, even when they are non-copyable in C++.
8241-
if (recordHasReferenceSemantics(recordDecl, desc.importerImpl))
8242+
if (recordHasReferenceSemantics(recordDecl, importerImpl))
82428243
return CxxValueSemanticsKind::Copyable;
82438244

82448245
if (recordDecl->isInStdNamespace()) {
@@ -8247,10 +8248,40 @@ CxxValueSemantics::evaluate(Evaluator &evaluator,
82478248
if (recordDecl->getIdentifier() &&
82488249
recordDecl->getName() == "_Optional_construct_base")
82498250
return CxxValueSemanticsKind::Copyable;
8251+
8252+
auto injectedStlAnnotation =
8253+
STLConditionalParams.find(recordDecl->getName());
8254+
8255+
if (injectedStlAnnotation != STLConditionalParams.end()) {
8256+
auto specDecl = cast<clang::ClassTemplateSpecializationDecl>(recordDecl);
8257+
auto &argList = specDecl->getTemplateArgs();
8258+
for (auto argToCheck : injectedStlAnnotation->second) {
8259+
auto arg = argList[argToCheck];
8260+
llvm::SmallVector<clang::TemplateArgument, 1> nonPackArgs;
8261+
if (arg.getKind() == clang::TemplateArgument::Pack) {
8262+
auto pack = arg.getPackAsArray();
8263+
nonPackArgs.assign(pack.begin(), pack.end());
8264+
} else
8265+
nonPackArgs.push_back(arg);
8266+
for (auto nonPackArg : nonPackArgs) {
8267+
8268+
auto argValueSemantics = evaluateOrDefault(
8269+
evaluator,
8270+
CxxValueSemantics(
8271+
{nonPackArg.getAsType()->getUnqualifiedDesugaredType(),
8272+
desc.importerImpl}),
8273+
{});
8274+
if (argValueSemantics != CxxValueSemanticsKind::Copyable)
8275+
return argValueSemantics;
8276+
}
8277+
}
8278+
8279+
return CxxValueSemanticsKind::Copyable;
8280+
}
82508281
}
82518282

82528283
const auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(recordDecl);
8253-
if (!cxxRecordDecl) {
8284+
if (!cxxRecordDecl || !cxxRecordDecl->isCompleteDefinition()) {
82548285
if (hasNonCopyableAttr(recordDecl))
82558286
return CxxValueSemanticsKind::MoveOnly;
82568287
return CxxValueSemanticsKind::Copyable;

test/Interop/Cxx/class/noncopyable-typechecker.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// RUN: split-file %s %t
3-
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify - -I %t/Inputs %t/test.swift
3+
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify -I %t/Inputs %t/test.swift
44
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -Xcc -std=c++20 -verify-additional-prefix cpp20- -D CPP20 -typecheck -verify -I %t/Inputs %t/test.swift
55

66
//--- Inputs/module.modulemap
@@ -45,17 +45,14 @@ using NonCopyableRequires = RequiresCopyableT<NonCopyable>;
4545
import Test
4646
import CxxStdlib
4747

48-
func takeCopyable<T: Copyable>(_ x: T) {}
49-
// expected-note@-1 {{'where T: Copyable' is implicit here}}
50-
// expected-cpp20-note@-2 {{'where T: Copyable' is implicit here}}
48+
func takeCopyable<T: Copyable>(_ x: T) {} // expected-note * {{'where T: Copyable' is implicit here}}
5149

5250
func userDefinedTypes() {
5351
let nCop = NonCopyable()
5452
takeCopyable(nCop) // expected-error {{global function 'takeCopyable' requires that 'NonCopyable' conform to 'Copyable'}}
5553

5654
let ownsT = OwnsNonCopyable()
5755
takeCopyable(ownsT) // no error, OwnsNonCopyable imported as Copyable
58-
5956
}
6057

6158
#if CPP20
@@ -64,4 +61,3 @@ func useOfRequires() {
6461
takeCopyable(nCop) // expected-cpp20-error {{global function 'takeCopyable' requires that 'NonCopyableRequires' (aka 'RequiresCopyableT<NonCopyable>') conform to 'Copyable'}}
6562
}
6663
#endif
67-

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,14 @@ inline UnorderedMap initUnorderedMap() { return {{1, 3}, {3, 3}, {2, 2}}; }
1717
inline Map initEmptyMap() { return {}; }
1818
inline UnorderedMap initEmptyUnorderedMap() { return {}; }
1919

20+
struct NonCopyable {
21+
NonCopyable() = default;
22+
NonCopyable(const NonCopyable &other) = delete;
23+
NonCopyable(NonCopyable &&other) = default;
24+
~NonCopyable() {}
25+
};
26+
27+
using MapNonCopyableKey = std::map<NonCopyable, int>;
28+
using MapNonCopyableValue = std::map<int, NonCopyable>;
29+
2030
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_MAP_H

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ inline SetOfCInt initSetOfCInt() { return {1, 5, 3}; }
1212
inline UnorderedSetOfCInt initUnorderedSetOfCInt() { return {2, 4, 6}; }
1313
inline MultisetOfCInt initMultisetOfCInt() { return {2, 2, 4, 6}; }
1414

15+
struct NonCopyable {
16+
NonCopyable() = default;
17+
NonCopyable(const NonCopyable &other) = delete;
18+
NonCopyable(NonCopyable &&other) = default;
19+
~NonCopyable() {}
20+
};
21+
22+
using SetOfNonCopyable = std::set<NonCopyable>;
23+
1524
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SET_H

test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <memory>
55
#include <string>
6+
#include <vector>
67

78
struct NonCopyable {
89
NonCopyable(int x) : x(x) {}
@@ -26,6 +27,13 @@ struct NonCopyableDerived: public NonCopyable {
2627
inline std::shared_ptr<NonCopyable> getNonCopyableSharedPtr() { return std::make_shared<NonCopyableDerived>(42); }
2728
inline std::unique_ptr<NonCopyable> getNonCopyableUniquePtr() { return std::make_unique<NonCopyableDerived>(42); }
2829

30+
inline std::vector<std::unique_ptr<NonCopyable>>
31+
getVectorNonCopyableUniquePtr() {
32+
std::vector<std::unique_ptr<NonCopyable>> vec;
33+
vec.emplace_back();
34+
return vec;
35+
}
36+
2937
std::unique_ptr<int> makeInt() {
3038
return std::make_unique<int>(42);
3139
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
22
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
33

4-
#include <vector>
54
#include <string>
5+
#include <vector>
66

77
using Vector = std::vector<int>;
88
using VectorOfString = std::vector<std::string>;
@@ -30,4 +30,14 @@ __attribute__((swift_attr("release:immortal"))) ImmortalRef {
3030
};
3131
using VectorOfImmortalRefPtr = std::vector<ImmortalRef *>;
3232

33+
struct NonCopyable {
34+
NonCopyable() = default;
35+
NonCopyable(const NonCopyable &other) = delete;
36+
NonCopyable(NonCopyable &&other) = default;
37+
~NonCopyable() {}
38+
};
39+
40+
using VectorOfNonCopyable = std::vector<NonCopyable>;
41+
using VectorOfPointer = std::vector<NonCopyable *>;
42+
3343
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
2+
3+
import StdMap
4+
import CxxStdlib
5+
6+
func takeCopyable<T: Copyable>(_ x: T) {}
7+
8+
let mapNonCopyableKey = MapNonCopyableKey()
9+
takeCopyable(mapNonCopyableKey)
10+
// CHECK: error: global function 'takeCopyable' requires that 'MapNonCopyableKey' {{.*}} conform to 'Copyable'
11+
// CHECK: note: 'where T: Copyable' is implicit here
12+
13+
let mapNonCopyableValue = MapNonCopyableValue()
14+
takeCopyable(mapNonCopyableValue)
15+
// CHECK: error: global function 'takeCopyable' requires that 'MapNonCopyableValue' {{.*}} conform to 'Copyable'
16+
// CHECK: note: 'where T: Copyable' is implicit here
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
2+
3+
import StdOptional
4+
import CxxStdlib
5+
6+
func takeCopyable<T: Copyable>(_ x: T) {}
7+
8+
let nonNilOptNonCopyable = getNonNilOptionalHasDeletedCopyCtor()
9+
takeCopyable(nonNilOptNonCopyable)
10+
// CHECK: error: global function 'takeCopyable' requires that 'StdOptionalHasDeletedCopyCtor' {{.*}} conform to 'Copyable'
11+
// CHECK: note: 'where T: Copyable' is implicit here
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
2+
3+
import StdSet
4+
import CxxStdlib
5+
6+
func takeCopyable<T: Copyable>(_ x: T) {}
7+
8+
let setNonCopyable = SetOfNonCopyable()
9+
takeCopyable(setNonCopyable)
10+
// CHECK: error: global function 'takeCopyable' requires that 'SetOfNonCopyable' {{.*}} conform to 'Copyable'
11+
// CHECK: note: 'where T: Copyable' is implicit here
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s
2+
3+
// UNSUPPORTED: OS=windows-msvc
4+
5+
import StdUniquePtr
6+
import CxxStdlib
7+
8+
func takeCopyable<T: Copyable>(_ x: T) {}
9+
10+
let vecUniquePtr = getVectorNonCopyableUniquePtr()
11+
takeCopyable(vecUniquePtr)
12+
// CHECK: error: global function 'takeCopyable' requires that 'std{{.*}}vector{{.*}}unique_ptr{{.*}}NonCopyable{{.*}}' conform to 'Copyable'

0 commit comments

Comments
 (0)