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
2 changes: 1 addition & 1 deletion cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5279,7 +5279,7 @@ Converter::ConvertInitFragment(clang::Expr *expr,
if (!tgt_ir || !tgt_ir->usesInit()) {
llvm::report_fatal_error("expression rule does not use Init");
}
auto type = RuleRegistry::GetInitType(expr);
auto type = RuleRegistry::GetInitType(ctx_, expr);

Buffer buf(*this);
PushExprKind push(*this, ExprKind::RValue);
Expand Down
52 changes: 52 additions & 0 deletions cpp2rust/converter/rules/expr_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,57 @@ void deduceMemberTypesFromOperands(
}
}

llvm::ArrayRef<clang::TemplateArgumentLoc>
getExplicitTemplateArgs(const clang::Expr *expr) {
auto call = llvm::dyn_cast<clang::CallExpr>(RuleDecls::SkipImplicit(expr));
if (!call) {
return {};
}
auto callee = call->getCallee()->IgnoreParenImpCasts();
if (auto ref = llvm::dyn_cast<clang::DeclRefExpr>(callee)) {
return ref->template_arguments();
}
if (auto member = llvm::dyn_cast<clang::MemberExpr>(callee)) {
return member->template_arguments();
}
if (auto overload = llvm::dyn_cast<clang::OverloadExpr>(callee)) {
return overload->template_arguments();
}
if (auto member =
llvm::dyn_cast<clang::CXXDependentScopeMemberExpr>(callee)) {
return member->template_arguments();
}
return {};
}

void deduceFromExplicitArgs(clang::Sema &sema,
clang::FunctionTemplateDecl *rule,
const clang::Expr *use,
std::vector<clang::TemplateArgument> &deduced) {
auto rule_args =
getExplicitTemplateArgs(RuleDecls::GetReturned(rule->getTemplatedDecl()));
auto use_args = getExplicitTemplateArgs(use);
for (unsigned i = 0; i < rule_args.size() && i < use_args.size(); ++i) {
const auto &rule_arg = rule_args[i].getArgument();
const auto &use_arg = use_args[i].getArgument();
if (rule_arg.getKind() != clang::TemplateArgument::Type ||
use_arg.getKind() != clang::TemplateArgument::Type) {
continue;
}
if (rule_arg.getAsType()->getAs<clang::PackExpansionType>()) {
break;
}
auto args = RuleDecls::DeduceFromType(
sema, rule->getTemplateParameters(), rule_arg.getAsType(),
use_arg.getAsType().getCanonicalType());
for (unsigned j = 0; args && j < deduced.size(); ++j) {
if (deduced[j].isNull()) {
deduced[j] = (*args)[j];
}
}
}
}

