Files
carbon-lang/toolchain/check/impl.cpp
T
Jon Ross-PerkinsandRichard Smith 5bb318cae6 Switch AddInst struct init style. (#4012)
Trying to conform with #4009. Changes SemIR::LocIdAndInst construction
to root out struct init cases with AddInst and related functions. I'm
using templating of AddInst functions in order to avoid `AddInst(loc_id,
InstName{...})` and instead have `AddInst<InstName>(loc_id, {...})` with
I think similar readability results. There are a couple cases where inst
construction is templated and so designated initializers couldn't be
used, so this may be better for those in particular due to the extra
type enforcement.

This probably doesn't clean up every last case, but I was trying to get
the bulk at once without bleeding over into less related changes.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
2024-05-31 22:49:44 +00:00

170 lines
7.0 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/impl.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/context.h"
#include "toolchain/check/function.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/subst.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/impl.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// Adds the location of the associated function to a diagnostic.
static auto NoteAssociatedFunction(Context& context,
Context::DiagnosticBuilder& builder,
SemIR::FunctionId function_id) -> void {
CARBON_DIAGNOSTIC(ImplAssociatedFunctionHere, Note,
"Associated function {0} declared here.", SemIR::NameId);
const auto& function = context.functions().Get(function_id);
builder.Note(function.decl_id, ImplAssociatedFunctionHere, function.name_id);
}
// Checks that `impl_function_id` is a valid implementation of the function
// described in the interface as `interface_function_id`. Returns the value to
// put into the corresponding slot in the witness table, which can be
// `BuiltinError` if the function is not usable.
static auto CheckAssociatedFunctionImplementation(
Context& context, SemIR::FunctionId interface_function_id,
SemIR::InstId impl_decl_id, Substitutions substitutions) -> SemIR::InstId {
auto impl_function_decl =
context.insts().TryGetAs<SemIR::FunctionDecl>(impl_decl_id);
if (!impl_function_decl) {
CARBON_DIAGNOSTIC(ImplFunctionWithNonFunction, Error,
"Associated function {0} implemented by non-function.",
SemIR::NameId);
auto builder = context.emitter().Build(
impl_decl_id, ImplFunctionWithNonFunction,
context.functions().Get(interface_function_id).name_id);
NoteAssociatedFunction(context, builder, interface_function_id);
builder.Emit();
return SemIR::InstId::BuiltinError;
}
// TODO: This should be a semantic check rather than a syntactic one. The
// functions should be allowed to have different signatures as long as we can
// synthesize a suitable thunk.
if (!CheckFunctionTypeMatches(
context, context.functions().Get(impl_function_decl->function_id),
context.functions().Get(interface_function_id), substitutions)) {
return SemIR::InstId::BuiltinError;
}
return impl_decl_id;
}
// Builds a witness that the specified impl implements the given interface.
static auto BuildInterfaceWitness(
Context& context, const SemIR::Impl& impl, SemIR::InterfaceId interface_id,
llvm::SmallVectorImpl<SemIR::InstId>& used_decl_ids) -> SemIR::InstId {
const auto& interface = context.interfaces().Get(interface_id);
if (!interface.is_defined()) {
CARBON_DIAGNOSTIC(ImplOfUndefinedInterface, Error,
"Implementation of undefined interface {0}.",
SemIR::NameId);
auto builder = context.emitter().Build(
impl.definition_id, ImplOfUndefinedInterface, interface.name_id);
context.NoteUndefinedInterface(interface_id, builder);
builder.Emit();
return SemIR::InstId::BuiltinError;
}
auto& impl_scope = context.name_scopes().Get(impl.scope_id);
llvm::SmallVector<SemIR::InstId> table;
auto assoc_entities =
context.inst_blocks().Get(interface.associated_entities_id);
table.reserve(assoc_entities.size());
// Substitute `Self` with the impl's self type when associated functions.
auto self_bind =
context.insts().GetAs<SemIR::BindSymbolicName>(interface.self_param_id);
Substitution substitutions[1] = {
{.bind_id = context.bind_names().Get(self_bind.bind_name_id).bind_index,
.replacement_id = context.types().GetConstantId(impl.self_id)}};
for (auto decl_id : assoc_entities) {
LoadImportRef(context, decl_id);
auto const_id = context.constant_values().Get(decl_id);
CARBON_CHECK(const_id.is_constant()) << "Non-constant associated entity";
auto decl = context.insts().Get(const_id.inst_id());
CARBON_KIND_SWITCH(decl) {
case CARBON_KIND(SemIR::StructValue struct_value): {
if (struct_value.type_id == SemIR::TypeId::Error) {
return SemIR::InstId::BuiltinError;
}
auto type_inst = context.types().GetAsInst(struct_value.type_id);
auto fn_type = type_inst.TryAs<SemIR::FunctionType>();
if (!fn_type) {
CARBON_FATAL() << "Unexpected type: " << type_inst;
}
auto& fn = context.functions().Get(fn_type->function_id);
auto impl_decl_id = context.LookupNameInExactScope(
decl_id, fn.name_id, impl.scope_id, impl_scope);
if (impl_decl_id.is_valid()) {
used_decl_ids.push_back(impl_decl_id);
table.push_back(CheckAssociatedFunctionImplementation(
context, fn_type->function_id, impl_decl_id, substitutions));
} else {
CARBON_DIAGNOSTIC(
ImplMissingFunction, Error,
"Missing implementation of {0} in impl of interface {1}.",
SemIR::NameId, SemIR::NameId);
auto builder =
context.emitter().Build(impl.definition_id, ImplMissingFunction,
fn.name_id, interface.name_id);
NoteAssociatedFunction(context, builder, fn_type->function_id);
builder.Emit();
table.push_back(SemIR::InstId::BuiltinError);
}
break;
}
case SemIR::AssociatedConstantDecl::Kind:
// TODO: Check we have a value for this constant in the constraint.
context.TODO(impl.definition_id,
"impl of interface with associated constant");
return SemIR::InstId::BuiltinError;
default:
CARBON_FATAL() << "Unexpected kind of associated entity " << decl;
}
}
auto table_id = context.inst_blocks().Add(table);
return context.AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::InterfaceWitness>(
{.type_id = context.GetBuiltinType(SemIR::BuiltinKind::WitnessType),
.elements_id = table_id}));
}
auto BuildImplWitness(Context& context, SemIR::ImplId impl_id)
-> SemIR::InstId {
auto& impl = context.impls().Get(impl_id);
CARBON_CHECK(impl.is_being_defined());
// TODO: Handle non-interface constraints.
auto interface_type =
context.types().TryGetAs<SemIR::InterfaceType>(impl.constraint_id);
if (!interface_type) {
context.TODO(impl.definition_id, "impl as non-interface");
return SemIR::InstId::BuiltinError;
}
llvm::SmallVector<SemIR::InstId> used_decl_ids;
auto witness_id = BuildInterfaceWitness(
context, impl, interface_type->interface_id, used_decl_ids);
// TODO: Diagnose if any declarations in the impl are not in used_decl_ids.
return witness_id;
}
} // namespace Carbon::Check