Skip to content

Commit e1b87ac

Browse files
authored
Change IndexWith to use a standard binary operator setup (carbon-language#6127)
This is closer to [the design](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/expressions/indexing.md?plain=1#L55-L64), just lacking `ref`, but does remove a lot of special-casing done for the lookup. The `ErrorInst` changes in `Build*Operator` are to align with what was being done for `IndexWith`; don't do an interface lookup if the relevant operand is an error. Otherwise, that becomes visible because some files have an error operand and don't provide the interface.
1 parent 49ba8cf commit e1b87ac

File tree

10 files changed

+325
-321
lines changed

10 files changed

+325
-321
lines changed

core/prelude/operators/index.carbon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package Core library "prelude/operators/index";
66

7-
interface IndexWith(SubscriptType:! type, ElementType:! type) {
7+
interface IndexWith(SubscriptType:! type) {
8+
let ElementType:! type;
89
fn At[self: Self](subscript: SubscriptType) -> ElementType;
910
}

toolchain/check/handle_index.cpp

Lines changed: 8 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -26,105 +26,20 @@ auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/)
2626
return true;
2727
}
2828

29-
// Returns the argument values of the `IndexWith` interface. Arguments
30-
// correspond to the `SubscriptType` and the `ElementType`. If no arguments are
31-
// used to define `IndexWith`, this returns an empty array reference. If the
32-
// class does not implement the said interface, this returns a `std::nullopt`.
33-
// TODO: Switch to using an associated type instead of a parameter for the
34-
// `ElementType`.
35-
static auto GetIndexWithArgs(Context& context, Parse::NodeId node_id,
36-
SemIR::TypeId self_id)
37-
-> std::optional<llvm::ArrayRef<SemIR::InstId>> {
38-
auto index_with_inst_id = LookupNameInCore(context, node_id, "IndexWith");
39-
// If the `IndexWith` interface doesn't have generic arguments then return an
40-
// empty reference.
41-
if (context.insts().Is<SemIR::FacetType>(index_with_inst_id)) {
42-
return llvm::ArrayRef<SemIR::InstId>();
43-
}
44-
45-
auto index_with_inst =
46-
context.insts().TryGetAsIfValid<SemIR::StructValue>(index_with_inst_id);
47-
if (!index_with_inst) {
48-
return std::nullopt;
49-
}
50-
51-
auto index_with_interface =
52-
context.types().TryGetAs<SemIR::GenericInterfaceType>(
53-
index_with_inst->type_id);
54-
if (!index_with_interface) {
55-
return std::nullopt;
56-
}
57-
58-
for (const auto& impl : context.impls().values()) {
59-
auto impl_self_type_id =
60-
context.types().GetTypeIdForTypeInstId(impl.self_id);
61-
auto impl_constraint_type_id =
62-
context.types().GetTypeIdForTypeInstId(impl.constraint_id);
63-
64-
if (impl_self_type_id != self_id) {
65-
continue;
66-
}
67-
auto facet_type =
68-
context.types().TryGetAs<SemIR::FacetType>(impl_constraint_type_id);
69-
if (!facet_type) {
70-
continue;
71-
}
72-
const auto& facet_type_info =
73-
context.facet_types().Get(facet_type->facet_type_id);
74-
auto interface_type = facet_type_info.TryAsSingleInterface();
75-
if (!interface_type) {
76-
continue;
77-
}
78-
if (index_with_interface->interface_id != interface_type->interface_id) {
79-
continue;
80-
}
81-
82-
return context.inst_blocks().GetOrEmpty(
83-
context.specifics().Get(interface_type->specific_id).args_id);
84-
}
85-
86-
return std::nullopt;
87-
}
88-
8929
// Performs an index with base expression `operand_inst_id` and
9030
// `operand_type_id` for types that are not an array. This checks if
9131
// the base expression implements the `IndexWith` interface; if so, uses the
9232
// `At` associative method, otherwise prints a diagnostic.
9333
static auto PerformIndexWith(Context& context, Parse::NodeId node_id,
9434
SemIR::InstId operand_inst_id,
95-
SemIR::TypeId operand_type_id,
9635
SemIR::InstId index_inst_id) -> SemIR::InstId {
97-
auto args = GetIndexWithArgs(context, node_id, operand_type_id);
98-
99-
// If the type does not implement the `IndexWith` interface, then return
100-
// an error.
101-
if (!args) {
102-
CARBON_DIAGNOSTIC(TypeNotIndexable, Error,
103-
"type {0} does not support indexing", SemIR::TypeId);
104-
context.emitter().Emit(node_id, TypeNotIndexable, operand_type_id);
105-
return SemIR::ErrorInst::InstId;
106-
}
107-
108-
Operator op{
109-
.interface_name = "IndexWith",
110-
.interface_args_ref = *args,
111-
.op_name = "At",
112-
};
113-
114-
// IndexWith is defined without generic arguments.
115-
if (args->empty()) {
116-
return BuildBinaryOperator(context, node_id, op, operand_inst_id,
117-
index_inst_id);
118-
}
119-
120-
// The first argument of the `IndexWith` interface corresponds to the
121-
// `SubscriptType`, so first cast `index_inst_id` to that type.
122-
auto subscript_type_id = context.types().GetTypeIdForTypeInstId((*args)[0]);
123-
auto cast_index_id =
124-
ConvertToValueOfType(context, node_id, index_inst_id, subscript_type_id);
125-
36+
SemIR::InstId args[] = {
37+
context.types().GetInstId(context.insts().Get(index_inst_id).type_id())};
38+
Operator op{.interface_name = "IndexWith",
39+
.interface_args_ref = args,
40+
.op_name = "At"};
12641
return BuildBinaryOperator(context, node_id, op, operand_inst_id,
127-
cast_index_id);
42+
index_inst_id);
12843
}
12944

