Skip to content

Commit 4d1aee2

Browse files
authored
Translate iterator categories (#540)
Closes #539. A rule for a container iterator such as `std::vector<T, A>::iterator` cannot match a usage, because `T` and `A` cannot be deduced from the iterator type (see #539). This PR matches iterator types by their [iterator category](https://en.cppreference.com/w/cpp/iterator) instead. The category is read from `std::iterator_traits<It>`, which every standard iterator provides, so the rules do not depend on how a particular standard library implements its iterators. This also removes duplicate rules. One rule for random access iterators replaces one rule per container that has a random access iterator. --- - `rules/iterator_category/shim.rs` defines the Rust types for the iterator categories used in translated code: iterator, input, output, bidirectional, and random ac cess. - `rules/iterator_category/src.cpp` defines a type rule for each category plus rules for its operations. For example, this is the rule for an iterator that is bidirectional but not random access: ```cpp template < typename It, typename T1 = typename std::iterator_traits<It>::value_type, typename = typename std::enable_if< std::is_base_of< std::bidirectional_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value && !std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value && !std::is_pointer<It>::value>::type> using t4 = It; ``` `It` is deduced directly from the usage, and `T1` and the `enable_if` condition are computed from it using only standard names. This rule matches `std::map<K, V>::iterator`, `std::set<T>::iterator`, `std::list<T>::iterator`, and so on, without naming any of them. --- The existing vector and map iterator rules are rewritten in terms of `RandomAccessIterator` and `BidirectionalIterator`. - vector: `RandomAccessIterator<T>` is a `Ptr<T>`, which is what vector iterators already translated to, so the change is small. - map: `RefcountMapIter` and `UnsafeMapIterator` were `MapIter<K, MapRef>`, which has two type parameters. A category rule only knows `T`, the iterator's `value_type`, which for a map is `std::pair<const K, V>`. The Rust iterator type therefore has to depend on `T` alone. The new map iterator is built on `MapNode<T>`. The map itself still owns a `BTreeMap`. The converter special cases for map iterators (`GetStrongestIteratorCategory`, `PushSuppressIteratorClone`, and the `Vec<`/`BTreeMap<` checks in `RecordHasCopyableFields`) are removed, since the rules now handle these types. --- Function rules that take the container as an argument already work, even when they also spell a container member type. For example `std::map::erase`: ```cpp template <typename T1, typename T2> typename std::map<T1, T2>::iterator f27(std::map<T1, T2> &o, typename std::map<T1, T2>::const_iterator it) { return o.erase(it); } template <typename T1, typename T2> std::size_t f28(std::map<T1, T2> &o, const typename std::map<T1, T2>::key_type &key) { return o.erase(key); } ``` `std::map<T1, T2>::const_iterator` is still a non-deduced context, but that does not matter here: `T1` and `T2` are deduced from the first parameter `o`, and Clang then substitutes them into the second parameter. Deduction only fails when the iterator is the only place the template parameters appear, as in the type rule `using t = typename std::map<T1, T2>::iterator;`. Those are the rules this PR replaces.
1 parent 5346ab0 commit 4d1aee2

82 files changed

Lines changed: 3810 additions & 2577 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CMakeLists.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ file(GLOB_RECURSE rule_preprocessor_sources
179179
file(GLOB_RECURSE libcc2rs_sources CONFIGURE_DEPENDS
180180
${PROJECT_SOURCE_DIR}/libcc2rs/src/*.rs
181181
${PROJECT_SOURCE_DIR}/libcc2rs-macros/src/*.rs)
182+
file(GLOB rule_shim_sources CONFIGURE_DEPENDS
183+
${PROJECT_SOURCE_DIR}/rules/*/shim.rs)
184+
list(APPEND libcc2rs_sources ${rule_shim_sources})
182185
list(APPEND libcc2rs_sources
183186
${PROJECT_SOURCE_DIR}/libcc2rs/Cargo.toml
184187
${PROJECT_SOURCE_DIR}/libcc2rs-macros/Cargo.toml)

‎cpp2rust/converter/converter.cpp‎

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -793,21 +793,6 @@ bool Converter::RecordHasCopyableFields(const clang::RecordDecl *decl) {
793793
return false;
794794
}
795795
for (auto f : decl->fields()) {
796-
// Records that contain std::vector, std::array, std::string or anything
797-
// that is translated to Vec<>, do not derive Copy
798-
auto mapped = Mapper::Map(*this, f->getType());
799-
if (mapped.starts_with("Vec<")) {
800-
return false;
801-
}
802-
803-
if (IsUniquePtr(f->getType())) {
804-
return false;
805-
}
806-
807-
if (mapped.starts_with("BTreeMap<")) {
808-
return false;
809-
}
810-
811796
if (auto ptr_ty = f->getType()->getAs<clang::PointerType>()) {
812797
if (ptr_ty->getPointeeType()->isFunctionType()) {
813798
if (!FunctionPointerImplementsCopy()) {
@@ -1575,7 +1560,7 @@ bool Converter::VisitCXXForRangeStmtMap(clang::CXXForRangeStmt *stmt) {
15751560
StrCat("'loop_:");
15761561
auto map_type = Mapper::Map(*this, stmt->getRangeInit()->getType());
15771562
StrCat(keyword::kFor, GetNamedDeclAsString(begin), keyword::kIn,
1578-
"UnsafeMapIterator::begin(&");
1563+
"UnsafeMap::range(&");
15791564
Convert(stmt->getRangeInit());
15801565
StrCat(std::format(" as *const {})", map_type));
15811566
{
@@ -3362,9 +3347,6 @@ bool Converter::ConvertCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr) {
33623347
case clang::OverloadedOperatorKind::OO_Arrow:
33633348
if (IsUniquePtr(expr->getArg(0)->getType())) {
33643349
ConvertUniquePtrDeref(expr);
3365-
} else if (GetStrongestIteratorCategory(ctx_, expr->getArg(0)->getType()) ==
3366-
IteratorCategory::Bidirectional) {
3367-
Convert(expr->getArg(0));
33683350
} else if (expr->getOperator() == clang::OverloadedOperatorKind::OO_Star) {
33693351
PushParen paren(*this);
33703352
StrCat(token::kStar);
@@ -3914,8 +3896,6 @@ void Converter::ConvertCXXConstructExprArgs(clang::CXXConstructExpr *expr) {
39143896
}
39153897

39163898
bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
3917-
PushSuppressIteratorClone push(*this, expr);
3918-
39193899
if (expr->isElidable()) {
39203900
Convert(expr->getArg(0));
39213901
return false;
@@ -3938,11 +3918,9 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
39383918
return false;
39393919
}
39403920
if (IsPassThroughConstructor(ctor)) {
3941-
// Take suppress before recursing into the child.
3942-
bool suppress = PushSuppressIteratorClone::take(*this);
39433921
Convert(expr->getArg(0));
39443922
if ((ctor->isCopyConstructor() || IsDefaultedMoveConstructor(ctor)) &&
3945-
!suppress && !TypeIsCopyable(expr->getType())) {
3923+
!TypeIsCopyable(expr->getType())) {
39463924
StrCat(".clone()");
39473925
SetFreshType(expr->getType());
39483926
}
@@ -5452,14 +5430,6 @@ void Converter::ConvertConstructFromArgs(clang::QualType type,
54525430
std::string Converter::AccessLValueObject(clang::MemberExpr *member) {
54535431
auto *object = member->getBase();
54545432
auto type = object->getType();
5455-
if (member->isArrow()) {
5456-
auto *op =
5457-
clang::dyn_cast<clang::CXXOperatorCallExpr>(object->IgnoreImplicit());
5458-
if (op && GetStrongestIteratorCategory(ctx_, op->getArg(0)->getType()) ==
5459-
IteratorCategory::Bidirectional) {
5460-
return ToString(object);
5461-
}
5462-
}
54635433
if (type->isPointerType() ||
54645434
(IsReferenceType(object) && clang::isa<clang::CallExpr>(object))) {
54655435
return std::format("({}{})", GetPointerDerefPrefix(type->getPointeeType()),

‎cpp2rust/converter/converter.h‎

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
841841
std::string ufcs_receiver_;
842842
bool in_const_initializer_ = false;
843843
std::optional<bool> autoref_mut_;
844-
bool suppress_iterator_clone_ = false;
845844

846845
struct PushExplicitAutoref {
847846
Converter &c;
@@ -853,41 +852,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
853852
~PushExplicitAutoref() { c.autoref_mut_ = prev; }
854853
};
855854

856-
struct PushSuppressIteratorClone {
857-
Converter &c;
858-
bool prev;
859-
PushSuppressIteratorClone(Converter &c, clang::CXXConstructExpr *expr)
860-
: c(c), prev(c.suppress_iterator_clone_) {
861-
auto *ctor = expr->getConstructor();
862-
if (!ctor->isCopyOrMoveConstructor() &&
863-
ctor->isConvertingConstructor(/*AllowExplicit=*/false) &&
864-
ctor->getNumParams() == 1 && IsIteratorType(expr->getType())) {
865-
c.suppress_iterator_clone_ = true;
866-
}
867-
}
868-
~PushSuppressIteratorClone() { c.suppress_iterator_clone_ = prev; }
869-
PushSuppressIteratorClone(const PushSuppressIteratorClone &) = delete;
870-
PushSuppressIteratorClone &
871-
operator=(const PushSuppressIteratorClone &) = delete;
872-
873-
static bool take(Converter &c) {
874-
return std::exchange(c.suppress_iterator_clone_, false);
875-
}
876-
877-
private:
878-
static bool IsIteratorType(clang::QualType qt) {
879-
if (auto *record = qt->getAsCXXRecordDecl()) {
880-
for (auto *d : record->decls()) {
881-
if (auto *tnd = llvm::dyn_cast<clang::TypedefNameDecl>(d)) {
882-
if (tnd->getName() == "iterator_category")
883-
return true;
884-
}
885-
}
886-
}
887-
return false;
888-
}
889-
};
890-
891855
struct PushConstInitializer {
892856
Converter &c;
893857
bool prev;

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,7 +1892,7 @@ clang::Expr *GetCallee(clang::CallExpr *expr) {
18921892
}
18931893

18941894
bool HasReceiver(clang::Expr *expr) {
1895-
if (clang::isa<clang::CXXMemberCallExpr>(expr))
1895+
if (clang::isa<clang::CXXMemberCallExpr, clang::MemberExpr>(expr))
18961896
return true;
18971897
if (auto *opcall = clang::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
18981898
if (auto *callee = opcall->getCalleeDecl()) {
@@ -1920,26 +1920,6 @@ std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
19201920
return fn->getParamDecl(param_idx)->getType().getNonReferenceType();
19211921
}
19221922

1923-
std::optional<IteratorCategory>
1924-
GetStrongestIteratorCategory(clang::ASTContext &ctx, clang::QualType type) {
1925-
type = type.getNonReferenceType().getUnqualifiedType();
1926-
if (!Mapper::Contains(ctx, type)) {
1927-
return std::nullopt;
1928-
}
1929-
if (RuleRegistry::MapsToRefcountPointer(ctx, type)) {
1930-
return IteratorCategory::Contiguous;
1931-
}
1932-
auto mapped = Mapper::Map(ctx, type);
1933-
if (mapped.empty()) {
1934-
return std::nullopt;
1935-
}
1936-
if (mapped.starts_with("RefcountMapIter<") ||
1937-
mapped.starts_with("UnsafeMapIterator<")) {
1938-
return IteratorCategory::Bidirectional;
1939-
}
1940-
return std::nullopt;
1941-
}
1942-
19431923
std::string GetClassName(clang::QualType type) {
19441924
if (auto *record = type->getAsCXXRecordDecl())
19451925
return record->getQualifiedNameAsString();

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,6 @@ class Sema;
2828

2929
namespace cpp2rust {
3030

31-
// Order matters: each category is a superset of the previous one.
32-
// Use >= to check "at least this capable".
33-
enum class IteratorCategory {
34-
Forward,
35-
Bidirectional,
36-
RandomAccess,
37-
Contiguous,
38-
};
39-
40-
std::optional<IteratorCategory>
41-
GetStrongestIteratorCategory(clang::ASTContext &ctx, clang::QualType type);
4231
bool IsBuiltinConstantP(const clang::Expr *expr);
4332

4433
bool IsGlobalVar(const clang::VarDecl *decl);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ static bool IsFieldOfVectorElem(const clang::MemberExpr *expr) {
9797
}
9898

9999
static bool IsPointerType(clang::ASTContext &ctx, clang::QualType type) {
100-
return type->isPointerType() || GetStrongestIteratorCategory(ctx, type) ==
101-
IteratorCategory::Contiguous;
100+
return type->isPointerType() ||
101+
RuleRegistry::MapsToRefcountPointer(ctx, type);
102102
}
103103

104104
void ConverterRefCount::PendingDeref::set(std::string str, bool fresh) {
@@ -2279,7 +2279,7 @@ bool ConverterRefCount::VisitCXXForRangeStmtMap(clang::CXXForRangeStmt *stmt) {
22792279
auto begin_name = GetNamedDeclAsString(begin);
22802280

22812281
StrCat("'loop_:");
2282-
StrCat(keyword::kFor, begin_name, keyword::kIn, "RefcountMapIter::begin(",
2282+
StrCat(keyword::kFor, begin_name, keyword::kIn, "Map::range(",
22832283
ConvertObject(stmt->getRangeInit()), ')');
22842284
PushBrace brace(*this);
22852285

@@ -2375,7 +2375,6 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
23752375
getConversionKind() != ConversionKind::Unboxed;
23762376
PushConversionKind push(*this, boxed_array ? ConversionKind::Pointee
23772377
: ConversionKind::Unboxed);
2378-
PushSuppressIteratorClone push_suppress(*this, expr);
23792378

23802379
if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs());
23812380
!str.empty()) {
@@ -2408,9 +2407,7 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
24082407

24092408
if (ctor->isCopyOrMoveConstructor() &&
24102409
!IsConvertibleCopyOrMoveConstructor(ctor)) {
2411-
StrCat(PushSuppressIteratorClone::take(*this)
2412-
? ConvertRValue(expr->getArg(0))
2413-
: ConvertFreshRValue(expr->getArg(0)));
2410+
StrCat(ConvertFreshRValue(expr->getArg(0)));
24142411
return false;
24152412
}
24162413

@@ -2848,12 +2845,6 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
28482845
break;
28492846
}
28502847

2851-
if (GetStrongestIteratorCategory(ctx_, expr->getArg(0)->getType()) ==
2852-
IteratorCategory::Bidirectional) {
2853-
Convert(expr->getArg(0));
2854-
break;
2855-
}
2856-
28572848
{
28582849
bool deref = !isAddrOf();
28592850
PushParen paren(*this, deref);
@@ -3314,7 +3305,8 @@ void ConverterRefCount::ConvertArrow(clang::Expr *expr) {
33143305
bool is_overloaded_arrow =
33153306
op && op->getOperator() == clang::OverloadedOperatorKind::OO_Arrow;
33163307

3317-
if (!is_overloaded_arrow || IsUserOperatorCall(op)) {
3308+
if (!is_overloaded_arrow || IsUserOperatorCall(op) ||
3309+
RuleRegistry::ReturnsPointer(ctx_, expr)) {
33183310
auto pointee_type = expr->getType()->getPointeeType();
33193311
// Virtual methods, which take &mut self, are called through PtrDyn.
33203312
if (auto *record = pointee_type->getAsRecordDecl();
@@ -3333,12 +3325,6 @@ void ConverterRefCount::ConvertArrow(clang::Expr *expr) {
33333325
return;
33343326
}
33353327

3336-
if (GetStrongestIteratorCategory(ctx_, op->getArg(0)->getType()) ==
3337-
IteratorCategory::Bidirectional) {
3338-
Convert(op->getArg(0));
3339-
return;
3340-
}
3341-
33423328
Convert(expr);
33433329
}
33443330

@@ -3348,12 +3334,6 @@ std::string ConverterRefCount::AccessLValueObject(clang::MemberExpr *member) {
33483334

33493335
bool is_mut = method && !method->isConst();
33503336
if (member->isArrow()) {
3351-
auto *op =
3352-
clang::dyn_cast<clang::CXXOperatorCallExpr>(object->IgnoreImplicit());
3353-
if (op && GetStrongestIteratorCategory(ctx_, op->getArg(0)->getType()) ==
3354-
IteratorCategory::Bidirectional) {
3355-
return ConvertRValue(op->getArg(0));
3356-
}
33573337
auto str = is_mut ? ConvertLValue(object) : ConvertRValue(object);
33583338
auto pointee_type = object->getType()->getPointeeType();
33593339
return DerefPtrExpr(str, pointee_type);

‎cpp2rust/converter/rules/decls.cpp‎

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,30 +77,6 @@ const clang::Expr *GetMacroMemberBase(const clang::SourceManager &sm,
7777
}
7878
}
7979

80-
clang::QualType EnableIfSameType(const clang::Decl *rule) {
81-
auto tmpl = llvm::dyn_cast<clang::TemplateDecl>(rule);
82-
if (!tmpl) {
83-
return {};
84-
}
85-
for (auto param : *tmpl->getTemplateParameters()) {
86-
auto type_param = llvm::dyn_cast<clang::TemplateTypeParmDecl>(param);
87-
if (!type_param || type_param->getIdentifier() ||
88-
!type_param->hasDefaultArgument()) {
89-
continue;
90-
}
91-
auto same = type_param->getDefaultArgument()
92-
.getArgument()
93-
.getAsType()
94-
->getAs<clang::TemplateSpecializationType>();
95-
if (!same || !same->isTypeAlias() ||
96-
same->getTemplateName().getAsTemplateDecl()->getName() != "SameAs") {
97-
continue;
98-
}
99-
return same->template_arguments()[1].getAsType();
100-
}
101-
return {};
102-
}
103-
10480
std::optional<std::vector<clang::TemplateArgument>>
10581
DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params,
10682
clang::QualType pattern, clang::QualType actual) {

‎cpp2rust/converter/rules/decls.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ const clang::Expr *GetReturned(const clang::FunctionDecl *rule);
4242
const clang::Expr *GetMacroMemberBase(const clang::SourceManager &sm,
4343
const clang::MemberExpr *member);
4444

45-
clang::QualType EnableIfSameType(const clang::Decl *rule);
46-
4745
std::optional<std::vector<clang::TemplateArgument>>
4846
DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params,
4947
clang::QualType pattern, clang::QualType actual);

‎cpp2rust/converter/rules/expr_matcher.cpp‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,6 @@ std::vector<clang::Expr *> getOperands(clang::ASTContext &ctx,
155155
auto base =
156156
const_cast<clang::Expr *>(macro_base ? macro_base : member->getBase())
157157
->IgnoreParenImpCasts();
158-
if (auto arrow = llvm::dyn_cast<clang::CXXOperatorCallExpr>(base);
159-
arrow && arrow->getOperator() == clang::OO_Arrow) {
160-
base = arrow->getArg(0);
161-
}
162158
operands.push_back(getObject(ctx, base));
163159
}
164160
while (!operands.empty() &&

0 commit comments

Comments
 (0)