mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +01:00
I'm doing this because I figured it'd be an incremental improvement for all the operator lookups that we do. Even to the extent that we've discussed witness caching, I think it'll still apply. It does add one more step to adding new interfaces (before, you'd just write the string, now you add it to the def file and reference it). I'll claim it makes GetClangOperatorKind a lot friendlier to read/edit, nevermind removing the string comparisons. :)
153 lines
5.9 KiB
C++
153 lines
5.9 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/check/operator.h"
|
|
|
|
#include <optional>
|
|
|
|
#include "toolchain/check/call.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/cpp/call.h"
|
|
#include "toolchain/check/cpp/operators.h"
|
|
#include "toolchain/check/generic.h"
|
|
#include "toolchain/check/member_access.h"
|
|
#include "toolchain/check/name_lookup.h"
|
|
#include "toolchain/sem_ir/class.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
#include "toolchain/sem_ir/name_scope.h"
|
|
#include "toolchain/sem_ir/typed_insts.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
// Returns the `Op` function for the specified operator.
|
|
static auto GetOperatorOpFunction(Context& context, SemIR::LocId loc_id,
|
|
Operator op) -> SemIR::InstId {
|
|
auto implicit_loc_id = context.insts().GetLocIdForDesugaring(loc_id);
|
|
|
|
// Look up the interface, and pass it any generic arguments.
|
|
// TODO: Improve diagnostics when the found `interface_id` isn't callable.
|
|
auto interface_id =
|
|
LookupNameInCore(context, implicit_loc_id, op.interface_name);
|
|
if (!op.interface_args_ref.empty()) {
|
|
interface_id = PerformCall(context, implicit_loc_id, interface_id,
|
|
op.interface_args_ref);
|
|
}
|
|
|
|
// Look up the interface member.
|
|
auto op_name_id = context.core_identifiers().AddNameId(op.op_name);
|
|
return PerformMemberAccess(context, implicit_loc_id, interface_id,
|
|
op_name_id);
|
|
}
|
|
|
|
// Returns whether the instruction is a C++ class.
|
|
static auto IsCppClassType(Context& context, SemIR::InstId inst_id) -> bool {
|
|
auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
|
|
context.types().GetInstId(context.insts().Get(inst_id).type_id()));
|
|
if (!class_type) {
|
|
// Not a class.
|
|
return false;
|
|
}
|
|
|
|
SemIR::NameScopeId class_scope_id =
|
|
context.classes().Get(class_type->class_id).scope_id;
|
|
return class_scope_id.has_value() &&
|
|
context.name_scopes().Get(class_scope_id).is_cpp_scope();
|
|
}
|
|
|
|
auto BuildUnaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
|
|
SemIR::InstId operand_id,
|
|
MakeDiagnosticBuilderFn missing_impl_diagnoser)
|
|
-> SemIR::InstId {
|
|
if (operand_id == SemIR::ErrorInst::InstId) {
|
|
// Exit early for errors, which prevent forming an `Op` function.
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Operator operands don't require `ref` tags.
|
|
context.ref_tags().Insert(operand_id, Context::RefTag::NotRequired);
|
|
|
|
SemIR::InstId op_fn_id = SemIR::InstId::None;
|
|
|
|
// For unary operators with a C++ class as the operand, try to import and call
|
|
// the C++ operator.
|
|
// TODO: Change impl lookup instead. See
|
|
// https://github.com/carbon-language/carbon-lang/blob/db0a00d713015436844c55e7ac190a0f95556499/toolchain/check/operator.cpp#L76
|
|
if (IsCppClassType(context, operand_id)) {
|
|
op_fn_id = LookupCppOperator(context, loc_id, op, {operand_id});
|
|
|
|
// If C++ operator lookup found a non-method operator, call it with one call
|
|
// argument. Otherwise fall through to call it with a self argument.
|
|
if (op_fn_id.has_value() && !IsCppOperatorMethod(context, op_fn_id)) {
|
|
return PerformCall(context, loc_id, op_fn_id, {operand_id});
|
|
}
|
|
}
|
|
|
|
if (!op_fn_id.has_value()) {
|
|
// Look up the operator function.
|
|
op_fn_id = GetOperatorOpFunction(context, loc_id, op);
|
|
}
|
|
|
|
// Form `operand.(Op)`.
|
|
auto bound_op_id = PerformCompoundMemberAccess(
|
|
context, loc_id, operand_id, op_fn_id, missing_impl_diagnoser);
|
|
if (bound_op_id == SemIR::ErrorInst::InstId) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Form `bound_op()`.
|
|
return PerformCall(context, loc_id, bound_op_id, {});
|
|
}
|
|
|
|
auto BuildBinaryOperator(Context& context, SemIR::LocId loc_id, Operator op,
|
|
SemIR::InstId lhs_id, SemIR::InstId rhs_id,
|
|
MakeDiagnosticBuilderFn missing_impl_diagnoser)
|
|
-> SemIR::InstId {
|
|
if (lhs_id == SemIR::ErrorInst::InstId) {
|
|
// Exit early for errors, which prevent forming an `Op` function.
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Operator operands don't require `ref` tags.
|
|
context.ref_tags().Insert(lhs_id, Context::RefTag::NotRequired);
|
|
context.ref_tags().Insert(rhs_id, Context::RefTag::NotRequired);
|
|
|
|
SemIR::InstId op_fn_id = SemIR::InstId::None;
|
|
|
|
// For binary operators with a C++ class as at least one of the operands, try
|
|
// to import and call the C++ operator.
|
|
// TODO: Instead of hooking this here, change impl lookup, so that a generic
|
|
// constraint such as `T:! Core.Add` is satisfied by C++ class types that are
|
|
// addable. See
|
|
// https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308666348
|
|
// and
|
|
// https://github.com/carbon-language/carbon-lang/pull/5996/files/5d01fa69511b76f87efbc0387f5e40abcf4c911a#r2308664536
|
|
if (IsCppClassType(context, lhs_id) || IsCppClassType(context, rhs_id)) {
|
|
op_fn_id = LookupCppOperator(context, loc_id, op, {lhs_id, rhs_id});
|
|
|
|
// If C++ operator lookup found a non-method operator, call it with two call
|
|
// arguments. Otherwise fall through to call it with a self argument and one
|
|
// call argument.
|
|
if (op_fn_id.has_value() && !IsCppOperatorMethod(context, op_fn_id)) {
|
|
return PerformCall(context, loc_id, op_fn_id, {lhs_id, rhs_id});
|
|
}
|
|
}
|
|
|
|
if (!op_fn_id.has_value()) {
|
|
// Look up the operator function.
|
|
op_fn_id = GetOperatorOpFunction(context, loc_id, op);
|
|
}
|
|
|
|
// Form `lhs.(Op)`.
|
|
auto bound_op_id = PerformCompoundMemberAccess(
|
|
context, loc_id, lhs_id, op_fn_id, missing_impl_diagnoser);
|
|
if (bound_op_id == SemIR::ErrorInst::InstId) {
|
|
return SemIR::ErrorInst::InstId;
|
|
}
|
|
|
|
// Form `bound_op(rhs)`.
|
|
return PerformCall(context, loc_id, bound_op_id, {rhs_id});
|
|
}
|
|
|
|
} // namespace Carbon::Check
|