Skip to content

Commit 26da219

Browse files
committed
Rules: deduce decay automatically
1 parent 39109e3 commit 26da219

70 files changed

Lines changed: 1112 additions & 870 deletions

File tree

Some content is hidden

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

‎cpp2rust/converter/converter.cpp‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -763,11 +763,22 @@ bool Converter::TypeDerivesDefault(clang::QualType qual_type) {
763763
return true;
764764
}
765765

766-
bool Converter::IsPassThroughRule(clang::Expr *expr) const {
766+
bool Converter::IsPassThroughRule(clang::Expr *expr) {
767767
const auto *rule = RuleRegistry::GetExprRule(ctx_, expr);
768-
return rule && rule->body.size() == 1 &&
769-
std::holds_alternative<TranslationRule::PlaceholderFragment>(
770-
rule->body[0]);
768+
if (!rule || rule->body.size() != 1) {
769+
return false;
770+
}
771+
auto *ph = std::get_if<TranslationRule::PlaceholderFragment>(&rule->body[0]);
772+
if (!ph) {
773+
return false;
774+
}
775+
// A pointer to the elements of a container argument is a new pointer.
776+
auto *call = clang::dyn_cast<clang::CallExpr>(expr);
777+
return !call ||
778+
!ParamIsElementPtr(
779+
expr,
780+
BuildUnifiedArgs(call, call->getArgs(), call->getNumArgs())[ph->n],
781+
ph->n);
771782
}
772783

773784
bool Converter::RecordDerivesCopy(clang::QualType type) {
@@ -5140,6 +5151,13 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
51405151
}
51415152
}
51425153

5154+
// A pointer to the elements of the container is a new pointer, whatever the
5155+
// rule does with it.
5156+
if ((ph_ctx.needs_pointer_receiver() || ph_ctx.needs_ptr_wrap()) &&
5157+
ParamIsElementPtr(expr, arg, ph_ctx.arg_idx)) {
5158+
return ConvertElementPtr(arg);
5159+
}
5160+
51435161
if (ph_ctx.needs_pointer_receiver()) {
51445162
auto param_type = Mapper::GetParamType(*this, expr, ph_ctx.arg_idx);
51455163
bool borrowed = ph_ctx.access == TranslationRule::Access::kBorrow ||
@@ -5233,6 +5251,8 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
52335251
if (!tgt_ir)
52345252
return {};
52355253

5254+
CheckRuleReturnType(expr);
5255+
52365256
auto saved_borrows = std::exchange(mut_borrows_, {});
52375257
auto *operand = tgt_ir->refExprOperand();
52385258
bool lvalue = operand && !isAddrOf();

‎cpp2rust/converter/converter.h‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,17 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
793793

794794
bool RecordDerivesCopy(clang::QualType type);
795795

796-
bool IsPassThroughRule(clang::Expr *expr) const;
796+
bool IsPassThroughRule(clang::Expr *expr);
797+
798+
// Whether the rule of expr takes its argument number index, arg, a container
799+
// in C++, as a pointer to its elements rather than to the container.
800+
virtual bool ParamIsElementPtr(clang::Expr * /*expr*/, clang::Expr * /*arg*/,
801+
unsigned /*index*/) {
802+
return false;
803+
}
804+
805+
// Checks that the rule of expr returns the type that expr returns in C++.
806+
virtual void CheckRuleReturnType(clang::Expr * /*expr*/) {}
797807

798808
bool RecordHasCopyableFields(const clang::RecordDecl *decl);
799809

@@ -1081,6 +1091,11 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
10811091
virtual std::string ConvertPointerReceiver(clang::Expr *arg,
10821092
std::string_view param_type);
10831093

1094+
// A pointer to the elements of the container arg.
1095+
virtual std::string ConvertElementPtr(clang::Expr *arg) {
1096+
llvm_unreachable("no pointers to elements in this model");
1097+
}
1098+
10841099
// A pointer to a new temporary with the value of the prvalue expr, which
10851100
// lives until the end of the statement.
10861101
virtual std::string

‎cpp2rust/converter/mapper.cpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,9 @@ std::string GetParamType(Converter &converter, const clang::Expr *expr,
8282
return Matcher::InstantiateTgt(converter, subs, rule->params.at(index).type);
8383
}
8484

85+
std::string GetReturnType(Converter &converter, const clang::Expr *expr) {
86+
auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr);
87+
return Matcher::InstantiateTgt(converter, subs, rule->return_type.type);
88+
}
89+
8590
} // namespace cpp2rust::Mapper

