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`.
This commit is contained in:
Richard Smith
2024-07-25 21:41:33 +00:00
committed by GitHub
parent 2ef1d1f8b9
commit 37a8bfa488
19 changed files with 194 additions and 194 deletions
+7 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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",
-48
View File
@@ -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
-31
View File
@@ -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
View File
@@ -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) {
-16
View File
@@ -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
-42
View File
@@ -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
View File
@@ -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
View File
@@ -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_