Skip to content

Commit bf51851

Browse files
authored
Fix default substitution inside nested containers (#564)
This fixes the default of `unique_ptr<string[]>`. Before this PR the translation did not use the default of string, now it does. I added a new node in Rules IR called default. When cpp2rust encounters this node, it replaces with the default of the substituted type. This also generates more idiomatic code: ```diff - arr: std::array::from_fn::<_, 3_usize, _>(|_| <i32>::default()).to_vec(), + arr: std::array::from_fn::<_, 3_usize, _>(|_| 0_i32).to_vec(), ```
1 parent 31427f9 commit bf51851

59 files changed

Lines changed: 309 additions & 232 deletions

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: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ std::string Converter::GetUnboxedTypeAsString(clang::QualType qual_type) {
122122
return ToString(qual_type);
123123
}
124124

125+
std::string Converter::GetUnboxedDefaultAsString(clang::QualType qual_type) {
126+
return GetDefaultAsString(qual_type);
127+
}
128+
125129
std::string
126130
Converter::GetTemplateArgumentAsString(const clang::TemplateArgument &arg) {
127131
switch (arg.getKind()) {
@@ -5041,15 +5045,16 @@ std::string Converter::ConvertTake(clang::Expr *arg) {
50415045

50425046
std::string Converter::ConvertMappedMethodCall(
50435047
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
5044-
clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) {
5045-
return ConvertIRFragment(mc.receiver, expr, args, num_args, ctx) +
5046-
ConvertIRFragment(mc.body, expr, args, num_args, ctx);
5048+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
5049+
unsigned num_args, TempMaterializationCtx *ctx) {
5050+
return ConvertIRFragment(mc.receiver, bindings, expr, args, num_args, ctx) +
5051+
ConvertIRFragment(mc.body, bindings, expr, args, num_args, ctx);
50475052
}
50485053

50495054
std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
50505055
unsigned num_args,
50515056
TempMaterializationCtx *ctx) {
5052-
auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr);
5057+
auto [tgt_ir, bindings] = RuleRegistry::Search(ctx_, expr);
50535058
if (!tgt_ir)
50545059
return {};
50555060

@@ -5058,8 +5063,8 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
50585063
auto saved_borrows = std::exchange(mut_borrows_, {});
50595064
auto *operand = tgt_ir->refExprOperand();
50605065
bool lvalue = operand && !isAddrOf();
5061-
auto result = ConvertIRFragment(lvalue ? *operand : tgt_ir->body, expr, args,
5062-
num_args, ctx);
5066+
auto result = ConvertIRFragment(lvalue ? *operand : tgt_ir->body, bindings,
5067+
expr, args, num_args, ctx);
50635068
if (tgt_ir->multi_statement) {
50645069
result = '{' + result + '}';
50655070
}
@@ -5073,19 +5078,28 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
50735078

50745079
std::string Converter::ConvertIRFragment(
50755080
const std::vector<TranslationRule::BodyFragment> &fragments,
5076-
clang::Expr *expr, clang::Expr **args, unsigned num_args,
5077-
TempMaterializationCtx *ctx) {
5081+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr *expr,
5082+
clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) {
50785083
using namespace TranslationRule;
50795084

5080-
auto all_args = BuildUnifiedArgs(expr, args, num_args);
5085+
std::vector<clang::Expr *> all_args;
5086+
if (expr) {
5087+
all_args = BuildUnifiedArgs(expr, args, num_args);
5088+
}
50815089

50825090
std::string result;
50835091
for (auto &frag : fragments) {
50845092
if (auto *t = std::get_if<TextFragment>(&frag)) {
50855093
result += t->text;
50865094
} else if (auto *g = std::get_if<GenericFragment>(&frag)) {
5087-
result += Mapper::InstantiateTemplate(*this, expr, g->n);
5095+
result += GetTemplateArgumentAsString(bindings[g->n - 1]);
5096+
} else if (auto *d = std::get_if<DefaultFragment>(&frag)) {
5097+
auto &arg = bindings[d->n - 1];
5098+
assert(arg.getKind() == clang::TemplateArgument::Type &&
5099+
"default of a non-type generic");
5100+
result += GetUnboxedDefaultAsString(arg.getAsType());
50885101
} else if (auto *ph = std::get_if<PlaceholderFragment>(&frag)) {
5102+
assert(expr && "placeholder outside an expression rule");
50895103
auto arg_idx = ph->n;
50905104
assert(arg_idx < all_args.size());
50915105
auto *arg = all_args[arg_idx];
@@ -5110,16 +5124,20 @@ std::string Converter::ConvertIRFragment(
51105124
};
51115125
result += ConvertPlaceholder(expr, arg, ph_ctx);
51125126
} else if (std::get_if<TranslationRule::VaArgsFragment>(&frag)) {
5127+
assert(expr && "variadic arguments outside an expression rule");
51135128
result += ConvertVariadicTail(expr, all_args);
51145129
} else if (std::get_if<TranslationRule::InitFragment>(&frag)) {
5130+
assert(expr && "init outside an expression rule");
51155131
result += ConvertInitFragment(expr, all_args);
51165132
} else if (auto *mc =
51175133
std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
5118-
result += ConvertMappedMethodCall(expr, **mc, args, num_args, ctx);
5134+
result +=
5135+
ConvertMappedMethodCall(expr, **mc, bindings, args, num_args, ctx);
51195136
} else if (auto *re =
51205137
std::get_if<std::unique_ptr<RefExprFragment>>(&frag)) {
5121-
result += (*re)->prefix + ' ' +
5122-
ConvertIRFragment((*re)->inner, expr, args, num_args, ctx);
5138+
result +=
5139+
(*re)->prefix + ' ' +
5140+
ConvertIRFragment((*re)->inner, bindings, expr, args, num_args, ctx);
51235141
}
51245142
}
51255143

‎cpp2rust/converter/converter.h‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
9292

9393
virtual std::string GetUnboxedTypeAsString(clang::QualType qual_type);
9494

95+
virtual std::string GetUnboxedDefaultAsString(clang::QualType qual_type);
96+
9597
std::string GetTemplateArgumentAsString(const clang::TemplateArgument &arg);
9698

9799
bool VisitRecoveryExpr(clang::RecoveryExpr *expr);
@@ -296,6 +298,12 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
296298
std::vector<std::string> materialized_refs_;
297299
};
298300

301+
std::string
302+
ConvertIRFragment(const std::vector<TranslationRule::BodyFragment> &fragments,
303+
llvm::ArrayRef<clang::TemplateArgument> bindings,
304+
clang::Expr *expr, clang::Expr **args, unsigned num_args,
305+
TempMaterializationCtx *ctx);
306+
299307
struct PlaceholderCtx {
300308
unsigned arg_idx;
301309
std::optional<clang::QualType> implicit_convert_to;
@@ -834,11 +842,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
834842
unsigned num_args = 0,
835843
TempMaterializationCtx *ctx = nullptr);
836844

837-
std::string
838-
ConvertIRFragment(const std::vector<TranslationRule::BodyFragment> &fragments,
839-
clang::Expr *expr, clang::Expr **args, unsigned num_args,
840-
TempMaterializationCtx *ctx);
841-
842845
std::string ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
843846
const PlaceholderCtx &ph_ctx);
844847

@@ -868,7 +871,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
868871

869872
virtual std::string ConvertMappedMethodCall(
870873
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
871-
clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx);
874+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
875+
unsigned num_args, TempMaterializationCtx *ctx);
872876

873877
virtual std::string AccessLValueObject(clang::MemberExpr *member);
874878

‎cpp2rust/converter/mapper.cpp‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,6 @@ std::string MapFunctionName(clang::ASTContext &ctx,
4444
return GetNamedDeclAsString(decl->getCanonicalDecl());
4545
}
4646

47-
std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr,
48-
unsigned n) {
49-
auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr);
50-
if (!rule) {
51-
return std::format("T{}", n);
52-
}
53-
return Matcher::MapBinding(converter, subs, n - 1);
54-
}
55-
5647
std::string Map(clang::ASTContext &ctx, clang::QualType qual_type) {
5748
auto rule = RuleRegistry::Search(ctx, qual_type).first;
5849
return rule ? rule->type_info.type : std::string();
@@ -71,7 +62,8 @@ std::string MapInitializer(Converter &converter, clang::QualType qual_type) {
7162
auto [rule, subs] =
7263
RuleRegistry::Search(converter.GetASTContext(), qual_type);
7364
if (rule && !rule->initializer.empty()) {
74-
return Matcher::InstantiateTgt(converter, subs, rule->initializer);
65+
return converter.ConvertIRFragment(rule->initializer, subs, nullptr,
66+
nullptr, 0, nullptr);
7567
}
7668
return {};
7769
}

‎cpp2rust/converter/mapper.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ std::string Map(Converter &converter, clang::QualType qual_type);
2525
std::string MapInitializer(Converter &converter, clang::QualType qual_type);
2626
std::string MapFunctionName(clang::ASTContext &ctx,
2727
const clang::FunctionDecl *decl);
28-
std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr,
29-
unsigned n);
3028
std::string GetParamType(Converter &converter, const clang::Expr *expr,
3129
unsigned index);
3230
std::string GetReturnType(Converter &converter, const clang::Expr *expr);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,12 @@ ConverterRefCount::GetUnboxedTypeAsString(clang::QualType qual_type) {
239239
return ToString(qual_type);
240240
}
241241

