mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:30:12 +01:00
Add support for C++ member operators (#6112)
Call `Sema::AddMemberOperatorCandidates()` to properly add candidates.
For C++ member operator calls, use the first arg as self.
C++ Interop Demo:
```c++
// my_number.h
class MyNumber {
public:
explicit MyNumber(int value) : value_(value) {}
int value() const { return value_; }
auto operator++() -> MyNumber;
private:
int value_;
};
```
```c++
// my_number.cpp
#include "my_number.h"
auto MyNumber::operator++() -> MyNumber {
++value_;
return *this;;
}
```
```carbon
// main.carbon
library "Main";
import Core library "io";
import Cpp library "my_number.h";
fn Run() -> i32 {
var num: Cpp.MyNumber = Cpp.MyNumber.MyNumber(14);
Core.Print(num.value());
++num;
Core.Print(num.value());
return 0;
}
```
```shell
$ clang -c my_number.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link my_number.o main.o --output=demo
$ ./demo
14
15
```
Part of https://github.com/carbon-language/carbon-lang/issues/5995.
This commit is contained in:
+10
-20
@@ -337,27 +337,17 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
|
||||
}
|
||||
|
||||
case CARBON_KIND(SemIR::CalleeCppOverloadSet overload): {
|
||||
callee_id = PerformCppOverloadResolution(context, loc_id,
|
||||
overload.cpp_overload_set_id,
|
||||
overload.self_id, arg_ids);
|
||||
auto overload_result = GetCallee(context.sem_ir(), callee_id);
|
||||
CARBON_KIND_SWITCH(overload_result) {
|
||||
case CARBON_KIND(SemIR::CalleeError _): {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeFunction fn): {
|
||||
// Preserve the `self` argument from the original callee.
|
||||
CARBON_CHECK(!fn.self_id.has_value());
|
||||
fn.self_id = overload.self_id;
|
||||
return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids);
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
|
||||
CARBON_FATAL("overloads can't be recursive");
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeNonFunction _): {
|
||||
CARBON_FATAL("overloads should produce functions");
|
||||
}
|
||||
CppOverloadResolutionResult overload_result =
|
||||
PerformCppOverloadResolution(context, loc_id,
|
||||
overload.cpp_overload_set_id,
|
||||
overload.self_id, arg_ids);
|
||||
if (overload_result.callee_id == SemIR::ErrorInst::InstId) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
CARBON_CHECK(overload_result.callee_function);
|
||||
return PerformCallToFunction(context, loc_id, overload_result.callee_id,
|
||||
*overload_result.callee_function,
|
||||
overload_result.arg_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
#include "clang/Basic/DiagnosticSema.h"
|
||||
#include "clang/Sema/Overload.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/check/cpp/import.h"
|
||||
#include "toolchain/check/cpp/location.h"
|
||||
#include "toolchain/check/cpp/type_mapping.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
@@ -56,29 +58,39 @@ static auto AddOverloadCandidataes(clang::Sema& sema,
|
||||
cast<clang::CXXRecordDecl>(template_decl->getDeclContext()),
|
||||
ExplicitTemplateArgs, self_type, self_classification, args,
|
||||
candidate_set, SuppressUserConversions, PartialOverloading);
|
||||
} else if (method_decl->isOverloadedOperator()) {
|
||||
sema.AddMemberOperatorCandidates(method_decl->getOverloadedOperator(),
|
||||
candidate_set.getLocation(), args,
|
||||
candidate_set);
|
||||
} else {
|
||||
sema.AddMethodCandidate(method_decl, found_decl,
|
||||
method_decl->getParent(), self_type,
|
||||
self_classification, args, candidate_set,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
}
|
||||
} else if (template_decl) {
|
||||
sema.AddTemplateOverloadCandidate(
|
||||
template_decl, found_decl, ExplicitTemplateArgs, args, candidate_set,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
} else {
|
||||
if (template_decl) {
|
||||
sema.AddTemplateOverloadCandidate(
|
||||
template_decl, found_decl, ExplicitTemplateArgs, args,
|
||||
candidate_set, SuppressUserConversions, PartialOverloading);
|
||||
} else {
|
||||
sema.AddOverloadCandidate(fn_decl, found_decl, args, candidate_set,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
}
|
||||
sema.AddOverloadCandidate(fn_decl, found_decl, args, candidate_set,
|
||||
SuppressUserConversions, PartialOverloading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::CppOverloadSetId overload_set_id,
|
||||
SemIR::InstId self_id,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
||||
// Returns whether the decl is an operator member function.
|
||||
static auto IsOperatorMethodDecl(clang::Decl* decl) -> bool {
|
||||
auto* clang_method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
|
||||
return clang_method_decl && clang_method_decl->isOverloadedOperator();
|
||||
}
|
||||
|
||||
// Resolve which function to call, or returns an error instruction if overload
|
||||
// resolution failed.
|
||||
static auto ResolveCalleeId(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::CppOverloadSetId overload_set_id,
|
||||
SemIR::InstId self_id,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
||||
-> SemIR::InstId {
|
||||
// Register an annotation scope to flush any Clang diagnostics when we return.
|
||||
// This is important to ensure that Clang diagnostics are properly interleaved
|
||||
@@ -125,7 +137,10 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
|
||||
CARBON_CHECK(best_viable_fn->Function);
|
||||
sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
|
||||
SemIR::InstId result = ImportCppFunctionDecl(
|
||||
context, loc_id, best_viable_fn->Function, arg_exprs.size());
|
||||
context, loc_id, best_viable_fn->Function,
|
||||
// If this is an operator method, the first arg will be used as self.
|
||||
arg_exprs.size() -
|
||||
(IsOperatorMethodDecl(best_viable_fn->Function) ? 1 : 0));
|
||||
return result;
|
||||
}
|
||||
case clang::OverloadingResult::OR_No_Viable_Function: {
|
||||
@@ -154,4 +169,50 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether the function is an imported C++ operator member function.
|
||||
static auto IsCppOperatorMethod(Context& context, SemIR::FunctionId function_id)
|
||||
-> bool {
|
||||
SemIR::ClangDeclId clang_decl_id =
|
||||
context.functions().Get(function_id).clang_decl_id;
|
||||
return clang_decl_id.has_value() &&
|
||||
IsOperatorMethodDecl(
|
||||
context.clang_decls().Get(clang_decl_id).key.decl);
|
||||
}
|
||||
|
||||
auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::CppOverloadSetId overload_set_id,
|
||||
SemIR::InstId self_id,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
||||
-> CppOverloadResolutionResult {
|
||||
CppOverloadResolutionResult result = {
|
||||
.callee_id =
|
||||
ResolveCalleeId(context, loc_id, overload_set_id, self_id, arg_ids),
|
||||
.arg_ids = arg_ids};
|
||||
SemIR::Callee callee = GetCallee(context.sem_ir(), result.callee_id);
|
||||
CARBON_KIND_SWITCH(callee) {
|
||||
case CARBON_KIND(SemIR::CalleeError _): {
|
||||
result.callee_id = SemIR::ErrorInst::InstId;
|
||||
return result;
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeFunction fn): {
|
||||
CARBON_CHECK(!fn.self_id.has_value());
|
||||
if (self_id.has_value()) {
|
||||
// Preserve the `self` argument from the original callee.
|
||||
fn.self_id = self_id;
|
||||
} else if (IsCppOperatorMethod(context, fn.function_id)) {
|
||||
// Adjust `self` and args for C++ overloaded operator methods.
|
||||
fn.self_id = result.arg_ids.consume_front();
|
||||
}
|
||||
result.callee_function = fn;
|
||||
return result;
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
|
||||
CARBON_FATAL("overloads can't be recursive");
|
||||
}
|
||||
case CARBON_KIND(SemIR::CalleeNonFunction _): {
|
||||
CARBON_FATAL("overloads should produce functions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -6,15 +6,34 @@
|
||||
#define CARBON_TOOLCHAIN_CHECK_CPP_OVERLOAD_RESOLUTION_H_
|
||||
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// The result of performing C++ overload resolution.
|
||||
struct CppOverloadResolutionResult {
|
||||
// The resolved callee id, or ErrorInst::InstId on error.
|
||||
SemIR::InstId callee_id;
|
||||
|
||||
// The resolved callee, which may be different from the result of
|
||||
// `GetCalleeFunction()` to preserve self or set it to be the first argument
|
||||
// for C++ member operators. Not set if overload resolution failed.
|
||||
std::optional<SemIR::CalleeFunction> callee_function;
|
||||
|
||||
// The arguments to pass to the callee, which may be different from the
|
||||
// original arguments if the callee is a C++ member operator.
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids;
|
||||
};
|
||||
|
||||
// Performs overloading resolution for a call to an overloaded C++ set. A set
|
||||
// with a single non-templated function goes through the same rules for
|
||||
// overloading resolution. Uses Clang to find the best viable function for the
|
||||
// call. Returns the resolved function, or an error instruction if overload
|
||||
// resolution failed.
|
||||
// call.
|
||||
//
|
||||
// The callee function preserves the given self, if set. If not set, and the
|
||||
// function is a a C++ member operator, self will be set to the first argument,
|
||||
// which in turn will be removed from the given args.
|
||||
//
|
||||
// Note on non-overloaded functions: In C++, a single non-templated function is
|
||||
// also treated as an overloaded set and goes through the overload resolution to
|
||||
@@ -27,7 +46,7 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::CppOverloadSetId overload_set_id,
|
||||
SemIR::InstId self_id,
|
||||
llvm::ArrayRef<SemIR::InstId> arg_ids)
|
||||
-> SemIR::InstId;
|
||||
-> CppOverloadResolutionResult;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
|
||||
+80
-51
@@ -677,10 +677,14 @@ fn F() {
|
||||
|
||||
class C {
|
||||
public:
|
||||
// Unary.
|
||||
auto operator-() -> C;
|
||||
|
||||
// Binary.
|
||||
auto operator+(C rhs) -> C;
|
||||
};
|
||||
|
||||
// --- fail_todo_import_member_add_with.carbon
|
||||
// --- import_member_add_with.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
@@ -688,17 +692,9 @@ import Cpp library "member_add_with.h";
|
||||
|
||||
fn F() {
|
||||
//@dump-sem-ir-begin
|
||||
let c1: Cpp.C = Cpp.C.C();
|
||||
let c2: Cpp.C = Cpp.C.C();
|
||||
// CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE+8]]:22: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
|
||||
// CHECK:STDERR: 18 | let c3: Cpp.C = c1 + c2;
|
||||
// CHECK:STDERR: | ^
|
||||
// CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE-9]]:10: in file included here [InCppInclude]
|
||||
// CHECK:STDERR: ./member_add_with.h:4:8: note: candidate function not viable: requires single argument 'rhs', but 2 arguments were provided [CppInteropParseNote]
|
||||
// CHECK:STDERR: 4 | auto operator+(C rhs) -> C;
|
||||
// CHECK:STDERR: | ^ ~~~~~
|
||||
// CHECK:STDERR:
|
||||
let c3: Cpp.C = c1 + c2;
|
||||
var c1: Cpp.C = Cpp.C.C();
|
||||
var c2: Cpp.C = -c1;
|
||||
var c3: Cpp.C = c1 + c2;
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
@@ -2652,17 +2648,21 @@ fn F() {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_todo_import_member_add_with.carbon
|
||||
// CHECK:STDOUT: --- import_member_add_with.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.217: type = pattern_type %C [concrete]
|
||||
// CHECK:STDOUT: %.d40: type = cpp_overload_set_type @C__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %.d40: type = cpp_overload_set_type @C.cpp_operator.1 [concrete]
|
||||
// CHECK:STDOUT: %empty_struct.e73: %.d40 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ptr.d9e: type = ptr_type %C [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.type: type = fn_type @operator-__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk: %operator-__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %operator+__carbon_thunk.type: type = fn_type @operator+__carbon_thunk [concrete]
|
||||
// CHECK:STDOUT: %operator+__carbon_thunk: %operator+__carbon_thunk.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
|
||||
// CHECK:STDOUT: %facet_value: %type_where = facet_value %C, () [concrete]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.type.fc1: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
|
||||
@@ -2675,74 +2675,103 @@ fn F() {
|
||||
// CHECK:STDOUT: import Cpp//...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
|
||||
// CHECK:STDOUT: %.40b: %.d40 = cpp_overload_set_value @C__carbon_thunk [concrete = constants.%empty_struct.e73]
|
||||
// CHECK:STDOUT: %.40b: %.d40 = cpp_overload_set_value @C.cpp_operator.1 [concrete = constants.%empty_struct.e73]
|
||||
// CHECK:STDOUT: %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.decl: %operator-__carbon_thunk.type = fn_decl @operator-__carbon_thunk [concrete = constants.%operator-__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %operator+__carbon_thunk.decl: %operator+__carbon_thunk.type = fn_decl @operator+__carbon_thunk [concrete = constants.%operator+__carbon_thunk] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c1.patt: %pattern_type.217 = binding_pattern c1 [concrete]
|
||||
// CHECK:STDOUT: %c1.var_patt: %pattern_type.217 = var_pattern %c1.patt [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c1.var: ref %C = var %c1.var_patt
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %C.ref.loc8_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct.e73]
|
||||
// CHECK:STDOUT: %.loc8_27.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %addr.loc8_27.1: %ptr.d9e = addr_of %.loc8_27.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_27.1)
|
||||
// CHECK:STDOUT: %.loc8_27.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_27.1
|
||||
// CHECK:STDOUT: %.loc8_3.1: ref %C = splice_block %c1.var {}
|
||||
// CHECK:STDOUT: %addr.loc8_27: %ptr.d9e = addr_of %.loc8_3.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc8_27)
|
||||
// CHECK:STDOUT: %.loc8_27: init %C = in_place_init %C__carbon_thunk.call, %.loc8_3.1
|
||||
// CHECK:STDOUT: assign %c1.var, %.loc8_27
|
||||
// CHECK:STDOUT: %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc8_27.3: ref %C = temporary %.loc8_27.1, %.loc8_27.2
|
||||
// CHECK:STDOUT: %.loc8_27.4: %C = bind_value %.loc8_27.3
|
||||
// CHECK:STDOUT: %c1: %C = bind_name c1, %.loc8_27.4
|
||||
// CHECK:STDOUT: %c1: ref %C = bind_name c1, %c1.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
|
||||
// CHECK:STDOUT: %c2.var_patt: %pattern_type.217 = var_pattern %c2.patt [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Cpp.ref.loc9_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %C.ref.loc9_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct.e73]
|
||||
// CHECK:STDOUT: %.loc9_27.1: ref %C = temporary_storage
|
||||
// CHECK:STDOUT: %addr.loc9_27.1: %ptr.d9e = addr_of %.loc9_27.1
|
||||
// CHECK:STDOUT: %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc9_27.1)
|
||||
// CHECK:STDOUT: %.loc9_27.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_27.1
|
||||
// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc9_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %c2.var: ref %C = var %c2.var_patt
|
||||
// CHECK:STDOUT: %c1.ref.loc9: ref %C = name_ref c1, %c1
|
||||
// CHECK:STDOUT: %.loc9_3.1: ref %C = splice_block %c2.var {}
|
||||
// CHECK:STDOUT: %addr.loc9_20: %ptr.d9e = addr_of %c1.ref.loc9
|
||||
// CHECK:STDOUT: %addr.loc9_19: %ptr.d9e = addr_of %.loc9_3.1
|
||||
// CHECK:STDOUT: %operator-__carbon_thunk.call: init %empty_tuple.type = call imports.%operator-__carbon_thunk.decl(%addr.loc9_20, %addr.loc9_19)
|
||||
// CHECK:STDOUT: %.loc9_19: init %C = in_place_init %operator-__carbon_thunk.call, %.loc9_3.1
|
||||
// CHECK:STDOUT: assign %c2.var, %.loc9_19
|
||||
// CHECK:STDOUT: %.loc9_14: type = splice_block %C.ref.loc9 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc9: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc9_27.3: ref %C = temporary %.loc9_27.1, %.loc9_27.2
|
||||
// CHECK:STDOUT: %.loc9_27.4: %C = bind_value %.loc9_27.3
|
||||
// CHECK:STDOUT: %c2: %C = bind_name c2, %.loc9_27.4
|
||||
// CHECK:STDOUT: %c2: ref %C = bind_name c2, %c2.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c3.patt: %pattern_type.217 = binding_pattern c3 [concrete]
|
||||
// CHECK:STDOUT: %c3.var_patt: %pattern_type.217 = var_pattern %c3.patt [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c1.ref: %C = name_ref c1, %c1
|
||||
// CHECK:STDOUT: %c2.ref: %C = name_ref c2, %c2
|
||||
// CHECK:STDOUT: %.loc18: type = splice_block %C.ref.loc18 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc18: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %c3.var: ref %C = var %c3.var_patt
|
||||
// CHECK:STDOUT: %c1.ref.loc10: ref %C = name_ref c1, %c1
|
||||
// CHECK:STDOUT: %c2.ref: ref %C = name_ref c2, %c2
|
||||
// CHECK:STDOUT: %.loc10_3.1: ref %C = splice_block %c3.var {}
|
||||
// CHECK:STDOUT: %addr.loc10_19: %ptr.d9e = addr_of %c1.ref.loc10
|
||||
// CHECK:STDOUT: %.loc10_24.1: %C = bind_value %c2.ref
|
||||
// CHECK:STDOUT: %.loc10_24.2: ref %C = value_as_ref %.loc10_24.1
|
||||
// CHECK:STDOUT: %addr.loc10_22.1: %ptr.d9e = addr_of %.loc10_24.2
|
||||
// CHECK:STDOUT: %addr.loc10_22.2: %ptr.d9e = addr_of %.loc10_3.1
|
||||
// CHECK:STDOUT: %operator+__carbon_thunk.call: init %empty_tuple.type = call imports.%operator+__carbon_thunk.decl(%addr.loc10_19, %addr.loc10_22.1, %addr.loc10_22.2)
|
||||
// CHECK:STDOUT: %.loc10_22: init %C = in_place_init %operator+__carbon_thunk.call, %.loc10_3.1
|
||||
// CHECK:STDOUT: assign %c3.var, %.loc10_22
|
||||
// CHECK:STDOUT: %.loc10_14: type = splice_block %C.ref.loc10 [concrete = constants.%C] {
|
||||
// CHECK:STDOUT: %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %C.ref.loc10: type = name_ref C, imports.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %c3: %C = bind_name c3, <error> [concrete = <error>]
|
||||
// CHECK:STDOUT: %c3: ref %C = bind_name c3, %c3.var
|
||||
// CHECK:STDOUT: %facet_value.loc10: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %.loc10_3.2: %type_where = converted constants.%C, %facet_value.loc10 [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc10: <bound method> = bound_method %c3.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc10: <bound method> = bound_method %c3.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
|
||||
// CHECK:STDOUT: %addr.loc10_3: %ptr.d9e = addr_of %c3.var
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10_3)
|
||||
// CHECK:STDOUT: %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %.loc9_27.5: %type_where = converted constants.%C, %facet_value.loc9 [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %.loc9_27.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
|
||||
// CHECK:STDOUT: %.loc9_3.2: %type_where = converted constants.%C, %facet_value.loc9 [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %c2.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc9: <bound method> = bound_method %.loc9_27.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
|
||||
// CHECK:STDOUT: %addr.loc9_27.2: %ptr.d9e = addr_of %.loc9_27.3
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_27.2)
|
||||
// CHECK:STDOUT: %bound_method.loc9: <bound method> = bound_method %c2.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
|
||||
// CHECK:STDOUT: %addr.loc9_3: %ptr.d9e = addr_of %c2.var
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_3)
|
||||
// CHECK:STDOUT: %facet_value.loc8: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %.loc8_27.5: %type_where = converted constants.%C, %facet_value.loc8 [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_27.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
|
||||
// CHECK:STDOUT: %.loc8_3.2: %type_where = converted constants.%C, %facet_value.loc8 [concrete = constants.%facet_value]
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %c1.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: %bound_method.loc8: <bound method> = bound_method %.loc8_27.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
|
||||
// CHECK:STDOUT: %addr.loc8_27.2: %ptr.d9e = addr_of %.loc8_27.3
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_27.2)
|
||||
// CHECK:STDOUT: %bound_method.loc8: <bound method> = bound_method %c1.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.3
|
||||
// CHECK:STDOUT: %addr.loc8_3: %ptr.d9e = addr_of %c1.var
|
||||
// CHECK:STDOUT: %AggregateT.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%addr.loc8_3)
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
Reference in New Issue
Block a user