Skip to content

Commit ca40e9d

Browse files
authored
Support making method calls to C++ overload sets. (#6069)
Fixes #6059
1 parent 0cafb8f commit ca40e9d

File tree

10 files changed

+475
-251
lines changed

10 files changed

+475
-251
lines changed

toolchain/check/call.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
300300
return SemIR::ErrorInst::InstId;
301301
}
302302
if (callee_function.cpp_overload_set_id.has_value()) {
303+
auto self_id = callee_function.self_id;
304+
// TODO: Pass self_id into overload resolution so that it's taken into
305+
// account when selecting the overload.
303306
auto resolved_fn_id = PerformCppOverloadResolution(
304307
context, loc_id, callee_function.cpp_overload_set_id, arg_ids);
305308
if (!resolved_fn_id) {
@@ -311,6 +314,10 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
311314
return SemIR::ErrorInst::InstId;
312315
}
313316
CARBON_CHECK(!callee_function.cpp_overload_set_id.has_value());
317+
318+
// Preserve the `self` argument from the original callee.
319+
CARBON_CHECK(!callee_function.self_id.has_value());
320+
callee_function.self_id = self_id;
314321
}
315322
if (callee_function.function_id.has_value()) {
316323
return PerformCallToFunction(context, loc_id, callee_id, callee_function,

toolchain/check/member_access.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,34 @@ static auto GetClassElementIndex(Context& context, SemIR::InstId element_id)
4545
CARBON_FATAL("Unexpected value {0} in class element name", element_inst);
4646
}
4747

48-
// Returns whether `function_id` is an instance method, that is, whether it has
49-
// an implicit `self` parameter.
48+
// Returns whether `function_id` is an instance method: in other words, whether
49+
// it has an implicit `self` parameter.
5050
static auto IsInstanceMethod(const SemIR::File& sem_ir,
5151
SemIR::FunctionId function_id) -> bool {
5252
const auto& function = sem_ir.functions().Get(function_id);
5353
return function.self_param_id.has_value();
5454
}
5555

56+
// Returns whether the callee function is an instance method, either because
57+
// it's a Carbon instance method or because it's a C++ overload set that might
58+
// contain an instance method.
59+
static auto IsInstanceMethod(const SemIR::File& sem_ir,
60+
const SemIR::CalleeFunction& callee) -> bool {
61+
if (callee.function_id.has_value()) {
62+
return IsInstanceMethod(sem_ir, callee.function_id);
63+
}
64+
if (callee.cpp_overload_set_id.has_value()) {
65+
// For now, treat all C++ overload sets as potentially containing instance
66+
// methods. Overload resolution will handle the case where we actually
67+
// found a static method.
68+
// TODO: Consider returning `false` if there are no non-instance methods in
69+
// the overload set. This would cause us to reject
70+
// `instance.(Class.StaticMethod)()` like we do in pure Carbon code.
71+
return true;
72+
}
73+
return false;
74+
}
75+
5676
// Return whether `type_id`, the type of an associated entity, is for an
5777
// instance member (currently true only for instance methods).
5878
static auto IsInstanceType(Context& context, SemIR::TypeId type_id) -> bool {
@@ -372,10 +392,9 @@ static auto PerformInstanceBinding(Context& context, SemIR::LocId loc_id,
372392
SemIR::InstId member_id) -> SemIR::InstId {
373393
// If the member is a function, check whether it's an instance method.
374394
if (auto callee = SemIR::GetCalleeFunction(context.sem_ir(), member_id);
375-
callee.function_id.has_value()) {
376-
if (!IsInstanceMethod(context.sem_ir(), callee.function_id) ||
377-
callee.self_id.has_value()) {
378-
// Found a static member function or an already-bound method.
395+
IsInstanceMethod(context.sem_ir(), callee)) {
396+
if (callee.self_id.has_value()) {
397+
// Found an already-bound method.
379398
return member_id;
380399
}
381400

toolchain/check/testdata/interop/cpp/class/access.carbon

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,19 +161,14 @@ struct S {
161161
auto foo() -> void;
162162
};
163163

164-
// --- fail_todo_5891_import_function_member_public.carbon
164+
// --- import_function_member_public.carbon
165165

166166
library "[[@TEST_NAME]]";
167167

168168
import Cpp library "function_member_public.h";
169169

170170
fn F(s: Cpp.S*) {
171171
//@dump-sem-ir-begin
172-
// CHECK:STDERR: fail_todo_5891_import_function_member_public.carbon:[[@LINE+5]]:3: error: missing object argument in method call [MissingObjectInMethodCall]
173-
// CHECK:STDERR: s->foo();
174-
// CHECK:STDERR: ^~~~~~~~
175-
// CHECK:STDERR: fail_todo_5891_import_function_member_public.carbon: note: calling function declared here [InCallToFunction]
176-
// CHECK:STDERR:
177172
s->foo();
178173
//@dump-sem-ir-end
179174
}
@@ -432,7 +427,7 @@ fn F() {
432427
// CHECK:STDOUT: <elided>
433428
// CHECK:STDOUT: }
434429
// CHECK:STDOUT:
435-
// CHECK:STDOUT: --- fail_todo_5891_import_function_member_public.carbon
430+
// CHECK:STDOUT: --- import_function_member_public.carbon
436431
// CHECK:STDOUT:
437432
// CHECK:STDOUT: constants {
438433
// CHECK:STDOUT: %S: type = class_type @S [concrete]
@@ -456,10 +451,11 @@ fn F() {
456451
// CHECK:STDOUT: fn @F(%s.param: %ptr.5c7) {
457452
// CHECK:STDOUT: !entry:
458453
// CHECK:STDOUT: %s.ref: %ptr.5c7 = name_ref s, %s
459-
// CHECK:STDOUT: %.loc13: ref %S = deref %s.ref
454+
// CHECK:STDOUT: %.loc8: ref %S = deref %s.ref
460455
// CHECK:STDOUT: %foo.ref: %.177 = name_ref foo, imports.%.dcb [concrete = constants.%empty_struct]
461-
// CHECK:STDOUT: <elided>
462-
// CHECK:STDOUT: %S.foo.call: init %empty_tuple.type = call imports.%S.foo.decl(<error>)
456+
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %.loc8, %foo.ref
457+
// CHECK:STDOUT: %addr: %ptr.5c7 = addr_of %.loc8
458+
// CHECK:STDOUT: %S.foo.call: init %empty_tuple.type = call imports.%S.foo.decl(%addr)
463459
// CHECK:STDOUT: <elided>
464460
// CHECK:STDOUT: }
465461
// CHECK:STDOUT:

toolchain/check/testdata/interop/cpp/class/base.carbon

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
// TIP: To dump output, run:
1111
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/class/base.carbon
1212

13-
// TODO: Tests marked as `fail_todo_5891_` to fixed as a follow-up of https://github.com/carbon-language/carbon-lang/pull/5891.
14-
1513
// --- derived_to_base_conversion.h
1614

1715
class Base {};
@@ -103,34 +101,20 @@ struct Derived : Base {
103101
void g() const;
104102
};
105103

106-
// --- fail_todo_5891_use_base_method.carbon
104+
// --- use_base_method.carbon
107105

108106
library "[[@TEST_NAME]]";
109107

110108
import Cpp library "base_method.h";
111109

112110
fn CallDirect(d: Cpp.Derived) {
113111
//@dump-sem-ir-begin
114-
// CHECK:STDERR: fail_todo_5891_use_base_method.carbon:[[@LINE+5]]:3: error: missing object argument in method call [MissingObjectInMethodCall]
115-
// CHECK:STDERR: d.f();
116-
// CHECK:STDERR: ^~~~~
117-
// CHECK:STDERR: fail_todo_5891_use_base_method.carbon: note: calling function declared here [InCallToFunction]
118-
// CHECK:STDERR:
119112
d.f();
120113
//@dump-sem-ir-end
121114
}
122115

123116
fn CallQualified(d: Cpp.Derived) {
124117
//@dump-sem-ir-begin
125-
// CHECK:STDERR: fail_todo_5891_use_base_method.carbon:[[@LINE+9]]:3: error: member name of type `<type of Cpp.g>` in compound member access is not an instance member or an interface member [CompoundMemberAccessDoesNotUseBase]
126-
// CHECK:STDERR: d.(Cpp.Base.g)();
127-
// CHECK:STDERR: ^~~~~~~~~~~~~~
128-
// CHECK:STDERR:
129-
// CHECK:STDERR: fail_todo_5891_use_base_method.carbon:[[@LINE+5]]:3: error: missing object argument in method call [MissingObjectInMethodCall]
130-
// CHECK:STDERR: d.(Cpp.Base.g)();
131-
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
132-
// CHECK:STDERR: fail_todo_5891_use_base_method.carbon: note: calling function declared here [InCallToFunction]
133-
// CHECK:STDERR:
134118
d.(Cpp.Base.g)();
135119
//@dump-sem-ir-end
136120
}
@@ -472,7 +456,7 @@ class V {
472456
// CHECK:STDOUT: return %Int.as.Copy.impl.Op.call to %return
473457
// CHECK:STDOUT: }
474458
// CHECK:STDOUT:
475-
// CHECK:STDOUT: --- fail_todo_5891_use_base_method.carbon
459+
// CHECK:STDOUT: --- use_base_method.carbon
476460
// CHECK:STDOUT:
477461
// CHECK:STDOUT: constants {
478462
// CHECK:STDOUT: %Derived: type = class_type @Derived [concrete]
@@ -517,23 +501,33 @@ class V {
517501
// CHECK:STDOUT: !entry:
518502
// CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d
519503
// CHECK:STDOUT: %f.ref: %.5b0 = name_ref f, imports.%.e54 [concrete = constants.%empty_struct.3f3]
520-
// CHECK:STDOUT: %addr: %ptr.fb2 = addr_of <error> [concrete = <error>]
521-
// CHECK:STDOUT: %.loc13_7.1: %ptr.a97 = as_compatible %addr [concrete = <error>]
522-
// CHECK:STDOUT: %.loc13_7.2: %ptr.a97 = converted %addr, %.loc13_7.1 [concrete = <error>]
523-
// CHECK:STDOUT: %f__carbon_thunk.call: init %empty_tuple.type = call imports.%f__carbon_thunk.decl(%.loc13_7.2)
504+
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %d.ref, %f.ref
505+
// CHECK:STDOUT: %.loc8_3.1: ref %Base = class_element_access %d.ref, element0
506+
// CHECK:STDOUT: %.loc8_3.2: ref %Base = converted %d.ref, %.loc8_3.1
507+
// CHECK:STDOUT: %.loc8_3.3: %Base = bind_value %.loc8_3.2
508+
// CHECK:STDOUT: %.loc8_3.4: ref %Base = value_as_ref %.loc8_3.3
509+
// CHECK:STDOUT: %addr: %ptr.fb2 = addr_of %.loc8_3.4
510+
// CHECK:STDOUT: %.loc8_7.1: %ptr.a97 = as_compatible %addr
511+
// CHECK:STDOUT: %.loc8_7.2: %ptr.a97 = converted %addr, %.loc8_7.1
512+
// CHECK:STDOUT: %f__carbon_thunk.call: init %empty_tuple.type = call imports.%f__carbon_thunk.decl(%.loc8_7.2)
524513
// CHECK:STDOUT: <elided>
525514
// CHECK:STDOUT: }
526515
// CHECK:STDOUT:
527516
// CHECK:STDOUT: fn @CallQualified(%d.param: %Derived) {
528517
// CHECK:STDOUT: !entry:
529518
// CHECK:STDOUT: %d.ref: %Derived = name_ref d, %d
530-
// CHECK:STDOUT: %Cpp.ref.loc28: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
519+
// CHECK:STDOUT: %Cpp.ref.loc14: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
531520
// CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Base.decl [concrete = constants.%Base]
532521
// CHECK:STDOUT: %g.ref: %.7c6 = name_ref g, imports.%.362 [concrete = constants.%empty_struct.49e]
533-
// CHECK:STDOUT: %addr: %ptr.fb2 = addr_of <error> [concrete = <error>]
534-
// CHECK:STDOUT: %.loc28_18.1: %ptr.a97 = as_compatible %addr [concrete = <error>]
535-
// CHECK:STDOUT: %.loc28_18.2: %ptr.a97 = converted %addr, %.loc28_18.1 [concrete = <error>]
536-
// CHECK:STDOUT: %g__carbon_thunk.call: init %empty_tuple.type = call imports.%g__carbon_thunk.decl(%.loc28_18.2)
522+
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %d.ref, %g.ref
523+
// CHECK:STDOUT: %.loc14_3.1: ref %Base = class_element_access %d.ref, element0
524+
// CHECK:STDOUT: %.loc14_3.2: ref %Base = converted %d.ref, %.loc14_3.1
525+
// CHECK:STDOUT: %.loc14_3.3: %Base = bind_value %.loc14_3.2
526+
// CHECK:STDOUT: %.loc14_3.4: ref %Base = value_as_ref %.loc14_3.3
527+
// CHECK:STDOUT: %addr: %ptr.fb2 = addr_of %.loc14_3.4
528+
// CHECK:STDOUT: %.loc14_18.1: %ptr.a97 = as_compatible %addr
529+
// CHECK:STDOUT: %.loc14_18.2: %ptr.a97 = converted %addr, %.loc14_18.1
530+
// CHECK:STDOUT: %g__carbon_thunk.call: init %empty_tuple.type = call imports.%g__carbon_thunk.decl(%.loc14_18.2)
537531
// CHECK:STDOUT: <elided>
538532
// CHECK:STDOUT: }
539533
// CHECK:STDOUT:

0 commit comments

Comments
 (0)