Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cpp2rust/compat/assert.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

#undef assert

#ifndef __cplusplus
#include <stdbool.h>
#endif

void __cpp2rust_assert_fail(bool condition) __attribute__((noreturn));

#define assert(expr) __cpp2rust_assert_fail(expr)
85 changes: 82 additions & 3 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,16 @@ bool Converter::VisitPointerType(clang::PointerType *type) {
return false;
}

if (IsVaListType(ctx_, clang::QualType(type, 0))) {
StrCat("VaList");
return false;
}

StrCat(token::kStar);
auto pointee_type = type->getPointeeType();
StrCat(pointee_type.isConstQualified() ? keyword::kConst : keyword_mut_);
if (pointee_type->isRecordType() &&
abstract_structs_.contains(GetID(pointee_type->getAsCXXRecordDecl()))) {
abstract_structs_.contains(GetID(pointee_type->getAsRecordDecl()))) {
StrCat(keyword::kDyn);
}
return Convert(pointee_type);
Expand Down Expand Up @@ -352,9 +357,25 @@ bool Converter::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) {
return false;
}

void Converter::ConvertVaListVarDecl(clang::VarDecl *decl) {
if (clang::isa<clang::ParmVarDecl>(decl)) {
// va_list parameter (decayed to __va_list_tag *)
} else {
// va_list local variable
StrCat(keyword::kLet);
}
StrCat(keyword_mut_, GetNamedDeclAsString(decl), token::kColon, "VaList");
}

bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
auto qual_type = decl->getType();
auto name = GetNamedDeclAsString(decl);

if (IsVaListType(ctx_, qual_type) && decl->isLocalVarDecl()) {
ConvertVaListVarDecl(decl);
return true;
}

