mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-14 19:08:42 +00:00
Adds support for exporting default values for concrete parameters of generic functions. (#8019)
Adds support for exporting both the generic function decl with concrete default arguments to clang, as well as exporting the monomorphized concrete function def. This required adding the `SpecificId` to the exported global variable name, to avoid duplicate names in the global vars. I also now set up an evaluated context in `clang_sema` when declaring the default arg expression with clang, to avoid the problem of emitting concrete AST expressions in an unevaluated context from the in-progress template function decl context when exporting default values for generics. Note: Default values for generic parameters of generic functions are not yet supported.
This commit is contained in:
1 parent
21c825a3f7
commit
c7de727575
2 files changed
+192
-119
No files matched your search
+166
-119
@@ -780,10 +780,12 @@ struct FunctionInfo {
|
||||
};
|
||||
|
||||
explicit FunctionInfo(Context& context, SemIR::FunctionId function_id,
|
||||
SemIR::SpecificId specific_id,
|
||||
const SemIR::Function& function,
|
||||
clang::DeclContext* decl_context,
|
||||
bool export_as_constructor)
|
||||
: function_id(function_id),
|
||||
specific_id(specific_id),
|
||||
function(function),
|
||||
decl_context(decl_context),
|
||||
return_type_id(function.GetDeclaredReturnType(context.sem_ir())),
|
||||
@@ -858,6 +860,7 @@ struct FunctionInfo {
|
||||
}
|
||||
|
||||
SemIR::FunctionId function_id;
|
||||
SemIR::SpecificId specific_id;
|
||||
const SemIR::Function& function;
|
||||
|
||||
// Parent scope in the C++ AST where a C++ thunk for this function can
|
||||
@@ -900,7 +903,8 @@ static auto MapToCppThunkParamType(Context& context, SemIR::TypeId type_id)
|
||||
// Build FunctionInfo for an export of the given Carbon function. Exports the
|
||||
// name scope if necessary. Returns `nullopt` if an error was diagnosed.
|
||||
static auto BuildFunctionInfo(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id)
|
||||
SemIR::FunctionId callee_function_id,
|
||||
SemIR::SpecificId callee_specific_id)
|
||||
-> std::optional<FunctionInfo> {
|
||||
const SemIR::Function& callee = context.functions().Get(callee_function_id);
|
||||
|
||||
@@ -948,22 +952,128 @@ static auto BuildFunctionInfo(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
return FunctionInfo(context, callee_function_id, callee, decl_context,
|
||||
export_as_constructor);
|
||||
return FunctionInfo(context, callee_function_id, callee_specific_id, callee,
|
||||
decl_context, export_as_constructor);
|
||||
}
|
||||
|
||||
// Synthesizes a global variable with a value of `value_id` and exports it to
|
||||
// clang. To generate a unique name, `parameter_index` should be the index of
|
||||
// the parameter within the function. `pattern_id` is the parameter pattern.
|
||||
// Returns a `clang::Expr*` suitable for conversion into an argument default
|
||||
// value to provide to clang, or `nullptr` if `value_id` is None.
|
||||
static auto BuildDefaultValueExpr(Context& context, SemIR::LocId loc_id,
|
||||
const FunctionInfo& target,
|
||||
int32_t parameter_index,
|
||||
SemIR::InstId pattern_id,
|
||||
SemIR::ConstantId value_id) -> clang::Expr* {
|
||||
if (!value_id.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Generic function decls must provide an evaluated context for synthesis
|
||||
// of these default values.
|
||||
CARBON_CHECK(!context.clang_sema().isUnevaluatedContext());
|
||||
|
||||
// Synthesize a global variable, initialized to the default value, for export
|
||||
// to C++.
|
||||
// TODO: making the mangler is expensive, we should cache and reuse it.
|
||||
SemIR::Mangler mangler(context.sem_ir(), context.total_ir_count(),
|
||||
context.mangle_string_fingerprint());
|
||||
auto mangled_fn_name = mangler.Mangle(target.function_id, target.specific_id);
|
||||
llvm::SmallString<64> var_name = llvm::formatv(
|
||||
"__carbon_default_value_{}_{}", mangled_fn_name, parameter_index);
|
||||
|
||||
// We should not be exporting the same variable twice.
|
||||
CARBON_CHECK(!context.identifiers().Lookup(var_name).has_value());
|
||||
|
||||
auto& ident = context.ast_context().Idents.get(var_name);
|
||||
auto name_id =
|
||||
SemIR::NameId::ForIdentifier(context.identifiers().Add(ident.getName()));
|
||||
|
||||
// First we synthesize the variable definition by creating an EntityName from
|
||||
// the generated name stored in `var_name`. We want to generate a
|
||||
// `SemIR::VarStorage` instruction in the `InstBlockId::ExtraGlobalVariables`
|
||||
// special block. During lowering this block is scanned for `VarStorage`
|
||||
// instructions, and each one encountered is exported as a global variable
|
||||
// with the associated typed storage.
|
||||
auto pattern_type_id = context.insts().Get(pattern_id).type_id();
|
||||
|
||||
// TODO: we are not storing the original TypeInstIds of the parameter patterns
|
||||
// with the function, so the location of those instructions are lost in this
|
||||
// entity. If we ever need to know that location (e.g. for diagnostics), we
|
||||
// will need to preserve them with functions and then pipe that through here.
|
||||
SemIR::EntityName entity_name = {
|
||||
.name_id = name_id,
|
||||
.parent_scope_id = SemIR::NameScopeId::Package,
|
||||
.type_inst_id = context.types().GetTypeInstId(pattern_type_id),
|
||||
};
|
||||
|
||||
auto entity_name_id = context.entity_names().AddCanonical(entity_name);
|
||||
|
||||
auto binding_pattern_id = AddInstInNoBlock<SemIR::RefBindingPattern>(
|
||||
context, loc_id,
|
||||
{.type_id = pattern_type_id, .entity_name_id = entity_name_id});
|
||||
context.extra_global_variables().push_back(binding_pattern_id);
|
||||
|
||||
auto var_pattern_id = AddInstInNoBlock(
|
||||
context, SemIR::LocIdAndInst::RuntimeVerified(
|
||||
context.sem_ir(), loc_id,
|
||||
SemIR::VarPattern({.type_id = pattern_type_id,
|
||||
.subpattern_id = binding_pattern_id})));
|
||||
context.extra_global_variables().push_back(var_pattern_id);
|
||||
// Note that as this is a global variable, we will not be pushing any cleanup
|
||||
// instructions.
|
||||
auto var_storage_id = AddInstInNoBlock(
|
||||
context,
|
||||
SemIR::LocIdAndInst(
|
||||
loc_id, SemIR::VarStorage({.type_id = ExtractScrutineeType(
|
||||
context.sem_ir(), pattern_type_id),
|
||||
.pattern_id = var_pattern_id})));
|
||||
context.extra_global_variables().push_back(var_storage_id);
|
||||
|
||||
// Add the variable initialization commands to the global initialization
|
||||
// section so that it will have its value set before being referenced by the
|
||||
// C++ code. Because we are exporting variables (vs. constants), we don't
|
||||
// export the value of that default value, only the location of the global
|
||||
// storage for that value. As a result, we must add instructions to
|
||||
// the global initialization table to actually initialize that storage
|
||||
// at binary startup time, so that when the C++ code dereferences
|
||||
// it, the value is correct.
|
||||
context.global_init().Resume();
|
||||
auto init_id = InitializeExisting(
|
||||
context, loc_id, var_storage_id,
|
||||
context.constant_values().GetInstId(value_id), /*for_return=*/false);
|
||||
AddInst<SemIR::Assign>(context, loc_id,
|
||||
{.lhs_id = var_storage_id, .rhs_id = init_id});
|
||||
context.global_init().Suspend();
|
||||
|
||||
auto* var_decl =
|
||||
ExportVarToCpp(context, var_storage_id,
|
||||
context.insts().GetAs<SemIR::VarStorage>(var_storage_id));
|
||||
auto decl_ref_expr = context.clang_sema().BuildDeclarationNameExpr(
|
||||
clang::CXXScopeSpec(),
|
||||
clang::DeclarationNameInfo(var_decl->getDeclName(),
|
||||
clang::SourceLocation()),
|
||||
var_decl);
|
||||
return decl_ref_expr.get();
|
||||
}
|
||||
|
||||
// Create a `clang::FunctionDecl` with the given parameter types and
|
||||
// return type.
|
||||
//
|
||||
// The function's name will match the one referenced by `function_name_id`,
|
||||
// and the function will be added to the given `decl_context`.
|
||||
static auto BuildCppFunctionDecl(Context& context,
|
||||
clang::DeclContext* decl_context,
|
||||
SemIR::LocId loc_id,
|
||||
clang::DeclarationName declaration_name,
|
||||
clang::ArrayRef<clang::QualType> param_types,
|
||||
clang::QualType return_type,
|
||||
bool export_as_constructor) {
|
||||
// and the function will be added to the given `decl_context`. If provided,
|
||||
// `default_value_exprs` must be index-aligned with `param_types`, and at every
|
||||
// index must have either a `clang::Expr*` suitable for use as a default value
|
||||
// for that parameter, or `nullptr` if no default value is specified.
|
||||
static auto BuildCppFunctionDecl(
|
||||
Context& context, clang::DeclContext* decl_context, SemIR::LocId loc_id,
|
||||
clang::DeclarationName declaration_name,
|
||||
clang::ArrayRef<clang::QualType> param_types,
|
||||
std::optional<clang::ArrayRef<clang::Expr*>> default_value_exprs,
|
||||
clang::QualType return_type, bool export_as_constructor) {
|
||||
CARBON_CHECK(!default_value_exprs.has_value() ||
|
||||
default_value_exprs->size() == param_types.size());
|
||||
auto clang_loc = GetCppLocation(context, loc_id);
|
||||
|
||||
auto cpp_function_type = context.ast_context().getFunctionType(
|
||||
@@ -992,13 +1102,22 @@ static auto BuildCppFunctionDecl(Context& context,
|
||||
|
||||
// Build parameter decls.
|
||||
llvm::SmallVector<clang::ParmVarDecl*> param_var_decls;
|
||||
for (auto type : param_types) {
|
||||
for (auto [i, type] : llvm::enumerate(param_types)) {
|
||||
auto* param_tinfo =
|
||||
context.ast_context().getTrivialTypeSourceInfo(type, clang_loc);
|
||||
clang::ParmVarDecl* param = clang::ParmVarDecl::Create(
|
||||
context.ast_context(), function_decl, /*StartLoc=*/clang_loc,
|
||||
/*IdLoc=*/clang_loc, /*Id=*/nullptr, type, param_tinfo, clang::SC_None,
|
||||
/*DefArg=*/nullptr);
|
||||
auto* default_value_expr =
|
||||
default_value_exprs ? default_value_exprs.value()[i] : nullptr;
|
||||
if (default_value_expr) {
|
||||
auto converted_result = context.clang_sema().ConvertParamDefaultArgument(
|
||||
param, default_value_expr, clang_loc);
|
||||
CARBON_CHECK(!converted_result.isInvalid());
|
||||
context.clang_sema().SetParamDefaultArgument(
|
||||
param, converted_result.get(), clang_loc);
|
||||
}
|
||||
param_var_decls.push_back(param);
|
||||
}
|
||||
function_decl->setParams(param_var_decls);
|
||||
@@ -1051,7 +1170,8 @@ static auto BuildCppFunctionDeclForNonGenericCarbonFn(Context& context,
|
||||
: context.ast_context().getTranslationUnitDecl();
|
||||
auto* function_decl = BuildCppFunctionDecl(
|
||||
context, decl_context, loc_id, target.GetCppName(context),
|
||||
cpp_param_types, cpp_return_type, target.export_as_constructor);
|
||||
cpp_param_types, std::nullopt, cpp_return_type,
|
||||
target.export_as_constructor);
|
||||
|
||||
// Mangle the function name and attach it to the `FunctionDecl`.
|
||||
SemIR::Mangler m(context.sem_ir(), context.total_ir_count(),
|
||||
@@ -1082,6 +1202,7 @@ static auto BuildCppFunctionDeclForGenericCarbonFn(Context& context,
|
||||
// irrelevant, and the parameter should instead map to something that will
|
||||
// guide C++ template argument deduction into doing the right thing.
|
||||
llvm::SmallVector<clang::QualType> cpp_param_types;
|
||||
llvm::SmallVector<clang::Expr*> cpp_default_values;
|
||||
if (callee.self_param) {
|
||||
auto cpp_type = MapToCppThunkParamType(context, callee.self_param->type_id);
|
||||
if (cpp_type.isNull()) {
|
||||
@@ -1089,14 +1210,18 @@ static auto BuildCppFunctionDeclForGenericCarbonFn(Context& context,
|
||||
return nullptr;
|
||||
}
|
||||
cpp_param_types.push_back(cpp_type);
|
||||
cpp_default_values.push_back(nullptr);
|
||||
}
|
||||
for (auto param : callee.explicit_params) {
|
||||
for (auto [i, param] : llvm::enumerate(callee.explicit_params)) {
|
||||
auto cpp_type = MapToCppThunkParamType(context, param.type_id);
|
||||
if (cpp_type.isNull()) {
|
||||
context.TODO(loc_id, "failed to map Carbon type to C++");
|
||||
return nullptr;
|
||||
}
|
||||
cpp_param_types.push_back(cpp_type);
|
||||
cpp_default_values.push_back(
|
||||
BuildDefaultValueExpr(context, loc_id, callee, i, param.pattern_inst_id,
|
||||
param.default_value_const_id));
|
||||
}
|
||||
|
||||
clang::QualType cpp_return_type = context.ast_context().VoidTy;
|
||||
@@ -1115,7 +1240,8 @@ static auto BuildCppFunctionDeclForGenericCarbonFn(Context& context,
|
||||
: context.ast_context().getTranslationUnitDecl();
|
||||
return BuildCppFunctionDecl(context, decl_context, loc_id,
|
||||
callee.GetCppName(context), cpp_param_types,
|
||||
cpp_return_type, callee.export_as_constructor);
|
||||
cpp_default_values, cpp_return_type,
|
||||
callee.export_as_constructor);
|
||||
}
|
||||
|
||||
// Returns whether the given Carbon parameter should be passed as a C++ const
|
||||
@@ -1201,96 +1327,6 @@ static auto BuildCppToCarbonThunkFunctionType(Context& context,
|
||||
->getAs<clang::FunctionProtoType>();
|
||||
}
|
||||
|
||||
// Synthesizes a global variable with a value of `value_id` and exports it to
|
||||
// clang. To generate a unique name, `parameter_index` should be the index of
|
||||
// the parameter within the function. `pattern_id` is the parameter pattern.
|
||||
// Returns a `clang::Expr*` suitable for conversion into an argument default
|
||||
// value to provide to clang, or `nullptr` if `value_id` is None.
|
||||
static auto BuildDefaultValueExpr(Context& context, SemIR::LocId loc_id,
|
||||
const FunctionInfo& target,
|
||||
int32_t parameter_index,
|
||||
SemIR::InstId pattern_id,
|
||||
SemIR::ConstantId value_id) -> clang::Expr* {
|
||||
if (!value_id.has_value()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Synthesize a global variable, initialized to the default value, for export
|
||||
// to C++.
|
||||
// TODO: making the mangler is expensive, we should cache and reuse it.
|
||||
SemIR::Mangler mangler(context.sem_ir(), context.total_ir_count(),
|
||||
context.mangle_string_fingerprint());
|
||||
auto mangled_fn_name =
|
||||
mangler.Mangle(target.function_id, SemIR::SpecificId::None);
|
||||
llvm::SmallString<64> var_name = llvm::formatv(
|
||||
"__carbon_default_value_{}_{}", mangled_fn_name, parameter_index);
|
||||
auto& ident = context.ast_context().Idents.get(var_name);
|
||||
auto name_id =
|
||||
SemIR::NameId::ForIdentifier(context.identifiers().Add(ident.getName()));
|
||||
|
||||
// First we synthesize the variable definition by creating an EntityName from
|
||||
// the generated name stored in `var_name`. We want to generate a
|
||||
// `SemIR::VarStorage` instruction in the `InstBlockId::ExtraGlobalVariables`
|
||||
// special block. During lowering this block is scanned for `VarStorage`
|
||||
// instructions, and each one encountered is exported as a global variable
|
||||
// with the associated typed storage.
|
||||
auto pattern_type_id = context.insts().Get(pattern_id).type_id();
|
||||
// TODO: we are not storing the original TypeInstIds of the parameter patterns
|
||||
// with the function, so the location of those instructions are lost in this
|
||||
// entity. If we ever need to know that location (e.g. for diagnostics), we
|
||||
// will need to preserve them with functions and then pipe that through here.
|
||||
SemIR::EntityName entity_name = {
|
||||
.name_id = name_id,
|
||||
.parent_scope_id = SemIR::NameScopeId::Package,
|
||||
.type_inst_id = context.types().GetTypeInstId(pattern_type_id),
|
||||
};
|
||||
auto entity_name_id = context.entity_names().AddCanonical(entity_name);
|
||||
|
||||
auto binding_pattern_id = AddInstInNoBlock<SemIR::RefBindingPattern>(
|
||||
context, loc_id,
|
||||
{.type_id = pattern_type_id, .entity_name_id = entity_name_id});
|
||||
context.extra_global_variables().push_back(binding_pattern_id);
|
||||
|
||||
auto var_pattern_id = AddInstInNoBlock(
|
||||
context, SemIR::LocIdAndInst::RuntimeVerified(
|
||||
context.sem_ir(), loc_id,
|
||||
SemIR::VarPattern({.type_id = pattern_type_id,
|
||||
.subpattern_id = binding_pattern_id})));
|
||||
context.extra_global_variables().push_back(var_pattern_id);
|
||||
|
||||
auto var_storage_id = AddInstWithCleanupInNoBlock<SemIR::VarStorage>(
|
||||
context, loc_id,
|
||||
{.type_id = ExtractScrutineeType(context.sem_ir(), pattern_type_id),
|
||||
.pattern_id = var_pattern_id});
|
||||
context.extra_global_variables().push_back(var_storage_id);
|
||||
|
||||
// Add the variable initialization commands to the global initialization
|
||||
// section so that it will have its value set before being referenced by the
|
||||
// C++ code. Because we are exporting variables (vs. constants), we don't
|
||||
// export the value of that default value, only the location of the global
|
||||
// storage for that value. As a result, we must add instructions to
|
||||
// the global initialization table to actually initialize that storage
|
||||
// at binary startup time, so that when the C++ code dereferences
|
||||
// it, the value is correct.
|
||||
context.global_init().Resume();
|
||||
auto init_id = InitializeExisting(
|
||||
context, loc_id, var_storage_id,
|
||||
context.constant_values().GetInstId(value_id), /*for_return=*/false);
|
||||
AddInst<SemIR::Assign>(context, loc_id,
|
||||
{.lhs_id = var_storage_id, .rhs_id = init_id});
|
||||
context.global_init().Suspend();
|
||||
|
||||
auto* var_decl =
|
||||
ExportVarToCpp(context, var_storage_id,
|
||||
context.insts().GetAs<SemIR::VarStorage>(var_storage_id));
|
||||
auto decl_ref_expr = context.clang_sema().BuildDeclarationNameExpr(
|
||||
clang::CXXScopeSpec(),
|
||||
clang::DeclarationNameInfo(var_decl->getDeclName(),
|
||||
clang::SourceLocation()),
|
||||
var_decl);
|
||||
return decl_ref_expr.get();
|
||||
}
|
||||
|
||||
// Create the declaration of the C++ thunk.
|
||||
static auto BuildCppToCarbonThunkDecl(Context& context, SemIR::LocId loc_id,
|
||||
const FunctionInfo& target,
|
||||
@@ -1376,9 +1412,16 @@ static auto BuildCppToCarbonThunkDecl(Context& context, SemIR::LocId loc_id,
|
||||
ast_context, thunk_function_decl, /*StartLoc=*/clang_loc,
|
||||
/*IdLoc=*/clang_loc, /*Id=*/nullptr, type,
|
||||
/*TInfo=*/nullptr, clang::SC_None, /*DefArg=*/nullptr);
|
||||
if (auto* default_value_expr = BuildDefaultValueExpr(
|
||||
context, loc_id, target, i, param.pattern_inst_id,
|
||||
param.default_value_const_id)) {
|
||||
if (param.default_value_const_id.has_value()) {
|
||||
clang::Sema& sema = context.clang_sema();
|
||||
clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
|
||||
// Ensure that the evaluation context is not `Unevaluated`, as that
|
||||
// would cause code generation to fail.
|
||||
clang::EnterExpressionEvaluationContext evaluated(
|
||||
sema, clang::Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
|
||||
auto* default_value_expr = BuildDefaultValueExpr(
|
||||
context, loc_id, target, i, param.pattern_inst_id,
|
||||
param.default_value_const_id);
|
||||
auto converted_result = context.clang_sema().ConvertParamDefaultArgument(
|
||||
thunk_param, default_value_expr, clang_loc);
|
||||
CARBON_CHECK(!converted_result.isInvalid());
|
||||
@@ -1590,6 +1633,7 @@ static auto BuildCarbonToCarbonThunk(Context& context, SemIR::LocId loc_id,
|
||||
target.function.first_decl_id(), target.export_as_constructor);
|
||||
|
||||
return FunctionInfo(context, carbon_thunk_function_id,
|
||||
SemIR::SpecificId::None,
|
||||
context.functions().Get(carbon_thunk_function_id),
|
||||
target.decl_context, target.export_as_constructor);
|
||||
}
|
||||
@@ -1606,7 +1650,7 @@ auto ExportVirtualFunctionDeclToCpp(Context& context, SemIR::LocId loc_id,
|
||||
clang::CXXRecordDecl* parent,
|
||||
SemIR::FunctionId function_id)
|
||||
-> clang::CXXMethodDecl* {
|
||||
FunctionInfo target(context, function_id,
|
||||
FunctionInfo target(context, function_id, SemIR::SpecificId::None,
|
||||
context.functions().Get(function_id), parent,
|
||||
/*export_as_constructor=*/false);
|
||||
return cast_or_null<clang::CXXMethodDecl>(
|
||||
@@ -1650,7 +1694,8 @@ auto DefineExportedVirtualFunction(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id,
|
||||
clang::CXXMethodDecl* method_decl) -> void {
|
||||
const SemIR::Function& callee = context.functions().Get(callee_function_id);
|
||||
FunctionInfo target_function_info(context, callee_function_id, callee,
|
||||
FunctionInfo target_function_info(context, callee_function_id,
|
||||
SemIR::SpecificId::None, callee,
|
||||
method_decl->getDeclContext(),
|
||||
/*export_as_constructor=*/false);
|
||||
BuildCppToCarbonThunk(context, loc_id, target_function_info, method_decl, "");
|
||||
@@ -1692,20 +1737,21 @@ auto ExportFunctionSpecializationToCpp(
|
||||
auto target_function =
|
||||
context.functions().Get(target_function_decl.function_id);
|
||||
|
||||
auto* decl_context = function_template_decl->getDeclContext();
|
||||
FunctionInfo target(context, target_function_decl.function_id,
|
||||
target_function, decl_context,
|
||||
llvm::isa<clang::CXXConstructorDecl>(
|
||||
function_template_decl->getTemplatedDecl()));
|
||||
SemIR::LocId loc_id(target.function.first_decl_id());
|
||||
SemIR::LocId loc_id(target_function.first_decl_id());
|
||||
|
||||
// Create a specific, and use that to convert return type and
|
||||
// parameters with symbolic types to concrete types.
|
||||
auto specific_id = MakeSpecificForTemplateArgs(
|
||||
context, loc_id, target.function.generic_id, template_args);
|
||||
context, loc_id, target_function.generic_id, template_args);
|
||||
if (specific_id == SemIR::SpecificId::None) {
|
||||
return false;
|
||||
}
|
||||
auto* decl_context = function_template_decl->getDeclContext();
|
||||
FunctionInfo target(context, target_function_decl.function_id, specific_id,
|
||||
target_function, decl_context,
|
||||
llvm::isa<clang::CXXConstructorDecl>(
|
||||
function_template_decl->getTemplatedDecl()));
|
||||
|
||||
if (!ResolveReturnTypeInSpecific(context, loc_id, target.function,
|
||||
specific_id)) {
|
||||
return false;
|
||||
@@ -1781,7 +1827,8 @@ static auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto target = BuildFunctionInfo(context, loc_id, callee_function_id);
|
||||
auto target = BuildFunctionInfo(context, loc_id, callee_function_id,
|
||||
SemIR::SpecificId::None);
|
||||
if (!target) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1976,7 +2023,7 @@ auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info,
|
||||
// TODO: Once we support exporting specific classes, export the specific
|
||||
// destructor here rather than a generic one.
|
||||
auto thunk_function_id = BuildDestroyThunk(context, loc_id, class_info);
|
||||
FunctionInfo thunk_target(context, thunk_function_id,
|
||||
FunctionInfo thunk_target(context, thunk_function_id, SemIR::SpecificId::None,
|
||||
context.functions().Get(thunk_function_id),
|
||||
record_decl, /*export_as_constructor=*/false);
|
||||
auto* cpp_function_decl =
|
||||
|
||||
Reference in new issue
Block a user