Substitute Self in associated function signatures before checking them against impls. (#3788)

Add a general substitution mechanism to support substituting symbolic
bindings with their values throughout symbolic constants and, more
specifically, types. This is done by decomposing the constant
instruction into its operands, substituting into the operands, and then
rebuilding the constant value by invoking the constant evaluator.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
Richard Smith
2024-03-15 23:21:12 +00:00
committed by GitHub
co-authored by Jon Ross-Perkins Carbon Infra Bot
parent 2584399673
commit 8cb932b99c
19 changed files with 755 additions and 189 deletions
+29 -17
View File
@@ -4,6 +4,8 @@
#include "toolchain/check/function.h"
#include "toolchain/check/subst.h"
namespace Carbon::Check {
// Returns true if there was an error in declaring the function, which will have
@@ -31,7 +33,8 @@ static auto CheckRedeclParam(Context& context,
llvm::StringLiteral param_diag_label,
int32_t param_index,
SemIR::InstId new_param_ref_id,
SemIR::InstId prev_param_ref_id) -> bool {
SemIR::InstId prev_param_ref_id,
Substitutions substitutions) -> bool {
// TODO: Consider differentiating between type and name mistakes. For now,
// taking the simpler approach because I also think we may want to refactor
// params.
@@ -52,7 +55,8 @@ static auto CheckRedeclParam(Context& context,
auto new_param_ref = context.insts().Get(new_param_ref_id);
auto prev_param_ref = context.insts().Get(prev_param_ref_id);
if (new_param_ref.kind() != prev_param_ref.kind() ||
new_param_ref.type_id() != prev_param_ref.type_id()) {
new_param_ref.type_id() !=
SubstType(context, prev_param_ref.type_id(), substitutions)) {
diagnose();
return false;
}
@@ -90,7 +94,8 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id,
SemIR::InstBlockId new_param_refs_id,
SemIR::InstId prev_decl_id,
SemIR::InstBlockId prev_param_refs_id,
llvm::StringLiteral param_diag_label) -> bool {
llvm::StringLiteral param_diag_label,
Substitutions substitutions) -> bool {
// This will often occur for empty params.
if (new_param_refs_id == prev_param_refs_id) {
return true;
@@ -116,7 +121,7 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id,
for (auto [index, new_param_ref_id, prev_param_ref_id] :
llvm::enumerate(new_param_ref_ids, prev_param_ref_ids)) {
if (!CheckRedeclParam(context, param_diag_label, index, new_param_ref_id,
prev_param_ref_id)) {
prev_param_ref_id, substitutions)) {
return false;
}
}
@@ -125,21 +130,26 @@ static auto CheckRedeclParams(Context& context, SemIR::InstId new_decl_id,
// Returns false if the provided function declarations differ.
static auto CheckRedecl(Context& context, const SemIR::Function& new_function,
const SemIR::Function& prev_function) -> bool {
const SemIR::Function& prev_function,
Substitutions substitutions) -> bool {
if (FunctionDeclHasError(context, new_function) ||
FunctionDeclHasError(context, prev_function)) {
return false;
}
if (!CheckRedeclParams(context, new_function.decl_id,
new_function.implicit_param_refs_id,
prev_function.decl_id,
prev_function.implicit_param_refs_id, "implicit ") ||
if (!CheckRedeclParams(
context, new_function.decl_id, new_function.implicit_param_refs_id,
prev_function.decl_id, prev_function.implicit_param_refs_id,
"implicit ", substitutions) ||
!CheckRedeclParams(context, new_function.decl_id,
new_function.param_refs_id, prev_function.decl_id,
prev_function.param_refs_id, "")) {
prev_function.param_refs_id, "", substitutions)) {
return false;
}
if (new_function.return_type_id != prev_function.return_type_id) {
auto prev_return_type_id =
prev_function.return_type_id.is_valid()
? SubstType(context, prev_function.return_type_id, substitutions)
: SemIR::TypeId::Invalid;
if (new_function.return_type_id != prev_return_type_id) {
CARBON_DIAGNOSTIC(
FunctionRedeclReturnTypeDiffers, Error,
"Function redeclaration differs because return type is `{0}`.",
@@ -154,12 +164,12 @@ static auto CheckRedecl(Context& context, const SemIR::Function& new_function,
new_function.return_type_id)
: context.emitter().Build(new_function.decl_id,
FunctionRedeclReturnTypeDiffersNoReturn);
if (prev_function.return_type_id.is_valid()) {
if (prev_return_type_id.is_valid()) {
CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePrevious, Note,
"Previously declared with return type `{0}`.",
SemIR::TypeId);
diag.Note(prev_function.decl_id, FunctionRedeclReturnTypePrevious,
prev_function.return_type_id);
prev_return_type_id);
} else {
CARBON_DIAGNOSTIC(FunctionRedeclReturnTypePreviousNoReturn, Note,
"Previously declared with no return type.");
@@ -173,10 +183,12 @@ static auto CheckRedecl(Context& context, const SemIR::Function& new_function,
return true;
}
auto CheckFunctionRedecl(Context& context, SemIR::FunctionId new_function_id,
SemIR::FunctionId prev_function_id) -> bool {
auto CheckFunctionTypeMatches(Context& context,
SemIR::FunctionId new_function_id,
SemIR::FunctionId prev_function_id,
Substitutions substitutions) -> bool {
return CheckRedecl(context, context.functions().Get(new_function_id),
context.functions().Get(prev_function_id));
context.functions().Get(prev_function_id), substitutions);
}
auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id,
@@ -186,7 +198,7 @@ auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id,
auto& prev_function = context.functions().Get(prev_function_id);
// TODO: Disallow redeclarations within classes?
if (!CheckRedecl(context, new_function, prev_function)) {
if (!CheckRedecl(context, new_function, prev_function, {})) {
return false;
}