diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 6182b43e83a50..53106f030f619 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5195,7 +5195,7 @@ TinyPtrVector CXXNamespaceMemberLookup::evaluate( return result; } -static const llvm::StringMap> STLConditionalEscapableParams{ +static const llvm::StringMap> STLConditionalParams{ {"basic_string", {0}}, {"vector", {0}}, {"array", {0}}, @@ -5271,10 +5271,10 @@ ClangTypeEscapability::evaluate(Evaluator &evaluator, return CxxEscapability::Escapable; auto injectedStlAnnotation = recordDecl->isInStdNamespace() - ? STLConditionalEscapableParams.find(recordDecl->getName()) - : STLConditionalEscapableParams.end(); + ? STLConditionalParams.find(recordDecl->getName()) + : STLConditionalParams.end(); bool hasInjectedSTLAnnotation = - injectedStlAnnotation != STLConditionalEscapableParams.end(); + injectedStlAnnotation != STLConditionalParams.end(); auto conditionalParams = getConditionalEscapableAttrParams(recordDecl); if (!conditionalParams.empty() || hasInjectedSTLAnnotation) { auto specDecl = cast(recordDecl); @@ -8227,6 +8227,7 @@ CxxValueSemantics::evaluate(Evaluator &evaluator, CxxValueSemanticsDescriptor desc) const { const auto *type = desc.type; + auto *importerImpl = desc.importerImpl; auto desugared = type->getUnqualifiedDesugaredType(); const auto *recordType = desugared->getAs(); @@ -8238,7 +8239,7 @@ CxxValueSemantics::evaluate(Evaluator &evaluator, // When a reference type is copied, the pointer’s value is copied rather than // the object’s storage. This means reference types can be imported as // copyable to Swift, even when they are non-copyable in C++. - if (recordHasReferenceSemantics(recordDecl, desc.importerImpl)) + if (recordHasReferenceSemantics(recordDecl, importerImpl)) return CxxValueSemanticsKind::Copyable; if (recordDecl->isInStdNamespace()) { @@ -8247,10 +8248,40 @@ CxxValueSemantics::evaluate(Evaluator &evaluator, if (recordDecl->getIdentifier() && recordDecl->getName() == "_Optional_construct_base") return CxxValueSemanticsKind::Copyable; + + auto injectedStlAnnotation = + STLConditionalParams.find(recordDecl->getName()); + + if (injectedStlAnnotation != STLConditionalParams.end()) { + auto specDecl = cast(recordDecl); + auto &argList = specDecl->getTemplateArgs(); + for (auto argToCheck : injectedStlAnnotation->second) { + auto arg = argList[argToCheck]; + llvm::SmallVector nonPackArgs; + if (arg.getKind() == clang::TemplateArgument::Pack) { + auto pack = arg.getPackAsArray(); + nonPackArgs.assign(pack.begin(), pack.end()); + } else + nonPackArgs.push_back(arg); + for (auto nonPackArg : nonPackArgs) { + + auto argValueSemantics = evaluateOrDefault( + evaluator, + CxxValueSemantics( + {nonPackArg.getAsType()->getUnqualifiedDesugaredType(), + desc.importerImpl}), + {}); + if (argValueSemantics != CxxValueSemanticsKind::Copyable) + return argValueSemantics; + } + } + + return CxxValueSemanticsKind::Copyable; + } } const auto cxxRecordDecl = dyn_cast(recordDecl); - if (!cxxRecordDecl) { + if (!cxxRecordDecl || !cxxRecordDecl->isCompleteDefinition()) { if (hasNonCopyableAttr(recordDecl)) return CxxValueSemanticsKind::MoveOnly; return CxxValueSemanticsKind::Copyable; diff --git a/test/Interop/Cxx/class/noncopyable-typechecker.swift b/test/Interop/Cxx/class/noncopyable-typechecker.swift index 535c8cbfc58ce..81e6707204286 100644 --- a/test/Interop/Cxx/class/noncopyable-typechecker.swift +++ b/test/Interop/Cxx/class/noncopyable-typechecker.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: split-file %s %t -// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify - -I %t/Inputs %t/test.swift +// RUN: %target-swift-frontend -cxx-interoperability-mode=default -typecheck -verify -I %t/Inputs %t/test.swift // 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 //--- Inputs/module.modulemap @@ -45,9 +45,7 @@ using NonCopyableRequires = RequiresCopyableT; import Test import CxxStdlib -func takeCopyable(_ x: T) {} -// expected-note@-1 {{'where T: Copyable' is implicit here}} -// expected-cpp20-note@-2 {{'where T: Copyable' is implicit here}} +func takeCopyable(_ x: T) {} // expected-note * {{'where T: Copyable' is implicit here}} func userDefinedTypes() { let nCop = NonCopyable() @@ -55,7 +53,6 @@ func userDefinedTypes() { let ownsT = OwnsNonCopyable() takeCopyable(ownsT) // no error, OwnsNonCopyable imported as Copyable - } #if CPP20 @@ -64,4 +61,3 @@ func useOfRequires() { takeCopyable(nCop) // expected-cpp20-error {{global function 'takeCopyable' requires that 'NonCopyableRequires' (aka 'RequiresCopyableT') conform to 'Copyable'}} } #endif - diff --git a/test/Interop/Cxx/stdlib/Inputs/std-map.h b/test/Interop/Cxx/stdlib/Inputs/std-map.h index e9275232ef1b5..d28739869947d 100644 --- a/test/Interop/Cxx/stdlib/Inputs/std-map.h +++ b/test/Interop/Cxx/stdlib/Inputs/std-map.h @@ -17,4 +17,14 @@ inline UnorderedMap initUnorderedMap() { return {{1, 3}, {3, 3}, {2, 2}}; } inline Map initEmptyMap() { return {}; } inline UnorderedMap initEmptyUnorderedMap() { return {}; } +struct NonCopyable { + NonCopyable() = default; + NonCopyable(const NonCopyable &other) = delete; + NonCopyable(NonCopyable &&other) = default; + ~NonCopyable() {} +}; + +using MapNonCopyableKey = std::map; +using MapNonCopyableValue = std::map; + #endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_MAP_H diff --git a/test/Interop/Cxx/stdlib/Inputs/std-set.h b/test/Interop/Cxx/stdlib/Inputs/std-set.h index e48ed78dafb0a..b66e10f3fba06 100644 --- a/test/Interop/Cxx/stdlib/Inputs/std-set.h +++ b/test/Interop/Cxx/stdlib/Inputs/std-set.h @@ -12,4 +12,13 @@ inline SetOfCInt initSetOfCInt() { return {1, 5, 3}; } inline UnorderedSetOfCInt initUnorderedSetOfCInt() { return {2, 4, 6}; } inline MultisetOfCInt initMultisetOfCInt() { return {2, 2, 4, 6}; } +struct NonCopyable { + NonCopyable() = default; + NonCopyable(const NonCopyable &other) = delete; + NonCopyable(NonCopyable &&other) = default; + ~NonCopyable() {} +}; + +using SetOfNonCopyable = std::set; + #endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_SET_H diff --git a/test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h b/test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h index f805ddb955cc3..e6cc6bf0d5ca0 100644 --- a/test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h +++ b/test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h @@ -3,6 +3,7 @@ #include #include +#include struct NonCopyable { NonCopyable(int x) : x(x) {} @@ -26,6 +27,13 @@ struct NonCopyableDerived: public NonCopyable { inline std::shared_ptr getNonCopyableSharedPtr() { return std::make_shared(42); } inline std::unique_ptr getNonCopyableUniquePtr() { return std::make_unique(42); } +inline std::vector> +getVectorNonCopyableUniquePtr() { + std::vector> vec; + vec.emplace_back(); + return vec; +} + std::unique_ptr makeInt() { return std::make_unique(42); } diff --git a/test/Interop/Cxx/stdlib/Inputs/std-vector.h b/test/Interop/Cxx/stdlib/Inputs/std-vector.h index a9a5f0ead920b..4d70fec7c78ad 100644 --- a/test/Interop/Cxx/stdlib/Inputs/std-vector.h +++ b/test/Interop/Cxx/stdlib/Inputs/std-vector.h @@ -1,8 +1,8 @@ #ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H #define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H -#include #include +#include using Vector = std::vector; using VectorOfString = std::vector; @@ -30,4 +30,14 @@ __attribute__((swift_attr("release:immortal"))) ImmortalRef { }; using VectorOfImmortalRefPtr = std::vector; +struct NonCopyable { + NonCopyable() = default; + NonCopyable(const NonCopyable &other) = delete; + NonCopyable(NonCopyable &&other) = default; + ~NonCopyable() {} +}; + +using VectorOfNonCopyable = std::vector; +using VectorOfPointer = std::vector; + #endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H diff --git a/test/Interop/Cxx/stdlib/use-std-map-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-map-typechecker.swift new file mode 100644 index 0000000000000..1ae4e73676583 --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-map-typechecker.swift @@ -0,0 +1,16 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s + +import StdMap +import CxxStdlib + +func takeCopyable(_ x: T) {} + +let mapNonCopyableKey = MapNonCopyableKey() +takeCopyable(mapNonCopyableKey) +// CHECK: error: global function 'takeCopyable' requires that 'MapNonCopyableKey' {{.*}} conform to 'Copyable' +// CHECK: note: 'where T: Copyable' is implicit here + +let mapNonCopyableValue = MapNonCopyableValue() +takeCopyable(mapNonCopyableValue) +// CHECK: error: global function 'takeCopyable' requires that 'MapNonCopyableValue' {{.*}} conform to 'Copyable' +// CHECK: note: 'where T: Copyable' is implicit here diff --git a/test/Interop/Cxx/stdlib/use-std-optional-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-optional-typechecker.swift new file mode 100644 index 0000000000000..70b1f3b98670e --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-optional-typechecker.swift @@ -0,0 +1,11 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s + +import StdOptional +import CxxStdlib + +func takeCopyable(_ x: T) {} + +let nonNilOptNonCopyable = getNonNilOptionalHasDeletedCopyCtor() +takeCopyable(nonNilOptNonCopyable) +// CHECK: error: global function 'takeCopyable' requires that 'StdOptionalHasDeletedCopyCtor' {{.*}} conform to 'Copyable' +// CHECK: note: 'where T: Copyable' is implicit here diff --git a/test/Interop/Cxx/stdlib/use-std-set-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-set-typechecker.swift new file mode 100644 index 0000000000000..e71a3e60717b4 --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-set-typechecker.swift @@ -0,0 +1,11 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s + +import StdSet +import CxxStdlib + +func takeCopyable(_ x: T) {} + +let setNonCopyable = SetOfNonCopyable() +takeCopyable(setNonCopyable) +// CHECK: error: global function 'takeCopyable' requires that 'SetOfNonCopyable' {{.*}} conform to 'Copyable' +// CHECK: note: 'where T: Copyable' is implicit here diff --git a/test/Interop/Cxx/stdlib/use-std-unique-ptr-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-unique-ptr-typechecker.swift new file mode 100644 index 0000000000000..636ba05653689 --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-unique-ptr-typechecker.swift @@ -0,0 +1,12 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s + +// UNSUPPORTED: OS=windows-msvc + +import StdUniquePtr +import CxxStdlib + +func takeCopyable(_ x: T) {} + +let vecUniquePtr = getVectorNonCopyableUniquePtr() +takeCopyable(vecUniquePtr) +// CHECK: error: global function 'takeCopyable' requires that 'std{{.*}}vector{{.*}}unique_ptr{{.*}}NonCopyable{{.*}}' conform to 'Copyable' diff --git a/test/Interop/Cxx/stdlib/use-std-vector-typechecker.swift b/test/Interop/Cxx/stdlib/use-std-vector-typechecker.swift new file mode 100644 index 0000000000000..47c3d649fabd6 --- /dev/null +++ b/test/Interop/Cxx/stdlib/use-std-vector-typechecker.swift @@ -0,0 +1,21 @@ +// RUN: not %target-swift-frontend %s -typecheck -I %S/Inputs -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1 | %FileCheck %s + +import StdVector +import CxxStdlib + +func takeCopyable(_ x: T) {} +func takeCxxVector(_ v: V) {} + +let vecNC = VectorOfNonCopyable() +takeCopyable(vecNC) +// CHECK: error: global function 'takeCopyable' requires that 'VectorOfNonCopyable' {{.*}} conform to 'Copyable' +// CHECK: note: 'where T: Copyable' is implicit here + +takeCxxVector(vecNC) +// CHECK: error: global function 'takeCxxVector' requires that 'VectorOfNonCopyable' {{.*}} conform to 'CxxVector' +// CHECK: note: where 'V' = 'VectorOfNonCopyable' {{.*}} + +let vecPointer = VectorOfPointer() +takeCopyable(vecPointer) +takeCxxVector(vecPointer) +// CHECK-NOT: error