Skip to content

Commit 55bd1ce

Browse files
committed
[cxx-interop] Use clang lookups for std::set conformance
Instead of looking up the *already imported* Swift members of std::set, this patch shifts the conformance synthesis logic to look up the equivalent members from Clang, and *then* imports them to Swift types to satisfy the CxxSet conformance. Doing so removes the logic's dependency on eagerly importing such members.
1 parent c5927a5 commit 55bd1ce

File tree

1 file changed

+121
-49
lines changed

1 file changed

+121
-49
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 121 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@
1313
#include "ClangDerivedConformances.h"
1414
#include "ImporterImpl.h"
1515
#include "swift/AST/ConformanceLookup.h"
16+
#include "swift/AST/LayoutConstraint.h"
1617
#include "swift/AST/ParameterList.h"
1718
#include "swift/AST/PrettyStackTrace.h"
1819
#include "swift/AST/ProtocolConformance.h"
1920
#include "swift/Basic/Assertions.h"
2021
#include "swift/ClangImporter/ClangImporterRequests.h"
22+
#include "clang/AST/ASTContext.h"
2123
#include "clang/AST/CXXInheritance.h"
24+
#include "clang/AST/Decl.h"
25+
#include "clang/AST/DeclCXX.h"
26+
#include "clang/AST/Type.h"
2227
#include "clang/Sema/DelayedDiagnostic.h"
2328
#include "clang/Sema/Lookup.h"
2429
#include "clang/Sema/Overload.h"
@@ -62,23 +67,22 @@ static CxxStdType identifyCxxStdTypeByName(StringRef name) {
6267

6368
static const clang::TypeDecl *
6469
lookupCxxTypeMember(clang::Sema &Sema, const clang::CXXRecordDecl *Rec,
65-
StringRef name) {
70+
StringRef name, bool mustBeComplete = false) {
6671
auto R = clang::LookupResult(Sema, &Sema.PP.getIdentifierTable().get(name),
6772
clang::SourceLocation(),
6873
clang::Sema::LookupMemberName);
6974
R.suppressDiagnostics();
70-
7175
auto *Ctx = static_cast<const clang::DeclContext *>(Rec);
7276
Sema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx));
7377

74-
if (R.isSingleResult()) {
78+
if (auto *td = R.getAsSingle<clang::TypeDecl>()) {
7579
if (auto *paths = R.getBasePaths();
76-
paths && R.getBasePaths()->front().Access != clang::AS_public)
80+
paths && paths->front().Access != clang::AS_public)
7781
return nullptr;
78-
79-
for (auto *nd : R)
80-
if (auto *td = dyn_cast<clang::TypeDecl>(nd))
81-
return td;
82+
if (mustBeComplete &&
83+
!Sema.isCompleteType({}, td->getASTContext().getTypeDeclType(td)))
84+
return nullptr;
85+
return td;
8286
}
8387
return nullptr;
8488
}
@@ -993,59 +997,127 @@ static void conformToCxxSet(ClangImporter::Implementation &impl,
993997
bool isUniqueSet) {
994998
PrettyStackTraceDecl trace("conforming to CxxSet", decl);
995999
ASTContext &ctx = decl->getASTContext();
1000+
auto &clangCtx = impl.getClangASTContext();
1001+
auto &clangSema = impl.getClangSema();
9961002

997-
auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
998-
decl, ctx.getIdentifier("value_type"));
999-
auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1000-
decl, ctx.getIdentifier("size_type"));
1001-
if (!valueType || !sizeType)
1002-
return;
1003-
1004-
auto insert = getInsertFunc(decl, valueType);
1005-
if (!insert)
1003+
// Look up the type members we need from Clang
1004+
//
1005+
// N.B. we don't actually need const_iterator for multiset, but it should be
1006+
// there. If it's not there for any reason, we should probably bail out.
1007+
1008+
auto *size_type = lookupCxxTypeMember(clangSema, clangDecl, "size_type",
1009+
/*mustBeComplete=*/true);
1010+
auto *value_type = lookupCxxTypeMember(clangSema, clangDecl, "value_type",
1011+
/*mustBeComplete=*/true);
1012+
auto *iterator = lookupCxxTypeMember(clangSema, clangDecl, "iterator",
1013+
/*mustBeComplete=*/true);
1014+
auto *const_iterator =
1015+
lookupCxxTypeMember(clangSema, clangDecl, "const_iterator",
1016+
/*mustBeComplete=*/true);
1017+
if (!size_type || !value_type || !iterator || !const_iterator)
10061018
return;
10071019

