Skip to content

Commit babf2dd

Browse files
authored
Merge pull request #85932 from j-hui/unify-cxx-derived-conformances
[cxx-interop] [NFC] Refactor ClangDerivedConformances
2 parents e68bcb8 + c8ecb1f commit babf2dd

File tree

4 files changed

+150
-193
lines changed

4 files changed

+150
-193
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 140 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,40 @@
2424
using namespace swift;
2525
using 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.
2963
static 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-
108132
static clang::TypeDecl *
109133
lookupNestedClangTypeDecl(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-
951955
bool 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

Comments
 (0)