Skip to content

Commit 04d055a

Browse files
authored
Merge pull request #77049 from swiftlang/egorzhdan/cxx-contiguous-fix
[cxx-interop] Conform to `UnsafeCxxContiguousIterator` based on `iterator_concept` nested type
2 parents c31ed73 + 34f6cd3 commit 04d055a

File tree

3 files changed

+117
-36
lines changed

3 files changed

+117
-36
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,12 @@ lookupNestedClangTypeDecl(const clang::CXXRecordDecl *clangDecl,
125125

126126
static clang::TypeDecl *
127127
getIteratorCategoryDecl(const clang::CXXRecordDecl *clangDecl) {
128-
clang::IdentifierInfo *iteratorCategoryDeclName =
129-
&clangDecl->getASTContext().Idents.get("iterator_category");
130-
auto iteratorCategories = clangDecl->lookup(iteratorCategoryDeclName);
131-
// If this is a templated typedef, Clang might have instantiated several
132-
// equivalent typedef decls. If they aren't equivalent, Clang has already
133-
// complained about this. Let's assume that they are equivalent. (see
134-
// filterNonConflictingPreviousTypedefDecls in clang/Sema/SemaDecl.cpp)
135-
if (iteratorCategories.empty())
136-
return nullptr;
137-
auto iteratorCategory = iteratorCategories.front();
128+
return lookupNestedClangTypeDecl(clangDecl, "iterator_category");
129+
}
138130

139-
return dyn_cast_or_null<clang::TypeDecl>(iteratorCategory);
131+
static clang::TypeDecl *
132+
getIteratorConceptDecl(const clang::CXXRecordDecl *clangDecl) {
133+
return lookupNestedClangTypeDecl(clangDecl, "iterator_concept");
140134
}
141135

142136
static ValueDecl *lookupOperator(NominalTypeDecl *decl, Identifier id,
@@ -435,55 +429,54 @@ void swift::conformToCxxIteratorIfNeeded(
435429
if (!iteratorCategory)
436430
return;
437431

432+
auto unwrapUnderlyingTypeDecl =
433+
[](clang::TypeDecl *typeDecl) -> clang::CXXRecordDecl * {
434+
clang::CXXRecordDecl *underlyingDecl = nullptr;
435+
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(typeDecl)) {
436+
auto type = typedefDecl->getUnderlyingType();
437+
underlyingDecl = type->getAsCXXRecordDecl();
438+
} else {
439+
underlyingDecl = dyn_cast<clang::CXXRecordDecl>(typeDecl);
440+
}
441+
if (underlyingDecl) {
442+
underlyingDecl = underlyingDecl->getDefinition();
443+
}
444+
return underlyingDecl;
445+
};
446+
438447
// If `iterator_category` is a typedef or a using-decl, retrieve the
439448
// underlying struct decl.
440-
clang::CXXRecordDecl *underlyingCategoryDecl = nullptr;
441-
if (auto typedefDecl = dyn_cast<clang::TypedefNameDecl>(iteratorCategory)) {
442-
auto type = typedefDecl->getUnderlyingType();
443-
underlyingCategoryDecl = type->getAsCXXRecordDecl();
444-
} else {
445-
underlyingCategoryDecl = dyn_cast<clang::CXXRecordDecl>(iteratorCategory);
446-
}
447-
if (underlyingCategoryDecl) {
448-
underlyingCategoryDecl = underlyingCategoryDecl->getDefinition();
449-
}
450-
449+
auto underlyingCategoryDecl = unwrapUnderlyingTypeDecl(iteratorCategory);
451450
if (!underlyingCategoryDecl)
452451
return;
453452

454-
auto isIteratorCategoryDecl = [&](const clang::CXXRecordDecl *base,
455-
StringRef tag) {
453+
auto isIteratorTagDecl = [&](const clang::CXXRecordDecl *base,
454+
StringRef tag) {
456455
return base->isInStdNamespace() && base->getIdentifier() &&
457456
base->getName() == tag;
458457
};
459458
auto isInputIteratorDecl = [&](const clang::CXXRecordDecl *base) {
460-
return isIteratorCategoryDecl(base, "input_iterator_tag");
459+
return isIteratorTagDecl(base, "input_iterator_tag");
461460
};
462461
auto isRandomAccessIteratorDecl = [&](const clang::CXXRecordDecl *base) {
463-
return isIteratorCategoryDecl(base, "random_access_iterator_tag");
462+
return isIteratorTagDecl(base, "random_access_iterator_tag");
464463
};
465464
auto isContiguousIteratorDecl = [&](const clang::CXXRecordDecl *base) {
466-
return isIteratorCategoryDecl(base, "contiguous_iterator_tag"); // C++20
465+
return isIteratorTagDecl(base, "contiguous_iterator_tag"); // C++20
467466
};
468467

469468
// Traverse all transitive bases of `underlyingDecl` to check if
470469
// it inherits from `std::input_iterator_tag`.
471470
bool isInputIterator = isInputIteratorDecl(underlyingCategoryDecl);
472471
bool isRandomAccessIterator =
473472
isRandomAccessIteratorDecl(underlyingCategoryDecl);
474-
bool isContiguousIterator = isContiguousIteratorDecl(underlyingCategoryDecl);
475473
underlyingCategoryDecl->forallBases([&](const clang::CXXRecordDecl *base) {
476474
if (isInputIteratorDecl(base)) {
477475
isInputIterator = true;
478476
}
479477
if (isRandomAccessIteratorDecl(base)) {
480478
isRandomAccessIterator = true;
481479
isInputIterator = true;
482-
}
483-
if (isContiguousIteratorDecl(base)) {
484-
isContiguousIterator = true;
485-
isRandomAccessIterator = true;
486-
isInputIterator = true;
487480
return false;
488481
}
489482
return true;
@@ -492,6 +485,27 @@ void swift::conformToCxxIteratorIfNeeded(
492485
if (!isInputIterator)
493486
return;
494487

488+
bool isContiguousIterator = false;
489+
// In C++20, `std::contiguous_iterator_tag` is specified as a type called
490+
// `iterator_concept`. It is not possible to detect a contiguous iterator
491+
// based on its `iterator_category`. The type might not have an
492+
// `iterator_concept` defined.
493+
if (auto iteratorConcept = getIteratorConceptDecl(clangDecl)) {
494+
if (auto underlyingConceptDecl =
495+
unwrapUnderlyingTypeDecl(iteratorConcept)) {
496+
isContiguousIterator = isContiguousIteratorDecl(underlyingConceptDecl);
497+
if (!isContiguousIterator)
498+
underlyingConceptDecl->forallBases(
499+
[&](const clang::CXXRecordDecl *base) {
500+
if (isContiguousIteratorDecl(base)) {
501+
isContiguousIterator = true;
502+
return false;
503+
}
504+
return true;
505+
});
506+
}
507+
}
508+
495509
// Check if present: `var pointee: Pointee { get }`
496510
auto pointeeId = ctx.getIdentifier("pointee");
497511
auto pointee = lookupDirectSingleWithoutExtensions<VarDecl>(decl, pointeeId);

test/Interop/Cxx/stdlib/overlay/Inputs/custom-iterator.h

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ struct ConstContiguousIterator {
348348
const int *value;
349349

350350
public:
351-
using iterator_category = std::contiguous_iterator_tag;
351+
using iterator_category = std::random_access_iterator_tag;
352+
using iterator_concept = std::contiguous_iterator_tag;
352353
using value_type = int;
353354
using pointer = int *;
354355
using reference = const int &;
@@ -403,7 +404,8 @@ struct HasCustomContiguousIteratorTag {
403404

404405
public:
405406
struct CustomTag : std::contiguous_iterator_tag {};
406-
using iterator_category = CustomTag;
407+
using iterator_category = std::random_access_iterator_tag;
408+
using iterator_concept = CustomTag;
407409
using value_type = int;
408410
using pointer = int *;
409411
using reference = const int &;
@@ -458,7 +460,8 @@ struct MutableContiguousIterator {
458460
int *value;
459461

460462
public:
461-
using iterator_category = std::contiguous_iterator_tag;
463+
using iterator_category = std::random_access_iterator_tag;
464+
using iterator_concept = std::contiguous_iterator_tag;
462465
using value_type = int;
463466
using pointer = int *;
464467
using reference = const int &;
@@ -507,6 +510,63 @@ struct MutableContiguousIterator {
507510
return value != other.value;
508511
}
509512
};
513+
514+
/// This is actually just a random access iterator
515+
struct HasNoContiguousIteratorConcept {
516+
private:
517+
const int *value;
518+
519+
public:
520+
using iterator_category = std::contiguous_iterator_tag;
521+
// no iterator_concept
522+
using value_type = int;
523+
using pointer = int *;
524+
using reference = const int &;
525+
using difference_type = int;
526+
527+
HasNoContiguousIteratorConcept(const int *value) : value(value) {}
528+
HasNoContiguousIteratorConcept(const HasNoContiguousIteratorConcept &other) =
529+
default;
530+
531+
const int &operator*() const { return *value; }
532+
533+
HasNoContiguousIteratorConcept &operator++() {
534+
value++;
535+
return *this;
536+
}
537+
HasNoContiguousIteratorConcept operator++(int) {
538+
auto tmp = HasNoContiguousIteratorConcept(value);
539+
value++;
540+
return tmp;
541+
}
542+
543+
void operator+=(difference_type v) { value += v; }
544+
void operator-=(difference_type v) { value -= v; }
545+
HasNoContiguousIteratorConcept operator+(difference_type v) const {
546+
return HasNoContiguousIteratorConcept(value + v);
547+
}
548+
HasNoContiguousIteratorConcept operator-(difference_type v) const {
549+
return HasNoContiguousIteratorConcept(value - v);
550+
}
551+
friend HasNoContiguousIteratorConcept
552+
operator+(difference_type v, const HasNoContiguousIteratorConcept &it) {
553+
return it + v;
554+
}
555+
int operator-(const HasNoContiguousIteratorConcept &other) const {
556+
return value - other.value;
557+
}
558+
559+
bool operator<(const HasNoContiguousIteratorConcept &other) const {
560+
return value < other.value;
561+
}
562+
563+
bool operator==(const HasNoContiguousIteratorConcept &other) const {
564+
return value == other.value;
565+
}
566+
bool operator!=(const HasNoContiguousIteratorConcept &other) const {
567+
return value != other.value;
568+
}
569+
};
510570
#endif
511571

512572
// MARK: Types that are not actually iterators

test/Interop/Cxx/stdlib/overlay/custom-contiguous-iterator-module-interface.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,10 @@
2626
// CHECK: typealias Pointee = Int32
2727
// CHECK: typealias Distance = Int32
2828
// CHECK: }
29+
30+
// CHECK: struct HasNoContiguousIteratorConcept : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
31+
// CHECK: func successor() -> HasNoContiguousIteratorConcept
32+
// CHECK: var pointee: Int32
33+
// CHECK: typealias Pointee = Int32
34+
// CHECK: typealias Distance = Int32
35+
// CHECK: }

0 commit comments

Comments
 (0)