242+
std::string
243+
ConverterRefCount::GetUnboxedDefaultAsString(clang::QualType qual_type) {
244+
PushConversionKind push(*this, ConversionKind::Unboxed);
245+
return GetDefaultAsString(qual_type);
246+
}
247+
242248
bool ConverterRefCount::Convert(clang::QualType qual_type) {
243249
// Catch va_list before desugaring
244250
if (IsVaListType(qual_type)) {
@@ -2971,11 +2977,13 @@ std::string ConverterRefCount::ConvertTake(clang::Expr *arg) {
29712977

29722978
std::string ConverterRefCount::ConvertMappedMethodCall(
29732979
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
2974-
clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) {
2980+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
2981+
unsigned num_args, TempMaterializationCtx *ctx) {
29752982
auto receiver_ph = mc.getReceiverPlaceholder();
29762983
if (!receiver_ph || receiver_ph->access == TranslationRule::Access::kBorrow ||
29772984
receiver_ph->access == TranslationRule::Access::kMove) {
2978-
return Converter::ConvertMappedMethodCall(expr, mc, args, num_args, ctx);
2985+
return Converter::ConvertMappedMethodCall(expr, mc, bindings, args,
2986+
num_args, ctx);
29792987
}
29802988

29812989
auto arg_idx = receiver_ph->n;
@@ -2988,24 +2996,26 @@ std::string ConverterRefCount::ConvertMappedMethodCall(
29882996
auto param_type = Mapper::GetParamType(*this, expr, arg_idx);
29892997

29902998
if (arg->getType()->isPointerType()) {
2991-
return std::format("{}.with_mut(|__v: {}| __v{})", ConvertPointer(arg),
2992-
param_type,
2993-
ConvertIRFragment(mc.body, expr, args, num_args, ctx));
2999+
return std::format(
3000+
"{}.with_mut(|__v: {}| __v{})", ConvertPointer(arg), param_type,
3001+
ConvertIRFragment(mc.body, bindings, expr, args, num_args, ctx));
29943002
}
29953003

29963004
// A receiver reached through a pointer is modified through it, only for
29973005
// the call, after the other arguments are computed.
29983006
std::string receiver;
29993007
{
30003008
ScopedValue in_receiver(in_receiver_, true);
3001-
receiver = ConvertIRFragment(mc.receiver, expr, args, num_args, ctx);
3009+
receiver =
3010+
ConvertIRFragment(mc.receiver, bindings, expr, args, num_args, ctx);
30023011
}
30033012
if (pending_deref_.empty()) {
3004-
return receiver + ConvertIRFragment(mc.body, expr, args, num_args, ctx);
3013+
return receiver +
3014+
ConvertIRFragment(mc.body, bindings, expr, args, num_args, ctx);
30053015
}
30063016

30073017
auto ptr = pending_deref_.take();
3008-
auto body = ConvertIRFragment(mc.body, expr, args, num_args, ctx);
3018+
auto body = ConvertIRFragment(mc.body, bindings, expr, args, num_args, ctx);
30093019
MarkFresh();
30103020

30113021
return std::format("{}.with_mut(|__v: {}| __v{})", ptr, param_type, body);

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class ConverterRefCount final : public Converter {
1919

2020
std::string GetUnboxedTypeAsString(clang::QualType qual_type) override;
2121

22+
std::string GetUnboxedDefaultAsString(clang::QualType qual_type) override;
23+
2224
static void EmitMethodsOnPtr(std::string &out);
2325

2426
bool VisitRecordType(clang::RecordType *type) override;
@@ -319,11 +321,10 @@ class ConverterRefCount final : public Converter {
319321
// converted.
320322
bool in_receiver_ = false;
321323

322-
std::string
323-
ConvertMappedMethodCall(clang::Expr *expr,
324-
const TranslationRule::MethodCallFragment &mc,
325-
clang::Expr **args, unsigned num_args,
326-
TempMaterializationCtx *ctx) override;
324+
std::string ConvertMappedMethodCall(
325+
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
326+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
327+
unsigned num_args, TempMaterializationCtx *ctx) override;
327328

328329
private:
329330
void SetUFCSReceiver(clang::Expr *base, bool is_arrow,

‎cpp2rust/converter/rules/registry.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ void collectGenerics(const std::vector<TranslationRule::BodyFragment> &body,
9393
std::get_if<TranslationRule::GenericFragment>(&fragment)) {
9494
used.insert(generic->n);
9595
}
96+
if (auto generic =
97+
std::get_if<TranslationRule::DefaultFragment>(&fragment)) {
98+
used.insert(generic->n);
99+
}
96100
return false;
97101
});
98102
}

‎cpp2rust/converter/translation_rule.cpp‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ std::vector<BodyFragment> ParseBodyFragmentsJSON(const llvm::json::Array &arr) {
9191
result.push_back(TextFragment{str->str()});
9292
} else if (auto n = frag_obj->getInteger("generic")) {
9393
result.push_back(GenericFragment{(unsigned)*n});
94+
} else if (auto n = frag_obj->getInteger("default")) {
95+
result.push_back(DefaultFragment{(unsigned)*n});
9496
} else if (auto *ph = frag_obj->getObject("placeholder")) {
9597
result.push_back(ParsePlaceholderFragmentJSON(*ph));
9698
} else if (auto *mc = frag_obj->getObject("method_call")) {
@@ -232,6 +234,8 @@ void RefExprFragment::dump() const {
232234

233235
void GenericFragment::dump() const { log() << " generic: " << n << '\n'; }
234236

237+
void DefaultFragment::dump() const { log() << " default: " << n << '\n'; }
238+
235239
void TypeInfo::dump() const {
236240
log() << type;
237241
if (is_refcount_pointer)
@@ -247,7 +251,10 @@ void TypeRule::dump() const {
247251
type_info.dump();
248252
log() << '\n';
249253
if (!initializer.empty()) {
250-
log() << " init: " << initializer << '\n';
254+
log() << " init:\n";
255+
for (auto &frag : initializer) {
256+
BodyFragmentDump(frag);
257+
}
251258
}
252259
}
253260

@@ -300,8 +307,8 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) {
300307

301308
TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) {
302309
TypeRule rule;
303-
if (auto init = obj.getString("init"))
304-
rule.initializer = init->str();
310+
if (auto *init = obj.getArray("init"))
311+
rule.initializer = ParseBodyFragmentsJSON(*init);
305312
if (auto generic = obj.getInteger("generic"))
306313
rule.generic = *generic;
307314
rule.type_info = ParseTypeInfoJSON(obj);

‎cpp2rust/converter/translation_rule.h‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ struct GenericFragment {
4141
void dump() const;
4242
};
4343

44+
struct DefaultFragment {
45+
unsigned n;
46+
47+
void dump() const;
48+
};
49+
4450
struct VaArgsFragment {
4551
void dump() const;
4652
};
@@ -52,10 +58,11 @@ struct InitFragment {
5258
struct MethodCallFragment; // forward declaration
5359
struct RefExprFragment;
5460

55-
using BodyFragment = std::variant<TextFragment, PlaceholderFragment,
56-
GenericFragment, VaArgsFragment, InitFragment,
57-
std::unique_ptr<MethodCallFragment>,
58-
std::unique_ptr<RefExprFragment>>;
61+
using BodyFragment =
62+
std::variant<TextFragment, PlaceholderFragment, GenericFragment,
63+
DefaultFragment, VaArgsFragment, InitFragment,
64+
std::unique_ptr<MethodCallFragment>,
65+
std::unique_ptr<RefExprFragment>>;
5966

6067
struct MethodCallFragment {
6168
std::vector<BodyFragment> receiver;
@@ -101,7 +108,7 @@ struct ExprRule {
101108
};
102109

103110
struct TypeRule {
104-
std::string initializer; // Rust initializer expression
111+
std::vector<BodyFragment> initializer; // Rust initializer expression
105112
unsigned generic = 0;
106113
TypeInfo type_info;
107114

‎rule-preprocessor/src/ir.rs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl FnIr {
142142

143143
#[derive(Debug, Clone, Serialize, Deserialize)]
144144
pub struct TypeIr {
145-
pub init: String,
145+
pub init: Vec<BodyFragment>,
146146
#[serde(skip_serializing_if = "Option::is_none")]
147147
pub generic: Option<i32>,
148148
#[serde(flatten)]
@@ -161,6 +161,9 @@ pub enum BodyFragment {
161161
Generic {
162162
generic: i32,
163163
},
164+
Default {
165+
default: i32,
166+
},
164167
MethodCall {
165168
method_call: MethodCallInner,
166169
},

0 commit comments

Comments
 (0)