13045
auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
@@ -170,11 +85,8 @@ auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
17085
}
17186

17287
default: {
173-
auto elem_id = SemIR::ErrorInst::InstId;
174-
if (operand_type_id != SemIR::ErrorInst::TypeId) {
175-
elem_id = PerformIndexWith(context, node_id, operand_inst_id,
176-
operand_type_id, index_inst_id);
177-
}
88+
auto elem_id =
89+
PerformIndexWith(context, node_id, operand_inst_id, index_inst_id);
17890
context.node_stack().Push(node_id, elem_id);
17991
return true;
18092
}

toolchain/check/operator.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id,
2525
auto implicit_loc_id = context.insts().GetLocIdForDesugaring(loc_id);
2626

2727
// Look up the interface, and pass it any generic arguments.
28+
// TODO: Improve diagnostics when the found `interface_id` isn't callable.
2829
auto interface_id =
2930
LookupNameInCore(context, implicit_loc_id, op.interface_name);
3031
if (!op.interface_args_ref.empty()) {
@@ -58,6 +59,11 @@ auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
5859
SemIR::InstId operand_id,
5960
MakeDiagnosticBuilderFn missing_impl_diagnoser)
6061
-> SemIR::InstId {
62+
if (operand_id == SemIR::ErrorInst::InstId) {
63+
// Exit early for errors, which prevent forming an `Op` function.
64+
return SemIR::ErrorInst::InstId;
65+
}
66+
6167
// For unary operators with a C++ class as the operand, try to import and call
6268
// the C++ operator.
6369
// TODO: Change impl lookup instead. See
@@ -91,6 +97,11 @@ auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
9197
SemIR::InstId lhs_id, SemIR::InstId rhs_id,
9298
MakeDiagnosticBuilderFn missing_impl_diagnoser)
9399
-> SemIR::InstId {
100+
if (lhs_id == SemIR::ErrorInst::InstId) {
101+
// Exit early for errors, which prevent forming an `Op` function.
102+
return SemIR::ErrorInst::InstId;
103+
}
104+
94105
// For binary operators with a C++ class as at least one of the operands, try
95106
// to import and call the C++ operator.
96107
// TODO: Instead of hooking this here, change impl lookup, so that a generic

toolchain/check/testdata/facet/aggregate_through_access.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface Z {
4747
let X:! type;
4848
}
4949

50-
// CHECK:STDERR: fail_todo_array_access_through_witness.carbon:[[@LINE+4]]:40: error: type `type` does not support indexing [TypeNotIndexable]
50+
// CHECK:STDERR: fail_todo_array_access_through_witness.carbon:[[@LINE+4]]:40: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `type` that does not implement that interface [MissingImplInMemberAccess]
5151
// CHECK:STDERR: fn F(T:! Z where .X = array({}, 1)) -> T.X[0] {
5252
// CHECK:STDERR: ^~~~~~
5353
// CHECK:STDERR:

toolchain/check/testdata/index/fail_invalid_base.carbon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ fn F();
2727
// CHECK:STDERR:
2828
var b: i32 = F[1];
2929

30-
// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+4]]:14: error: type `{.a: Core.IntLiteral, .b: Core.IntLiteral}` does not support indexing [TypeNotIndexable]
30+
// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+4]]:14: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `{.a: Core.IntLiteral, .b: Core.IntLiteral}` that does not implement that interface [MissingImplInMemberAccess]
3131
// CHECK:STDERR: var c: i32 = {.a = 1, .b = 2}[0];
3232
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
3333
// CHECK:STDERR:
3434
var c: i32 = {.a = 1, .b = 2}[0];
3535

36-
// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+4]]:14: error: type `type` does not support indexing [TypeNotIndexable]
36+
// CHECK:STDERR: fail_invalid_base.carbon:[[@LINE+4]]:14: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `type` that does not implement that interface [MissingImplInMemberAccess]
3737
// CHECK:STDERR: var d: i32 = {.a: i32, .b: i32}[0];
3838
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
3939
// CHECK:STDERR:

toolchain/check/testdata/index/fail_non_tuple_access.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/index/fail_non_tuple_access.carbon
1414

1515
fn Main() {
16-
// CHECK:STDERR: fail_non_tuple_access.carbon:[[@LINE+4]]:3: error: type `Core.IntLiteral` does not support indexing [TypeNotIndexable]
16+
// CHECK:STDERR: fail_non_tuple_access.carbon:[[@LINE+4]]:3: error: cannot access member of interface `Core.IndexWith(Core.IntLiteral)` in type `Core.IntLiteral` that does not implement that interface [MissingImplInMemberAccess]
1717
// CHECK:STDERR: 0[1];
1818
// CHECK:STDERR: ^~~~
1919
// CHECK:STDERR:

toolchain/check/testdata/operators/builtin/fail_type_mismatch_once.carbon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ fn Main() -> i32 {
7272
// CHECK:STDOUT: %.loc22: %empty_struct_type = struct_literal ()
7373
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 34e-1 [concrete = constants.%float]
7474
// CHECK:STDOUT: %int_12: Core.IntLiteral = int_value 12 [concrete = constants.%int_12]
75-
// CHECK:STDOUT: %impl.elem1: <error> = impl_witness_access <error>, element1 [concrete = <error>]
7675
// CHECK:STDOUT: return <error> to %return
7776
// CHECK:STDOUT: }
7877
// CHECK:STDOUT:

0 commit comments

Comments
 (0)