Skip to content

Commit b636336

Browse files
committed
[cxx-interop] Use clang lookups for std::map conformance
Instead of looking up the *imported* Swift type aliases, this patch shifts the conformance to look up these typedefs from Clang, and *then* imports them to Swift types to satisfy CxxVector conformance. Doing so removes the conformance's dependency on eagerly importing such typedefs. There is one remaining dependency on eager imports, which is the unavailability patching we do for the subscript operator. This patch also drops a conformance check that the iterators conform to the our Cxx iterator protocols. It shouldn't be necessary, because we are looking at std::map, which we assume comes from a conforming stdlib.
1 parent b570fce commit b636336

File tree

1 file changed

+88
-44
lines changed

1 file changed

+88
-44
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,69 +1153,113 @@ static void conformToCxxDictionary(ClangImporter::Implementation &impl,
11531153
const clang::CXXRecordDecl *clangDecl) {
11541154
PrettyStackTraceDecl trace("conforming to CxxDictionary", decl);
11551155
ASTContext &ctx = decl->getASTContext();
1156+
clang::ASTContext &clangCtx = impl.getClangASTContext();
1157+
clang::Sema &clangSema = impl.getClangSema();
11561158

1157-
auto keyType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1158-
decl, ctx.getIdentifier("key_type"));
1159-
auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1160-
decl, ctx.getIdentifier("mapped_type"));
1161-
auto iterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1162-
decl, ctx.getIdentifier("const_iterator"));
1163-
auto mutableIterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1164-
decl, ctx.getIdentifier("iterator"));
1165-
auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1166-
decl, ctx.getIdentifier("size_type"));
1167-
auto keyValuePairType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1168-
decl, ctx.getIdentifier("value_type"));
1169-
if (!keyType || !valueType || !iterType || !mutableIterType || !sizeType ||
1170-
!keyValuePairType)
1171-
return;
1159+
#define lookup_type(member) \
1160+
auto *member = lookupCxxTypeMember(clangSema, clangDecl, #member, \
1161+
/*mustBeComplete=*/true); \
1162+
if (!member) \
1163+
return
1164+
1165+
lookup_type(key_type);
1166+
lookup_type(mapped_type);
1167+
lookup_type(value_type);
1168+
lookup_type(size_type);
1169+
lookup_type(iterator);
1170+
lookup_type(const_iterator);
1171+
#undef lookup_type
1172+
1173+
const clang::CXXMethodDecl *insert = nullptr;
1174+
{
1175+
// CxxDictionary requires the InsertionResult associated type, which is the
1176+
// return type of std::map (and co.)'s insert function. But there is no
1177+
// equivalent typedef in C++ we can use directly, so we need get it by
1178+
// converting the return type of this overload of the insert function:
1179+
//
1180+
// insert_return_type insert(const value_type &value);
1181+
//
1182+
// See also: extended monologuing in conformToCxxSet().
1183+
auto R = clang::LookupResult(
1184+
clangSema, &clangSema.PP.getIdentifierTable().get("insert"),
1185+
clang::SourceLocation(), clang::Sema::LookupMemberName);
1186+
R.suppressDiagnostics();
1187+
auto *Ctx = static_cast<const clang::DeclContext *>(clangDecl);
1188+
clangSema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx));
1189+
switch (R.getResultKind()) {
1190+
case clang::LookupResultKind::Found:
1191+
case clang::LookupResultKind::FoundOverloaded:
1192+
break;
1193+
default:
1194+
return;
1195+
}
11721196

1173-
auto insert = getInsertFunc(decl, keyValuePairType);
1197+
for (auto *nd : R) {
1198+
if (auto *insertOverload = dyn_cast<clang::CXXMethodDecl>(nd)) {
1199+
if (insertOverload->param_size() != 1)
1200+
continue;
1201+
auto *paramTy = (*insertOverload->param_begin())
1202+
->getType()
1203+
->getAs<clang::ReferenceType>();
1204+
if (!paramTy)
1205+
continue;
1206+
if (paramTy->getPointeeType()->getCanonicalTypeUnqualified() !=
1207+
clangCtx.getTypeDeclType(value_type)->getCanonicalTypeUnqualified())
1208+
continue;
1209+
if (!paramTy->getPointeeType().isConstQualified())
1210+
continue;
1211+
insert = insertOverload; // Found the insert() we're looking for
1212+
break;
1213+
}
1214+
}
1215+
}
11741216
if (!insert)
11751217
return;
11761218