1008-
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
1009-
valueType->getUnderlyingType());
1010-
impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement,
1011-
valueType->getUnderlyingType());
1012-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
1013-
sizeType->getUnderlyingType());
1014-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"),
1015-
insert->getResultInterfaceType());
1016-
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet});
1020+
const clang::CXXMethodDecl *insert = nullptr;
1021+
{
1022+
// CxxSet requires the InsertionResult associated type, which is the return
1023+
// type of std::set (and co.)'s insert function. But there is no equivalent
1024+
// typedef in C++ we can use directly, so we need get it by converting the
1025+
// return type of the insert function.
1026+
//
1027+
// A wrinkle here is that std::set actually has multiple insert overloads.
1028+
// There are two overloads that could work for us:
1029+
//
1030+
// insert_return_type insert(const value_type &value);
1031+
// insert_return_type insert(value_type &&value);
1032+
//
1033+
// where insert_return_type is std::pair<iterator, bool> for std::set and
1034+
// std::unordered_set and just iterator for std::multiset.
1035+
//
1036+
// Look for the version with the single const-lref value_type parameter,
1037+
// since that's the one that maps to Swift's semantics most closely.
1038+
//
1039+
// NOTE: this code is a bit lengthy, and could be abstracted into a helper
1040+
// function, but at this time of writing, the only two times we need to look
1041+
// for a member is for std::set and std::map's insert methods. We keep this
1042+
// lookup routine inlined for now until the interface for a reasonably
1043+
// encapsulated helper function emerges.
1044+
1045+
auto R = clang::LookupResult(
1046+
clangSema, &clangSema.PP.getIdentifierTable().get("insert"),
1047+
clang::SourceLocation(), clang::Sema::LookupMemberName);
1048+
R.suppressDiagnostics();
1049+
auto *Ctx = static_cast<const clang::DeclContext *>(clangDecl);
1050+
clangSema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx));
1051+
switch (R.getResultKind()) {
1052+
case clang::LookupResultKind::Found:
1053+
case clang::LookupResultKind::FoundOverloaded:
1054+
break;
1055+
default:
1056+
return;
1057+
}
10171058

1018-
ProtocolDecl *cxxInputIteratorProto =
1019-
ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator);
1020-
if (!cxxInputIteratorProto)
1059+
for (auto *nd : R) {
1060+
if (auto *insertOverload = dyn_cast<clang::CXXMethodDecl>(nd)) {
1061+
if (insertOverload->param_size() != 1)
1062+
continue;
1063+
auto *paramTy = (*insertOverload->param_begin())
1064+
->getType()
1065+
->getAs<clang::ReferenceType>();
1066+
if (!paramTy)
1067+
continue;
1068+
if (paramTy->getPointeeType()->getCanonicalTypeUnqualified() !=
1069+
clangCtx.getTypeDeclType(value_type)->getCanonicalTypeUnqualified())
1070+
continue;
1071+
if (!paramTy->getPointeeType().isConstQualified())
1072+
continue;
1073+
insert = insertOverload; // Found the insert() we're looking for
1074+
break;
1075+
}
1076+
}
1077+
}
1078+
if (!insert)
10211079
return;
10221080

1023-
auto rawIteratorType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1024-
decl, ctx.getIdentifier("const_iterator"));
1025-
auto rawMutableIteratorType =
1026-
lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1027-
decl, ctx.getIdentifier("iterator"));
1028-
if (!rawIteratorType || !rawMutableIteratorType)
1081+
// We've looked up everything we need from Clang for the conformance.
1082+
// Now, use ClangImporter to convert import those types to Swift.
1083+
//
1084+
// NOTE: we're actually importing the typedefs and function members here,
1085+
// but not *adding* them as members to the Swift StructDecl---that is done
1086+
// elsewhere (and could be lazy too, though not at this time of writing).
1087+
// We are just using these imported Swift members for their type fields,
1088+
// because importDecl() needs fewer arguments than importTypeIgnoreIUO().
1089+
1090+
auto *Size = dyn_cast_or_null<TypeAliasDecl>(
1091+
impl.importDecl(size_type, impl.CurrentVersion));
1092+
auto *Value = dyn_cast_or_null<TypeAliasDecl>(
1093+
impl.importDecl(value_type, impl.CurrentVersion));
1094+
auto *RawMutableIterator = dyn_cast_or_null<TypeAliasDecl>(
1095+
impl.importDecl(iterator, impl.CurrentVersion));
1096+
auto *RawIterator = dyn_cast_or_null<TypeAliasDecl>(
1097+
impl.importDecl(const_iterator, impl.CurrentVersion));
1098+
auto *Insert =
1099+
dyn_cast_or_null<FuncDecl>(impl.importDecl(insert, impl.CurrentVersion));
1100+
if (!Size || !Value || !RawMutableIterator || !RawIterator || !Insert)
10291101
return;
10301102

1031-
auto rawIteratorTy = rawIteratorType->getUnderlyingType();
1032-
auto rawMutableIteratorTy = rawMutableIteratorType->getUnderlyingType();
1033-
1034-
if (!checkConformance(rawIteratorTy, cxxInputIteratorProto) ||
1035-
!checkConformance(rawMutableIteratorTy, cxxInputIteratorProto))
1036-
return;
1103+
// We have our Swift types, synthesize type aliases and conformances
10371104

1105+
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
1106+
Value->getUnderlyingType());
1107+
impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement,
1108+
Value->getUnderlyingType());
1109+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
1110+
Size->getUnderlyingType());
10381111
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"),
1039-
rawIteratorTy);
1112+
RawIterator->getUnderlyingType());
10401113
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"),
1041-
rawMutableIteratorTy);
1042-
1043-
// Synthesize conformance to CxxUniqueSet if the caller asked for it
1044-
// (if decl is std::set or std::unordered_set, but not std::multiset)
1045-
if (!isUniqueSet)
1046-
return;
1114+
RawMutableIterator->getUnderlyingType());
1115+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"),
1116+
Insert->getResultInterfaceType());
10471117

1048-
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet});
1118+
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet});
1119+
if (isUniqueSet)
1120+
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxUniqueSet});
10491121
}
10501122

10511123
static void conformToCxxPair(ClangImporter::Implementation &impl,

0 commit comments

Comments
 (0)