Skip to content

Commit fcfb134

Browse files
zygoloidjonmeow
andauthored
Support accessing associated functions by member access into facets (#4872)
For an expression such as `(Type as Interface).AssocFn()`, track the `Self` type `Type` in the result of the member access so that it's available when checking the function call. This introduces a new kind of type, `ImplFunctionType`, that represents the type of a function that is expected within an impl, modeled as the type of the function within the interface plus a value to use as `Self`. Calls to values of this type behave like calls to the underlying function except that the `Self` parameter is pre-bound to the self type from the facet. In order to support this, fix an issue where the imported list of generic bindings lost their association with their enclosing generic. This adds a little complexity to `import_ref`, including a new recursive cycle that I intend to address in a follow-up PR. --------- Co-authored-by: Jon Ross-Perkins <[email protected]>
1 parent dcfccd3 commit fcfb134

File tree

234 files changed

+4214
-2532
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

234 files changed

+4214
-2532
lines changed

toolchain/check/call.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
4141
const SemIR::EntityWithParamsBase& entity,
4242
EntityKind entity_kind_for_diagnostic,
4343
SemIR::SpecificId enclosing_specific_id,
44+
SemIR::InstId self_type_id,
4445
SemIR::InstId self_id,
4546
llvm::ArrayRef<SemIR::InstId> arg_ids)
4647
-> std::optional<SemIR::SpecificId> {
@@ -70,7 +71,7 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
7071
auto specific_id = SemIR::SpecificId::None;
7172
if (entity.generic_id.has_value()) {
7273
specific_id = DeduceGenericCallArguments(
73-
context, loc_id, entity.generic_id, enclosing_specific_id,
74+
context, loc_id, entity.generic_id, enclosing_specific_id, self_type_id,
7475
entity.implicit_param_patterns_id, entity.param_patterns_id, self_id,
7576
arg_ids);
7677
if (!specific_id.has_value()) {
@@ -91,6 +92,7 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
9192
auto callee_specific_id =
9293
ResolveCalleeInCall(context, loc_id, generic_class,
9394
EntityKind::GenericClass, enclosing_specific_id,
95+
/*self_type_id=*/SemIR::InstId::None,
9496
/*self_id=*/SemIR::InstId::None, arg_ids);
9597
if (!callee_specific_id) {
9698
return SemIR::ErrorInst::SingletonInstId;
@@ -111,6 +113,7 @@ static auto PerformCallToGenericInterface(
111113
auto callee_specific_id =
112114
ResolveCalleeInCall(context, loc_id, interface,
113115
EntityKind::GenericInterface, enclosing_specific_id,
116+
/*self_type_id=*/SemIR::InstId::None,
114117
/*self_id=*/SemIR::InstId::None, arg_ids);
115118
if (!callee_specific_id) {
116119
return SemIR::ErrorInst::SingletonInstId;
@@ -153,7 +156,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
153156
auto callee_specific_id = ResolveCalleeInCall(
154157
context, loc_id, context.functions().Get(callee_function.function_id),
155158
EntityKind::Function, callee_function.enclosing_specific_id,
156-
callee_function.self_id, arg_ids);
159+
callee_function.self_type_id, callee_function.self_id, arg_ids);
157160
if (!callee_specific_id) {
158161
return SemIR::ErrorInst::SingletonInstId;
159162
}
@@ -165,7 +168,12 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
165168
SemIR::SpecificFunctionType::SingletonInstId),
166169
.callee_id = callee_id,
167170
.specific_id = *callee_specific_id});
168-
context.definitions_required().push_back(callee_id);
171+
if (callee_function.self_type_id.has_value()) {
172+
// This is an associated function, and will be required to be defined as
173+
// part of checking that the impl is complete.
174+
} else {
175+
context.definitions_required().push_back(callee_id);
176+
}
169177
}
170178

171179
// If there is a return slot, build storage for the result.

toolchain/check/context.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1292,7 +1292,8 @@ class TypeCompleter {
12921292
template <typename InstT>
12931293
requires(InstT::Kind.template IsAnyOf<
12941294
SemIR::AssociatedEntityType, SemIR::FacetAccessType,
1295-
SemIR::FacetType, SemIR::FunctionType, SemIR::GenericClassType,
1295+
SemIR::FacetType, SemIR::FunctionType,
1296+
SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType,
12961297
SemIR::GenericInterfaceType, SemIR::UnboundElementType,
12971298
SemIR::WhereExpr>())
12981299
auto BuildValueReprForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const
@@ -1555,6 +1556,13 @@ auto Context::GetFunctionType(SemIR::FunctionId fn_id,
15551556
return GetCompleteTypeImpl<SemIR::FunctionType>(*this, fn_id, specific_id);
15561557
}
15571558

1559+
auto Context::GetFunctionTypeWithSelfType(
1560+
SemIR::InstId interface_function_type_id, SemIR::InstId self_id)
1561+
-> SemIR::TypeId {
1562+
return GetCompleteTypeImpl<SemIR::FunctionTypeWithSelfType>(
1563+
*this, interface_function_type_id, self_id);
1564+
}
1565+
15581566
auto Context::GetGenericClassType(SemIR::ClassId class_id,
15591567
SemIR::SpecificId enclosing_specific_id)
15601568
-> SemIR::TypeId {

toolchain/check/context.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,11 @@ class Context {
475475
auto GetFunctionType(SemIR::FunctionId fn_id, SemIR::SpecificId specific_id)
476476
-> SemIR::TypeId;
477477

478+
// Gets the type of an associated function with the `Self` parameter bound to
479+
// a particular value. The returned type will be complete.
480+
auto GetFunctionTypeWithSelfType(SemIR::InstId interface_function_type_id,
481+
SemIR::InstId self_id) -> SemIR::TypeId;
482+
478483
// Gets a generic class type, which is the type of a name of a generic class,
479484
// such as the type of `Vector` given `class Vector(T:! type)`. The returned
480485
// type will be complete.

toolchain/check/deduce.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,13 @@ class DeductionWorklist {
188188
// State that is tracked throughout the deduction process.
189189
class DeductionContext {
190190
public:
191-
// Preparse to perform deduction. If an enclosing specific is provided, adds
192-
// the arguments from the given specific as known arguments that will not be
193-
// deduced.
191+
// Preparse to perform deduction. If an enclosing specific or self type
192+
// are provided, adds the corresponding arguments as known arguments that will
193+
// not be deduced.
194194
DeductionContext(Context& context, SemIR::LocId loc_id,
195195
SemIR::GenericId generic_id,
196-
SemIR::SpecificId enclosing_specific_id, bool diagnose);
196+
SemIR::SpecificId enclosing_specific_id,
197+
SemIR::InstId self_type_id, bool diagnose);
197198

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

@@ -250,7 +251,7 @@ static auto NoteGenericHere(Context& context, SemIR::GenericId generic_id,
250251
DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
251252
SemIR::GenericId generic_id,
252253
SemIR::SpecificId enclosing_specific_id,
253-
bool diagnose)
254+
SemIR::InstId self_type_id, bool diagnose)
254255
: context_(&context),
255256
loc_id_(loc_id),
256257
generic_id_(generic_id),
@@ -285,6 +286,16 @@ DeductionContext::DeductionContext(Context& context, SemIR::LocId loc_id,
285286
first_deduced_index_ = SemIR::CompileTimeBindIndex(args.size());
286287
}
287288

289+
if (self_type_id.has_value()) {
290+
// Copy the provided `Self` type as the value of the next binding.
291+
auto self_index = first_deduced_index_;
292+
result_arg_ids_[self_index.index] = self_type_id;
293+
substitutions_.push_back(
294+
{.bind_id = SemIR::CompileTimeBindIndex(self_index),
295+
.replacement_id = context.constant_values().Get(self_type_id)});
296+
first_deduced_index_ = SemIR::CompileTimeBindIndex(self_index.index + 1);
297+
}
298+
288299
non_deduced_indexes_.resize(result_arg_ids_.size() -
289300
first_deduced_index_.index);
290301
}
@@ -504,19 +515,17 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
504515
auto DeductionContext::MakeSpecific() -> SemIR::SpecificId {
505516
// TODO: Convert the deduced values to the types of the bindings.
506517

507-
return Check::MakeSpecific(
508-
context(), loc_id_, generic_id_,
509-
context().inst_blocks().AddCanonical(result_arg_ids_));
518+
return Check::MakeSpecific(context(), loc_id_, generic_id_, result_arg_ids_);
510519
}
511520

512521
auto DeduceGenericCallArguments(
513522
Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id,
514-
SemIR::SpecificId enclosing_specific_id,
523+
SemIR::SpecificId enclosing_specific_id, SemIR::InstId self_type_id,
515524
[[maybe_unused]] SemIR::InstBlockId implicit_params_id,
516525
SemIR::InstBlockId params_id, [[maybe_unused]] SemIR::InstId self_id,
517526
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::SpecificId {
518527
DeductionContext deduction(context, loc_id, generic_id, enclosing_specific_id,
519-
/*diagnose=*/true);
528+
self_type_id, /*diagnose=*/true);
520529

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

542552
// Prepare to perform deduction of the type and interface.

toolchain/check/deduce.h

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
namespace Carbon::Check {
1212

1313
// Deduces the generic arguments to use in a call to a generic.
14-
auto DeduceGenericCallArguments(Context& context, SemIR::LocId loc_id,
15-
SemIR::GenericId generic_id,
16-
SemIR::SpecificId enclosing_specific_id,
17-
SemIR::InstBlockId implicit_params_id,
18-
SemIR::InstBlockId params_id,
19-
SemIR::InstId self_id,
20-
llvm::ArrayRef<SemIR::InstId> arg_ids)
14+
auto DeduceGenericCallArguments(
15+
Context& context, SemIR::LocId loc_id, SemIR::GenericId generic_id,
16+
SemIR::SpecificId enclosing_specific_id, SemIR::InstId self_type_id,
17+
SemIR::InstBlockId implicit_params_id, SemIR::InstBlockId params_id,
18+
SemIR::InstId self_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
2119
-> SemIR::SpecificId;
2220

2321
// Deduces the impl arguments to use in a use of a parameterized impl. Returns

toolchain/check/eval.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,11 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
15881588
case SemIR::FunctionType::Kind:
15891589
return RebuildIfFieldsAreConstant(eval_context, inst,
15901590
&SemIR::FunctionType::specific_id);
1591+
case SemIR::FunctionTypeWithSelfType::Kind:
1592+
return RebuildIfFieldsAreConstant(
1593+
eval_context, inst,
1594+
&SemIR::FunctionTypeWithSelfType::interface_function_type_id,
1595+
&SemIR::FunctionTypeWithSelfType::self_id);
15911596
case SemIR::GenericClassType::Kind:
15921597
return RebuildIfFieldsAreConstant(
15931598
eval_context, inst, &SemIR::GenericClassType::enclosing_specific_id);

toolchain/check/generic.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,12 @@ auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
429429
return specific_id;
430430
}
431431

432+
auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
433+
llvm::ArrayRef<SemIR::InstId> args) -> SemIR::SpecificId {
434+
auto args_id = context.inst_blocks().AddCanonical(args);
435+
return MakeSpecific(context, loc, generic_id, args_id);
436+
}
437+
432438
static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id)
433439
-> SemIR::SpecificId {
434440
if (!generic_id.has_value()) {

toolchain/check/generic.h

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,17 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
5252
llvm::ArrayRef<SemIR::InstId> const_ids)
5353
-> SemIR::InstBlockId;
5454

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

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

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

0 commit comments

Comments
 (0)