Rework how we check calls to support deduced implicit parameters (#4302)

Instead of the `call` instruction having a block with one argument per
explicit argument, preceded optionally by `self` and followed optionally
by a return slot, change the `call` to store only the *runtime*
arguments. Store an index on the runtime parameters to make it easier to
determine the correspondence between arguments and parameters in a call.
Compile-time parameters, whether implicit or explicit, are no longer
included in the call argument list. Instead, they're tracked only in the
`specific_id` on the callee.

For calls to generic classes and generic interfaces, it no longer makes
sense to form a `call` instruction, given that the entirety of the
result is determined by the `specific_id`, which is now formed when
checking the call. Instead, the `call` instruction now only models
function calls, and not calls to other kinds of parameterized entity
names, and we create a `class_type` or `interface_type` instead of a
`call` instruction to model these kinds of calls. Notionally the model
here is that we're following the #3720 approach for calls, but for now
we inline the `Call.Op` function when forming SemIR.

We now also track the enclosing specific for a generic class or generic
interface that appears within an enclosing generic. This is necessary in
order for deduction of the inner generic parameters to not get confused
by the outer generic parameters being absent.

In order to not regress diagnostics, the template argument deduction
mechanism has been extended to specify the name of the parameter we're
deducing against when possible, and call arity mismatch errors are now
diagnosed before performing deduction rather than afterwards.
This commit is contained in:
Richard Smith
2024-09-13 21:31:43 +00:00
committed by GitHub
parent 8b92b996b1
commit 0354efa1fc
287 changed files with 2207 additions and 1806 deletions
+54 -11
View File
@@ -65,6 +65,51 @@ static auto DiagnoseModifiers(Context& context, DeclIntroducerState& introducer,
RequireDefaultFinalOnlyInInterfaces(context, introducer, parent_scope_inst);
}
// Checks that the parameter lists specified in a function declaration are
// valid for a function declaration, and numbers the parameters.
static auto CheckFunctionSignature(Context& context,
const NameComponent& name_and_params)
-> void {
SemIR::RuntimeParamIndex next_index(0);
for (auto param_id : llvm::concat<const SemIR::InstId>(
context.inst_blocks().GetOrEmpty(name_and_params.implicit_params_id),
context.inst_blocks().GetOrEmpty(name_and_params.params_id))) {
auto param = context.insts().Get(param_id);
// Find the parameter in the pattern.
// TODO: This duplicates work done by Function::GetParamFromParamRefId.
if (auto addr_pattern = param.TryAs<SemIR::AddrPattern>()) {
param_id = addr_pattern->inner_id;
param = context.insts().Get(param_id);
}
auto bind_name = param.TryAs<SemIR::AnyBindName>();
if (bind_name) {
param_id = bind_name->value_id;
param = context.insts().Get(param_id);
}
auto param_inst = param.TryAs<SemIR::Param>();
if (!param_inst) {
// Once we support more generalized patterns we will need to diagnose
// parameters with unsupported patterns.
context.TODO(param_id, "unexpected syntax for parameter");
// TODO: Also repair the param ID so downstream code doesn't need to deal
// with this.
continue;
}
// If this is a runtime parameter, number it.
if (bind_name && bind_name->kind == SemIR::BindName::Kind) {
param_inst->runtime_index = next_index;
context.ReplaceInstBeforeConstantUse(param_id, *param_inst);
++next_index.index;
}
}
// TODO: Also assign a parameter index to the return storage, if present.
}
// Tries to merge new_function into prev_function_id. Since new_function won't
// have a definition even if one is upcoming, set is_definition to indicate the
// planned result.
@@ -182,6 +227,10 @@ static auto BuildFunctionDecl(Context& context,
context.TODO(node_id, "function with positional parameters");
name.params_id = SemIR::InstBlockId::Empty;
}
// Check that the function signature is valid and number the parameters.
CheckFunctionSignature(context, name);
auto name_context = context.decl_name_stack().FinishName(name);
context.node_stack()
.PopAndDiscardSoloNodeId<Parse::NodeKind::FunctionIntroducer>();
@@ -315,26 +364,20 @@ static auto HandleFunctionDefinitionAfterSignature(
SemIR::SpecificId::Invalid);
// Check the parameter types are complete.
for (auto param_id : llvm::concat<const SemIR::InstId>(
for (auto param_ref_id : llvm::concat<const SemIR::InstId>(
context.inst_blocks().GetOrEmpty(function.implicit_param_refs_id),
context.inst_blocks().GetOrEmpty(function.param_refs_id))) {
auto param = context.insts().Get(param_id);
// Find the parameter in the pattern.
// TODO: More general pattern handling?
if (auto addr_pattern = param.TryAs<SemIR::AddrPattern>()) {
param_id = addr_pattern->inner_id;
param = context.insts().Get(param_id);
}
auto [param_id, param] =
SemIR::Function::GetParamFromParamRefId(context.sem_ir(), param_ref_id);
// The parameter types need to be complete.
context.TryToCompleteType(param.type_id(), [&] {
context.TryToCompleteType(param.type_id, [&] {
CARBON_DIAGNOSTIC(
IncompleteTypeInFunctionParam, Error,
"Parameter has incomplete type `{0}` in function definition.",
SemIR::TypeId);
return context.emitter().Build(param_id, IncompleteTypeInFunctionParam,
param.type_id());
param.type_id);
});
}