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: 3 additions & 1 deletion cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5163,7 +5163,9 @@ std::string Converter::ConvertIRFragment(
.is_cpp_ptr = is_cpp_ptr,
// Whatever its spelling, a pointer in C++ is converted as one.
.maps_to_rust_ptr =
!is_cpp_ptr && RuleRegistry::MapsToPointer(ctx_, arg->getType()),
!is_cpp_ptr &&
RuleRegistry::MapsToPointer(
ctx_, arg->IgnoreParenNoopCasts(ctx_)->getType()),
.declared_in_rule_as_rust_ptr =
RuleRegistry::ParamIsPointer(ctx_, expr, arg_idx),
.is_index_base = ph->is_index_base,
Expand Down
13 changes: 7 additions & 6 deletions cpp2rust/converter/models/converter_refcount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -444,12 +444,13 @@ void ConverterRefCount::CheckRuleReturnType(clang::Expr *expr) {
!RuleRegistry::GetExprRule(ctx_, expr)->return_type.is_refcount_pointer) {
return;
}
// A reference is returned as a pointer. The library may spell the pointer
// or reference type with a typedef of its own, e.g., iterator::pointer,
// which is not what the pointee is spelled with.
auto type = GetReturnTypeOfFunction(call).getDesugaredType(ctx_);
if (auto *ref = type->getAs<clang::ReferenceType>()) {
type = ctx_.getPointerType(ref->getPointeeType());
auto type = RuleRegistry::GetDeclaredReturnType(expr);
if (auto ref = type->getAs<clang::ReferenceType>()) {
auto pointee = ref->getPointeeType();
type = pointee->isRecordType() &&
RuleRegistry::MapsToRefcountPointer(ctx_, pointee)
? pointee
: ctx_.getPointerType(pointee);
}
auto ret = NormalizeType(Mapper::GetReturnType(*this, expr));
auto expected = NormalizeType(ToString(type));
Expand Down
110 changes: 110 additions & 0 deletions cpp2rust/converter/rules/decls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <clang/AST/ExprCXX.h>
#include <clang/Sema/Template.h>
#include <clang/Sema/TemplateDeduction.h>
#include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/SmallVector.h>

#include <cassert>
Expand Down Expand Up @@ -105,6 +106,115 @@ DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params,
return std::vector<clang::TemplateArgument>(deduced.begin(), deduced.end());
}

namespace {

const clang::TemplateSpecializationType *
getMemberParentClass(clang::QualType pattern, clang::ClassTemplateDecl *&tmpl) {
auto member = pattern->getAs<clang::DependentNameType>();
if (!member || member->getQualifier().getKind() !=
clang::NestedNameSpecifier::Kind::Type) {
return nullptr;
}
auto spec = member->getQualifier()
.getAsType()
->getAs<clang::TemplateSpecializationType>();
tmpl = spec ? llvm::dyn_cast_or_null<clang::ClassTemplateDecl>(
spec->getTemplateName().getAsTemplateDecl())
: nullptr;
return tmpl && !tmpl->getTemplateParameters()->hasParameterPack() ? spec
: nullptr;
}

std::optional<std::vector<clang::TemplateArgument>>
getDefaultedArgumentsOfRule(clang::Sema &sema, const clang::TemplateDecl *rule,
clang::ClassTemplateDecl *tmpl,
const clang::TemplateSpecializationType *spec) {
auto params = rule->getTemplateParameters();
auto loc = params->getTemplateLoc();
clang::TemplateArgumentListInfo written(loc, loc);
llvm::SmallVector<std::optional<unsigned>, 4> positions(params->size());
for (auto [i, arg] : llvm::enumerate(spec->template_arguments())) {
auto parm = arg.getKind() == clang::TemplateArgument::Type
? arg.getAsType()->getAs<clang::TemplateTypeParmType>()
: nullptr;
if (parm && parm->getDepth() == params->getDepth() &&
!parm->isParameterPack()) {
if (positions[parm->getIndex()]) {
return std::nullopt;
}
positions[parm->getIndex()] = i;
continue;
}
if (arg.isDependent() || written.size() != i) {
return std::nullopt;
}
written.addArgument(sema.getTrivialTemplateArgumentLoc(
arg, arg.getNonTypeTemplateArgumentType(), loc));
}
if (!llvm::all_of(positions, [](const std::optional<unsigned> &position) {
return position.has_value();
})) {
return std::nullopt;
}
Trial trial(sema);
clang::Sema::CheckTemplateArgumentInfo info;
if (sema.CheckTemplateArgumentList(tmpl, loc, written, /*DefaultArgs=*/{},
/*PartialTemplateArgs=*/false, info) ||
trial.failed()) {
return std::nullopt;
}
std::vector<clang::TemplateArgument> args(params->size());
for (unsigned i = 0; i < params->size(); ++i) {
args[i] = info.CanonicalConverted[*positions[i]];
}
return args;
}

bool resolvesTo(clang::Sema &sema, const clang::TemplateDecl *rule,
llvm::ArrayRef<clang::TemplateArgument> args,
clang::QualType pattern, clang::QualType actual) {
Trial trial(sema);
clang::MultiLevelTemplateArgumentList bound(
const_cast<clang::TemplateDecl *>(rule), args, /*Final=*/true);
auto resolved = sema.SubstType(
pattern, bound, rule->getTemplateParameters()->getTemplateLoc(),
clang::DeclarationName());
return !resolved.isNull() && !trial.failed() &&
sema.Context.hasSameUnqualifiedType(resolved, actual);
}

} // namespace

std::optional<std::vector<clang::TemplateArgument>>
DeduceMemberType(clang::Sema &sema, const clang::TemplateDecl *rule,
clang::QualType pattern, clang::QualType use) {
auto &ctx = sema.Context;
clang::ClassTemplateDecl *tmpl = nullptr;
auto spec = getMemberParentClass(pattern, tmpl);
if (!spec) {
return std::nullopt;
}
auto member = pattern->getAs<clang::DependentNameType>();
auto params = rule->getTemplateParameters();
if (auto alias = SpelledTypedef(ctx, use)) {
if (alias->getIdentifier() != member->getIdentifier()) {
return std::nullopt;
}
auto owner = llvm::dyn_cast<clang::CXXRecordDecl>(alias->getDeclContext());
if (auto args = owner
? DeduceFromType(sema, params, clang::QualType(spec, 0),
ctx.getCanonicalTagType(owner))
: std::nullopt) {
return args;
}
}
auto args = getDefaultedArgumentsOfRule(sema, rule, tmpl, spec);
if (!args || !resolvesTo(sema, rule, *args, pattern, use)) {
return std::nullopt;
}
return args;
}

bool IsInternal(const clang::NamedDecl *decl) {
if (decl->getAccess() == clang::AS_private ||
decl->getAccess() == clang::AS_protected) {
Expand Down
4 changes: 4 additions & 0 deletions cpp2rust/converter/rules/decls.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ std::optional<std::vector<clang::TemplateArgument>>
DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params,
clang::QualType pattern, clang::QualType actual);

std::optional<std::vector<clang::TemplateArgument>>
DeduceMemberType(clang::Sema &sema, const clang::TemplateDecl *rule,
clang::QualType pattern, clang::QualType use);

bool IsInternal(const clang::NamedDecl *decl);

clang::QualType PeelSugar(clang::ASTContext &ctx, clang::QualType type);
Expand Down
42 changes: 31 additions & 11 deletions cpp2rust/converter/rules/expr_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,26 @@ substituteSpecializationInRuleTmpl(clang::Sema &sema,
return result.isInvalid() || trial.failed() ? nullptr : result.get();
}

void deduceMemberTypesFromOperands(
clang::Sema &sema, clang::FunctionTemplateDecl *rule,
const clang::Expr *use, std::vector<clang::TemplateArgument> &deduced) {
auto pattern = rule->getTemplatedDecl();
auto operands = getOperands(sema.Context, use, pattern->getNumParams());
for (unsigned i = 0; i < operands.size() && i < pattern->getNumParams();
++i) {
auto param = pattern->getParamDecl(i)->getType().getNonReferenceType();
auto operand = operands[i]->getType().getNonReferenceType();
auto args = param->getAs<clang::DependentNameType>()
? RuleDecls::DeduceMemberType(sema, rule, param, operand)
: std::nullopt;
for (unsigned j = 0; args && j < deduced.size(); ++j) {
if (deduced[j].isNull()) {
deduced[j] = (*args)[j];
}
}
}
}

clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema,
clang::FunctionTemplateDecl *rule,
const clang::Expr *use) {
Expand All @@ -266,27 +286,27 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema,
return explicit_args;
}
auto params = rule->getTemplateParameters();
auto deduced = RuleDecls::DeduceFromType(
sema, params, rule->getTemplatedDecl()->getReturnType(),
RuleDecls::SkipImplicit(use)
->getType()
.getCanonicalType()
.getUnqualifiedType());
if (!deduced) {
return explicit_args;
}
auto deduced =
RuleDecls::DeduceFromType(sema, params,
rule->getTemplatedDecl()->getReturnType(),
RuleDecls::SkipImplicit(use)
->getType()
.getCanonicalType()
.getUnqualifiedType())
.value_or(std::vector<clang::TemplateArgument>(params->size()));
deduceMemberTypesFromOperands(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);
if (param->isParameterPack() || (*deduced)[i].isNull()) {
if (param->isParameterPack() || deduced[i].isNull()) {
break;
}
clang::QualType nttp_type;
if (auto nttp = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(param)) {
nttp_type = nttp->getType();
}
explicit_args.addArgument(
sema.getTrivialTemplateArgumentLoc((*deduced)[i], nttp_type, loc));
sema.getTrivialTemplateArgumentLoc(deduced[i], nttp_type, loc));
}
return explicit_args;
}
Expand Down
22 changes: 21 additions & 1 deletion cpp2rust/converter/rules/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ void addRule(clang::ASTContext &ctx, const std::string &dir,
}
assert(rule && "rule has no target");
if (!name.starts_with('f')) {
auto keys = RuleKeys::GetTypeKeys(decl);
auto keys = RuleKeys::GetTypeKeys(*sema_, decl);
assert(!keys.empty() && "type rule has no key");
for (auto key : keys) {
types_.emplace(
Expand Down Expand Up @@ -302,6 +302,9 @@ TypeRuleMatch findType(clang::QualType use) {
if (!templates && llvm::isa<clang::TemplateDecl>(it->second.decl)) {
continue;
}
if (best.rule_decl && best.rule_decl->decl == it->second.decl) {
continue;
}
auto match = AstMatcher::MatchType(*sema_, type, it->second.decl);
if (!match) {
continue;
Expand Down Expand Up @@ -458,6 +461,23 @@ clang::QualType GetInitType(const clang::Expr *expr) {
clang::DeclarationName());
}

clang::QualType GetDeclaredReturnType(const clang::Expr *expr) {
auto match = findExpr(expr);
assert(match.rule_decl && "expression must have a translation rule");
auto function = match.rule_decl->decl->getAsFunction();
assert(function && "rule is not a function");
auto tmpl = function->getDescribedFunctionTemplate();
if (!tmpl) {
return function->getReturnType();
}
clang::MultiLevelTemplateArgumentList args(tmpl, match.args,
/*Final=*/false);
clang::Sema::InstantiatingTemplate inst(
*sema_, expr->getBeginLoc(), const_cast<clang::FunctionDecl *>(function));
return sema_->SubstType(function->getReturnType(), args, expr->getBeginLoc(),
clang::DeclarationName());
}

bool HasExprRule(const clang::FunctionDecl *decl) {
auto key = RuleKeys::GetDeclKey(decl);
if (!key) {
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/rules/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr,
clang::QualType GetConversionSourceType(clang::ASTContext &ctx,
const clang::Expr *expr, unsigned to);

clang::QualType GetDeclaredReturnType(const clang::Expr *expr);

bool HasExprRule(const clang::FunctionDecl *decl);
bool IsLibcPassthrough(clang::ASTContext &ctx, const clang::Expr *expr);
bool ReturnsPointer(clang::ASTContext &ctx, const clang::Expr *expr);
Expand Down
31 changes: 27 additions & 4 deletions cpp2rust/converter/rules/rule_keys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ std::vector<clang::QualType> resolveMemberType(clang::Sema &sema,
auto tmpl = spec ? llvm::dyn_cast_or_null<clang::ClassTemplateDecl>(
spec->getTemplateName().getAsTemplateDecl())
: nullptr;
assert(tmpl && "member type is not qualified by a class template");
if (!tmpl) {
continue;
}
for (auto record : matchingDefinitions(sema, tmpl, spec)) {
if (!record->hasDefinition()) {
continue;
Expand Down Expand Up @@ -266,7 +268,8 @@ std::optional<TypeKey> GetTypeKey(clang::QualType type) {
return type.getTypePtr();
}

std::vector<TypeKey> GetTypeKeys(const clang::NamedDecl *rule) {
std::vector<TypeKey> GetTypeKeys(clang::Sema &sema,
const clang::NamedDecl *rule) {
auto alias = llvm::dyn_cast<clang::TypedefNameDecl>(rule);
if (auto tmpl = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(rule)) {
alias = tmpl->getTemplatedDecl();
Expand All @@ -279,9 +282,29 @@ std::vector<TypeKey> GetTypeKeys(const clang::NamedDecl *rule) {
if (auto member = pattern->getAs<clang::DependentNameType>()) {
auto qualifier = member->getQualifier().getAsType();
assert(qualifier && "member type is not qualified by a type");
auto parent = getParent(clang::QualType(qualifier, 0));
auto outermost = qualifier;
while (auto outer = outermost->getAs<clang::DependentNameType>()) {
outermost = outer->getQualifier().getAsType();
assert(outermost && "member type is not qualified by a type");
}
auto parent = getParent(clang::QualType(outermost, 0));
assert(parent && "type rule has no key");
return {parent};
std::vector<TypeKey> keys{parent};
auto spec = qualifier->getAs<clang::TemplateSpecializationType>();
if (outermost == qualifier && spec &&
llvm::isa_and_nonnull<clang::ClassTemplateDecl>(
spec->getTemplateName().getAsTemplateDecl())) {
for (auto resolved : resolveMemberType(sema, pattern)) {
auto key = GetTypeKey(resolved);
auto decl = key ? std::get_if<const clang::Decl *>(&*key) : nullptr;
if (decl && *decl &&
RuleDecls::IsInternal(llvm::cast<clang::NamedDecl>(*decl)) &&
!llvm::is_contained(keys, *key)) {
keys.push_back(*key);
}
}
}
return keys;
}
auto key = GetTypeKey(pattern);
assert(key && "type rule has no key");
Expand Down
3 changes: 2 additions & 1 deletion cpp2rust/converter/rules/rule_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ std::optional<ExprKey> GetDeclKey(const clang::NamedDecl *decl);

std::optional<TypeKey> GetTypeKey(clang::QualType type);

std::vector<TypeKey> GetTypeKeys(const clang::NamedDecl *rule);
std::vector<TypeKey> GetTypeKeys(clang::Sema &sema,
const clang::NamedDecl *rule);

std::vector<TypeKey> GetTypeKeys(clang::ASTContext &ctx, clang::QualType use);
} // namespace cpp2rust::RuleKeys
Expand Down
18 changes: 2 additions & 16 deletions cpp2rust/converter/rules/type_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,8 @@ std::optional<TypeMatch> MatchType(clang::Sema &sema, clang::QualType use,
return TypeMatch{};
}
auto params = tmpl->getTemplateParameters();
if (auto member = pattern->getAs<clang::DependentNameType>()) {
auto qualifier = member->getQualifier().getAsType();
auto typedef_type =
llvm::dyn_cast<clang::TypedefType>(spelled.getTypePtr());
if (!qualifier || !typedef_type ||
typedef_type->getDecl()->getIdentifier() != member->getIdentifier()) {
return std::nullopt;
}
auto owner = llvm::dyn_cast<clang::CXXRecordDecl>(
typedef_type->getDecl()->getDeclContext());
if (!owner) {
return std::nullopt;
}
auto args =
RuleDecls::DeduceFromType(sema, params, clang::QualType(qualifier, 0),
ctx.getCanonicalTagType(owner));
if (pattern->getAs<clang::DependentNameType>()) {
auto args = RuleDecls::DeduceMemberType(sema, tmpl, pattern, spelled);
if (args && (args = completeArgs(sema, tmpl, *std::move(args)))) {
return TypeMatch{*std::move(args)};
}
Expand Down
Loading
Loading