‎cpp2rust/converter/mapper.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr,
2929
unsigned n);
3030
std::string GetParamType(Converter &converter, const clang::Expr *expr,
3131
unsigned index);
32+
std::string GetReturnType(Converter &converter, const clang::Expr *expr);
3233
} // namespace cpp2rust::Mapper

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 87 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ static bool IsBoxedType(clang::ASTContext &ctx, clang::QualType type) {
6060
return IsBoxedType(Mapper::Map(ctx, type.getUnqualifiedType()));
6161
}
6262

63+
// A Rust type without whitespace, and with the paths into libcc2rs, which the
64+
// rules import, unqualified, to compare it with another.
65+
static std::string NormalizeType(std::string type) {
66+
std::erase_if(type, [](char c) { return std::isspace(c); });
67+
static constexpr std::string_view kLib = "libcc2rs::";
68+
for (size_t pos; (pos = type.find(kLib)) != std::string::npos;) {
69+
type.erase(pos, kLib.size());
70+
}
71+
return type;
72+
}
73+
6374
// Virtual methods take &mut self, as the fields of the struct are stored in
6475
// it.
6576
static bool NeedsMutAccess(clang::ASTContext &ctx,
@@ -379,31 +390,92 @@ std::string ConverterRefCount::ConvertObject(clang::Expr *expr,
379390
PushExprKind push(*this, ExprKind::Object);
380391
auto saved_shape = std::exchange(object_shape_, shape);
381392
auto str = ToString(expr);
393+
// Unless the visitor already made a pointer to the elements.
394+
bool decay = object_shape_ == ObjectShape::Element;
382395
object_shape_ = saved_shape;
383-
if (shape == ObjectShape::Element && expr->getType()->isPointerType()) {
384-
auto pointee = expr->getType()->getPointeeType();
385-
if (IsBoxedType(ctx_, pointee) || pointee->isArrayType()) {
386-
PushConversionKind push(*this, ConversionKind::Pointee);
396+
if (decay) {
397+
auto type = expr->getType().getNonReferenceType();
398+
bool is_ptr = type->isPointerType();
399+
if (is_ptr) {
400+
type = type->getPointeeType();
401+
}
402+
if (IsBoxedType(ctx_, type) || (is_ptr && type->isArrayType())) {
387403
computed_expr_type_ = ComputedExprType::FreshPointer;
388-
return std::format("Ptr::<{}>::decay(&({}))", ToString(pointee),
389-
std::move(str));
404+
return std::move(str) + ".decay()";
390405
}
391406
}
392407
return str;
393408
}
394409

395-
std::string ConverterRefCount::AsPointerMethod(clang::QualType type) const {
396-
return isObject() && WantsElementPtr() &&
397-
type.getNonReferenceType()->isRecordType()
398-
? ".as_pointer().decay()"
399-
: ".as_pointer()";
410+
std::string ConverterRefCount::AsPointerMethod(clang::QualType type) {
411+
if (isObject() && object_shape_ == ObjectShape::Element &&
412+
type->isRecordType() && IsBoxedType(ctx_, type)) {
413+
object_shape_ = ObjectShape::Whole;
414+
return ".decay()";
415+
}
416+
return ".as_pointer()";
400417
}
401418

402419
std::string ConverterRefCount::ConvertPointerReceiver(clang::Expr *arg,
403420
std::string_view) {
404421
return isObject() ? ConvertObject(arg) : ConvertFreshObject(arg);
405422
}
406423

424+
bool ConverterRefCount::ParamIsElementPtr(clang::Expr *expr, clang::Expr *arg,
425+
unsigned index) {
426+
if (!RuleRegistry::ParamIsPointer(ctx_, expr, index)) {
427+
return false;
428+
}
429+
auto type = arg->getType().getNonReferenceType();
430+
if (type->isPointerType()) {
431+
type = type->getPointeeType();
432+
}
433+
type = type.getUnqualifiedType();
434+
if (!IsBoxedType(ctx_, type)) {
435+
return false;
436+
}
437+
// The C++ parameter is a reference or pointer to the container, which the
438+
// rule takes either as a pointer to it or as one to its elements.
439+
auto param = NormalizeType(Mapper::GetParamType(*this, expr, index));
440+
auto whole = NormalizeType(ToString(ctx_.getPointerType(type)));
441+
if (param == whole) {
442+
return false;
443+
}
444+
auto element = NormalizeType(ConvertPtrType(type));
445+
if (param != element) {
446+
llvm::errs() << "rule parameter " << index << " of type " << param
447+
<< " is neither " << whole << " nor " << element << '\n';
448+
llvm::report_fatal_error("rule parameter does not match the C++ one");
449+
}
450+
return true;
451+
}
452+
453+
std::string ConverterRefCount::ConvertElementPtr(clang::Expr *arg) {
454+
return ConvertObject(arg, ObjectShape::Element);
455+
}
456+
457+
void ConverterRefCount::CheckRuleReturnType(clang::Expr *expr) {
458+
auto *call = clang::dyn_cast<clang::CallExpr>(expr);
459+
if (!call ||
460+
!RuleRegistry::GetExprRule(ctx_, expr)->return_type.is_refcount_pointer) {
461+
return;
462+
}
463+
// A reference is returned as a pointer. The library may spell the pointer
464+
// or reference type with a typedef of its own, e.g., iterator::pointer,
465+
// which is not what the pointee is spelled with.
466+
auto type = GetReturnTypeOfFunction(call).getDesugaredType(ctx_);
467+
if (auto *ref = type->getAs<clang::ReferenceType>()) {
468+
type = ctx_.getPointerType(ref->getPointeeType());
469+
}
470+
auto ret = NormalizeType(Mapper::GetReturnType(*this, expr));
471+
auto expected = NormalizeType(ToString(type));
472+
if (ret != expected) {
473+
llvm::errs() << "rule return type " << ret << " is not " << expected
474+
<< '\n';
475+
llvm::report_fatal_error("rule return type does not match the C++ one");
476+
}
477+
}
478+
407479
std::string ConverterRefCount::ConvertFreshObject(clang::Expr *expr) {
408480
auto str = ConvertObject(expr);
409481
if (isFresh()) {
@@ -1020,14 +1092,6 @@ void ConverterRefCount::ConvertDeclRefValue(clang::Expr *expr,
10201092
}
10211093

10221094
if (auto *ref = decl_t->getAs<clang::ReferenceType>()) {
1023-
if (auto pointee = ref->getPointeeType();
1024-
isObject() && WantsElementPtr() && IsBoxedType(ctx_, pointee)) {
1025-
StrCat(std::format("Ptr::<{}>::decay(&({}))", ToString(pointee),
1026-
std::move(str)));
1027-
computed_expr_type_ = ComputedExprType::FreshPointer;
1028-
return;
1029-
}
1030-
10311095
// references are not boxed
10321096
if (isAddrOf()) {
10331097
StrCat(str);
@@ -1351,14 +1415,6 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
13511415
return false;
13521416
}
13531417

1354-
if (isObject() && WantsElementPtr() && ref &&
1355-
IsBoxedType(ctx_, ref->getPointeeType())) {
1356-
StrCat(std::format("Ptr::<{}>::decay(&({}))",
1357-
ToString(ref->getPointeeType()), std::move(str)));
1358-
computed_expr_type_ = ComputedExprType::FreshPointer;
1359-
return false;
1360-
}
1361-
13621418
if (ctx && !ctx->temporary_bindings.empty()) {
13631419
str = std::format("({{ {} {} }})", ctx->temporary_bindings, str);
13641420
}
@@ -2299,8 +2355,8 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
22992355
if (auto str = GetMappedAsString(expr, expr->getArgs(), expr->getNumArgs());
23002356
!str.empty()) {
23012357
if (isAddrOf()) {
2302-
StrCat(std::format("Rc::new(RefCell::new({})).as_pointer()",
2303-
std::move(str)));
2358+
StrCat(std::format("Rc::new(RefCell::new({})){}", std::move(str),
2359+
AsPointerMethod(expr->getType())));
23042360
computed_expr_type_ = ComputedExprType::FreshPointer;
23052361
} else {
23062362
StrCat(str);
@@ -2841,10 +2897,6 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
28412897
ConvertSubscriptIndex(expr->getArg(1))));
28422898
}
28432899

2844-
if (is_inner_boxed && isObject() && WantsElementPtr()) {
2845-
StrCat(".decay()");
2846-
}
2847-
28482900
if (isAddrOf()) {
28492901
computed_expr_type_ = ComputedExprType::FreshPointer;
28502902
} else {
@@ -3123,16 +3175,6 @@ pub fn main() {{
31233175

31243176
void ConverterRefCount::ConvertAddrOf(clang::Expr *expr,
31253177
clang::QualType pointer_type) {
3126-
auto pointee_type = pointer_type->getPointeeType();
3127-
if (isObject() && WantsElementPtr() &&
3128-
(IsBoxedType(ctx_, pointee_type) || pointee_type->isArrayType())) {
3129-
auto ptr = ConvertPointer(expr);
3130-
PushConversionKind push(*this, ConversionKind::Pointee);
3131-
StrCat(std::format("Ptr::<{}>::decay(&({}))", ToString(pointee_type),
3132-
std::move(ptr)));
3133-
computed_expr_type_ = ComputedExprType::FreshPointer;
3134-
return;
3135-
}
31363178
StrCat(ConvertPointer(expr));
31373179
}
31383180

@@ -3171,15 +3213,6 @@ void ConverterRefCount::ConvertDeref(clang::Expr *expr) {
31713213
if (isAddrOf() && pointee_type->isArrayType()) {
31723214
str = std::format("({}).as_pointer()", std::move(str));
31733215
}
3174-
3175-
if (isObject() && WantsElementPtr() &&
3176-
(IsBoxedType(ctx_, pointee_type) || pointee_type->isArrayType())) {
3177-
PushConversionKind push(*this, ConversionKind::Pointee);
3178-
StrCat(std::format("Ptr::<{}>::decay(&({}))", ToString(pointee_type),
3179-
std::move(str)));
3180-
computed_expr_type_ = ComputedExprType::FreshPointer;
3181-
return;
3182-
}
31833216
StrCat(std::move(str));
31843217
}
31853218

@@ -3402,8 +3435,8 @@ void ConverterRefCount::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
34023435
if (IsTemporaryObject(base) && base->getType()->isRecordType() &&
34033436
!IsReferenceType(base->IgnoreImplicit())) {
34043437
PushConversionKind push(*this, ConversionKind::FullRefCount);
3405-
ufcs_receiver_ = token::kRef + BoxValue(ConvertRValue(base)) +
3406-
AsPointerMethod(base->getType());
3438+
ufcs_receiver_ =
3439+
token::kRef + BoxValue(ConvertRValue(base)) + ".as_pointer()";
34073440
return;
34083441
}
34093442
ufcs_receiver_ = token::kRef + (base_is_pointer ? ConvertRValue(base)

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,13 +398,18 @@ class ConverterRefCount final : public Converter {
398398
std::string ConvertPointerReceiver(clang::Expr *arg,
399399
std::string_view param_type) override;
400400

401-
std::string AsPointerMethod(clang::QualType type) const;
401+
bool ParamIsElementPtr(clang::Expr *expr, clang::Expr *arg,
402+
unsigned index) override;
403+
std::string ConvertElementPtr(clang::Expr *arg) override;
404+
// The method that makes a pointer to an object of the given type, which is
405+
// one to its elements if they are what ConvertObject wants.
406+
std::string AsPointerMethod(clang::QualType type);
407+
void CheckRuleReturnType(clang::Expr *expr) override;
402408

403409
std::string ConvertTemporaryPointer(
404410
clang::Expr *expr,
405411
std::optional<clang::QualType> implicit_convert_to) override;
406412

407-
bool WantsElementPtr() const { return object_shape_ == ObjectShape::Element; }
408413
std::string
409414
ConvertFresh(clang::Expr *expr,
410415
std::optional<clang::QualType> implicit_convert_to = {});
@@ -516,6 +521,8 @@ class ConverterRefCount final : public Converter {
516521
std::string BoxValue(std::string &&str) const;
517522

518523
std::vector<ConversionKind> conversion_kind_;
524+
// The shape that the innermost ConvertObject wants, until a visitor makes a
525+
// pointer of that shape.
519526
ObjectShape object_shape_ = ObjectShape::Whole;
520527

521528
// Set by pointer-related visit methods (ConvertDeref,

‎libcc2rs/src/cstr.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ impl<T: CChar> Ptr<T> {
331331
#[cfg(test)]
332332
mod tests {
333333
use super::*;
334-
use crate::AsPointer;
334+
use crate::{AsPointer, Decay};
335335

336336
// "ab\0cd\0": C string functions must stop at the first NUL.
337337
const EMBEDDED_NUL: [u8; 6] = [b'a', b'b', 0, b'c', b'd', 0];
@@ -357,7 +357,7 @@ mod tests {
357357
#[test]
358358
fn stops_at_first_nul_in_vec() {
359359
let v = std::rc::Rc::new(RefCell::new(EMBEDDED_NUL.to_vec()));
360-
check(&v.as_pointer().decay());
360+
check(&v.decay());
361361
}
362362

363363
#[test]

0 commit comments

Comments
 (0)