Initial support for C++ function pointers (#7788)

This change introduces a new inst kind `CppFunctionPointerType`, which
represents an imported C++ function pointer that can be invoked from
Carbon (support for forming such a pointer from a Carbon function is in
a follow-up PR). This is implemented by treating the operation of
invoking a function pointer in C++ as if it were a call to an `__invoke`
method on the function pointer type, and extending the existing
function-import logic to support importing this fictitious method.

---------

Co-authored-by: Nicholas Bishop <nbishop@nbishop.net>
This commit is contained in:
Geoff Romer
2026-09-30 00:06:06 +00:00
committed by GitHub
co-authored by Nicholas Bishop
parent f65572a1e6
commit 12e1afeb15
41 changed files with 2030 additions and 200 deletions
+8
View File
@@ -58,6 +58,14 @@ class CanonicalValueStore {
// the value is already in the store, returns the ID of the existing value.
auto Add(ValueType value) -> IdT;
// Updates the value with the given id. The new value must have the same
// key as the old one.
auto Update(IdT id, ValueType value) -> void {
RefType storage = values_.Get(id);
CARBON_CHECK(GetAsKey(value) == GetAsKey(storage));
storage = std::move(value);
}
// Returns the value for an ID.
auto Get(IdT id) const -> ConstRefType { return values_.Get(id); }
+24 -1
View File
@@ -12,6 +12,7 @@
#include "toolchain/check/control_flow.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/cpp/call.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/thunk.h"
#include "toolchain/check/deduce.h"
#include "toolchain/check/facet_type.h"
@@ -311,7 +312,8 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
case SemIR::Function::SpecialFunctionKind::None:
case SemIR::Function::SpecialFunctionKind::Builtin:
case SemIR::Function::SpecialFunctionKind::Generated:
case SemIR::Function::SpecialFunctionKind::CppThunk: {
case SemIR::Function::SpecialFunctionKind::CppThunk:
case SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk: {
return GetOrAddInst<SemIR::Call>(context, loc_id,
{.type_id = return_type_id,
.callee_id = callee_id,
@@ -401,6 +403,23 @@ static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id,
}
}
static auto PerformCallToCppFunctionPointer(
Context& context, SemIR::LocId loc_id, SemIR::InstId function_ptr_id,
SemIR::CalleeCppFunctionPointer fn_ptr,
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
auto pointer_info =
ImportFunctionPointerInvoke(context, loc_id, fn_ptr.function_type_id);
SemIR::CalleeFunction callee_function = {
.function_id = pointer_info.function_id,
.enclosing_specific_id = SemIR::SpecificId::None,
.resolved_specific_id = SemIR::SpecificId::None,
.self_type_id = SemIR::InstId::None,
.self_id = function_ptr_id};
return PerformCallToFunction(context, loc_id, pointer_info.decl_id,
callee_function, arg_ids, /*is_desugared=*/true);
}
// Determines whether a call can be performed immediately (i.e. whether it is
// non-template-dependent).
static auto IsCallPerformable(Context& context, SemIR::InstId callee_id,
@@ -433,6 +452,10 @@ static auto PerformCallHelper(Context& context, SemIR::LocId loc_id,
return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids,
is_desugared);
}
case CARBON_KIND(SemIR::CalleeCppFunctionPointer fn_ptr): {
return PerformCallToCppFunctionPointer(context, loc_id, callee_id, fn_ptr,
arg_ids);
}
case CARBON_KIND(SemIR::CalleeNonFunction _): {
return PerformCallToNonFunction(context, loc_id, callee_id, arg_ids);
}
+8
View File
@@ -451,6 +451,14 @@ class Context {
return mangle_string_fingerprint_;
}
auto clang_function_pointer_types() -> SemIR::ClangFunctionPointerTypeStore& {
return sem_ir().clang_function_pointer_types();
}
auto clang_function_pointer_types() const
-> const SemIR::ClangFunctionPointerTypeStore& {
return sem_ir().clang_function_pointer_types();
}
// --------------------------------------------------------------------------
// End of SemIR::File members.
// --------------------------------------------------------------------------
+3
View File
@@ -81,6 +81,9 @@ auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id,
case CARBON_KIND(SemIR::CalleeNonFunction _): {
CARBON_FATAL("overloads should produce functions");
}
case CARBON_KIND(SemIR::CalleeCppFunctionPointer _): {
CARBON_FATAL("overloads should produce functions");
}
}
}
+7
View File
@@ -313,6 +313,13 @@ auto MaybeModifyCppThunkCallForConstEval(Context& context, SemIR::Call* call)
.GetAs<SemIR::FunctionDecl>(thunk_callee_inst_id)
.function_id);
const auto* clang_decl_info =
context.clang_decls().Lookup(thunk_callee_function.first_decl_id());
if (clang_decl_info == nullptr) {
// The `__invoke` thunk for a function pointer doesn't have a declared
// C++ callee.
return;
}
function_decl = cast<clang::FunctionDecl>(
context.clang_decls()
.Lookup(thunk_callee_function.first_decl_id())
+287 -149
View File
@@ -440,10 +440,22 @@ static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id,
return lookup;
}
// Returns whether `decl` already mapped to an instruction.
static auto IsClangDeclImported(Context& context, SemIR::ClangDeclKey key)
-> bool {
return context.clang_decls().LookupId(key).has_value();
namespace {
// A key that identifies a C++ declaration to import. A `ClangDeclKey`
// represents an ordinary clang declaration, and a `clang::Type*` represents
// that function pointer type's notional `__invoke` method, which has no clang
// declaration.
using ImportKey = std::variant<SemIR::ClangDeclKey, const clang::Type*>;
} // namespace
// Returns whether `key` already mapped to an instruction.
static auto IsImported(Context& context, ImportKey key) -> bool {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key):
return context.clang_decls().LookupId(decl_key).has_value();
case CARBON_KIND(const clang::Type* type):
return context.clang_function_pointer_types().Lookup(type).has_value();
}
}
// If `decl` already mapped to an instruction, returns that instruction.
@@ -1073,7 +1085,7 @@ static auto ImportEnumConstantDecl(Context& context,
clang::EnumConstantDecl* enumerator_decl)
-> SemIR::InstId {
auto key = SemIR::ClangDeclKey(enumerator_decl);
CARBON_CHECK(!IsClangDeclImported(context, key));
CARBON_CHECK(!IsImported(context, key));
// Find the enclosing enum type.
auto enum_key = SemIR::ClangDeclKey(
@@ -1311,6 +1323,20 @@ static auto MapTagType(Context& context, const clang::TagType& type)
.type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
}
static auto MapFunctionPointerType(Context& context, SemIR::LocId /*loc_id*/,
clang::QualType type) -> TypeExpr {
CARBON_CHECK(type->isFunctionPointerType());
auto clang_type_id = context.clang_function_pointer_types().Lookup(
type.getCanonicalType().getTypePtr());
CARBON_CHECK(clang_type_id.has_value());
// TODO: wrap the pointer type in `Optional` unless it was marked
// non-nullable.
return TypeExpr::ForUnsugared(
context, GetCppFunctionPointerType(context, clang_type_id));
}
// Maps a C++ type that is not a wrapper type such as a pointer to a Carbon
// type.
// TODO: Support more types.
@@ -1324,6 +1350,10 @@ static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
return MapTagType(context, *tag_type);
}
if (type->isFunctionPointerType()) {
return MapFunctionPointerType(context, loc_id, type);
}
CARBON_CHECK(!type.hasQualifiers() && !type->isPointerType(),
"Should not see wrapper types here");
@@ -1378,11 +1408,11 @@ static auto MakeOptionalType(Context& context, SemIR::LocId loc_id,
return ExprAsType(context, loc_id, call_id);
}
// Maps a C++ pointer type to a Carbon pointer type.
static auto MapPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type, TypeExpr pointee_type_expr)
-> TypeExpr {
CARBON_CHECK(type->isPointerType());
// Maps a C++ object pointer type to a Carbon pointer type.
static auto MapObjectPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type,
TypeExpr pointee_type_expr) -> TypeExpr {
CARBON_CHECK(type->isObjectPointerType());
bool optional =
!IsClangTypeNonNull(type) &&
@@ -1444,7 +1474,7 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
clang::QualType orig_type = type;
if (type.hasQualifiers()) {
type = ClangGetUnqualifiedTypePreserveNonNull(context, type);
} else if (type->isPointerType()) {
} else if (type->isObjectPointerType()) {
type = type->getPointeeType();
} else if (type->isReferenceType()) {
type = type.getNonReferenceType();
@@ -1466,8 +1496,8 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
if (wrapper.hasQualifiers()) {
mapped = MapQualifiedType(context, wrapper, mapped);
} else if (wrapper->isPointerType()) {
mapped = MapPointerType(context, loc_id, wrapper, mapped);
} else if (wrapper->isObjectPointerType()) {
mapped = MapObjectPointerType(context, loc_id, wrapper, mapped);
} else if (wrapper->isReferenceType()) {
mapped = MapReferenceType(context, wrapper, mapped);
} else if (const auto* array_type = wrapper->getAsArrayTypeUnsafe()) {
@@ -1956,6 +1986,50 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
return decl_id;
}
// Given a C++ callee and its imported Carbon function (as produced by
// `ImportFunction`), this builds a simple-ABI thunk that invokes the callee,
// and defines the imported function as calling it.
static auto DefineAsThunkCall(Context& context, SemIR::LocId loc_id,
const CalleeFunctionInfo& callee_info,
SemIR::Function& function) -> void {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppThunk, Note,
"in thunk for C++ function used here");
builder.Note(loc_id, InCppThunk);
});
clang::FunctionDecl* thunk_clang_decl = BuildCppThunk(context, callee_info);
if (thunk_clang_decl == nullptr) {
return;
}
SemIR::ClangDeclSignature thunk_signature;
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
thunk_signature.num_params =
static_cast<int32_t>(thunk_clang_decl->getNumParams());
thunk_signature.passing_modes.assign(
thunk_signature.num_params,
SemIR::ClangDeclSignature::PassingMode::ByValue);
SemIR::ClangDeclSignatureId thunk_signature_id =
context.clang_decl_signatures().Add(std::move(thunk_signature));
CalleeFunctionInfo thunk_callee_info(context, thunk_clang_decl,
thunk_signature_id);
auto thunk_decl_id = ImportFunction(context, loc_id, thunk_callee_info);
if (thunk_decl_id == std::nullopt) {
return;
}
context.clang_decls().Add({.key = SemIR::ClangDeclKey::ForFunctionDecl(
thunk_clang_decl, thunk_signature_id),
.inst_id = *thunk_decl_id,
.is_imported = true});
auto thunk_function_id =
context.insts().GetAs<SemIR::FunctionDecl>(*thunk_decl_id).function_id;
auto& thunk_function = context.functions().Get(thunk_function_id);
thunk_function.SetCppThunk(function.first_owning_decl_id);
function.SetHasCppThunk(*thunk_decl_id);
}
// Imports a C++ function, returning a corresponding Carbon function.
// `signature` specifies how to convert the C++ function signature to the Carbon
// function signature. `signature.num_params` may be less than the number of
@@ -2001,41 +2075,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
context.insts().GetAs<SemIR::FunctionDecl>(*function_decl_id).function_id;
SemIR::Function& imported_function = context.functions().Get(function_id);
if (IsCppThunkRequired(context, callee_info)) {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppThunk, Note,
"in thunk for C++ function used here");
builder.Note(loc_id, InCppThunk);
});
if (clang::FunctionDecl* thunk_clang_decl =
BuildCppThunk(context, callee_info)) {
SemIR::ClangDeclSignature thunk_signature;
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
thunk_signature.num_params =
static_cast<int32_t>(thunk_clang_decl->getNumParams());
thunk_signature.passing_modes.assign(
thunk_signature.num_params,
SemIR::ClangDeclSignature::PassingMode::ByValue);
SemIR::ClangDeclSignatureId thunk_signature_id =
context.clang_decl_signatures().Add(std::move(thunk_signature));
CalleeFunctionInfo thunk_callee_info(context, thunk_clang_decl,
thunk_signature_id);
if (auto thunk_decl_id =
ImportFunction(context, loc_id, thunk_callee_info)) {
context.clang_decls().Add({.key = SemIR::ClangDeclKey::ForFunctionDecl(
thunk_clang_decl, thunk_signature_id),
.inst_id = *thunk_decl_id,
.is_imported = true});
auto thunk_function_id = context.insts()
.GetAs<SemIR::FunctionDecl>(*thunk_decl_id)
.function_id;
auto& thunk_function = context.functions().Get(thunk_function_id);
thunk_function.SetCppThunk(imported_function.first_owning_decl_id);
imported_function.SetHasCppThunk(*thunk_decl_id);
}
}
DefineAsThunkCall(context, loc_id, callee_info, imported_function);
} else {
// Inform Clang that the function has been referenced. This will trigger
// instantiation if needed.
@@ -2059,13 +2099,70 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
return *function_decl_id;
}
// Imports a C++ function pointer type, and prepares to import its `__invoke`
// function. The function itself must be imported separately with
// `ImportFunctionPointerInvoke` when it's called, because that requires the
// types in the signature to be complete.
static auto ImportFunctionPointer(Context& context,
const clang::Type* pointer_type)
-> SemIR::InstId {
CARBON_CHECK(pointer_type->isFunctionPointerType());
// Allocate an ID for the function pointer type and return it.
pointer_type = clang::QualType(pointer_type, /*Quals=*/0)
.getCanonicalType()
.getTypePtr();
auto clang_type_id =
context.clang_function_pointer_types().Lookup(pointer_type);
if (!clang_type_id.has_value()) {
clang_type_id = context.clang_function_pointer_types().Add(
{.clang_type = pointer_type,
.decl_id = SemIR::InstId::None,
.function_id = SemIR::FunctionId::None});
}
return context.types().GetTypeInstId(
GetCppFunctionPointerType(context, clang_type_id));
}
auto ImportFunctionPointerInvoke(
Context& context, SemIR::LocId loc_id,
SemIR::ClangFunctionPointerTypeId clang_type_id)
-> SemIR::ClangFunctionPointerTypeInfo {
const auto& info = context.clang_function_pointer_types().Get(clang_type_id);
if (info.decl_id.has_value()) {
return info;
}
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppFunctionPointerThunk, Note,
"in thunk for C++ function pointer type used here");
builder.Note(loc_id, InCppFunctionPointerThunk);
});
CalleeFunctionInfo callee_info(context, info.clang_type);
SemIR::ClangFunctionPointerTypeInfo result = {
.clang_type = info.clang_type,
.decl_id = SemIR::ErrorInst::InstId,
.function_id = SemIR::FunctionId::None};
auto function_decl_id = ImportFunction(context, loc_id, callee_info);
if (function_decl_id.has_value()) {
result.decl_id = *function_decl_id;
result.function_id = context.insts()
.GetAs<SemIR::FunctionDecl>(*function_decl_id)
.function_id;
DefineAsThunkCall(context, loc_id, callee_info,
context.functions().Get(result.function_id));
}
context.clang_function_pointer_types().Update(clang_type_id, result);
return result;
}
namespace {
// An item to be imported in an import worklist.
// TODO: If worklists ever become particularly large, consider changing this
// to use a `PointerIntPair`.
// to use a `PointerUnion` and/or `PointerIntPair`.
struct ImportItem {
// A declaration that we want to import.
SemIR::ClangDeclKey decl_key;
ImportKey key;
// Whether we have added `decl`'s dependencies to the worklist.
bool added_dependencies;
};
@@ -2073,20 +2170,27 @@ struct ImportItem {
using ImportWorklist = llvm::SmallVector<ImportItem>;
} // namespace
// Adds the given declaration to our list of declarations to import.
static auto AddDependentDecl(Context& context, SemIR::ClangDeclKey decl,
ImportWorklist& worklist) -> void {
if (!IsClangDeclImported(context, decl)) {
worklist.push_back({.decl_key = decl, .added_dependencies = false});
// Adds the given declaration to the worklist. As an optimization, does nothing
// if the declaration has already been imported.
static auto PushDecl(Context& context, SemIR::ClangDeclKey decl,
ImportWorklist& worklist) -> void {
if (!IsImported(context, decl)) {
worklist.push_back({.key = decl, .added_dependencies = false});
}
}
// Finds all decls that need to be imported before importing the given type and
// adds them to the given set.
static auto AddDependentUnimportedTypeDecls(Context& context,
clang::QualType type,
ImportWorklist& worklist) -> void {
// Adds the declarations that the given type directly depends on to the
// worklist.
static auto PushType(Context& context, clang::QualType type,
ImportWorklist& worklist) -> void {
while (true) {
if (type->isFunctionPointerType()) {
const clang::Type* type_ptr = type.getCanonicalType().getTypePtr();
if (!IsImported(context, type_ptr)) {
worklist.push_back({.key = type_ptr, .added_dependencies = false});
}
return;
}
if (type->isPointerType() || type->isReferenceType()) {
type = type->getPointeeType();
} else if (const clang::ArrayType* array_type =
@@ -2098,52 +2202,68 @@ static auto AddDependentUnimportedTypeDecls(Context& context,
}
if (const auto* tag_type = type->getAs<clang::TagType>()) {
AddDependentDecl(context, SemIR::ClangDeclKey(tag_type->getDecl()),
worklist);
PushDecl(context, SemIR::ClangDeclKey(tag_type->getDecl()), worklist);
}
}
// Finds all decls that need to be imported before importing the given function
// and adds them to the given set.
static auto AddDependentUnimportedFunctionDecls(
Context& context, const clang::FunctionDecl& clang_decl,
SemIR::ClangDeclSignatureId signature_id, ImportWorklist& worklist)
// Finds all decls that need to be imported before importing the given type,
// which must be a `FunctionProtoType`, and adds them to the worklist. If
// `num_params` is non-negative, only the first `num_params` parameter types
// will be added.
static auto AddUnimportedDependenciesForFunctionType(Context& context,
clang::QualType type,
int num_params,
ImportWorklist& worklist)
-> void {
const auto& signature = context.clang_decl_signatures().Get(signature_id);
const auto* function_type =
clang_decl.getType()->castAs<clang::FunctionProtoType>();
for (int i : llvm::seq(clang_decl.hasCXXExplicitFunctionObjectParameter() +
signature.num_params)) {
AddDependentUnimportedTypeDecls(context, function_type->getParamType(i),
worklist);
const auto* function_type = type->castAs<clang::FunctionProtoType>();
if (num_params < 0) {
num_params = function_type->getNumParams();
}
AddDependentUnimportedTypeDecls(context, clang_decl.getReturnType(),
worklist);
for (int i : llvm::seq(num_params)) {
PushType(context, function_type->getParamType(i), worklist);
}
PushType(context, function_type->getReturnType(), worklist);
}
// Finds all decls that need to be imported before importing the given
// declaration and adds them to the given set.
static auto AddDependentUnimportedDecls(Context& context,
SemIR::ClangDeclKey key,
ImportWorklist& worklist) -> void {
clang::Decl* clang_decl = key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
AddDependentUnimportedFunctionDecls(context, *clang_function_decl,
key.signature_id, worklist);
} else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
if (!isa<clang::TagDecl>(clang_decl)) {
AddDependentUnimportedTypeDecls(
context, type_decl->getASTContext().getTypeDeclType(type_decl),
worklist);
// declaration and adds them to the worklist.
static auto AddUnimportedDependencies(Context& context, ImportKey key,
ImportWorklist& worklist) -> void {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key): {
clang::Decl* clang_decl = decl_key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
const auto& signature =
context.clang_decl_signatures().Get(decl_key.signature_id);
int num_params =
clang_function_decl->hasCXXExplicitFunctionObjectParameter() +
signature.num_params;
AddUnimportedDependenciesForFunctionType(
context, clang_function_decl->getType(), num_params, worklist);
} else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
if (!isa<clang::TagDecl>(clang_decl)) {
PushType(context,
type_decl->getASTContext().getTypeDeclType(type_decl),
worklist);
}
} else if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
PushType(context, var_decl->getType(), worklist);
}
auto* parent = GetParentDecl(clang_decl);
if (llvm::isa_and_nonnull<clang::TagDecl, clang::NamespaceDecl,
clang::TranslationUnitDecl>(parent)) {
PushDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent),
worklist);
}
break;
}
case CARBON_KIND(const clang::Type* type): {
if (type->isFunctionPointerType()) {
AddUnimportedDependenciesForFunctionType(
context, type->getPointeeType(), -1, worklist);
}
break;
}
} else if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
AddDependentUnimportedTypeDecls(context, var_decl->getType(), worklist);
}
auto* parent = GetParentDecl(clang_decl);
if (llvm::isa_and_nonnull<clang::TagDecl, clang::NamespaceDecl,
clang::TranslationUnitDecl>(parent)) {
AddDependentDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent),
worklist);
}
}
@@ -2243,54 +2363,72 @@ static auto ImportTemplateDecl(Context& context,
// Imports a declaration from Clang to Carbon. Returns the instruction for the
// new Carbon declaration, which will be an ErrorInst on failure. Assumes all
// dependencies have already been imported.
static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
SemIR::ClangDeclKey key)
-> SemIR::InstId {
clang::Decl* clang_decl = key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
return ImportFunctionDecl(context, loc_id, clang_function_decl,
key.signature_id);
}
if (auto* clang_namespace_decl = dyn_cast<clang::NamespaceDecl>(clang_decl)) {
return ImportNamespaceDecl(context, clang_namespace_decl);
}
if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
auto type = clang_decl->getASTContext().getTypeDeclType(type_decl);
auto type_inst_id = MapType(context, loc_id, type).inst_id;
if (!type_inst_id.has_value()) {
context.TODO(AddImportIRInst(context.sem_ir(), type_decl->getLocation()),
llvm::formatv("Unsupported: Type declaration: {0}",
type.getAsString()));
static auto ImportAfterDependencies(Context& context, SemIR::LocId loc_id,
ImportKey key) -> SemIR::InstId {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key): {
clang::Decl* clang_decl = decl_key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
return ImportFunctionDecl(context, loc_id, clang_function_decl,
decl_key.signature_id);
}
if (auto* clang_namespace_decl =
dyn_cast<clang::NamespaceDecl>(clang_decl)) {
return ImportNamespaceDecl(context, clang_namespace_decl);
}
if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
auto type = clang_decl->getASTContext().getTypeDeclType(type_decl);
auto type_inst_id = MapType(context, loc_id, type).inst_id;
if (!type_inst_id.has_value()) {
context.TODO(
AddImportIRInst(context.sem_ir(), type_decl->getLocation()),
llvm::formatv("Unsupported: Type declaration: {0}",
type.getAsString()));
return SemIR::ErrorInst::InstId;
}
context.clang_decls().Add(
{.key = decl_key, .inst_id = type_inst_id, .is_imported = true});
return type_inst_id;
}
if (isa<clang::FieldDecl, clang::IndirectFieldDecl>(clang_decl)) {
// Usable fields get imported as a side effect of importing the class.
if (SemIR::InstId existing_inst_id =
LookupClangDeclInstId(context, decl_key);
existing_inst_id.has_value()) {
return existing_inst_id;
}
context.TODO(
AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
"Unsupported: field declaration has unhandled type or kind");
return SemIR::ErrorInst::InstId;
}
if (auto* enum_const_decl =
dyn_cast<clang::EnumConstantDecl>(clang_decl)) {
return ImportEnumConstantDecl(context, enum_const_decl);
}
if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
return ImportVarDecl(context, loc_id, var_decl);
}
if (auto* template_decl = dyn_cast<clang::TemplateDecl>(clang_decl)) {
return ImportTemplateDecl(context, template_decl);
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
llvm::formatv("Unsupported: Declaration type {0}",
clang_decl->getDeclKindName()));
return SemIR::ErrorInst::InstId;
}
context.clang_decls().Add(
{.key = key, .inst_id = type_inst_id, .is_imported = true});
return type_inst_id;
}
if (isa<clang::FieldDecl, clang::IndirectFieldDecl>(clang_decl)) {
// Usable fields get imported as a side effect of importing the class.
if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key);
existing_inst_id.has_value()) {
return existing_inst_id;
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
"Unsupported: field declaration has unhandled type or kind");
return SemIR::ErrorInst::InstId;
}
if (auto* enum_const_decl = dyn_cast<clang::EnumConstantDecl>(clang_decl)) {
return ImportEnumConstantDecl(context, enum_const_decl);
}
if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
return ImportVarDecl(context, loc_id, var_decl);
}
if (auto* template_decl = dyn_cast<clang::TemplateDecl>(clang_decl)) {
return ImportTemplateDecl(context, template_decl);
}
case CARBON_KIND(const clang::Type* type): {
if (type->isFunctionPointerType()) {
return ImportFunctionPointer(context, type);
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
llvm::formatv("Unsupported: Declaration type {0}",
clang_decl->getDeclKindName()));
return SemIR::ErrorInst::InstId;
context.TODO(loc_id,
llvm::formatv("Unsupported: type {0}",
clang::QualType(type, 0).getAsString()));
return SemIR::ErrorInst::InstId;
}
}
}
// Attempts to import a set of declarations. Returns `false` if an error was
@@ -2306,7 +2444,7 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
// adding the item to the worklist, but it might have been added to the
// worklist twice before the first time we visited it. For example, this
// happens for `fn F(a: Cpp.T, b: Cpp.T)`.
if (IsClangDeclImported(context, item.decl_key)) {
if (IsImported(context, item.key)) {
worklist.pop_back();
continue;
}
@@ -2314,17 +2452,17 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
// First time visiting this declaration (preorder): add its dependencies
// to the work list.
item.added_dependencies = true;
AddDependentUnimportedDecls(context, item.decl_key, worklist);
AddUnimportedDependencies(context, item.key, worklist);
} else {
// Second time visiting this declaration (postorder): its dependencies are
// already imported, so we can import it now.
auto decl_key = worklist.pop_back_val().decl_key;
auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl_key);
auto decl_key = worklist.pop_back_val().key;
auto inst_id = ImportAfterDependencies(context, loc_id, decl_key);
CARBON_CHECK(inst_id.has_value());
if (inst_id == SemIR::ErrorInst::InstId) {
return false;
}
CARBON_CHECK(IsClangDeclImported(context, decl_key));
CARBON_CHECK(IsImported(context, decl_key));
}
}
@@ -2335,7 +2473,7 @@ auto ImportCppDecl(Context& context, SemIR::LocId loc_id,
SemIR::ClangDeclKey key) -> SemIR::InstId {
// Collect dependencies by walking the dependency graph in depth-first order.
ImportWorklist worklist;
AddDependentDecl(context, key, worklist);
PushDecl(context, key, worklist);
if (!ImportDeclSet(context, loc_id, worklist)) {
return SemIR::ErrorInst::InstId;
}
@@ -2346,7 +2484,7 @@ auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type)
-> TypeExpr {
// Collect dependencies by walking the dependency graph in depth-first order.
ImportWorklist worklist;
AddDependentUnimportedTypeDecls(context, type, worklist);
PushType(context, type, worklist);
if (!ImportDeclSet(context, loc_id, worklist)) {
return {.inst_id = SemIR::ErrorInst::TypeInstId,
.type_id = SemIR::ErrorInst::TypeId};
+8
View File
@@ -83,6 +83,14 @@ inline auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, signature_id));
}
// Imports a notional `__invoke` method of the given function pointer type.
// The imported carbon function takes the pointer value as a `self` parameter,
// and the pointer type's parameter types as ordinary parameters.
auto ImportFunctionPointerInvoke(
Context& context, SemIR::LocId loc_id,
SemIR::ClangFunctionPointerTypeId clang_type_id)
-> SemIR::ClangFunctionPointerTypeInfo;
// Returns the type that intN_t or uintN_t is an alias for.
auto GetIntNType(const clang::ASTContext& ast_context, unsigned width,
bool is_signed) -> clang::QualType;
+49 -3
View File
@@ -197,14 +197,55 @@ CalleeFunctionInfo::CalleeFunctionInfo(Context& context,
/*for_parameter=*/false);
}
CalleeFunctionInfo::CalleeFunctionInfo(Context& context,
const clang::Type* function_pointer_type)
: self_param_kind(SelfParamKind::FunctionPointer),
decl(nullptr),
decl_name(&context.ast_context().Idents.get("__invoke")),
clang_loc(),
sem_ir_loc(SemIR::LocId::None),
function_type(function_pointer_type->getPointeeType()
->getAs<clang::FunctionProtoType>()),
signature_id(SemIR::ClangDeclSignatureId::None),
signature(nullptr),
num_callee_params(function_type->getNumParams()),
self_param_type(function_pointer_type, 0),
effective_return_type(function_type->getReturnType()),
has_simple_return_type(IsSimpleAbiType(context.ast_context(),
effective_return_type,
/*for_parameter=*/false)) {
SemIR::ClangDeclSignature local_signature;
local_signature.kind = SemIR::ClangDeclSignature::Normal;
local_signature.num_params =
static_cast<int32_t>(function_type->getNumParams());
local_signature.self_passing_mode =
SemIR::ClangDeclSignature::PassingMode::ByValue;
local_signature.passing_modes.assign(
local_signature.num_params,
SemIR::ClangDeclSignature::PassingMode::ByValue);
signature_id =
context.clang_decl_signatures().Add(std::move(local_signature));
signature = &context.clang_decl_signatures().Get(signature_id);
}
auto CalleeFunctionInfo::GetCalleeParamIdentifier(int i) const
-> clang::IdentifierInfo* {
return decl->getParamDecl(i)->getIdentifier();
switch (self_param_kind) {
case SelfParamKind::FunctionPointer:
return nullptr;
default:
return decl->getParamDecl(i)->getIdentifier();
}
}
auto CalleeFunctionInfo::GetCalleeParamLocation(int i) const
-> clang::SourceLocation {
return decl->getParamDecl(i)->getLocation();
switch (self_param_kind) {
case SelfParamKind::FunctionPointer:
return {};
default:
return decl->getParamDecl(i)->getLocation();
}
}
auto IsCppThunkRequired(Context& context, const CalleeFunctionInfo& callee_info)
@@ -514,7 +555,7 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
cast<clang::CXXMethodDecl>(callee_info.decl)
->getFunctionObjectParameterReferenceType();
auto* object_param_ref = BuildThunkParamRef(
sema, thunk_function_decl, 0,
sema, thunk_function_decl, /*thunk_index=*/0,
callee_info.signature->self_passing_mode, object_param_type);
constexpr bool IsArrow = false;
auto object =
@@ -533,6 +574,11 @@ static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
clang::OK_Ordinary);
break;
}
case CalleeFunctionInfo::SelfParamKind::FunctionPointer:
callee = BuildThunkParamRef(sema, thunk_function_decl, 0,
callee_info.signature->self_passing_mode,
callee_info.self_param_type);
break;
case CalleeFunctionInfo::SelfParamKind::None:
if (isa<clang::CXXConstructorDecl>(callee_info.decl)) {
break;
+19
View File
@@ -28,11 +28,23 @@ struct CalleeFunctionInfo {
explicit CalleeFunctionInfo(Context& context, clang::FunctionDecl* decl,
SemIR::ClangDeclSignatureId signature_id);
// Constructs a CalleeFunctionInfo that represents a C++ function pointer.
// We treat function pointer types as having an `__invoke` method, with the
// pointer value acting as the implicit object parameter. On the C++ side this
// method is notional, and has no declaration: in effect, it is inlined into
// its own simple-ABI thunk (which always exists, even if the parameter and
// return types already have simple ABIs). The Carbon counterpart of this
// method is real, however, and takes the pointer value as its `self`
// parameter.
explicit CalleeFunctionInfo(Context& context,
const clang::Type* function_pointer_type);
// Returns the offset I such that callee parameter N corresponds to
// parameter N+I of the imported Carbon function.
auto callee_param_to_carbon_param_offset() const -> int {
switch (self_param_kind) {
case SelfParamKind::ImplicitObjectParam:
case SelfParamKind::FunctionPointer:
return 1;
case SelfParamKind::None:
case SelfParamKind::ExplicitObjectParam:
@@ -47,6 +59,7 @@ struct CalleeFunctionInfo {
case SelfParamKind::ExplicitObjectParam:
return 1;
case SelfParamKind::ImplicitObjectParam:
case SelfParamKind::FunctionPointer:
case SelfParamKind::None:
return 0;
}
@@ -101,6 +114,12 @@ struct CalleeFunctionInfo {
// argument list. Consequently, the Nth Carbon parameter corresponds to the
// N-1th callee argument, and the Nth callee parameter.
ExplicitObjectParam,
// The callee is the notional `__invoke` method of a C++ function pointer,
// which is treated has having the pointer value as its implicit object
// parameter (see the constructor comments for details). Consequently
// the Nth thunk parameter corresponds to the N-1th callee argument and the
// N-1th callee parameter.
FunctionPointer,
};
SelfParamKind self_param_kind;
+6
View File
@@ -275,6 +275,12 @@ static auto TryMapType(Context& context, SemIR::TypeId type_id)
return inner_type.withConst();
}};
}
case CARBON_KIND(SemIR::CppFunctionPointerType fn_type): {
return clang::QualType(context.clang_function_pointer_types()
.Get(fn_type.clang_type_id)
.clang_type,
/*Quals=*/0);
}
case SemIR::FloatLiteralType::Kind: {
return context.ast_context().DoubleTy;
}
+1
View File
@@ -184,6 +184,7 @@ static auto IsBuiltinWithTrivialDestruction(Context& context,
SemIR::InstId inst_id) -> bool {
CARBON_KIND_SWITCH(context.insts().Get(inst_id)) {
case SemIR::BoolType::Kind:
case SemIR::CppFunctionPointerType::Kind:
case SemIR::FacetType::Kind:
case SemIR::FloatType::Kind:
case SemIR::FormType::Kind:
+18 -4
View File
@@ -2888,10 +2888,24 @@ static auto TryEvalCall(EvalContext& outer_eval_context, SemIR::LocId loc_id,
static auto GetReturnStorageParamIndexRange(EvalContext& eval_context,
const SemIR::Callee& callee)
-> std::pair<int, int> {
if (const auto* callee_function =
std::get_if<SemIR::CalleeFunction>(&callee)) {
const auto& function =
eval_context.functions().Get(callee_function->function_id);
auto function_id = SemIR::FunctionId::None;
CARBON_KIND_SWITCH(callee) {
case CARBON_KIND(SemIR::CalleeFunction callee_function): {
function_id = callee_function.function_id;
break;
}
case CARBON_KIND(SemIR::CalleeCppFunctionPointer callee_function_ptr): {
function_id = eval_context.context()
.clang_function_pointer_types()
.Get(callee_function_ptr.function_type_id)
.function_id;
break;
}
default:
break;
}
if (function_id.has_value()) {
const auto& function = eval_context.functions().Get(function_id);
return {function.call_param_ranges.return_begin().index,
function.call_param_ranges.return_end().index};
}
+6
View File
@@ -2695,6 +2695,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
resolver.local_context().TODO(SemIR::LocId::None,
"Unsupported: Importing C++ functions that "
"require thunks indirectly");
break;
}
case SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk: {
CARBON_FATAL(
"Function pointer thunks are always file-local, and should never be "
"imported.");
}
}
+6 -1
View File
@@ -79,7 +79,12 @@ static auto GetSelfIfInstanceMethod(const SemIR::File& sem_ir,
// `instance.(Class.StaticMethod)()` like we do in pure Carbon code.
return overload.self_id;
}
case CARBON_KIND(SemIR::CalleeCppFunctionPointer _): {
// We model a function pointer callee as a method, but it can't be
// accessed via instance binding, so for this purpose it's not an instance
// method.
return std::nullopt;
}
case CARBON_KIND(SemIR::CalleeError _): {
return std::nullopt;
}
@@ -67,9 +67,16 @@ fn F() {
// CHECK:STDERR:
Cpp.TakesFunction(&n[0]);
// CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: int (*)(int)` [SemanticsTodo]
// CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+11]]:21: error: cannot implicitly convert expression of type `Cpp.nullptr_t` to `<C++ type int (*)(int)>` [ConversionFailure]
// CHECK:STDERR: Cpp.TakesFunction(Cpp.nullptr);
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: ^~~~~~~~~~~
// CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+8]]:21: note: type `Cpp.nullptr_t` does not implement interface `Core.ImplicitAs(<C++ type int (*)(int)>)` [MissingImplInMemberAccessInContext]
// CHECK:STDERR: Cpp.TakesFunction(Cpp.nullptr);
// CHECK:STDERR: ^~~~~~~~~~~
// CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE-35]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./params.h:3:24: note: initializing function parameter [InCallToFunctionParam]
// CHECK:STDERR: void TakesFunction(int f(int));
// CHECK:STDERR: ^
// CHECK:STDERR:
Cpp.TakesFunction(Cpp.nullptr);
//@dump-sem-ir-end
@@ -500,6 +507,9 @@ fn F() {
// CHECK:STDOUT: %ptr.d08: type = ptr_type %i32 [concrete]
// CHECK:STDOUT: %Cpp.nullptr_t: type = class_type @NullptrT [concrete]
// CHECK:STDOUT: %uninit: %Cpp.nullptr_t = uninitialized_value [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type41000000 [concrete]
// CHECK:STDOUT: %TakesFunction.type: type = fn_type @TakesFunction [concrete]
// CHECK:STDOUT: %TakesFunction: %TakesFunction.type = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.705: %pattern_type.6b6 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.70d: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt.705 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc22_3.1 [concrete]
@@ -531,6 +541,11 @@ fn F() {
// CHECK:STDOUT: %UnformedInit.impl_witness_table.9b9 = impl_witness_table (), @Int.as.UnformedInit.impl [concrete]
// CHECK:STDOUT: %Core.import_ref.edf: @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e74) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.0e9.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.845)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1aa = impl_witness_table (%Core.import_ref.edf), @Core.IntLiteral.as.ImplicitAs.impl.0e9 [concrete]
// CHECK:STDOUT: %TakesFunction.decl: %TakesFunction.type = fn_decl @TakesFunction [concrete = constants.%TakesFunction] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
@@ -605,11 +620,13 @@ fn F() {
// CHECK:STDOUT: %.loc31_24.2: %i32 = converted %int_0, %.loc31_24.1 [concrete = constants.%int_0.3c0]
// CHECK:STDOUT: %.loc31_25: ref %i32 = array_index %n.ref, %.loc31_24.2
// CHECK:STDOUT: %addr: %ptr.d08 = addr_of %.loc31_25
// CHECK:STDOUT: %Cpp.ref.loc37_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakesFunction.ref.loc37: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value]
// CHECK:STDOUT: %Cpp.ref.loc37_21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %Cpp.ref.loc44_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakesFunction.ref.loc44: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value]
// CHECK:STDOUT: %Cpp.ref.loc44_21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %nullptr.ref: %Cpp.nullptr_t = name_ref nullptr, %uninit [concrete = constants.%uninit]
// CHECK:STDOUT: %.loc44: %.659 = converted %nullptr.ref, <error> [concrete = <error>]
// CHECK:STDOUT: %TakesFunction.call: init %empty_tuple.type = call imports.%TakesFunction.decl(<error>)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %n.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%n.var)
// CHECK:STDOUT: <elided>
@@ -0,0 +1,885 @@
// 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/import/function_ptr.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/import/function_ptr.carbon
// --- simple_callback.h
using CallbackType = int (*)(float);
void TakeCallback(CallbackType callback);
auto GetCallback() -> CallbackType;
// --- relay_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn F() {
//@dump-sem-ir-begin
Cpp.TakeCallback(Cpp.GetCallback());
//@dump-sem-ir-end
}
// --- fail_todo_convert_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn MyCallback(arg: f32) -> i32;
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_convert_fn_ptr.carbon:[[@LINE+4]]:41: error: expression cannot be used as a value [UseOfNonExprAsValue]
// CHECK:STDERR: var unused fn_ptr: Cpp.CallbackType = MyCallback;
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
var unused fn_ptr: Cpp.CallbackType = MyCallback;
//@dump-sem-ir-end
}
// --- fail_todo_pass_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn MyCallback(arg: f32) -> i32;
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_pass_fn_ptr.carbon:[[@LINE+4]]:20: error: call argument of type `<type of MyCallback>` is not supported [CppCallArgTypeNotSupported]
// CHECK:STDERR: Cpp.TakeCallback(MyCallback);
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
Cpp.TakeCallback(MyCallback);
//@dump-sem-ir-end
}
// --- call_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn F() {
//@dump-sem-ir-begin
var unused x: i32 = Cpp.GetCallback()(1.0);
//@dump-sem-ir-end
}
// --- complex_callback.h
class C {};
using CallbackType = C (*)(C);
void TakeComplexCallback(CallbackType callback);
auto GetComplexCallback() -> CallbackType;
// --- relay_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn F() {
//@dump-sem-ir-begin
Cpp.TakeComplexCallback(Cpp.GetComplexCallback());
//@dump-sem-ir-end
}
// --- fail_todo_convert_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn MyCallback(arg: Cpp.C) -> Cpp.C;
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_convert_complex_fn_ptr.carbon:[[@LINE+4]]:41: error: expression cannot be used as a value [UseOfNonExprAsValue]
// CHECK:STDERR: var unused fn_ptr: Cpp.CallbackType = MyCallback;
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
var unused fn_ptr: Cpp.CallbackType = MyCallback;
//@dump-sem-ir-end
}
// --- fail_todo_pass_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn MyCallback(arg: Cpp.C) -> Cpp.C;
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_pass_complex_fn_ptr.carbon:[[@LINE+5]]:27: error: call argument of type `<type of MyCallback>` is not supported [CppCallArgTypeNotSupported]
// CHECK:STDERR: Cpp.TakeComplexCallback(MyCallback);
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: error: calling function with incomplete return type 'C' [CppInteropParseError]
Cpp.TakeComplexCallback(MyCallback);
//@dump-sem-ir-end
}
// --- call_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn F() {
//@dump-sem-ir-begin
var unused x: Cpp.C = Cpp.GetComplexCallback()(Cpp.C.C());
//@dump-sem-ir-end
}
// --- forward_decl.h
class C;
using CallbackType = C (*)();
auto ReturnCallback() -> CallbackType;
// --- mention_forward_decl.carbon
library "[[@TEST_NAME]]";
import Cpp library "forward_decl.h";
fn F() {
//@dump-sem-ir-begin
var unused callback: Cpp.CallbackType = Cpp.ReturnCallback();
//@dump-sem-ir-end
}
// --- fail_use_forward_decl.carbon
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_use_forward_decl.carbon:[[@LINE+4]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./forward_decl.h:2:7: note: forward declaration of 'C' [CppInteropParseNote]
// CHECK:STDERR: 2 | class C;
// CHECK:STDERR: | ^
import Cpp library "forward_decl.h";
fn F() {
// CHECK:STDERR: fail_use_forward_decl.carbon:[[@LINE+16]]:3: note: in thunk for C++ function used here [InCppThunk]
// CHECK:STDERR: Cpp.ReturnCallback()();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_use_forward_decl.carbon:[[@LINE+13]]:3: note: in thunk for C++ function pointer type used here [InCppFunctionPointerThunk]
// CHECK:STDERR: Cpp.ReturnCallback()();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_use_forward_decl.carbon:[[@LINE+9]]:3: error: function returns incomplete type `Cpp.C` [IncompleteTypeInFunctionReturnType]
// CHECK:STDERR: Cpp.ReturnCallback()();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_use_forward_decl.carbon:[[@LINE-13]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./forward_decl.h:2:7: note: class was forward declared here [ClassForwardDeclaredHere]
// CHECK:STDERR: class C;
// CHECK:STDERR: ^
// CHECK:STDERR: note: return type declared here [IncompleteReturnTypeHere]
// CHECK:STDERR:
Cpp.ReturnCallback()();
}
// CHECK:STDOUT: --- relay_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.type: type = cpp_overload_set_type @TakeCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.value: %TakeCallback.cpp_overload_set.type = cpp_overload_set_value @TakeCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetCallback.cpp_overload_set.type: type = cpp_overload_set_type @GetCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetCallback.cpp_overload_set.value: %GetCallback.cpp_overload_set.type = cpp_overload_set_value @GetCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type7E000000 [concrete]
// CHECK:STDOUT: %GetCallback.type: type = fn_type @GetCallback [concrete]
// CHECK:STDOUT: %GetCallback: %GetCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %TakeCallback.type: type = fn_type @TakeCallback [concrete]
// CHECK:STDOUT: %TakeCallback: %TakeCallback.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .TakeCallback = %TakeCallback.cpp_overload_set.value
// CHECK:STDOUT: .GetCallback = %GetCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.value: %TakeCallback.cpp_overload_set.type = cpp_overload_set_value @TakeCallback.cpp_overload_set [concrete = constants.%TakeCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetCallback.cpp_overload_set.value: %GetCallback.cpp_overload_set.type = cpp_overload_set_value @GetCallback.cpp_overload_set [concrete = constants.%GetCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetCallback.decl: %GetCallback.type = fn_decl @GetCallback [concrete = constants.%GetCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %TakeCallback.decl: %TakeCallback.type = fn_decl @TakeCallback [concrete = constants.%TakeCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref.loc7_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakeCallback.ref: %TakeCallback.cpp_overload_set.type = name_ref TakeCallback, imports.%TakeCallback.cpp_overload_set.value [concrete = constants.%TakeCallback.cpp_overload_set.value]
// CHECK:STDOUT: %Cpp.ref.loc7_20: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %GetCallback.ref: %GetCallback.cpp_overload_set.type = name_ref GetCallback, imports.%GetCallback.cpp_overload_set.value [concrete = constants.%GetCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetCallback.call: init %.659 = call imports.%GetCallback.decl()
// CHECK:STDOUT: %.loc7_36.1: %.659 = value_of_initializer %GetCallback.call
// CHECK:STDOUT: %.loc7_36.2: %.659 = converted %GetCallback.call, %.loc7_36.1
// CHECK:STDOUT: %TakeCallback.call: init %empty_tuple.type = call imports.%TakeCallback.decl(%.loc7_36.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_convert_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %MyCallback.type: type = fn_type @MyCallback [concrete]
// CHECK:STDOUT: %MyCallback: %MyCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type41000000 [concrete]
// CHECK:STDOUT: %pattern_type.4b7: type = pattern_type %.659 [concrete]
// CHECK:STDOUT: %fn_ptr.patt: %pattern_type.4b7 = ref_binding_pattern fn_ptr [concrete]
// CHECK:STDOUT: %fn_ptr.var_patt: %pattern_type.4b7 = var_pattern %fn_ptr.patt [concrete]
// CHECK:STDOUT: %self.param_patt.952: %pattern_type.4b7 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.a35: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt.952 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .CallbackType = constants.%.659
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc13 [concrete = constants.%Destroy.WithSelf.Op.403] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %fn_ptr.var: ref %.659 = var_storage %fn_ptr.var_patt
// CHECK:STDOUT: %MyCallback.ref: %MyCallback.type = name_ref MyCallback, file.%MyCallback.decl [concrete = constants.%MyCallback]
// CHECK:STDOUT: assign %fn_ptr.var, <error>
// CHECK:STDOUT: %.loc13: type = splice_block %CallbackType.ref [concrete = constants.%.659] {
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %CallbackType.ref: type = name_ref CallbackType, constants.%.659 [concrete = constants.%.659]
// CHECK:STDOUT: }
// CHECK:STDOUT: %fn_ptr: ref %.659 = wrapper_binding fn_ptr, %fn_ptr.var
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %fn_ptr.patt: %pattern_type.4b7 = ref_binding_pattern fn_ptr [concrete = constants.%fn_ptr.patt]
// CHECK:STDOUT: %fn_ptr.var_patt: %pattern_type.4b7 = var_pattern %fn_ptr.patt [concrete = constants.%fn_ptr.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %fn_ptr.var, constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%fn_ptr.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13(%self.param: ref %.659) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_pass_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %MyCallback.type: type = fn_type @MyCallback [concrete]
// CHECK:STDOUT: %MyCallback: %MyCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.type: type = cpp_overload_set_type @TakeCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.value: %TakeCallback.cpp_overload_set.type = cpp_overload_set_value @TakeCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .TakeCallback = %TakeCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %TakeCallback.cpp_overload_set.value: %TakeCallback.cpp_overload_set.type = cpp_overload_set_value @TakeCallback.cpp_overload_set [concrete = constants.%TakeCallback.cpp_overload_set.value]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakeCallback.ref: %TakeCallback.cpp_overload_set.type = name_ref TakeCallback, imports.%TakeCallback.cpp_overload_set.value [concrete = constants.%TakeCallback.cpp_overload_set.value]
// CHECK:STDOUT: %MyCallback.ref: %MyCallback.type = name_ref MyCallback, file.%MyCallback.decl [concrete = constants.%MyCallback]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- call_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.6b6: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %x.patt: %pattern_type.6b6 = ref_binding_pattern x [concrete]
// CHECK:STDOUT: %x.var_patt: %pattern_type.6b6 = var_pattern %x.patt [concrete]
// CHECK:STDOUT: %GetCallback.cpp_overload_set.type: type = cpp_overload_set_type @GetCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetCallback.cpp_overload_set.value: %GetCallback.cpp_overload_set.type = cpp_overload_set_value @GetCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type51000000 [concrete]
// CHECK:STDOUT: %GetCallback.type: type = fn_type @GetCallback [concrete]
// CHECK:STDOUT: %GetCallback: %GetCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %float.6da: Core.FloatLiteral = float_literal_value 10e-1 [concrete]
// CHECK:STDOUT: %f32.c73: type = class_type @Float, @Float(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.240: type = pattern_type %f32.c73 [concrete]
// CHECK:STDOUT: %ptr.da4: type = ptr_type %f32.c73 [concrete]
// CHECK:STDOUT: %__invoke__carbon_thunk.type: type = fn_type @__invoke__carbon_thunk [concrete]
// CHECK:STDOUT: %__invoke__carbon_thunk: %__invoke__carbon_thunk.type = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.type.ed3: type = facet_type <@ImplicitAs, @ImplicitAs(%f32.c73)> [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.063: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%N) [symbolic]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.d4f: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.063 = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
// CHECK:STDOUT: %ImplicitAs.impl_witness.2fa: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.1e0, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.a62: type = fn_type @Core.FloatLiteral.as.ImplicitAs.impl.Convert, @Core.FloatLiteral.as.ImplicitAs.impl(%int_32) [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b37: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.a62 = struct_value () [concrete]
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.ed3 = facet_value Core.FloatLiteral, (%ImplicitAs.impl_witness.2fa) [concrete]
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.7cb: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%f32.c73, %ImplicitAs.facet) [concrete]
// CHECK:STDOUT: %.7fe: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.7cb, %ImplicitAs.facet [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b37 [concrete]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b37, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
// CHECK:STDOUT: %bound_method.3e4: <bound method> = bound_method %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %float.943: %f32.c73 = float_value 1 [concrete]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.type.ff3: type = fn_type @Float.as.Copy.impl.Op, @Float.as.Copy.impl(%N) [symbolic]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.d93: %Float.as.Copy.impl.Op.type.ff3 = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.impl_witness.f13: <witness> = impl_witness imports.%Copy.impl_witness_table.8dd, @Float.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.type.bf9: type = fn_type @Float.as.Copy.impl.Op, @Float.as.Copy.impl(%int_32) [concrete]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.ed6: %Float.as.Copy.impl.Op.type.bf9 = struct_value () [concrete]
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %f32.c73, (%Copy.impl_witness.f13) [concrete]
// CHECK:STDOUT: %Copy.WithSelf.Op.type.1f6: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
// CHECK:STDOUT: %.241: type = fn_type_with_self_type %Copy.WithSelf.Op.type.1f6, %Copy.facet [concrete]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.bound: <bound method> = bound_method %float.943, %Float.as.Copy.impl.Op.ed6 [concrete]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Float.as.Copy.impl.Op.ed6, @Float.as.Copy.impl.Op(%int_32) [concrete]
// CHECK:STDOUT: %bound_method.af3: <bound method> = bound_method %float.943, %Float.as.Copy.impl.Op.specific_fn [concrete]
// CHECK:STDOUT: %.ed2: ref %f32.c73 = temporary invalid, %float.943 [concrete]
// CHECK:STDOUT: %addr: %ptr.da4 = addr_of %.ed2 [concrete]
// CHECK:STDOUT: %self.param_patt.c62: %pattern_type.240 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.ed0: %pattern_type.240 = wrapper_binding_pattern self, %self.param_patt.c62 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc7_41 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc7_41 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.1: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7_41 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.1: %Destroy.WithSelf.SelfDestruct.type.fbceb5.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %.ed2, %Destroy.WithSelf.SelfDestruct.db3fdb.1 [concrete]
// CHECK:STDOUT: %self.param_patt.705: %pattern_type.6b6 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.70d: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt.705 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc7_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc7_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .GetCallback = %GetCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %GetCallback.cpp_overload_set.value: %GetCallback.cpp_overload_set.type = cpp_overload_set_value @GetCallback.cpp_overload_set [concrete = constants.%GetCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetCallback.decl: %GetCallback.type = fn_decl @GetCallback [concrete = constants.%GetCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %__invoke__carbon_thunk.decl: %__invoke__carbon_thunk.type = fn_decl @__invoke__carbon_thunk [concrete = constants.%__invoke__carbon_thunk] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import_ref.c4c: @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type (%Core.FloatLiteral.as.ImplicitAs.impl.Convert.type.063) = import_ref Core//prelude/types/float, loc{{\d+_\d+}}, loaded [symbolic = @Core.FloatLiteral.as.ImplicitAs.impl.%Core.FloatLiteral.as.ImplicitAs.impl.Convert (constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.d4f)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1e0 = impl_witness_table (%Core.import_ref.c4c), @Core.FloatLiteral.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: %Core.import_ref.e69: @Float.as.Copy.impl.%Float.as.Copy.impl.Op.type (%Float.as.Copy.impl.Op.type.ff3) = import_ref Core//prelude/types/float, loc{{\d+_\d+}}, loaded [symbolic = @Float.as.Copy.impl.%Float.as.Copy.impl.Op (constants.%Float.as.Copy.impl.Op.d93)]
// CHECK:STDOUT: %Copy.impl_witness_table.8dd = impl_witness_table (%Core.import_ref.e69), @Float.as.Copy.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc7_41 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.240 = ref_param_pattern [concrete = constants.%self.param_patt.c62]
// CHECK:STDOUT: %self.patt: %pattern_type.240 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.ed0]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %f32.c73 = ref_param call_param0
// CHECK:STDOUT: %self: ref %f32.c73 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc7_41 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.240 = ref_param_pattern [concrete = constants.%self.param_patt.c62]
// CHECK:STDOUT: %self.patt: %pattern_type.240 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.ed0]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %f32.c73 = ref_param call_param0
// CHECK:STDOUT: %self: ref %f32.c73 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc7_3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc7_3 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %x.var: ref %i32 = var_storage %x.var_patt
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %GetCallback.ref: %GetCallback.cpp_overload_set.type = name_ref GetCallback, imports.%GetCallback.cpp_overload_set.value [concrete = constants.%GetCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetCallback.call: init %.659 = call imports.%GetCallback.decl()
// CHECK:STDOUT: %float: Core.FloatLiteral = float_literal_value 10e-1 [concrete = constants.%float.6da]
// CHECK:STDOUT: %.loc7_39.1: %.659 = value_of_initializer %GetCallback.call
// CHECK:STDOUT: %.loc7_39.2: %.659 = converted %GetCallback.call, %.loc7_39.1
// CHECK:STDOUT: %impl.elem0.loc7_41.1: %.7fe = impl_witness_access constants.%ImplicitAs.impl_witness.2fa, element0 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.b37]
// CHECK:STDOUT: %bound_method.loc7_41.1: <bound method> = bound_method %float, %impl.elem0.loc7_41.1 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound]
// CHECK:STDOUT: %specific_fn.loc7_41.1: <specific function> = specific_function %impl.elem0.loc7_41.1, @Core.FloatLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn]
// CHECK:STDOUT: %bound_method.loc7_41.2: <bound method> = bound_method %float, %specific_fn.loc7_41.1 [concrete = constants.%bound_method.3e4]
// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call: init %f32.c73 = call %bound_method.loc7_41.2(%float) [concrete = constants.%float.943]
// CHECK:STDOUT: %.loc7_41.1: %f32.c73 = value_of_initializer %Core.FloatLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%float.943]
// CHECK:STDOUT: %.loc7_41.2: %f32.c73 = converted %float, %.loc7_41.1 [concrete = constants.%float.943]
// CHECK:STDOUT: %impl.elem0.loc7_41.2: %.241 = impl_witness_access constants.%Copy.impl_witness.f13, element0 [concrete = constants.%Float.as.Copy.impl.Op.ed6]
// CHECK:STDOUT: %bound_method.loc7_41.3: <bound method> = bound_method %.loc7_41.2, %impl.elem0.loc7_41.2 [concrete = constants.%Float.as.Copy.impl.Op.bound]
// CHECK:STDOUT: %specific_fn.loc7_41.2: <specific function> = specific_function %impl.elem0.loc7_41.2, @Float.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Float.as.Copy.impl.Op.specific_fn]
// CHECK:STDOUT: %bound_method.loc7_41.4: <bound method> = bound_method %.loc7_41.2, %specific_fn.loc7_41.2 [concrete = constants.%bound_method.af3]
// CHECK:STDOUT: %Float.as.Copy.impl.Op.call: init %f32.c73 = call %bound_method.loc7_41.4(%.loc7_41.2) [concrete = constants.%float.943]
// CHECK:STDOUT: %.loc7_41.3: ref %f32.c73 = temporary_storage
// CHECK:STDOUT: %.loc7_41.4: ref %f32.c73 = temporary %.loc7_41.3, %Float.as.Copy.impl.Op.call [concrete = constants.%.ed2]
// CHECK:STDOUT: %addr: %ptr.da4 = addr_of %.loc7_41.4 [concrete = constants.%addr]
// CHECK:STDOUT: %__invoke__carbon_thunk.call: init %i32 = call imports.%__invoke__carbon_thunk.decl(%.loc7_39.2, %addr)
// CHECK:STDOUT: assign %x.var, %__invoke__carbon_thunk.call
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
// CHECK:STDOUT: %x: ref %i32 = wrapper_binding x, %x.var
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %x.patt: %pattern_type.6b6 = ref_binding_pattern x [concrete = constants.%x.patt]
// CHECK:STDOUT: %x.var_patt: %pattern_type.6b6 = var_pattern %x.patt [concrete = constants.%x.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc7_41: init %empty_tuple.type = call constants.%Destroy.WithSelf.SelfDestruct.bound(constants.%.ed2)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %x.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc7_3: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%x.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc7_41(%self.param: ref %f32.c73) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7_41(%self.param: ref %f32.c73) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7_41(%self.param: ref %f32.c73) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc7_3(%self.param: ref %i32) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7_3(%self.param: ref %i32) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7_3(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- relay_complex_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.type: type = cpp_overload_set_type @TakeComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.value: %TakeComplexCallback.cpp_overload_set.type = cpp_overload_set_value @TakeComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.type: type = cpp_overload_set_type @GetComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.value: %GetComplexCallback.cpp_overload_set.type = cpp_overload_set_value @GetComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type71000000 [concrete]
// CHECK:STDOUT: %GetComplexCallback.type: type = fn_type @GetComplexCallback [concrete]
// CHECK:STDOUT: %GetComplexCallback: %GetComplexCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %TakeComplexCallback.type: type = fn_type @TakeComplexCallback [concrete]
// CHECK:STDOUT: %TakeComplexCallback: %TakeComplexCallback.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .TakeComplexCallback = %TakeComplexCallback.cpp_overload_set.value
// CHECK:STDOUT: .GetComplexCallback = %GetComplexCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.value: %TakeComplexCallback.cpp_overload_set.type = cpp_overload_set_value @TakeComplexCallback.cpp_overload_set [concrete = constants.%TakeComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.value: %GetComplexCallback.cpp_overload_set.type = cpp_overload_set_value @GetComplexCallback.cpp_overload_set [concrete = constants.%GetComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetComplexCallback.decl: %GetComplexCallback.type = fn_decl @GetComplexCallback [concrete = constants.%GetComplexCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %TakeComplexCallback.decl: %TakeComplexCallback.type = fn_decl @TakeComplexCallback [concrete = constants.%TakeComplexCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref.loc7_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakeComplexCallback.ref: %TakeComplexCallback.cpp_overload_set.type = name_ref TakeComplexCallback, imports.%TakeComplexCallback.cpp_overload_set.value [concrete = constants.%TakeComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %Cpp.ref.loc7_27: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %GetComplexCallback.ref: %GetComplexCallback.cpp_overload_set.type = name_ref GetComplexCallback, imports.%GetComplexCallback.cpp_overload_set.value [concrete = constants.%GetComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetComplexCallback.call: init %.659 = call imports.%GetComplexCallback.decl()
// CHECK:STDOUT: %.loc7_50.1: %.659 = value_of_initializer %GetComplexCallback.call
// CHECK:STDOUT: %.loc7_50.2: %.659 = converted %GetComplexCallback.call, %.loc7_50.1
// CHECK:STDOUT: %TakeComplexCallback.call: init %empty_tuple.type = call imports.%TakeComplexCallback.decl(%.loc7_50.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_convert_complex_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %MyCallback.type: type = fn_type @MyCallback [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %MyCallback: %MyCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type49000000 [concrete]
// CHECK:STDOUT: %pattern_type.4b7: type = pattern_type %.659 [concrete]
// CHECK:STDOUT: %fn_ptr.patt: %pattern_type.4b7 = ref_binding_pattern fn_ptr [concrete]
// CHECK:STDOUT: %fn_ptr.var_patt: %pattern_type.4b7 = var_pattern %fn_ptr.patt [concrete]
// CHECK:STDOUT: %self.param_patt.952: %pattern_type.4b7 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.a35: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt.952 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc13 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: .CallbackType = constants.%.659
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc13 [concrete = constants.%Destroy.WithSelf.Op.403] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %fn_ptr.var: ref %.659 = var_storage %fn_ptr.var_patt
// CHECK:STDOUT: %MyCallback.ref: %MyCallback.type = name_ref MyCallback, file.%MyCallback.decl [concrete = constants.%MyCallback]
// CHECK:STDOUT: assign %fn_ptr.var, <error>
// CHECK:STDOUT: %.loc13: type = splice_block %CallbackType.ref [concrete = constants.%.659] {
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %CallbackType.ref: type = name_ref CallbackType, constants.%.659 [concrete = constants.%.659]
// CHECK:STDOUT: }
// CHECK:STDOUT: %fn_ptr: ref %.659 = wrapper_binding fn_ptr, %fn_ptr.var
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %fn_ptr.patt: %pattern_type.4b7 = ref_binding_pattern fn_ptr [concrete = constants.%fn_ptr.patt]
// CHECK:STDOUT: %fn_ptr.var_patt: %pattern_type.4b7 = var_pattern %fn_ptr.patt [concrete = constants.%fn_ptr.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %fn_ptr.var, constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%fn_ptr.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13(%self.param: ref %.659) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_pass_complex_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %MyCallback.type: type = fn_type @MyCallback [concrete]
// CHECK:STDOUT: %MyCallback: %MyCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.type: type = cpp_overload_set_type @TakeComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.value: %TakeComplexCallback.cpp_overload_set.type = cpp_overload_set_value @TakeComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: .TakeComplexCallback = %TakeComplexCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: %TakeComplexCallback.cpp_overload_set.value: %TakeComplexCallback.cpp_overload_set.type = cpp_overload_set_value @TakeComplexCallback.cpp_overload_set [concrete = constants.%TakeComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %TakeComplexCallback.ref: %TakeComplexCallback.cpp_overload_set.type = name_ref TakeComplexCallback, imports.%TakeComplexCallback.cpp_overload_set.value [concrete = constants.%TakeComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %MyCallback.ref: %MyCallback.type = name_ref MyCallback, file.%MyCallback.decl [concrete = constants.%MyCallback]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- call_complex_fn_ptr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %pattern_type.bbc: type = pattern_type %C [concrete]
// CHECK:STDOUT: %x.patt: %pattern_type.bbc = ref_binding_pattern x [concrete]
// CHECK:STDOUT: %x.var_patt: %pattern_type.bbc = var_pattern %x.patt [concrete]
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.type: type = cpp_overload_set_type @GetComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.value: %GetComplexCallback.cpp_overload_set.type = cpp_overload_set_value @GetComplexCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type59000000 [concrete]
// CHECK:STDOUT: %GetComplexCallback.type: type = fn_type @GetComplexCallback [concrete]
// CHECK:STDOUT: %GetComplexCallback: %GetComplexCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %C.C.cpp_overload_set.type: type = cpp_overload_set_type @C.C.cpp_overload_set [concrete]
// CHECK:STDOUT: %C.C.cpp_overload_set.value: %C.C.cpp_overload_set.type = cpp_overload_set_value @C.C.cpp_overload_set [concrete]
// CHECK:STDOUT: %ptr.0a2: 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: %__invoke__carbon_thunk.type: type = fn_type @__invoke__carbon_thunk [concrete]
// CHECK:STDOUT: %__invoke__carbon_thunk: %__invoke__carbon_thunk.type = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.e2f: %pattern_type.bbc = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.790: %pattern_type.bbc = wrapper_binding_pattern self, %self.param_patt.e2f [concrete]
// CHECK:STDOUT: %C.cpp_destructor.type: type = fn_type @C.cpp_destructor [concrete]
// CHECK:STDOUT: %C.cpp_destructor: %C.cpp_destructor.type = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: .GetComplexCallback = %GetComplexCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: %GetComplexCallback.cpp_overload_set.value: %GetComplexCallback.cpp_overload_set.type = cpp_overload_set_value @GetComplexCallback.cpp_overload_set [concrete = constants.%GetComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetComplexCallback.decl: %GetComplexCallback.type = fn_decl @GetComplexCallback [concrete = constants.%GetComplexCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.C.cpp_overload_set.value: %C.C.cpp_overload_set.type = cpp_overload_set_value @C.C.cpp_overload_set [concrete = constants.%C.C.cpp_overload_set.value]
// 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: %__invoke__carbon_thunk.decl: %__invoke__carbon_thunk.type = fn_decl @__invoke__carbon_thunk [concrete = constants.%__invoke__carbon_thunk] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.cpp_destructor.decl: %C.cpp_destructor.type = fn_decl @C.cpp_destructor [concrete = constants.%C.cpp_destructor] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc7 [concrete = constants.%Destroy.WithSelf.Op.403] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.bbc = ref_param_pattern [concrete = constants.%self.param_patt.e2f]
// CHECK:STDOUT: %self.patt: %pattern_type.bbc = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.790]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %C = ref_param call_param0
// CHECK:STDOUT: %self: ref %C = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %x.var: ref %C = var_storage %x.var_patt
// CHECK:STDOUT: %Cpp.ref.loc7_25: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %GetComplexCallback.ref: %GetComplexCallback.cpp_overload_set.type = name_ref GetComplexCallback, imports.%GetComplexCallback.cpp_overload_set.value [concrete = constants.%GetComplexCallback.cpp_overload_set.value]
// CHECK:STDOUT: %GetComplexCallback.call: init %.659 = call imports.%GetComplexCallback.decl()
// CHECK:STDOUT: %Cpp.ref.loc7_50: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %C.ref.loc7_53: type = name_ref C, imports.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %C.ref.loc7_55: %C.C.cpp_overload_set.type = name_ref C, imports.%C.C.cpp_overload_set.value [concrete = constants.%C.C.cpp_overload_set.value]
// CHECK:STDOUT: %.loc7_58.1: ref %C = temporary_storage
// CHECK:STDOUT: %addr.loc7_58: %ptr.0a2 = addr_of %.loc7_58.1
// CHECK:STDOUT: %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%addr.loc7_58)
// CHECK:STDOUT: %.loc7_58.2: init %C to %.loc7_58.1 = mark_in_place_init %C__carbon_thunk.call
// CHECK:STDOUT: %.loc7_3: ref %C = splice_block %x.var {}
// CHECK:STDOUT: %.loc7_48.1: %.659 = value_of_initializer %GetComplexCallback.call
// CHECK:STDOUT: %.loc7_48.2: %.659 = converted %GetComplexCallback.call, %.loc7_48.1
// CHECK:STDOUT: %.loc7_58.3: ref %C = temporary %.loc7_58.1, %.loc7_58.2
// CHECK:STDOUT: %.loc7_58.4: %C = acquire_value %.loc7_58.3
// CHECK:STDOUT: %.loc7_58.5: ref %C = value_as_ref %.loc7_58.4
// CHECK:STDOUT: %addr.loc7_59.1: %ptr.0a2 = addr_of %.loc7_58.5
// CHECK:STDOUT: %addr.loc7_59.2: %ptr.0a2 = addr_of %.loc7_3
// CHECK:STDOUT: %__invoke__carbon_thunk.call: init %empty_tuple.type = call imports.%__invoke__carbon_thunk.decl(%.loc7_48.2, %addr.loc7_59.1, %addr.loc7_59.2)
// CHECK:STDOUT: %.loc7_59: init %C to %.loc7_3 = mark_in_place_init %__invoke__carbon_thunk.call
// CHECK:STDOUT: assign %x.var, %.loc7_59
// CHECK:STDOUT: %.loc7_20: type = splice_block %C.ref.loc7_20 [concrete = constants.%C] {
// CHECK:STDOUT: %Cpp.ref.loc7_17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %C.ref.loc7_20: type = name_ref C, imports.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: }
// CHECK:STDOUT: %x: ref %C = wrapper_binding x, %x.var
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %x.patt: %pattern_type.bbc = ref_binding_pattern x [concrete = constants.%x.patt]
// CHECK:STDOUT: %x.var_patt: %pattern_type.bbc = var_pattern %x.patt [concrete = constants.%x.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc7_58: <bound method> = bound_method %.loc7_58.3, constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc7_58: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc7_58(%.loc7_58.3)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc7_3: <bound method> = bound_method %x.var, constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc7_3: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc7_3(%x.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7(%self.param: ref %C) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7(%self.param: ref %C) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
// CHECK:STDOUT: %C.cpp_destructor.call: init %empty_tuple.type = call imports.%C.cpp_destructor.decl(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- mention_forward_decl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %.659: type = cpp_fn_ptr_type clang_function_pointer_type79000000 [concrete]
// CHECK:STDOUT: %pattern_type.4b7: type = pattern_type %.659 [concrete]
// CHECK:STDOUT: %callback.patt: %pattern_type.4b7 = ref_binding_pattern callback [concrete]
// CHECK:STDOUT: %callback.var_patt: %pattern_type.4b7 = var_pattern %callback.patt [concrete]
// CHECK:STDOUT: %ReturnCallback.cpp_overload_set.type: type = cpp_overload_set_type @ReturnCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %ReturnCallback.cpp_overload_set.value: %ReturnCallback.cpp_overload_set.type = cpp_overload_set_value @ReturnCallback.cpp_overload_set [concrete]
// CHECK:STDOUT: %ReturnCallback.type: type = fn_type @ReturnCallback [concrete]
// CHECK:STDOUT: %ReturnCallback: %ReturnCallback.type = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.952: %pattern_type.4b7 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.a35: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt.952 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .CallbackType = constants.%.659
// CHECK:STDOUT: .ReturnCallback = %ReturnCallback.cpp_overload_set.value
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %ReturnCallback.cpp_overload_set.value: %ReturnCallback.cpp_overload_set.type = cpp_overload_set_value @ReturnCallback.cpp_overload_set [concrete = constants.%ReturnCallback.cpp_overload_set.value]
// CHECK:STDOUT: %ReturnCallback.decl: %ReturnCallback.type = fn_decl @ReturnCallback [concrete = constants.%ReturnCallback] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc7 [concrete = constants.%Destroy.WithSelf.Op.403] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.4b7 = ref_param_pattern [concrete = constants.%self.param_patt.952]
// CHECK:STDOUT: %self.patt: %pattern_type.4b7 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a35]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %.659 = ref_param call_param0
// CHECK:STDOUT: %self: ref %.659 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %callback.var: ref %.659 = var_storage %callback.var_patt
// CHECK:STDOUT: %Cpp.ref.loc7_43: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %ReturnCallback.ref: %ReturnCallback.cpp_overload_set.type = name_ref ReturnCallback, imports.%ReturnCallback.cpp_overload_set.value [concrete = constants.%ReturnCallback.cpp_overload_set.value]
// CHECK:STDOUT: %ReturnCallback.call: init %.659 = call imports.%ReturnCallback.decl()
// CHECK:STDOUT: assign %callback.var, %ReturnCallback.call
// CHECK:STDOUT: %.loc7: type = splice_block %CallbackType.ref [concrete = constants.%.659] {
// CHECK:STDOUT: %Cpp.ref.loc7_24: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %CallbackType.ref: type = name_ref CallbackType, constants.%.659 [concrete = constants.%.659]
// CHECK:STDOUT: }
// CHECK:STDOUT: %callback: ref %.659 = wrapper_binding callback, %callback.var
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %callback.patt: %pattern_type.4b7 = ref_binding_pattern callback [concrete = constants.%callback.patt]
// CHECK:STDOUT: %callback.var_patt: %pattern_type.4b7 = var_pattern %callback.patt [concrete = constants.%callback.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %callback.var, constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%callback.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc7(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7(%self.param: ref %.659) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7(%self.param: ref %.659) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
+6
View File
@@ -178,6 +178,12 @@ auto GetClassType(Context& context, SemIR::ClassId class_id,
return GetTypeImpl<SemIR::ClassType>(context, class_id, specific_id);
}
auto GetCppFunctionPointerType(Context& context,
SemIR::ClangFunctionPointerTypeId clang_type_id)
-> SemIR::TypeId {
return GetTypeImpl<SemIR::CppFunctionPointerType>(context, clang_type_id);
}
auto GetCppOverloadSetType(Context& context,
SemIR::CppOverloadSetId overload_set_id,
SemIR::SpecificId specific_id) -> SemIR::TypeId {
+5
View File
@@ -47,6 +47,11 @@ auto GetQualifiedType(Context& context, SemIR::TypeId type_id,
auto GetClassType(Context& context, SemIR::ClassId class_id,
SemIR::SpecificId specific_id) -> SemIR::TypeId;
// Gets a C++ function pointer type. The returned type will be complete.
auto GetCppFunctionPointerType(Context& context,
SemIR::ClangFunctionPointerTypeId clang_type_id)
-> SemIR::TypeId;
// Gets a C++ overload set type. The returned type will be complete.
auto GetCppOverloadSetType(Context& context,
SemIR::CppOverloadSetId overload_set_id,
+10
View File
@@ -270,6 +270,9 @@ class TypeCompleter {
-> SemIR::CompleteTypeInfo;
auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::PointerType inst) const
-> SemIR::CompleteTypeInfo;
auto BuildInfoForInst(SemIR::TypeId type_id,
SemIR::CppFunctionPointerType inst) const
-> SemIR::CompleteTypeInfo;
auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::IntType inst) const
-> SemIR::CompleteTypeInfo;
auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::FloatType inst) const
@@ -715,6 +718,13 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id,
.object_layout = context_->sem_ir().GetPointerLayout()};
}
auto TypeCompleter::BuildInfoForInst(
SemIR::TypeId type_id, SemIR::CppFunctionPointerType /*inst*/) const
-> SemIR::CompleteTypeInfo {
return {.value_repr = MakeCopyValueRepr(type_id),
.object_layout = context_->sem_ir().GetPointerLayout()};
}
auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id,
SemIR::IntType inst) const
-> SemIR::CompleteTypeInfo {
+1
View File
@@ -225,6 +225,7 @@ CARBON_DIAGNOSTIC_KIND(InCppMacroExpansion)
CARBON_DIAGNOSTIC_KIND(ResolvingSpecificHere)
CARBON_DIAGNOSTIC_KIND(InCallToEvalFn)
CARBON_DIAGNOSTIC_KIND(InCppThunk)
CARBON_DIAGNOSTIC_KIND(InCppFunctionPointerThunk)
// Package/import checking diagnostics.
CARBON_DIAGNOSTIC_KIND(CppInteropFuzzing)
+11 -4
View File
@@ -125,9 +125,11 @@ auto FileContext::LowerDefinitions() -> void {
// Lower function definitions.
for (auto [id, fn_info] : sem_ir_->functions().enumerate()) {
// If we created a declaration and the function definition is not imported,
// build a definition.
// If we created a declaration and the function definition is needed but not
// imported, build a definition.
if (functions_.Get(id) && fn_info.definition_id.has_value() &&
fn_info.special_function_kind !=
SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk &&
!sem_ir().insts().GetImportSource(fn_info.definition_id).has_value()) {
BuildFunctionDefinition(id);
}
@@ -445,8 +447,13 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
{fallback_file, fallback_function_id, fallback_specific_id}};
auto function_type_info =
BuildFunctionTypeInfo(llvm::ArrayRef(func_infos, fallback_file ? 2 : 1));
auto* llvm_function =
GetOrCreateLLVMFunction(function_type_info, function_id, specific_id);
llvm::Function* llvm_function = nullptr;
// If we're calling a function pointer, we don't need an LLVM function.
if (function.special_function_kind !=
SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk) {
llvm_function =
GetOrCreateLLVMFunction(function_type_info, function_id, specific_id);
}
return {{.type = function_type_info.type,
.di_type = function_type_info.di_type,
+14 -2
View File
@@ -747,7 +747,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
// order.
std::vector<llvm::Value*> args;
bool args_ok = true;
auto* callee_type = function_info->llvm_function->getFunctionType();
// TODO: We should be able to use function_info->type unconditionally, but
// that seems to sometimes have the wrong value for C++ thunks.
auto* callee_type = function_info->llvm_function != nullptr
? function_info->llvm_function->getFunctionType()
: function_info->type;
for (auto index : function_info->lowered_param_indices) {
args.push_back(context.GetValue(arg_ids[index.index]));
@@ -776,7 +780,15 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
return out.TakeStr();
};
CARBON_CHECK(args_ok, "Argument mismatch: {0}", describe_call());
call = context.builder().CreateCall(llvm_callee, args);
if (function.special_function_kind ==
SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk) {
llvm::ArrayRef<llvm::Value*> args_ref = args;
llvm::Value* callee_ptr = args_ref.consume_front();
call =
context.builder().CreateCall(function_info->type, callee_ptr, args);
} else {
call = context.builder().CreateCall(llvm_callee, args);
}
} else {
call = HandleVirtualCall(context, args, function, *function_info);
}
@@ -0,0 +1,457 @@
// 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-FILE: toolchain/testing/testdata/min_prelude/full.carbon
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/function/import/function_ptr.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/function/import/function_ptr.carbon
// --- simple_callback.h
using CallbackType = int (*)(float);
void TakeCallback(CallbackType callback);
auto GetCallback() -> CallbackType;
// --- relay_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn F() {
Cpp.TakeCallback(Cpp.GetCallback());
}
// --- call_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "simple_callback.h";
fn F() {
var unused x: i32 = Cpp.GetCallback()(1.0);
}
// --- complex_callback.h
class C {
public:
C();
C(const C&);
C& operator=(const C&);
private:
int x;
};
using CallbackType = C (*)(C);
void TakeComplexCallback(CallbackType callback);
auto GetComplexCallback() -> CallbackType;
// --- relay_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn F() {
Cpp.TakeComplexCallback(Cpp.GetComplexCallback());
}
// --- call_complex_fn_ptr.carbon
library "[[@TEST_NAME]]";
import Cpp library "complex_callback.h";
fn F() {
var unused x: Cpp.C = Cpp.GetComplexCallback()(Cpp.C.C());
}
// --- forward_decl.h
class C;
using CallbackType = C (*)();
auto ReturnCallback() -> CallbackType;
// --- mention_forward_decl.carbon
library "[[@TEST_NAME]]";
import Cpp library "forward_decl.h";
fn F() {
var unused callback: Cpp.CallbackType = Cpp.ReturnCallback();
}
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'relay_fn_ptr.carbon'
// CHECK:STDOUT: source_filename = "relay_fn_ptr.carbon"
// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !13 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %GetCallback.call = call ptr @_Z11GetCallbackv(), !dbg !14
// CHECK:STDOUT: call void @_Z12TakeCallbackPFifE(ptr %GetCallback.call), !dbg !15
// CHECK:STDOUT: ret void, !dbg !16
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare noundef ptr @_Z11GetCallbackv() #1
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_Z12TakeCallbackPFifE(ptr noundef) #1
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.dbg.cu = !{!5}
// CHECK:STDOUT: !llvm.module.flags = !{!17, !18, !19, !2, !3}
// CHECK:STDOUT: !llvm.errno.tbaa = !{!24}
// CHECK:STDOUT:
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !4 = !DIFile(filename: "relay_fn_ptr.carbon", directory: "")
// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !4, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !4, line: 5, type: !7, spFlags: DISPFlagDefinition, unit: !5)
// CHECK:STDOUT: !14 = !DILocation(line: 6, column: 20, scope: !13)
// CHECK:STDOUT: !15 = !DILocation(line: 6, column: 3, scope: !13)
// CHECK:STDOUT: !16 = !DILocation(line: 5, column: 1, scope: !13)
// CHECK:STDOUT: !17 = !{i32 8, !"PIC Level", i32 2}
// CHECK:STDOUT: !18 = !{i32 7, !"PIE Level", i32 2}
// CHECK:STDOUT: !19 = !{i32 7, !"uwtable", i32 2}
// CHECK:STDOUT: !20 = !{!"Simple C++ TBAA"}
// CHECK:STDOUT: !21 = !{!"omnipotent char", !20, i64 0}
// CHECK:STDOUT: !22 = !{!"int", !21, i64 0}
// CHECK:STDOUT: !23 = !{!"__libc_errno", !22, i64 0}
// CHECK:STDOUT: !24 = !{!23, !22, i64 0}
// CHECK:STDOUT:
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'call_fn_ptr.carbon'
// CHECK:STDOUT: source_filename = "call_fn_ptr.carbon"
// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
// CHECK:STDOUT:
// CHECK:STDOUT: @float.943 = internal constant float 1.000000e+00
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !32 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %x.var = alloca i32, align 4, !dbg !33
// CHECK:STDOUT: %.loc6_41.3.temp = alloca float, align 4, !dbg !35
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %x.var), !dbg !33
// CHECK:STDOUT: %GetCallback.call = call ptr @_Z11GetCallbackv(), !dbg !34
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc6_41.3.temp), !dbg !35
// CHECK:STDOUT: %__invoke__carbon_thunk.call = call i32 @.carbon_thunk.__(ptr %GetCallback.call, ptr @float.943), !dbg !34
// CHECK:STDOUT: store i32 %__invoke__carbon_thunk.call, ptr %x.var, align 4, !dbg !33
// CHECK:STDOUT: call void @"_CSelfDestruct.c73f6eecd880c71a:core.Destroy.Core"(ptr @float.943), !dbg !35
// CHECK:STDOUT: call void @"_CSelfDestruct.f58524831c37807e:core.Destroy.Core"(ptr %x.var), !dbg !33
// CHECK:STDOUT: ret void, !dbg !37
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare noundef ptr @_Z11GetCallbackv() #1
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable
// CHECK:STDOUT: define internal noundef i32 @.carbon_thunk.__(ptr noundef %this, ptr noundef %0) #2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: %.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !47
// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8, !tbaa !50
// CHECK:STDOUT: %1 = load ptr, ptr %this.addr, align 8, !tbaa !47
// CHECK:STDOUT: %2 = load ptr, ptr %.addr, align 8, !tbaa !50
// CHECK:STDOUT: %3 = load float, ptr %2, align 4, !tbaa !51
// CHECK:STDOUT: %call = call noundef i32 %1(float noundef %3)
// CHECK:STDOUT: ret i32 %call
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.c73f6eecd880c71a:core.Destroy.Core"(ptr %self) #0 !dbg !38 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !40
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.f58524831c37807e:core.Destroy.Core"(ptr %self) #0 !dbg !42 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !44
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #3
// CHECK:STDOUT:
// CHECK:STDOUT: ; uselistorder directives
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT: attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.dbg.cu = !{!26}
// CHECK:STDOUT: !llvm.module.flags = !{!17, !18, !19, !2, !3}
// CHECK:STDOUT: !llvm.errno.tbaa = !{!24}
// CHECK:STDOUT:
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !11 = !{null, !8}
// CHECK:STDOUT: !12 = !DISubroutineType(types: !11)
// CHECK:STDOUT: !17 = !{i32 8, !"PIC Level", i32 2}
// CHECK:STDOUT: !18 = !{i32 7, !"PIE Level", i32 2}
// CHECK:STDOUT: !19 = !{i32 7, !"uwtable", i32 2}
// CHECK:STDOUT: !20 = !{!"Simple C++ TBAA"}
// CHECK:STDOUT: !21 = !{!"omnipotent char", !20, i64 0}
// CHECK:STDOUT: !22 = !{!"int", !21, i64 0}
// CHECK:STDOUT: !23 = !{!"__libc_errno", !22, i64 0}
// CHECK:STDOUT: !24 = !{!23, !22, i64 0}
// CHECK:STDOUT: !25 = !DIFile(filename: "call_fn_ptr.carbon", directory: "")
// CHECK:STDOUT: !26 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !25, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !27 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
// CHECK:STDOUT: !30 = !{null, !27}
// CHECK:STDOUT: !31 = !DISubroutineType(types: !30)
// CHECK:STDOUT: !32 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !25, line: 5, type: !7, spFlags: DISPFlagDefinition, unit: !26)
// CHECK:STDOUT: !33 = !DILocation(line: 6, column: 3, scope: !32)
// CHECK:STDOUT: !34 = !DILocation(line: 6, column: 23, scope: !32)
// CHECK:STDOUT: !35 = !DILocation(line: 6, column: 41, scope: !32)
// CHECK:STDOUT: !37 = !DILocation(line: 5, column: 1, scope: !32)
// CHECK:STDOUT: !38 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.c73f6eecd880c71a:core.Destroy.Core", scope: null, file: !25, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !26, retainedNodes: !41)
// CHECK:STDOUT: !39 = !DILocalVariable(arg: 1, scope: !38, type: !8)
// CHECK:STDOUT: !40 = !DILocation(line: 6, column: 41, scope: !38)
// CHECK:STDOUT: !41 = !{!39}
// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.f58524831c37807e:core.Destroy.Core", scope: null, file: !25, line: 6, type: !31, spFlags: DISPFlagDefinition, unit: !26, retainedNodes: !45)
// CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !42, type: !27)
// CHECK:STDOUT: !44 = !DILocation(line: 6, column: 3, scope: !42)
// CHECK:STDOUT: !45 = !{!43}
// CHECK:STDOUT: !46 = !{!"any pointer", !21, i64 0}
// CHECK:STDOUT: !47 = !{!46, !46, i64 0}
// CHECK:STDOUT: !48 = !{!"float", !21, i64 0}
// CHECK:STDOUT: !49 = !{!"p1 float", !46, i64 0}
// CHECK:STDOUT: !50 = !{!49, !49, i64 0}
// CHECK:STDOUT: !51 = !{!48, !48, i64 0}
// CHECK:STDOUT:
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'relay_complex_fn_ptr.carbon'
// CHECK:STDOUT: source_filename = "relay_complex_fn_ptr.carbon"
// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !54 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %GetComplexCallback.call = call ptr @_Z18GetComplexCallbackv(), !dbg !55
// CHECK:STDOUT: call void @_Z19TakeComplexCallbackPF1CS_E(ptr %GetComplexCallback.call), !dbg !56
// CHECK:STDOUT: ret void, !dbg !57
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare noundef ptr @_Z18GetComplexCallbackv() #1
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_Z19TakeComplexCallbackPF1CS_E(ptr noundef) #1
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.dbg.cu = !{!53}
// CHECK:STDOUT: !llvm.module.flags = !{!17, !18, !19, !2, !3}
// CHECK:STDOUT: !llvm.errno.tbaa = !{!24}
// CHECK:STDOUT:
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !17 = !{i32 8, !"PIC Level", i32 2}
// CHECK:STDOUT: !18 = !{i32 7, !"PIE Level", i32 2}
// CHECK:STDOUT: !19 = !{i32 7, !"uwtable", i32 2}
// CHECK:STDOUT: !20 = !{!"Simple C++ TBAA"}
// CHECK:STDOUT: !21 = !{!"omnipotent char", !20, i64 0}
// CHECK:STDOUT: !22 = !{!"int", !21, i64 0}
// CHECK:STDOUT: !23 = !{!"__libc_errno", !22, i64 0}
// CHECK:STDOUT: !24 = !{!23, !22, i64 0}
// CHECK:STDOUT: !52 = !DIFile(filename: "relay_complex_fn_ptr.carbon", directory: "")
// CHECK:STDOUT: !53 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !52, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !54 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !52, line: 5, type: !7, spFlags: DISPFlagDefinition, unit: !53)
// CHECK:STDOUT: !55 = !DILocation(line: 6, column: 27, scope: !54)
// CHECK:STDOUT: !56 = !DILocation(line: 6, column: 3, scope: !54)
// CHECK:STDOUT: !57 = !DILocation(line: 5, column: 1, scope: !54)
// CHECK:STDOUT:
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'call_complex_fn_ptr.carbon'
// CHECK:STDOUT: source_filename = "call_complex_fn_ptr.carbon"
// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
// CHECK:STDOUT:
// CHECK:STDOUT: %class.C = type { i32 }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !62 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %x.var = alloca [4 x i8], align 4, !dbg !63
// CHECK:STDOUT: %.loc6_58.1.temp = alloca [4 x i8], align 4, !dbg !65
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %x.var), !dbg !63
// CHECK:STDOUT: %GetComplexCallback.call = call ptr @_Z18GetComplexCallbackv(), !dbg !64
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc6_58.1.temp), !dbg !65
// CHECK:STDOUT: call void @_ZN1CC1Ev.carbon_thunk.(ptr %.loc6_58.1.temp), !dbg !65
// CHECK:STDOUT: call void @.carbon_thunk.__(ptr %GetComplexCallback.call, ptr %.loc6_58.1.temp, ptr %x.var), !dbg !64
// CHECK:STDOUT: call void @"_CSelfDestruct.417eccf5eb8addbd:core.Destroy.Core"(ptr %.loc6_58.1.temp), !dbg !65
// CHECK:STDOUT: call void @"_CSelfDestruct.417eccf5eb8addbd:core.Destroy.Core"(ptr %x.var), !dbg !63
// CHECK:STDOUT: ret void, !dbg !67
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare noundef ptr @_Z18GetComplexCallbackv() #1
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable
// CHECK:STDOUT: define internal void @_ZN1CC1Ev.carbon_thunk.(ptr noundef %return) #2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %return.addr = alloca ptr, align 8
// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !73
// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8, !tbaa !73
// CHECK:STDOUT: call void @_ZN1CC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %0)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress uwtable
// CHECK:STDOUT: define internal void @.carbon_thunk.__(ptr noundef %this, ptr noundef %0, ptr noundef %return) #2 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %this.addr = alloca ptr, align 8
// CHECK:STDOUT: %.addr = alloca ptr, align 8
// CHECK:STDOUT: %return.addr = alloca ptr, align 8
// CHECK:STDOUT: %agg.tmp = alloca %class.C, align 4
// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !47
// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8, !tbaa !73
// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8, !tbaa !73
// CHECK:STDOUT: %1 = load ptr, ptr %return.addr, align 8, !tbaa !73
// CHECK:STDOUT: %2 = load ptr, ptr %this.addr, align 8, !tbaa !47
// CHECK:STDOUT: %3 = load ptr, ptr %.addr, align 8, !tbaa !73
// CHECK:STDOUT: call void @_ZN1CC1ERKS_(ptr noundef nonnull align 4 dereferenceable(4) %agg.tmp, ptr noundef nonnull align 4 dereferenceable(4) %3)
// CHECK:STDOUT: call void %2(ptr dead_on_unwind writable sret(%class.C) align 4 %1, ptr nofreeobj noundef align 4 dead_on_return dereferenceable(4) %agg.tmp)
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.417eccf5eb8addbd:core.Destroy.Core"(ptr %self) #0 !dbg !68 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !70
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #3
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_ZN1CC1Ev(ptr noundef nonnull align 4 dereferenceable(4)) unnamed_addr #1
// CHECK:STDOUT:
// CHECK:STDOUT: declare void @_ZN1CC1ERKS_(ptr noundef nonnull align 4 dereferenceable(4), ptr noundef nonnull align 4 dereferenceable(4)) unnamed_addr #1
// CHECK:STDOUT:
// CHECK:STDOUT: ; uselistorder directives
// CHECK:STDOUT: uselistorder ptr @"_CSelfDestruct.417eccf5eb8addbd:core.Destroy.Core", { 1, 0 }
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT: attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.dbg.cu = !{!59}
// CHECK:STDOUT: !llvm.module.flags = !{!17, !18, !19, !2, !3}
// CHECK:STDOUT: !llvm.errno.tbaa = !{!24}
// CHECK:STDOUT:
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !11 = !{null, !8}
// CHECK:STDOUT: !12 = !DISubroutineType(types: !11)
// CHECK:STDOUT: !17 = !{i32 8, !"PIC Level", i32 2}
// CHECK:STDOUT: !18 = !{i32 7, !"PIE Level", i32 2}
// CHECK:STDOUT: !19 = !{i32 7, !"uwtable", i32 2}
// CHECK:STDOUT: !20 = !{!"Simple C++ TBAA"}
// CHECK:STDOUT: !21 = !{!"omnipotent char", !20, i64 0}
// CHECK:STDOUT: !22 = !{!"int", !21, i64 0}
// CHECK:STDOUT: !23 = !{!"__libc_errno", !22, i64 0}
// CHECK:STDOUT: !24 = !{!23, !22, i64 0}
// CHECK:STDOUT: !46 = !{!"any pointer", !21, i64 0}
// CHECK:STDOUT: !47 = !{!46, !46, i64 0}
// CHECK:STDOUT: !58 = !DIFile(filename: "call_complex_fn_ptr.carbon", directory: "")
// CHECK:STDOUT: !59 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !58, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !62 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !58, line: 5, type: !7, spFlags: DISPFlagDefinition, unit: !59)
// CHECK:STDOUT: !63 = !DILocation(line: 6, column: 3, scope: !62)
// CHECK:STDOUT: !64 = !DILocation(line: 6, column: 25, scope: !62)
// CHECK:STDOUT: !65 = !DILocation(line: 6, column: 50, scope: !62)
// CHECK:STDOUT: !67 = !DILocation(line: 5, column: 1, scope: !62)
// CHECK:STDOUT: !68 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.417eccf5eb8addbd:core.Destroy.Core", scope: null, file: !58, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !59, retainedNodes: !71)
// CHECK:STDOUT: !69 = !DILocalVariable(arg: 1, scope: !68, type: !8)
// CHECK:STDOUT: !70 = !DILocation(line: 6, column: 50, scope: !68)
// CHECK:STDOUT: !71 = !{!69}
// CHECK:STDOUT: !72 = !{!"p1 _ZTS1C", !46, i64 0}
// CHECK:STDOUT: !73 = !{!72, !72, i64 0}
// CHECK:STDOUT:
// CHECK:STDOUT: ; ---
// CHECK:STDOUT: ; ModuleID = 'mention_forward_decl.carbon'
// CHECK:STDOUT: source_filename = "mention_forward_decl.carbon"
// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !77 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: %callback.var = alloca ptr, align 8, !dbg !78
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %callback.var), !dbg !78
// CHECK:STDOUT: %ReturnCallback.call = call ptr @_Z14ReturnCallbackv(), !dbg !79
// CHECK:STDOUT: store ptr %ReturnCallback.call, ptr %callback.var, align 8, !dbg !78
// CHECK:STDOUT: call void @"_CSelfDestruct.659d177172b60c17:core.Destroy.Core"(ptr %callback.var), !dbg !78
// CHECK:STDOUT: ret void, !dbg !81
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: declare noundef ptr @_Z14ReturnCallbackv() #1
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nounwind
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.659d177172b60c17:core.Destroy.Core"(ptr %self) #0 !dbg !82 {
// CHECK:STDOUT: entry:
// CHECK:STDOUT: ret void, !dbg !84
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #2
// CHECK:STDOUT:
// CHECK:STDOUT: attributes #0 = { nounwind }
// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
// CHECK:STDOUT: attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
// CHECK:STDOUT:
// CHECK:STDOUT: !llvm.dbg.cu = !{!76}
// CHECK:STDOUT: !llvm.module.flags = !{!17, !18, !19, !2, !3}
// CHECK:STDOUT: !llvm.errno.tbaa = !{!24}
// CHECK:STDOUT:
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
// CHECK:STDOUT: !6 = !{null}
// CHECK:STDOUT: !7 = !DISubroutineType(types: !6)
// CHECK:STDOUT: !8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
// CHECK:STDOUT: !11 = !{null, !8}
// CHECK:STDOUT: !12 = !DISubroutineType(types: !11)
// CHECK:STDOUT: !17 = !{i32 8, !"PIC Level", i32 2}
// CHECK:STDOUT: !18 = !{i32 7, !"PIE Level", i32 2}
// CHECK:STDOUT: !19 = !{i32 7, !"uwtable", i32 2}
// CHECK:STDOUT: !20 = !{!"Simple C++ TBAA"}
// CHECK:STDOUT: !21 = !{!"omnipotent char", !20, i64 0}
// CHECK:STDOUT: !22 = !{!"int", !21, i64 0}
// CHECK:STDOUT: !23 = !{!"__libc_errno", !22, i64 0}
// CHECK:STDOUT: !24 = !{!23, !22, i64 0}
// CHECK:STDOUT: !75 = !DIFile(filename: "mention_forward_decl.carbon", directory: "")
// CHECK:STDOUT: !76 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !75, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
// CHECK:STDOUT: !77 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !75, line: 5, type: !7, spFlags: DISPFlagDefinition, unit: !76)
// CHECK:STDOUT: !78 = !DILocation(line: 6, column: 3, scope: !77)
// CHECK:STDOUT: !79 = !DILocation(line: 6, column: 43, scope: !77)
// CHECK:STDOUT: !81 = !DILocation(line: 5, column: 1, scope: !77)
// CHECK:STDOUT: !82 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.659d177172b60c17:core.Destroy.Core", scope: null, file: !75, line: 6, type: !12, spFlags: DISPFlagDefinition, unit: !76, retainedNodes: !85)
// CHECK:STDOUT: !83 = !DILocalVariable(arg: 1, scope: !82, type: !8)
// CHECK:STDOUT: !84 = !DILocation(line: 6, column: 3, scope: !82)
// CHECK:STDOUT: !85 = !{!83}
// CHECK:STDOUT:
+7
View File
@@ -651,6 +651,13 @@ static auto BuildTypeForInst(FileContext& /*context*/,
return {nullptr, nullptr};
}
static auto BuildTypeForInst(FileContext& context,
SemIR::CppFunctionPointerType /*inst*/)
-> LoweredTypes {
return {llvm::PointerType::get(context.llvm_context(), /*AddressSpace=*/0),
nullptr};
}
static auto BuildTypeForInst(FileContext& context, SemIR::FloatType inst)
-> LoweredTypes {
return {llvm::Type::getFloatingPointTy(context.llvm_context(),
+1
View File
@@ -229,6 +229,7 @@ cc_library(
"//common:concepts",
"//common:raw_string_ostream",
"//toolchain/base:kind_switch",
"@llvm-project//clang:ast",
"@llvm-project//llvm:Support",
],
)
+35 -5
View File
@@ -7,6 +7,7 @@
#include <concepts>
#include "clang/AST/TypeBase.h"
#include "common/hashtable_key_context.h"
#include "common/ostream.h"
#include "common/set.h"
@@ -21,7 +22,7 @@ class FunctionDecl;
namespace Carbon::SemIR {
// Information about how to form the Carbon function signature from the Clang
// function declaration.
// function signature.
struct ClangDeclSignature : public Printable<ClangDeclSignature> {
// A passing mode for a parameter in a C++ function signature.
enum class PassingMode : int8_t {
@@ -160,6 +161,11 @@ struct ClangDeclKey : public Printable<ClangDeclKey> {
UncheckedTag /*_*/);
};
// A ClangDeclSignature mapped to an ID.
using ClangDeclSignatureStore =
CanonicalValueStore<ClangDeclSignatureId, ClangDeclSignature,
Tag<CheckIRId>, ClangDeclSignature>;
// A Clang declaration mapped to a Carbon instruction.
//
// Instances of this type are managed by a `ClangDeclStore`, which ensures that
@@ -230,10 +236,34 @@ class ClangDeclStore {
Set<ClangDeclId, 0, KeyContext> reverse_lookup_;
};
// A ClangDeclSignature mapped to an ID.
using ClangDeclSignatureStore =
CanonicalValueStore<ClangDeclSignatureId, ClangDeclSignature,
Tag<CheckIRId>, ClangDeclSignature>;
// Information about a Clang function pointer type. We can't use `ClangDecl`
// to represent function pointer callees, because function pointer types
// don't have declarations in C++, so this type is used in its place.
struct ClangFunctionPointerTypeInfo {
auto GetAsKey() const -> const clang::Type* { return clang_type; }
// The function pointer type. This should be a canonical type.
//
// TODO: figure out how to get a canonical type that preserves nullability
// attributes, which canonicalization discards. Note that this applies to both
// the function pointer type and its parameter/return types.
const clang::Type* clang_type;
// The ID of the `FunctionDecl` for the Carbon thunk that invokes
// function pointers of this type, or `None` if the thunk has not yet
// been imported.
SemIR::InstId decl_id;
// The corresponding function ID, or `None` if it has not yet been
// imported. This should always be the same as `decl_id`'s `function_id`
// field, but we cache it here for convenience and efficiency.
SemIR::FunctionId function_id;
};
// Canonical storage for `ClangFunctionPointerTypeInfo`.
using ClangFunctionPointerTypeStore =
CanonicalValueStore<SemIR::ClangFunctionPointerTypeId, const clang::Type*,
Tag<SemIR::CheckIRId>, ClangFunctionPointerTypeInfo>;
} // namespace Carbon::SemIR
+34 -21
View File
@@ -40,6 +40,31 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id,
auto untyped_inst = ir->insts().Get(inst_id);
auto category_from_kind = untyped_inst.kind().expr_category();
auto handle_function_return =
[&](SemIR::FunctionId function_id,
SemIR::SpecificId resolved_specific_id) -> ExprCategory {
const auto& function = ir->functions().Get(function_id);
auto return_form_id =
function.GetDeclaredReturnForm(*ir, resolved_specific_id);
if (!return_form_id.has_value()) {
// Treat as equivalent to `-> ()`.
return ExprCategory::ReprInitializing;
}
auto return_form = ir->insts().Get(return_form_id);
CARBON_KIND_SWITCH(return_form) {
case CARBON_KIND(InitForm _):
return ExprCategory::ReprInitializing;
case CARBON_KIND(RefForm _):
return ExprCategory::DurableRef;
case CARBON_KIND(ValueForm _):
return ExprCategory::Value;
case CARBON_KIND(ErrorInst _):
return ExprCategory::Error;
default:
CARBON_FATAL("Unexpected form inst kind: {0}", return_form);
}
};
// Handle any special cases that use
// ComputedExprCategory::DependsOnOperands.
auto handle_special_case =
@@ -69,27 +94,8 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id,
return ExprCategory::Error;
}
case CARBON_KIND(SemIR::CalleeFunction callee_function): {
const auto& function =
ir->functions().Get(callee_function.function_id);
auto return_form_id = function.GetDeclaredReturnForm(
*ir, callee_function.resolved_specific_id);
if (!return_form_id.has_value()) {
// Treat as equivalent to `-> ()`.
return ExprCategory::ReprInitializing;
}
auto return_form = ir->insts().Get(return_form_id);
CARBON_KIND_SWITCH(return_form) {
case CARBON_KIND(InitForm _):
return ExprCategory::ReprInitializing;
case CARBON_KIND(RefForm _):
return ExprCategory::DurableRef;
case CARBON_KIND(ValueForm _):
return ExprCategory::Value;
case CARBON_KIND(ErrorInst _):
return ExprCategory::Error;
default:
CARBON_FATAL("Unexpected inst kind: {0}", return_form);
}
return handle_function_return(callee_function.function_id,
callee_function.resolved_specific_id);
}
case CARBON_KIND(SemIR::CalleeNonFunction _): {
return ExprCategory::NotExpr;
@@ -98,6 +104,13 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id,
// TODO: support `ref` returns from C++.
return ExprCategory::ReprInitializing;
}
case CARBON_KIND(SemIR::CalleeCppFunctionPointer function_ptr): {
return handle_function_return(
ir->clang_function_pointer_types()
.Get(function_ptr.function_type_id)
.function_id,
SemIR::SpecificId::None);
}
}
} else if constexpr (std::same_as<TypedInstT, SpecificInst>) {
// Switch to looking at the inner instruction and its specific, which is
+6 -1
View File
@@ -14,6 +14,7 @@
#include "common/check.h"
#include "common/error.h"
#include "toolchain/base/block_value_store_impl.h"
#include "toolchain/base/canonical_value_store_impl.h"
#include "toolchain/base/shared_value_stores.h"
#include "toolchain/base/value_store_impl.h"
#include "toolchain/base/yaml.h"
@@ -81,7 +82,8 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id,
custom_layouts_(allocator_, check_ir_id, 1),
expr_regions_(check_ir_id),
clang_source_locs_(check_ir_id),
bundles_(allocator_, check_ir_id) {
bundles_(allocator_, check_ir_id),
clang_function_pointer_types_(check_ir_id) {
// `type`, `form`, and the error type are both complete & concrete types.
// TODO: This duplicates the code in `check/type_completion.cpp`. Consider
// requiring these types to be complete from Check initialization instead,
@@ -279,4 +281,7 @@ template class BlockValueStore<SemIR::CustomLayoutId, SemIR::ObjectSize,
Tag<SemIR::CheckIRId>>;
template class BlockValueStore<SemIR::RawBundleId, SemIR::AnyRawId,
Tag<SemIR::CheckIRId>>;
template class CanonicalValueStore<SemIR::ClangFunctionPointerTypeId,
const clang::Type*, Tag<SemIR::CheckIRId>,
SemIR::ClangFunctionPointerTypeInfo>;
} // namespace Carbon
+18
View File
@@ -49,6 +49,10 @@
#include "toolchain/sem_ir/type_info.h"
#include "toolchain/sem_ir/vtable.h"
namespace clang {
class Type;
} // namespace clang
namespace Carbon::SemIR {
// An expression that may contain control flow, represented as a
@@ -320,6 +324,14 @@ class File : public Printable<File> {
auto bundles() -> BundleStore& { return bundles_; }
auto bundles() const -> const BundleStore& { return bundles_; }
auto clang_function_pointer_types() -> ClangFunctionPointerTypeStore& {
return clang_function_pointer_types_;
}
auto clang_function_pointer_types() const
-> const ClangFunctionPointerTypeStore& {
return clang_function_pointer_types_;
}
auto top_inst_block_id() const -> InstBlockId { return top_inst_block_id_; }
auto set_top_inst_block_id(InstBlockId block_id) -> void {
top_inst_block_id_ = block_id;
@@ -487,6 +499,9 @@ class File : public Printable<File> {
// Storage for instruction argument bundles.
BundleStore bundles_;
// Cache of thunks for C++ function pointer types.
ClangFunctionPointerTypeStore clang_function_pointer_types_;
};
} // namespace Carbon::SemIR
@@ -501,6 +516,9 @@ extern template class ValueStore<SemIR::CustomLayoutId,
Tag<SemIR::CheckIRId>>;
extern template class BlockValueStore<SemIR::CustomLayoutId, SemIR::ObjectSize,
Tag<SemIR::CheckIRId>>;
extern template class CanonicalValueStore<
SemIR::ClangFunctionPointerTypeId, const clang::Type*,
Tag<SemIR::CheckIRId>, SemIR::ClangFunctionPointerTypeInfo>;
} // namespace Carbon
#endif // CARBON_TOOLCHAIN_SEM_IR_FILE_H_
+4
View File
@@ -1720,6 +1720,10 @@ auto Formatter::FormatArg(StringLiteralValueId id) -> void {
auto Formatter::FormatArg(ClangDeclId id) -> void { out() << id; }
auto Formatter::FormatArg(ClangFunctionPointerTypeId id) -> void {
out() << id;
}
auto Formatter::FormatReturnSlotArg(InstId dest_id) -> void {
if (dest_id.has_value()) {
out() << " to ";
+1
View File
@@ -290,6 +290,7 @@ class Formatter {
auto FormatArg(StringLiteralValueId id) -> void;
auto FormatArg(ConstantId id) -> void { FormatConstant(id); }
auto FormatArg(ClangDeclId id) -> void;
auto FormatArg(ClangFunctionPointerTypeId id) -> void;
template <typename BundleT>
auto FormatArg(BundleId<BundleT> bundle_id) -> void {
+6
View File
@@ -64,6 +64,12 @@ auto GetCallee(const File& sem_ir, InstId callee_id,
"Invalid callee id in a specific context");
}
if (auto fn_ptr_type = sem_ir.types().TryGetAs<CppFunctionPointerType>(
sem_ir.insts().Get(callee_id).type_id())) {
return CalleeCppFunctionPointer{.function_type_id =
fn_ptr_type->clang_type_id};
}
auto val_id = sem_ir.constant_values().GetConstantInstId(callee_id);
if (!val_id.has_value()) {
return CalleeNonFunction();
+18 -4
View File
@@ -51,6 +51,9 @@ struct FunctionFields {
// A function that was imported from C++, for which we generated a
// `CppThunk`. `special_function_kind_data` is the `InstId` of that thunk.
HasCppThunk,
// A thunk that calls a C++ function pointer. `special_function_kind_data`
// is unused.
CppFunctionPointerThunk,
};
// Kinds of virtual modifiers that can apply to functions.
@@ -368,13 +371,19 @@ struct Function : public EntityWithParamsBase,
special_function_kind_data = AnyRawId(thunk_id.index);
}
// Sets that this function is a C++ thunk.
// Sets that this function is a thunk for a C++ function.
auto SetCppThunk(InstId decl_id) -> void {
CARBON_CHECK(special_function_kind == SpecialFunctionKind::None);
special_function_kind = SpecialFunctionKind::CppThunk;
special_function_kind_data = AnyRawId(decl_id.index);
}
// Sets that this function is a thunk for a C++ function pointer.
auto SetCppFunctionPointerThunk() -> void {
CARBON_CHECK(special_function_kind == SpecialFunctionKind::None);
special_function_kind = SpecialFunctionKind::CppFunctionPointerThunk;
}
// Sets that this function is a C++ function that should be called using a C++
// thunk.
auto SetHasCppThunk(InstId decl_id) -> void {
@@ -409,17 +418,22 @@ struct CalleeFunction {
SpecificId resolved_specific_id;
// The bound `Self` facet. `None` if not a bound interface member.
InstId self_type_id;
// The bound `self` parameter. `None` if not a method.
// The bound `self` argument. `None` if not a method.
InstId self_id;
};
// Information about a callee that's a C++ function pointer.
struct CalleeCppFunctionPointer {
ClangFunctionPointerTypeId function_type_id;
};
// Information about a callee that may be a generic type, or could be an
// invalid callee.
struct CalleeNonFunction {};
// A variant combining the callee forms.
using Callee = std::variant<CalleeCppOverloadSet, CalleeError, CalleeFunction,
CalleeNonFunction>;
using Callee = std::variant<CalleeCppFunctionPointer, CalleeCppOverloadSet,
CalleeError, CalleeFunction, CalleeNonFunction>;
// Given a callee expression in a function call, attempt to convert the callee
// to a `BoundMethod`, minimally unwrapping it while doing so.
+1
View File
@@ -39,6 +39,7 @@ using IdKind = TypeEnum<
CallParamIndex,
CharId,
ClangDeclId,
ClangFunctionPointerTypeId,
ClassId,
CompileTimeBindIndex,
ConstantId,
+7
View File
@@ -509,6 +509,13 @@ struct ClangDeclSignatureId : public IdBase<ClangDeclSignatureId> {
using IdBase::IdBase;
};
// The ID of a `ClangFunctionPointerTypeInfo`.
struct ClangFunctionPointerTypeId : public IdBase<ClangFunctionPointerTypeId> {
static constexpr llvm::StringLiteral Label = "clang_function_pointer_type";
using IdBase::IdBase;
};
// A boolean value.
struct BoolValue : public IdBase<BoolValue> {
// Not used by `Print`, but for `IdKind`.
+5
View File
@@ -439,6 +439,11 @@ struct Worklist {
// See also: https://github.com/carbon-language/carbon-lang/issues/6728
}
auto Add(ClangFunctionPointerTypeId /*type_id*/) -> void {
// TODO: Add fingerprinting for `ClangFunctionPointerTypeId`.
// See also: https://github.com/carbon-language/carbon-lang/issues/6728
}
auto Add(ClassId class_id) -> void {
AddEntity(sem_ir->classes().Get(class_id));
// Imported C++ classes are not uniquely identified by their name and parent
+1
View File
@@ -59,6 +59,7 @@ CARBON_SEM_IR_INST_KIND(ConvertAction)
CARBON_SEM_IR_INST_KIND(ConvertToCategoryAction)
CARBON_SEM_IR_INST_KIND(ConvertToValueAction)
CARBON_SEM_IR_INST_KIND(Converted)
CARBON_SEM_IR_INST_KIND(CppFunctionPointerType)
CARBON_SEM_IR_INST_KIND(CppOverloadSetType)
CARBON_SEM_IR_INST_KIND(CppOverloadSetValue)
CARBON_SEM_IR_INST_KIND(CppTemplateNameType)
+2
View File
@@ -268,6 +268,8 @@ auto Mangler::MangleImpl(SemIR::FunctionId function_id,
function.non_generated_builtin_function_kind());
case SemIR::Function::SpecialFunctionKind::HasCppThunk:
CARBON_FATAL("C++ functions should have been handled earlier");
case SemIR::Function::SpecialFunctionKind::CppFunctionPointerThunk:
CARBON_FATAL("Attempting to mangle call to function pointer");
}
MangleInverseQualifiedNameScope(os, function.parent_scope_id,
+9
View File
@@ -9,6 +9,7 @@
#include <utility>
#include <variant>
#include "clang/AST/Type.h"
#include "common/concepts.h"
#include "common/raw_string_ostream.h"
#include "toolchain/base/kind_switch.h"
@@ -654,6 +655,14 @@ class Stringifier {
">");
}
auto StringifyInst(InstId /*inst_id*/, CppFunctionPointerType inst) -> void {
clang::QualType clang_type(sem_ir_->clang_function_pointer_types()
.Get(inst.clang_type_id)
.clang_type,
0);
*out_ << "<C++ type " << clang_type.getAsString() << ">";
}
auto StringifyInst(InstId /*inst_id*/, FunctionType inst) -> void {
const auto& fn = sem_ir_->functions().Get(inst.function_id);
*out_ << "<type of ";
+1
View File
@@ -132,6 +132,7 @@ auto TypeIterator::ProcessType(InstId inst_id) -> std::optional<Step> {
case AssociatedEntityType::Kind:
case BoolType::Kind:
case CharLiteralType::Kind:
case CppFunctionPointerType::Kind:
case CppOverloadSetType::Kind:
case CppTemplateNameType::Kind:
case FacetType::Kind:
+13
View File
@@ -652,6 +652,19 @@ struct CppOverloadSetType {
SpecificId specific_id;
};
// The type of a C++ function pointer.
struct CppFunctionPointerType {
static constexpr auto Kind =
InstKind::CppFunctionPointerType.Define<Parse::NodeId>(
{.ir_name = "cpp_fn_ptr_type",
.is_type = InstIsType::Always,
.constant_kind = InstConstantKind::WheneverPossible});
// Always TypeType.
TypeId type_id;
ClangFunctionPointerTypeId clang_type_id;
};
// An unresolved C++ overload set value.
struct CppOverloadSetValue {
static constexpr auto Kind =