Files
carbon-lang/toolchain/sem_ir/expr_info.cpp
T
Ivana Ivanovska 12ddfb9c7c [Carbon/C++ interop] Add support for C++ overloaded functions (#5891)
As proposed in [Carbon: C++ interop for overloaded functions and
function
templates](https://docs.google.com/document/d/1KUxumZtNe3mY3TsjW2s_ZADOlAaFlrtsLKHVILtqIaM/edit?tab=t.0),
Clang is used to perform the overload resolution using C++ rules, when
an overloaded C++ set is called from Carbon. Once a function is
selected, it's converted into a Carbon function and called using the
Carbon rules including argument conversions.

A single non-templated function is treated the same way as an overload
set and the same rules apply for its call.
Template functions are not supported yet.

Demo:

a) Non-templated function calls:

```c++
// --- overloads.h

auto foo(int a, short b) -> void;
auto foo(double a) -> void;
auto foo(int a) -> void;
```

```c++
// overloads.cpp

#include "overloads.h"
#include <cstdio>

auto foo(int a, short b) -> void {
  printf("hello from foo_int_short(%d, %d) \n", a, b);
}
auto foo(double a) -> void { printf("hello from foo_double(%f) \n", a); }
auto foo(int a) -> void { printf("hello from foo_int(%d) \n", a); }
```
```c++
library "Main";

import Cpp library "overloads.h";

fn Run() -> i32 {
  Cpp.foo(1.1 as f64);
  return 0;
}
```
```
$ clang -c overloads.cpp 
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link overloads.o main.o --output=demo
$ ./demo
hello from foo_double(1.100000) 
```

b) Constructors:
```c++
// --- constructor_overloads.h
class C {
 public:
  C();
  C(int a, int b);
};
```

```c++
// constructor_overloads.cpp
#include "constructor_overloads.h"
#include <cstdio>

C::C() { printf("hello from C() \n"); }
C::C(int a, int b) { printf("hello from C(%d, %d) \n", a, b); }
```
```c++
library "Main";

import Cpp library "constructor_overloads.h";

fn Run() -> i32 {
  let c1: Cpp.C = Cpp.C.C();
  let c2: Cpp.C = Cpp.C.C(1, 2);
  return 0;
}
```

```
$ clang -c constructor_overloads.cpp 
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link constructor_overloads.o main.o \--output=demo
$ ./demo
hello from C() 
hello from C(1, 2) 
```


Follow-ups:

- `Cpp.foo({})` - proper handling of struct literals as call args.
- Fix access for overloaded sets.
- Fix tests:
- Method calls: `error: missing object argument in method call
[MissingObjectInMethodCall]` in tests.
    - Fix `toolchain/check/testdata/interop/cpp/import.carbon` test.
    - Fix `enums` support.
    - Fix `str` -> `std::string_view` mapping.


Part of #5915
2025-09-15 12:29:49 +00:00

