2424using namespace swift ;
2525using namespace swift ::importer;
2626
27+ // / Known C++ stdlib types, for which we can assume conformance to the standard
28+ // / (e.g., std::map has template parameters for key and value types, and has
29+ // / members like key_type, size_type, and operator[]).
30+ enum class CxxStdType {
31+ uncategorized,
32+ optional,
33+ set,
34+ unordered_set,
35+ multiset,
36+ pair,
37+ map,
38+ unordered_map,
39+ multimap,
40+ vector,
41+ span,
42+ };
43+
44+ static CxxStdType identifyCxxStdTypeByName (StringRef name) {
45+ #define CaseStd (name ) Case(#name, CxxStdType::name)
46+ return llvm::StringSwitch<CxxStdType>(name)
47+ .CaseStd (optional)
48+ .CaseStd (set)
49+ .CaseStd (unordered_set)
50+ .CaseStd (multiset)
51+ .CaseStd (pair)
52+ .CaseStd (map)
53+ .CaseStd (unordered_map)
54+ .CaseStd (multimap)
55+ .CaseStd (vector)
56+ .CaseStd (span)
57+ .Default (CxxStdType::uncategorized);
58+ #undef CxxStdCase
59+ }
60+
2761// / Alternative to `NominalTypeDecl::lookupDirect`.
2862// / This function does not attempt to load extensions of the nominal decl.
2963static TinyPtrVector<ValueDecl *>
@@ -95,16 +129,6 @@ static FuncDecl *getInsertFunc(NominalTypeDecl *decl,
95129 return insert;
96130}
97131
98- static bool isStdDecl (const clang::CXXRecordDecl *clangDecl,
99- llvm::ArrayRef<StringRef> names) {
100- if (!clangDecl->isInStdNamespace ())
101- return false ;
102- if (!clangDecl->getIdentifier ())
103- return false ;
104- StringRef name = clangDecl->getName ();
105- return llvm::is_contained (names, name);
106- }
107-
108132static clang::TypeDecl *
109133lookupNestedClangTypeDecl (const clang::CXXRecordDecl *clangDecl,
110134 StringRef name) {
@@ -422,13 +446,11 @@ swift::importer::getImportedMemberOperator(const DeclBaseName &name,
422446 return nullptr ;
423447}
424448
425- void swift::conformToCxxIteratorIfNeeded (
426- ClangImporter::Implementation &impl, NominalTypeDecl *decl,
427- const clang::CXXRecordDecl *clangDecl) {
428- PrettyStackTraceDecl trace (" conforming to UnsafeCxxInputIterator" , decl);
429-
430- assert (decl);
431- assert (clangDecl);
449+ static void
450+ conformToCxxIteratorIfNeeded (ClangImporter::Implementation &impl,
451+ NominalTypeDecl *decl,
452+ const clang::CXXRecordDecl *clangDecl) {
453+ PrettyStackTraceDecl trace (" trying to conform to UnsafeCxxInputIterator" , decl);
432454 ASTContext &ctx = decl->getASTContext ();
433455 clang::ASTContext &clangCtx = clangDecl->getASTContext ();
434456
@@ -641,13 +663,11 @@ void swift::conformToCxxIteratorIfNeeded(
641663 }
642664}
643665
644- void swift::conformToCxxConvertibleToBoolIfNeeded (
645- ClangImporter::Implementation &impl, swift::NominalTypeDecl *decl,
646- const clang::CXXRecordDecl *clangDecl) {
647- PrettyStackTraceDecl trace (" conforming to CxxConvertibleToBool" , decl);
648-
649- assert (decl);
650- assert (clangDecl);
666+ static void
667+ conformToCxxConvertibleToBoolIfNeeded (ClangImporter::Implementation &impl,
668+ swift::NominalTypeDecl *decl,
669+ const clang::CXXRecordDecl *clangDecl) {
670+ PrettyStackTraceDecl trace (" trying to conform to CxxConvertibleToBool" , decl);
651671 ASTContext &ctx = decl->getASTContext ();
652672
653673 auto conversionId = ctx.getIdentifier (" __convertToBool" );
@@ -674,20 +694,14 @@ void swift::conformToCxxConvertibleToBoolIfNeeded(
674694 {KnownProtocolKind::CxxConvertibleToBool});
675695}
676696
677- void swift::conformToCxxOptionalIfNeeded (
678- ClangImporter::Implementation &impl, NominalTypeDecl *decl,
679- const clang::CXXRecordDecl *clangDecl) {
697+ static void conformToCxxOptional (ClangImporter::Implementation &impl,
698+ NominalTypeDecl *decl,
699+ const clang::CXXRecordDecl *clangDecl) {
680700 PrettyStackTraceDecl trace (" conforming to CxxOptional" , decl);
681-
682- assert (decl);
683- assert (clangDecl);
684701 ASTContext &ctx = decl->getASTContext ();
685702 clang::ASTContext &clangCtx = impl.getClangASTContext ();
686703 clang::Sema &clangSema = impl.getClangSema ();
687704
688- if (!isStdDecl (clangDecl, {" optional" }))
689- return ;
690-
691705 ProtocolDecl *cxxOptionalProto =
692706 ctx.getProtocol (KnownProtocolKind::CxxOptional);
693707 // If the Cxx module is missing, or does not include one of the necessary
@@ -766,13 +780,11 @@ void swift::conformToCxxOptionalIfNeeded(
766780 decl->addMember (importedConstructor);
767781}
768782
769- void swift::conformToCxxSequenceIfNeeded (
770- ClangImporter::Implementation &impl, NominalTypeDecl *decl,
771- const clang::CXXRecordDecl *clangDecl) {
772- PrettyStackTraceDecl trace (" conforming to CxxSequence" , decl);
773-
774- assert (decl);
775- assert (clangDecl);
783+ static void
784+ conformToCxxSequenceIfNeeded (ClangImporter::Implementation &impl,
785+ NominalTypeDecl *decl,
786+ const clang::CXXRecordDecl *clangDecl) {
787+ PrettyStackTraceDecl trace (" trying to conform to CxxSequence" , decl);
776788 ASTContext &ctx = decl->getASTContext ();
777789
778790 ProtocolDecl *cxxIteratorProto =
@@ -940,41 +952,31 @@ void swift::conformToCxxSequenceIfNeeded(
940952 }
941953}
942954
943- static bool isStdSetType (const clang::CXXRecordDecl *clangDecl) {
944- return isStdDecl (clangDecl, {" set" , " unordered_set" , " multiset" });
945- }
946-
947- static bool isStdMapType (const clang::CXXRecordDecl *clangDecl) {
948- return isStdDecl (clangDecl, {" map" , " unordered_map" , " multimap" });
949- }
950-
951955bool swift::isUnsafeStdMethod (const clang::CXXMethodDecl *methodDecl) {
952- auto parentDecl =
956+ auto * parentDecl =
953957 dyn_cast<clang::CXXRecordDecl>(methodDecl->getDeclContext ());
954- if (!parentDecl)
955- return false ;
956- if (!isStdSetType (parentDecl) && !isStdMapType (parentDecl))
958+ if (!parentDecl || !parentDecl->isInStdNamespace () ||
959+ !parentDecl->getIdentifier ())
957960 return false ;
958- if (methodDecl->getDeclName ().isIdentifier () &&
959- methodDecl->getName () == " insert" )
960- return true ;
961+
962+ if (methodDecl->getIdentifier () && methodDecl->getName () == " insert" ) {
963+ // Types for which the insert method is considered unsafe,
964+ // due to potential iterator invalidation.
965+ return llvm::StringSwitch<bool >(parentDecl->getName ())
966+ .Cases ({" set" , " unordered_set" , " multiset" }, true )
967+ .Cases ({" map" , " unordered_map" , " multimap" }, true )
968+ .Default (false );
969+ }
961970 return false ;
962971}
963972
964- void swift::conformToCxxSetIfNeeded (ClangImporter::Implementation &impl,
965- NominalTypeDecl *decl,
966- const clang::CXXRecordDecl *clangDecl) {
973+ static void conformToCxxSet (ClangImporter::Implementation &impl,
974+ NominalTypeDecl *decl,
975+ const clang::CXXRecordDecl *clangDecl,
976+ bool isUniqueSet) {
967977 PrettyStackTraceDecl trace (" conforming to CxxSet" , decl);
968-
969- assert (decl);
970- assert (clangDecl);
971978 ASTContext &ctx = decl->getASTContext ();
972979
973- // Only auto-conform types from the C++ standard library. Custom user types
974- // might have a similar interface but different semantics.
975- if (!isStdSetType (clangDecl))
976- return ;
977-
978980 auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
979981 decl, ctx.getIdentifier (" value_type" ));
980982 auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -1021,28 +1023,20 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl,
10211023 impl.addSynthesizedTypealias (decl, ctx.getIdentifier (" RawMutableIterator" ),
10221024 rawMutableIteratorTy);
10231025
1024- // If this isn't a std::multiset, try to also synthesize the conformance to
1025- // CxxUniqueSet.
1026- if (!isStdDecl (clangDecl, { " set " , " unordered_set " }) )
1026+ // Synthesize conformance to CxxUniqueSet if the caller asked for it
1027+ // (if decl is std::set or std::unordered_set, but not std::multiset)
1028+ if (!isUniqueSet )
10271029 return ;
10281030
10291031 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxUniqueSet});
10301032}
10311033
1032- void swift::conformToCxxPairIfNeeded (ClangImporter::Implementation &impl,
1033- NominalTypeDecl *decl,
1034- const clang::CXXRecordDecl *clangDecl) {
1034+ static void conformToCxxPair (ClangImporter::Implementation &impl,
1035+ NominalTypeDecl *decl,
1036+ const clang::CXXRecordDecl *clangDecl) {
10351037 PrettyStackTraceDecl trace (" conforming to CxxPair" , decl);
1036-
1037- assert (decl);
1038- assert (clangDecl);
10391038 ASTContext &ctx = decl->getASTContext ();
10401039
1041- // Only auto-conform types from the C++ standard library. Custom user types
1042- // might have a similar interface but different semantics.
1043- if (!isStdDecl (clangDecl, {" pair" }))
1044- return ;
1045-
10461040 auto firstType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
10471041 decl, ctx.getIdentifier (" first_type" ));
10481042 auto secondType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -1057,20 +1051,12 @@ void swift::conformToCxxPairIfNeeded(ClangImporter::Implementation &impl,
10571051 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxPair});
10581052}
10591053
1060- void swift::conformToCxxDictionaryIfNeeded (
1061- ClangImporter::Implementation &impl, NominalTypeDecl *decl,
1062- const clang::CXXRecordDecl *clangDecl) {
1054+ static void conformToCxxDictionary (ClangImporter::Implementation &impl,
1055+ NominalTypeDecl *decl,
1056+ const clang::CXXRecordDecl *clangDecl) {
10631057 PrettyStackTraceDecl trace (" conforming to CxxDictionary" , decl);
1064-
1065- assert (decl);
1066- assert (clangDecl);
10671058 ASTContext &ctx = decl->getASTContext ();
10681059
1069- // Only auto-conform types from the C++ standard library. Custom user types
1070- // might have a similar interface but different semantics.
1071- if (!isStdMapType (clangDecl))
1072- return ;
1073-
10741060 auto keyType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
10751061 decl, ctx.getIdentifier (" key_type" ));
10761062 auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -1135,20 +1121,12 @@ void swift::conformToCxxDictionaryIfNeeded(
11351121 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxDictionary});
11361122}
11371123
1138- void swift::conformToCxxVectorIfNeeded (ClangImporter::Implementation &impl,
1139- NominalTypeDecl *decl,
1140- const clang::CXXRecordDecl *clangDecl) {
1124+ static void conformToCxxVector (ClangImporter::Implementation &impl,
1125+ NominalTypeDecl *decl,
1126+ const clang::CXXRecordDecl *clangDecl) {
11411127 PrettyStackTraceDecl trace (" conforming to CxxVector" , decl);
1142-
1143- assert (decl);
1144- assert (clangDecl);
11451128 ASTContext &ctx = decl->getASTContext ();
11461129
1147- // Only auto-conform types from the C++ standard library. Custom user types
1148- // might have a similar interface but different semantics.
1149- if (!isStdDecl (clangDecl, {" vector" }))
1150- return ;
1151-
11521130 auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
11531131 decl, ctx.getIdentifier (" value_type" ));
11541132 auto iterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -1180,22 +1158,14 @@ void swift::conformToCxxVectorIfNeeded(ClangImporter::Implementation &impl,
11801158 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxVector});
11811159}
11821160
1183- void swift::conformToCxxSpanIfNeeded (ClangImporter::Implementation &impl,
1184- NominalTypeDecl *decl,
1185- const clang::CXXRecordDecl *clangDecl) {
1161+ static void conformToCxxSpan (ClangImporter::Implementation &impl,
1162+ NominalTypeDecl *decl,
1163+ const clang::CXXRecordDecl *clangDecl) {
11861164 PrettyStackTraceDecl trace (" conforming to CxxSpan" , decl);
1187-
1188- assert (decl);
1189- assert (clangDecl);
11901165 ASTContext &ctx = decl->getASTContext ();
11911166 clang::ASTContext &clangCtx = impl.getClangASTContext ();
11921167 clang::Sema &clangSema = impl.getClangSema ();
11931168
1194- // Only auto-conform types from the C++ standard library. Custom user types
1195- // might have a similar interface but different semantics.
1196- if (!isStdDecl (clangDecl, {" span" }))
1197- return ;
1198-
11991169 auto elementType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
12001170 decl, ctx.getIdentifier (" element_type" ));
12011171 auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -1276,3 +1246,60 @@ void swift::conformToCxxSpanIfNeeded(ClangImporter::Implementation &impl,
12761246 impl.addSynthesizedProtocolAttrs (decl, {KnownProtocolKind::CxxMutableSpan});
12771247 }
12781248}
1249+
1250+ void swift::deriveAutomaticCxxConformances (
1251+ ClangImporter::Implementation &Impl, NominalTypeDecl *result,
1252+ const clang::CXXRecordDecl *clangDecl) {
1253+
1254+ ASSERT (result && clangDecl && " this should not be called with nullptrs" );
1255+
1256+ // Skip synthesizing conformances if the associated Clang node is from
1257+ // a module that doesn't require cplusplus, to prevent us from accidentally
1258+ // pulling in Cxx/CxxStdlib modules when a client is importing a C library.
1259+ //
1260+ // We will still attempt to synthesize to account for scenarios where the
1261+ // module specification is missing altogether.
1262+ if (auto *clangModule = Impl.getClangOwningModule (result->getClangNode ());
1263+ clangModule && !requiresCPlusPlus (clangModule))
1264+ return ;
1265+
1266+ // Automatic conformances: these may be applied to any type that fits the
1267+ // requirements.
1268+ conformToCxxIteratorIfNeeded (Impl, result, clangDecl);
1269+ conformToCxxSequenceIfNeeded (Impl, result, clangDecl);
1270+ conformToCxxConvertibleToBoolIfNeeded (Impl, result, clangDecl);
1271+
1272+ // CxxStdlib conformances: these should only apply to known C++ stdlib types,
1273+ // which we determine by name and membership in the std namespace.
1274+ if (!clangDecl->getIdentifier () || !clangDecl->isInStdNamespace ())
1275+ return ;
1276+
1277+ auto ty = identifyCxxStdTypeByName (clangDecl->getName ());
1278+ switch (ty) {
1279+ case CxxStdType::uncategorized:
1280+ return ;
1281+ case CxxStdType::optional:
1282+ conformToCxxOptional (Impl, result, clangDecl);
1283+ return ;
1284+ case CxxStdType::set:
1285+ case CxxStdType::unordered_set:
1286+ case CxxStdType::multiset:
1287+ conformToCxxSet (Impl, result, clangDecl,
1288+ /* isUniqueSet=*/ ty != CxxStdType::multiset);
1289+ return ;
1290+ case CxxStdType::pair:
1291+ conformToCxxPair (Impl, result, clangDecl);
1292+ return ;
1293+ case CxxStdType::map:
1294+ case CxxStdType::unordered_map:
1295+ case CxxStdType::multimap:
1296+ conformToCxxDictionary (Impl, result, clangDecl);
1297+ return ;
1298+ case CxxStdType::vector:
1299+ conformToCxxVector (Impl, result, clangDecl);
1300+ return ;
1301+ case CxxStdType::span:
1302+ conformToCxxSpan (Impl, result, clangDecl);
1303+ return ;
1304+ }
1305+ }
0 commit comments