Просмотр исходного кода

Refactor `ReturnTypeInfo` and `InitRepr`. (#4169)

Rename `ReturnInfo` to `ReturnTypeInfo`. Move it and `InitRepr` into
`type_info.h` alongside `ValueRepr`. Replace `ReturnSlot` with
`InitRepr`, and extend `InitRepr` to be able to represent the
incomplete-type case instead of CHECK-failing. Remove `has_return_slot`
from `InitRepr` and instead only provide that as part of
`ReturnTypeInfo`.
Richard Smith 1 год назад
Родитель
Сommit
37a8bfa488

+ 7 - 5
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<SemIR::TemporaryStorage>(
           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;

+ 8 - 7
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<SemIR::InitializeFrom>(

+ 6 - 4
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);
     }
   }
 

+ 1 - 1
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
 

+ 6 - 3
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);

+ 8 - 11
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<llvm::Type*> 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;

+ 5 - 2
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:

+ 4 - 1
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);
   }
 }
 

+ 9 - 4
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";

+ 3 - 2
toolchain/lower/handle_call.cpp

@@ -312,14 +312,15 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
 
   std::vector<llvm::Value*> 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));
     }

+ 4 - 4
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));
 }

+ 3 - 11
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",

+ 0 - 48
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

+ 0 - 31
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_

+ 2 - 2
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<InstId> 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) {

+ 0 - 16
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

+ 0 - 42
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;

+ 61 - 0
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

+ 67 - 0
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<ValueRepr> {
+  // 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<CompleteTypeInfo> {
   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_