diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 6abf044a6a65..4b440e395ac9 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -2448,12 +2448,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, resolver, resolver.import_classes() .Get(resolver.import_vtables().Get(inst.vtable_id).class_id) .vtable_decl_id); - auto vtable_const_inst = resolver.local_insts().Get( - resolver.local_constant_values().GetInstId(vtable_const_id)); if (resolver.HasNewWork()) { return ResolveResult::Retry(); } + auto vtable_const_inst = resolver.local_insts().Get( + resolver.local_constant_values().GetInstId(vtable_const_id)); return ResolveResult::Deduplicated( resolver, {.type_id = GetPointerType(resolver.local_context(), diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 0db095c5fbe4..107386dd86a8 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -247,13 +247,17 @@ auto FileContext::GetConstant(SemIR::ConstantId const_id, return global_variable; } -auto FileContext::GetOrCreateFunctionInfo(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) - -> std::optional& { +auto FileContext::GetOrCreateFunctionInfo( + SemIR::FunctionId function_id, SemIR::SpecificId specific_id, + FileContext* fallback_file, SemIR::FunctionId fallback_function_id, + SemIR::SpecificId fallback_specific_id) -> std::optional& { // If we have already lowered a declaration of this function, just return it. + // TODO: If the existing declaration is inexact, and we now have a fallback, + // we should try again. auto& result = GetFunctionInfo(function_id, specific_id); if (!result) { - result = BuildFunctionDecl(function_id, specific_id); + result = BuildFunctionDecl(function_id, specific_id, fallback_file, + fallback_function_id, fallback_specific_id); } return result; } @@ -276,57 +280,103 @@ auto FileContext::GetOrCreateFunctionInfo(SemIR::FunctionId function_id, // Furthermore, SemIR is designed to eventually support compound return forms, // in which case there can be multiple output parameters for different pieces of // the return form, but it's not yet clear how we will lower such functions. +// +// We also deal with the case where the function signature involves incomplete +// types. This can happen if the function is declared but never defined nor +// called in this file. Declarations of such functions can still need to be +// emitted; currently this happens if they are part of a class's vtable. Such +// uses do not need an exact signature, so we emit them with the LLVM type +// `void()` and set `inexact` on the result to indicate the type is not known. +// LLVM can handle merging inexact and exact signatures, and this matches how +// Clang handles the corresponding situation in C++. +// +// One additional complexity is that we may need to fetch information about the +// same function from multiple different files. For a call to a generic +// function, there may be no single file in which all the relevant types are +// complete, so we will look at both the specific function definition that is +// the resolved callee, as well as the partially-specific function from the call +// site. +// +// In general, we support being given a list of variants of the function, in +// which the first function in the list is the primary declaration and should be +// the most specific function, and the others are used as fallbacks if an +// incomplete type is encountered. class FileContext::FunctionTypeInfoBuilder { public: - // Creates a FunctionTypeInfoBuilder that uses the given FileContext, and - // the given specific of the function. - FunctionTypeInfoBuilder(FileContext* context, SemIR::SpecificId specific_id) - : context_(*context), specific_id_(specific_id) {} + struct FunctionInContext { + FileContext* context; + SemIR::FunctionId function_id; + SemIR::SpecificId specific_id; + }; - // Retrieves various features of `function`'s type useful for constructing the - // `llvm::Type` and `llvm::DISubroutineType` for the `llvm::Function`. If any - // part of the type can't be manifest (eg: incomplete return or parameter - // types), then the result is as if the type was `void()`. Should only be - // called once on a given builder. - auto Build(const SemIR::Function& function) && -> FunctionTypeInfo; - - private: - // By convention, state transition methods return false to indicate that - // `Abort` was called. As a convenience, that applies even to methods that - // never call `Abort`, and to `Abort` itself, so that their callers can easily - // propagate the failure. - - // Resets the builder to the fallback state `void()`. This puts the builder in - // a state where Finalize can be called, and no other operation should be - // called. - auto Abort() -> bool { - call_param_pattern_ids_ = {}; - lowered_param_indices_.clear(); - unused_param_indices_.clear(); - param_name_ids_.clear(); - param_types_.clear(); - param_di_types_.clear(); - return_type_ = nullptr; - inexact_ = true; - SetReturnByCopy(SemIR::TypeId::None); - return false; + // Creates a FunctionTypeInfoBuilder that uses the given functions. + explicit FunctionTypeInfoBuilder(llvm::ArrayRef functions) + : context_(&functions.front().context->context()), functions_(functions) { + CARBON_CHECK(!functions_.empty()); } - // Handles the function's return form. The argument can be None, indicating - // that there was no explicitly declared return form. + // Retrieves various features of the function's type useful for constructing + // the `llvm::Type` and `llvm::DISubroutineType` for the `llvm::Function`. If + // any part of the type can't be manifest (eg: incomplete return or parameter + // types), then the result is as if the type was `void()`. Should only be + // called once on a given builder. + auto Build() && -> FunctionTypeInfo; + + private: + // By convention, state transition methods return false (without changing the + // accumulated information about the function) to indicate that we could not + // manifest the complete function type successfully in this context. + + // Information about how a function is called in SemIR. + struct SemIRIndexInfo { + // The number of parameters in the SemIR call signature. + int num_params; + // The index of the first return parameter in the SemIR call signature. + int return_param_index; + + friend auto operator==(const SemIRIndexInfo& lhs, const SemIRIndexInfo& rhs) + -> bool = default; + }; + + // Get information about the SemIR function signature. + auto GetSemIRIndexInfo(const FunctionInContext& fn_in_context) + -> SemIRIndexInfo { + const auto& sem_ir = fn_in_context.context->sem_ir(); + const auto& function = sem_ir.functions().Get(fn_in_context.function_id); + + int num_params = + sem_ir.inst_blocks().Get(function.call_param_patterns_id).size(); + + int return_param_index = -1; + if (function.call_param_ranges.return_size() > 0) { + CARBON_CHECK(function.call_param_ranges.return_size() == 1, + "TODO: support multiple return forms"); + return_param_index = function.call_param_ranges.return_begin().index; + } + + return {.num_params = num_params, .return_param_index = return_param_index}; + } + + // Handles the function's return form. // - // This should be called before HandleParameter. It delegates to exactly one - // of SetReturnByCopy, SetReturnByReference, SetReturnInPlace, or Abort, and - // returns false if Abort was called. - auto HandleReturnForm(SemIR::InstId return_form_inst_id) -> bool; + // This should be called before `HandleParameter`. It handles the return form + // by trying each `FunctionInContext` until one succeeds, and returns false if + // all attempts failed. + auto HandleReturnForm() -> bool; + + // Tries to handle the return form using the given context. Delegates to + // exactly one of `SetReturnByCopy`, `SetReturnByReference`, or + // `SetReturnInPlace`, or returns false if the return type is incomplete. + auto TryHandleReturnForm(const FunctionInContext& func_ctx) -> bool; // Records that the LLVM function returns by copy, with type `return_type_id`. // `return_type_id` can be `None`, which is treated as equivalent to the // default return type `()`. - auto SetReturnByCopy(SemIR::TypeId return_type_id) -> bool { + auto SetReturnByCopy(const FunctionInContext& func_ctx, + SemIR::TypeId return_type_id) -> bool { CARBON_CHECK(return_type_ == nullptr); CARBON_CHECK(param_di_types_.empty()); - auto lowered_return_types = GetLoweredTypes(return_type_id); + auto lowered_return_types = GetLoweredTypes(func_ctx, return_type_id); return_type_ = lowered_return_types.llvm_ir_type; param_di_types_.push_back(lowered_return_types.llvm_di_type); return true; @@ -334,9 +384,10 @@ class FileContext::FunctionTypeInfoBuilder { // Records that the LLVM function returns by reference, with type // `return_type_id`. - auto SetReturnByReference(SemIR::TypeId /*return_type_id*/) -> bool { - return_type_ = - llvm::PointerType::get(context_.llvm_context(), /*AddressSpace=*/0); + auto SetReturnByReference(const FunctionInContext& func_ctx, + SemIR::TypeId /*return_type_id*/) -> bool { + return_type_ = llvm::PointerType::get(func_ctx.context->llvm_context(), + /*AddressSpace=*/0); // TODO: replace this with a reference type. param_di_types_.push_back(GetPointerDIType(nullptr)); return true; @@ -344,9 +395,10 @@ class FileContext::FunctionTypeInfoBuilder { // Records that the LLVM function returns in place, with type // `return_type_id`. - auto SetReturnInPlace(SemIR::TypeId return_type_id) -> bool { - return_type_ = llvm::Type::getVoidTy(context_.llvm_context()); - sret_type_ = context_.GetType(return_type_id); + auto SetReturnInPlace(const FunctionInContext& func_ctx, + SemIR::TypeId return_type_id) -> bool { + return_type_ = llvm::Type::getVoidTy(func_ctx.context->llvm_context()); + sret_type_ = func_ctx.context->GetType(return_type_id); // We don't add to param_di_types_ because that will be handled by the // loop over the SemIR parameters. return true; @@ -359,18 +411,25 @@ class FileContext::FunctionTypeInfoBuilder { // parameters; it will determine which parameters belong in the LLVM IR // parameter list. // - // This delegates to exactly one of AddLoweredParam, IgnoreParam, or Abort, - // and returns false if Abort was called. + // This tries each `FunctionInContext` until one succeeds, and returns false + // if all attempts failed. auto HandleParameter(SemIR::CallParamIndex index) -> bool; - // Records that the parameter pattern at the given index jas the given ID, and + // Tries to handle the parameter pattern at the given index using the given + // context. Delegates to either `AddLoweredParam` or `IgnoreParam`, or returns + // false if the parameter type is incomplete. + auto TryHandleParameter(const FunctionInContext& func_ctx, + SemIR::CallParamIndex index) -> bool; + + // Records that the parameter pattern at the given index has the given ID, and // lowers to the given IR and DI types. - auto AddLoweredParam(SemIR::CallParamIndex index, + auto AddLoweredParam(const FunctionInContext& func_ctx, + SemIR::CallParamIndex index, SemIR::InstId param_pattern_id, LoweredTypes param_types) -> bool { lowered_param_indices_.push_back(index); - param_name_ids_.push_back( - SemIR::GetPrettyNameFromPatternId(context_.sem_ir(), param_pattern_id)); + param_name_ids_.push_back(SemIR::GetPrettyNameFromPatternId( + func_ctx.context->sem_ir(), param_pattern_id)); param_types_.push_back(param_types.llvm_ir_type); param_di_types_.push_back(param_types.llvm_di_type); return true; @@ -386,26 +445,32 @@ class FileContext::FunctionTypeInfoBuilder { // Builds and returns a FunctionTypeInfo from the accumulated information. auto Finalize() -> FunctionTypeInfo; + // Clears out accumulated state and returns a FunctionTypeInfo with the + // fallback state `void()`. + auto Abort() -> FunctionTypeInfo; + // Returns LLVM IR and DI types for the given SemIR type. This is not a state // transition. It mostly delegates to context_.GetTypeAndDIType, but treats // TypeId::None as equivalent to the unit type, and uses an untyped pointer as // a placeholder DI type if context_ doesn't provide one. - auto GetLoweredTypes(SemIR::TypeId type_id) -> LoweredTypes; + auto GetLoweredTypes(const FunctionInContext& func_ctx, SemIR::TypeId type_id) + -> LoweredTypes; // Returns a DI type for a pointer to the given pointee. The pointee type may // be null. auto GetPointerDIType(llvm::DIType* pointee_type, unsigned address_space = 0) -> llvm::DIDerivedType* { - const auto& data_layout = context_.llvm_module().getDataLayout(); - return context_.context().di_builder().createPointerType( + const auto& data_layout = context_->llvm_module().getDataLayout(); + return context_->di_builder().createPointerType( pointee_type, data_layout.getPointerSizeInBits(address_space)); } - FileContext& context_; - const SemIR::SpecificId specific_id_; + Context* context_; - // The input `Call` parameter patterns. - llvm::ArrayRef call_param_pattern_ids_; + llvm::ArrayRef functions_; + + // The number of input `Call` parameter patterns. + int num_params_ = 0; // The types of the parameters in the LLVM IR function. Each one corresponds // to a SemIR `Call` parameter, but some `Call` parameters may be omitted @@ -433,11 +498,6 @@ class FileContext::FunctionTypeInfoBuilder { // lowered_param_indices_. llvm::SmallVector unused_param_indices_; - // The `index` member of the SemIR function's return parameter, or -1 if it - // has no return parameter. Note that even if the SemIR function has a return - // parameter, the LLVM IR function might not. - int semir_return_param_index_ = -1; - // The LLVM function's return type. llvm::Type* return_type_ = nullptr; @@ -452,110 +512,127 @@ class FileContext::FunctionTypeInfoBuilder { bool inexact_ = false; }; -auto FileContext::FunctionTypeInfoBuilder::Build( - const SemIR::Function& function) && -> FunctionTypeInfo { +auto FileContext::FunctionTypeInfoBuilder::Build() && -> FunctionTypeInfo { // TODO: For the `Run` entry point, remap return type to i32 if it doesn't // return a value. - call_param_pattern_ids_ = - context_.sem_ir().inst_blocks().Get(function.call_param_patterns_id); - lowered_param_indices_.reserve(call_param_pattern_ids_.size()); - param_name_ids_.reserve(call_param_pattern_ids_.size()); - param_types_.reserve(call_param_pattern_ids_.size()); - param_di_types_.reserve(call_param_pattern_ids_.size()); + // Determine how the parameters are numbered in SemIR, and make sure it's the + // same for all versions of the function. + auto semir_info = GetSemIRIndexInfo(functions_.front()); + CARBON_CHECK( + llvm::all_of(functions_.drop_front(), [&](const auto& fn_in_context) { + return GetSemIRIndexInfo(fn_in_context) == semir_info; + })); - if (function.call_param_ranges.return_size() > 0) { - CARBON_CHECK(function.call_param_ranges.return_size() == 1, - "TODO: support multiple return forms"); - semir_return_param_index_ = function.call_param_ranges.return_begin().index; + num_params_ = semir_info.num_params; + lowered_param_indices_.reserve(num_params_); + param_name_ids_.reserve(num_params_); + param_types_.reserve(num_params_); + param_di_types_.reserve(num_params_); + + if (!HandleReturnForm()) { + return Abort(); } - if (!HandleReturnForm(function.return_form_inst_id)) { - return Finalize(); - } - int params_end = call_param_pattern_ids_.size(); - if (semir_return_param_index_ >= 0) { - CARBON_CHECK(semir_return_param_index_ == - static_cast(call_param_pattern_ids_.size()) - 1, + int params_end = num_params_; + if (semir_info.return_param_index >= 0) { + CARBON_CHECK(semir_info.return_param_index == semir_info.num_params - 1, "Unexpected parameter order"); - params_end = semir_return_param_index_; + params_end = semir_info.return_param_index; // Handle the return parameter first, because it goes first in the LLVM // convention. - if (!HandleParameter(SemIR::CallParamIndex(semir_return_param_index_))) { - return Finalize(); + if (!HandleParameter( + SemIR::CallParamIndex(semir_info.return_param_index))) { + return Abort(); } } for (int i : llvm::seq(params_end)) { if (!HandleParameter(SemIR::CallParamIndex(i))) { - return Finalize(); + return Abort(); } } return Finalize(); } -auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm( - SemIR::InstId return_form_inst_id) -> bool { +auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm() -> bool { + for (const auto& func_ctx : functions_) { + if (TryHandleReturnForm(func_ctx)) { + return true; + } + } + return false; +} + +auto FileContext::FunctionTypeInfoBuilder::TryHandleReturnForm( + const FunctionInContext& func_ctx) -> bool { + const auto& function = + func_ctx.context->sem_ir().functions().Get(func_ctx.function_id); + auto return_form_inst_id = function.return_form_inst_id; if (!return_form_inst_id.has_value()) { - return SetReturnByCopy(SemIR::TypeId::None); + return SetReturnByCopy(func_ctx, SemIR::TypeId::None); } auto return_form_const_id = SemIR::GetConstantValueInSpecific( - context_.sem_ir(), specific_id_, return_form_inst_id); - auto return_form_inst = context_.sem_ir().insts().Get( - context_.sem_ir().constant_values().GetInstId(return_form_const_id)); + func_ctx.context->sem_ir(), func_ctx.specific_id, return_form_inst_id); + auto return_form_inst = func_ctx.context->sem_ir().insts().Get( + func_ctx.context->sem_ir().constant_values().GetInstId( + return_form_const_id)); CARBON_KIND_SWITCH(return_form_inst) { case CARBON_KIND(SemIR::InitForm init_form): { auto return_type_id = - context_.sem_ir().types().GetTypeIdForTypeConstantId( + func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId( SemIR::GetConstantValueInSpecific( - context_.sem_ir(), specific_id_, + func_ctx.context->sem_ir(), func_ctx.specific_id, init_form.type_component_inst_id)); switch ( - SemIR::InitRepr::ForType(context_.sem_ir(), return_type_id).kind) { + SemIR::InitRepr::ForType(func_ctx.context->sem_ir(), return_type_id) + .kind) { case SemIR::InitRepr::InPlace: { - return SetReturnInPlace(return_type_id); + return SetReturnInPlace(func_ctx, return_type_id); } case SemIR::InitRepr::ByCopy: { - return SetReturnByCopy(return_type_id); + return SetReturnByCopy(func_ctx, return_type_id); } case SemIR::InitRepr::None: - return SetReturnByCopy(SemIR::TypeId::None); + return SetReturnByCopy(func_ctx, SemIR::TypeId::None); case SemIR::InitRepr::Dependent: CARBON_FATAL("Lowering function return with dependent type: {0}", return_form_inst); case SemIR::InitRepr::Incomplete: case SemIR::InitRepr::Abstract: - return Abort(); + return false; } } case CARBON_KIND(SemIR::RefForm ref_form): { auto return_type_id = - context_.sem_ir().types().GetTypeIdForTypeConstantId( + func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId( SemIR::GetConstantValueInSpecific( - context_.sem_ir(), specific_id_, + func_ctx.context->sem_ir(), func_ctx.specific_id, ref_form.type_component_inst_id)); - return SetReturnByReference(return_type_id); + return SetReturnByReference(func_ctx, return_type_id); } case CARBON_KIND(SemIR::ValueForm val_form): { auto return_type_id = - context_.sem_ir().types().GetTypeIdForTypeConstantId( + func_ctx.context->sem_ir().types().GetTypeIdForTypeConstantId( SemIR::GetConstantValueInSpecific( - context_.sem_ir(), specific_id_, + func_ctx.context->sem_ir(), func_ctx.specific_id, val_form.type_component_inst_id)); switch ( - SemIR::ValueRepr::ForType(context_.sem_ir(), return_type_id).kind) { + SemIR::ValueRepr::ForType(func_ctx.context->sem_ir(), return_type_id) + .kind) { case SemIR::ValueRepr::Unknown: - return Abort(); + return false; + case SemIR::ValueRepr::Dependent: CARBON_FATAL("Lowering function return with dependent type: {0}", return_form_inst); case SemIR::ValueRepr::None: - return SetReturnByCopy(SemIR::TypeId::None); + return SetReturnByCopy(func_ctx, SemIR::TypeId::None); case SemIR::ValueRepr::Copy: - return SetReturnByCopy(return_type_id); + return SetReturnByCopy(func_ctx, return_type_id); case SemIR::ValueRepr::Pointer: case SemIR::ValueRepr::Custom: - return SetReturnByReference(return_type_id); + return SetReturnByReference(func_ctx, return_type_id); } } default: @@ -565,19 +642,33 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm( auto FileContext::FunctionTypeInfoBuilder::HandleParameter( SemIR::CallParamIndex index) -> bool { - const auto& sem_ir = context_.sem_ir(); - auto param_pattern_id = call_param_pattern_ids_[index.index]; + for (const auto& func_ctx : functions_) { + if (TryHandleParameter(func_ctx, index)) { + return true; + } + } + return false; +} + +auto FileContext::FunctionTypeInfoBuilder::TryHandleParameter( + const FunctionInContext& func_ctx, SemIR::CallParamIndex index) -> bool { + const auto& sem_ir = func_ctx.context->sem_ir(); + auto param_pattern_id = + sem_ir.inst_blocks().Get(sem_ir.functions() + .Get(func_ctx.function_id) + .call_param_patterns_id)[index.index]; auto param_pattern = sem_ir.insts().Get(param_pattern_id); auto param_type_id = ExtractScrutineeType( - sem_ir, - SemIR::GetTypeOfInstInSpecific(sem_ir, specific_id_, param_pattern_id)); + sem_ir, SemIR::GetTypeOfInstInSpecific(sem_ir, func_ctx.specific_id, + param_pattern_id)); // Returns the appropriate LoweredTypes for reference-like parameters. auto ref_lowered_types = [&]() -> LoweredTypes { - return {.llvm_ir_type = llvm::PointerType::get(context_.llvm_context(), - /*AddressSpace=*/0), - // TODO: replace this with a reference type. - .llvm_di_type = GetLoweredTypes(param_type_id).llvm_di_type}; + return { + .llvm_ir_type = llvm::PointerType::get(func_ctx.context->llvm_context(), + /*AddressSpace=*/0), + // TODO: replace this with a reference type. + .llvm_di_type = GetLoweredTypes(func_ctx, param_type_id).llvm_di_type}; }; CARBON_CHECK( @@ -613,12 +704,14 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( switch (param_kind) { case SemIR::RefParamPattern::Kind: case SemIR::VarParamPattern::Kind: { - return AddLoweredParam(index, param_pattern_id, ref_lowered_types()); + return AddLoweredParam(func_ctx, index, param_pattern_id, + ref_lowered_types()); } case SemIR::OutParamPattern::Kind: { switch (SemIR::InitRepr::ForType(sem_ir, param_type_id).kind) { case SemIR::InitRepr::InPlace: - return AddLoweredParam(index, param_pattern_id, ref_lowered_types()); + return AddLoweredParam(func_ctx, index, param_pattern_id, + ref_lowered_types()); case SemIR::InitRepr::ByCopy: case SemIR::InitRepr::None: return IgnoreParam(index); @@ -627,14 +720,14 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( param_pattern); case SemIR::InitRepr::Incomplete: case SemIR::InitRepr::Abstract: - return Abort(); + return false; } } case SemIR::ValueParamPattern::Kind: { switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir, param_type_id); value_rep.kind) { case SemIR::ValueRepr::Unknown: - return Abort(); + return false; case SemIR::ValueRepr::Dependent: CARBON_FATAL("Lowering function parameter with dependent type: {0}", param_pattern); @@ -644,8 +737,9 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( case SemIR::ValueRepr::Custom: case SemIR::ValueRepr::Pointer: { if (value_rep.type_id.has_value()) { - return AddLoweredParam(index, param_pattern_id, - GetLoweredTypes(value_rep.type_id)); + return AddLoweredParam( + func_ctx, index, param_pattern_id, + GetLoweredTypes(func_ctx, value_rep.type_id)); } else { return IgnoreParam(index); } @@ -659,9 +753,9 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo { CARBON_CHECK(lowered_param_indices_.size() + unused_param_indices_.size() == - call_param_pattern_ids_.size()); + static_cast(num_params_)); CARBON_CHECK(!param_di_types_.empty()); - auto& di_builder = context_.context().di_builder(); + auto& di_builder = context_->di_builder(); return {.type = llvm::FunctionType::get(return_type_, param_types_, /*isVarArg=*/false), .di_type = di_builder.createSubroutineType( @@ -674,13 +768,27 @@ auto FileContext::FunctionTypeInfoBuilder::Finalize() -> FunctionTypeInfo { .inexact = inexact_}; } +auto FileContext::FunctionTypeInfoBuilder::Abort() -> FunctionTypeInfo { + num_params_ = 0; + lowered_param_indices_.clear(); + unused_param_indices_.clear(); + param_name_ids_.clear(); + param_types_.clear(); + param_di_types_.clear(); + return_type_ = llvm::Type::getVoidTy(context_->llvm_context()); + param_di_types_.push_back(nullptr); + inexact_ = true; + return Finalize(); +} + auto FileContext::FunctionTypeInfoBuilder::GetLoweredTypes( - SemIR::TypeId type_id) -> LoweredTypes { + const FunctionInContext& func_ctx, SemIR::TypeId type_id) -> LoweredTypes { if (!type_id.has_value()) { - return {.llvm_ir_type = llvm::Type::getVoidTy(context_.llvm_context()), - .llvm_di_type = nullptr}; + return { + .llvm_ir_type = llvm::Type::getVoidTy(func_ctx.context->llvm_context()), + .llvm_di_type = nullptr}; } - auto result = context_.GetTypeAndDIType(type_id); + auto result = func_ctx.context->GetTypeAndDIType(type_id); if (result.llvm_di_type == nullptr) { // TODO: figure out what type should go here, or ensure this doesn't // happen. @@ -792,7 +900,10 @@ auto FileContext::GetOrCreateLLVMFunction( } auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) + SemIR::SpecificId specific_id, + FileContext* fallback_file, + SemIR::FunctionId fallback_function_id, + SemIR::SpecificId fallback_specific_id) -> std::optional { const auto& function = sem_ir().functions().Get(function_id); @@ -818,8 +929,12 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id, // TODO: Consider tracking whether the function has been used, and only // lowering it if it's needed. + FunctionTypeInfoBuilder::FunctionInContext func_infos[] = { + {this, function_id, specific_id}, + {fallback_file, fallback_function_id, fallback_specific_id}}; auto function_type_info = - FunctionTypeInfoBuilder(this, specific_id).Build(function); + FunctionTypeInfoBuilder(llvm::ArrayRef(func_infos, fallback_file ? 2 : 1)) + .Build(); auto* llvm_function = GetOrCreateLLVMFunction(function_type_info, function_id, specific_id); diff --git a/toolchain/lower/file_context.h b/toolchain/lower/file_context.h index 8c4447043a0a..0be7578c9196 100644 --- a/toolchain/lower/file_context.h +++ b/toolchain/lower/file_context.h @@ -87,8 +87,14 @@ class FileContext { // Returns the FunctionInfo for the given function in the given specific. If // it's not already available, this function will compute it, including // creating the `llvm::Function` for it. Returns nullopt for a builtin. - auto GetOrCreateFunctionInfo(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) + // + // The fallback information is used if the specific function has incomplete + // types. + auto GetOrCreateFunctionInfo( + SemIR::FunctionId function_id, SemIR::SpecificId specific_id, + FileContext* fallback_file = nullptr, + SemIR::FunctionId fallback_function_id = SemIR::FunctionId::None, + SemIR::SpecificId fallback_specific_id = SemIR::SpecificId::None) -> std::optional&; // Returns a lowered type for the given type_id. @@ -238,7 +244,10 @@ class FileContext { // by the caller. auto BuildFunctionDecl( SemIR::FunctionId function_id, - SemIR::SpecificId specific_id = SemIR::SpecificId::None) + SemIR::SpecificId specific_id = SemIR::SpecificId::None, + FileContext* fallback_file = nullptr, + SemIR::FunctionId fallback_function_id = SemIR::FunctionId::None, + SemIR::SpecificId fallback_specific_id = SemIR::SpecificId::None) -> std::optional; // Builds a function's body. Common functionality for all functions. diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 5706915a1376..3041f6b6bc26 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -591,9 +591,8 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, static auto HandleVirtualCall(FunctionContext& context, llvm::ArrayRef args, - const SemIR::File* callee_file, const SemIR::Function& function, - const SemIR::CalleeFunction& callee_function) + const FunctionInfo& function_info) -> llvm::CallInst* { CARBON_CHECK(!args.empty(), "Virtual functions must have at least one parameter"); @@ -609,10 +608,6 @@ static auto HandleVirtualCall(FunctionContext& context, auto* i32_type = llvm::IntegerType::getInt32Ty(context.llvm_context()); auto* pointer_type = llvm::PointerType::get(context.llvm_context(), /* address space */ 0); - const auto& function_info = - context.GetFileContext(callee_file) - .GetOrCreateFunctionInfo(callee_function.function_id, - callee_function.resolved_specific_id); llvm::Value* virtual_fn; if (function.clang_decl_id.has_value()) { // Use absolute vtables for clang interop - the itanium vtable contains @@ -640,7 +635,7 @@ static auto HandleVirtualCall(FunctionContext& context, llvm::ConstantInt::get( i32_type, static_cast(function.virtual_index) * 4)}); } - return context.builder().CreateCall(function_info->type, virtual_fn, args); + return context.builder().CreateCall(function_info.type, virtual_fn, args); } auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, @@ -660,6 +655,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, callee.inst_id = bound_method->function_decl_id; } + // Find the callee that the call instruction was type-checked against. This + // determines the meaning of the `arg_ids`. + auto inst_callee_function = + SemIR::GetCalleeAsFunction(*callee.file, callee.inst_id); + // Map to the callee in the specific. This might be in a different file than // the one we're currently lowering. if (context.specific_id().has_value()) { @@ -686,10 +686,15 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, return; } + // Get the function info for the callee. If the callee has incomplete types, + // fall back to using the information from the call instruction. const auto& function_info = context.GetFileContext(callee.file) .GetOrCreateFunctionInfo(callee_function.function_id, - callee_function.resolved_specific_id); + callee_function.resolved_specific_id, + &context.GetFileContext(&context.sem_ir()), + inst_callee_function.function_id, + inst_callee_function.resolved_specific_id); CARBON_CHECK(!function_info->inexact, "Attempting to emit call to inexact function: {0}", *function_info->llvm_function); @@ -722,8 +727,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, "Argument count mismatch: {0}", describe_call()); call = context.builder().CreateCall(llvm_callee, args); } else { - call = HandleVirtualCall(context, args, callee.file, function, - callee_function); + call = HandleVirtualCall(context, args, function, *function_info); } context.SetLocal(inst_id, call); diff --git a/toolchain/lower/testdata/packages/imported_type_completeness.carbon b/toolchain/lower/testdata/packages/imported_type_completeness.carbon new file mode 100644 index 000000000000..a30809774493 --- /dev/null +++ b/toolchain/lower/testdata/packages/imported_type_completeness.carbon @@ -0,0 +1,390 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/packages/imported_type_completeness.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/packages/imported_type_completeness.carbon + +// --- require_completion_in_call.carbon + +library "[[@TEST_NAME]]"; + +class C(T:! type) { + // Core.Int(123) is required to be complete here. + fn F() -> Core.Int(123) { + return 123; + } + + // Core.Int(456) is required to be complete here. + fn G(unused t: T) -> Core.Int(456) { + return 456; + } +} + +fn G(T:! type, t: T) { + // Core.Int(123) is also required to be complete here. But because it's not + // dependent, we don't require it to be complete when forming a specific for G. + ({} as C(T)).F(); + + // Same for Core.Int(456). + ({} as C(T)).G(t); +} + +// --- use_without_completion_in_call.carbon + +library "[[@TEST_NAME]]"; + +import library "require_completion_in_call"; + +fn I() { + // OK, even though nothing in the compilation of this file requires i123 or + // i456 to be complete. + G((), ()); +} + +// --- require_completion_in_vtable.carbon + +library "[[@TEST_NAME]]"; + +base class C(T:! type) { + // Require Core.Int(11) and Core.Int(12) to be complete. + virtual fn F[unused self: Self]() -> Core.Int(11) { return 11; } + virtual fn G[unused self: Self](unused t: T) -> Core.Int(12) { return 12; } +} + +// --- use_without_completion_in_vtable.carbon + +library "[[@TEST_NAME]]"; + +import library "require_completion_in_vtable"; + +// Trigger emission of C({})'s vtable without requiring Core.Int(N) to be +// complete in this file. +// TODO: This currently emits an undefined external reference to the vtable! +var v: C({}) = {}; + +// --- require_completion_in_virtual_call.carbon + +library "[[@TEST_NAME]]"; + +import library "require_completion_in_vtable"; + +fn F(T:! type, t: T) { + var c: C(T) = {}; + c.F(); + c.G(t); +} + +// --- use_without_completion_in_virtual_call.carbon + +library "[[@TEST_NAME]]"; + +import library "require_completion_in_virtual_call"; + +class C {} + +fn CallF() { + F(C, {} as C); +} + +// CHECK:STDOUT: ; ModuleID = 'require_completion_in_call.carbon' +// CHECK:STDOUT: source_filename = "require_completion_in_call.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "require_completion_in_call.carbon", directory: "") +// CHECK:STDOUT: ; ModuleID = 'use_without_completion_in_call.carbon' +// CHECK:STDOUT: source_filename = "use_without_completion_in_call.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @C.val.7d0 = internal constant {} zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CI.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CG.Main.e43630e9a6c38c3f(), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.30c7a4d8cf1d8b9f:core.Destroy.Core"(ptr %self) #0 !dbg !9 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr void @_CG.Main.e43630e9a6c38c3f() #0 !dbg !17 { +// CHECK:STDOUT: %temp = alloca {}, align 8, !dbg !18 +// CHECK:STDOUT: %temp1 = alloca {}, align 8, !dbg !19 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %temp), !dbg !18 +// CHECK:STDOUT: %1 = call i123 @_CF.C.Main.e43630e9a6c38c3f(), !dbg !20 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %temp1), !dbg !19 +// CHECK:STDOUT: %2 = call i456 @_CG.C.Main.e43630e9a6c38c3f(), !dbg !21 +// CHECK:STDOUT: call void @"_COp.30c7a4d8cf1d8b9f:core.Destroy.Core"(ptr @C.val.7d0), !dbg !19 +// CHECK:STDOUT: call void @"_COp.30c7a4d8cf1d8b9f:core.Destroy.Core"(ptr @C.val.7d0), !dbg !18 +// CHECK:STDOUT: ret void, !dbg !22 +// 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)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i123 @_CF.C.Main.e43630e9a6c38c3f() #0 !dbg !23 { +// CHECK:STDOUT: ret i123 123, !dbg !27 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i456 @_CG.C.Main.e43630e9a6c38c3f() #0 !dbg !28 { +// CHECK:STDOUT: ret i456 456, !dbg !32 +// CHECK:STDOUT: } +// 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 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "use_without_completion_in_call.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "I", linkageName: "_CI.Main", scope: null, file: !3, line: 6, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 1, scope: !4) +// CHECK:STDOUT: !9 = distinct !DISubprogram(name: "Op", linkageName: "_COp.30c7a4d8cf1d8b9f:core.Destroy.Core", scope: null, file: !10, line: 22, type: !11, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !14) +// CHECK:STDOUT: !10 = !DIFile(filename: "require_completion_in_call.carbon", directory: "") +// CHECK:STDOUT: !11 = !DISubroutineType(types: !12) +// CHECK:STDOUT: !12 = !{null, !13} +// CHECK:STDOUT: !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !14 = !{!15} +// CHECK:STDOUT: !15 = !DILocalVariable(arg: 1, scope: !9, type: !13) +// CHECK:STDOUT: !16 = !DILocation(line: 22, column: 4, scope: !9) +// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main.e43630e9a6c38c3f", scope: null, file: !10, line: 16, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !18 = !DILocation(line: 19, column: 4, scope: !17) +// CHECK:STDOUT: !19 = !DILocation(line: 22, column: 4, scope: !17) +// CHECK:STDOUT: !20 = !DILocation(line: 19, column: 3, scope: !17) +// CHECK:STDOUT: !21 = !DILocation(line: 22, column: 3, scope: !17) +// CHECK:STDOUT: !22 = !DILocation(line: 16, column: 1, scope: !17) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "F", linkageName: "_CF.C.Main.e43630e9a6c38c3f", scope: null, file: !10, line: 6, type: !24, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !24 = !DISubroutineType(types: !25) +// CHECK:STDOUT: !25 = !{!26} +// CHECK:STDOUT: !26 = !DIBasicType(name: "int", size: 123, encoding: DW_ATE_signed) +// CHECK:STDOUT: !27 = !DILocation(line: 7, column: 5, scope: !23) +// CHECK:STDOUT: !28 = distinct !DISubprogram(name: "G", linkageName: "_CG.C.Main.e43630e9a6c38c3f", scope: null, file: !10, line: 11, type: !29, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !29 = !DISubroutineType(types: !30) +// CHECK:STDOUT: !30 = !{!31} +// CHECK:STDOUT: !31 = !DIBasicType(name: "int", size: 456, encoding: DW_ATE_signed) +// CHECK:STDOUT: !32 = !DILocation(line: 12, column: 5, scope: !28) +// CHECK:STDOUT: ; ModuleID = 'require_completion_in_vtable.carbon' +// CHECK:STDOUT: source_filename = "require_completion_in_vtable.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "require_completion_in_vtable.carbon", directory: "") +// CHECK:STDOUT: ; ModuleID = 'use_without_completion_in_vtable.carbon' +// CHECK:STDOUT: source_filename = "use_without_completion_in_vtable.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @_Cv.Main = global { ptr } zeroinitializer +// CHECK:STDOUT: @"_CC.Main.$vtable.6944efcfbb6a8360" = external unnamed_addr constant ptr +// CHECK:STDOUT: @C.val.loc9_1 = internal constant { ptr } { ptr @"_CC.Main.$vtable.6944efcfbb6a8360" } +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 @_Cv.Main, ptr align 8 @C.val.loc9_1, i64 8, i1 false), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "use_without_completion_in_vtable.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !3, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 1, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 0, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'require_completion_in_virtual_call.carbon' +// CHECK:STDOUT: source_filename = "require_completion_in_virtual_call.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.a8f528cd70a29ff8:core.Destroy.Core"(ptr %self) #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "require_completion_in_virtual_call.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a8f528cd70a29ff8:core.Destroy.Core", scope: null, file: !3, line: 7, type: !5, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !8) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null, !7} +// CHECK:STDOUT: !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !8 = !{!9} +// CHECK:STDOUT: !9 = !DILocalVariable(arg: 1, scope: !4, type: !7) +// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'use_without_completion_in_virtual_call.carbon' +// CHECK:STDOUT: source_filename = "use_without_completion_in_virtual_call.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @C.val.4a3 = internal constant {} zeroinitializer +// CHECK:STDOUT: @"_CC.Main.$vtable.0c4ab795cec6cb27" = external unnamed_addr constant ptr +// CHECK:STDOUT: @C.val.4a3.loc9_11.3 = internal constant {} zeroinitializer +// CHECK:STDOUT: @C.val.011.anon = internal constant { ptr } { ptr @"_CC.Main.$vtable.0c4ab795cec6cb27" } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CCallF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc9_9.2.temp = alloca {}, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc9_9.2.temp), !dbg !7 +// CHECK:STDOUT: call void @_CF.Main.0c4ab795cec6cb27(ptr @C.val.4a3.loc9_11.3), !dbg !8 +// CHECK:STDOUT: call void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr @C.val.4a3), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !9 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr %self) #0 !dbg !10 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.a8f528cd70a29ff8:core.Destroy.Core"(ptr %self) #0 !dbg !17 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.e4f158e1c31bad9e:core.Destroy.Core"(ptr %self) #0 !dbg !22 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !25 +// 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)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr void @_CF.Main.0c4ab795cec6cb27(ptr %t) #0 !dbg !26 { +// CHECK:STDOUT: %1 = alloca { ptr }, align 8, !dbg !29 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %1), !dbg !29 +// CHECK:STDOUT: %vptr = getelementptr inbounds nuw { ptr }, ptr %1, i32 0, i32 0, !dbg !30 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %1, ptr align 8 @C.val.011.anon, i64 8, i1 false), !dbg !29 +// CHECK:STDOUT: %vtable = load ptr, ptr %1, align 8, !dbg !31 +// CHECK:STDOUT: %2 = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0), !dbg !31 +// CHECK:STDOUT: %3 = call i11 %2(ptr %1), !dbg !31 +// CHECK:STDOUT: %vtable1 = load ptr, ptr %1, align 8, !dbg !32 +// CHECK:STDOUT: %4 = call ptr @llvm.load.relative.i32(ptr %vtable1, i32 4), !dbg !32 +// CHECK:STDOUT: %5 = call i12 %4(ptr %1, ptr %t), !dbg !32 +// CHECK:STDOUT: call void @"_COp.e4f158e1c31bad9e:core.Destroy.Core"(ptr %1), !dbg !29 +// CHECK:STDOUT: ret void, !dbg !33 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i11 @_CF.C.Main.0c4ab795cec6cb27(ptr %self) #0 !dbg !34 { +// CHECK:STDOUT: ret i11 11, !dbg !41 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read) +// CHECK:STDOUT: declare ptr @llvm.load.relative.i32(ptr, i32) #3 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr i12 @_CG.C.Main.0c4ab795cec6cb27(ptr %self, ptr %t) #0 !dbg !42 { +// CHECK:STDOUT: ret i12 12, !dbg !49 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.load.relative.i32, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "use_without_completion_in_virtual_call.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "CallF", linkageName: "_CCallF.Main", scope: null, file: !3, line: 8, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 8, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 8, column: 1, scope: !4) +// CHECK:STDOUT: !10 = distinct !DISubprogram(name: "Op", linkageName: "_COp.71e25083d63c4d6c:core.Destroy.Core", scope: null, file: !3, line: 9, type: !11, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !14) +// CHECK:STDOUT: !11 = !DISubroutineType(types: !12) +// CHECK:STDOUT: !12 = !{null, !13} +// CHECK:STDOUT: !13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !14 = !{!15} +// CHECK:STDOUT: !15 = !DILocalVariable(arg: 1, scope: !10, type: !13) +// CHECK:STDOUT: !16 = !DILocation(line: 9, column: 8, scope: !10) +// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a8f528cd70a29ff8:core.Destroy.Core", scope: null, file: !18, line: 7, type: !11, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !19) +// CHECK:STDOUT: !18 = !DIFile(filename: "require_completion_in_virtual_call.carbon", directory: "") +// CHECK:STDOUT: !19 = !{!20} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !17, type: !13) +// CHECK:STDOUT: !21 = !DILocation(line: 7, column: 3, scope: !17) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "Op", linkageName: "_COp.e4f158e1c31bad9e:core.Destroy.Core", scope: null, file: !18, line: 7, type: !11, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !23) +// CHECK:STDOUT: !23 = !{!24} +// CHECK:STDOUT: !24 = !DILocalVariable(arg: 1, scope: !22, type: !13) +// CHECK:STDOUT: !25 = !DILocation(line: 7, column: 3, scope: !22) +// CHECK:STDOUT: !26 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main.0c4ab795cec6cb27", scope: null, file: !18, line: 6, type: !11, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !27) +// CHECK:STDOUT: !27 = !{!28} +// CHECK:STDOUT: !28 = !DILocalVariable(arg: 1, scope: !26, type: !13) +// CHECK:STDOUT: !29 = !DILocation(line: 7, column: 3, scope: !26) +// CHECK:STDOUT: !30 = !DILocation(line: 7, column: 17, scope: !26) +// CHECK:STDOUT: !31 = !DILocation(line: 8, column: 3, scope: !26) +// CHECK:STDOUT: !32 = !DILocation(line: 9, column: 3, scope: !26) +// CHECK:STDOUT: !33 = !DILocation(line: 6, column: 1, scope: !26) +// CHECK:STDOUT: !34 = distinct !DISubprogram(name: "F", linkageName: "_CF.C.Main.0c4ab795cec6cb27", scope: null, file: !35, line: 6, type: !36, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !39) +// CHECK:STDOUT: !35 = !DIFile(filename: "require_completion_in_vtable.carbon", directory: "") +// CHECK:STDOUT: !36 = !DISubroutineType(types: !37) +// CHECK:STDOUT: !37 = !{!38, !13} +// CHECK:STDOUT: !38 = !DIBasicType(name: "int", size: 11, encoding: DW_ATE_signed) +// CHECK:STDOUT: !39 = !{!40} +// CHECK:STDOUT: !40 = !DILocalVariable(arg: 1, scope: !34, type: !13) +// CHECK:STDOUT: !41 = !DILocation(line: 6, column: 55, scope: !34) +// CHECK:STDOUT: !42 = distinct !DISubprogram(name: "G", linkageName: "_CG.C.Main.0c4ab795cec6cb27", scope: null, file: !35, line: 7, type: !43, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !46) +// CHECK:STDOUT: !43 = !DISubroutineType(types: !44) +// CHECK:STDOUT: !44 = !{!45, !13, !13} +// CHECK:STDOUT: !45 = !DIBasicType(name: "int", size: 12, encoding: DW_ATE_signed) +// CHECK:STDOUT: !46 = !{!47, !48} +// CHECK:STDOUT: !47 = !DILocalVariable(arg: 1, scope: !42, type: !13) +// CHECK:STDOUT: !48 = !DILocalVariable(arg: 2, scope: !42, type: !13) +// CHECK:STDOUT: !49 = !DILocation(line: 7, column: 66, scope: !42)