Fix crash lowering call to generic function with concrete type in signature (#7009)

When a function call appears in a generic, and calls another generic
that has a concrete type in its call-site signature, that concrete type
will be completed only in the file that contains the call. The generic
containing the call won't require completeness to be checked again when
forming a specific call, because the type was concrete. This means that
when lowering the call instruction, there is no single file that is
guaranteed to contain complete types for all of the callee's parameters
-- the file containing the specific callee won't necessarily have
completed the concrete parts of the signature, and the files containing
the definition and call won't necessarily have completed the symbolic
parts of the signature.

To handle this, look at both versions of the function when building its
lowered signature -- the version that we saw when forming the `call`
instruction and the version corresponding to the concrete, specific
callee, and combine information from both to form the LLVM function
type.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
Richard Smith
2026-04-03 21:24:00 +00:00
committed by GitHub
co-authored by Geoff Romer
parent fed9e8c878
commit ea409f7cbf
5 changed files with 668 additions and 150 deletions
+250 -135
View File
@@ -247,13 +247,17 @@ auto FileContext::GetConstant(SemIR::ConstantId const_id,
return global_variable;
}
auto FileContext::GetOrCreateFunctionInfo(SemIR::FunctionId function_id,
SemIR::SpecificId specific_id)
-> std::optional<FunctionInfo>& {
auto FileContext::GetOrCreateFunctionInfo(
SemIR::FunctionId function_id, SemIR::SpecificId specific_id,
FileContext* fallback_file, SemIR::FunctionId fallback_function_id,
SemIR::SpecificId fallback_specific_id) -> std::optional<FunctionInfo>& {
// If we have already lowered a declaration of this function, just return it.
// TODO: If the existing declaration is inexact, and we now have a fallback,
// we should try again.
auto& result = GetFunctionInfo(function_id, specific_id);
if (!result) {
result = BuildFunctionDecl(function_id, specific_id);
result = BuildFunctionDecl(function_id, specific_id, fallback_file,
fallback_function_id, fallback_specific_id);
}
return result;
}
@@ -276,57 +280,103 @@ auto FileContext::GetOrCreateFunctionInfo(SemIR::FunctionId function_id,
// Furthermore, SemIR is designed to eventually support compound return forms,
// in which case there can be multiple output parameters for different pieces of
// the return form, but it's not yet clear how we will lower such functions.
//
// We also deal with the case where the function signature involves incomplete
// types. This can happen if the function is declared but never defined nor
// called in this file. Declarations of such functions can still need to be
// emitted; currently this happens if they are part of a class's vtable. Such
// uses do not need an exact signature, so we emit them with the LLVM type
// `void()` and set `inexact` on the result to indicate the type is not known.
// LLVM can handle merging inexact and exact signatures, and this matches how
// Clang handles the corresponding situation in C++.
//
// One additional complexity is that we may need to fetch information about the
// same function from multiple different files. For a call to a generic
// function, there may be no single file in which all the relevant types are
// complete, so we will look at both the specific function definition that is
// the resolved callee, as well as the partially-specific function from the call
// site.
//
// In general, we support being given a list of variants of the function, in
// which the first function in the list is the primary declaration and should be
// the most specific function, and the others are used as fallbacks if an
// incomplete type is encountered.
class FileContext::FunctionTypeInfoBuilder {
public:
// Creates a FunctionTypeInfoBuilder that uses the given FileContext, and
// the given specific of the function.
FunctionTypeInfoBuilder(FileContext* context, SemIR::SpecificId specific_id)
: context_(*context), specific_id_(specific_id) {}
struct FunctionInContext {
FileContext* context;
SemIR::FunctionId function_id;
SemIR::SpecificId specific_id;
};
// Retrieves various features of `function`'s type useful for constructing the
// `llvm::Type` and `llvm::DISubroutineType` for the `llvm::Function`. If any
// part of the type can't be manifest (eg: incomplete return or parameter
// types), then the result is as if the type was `void()`. Should only be
// called once on a given builder.
auto Build(const SemIR::Function& function) && -> FunctionTypeInfo;
private:
// By convention, state transition methods return false to indicate that
// `Abort` was called. As a convenience, that applies even to methods that
// never call `Abort`, and to `Abort` itself, so that their callers can easily
// propagate the failure.
// Resets the builder to the fallback state `void()`. This puts the builder in
// a state where Finalize can be called, and no other operation should be
// called.
auto Abort() -> bool {
call_param_pattern_ids_ = {};
lowered_param_indices_.clear();
unused_param_indices_.clear();
param_name_ids_.clear();
param_types_.clear();
param_di_types_.clear();
return_type_ = nullptr;
inexact_ = true;
SetReturnByCopy(SemIR::TypeId::None);
return false;
// Creates a FunctionTypeInfoBuilder that uses the given functions.
explicit FunctionTypeInfoBuilder(llvm::ArrayRef<FunctionInContext> functions)
: context_(&functions.front().context->context()), functions_(functions) {
CARBON_CHECK(!functions_.empty());
}
// Handles the function's return form. The argument can be None, indicating
// that there was no explicitly declared return form.
// Retrieves various features of the function's type useful for constructing
// the `llvm::Type` and `llvm::DISubroutineType` for the `llvm::Function`. If
// any part of the type can't be manifest (eg: incomplete return or parameter
// types), then the result is as if the type was `void()`. Should only be
// called once on a given builder.
auto Build() && -> FunctionTypeInfo;
private:
// By convention, state transition methods return false (without changing the
// accumulated information about the function) to indicate that we could not
// manifest the complete function type successfully in this context.
// Information about how a function is called in SemIR.
struct SemIRIndexInfo {
// The number of parameters in the SemIR call signature.
int num_params;
// The index of the first return parameter in the SemIR call signature.
int return_param_index;
friend auto operator==(const SemIRIndexInfo& lhs, const SemIRIndexInfo& rhs)
-> bool = default;
};
// Get information about the SemIR function signature.
auto GetSemIRIndexInfo(const FunctionInContext& fn_in_context)
-> SemIRIndexInfo {
const auto& sem_ir = fn_in_context.context->sem_ir();
const auto& function = sem_ir.functions().Get(fn_in_context.function_id);
int num_params =
sem_ir.inst_blocks().Get(function.call_param_patterns_id).size();
int return_param_index = -1;
if (function.call_param_ranges.return_size() > 0) {
CARBON_CHECK(function.call_param_ranges.return_size() == 1,
"TODO: support multiple return forms");
return_param_index = function.call_param_ranges.return_begin().index;
}
return {.num_params = num_params, .return_param_index = return_param_index};
}
// Handles the function's return form.
//
// This should be called before HandleParameter. It delegates to exactly one
// of SetReturnByCopy, SetReturnByReference, SetReturnInPlace, or Abort, and
// returns false if Abort was called.
auto HandleReturnForm(SemIR::InstId return_form_inst_id) -> bool;
// This should be called before `HandleParameter`. It handles the return form
// by trying each `FunctionInContext` until one succeeds, and returns false if
// all attempts failed.
auto HandleReturnForm() -> bool;
// Tries to handle the return form using the given context. Delegates to
// exactly one of `SetReturnByCopy`, `SetReturnByReference`, or
// `SetReturnInPlace`, or returns false if the return type is incomplete.
auto TryHandleReturnForm(const FunctionInContext& func_ctx) -> bool;
// Records that the LLVM function returns by copy, with type `return_type_id`.
// `return_type_id` can be `None`, which is treated as equivalent to the
// default return type `()`.
auto SetReturnByCopy(SemIR::TypeId return_type_id) -> bool {
auto SetReturnByCopy(const FunctionInContext& func_ctx,
SemIR::TypeId return_type_id) -> bool {
CARBON_CHECK(return_type_ == nullptr);
CARBON_CHECK(param_di_types_.empty());
auto lowered_return_types = GetLoweredTypes(return_type_id);
auto lowered_return_types = GetLoweredTypes(func_ctx, return_type_id);
return_type_ = lowered_return_types.llvm_ir_type;
param_di_types_.push_back(lowered_return_types.llvm_di_type);
return true;
@@ -334,9 +384,10 @@ class FileContext::FunctionTypeInfoBuilder {
// Records that the LLVM function returns by reference, with type
// `return_type_id`.
auto SetReturnByReference(SemIR::TypeId /*return_type_id*/) -> bool {
return_type_ =
llvm::PointerType::get(context_.llvm_context(), /*AddressSpace=*/0);
auto SetReturnByReference(const FunctionInContext& func_ctx,
SemIR::TypeId /*return_type_id*/) -> bool {
return_type_ = llvm::PointerType::get(func_ctx.context->llvm_context(),
/*AddressSpace=*/0);
// TODO: replace this with a reference type.
param_di_types_.push_back(GetPointerDIType(nullptr));
return true;
@@ -344,9 +395,10 @@ class FileContext::FunctionTypeInfoBuilder {
// Records that the LLVM function returns in place, with type
// `return_type_id`.
auto SetReturnInPlace(SemIR::TypeId return_type_id) -> bool {
return_type_ = llvm::Type::getVoidTy(context_.llvm_context());
sret_type_ = context_.GetType(return_type_id);
auto SetReturnInPlace(const FunctionInContext& func_ctx,
SemIR::TypeId return_type_id) -> bool {
return_type_ = llvm::Type::getVoidTy(func_ctx.context->llvm_context());
sret_type_ = func_ctx.context->GetType(return_type_id);
// We don't add to param_di_types_ because that will be handled by the
// loop over the SemIR parameters.
return true;
@@ -359,18 +411,25 @@ class FileContext::FunctionTypeInfoBuilder {
// parameters; it will determine which parameters belong in the LLVM IR
// parameter list.
//
// This delegates to exactly one of AddLoweredParam, IgnoreParam, or Abort,
// and returns false if Abort was called.
// This tries each `FunctionInContext` until one succeeds, and returns false
// if all attempts failed.
auto HandleParameter(SemIR::CallParamIndex index) -> bool;
// Records that the parameter pattern at the given index jas the given ID, and
// Tries to handle the parameter pattern at the given index using the given
// context. Delegates to either `AddLoweredParam` or `IgnoreParam`, or returns
// false if the parameter type is incomplete.
auto TryHandleParameter(const FunctionInContext& func_ctx,
SemIR::CallParamIndex index) -> bool;
// Records that the parameter pattern at the given index has the given ID, and
// lowers to the given IR and DI types.
auto AddLoweredParam(SemIR::CallParamIndex index,
auto AddLoweredParam(const FunctionInContext& func_ctx,
SemIR::CallParamIndex index,
SemIR::InstId param_pattern_id, LoweredTypes param_types)
-> bool {
lowered_param_indices_.push_back(index);
param_name_ids_.push_back(
SemIR::GetPrettyNameFromPatternId(context_.sem_ir(), param_pattern_id));
param_name_ids_.push_back(SemIR::GetPrettyNameFromPatternId(
func_ctx.context->sem_ir(), param_pattern_id));
param_types_.push_back(param_types.llvm_ir_type);
param_di_types_.push_back(param_types.llvm_di_type);
return true;
@@ -386,26 +445,32 @@ class FileContext::FunctionTypeInfoBuilder {
// Builds and returns a FunctionTypeInfo from the accumulated information.
auto Finalize() -> FunctionTypeInfo;
// Clears out accumulated state and returns a FunctionTypeInfo with the
// fallback state `void()`.
auto Abort() -> FunctionTypeInfo;
// Returns LLVM IR and DI types for the given SemIR type. This is not a state
// transition. It mostly delegates to context_.GetTypeAndDIType, but treats
// TypeId::None as equivalent to the unit type, and uses an untyped pointer as
// a placeholder DI type if context_ doesn't provide one.
auto GetLoweredTypes(SemIR::TypeId type_id) -> LoweredTypes;
auto GetLoweredTypes(const FunctionInContext& func_ctx, SemIR::TypeId type_id)
-> LoweredTypes;
// Returns a DI type for a pointer to the given pointee. The pointee type may
// be null.
auto GetPointerDIType(llvm::DIType* pointee_type, unsigned address_space = 0)
-> llvm::DIDerivedType* {
const auto& data_layout = context_.llvm_module().getDataLayout();
return context_.context().di_builder().createPointerType(
const auto& data_layout = context_->llvm_module().getDataLayout();
return context_->di_builder().createPointerType(
pointee_type, data_layout.getPointerSizeInBits(address_space));
}
FileContext& context_;
const SemIR::SpecificId specific_id_;
Context* context_;
// The input `Call` parameter patterns.
llvm::ArrayRef<SemIR::InstId> call_param_pattern_ids_;
llvm::ArrayRef<FunctionInContext> functions_;
// The number of input `Call` parameter patterns.
int num_params_ = 0;
// The types of the parameters in the LLVM IR function. Each one corresponds
// to a SemIR `Call` parameter, but some `Call` parameters may be omitted
@@ -433,11 +498,6 @@ class FileContext::FunctionTypeInfoBuilder {
// lowered_param_indices_.
llvm::SmallVector<SemIR::CallParamIndex> unused_param_indices_;
// The `index` member of the SemIR function's return parameter, or -1 if it
// has no return parameter. Note that even if the SemIR function has a return
// parameter, the LLVM IR function might not.
int semir_return_param_index_ = -1;
// The LLVM function's return type.
llvm::Type* return_type_ = nullptr;
@@ -452,110 +512,127 @@ class FileContext::FunctionTypeInfoBuilder {
bool inexact_ = false;
};
auto FileContext::FunctionTypeInfoBuilder::Build(
const SemIR::Function& function) && -> FunctionTypeInfo {
auto FileContext::FunctionTypeInfoBuilder::Build() && -> FunctionTypeInfo {
// TODO: For the `Run` entry point, remap return type to i32 if it doesn't
// return a value.
call_param_pattern_ids_ =
context_.sem_ir().inst_blocks().Get(function.call_param_patterns_id);
lowered_param_indices_.reserve(call_param_pattern_ids_.size());
param_name_ids_.reserve(call_param_pattern_ids_.size());
param_types_.reserve(call_param_pattern_ids_.size());
param_di_types_.reserve(call_param_pattern_ids_.size());
// Determine how the parameters are numbered in SemIR, and make sure it's the
// same for all versions of the function.
auto semir_info = GetSemIRIndexInfo(functions_.front());
CARBON_CHECK(
llvm::all_of(functions_.drop_front(), [&](const auto& fn_in_context) {
return GetSemIRIndexInfo(fn_in_context) == semir_info;
}));
if (function.call_param_ranges.return_size() > 0) {
CARBON_CHECK(function.call_param_ranges.return_size() == 1,
"TODO: support multiple return forms");
semir_return_param_index_ = function.call_param_ranges.return_begin().index;
num_params_ = semir_info.num_params;
lowered_param_indices_.reserve(num_params_);
param_name_ids_.reserve(num_params_);
param_types_.reserve(num_params_);
param_di_types_.reserve(num_params_);
if (!HandleReturnForm()) {
return Abort();
}
if (!HandleReturnForm(function.return_form_inst_id)) {
return Finalize();
}
int params_end = call_param_pattern_ids_.size();
if (semir_return_param_index_ >= 0) {
CARBON_CHECK(semir_return_param_index_ ==
static_cast<int>(call_param_pattern_ids_.size()) - 1,
int params_end = num_params_;
if (semir_info.return_param_index >= 0) {
CARBON_CHECK(semir_info.return_param_index == semir_info.num_params - 1,
"Unexpected parameter order");
params_end = semir_return_param_index_;
params_end = semir_info.return_param_index;
// Handle the return parameter first, because it goes first in the LLVM
// convention.
if (!HandleParameter(SemIR::CallParamIndex(semir_return_param_index_))) {
return Finalize();
if (!HandleParameter(
SemIR::CallParamIndex(semir_info.return_param_index))) {
return Abort();
}
}
for (int i : llvm::seq(params_end)) {
if (!HandleParameter(SemIR::CallParamIndex(i))) {
return Finalize();
return Abort();
}
}
return Finalize();
}
auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm(
SemIR::InstId return_form_inst_id) -> bool {
auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm() -> bool {
for (const auto& func_ctx : functions_) {
if (TryHandleReturnForm(func_ctx)) {
return true;
}
}
return false;
}
auto FileContext::FunctionTypeInfoBuilder::TryHandleReturnForm(
const FunctionInContext& func_ctx) -> bool {
const auto& function =
func_ctx.context->sem_ir().functions().Get(func_ctx.function_id);
auto return_form_inst_id = function.return_form_inst_id;
if (!return_form_inst_id.has_value()) {
return SetReturnByCopy(SemIR::TypeId::None);
return SetReturnByCopy(func_ctx, SemIR::TypeId::None);
}
auto return_form_const_id = SemIR::GetConstantValueInSpecific(
context_.sem_ir(), specific_id_, return_form_inst_id);
auto return_form_inst = context_.sem_ir().insts().Get(
context_.sem_ir().constant_values().GetInstId(return_form_const_id));
func_ctx.context->sem_ir(), func_ctx.specific_id, return_form_inst_id);
auto return_form_inst = func_ctx.context->sem_ir().insts().Get(
func_ctx.context->sem_ir().constant_values().GetInstId(
return_form_const_id));
CARBON_KIND_SWITCH(return_form_inst) {
case CARBON_KIND(SemIR::InitForm init_form): {
auto return_type_id =
context_.sem_ir().types().GetTypeIdForTypeConstantId(
func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId(
SemIR::GetConstantValueInSpecific(
context_.sem_ir(), specific_id_,
func_ctx.context->sem_ir(), func_ctx.specific_id,
init_form.type_component_inst_id));
switch (
SemIR::InitRepr::ForType(context_.sem_ir(), return_type_id).kind) {
SemIR::InitRepr::ForType(func_ctx.context->sem_ir(), return_type_id)
.kind) {
case SemIR::InitRepr::InPlace: {
return SetReturnInPlace(return_type_id);
return SetReturnInPlace(func_ctx, return_type_id);
}
case SemIR::InitRepr::ByCopy: {
return SetReturnByCopy(return_type_id);
return SetReturnByCopy(func_ctx, return_type_id);
}
case SemIR::InitRepr::None:
return SetReturnByCopy(SemIR::TypeId::None);
return SetReturnByCopy(func_ctx, SemIR::TypeId::None);
case SemIR::InitRepr::Dependent:
CARBON_FATAL("Lowering function return with dependent type: {0}",
return_form_inst);
case SemIR::InitRepr::Incomplete:
case SemIR::InitRepr::Abstract:
return Abort();
return false;
}
}
case CARBON_KIND(SemIR::RefForm ref_form): {
auto return_type_id =
context_.sem_ir().types().GetTypeIdForTypeConstantId(
func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId(
SemIR::GetConstantValueInSpecific(
context_.sem_ir(), specific_id_,
func_ctx.context->sem_ir(), func_ctx.specific_id,
ref_form.type_component_inst_id));
return SetReturnByReference(return_type_id);
return SetReturnByReference(func_ctx, return_type_id);
}
case CARBON_KIND(SemIR::ValueForm val_form): {
auto return_type_id =
context_.sem_ir().types().GetTypeIdForTypeConstantId(
func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId(
SemIR::GetConstantValueInSpecific(
context_.sem_ir(), specific_id_,
func_ctx.context->sem_ir(), func_ctx.specific_id,
val_form.type_component_inst_id));
switch (
SemIR::ValueRepr::ForType(context_.sem_ir(), return_type_id).kind) {
SemIR::ValueRepr::ForType(func_ctx.context->sem_ir(), return_type_id)
.kind) {
case SemIR::ValueRepr::Unknown:
return Abort();
return false;
case SemIR::ValueRepr::Dependent:
CARBON_FATAL("Lowering function return with dependent type: {0}",
return_form_inst);
case SemIR::ValueRepr::None:
return SetReturnByCopy(SemIR::TypeId::None);
return SetReturnByCopy(func_ctx, SemIR::TypeId::None);
case SemIR::ValueRepr::Copy:
return SetReturnByCopy(return_type_id);
return SetReturnByCopy(func_ctx, return_type_id);
case SemIR::ValueRepr::Pointer:
case SemIR::ValueRepr::Custom:
return SetReturnByReference(return_type_id);
return SetReturnByReference(func_ctx, return_type_id);
}
}
default:
@@ -565,19 +642,33 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm(
auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
SemIR::CallParamIndex index) -> bool {
const auto& sem_ir = context_.sem_ir();
auto param_pattern_id = call_param_pattern_ids_[index.index];
for (const auto& func_ctx : functions_) {
if (TryHandleParameter(func_ctx, index)) {
return true;
}
}
return false;
}
auto FileContext::FunctionTypeInfoBuilder::TryHandleParameter(
const FunctionInContext& func_ctx, SemIR::CallParamIndex index) -> bool {
const auto& sem_ir = func_ctx.context->sem_ir();
auto param_pattern_id =
sem_ir.inst_blocks().Get(sem_ir.functions()
.Get(func_ctx.function_id)
.call_param_patterns_id)[index.index];
auto param_pattern = sem_ir.insts().Get(param_pattern_id);
auto param_type_id = ExtractScrutineeType(
sem_ir,
SemIR::GetTypeOfInstInSpecific(sem_ir, specific_id_, param_pattern_id));
sem_ir, SemIR::GetTypeOfInstInSpecific(sem_ir, func_ctx.specific_id,
param_pattern_id));
// Returns the appropriate LoweredTypes for reference-like parameters.
auto ref_lowered_types = [&]() -> LoweredTypes {
return {.llvm_ir_type = llvm::PointerType::get(context_.llvm_context(),
/*AddressSpace=*/0),
// TODO: replace this with a reference type.
.llvm_di_type = GetLoweredTypes(param_type_id).llvm_di_type};
return {
.llvm_ir_type = llvm::PointerType::get(func_ctx.context->llvm_context(),
/*AddressSpace=*/0),
// TODO: replace this with a reference type.
.llvm_di_type = GetLoweredTypes(func_ctx, param_type_id).llvm_di_type};
};
CARBON_CHECK(
@@ -613,12 +704,14 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
switch (param_kind) {
case SemIR::RefParamPattern::Kind:
case SemIR::VarParamPattern::Kind: {
return AddLoweredParam(index, param_pattern_id, ref_lowered_types());
return AddLoweredParam(func_ctx, index, param_pattern_id,
ref_lowered_types());
}
case SemIR::OutParamPattern::Kind: {
switch (SemIR::InitRepr::ForType(sem_ir, param_type_id).kind) {
case SemIR::InitRepr::InPlace:
return AddLoweredParam(index, param_pattern_id, ref_lowered_types());
return AddLoweredParam(func_ctx, index, param_pattern_id,
ref_lowered_types());
case SemIR::InitRepr::ByCopy:
case SemIR::InitRepr::None:
return IgnoreParam(index);
@@ -627,14 +720,14 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
param_pattern);
case SemIR::InitRepr::Incomplete:
case SemIR::InitRepr::Abstract:
return Abort();
return false;
}
}
case SemIR::ValueParamPattern::Kind: {
switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir, param_type_id);
value_rep.kind) {
case SemIR::ValueRepr::Unknown:
return Abort();
return false;
case SemIR::ValueRepr::Dependent:
CARBON_FATAL("Lowering function parameter with dependent type: {0}",
param_pattern);
@@ -644,8 +737,9 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
case SemIR::ValueRepr::Custom:
case SemIR::ValueRepr::Pointer: {
if (value_rep.type_id.has_value()) {
return AddLoweredParam(index, param_pattern_id,
GetLoweredTypes(value_rep.type_id));
return AddLoweredParam(
func_ctx, index, param_pattern_id,
GetLoweredTypes(func_ctx, value_rep.type_id));
} else {
return IgnoreParam(index);
}
@@ -659,9 +753,9 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter(
auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo {
CARBON_CHECK(lowered_param_indices_.size() + unused_param_indices_.size() ==
call_param_pattern_ids_.size());
static_cast<size_t>(num_params_));
CARBON_CHECK(!param_di_types_.empty());
auto& di_builder = context_.context().di_builder();
auto& di_builder = context_->di_builder();
return {.type = llvm::FunctionType::get(return_type_, param_types_,
/*isVarArg=*/false),
.di_type = di_builder.createSubroutineType(
@@ -674,13 +768,27 @@ auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo {
.inexact = inexact_};
}
auto FileContext::FunctionTypeInfoBuilder::Abort() -> FunctionTypeInfo {
num_params_ = 0;
lowered_param_indices_.clear();
unused_param_indices_.clear();
param_name_ids_.clear();
param_types_.clear();
param_di_types_.clear();
return_type_ = llvm::Type::getVoidTy(context_->llvm_context());
param_di_types_.push_back(nullptr);
inexact_ = true;
return Finalize();
}
auto FileContext::FunctionTypeInfoBuilder::GetLoweredTypes(
SemIR::TypeId type_id) -> LoweredTypes {
const FunctionInContext& func_ctx, SemIR::TypeId type_id) -> LoweredTypes {
if (!type_id.has_value()) {
return {.llvm_ir_type = llvm::Type::getVoidTy(context_.llvm_context()),
.llvm_di_type = nullptr};
return {
.llvm_ir_type = llvm::Type::getVoidTy(func_ctx.context->llvm_context()),
.llvm_di_type = nullptr};
}
auto result = context_.GetTypeAndDIType(type_id);
auto result = func_ctx.context->GetTypeAndDIType(type_id);
if (result.llvm_di_type == nullptr) {
// TODO: figure out what type should go here, or ensure this doesn't
// happen.
@@ -792,7 +900,10 @@ auto FileContext::GetOrCreateLLVMFunction(
}
auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
SemIR::SpecificId specific_id)
SemIR::SpecificId specific_id,
FileContext* fallback_file,
SemIR::FunctionId fallback_function_id,
SemIR::SpecificId fallback_specific_id)
-> std::optional<FunctionInfo> {
const auto& function = sem_ir().functions().Get(function_id);
@@ -818,8 +929,12 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
// TODO: Consider tracking whether the function has been used, and only
// lowering it if it's needed.
FunctionTypeInfoBuilder::FunctionInContext func_infos[] = {
{this, function_id, specific_id},
{fallback_file, fallback_function_id, fallback_specific_id}};
auto function_type_info =
FunctionTypeInfoBuilder(this, specific_id).Build(function);
FunctionTypeInfoBuilder(llvm::ArrayRef(func_infos, fallback_file ? 2 : 1))
.Build();
auto* llvm_function =
GetOrCreateLLVMFunction(function_type_info, function_id, specific_id);