Skip to content

Commit c664a65

Browse files
committed
[cxx-interop] Use LookupQualifiedName in derived conformances
This is the first patch of a series of patches intended to shift lookup toward using Clang lookup, to avoid Swift lookup's dependency on eager Clang member importation. The lookupCxxTypeMember() helper function replaces the previous helper function, lookupNestedClangTypeDecl(). Functionally, the main difference is that lookupCxxTypeMember() uses clang::Sema::LookupQualifiedName(), which takes cares of issues like inheritance, ambiguity, etc. Meanwhile, lookupNestedClangTypeDecl() only used clang::DeclContext::lookup(), which is limited to the lexical context, and does not work with inheritance. The isIterator() function is renamed to hasIteratorConcept() to better reflect what it is checking, and still uses clang::DeclContext::lookup() to preserve the original behavior and to avoid requiring its callers to supply a clang::Sema instance.
1 parent c8ecb1f commit c664a65

File tree

3 files changed

+59
-40
lines changed

3 files changed

+59
-40
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
#include "swift/AST/ProtocolConformance.h"
1919
#include "swift/Basic/Assertions.h"
2020
#include "swift/ClangImporter/ClangImporterRequests.h"
21+
#include "clang/AST/CXXInheritance.h"
2122
#include "clang/Sema/DelayedDiagnostic.h"
23+
#include "clang/Sema/Lookup.h"
2224
#include "clang/Sema/Overload.h"
2325

2426
using namespace swift;
@@ -58,6 +60,29 @@ static CxxStdType identifyCxxStdTypeByName(StringRef name) {
5860
#undef CxxStdCase
5961
}
6062

63+
static const clang::TypeDecl *
64+
lookupCxxTypeMember(clang::Sema &Sema, const clang::CXXRecordDecl *Rec,
65+
StringRef name) {
66+
auto R = clang::LookupResult(Sema, &Sema.PP.getIdentifierTable().get(name),
67+
clang::SourceLocation(),
68+
clang::Sema::LookupMemberName);
69+
R.suppressDiagnostics();
70+
71+
auto *Ctx = static_cast<const clang::DeclContext *>(Rec);
72+
Sema.LookupQualifiedName(R, const_cast<clang::DeclContext *>(Ctx));
73+
74+
if (R.isSingleResult()) {
75+
if (auto *paths = R.getBasePaths();
76+
paths && R.getBasePaths()->front().Access != clang::AS_public)
77+
return nullptr;
78+
79+
for (auto *nd : R)
80+
if (auto *td = dyn_cast<clang::TypeDecl>(nd))
81+
return td;
82+
}
83+
return nullptr;
84+
}
85+
6186
/// Alternative to `NominalTypeDecl::lookupDirect`.
6287
/// This function does not attempt to load extensions of the nominal decl.
6388
static TinyPtrVector<ValueDecl *>
@@ -129,32 +154,6 @@ static FuncDecl *getInsertFunc(NominalTypeDecl *decl,
129154
return insert;
130155
}
131156

