22// Exceptions. See /LICENSE for license information.
33// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44
5+ #include < optional>
6+
57#include " toolchain/base/kind_switch.h"
68#include " toolchain/check/context.h"
79#include " toolchain/check/convert.h"
810#include " toolchain/check/handle.h"
11+ #include " toolchain/check/operator.h"
12+ #include " toolchain/diagnostics/diagnostic.h"
13+ #include " toolchain/sem_ir/builtin_inst_kind.h"
914#include " toolchain/sem_ir/inst.h"
15+ #include " toolchain/sem_ir/typed_insts.h"
1016
1117namespace Carbon ::Check {
1218
@@ -16,17 +22,113 @@ auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/)
1622 return true ;
1723}
1824
25+ // Returns the argument values of the `IndexWith` interface. Arguments
26+ // correspond to the `SubscriptType` and the `ElementType`. If no arguments are
27+ // used to define `IndexWith`, this returns an empty array reference. If the
28+ // class does not implement the said interface, this returns a `std::nullopt`.
29+ // TODO: Switch to using an associated type instead of a parameter for the
30+ // `ElementType`.
31+ static auto GetIndexWithArgs (Context& context, Parse::NodeId node_id,
32+ SemIR::TypeId self_id)
33+ -> std::optional<llvm::ArrayRef<SemIR::InstId>> {
34+ auto index_with_inst_id = context.LookupNameInCore (node_id, " IndexWith" );
35+ // If the `IndexWith` interface doesn't have generic arguments then return an
36+ // empty reference.
37+ if (context.insts ().Is <SemIR::InterfaceType>(index_with_inst_id)) {
38+ return llvm::ArrayRef<SemIR::InstId>();
39+ }
40+
41+ auto index_with_inst =
42+ context.insts ().TryGetAsIfValid <SemIR::StructValue>(index_with_inst_id);
43+ if (!index_with_inst) {
44+ return std::nullopt ;
45+ }
46+
47+ auto index_with_interface =
48+ context.types ().TryGetAs <SemIR::GenericInterfaceType>(
49+ index_with_inst->type_id );
50+ if (!index_with_interface) {
51+ return std::nullopt ;
52+ }
53+
54+ for (const auto & impl : context.impls ().array_ref ()) {
55+ auto impl_self_type_id = context.GetTypeIdForTypeInst (impl.self_id );
56+ auto impl_constraint_type_id =
57+ context.GetTypeIdForTypeInst (impl.constraint_id );
58+
59+ if (impl_self_type_id != self_id) {
60+ continue ;
61+ }
62+ auto interface_type =
63+ context.types ().TryGetAs <SemIR::InterfaceType>(impl_constraint_type_id);
64+ if (!interface_type) {
65+ continue ;
66+ }
67+
68+ if (index_with_interface->interface_id != interface_type->interface_id ) {
69+ continue ;
70+ }
71+
72+ return context.inst_blocks ().GetOrEmpty (
73+ context.specifics ().Get (interface_type->specific_id ).args_id );
74+ }
75+
76+ return std::nullopt ;
77+ }
78+
79+ // Performs an index with base expression `operand_inst_id` and
80+ // `operand_type_id` for types that are not an array. This checks if
81+ // the base expression implements the `IndexWith` interface; if so, uses the
82+ // `At` associative method, otherwise prints a diagnostic.
83+ static auto PerformIndexWith (Context& context, Parse::NodeId node_id,
84+ SemIR::InstId operand_inst_id,
85+ SemIR::TypeId operand_type_id,
86+ SemIR::InstId index_inst_id) -> SemIR::InstId {
87+ auto args = GetIndexWithArgs (context, node_id, operand_type_id);
88+
89+ // If the type does not implement the `IndexWith` interface, then return
90+ // an error.
91+ if (!args) {
92+ CARBON_DIAGNOSTIC (TypeNotIndexable, Error,
93+ " type {0} does not support indexing" , SemIR::TypeId);
94+ context.emitter ().Emit (node_id, TypeNotIndexable, operand_type_id);
95+ return SemIR::InstId::BuiltinError;
96+ }
97+
98+ Operator op{
99+ .interface_name = " IndexWith" ,
100+ .interface_args_ref = *args,
101+ .op_name = " At" ,
102+ };
103+
104+ // IndexWith is defined without generic arguments.
105+ if (args->empty ()) {
106+ return BuildBinaryOperator (context, node_id, op, operand_inst_id,
107+ index_inst_id);
108+ }
109+
110+ // The first argument of the `IndexWith` interface corresponds to the
111+ // `SubscriptType`, so first cast `index_inst_id` to that type.
112+ auto subscript_type_id = context.GetTypeIdForTypeInst ((*args)[0 ]);
113+ auto cast_index_id =
114+ ConvertToValueOfType (context, node_id, index_inst_id, subscript_type_id);
115+
116+ return BuildBinaryOperator (context, node_id, op, operand_inst_id,
117+ cast_index_id);
118+ }
119+
19120auto HandleParseNode (Context& context, Parse::IndexExprId node_id) -> bool {
20121 auto index_inst_id = context.node_stack ().PopExpr ();
21122 auto operand_inst_id = context.node_stack ().PopExpr ();
22123 operand_inst_id = ConvertToValueOrRefExpr (context, operand_inst_id);
23124 auto operand_inst = context.insts ().Get (operand_inst_id);
24125 auto operand_type_id = operand_inst.type_id ();
126+
25127 CARBON_KIND_SWITCH (context.types ().GetAsInst (operand_type_id)) {
26128 case CARBON_KIND (SemIR::ArrayType array_type): {
27- auto index_node_id = context.insts ().GetLocId (index_inst_id);
129+ auto index_loc_id = context.insts ().GetLocId (index_inst_id);
28130 auto cast_index_id = ConvertToValueOfType (
29- context, index_node_id , index_inst_id,
131+ context, index_loc_id , index_inst_id,
30132 context.GetBuiltinType (SemIR::BuiltinInstKind::IntType));
31133 auto array_cat =
32134 SemIR::GetExprCategory (context.sem_ir (), operand_inst_id);
@@ -52,13 +154,14 @@ auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
52154 context.node_stack ().Push (node_id, elem_id);
53155 return true ;
54156 }
157+
55158 default : {
159+ auto elem_id = SemIR::InstId::BuiltinError;
56160 if (operand_type_id != SemIR::TypeId::Error) {
57- CARBON_DIAGNOSTIC (TypeNotIndexable, Error,
58- " type {0} does not support indexing" , TypeOfInstId);
59- context.emitter ().Emit (node_id, TypeNotIndexable, operand_inst_id);
161+ elem_id = PerformIndexWith (context, node_id, operand_inst_id,
162+ operand_type_id, index_inst_id);
60163 }
61- context.node_stack ().Push (node_id, SemIR::InstId::BuiltinError );
164+ context.node_stack ().Push (node_id, elem_id );
62165 return true ;
63166 }
64167 }
0 commit comments