Skip to content

Commit cf361a8

Browse files
zygoloidjonmeowCarbonInfraBot
authored
Overloaded operator support. (#3796)
Support is added for all overloaded operator interfaces in the current design apart from `Assign`, which is going to require some more work to properly handle, given that primitive assignment currently has a special implementation for quite a few builtin types. As we don't have support for generics yet -- in particular, generic interfaces -- there is no support for `*With` interfaces, but homogenous interfaces such as `Add` are supported instead. Factor out building of call expressions so that overloaded operators can generate calls. Switch a few places from using specific kinds of NodeId to a general NodeId. Because overloaded operators and other things like implicit conversions can result in member access and function calls, those operations can't require a specific kind of NodeId. Add import support for associated entities, and fix import support for interfaces and symbolic bindings. We now import interfaces in two steps, first importing a forward declaration then a definition, just like we do for classes. For symbolic bindings, we ensure that each BindSymbolicName is imported only once, because its ID is used as its symbolic identity. This is necessary because we (only) support operator interfaces that are defined in an imported Carbon package for now. The entire contents of `check/operator.cpp` should probably be rethought. In particular, doing a lot of name lookups on each operator is likely to be bad for performance. But this gets us to the point where overloaded operators are basically working, which seems like a good place to iterate from. For now, the tests that the individual operators map to the right interfaces are mostly generated by a script, but that's just because I'm expecting a fair bit of churn in how we define the prelude and the `impl`s -- in particular, when we add support for `AddWith`, we'll need to update all the tests. The plan is to remove the script once things settle down. --------- Co-authored-by: Jon Ross-Perkins <[email protected]> Co-authored-by: Carbon Infra Bot <[email protected]>
1 parent 15932ac commit cf361a8

Some content is hidden

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

59 files changed

+6358
-511
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ repos:
180180
Exceptions. See /LICENSE for license information.
181181
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
182182
- --custom_format
183-
- '\.(carbon|proto|ypp)$'
183+
- '\.(carbon|proto|ypp)(\.tmpl)?$'
184184
- ''
185185
- '// '
186186
- ''

toolchain/check/BUILD

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ cc_library(
5555
"decl_name_stack.cpp",
5656
"eval.cpp",
5757
"import_ref.cpp",
58-
"import_ref.h",
5958
"inst_block_stack.cpp",
6059
"modifiers.cpp",
6160
"return.cpp",
@@ -66,6 +65,7 @@ cc_library(
6665
"decl_name_stack.h",
6766
"decl_state.h",
6867
"eval.h",
68+
"import_ref.h",
6969
"inst_block_stack.h",
7070
"modifiers.h",
7171
"param_and_arg_refs_stack.h",
@@ -101,12 +101,14 @@ cc_library(
101101
]),
102102
hdrs = ["check.h"],
103103
deps = [
104+
":call",
104105
":context",
105106
":function",
106107
":impl",
107108
":import",
108109
":interface",
109110
":member_access",
111+
":operator",
110112
":pointer_dereference",
111113
"//common:check",
112114
"//common:ostream",
@@ -138,6 +140,20 @@ cc_fuzz_test(
138140
],
139141
)
140142

143+
cc_library(
144+
name = "call",
145+
srcs = ["call.cpp"],
146+
hdrs = ["call.h"],
147+
deps = [
148+
":context",
149+
"//common:check",
150+
"//toolchain/sem_ir:file",
151+
"//toolchain/sem_ir:ids",
152+
"//toolchain/sem_ir:inst",
153+
"//toolchain/sem_ir:inst_kind",
154+
],
155+
)
156+
141157
cc_library(
142158
name = "function",
143159
srcs = ["function.cpp"],
@@ -213,6 +229,23 @@ cc_library(
213229
],
214230
)
215231

232+
cc_library(
233+
name = "operator",
234+
srcs = ["operator.cpp"],
235+
hdrs = ["operator.h"],
236+
deps = [
237+
":call",
238+
":context",
239+
":member_access",
240+
"//common:check",
241+
"//toolchain/parse:node_kind",
242+
"//toolchain/sem_ir:file",
243+
"//toolchain/sem_ir:ids",
244+
"//toolchain/sem_ir:inst",
245+
"//toolchain/sem_ir:inst_kind",
246+
],
247+
)
248+
216249
cc_library(
217250
name = "pointer_dereference",
218251
srcs = ["pointer_dereference.cpp"],

toolchain/check/call.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include "toolchain/check/call.h"
6+
7+
#include "toolchain/check/context.h"
8+
#include "toolchain/check/convert.h"
9+
#include "toolchain/sem_ir/inst.h"
10+
#include "toolchain/sem_ir/typed_insts.h"
11+
12+
namespace Carbon::Check {
13+
14+
auto PerformCall(Context& context, Parse::NodeId node_id,
15+
SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
16+
-> SemIR::InstId {
17+
auto diagnose_not_callable = [&] {
18+
auto callee_type_id = context.insts().Get(callee_id).type_id();
19+
if (callee_type_id != SemIR::TypeId::Error) {
20+
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
21+
"Value of type `{0}` is not callable.", SemIR::TypeId);
22+
context.emitter().Emit(node_id, CallToNonCallable, callee_type_id);
23+
}
24+
return SemIR::InstId::BuiltinError;
25+
};
26+
27+
// For a method call, pick out the `self` value.
28+
auto function_callee_id = callee_id;
29+
SemIR::InstId self_id = SemIR::InstId::Invalid;
30+
if (auto bound_method =
31+
context.insts().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
32+
self_id = bound_method->object_id;
33+
function_callee_id = bound_method->function_id;
34+
}
35+
36+
// Identify the function we're calling.
37+
auto function_decl_id = context.constant_values().Get(function_callee_id);
38+
if (!function_decl_id.is_constant()) {
39+
return diagnose_not_callable();
40+
}
41+
auto function_decl = context.insts()
42+
.Get(function_decl_id.inst_id())
43+
.TryAs<SemIR::FunctionDecl>();
44+
if (!function_decl) {
45+
return diagnose_not_callable();
46+
}
47+
auto function_id = function_decl->function_id;
48+
const auto& callable = context.functions().Get(function_id);
49+
50+
// For functions with an implicit return type, the return type is the empty
51+
// tuple type.
52+
SemIR::TypeId type_id = callable.return_type_id;
53+
if (!type_id.is_valid()) {
54+
type_id = context.GetTupleType({});
55+
}
56+
57+
// If there is a return slot, build storage for the result.
58+
SemIR::InstId return_storage_id = SemIR::InstId::Invalid;
59+
if (callable.return_slot_id.is_valid()) {
60+
// Tentatively put storage for a temporary in the function's return slot.
61+
// This will be replaced if necessary when we perform initialization.
62+
return_storage_id = context.AddInst(
63+
{node_id, SemIR::TemporaryStorage{callable.return_type_id}});
64+
}
65+
66+
// Convert the arguments to match the parameters.
67+
auto converted_args_id =
68+
ConvertCallArgs(context, node_id, self_id, arg_ids, return_storage_id,
69+
function_decl_id.inst_id(),
70+
callable.implicit_param_refs_id, callable.param_refs_id);
71+
auto call_inst_id = context.AddInst(
72+
{node_id, SemIR::Call{type_id, callee_id, converted_args_id}});
73+
74+
return call_inst_id;
75+
}
76+
77+
} // namespace Carbon::Check

toolchain/check/call.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef CARBON_TOOLCHAIN_CHECK_CALL_H_
6+
#define CARBON_TOOLCHAIN_CHECK_CALL_H_
7+
8+
#include "toolchain/check/context.h"
9+
#include "toolchain/sem_ir/ids.h"
10+
11+
namespace Carbon::Check {
12+
13+
// Checks and builds SemIR for a call to `callee_id` with arguments `args_id`.
14+
auto PerformCall(Context& context, Parse::NodeId node_id,
15+
SemIR::InstId callee_id, llvm::ArrayRef<SemIR::InstId> arg_ids)
16+
-> SemIR::InstId;
17+
18+
} // namespace Carbon::Check
19+
20+
#endif // CARBON_TOOLCHAIN_CHECK_CALL_H_

toolchain/check/eval.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static auto RebuildAndValidateIfFieldsAreConstant(
164164
if ((ReplaceFieldWithConstantValue(context, &typed_inst, each_field_id,
165165
&phase) &&
166166
...)) {
167-
if (!validate_fn(typed_inst)) {
167+
if (phase == Phase::UnknownDueToError || !validate_fn(typed_inst)) {
168168
return SemIR::ConstantId::Error;
169169
}
170170
return MakeConstantResult(context, typed_inst, phase);

toolchain/check/handle_call_expr.cpp

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// Exceptions. See /LICENSE for license information.
33
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44

5-
#include "llvm/ADT/ScopeExit.h"
5+
#include "toolchain/check/call.h"
66
#include "toolchain/check/context.h"
7-
#include "toolchain/check/convert.h"
87
#include "toolchain/sem_ir/inst.h"
98

109
namespace Carbon::Check {
@@ -27,74 +26,15 @@ auto HandleCallExpr(Context& context, Parse::CallExprId node_id) -> bool {
2726
// Process the final explicit call argument now, but leave the arguments
2827
// block on the stack until the end of this function.
2928
context.param_and_arg_refs_stack().EndNoPop(Parse::NodeKind::CallExprStart);
30-
auto discard_args_block = llvm::make_scope_exit(
31-
[&] { context.param_and_arg_refs_stack().PopAndDiscard(); });
32-
3329
auto [call_expr_node_id, callee_id] =
3430
context.node_stack().PopWithNodeId<Parse::NodeKind::CallExprStart>();
3531

36-
auto diagnose_not_callable = [&, call_expr_node_id = call_expr_node_id,
37-
callee_id = callee_id] {
38-
auto callee_type_id = context.insts().Get(callee_id).type_id();
39-
if (callee_type_id != SemIR::TypeId::Error) {
40-
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
41-
"Value of type `{0}` is not callable.", SemIR::TypeId);
42-
context.emitter().Emit(call_expr_node_id, CallToNonCallable,
43-
callee_type_id);
44-
}
45-
context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);
46-
return true;
47-
};
48-
49-
// For a method call, pick out the `self` value.
50-
auto function_callee_id = callee_id;
51-
SemIR::InstId self_id = SemIR::InstId::Invalid;
52-
if (auto bound_method =
53-
context.insts().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
54-
self_id = bound_method->object_id;
55-
function_callee_id = bound_method->function_id;
56-
}
57-
58-
// Identify the function we're calling.
59-
auto function_decl_id = context.constant_values().Get(function_callee_id);
60-
if (!function_decl_id.is_constant()) {
61-
return diagnose_not_callable();
62-
}
63-
auto function_decl = context.insts()
64-
.Get(function_decl_id.inst_id())
65-
.TryAs<SemIR::FunctionDecl>();
66-
if (!function_decl) {
67-
return diagnose_not_callable();
68-
}
69-
auto function_id = function_decl->function_id;
70-
const auto& callable = context.functions().Get(function_id);
71-
72-
// For functions with an implicit return type, the return type is the empty
73-
// tuple type.
74-
SemIR::TypeId type_id = callable.return_type_id;
75-
if (!type_id.is_valid()) {
76-
type_id = context.GetTupleType({});
77-
}
78-
79-
// If there is a return slot, build storage for the result.
80-
SemIR::InstId return_storage_id = SemIR::InstId::Invalid;
81-
if (callable.return_slot_id.is_valid()) {
82-
// Tentatively put storage for a temporary in the function's return slot.
83-
// This will be replaced if necessary when we perform initialization.
84-
return_storage_id = context.AddInst(
85-
{call_expr_node_id, SemIR::TemporaryStorage{callable.return_type_id}});
86-
}
87-
88-
// Convert the arguments to match the parameters.
89-
auto converted_args_id = ConvertCallArgs(
90-
context, call_expr_node_id, self_id,
91-
context.param_and_arg_refs_stack().PeekCurrentBlockContents(),
92-
return_storage_id, function_decl_id.inst_id(),
93-
callable.implicit_param_refs_id, callable.param_refs_id);
94-
auto call_inst_id = context.AddInst(
95-
{call_expr_node_id, SemIR::Call{type_id, callee_id, converted_args_id}});
32+
auto call_id = PerformCall(
33+
context, call_expr_node_id, callee_id,
34+
context.param_and_arg_refs_stack().PeekCurrentBlockContents());
9635

97-
context.node_stack().Push(node_id, call_inst_id);
36+
context.param_and_arg_refs_stack().PopAndDiscard();
37+
context.node_stack().Push(node_id, call_id);
9838
return true;
9939
}
10040

0 commit comments

Comments
 (0)