|
10 | 10 | #include <clang/Basic/Version.h> |
11 | 11 | #include <clang/Sema/Template.h> |
12 | 12 | #include <llvm/ADT/DenseMap.h> |
| 13 | +#include <llvm/ADT/StringExtras.h> |
13 | 14 | #include <llvm/Support/ConvertUTF.h> |
14 | 15 | #include <llvm/Support/ErrorHandling.h> |
15 | 16 |
|
@@ -879,6 +880,9 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { |
879 | 880 | EmitReprC(decl); |
880 | 881 | } |
881 | 882 | EmitDerives(decl); |
| 883 | + if (auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl)) { |
| 884 | + EmitConversionAttrs(cxx); |
| 885 | + } |
882 | 886 | EmitByteSizeAttr(decl); |
883 | 887 |
|
884 | 888 | // Fields |
@@ -956,6 +960,41 @@ void Converter::EmitDerives(const clang::RecordDecl *decl) { |
956 | 960 | StrCat(")]"); |
957 | 961 | } |
958 | 962 |
|
| 963 | +void Converter::EmitConversionAttrs(clang::CXXRecordDecl *decl) { |
| 964 | + std::unordered_set<std::string> from_types; |
| 965 | + ForEachConvertingConstructor(decl, [&](clang::CXXConstructorDecl *ctor) { |
| 966 | + auto *param = ctor->getParamDecl(0); |
| 967 | + auto type = param->getType(); |
| 968 | + auto source = |
| 969 | + GetUnboxedTypeAsString(type.getNonReferenceType().getUnqualifiedType()); |
| 970 | + bool is_move = type->isRValueReferenceType(); |
| 971 | + if (!from_types.insert(source + (is_move ? "&&" : "")).second) { |
| 972 | + return; |
| 973 | + } |
| 974 | + const char *kind = |
| 975 | + !type->isReferenceType() ? "value" |
| 976 | + : is_move || type.getNonReferenceType().isConstQualified() ? "ptr" |
| 977 | + : "mut_ptr"; |
| 978 | + StrCat(std::format( |
| 979 | + "#[{}({}, {}, {}{})]", is_move ? "cpp_from_move" : "cpp_from", source, |
| 980 | + GetCtorName(ctor), param->hasDefaultArg() ? "some_" : "", kind)); |
| 981 | + }); |
| 982 | + std::unordered_set<std::string> into_types; |
| 983 | + ForEachMethod(decl, [&](clang::CXXMethodDecl *method) { |
| 984 | + if (!IsImplicitConversionOperator(method)) { |
| 985 | + return; |
| 986 | + } |
| 987 | + auto *conversion = clang::cast<clang::CXXConversionDecl>(method); |
| 988 | + auto target = GetUnboxedTypeAsString( |
| 989 | + conversion->getConversionType().getUnqualifiedType()); |
| 990 | + if (!into_types.insert(target).second) { |
| 991 | + return; |
| 992 | + } |
| 993 | + StrCat( |
| 994 | + std::format("#[cpp_into({}, {})]", target, GetMethodName(conversion))); |
| 995 | + }); |
| 996 | +} |
| 997 | + |
959 | 998 | void Converter::EmitReprC(clang::RecordDecl *decl) { |
960 | 999 | if (decl->hasAttr<clang::AlignedAttr>()) { |
961 | 1000 | StrCat(std::format("#[repr(C, align({}))]", |
@@ -4208,6 +4247,10 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) { |
4208 | 4247 | struct_attrs.emplace_back("MoveCtorUnsafe"); |
4209 | 4248 | } |
4210 | 4249 |
|
| 4250 | + if (HasImplicitConversions(decl)) { |
| 4251 | + struct_attrs.emplace_back("CppFromUnsafe"); |
| 4252 | + } |
| 4253 | + |
4211 | 4254 | if (HasCopyAssignOperator(decl)) { |
4212 | 4255 | struct_attrs.emplace_back("CopyAssignUnsafe"); |
4213 | 4256 | } |
@@ -4946,6 +4989,14 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, |
4946 | 4989 | ToStringBase(ctx_.getPointerType(elem))); |
4947 | 4990 | } |
4948 | 4991 |
|
| 4992 | + if (auto *init = |
| 4993 | + RuleRegistry::BuildParamConversion(ctx_, expr, ph_ctx.arg_idx, arg)) { |
| 4994 | + Buffer buf(*this); |
| 4995 | + PushExprKind push(*this, ExprKind::RValue); |
| 4996 | + ConvertInit(init->getType(), init); |
| 4997 | + return std::move(buf).str(); |
| 4998 | + } |
| 4999 | + |
4949 | 5000 | if (ph_ctx.needs_materialization()) { |
4950 | 5001 | auto materialized = ph_ctx.materialize_ctx->GetOrMaterialize( |
4951 | 5002 | static_cast<unsigned>(ph_ctx.materialize_idx), |
@@ -5093,11 +5144,6 @@ std::string Converter::ConvertIRFragment( |
5093 | 5144 | result += t->text; |
5094 | 5145 | } else if (auto *g = std::get_if<GenericFragment>(&frag)) { |
5095 | 5146 | 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()); |
5101 | 5147 | } else if (auto *ph = std::get_if<PlaceholderFragment>(&frag)) { |
5102 | 5148 | assert(expr && "placeholder outside an expression rule"); |
5103 | 5149 | auto arg_idx = ph->n; |
@@ -5138,12 +5184,78 @@ std::string Converter::ConvertIRFragment( |
5138 | 5184 | result += |
5139 | 5185 | (*re)->prefix + ' ' + |
5140 | 5186 | ConvertIRFragment((*re)->inner, bindings, expr, args, num_args, ctx); |
| 5187 | + } else if (auto *call = |
| 5188 | + std::get_if<std::unique_ptr<GenericCallFragment>>(&frag)) { |
| 5189 | + result += ConvertGenericCall(**call, bindings, expr, args, num_args, ctx); |
5141 | 5190 | } |
5142 | 5191 | } |
5143 | 5192 |
|
5144 | 5193 | return result; |
5145 | 5194 | } |
5146 | 5195 |
|
| 5196 | +std::string |
| 5197 | +Converter::ConvertGenericCall(const TranslationRule::GenericCallFragment &call, |
| 5198 | + llvm::ArrayRef<clang::TemplateArgument> bindings, |
| 5199 | + clang::Expr *expr, clang::Expr **args, |
| 5200 | + unsigned num_args, TempMaterializationCtx *ctx) { |
| 5201 | + std::vector<std::string> call_args; |
| 5202 | + for (const auto &arg : call.args) { |
| 5203 | + call_args.push_back( |
| 5204 | + ConvertIRFragment(arg, bindings, expr, args, num_args, ctx)); |
| 5205 | + } |
| 5206 | + clang::QualType type; |
| 5207 | + if (call.generic <= bindings.size() && |
| 5208 | + bindings[call.generic - 1].getKind() == clang::TemplateArgument::Type) { |
| 5209 | + type = bindings[call.generic - 1].getAsType(); |
| 5210 | + } else { |
| 5211 | + assert(expr && "unbound generic outside an expression rule"); |
| 5212 | + type = RuleRegistry::GetGenericType(ctx_, expr, call.generic); |
| 5213 | + } |
| 5214 | + if (call.method == "default") { |
| 5215 | + assert(call_args.empty() && "default takes no arguments"); |
| 5216 | + return GetUnboxedDefaultAsString(type); |
| 5217 | + } |
| 5218 | + if (call.method == "cpp_from" || call.method == "cpp_from_move") { |
| 5219 | + assert(call_args.size() == 1 && "cpp_from takes one argument"); |
| 5220 | + return ConvertConversion( |
| 5221 | + type, RuleRegistry::GetConversionSourceType(ctx_, expr, call.generic), |
| 5222 | + call.method == "cpp_from_move", call_args[0]); |
| 5223 | + } |
| 5224 | + return std::format("<{}>::{}({})", GetUnboxedTypeAsString(type), call.method, |
| 5225 | + llvm::join(call_args, ", ")); |
| 5226 | +} |
| 5227 | + |
| 5228 | +std::string Converter::ConvertConversion(clang::QualType to, |
| 5229 | + clang::QualType from, bool is_move, |
| 5230 | + const std::string &ptr) { |
| 5231 | + if (to->isArithmeticType() && from->isArithmeticType()) { |
| 5232 | + auto value = DerefPtrExpr(ptr, from); |
| 5233 | + if (ctx_.hasSameUnqualifiedType(to, from)) { |
| 5234 | + return value; |
| 5235 | + } |
| 5236 | + if (to->isBooleanType()) { |
| 5237 | + return std::format("({} != {})", value, |
| 5238 | + from->isFloatingType() ? "0.0" : "0"); |
| 5239 | + } |
| 5240 | + if (from->isBooleanType() && to->isFloatingType()) { |
| 5241 | + return std::format("({} as u8 as {})", value, GetUnsafeTypeAsString(to)); |
| 5242 | + } |
| 5243 | + return std::format("({} as {})", value, GetUnsafeTypeAsString(to)); |
| 5244 | + } |
| 5245 | + return std::format("<{} as {}<{}>>::{}({})", GetUnboxedTypeAsString(to), |
| 5246 | + GetConversionTraitName(), GetUnboxedTypeAsString(from), |
| 5247 | + is_move ? "cpp_from_move" : "cpp_from", ptr); |
| 5248 | +} |
| 5249 | + |
| 5250 | +const char *Converter::GetConversionTraitName() const { |
| 5251 | + return "CppFromUnsafe"; |
| 5252 | +} |
| 5253 | + |
| 5254 | +std::string Converter::DerefPtrExpr(std::string_view ptr_expr, |
| 5255 | + clang::QualType pointee_type) { |
| 5256 | + return std::format("({}{})", GetPointerDerefPrefix(pointee_type), ptr_expr); |
| 5257 | +} |
| 5258 | + |
5147 | 5259 | std::string |
5148 | 5260 | Converter::ConvertVariadicTail(clang::Expr *expr, |
5149 | 5261 | const std::vector<clang::Expr *> &all_args) { |
@@ -5180,14 +5292,18 @@ void Converter::ConvertConstructFromArgs(clang::QualType type, |
5180 | 5292 | clang::SourceLocation loc) { |
5181 | 5293 | auto *init = BuildInitExpr(GetSema(), type, args, loc); |
5182 | 5294 | assert(init && "type cannot be initialized from the arguments"); |
5183 | | - if (auto *ctor = |
5184 | | - clang::dyn_cast<clang::CXXConstructExpr>(init->IgnoreImplicit())) { |
5185 | | - ConvertConstructedValue(type, ctor); |
| 5295 | + if (args.empty() && |
| 5296 | + !clang::isa<clang::CXXConstructExpr>(init->IgnoreImplicit())) { |
| 5297 | + StrCat(GetDefaultAsString(type)); |
5186 | 5298 | return; |
5187 | 5299 | } |
| 5300 | + ConvertInit(type, init); |
| 5301 | +} |
5188 | 5302 |
|
5189 | | - if (args.empty()) { |
5190 | | - StrCat(GetDefaultAsString(type)); |
| 5303 | +void Converter::ConvertInit(clang::QualType type, clang::Expr *init) { |
| 5304 | + if (auto *ctor = |
| 5305 | + clang::dyn_cast<clang::CXXConstructExpr>(init->IgnoreImplicit())) { |
| 5306 | + ConvertConstructedValue(type, ctor); |
5191 | 5307 | return; |
5192 | 5308 | } |
5193 | 5309 | Convert(init); |
|
0 commit comments