132-
static clang::TypeDecl *
133-
lookupNestedClangTypeDecl(const clang::CXXRecordDecl *clangDecl,
134-
StringRef name) {
135-
clang::IdentifierInfo *nestedDeclName =
136-
&clangDecl->getASTContext().Idents.get(name);
137-
auto nestedDecls = clangDecl->lookup(nestedDeclName);
138-
// If this is a templated typedef, Clang might have instantiated several
139-
// equivalent typedef decls. If they aren't equivalent, Clang has already
140-
// complained about this. Let's assume that they are equivalent. (see
141-
// filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp)
142-
if (nestedDecls.empty())
143-
return nullptr;
144-
auto nestedDecl = nestedDecls.front();
145-
return dyn_cast_or_null<clang::TypeDecl>(nestedDecl);
146-
}
147-
148-
static clang::TypeDecl *
149-
getIteratorCategoryDecl(const clang::CXXRecordDecl *clangDecl) {
150-
return lookupNestedClangTypeDecl(clangDecl, "iterator_category");
151-
}
152-
153-
static clang::TypeDecl *
154-
getIteratorConceptDecl(const clang::CXXRecordDecl *clangDecl) {
155-
return lookupNestedClangTypeDecl(clangDecl, "iterator_concept");
156-
}
157-
158157
static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id,
159158
function_ref<bool(ValueDecl *)> isValid) {
160159
// First look for operator declared as a member.
@@ -420,8 +419,13 @@ static bool synthesizeCXXOperator(ClangImporter::Implementation &impl,
420419
return true;
421420
}
422421

423-
bool swift::isIterator(const clang::CXXRecordDecl *clangDecl) {
424-
return getIteratorCategoryDecl(clangDecl);
422+
bool swift::hasIteratorCategory(const clang::CXXRecordDecl *clangDecl) {
423+
clang::IdentifierInfo *name =
424+
&clangDecl->getASTContext().Idents.get("iterator_category");
425+
auto members = clangDecl->lookup(name);
426+
if (members.empty() || !members.isSingleResult())
427+
return false;
428+
return isa<clang::TypeDecl>(members.front());
425429
}
426430

427431
ValueDecl *
@@ -453,20 +457,27 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,
453457
PrettyStackTraceDecl trace("trying to conform to UnsafeCxxInputIterator", decl);
454458
ASTContext &ctx = decl->getASTContext();
455459
clang::ASTContext &clangCtx = clangDecl->getASTContext();
460+
clang::Sema &clangSema = impl.getClangSema();
456461

457462
if (!ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator))
458463
return;
459464

460465
// We consider a type to be an input iterator if it defines an
461466
// `iterator_category` that inherits from `std::input_iterator_tag`, e.g.
462467
// `using iterator_category = std::input_iterator_tag`.
463-
auto iteratorCategory = getIteratorCategoryDecl(clangDecl);
464-
if (!iteratorCategory)
468+
//
469+
// FIXME: The second hasIteratorCategory() is more conservative than it should
470+
// be because it doesn't consider things like inheritance, but checking this
471+
// here maintains existing behavior and ensures consistency across
472+
// ClangImporter, where clang::Sema isn't always readily available.
473+
const auto *iteratorCategory =
474+
lookupCxxTypeMember(clangSema, clangDecl, "iterator_category");
475+
if (!iteratorCategory || hasIteratorCategory(clangDecl))
465476
return;
466477

467478
auto unwrapUnderlyingTypeDecl =
468-
[](clang::TypeDecl *typeDecl) -> clang::CXXRecordDecl * {
469-
clang::CXXRecordDecl *underlyingDecl = nullptr;
479+
[](const clang::TypeDecl *typeDecl) -> const clang::CXXRecordDecl * {
480+
const clang::CXXRecordDecl *underlyingDecl = nullptr;
470481
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(typeDecl)) {
471482
auto type = typedefDecl->getUnderlyingType();
472483
underlyingDecl = type->getAsCXXRecordDecl();
@@ -525,7 +536,8 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,
525536
// `iterator_concept`. It is not possible to detect a contiguous iterator
526537
// based on its `iterator_category`. The type might not have an
527538
// `iterator_concept` defined.
528-
if (auto iteratorConcept = getIteratorConceptDecl(clangDecl)) {
539+
if (const auto *iteratorConcept =
540+
lookupCxxTypeMember(clangSema, clangDecl, "iterator_concept")) {
529541
if (auto underlyingConceptDecl =
530542
unwrapUnderlyingTypeDecl(iteratorConcept)) {
531543
isContiguousIterator = isContiguousIteratorDecl(underlyingConceptDecl);
@@ -726,7 +738,7 @@ static void conformToCxxOptional(ClangImporter::Implementation &impl,
726738
// it isn't directly usable from Swift. Let's explicitly instantiate a
727739
// constructor with the wrapped value type, and then import it into Swift.
728740

729-
auto valueTypeDecl = lookupNestedClangTypeDecl(clangDecl, "value_type");
741+
auto valueTypeDecl = lookupCxxTypeMember(clangSema, clangDecl, "value_type");
730742
if (!valueTypeDecl)
731743
// `std::optional` without a value_type?!
732744
return;
@@ -1174,8 +1186,8 @@ static void conformToCxxSpan(ClangImporter::Implementation &impl,
11741186
if (!elementType || !sizeType)
11751187
return;
11761188

1177-
auto pointerTypeDecl = lookupNestedClangTypeDecl(clangDecl, "pointer");
1178-
auto countTypeDecl = lookupNestedClangTypeDecl(clangDecl, "size_type");
1189+
auto pointerTypeDecl = lookupCxxTypeMember(clangSema, clangDecl, "pointer");
1190+
auto countTypeDecl = lookupCxxTypeMember(clangSema, clangDecl, "size_type");
11791191

11801192
if (!pointerTypeDecl || !countTypeDecl)
11811193
return;

lib/ClangImporter/ClangDerivedConformances.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@
1818

1919
namespace swift {
2020

21-
bool isIterator(const clang::CXXRecordDecl *clangDecl);
21+
/// Whether a C++ record decl contains a type member named "iterator_category",
22+
/// a heuristic we use to determine whether that record type is an iterator.
23+
///
24+
/// This function returns true if there is exactly one public type member named
25+
/// "iterator_category", but does not look for inherited members. Note that, as
26+
/// a result of these limitations, it may return false even if that record type
27+
/// is usable as an iterator.
28+
bool hasIteratorCategory(const clang::CXXRecordDecl *clangDecl);
2229

2330
bool isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl);
2431

lib/ClangImporter/ClangImporter.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8376,7 +8376,7 @@ static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) {
83768376
hasUnsafeAPIAttr(cxxRecord))
83778377
return false;
83788378

8379-
if (hasIteratorAPIAttr(cxxRecord) || isIterator(cxxRecord))
8379+
if (hasIteratorAPIAttr(cxxRecord) || hasIteratorCategory(cxxRecord))
83808380
return true;
83818381

83828382
if (hasPointerInSubobjects(cxxRecord))
@@ -8497,7 +8497,7 @@ CxxRecordSemantics::evaluate(Evaluator &evaluator,
84978497
if (isSwiftClassType(cxxDecl))
84988498
return CxxRecordSemanticsKind::SwiftClassType;
84998499

8500-
if (hasIteratorAPIAttr(cxxDecl) || isIterator(cxxDecl)) {
8500+
if (hasIteratorAPIAttr(cxxDecl) || hasIteratorCategory(cxxDecl)) {
85018501
return CxxRecordSemanticsKind::Iterator;
85028502
}
85038503

@@ -8760,7 +8760,7 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
87608760
return true;
87618761

87628762
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
8763-
isIterator(cxxRecordReturnType))
8763+
hasIteratorCategory(cxxRecordReturnType))
87648764
return false;
87658765

87668766
// Mark this as safe to help our diganostics down the road.

0 commit comments

Comments
 (0)