mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-15 09:48:32 +00:00
We allow using `InstId` as a `LocId` for diagnostics, which works great in `handle_*` functions, where we are working with non-canonical insts. And convert and pattern matching often are given non-canonical insts, which they can use, or pull apart to find some other inst, and use those as a location for diagnostics. However these operations can also be called with canonical insts, which have no location. In that case if we need to diagnose an error, it ends up not being attached to any code at all. Since we always have a `LocId` somewhere up the call stack, pass it along and in these places that handle canonical instructions too, use `GetInstLocationOrFallback()` to get a valid location for diagnostics. This is also similar to what eval does with its fallback `LocId`. The result is that many diagnostics now have a location where they had none before. It also means some insts don't get `<elided>` in formatted semir for not having a location at all and thus not being in range.
570 lines
25 KiB
C++
570 lines
25 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/check/thunk.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
#include "toolchain/check/call.h"
|
|
#include "toolchain/check/convert.h"
|
|
#include "toolchain/check/cpp/operators.h"
|
|
#include "toolchain/check/deferred_definition_worklist.h"
|
|
#include "toolchain/check/diagnostic_helpers.h"
|
|
#include "toolchain/check/function.h"
|
|
#include "toolchain/check/generic.h"
|
|
#include "toolchain/check/inst.h"
|
|
#include "toolchain/check/member_access.h"
|
|
#include "toolchain/check/operator.h"
|
|
#include "toolchain/check/pattern.h"
|
|
#include "toolchain/check/pattern_match.h"
|
|
#include "toolchain/check/pointer_dereference.h"
|
|
#include "toolchain/check/return.h"
|
|
#include "toolchain/check/type.h"
|
|
#include "toolchain/diagnostics/diagnostic.h"
|
|
#include "toolchain/sem_ir/function.h"
|
|
#include "toolchain/sem_ir/generic.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/inst.h"
|
|
#include "toolchain/sem_ir/pattern.h"
|
|
#include "toolchain/sem_ir/thunk.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// Adds and returns a new InstId that's equivalent to `inst_id`, but with
|
|
// constant values substituted from `specific_id`. `inst_id` must not depend
|
|
// on a binding pattern.
|
|
//
|
|
// TODO: Remove that restriction, and get rid of `ClonePattern`. The major
|
|
// open question is when and how to create the binding inst that corresponds
|
|
// to a binding pattern cloned via this function. `ScopeStack`, and specifically
|
|
// the compile-time binding stack, seems to require `SymbolicBinding`s to be
|
|
// created at the same time as the corresponding `SymbolicBindingPattern`s, but
|
|
// that would require this function to traverse through the dependencies of
|
|
// `inst_id` looking for binding patterns in order to eagerly create the
|
|
// corresponding bindings, which would defeat much of the purpose of this
|
|
// "shallow" approach to cloning.
|
|
static auto CloneInstId(Context& context, SemIR::SpecificId specific_id,
|
|
SemIR::InstId inst_id) -> SemIR::InstId {
|
|
if (!inst_id.has_value()) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
|
|
return GetOrAddInst<SemIR::SpecificConstant>(
|
|
context, SemIR::LocId(inst_id),
|
|
{.type_id = SemIR::GetTypeOfInstInSpecific(context.sem_ir(), specific_id,
|
|
inst_id),
|
|
.inst_id = inst_id,
|
|
.specific_id = specific_id});
|
|
}
|
|
|
|
static auto CloneTypeInstId(Context& context, SemIR::SpecificId specific_id,
|
|
SemIR::TypeInstId inst_id) -> SemIR::TypeInstId {
|
|
if (!inst_id.has_value()) {
|
|
return SemIR::TypeInstId::None;
|
|
}
|
|
|
|
return context.types().GetAsTypeInstId(
|
|
CloneInstId(context, specific_id, inst_id));
|
|
}
|
|
|
|
// Adds a pattern instruction for a thunk, copying the location from an existing
|
|
// instruction.
|
|
static auto RebuildPatternInst(Context& context, SemIR::InstId orig_inst_id,
|
|
SemIR::Inst new_inst) -> SemIR::InstId {
|
|
// Ensure we built the same kind of instruction. In particular, this ensures
|
|
// that the location of the old instruction can be reused for the new one.
|
|
CARBON_CHECK(context.insts().Get(orig_inst_id).kind() == new_inst.kind(),
|
|
"Rebuilt pattern with the wrong kind: {0} -> {1}",
|
|
context.insts().Get(orig_inst_id), new_inst);
|
|
return AddInst(context,
|
|
SemIR::LocIdAndInst::RuntimeVerified(
|
|
context.sem_ir(), SemIR::LocId(orig_inst_id), new_inst));
|
|
}
|
|
|
|
// Wrapper to allow the type to be specified as a template argument for API
|
|
// consistency with `AddInst`.
|
|
template <typename InstT>
|
|
static auto RebuildPatternInst(Context& context, SemIR::InstId orig_inst_id,
|
|
InstT new_inst) -> SemIR::InstId {
|
|
return RebuildPatternInst(context, orig_inst_id, SemIR::Inst(new_inst));
|
|
}
|
|
|
|
// Makes a copy of the given binding pattern, with its type adjusted to be
|
|
// `new_pattern_type_id`.
|
|
static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id,
|
|
SemIR::AnyBindingPattern pattern,
|
|
SemIR::TypeId new_pattern_type_id,
|
|
SemIR::SpecificId specific_id)
|
|
-> SemIR::InstId {
|
|
auto entity_name = context.entity_names().Get(pattern.entity_name_id);
|
|
CARBON_CHECK((pattern.kind == SemIR::SymbolicBindingPattern::Kind) ==
|
|
entity_name.bind_index().has_value());
|
|
// Get the transformed type of the binding.
|
|
if (new_pattern_type_id == SemIR::ErrorInst::TypeId) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
pattern.type_id = new_pattern_type_id;
|
|
auto phase = BindingPhase::Runtime;
|
|
if (pattern.kind == SemIR::SymbolicBindingPattern::Kind) {
|
|
phase = entity_name.is_template ? BindingPhase::Template
|
|
: BindingPhase::Symbolic;
|
|
}
|
|
// The type of the clone is the result of substitution, so the type as
|
|
// written in the original declaration no longer describes it.
|
|
pattern.entity_name_id = AddBindingEntityName(
|
|
context, entity_name.name_id,
|
|
/*type_inst_id=*/SemIR::TypeInstId::None,
|
|
/*form_id=*/SemIR::InstId::None, entity_name.is_unused, phase);
|
|
if (pattern.kind == SemIR::WrapperBindingPattern::Kind) {
|
|
// Now that we're inside the binding pattern, we can use CloneInstId.
|
|
pattern.subpattern_id =
|
|
CloneInstId(context, specific_id, pattern.subpattern_id);
|
|
}
|
|
// Rebuild the binding pattern.
|
|
return AddBindingPattern(
|
|
context, SemIR::LocId(pattern_id), SemIR::ExprRegionId::None,
|
|
SemIR::ExtractScrutineeType(context.sem_ir(), new_pattern_type_id),
|
|
pattern)
|
|
.pattern_id;
|
|
}
|
|
|
|
// Makes a copy of the given pattern instruction, substituting values from a
|
|
// specific as needed. The resulting pattern behaves like a newly-created
|
|
// pattern, so is suitable for running `CalleePatternMatch` against.
|
|
static auto ClonePattern(Context& context, SemIR::SpecificId specific_id,
|
|
SemIR::InstId pattern_id) -> SemIR::InstId {
|
|
if (!pattern_id.has_value()) {
|
|
return SemIR::InstId::None;
|
|
}
|
|
|
|
auto get_type = [&](SemIR::InstId inst_id) -> SemIR::TypeId {
|
|
return SemIR::GetTypeOfInstInSpecific(context.sem_ir(), specific_id,
|
|
inst_id);
|
|
};
|
|
|
|
auto pattern_const_id = SemIR::GetConstantValueInSpecific(
|
|
context.sem_ir(), specific_id, pattern_id);
|
|
pattern_id = context.constant_values().GetInstId(pattern_const_id);
|
|
auto pattern = context.insts().Get(pattern_id);
|
|
|
|
// Decompose the pattern. The forms we allow for patterns in a function
|
|
// parameter list are currently fairly restrictive.
|
|
// Optional var parameter pattern.
|
|
auto [var_param, var_param_id] = context.insts().TryUnwrap(
|
|
pattern, pattern_id, &SemIR::VarParamPattern::subpattern_id);
|
|
|
|
// Finally, either a binding pattern or a return slot pattern.
|
|
auto new_pattern_id = SemIR::InstId::None;
|
|
if (auto binding = pattern.TryAs<SemIR::AnyBindingPattern>()) {
|
|
auto type_id = get_type(pattern_id);
|
|
new_pattern_id = CloneBindingPattern(context, pattern_id, *binding, type_id,
|
|
specific_id);
|
|
} else if (auto return_slot = pattern.TryAs<SemIR::ReturnSlotPattern>()) {
|
|
new_pattern_id = CloneInstId(context, specific_id, pattern_id);
|
|
} else {
|
|
CARBON_CHECK(pattern.Is<SemIR::ErrorInst>(),
|
|
"Unexpected pattern {0} in function signature", pattern);
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Rebuild parameter.
|
|
if (var_param && new_pattern_id != SemIR::ErrorInst::InstId) {
|
|
new_pattern_id = RebuildPatternInst<SemIR::VarParamPattern>(
|
|
context, var_param_id,
|
|
{.type_id = context.insts().Get(new_pattern_id).type_id(),
|
|
.subpattern_id = new_pattern_id});
|
|
}
|
|
|
|
return new_pattern_id;
|
|
}
|
|
|
|
static auto ClonePatternBlock(Context& context, SemIR::SpecificId specific_id,
|
|
SemIR::InstBlockId inst_block_id)
|
|
-> SemIR::InstBlockId {
|
|
if (!inst_block_id.has_value()) {
|
|
return SemIR::InstBlockId::None;
|
|
}
|
|
return context.inst_blocks().Transform(
|
|
inst_block_id, [&](SemIR::InstId inst_id) {
|
|
return ClonePattern(context, specific_id, inst_id);
|
|
});
|
|
}
|
|
|
|
static auto CloneFunctionDecl(Context& context, SemIR::LocId loc_id,
|
|
SemIR::FunctionId signature_id,
|
|
SemIR::SpecificId signature_specific_id,
|
|
SemIR::FunctionId callee_id)
|
|
-> std::pair<SemIR::FunctionId, SemIR::InstId> {
|
|
StartGenericDecl(context);
|
|
|
|
const auto& signature = context.functions().Get(signature_id);
|
|
|
|
// Clone the signature.
|
|
context.pattern_block_stack().Push();
|
|
auto implicit_param_patterns_id = ClonePatternBlock(
|
|
context, signature_specific_id, signature.implicit_param_patterns_id);
|
|
auto param_patterns_id = ClonePatternBlock(context, signature_specific_id,
|
|
signature.param_patterns_id);
|
|
auto return_pattern_id =
|
|
ClonePattern(context, signature_specific_id, signature.return_pattern_id);
|
|
auto return_type_inst_id = CloneTypeInstId(context, signature_specific_id,
|
|
signature.return_type_inst_id);
|
|
auto return_form_inst_id = CloneInstId(context, signature_specific_id,
|
|
signature.return_form_inst_id);
|
|
auto self_param_id =
|
|
FindSelfPattern(context, implicit_param_patterns_id, param_patterns_id);
|
|
|
|
// Perform callee-side pattern matching to rebuild the parameter list.
|
|
context.inst_block_stack().Push();
|
|
auto match_results =
|
|
CalleePatternMatch(context, loc_id, implicit_param_patterns_id,
|
|
param_patterns_id, return_pattern_id);
|
|
auto pattern_block_id = context.pattern_block_stack().Pop();
|
|
auto decl_block_id = context.inst_block_stack().Pop();
|
|
|
|
// Create the `FunctionDecl` instruction.
|
|
auto& callee = context.functions().Get(callee_id);
|
|
auto [decl_id, function_id] = MakeFunctionDecl(
|
|
context, loc_id, decl_block_id, /*build_generic=*/true,
|
|
/*is_definition=*/true,
|
|
SemIR::Function{
|
|
{
|
|
.name_id = signature.name_id,
|
|
.parent_scope_id = callee.parent_scope_id,
|
|
// Set by `MakeFunctionDecl`.
|
|
.generic_id = SemIR::GenericId::None,
|
|
.first_param_node_id = signature.first_param_node_id,
|
|
.last_param_node_id = signature.last_param_node_id,
|
|
.pattern_block_id = pattern_block_id,
|
|
.implicit_param_patterns_id = implicit_param_patterns_id,
|
|
.param_patterns_id = param_patterns_id,
|
|
.is_extern = false,
|
|
.extern_library_id = SemIR::LibraryNameId::None,
|
|
.non_owning_decl_id = SemIR::InstId::None,
|
|
// Set by `MakeFunctionDecl`.
|
|
.first_owning_decl_id = SemIR::InstId::None,
|
|
},
|
|
{
|
|
.call_param_patterns_id = match_results.call_param_patterns_id,
|
|
.call_params_id = match_results.call_params_id,
|
|
.call_param_ranges = match_results.param_ranges,
|
|
.return_type_inst_id = return_type_inst_id,
|
|
.return_form_inst_id = return_form_inst_id,
|
|
.return_pattern_id = return_pattern_id,
|
|
.virtual_modifier = callee.virtual_modifier,
|
|
.virtual_index = callee.virtual_index,
|
|
.evaluation_mode = signature.evaluation_mode,
|
|
.self_param_id = self_param_id,
|
|
}});
|
|
context.inst_block_stack().AddInstId(decl_id);
|
|
return {function_id, decl_id};
|
|
}
|
|
|
|
static auto HasDeclaredReturnType(Context& context,
|
|
SemIR::FunctionId function_id) -> bool {
|
|
return context.functions().Get(function_id).return_type_inst_id.has_value();
|
|
}
|
|
|
|
auto PerformThunkCall(Context& context, SemIR::LocId loc_id,
|
|
SemIR::FunctionId function_id,
|
|
llvm::ArrayRef<SemIR::InstId> param_pattern_ids,
|
|
llvm::ArrayRef<SemIR::InstId> call_arg_ids,
|
|
SemIR::InstId callee_id,
|
|
SemIR::TypeId override_self_type_id) -> SemIR::InstId {
|
|
auto& function = context.functions().Get(function_id);
|
|
|
|
auto [args_vec, ignored_call_args] =
|
|
ThunkPatternMatch(context, loc_id, param_pattern_ids, call_arg_ids);
|
|
llvm::ArrayRef<SemIR::InstId> args = args_vec;
|
|
|
|
if (override_self_type_id.has_value()) {
|
|
args_vec.front() = UnsafeUndoConvert(context, loc_id, args_vec.front(),
|
|
override_self_type_id);
|
|
}
|
|
|
|
// If we have a self parameter, form `self.<callee_id>` if needed.
|
|
// When calling a C++ constructor to implement `Copy`, or calling a C++
|
|
// non-method operator to implement a Carbon operator, the interface has a
|
|
// `self` parameter but C++ models that parameter as an explicit argument
|
|
// instead, so add the `self` to the argument list instead in that case.
|
|
if (function.self_param_id.has_value() &&
|
|
!IsCppConstructorOrNonMethod(context, callee_id)) {
|
|
callee_id = PerformCompoundMemberAccess(context, loc_id,
|
|
args.consume_front(), callee_id);
|
|
}
|
|
|
|
// We treat this as an operator call because it's a call that's synthesized
|
|
// by the toolchain, not written by the user.
|
|
return PerformCall(context, loc_id, callee_id, args,
|
|
/*is_desugared=*/true);
|
|
}
|
|
|
|
// Build a call to a function that forwards the arguments of the enclosing
|
|
// function, for use when constructing a thunk. `param_pattern_ids` contains the
|
|
// parameter patterns of `function_id`. If `override_self_type_id` is not
|
|
// `None`, `callee_id` refers to a virtual function override declared in the
|
|
// given type.
|
|
static auto BuildThunkCall(Context& context, SemIR::FunctionId function_id,
|
|
SemIR::InstId callee_id,
|
|
llvm::ArrayRef<SemIR::InstId> param_pattern_ids,
|
|
llvm::ArrayRef<SemIR::InstId> call_arg_ids,
|
|
SemIR::TypeId override_self_type_id)
|
|
-> SemIR::InstId {
|
|
auto& function = context.functions().Get(function_id);
|
|
|
|
// Build a `NameRef` naming the callee, and a `SpecificConstant` if needed.
|
|
auto loc_id = SemIR::LocId(callee_id);
|
|
auto callee_type = context.types().GetAs<SemIR::FunctionType>(
|
|
context.insts().Get(callee_id).type_id());
|
|
callee_id = BuildNameRef(context, loc_id, function.name_id, callee_id,
|
|
callee_type.specific_id);
|
|
|
|
return PerformThunkCall(context, loc_id, function_id, param_pattern_ids,
|
|
call_arg_ids, callee_id, override_self_type_id);
|
|
}
|
|
|
|
static auto StartThunkFunctionDefinition(Context& context,
|
|
SemIR::FunctionId function_id,
|
|
SemIR::InstId thunk_id,
|
|
SemIR::InstId callee_id) {
|
|
// The check below produces diagnostics referring to the signature, so also
|
|
// note the callee.
|
|
Diagnostics::AnnotationScope annot_scope(
|
|
&context.emitter(), [&](DiagnosticBuilder& builder) {
|
|
CARBON_DIAGNOSTIC_LABEL(ThunkCallee, Info,
|
|
"while building thunk calling this function");
|
|
builder.Attach(callee_id, ThunkCallee);
|
|
});
|
|
|
|
StartFunctionDefinition(context, thunk_id, function_id);
|
|
}
|
|
|
|
// Given a declaration of a thunk and the function that it should call, build
|
|
// the thunk body.
|
|
static auto BuildThunkDefinition(Context& context,
|
|
SemIR::FunctionId signature_id,
|
|
SemIR::FunctionId function_id,
|
|
SemIR::InstId thunk_id,
|
|
SemIR::InstId callee_id,
|
|
SemIR::TypeId override_self_type_id) -> void {
|
|
StartThunkFunctionDefinition(context, function_id, thunk_id, callee_id);
|
|
|
|
// The checks below produce diagnostics pointing at the callee, so also note
|
|
// the signature.
|
|
Diagnostics::AnnotationScope annot_scope(
|
|
&context.emitter(), [&](DiagnosticBuilder& builder) {
|
|
CARBON_DIAGNOSTIC_LABEL(
|
|
ThunkSignature, Info,
|
|
"while building thunk to match the signature of this function");
|
|
builder.Attach(
|
|
context.functions().Get(signature_id).first_owning_decl_id,
|
|
ThunkSignature);
|
|
});
|
|
|
|
const auto& function = context.functions().Get(function_id);
|
|
llvm::ArrayRef<SemIR::InstId> param_pattern_ids;
|
|
if (function.param_patterns_id.has_value()) {
|
|
param_pattern_ids = context.inst_blocks().Get(function.param_patterns_id);
|
|
}
|
|
auto call_param_ids = context.inst_blocks().Get(function.call_params_id);
|
|
|
|
auto call_id =
|
|
BuildThunkCall(context, function_id, callee_id, param_pattern_ids,
|
|
call_param_ids, override_self_type_id);
|
|
if (HasDeclaredReturnType(context, function_id)) {
|
|
BuildReturnWithExpr(context, SemIR::LocId(callee_id), call_id);
|
|
} else {
|
|
DiscardExpr(context, call_id);
|
|
BuildReturnWithNoExpr(context, SemIR::LocId(callee_id));
|
|
}
|
|
|
|
FinishFunctionDefinition(context, function_id);
|
|
}
|
|
|
|
auto BuildThunkDefinitionForExport(Context& context,
|
|
SemIR::FunctionId thunk_function_id,
|
|
SemIR::FunctionId callee_function_id,
|
|
SemIR::InstId thunk_id,
|
|
SemIR::InstId callee_id,
|
|
bool export_as_constructor) -> void {
|
|
auto& thunk_function = context.functions().Get(thunk_function_id);
|
|
auto& callee_function = context.functions().Get(callee_function_id);
|
|
|
|
StartThunkFunctionDefinition(context, thunk_function_id, thunk_id, callee_id);
|
|
|
|
const bool thunk_has_return_param =
|
|
callee_function.return_type_inst_id != SemIR::TypeInstId::None;
|
|
|
|
llvm::ArrayRef<SemIR::InstId> param_pattern_ids;
|
|
if (thunk_function.param_patterns_id.has_value()) {
|
|
param_pattern_ids =
|
|
context.inst_blocks().Get(thunk_function.param_patterns_id);
|
|
}
|
|
llvm::ArrayRef<SemIR::InstId> call_param_ids(
|
|
context.inst_blocks().Get(thunk_function.call_params_id));
|
|
|
|
// If the thunk has an explicit output parameter, it is not passed to the
|
|
// callee function. The output parameter is normally last, but comes first
|
|
// for constructors.
|
|
auto out_param_id = SemIR::InstId::None;
|
|
if (thunk_has_return_param) {
|
|
if (export_as_constructor) {
|
|
param_pattern_ids.consume_front();
|
|
out_param_id = call_param_ids.consume_front();
|
|
} else {
|
|
param_pattern_ids.consume_back();
|
|
out_param_id = call_param_ids.consume_back();
|
|
}
|
|
}
|
|
|
|
auto call_id = BuildThunkCall(context, thunk_function_id, callee_id,
|
|
param_pattern_ids, call_param_ids,
|
|
/*override_self_type_id=*/SemIR::TypeId::None);
|
|
if (out_param_id.has_value()) {
|
|
SemIR::LocId loc_id(out_param_id);
|
|
auto init_id = InitializeExisting(context, loc_id, out_param_id, call_id,
|
|
/*for_return=*/true);
|
|
AddInst(context, loc_id,
|
|
SemIR::Assign{
|
|
.lhs_id = out_param_id,
|
|
.rhs_id = init_id,
|
|
});
|
|
} else {
|
|
DiscardExpr(context, call_id);
|
|
}
|
|
|
|
BuildReturnWithNoExpr(context, SemIR::LocId(callee_id));
|
|
|
|
FinishFunctionDefinition(context, thunk_function_id);
|
|
}
|
|
|
|
auto BuildThunkDefinition(Context& context,
|
|
DeferredDefinitionWorklist::DefineThunk&& task)
|
|
-> void {
|
|
context.scope_stack().Restore(std::move(task.scope));
|
|
|
|
BuildThunkDefinition(context, task.info.signature_id, task.info.function_id,
|
|
task.info.decl_id, task.info.callee_id,
|
|
task.info.override_self_type_id);
|
|
|
|
context.scope_stack().Pop();
|
|
}
|
|
|
|
auto BuildThunk(Context& context, SemIR::FunctionId signature_id,
|
|
SemIR::SpecificId signature_specific_id,
|
|
SemIR::TypeId override_self_type_id, SemIR::InstId callee_id,
|
|
bool defer_definition) -> SemIR::InstId {
|
|
auto callee = SemIR::GetCalleeAsFunction(context.sem_ir(), callee_id);
|
|
|
|
// Check whether we can use the given function without a thunk.
|
|
// TODO: This is too strict; for example, we should not compare parameter
|
|
// names here.
|
|
if (context.functions().Get(callee.function_id).special_function_kind !=
|
|
SemIR::Function::SpecialFunctionKind::HasCppThunk &&
|
|
!override_self_type_id.has_value() &&
|
|
CheckFunctionTypeMatches(
|
|
context, context.functions().Get(callee.function_id),
|
|
context.functions().Get(signature_id), signature_specific_id,
|
|
/*check_syntax=*/false, /*diagnose=*/false)) {
|
|
return callee_id;
|
|
}
|
|
|
|
// From P3763:
|
|
// If the function in the interface does not have a return type, the
|
|
// program is invalid if the function in the impl specifies a return type.
|
|
//
|
|
// Call into the redeclaration checking logic to produce a suitable error.
|
|
//
|
|
// TODO: Consider a different rule: always use an explicit return type for the
|
|
// thunk, and always convert the result of the wrapped call to the return type
|
|
// of the thunk.
|
|
if (!HasDeclaredReturnType(context, signature_id) &&
|
|
HasDeclaredReturnType(context, callee.function_id) &&
|
|
context.functions().Get(callee.function_id).name_id !=
|
|
SemIR::NameId::CppOperator) {
|
|
bool success = CheckFunctionReturnTypeMatches(
|
|
context, context.functions().Get(callee.function_id),
|
|
context.functions().Get(signature_id), signature_specific_id);
|
|
CARBON_CHECK(!success, "Return type unexpectedly matches");
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Create a scope for the function's parameters and generic parameters.
|
|
context.scope_stack().PushForDeclName();
|
|
|
|
// We can't use the function directly. Build a thunk.
|
|
// TODO: Before building the thunk, check for and diagnose any reasons why
|
|
// that would fail. Diagnostics emitted while building the thunk aren't
|
|
// meaningful to users, because they point to errors in code that the user
|
|
// didn't write (and doesn't have a correct location).
|
|
auto [function_id, thunk_inst_id] =
|
|
CloneFunctionDecl(context, SemIR::LocId(callee_id), signature_id,
|
|
signature_specific_id, callee.function_id);
|
|
|
|
auto thunk_id = context.sem_ir().thunks().Add(
|
|
{.callee_id = callee_id,
|
|
.signature_id = signature_id,
|
|
.specific_id = signature_specific_id,
|
|
.override_self_type_id = override_self_type_id});
|
|
|
|
// Track that this function is a thunk.
|
|
context.functions().Get(function_id).SetThunk(thunk_id);
|
|
|
|
if (defer_definition) {
|
|
// Register the thunk to be defined when we reach the end of the enclosing
|
|
// deferred definition scope, for example an `impl` or `class` definition,
|
|
// as if the thunk's body were written inline in this location.
|
|
context.deferred_definition_worklist().SuspendThunkAndPush(
|
|
context, {
|
|
.signature_id = signature_id,
|
|
.function_id = function_id,
|
|
.decl_id = thunk_inst_id,
|
|
.callee_id = callee_id,
|
|
.override_self_type_id = override_self_type_id,
|
|
});
|
|
} else {
|
|
BuildThunkDefinition(context, signature_id, function_id, thunk_inst_id,
|
|
callee_id, override_self_type_id);
|
|
context.scope_stack().Pop();
|
|
}
|
|
|
|
return thunk_inst_id;
|
|
}
|
|
|
|
auto BuildDestroyThunk(Context& context, SemIR::LocId loc_id,
|
|
const SemIR::Class& class_info) -> SemIR::FunctionId {
|
|
// Create a new function declaration.
|
|
auto thunk_name_id = SemIR::NameId::ForIdentifier(
|
|
context.identifiers().Add("__destroy_thunk"));
|
|
auto [thunk_inst_id, thunk_function_id] =
|
|
MakeGeneratedFunctionDecl(context, loc_id,
|
|
{.parent_scope_id = class_info.scope_id,
|
|
.name_id = thunk_name_id,
|
|
.self_type_id = class_info.self_type_id});
|
|
|
|
auto& thunk_function = context.functions().Get(thunk_function_id);
|
|
thunk_function.SetThunk(SemIR::ThunkId::None);
|
|
|
|
context.scope_stack().PushForDeclName();
|
|
StartFunctionDefinition(context, thunk_inst_id, thunk_function_id);
|
|
|
|
// Get `self` arg.
|
|
auto params = context.inst_blocks().Get(thunk_function.call_params_id);
|
|
auto self_inst_id = params[0];
|
|
|
|
// Build the function body. This calls the `Destroy` operator on `self`.
|
|
auto destroy_inst_id = BuildSelfDestructCall(context, self_inst_id);
|
|
DiscardExpr(context, destroy_inst_id);
|
|
BuildReturnWithNoExpr(context, loc_id);
|
|
|
|
FinishFunctionDefinition(context, thunk_function_id);
|
|
context.scope_stack().Pop();
|
|
|
|
return thunk_function_id;
|
|
}
|
|
|
|
} // namespace Carbon::Check
|