Skip to content

Commit c8ecb1f

Browse files
committed
[cxx-interop] [NFC] Refactor ClangDerivedConformances
This refactoring restructures the code in a couple of ways: - move all the "conformToXXXIfNeeded()" functions into a single function and remove them from the ClangDerivedConformances.h interface - unify the check for isInStdNamespace() for the C++ stdlib types - unify the name comparisons into a single llvm::StringSwitch - unify redundant nullptr assert()s, and upgrade them to ASSERT() - move the known stdlib type name-checking logic out of the "conformToCxxSTDLIBTYPEIfNeeded()" functions, and rename them to drop the "IfNeeded" suffix. This patch also introduces a local CxxStdType enum class that enumerates (and thus documents) all of the known (and thus supported) types from the C++ stdlib types. The ultimate goal is to organize the code such that by the time we call "conformToCxxSTDLIBTYPE()" we are already sure that the type is (or at least claims to be, based on its name and DeclContext) the C++ stdlib type we think it is, rather than interleaving such checks with the conformance synthesis logic. The main observable difference should be the logging: now, we will no longer have PrettyStackTraceDecls logging for C++ stdlib types like "conforming to CxxVector" unless we are already certain we are looking at a relevant type, e.g., std::vector. Otherwise, the functional behavior of this code should be the same as before.
1 parent 0ab6a71 commit c8ecb1f

File tree

3 files changed

+148
-188
lines changed

3 files changed

+148
-188
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)