diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index 1dae5dc3ae29..74cb43c17fac 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -115,7 +115,7 @@ auto PerformCall(Context& context, Parse::NodeId node_id, // If there is a return slot, build storage for the result. SemIR::InstId return_storage_id = SemIR::InstId::Invalid; - SemIR::ReturnInfo return_info = [&] { + SemIR::ReturnTypeInfo return_info = [&] { DiagnosticAnnotationScope annotate_diagnostics( &context.emitter(), [&](auto& builder) { CARBON_DIAGNOSTIC(IncompleteReturnTypeHere, Note, @@ -124,21 +124,23 @@ auto PerformCall(Context& context, Parse::NodeId node_id, }); return CheckFunctionReturnType(context, callee_id, callable, specific_id); }(); - switch (return_info.return_slot) { - case SemIR::ReturnSlot::Present: + switch (return_info.init_repr.kind) { + case SemIR::InitRepr::InPlace: // Tentatively put storage for a temporary in the function's return slot. // This will be replaced if necessary when we perform initialization. return_storage_id = context.AddInst( node_id, {.type_id = return_info.type_id}); break; - case SemIR::ReturnSlot::Absent: + case SemIR::InitRepr::None: // For functions with an implicit return type, the return type is the // empty tuple type. if (!return_info.type_id.is_valid()) { return_info.type_id = context.GetTupleType({}); } break; - case SemIR::ReturnSlot::Incomplete: + case SemIR::InitRepr::ByCopy: + break; + case SemIR::InitRepr::Incomplete: // Don't form an initializing expression with an incomplete type. // CheckFunctionReturnType will have diagnosed this for us if needed. return_info.type_id = SemIR::TypeId::Error; diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 850403adf555..307f81dc06c0 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -54,7 +54,8 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir, return init.dest_id; } case CARBON_KIND(SemIR::Call call): { - if (!SemIR::GetInitRepr(sem_ir, call.type_id).has_return_slot()) { + if (!SemIR::ReturnTypeInfo::ForType(sem_ir, call.type_id) + .has_return_slot()) { return SemIR::InstId::Invalid; } if (!call.args_id.is_valid()) { @@ -332,7 +333,7 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type, bool is_init = target.is_initializer(); ConversionTarget::Kind inner_kind = !is_init ? ConversionTarget::Value - : SemIR::GetInitRepr(sem_ir, target.type_id).kind == + : SemIR::InitRepr::ForType(sem_ir, target.type_id).kind == SemIR::InitRepr::InPlace ? ConversionTarget::FullInitializer : ConversionTarget::Initializer; @@ -433,7 +434,7 @@ static auto ConvertStructToStructOrClass(Context& context, bool is_init = target.is_initializer(); ConversionTarget::Kind inner_kind = !is_init ? ConversionTarget::Value - : SemIR::GetInitRepr(sem_ir, target.type_id).kind == + : SemIR::InitRepr::ForType(sem_ir, target.type_id).kind == SemIR::InitRepr::InPlace ? ConversionTarget::FullInitializer : ConversionTarget::Initializer; @@ -734,9 +735,9 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id, if (value_cat == SemIR::ExprCategory::Initializing && IsValidExprCategoryForConversionTarget(SemIR::ExprCategory::Value, target.kind) && - SemIR::GetInitRepr(sem_ir, value_type_id).kind == + SemIR::InitRepr::ForType(sem_ir, value_type_id).kind == SemIR::InitRepr::ByCopy) { - auto value_rep = SemIR::GetValueRepr(sem_ir, value_type_id); + auto value_rep = SemIR::ValueRepr::ForType(sem_ir, value_type_id); if (value_rep.kind == SemIR::ValueRepr::Copy && value_rep.type_id == value_type_id) { // The initializer produces an object representation by copy, and the @@ -890,7 +891,7 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id) // TODO: Directly track on the value representation whether it's a copy of // the object representation. - auto value_rep = SemIR::GetValueRepr(context.sem_ir(), type_id); + auto value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), type_id); if (value_rep.kind == SemIR::ValueRepr::Copy && value_rep.aggregate_kind == SemIR::ValueRepr::NotAggregate && value_rep.type_id == type_id) { @@ -1049,7 +1050,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, // Perform a final destination store, if necessary. if (target.kind == ConversionTarget::FullInitializer) { - if (auto init_rep = SemIR::GetInitRepr(sem_ir, target.type_id); + if (auto init_rep = SemIR::InitRepr::ForType(sem_ir, target.type_id); init_rep.kind == SemIR::InitRepr::ByCopy) { target.init_block->InsertHere(); expr_id = context.AddInst( diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 4518b5ab9b3d..6dc12f505c34 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -73,12 +73,13 @@ auto CheckFunctionTypeMatches(Context& context, auto CheckFunctionReturnType(Context& context, SemIRLoc loc, SemIR::Function& function, SemIR::SpecificId specific_id) - -> SemIR::ReturnInfo { - auto return_info = function.GetReturnInfo(context.sem_ir(), specific_id); + -> SemIR::ReturnTypeInfo { + auto return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), + function, specific_id); // If we couldn't determine the return information due to the return type // being incomplete, try to complete it now. - if (return_info.return_slot == SemIR::ReturnSlot::Incomplete) { + if (return_info.init_repr.kind == SemIR::InitRepr::Incomplete) { auto diagnose_incomplete_return_type = [&] { CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, "Function returns incomplete type `{0}`.", @@ -91,7 +92,8 @@ auto CheckFunctionReturnType(Context& context, SemIRLoc loc, // definition or call to this function. if (context.TryToCompleteType(return_info.type_id, diagnose_incomplete_return_type)) { - return_info = function.GetReturnInfo(context.sem_ir(), specific_id); + return_info = SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), + function, specific_id); } } diff --git a/toolchain/check/function.h b/toolchain/check/function.h index bfc76ac1010a..a2d7a392001d 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -42,7 +42,7 @@ auto CheckFunctionTypeMatches(Context& context, auto CheckFunctionReturnType(Context& context, SemIRLoc loc, SemIR::Function& function, SemIR::SpecificId specific_id) - -> SemIR::ReturnInfo; + -> SemIR::ReturnTypeInfo; } // namespace Carbon::Check diff --git a/toolchain/check/return.cpp b/toolchain/check/return.cpp index 07e50cf6eb25..f02513711b5e 100644 --- a/toolchain/check/return.cpp +++ b/toolchain/check/return.cpp @@ -57,7 +57,8 @@ auto CheckReturnedVar(Context& context, Parse::NodeId returned_node, Parse::NodeId type_node, SemIR::TypeId type_id) -> SemIR::InstId { auto& function = GetCurrentFunction(context); - auto return_info = function.GetReturnInfo(context.sem_ir()); + auto return_info = + SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function); if (!return_info.is_valid()) { // We already diagnosed this when we started defining the function. Create a // placeholder for error recovery. @@ -131,7 +132,8 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id, const auto& function = GetCurrentFunction(context); auto returned_var_id = GetCurrentReturnedVar(context); auto return_slot_id = SemIR::InstId::Invalid; - auto return_info = function.GetReturnInfo(context.sem_ir()); + auto return_info = + SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function); if (!return_info.type_id.is_valid()) { CARBON_DIAGNOSTIC( @@ -178,7 +180,8 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) } auto return_slot_id = function.return_storage_id; - if (!function.GetReturnInfo(context.sem_ir()).has_return_slot()) { + if (!SemIR::ReturnTypeInfo::ForFunction(context.sem_ir(), function) + .has_return_slot()) { // If we don't have a return slot, we're returning by value. Convert to a // value expression. returned_var_id = ConvertToValueExpr(context, returned_var_id); diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index af1099663c76..ae264c64677d 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -82,7 +82,7 @@ auto FileContext::GetGlobal(SemIR::InstId inst_id) -> llvm::Value* { // If we want a pointer to the constant, materialize a global to hold it. // TODO: We could reuse the same global if the constant is used more than // once. - auto value_rep = SemIR::GetValueRepr(sem_ir(), inst.type_id()); + auto value_rep = SemIR::ValueRepr::ForType(sem_ir(), inst.type_id()); if (value_rep.kind == SemIR::ValueRepr::Pointer) { // Include both the name of the constant, if any, and the point of use in // the name of the variable. @@ -143,7 +143,8 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) // TODO: Pass in a specific ID for generic functions. const auto specific_id = SemIR::SpecificId::Invalid; - const auto return_info = function.GetReturnInfo(sem_ir(), specific_id); + const auto return_info = + SemIR::ReturnTypeInfo::ForFunction(sem_ir(), function, specific_id); CARBON_CHECK(return_info.is_valid()) << "Should not lower invalid functions."; auto implicit_param_refs = @@ -153,11 +154,6 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) auto* return_type = return_info.type_id.is_valid() ? GetType(return_info.type_id) : nullptr; - SemIR::InitRepr return_rep = - return_info.type_id.is_valid() - ? SemIR::GetInitRepr(sem_ir(), return_info.type_id) - : SemIR::InitRepr{.kind = SemIR::InitRepr::None}; - CARBON_CHECK(return_rep.has_return_slot() == return_info.has_return_slot()); llvm::SmallVector param_types; // TODO: Consider either storing `param_inst_ids` somewhere so that we can @@ -178,7 +174,7 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) auto param_type_id = SemIR::Function::GetParamFromParamRefId(sem_ir(), param_ref_id) .second.type_id; - switch (auto value_rep = SemIR::GetValueRepr(sem_ir(), param_type_id); + switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir(), param_type_id); value_rep.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL() @@ -197,7 +193,7 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id) // Compute the return type to use for the LLVM function. If the initializing // representation doesn't produce a value, set the return type to void. llvm::Type* function_return_type = - return_rep.kind == SemIR::InitRepr::ByCopy + return_info.init_repr.kind == SemIR::InitRepr::ByCopy ? return_type : llvm::Type::getVoidTy(llvm_context()); @@ -267,7 +263,8 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) sem_ir().inst_blocks().GetOrEmpty(function.implicit_param_refs_id); auto param_refs = sem_ir().inst_blocks().GetOrEmpty(function.param_refs_id); int param_index = 0; - if (function.GetReturnInfo(sem_ir(), specific_id).has_return_slot()) { + if (SemIR::ReturnTypeInfo::ForFunction(sem_ir(), function, specific_id) + .has_return_slot()) { function_lowering.SetLocal(function.return_storage_id, llvm_function->getArg(param_index)); ++param_index; @@ -280,7 +277,7 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) // Get the value of the parameter from the function argument. auto param_type_id = param.type_id; llvm::Value* param_value = llvm::PoisonValue::get(GetType(param_type_id)); - if (SemIR::GetValueRepr(sem_ir(), param_type_id).kind != + if (SemIR::ValueRepr::ForType(sem_ir(), param_type_id).kind != SemIR::ValueRepr::None) { param_value = llvm_function->getArg(param_index); ++param_index; diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 4a8400c56dc6..858c0b3a3cfe 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -133,7 +133,7 @@ auto FunctionContext::MakeSyntheticBlock() -> llvm::BasicBlock* { auto FunctionContext::FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id, SemIR::InstId source_id) -> void { - switch (SemIR::GetInitRepr(sem_ir(), type_id).kind) { + switch (SemIR::InitRepr::ForType(sem_ir(), type_id).kind) { case SemIR::InitRepr::None: break; case SemIR::InitRepr::InPlace: @@ -146,12 +146,15 @@ auto FunctionContext::FinishInit(SemIR::TypeId type_id, SemIR::InstId dest_id, case SemIR::InitRepr::ByCopy: CopyValue(type_id, source_id, dest_id); break; + case SemIR::InitRepr::Incomplete: + CARBON_FATAL() << "Lowering aggregate initialization of incomplete type " + << sem_ir().types().GetAsInst(type_id); } } auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id, SemIR::InstId dest_id) -> void { - switch (auto rep = SemIR::GetValueRepr(sem_ir(), type_id); rep.kind) { + switch (auto rep = SemIR::ValueRepr::ForType(sem_ir(), type_id); rep.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL() << "Attempt to copy incomplete type"; case SemIR::ValueRepr::None: diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 38140e144c78..656bec92d891 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -194,7 +194,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, SemIR::ReturnExpr inst) -> void { auto result_type_id = context.sem_ir().insts().Get(inst.expr_id).type_id(); - switch (SemIR::GetInitRepr(context.sem_ir(), result_type_id).kind) { + switch (SemIR::InitRepr::ForType(context.sem_ir(), result_type_id).kind) { case SemIR::InitRepr::None: // Nothing to return. context.builder().CreateRetVoid(); @@ -207,6 +207,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId /*inst_id*/, // The expression produces the value representation for the type. context.builder().CreateRet(context.GetValue(inst.expr_id)); return; + case SemIR::InitRepr::Incomplete: + CARBON_FATAL() << "Lowering return of incomplete type " + << context.sem_ir().types().GetAsInst(result_type_id); } } diff --git a/toolchain/lower/handle_aggregates.cpp b/toolchain/lower/handle_aggregates.cpp index c94958c7ba28..40fac01fa2e7 100644 --- a/toolchain/lower/handle_aggregates.cpp +++ b/toolchain/lower/handle_aggregates.cpp @@ -38,7 +38,7 @@ static auto GetAggregateElement(FunctionContext& context, case SemIR::ExprCategory::Value: { auto value_rep = - SemIR::GetValueRepr(context.sem_ir(), aggr_inst.type_id()); + SemIR::ValueRepr::ForType(context.sem_ir(), aggr_inst.type_id()); CARBON_CHECK(value_rep.aggregate_kind != SemIR::ValueRepr::NotAggregate) << "aggregate type should have aggregate value representation"; switch (value_rep.kind) { @@ -67,7 +67,8 @@ static auto GetAggregateElement(FunctionContext& context, // `elem_ptr` points to a value representation. Load it. auto result_value_type_id = - SemIR::GetValueRepr(context.sem_ir(), result_type_id).type_id; + SemIR::ValueRepr::ForType(context.sem_ir(), result_type_id) + .type_id; return context.builder().CreateLoad( context.GetType(result_value_type_id), elem_ptr, name + ".load"); } @@ -123,7 +124,7 @@ static auto EmitAggregateInitializer(FunctionContext& context, llvm::Twine name) -> llvm::Value* { auto* llvm_type = context.GetType(type_id); - switch (SemIR::GetInitRepr(context.sem_ir(), type_id).kind) { + switch (SemIR::InitRepr::ForType(context.sem_ir(), type_id).kind) { case SemIR::InitRepr::None: case SemIR::InitRepr::InPlace: // TODO: Add a helper to poison a value slot. @@ -139,6 +140,10 @@ static auto EmitAggregateInitializer(FunctionContext& context, llvm::PoisonValue::get(llvm_type), context.GetValue(refs[0]), {0}, name); } + + case SemIR::InitRepr::Incomplete: + CARBON_FATAL() << "Lowering aggregate initialization of incomplete type " + << context.sem_ir().types().GetAsInst(type_id); } } @@ -169,7 +174,7 @@ auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, static auto EmitAggregateValueRepr(FunctionContext& context, SemIR::TypeId type_id, SemIR::InstBlockId refs_id) -> llvm::Value* { - auto value_rep = SemIR::GetValueRepr(context.sem_ir(), type_id); + auto value_rep = SemIR::ValueRepr::ForType(context.sem_ir(), type_id); switch (value_rep.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL() << "Incomplete aggregate type in lowering"; diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 4e2685548fc6..d3281f1186d8 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -312,14 +312,15 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, std::vector args; - if (SemIR::GetInitRepr(context.sem_ir(), inst.type_id).has_return_slot()) { + if (SemIR::ReturnTypeInfo::ForType(context.sem_ir(), inst.type_id) + .has_return_slot()) { args.push_back(context.GetValue(arg_ids.back())); arg_ids = arg_ids.drop_back(); } for (auto arg_id : arg_ids) { auto arg_type_id = context.sem_ir().insts().Get(arg_id).type_id(); - if (SemIR::GetValueRepr(context.sem_ir(), arg_type_id).kind != + if (SemIR::ValueRepr::ForType(context.sem_ir(), arg_type_id).kind != SemIR::ValueRepr::None) { args.push_back(context.GetValue(arg_id)); } diff --git a/toolchain/lower/handle_expr_category.cpp b/toolchain/lower/handle_expr_category.cpp index c19d24f6239f..7a6739229a6b 100644 --- a/toolchain/lower/handle_expr_category.cpp +++ b/toolchain/lower/handle_expr_category.cpp @@ -9,7 +9,7 @@ namespace Carbon::Lower { auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::BindValue inst) -> void { - switch (auto rep = SemIR::GetValueRepr(context.sem_ir(), inst.type_id); + switch (auto rep = SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id); rep.kind) { case SemIR::ValueRepr::Unknown: CARBON_FATAL() @@ -51,7 +51,7 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ValueAsRef inst) -> void { CARBON_CHECK(SemIR::GetExprCategory(context.sem_ir(), inst.value_id) == SemIR::ExprCategory::Value); - CARBON_CHECK(SemIR::GetValueRepr(context.sem_ir(), inst.type_id).kind == + CARBON_CHECK(SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id).kind == SemIR::ValueRepr::Pointer); context.SetLocal(inst_id, context.GetValue(inst.value_id)); } @@ -60,9 +60,9 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::ValueOfInitializer inst) -> void { CARBON_CHECK(SemIR::GetExprCategory(context.sem_ir(), inst.init_id) == SemIR::ExprCategory::Initializing); - CARBON_CHECK(SemIR::GetValueRepr(context.sem_ir(), inst.type_id).kind == + CARBON_CHECK(SemIR::ValueRepr::ForType(context.sem_ir(), inst.type_id).kind == SemIR::ValueRepr::Copy); - CARBON_CHECK(SemIR::GetInitRepr(context.sem_ir(), inst.type_id).kind == + CARBON_CHECK(SemIR::InitRepr::ForType(context.sem_ir(), inst.type_id).kind == SemIR::InitRepr::ByCopy); context.SetLocal(inst_id, context.GetValue(inst.init_id)); } diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 8c6e3155deea..7d6294368d35 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -88,6 +88,7 @@ cc_library( "function.cpp", "generic.cpp", "name.cpp", + "type_info.cpp", ], hdrs = [ "builtin_function_kind.h", @@ -105,6 +106,7 @@ cc_library( "name.h", "name_scope.h", "type.h", + "type_info.h", ], textual_hdrs = [ "builtin_function_kind.def", @@ -115,12 +117,12 @@ cc_library( ":ids", ":inst", ":inst_kind", - ":type_info", "//common:check", "//common:enum_base", "//common:error", "//common:hashing", "//common:map", + "//common:ostream", "//common:set", "//toolchain/base:kind_switch", "//toolchain/base:value_store", @@ -176,16 +178,6 @@ cc_library( ], ) -cc_library( - name = "type_info", - hdrs = ["type_info.h"], - deps = [ - ":ids", - ":inst", - "//common:ostream", - ], -) - cc_test( name = "typed_insts_test", size = "small", diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index dec354c32a7d..caa33caf40b7 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -19,32 +19,6 @@ namespace Carbon::SemIR { -auto ValueRepr::Print(llvm::raw_ostream& out) const -> void { - out << "{kind: "; - switch (kind) { - case Unknown: - out << "unknown"; - break; - case None: - out << "none"; - break; - case Copy: - out << "copy"; - break; - case Pointer: - out << "pointer"; - break; - case Custom: - out << "custom"; - break; - } - out << ", type: " << type_id << "}"; -} - -auto CompleteTypeInfo::Print(llvm::raw_ostream& out) const -> void { - out << "{value_rep: " << value_repr << "}"; -} - File::File(CheckIRId check_ir_id, IdentifierId package_id, StringLiteralValueId library_id, SharedValueStores& value_stores, std::string filename) @@ -690,26 +664,4 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { } } -auto GetInitRepr(const File& file, TypeId type_id) -> InitRepr { - auto value_rep = GetValueRepr(file, type_id); - switch (value_rep.kind) { - case ValueRepr::None: - return {.kind = InitRepr::None}; - - case ValueRepr::Copy: - // TODO: Use in-place initialization for types that have non-trivial - // destructive move. - return {.kind = InitRepr::ByCopy}; - - case ValueRepr::Pointer: - case ValueRepr::Custom: - return {.kind = InitRepr::InPlace}; - - case ValueRepr::Unknown: - CARBON_FATAL() - << "Attempting to perform initialization of incomplete type " - << file.types().GetAsInst(type_id); - } -} - } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index d20e47879458..e2ea1d3a6ef2 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -282,37 +282,6 @@ enum class ExprCategory : int8_t { // Returns the expression category for an instruction. auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory; -// Returns information about the value representation to use for a type. -inline auto GetValueRepr(const File& file, TypeId type_id) -> ValueRepr { - return file.types().GetValueRepr(type_id); -} - -// The initializing representation to use when returning by value. -struct InitRepr { - enum Kind : int8_t { - // The type has no initializing representation. This is used for empty - // types, where no initialization is necessary. - None, - // An initializing expression produces an object representation by value, - // which is copied into the initialized object. - ByCopy, - // An initializing expression takes a location as input, which is - // initialized as a side effect of evaluating the expression. - InPlace, - // TODO: Consider adding a kind where the expression takes an advisory - // location and returns a value plus an indicator of whether the location - // was actually initialized. - }; - // The kind of initializing representation used by this type. - Kind kind; - - // Returns whether a return slot is used when returning this type. - auto has_return_slot() const -> bool { return kind == InPlace; } -}; - -// Returns information about the initializing representation to use for a type. -auto GetInitRepr(const File& file, TypeId type_id) -> InitRepr; - } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_FILE_H_ diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 06da042dae7b..2a68477ef3a5 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -306,7 +306,7 @@ class FormatterImpl { if (fn.return_storage_id.is_valid()) { out_ << " -> "; - auto return_info = fn.GetReturnInfo(sem_ir_); + auto return_info = ReturnTypeInfo::ForFunction(sem_ir_, fn); if (!fn.body_block_ids.empty() && return_info.is_valid() && return_info.has_return_slot()) { FormatName(fn.return_storage_id); @@ -712,7 +712,7 @@ class FormatterImpl { llvm::ArrayRef args = sem_ir_.inst_blocks().Get(inst.args_id); - auto return_info = ReturnInfo::ForType(sem_ir_, inst.type_id); + auto return_info = ReturnTypeInfo::ForType(sem_ir_, inst.type_id); bool has_return_slot = return_info.has_return_slot(); InstId return_slot_id = InstId::Invalid; if (has_return_slot) { diff --git a/toolchain/sem_ir/function.cpp b/toolchain/sem_ir/function.cpp index e62685314876..2b667b9ba35d 100644 --- a/toolchain/sem_ir/function.cpp +++ b/toolchain/sem_ir/function.cpp @@ -68,20 +68,4 @@ auto Function::GetDeclaredReturnType(const File& file, file.insts().Get(return_storage_id).type_id()); } -auto ReturnInfo::ForType(const File& file, TypeId type_id) -> ReturnInfo { - if (!type_id.is_valid()) { - // Implicit `-> ()` has no return slot. - return {.type_id = type_id, .return_slot = ReturnSlot::Absent}; - } - - if (!file.types().IsComplete(type_id)) { - return {.type_id = type_id, .return_slot = ReturnSlot::Incomplete}; - } - - return {.type_id = type_id, - .return_slot = GetInitRepr(file, type_id).has_return_slot() - ? SemIR::ReturnSlot::Present - : SemIR::ReturnSlot::Absent}; -} - } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/function.h b/toolchain/sem_ir/function.h index 5bc1e824f489..cb8dfc621672 100644 --- a/toolchain/sem_ir/function.h +++ b/toolchain/sem_ir/function.h @@ -12,41 +12,6 @@ namespace Carbon::SemIR { -// A value that describes whether the function uses a return slot. -enum class ReturnSlot : int8_t { - // The function is known to not use a return slot. - Absent, - // The function has a return slot, and a call to the function is expected to - // have an additional final argument corresponding to the return slot. - Present, - // Computing whether the function should have a return slot failed because - // the return type was incomplete. - Incomplete, -}; - -// Information about how a function returns its return value. -struct ReturnInfo { - // Builds return information for a given declared return type. - static auto ForType(const File& file, TypeId type_id) -> ReturnInfo; - - // Returns whether the return information could be fully computed. - auto is_valid() const -> bool { - return return_slot != ReturnSlot::Incomplete; - } - - // Returns whether the function has a return slot. Can only be called for - // valid return info. - auto has_return_slot() const -> bool { - CARBON_CHECK(is_valid()); - return return_slot == ReturnSlot::Present; - } - - // The return type. Invalid if no return type was specified. - TypeId type_id; - // The return slot usage for this function. - ReturnSlot return_slot; -}; - // Function-specific fields. struct FunctionFields { // The following members always have values, and do not change throughout the @@ -107,13 +72,6 @@ struct Function : public EntityWithParamsBase, auto GetDeclaredReturnType(const File& file, SpecificId specific_id = SpecificId::Invalid) const -> TypeId; - - // Returns information about how the function returns its return value. - auto GetReturnInfo(const File& file, - SpecificId specific_id = SpecificId::Invalid) const - -> ReturnInfo { - return ReturnInfo::ForType(file, GetDeclaredReturnType(file, specific_id)); - } }; class File; diff --git a/toolchain/sem_ir/type_info.cpp b/toolchain/sem_ir/type_info.cpp new file mode 100644 index 000000000000..bc42360ef279 --- /dev/null +++ b/toolchain/sem_ir/type_info.cpp @@ -0,0 +1,61 @@ +// 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 "toolchain/sem_ir/type_info.h" + +#include "toolchain/sem_ir/file.h" + +namespace Carbon::SemIR { + +auto ValueRepr::Print(llvm::raw_ostream& out) const -> void { + out << "{kind: "; + switch (kind) { + case Unknown: + out << "unknown"; + break; + case None: + out << "none"; + break; + case Copy: + out << "copy"; + break; + case Pointer: + out << "pointer"; + break; + case Custom: + out << "custom"; + break; + } + out << ", type: " << type_id << "}"; +} + +auto CompleteTypeInfo::Print(llvm::raw_ostream& out) const -> void { + out << "{value_rep: " << value_repr << "}"; +} + +auto ValueRepr::ForType(const File& file, TypeId type_id) -> ValueRepr { + return file.types().GetValueRepr(type_id); +} + +auto InitRepr::ForType(const File& file, TypeId type_id) -> InitRepr { + auto value_rep = ValueRepr::ForType(file, type_id); + switch (value_rep.kind) { + case ValueRepr::None: + return {.kind = InitRepr::None}; + + case ValueRepr::Copy: + // TODO: Use in-place initialization for types that have non-trivial + // destructive move. + return {.kind = InitRepr::ByCopy}; + + case ValueRepr::Pointer: + case ValueRepr::Custom: + return {.kind = InitRepr::InPlace}; + + case ValueRepr::Unknown: + return {.kind = InitRepr::Incomplete}; + } +} + +} // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/type_info.h b/toolchain/sem_ir/type_info.h index a10505cdca47..aa5b00999d5e 100644 --- a/toolchain/sem_ir/type_info.h +++ b/toolchain/sem_ir/type_info.h @@ -6,12 +6,16 @@ #define CARBON_TOOLCHAIN_SEM_IR_TYPE_INFO_H_ #include "common/ostream.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::SemIR { // The value representation to use when passing by value. struct ValueRepr : public Printable { + // Returns information about the value representation to use for a type. + static auto ForType(const File& file, TypeId type_id) -> ValueRepr; + auto Print(llvm::raw_ostream& out) const -> void; enum Kind : int8_t { @@ -71,6 +75,69 @@ struct CompleteTypeInfo : public Printable { ValueRepr value_repr = ValueRepr(); }; +// The initializing representation to use when returning by value. +struct InitRepr { + // Returns information about the initializing representation to use for a + // type. + static auto ForType(const File& file, TypeId type_id) -> InitRepr; + + enum Kind : int8_t { + // The type has no initializing representation. This is used for empty + // types, where no initialization is necessary. + None, + // An initializing expression produces an object representation by value, + // which is copied into the initialized object. + ByCopy, + // An initializing expression takes a location as input, which is + // initialized as a side effect of evaluating the expression. + InPlace, + // No initializing expressions should exist because the type is not + // complete. + Incomplete, + // TODO: Consider adding a kind where the expression takes an advisory + // location and returns a value plus an indicator of whether the location + // was actually initialized. + }; + // The kind of initializing representation used by this type. + Kind kind; + + // Returns whether the initializing representation information could be fully + // computed. + auto is_valid() const -> bool { return kind != Incomplete; } +}; + +// Information about a function's return type. +struct ReturnTypeInfo { + // Builds return type information for a given declared return type. + static auto ForType(const File& file, TypeId type_id) -> ReturnTypeInfo { + return {.type_id = type_id, + .init_repr = type_id.is_valid() ? InitRepr::ForType(file, type_id) + : InitRepr{.kind = InitRepr::None}}; + } + + // Builds return type information for a given function. + static auto ForFunction(const File& file, const Function& function, + SpecificId specific_id = SpecificId::Invalid) + -> ReturnTypeInfo { + return ForType(file, function.GetDeclaredReturnType(file, specific_id)); + } + + // Returns whether the return information could be fully computed. + auto is_valid() const -> bool { return init_repr.is_valid(); } + + // Returns whether a function with this return type has a return slot. Can + // only be called for valid return info. + auto has_return_slot() const -> bool { + CARBON_CHECK(is_valid()); + return init_repr.kind == InitRepr::InPlace; + } + + // The declared return type. Invalid if no return type was specified. + TypeId type_id; + // The initializing representation for the return type. + InitRepr init_repr; +}; + } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_TYPE_INFO_H_