std::optional<std::vector<clang::TemplateArgument>>
deduceFromCallee(clang::Sema &sema, clang::FunctionTemplateDecl *rule,
const clang::Expr *use) {
Expand Down Expand Up @@ -343,6 +394,7 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema,
}
}
deduceMemberTypesFromOperands(sema, rule, use, deduced);
deduceFromExplicitArgs(sema, rule, use, deduced);
auto last = static_cast<unsigned>(deducible.find_last_unset());
for (unsigned i = 0; i < params->size() && i <= last; ++i) {
auto param = params->getParam(i);
Expand Down
91 changes: 61 additions & 30 deletions cpp2rust/converter/rules/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,13 @@ bool pointsToUserType(clang::QualType type) {
return tag && IsUserDefinedDecl(tag);
}

constexpr llvm::StringLiteral kInitAliasPrefix = "cpp2rust_init_";

bool isRuleName(llvm::StringRef name) {
return name.size() > 1 && (name[0] == 'f' || name[0] == 't') &&
llvm::all_of(name.drop_front(), llvm::isDigit);
}

// The T of a rule's trailing `Init<T, Args> &&...args` pack.
clang::QualType getInitPattern(const clang::FunctionDecl *rule) {
if (rule->param_empty()) {
return {};
}
auto expansion =
rule->parameters().back()->getType()->getAs<clang::PackExpansionType>();
if (!expansion) {
return {};
}
auto alias = expansion->getPattern()
.getNonReferenceType()
->getAs<clang::TemplateSpecializationType>();
if (!alias || !alias->isTypeAlias() ||
alias->getTemplateName().getAsTemplateDecl()->getName() != "Init") {
return {};
}
return alias->template_arguments()[0].getAsType();
}

void collectGenerics(const std::vector<TranslationRule::BodyFragment> &body,
std::unordered_set<unsigned> &used) {
TranslationRule::AnyFragment(body, [&](const TranslationRule::BodyFragment
Expand Down Expand Up @@ -158,6 +140,7 @@ void checkGenerics(const std::string &dir, const std::string &name,
for (const auto &param : rule.params) {
collectGenerics(param.type, used);
}
collectGenerics(rule.init.type, used);
for (unsigned i : used) {
if (!declared.contains(std::format("T{}", i)) &&
!isBoundArgument(rule, i)) {
Expand All @@ -166,10 +149,14 @@ void checkGenerics(const std::string &dir, const std::string &name,
llvm::report_fatal_error("Absent generic from src");
}
}
if (rule.usesInit() && getInitPattern(decl).isNull()) {
if (rule.usesInit() &&
(decl->param_empty() || !decl->parameters()
.back()
->getType()
->getAs<clang::PackExpansionType>())) {
llvm::errs() << dir << "::" << name << '\n';
llvm::report_fatal_error("Expr rule uses init but its src pack is not "
"declared as Init<T, Args>");
llvm::report_fatal_error(
"Expr rule uses init but its src does not end in a pack");
}
}

Expand Down Expand Up @@ -219,6 +206,10 @@ void addCxxRules(clang::ASTContext &ctx, const clang::NamespaceDecl *rules) {
}
for (auto decl : ns->decls()) {
auto named = llvm::cast<clang::NamedDecl>(decl);
if (llvm::isa<clang::TypeAliasTemplateDecl>(named) &&
named->getName().starts_with(kInitAliasPrefix)) {
continue;
}
if (!isRuleName(named->getName())) {
llvm::report_fatal_error("declaration is not a rule");
}
Expand Down Expand Up @@ -451,18 +442,58 @@ clang::QualType GetConversionSourceType(clang::ASTContext &ctx,
llvm::report_fatal_error("converted generic has no CppFrom bound");
}

clang::QualType GetInitType(const clang::Expr *expr) {
clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) {
auto match = findExpr(expr);
assert(match.rule_decl && "expression must have a translation rule");
const auto &rule = match.rule_decl->rule;
if (rule.init.cpp_type.empty()) {
llvm::StringRef type = rule.init.type;
unsigned n = 0;
[[maybe_unused]] bool bad =
!type.consume_front("T") || type.getAsInteger(10, n);
assert(!bad && "init type must be a bare generic or a cpp_type!");
return GetGenericType(ctx, expr, n);
}
auto function = match.rule_decl->decl->getAsFunction();
auto pattern = getInitPattern(function);
assert(!pattern.isNull() && "rule has no Init<T, Args> pack");
clang::MultiLevelTemplateArgumentList args(
function->getDescribedFunctionTemplate(), match.args, /*Final=*/false);
auto tmpl = function->getDescribedFunctionTemplate();
assert(tmpl && "cpp_type! init in a non-template rule");
clang::TypeAliasTemplateDecl *alias = nullptr;
for (auto decl : function->getDeclContext()->lookup(
&ctx.Idents.get(InitAliasName(model_, function->getName().str())))) {
if ((alias = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl))) {
break;
}
}
assert(alias && "cpp_type! init has no alias next to its rule");
clang::MultiLevelTemplateArgumentList args(tmpl, match.args,
/*Final=*/false);
clang::Sema::InstantiatingTemplate inst(
*sema_, expr->getBeginLoc(), const_cast<clang::FunctionDecl *>(function));
return sema_->SubstType(pattern, args, expr->getBeginLoc(),
clang::DeclarationName());
auto type =
sema_->SubstType(alias->getTemplatedDecl()->getUnderlyingType(), args,
expr->getBeginLoc(), clang::DeclarationName());
assert(!type.isNull() && "cpp_type! init does not substitute");
return type;
}

std::string InitCppType(Model model, const std::string &dir,
const std::string &name) {
auto rule = findRule(model, dir, name);
if (!rule) {
rule = findRule(Model::kUnsafe, dir, name);
}
if (!rule) {
return {};
}
auto params = rule->getObject("params");
auto init = params ? params->getObject("init") : nullptr;
auto cpp_type = init ? init->getString("cpp_type") : std::nullopt;
return cpp_type ? cpp_type->str() : std::string();
}

std::string InitAliasName(Model model, const std::string &name) {
return std::format("{}{}_{}", kInitAliasPrefix.str(),
model == Model::kRefCount ? "refcount" : "unsafe", name);
}

clang::QualType GetDeclaredReturnType(const clang::Expr *expr) {
Expand Down
7 changes: 6 additions & 1 deletion cpp2rust/converter/rules/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ Matcher::Match<TranslationRule::TypeRule> Search(clang::ASTContext &ctx,

const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx,
const clang::Expr *expr);
clang::QualType GetInitType(const clang::Expr *expr);
clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr);

std::string InitCppType(Model model, const std::string &dir,
const std::string &name);

std::string InitAliasName(Model model, const std::string &name);

clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr,
unsigned n);
Expand Down
11 changes: 11 additions & 0 deletions cpp2rust/converter/rules/rules_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "converter/converter_lib.h"
#include "converter/rules/decls.h"
#include "converter/rules/registry.h"
#include "logging.h"

namespace cpp2rust::RulesLoader {
Expand Down Expand Up @@ -314,6 +315,16 @@ const llvm::json::Object &CxxIndex() {
std::string WrapRule(const llvm::json::Object &rule, bool is_cxx) {
auto dir = rule.getString("dir")->str();
auto text = rule.getString("text")->str();
if (auto header = rule.getString("template_header")) {
auto name = rule.getString("name")->str();
for (auto model : {Model::kUnsafe, Model::kRefCount}) {
auto cpp_type = RuleRegistry::InitCppType(model, dir, name);
if (!cpp_type.empty()) {
text += std::format("{} using {} = {};\n", header->str(),
RuleRegistry::InitAliasName(model, name), cpp_type);
}
}
}
if (!is_cxx) {
return std::format("#define {0} cpp2rust_rules_{1}_{0}\n{2}#undef {0}\n",
rule.getString("name")->str(), dir, text);
Expand Down
6 changes: 6 additions & 0 deletions cpp2rust/converter/translation_rule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) {
TypeInfo info;
if (auto ty = obj.getString("type"))
info.type = ty->str();
if (auto cpp_type = obj.getString("cpp_type"))
info.cpp_type = cpp_type->str();
if (auto v = obj.getBoolean("is_refcount_pointer"))
info.is_refcount_pointer = *v;
if (auto v = obj.getBoolean("is_unsafe_pointer"))
Expand Down Expand Up @@ -296,6 +298,10 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) {
if (auto *params = obj.getObject("params")) {
for (auto &[key, val] : *params) {
if (auto *param_obj = val.getAsObject()) {
if (key == "init") {
ir.init = ParseTypeInfoJSON(*param_obj);
continue;
}
size_t id = atoi(llvm::StringRef(key).data() + 1);
ir.params.resize(std::max(ir.params.size(), id + 1));
ir.params[id] = ParseTypeInfoJSON(*param_obj);
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/translation_rule.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ bool AnyFragment(const std::vector<BodyFragment> &body,
struct TypeInfo {
std::vector<std::string> derives;
std::string type;
std::string cpp_type;
bool is_refcount_pointer = false;
bool is_unsafe_pointer = false;

Expand All @@ -102,6 +103,7 @@ struct TypeInfo {

struct ExprRule {
std::vector<TypeInfo> params;
TypeInfo init;
TypeInfo return_type;
std::vector<std::vector<Bound>> generics; // "T1" -> [Ord, Clone]
std::vector<BodyFragment> body;
Expand Down
17 changes: 16 additions & 1 deletion cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,14 @@ void AddRequiredClasses(clang::QualType type,
if (auto name = type->getAs<clang::DependentNameType>()) {
auto qualifier = name->getQualifier();
if (qualifier.getKind() == clang::NestedNameSpecifier::Kind::Type) {
AddRequiredClasses(clang::QualType(qualifier.getAsType(), 0), required);
if (auto spec = qualifier.getAsType()
->getAs<clang::TemplateSpecializationType>()) {
for (const auto &arg : spec->template_arguments()) {
if (arg.getKind() == clang::TemplateArgument::Type) {
AddRequiredClasses(arg.getAsType(), required);
}
}
}
}
return;
}
Expand Down Expand Up @@ -286,6 +293,14 @@ void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name,
auto range = clang::tooling::getExtendedRange(*decl, clang::tok::semi, ctx);
auto text = clang::tooling::getText(range, ctx).str() + '\n';
llvm::json::Object rule{{"dir", dir_name}, {"name", name}, {"text", text}};
if (auto tmpl = llvm::dyn_cast<clang::FunctionTemplateDecl>(decl)) {
rule["template_header"] =
clang::tooling::getText(
clang::CharSourceRange::getTokenRange(
tmpl->getTemplateParameters()->getSourceRange()),
ctx)
.str();
}
if (any_class) {
rule["requires"] = llvm::json::Array{RulesLoader::kAnyIteratorKey};
} else if (auto required = RequiredClasses(decl, alias);
Expand Down
7 changes: 7 additions & 0 deletions libcc2rs/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ impl<T: Clone> CppFromUnsafe<T> for T {
unsafe { (*src).clone() }
}
}

#[macro_export]
macro_rules! cpp_type {
($cpp:literal, $rust:ty) => {
$rust
};
}
1 change: 1 addition & 0 deletions libcc2rs/src/reinterpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ impl<T: 'static> ByteRepr for *const T {}
impl<T: 'static> ByteRepr for *mut T {}
impl<A: ByteRepr, B: ByteRepr> ByteRepr for (A, B) {}
impl<K: 'static, V: 'static> ByteRepr for std::collections::BTreeMap<K, V> {}
impl ByteRepr for std::rc::Rc<dyn std::any::Any> {}

// Runs `f` with a zeroed scratch buffer of `len` elements. Small buffers live
// on the stack so that accessing memory through a reinterpreted pointer does
Expand Down
9 changes: 8 additions & 1 deletion rule-preprocessor/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub struct TypeInfo {
pub is_unsafe_pointer: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub derives: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cpp_type: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -121,7 +123,12 @@ impl FnIr {

pub fn validate(&self, name: &str) {
validate_consecutive_keys(
self.params.as_ref().map(|p| p.keys()).into_iter().flatten(),
self.params
.as_ref()
.map(|p| p.keys())
.into_iter()
.flatten()
.filter(|key| *key != "init"),
'a',
0,
&format!("Rule {name} params"),
Expand Down
Loading
Loading