From 4aaa112cc3ab0b19d1171612ec262f694cbf9544 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 23 May 2026 17:13:52 +0100 Subject: [PATCH 01/26] Refactor ConvertGenericCallExpr --- cpp2rust/converter/converter.cpp | 462 ++++++++++++------------------- cpp2rust/converter/converter.h | 33 +++ 2 files changed, 210 insertions(+), 285 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 12e34426..ed900968 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -79,7 +78,7 @@ bool Converter::Convert(clang::QualType qual_type) { } auto mapped = Mapper::Map(qual_type); - if (!mapped.empty() && mapped != token::kIgnoreRule) { + if (!mapped.empty() && mapped != ignore_rule_type_) { StrCat(mapped); return false; } @@ -90,7 +89,7 @@ bool Converter::Convert(clang::QualType qual_type) { bool Converter::ConvertMappedType(clang::QualType qual_type) { std::string type_as_string = Mapper::Map(qual_type); - if (type_as_string == token::kIgnoreRule) { + if (type_as_string == ignore_rule_type_) { return false; } StrCat(type_as_string); @@ -165,7 +164,7 @@ bool Converter::VisitRecordType(clang::RecordType *type) { ->getAs(), FnProtoType::LambdaCallOperator)); } else { - StrCat('_'); + StrCat("_"); } return false; } @@ -364,59 +363,7 @@ bool Converter::VisitFunctionDecl(clang::FunctionDecl *decl) { return false; } -void Converter::EmitHoistedDecls(clang::CompoundStmt *body) { - for (auto *child : body->body()) { - if (auto *decl_stmt = clang::dyn_cast(child)) { - for (auto *decl : decl_stmt->decls()) { - if (auto *var = clang::dyn_cast(decl); - var && var->isLocalVarDecl() && !IsGlobalVar(var)) { - hoisted_decls_.insert(var); - if (ConvertVarDeclSkipInit(var)) { - StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()), - token::kSemiColon); - } - } - } - } - } -} - -void Converter::ConvertGotoBlock(clang::CompoundStmt *body) { - PushHoistedDecls push(hoisted_decls_); - EmitHoistedDecls(body); - - StrCat("goto_block!"); - { - PushParen paren(*this); - PushBrace outer(*this); - StrCat("'__entry: "); - std::optional arm; - arm.emplace(*this); - for (auto *child : body->body()) { - if (auto *label = clang::dyn_cast(child)) { - arm.reset(); - StrCat(std::format("'{}: ", label->getDecl()->getName().str())); - arm.emplace(*this); - Convert(label->getSubStmt()); - } else { - Convert(child); - } - } - } - StrCat(token::kSemiColon); -} - void Converter::ConvertFunctionBody(clang::FunctionDecl *decl) { - if (auto compound = clang::dyn_cast(decl->getBody())) { - if (CompoundHasTopLevelLabel(compound)) { - ConvertGotoBlock(compound); - if (!decl->getReturnType()->isVoidType()) { - StrCat(R"(panic!("ub: non-void function does not return a value"))"); - } - return; - } - } - Convert(decl->getBody()); if (!decl->getReturnType()->isVoidType()) { if (auto compound = clang::dyn_cast(decl->getBody())) { @@ -519,20 +466,7 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) { } } -void Converter::EmitHoistedInArmAssignment(clang::VarDecl *decl) { - if (!decl->hasInit()) { - return; - } - StrCat(GetNamedDeclAsString(decl), token::kAssign); - ConvertVarInit(decl->getType(), decl->getInit()); - StrCat(token::kSemiColon); -} - void Converter::ConvertVarDecl(clang::VarDecl *decl) { - if (hoisted_decls_.contains(decl)) { - EmitHoistedInArmAssignment(decl); - return; - } if (!ConvertVarDeclSkipInit(decl)) { // Skip global variables declared extern return; @@ -1050,10 +984,6 @@ bool Converter::Convert(clang::Stmt *stmt) { } bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) { - if (CompoundHasTopLevelLabel(stmt)) { - ConvertGotoBlock(stmt); - return false; - } for (auto *child : stmt->body()) { Convert(child); } @@ -1080,11 +1010,6 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) { return false; } -bool Converter::VisitGotoStmt(clang::GotoStmt *stmt) { - StrCat(std::format("goto!('{})", stmt->getLabel()->getName().str())); - return false; -} - void Converter::ConvertCondition(clang::Expr *cond) { PushExprKind push(*this, ExprKind::RValue); Convert(NormalizeToBool(cond, ctx_)); @@ -1116,29 +1041,31 @@ bool Converter::VisitWhileStmt(clang::WhileStmt *stmt) { ConvertCondition(stmt->getCond()); { PushBrace brace(*this); - curr_for_inc_.emplace_back(nullptr); + curr_for_inc_.emplace(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop_back(); + curr_for_inc_.pop(); } return false; } bool Converter::VisitDoStmt(clang::DoStmt *stmt) { PushBreakTarget push(break_target_, BreakTarget::Loop); - const char *control_var = "__do_while"; - StrCat(keyword::kLet, "mut", control_var, token::kAssign, keyword::kTrue, - token::kSemiColon); - StrCat("'loop_:", keyword::kWhile, control_var, "||"); - { - PushParen paren(*this); - ConvertCondition(stmt->getCond()); - } + StrCat("'loop_:"); + StrCat(keyword::kLoop); { PushBrace loop_brace(*this); - StrCat(control_var, token::kAssign, keyword::kFalse, token::kSemiColon); - curr_for_inc_.emplace_back(nullptr); + curr_for_inc_.emplace(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop_back(); + curr_for_inc_.pop(); + StrCat(keyword::kIf, token::kNot); + { + PushParen paren(*this); + ConvertCondition(stmt->getCond()); + } + { + PushBrace if_brace(*this); + StrCat(keyword::kBreak, token::kSemiColon); + } } return false; } @@ -1155,9 +1082,9 @@ bool Converter::VisitForStmt(clang::ForStmt *stmt) { } { PushBrace brace(*this); - curr_for_inc_.emplace_back(stmt->getInc()); + curr_for_inc_.emplace(stmt->getInc()); Convert(stmt->getBody()); - curr_for_inc_.pop_back(); + curr_for_inc_.pop(); Convert(stmt->getInc()); StrCat(token::kSemiColon); } @@ -1193,9 +1120,9 @@ void Converter::ConvertForRangeBody(clang::CXXForRangeStmt *stmt, std::optional skip; if (map_iter_decl) skip.emplace(*this, map_iter_decl); - curr_for_inc_.emplace_back(nullptr); + curr_for_inc_.emplace(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop_back(); + curr_for_inc_.pop(); } bool Converter::VisitCXXForRangeStmt(clang::CXXForRangeStmt *stmt) { @@ -1286,7 +1213,7 @@ bool Converter::VisitBreakStmt([[maybe_unused]] clang::BreakStmt *stmt) { bool Converter::VisitContinueStmt([[maybe_unused]] clang::ContinueStmt *stmt) { if (!curr_for_inc_.empty()) { - Convert(curr_for_inc_.back()); + Convert(curr_for_inc_.top()); StrCat(token::kSemiColon); } StrCat(keyword::kContinue); @@ -1329,30 +1256,27 @@ bool Converter::IsSubExprOf(const clang::Expr *sub_expr, } bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, - std::string &fmt_args, const char *&fmt_trait, + std::string &fmt_args, std::string &fmt_trait, std::string &fmt_width) { std::string arg_str = Mapper::ToString(arg); - if (auto *str_lit = - clang::dyn_cast(arg->IgnoreImplicit())) { - if (!IsAsciiStringLiteral(str_lit)) { - return false; - } + if (clang::isa(arg->IgnoreImplicit())) { auto str = GetEscapedStringLiteral(arg); - std::string_view trim(str); // Delete " from string - trim.remove_prefix(1); - trim.remove_suffix(1); - fmt += trim; + str.erase(std::remove(str.begin(), str.end(), '"'), str.end()); + fmt += std::move(str); } else if (auto ch = GetEscapedUTF8CharLiteral(arg); !ch.empty()) { fmt += std::move(ch); } else if (arg_str.contains("std::endl")) { fmt += "\\n"; } else if (arg_str.contains("std::hex")) { - fmt_trait = "x"; + fmt_trait = 'x'; } else if (arg_str.contains("std::dec")) { fmt_trait = ""; } else if (arg_str.contains("Setw")) { - fmt_width = Trim(ToString(arg)); + fmt_width = ToString(arg); + // Delete leading and trailing whitespaces + fmt_width.erase(0, fmt_width.find_first_not_of(' ')); + fmt_width.erase(fmt_width.find_last_not_of(' ') + 1); } else if (!arg->getType()->isCharType() && Mapper::Map(arg->getType()) != "Vec") { fmt += ("{:" + fmt_width + fmt_trait + "}"); @@ -1377,8 +1301,6 @@ bool Converter::GetRawArg(clang::Expr *arg, std::string &raw_args) { raw_args += "(&(" + str + ")[..(" + str + ").len() - 1]"; } else if (Mapper::ToString(arg).contains("std::endl")) { raw_args += "(&[b'\\n']"; - } else if (clang::isa(arg->IgnoreImplicit())) { - raw_args += "(b" + GetEscapedStringLiteral(arg); } else { return false; } @@ -1421,7 +1343,7 @@ void Converter::ConvertCallToOstream(clang::CallExpr *expr) { } std::string fmt; - const char *fmt_trait = ""; + std::string fmt_trait; std::string fmt_width; std::string fmt_args; std::string raw_args; @@ -1457,7 +1379,7 @@ void Converter::ConvertCallToOstream(clang::CallExpr *expr) { write_raw_args(); } - assert(*fmt_trait == '\0' && "Stream state was not restored after call"); + assert(fmt_trait == "" && "Stream state was not restored after call"); } void Converter::ConvertPrintf(clang::CallExpr *expr) { @@ -1488,6 +1410,7 @@ std::optional Converter::TryPluginConvert(clang::CallExpr *call) { void Converter::ConvertVariadicArg(clang::Expr *arg) { if (arg->getType()->isFunctionPointerType()) { + PushParen p(*this); Convert(arg); StrCat(".map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void)"); return; @@ -1594,128 +1517,159 @@ void Converter::ConvertFunctionToFunctionPointer( StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl))); } -void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { - clang::Expr *callee = expr->getCallee(); - auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) { - if (param_type->isLValueReferenceType()) { - PushExprKind push(*this, ExprKind::AddrOf); - ConvertVarInit(param_type, expr); - } else { - ConvertVarInit(param_type, expr); - } - }; +Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { + using Kind = CallArg::Kind; - unsigned arg_begin = 0; // skip count for operator()'s implicit object arg + CallInfo info{}; + info.callee = expr->getCallee(); + unsigned arg_begin = 0; if (auto op_call = llvm::dyn_cast(expr)) { if (op_call->getOperator() == clang::OO_Call) { - callee = op_call->getArg(0); + info.callee = op_call->getArg(0); arg_begin = 1; } } - PushParen outer(*this); - StrCat(keyword_unsafe_); - PushBrace unsafe_brace(*this); const auto *function = expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr; const clang::FunctionProtoType *proto = nullptr; - if (!function) { - auto callee_ty = callee->getType().getDesugaredType(ctx_); + auto callee_ty = info.callee->getType().getDesugaredType(ctx_); if (auto ptr_ty = callee_ty->getAs()) { proto = ptr_ty->getPointeeType()->getAs(); } } - assert((function || proto) && "Either function decl or function prototype should be known"); - auto num_args = expr->getNumArgs() - arg_begin; - bool is_variadic = - function ? function->isVariadic() : (proto && proto->isVariadic()); - unsigned num_named_params = function - ? function->getNumParams() - : (proto ? proto->getNumParams() : num_args); - - // Track which args are materialized temps bound to reference params - std::vector temp_refs(num_args); + unsigned num_args = expr->getNumArgs() - arg_begin; + unsigned num_named_params = + function ? function->getNumParams() : proto->getNumParams(); + info.is_variadic = function ? function->isVariadic() : proto->isVariadic(); + info.is_fn_ptr_call = !function; for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); - std::string param_name = function - ? function->getParamDecl(i)->getNameAsString() - : ("arg" + std::to_string(i)); - clang::QualType param_type = function ? function->getParamDecl(i)->getType() - : proto->getParamType(i); - - bool is_materialize_to_ref = - clang::isa(arg) && - param_type->isLValueReferenceType(); - - if (is_materialize_to_ref) { - auto [binding, ref] = - MaterializeTemp(std::format("_{}", param_name), param_type, arg); - StrCat(binding); - temp_refs[i] = std::move(ref); - } else if (!clang::isa(arg)) { - StrCat("let", std::format("_{}: {}", param_name, ToString(param_type)), - '='); - convert_param_ty(param_type, arg); - StrCat(';'); + CallArg ca{ + .expr = arg, + .kind = Kind::Hoisted, + .param_name = function ? function->getParamDecl(i)->getNameAsString() + : ("arg" + std::to_string(i)), + .param_type = function ? function->getParamDecl(i)->getType() + : proto->getParamType(i), + .has_default = function && function->getParamDecl(i)->hasDefaultArg(), + }; + bool is_materialize = clang::isa(arg); + if (is_materialize && ca.param_type->isLValueReferenceType()) { + ca.kind = Kind::Materialized; + } else if (is_materialize) { + ca.kind = Kind::Inline; } + info.args.push_back(std::move(ca)); } - if (proto && !function) { - EmitFnPtrCall(callee); + if (info.is_variadic) { + for (unsigned i = num_named_params; i < num_args; ++i) { + info.variadic_args.push_back(expr->getArg(i + arg_begin)); + } + } + + return info; +} + +void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) { + if (param_type->isLValueReferenceType()) { + PushExprKind push(*this, ExprKind::AddrOf); + ConvertVarInit(param_type, expr); } else { - PushExprKind push(*this, ExprKind::Callee); - Convert(callee); + ConvertVarInit(param_type, expr); } - { - PushParen call_args(*this); - for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { - auto *arg = expr->getArg(i + arg_begin); - std::string param_name = - function ? function->getParamDecl(i)->getNameAsString() - : ("arg" + std::to_string(i)); - clang::QualType param_type = function - ? function->getParamDecl(i)->getType() - : proto->getParamType(i); - bool is_parm_with_default_value = - function && function->getParamDecl(i)->hasDefaultArg(); - - if (is_parm_with_default_value) { - StrCat("Some("); - } - if (!temp_refs[i].empty()) { - StrCat(temp_refs[i]); - } else if (clang::isa(arg)) { - convert_param_ty(param_type, arg); - } else { - StrCat(std::format("_{}", param_name)); - } - if (is_parm_with_default_value) { - StrCat(')'); +} + +void Converter::EmitArgBindings(CallInfo &info) { + using Kind = CallArg::Kind; + for (auto &ca : info.args) { + switch (ca.kind) { + case Kind::Hoisted: + StrCat("let", + std::format("_{}: {}", ca.param_name, ToString(ca.param_type)), + "="); + ConvertParamTy(ca.param_type, ca.expr); + StrCat(";"); + break; + case Kind::Materialized: { + auto [binding, ref] = MaterializeTemp(std::format("_{}", ca.param_name), + ca.param_type, ca.expr); + StrCat(binding); + ca.ref_temp_name = std::move(ref); + break; + } + case Kind::Inline: + break; + } + } +} + +void Converter::EmitArgList(const CallInfo &info) { + using Kind = CallArg::Kind; + PushParen call_args(*this); + + for (const auto &ca : info.args) { + if (ca.has_default) { + StrCat("Some"); + } + + { + PushParen push(*this, ca.has_default); + switch (ca.kind) { + case Kind::Hoisted: + StrCat(std::format("_{}", ca.param_name)); + break; + case Kind::Materialized: + StrCat(ca.ref_temp_name); + break; + case Kind::Inline: + ConvertParamTy(ca.param_type, ca.expr); + break; } - StrCat(token::kComma); } - // Variadic args: wrap in &[arg.into(), ...] - if (is_variadic) { - StrCat("& ["); - for (unsigned i = num_named_params; i < num_args; ++i) { - auto *arg = expr->getArg(i + arg_begin); - { - PushParen p(*this); - ConvertVariadicArg(arg); - } - StrCat(".into()", token::kComma); + StrCat(token::kComma); + } + + if (info.is_variadic) { + StrCat(token::kRef); + PushBracket push(*this); + for (auto *arg : info.variadic_args) { + { + PushParen p(*this); + ConvertVariadicArg(arg); } - StrCat(']'); + StrCat(".into()", token::kComma); } } } +void Converter::EmitCall(CallInfo info) { + EmitArgBindings(info); + + if (info.is_fn_ptr_call) { + EmitFnPtrCall(info.callee); + } else { + PushExprKind push(*this, ExprKind::Callee); + Convert(info.callee); + } + + EmitArgList(info); +} + +void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { + PushParen outer(*this); + StrCat(keyword_unsafe_); + PushBrace unsafe_brace(*this); + EmitCall(CollectCallInfo(expr)); +} + std::optional Converter::ConvertCallExpr(clang::CallExpr *expr) { auto *callee = expr->getCallee(); @@ -1782,9 +1736,8 @@ bool Converter::VisitFloatingLiteral(clang::FloatingLiteral *expr) { } bool Converter::VisitCharacterLiteral(clang::CharacterLiteral *expr) { - auto uc = static_cast(expr->getValue()); std::string ch = GetEscapedCharLiteral(expr->getValue()); - ch = (uc > 0x7F ? "b'" : "'") + std::move(ch) + '\''; + ch = "'" + std::move(ch) + "'"; { PushParen paren(*this); StrCat(ch, keyword::kAs, ToStringBase(expr->getType())); @@ -1811,7 +1764,7 @@ std::string Converter::GetEscapedCharLiteral(char character) const { return "\\0"; } auto uc = static_cast(character); - if (uc < 0x20 || uc >= 0x7F) { + if (uc < 0x20 || uc == 0x7F) { return std::format("\\x{:02x}", uc); } return std::string(1, character); @@ -1847,8 +1800,8 @@ std::string Converter::GetEscapedStringLiteral(clang::Expr *expr, } bool Converter::VisitStringLiteral(clang::StringLiteral *expr) { - if (!curr_init_type_.empty() && curr_init_type_.back()->isArrayType()) { - if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.back())) { + if (!curr_init_type_.empty() && curr_init_type_.top()->isArrayType()) { + if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.top())) { uint64_t arr_size = arr_ty->getSize().getZExtValue(); if (expr->getString().empty()) { StrCat(std::format("[0u8; {}]", arr_size)); @@ -1944,18 +1897,9 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { Convert(sub_expr); break; } + Convert(sub_expr); bool dest_pointee_const = expr->getType()->getPointeeType().isConstQualified(); - if (const auto *member = - clang::dyn_cast(sub_expr->IgnoreParenImpCasts()); - member && IsCharArrayFieldFromLibc(member->getMemberDecl())) { - PushParen paren(*this); - Convert(sub_expr); - StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()"); - StrCat(keyword::kAs, dest_pointee_const ? "*const u8" : "*mut u8"); - break; - } - Convert(sub_expr); if (clang::isa(sub_expr) || clang::isa(sub_expr)) { StrCat(".as_ptr()"); @@ -2167,8 +2111,8 @@ void Converter::ConvertBinaryOperator(clang::BinaryOperator *expr) { Convert(lhs); ConvertCast(computation_result_type); } - auto op = opcode_as_string; - op.remove_suffix(1); // remove '=' from operator + std::string op(opcode_as_string); + op.erase(std::remove(op.begin(), op.end(), '='), op.end()); StrCat(op); Convert(rhs); } @@ -2663,7 +2607,6 @@ replaceNonUniformLibcField(clang::MemberExpr *expr) { static constexpr Mapping kFields[] = { {"stat", "st_mtim", "tv_sec", "st_mtime"}, // Linux {"stat", "st_mtimespec", "tv_sec", "st_mtime"}, // macOS - {"in6_addr", "__in6_u", "__u6_addr8", "s6_addr"}, }; auto getNamedIdentifierOrNull = [](auto *decl) { @@ -2770,10 +2713,6 @@ bool Converter::VisitInitListExpr(clang::InitListExpr *expr) { StrCat(token::kComma); } } else { - if (IsInitExprOfStringLiteral(expr)) { - Convert(expr->getInit(0)->IgnoreParenImpCasts()); - return false; - } PushBracket bracket(*this); for (auto *init : expr->inits()) { ConvertVarInit(init->getType(), init); @@ -2819,7 +2758,7 @@ bool Converter::VisitArraySubscriptExpr(clang::ArraySubscriptExpr *expr) { } bool Converter::VisitCXXNullPtrLiteralExpr(clang::CXXNullPtrLiteralExpr *expr) { - StrCat(token::kDefault); + StrCat(keyword_default_); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -2849,7 +2788,7 @@ bool Converter::VisitVAArgExpr(clang::VAArgExpr *expr) { } bool Converter::VisitGNUNullExpr(clang::GNUNullExpr *expr) { - StrCat(token::kDefault); + StrCat(keyword_default_); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -2873,7 +2812,7 @@ bool Converter::VisitCXXNewExpr(clang::CXXNewExpr *expr) { alloc_type_as_string); StrCat(new_array_as_string); } - if (!curr_init_type_.empty() && curr_init_type_.back()->isPointerType()) { + if (!curr_init_type_.empty() && curr_init_type_.top()->isPointerType()) { StrCat(".as_mut_ptr()"); } } else { @@ -3029,7 +2968,7 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { Mapper::AddRuleForUserDefinedType(decl); StrCat("#[derive(Clone, Copy, PartialEq, Debug, Default)]"); StrCat(std::format("enum {}", GetRecordName(decl))); - StrCat('{'); + StrCat("{"); bool first_enumerator = true; for (auto e : decl->enumerators()) { llvm::SmallVector init; @@ -3041,7 +2980,7 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { StrCat(std::format("{} = {},", std::string_view(e->getName()), std::string_view(init.data(), init.size()))); } - StrCat('}'); + StrCat("}"); AddFromImpl(decl); AddIncDecImpls(decl); @@ -3072,7 +3011,7 @@ void Converter::AddIncDecImpls(clang::EnumDecl *decl) { bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { if (expr->getType()->isPointerType()) { - StrCat(token::kDefault); + StrCat(keyword_default_); } return false; } @@ -3082,7 +3021,7 @@ bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) { StrCat("Some"); } PushParen paren(*this); - StrCat('|'); + StrCat("|"); for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) { StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()), token::kComma); @@ -3094,7 +3033,7 @@ bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) { curr_function_ = expr->getLambdaClass()->getLambdaCallOperator(); ConvertFunctionBody(curr_function_); curr_function_ = old_function; - StrCat('}'); + StrCat("}"); return false; } @@ -3121,7 +3060,7 @@ bool Converter::ConvertSwitchCaseCondition(clang::SwitchCase *stmt) { while (auto *sc = clang::dyn_cast(cur)) { if (auto *case_stmt = clang::dyn_cast(sc)) { if (!first) { - StrCat("|| __v == "); + StrCat("|| v == "); } Convert(case_stmt->getLHS()); } @@ -3143,7 +3082,7 @@ void Converter::EmitSwitchArm(clang::CompoundStmt *body, clang::SwitchCase *sc, if (is_default) { StrCat("_ => {"); } else { - StrCat("__v if __v == "); + StrCat("v if v == "); ConvertSwitchCaseCondition(sc); } for (auto *t : GetSwitchCaseBody(body, sc)) { @@ -3393,7 +3332,7 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) { return {"Copy", "Clone"}; } - std::vector struct_attrs; + std::vector struct_attrs = {}; if (RecordDerivesCopy(decl)) { struct_attrs.emplace_back("Copy"); @@ -3519,42 +3458,8 @@ void Converter::ConvertPointerOffset(clang::Expr *base, clang::Expr *idx, computed_expr_type_ = ComputedExprType::FreshPointer; } -static bool IsFlexibleArrayMemberAccess(clang::ASTContext &ctx, - clang::Expr *array) { - return array->isFlexibleArrayMemberLike( - ctx, clang::LangOptions::StrictFlexArraysLevelKind::OneZeroOrIncomplete, - /*IgnoreTemplateOrMacroSubstitution=*/true); -} - -void Converter::EmitFlexibleArrayElementPtr(clang::Expr *array, - clang::Expr *idx, bool is_mut) { - { - PushExplicitAutoref no_autoref(*this, std::nullopt); - Convert(array); - } - StrCat(is_mut ? ".as_mut_ptr()" : ".as_ptr()", ".add"); - { - PushParen call(*this); - { - PushParen paren(*this); - Convert(idx); - } - StrCat(keyword::kAs, "usize"); - } -} - void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx, clang::QualType type) { - if (auto inner = base->IgnoreImplicit()) { - if (inner->getType()->isArrayType() && - IsFlexibleArrayMemberAccess(ctx_, inner)) { - PushParen outer(*this); - StrCat(token::kStar); - EmitFlexibleArrayElementPtr(inner, idx, - !inner->getType().isConstQualified()); - return; - } - } if (IsUniquePtr(base->getType())) { PushExplicitAutoref no_autoref(*this, std::nullopt); Convert(base->IgnoreImplicit()); @@ -3670,21 +3575,21 @@ void Converter::ConvertOrdAndPartialOrdTraitsBase( std::string_view first_branch, std::string_view second_branch, std::string_view first_return, std::string_view second_return, std::string_view record_name) { - StrCat(keyword::kImpl, "Ord for ", record_name, '{'); + StrCat(keyword::kImpl, "Ord for ", record_name, "{"); StrCat("fn cmp(&self, other: &Self) -> std::cmp::Ordering {"); StrCat(std::format("{} {{", keyword_unsafe_)); - StrCat("if", first_branch, '{', first_return, "} else if", second_branch, '{', + StrCat("if", first_branch, "{", first_return, "} else if", second_branch, "{", second_return, "} else { std::cmp::Ordering::Equal }"); StrCat("}}}"); - StrCat(keyword::kImpl, "PartialOrd for", record_name, '{'); + StrCat(keyword::kImpl, "PartialOrd for", record_name, "{"); StrCat(R"( fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } })"); - StrCat(keyword::kImpl, "PartialEq for", record_name, '{'); + StrCat(keyword::kImpl, "PartialEq for", record_name, "{"); StrCat("fn eq(&self, other: &Self) -> bool {"); StrCat(std::format("{} {{", keyword_unsafe_)); StrCat("!(", first_branch, ") && !(", second_branch, ')'); @@ -3701,7 +3606,7 @@ void Converter::ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, case clang::OO_Less: if (clang::isa(op)) { first_branch = std::format("self.{}(other)", GetOverloadedOperator(op)); - second_branch = std::format("other.{}(self)", GetOverloadedOperator(op)); + second_branch = std::format("other.{}(other)", GetOverloadedOperator(op)); } else { first_branch = std::format("{}(self, other)", GetOverloadedOperator(op)); second_branch = std::format("{}(other, self)", GetOverloadedOperator(op)); @@ -3857,19 +3762,6 @@ void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op, void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) { assert(pointer_type->isPointerType()); - if (auto ase = - clang::dyn_cast(expr->IgnoreParens())) { - auto base = ase->getBase(); - auto inner = base->IgnoreImplicit(); - if (base->IgnoreCasts()->getType()->isArrayType() && - IsFlexibleArrayMemberAccess(ctx_, inner)) { - EmitFlexibleArrayElementPtr( - inner, ase->getIdx(), - !pointer_type->getPointeeType().isConstQualified()); - computed_expr_type_ = ComputedExprType::FreshPointer; - return; - } - } if (IsReferenceType(expr) || pointer_type->isFunctionPointerType()) { PushExprKind push(*this, ExprKind::AddrOf); Convert(expr); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 6292335c..4f396c66 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -224,6 +224,39 @@ class Converter : public clang::RecursiveASTVisitor { std::optional ConvertCallExpr(clang::CallExpr *expr); + struct CallArg { + enum class Kind { + Hoisted, + Inline, + Materialized, + }; + + clang::Expr *expr; + Kind kind; + std::string param_name; + clang::QualType param_type; + bool has_default; + std::string ref_temp_name; + }; + + struct CallInfo { + clang::Expr *callee; + bool is_variadic; + bool is_fn_ptr_call; + std::vector args; + std::vector variadic_args; + }; + + CallInfo CollectCallInfo(clang::CallExpr *expr); + + void ConvertParamTy(clang::QualType param_type, clang::Expr *expr); + + void EmitArgBindings(CallInfo &info); + + void EmitArgList(const CallInfo &info); + + void EmitCall(CallInfo info); + void ConvertGenericCallExpr(clang::CallExpr *expr); virtual void EmitFnPtrCall(clang::Expr *callee); From 8422cda3d88e36f365fbfe89139f9934d65a2b11 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 23 May 2026 18:44:57 +0100 Subject: [PATCH 02/26] EmitArgBindings -> EmitHoistedArgs --- cpp2rust/converter/converter.cpp | 4 ++-- cpp2rust/converter/converter.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index ed900968..f3a12cf1 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1586,7 +1586,7 @@ void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) { } } -void Converter::EmitArgBindings(CallInfo &info) { +void Converter::EmitHoistedArgs(CallInfo &info) { using Kind = CallArg::Kind; for (auto &ca : info.args) { switch (ca.kind) { @@ -1651,7 +1651,7 @@ void Converter::EmitArgList(const CallInfo &info) { } void Converter::EmitCall(CallInfo info) { - EmitArgBindings(info); + EmitHoistedArgs(info); if (info.is_fn_ptr_call) { EmitFnPtrCall(info.callee); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 4f396c66..b09d9b50 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -251,7 +251,7 @@ class Converter : public clang::RecursiveASTVisitor { void ConvertParamTy(clang::QualType param_type, clang::Expr *expr); - void EmitArgBindings(CallInfo &info); + void EmitHoistedArgs(CallInfo &info); void EmitArgList(const CallInfo &info); From 79a09922b821a3ec07c257243d015bd440865f23 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 24 May 2026 12:14:19 +0100 Subject: [PATCH 03/26] Drop default initialization fom CallInfo --- cpp2rust/converter/converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index f3a12cf1..4d3310ae 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1520,7 +1520,7 @@ void Converter::ConvertFunctionToFunctionPointer( Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { using Kind = CallArg::Kind; - CallInfo info{}; + CallInfo info; info.callee = expr->getCallee(); unsigned arg_begin = 0; if (auto op_call = llvm::dyn_cast(expr)) { From 0e9484ff2419aa8998a0df2eac5781b94af72c1f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 24 May 2026 12:14:50 +0100 Subject: [PATCH 04/26] Add prefix on param_name from construction --- cpp2rust/converter/converter.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 4d3310ae..1e312087 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1553,8 +1553,9 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { CallArg ca{ .expr = arg, .kind = Kind::Hoisted, - .param_name = function ? function->getParamDecl(i)->getNameAsString() - : ("arg" + std::to_string(i)), + .param_name = function + ? ("_" + function->getParamDecl(i)->getNameAsString()) + : ("_arg" + std::to_string(i)), .param_type = function ? function->getParamDecl(i)->getType() : proto->getParamType(i), .has_default = function && function->getParamDecl(i)->hasDefaultArg(), @@ -1591,15 +1592,14 @@ void Converter::EmitHoistedArgs(CallInfo &info) { for (auto &ca : info.args) { switch (ca.kind) { case Kind::Hoisted: - StrCat("let", - std::format("_{}: {}", ca.param_name, ToString(ca.param_type)), - "="); + StrCat( + std::format("let {}: {} =", ca.param_name, ToString(ca.param_type))); ConvertParamTy(ca.param_type, ca.expr); StrCat(";"); break; case Kind::Materialized: { - auto [binding, ref] = MaterializeTemp(std::format("_{}", ca.param_name), - ca.param_type, ca.expr); + auto [binding, ref] = + MaterializeTemp(ca.param_name, ca.param_type, ca.expr); StrCat(binding); ca.ref_temp_name = std::move(ref); break; @@ -1623,7 +1623,7 @@ void Converter::EmitArgList(const CallInfo &info) { PushParen push(*this, ca.has_default); switch (ca.kind) { case Kind::Hoisted: - StrCat(std::format("_{}", ca.param_name)); + StrCat(ca.param_name); break; case Kind::Materialized: StrCat(ca.ref_temp_name); From 49ef2b1c2f694a3230c24e550b23d28985d31d53 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 24 May 2026 12:15:11 +0100 Subject: [PATCH 05/26] Pass CallInfo by rvalue reference --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 1e312087..68a1c329 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1650,7 +1650,7 @@ void Converter::EmitArgList(const CallInfo &info) { } } -void Converter::EmitCall(CallInfo info) { +void Converter::EmitCall(CallInfo &&info) { EmitHoistedArgs(info); if (info.is_fn_ptr_call) { diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b09d9b50..b930317e 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -255,7 +255,7 @@ class Converter : public clang::RecursiveASTVisitor { void EmitArgList(const CallInfo &info); - void EmitCall(CallInfo info); + void EmitCall(CallInfo &&info); void ConvertGenericCallExpr(clang::CallExpr *expr); From fc309a6eb0c5572b88dc44e9a986315ca1ff9673 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 24 May 2026 12:15:27 +0100 Subject: [PATCH 06/26] Reorder CallArg fields by size --- cpp2rust/converter/converter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b930317e..b1f46f58 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -225,18 +225,18 @@ class Converter : public clang::RecursiveASTVisitor { std::optional ConvertCallExpr(clang::CallExpr *expr); struct CallArg { - enum class Kind { + enum class Kind : int8_t { Hoisted, Inline, Materialized, }; - clang::Expr *expr; - Kind kind; std::string param_name; + std::string ref_temp_name; clang::QualType param_type; + clang::Expr *expr; bool has_default; - std::string ref_temp_name; + Kind kind; }; struct CallInfo { From be780905b2f48273ce99efa51d9e89b49849e673 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 24 May 2026 12:18:54 +0100 Subject: [PATCH 07/26] Reorder initialization --- cpp2rust/converter/converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 68a1c329..c16223c2 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1551,14 +1551,14 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); CallArg ca{ - .expr = arg, - .kind = Kind::Hoisted, .param_name = function ? ("_" + function->getParamDecl(i)->getNameAsString()) : ("_arg" + std::to_string(i)), .param_type = function ? function->getParamDecl(i)->getType() : proto->getParamType(i), + .expr = arg, .has_default = function && function->getParamDecl(i)->hasDefaultArg(), + .kind = Kind::Hoisted, }; bool is_materialize = clang::isa(arg); if (is_materialize && ca.param_type->isLValueReferenceType()) { From a6058530669bb9a1d3a61c79e4838f55599f2973 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 27 May 2026 21:44:32 +0100 Subject: [PATCH 08/26] Reorder CallInfo fields --- cpp2rust/converter/converter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b1f46f58..9d64ef22 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -240,11 +240,11 @@ class Converter : public clang::RecursiveASTVisitor { }; struct CallInfo { + std::vector args; + std::vector variadic_args; clang::Expr *callee; bool is_variadic; bool is_fn_ptr_call; - std::vector args; - std::vector variadic_args; }; CallInfo CollectCallInfo(clang::CallExpr *expr); From a785482515363faec2a3faef5fcb160fcaa7d320 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 15:04:20 +0100 Subject: [PATCH 09/26] Add codegen support for goto (#163) Depends on #161. See #161 for a more detailed description of how goto is implemented. On the codegen part, this PR emits the goto_block and goto macros when it hits functions that use labels and gotos internally. It also hoists local variables at the start of the function so that all goto_block arms can see and use the local variables. --- cpp2rust/converter/converter.cpp | 74 +++++++++++++++++++ tests/unit/out/refcount/goto_cleanup.rs | 2 +- .../out/refcount/goto_switch_fallthrough.rs | 4 +- tests/unit/out/unsafe/goto_cleanup.rs | 2 +- .../out/unsafe/goto_switch_fallthrough.rs | 4 +- 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index c16223c2..bcef5bdc 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -363,7 +363,59 @@ bool Converter::VisitFunctionDecl(clang::FunctionDecl *decl) { return false; } +void Converter::EmitHoistedDecls(clang::CompoundStmt *body) { + for (auto *child : body->body()) { + if (auto *decl_stmt = clang::dyn_cast(child)) { + for (auto *decl : decl_stmt->decls()) { + if (auto *var = clang::dyn_cast(decl); + var && var->isLocalVarDecl() && !IsGlobalVar(var)) { + hoisted_decls_.insert(var); + if (ConvertVarDeclSkipInit(var)) { + StrCat(token::kAssign, ConvertVarDefaultInit(var->getType()), + token::kSemiColon); + } + } + } + } + } +} + +void Converter::ConvertGotoBlock(clang::CompoundStmt *body) { + PushHoistedDecls push(hoisted_decls_); + EmitHoistedDecls(body); + + StrCat("goto_block!"); + { + PushParen paren(*this); + PushBrace outer(*this); + StrCat("'__entry: "); + std::optional arm; + arm.emplace(*this); + for (auto *child : body->body()) { + if (auto *label = clang::dyn_cast(child)) { + arm.reset(); + StrCat(std::format("'{}: ", label->getDecl()->getName().str())); + arm.emplace(*this); + Convert(label->getSubStmt()); + } else { + Convert(child); + } + } + } + StrCat(token::kSemiColon); +} + void Converter::ConvertFunctionBody(clang::FunctionDecl *decl) { + if (auto compound = clang::dyn_cast(decl->getBody())) { + if (CompoundHasTopLevelLabel(compound)) { + ConvertGotoBlock(compound); + if (!decl->getReturnType()->isVoidType()) { + StrCat(R"(panic!("ub: non-void function does not return a value"))"); + } + return; + } + } + Convert(decl->getBody()); if (!decl->getReturnType()->isVoidType()) { if (auto compound = clang::dyn_cast(decl->getBody())) { @@ -466,7 +518,20 @@ void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) { } } +void Converter::EmitHoistedInArmAssignment(clang::VarDecl *decl) { + if (!decl->hasInit()) { + return; + } + StrCat(GetNamedDeclAsString(decl), token::kAssign); + ConvertVarInit(decl->getType(), decl->getInit()); + StrCat(token::kSemiColon); +} + void Converter::ConvertVarDecl(clang::VarDecl *decl) { + if (hoisted_decls_.contains(decl)) { + EmitHoistedInArmAssignment(decl); + return; + } if (!ConvertVarDeclSkipInit(decl)) { // Skip global variables declared extern return; @@ -984,6 +1049,10 @@ bool Converter::Convert(clang::Stmt *stmt) { } bool Converter::VisitCompoundStmt(clang::CompoundStmt *stmt) { + if (CompoundHasTopLevelLabel(stmt)) { + ConvertGotoBlock(stmt); + return false; + } for (auto *child : stmt->body()) { Convert(child); } @@ -1010,6 +1079,11 @@ bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) { return false; } +bool Converter::VisitGotoStmt(clang::GotoStmt *stmt) { + StrCat(std::format("goto!('{})", stmt->getLabel()->getName().str())); + return false; +} + void Converter::ConvertCondition(clang::Expr *cond) { PushExprKind push(*this, ExprKind::RValue); Convert(NormalizeToBool(cond, ctx_)); diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs index 4ba868ee..a44fb20f 100644 --- a/tests/unit/out/refcount/goto_cleanup.rs +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -56,7 +56,7 @@ pub fn from_switch_2(n: i32) -> i32 { 'switch: { let __match_cond = (*n.borrow()); match __match_cond { - __v if __v == 1 => { + v if v == 1 => { (*ret.borrow_mut()) = 10; goto!('out); } diff --git a/tests/unit/out/refcount/goto_switch_fallthrough.rs b/tests/unit/out/refcount/goto_switch_fallthrough.rs index 68d74f8d..74581ceb 100644 --- a/tests/unit/out/refcount/goto_switch_fallthrough.rs +++ b/tests/unit/out/refcount/goto_switch_fallthrough.rs @@ -13,10 +13,10 @@ pub fn sm_0(n: i32) -> i32 { '__entry: { *ret.borrow_mut() = 0; switch!(match (*n.borrow()) { - __v if __v == 0 => { + v if v == 0 => { (*ret.borrow_mut()) += 1; } - __v if __v == 1 => { + v if v == 1 => { (*ret.borrow_mut()) += 10; goto!('out); } diff --git a/tests/unit/out/unsafe/goto_cleanup.rs b/tests/unit/out/unsafe/goto_cleanup.rs index 6cba984d..b4bcb361 100644 --- a/tests/unit/out/unsafe/goto_cleanup.rs +++ b/tests/unit/out/unsafe/goto_cleanup.rs @@ -53,7 +53,7 @@ pub unsafe fn from_switch_2(mut n: i32) -> i32 { 'switch: { let __match_cond = n; match __match_cond { - __v if __v == 1 => { + v if v == 1 => { ret = 10; goto!('out); } diff --git a/tests/unit/out/unsafe/goto_switch_fallthrough.rs b/tests/unit/out/unsafe/goto_switch_fallthrough.rs index 81961849..af0011cd 100644 --- a/tests/unit/out/unsafe/goto_switch_fallthrough.rs +++ b/tests/unit/out/unsafe/goto_switch_fallthrough.rs @@ -12,10 +12,10 @@ pub unsafe fn sm_0(mut n: i32) -> i32 { '__entry: { ret = 0; switch!(match n { - __v if __v == 0 => { + v if v == 0 => { ret += 1; } - __v if __v == 1 => { + v if v == 1 => { ret += 10; goto!('out); } From 42a07be7a6dc931b0eb285a2e7939166ca52e700 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 1 Jun 2026 21:04:40 +0100 Subject: [PATCH 10/26] Translate access to flexible array member (#165) Changed the access pattern of flexible arrays from ```rs let bytes: [u8; 1] = ...; bytes[10] = ... ``` to ```rs let bytes: [u8; 1] = ...; *bytes.as_mut_ptr().add(10) = ...; ``` The original access panics because the bounds are violated, i.e. accessing the 10th element in an array declared with 1 element. The new access does not panic, it preserves the C/C++ behavior. --- cpp2rust/converter/converter.cpp | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index bcef5bdc..b83b378e 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -3532,8 +3533,42 @@ void Converter::ConvertPointerOffset(clang::Expr *base, clang::Expr *idx, computed_expr_type_ = ComputedExprType::FreshPointer; } +static bool IsFlexibleArrayMemberAccess(clang::ASTContext &ctx, + clang::Expr *array) { + return array->isFlexibleArrayMemberLike( + ctx, clang::LangOptions::StrictFlexArraysLevelKind::OneZeroOrIncomplete, + /*IgnoreTemplateOrMacroSubstitution=*/true); +} + +void Converter::EmitFlexibleArrayElementPtr(clang::Expr *array, + clang::Expr *idx, bool is_mut) { + { + PushExplicitAutoref no_autoref(*this, std::nullopt); + Convert(array); + } + StrCat(is_mut ? ".as_mut_ptr()" : ".as_ptr()", ".add"); + { + PushParen call(*this); + { + PushParen paren(*this); + Convert(idx); + } + StrCat(keyword::kAs, "usize"); + } +} + void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx, clang::QualType type) { + if (auto inner = base->IgnoreImplicit()) { + if (inner->getType()->isArrayType() && + IsFlexibleArrayMemberAccess(ctx_, inner)) { + PushParen outer(*this); + StrCat(token::kStar); + EmitFlexibleArrayElementPtr(inner, idx, + !inner->getType().isConstQualified()); + return; + } + } if (IsUniquePtr(base->getType())) { PushExplicitAutoref no_autoref(*this, std::nullopt); Convert(base->IgnoreImplicit()); @@ -3836,6 +3871,19 @@ void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op, void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) { assert(pointer_type->isPointerType()); + if (auto ase = + clang::dyn_cast(expr->IgnoreParens())) { + auto base = ase->getBase(); + auto inner = base->IgnoreImplicit(); + if (base->IgnoreCasts()->getType()->isArrayType() && + IsFlexibleArrayMemberAccess(ctx_, inner)) { + EmitFlexibleArrayElementPtr( + inner, ase->getIdx(), + !pointer_type->getPointeeType().isConstQualified()); + computed_expr_type_ = ComputedExprType::FreshPointer; + return; + } + } if (IsReferenceType(expr) || pointer_type->isFunctionPointerType()) { PushExprKind push(*this, ExprKind::AddrOf); Convert(expr); From 15fbe1d22accfff3f55a1c379425c48d67b25bd8 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 15:06:32 +0100 Subject: [PATCH 11/26] Avoid collision between tag and typedef identifier (#166) C allows name collision between the following 2 declarations: ```c struct X {}; typedef enum {} X; ``` The 2 declarations live in different name spaces: 6.2.3 Name spaces of identifiers [...] there are separate namespaces for various categories of identifiers, as follows: 1. the tags of structures, unions, and enumerations (disambiguated by following any) of the keywords struct, union, or enum); 2. all other identifiers, called ordinary identifiers (declared in ordinary declarators or as enumeration constants). `struct X {}` lives in namespace 1 and `typedef enum {} X` lives in namespace 2. We cannot translate both to `X` in the Rust code. We need to disambiguate between the 2 names. I chose to translate them as: ``` struct X {} -> X typedef enum {} X -> X_enum ``` C++ does not have the name space rule for identifiers. I added the `tag->getASTContext().getLangOpts().CPlusPlus` check in DisambiguateAnonymousTag to avoid polluting the C++ generated files. --------- Co-authored-by: Nuno Lopes --- tests/unit/out/refcount/anonymous_enum_c.rs | 74 ++++++++++--------- tests/unit/out/refcount/enum_int_interop_c.rs | 26 +++---- tests/unit/out/unsafe/anonymous_enum_c.rs | 72 +++++++++--------- tests/unit/out/unsafe/enum_int_interop_c.rs | 20 ++--- .../out/unsafe/union_void_ptr_sized_deref.rs | 6 +- 5 files changed, 100 insertions(+), 98 deletions(-) diff --git a/tests/unit/out/refcount/anonymous_enum_c.rs b/tests/unit/out/refcount/anonymous_enum_c.rs index 772ea6e5..d4d31b73 100644 --- a/tests/unit/out/refcount/anonymous_enum_c.rs +++ b/tests/unit/out/refcount/anonymous_enum_c.rs @@ -7,37 +7,37 @@ use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_0 { +enum anon_enum_3 { #[default] FIRST_A = 0, FIRST_B = 1, } -impl From for anon_0 { - fn from(n: i32) -> anon_0 { +impl From for anon_enum_3 { + fn from(n: i32) -> anon_enum_3 { match n { - 0 => anon_0::FIRST_A, - 1 => anon_0::FIRST_B, - _ => panic!("invalid anon_0 value: {}", n), + 0 => anon_enum_3::FIRST_A, + 1 => anon_enum_3::FIRST_B, + _ => panic!("invalid anon_enum_3 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_0); +libcc2rs::impl_enum_inc_dec!(anon_enum_3); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_1 { +enum anon_enum_11 { #[default] SECOND_A = 0, SECOND_B = 1, } -impl From for anon_1 { - fn from(n: i32) -> anon_1 { +impl From for anon_enum_11 { + fn from(n: i32) -> anon_enum_11 { match n { - 0 => anon_1::SECOND_A, - 1 => anon_1::SECOND_B, - _ => panic!("invalid anon_1 value: {}", n), + 0 => anon_enum_11::SECOND_A, + 1 => anon_enum_11::SECOND_B, + _ => panic!("invalid anon_enum_11 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_1); +libcc2rs::impl_enum_inc_dec!(anon_enum_11); #[derive(Default)] pub struct S { pub a: Value, @@ -69,25 +69,25 @@ impl From for TdEnum_enum { } libcc2rs::impl_enum_inc_dec!(TdEnum_enum); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_2 { +enum anon_enum_24 { #[default] FIELD_A = 0, FIELD_B = 1, } -impl From for anon_2 { - fn from(n: i32) -> anon_2 { +impl From for anon_enum_24 { + fn from(n: i32) -> anon_enum_24 { match n { - 0 => anon_2::FIELD_A, - 1 => anon_2::FIELD_B, - _ => panic!("invalid anon_2 value: {}", n), + 0 => anon_enum_24::FIELD_A, + 1 => anon_enum_24::FIELD_B, + _ => panic!("invalid anon_enum_24 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_2); +libcc2rs::impl_enum_inc_dec!(anon_enum_24); #[derive(Default)] pub struct WithAnonField { pub a: Value, - pub field: Value, + pub field: Value, } impl ByteRepr for WithAnonField {} pub fn main() { @@ -95,37 +95,39 @@ pub fn main() { } fn main_0() -> i32 { #[derive(Clone, Copy, PartialEq, Debug, Default)] - enum anon_3 { + enum anon_enum_31 { #[default] THIRD_A = 0, THIRD_B = 1, } - impl From for anon_3 { - fn from(n: i32) -> anon_3 { + impl From for anon_enum_31 { + fn from(n: i32) -> anon_enum_31 { match n { - 0 => anon_3::THIRD_A, - 1 => anon_3::THIRD_B, - _ => panic!("invalid anon_3 value: {}", n), + 0 => anon_enum_31::THIRD_A, + 1 => anon_enum_31::THIRD_B, + _ => panic!("invalid anon_enum_31 value: {}", n), } } } - libcc2rs::impl_enum_inc_dec!(anon_3); - assert!(((((anon_0::FIRST_A as i32) != (anon_0::FIRST_B as i32)) as i32) != 0)); - assert!(((((anon_1::SECOND_A as i32) != (anon_1::SECOND_B as i32)) as i32) != 0)); - assert!(((((anon_3::THIRD_A as i32) != (anon_3::THIRD_B as i32)) as i32) != 0)); + libcc2rs::impl_enum_inc_dec!(anon_enum_31); + assert!(((((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32)) as i32) != 0)); + assert!(((((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32)) as i32) != 0)); + assert!(((((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32)) as i32) != 0)); let td: Value = Rc::new(RefCell::new(TdEnum_enum::TD_A)); assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_A as i32) as u32)) as i32) != 0)); (*td.borrow_mut()) = TdEnum_enum::TD_B; assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_B as i32) as u32)) as i32) != 0)); let w: Value = >::default(); - (*(*w.borrow()).field.borrow_mut()) = anon_2::FIELD_A; + (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::FIELD_A; assert!( - (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_2::FIELD_A as i32) as u32)) as i32) + (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_A as i32) as u32)) + as i32) != 0) ); - (*(*w.borrow()).field.borrow_mut()) = anon_2::FIELD_B; + (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::FIELD_B; assert!( - (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_2::FIELD_B as i32) as u32)) as i32) + (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_B as i32) as u32)) + as i32) != 0) ); return 0; diff --git a/tests/unit/out/refcount/enum_int_interop_c.rs b/tests/unit/out/refcount/enum_int_interop_c.rs index 817170c6..721a1cd1 100644 --- a/tests/unit/out/refcount/enum_int_interop_c.rs +++ b/tests/unit/out/refcount/enum_int_interop_c.rs @@ -81,17 +81,17 @@ thread_local!( thread_local!( pub static entries_3: Value> = Rc::new(RefCell::new(Box::new([ Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal(b"first"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal("first"))), color: Rc::new(RefCell::new(Color::RED)), opt: Rc::new(RefCell::new(Option::OPT_NONE)), }, Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal(b"second"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal("second"))), color: Rc::new(RefCell::new(Color::GREEN)), opt: Rc::new(RefCell::new(Option::OPT_A)), }, Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal(b"third"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal("third"))), color: Rc::new(RefCell::new(Color::BLUE)), opt: Rc::new(RefCell::new(Option::OPT_C)), }, @@ -106,16 +106,16 @@ pub fn classify_option_5(option: i32) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - __v if __v == (Option::OPT_NONE as i32) => { + v if v == (Option::OPT_NONE as i32) => { return -1_i32; } - __v if __v == (Option::OPT_A as i32) => { + v if v == (Option::OPT_A as i32) => { return 1; } - __v if __v == (Option::OPT_B as i32) => { + v if v == (Option::OPT_B as i32) => { return 2; } - __v if __v == (Option::OPT_C as i32) => { + v if v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -143,13 +143,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*c.borrow()) as u32); match __match_cond { - __v if __v == (0 as u32) => { + v if v == (0 as u32) => { break 'switch; } - __v if __v == (1 as u32) => { + v if v == (1 as u32) => { return 1; } - __v if __v == (2 as u32) => { + v if v == (2 as u32) => { return 2; } _ => { @@ -209,13 +209,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*t.borrow()) as u32); match __match_cond { - __v if __v == ((Tag_enum::TAG_ZERO as i32) as u32) => { + v if v == ((Tag_enum::TAG_ZERO as i32) as u32) => { return 90; } - __v if __v == (1 as u32) => { + v if v == (1 as u32) => { return 91; } - __v if __v == (2 as u32) => { + v if v == (2 as u32) => { break 'switch; } _ => {} diff --git a/tests/unit/out/unsafe/anonymous_enum_c.rs b/tests/unit/out/unsafe/anonymous_enum_c.rs index 8bdf3f73..260b7a26 100644 --- a/tests/unit/out/unsafe/anonymous_enum_c.rs +++ b/tests/unit/out/unsafe/anonymous_enum_c.rs @@ -7,37 +7,37 @@ use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_0 { +enum anon_enum_3 { #[default] FIRST_A = 0, FIRST_B = 1, } -impl From for anon_0 { - fn from(n: i32) -> anon_0 { +impl From for anon_enum_3 { + fn from(n: i32) -> anon_enum_3 { match n { - 0 => anon_0::FIRST_A, - 1 => anon_0::FIRST_B, - _ => panic!("invalid anon_0 value: {}", n), + 0 => anon_enum_3::FIRST_A, + 1 => anon_enum_3::FIRST_B, + _ => panic!("invalid anon_enum_3 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_0); +libcc2rs::impl_enum_inc_dec!(anon_enum_3); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_1 { +enum anon_enum_11 { #[default] SECOND_A = 0, SECOND_B = 1, } -impl From for anon_1 { - fn from(n: i32) -> anon_1 { +impl From for anon_enum_11 { + fn from(n: i32) -> anon_enum_11 { match n { - 0 => anon_1::SECOND_A, - 1 => anon_1::SECOND_B, - _ => panic!("invalid anon_1 value: {}", n), + 0 => anon_enum_11::SECOND_A, + 1 => anon_enum_11::SECOND_B, + _ => panic!("invalid anon_enum_11 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_1); +libcc2rs::impl_enum_inc_dec!(anon_enum_11); #[repr(C)] #[derive(Copy, Clone, Default)] pub struct S { @@ -60,26 +60,26 @@ impl From for TdEnum_enum { } libcc2rs::impl_enum_inc_dec!(TdEnum_enum); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_2 { +enum anon_enum_24 { #[default] FIELD_A = 0, FIELD_B = 1, } -impl From for anon_2 { - fn from(n: i32) -> anon_2 { +impl From for anon_enum_24 { + fn from(n: i32) -> anon_enum_24 { match n { - 0 => anon_2::FIELD_A, - 1 => anon_2::FIELD_B, - _ => panic!("invalid anon_2 value: {}", n), + 0 => anon_enum_24::FIELD_A, + 1 => anon_enum_24::FIELD_B, + _ => panic!("invalid anon_enum_24 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_2); +libcc2rs::impl_enum_inc_dec!(anon_enum_24); #[repr(C)] #[derive(Copy, Clone, Default)] pub struct WithAnonField { pub a: i32, - pub field: anon_2, + pub field: anon_enum_24, } pub fn main() { unsafe { @@ -88,32 +88,32 @@ pub fn main() { } unsafe fn main_0() -> i32 { #[derive(Clone, Copy, PartialEq, Debug, Default)] - enum anon_3 { + enum anon_enum_31 { #[default] THIRD_A = 0, THIRD_B = 1, } - impl From for anon_3 { - fn from(n: i32) -> anon_3 { + impl From for anon_enum_31 { + fn from(n: i32) -> anon_enum_31 { match n { - 0 => anon_3::THIRD_A, - 1 => anon_3::THIRD_B, - _ => panic!("invalid anon_3 value: {}", n), + 0 => anon_enum_31::THIRD_A, + 1 => anon_enum_31::THIRD_B, + _ => panic!("invalid anon_enum_31 value: {}", n), } } } - libcc2rs::impl_enum_inc_dec!(anon_3); - assert!(((((anon_0::FIRST_A as i32) != (anon_0::FIRST_B as i32)) as i32) != 0)); - assert!(((((anon_1::SECOND_A as i32) != (anon_1::SECOND_B as i32)) as i32) != 0)); - assert!(((((anon_3::THIRD_A as i32) != (anon_3::THIRD_B as i32)) as i32) != 0)); + libcc2rs::impl_enum_inc_dec!(anon_enum_31); + assert!(((((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32)) as i32) != 0)); + assert!(((((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32)) as i32) != 0)); + assert!(((((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32)) as i32) != 0)); let mut td: TdEnum_enum = TdEnum_enum::TD_A; assert!(((((td as u32) == ((TdEnum_enum::TD_A as i32) as u32)) as i32) != 0)); td = (TdEnum_enum::TD_B).clone(); assert!(((((td as u32) == ((TdEnum_enum::TD_B as i32) as u32)) as i32) != 0)); let mut w: WithAnonField = ::default(); - w.field = anon_2::FIELD_A; - assert!(((((w.field as u32) == ((anon_2::FIELD_A as i32) as u32)) as i32) != 0)); - w.field = (anon_2::FIELD_B).clone(); - assert!(((((w.field as u32) == ((anon_2::FIELD_B as i32) as u32)) as i32) != 0)); + w.field = anon_enum_24::FIELD_A; + assert!(((((w.field as u32) == ((anon_enum_24::FIELD_A as i32) as u32)) as i32) != 0)); + w.field = (anon_enum_24::FIELD_B).clone(); + assert!(((((w.field as u32) == ((anon_enum_24::FIELD_B as i32) as u32)) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/enum_int_interop_c.rs b/tests/unit/out/unsafe/enum_int_interop_c.rs index f4e9feeb..56d90545 100644 --- a/tests/unit/out/unsafe/enum_int_interop_c.rs +++ b/tests/unit/out/unsafe/enum_int_interop_c.rs @@ -98,16 +98,16 @@ pub unsafe fn classify_option_5(mut option: i32) -> i32 { 'switch: { let __match_cond = option; match __match_cond { - __v if __v == (Option::OPT_NONE as i32) => { + v if v == (Option::OPT_NONE as i32) => { return -1_i32; } - __v if __v == (Option::OPT_A as i32) => { + v if v == (Option::OPT_A as i32) => { return 1; } - __v if __v == (Option::OPT_B as i32) => { + v if v == (Option::OPT_B as i32) => { return 2; } - __v if __v == (Option::OPT_C as i32) => { + v if v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -136,13 +136,13 @@ unsafe fn main_0() -> i32 { 'switch: { let __match_cond = (c as u32); match __match_cond { - __v if __v == (0 as u32) => { + v if v == (0 as u32) => { break 'switch; } - __v if __v == (1 as u32) => { + v if v == (1 as u32) => { return 1; } - __v if __v == (2 as u32) => { + v if v == (2 as u32) => { return 2; } _ => { @@ -196,13 +196,13 @@ unsafe fn main_0() -> i32 { 'switch: { let __match_cond = (t as u32); match __match_cond { - __v if __v == ((Tag_enum::TAG_ZERO as i32) as u32) => { + v if v == ((Tag_enum::TAG_ZERO as i32) as u32) => { return 90; } - __v if __v == (1 as u32) => { + v if v == (1 as u32) => { return 91; } - __v if __v == (2 as u32) => { + v if v == (2 as u32) => { break 'switch; } _ => {} diff --git a/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs b/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs index 25afdaa7..79556876 100644 --- a/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs +++ b/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs @@ -47,15 +47,15 @@ pub unsafe fn write_count_1(mut s: *mut Sink, mut count: i64) { 'switch: { let __match_cond = ((*s).width as u32); match __match_cond { - __v if __v == ((Width_enum::W_64 as i32) as u32) => { + v if v == ((Width_enum::W_64 as i32) as u32) => { (*((*s).out.handle as *mut i64)) = count; break 'switch; } - __v if __v == ((Width_enum::W_32 as i32) as u32) => { + v if v == ((Width_enum::W_32 as i32) as u32) => { (*((*s).out.handle as *mut i32)) = (count as i32); break 'switch; } - __v if __v == ((Width_enum::W_16 as i32) as u32) => { + v if v == ((Width_enum::W_16 as i32) as u32) => { (*((*s).out.handle as *mut i16)) = (count as i16); break 'switch; } From a194df994cca7b5864456300fdeef1a354dc431c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 15:18:22 +0100 Subject: [PATCH 12/26] Handle non-printable ASCII in str and byte arrays (#169) `"\xff"` or `'\xff'` are valid strings/char literals in C/C++. They were not escaped and produced an invalid character that broke the compilation. After I escaped them, I realized that they cannot be translated to `\xff` in Rust `str`'s because str only accepts `\x00 - \x7f`. Byte array accepts `> \x7f`. I changed Ptr::from_string_literal to accept a byte array instead of a str. I also changed GetFmtArg to refuse to create Rust `str` that contains chars `> \x7f` and fallback to GetRawArg that produces escaped byte arrays. --- cpp2rust/converter/converter.cpp | 13 +- tests/unit/out/refcount/enum_int_interop.rs | 20 +-- tests/unit/out/refcount/enum_int_interop_c.rs | 6 +- tests/unit/out/refcount/string.rs | 138 +++++++++--------- tests/unit/out/refcount/string2.rs | 2 +- tests/unit/out/refcount/string_literals.rs | 7 - tests/unit/out/refcount/string_literals_c.rs | 7 - tests/unit/out/refcount/va_arg_conditional.rs | 4 +- .../out/refcount/va_arg_non_primitive_ptrs.rs | 16 +- tests/unit/out/refcount/va_arg_printf.rs | 13 +- tests/unit/out/refcount/va_arg_snprintf.rs | 4 +- tests/unit/out/refcount/va_arg_struct_ctx.rs | 4 +- 12 files changed, 108 insertions(+), 126 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index b83b378e..3b549fe0 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1334,7 +1334,11 @@ bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, std::string &fmt_args, std::string &fmt_trait, std::string &fmt_width) { std::string arg_str = Mapper::ToString(arg); - if (clang::isa(arg->IgnoreImplicit())) { + if (auto *str_lit = + clang::dyn_cast(arg->IgnoreImplicit())) { + if (!IsAsciiStringLiteral(str_lit)) { + return false; + } auto str = GetEscapedStringLiteral(arg); // Delete " from string str.erase(std::remove(str.begin(), str.end(), '"'), str.end()); @@ -1376,6 +1380,8 @@ bool Converter::GetRawArg(clang::Expr *arg, std::string &raw_args) { raw_args += "(&(" + str + ")[..(" + str + ").len() - 1]"; } else if (Mapper::ToString(arg).contains("std::endl")) { raw_args += "(&[b'\\n']"; + } else if (clang::isa(arg->IgnoreImplicit())) { + raw_args += "(b" + GetEscapedStringLiteral(arg); } else { return false; } @@ -1811,8 +1817,9 @@ bool Converter::VisitFloatingLiteral(clang::FloatingLiteral *expr) { } bool Converter::VisitCharacterLiteral(clang::CharacterLiteral *expr) { + auto uc = static_cast(expr->getValue()); std::string ch = GetEscapedCharLiteral(expr->getValue()); - ch = "'" + std::move(ch) + "'"; + ch = (uc > 0x7F ? "b'" : "'") + std::move(ch) + '\''; { PushParen paren(*this); StrCat(ch, keyword::kAs, ToStringBase(expr->getType())); @@ -1839,7 +1846,7 @@ std::string Converter::GetEscapedCharLiteral(char character) const { return "\\0"; } auto uc = static_cast(character); - if (uc < 0x20 || uc == 0x7F) { + if (uc < 0x20 || uc >= 0x7F) { return std::format("\\x{:02x}", uc); } return std::string(1, character); diff --git a/tests/unit/out/refcount/enum_int_interop.rs b/tests/unit/out/refcount/enum_int_interop.rs index 3354d952..930587cc 100644 --- a/tests/unit/out/refcount/enum_int_interop.rs +++ b/tests/unit/out/refcount/enum_int_interop.rs @@ -116,16 +116,16 @@ pub fn classify_option_5(option: i32) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - __v if __v == (Option::OPT_NONE as i32) => { + v if v == (Option::OPT_NONE as i32) => { return -1_i32; } - __v if __v == (Option::OPT_A as i32) => { + v if v == (Option::OPT_A as i32) => { return 1; } - __v if __v == (Option::OPT_B as i32) => { + v if v == (Option::OPT_B as i32) => { return 2; } - __v if __v == (Option::OPT_C as i32) => { + v if v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -153,13 +153,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*c.borrow()) as i32); match __match_cond { - __v if __v == 0 => { + v if v == 0 => { break 'switch; } - __v if __v == 1 => { + v if v == 1 => { return 1; } - __v if __v == 2 => { + v if v == 2 => { return 2; } _ => { @@ -215,13 +215,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*t.borrow()) as i32); match __match_cond { - __v if __v == (Tag::TAG_ZERO as i32) => { + v if v == (Tag::TAG_ZERO as i32) => { return 90; } - __v if __v == 1 => { + v if v == 1 => { return 91; } - __v if __v == 2 => { + v if v == 2 => { break 'switch; } _ => {} diff --git a/tests/unit/out/refcount/enum_int_interop_c.rs b/tests/unit/out/refcount/enum_int_interop_c.rs index 721a1cd1..572dfce3 100644 --- a/tests/unit/out/refcount/enum_int_interop_c.rs +++ b/tests/unit/out/refcount/enum_int_interop_c.rs @@ -81,17 +81,17 @@ thread_local!( thread_local!( pub static entries_3: Value> = Rc::new(RefCell::new(Box::new([ Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal("first"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal(b"first"))), color: Rc::new(RefCell::new(Color::RED)), opt: Rc::new(RefCell::new(Option::OPT_NONE)), }, Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal("second"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal(b"second"))), color: Rc::new(RefCell::new(Color::GREEN)), opt: Rc::new(RefCell::new(Option::OPT_A)), }, Entry { - name: Rc::new(RefCell::new(Ptr::from_string_literal("third"))), + name: Rc::new(RefCell::new(Ptr::from_string_literal(b"third"))), color: Rc::new(RefCell::new(Color::BLUE)), opt: Rc::new(RefCell::new(Option::OPT_C)), }, diff --git a/tests/unit/out/refcount/string.rs b/tests/unit/out/refcount/string.rs index 5e1da4db..b52bacde 100644 --- a/tests/unit/out/refcount/string.rs +++ b/tests/unit/out/refcount/string.rs @@ -41,7 +41,7 @@ fn main_0() -> i32 { assert!((*s1.borrow()) .iter() .copied() - .take((*s1.borrow()).len().saturating_sub(1)) + .take((*s1.borrow()).len() - 1) .eq(Ptr::from_string_literal(b"hello").to_c_string_iterator())); let p1: Value> = Rc::new(RefCell::new((s1.as_pointer() as Ptr))); assert!(((((*p1.borrow()).offset((0) as isize).read()) as i32) == (('h' as u8) as i32))); @@ -98,11 +98,8 @@ fn main_0() -> i32 { (*i.borrow_mut()).prefix_inc(); } let s3: Value> = Rc::new(RefCell::new({ - let mut __tmp1 = (*s2.borrow())[(2_u64) as usize - ..::std::cmp::min( - (2_u64 + 5_u64) as usize, - (*s2.borrow()).len().saturating_sub(1), - )] + let mut __tmp1 = (*s2.borrow()) + [(2_u64) as usize..::std::cmp::min((2_u64 + 5_u64) as usize, (*s2.borrow()).len() - 1)] .to_vec(); __tmp1.push(0); __tmp1 @@ -123,18 +120,20 @@ fn main_0() -> i32 { let s4: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*s1.borrow())[(1_u64) as usize ..::std::cmp::min( - (1_u64 + { - let __lookup: Vec = Ptr::from_string_literal(b"l") - .to_c_string_iterator() - .collect(); - (*s1.borrow()) + (1_u64 + + match (*s1.borrow()) .iter() - .take((*s1.borrow()).len().saturating_sub(1)) - .rposition(|&x| __lookup.contains(&x)) - .map(|idx| idx as u64) - .unwrap_or(u64::MAX) - }) as usize, - (*s1.borrow()).len().saturating_sub(1), + .take((*s1.borrow()).len() - 1) + .rposition(|&x| { + Ptr::from_string_literal(b"l") + .to_c_string_iterator() + .position(|ch| ch == x) + .is_some() + }) { + Some(idx) => idx as u64, + None => u64::MAX, + }) as usize, + (*s1.borrow()).len() - 1, )] .to_vec(); __tmp1.push(0); @@ -211,7 +210,7 @@ fn main_0() -> i32 { assert!((*string.borrow()) .iter() .copied() - .take((*string.borrow()).len().saturating_sub(1)) + .take((*string.borrow()).len() - 1) .eq(Ptr::from_string_literal(b"bar").to_c_string_iterator())); { (*string.borrow_mut()).pop(); @@ -240,7 +239,7 @@ fn main_0() -> i32 { assert!((*string.borrow()) .iter() .copied() - .take((*string.borrow()).len().saturating_sub(1)) + .take((*string.borrow()).len() - 1) .eq(Ptr::from_string_literal(b"bar").to_c_string_iterator())); { (*string.borrow_mut()).pop(); @@ -390,10 +389,7 @@ fn main_0() -> i32 { ); let substr_0: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(5_u64) as usize - ..::std::cmp::min( - (5_u64 + 3_u64) as usize, - (*result.borrow()).len().saturating_sub(1), - )] + ..::std::cmp::min((5_u64 + 3_u64) as usize, (*result.borrow()).len() - 1)] .to_vec(); __tmp1.push(0); __tmp1 @@ -419,10 +415,7 @@ fn main_0() -> i32 { ); let substr_1: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(0_u64) as usize - ..::std::cmp::min( - (0_u64 + 5_u64) as usize, - (*result.borrow()).len().saturating_sub(1), - )] + ..::std::cmp::min((0_u64 + 5_u64) as usize, (*result.borrow()).len() - 1)] .to_vec(); __tmp1.push(0); __tmp1 @@ -460,10 +453,7 @@ fn main_0() -> i32 { ); let substr_2: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(0_u64) as usize - ..::std::cmp::min( - (0_u64 + 15_u64) as usize, - (*result.borrow()).len().saturating_sub(1), - )] + ..::std::cmp::min((0_u64 + 15_u64) as usize, (*result.borrow()).len() - 1)] .to_vec(); __tmp1.push(0); __tmp1 @@ -517,52 +507,58 @@ fn main_0() -> i32 { .read()) as i32) == (('o' as u8) as i32)) ); - let pos: Value = Rc::new(RefCell::new({ - let __lookup: Vec = Ptr::from_string_literal(b"b") - .to_c_string_iterator() - .collect(); - (*result.borrow()) + let pos: Value = Rc::new(RefCell::new( + match (*result.borrow()) .iter() - .take((*result.borrow()).len().saturating_sub(1)) - .rposition(|&x| __lookup.contains(&x)) - .map(|idx| idx as u64) - .unwrap_or(u64::MAX) - })); + .take((*result.borrow()).len() - 1) + .rposition(|&x| { + Ptr::from_string_literal(b"b") + .to_c_string_iterator() + .position(|ch| ch == x) + .is_some() + }) { + Some(idx) => idx as u64, + None => u64::MAX, + }, + )); assert!(((*pos.borrow()) == 0_u64)); - (*pos.borrow_mut()) = { - let __lookup: Vec = Ptr::from_string_literal(b"f") - .to_c_string_iterator() - .collect(); - (*result.borrow()) - .iter() - .take((*result.borrow()).len().saturating_sub(1)) - .rposition(|&x| __lookup.contains(&x)) - .map(|idx| idx as u64) - .unwrap_or(u64::MAX) + (*pos.borrow_mut()) = match (*result.borrow()) + .iter() + .take((*result.borrow()).len() - 1) + .rposition(|&x| { + Ptr::from_string_literal(b"f") + .to_c_string_iterator() + .position(|ch| ch == x) + .is_some() + }) { + Some(idx) => idx as u64, + None => u64::MAX, }; assert!(((*pos.borrow()) == 5_u64)); - (*pos.borrow_mut()) = { - let __lookup: Vec = Ptr::from_string_literal(b"o") - .to_c_string_iterator() - .collect(); - (*result.borrow()) - .iter() - .take((*result.borrow()).len().saturating_sub(1)) - .rposition(|&x| __lookup.contains(&x)) - .map(|idx| idx as u64) - .unwrap_or(u64::MAX) + (*pos.borrow_mut()) = match (*result.borrow()) + .iter() + .take((*result.borrow()).len() - 1) + .rposition(|&x| { + Ptr::from_string_literal(b"o") + .to_c_string_iterator() + .position(|ch| ch == x) + .is_some() + }) { + Some(idx) => idx as u64, + None => u64::MAX, }; assert!(((*pos.borrow()) == 7_u64)); - (*pos.borrow_mut()) = { - let __lookup: Vec = Ptr::from_string_literal(b"x") - .to_c_string_iterator() - .collect(); - (*result.borrow()) - .iter() - .take((*result.borrow()).len().saturating_sub(1)) - .rposition(|&x| __lookup.contains(&x)) - .map(|idx| idx as u64) - .unwrap_or(u64::MAX) + (*pos.borrow_mut()) = match (*result.borrow()) + .iter() + .take((*result.borrow()).len() - 1) + .rposition(|&x| { + Ptr::from_string_literal(b"x") + .to_c_string_iterator() + .position(|ch| ch == x) + .is_some() + }) { + Some(idx) => idx as u64, + None => u64::MAX, }; assert!(((*pos.borrow()) == (-1_i64 as u64))); let string_to_cast: Value> = Rc::new(RefCell::new( diff --git a/tests/unit/out/refcount/string2.rs b/tests/unit/out/refcount/string2.rs index 76454809..2ed6f7aa 100644 --- a/tests/unit/out/refcount/string2.rs +++ b/tests/unit/out/refcount/string2.rs @@ -26,7 +26,7 @@ fn main_0() -> i32 { assert!((*arr.borrow()) .iter() .copied() - .take((*arr.borrow()).len().saturating_sub(1)) + .take((*arr.borrow()).len() - 1) .eq(Ptr::from_string_literal(b"fbo").to_c_string_iterator())); return 0; } diff --git a/tests/unit/out/refcount/string_literals.rs b/tests/unit/out/refcount/string_literals.rs index 4cc09dbc..ffa32466 100644 --- a/tests/unit/out/refcount/string_literals.rs +++ b/tests/unit/out/refcount/string_literals.rs @@ -60,12 +60,5 @@ fn main_0() -> i32 { let _str: Ptr = (immutable_empty_arr.as_pointer() as Ptr); foo_const_1(_str) }); - let inited_through_init_list: Value> = Rc::new(RefCell::new(Box::<[u8]>::from( - b"papanasi cu smantana\0".as_slice(), - ))); - ({ - let _str: Ptr = (inited_through_init_list.as_pointer() as Ptr); - foo_const_1(_str) - }); return 0; } diff --git a/tests/unit/out/refcount/string_literals_c.rs b/tests/unit/out/refcount/string_literals_c.rs index 4706b590..9900b0c9 100644 --- a/tests/unit/out/refcount/string_literals_c.rs +++ b/tests/unit/out/refcount/string_literals_c.rs @@ -91,12 +91,5 @@ fn main_0() -> i32 { let _str: Ptr = (immutable_empty_arr.as_pointer() as Ptr); foo_const_1(_str) }); - let inited_through_init_list: Value> = Rc::new(RefCell::new(Box::<[u8]>::from( - b"papanasi cu smantana\0".as_slice(), - ))); - ({ - let _str: Ptr = (inited_through_init_list.as_pointer() as Ptr); - foo_const_1(_str) - }); return 0; } diff --git a/tests/unit/out/refcount/va_arg_conditional.rs b/tests/unit/out/refcount/va_arg_conditional.rs index 3dd65378..9673c8de 100644 --- a/tests/unit/out/refcount/va_arg_conditional.rs +++ b/tests/unit/out/refcount/va_arg_conditional.rs @@ -25,7 +25,7 @@ fn main_0() -> i32 { (((({ let _verbose: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - conditional_log_0(_verbose, _fmt, &[(42).into()]) + conditional_log_0(_verbose, _fmt, &[42.into()]) }) == 42) as i32) != 0) ); @@ -33,7 +33,7 @@ fn main_0() -> i32 { (((({ let _verbose: i32 = 0; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - conditional_log_0(_verbose, _fmt, &[(99).into()]) + conditional_log_0(_verbose, _fmt, &[99.into()]) }) == -1_i32) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs index e62b86ee..fab1c2a2 100644 --- a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs +++ b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs @@ -40,7 +40,7 @@ pub fn dispatch_0(option: i32, __args: &[VaArg]) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - __v if __v == (opt::OPT_STRING_OUT as i32) => { + v if v == (opt::OPT_STRING_OUT as i32) => { let out: Value>> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>>()).clone(), )); @@ -48,21 +48,21 @@ pub fn dispatch_0(option: i32, __args: &[VaArg]) -> i32 { (*result.borrow_mut()) = 1; break 'switch; } - __v if __v == (opt::OPT_FILE as i32) => { + v if v == (opt::OPT_FILE as i32) => { let f: Value> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>()).clone(), )); (*result.borrow_mut()) = ((!((*f.borrow()).is_null())) as i32).clone(); break 'switch; } - __v if __v == (opt::OPT_NODE as i32) => { + v if v == (opt::OPT_NODE as i32) => { let n: Value> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>()).clone(), )); (*result.borrow_mut()) = (*(*(*n.borrow()).upgrade().deref()).data.borrow()); break 'switch; } - __v if __v == (opt::OPT_NODE_OUT as i32) => { + v if v == (opt::OPT_NODE_OUT as i32) => { let out: Value>> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>>()).clone(), )); @@ -91,7 +91,7 @@ fn main_0() -> i32 { assert!( (((({ let _option: i32 = (opt::OPT_FILE as i32); - dispatch_0(_option, &[(libcc2rs::cout()).into()]) + dispatch_0(_option, &[libcc2rs::cout().into()]) }) == 1) as i32) != 0) ); @@ -100,10 +100,10 @@ fn main_0() -> i32 { let _option: i32 = (opt::OPT_FILE as i32); dispatch_0( _option, - &[((AnyPtr::default()) + &[(AnyPtr::default()) .cast::<::std::fs::File>() - .expect("ub:wrong type")) - .into()], + .expect("ub:wrong type") + .into()], ) }) == 0) as i32) != 0) diff --git a/tests/unit/out/refcount/va_arg_printf.rs b/tests/unit/out/refcount/va_arg_printf.rs index 0c1850e3..6a2458b1 100644 --- a/tests/unit/out/refcount/va_arg_printf.rs +++ b/tests/unit/out/refcount/va_arg_printf.rs @@ -32,24 +32,17 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let dummy: Value> = Rc::new(RefCell::new(Ptr::from_string_literal(b"dummy"))); assert!( (((({ let _fmt: Ptr = Ptr::from_string_literal(b"hello %d %d"); - logf_1( - _fmt, - &[ - (10).into(), - ((*dummy.borrow()).to_string_iterator().count() as u64).into(), - ], - ) - }) == 15) as i32) + logf_1(_fmt, &[10.into(), 32.into()]) + }) == 42) as i32) != 0) ); assert!( (((({ let _fmt: Ptr = Ptr::from_string_literal(b"x %d %d"); - logf_1(_fmt, &[(1).into(), (2).into()]) + logf_1(_fmt, &[1.into(), 2.into()]) }) == 3) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_snprintf.rs b/tests/unit/out/refcount/va_arg_snprintf.rs index e18d2d72..f52b086d 100644 --- a/tests/unit/out/refcount/va_arg_snprintf.rs +++ b/tests/unit/out/refcount/va_arg_snprintf.rs @@ -29,7 +29,7 @@ fn main_0() -> i32 { let _buf: Ptr = (buf.as_pointer() as Ptr); let _size: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - extract_first_0(_buf, _size, _fmt, &[(42).into()]) + extract_first_0(_buf, _size, _fmt, &[42.into()]) }) == 42) as i32) != 0) ); @@ -39,7 +39,7 @@ fn main_0() -> i32 { let _buf: Ptr = (buf.as_pointer() as Ptr); let _size: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - extract_first_0(_buf, _size, _fmt, &[(65).into()]) + extract_first_0(_buf, _size, _fmt, &[65.into()]) }) == 65) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_struct_ctx.rs b/tests/unit/out/refcount/va_arg_struct_ctx.rs index e6e9ad73..f5ccee40 100644 --- a/tests/unit/out/refcount/va_arg_struct_ctx.rs +++ b/tests/unit/out/refcount/va_arg_struct_ctx.rs @@ -43,14 +43,14 @@ fn main_0() -> i32 { ({ let _ctx: Ptr = (ctx.as_pointer()); let _fmt: Ptr = Ptr::from_string_literal(b"error %d"); - set_error_0(_ctx, _fmt, &[(42).into()]) + set_error_0(_ctx, _fmt, &[42.into()]) }); assert!(((((*(*ctx.borrow()).last_error.borrow()) == 42) as i32) != 0)); (*(*ctx.borrow()).verbose.borrow_mut()) = 0; ({ let _ctx: Ptr = (ctx.as_pointer()); let _fmt: Ptr = Ptr::from_string_literal(b"error %d"); - set_error_0(_ctx, _fmt, &[(99).into()]) + set_error_0(_ctx, _fmt, &[99.into()]) }); assert!(((((*(*ctx.borrow()).last_error.borrow()) == 42) as i32) != 0)); return 0; From fe2f47259bfd93bc146d7f20c18ac4fda17ce06f Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 15:19:56 +0100 Subject: [PATCH 13/26] Fix `Ord` codegen to use `other < self` in reverse branch (#170) The generated `Ord::cmp` for method-based `operator<` used `other.(other)` in its second branch, which can never evaluate true and therefore suppresses `Ordering::Greater`. This change aligns that branch with the intended reverse comparison (`other.(self)`), matching existing model-specific behavior. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- cpp2rust/converter/converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 3b549fe0..4c4a5b92 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3722,7 +3722,7 @@ void Converter::ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, case clang::OO_Less: if (clang::isa(op)) { first_branch = std::format("self.{}(other)", GetOverloadedOperator(op)); - second_branch = std::format("other.{}(other)", GetOverloadedOperator(op)); + second_branch = std::format("other.{}(self)", GetOverloadedOperator(op)); } else { first_branch = std::format("{}(self, other)", GetOverloadedOperator(op)); second_branch = std::format("{}(other, self)", GetOverloadedOperator(op)); From 891afa87893488943a194380df4daa88f04fbded Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 16:09:32 +0100 Subject: [PATCH 14/26] Handle char array initialization through `{ string literal }` (#175) `const char var[] = {"string literal"}` is a valid initialization. Add support for it in codegen. --- cpp2rust/converter/converter.cpp | 4 ++++ tests/unit/out/refcount/string_literals.rs | 7 +++++++ tests/unit/out/refcount/string_literals_c.rs | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 4c4a5b92..a27f5da8 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2795,6 +2795,10 @@ bool Converter::VisitInitListExpr(clang::InitListExpr *expr) { StrCat(token::kComma); } } else { + if (IsInitExprOfStringLiteral(expr)) { + Convert(expr->getInit(0)->IgnoreParenImpCasts()); + return false; + } PushBracket bracket(*this); for (auto *init : expr->inits()) { ConvertVarInit(init->getType(), init); diff --git a/tests/unit/out/refcount/string_literals.rs b/tests/unit/out/refcount/string_literals.rs index ffa32466..4cc09dbc 100644 --- a/tests/unit/out/refcount/string_literals.rs +++ b/tests/unit/out/refcount/string_literals.rs @@ -60,5 +60,12 @@ fn main_0() -> i32 { let _str: Ptr = (immutable_empty_arr.as_pointer() as Ptr); foo_const_1(_str) }); + let inited_through_init_list: Value> = Rc::new(RefCell::new(Box::<[u8]>::from( + b"papanasi cu smantana\0".as_slice(), + ))); + ({ + let _str: Ptr = (inited_through_init_list.as_pointer() as Ptr); + foo_const_1(_str) + }); return 0; } diff --git a/tests/unit/out/refcount/string_literals_c.rs b/tests/unit/out/refcount/string_literals_c.rs index 9900b0c9..4706b590 100644 --- a/tests/unit/out/refcount/string_literals_c.rs +++ b/tests/unit/out/refcount/string_literals_c.rs @@ -91,5 +91,12 @@ fn main_0() -> i32 { let _str: Ptr = (immutable_empty_arr.as_pointer() as Ptr); foo_const_1(_str) }); + let inited_through_init_list: Value> = Rc::new(RefCell::new(Box::<[u8]>::from( + b"papanasi cu smantana\0".as_slice(), + ))); + ({ + let _str: Ptr = (inited_through_init_list.as_pointer() as Ptr); + foo_const_1(_str) + }); return 0; } From f41f1458243da7981e85ba186ed9cd1afbb517ec Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 18:43:26 +0100 Subject: [PATCH 15/26] Fix clash between switch arm variable and local variable (#172) --- cpp2rust/converter/converter.cpp | 4 ++-- tests/unit/out/refcount/enum_int_interop.rs | 20 +++++++++---------- tests/unit/out/refcount/enum_int_interop_c.rs | 20 +++++++++---------- tests/unit/out/refcount/goto_cleanup.rs | 2 +- .../out/refcount/goto_switch_fallthrough.rs | 4 ++-- .../out/refcount/va_arg_non_primitive_ptrs.rs | 8 ++++---- tests/unit/out/unsafe/enum_int_interop_c.rs | 20 +++++++++---------- tests/unit/out/unsafe/goto_cleanup.rs | 2 +- .../out/unsafe/goto_switch_fallthrough.rs | 4 ++-- .../out/unsafe/union_void_ptr_sized_deref.rs | 6 +++--- .../out/unsafe/va_arg_non_primitive_ptrs.rs | 2 +- 11 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index a27f5da8..3470138e 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3146,7 +3146,7 @@ bool Converter::ConvertSwitchCaseCondition(clang::SwitchCase *stmt) { while (auto *sc = clang::dyn_cast(cur)) { if (auto *case_stmt = clang::dyn_cast(sc)) { if (!first) { - StrCat("|| v == "); + StrCat("|| __v == "); } Convert(case_stmt->getLHS()); } @@ -3168,7 +3168,7 @@ void Converter::EmitSwitchArm(clang::CompoundStmt *body, clang::SwitchCase *sc, if (is_default) { StrCat("_ => {"); } else { - StrCat("v if v == "); + StrCat("__v if __v == "); ConvertSwitchCaseCondition(sc); } for (auto *t : GetSwitchCaseBody(body, sc)) { diff --git a/tests/unit/out/refcount/enum_int_interop.rs b/tests/unit/out/refcount/enum_int_interop.rs index 930587cc..3354d952 100644 --- a/tests/unit/out/refcount/enum_int_interop.rs +++ b/tests/unit/out/refcount/enum_int_interop.rs @@ -116,16 +116,16 @@ pub fn classify_option_5(option: i32) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - v if v == (Option::OPT_NONE as i32) => { + __v if __v == (Option::OPT_NONE as i32) => { return -1_i32; } - v if v == (Option::OPT_A as i32) => { + __v if __v == (Option::OPT_A as i32) => { return 1; } - v if v == (Option::OPT_B as i32) => { + __v if __v == (Option::OPT_B as i32) => { return 2; } - v if v == (Option::OPT_C as i32) => { + __v if __v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -153,13 +153,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*c.borrow()) as i32); match __match_cond { - v if v == 0 => { + __v if __v == 0 => { break 'switch; } - v if v == 1 => { + __v if __v == 1 => { return 1; } - v if v == 2 => { + __v if __v == 2 => { return 2; } _ => { @@ -215,13 +215,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*t.borrow()) as i32); match __match_cond { - v if v == (Tag::TAG_ZERO as i32) => { + __v if __v == (Tag::TAG_ZERO as i32) => { return 90; } - v if v == 1 => { + __v if __v == 1 => { return 91; } - v if v == 2 => { + __v if __v == 2 => { break 'switch; } _ => {} diff --git a/tests/unit/out/refcount/enum_int_interop_c.rs b/tests/unit/out/refcount/enum_int_interop_c.rs index 572dfce3..817170c6 100644 --- a/tests/unit/out/refcount/enum_int_interop_c.rs +++ b/tests/unit/out/refcount/enum_int_interop_c.rs @@ -106,16 +106,16 @@ pub fn classify_option_5(option: i32) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - v if v == (Option::OPT_NONE as i32) => { + __v if __v == (Option::OPT_NONE as i32) => { return -1_i32; } - v if v == (Option::OPT_A as i32) => { + __v if __v == (Option::OPT_A as i32) => { return 1; } - v if v == (Option::OPT_B as i32) => { + __v if __v == (Option::OPT_B as i32) => { return 2; } - v if v == (Option::OPT_C as i32) => { + __v if __v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -143,13 +143,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*c.borrow()) as u32); match __match_cond { - v if v == (0 as u32) => { + __v if __v == (0 as u32) => { break 'switch; } - v if v == (1 as u32) => { + __v if __v == (1 as u32) => { return 1; } - v if v == (2 as u32) => { + __v if __v == (2 as u32) => { return 2; } _ => { @@ -209,13 +209,13 @@ fn main_0() -> i32 { 'switch: { let __match_cond = ((*t.borrow()) as u32); match __match_cond { - v if v == ((Tag_enum::TAG_ZERO as i32) as u32) => { + __v if __v == ((Tag_enum::TAG_ZERO as i32) as u32) => { return 90; } - v if v == (1 as u32) => { + __v if __v == (1 as u32) => { return 91; } - v if v == (2 as u32) => { + __v if __v == (2 as u32) => { break 'switch; } _ => {} diff --git a/tests/unit/out/refcount/goto_cleanup.rs b/tests/unit/out/refcount/goto_cleanup.rs index a44fb20f..4ba868ee 100644 --- a/tests/unit/out/refcount/goto_cleanup.rs +++ b/tests/unit/out/refcount/goto_cleanup.rs @@ -56,7 +56,7 @@ pub fn from_switch_2(n: i32) -> i32 { 'switch: { let __match_cond = (*n.borrow()); match __match_cond { - v if v == 1 => { + __v if __v == 1 => { (*ret.borrow_mut()) = 10; goto!('out); } diff --git a/tests/unit/out/refcount/goto_switch_fallthrough.rs b/tests/unit/out/refcount/goto_switch_fallthrough.rs index 74581ceb..68d74f8d 100644 --- a/tests/unit/out/refcount/goto_switch_fallthrough.rs +++ b/tests/unit/out/refcount/goto_switch_fallthrough.rs @@ -13,10 +13,10 @@ pub fn sm_0(n: i32) -> i32 { '__entry: { *ret.borrow_mut() = 0; switch!(match (*n.borrow()) { - v if v == 0 => { + __v if __v == 0 => { (*ret.borrow_mut()) += 1; } - v if v == 1 => { + __v if __v == 1 => { (*ret.borrow_mut()) += 10; goto!('out); } diff --git a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs index fab1c2a2..394d0439 100644 --- a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs +++ b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs @@ -40,7 +40,7 @@ pub fn dispatch_0(option: i32, __args: &[VaArg]) -> i32 { 'switch: { let __match_cond = (*option.borrow()); match __match_cond { - v if v == (opt::OPT_STRING_OUT as i32) => { + __v if __v == (opt::OPT_STRING_OUT as i32) => { let out: Value>> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>>()).clone(), )); @@ -48,21 +48,21 @@ pub fn dispatch_0(option: i32, __args: &[VaArg]) -> i32 { (*result.borrow_mut()) = 1; break 'switch; } - v if v == (opt::OPT_FILE as i32) => { + __v if __v == (opt::OPT_FILE as i32) => { let f: Value> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>()).clone(), )); (*result.borrow_mut()) = ((!((*f.borrow()).is_null())) as i32).clone(); break 'switch; } - v if v == (opt::OPT_NODE as i32) => { + __v if __v == (opt::OPT_NODE as i32) => { let n: Value> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>()).clone(), )); (*result.borrow_mut()) = (*(*(*n.borrow()).upgrade().deref()).data.borrow()); break 'switch; } - v if v == (opt::OPT_NODE_OUT as i32) => { + __v if __v == (opt::OPT_NODE_OUT as i32) => { let out: Value>> = Rc::new(RefCell::new( ((*ap.borrow_mut()).arg::>>()).clone(), )); diff --git a/tests/unit/out/unsafe/enum_int_interop_c.rs b/tests/unit/out/unsafe/enum_int_interop_c.rs index 56d90545..f4e9feeb 100644 --- a/tests/unit/out/unsafe/enum_int_interop_c.rs +++ b/tests/unit/out/unsafe/enum_int_interop_c.rs @@ -98,16 +98,16 @@ pub unsafe fn classify_option_5(mut option: i32) -> i32 { 'switch: { let __match_cond = option; match __match_cond { - v if v == (Option::OPT_NONE as i32) => { + __v if __v == (Option::OPT_NONE as i32) => { return -1_i32; } - v if v == (Option::OPT_A as i32) => { + __v if __v == (Option::OPT_A as i32) => { return 1; } - v if v == (Option::OPT_B as i32) => { + __v if __v == (Option::OPT_B as i32) => { return 2; } - v if v == (Option::OPT_C as i32) => { + __v if __v == (Option::OPT_C as i32) => { return 3; } _ => { @@ -136,13 +136,13 @@ unsafe fn main_0() -> i32 { 'switch: { let __match_cond = (c as u32); match __match_cond { - v if v == (0 as u32) => { + __v if __v == (0 as u32) => { break 'switch; } - v if v == (1 as u32) => { + __v if __v == (1 as u32) => { return 1; } - v if v == (2 as u32) => { + __v if __v == (2 as u32) => { return 2; } _ => { @@ -196,13 +196,13 @@ unsafe fn main_0() -> i32 { 'switch: { let __match_cond = (t as u32); match __match_cond { - v if v == ((Tag_enum::TAG_ZERO as i32) as u32) => { + __v if __v == ((Tag_enum::TAG_ZERO as i32) as u32) => { return 90; } - v if v == (1 as u32) => { + __v if __v == (1 as u32) => { return 91; } - v if v == (2 as u32) => { + __v if __v == (2 as u32) => { break 'switch; } _ => {} diff --git a/tests/unit/out/unsafe/goto_cleanup.rs b/tests/unit/out/unsafe/goto_cleanup.rs index b4bcb361..6cba984d 100644 --- a/tests/unit/out/unsafe/goto_cleanup.rs +++ b/tests/unit/out/unsafe/goto_cleanup.rs @@ -53,7 +53,7 @@ pub unsafe fn from_switch_2(mut n: i32) -> i32 { 'switch: { let __match_cond = n; match __match_cond { - v if v == 1 => { + __v if __v == 1 => { ret = 10; goto!('out); } diff --git a/tests/unit/out/unsafe/goto_switch_fallthrough.rs b/tests/unit/out/unsafe/goto_switch_fallthrough.rs index af0011cd..81961849 100644 --- a/tests/unit/out/unsafe/goto_switch_fallthrough.rs +++ b/tests/unit/out/unsafe/goto_switch_fallthrough.rs @@ -12,10 +12,10 @@ pub unsafe fn sm_0(mut n: i32) -> i32 { '__entry: { ret = 0; switch!(match n { - v if v == 0 => { + __v if __v == 0 => { ret += 1; } - v if v == 1 => { + __v if __v == 1 => { ret += 10; goto!('out); } diff --git a/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs b/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs index 79556876..25afdaa7 100644 --- a/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs +++ b/tests/unit/out/unsafe/union_void_ptr_sized_deref.rs @@ -47,15 +47,15 @@ pub unsafe fn write_count_1(mut s: *mut Sink, mut count: i64) { 'switch: { let __match_cond = ((*s).width as u32); match __match_cond { - v if v == ((Width_enum::W_64 as i32) as u32) => { + __v if __v == ((Width_enum::W_64 as i32) as u32) => { (*((*s).out.handle as *mut i64)) = count; break 'switch; } - v if v == ((Width_enum::W_32 as i32) as u32) => { + __v if __v == ((Width_enum::W_32 as i32) as u32) => { (*((*s).out.handle as *mut i32)) = (count as i32); break 'switch; } - v if v == ((Width_enum::W_16 as i32) as u32) => { + __v if __v == ((Width_enum::W_16 as i32) as u32) => { (*((*s).out.handle as *mut i16)) = (count as i16); break 'switch; } diff --git a/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs b/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs index 7dfcc03e..b9b89d6d 100644 --- a/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs +++ b/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs @@ -84,7 +84,7 @@ unsafe fn main_0() -> i32 { assert!( ((((unsafe { let _option: i32 = (opt::OPT_FILE as i32); - dispatch_0(_option, &[(libcc2rs::stdout_unsafe()).into()]) + dispatch_0(_option, &[libcc2rs::stdout_unsafe().into()]) }) == (1)) as i32) != 0) ); From cd5ba7e391ffe7e64f9460e189662bc8401e4d6e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 18:44:58 +0100 Subject: [PATCH 16/26] Rewrite in6_addr::__in6_u::__u8_addr8 as libc::in6_addr::s6_addr (#177) --- cpp2rust/converter/converter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 3470138e..ef3259d5 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2689,6 +2689,7 @@ replaceNonUniformLibcField(clang::MemberExpr *expr) { static constexpr Mapping kFields[] = { {"stat", "st_mtim", "tv_sec", "st_mtime"}, // Linux {"stat", "st_mtimespec", "tv_sec", "st_mtime"}, // macOS + {"in6_addr", "__in6_u", "__u6_addr8", "s6_addr"}, }; auto getNamedIdentifierOrNull = [](auto *decl) { From 301a669fef0e865863ab8fc29db1879484658bac Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 18:47:52 +0100 Subject: [PATCH 17/26] Fix name clash between anon enums across TU (#171) `return std::format("anon_enum_{}", GetLineNumber(enum_decl));` results in name clashes between enums declared in different files at the same line, I replaced that with `return GetNamedDeclAsString(enum_decl);` --- tests/unit/out/refcount/anonymous_enum_c.rs | 74 ++++++++++----------- tests/unit/out/unsafe/anonymous_enum_c.rs | 72 ++++++++++---------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/tests/unit/out/refcount/anonymous_enum_c.rs b/tests/unit/out/refcount/anonymous_enum_c.rs index d4d31b73..772ea6e5 100644 --- a/tests/unit/out/refcount/anonymous_enum_c.rs +++ b/tests/unit/out/refcount/anonymous_enum_c.rs @@ -7,37 +7,37 @@ use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_3 { +enum anon_0 { #[default] FIRST_A = 0, FIRST_B = 1, } -impl From for anon_enum_3 { - fn from(n: i32) -> anon_enum_3 { +impl From for anon_0 { + fn from(n: i32) -> anon_0 { match n { - 0 => anon_enum_3::FIRST_A, - 1 => anon_enum_3::FIRST_B, - _ => panic!("invalid anon_enum_3 value: {}", n), + 0 => anon_0::FIRST_A, + 1 => anon_0::FIRST_B, + _ => panic!("invalid anon_0 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_3); +libcc2rs::impl_enum_inc_dec!(anon_0); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_11 { +enum anon_1 { #[default] SECOND_A = 0, SECOND_B = 1, } -impl From for anon_enum_11 { - fn from(n: i32) -> anon_enum_11 { +impl From for anon_1 { + fn from(n: i32) -> anon_1 { match n { - 0 => anon_enum_11::SECOND_A, - 1 => anon_enum_11::SECOND_B, - _ => panic!("invalid anon_enum_11 value: {}", n), + 0 => anon_1::SECOND_A, + 1 => anon_1::SECOND_B, + _ => panic!("invalid anon_1 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_11); +libcc2rs::impl_enum_inc_dec!(anon_1); #[derive(Default)] pub struct S { pub a: Value, @@ -69,25 +69,25 @@ impl From for TdEnum_enum { } libcc2rs::impl_enum_inc_dec!(TdEnum_enum); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_24 { +enum anon_2 { #[default] FIELD_A = 0, FIELD_B = 1, } -impl From for anon_enum_24 { - fn from(n: i32) -> anon_enum_24 { +impl From for anon_2 { + fn from(n: i32) -> anon_2 { match n { - 0 => anon_enum_24::FIELD_A, - 1 => anon_enum_24::FIELD_B, - _ => panic!("invalid anon_enum_24 value: {}", n), + 0 => anon_2::FIELD_A, + 1 => anon_2::FIELD_B, + _ => panic!("invalid anon_2 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_24); +libcc2rs::impl_enum_inc_dec!(anon_2); #[derive(Default)] pub struct WithAnonField { pub a: Value, - pub field: Value, + pub field: Value, } impl ByteRepr for WithAnonField {} pub fn main() { @@ -95,39 +95,37 @@ pub fn main() { } fn main_0() -> i32 { #[derive(Clone, Copy, PartialEq, Debug, Default)] - enum anon_enum_31 { + enum anon_3 { #[default] THIRD_A = 0, THIRD_B = 1, } - impl From for anon_enum_31 { - fn from(n: i32) -> anon_enum_31 { + impl From for anon_3 { + fn from(n: i32) -> anon_3 { match n { - 0 => anon_enum_31::THIRD_A, - 1 => anon_enum_31::THIRD_B, - _ => panic!("invalid anon_enum_31 value: {}", n), + 0 => anon_3::THIRD_A, + 1 => anon_3::THIRD_B, + _ => panic!("invalid anon_3 value: {}", n), } } } - libcc2rs::impl_enum_inc_dec!(anon_enum_31); - assert!(((((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32)) as i32) != 0)); - assert!(((((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32)) as i32) != 0)); - assert!(((((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32)) as i32) != 0)); + libcc2rs::impl_enum_inc_dec!(anon_3); + assert!(((((anon_0::FIRST_A as i32) != (anon_0::FIRST_B as i32)) as i32) != 0)); + assert!(((((anon_1::SECOND_A as i32) != (anon_1::SECOND_B as i32)) as i32) != 0)); + assert!(((((anon_3::THIRD_A as i32) != (anon_3::THIRD_B as i32)) as i32) != 0)); let td: Value = Rc::new(RefCell::new(TdEnum_enum::TD_A)); assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_A as i32) as u32)) as i32) != 0)); (*td.borrow_mut()) = TdEnum_enum::TD_B; assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_B as i32) as u32)) as i32) != 0)); let w: Value = >::default(); - (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::FIELD_A; + (*(*w.borrow()).field.borrow_mut()) = anon_2::FIELD_A; assert!( - (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_A as i32) as u32)) - as i32) + (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_2::FIELD_A as i32) as u32)) as i32) != 0) ); - (*(*w.borrow()).field.borrow_mut()) = anon_enum_24::FIELD_B; + (*(*w.borrow()).field.borrow_mut()) = anon_2::FIELD_B; assert!( - (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_enum_24::FIELD_B as i32) as u32)) - as i32) + (((((*(*w.borrow()).field.borrow()) as u32) == ((anon_2::FIELD_B as i32) as u32)) as i32) != 0) ); return 0; diff --git a/tests/unit/out/unsafe/anonymous_enum_c.rs b/tests/unit/out/unsafe/anonymous_enum_c.rs index 260b7a26..8bdf3f73 100644 --- a/tests/unit/out/unsafe/anonymous_enum_c.rs +++ b/tests/unit/out/unsafe/anonymous_enum_c.rs @@ -7,37 +7,37 @@ use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_3 { +enum anon_0 { #[default] FIRST_A = 0, FIRST_B = 1, } -impl From for anon_enum_3 { - fn from(n: i32) -> anon_enum_3 { +impl From for anon_0 { + fn from(n: i32) -> anon_0 { match n { - 0 => anon_enum_3::FIRST_A, - 1 => anon_enum_3::FIRST_B, - _ => panic!("invalid anon_enum_3 value: {}", n), + 0 => anon_0::FIRST_A, + 1 => anon_0::FIRST_B, + _ => panic!("invalid anon_0 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_3); +libcc2rs::impl_enum_inc_dec!(anon_0); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_11 { +enum anon_1 { #[default] SECOND_A = 0, SECOND_B = 1, } -impl From for anon_enum_11 { - fn from(n: i32) -> anon_enum_11 { +impl From for anon_1 { + fn from(n: i32) -> anon_1 { match n { - 0 => anon_enum_11::SECOND_A, - 1 => anon_enum_11::SECOND_B, - _ => panic!("invalid anon_enum_11 value: {}", n), + 0 => anon_1::SECOND_A, + 1 => anon_1::SECOND_B, + _ => panic!("invalid anon_1 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_11); +libcc2rs::impl_enum_inc_dec!(anon_1); #[repr(C)] #[derive(Copy, Clone, Default)] pub struct S { @@ -60,26 +60,26 @@ impl From for TdEnum_enum { } libcc2rs::impl_enum_inc_dec!(TdEnum_enum); #[derive(Clone, Copy, PartialEq, Debug, Default)] -enum anon_enum_24 { +enum anon_2 { #[default] FIELD_A = 0, FIELD_B = 1, } -impl From for anon_enum_24 { - fn from(n: i32) -> anon_enum_24 { +impl From for anon_2 { + fn from(n: i32) -> anon_2 { match n { - 0 => anon_enum_24::FIELD_A, - 1 => anon_enum_24::FIELD_B, - _ => panic!("invalid anon_enum_24 value: {}", n), + 0 => anon_2::FIELD_A, + 1 => anon_2::FIELD_B, + _ => panic!("invalid anon_2 value: {}", n), } } } -libcc2rs::impl_enum_inc_dec!(anon_enum_24); +libcc2rs::impl_enum_inc_dec!(anon_2); #[repr(C)] #[derive(Copy, Clone, Default)] pub struct WithAnonField { pub a: i32, - pub field: anon_enum_24, + pub field: anon_2, } pub fn main() { unsafe { @@ -88,32 +88,32 @@ pub fn main() { } unsafe fn main_0() -> i32 { #[derive(Clone, Copy, PartialEq, Debug, Default)] - enum anon_enum_31 { + enum anon_3 { #[default] THIRD_A = 0, THIRD_B = 1, } - impl From for anon_enum_31 { - fn from(n: i32) -> anon_enum_31 { + impl From for anon_3 { + fn from(n: i32) -> anon_3 { match n { - 0 => anon_enum_31::THIRD_A, - 1 => anon_enum_31::THIRD_B, - _ => panic!("invalid anon_enum_31 value: {}", n), + 0 => anon_3::THIRD_A, + 1 => anon_3::THIRD_B, + _ => panic!("invalid anon_3 value: {}", n), } } } - libcc2rs::impl_enum_inc_dec!(anon_enum_31); - assert!(((((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32)) as i32) != 0)); - assert!(((((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32)) as i32) != 0)); - assert!(((((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32)) as i32) != 0)); + libcc2rs::impl_enum_inc_dec!(anon_3); + assert!(((((anon_0::FIRST_A as i32) != (anon_0::FIRST_B as i32)) as i32) != 0)); + assert!(((((anon_1::SECOND_A as i32) != (anon_1::SECOND_B as i32)) as i32) != 0)); + assert!(((((anon_3::THIRD_A as i32) != (anon_3::THIRD_B as i32)) as i32) != 0)); let mut td: TdEnum_enum = TdEnum_enum::TD_A; assert!(((((td as u32) == ((TdEnum_enum::TD_A as i32) as u32)) as i32) != 0)); td = (TdEnum_enum::TD_B).clone(); assert!(((((td as u32) == ((TdEnum_enum::TD_B as i32) as u32)) as i32) != 0)); let mut w: WithAnonField = ::default(); - w.field = anon_enum_24::FIELD_A; - assert!(((((w.field as u32) == ((anon_enum_24::FIELD_A as i32) as u32)) as i32) != 0)); - w.field = (anon_enum_24::FIELD_B).clone(); - assert!(((((w.field as u32) == ((anon_enum_24::FIELD_B as i32) as u32)) as i32) != 0)); + w.field = anon_2::FIELD_A; + assert!(((((w.field as u32) == ((anon_2::FIELD_A as i32) as u32)) as i32) != 0)); + w.field = (anon_2::FIELD_B).clone(); + assert!(((((w.field as u32) == ((anon_2::FIELD_B as i32) as u32)) as i32) != 0)); return 0; } From c4580d2d53c57d2468f810484fa10a3160f60662 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 3 Jun 2026 22:32:43 +0100 Subject: [PATCH 18/26] simplify some string rules --- tests/unit/out/refcount/string.rs | 138 +++++++++++++++-------------- tests/unit/out/refcount/string2.rs | 2 +- 2 files changed, 72 insertions(+), 68 deletions(-) diff --git a/tests/unit/out/refcount/string.rs b/tests/unit/out/refcount/string.rs index b52bacde..5e1da4db 100644 --- a/tests/unit/out/refcount/string.rs +++ b/tests/unit/out/refcount/string.rs @@ -41,7 +41,7 @@ fn main_0() -> i32 { assert!((*s1.borrow()) .iter() .copied() - .take((*s1.borrow()).len() - 1) + .take((*s1.borrow()).len().saturating_sub(1)) .eq(Ptr::from_string_literal(b"hello").to_c_string_iterator())); let p1: Value> = Rc::new(RefCell::new((s1.as_pointer() as Ptr))); assert!(((((*p1.borrow()).offset((0) as isize).read()) as i32) == (('h' as u8) as i32))); @@ -98,8 +98,11 @@ fn main_0() -> i32 { (*i.borrow_mut()).prefix_inc(); } let s3: Value> = Rc::new(RefCell::new({ - let mut __tmp1 = (*s2.borrow()) - [(2_u64) as usize..::std::cmp::min((2_u64 + 5_u64) as usize, (*s2.borrow()).len() - 1)] + let mut __tmp1 = (*s2.borrow())[(2_u64) as usize + ..::std::cmp::min( + (2_u64 + 5_u64) as usize, + (*s2.borrow()).len().saturating_sub(1), + )] .to_vec(); __tmp1.push(0); __tmp1 @@ -120,20 +123,18 @@ fn main_0() -> i32 { let s4: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*s1.borrow())[(1_u64) as usize ..::std::cmp::min( - (1_u64 - + match (*s1.borrow()) + (1_u64 + { + let __lookup: Vec = Ptr::from_string_literal(b"l") + .to_c_string_iterator() + .collect(); + (*s1.borrow()) .iter() - .take((*s1.borrow()).len() - 1) - .rposition(|&x| { - Ptr::from_string_literal(b"l") - .to_c_string_iterator() - .position(|ch| ch == x) - .is_some() - }) { - Some(idx) => idx as u64, - None => u64::MAX, - }) as usize, - (*s1.borrow()).len() - 1, + .take((*s1.borrow()).len().saturating_sub(1)) + .rposition(|&x| __lookup.contains(&x)) + .map(|idx| idx as u64) + .unwrap_or(u64::MAX) + }) as usize, + (*s1.borrow()).len().saturating_sub(1), )] .to_vec(); __tmp1.push(0); @@ -210,7 +211,7 @@ fn main_0() -> i32 { assert!((*string.borrow()) .iter() .copied() - .take((*string.borrow()).len() - 1) + .take((*string.borrow()).len().saturating_sub(1)) .eq(Ptr::from_string_literal(b"bar").to_c_string_iterator())); { (*string.borrow_mut()).pop(); @@ -239,7 +240,7 @@ fn main_0() -> i32 { assert!((*string.borrow()) .iter() .copied() - .take((*string.borrow()).len() - 1) + .take((*string.borrow()).len().saturating_sub(1)) .eq(Ptr::from_string_literal(b"bar").to_c_string_iterator())); { (*string.borrow_mut()).pop(); @@ -389,7 +390,10 @@ fn main_0() -> i32 { ); let substr_0: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(5_u64) as usize - ..::std::cmp::min((5_u64 + 3_u64) as usize, (*result.borrow()).len() - 1)] + ..::std::cmp::min( + (5_u64 + 3_u64) as usize, + (*result.borrow()).len().saturating_sub(1), + )] .to_vec(); __tmp1.push(0); __tmp1 @@ -415,7 +419,10 @@ fn main_0() -> i32 { ); let substr_1: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(0_u64) as usize - ..::std::cmp::min((0_u64 + 5_u64) as usize, (*result.borrow()).len() - 1)] + ..::std::cmp::min( + (0_u64 + 5_u64) as usize, + (*result.borrow()).len().saturating_sub(1), + )] .to_vec(); __tmp1.push(0); __tmp1 @@ -453,7 +460,10 @@ fn main_0() -> i32 { ); let substr_2: Value> = Rc::new(RefCell::new({ let mut __tmp1 = (*result.borrow())[(0_u64) as usize - ..::std::cmp::min((0_u64 + 15_u64) as usize, (*result.borrow()).len() - 1)] + ..::std::cmp::min( + (0_u64 + 15_u64) as usize, + (*result.borrow()).len().saturating_sub(1), + )] .to_vec(); __tmp1.push(0); __tmp1 @@ -507,58 +517,52 @@ fn main_0() -> i32 { .read()) as i32) == (('o' as u8) as i32)) ); - let pos: Value = Rc::new(RefCell::new( - match (*result.borrow()) + let pos: Value = Rc::new(RefCell::new({ + let __lookup: Vec = Ptr::from_string_literal(b"b") + .to_c_string_iterator() + .collect(); + (*result.borrow()) .iter() - .take((*result.borrow()).len() - 1) - .rposition(|&x| { - Ptr::from_string_literal(b"b") - .to_c_string_iterator() - .position(|ch| ch == x) - .is_some() - }) { - Some(idx) => idx as u64, - None => u64::MAX, - }, - )); + .take((*result.borrow()).len().saturating_sub(1)) + .rposition(|&x| __lookup.contains(&x)) + .map(|idx| idx as u64) + .unwrap_or(u64::MAX) + })); assert!(((*pos.borrow()) == 0_u64)); - (*pos.borrow_mut()) = match (*result.borrow()) - .iter() - .take((*result.borrow()).len() - 1) - .rposition(|&x| { - Ptr::from_string_literal(b"f") - .to_c_string_iterator() - .position(|ch| ch == x) - .is_some() - }) { - Some(idx) => idx as u64, - None => u64::MAX, + (*pos.borrow_mut()) = { + let __lookup: Vec = Ptr::from_string_literal(b"f") + .to_c_string_iterator() + .collect(); + (*result.borrow()) + .iter() + .take((*result.borrow()).len().saturating_sub(1)) + .rposition(|&x| __lookup.contains(&x)) + .map(|idx| idx as u64) + .unwrap_or(u64::MAX) }; assert!(((*pos.borrow()) == 5_u64)); - (*pos.borrow_mut()) = match (*result.borrow()) - .iter() - .take((*result.borrow()).len() - 1) - .rposition(|&x| { - Ptr::from_string_literal(b"o") - .to_c_string_iterator() - .position(|ch| ch == x) - .is_some() - }) { - Some(idx) => idx as u64, - None => u64::MAX, + (*pos.borrow_mut()) = { + let __lookup: Vec = Ptr::from_string_literal(b"o") + .to_c_string_iterator() + .collect(); + (*result.borrow()) + .iter() + .take((*result.borrow()).len().saturating_sub(1)) + .rposition(|&x| __lookup.contains(&x)) + .map(|idx| idx as u64) + .unwrap_or(u64::MAX) }; assert!(((*pos.borrow()) == 7_u64)); - (*pos.borrow_mut()) = match (*result.borrow()) - .iter() - .take((*result.borrow()).len() - 1) - .rposition(|&x| { - Ptr::from_string_literal(b"x") - .to_c_string_iterator() - .position(|ch| ch == x) - .is_some() - }) { - Some(idx) => idx as u64, - None => u64::MAX, + (*pos.borrow_mut()) = { + let __lookup: Vec = Ptr::from_string_literal(b"x") + .to_c_string_iterator() + .collect(); + (*result.borrow()) + .iter() + .take((*result.borrow()).len().saturating_sub(1)) + .rposition(|&x| __lookup.contains(&x)) + .map(|idx| idx as u64) + .unwrap_or(u64::MAX) }; assert!(((*pos.borrow()) == (-1_i64 as u64))); let string_to_cast: Value> = Rc::new(RefCell::new( diff --git a/tests/unit/out/refcount/string2.rs b/tests/unit/out/refcount/string2.rs index 2ed6f7aa..76454809 100644 --- a/tests/unit/out/refcount/string2.rs +++ b/tests/unit/out/refcount/string2.rs @@ -26,7 +26,7 @@ fn main_0() -> i32 { assert!((*arr.borrow()) .iter() .copied() - .take((*arr.borrow()).len() - 1) + .take((*arr.borrow()).len().saturating_sub(1)) .eq(Ptr::from_string_literal(b"fbo").to_c_string_iterator())); return 0; } From db6226c2fc3a1c6ddd451bacc86c138e43c66c4d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 3 Jun 2026 22:50:06 +0100 Subject: [PATCH 19/26] Wrap variadic arg in paren before calling .into() (#168) `libc::strlen(dummy as *const i8) as u64.into()` (without paren) errors because we can't write `as u64.into()`, we need to wrap the entire arg in a paren before calling .into(): `(libc::strlen(dummy as *const i8) as u64).into()` --- tests/unit/out/refcount/va_arg_conditional.rs | 4 ++-- .../unit/out/refcount/va_arg_non_primitive_ptrs.rs | 8 ++++---- tests/unit/out/refcount/va_arg_printf.rs | 13 ++++++++++--- tests/unit/out/refcount/va_arg_snprintf.rs | 4 ++-- tests/unit/out/refcount/va_arg_struct_ctx.rs | 4 ++-- tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/tests/unit/out/refcount/va_arg_conditional.rs b/tests/unit/out/refcount/va_arg_conditional.rs index 9673c8de..3dd65378 100644 --- a/tests/unit/out/refcount/va_arg_conditional.rs +++ b/tests/unit/out/refcount/va_arg_conditional.rs @@ -25,7 +25,7 @@ fn main_0() -> i32 { (((({ let _verbose: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - conditional_log_0(_verbose, _fmt, &[42.into()]) + conditional_log_0(_verbose, _fmt, &[(42).into()]) }) == 42) as i32) != 0) ); @@ -33,7 +33,7 @@ fn main_0() -> i32 { (((({ let _verbose: i32 = 0; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - conditional_log_0(_verbose, _fmt, &[99.into()]) + conditional_log_0(_verbose, _fmt, &[(99).into()]) }) == -1_i32) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs index 394d0439..e62b86ee 100644 --- a/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs +++ b/tests/unit/out/refcount/va_arg_non_primitive_ptrs.rs @@ -91,7 +91,7 @@ fn main_0() -> i32 { assert!( (((({ let _option: i32 = (opt::OPT_FILE as i32); - dispatch_0(_option, &[libcc2rs::cout().into()]) + dispatch_0(_option, &[(libcc2rs::cout()).into()]) }) == 1) as i32) != 0) ); @@ -100,10 +100,10 @@ fn main_0() -> i32 { let _option: i32 = (opt::OPT_FILE as i32); dispatch_0( _option, - &[(AnyPtr::default()) + &[((AnyPtr::default()) .cast::<::std::fs::File>() - .expect("ub:wrong type") - .into()], + .expect("ub:wrong type")) + .into()], ) }) == 0) as i32) != 0) diff --git a/tests/unit/out/refcount/va_arg_printf.rs b/tests/unit/out/refcount/va_arg_printf.rs index 6a2458b1..0c1850e3 100644 --- a/tests/unit/out/refcount/va_arg_printf.rs +++ b/tests/unit/out/refcount/va_arg_printf.rs @@ -32,17 +32,24 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { + let dummy: Value> = Rc::new(RefCell::new(Ptr::from_string_literal(b"dummy"))); assert!( (((({ let _fmt: Ptr = Ptr::from_string_literal(b"hello %d %d"); - logf_1(_fmt, &[10.into(), 32.into()]) - }) == 42) as i32) + logf_1( + _fmt, + &[ + (10).into(), + ((*dummy.borrow()).to_string_iterator().count() as u64).into(), + ], + ) + }) == 15) as i32) != 0) ); assert!( (((({ let _fmt: Ptr = Ptr::from_string_literal(b"x %d %d"); - logf_1(_fmt, &[1.into(), 2.into()]) + logf_1(_fmt, &[(1).into(), (2).into()]) }) == 3) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_snprintf.rs b/tests/unit/out/refcount/va_arg_snprintf.rs index f52b086d..e18d2d72 100644 --- a/tests/unit/out/refcount/va_arg_snprintf.rs +++ b/tests/unit/out/refcount/va_arg_snprintf.rs @@ -29,7 +29,7 @@ fn main_0() -> i32 { let _buf: Ptr = (buf.as_pointer() as Ptr); let _size: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - extract_first_0(_buf, _size, _fmt, &[42.into()]) + extract_first_0(_buf, _size, _fmt, &[(42).into()]) }) == 42) as i32) != 0) ); @@ -39,7 +39,7 @@ fn main_0() -> i32 { let _buf: Ptr = (buf.as_pointer() as Ptr); let _size: i32 = 1; let _fmt: Ptr = Ptr::from_string_literal(b"%d"); - extract_first_0(_buf, _size, _fmt, &[65.into()]) + extract_first_0(_buf, _size, _fmt, &[(65).into()]) }) == 65) as i32) != 0) ); diff --git a/tests/unit/out/refcount/va_arg_struct_ctx.rs b/tests/unit/out/refcount/va_arg_struct_ctx.rs index f5ccee40..e6e9ad73 100644 --- a/tests/unit/out/refcount/va_arg_struct_ctx.rs +++ b/tests/unit/out/refcount/va_arg_struct_ctx.rs @@ -43,14 +43,14 @@ fn main_0() -> i32 { ({ let _ctx: Ptr = (ctx.as_pointer()); let _fmt: Ptr = Ptr::from_string_literal(b"error %d"); - set_error_0(_ctx, _fmt, &[42.into()]) + set_error_0(_ctx, _fmt, &[(42).into()]) }); assert!(((((*(*ctx.borrow()).last_error.borrow()) == 42) as i32) != 0)); (*(*ctx.borrow()).verbose.borrow_mut()) = 0; ({ let _ctx: Ptr = (ctx.as_pointer()); let _fmt: Ptr = Ptr::from_string_literal(b"error %d"); - set_error_0(_ctx, _fmt, &[99.into()]) + set_error_0(_ctx, _fmt, &[(99).into()]) }); assert!(((((*(*ctx.borrow()).last_error.borrow()) == 42) as i32) != 0)); return 0; diff --git a/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs b/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs index b9b89d6d..7dfcc03e 100644 --- a/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs +++ b/tests/unit/out/unsafe/va_arg_non_primitive_ptrs.rs @@ -84,7 +84,7 @@ unsafe fn main_0() -> i32 { assert!( ((((unsafe { let _option: i32 = (opt::OPT_FILE as i32); - dispatch_0(_option, &[libcc2rs::stdout_unsafe().into()]) + dispatch_0(_option, &[(libcc2rs::stdout_unsafe()).into()]) }) == (1)) as i32) != 0) ); From 968f1cacfa028f00ff8989fb8a5de63fcf92bdb0 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 4 Jun 2026 08:20:55 +0100 Subject: [PATCH 20/26] Handle array to pointer decay for libc members (#176) --- cpp2rust/converter/converter.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index ef3259d5..9f493c8c 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1979,9 +1979,18 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { Convert(sub_expr); break; } - Convert(sub_expr); bool dest_pointee_const = expr->getType()->getPointeeType().isConstQualified(); + if (const auto *member = + clang::dyn_cast(sub_expr->IgnoreParenImpCasts()); + member && IsCharArrayFieldFromLibc(member->getMemberDecl())) { + PushParen paren(*this); + Convert(sub_expr); + StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()"); + StrCat(keyword::kAs, dest_pointee_const ? "*const u8" : "*mut u8"); + break; + } + Convert(sub_expr); if (clang::isa(sub_expr) || clang::isa(sub_expr)) { StrCat(".as_ptr()"); From 39c5102d0c0b36c7bee39d12d1b6ded8018b11a4 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 27 May 2026 21:14:53 +0100 Subject: [PATCH 21/26] remove a few string copies --- cpp2rust/converter/converter.cpp | 47 ++++++++++++++++---------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 9f493c8c..de02eb7c 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1116,9 +1116,9 @@ bool Converter::VisitWhileStmt(clang::WhileStmt *stmt) { ConvertCondition(stmt->getCond()); { PushBrace brace(*this); - curr_for_inc_.emplace(nullptr); + curr_for_inc_.emplace_back(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop(); + curr_for_inc_.pop_back(); } return false; } @@ -1129,9 +1129,9 @@ bool Converter::VisitDoStmt(clang::DoStmt *stmt) { StrCat(keyword::kLoop); { PushBrace loop_brace(*this); - curr_for_inc_.emplace(nullptr); + curr_for_inc_.emplace_back(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop(); + curr_for_inc_.pop_back(); StrCat(keyword::kIf, token::kNot); { PushParen paren(*this); @@ -1157,9 +1157,9 @@ bool Converter::VisitForStmt(clang::ForStmt *stmt) { } { PushBrace brace(*this); - curr_for_inc_.emplace(stmt->getInc()); + curr_for_inc_.emplace_back(stmt->getInc()); Convert(stmt->getBody()); - curr_for_inc_.pop(); + curr_for_inc_.pop_back(); Convert(stmt->getInc()); StrCat(token::kSemiColon); } @@ -1195,9 +1195,9 @@ void Converter::ConvertForRangeBody(clang::CXXForRangeStmt *stmt, std::optional skip; if (map_iter_decl) skip.emplace(*this, map_iter_decl); - curr_for_inc_.emplace(nullptr); + curr_for_inc_.emplace_back(nullptr); Convert(stmt->getBody()); - curr_for_inc_.pop(); + curr_for_inc_.pop_back(); } bool Converter::VisitCXXForRangeStmt(clang::CXXForRangeStmt *stmt) { @@ -1288,7 +1288,7 @@ bool Converter::VisitBreakStmt([[maybe_unused]] clang::BreakStmt *stmt) { bool Converter::VisitContinueStmt([[maybe_unused]] clang::ContinueStmt *stmt) { if (!curr_for_inc_.empty()) { - Convert(curr_for_inc_.top()); + Convert(curr_for_inc_.back()); StrCat(token::kSemiColon); } StrCat(keyword::kContinue); @@ -1331,7 +1331,7 @@ bool Converter::IsSubExprOf(const clang::Expr *sub_expr, } bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, - std::string &fmt_args, std::string &fmt_trait, + std::string &fmt_args, const char *&fmt_trait, std::string &fmt_width) { std::string arg_str = Mapper::ToString(arg); if (auto *str_lit = @@ -1340,22 +1340,21 @@ bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, return false; } auto str = GetEscapedStringLiteral(arg); + std::string_view trim(str); // Delete " from string - str.erase(std::remove(str.begin(), str.end(), '"'), str.end()); - fmt += std::move(str); + trim.remove_prefix(1); + trim.remove_suffix(1); + fmt += trim; } else if (auto ch = GetEscapedUTF8CharLiteral(arg); !ch.empty()) { fmt += std::move(ch); } else if (arg_str.contains("std::endl")) { fmt += "\\n"; } else if (arg_str.contains("std::hex")) { - fmt_trait = 'x'; + fmt_trait = "x"; } else if (arg_str.contains("std::dec")) { fmt_trait = ""; } else if (arg_str.contains("Setw")) { - fmt_width = ToString(arg); - // Delete leading and trailing whitespaces - fmt_width.erase(0, fmt_width.find_first_not_of(' ')); - fmt_width.erase(fmt_width.find_last_not_of(' ') + 1); + fmt_width = Trim(ToString(arg)); } else if (!arg->getType()->isCharType() && Mapper::Map(arg->getType()) != "Vec") { fmt += ("{:" + fmt_width + fmt_trait + "}"); @@ -1424,7 +1423,7 @@ void Converter::ConvertCallToOstream(clang::CallExpr *expr) { } std::string fmt; - std::string fmt_trait; + const char *fmt_trait = ""; std::string fmt_width; std::string fmt_args; std::string raw_args; @@ -1460,7 +1459,7 @@ void Converter::ConvertCallToOstream(clang::CallExpr *expr) { write_raw_args(); } - assert(fmt_trait == "" && "Stream state was not restored after call"); + assert(*fmt_trait == '\0' && "Stream state was not restored after call"); } void Converter::ConvertPrintf(clang::CallExpr *expr) { @@ -1882,8 +1881,8 @@ std::string Converter::GetEscapedStringLiteral(clang::Expr *expr, } bool Converter::VisitStringLiteral(clang::StringLiteral *expr) { - if (!curr_init_type_.empty() && curr_init_type_.top()->isArrayType()) { - if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.top())) { + if (!curr_init_type_.empty() && curr_init_type_.back()->isArrayType()) { + if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.back())) { uint64_t arr_size = arr_ty->getSize().getZExtValue(); if (expr->getString().empty()) { StrCat(std::format("[0u8; {}]", arr_size)); @@ -2202,8 +2201,8 @@ void Converter::ConvertBinaryOperator(clang::BinaryOperator *expr) { Convert(lhs); ConvertCast(computation_result_type); } - std::string op(opcode_as_string); - op.erase(std::remove(op.begin(), op.end(), '='), op.end()); + auto op = opcode_as_string; + op.remove_suffix(1); // remove '=' from operator StrCat(op); Convert(rhs); } @@ -2908,7 +2907,7 @@ bool Converter::VisitCXXNewExpr(clang::CXXNewExpr *expr) { alloc_type_as_string); StrCat(new_array_as_string); } - if (!curr_init_type_.empty() && curr_init_type_.top()->isPointerType()) { + if (!curr_init_type_.empty() && curr_init_type_.back()->isPointerType()) { StrCat(".as_mut_ptr()"); } } else { From a2c71b8104300d243b560163048eaf7364a22b10 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 27 May 2026 21:38:33 +0100 Subject: [PATCH 22/26] minor code simplifications --- cpp2rust/converter/converter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index de02eb7c..14f2fd10 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -79,7 +79,7 @@ bool Converter::Convert(clang::QualType qual_type) { } auto mapped = Mapper::Map(qual_type); - if (!mapped.empty() && mapped != ignore_rule_type_) { + if (!mapped.empty() && mapped != token::kIgnoreRule) { StrCat(mapped); return false; } @@ -90,7 +90,7 @@ bool Converter::Convert(clang::QualType qual_type) { bool Converter::ConvertMappedType(clang::QualType qual_type) { std::string type_as_string = Mapper::Map(qual_type); - if (type_as_string == ignore_rule_type_) { + if (type_as_string == token::kIgnoreRule) { return false; } StrCat(type_as_string); @@ -2853,7 +2853,7 @@ bool Converter::VisitArraySubscriptExpr(clang::ArraySubscriptExpr *expr) { } bool Converter::VisitCXXNullPtrLiteralExpr(clang::CXXNullPtrLiteralExpr *expr) { - StrCat(keyword_default_); + StrCat(token::kDefault); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -2883,7 +2883,7 @@ bool Converter::VisitVAArgExpr(clang::VAArgExpr *expr) { } bool Converter::VisitGNUNullExpr(clang::GNUNullExpr *expr) { - StrCat(keyword_default_); + StrCat(token::kDefault); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -3106,7 +3106,7 @@ void Converter::AddIncDecImpls(clang::EnumDecl *decl) { bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { if (expr->getType()->isPointerType()) { - StrCat(keyword_default_); + StrCat(token::kDefault); } return false; } @@ -3427,7 +3427,7 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) { return {"Copy", "Clone"}; } - std::vector struct_attrs = {}; + std::vector struct_attrs; if (RecordDerivesCopy(decl)) { struct_attrs.emplace_back("Copy"); From a6d0d4817427a037c86c2d1e3bf09da03f0e2641 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sun, 31 May 2026 11:24:12 +0100 Subject: [PATCH 23/26] Fix do while continue stmt (#160) A continue statement inside a `do while` breakes the iteration and re-evaluates the condition. Our previous translation, i.e. ```rs loop { ... if !cond { break } } ``` never re-evaluated the condition after a continue, resulting in an infinite loop. To fix this, use while instead of loop. This ensures that the condition is re-evaluated right after the continue: ```rs let mut __do_while = true; while __do_while || cond { __do_while = false; ... } ``` __do_while makes sure that the first iteration of the loop is always executed. --- cpp2rust/converter/converter.cpp | 20 +++++++--------- .../out/refcount/fn_ptr_stdlib_compare.rs | 24 +++++++++---------- tests/unit/out/refcount/switch_in_dowhile.rs | 4 ++-- tests/unit/out/unsafe/switch_in_dowhile.rs | 4 ++-- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 14f2fd10..8d43ec50 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1125,22 +1125,20 @@ bool Converter::VisitWhileStmt(clang::WhileStmt *stmt) { bool Converter::VisitDoStmt(clang::DoStmt *stmt) { PushBreakTarget push(break_target_, BreakTarget::Loop); - StrCat("'loop_:"); - StrCat(keyword::kLoop); + const char *control_var = "__do_while"; + StrCat(keyword::kLet, "mut", control_var, token::kAssign, keyword::kTrue, + token::kSemiColon); + StrCat("'loop_:", keyword::kWhile, control_var, "||"); + { + PushParen paren(*this); + ConvertCondition(stmt->getCond()); + } { PushBrace loop_brace(*this); + StrCat(control_var, token::kAssign, keyword::kFalse, token::kSemiColon); curr_for_inc_.emplace_back(nullptr); Convert(stmt->getBody()); curr_for_inc_.pop_back(); - StrCat(keyword::kIf, token::kNot); - { - PushParen paren(*this); - ConvertCondition(stmt->getCond()); - } - { - PushBrace if_brace(*this); - StrCat(keyword::kBreak, token::kSemiColon); - } } return false; } diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index 3402eca2..41214323 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -77,17 +77,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal(b"rb").to_rust_string() { + match Ptr::from_string_literal("rb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -131,17 +131,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal(b"rb").to_rust_string() { + match Ptr::from_string_literal("rb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -241,17 +241,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal(b"wb").to_rust_string() { + match Ptr::from_string_literal("wb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) + .open(Ptr::from_string_literal("/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) + .open(Ptr::from_string_literal("/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -285,17 +285,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal(b"wb").to_rust_string() { + match Ptr::from_string_literal("wb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) + .open(Ptr::from_string_literal("/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) + .open(Ptr::from_string_literal("/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), diff --git a/tests/unit/out/refcount/switch_in_dowhile.rs b/tests/unit/out/refcount/switch_in_dowhile.rs index d7b72a1d..03d69848 100644 --- a/tests/unit/out/refcount/switch_in_dowhile.rs +++ b/tests/unit/out/refcount/switch_in_dowhile.rs @@ -16,11 +16,11 @@ pub fn switch_in_dowhile_0(n: i32) -> i32 { 'switch: { let __match_cond = (*i.borrow()); match __match_cond { - __v if __v == 0 => { + v if v == 0 => { (*r.borrow_mut()) += 1; break 'switch; } - __v if __v == 1 => { + v if v == 1 => { (*r.borrow_mut()) += 10; break 'switch; } diff --git a/tests/unit/out/unsafe/switch_in_dowhile.rs b/tests/unit/out/unsafe/switch_in_dowhile.rs index a9b42531..3c34a732 100644 --- a/tests/unit/out/unsafe/switch_in_dowhile.rs +++ b/tests/unit/out/unsafe/switch_in_dowhile.rs @@ -15,11 +15,11 @@ pub unsafe fn switch_in_dowhile_0(mut n: i32) -> i32 { 'switch: { let __match_cond = i; match __match_cond { - __v if __v == 0 => { + v if v == 0 => { r += 1; break 'switch; } - __v if __v == 1 => { + v if v == 1 => { r += 10; break 'switch; } From 0181b75eeffd5bf838fdafe8ea8a1d9de3ecb8b8 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:17:39 +0100 Subject: [PATCH 24/26] Convert single-character `StrCat` string args to char literals (#178) `StrCat` calls were using single-character string literals in many places (e.g. `">"`-style delimiters and punctuation), which is inconsistent with the intended char-based usage for single codepoints. This updates those call sites to pass character literals instead. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- cpp2rust/converter/converter.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8d43ec50..1da3bd50 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -165,7 +165,7 @@ bool Converter::VisitRecordType(clang::RecordType *type) { ->getAs(), FnProtoType::LambdaCallOperator)); } else { - StrCat("_"); + StrCat('_'); } return false; } @@ -3061,7 +3061,7 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { Mapper::AddRuleForUserDefinedType(decl); StrCat("#[derive(Clone, Copy, PartialEq, Debug, Default)]"); StrCat(std::format("enum {}", GetRecordName(decl))); - StrCat("{"); + StrCat('{'); bool first_enumerator = true; for (auto e : decl->enumerators()) { llvm::SmallVector init; @@ -3073,7 +3073,7 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { StrCat(std::format("{} = {},", std::string_view(e->getName()), std::string_view(init.data(), init.size()))); } - StrCat("}"); + StrCat('}'); AddFromImpl(decl); AddIncDecImpls(decl); @@ -3114,7 +3114,7 @@ bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) { StrCat("Some"); } PushParen paren(*this); - StrCat("|"); + StrCat('|'); for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) { StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()), token::kComma); @@ -3126,7 +3126,7 @@ bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) { curr_function_ = expr->getLambdaClass()->getLambdaCallOperator(); ConvertFunctionBody(curr_function_); curr_function_ = old_function; - StrCat("}"); + StrCat('}'); return false; } @@ -3702,21 +3702,21 @@ void Converter::ConvertOrdAndPartialOrdTraitsBase( std::string_view first_branch, std::string_view second_branch, std::string_view first_return, std::string_view second_return, std::string_view record_name) { - StrCat(keyword::kImpl, "Ord for ", record_name, "{"); + StrCat(keyword::kImpl, "Ord for ", record_name, '{'); StrCat("fn cmp(&self, other: &Self) -> std::cmp::Ordering {"); StrCat(std::format("{} {{", keyword_unsafe_)); - StrCat("if", first_branch, "{", first_return, "} else if", second_branch, "{", + StrCat("if", first_branch, '{', first_return, "} else if", second_branch, '{', second_return, "} else { std::cmp::Ordering::Equal }"); StrCat("}}}"); - StrCat(keyword::kImpl, "PartialOrd for", record_name, "{"); + StrCat(keyword::kImpl, "PartialOrd for", record_name, '{'); StrCat(R"( fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } })"); - StrCat(keyword::kImpl, "PartialEq for", record_name, "{"); + StrCat(keyword::kImpl, "PartialEq for", record_name, '{'); StrCat("fn eq(&self, other: &Self) -> bool {"); StrCat(std::format("{} {{", keyword_unsafe_)); StrCat("!(", first_branch, ") && !(", second_branch, ')'); From 7be35381f7391f9a0a3705b85a370d0d2942177b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 4 Jun 2026 17:17:13 +0100 Subject: [PATCH 25/26] Update tests --- .../out/refcount/fn_ptr_stdlib_compare.rs | 24 +++++++++---------- tests/unit/out/refcount/switch_in_dowhile.rs | 4 ++-- tests/unit/out/unsafe/switch_in_dowhile.rs | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index 41214323..3402eca2 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -77,17 +77,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal("rb").to_rust_string() { + match Ptr::from_string_literal(b"rb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -131,17 +131,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal("rb").to_rust_string() { + match Ptr::from_string_literal(b"rb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/zero").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -241,17 +241,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal("wb").to_rust_string() { + match Ptr::from_string_literal(b"wb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal("/dev/null").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal("/dev/null").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), @@ -285,17 +285,17 @@ fn main_0() -> i32 { 'loop_: while __do_while || (0 != 0) { __do_while = false; let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal("wb").to_rust_string() { + match Ptr::from_string_literal(b"wb").to_rust_string() { v if v == "rb" => std::fs::OpenOptions::new() .read(true) - .open(Ptr::from_string_literal("/dev/null").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), v if v == "wb" => std::fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(Ptr::from_string_literal("/dev/null").to_rust_string()) + .open(Ptr::from_string_literal(b"/dev/null").to_rust_string()) .ok() .map_or(Ptr::null(), |f| Ptr::alloc(f)), _ => panic!("unsupported mode"), diff --git a/tests/unit/out/refcount/switch_in_dowhile.rs b/tests/unit/out/refcount/switch_in_dowhile.rs index 03d69848..d7b72a1d 100644 --- a/tests/unit/out/refcount/switch_in_dowhile.rs +++ b/tests/unit/out/refcount/switch_in_dowhile.rs @@ -16,11 +16,11 @@ pub fn switch_in_dowhile_0(n: i32) -> i32 { 'switch: { let __match_cond = (*i.borrow()); match __match_cond { - v if v == 0 => { + __v if __v == 0 => { (*r.borrow_mut()) += 1; break 'switch; } - v if v == 1 => { + __v if __v == 1 => { (*r.borrow_mut()) += 10; break 'switch; } diff --git a/tests/unit/out/unsafe/switch_in_dowhile.rs b/tests/unit/out/unsafe/switch_in_dowhile.rs index 3c34a732..a9b42531 100644 --- a/tests/unit/out/unsafe/switch_in_dowhile.rs +++ b/tests/unit/out/unsafe/switch_in_dowhile.rs @@ -15,11 +15,11 @@ pub unsafe fn switch_in_dowhile_0(mut n: i32) -> i32 { 'switch: { let __match_cond = i; match __match_cond { - v if v == 0 => { + __v if __v == 0 => { r += 1; break 'switch; } - v if v == 1 => { + __v if __v == 1 => { r += 10; break 'switch; } From 063bd73487a9d1bad5c4b1d856ba66ce1a56983e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 4 Jun 2026 17:19:48 +0100 Subject: [PATCH 26/26] Delete extra paren --- cpp2rust/converter/converter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 1da3bd50..3c96df3c 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1488,7 +1488,6 @@ std::optional Converter::TryPluginConvert(clang::CallExpr *call) { void Converter::ConvertVariadicArg(clang::Expr *arg) { if (arg->getType()->isFunctionPointerType()) { - PushParen p(*this); Convert(arg); StrCat(".map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void)"); return;