Skip to content

Support accessing associated functions by member access into facets #4872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
70b8111
WIP
zygoloid Jan 29, 2025
de28928
Switch FunctionType to storing a list of arguments instead of a speci…
zygoloid Jan 29, 2025
e1e86af
Roll back changes to FunctionType.
zygoloid Jan 29, 2025
f149b8e
Preserve `Self` type when accessing associated function in a facet.
zygoloid Jan 30, 2025
8bfeff2
Recognise ImplFunctionType as a function.
zygoloid Jan 30, 2025
4d0b522
Use the Self type from impl lookup.
zygoloid Jan 30, 2025
0f3f420
clang-format
zygoloid Jan 30, 2025
516cdd4
Add comment to MakeSpecific overload and add another usage.
zygoloid Jan 31, 2025
fa84987
Remove unnecessary type name.
zygoloid Jan 31, 2025
8e90b34
Avoid and/or.
zygoloid Jan 31, 2025
da105b2
Make comment more explicit.
zygoloid Jan 31, 2025
1d76656
Add comment.
zygoloid Jan 31, 2025
14f8f45
Rename ImplFunctionType -> FunctionTypeWithSelf.
zygoloid Jan 31, 2025
2ab0181
Add Type suffix. Make StringifyType handle error cases.
zygoloid Feb 1, 2025
14a4c5d
Reuse GetCalleeFunction.
zygoloid Feb 1, 2025
315f030
Diagnose repeated `self` binding.
zygoloid Feb 1, 2025
8c52927
Comment.
zygoloid Feb 1, 2025
7f9ab7c
Merge GetSelfFacets together.
zygoloid Feb 1, 2025
3a0abe1
Rename for consistency.
zygoloid Feb 1, 2025
cf62880
Avoid using the generic to reserve
zygoloid Feb 1, 2025
34d2133
Rename
zygoloid Feb 4, 2025
1eebb98
Fix stringify and add test.
zygoloid Feb 4, 2025
3aa7ad7
Merge branch 'trunk' into toolchain-facet-access
zygoloid Feb 4, 2025
05440ec
Temporarily suppress recursion lint.
zygoloid Feb 4, 2025
e05df21
Add missing `fail_` to test.
zygoloid Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions toolchain/check/call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
const SemIR::EntityWithParamsBase& entity,
EntityKind entity_kind_for_diagnostic,
SemIR::SpecificId enclosing_specific_id,
SemIR::InstId self_type_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
-> std::optional<SemIR::SpecificId> {
Expand Down Expand Up @@ -70,7 +71,7 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
auto specific_id = SemIR::SpecificId::None;
if (entity.generic_id.has_value()) {
specific_id = DeduceGenericCallArguments(
context, loc_id, entity.generic_id, enclosing_specific_id,
context, loc_id, entity.generic_id, enclosing_specific_id, self_type_id,
entity.implicit_param_patterns_id, entity.param_patterns_id, self_id,
arg_ids);
if (!specific_id.has_value()) {
Expand All @@ -91,6 +92,7 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
auto callee_specific_id =
ResolveCalleeInCall(context, loc_id, generic_class,
EntityKind::GenericClass, enclosing_specific_id,
/*self_type_id=*/SemIR::InstId::None,
/*self_id=*/SemIR::InstId::None, arg_ids);
if (!callee_specific_id) {
return SemIR::ErrorInst::SingletonInstId;
Expand All @@ -111,6 +113,7 @@ static auto PerformCallToGenericInterface(
auto callee_specific_id =
ResolveCalleeInCall(context, loc_id, interface,
EntityKind::GenericInterface, enclosing_specific_id,
/*self_type_id=*/SemIR::InstId::None,
/*self_id=*/SemIR::InstId::None, arg_ids);
if (!callee_specific_id) {
return SemIR::ErrorInst::SingletonInstId;
Expand Down Expand Up @@ -153,7 +156,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
auto callee_specific_id = ResolveCalleeInCall(
context, loc_id, context.functions().Get(callee_function.function_id),
EntityKind::Function, callee_function.enclosing_specific_id,
callee_function.self_id, arg_ids);
callee_function.self_type_id, callee_function.self_id, arg_ids);
if (!callee_specific_id) {
return SemIR::ErrorInst::SingletonInstId;
}
Expand All @@ -165,7 +168,12 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
SemIR::SpecificFunctionType::SingletonInstId),
.callee_id = callee_id,
.specific_id = *callee_specific_id});
context.definitions_required().push_back(callee_id);
if (callee_function.self_type_id.has_value()) {
// This is an associated function, and will be required to be defined as
// part of checking that the impl is complete.
} else {
context.definitions_required().push_back(callee_id);
}
}

// If there is a return slot, build storage for the result.
Expand Down
10 changes: 9 additions & 1 deletion toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1292,7 +1292,8 @@ class TypeCompleter {
template <typename InstT>
requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::FacetAccessType,
SemIR::FacetType, SemIR::FunctionType, SemIR::GenericClassType,
SemIR::FacetType, SemIR::FunctionType,
SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType,
SemIR::GenericInterfaceType, SemIR::UnboundElementType,
SemIR::WhereExpr>())
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
Expand Down Expand Up @@ -1555,6 +1556,13 @@ auto Context::GetFunctionType(SemIR::FunctionId fn_id,
return GetCompleteTypeImpl<SemIR::FunctionType>(*this, fn_id, specific_id);
}

auto Context::GetFunctionTypeWithSelfType(
SemIR::InstId interface_function_type_id, SemIR::InstId self_id)
-> SemIR::TypeId {
return GetCompleteTypeImpl<SemIR::FunctionTypeWithSelfType>(
*this, interface_function_type_id, self_id);
}

auto Context::GetGenericClassType(SemIR::ClassId class_id,
SemIR::SpecificId enclosing_specific_id)
-> SemIR::TypeId {
Expand Down
5 changes: 5 additions & 0 deletions toolchain/check/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,11 @@ class Context {
auto GetFunctionType(SemIR::FunctionId fn_id, SemIR::SpecificId specific_id)
-> SemIR::TypeId;

// Gets the type of an associated function with the `Self` parameter bound to
// a particular value. The returned type will be complete.
auto GetFunctionTypeWithSelfType(SemIR::InstId interface_function_type_id,
SemIR::InstId self_id) -> SemIR::TypeId;

// Gets a generic class type, which is the type of a name of a generic class,
// such as the type of `Vector` given `class Vector(T:! type)`. The returned
// type will be complete.
Expand Down
30 changes: 20 additions & 10 deletions toolchain/check/deduce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,13 @@ class DeductionWorklist {
// State that is tracked throughout the deduction process.
class DeductionContext {
public:
// Preparse to perform deduction. If an enclosing specific is provided, adds
// the arguments from the given specific as known arguments that will not be
// deduced.
// Preparse to perform deduction. If an enclosing specific or self type
// are provided, adds the corresponding arguments as known arguments that will
// not be deduced.
DeductionContext(Context& context, SemIR::LocId loc_id,
SemIR::GenericId generic_id,
SemIR::SpecificId enclosing_specific_id, bool diagnose);
SemIR::SpecificId enclosing_specific_id,
SemIR::InstId self_type_id, bool diagnose);

auto context() const -> Context& { return *context_; }

Expand Down Expand Up @@ -250,7 +251,7 @@ static auto NoteGenericHere(Context& context, SemIR::GenericId generic_id,
DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
SemIR::GenericId generic_id,
SemIR::SpecificId enclosing_specific_id,
bool diagnose)
SemIR::InstId self_type_id, bool diagnose)
: context_(&context),
loc_id_(loc_id),
generic_id_(generic_id),
Expand Down Expand Up @@ -285,6 +286,16 @@ DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
first_deduced_index_ = SemIR::CompileTimeBindIndex(args.size());
}

if (self_type_id.has_value()) {
// Copy the provided `Self` type as the value of the next binding.
auto self_index = first_deduced_index_;
result_arg_ids_[self_index.index] = self_type_id;
substitutions_.push_back(
{.bind_id = SemIR::CompileTimeBindIndex(self_index),
.replacement_id = context.constant_values().Get(self_type_id)});
first_deduced_index_ = SemIR::CompileTimeBindIndex(self_index.index + 1);
}

non_deduced_indexes_.resize(result_arg_ids_.size() -
first_deduced_index_.index);
}
Expand Down Expand Up @@ -504,19 +515,17 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
auto DeductionContext::MakeSpecific() -> SemIR::SpecificId {
// TODO: Convert the deduced values to the types of the bindings.

return Check::MakeSpecific(
context(), loc_id_, generic_id_,
context().inst_blocks().AddCanonical(result_arg_ids_));
return Check::MakeSpecific(context(), loc_id_, generic_id_, result_arg_ids_);
}

auto DeduceGenericCallArguments(
Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id,
SemIR::SpecificId enclosing_specific_id,
SemIR::SpecificId enclosing_specific_id, SemIR::InstId self_type_id,
[[maybe_unused]] SemIR::InstBlockId implicit_params_id,
SemIR::InstBlockId params_id, [[maybe_unused]] SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::SpecificId {
DeductionContext deduction(context, loc_id, generic_id, enclosing_specific_id,
/*diagnose=*/true);
self_type_id, /*diagnose=*/true);

// Prepare to perform deduction of the explicit parameters against their
// arguments.
Expand All @@ -537,6 +546,7 @@ auto DeduceImplArguments(Context& context, SemIR::LocId loc_id,
SemIR::ConstantId constraint_id) -> SemIR::SpecificId {
DeductionContext deduction(context, loc_id, impl.generic_id,
/*enclosing_specific_id=*/SemIR::SpecificId::None,
/*self_type_id=*/SemIR::InstId::None,
/*diagnose=*/false);

// Prepare to perform deduction of the type and interface.
Expand Down
12 changes: 5 additions & 7 deletions toolchain/check/deduce.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,11 @@
namespace Carbon::Check {

// Deduces the generic arguments to use in a call to a generic.
auto DeduceGenericCallArguments(Context& context, SemIR::LocId loc_id,
SemIR::GenericId generic_id,
SemIR::SpecificId enclosing_specific_id,
SemIR::InstBlockId implicit_params_id,
SemIR::InstBlockId params_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
auto DeduceGenericCallArguments(
Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id,
SemIR::SpecificId enclosing_specific_id, SemIR::InstId self_type_id,
SemIR::InstBlockId implicit_params_id, SemIR::InstBlockId params_id,
SemIR::InstId self_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
-> SemIR::SpecificId;

// Deduces the impl arguments to use in a use of a parameterized impl. Returns
Expand Down
5 changes: 5 additions & 0 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,11 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
case SemIR::FunctionType::Kind:
return RebuildIfFieldsAreConstant(eval_context, inst,
&SemIR::FunctionType::specific_id);
case SemIR::FunctionTypeWithSelfType::Kind:
return RebuildIfFieldsAreConstant(
eval_context, inst,
&SemIR::FunctionTypeWithSelfType::interface_function_type_id,
&SemIR::FunctionTypeWithSelfType::self_id);
case SemIR::GenericClassType::Kind:
return RebuildIfFieldsAreConstant(
eval_context, inst, &SemIR::GenericClassType::enclosing_specific_id);
Expand Down
6 changes: 6 additions & 0 deletions toolchain/check/generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,12 @@ auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
return specific_id;
}

auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
llvm::ArrayRef<SemIR::InstId> args) -> SemIR::SpecificId {
auto args_id = context.inst_blocks().AddCanonical(args);
return MakeSpecific(context, loc, generic_id, args_id);
}

static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id)
-> SemIR::SpecificId {
if (!generic_id.has_value()) {
Expand Down
25 changes: 9 additions & 16 deletions toolchain/check/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,17 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
llvm::ArrayRef<SemIR::InstId> const_ids)
-> SemIR::InstBlockId;

// Builds a new specific, or finds an existing one if this generic has already
// been referenced with these arguments. Performs substitution into the
// declaration, but not the definition, of the generic.
//
// `args_id` should be a canonical instruction block referring to constants.
// Builds a new specific with a given argument list, or finds an existing one if
// this generic has already been referenced with these arguments. Performs
// substitution into the declaration, but not the definition, of the generic.
auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id) -> SemIR::SpecificId;
llvm::ArrayRef<SemIR::InstId> args) -> SemIR::SpecificId;

// Builds a new specific if the given generic is it has a value. Otherwise
// returns `None`.
inline auto MakeSpecificIfGeneric(Context& context, SemIRLoc loc,
SemIR::GenericId generic_id,
SemIR::InstBlockId args_id)
-> SemIR::SpecificId {
return generic_id.has_value()
? MakeSpecific(context, loc, generic_id, args_id)
: SemIR::SpecificId::None;
}
// Builds a new specific or finds an existing one in the case where the argument
// list has already been converted into an instruction block. `args_id` should
// be a canonical instruction block referring to constants.
auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id) -> SemIR::SpecificId;

// Builds the specific that describes how the generic should refer to itself.
// For example, for a generic `G(T:! type)`, this is the specific `G(T)`. If
Expand Down
Loading