-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Conform to UnsafeCxxContiguousIterator
based on iterator_concept
nested type
#77049
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,18 +125,12 @@ lookupNestedClangTypeDecl(const clang::CXXRecordDecl *clangDecl, | |
|
||
static clang::TypeDecl * | ||
getIteratorCategoryDecl(const clang::CXXRecordDecl *clangDecl) { | ||
clang::IdentifierInfo *iteratorCategoryDeclName = | ||
&clangDecl->getASTContext().Idents.get("iterator_category"); | ||
auto iteratorCategories = clangDecl->lookup(iteratorCategoryDeclName); | ||
// If this is a templated typedef, Clang might have instantiated several | ||
// equivalent typedef decls. If they aren't equivalent, Clang has already | ||
// complained about this. Let's assume that they are equivalent. (see | ||
// filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp) | ||
if (iteratorCategories.empty()) | ||
return nullptr; | ||
auto iteratorCategory = iteratorCategories.front(); | ||
return lookupNestedClangTypeDecl(clangDecl, "iterator_category"); | ||
} | ||
|
||
return dyn_cast_or_null<clang::TypeDecl>(iteratorCategory); | ||
static clang::TypeDecl * | ||
getIteratorConceptDecl(const clang::CXXRecordDecl *clangDecl) { | ||
return lookupNestedClangTypeDecl(clangDecl, "iterator_concept"); | ||
} | ||
|
||
static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id, | ||
|
@@ -435,55 +429,54 @@ void swift::conformToCxxIteratorIfNeeded( | |
if (!iteratorCategory) | ||
return; | ||
|
||
auto unwrapUnderlyingTypeDecl = | ||
[](clang::TypeDecl *typeDecl) -> clang::CXXRecordDecl * { | ||
clang::CXXRecordDecl *underlyingDecl = nullptr; | ||
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(typeDecl)) { | ||
auto type = typedefDecl->getUnderlyingType(); | ||
underlyingDecl = type->getAsCXXRecordDecl(); | ||
} else { | ||
underlyingDecl = dyn_cast<clang::CXXRecordDecl>(typeDecl); | ||
} | ||
if (underlyingDecl) { | ||
underlyingDecl = underlyingDecl->getDefinition(); | ||
} | ||
return underlyingDecl; | ||
}; | ||
|
||
// If `iterator_category` is a typedef or a using-decl, retrieve the | ||
// underlying struct decl. | ||
clang::CXXRecordDecl *underlyingCategoryDecl = nullptr; | ||
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(iteratorCategory)) { | ||
auto type = typedefDecl->getUnderlyingType(); | ||
underlyingCategoryDecl = type->getAsCXXRecordDecl(); | ||
} else { | ||
underlyingCategoryDecl = dyn_cast<clang::CXXRecordDecl>(iteratorCategory); | ||
} | ||
if (underlyingCategoryDecl) { | ||
underlyingCategoryDecl = underlyingCategoryDecl->getDefinition(); | ||
} | ||
|
||
auto underlyingCategoryDecl = unwrapUnderlyingTypeDecl(iteratorCategory); | ||
if (!underlyingCategoryDecl) | ||
return; | ||
|
||
auto isIteratorCategoryDecl = [&](const clang::CXXRecordDecl *base, | ||
StringRef tag) { | ||
auto isIteratorTagDecl = [&](const clang::CXXRecordDecl *base, | ||
StringRef tag) { | ||
return base->isInStdNamespace() && base->getIdentifier() && | ||
base->getName() == tag; | ||
}; | ||
auto isInputIteratorDecl = [&](const clang::CXXRecordDecl *base) { | ||
return isIteratorCategoryDecl(base, "input_iterator_tag"); | ||
return isIteratorTagDecl(base, "input_iterator_tag"); | ||
}; | ||
auto isRandomAccessIteratorDecl = [&](const clang::CXXRecordDecl *base) { | ||
return isIteratorCategoryDecl(base, "random_access_iterator_tag"); | ||
return isIteratorTagDecl(base, "random_access_iterator_tag"); | ||
}; | ||
auto isContiguousIteratorDecl = [&](const clang::CXXRecordDecl *base) { | ||
return isIteratorCategoryDecl(base, "contiguous_iterator_tag"); // C++20 | ||
return isIteratorTagDecl(base, "contiguous_iterator_tag"); // C++20 | ||
}; | ||
|
||
// Traverse all transitive bases of `underlyingDecl` to check if | ||
// it inherits from `std::input_iterator_tag`. | ||
bool isInputIterator = isInputIteratorDecl(underlyingCategoryDecl); | ||
bool isRandomAccessIterator = | ||
isRandomAccessIteratorDecl(underlyingCategoryDecl); | ||
bool isContiguousIterator = isContiguousIteratorDecl(underlyingCategoryDecl); | ||
underlyingCategoryDecl->forallBases([&](const clang::CXXRecordDecl *base) { | ||
if (isInputIteratorDecl(base)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not shortcut the search if we only found an input iterator but we do when we found a random access one. Is this intended? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeap, this is intended. If we found an |
||
isInputIterator = true; | ||
} | ||
if (isRandomAccessIteratorDecl(base)) { | ||
isRandomAccessIterator = true; | ||
isInputIterator = true; | ||
} | ||
if (isContiguousIteratorDecl(base)) { | ||
isContiguousIterator = true; | ||
isRandomAccessIterator = true; | ||
isInputIterator = true; | ||
return false; | ||
} | ||
return true; | ||
|
@@ -492,6 +485,27 @@ void swift::conformToCxxIteratorIfNeeded( | |
if (!isInputIterator) | ||
return; | ||
|
||
bool isContiguousIterator = false; | ||
// In C++20, `std::contiguous_iterator_tag` is specified as a type called | ||
// `iterator_concept`. It is not possible to detect a contiguous iterator | ||
// based on its `iterator_category`. The type might not have an | ||
// `iterator_concept` defined. | ||
if (auto iteratorConcept = getIteratorConceptDecl(clangDecl)) { | ||
if (auto underlyingConceptDecl = | ||
unwrapUnderlyingTypeDecl(iteratorConcept)) { | ||
isContiguousIterator = isContiguousIteratorDecl(underlyingConceptDecl); | ||
if (!isContiguousIterator) | ||
underlyingConceptDecl->forallBases( | ||
[&](const clang::CXXRecordDecl *base) { | ||
if (isContiguousIteratorDecl(base)) { | ||
isContiguousIterator = true; | ||
return false; | ||
} | ||
return true; | ||
}); | ||
} | ||
} | ||
|
||
// Check if present: `var pointee: Pointee { get }` | ||
auto pointeeId = ctx.getIdentifier("pointee"); | ||
auto pointee = lookupDirectSingleWithoutExtensions<VarDecl>(decl, pointeeId); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have multiple layers of
TypedefNameDecl
s? Would that be a problem?E.g.:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Let's make sure this works. I'll add a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this already works fine, I'll add a test in a separate PR.