if (decl->isFileVarDecl()) {
name = std::regex_replace(Mapper::ToString(decl), std::regex("::"), "_");
if ((decl->isExternallyDeclarable() && !decl->hasInit()) ||
Expand Down Expand Up @@ -1259,7 +1280,28 @@ std::optional<std::string> Converter::TryPluginConvert(clang::CallExpr *call) {
return std::nullopt;
}

void Converter::ConvertVAArgCall(clang::CallExpr *expr) {
if (IsBuiltinVaStart(expr)) {
StrCat(ToString(expr->getArg(0)->IgnoreImpCasts()), "= VaList::new(args)");
return;
}
if (IsBuiltinVaEnd(expr)) {
// va_end is a no-op
return;
}
if (IsBuiltinVaCopy(expr)) {
StrCat(ToString(expr->getArg(0)->IgnoreImpCasts()), "=",
ToString(expr->getArg(1)->IgnoreImpCasts()), ".clone()");
return;
}
}

bool Converter::VisitCallExpr(clang::CallExpr *expr) {
if (IsBuiltinVaStart(expr) || IsBuiltinVaEnd(expr) || IsBuiltinVaCopy(expr)) {
ConvertVAArgCall(expr);
return false;
}

if (auto plugin_str = TryPluginConvert(expr)) {
StrCat(*plugin_str);
return false;
Expand Down Expand Up @@ -1356,11 +1398,16 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
"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<std::string> temp_refs(num_args);

for (unsigned i = 0; i < num_args; ++i) {
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()
Expand All @@ -1387,7 +1434,7 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {

Convert(callee);
StrCat(token::kOpenParen);
for (unsigned i = 0; i < num_args; ++i) {
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()
Expand All @@ -1412,6 +1459,18 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
}
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);
Convert(arg);
StrCat(".into()", token::kComma);
}
StrCat("]");
}

StrCat(token::kCloseParen);
StrCat(token::kCloseCurlyBracket);
StrCat(token::kCloseParen);
Expand Down Expand Up @@ -1561,6 +1620,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
clang::isa<clang::PredefinedExpr>(sub_expr)) {
return Convert(sub_expr);
}
// __va_list_tag [1] decays to __va_list_tag *. Just pass through by value
if (IsVaListType(ctx_, sub_expr->getType())) {
Convert(sub_expr);
break;
}
Convert(sub_expr);
if (sub_expr->getType().isConstQualified()) {
StrCat(keyword_ptr_decay_const_);
Expand Down Expand Up @@ -2209,6 +2273,18 @@ bool Converter::VisitCXXNullPtrLiteralExpr(clang::CXXNullPtrLiteralExpr *expr) {
return false;
}

bool Converter::VisitVAArgExpr(clang::VAArgExpr *expr) {
auto va_list_expr = expr->getSubExpr();
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(va_list_expr)) {
va_list_expr = cast->getSubExpr();
}
Convert(va_list_expr);
StrCat(".arg::<");
Convert(expr->getType());
StrCat(">()");
return false;
}

bool Converter::VisitGNUNullExpr(clang::GNUNullExpr *expr) {
StrCat(keyword_default_);
computed_expr_type_ = ComputedExprType::FreshPointer;
Expand Down Expand Up @@ -2807,6 +2883,9 @@ void Converter::ConvertFunctionParameters(clang::FunctionDecl *decl) {
ConvertVarDeclSkipInit(parameter);
StrCat(token::kComma);
}
if (decl->isVariadic()) {
StrCat("args: &[VaArg]", token::kComma);
}
in_function_formals_ = false;
}

Expand Down
8 changes: 7 additions & 1 deletion cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual void ConvertGlobalVarDecl(clang::VarDecl *decl);

bool ConvertVarDeclSkipInit(clang::VarDecl *decl);
virtual void ConvertVaListVarDecl(clang::VarDecl *decl);

virtual bool ConvertVarDeclSkipInit(clang::VarDecl *decl);

virtual bool ConvertLambdaVarDecl(clang::VarDecl *decl);

Expand Down Expand Up @@ -199,6 +201,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual void ConvertPrintf(clang::CallExpr *expr);

void ConvertVAArgCall(clang::CallExpr *expr);

virtual bool VisitCallExpr(clang::CallExpr *expr);

virtual bool VisitIntegerLiteral(clang::IntegerLiteral *expr);
Expand Down Expand Up @@ -273,6 +277,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual bool VisitSwitchCase(clang::SwitchCase *stmt);

virtual bool VisitVAArgExpr(clang::VAArgExpr *expr);

virtual bool VisitCXXDefaultInitExpr(clang::CXXDefaultInitExpr *expr);

virtual bool VisitPredefinedExpr(clang::PredefinedExpr *expr);
Expand Down
55 changes: 55 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ unsigned GetArraySize(clang::QualType array_type) {
}

std::string GetID(const clang::Decl *decl) {
assert(decl);
const auto file_name = GetFileName(decl);
const auto line_num = GetLineNumber(decl);
const auto column_num = GetColumnNumber(decl);
Expand Down Expand Up @@ -557,4 +558,58 @@ bool IsRedundantCopyInConversion(clang::ASTContext &ctx,
return parent && parent->getConstructor()->isConvertingConstructor(false);
}

// va_list is implemented as __va_list_tag[1] and decays to __va_list_tag *.
// That's because va_list must have pointer semantics, but still be passed as
// value by user code.
bool IsVaListType(clang::ASTContext &ctx, clang::QualType type) {
auto canonical = type.getCanonicalType();
auto va_list = ctx.getBuiltinVaListType().getCanonicalType();

// Direct match: va_list itself
if (canonical == va_list) {
return true;
}

// Decayed match: __va_list_tag[1] decays to __va_list_tag *
if (auto *arr = clang::dyn_cast<clang::ConstantArrayType>(va_list)) {
return canonical ==
ctx.getPointerType(arr->getElementType()).getCanonicalType();
}

return false;
}

bool IsBuiltinVaStart(const clang::CallExpr *expr) {
if (auto *fn = expr->getDirectCallee()) {
return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_start;
}
return false;
}

bool IsBuiltinVaEnd(const clang::CallExpr *expr) {
if (auto *fn = expr->getDirectCallee()) {
return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_end;
}
return false;
}

bool IsBuiltinVaCopy(const clang::CallExpr *expr) {
if (auto *fn = expr->getDirectCallee()) {
return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_copy;
}
return false;
}

bool ContainsVAArgExpr(const clang::Stmt *stmt) {
if (clang::isa<clang::VAArgExpr>(stmt)) {
return true;
}
for (auto *child : stmt->children()) {
if (ContainsVAArgExpr(child)) {
return true;
}
}
return false;
}

} // namespace cpp2rust
10 changes: 10 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,14 @@ std::string GetClassName(clang::QualType type);
bool IsRedundantCopyInConversion(clang::ASTContext &ctx,
const clang::CXXConstructExpr *expr);

bool IsVaListType(clang::ASTContext &ctx, clang::QualType type);

bool IsBuiltinVaStart(const clang::CallExpr *expr);

bool IsBuiltinVaEnd(const clang::CallExpr *expr);

bool IsBuiltinVaCopy(const clang::CallExpr *expr);

bool ContainsVAArgExpr(const clang::Stmt *stmt);

} // namespace cpp2rust
48 changes: 45 additions & 3 deletions cpp2rust/converter/models/converter_refcount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ bool ConverterRefCount::VisitPointerType(clang::PointerType *type) {
return false;
}

if (IsVaListType(ctx_, clang::QualType(type, 0))) {
StrCat("VaList");
return false;
}

if (type->isVoidPointerType()) {
StrCat("AnyPtr");
return false;
Expand Down Expand Up @@ -472,6 +477,17 @@ void ConverterRefCount::EmitFunctionPreamble(clang::FunctionDecl *decl) {
}
}

void ConverterRefCount::ConvertVaListVarDecl(clang::VarDecl *decl) {
if (clang::isa<clang::ParmVarDecl>(decl)) {
// va_list parameter (decayed to __va_list_tag *)
} else {
// va_list local variable
StrCat(keyword::kLet);
}

StrCat(GetNamedDeclAsString(decl), token::kColon, "Value<VaList>");
}

bool ConverterRefCount::ConvertLambdaVarDecl(clang::VarDecl *decl) {
return false;
}
Expand Down Expand Up @@ -775,6 +791,11 @@ void ConverterRefCount::ConvertPrintf(clang::CallExpr *expr) {
}

bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
if (IsBuiltinVaStart(expr) || IsBuiltinVaEnd(expr) || IsBuiltinVaCopy(expr)) {
ConvertVAArgCall(expr);
return false;
}

if (expr->isCallToStdMove()) {
if (IsUniquePtr(expr->getArg(0)->getType())) {
StrCat(std::format("{}.take()", ConvertLValue(expr->getArg(0))));
Expand Down Expand Up @@ -912,6 +933,10 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
}

if (expr->getCastKind() == clang::CastKind::CK_ArrayToPointerDecay) {
if (IsVaListType(ctx_, sub_expr->getType())) {
Convert(sub_expr);
return false;
}
if (clang::isa<clang::StringLiteral>(sub_expr) ||
clang::isa<clang::PredefinedExpr>(sub_expr)) {
StrCat(std::format("Ptr::from_string_literal({})", ToString(sub_expr)));
Expand Down Expand Up @@ -1366,6 +1391,18 @@ bool ConverterRefCount::VisitImplicitValueInitExpr(
return Converter::VisitImplicitValueInitExpr(expr);
}

bool ConverterRefCount::VisitVAArgExpr(clang::VAArgExpr *expr) {
auto va_list_expr = expr->getSubExpr();
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(va_list_expr)) {
va_list_expr = cast->getSubExpr();
}
StrCat(ConvertLValue(va_list_expr));
StrCat(".arg::<");
StrCat(GetUnsafeTypeAsString(expr->getType()));
StrCat(">()");
return false;
}

bool ConverterRefCount::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) {
return Converter::VisitCXXDefaultArgExpr(expr);
}
Expand Down Expand Up @@ -1569,9 +1606,14 @@ void ConverterRefCount::ConvertGenericBinaryOperator(
auto sides_contain_ptr_or_deref = std::ranges::any_of(rhs_vars, predicate) ||
std::ranges::any_of(lhs_vars, predicate);

auto may_cause_borrow_mut_err = !sides_contains_literal &&
!same_var_on_both_sides &&
sides_contain_ptr_or_deref;
auto both_sides_have_va_arg = same_var_on_both_sides &&
ContainsVAArgExpr(lhs) &&
ContainsVAArgExpr(rhs);

auto may_cause_borrow_mut_err =
both_sides_have_va_arg ||
(!sides_contains_literal && !same_var_on_both_sides &&
sides_contain_ptr_or_deref);

if (may_cause_borrow_mut_err) {
StrCat(std::format("{{ let _lhs = {}; _lhs {} {} }}",
Expand Down
4 changes: 4 additions & 0 deletions cpp2rust/converter/models/converter_refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class ConverterRefCount final : public Converter {

void ConvertGlobalVarDecl(clang::VarDecl *decl) override;

void ConvertVaListVarDecl(clang::VarDecl *decl) override;

bool ConvertLambdaVarDecl(clang::VarDecl *decl) override;

bool VisitDeclRefExpr(clang::DeclRefExpr *expr) override;
Expand Down Expand Up @@ -93,6 +95,8 @@ class ConverterRefCount final : public Converter {

bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) override;

bool VisitVAArgExpr(clang::VAArgExpr *expr) override;

void ConvertArrayCXXConstructExpr(clang::CXXConstructExpr *expr) override;

bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) override;
Expand Down
3 changes: 3 additions & 0 deletions libcc2rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ pub use iterators::*;

mod compat;
pub use compat::*;

mod va_args;
pub use va_args::*;
Loading