Skip to content

Commit 12ddfb9

Browse files
[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
1 parent 20ac6b9 commit 12ddfb9

Some content is hidden

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

61 files changed

+4631
-2250
lines changed

toolchain/check/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ cc_library(
2222
"control_flow.cpp",
2323
"convert.cpp",
2424
"cpp_custom_type_mapping.cpp",
25+
"cpp_overload_resolution.cpp",
2526
"cpp_thunk.cpp",
27+
"cpp_type_mapping.cpp",
2628
"decl_name_stack.cpp",
2729
"deduce.cpp",
2830
"deferred_definition_worklist.cpp",
@@ -69,7 +71,9 @@ cc_library(
6971
"control_flow.h",
7072
"convert.h",
7173
"cpp_custom_type_mapping.h",
74+
"cpp_overload_resolution.h",
7275
"cpp_thunk.h",
76+
"cpp_type_mapping.h",
7377
"decl_introducer_state.h",
7478
"decl_name_stack.h",
7579
"deduce.h",
@@ -129,6 +133,7 @@ cc_library(
129133
"//common:vlog",
130134
"//toolchain/base:canonical_value_store",
131135
"//toolchain/base:index_base",
136+
"//toolchain/base:int",
132137
"//toolchain/base:kind_switch",
133138
"//toolchain/base:value_ids",
134139
"//toolchain/base:value_store",

toolchain/check/call.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "toolchain/check/context.h"
1111
#include "toolchain/check/control_flow.h"
1212
#include "toolchain/check/convert.h"
13+
#include "toolchain/check/cpp_overload_resolution.h"
1314
#include "toolchain/check/cpp_thunk.h"
1415
#include "toolchain/check/deduce.h"
1516
#include "toolchain/check/facet_type.h"
@@ -298,6 +299,19 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
298299
if (callee_function.is_error) {
299300
return SemIR::ErrorInst::InstId;
300301
}
302+
if (callee_function.is_cpp_overload_set) {
303+
auto resolved_fn_id =
304+
PerformCppOverloadResolution(context, loc_id, callee_id, arg_ids);
305+
if (!resolved_fn_id) {
306+
return SemIR::ErrorInst::InstId;
307+
}
308+
callee_id = resolved_fn_id.value();
309+
callee_function = GetCalleeFunction(context.sem_ir(), callee_id);
310+
if (callee_function.is_error) {
311+
return SemIR::ErrorInst::InstId;
312+
}
313+
CARBON_CHECK(!callee_function.is_cpp_overload_set);
314+
}
301315
if (callee_function.function_id.has_value()) {
302316
return PerformCallToFunction(context, loc_id, callee_id, callee_function,
303317
arg_ids);

toolchain/check/context.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ class Context {
252252
auto entity_names() -> SemIR::EntityNameStore& {
253253
return sem_ir().entity_names();
254254
}
255+
auto cpp_overload_sets() -> SemIR::CppOverloadSetStore& {
256+
return sem_ir().cpp_overload_sets();
257+
}
255258
auto functions() -> SemIR::FunctionStore& { return sem_ir().functions(); }
256259
auto classes() -> SemIR::ClassStore& { return sem_ir().classes(); }
257260
auto vtables() -> SemIR::VtableStore& { return sem_ir().vtables(); }
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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/cpp_overload_resolution.h"
6+
7+
#include "clang/Sema/Overload.h"
8+
#include "clang/Sema/Sema.h"
9+
#include "toolchain/check/cpp_type_mapping.h"
10+
#include "toolchain/check/import_cpp.h"
11+
12+
namespace Carbon::Check {
13+
14+
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
15+
SemIR::InstId callee_id,
16+
llvm::ArrayRef<SemIR::InstId> arg_ids)
17+
-> std::optional<SemIR::InstId> {
18+
Diagnostics::AnnotationScope annotate_diagnostics(
19+
&context.emitter(), [&](auto& builder) {
20+
CARBON_DIAGNOSTIC(InCallToCppFunction, Note,
21+
"in call to Cpp function here");
22+
builder.Note(loc_id, InCallToCppFunction);
23+
});
24+
25+
// Map Carbon call argument types to C++ types.
26+
llvm::SmallVector<clang::Expr*> arg_exprs;
27+
arg_exprs.reserve(arg_ids.size());
28+
for (SemIR::InstId arg_id : arg_ids) {
29+
clang::QualType arg_cpp_type = MapToCppType(context, arg_id);
30+
if (arg_cpp_type.isNull()) {
31+
CARBON_DIAGNOSTIC(CppCallArgTypeNotSupported, Error,
32+
"call argument of type {0} is not supported",
33+
TypeOfInstId);
34+
context.emitter().Emit(loc_id, CppCallArgTypeNotSupported, arg_id);
35+
return std::nullopt;
36+
}
37+
// TODO: Allocate these on the stack.
38+
arg_exprs.emplace_back(new (context.ast_context()) clang::OpaqueValueExpr(
39+
// TODO: Add location accordingly.
40+
clang::SourceLocation(), arg_cpp_type.getNonReferenceType(),
41+
clang::ExprValueKind::VK_LValue));
42+
}
43+
44+
auto overload_set_type =
45+
context.types()
46+
.GetAsInst(context.insts().Get(callee_id).type_id())
47+
.TryAs<SemIR::CppOverloadSetType>();
48+
// TODO: CHECK-fail or store CppOverloadSetId in the CalleeFunction and pass
49+
// it in here.
50+
if (!overload_set_type) {
51+
return std::nullopt;
52+
}
53+
const SemIR::CppOverloadSet& overload_set =
54+
context.cpp_overload_sets().Get(overload_set_type->overload_set_id);
55+
56+
// Add candidate functions from the name lookup.
57+
clang::OverloadCandidateSet candidate_set(
58+
// TODO: Add location accordingly.
59+
clang::SourceLocation(),
60+
clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
61+
62+
clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
63+
CARBON_CHECK(ast);
64+
clang::Sema& sema = ast->getSema();
65+
66+
// TODO: Add support for method calls.
67+
for (clang::NamedDecl* candidate : overload_set.candidate_functions) {
68+
if (auto* fn_decl = dyn_cast<clang::FunctionDecl>(candidate)) {
69+
sema.AddOverloadCandidate(
70+
fn_decl, clang::DeclAccessPair::make(fn_decl, candidate->getAccess()),
71+
arg_exprs, candidate_set);
72+
} else if (isa<clang::FunctionTemplateDecl>(candidate)) {
73+
CARBON_DIAGNOSTIC(CppTemplateFunctionNotSupported, Error,
74+
"template function is not supported");
75+
context.emitter().Emit(loc_id, CppTemplateFunctionNotSupported);
76+
return std::nullopt;
77+
}
78+
// TODO: Diagnose if it's neither of these types.
79+
}
80+
81+
// Find best viable function among the candidates.
82+
clang::OverloadCandidateSet::iterator best_viable_fn;
83+
clang::OverloadingResult overloading_result =
84+
// TODO: Add location accordingly.
85+
candidate_set.BestViableFunction(sema, clang::SourceLocation(),
86+
best_viable_fn);
87+
88+
switch (overloading_result) {
89+
case clang::OverloadingResult::OR_Success: {
90+
// TODO: Handle the cases when Function is null.
91+
CARBON_CHECK(best_viable_fn->Function);
92+
SemIR::InstId result =
93+
ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function);
94+
return result;
95+
}
96+
case clang::OverloadingResult::OR_No_Viable_Function: {
97+
// TODO: Add notes with the candidates.
98+
CARBON_DIAGNOSTIC(CppOverloadingNoViableFunctionFound, Error,
99+
"no matching function for call to `{0}`",
100+
SemIR::NameId);
101+
context.emitter().Emit(loc_id, CppOverloadingNoViableFunctionFound,
102+
overload_set.name_id);
103+
return std::nullopt;
104+
}
105+
case clang::OverloadingResult::OR_Ambiguous: {
106+
// TODO: Add notes with the candidates.
107+
CARBON_DIAGNOSTIC(CppOverloadingAmbiguousCandidatesFound, Error,
108+
"call to `{0}` is ambiguous", SemIR::NameId);
109+
context.emitter().Emit(loc_id, CppOverloadingAmbiguousCandidatesFound,
110+
overload_set.name_id);
111+
return std::nullopt;
112+
}
113+
case clang::OverloadingResult::OR_Deleted: {
114+
// TODO: Add notes with the candidates.
115+
CARBON_DIAGNOSTIC(CppOverloadingDeletedFunctionFound, Error,
116+
"call to deleted function `{0}`", SemIR::NameId);
117+
context.emitter().Emit(loc_id, CppOverloadingDeletedFunctionFound,
118+
overload_set.name_id);
119+
return std::nullopt;
120+
}
121+
}
122+
}
123+
124+
} // namespace Carbon::Check
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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_CPP_OVERLOAD_RESOLUTION_H_
6+
#define CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_
7+
8+
#include "toolchain/check/context.h"
9+
10+
namespace Carbon::Check {
11+
12+
// Performs overloading resolution for a call to an overloaded C++ set. A set
13+
// with a single non-templated function goes through the same rules for
14+
// overloading resolution. Uses Clang to find the best viable function for the
15+
// call. Returns the resolved function, or `nullopt` if overload resolution
16+
// failed.
17+
//
18+
// Note on non-overloaded functions: In C++, a single non-templated function is
19+
// also treated as an overloaded set and goes through the overload resolution to
20+
// ensure that the function is viable for the call. This is to make sure that
21+
// calls that have no viable implicit conversion sequence are rejected even when
22+
// an implicit conversion is possible. Keeping the same behavior here for
23+
// consistency and supporting migrations so that the migrated callers from C++
24+
// remain valid.
25+
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
26+
SemIR::InstId callee_id,
27+
llvm::ArrayRef<SemIR::InstId> arg_ids)
28+
-> std::optional<SemIR::InstId>;
29+
30+
} // namespace Carbon::Check
31+
32+
#endif // CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_

0 commit comments

Comments
 (0)