|
18 | 18 | #include "swift/AST/ProtocolConformance.h" |
19 | 19 | #include "swift/Basic/Assertions.h" |
20 | 20 | #include "swift/ClangImporter/ClangImporterRequests.h" |
| 21 | +#include "clang/AST/CXXInheritance.h" |
21 | 22 | #include "clang/Sema/DelayedDiagnostic.h" |
| 23 | +#include "clang/Sema/Lookup.h" |
22 | 24 | #include "clang/Sema/Overload.h" |
23 | 25 |
|
24 | 26 | using namespace swift; |
@@ -58,6 +60,29 @@ static CxxStdType identifyCxxStdTypeByName(StringRef name) { |
58 | 60 | #undef CxxStdCase |
59 | 61 | } |
60 | 62 |
|
| 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 | + |
61 | 86 | /// Alternative to `NominalTypeDecl::lookupDirect`. |
62 | 87 | /// This function does not attempt to load extensions of the nominal decl. |
63 | 88 | static TinyPtrVector<ValueDecl *> |
@@ -129,32 +154,6 @@ static FuncDecl *getInsertFunc(NominalTypeDecl *decl, |
129 | 154 | return insert; |
130 | 155 | } |
131 | 156 |
|
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 | | - |
158 | 157 | static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id, |
159 | 158 | function_ref<bool(ValueDecl *)> isValid) { |
160 | 159 | // First look for operator declared as a member. |
@@ -420,8 +419,13 @@ static bool synthesizeCXXOperator(ClangImporter::Implementation &impl, |
420 | 419 | return true; |
421 | 420 | } |
422 | 421 |
|
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()); |
425 | 429 | } |
426 | 430 |
|
427 | 431 | ValueDecl * |
@@ -453,20 +457,27 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, |
453 | 457 | PrettyStackTraceDecl trace("trying to conform to UnsafeCxxInputIterator", decl); |
454 | 458 | ASTContext &ctx = decl->getASTContext(); |
455 | 459 | clang::ASTContext &clangCtx = clangDecl->getASTContext(); |
| 460 | + clang::Sema &clangSema = impl.getClangSema(); |
456 | 461 |
|
457 | 462 | if (!ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator)) |
458 | 463 | return; |
459 | 464 |
|
460 | 465 | // We consider a type to be an input iterator if it defines an |
461 | 466 | // `iterator_category` that inherits from `std::input_iterator_tag`, e.g. |
462 | 467 | // `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)) |
465 | 476 | return; |
466 | 477 |
|
467 | 478 | 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; |
470 | 481 | if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(typeDecl)) { |
471 | 482 | auto type = typedefDecl->getUnderlyingType(); |
472 | 483 | underlyingDecl = type->getAsCXXRecordDecl(); |
@@ -525,7 +536,8 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, |
525 | 536 | // `iterator_concept`. It is not possible to detect a contiguous iterator |
526 | 537 | // based on its `iterator_category`. The type might not have an |
527 | 538 | // `iterator_concept` defined. |
528 | | - if (auto iteratorConcept = getIteratorConceptDecl(clangDecl)) { |
| 539 | + if (const auto *iteratorConcept = |
| 540 | + lookupCxxTypeMember(clangSema, clangDecl, "iterator_concept")) { |
529 | 541 | if (auto underlyingConceptDecl = |
530 | 542 | unwrapUnderlyingTypeDecl(iteratorConcept)) { |
531 | 543 | isContiguousIterator = isContiguousIteratorDecl(underlyingConceptDecl); |
@@ -726,7 +738,7 @@ static void conformToCxxOptional(ClangImporter::Implementation &impl, |
726 | 738 | // it isn't directly usable from Swift. Let's explicitly instantiate a |
727 | 739 | // constructor with the wrapped value type, and then import it into Swift. |
728 | 740 |
|
729 | | - auto valueTypeDecl = lookupNestedClangTypeDecl(clangDecl, "value_type"); |
| 741 | + auto valueTypeDecl = lookupCxxTypeMember(clangSema, clangDecl, "value_type"); |
730 | 742 | if (!valueTypeDecl) |
731 | 743 | // `std::optional` without a value_type?! |
732 | 744 | return; |
@@ -1174,8 +1186,8 @@ static void conformToCxxSpan(ClangImporter::Implementation &impl, |
1174 | 1186 | if (!elementType || !sizeType) |
1175 | 1187 | return; |
1176 | 1188 |
|
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"); |
1179 | 1191 |
|
1180 | 1192 | if (!pointerTypeDecl || !countTypeDecl) |
1181 | 1193 | return; |
|
0 commit comments