1177-
ProtocolDecl *cxxInputIteratorProto =
1178-
ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator);
1179-
ProtocolDecl *cxxMutableInputIteratorProto =
1180-
ctx.getProtocol(KnownProtocolKind::UnsafeCxxMutableInputIterator);
1181-
if (!cxxInputIteratorProto || !cxxMutableInputIteratorProto)
1182-
return;
1219+
#define importTypeAlias(to, from) \
1220+
auto *to = dyn_cast_or_null<TypeAliasDecl>( \
1221+
impl.importDecl(from, impl.CurrentVersion)); \
1222+
if (!to) \
1223+
return
11831224

1184-
auto rawIteratorTy = iterType->getUnderlyingType();
1185-
auto rawMutableIteratorTy = mutableIterType->getUnderlyingType();
1225+
importTypeAlias(Size, size_type);
1226+
importTypeAlias(Key, key_type);
1227+
importTypeAlias(Value, mapped_type);
1228+
importTypeAlias(Element, value_type);
1229+
importTypeAlias(RawIterator, const_iterator);
1230+
importTypeAlias(RawMutableIterator, iterator);
1231+
#undef importTypeAlias
11861232

1187-
// Check if RawIterator conforms to UnsafeCxxInputIterator.
1188-
if (!checkConformance(rawIteratorTy, cxxInputIteratorProto))
1233+
auto *Insert =
1234+
dyn_cast_or_null<FuncDecl>(impl.importDecl(insert, impl.CurrentVersion));
1235+
if (!Insert)
11891236
return;
11901237

1191-
// Check if RawMutableIterator conforms to UnsafeCxxMutableInputIterator.
1192-
if (!checkConformance(rawMutableIteratorTy, cxxMutableInputIteratorProto))
1193-
return;
1238+
impl.addSynthesizedTypealias(decl, ctx.Id_Key, Key->getUnderlyingType());
1239+
impl.addSynthesizedTypealias(decl, ctx.Id_Value, Value->getUnderlyingType());
1240+
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
1241+
Element->getUnderlyingType());
1242+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"),
1243+
RawIterator->getUnderlyingType());
1244+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"),
1245+
RawMutableIterator->getUnderlyingType());
1246+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
1247+
Size->getUnderlyingType());
1248+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"),
1249+
Insert->getResultInterfaceType());
1250+
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxDictionary});
11941251

11951252
// Make the original subscript that returns a non-optional value unavailable.
11961253
// CxxDictionary adds another subscript that returns an optional value,
11971254
// similarly to Swift.Dictionary.
1255+
//
1256+
// NOTE: this relies on the SubscriptDecl member being imported eagerly.
11981257
for (auto member : decl->getCurrentMembersWithoutLoading()) {
11991258
if (auto subscript = dyn_cast<SubscriptDecl>(member)) {
12001259
impl.markUnavailable(subscript,
12011260
"use subscript with optional return value");
12021261
}
12031262
}
1204-
1205-
impl.addSynthesizedTypealias(decl, ctx.Id_Key, keyType->getUnderlyingType());
1206-
impl.addSynthesizedTypealias(decl, ctx.Id_Value,
1207-
valueType->getUnderlyingType());
1208-
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
1209-
keyValuePairType->getUnderlyingType());
1210-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"),
1211-
rawIteratorTy);
1212-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"),
1213-
rawMutableIteratorTy);
1214-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
1215-
sizeType->getUnderlyingType());
1216-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"),
1217-
insert->getResultInterfaceType());
1218-
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxDictionary});
12191263
}
12201264

12211265
static void conformToCxxVector(ClangImporter::Implementation &impl,

0 commit comments

Comments
 (0)