303 lines
8.7 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/sem_ir/expr_info.h"
#include "common/check.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
const File* ir = &file;
// The overall expression category if the current instruction is a value
// expression.
ExprCategory value_category = ExprCategory::Value;
while (true) {
auto untyped_inst = ir->insts().Get(inst_id);
CARBON_KIND_SWITCH(untyped_inst) {
case AdaptDecl::Kind:
case AddrPattern::Kind:
case Assign::Kind:
case BaseDecl::Kind:
case BindingPattern::Kind:
case Branch::Kind:
case BranchIf::Kind:
case BranchWithArg::Kind:
case CppOverloadSetValue::Kind:
case FieldDecl::Kind:
case FunctionDecl::Kind:
case ImplDecl::Kind:
case NameBindingDecl::Kind:
case Namespace::Kind:
case OutParamPattern::Kind:
case RefParamPattern::Kind:
case RequirementBaseFacetType::Kind:
case RequirementEquivalent::Kind:
case RequirementImpls::Kind:
case RequirementRewrite::Kind:
case Return::Kind:
case ReturnSlotPattern::Kind:
case ReturnExpr::Kind:
case SymbolicBindingPattern::Kind:
case TuplePattern::Kind:
case ValueParamPattern::Kind:
case VarPattern::Kind:
return ExprCategory::NotExpr;
case ImportRefUnloaded::Kind:
case ImportRefLoaded::Kind: {
auto import_ir_inst = ir->import_ir_insts().Get(
untyped_inst.As<AnyImportRef>().import_ir_inst_id);
ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
inst_id = import_ir_inst.inst_id();
continue;
}
case CARBON_KIND(AsCompatible inst): {
inst_id = inst.source_id;
continue;
}
case CARBON_KIND(BindAlias inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(ExportDecl inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(NameRef inst): {
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(Converted inst): {
inst_id = inst.result_id;
continue;
}
case CARBON_KIND(ImplWitnessAssociatedConstant inst): {
inst_id = inst.inst_id;
continue;
}
case CARBON_KIND(SpecificConstant inst): {
inst_id = inst.inst_id;
continue;
}
case AccessMemberAction::Kind:
case AccessOptionalMemberAction::Kind:
case AddrOf::Kind:
case ArrayType::Kind:
case AssociatedConstantDecl::Kind:
case AssociatedEntity::Kind:
case AssociatedEntityType::Kind:
case AutoType::Kind:
case BindSymbolicName::Kind:
case BindValue::Kind:
case BlockArg::Kind:
case BoolLiteral::Kind:
case BoolType::Kind:
case BoundMethod::Kind:
case BoundMethodType::Kind:
case CharLiteralType::Kind:
case CharLiteralValue::Kind:
case ClassDecl::Kind:
case ClassType::Kind:
case CompleteTypeWitness::Kind:
case ConstType::Kind:
case ConvertToValueAction::Kind:
case CppOverloadSetType::Kind:
case CustomLayoutType::Kind:
case FacetAccessType::Kind:
case FacetType::Kind:
case FacetValue::Kind:
case FloatLiteralType::Kind:
case FloatLiteralValue::Kind:
case FloatType::Kind:
case FloatValue::Kind:
case FunctionType::Kind:
case FunctionTypeWithSelfType::Kind:
case GenericClassType::Kind:
case GenericInterfaceType::Kind:
case LookupImplWitness::Kind:
case ImplWitness::Kind:
case ImplWitnessAccess::Kind:
case ImplWitnessAccessSubstituted::Kind:
case ImplWitnessTable::Kind:
case ImplWitnessTablePlaceholder::Kind:
case ImportCppDecl::Kind:
case ImportDecl::Kind:
case InstType::Kind:
case InstValue::Kind:
case IntLiteralType::Kind:
case IntType::Kind:
case IntValue::Kind:
case InterfaceDecl::Kind:
case MaybeUnformedType::Kind:
case NamespaceType::Kind:
case PartialType::Kind:
case PatternType::Kind:
case PointerType::Kind:
case RefineTypeAction::Kind:
case RequireCompleteType::Kind:
case SpecificFunction::Kind:
case SpecificFunctionType::Kind:
case SpecificImplFunction::Kind:
case StringLiteral::Kind:
case StructType::Kind:
case StructValue::Kind:
case TupleType::Kind:
case TupleValue::Kind:
case TypeOfInst::Kind:
case TypeType::Kind:
case UnaryOperatorNot::Kind:
case UnboundElementType::Kind:
case ValueOfInitializer::Kind:
case ValueParam::Kind:
case VtableType::Kind:
case WhereExpr::Kind:
case WitnessType::Kind:
return value_category;
case ErrorInst::Kind:
return ExprCategory::Error;
case CARBON_KIND(BindName inst): {
// TODO: Don't rely on value_id for expression category, since it may
// not be valid yet. This workaround only works because we don't support
// `var` in function signatures yet.
if (!inst.value_id.has_value()) {
return value_category;
}
inst_id = inst.value_id;
continue;
}
case CARBON_KIND(ArrayIndex inst): {
inst_id = inst.array_id;
continue;
}
case VtablePtr::Kind:
case VtableDecl::Kind:
return ExprCategory::EphemeralRef;
case CARBON_KIND(ClassElementAccess inst): {
inst_id = inst.base_id;
// A value of class type is a pointer to an object representation.
// Therefore, if the base is a value, the result is an ephemeral
// reference.
value_category = ExprCategory::EphemeralRef;
continue;
}
case CARBON_KIND(StructAccess inst): {
inst_id = inst.struct_id;
continue;
}
case CARBON_KIND(TupleAccess inst): {
inst_id = inst.tuple_id;
continue;
}
case CARBON_KIND(SpliceBlock inst): {
inst_id = inst.result_id;
continue;
}
case SpliceInst::Kind:
// TODO: Add ExprCategory::Dependent.
return value_category;
case StructLiteral::Kind:
case TupleLiteral::Kind:
return ExprCategory::Mixed;
case ArrayInit::Kind:
case Call::Kind:
case InitializeFrom::Kind:
case InPlaceInit::Kind:
case ClassInit::Kind:
case StructInit::Kind:
case TupleInit::Kind:
return ExprCategory::Initializing;
case Deref::Kind:
case VarStorage::Kind:
case ReturnSlot::Kind:
return ExprCategory::DurableRef;
case Temporary::Kind:
case TemporaryStorage::Kind:
case ValueAsRef::Kind:
return ExprCategory::EphemeralRef;
case OutParam::Kind:
case RefParam::Kind:
// TODO: Consider introducing a separate category for OutParam:
// unlike other DurableRefs, it permits initialization.
return ExprCategory::DurableRef;
}
}
}
auto FindReturnSlotArgForInitializer(const File& sem_ir, InstId init_id)
-> InstId {
while (true) {
Inst init_untyped = sem_ir.insts().Get(init_id);
CARBON_KIND_SWITCH(init_untyped) {
case CARBON_KIND(AsCompatible init): {
init_id = init.source_id;
continue;
}
case CARBON_KIND(Converted init): {
init_id = init.result_id;
continue;
}
case CARBON_KIND(ArrayInit init): {
return init.dest_id;
}
case CARBON_KIND(ClassInit init): {
return init.dest_id;
}
case CARBON_KIND(StructInit init): {
return init.dest_id;
}
case CARBON_KIND(TupleInit init): {
return init.dest_id;
}
case CARBON_KIND(InitializeFrom init): {
return init.dest_id;
}
case CARBON_KIND(InPlaceInit init): {
if (!ReturnTypeInfo::ForType(sem_ir, init.type_id).has_return_slot()) {
return InstId::None;
}
return init.dest_id;
}
case CARBON_KIND(Call call): {
if (!ReturnTypeInfo::ForType(sem_ir, call.type_id).has_return_slot()) {
return InstId::None;
}
if (!call.args_id.has_value()) {
// Argument initialization failed, so we have no return slot.
return InstId::None;
}
return sem_ir.inst_blocks().Get(call.args_id).back();
}
default:
CARBON_FATAL("Initialization from unexpected inst {0}", init_untyped);
}
}
}
} // namespace Carbon::SemIR