diff --git a/toolchain/base/canonical_value_store.h b/toolchain/base/canonical_value_store.h index 700a4f46f0fc..9fb8c33f5e94 100644 --- a/toolchain/base/canonical_value_store.h +++ b/toolchain/base/canonical_value_store.h @@ -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); } diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index dc649ad0da09..fc4bea5b2faf 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -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(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 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); } diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 5e2b46f1092b..530377e422ec 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -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. // -------------------------------------------------------------------------- diff --git a/toolchain/check/cpp/call.cpp b/toolchain/check/cpp/call.cpp index 50f7313b1240..c9fc293c4ce2 100644 --- a/toolchain/check/cpp/call.cpp +++ b/toolchain/check/cpp/call.cpp @@ -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"); + } } } diff --git a/toolchain/check/cpp/constant.cpp b/toolchain/check/cpp/constant.cpp index 409b1ef82852..d76e487c2646 100644 --- a/toolchain/check/cpp/constant.cpp +++ b/toolchain/check/cpp/constant.cpp @@ -313,6 +313,13 @@ auto MaybeModifyCppThunkCallForConstEval(Context& context, SemIR::Call* call) .GetAs(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( context.clang_decls() .Lookup(thunk_callee_function.first_decl_id()) diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 71203c31bd90..0660c20a7276 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -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; +} // 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(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(*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(*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(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(*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(*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; } // 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()) { - 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(); - for (int i : llvm::seq(clang_decl.hasCXXExplicitFunctionObjectParameter() + - signature.num_params)) { - AddDependentUnimportedTypeDecls(context, function_type->getParamType(i), - worklist); + const auto* function_type = type->castAs(); + 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_decl)) { - if (!isa(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_decl)) { + if (!isa(clang_decl)) { + PushType(context, + type_decl->getASTContext().getTypeDeclType(type_decl), + worklist); + } + } else if (auto* var_decl = dyn_cast(clang_decl)) { + PushType(context, var_decl->getType(), worklist); + } + auto* parent = GetParentDecl(clang_decl); + if (llvm::isa_and_nonnull(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_decl)) { - AddDependentUnimportedTypeDecls(context, var_decl->getType(), worklist); - } - auto* parent = GetParentDecl(clang_decl); - if (llvm::isa_and_nonnull(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_decl)) { - return ImportNamespaceDecl(context, clang_namespace_decl); - } - if (auto* type_decl = dyn_cast(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_decl)) { + return ImportNamespaceDecl(context, clang_namespace_decl); + } + if (auto* type_decl = dyn_cast(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_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_decl)) { + return ImportEnumConstantDecl(context, enum_const_decl); + } + if (auto* var_decl = dyn_cast(clang_decl)) { + return ImportVarDecl(context, loc_id, var_decl); + } + if (auto* template_decl = dyn_cast(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_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_decl)) { - return ImportEnumConstantDecl(context, enum_const_decl); - } - if (auto* var_decl = dyn_cast(clang_decl)) { - return ImportVarDecl(context, loc_id, var_decl); - } - if (auto* template_decl = dyn_cast(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}; diff --git a/toolchain/check/cpp/import.h b/toolchain/check/cpp/import.h index 157269ed4688..14975b5c223b 100644 --- a/toolchain/check/cpp/import.h +++ b/toolchain/check/cpp/import.h @@ -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; diff --git a/toolchain/check/cpp/thunk.cpp b/toolchain/check/cpp/thunk.cpp index 457705d7fd62..a21c00dc9b11 100644 --- a/toolchain/check/cpp/thunk.cpp +++ b/toolchain/check/cpp/thunk.cpp @@ -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()), + 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(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(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(callee_info.decl)) { break; diff --git a/toolchain/check/cpp/thunk.h b/toolchain/check/cpp/thunk.h index 19ba53541a5d..adaafbd0bb8e 100644 --- a/toolchain/check/cpp/thunk.h +++ b/toolchain/check/cpp/thunk.h @@ -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; diff --git a/toolchain/check/cpp/type_mapping.cpp b/toolchain/check/cpp/type_mapping.cpp index 0254b11a3c3b..102dbb930a9e 100644 --- a/toolchain/check/cpp/type_mapping.cpp +++ b/toolchain/check/cpp/type_mapping.cpp @@ -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; } diff --git a/toolchain/check/custom_witness.cpp b/toolchain/check/custom_witness.cpp index 1400fee86a13..6de243f42c40 100644 --- a/toolchain/check/custom_witness.cpp +++ b/toolchain/check/custom_witness.cpp @@ -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: diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index d9d24ce436fe..cd97f70bb83b 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -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 { - if (const auto* callee_function = - std::get_if(&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}; } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index ec35cf52706f..fff82a0bc065 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -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."); } } diff --git a/toolchain/check/member_access.cpp b/toolchain/check/member_access.cpp index c656b930914f..cf8994c57fc3 100644 --- a/toolchain/check/member_access.cpp +++ b/toolchain/check/member_access.cpp @@ -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; } diff --git a/toolchain/check/testdata/interop/cpp/function/import/decayed_param.carbon b/toolchain/check/testdata/interop/cpp/function/import/decayed_param.carbon index 11d8e37b95f7..a62a3cab4f0a 100644 --- a/toolchain/check/testdata/interop/cpp/function/import/decayed_param.carbon +++ b/toolchain/check/testdata/interop/cpp/function/import/decayed_param.carbon @@ -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 `` [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()` [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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc44_3: = 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: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: // CHECK:STDOUT: %nullptr.ref: %Cpp.nullptr_t = name_ref nullptr, %uninit [concrete = constants.%uninit] +// CHECK:STDOUT: %.loc44: %.659 = converted %nullptr.ref, [concrete = ] +// CHECK:STDOUT: %TakesFunction.call: init %empty_tuple.type = call imports.%TakesFunction.decl() // CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: = 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: diff --git a/toolchain/check/testdata/interop/cpp/function/import/function_ptr.carbon b/toolchain/check/testdata/interop/cpp/function/import/function_ptr.carbon new file mode 100644 index 000000000000..63b78eaceacb --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/function/import/function_ptr.carbon @@ -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 `` 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 `` 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 [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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %TakeCallback.decl: %TakeCallback.type = fn_decl @TakeCallback [concrete = constants.%TakeCallback] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc7_3: = 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: = 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_convert_fn_ptr.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_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 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, +// CHECK:STDOUT: %.loc13: type = splice_block %CallbackType.ref [concrete = constants.%.659] { +// CHECK:STDOUT: %Cpp.ref: = 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 %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: +// 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 [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 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: = 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- call_fn_ptr.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_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: = 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 %float.6da, %Core.FloatLiteral.as.ImplicitAs.impl.Convert.b37 [concrete] +// CHECK:STDOUT: %Core.FloatLiteral.as.ImplicitAs.impl.Convert.specific_fn: = 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 %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: = 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 %float.943, %Float.as.Copy.impl.Op.ed6 [concrete] +// CHECK:STDOUT: %Float.as.Copy.impl.Op.specific_fn: = specific_function %Float.as.Copy.impl.Op.ed6, @Float.as.Copy.impl.Op(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.af3: = 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 %.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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %__invoke__carbon_thunk.decl: %__invoke__carbon_thunk.type = fn_decl @__invoke__carbon_thunk [concrete = constants.%__invoke__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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 %float, %impl.elem0.loc7_41.1 [concrete = constants.%Core.FloatLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn.loc7_41.1: = 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 %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 %.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 %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 %.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 %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: +// 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 [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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %TakeComplexCallback.decl: %TakeComplexCallback.type = fn_decl @TakeComplexCallback [concrete = constants.%TakeComplexCallback] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc7_3: = 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: = 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_convert_complex_fn_ptr.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_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 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, +// CHECK:STDOUT: %.loc13: type = splice_block %CallbackType.ref [concrete = constants.%.659] { +// CHECK:STDOUT: %Cpp.ref: = 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 %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: +// 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 [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 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: = 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: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- call_complex_fn_ptr.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %__invoke__carbon_thunk.decl: %__invoke__carbon_thunk.type = fn_decl @__invoke__carbon_thunk [concrete = constants.%__invoke__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.cpp_destructor.decl: %C.cpp_destructor.type = fn_decl @C.cpp_destructor [concrete = constants.%C.cpp_destructor] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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: = 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: = 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 %.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 %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: +// 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 [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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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: = 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 %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: +// 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: diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index e73a6a0884df..6dec14e82317 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -178,6 +178,12 @@ auto GetClassType(Context& context, SemIR::ClassId class_id, return GetTypeImpl(context, class_id, specific_id); } +auto GetCppFunctionPointerType(Context& context, + SemIR::ClangFunctionPointerTypeId clang_type_id) + -> SemIR::TypeId { + return GetTypeImpl(context, clang_type_id); +} + auto GetCppOverloadSetType(Context& context, SemIR::CppOverloadSetId overload_set_id, SemIR::SpecificId specific_id) -> SemIR::TypeId { diff --git a/toolchain/check/type.h b/toolchain/check/type.h index bcd81f915d5c..fd9cf4db9a95 100644 --- a/toolchain/check/type.h +++ b/toolchain/check/type.h @@ -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, diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 6406444c6c30..e2b2b95f9ab6 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -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 { diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 3038b52dd224..ac764b61b3e9 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -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) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index fda5731f5c69..ffd3ff4b0fe2 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -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, diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 9cbfe964bc3e..3299146fdcc0 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -747,7 +747,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, // order. std::vector 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 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); } diff --git a/toolchain/lower/testdata/interop/cpp/function/import/function_ptr.carbon b/toolchain/lower/testdata/interop/cpp/function/import/function_ptr.carbon new file mode 100644 index 000000000000..64a1fd2f1c42 --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/function/import/function_ptr.carbon @@ -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: diff --git a/toolchain/lower/type.cpp b/toolchain/lower/type.cpp index 8f09ebe33b14..d769f3dd529c 100644 --- a/toolchain/lower/type.cpp +++ b/toolchain/lower/type.cpp @@ -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(), diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 26c4c809b0f2..56665a8cbdf7 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -229,6 +229,7 @@ cc_library( "//common:concepts", "//common:raw_string_ostream", "//toolchain/base:kind_switch", + "@llvm-project//clang:ast", "@llvm-project//llvm:Support", ], ) diff --git a/toolchain/sem_ir/clang_decl.h b/toolchain/sem_ir/clang_decl.h index 7983a19a16b4..2a975673d2a2 100644 --- a/toolchain/sem_ir/clang_decl.h +++ b/toolchain/sem_ir/clang_decl.h @@ -7,6 +7,7 @@ #include +#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 { // A passing mode for a parameter in a C++ function signature. enum class PassingMode : int8_t { @@ -160,6 +161,11 @@ struct ClangDeclKey : public Printable { UncheckedTag /*_*/); }; +// A ClangDeclSignature mapped to an ID. +using ClangDeclSignatureStore = + CanonicalValueStore, 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 reverse_lookup_; }; -// A ClangDeclSignature mapped to an ID. -using ClangDeclSignatureStore = - CanonicalValueStore, 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, ClangFunctionPointerTypeInfo>; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index 2fbc4e1b05b9..1dfb95c14516 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -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) { // Switch to looking at the inner instruction and its specific, which is diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 0d6e73954886..a0f9adbc63a9 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -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>; template class BlockValueStore>; +template class CanonicalValueStore, + SemIR::ClangFunctionPointerTypeInfo>; } // namespace Carbon diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 5219747dc481..22389dce33fa 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -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 { 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 { // 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>; extern template class BlockValueStore>; +extern template class CanonicalValueStore< + SemIR::ClangFunctionPointerTypeId, const clang::Type*, + Tag, SemIR::ClangFunctionPointerTypeInfo>; } // namespace Carbon #endif // CARBON_TOOLCHAIN_SEM_IR_FILE_H_ diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 833ab803f222..413066a9a392 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -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 "; diff --git a/toolchain/sem_ir/formatter.h b/toolchain/sem_ir/formatter.h index dfde52b9adb3..d7327abf415f 100644 --- a/toolchain/sem_ir/formatter.h +++ b/toolchain/sem_ir/formatter.h @@ -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 auto FormatArg(BundleId bundle_id) -> void { diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index 3b201657294d..69fe62d6046d 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -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( + 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(); diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index 0e19ac75ff46..b9335867c66a 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -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; +using Callee = std::variant; // Given a callee expression in a function call, attempt to convert the callee // to a `BoundMethod`, minimally unwrapping it while doing so. diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index fa0426cc1685..ae28e4303d28 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -39,6 +39,7 @@ using IdKind = TypeEnum< CallParamIndex, CharId, ClangDeclId, + ClangFunctionPointerTypeId, ClassId, CompileTimeBindIndex, ConstantId, diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 990f9af11fc4..8ef59cc7bdf0 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -509,6 +509,13 @@ struct ClangDeclSignatureId : public IdBase { using IdBase::IdBase; }; +// The ID of a `ClangFunctionPointerTypeInfo`. +struct ClangFunctionPointerTypeId : public IdBase { + static constexpr llvm::StringLiteral Label = "clang_function_pointer_type"; + + using IdBase::IdBase; +}; + // A boolean value. struct BoolValue : public IdBase { // Not used by `Print`, but for `IdKind`. diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index a8be4ff322a9..9d2cf6b678a8 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -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 diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 15bdbb546111..c2b2d112e298 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -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) diff --git a/toolchain/sem_ir/mangler.cpp b/toolchain/sem_ir/mangler.cpp index 19bfd90573fa..01f95cd7920d 100644 --- a/toolchain/sem_ir/mangler.cpp +++ b/toolchain/sem_ir/mangler.cpp @@ -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, diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 42d4b9c8457d..67f8a00aac7a 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -9,6 +9,7 @@ #include #include +#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_ << ""; + } + auto StringifyInst(InstId /*inst_id*/, FunctionType inst) -> void { const auto& fn = sem_ir_->functions().Get(inst.function_id); *out_ << " std::optional { case AssociatedEntityType::Kind: case BoolType::Kind: case CharLiteralType::Kind: + case CppFunctionPointerType::Kind: case CppOverloadSetType::Kind: case CppTemplateNameType::Kind: case FacetType::Kind: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 9b79b1b8972c..afe9240e5a18 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -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( + {.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 =