From 1d6298290f59ec1a938289573b77c8ba1b931ad0 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Tue, 24 Oct 2023 11:23:40 -0700 Subject: [PATCH] Add more value store types to File. (#3317) Finishing what #3316 started, add more bespoke ValueStore-like structures to File. With this, the things which previously had somewhat boilerplate Add/Get functions are now all on side classes, giving a uniform style of API for calling. Note, I was on the fence about making things public on ValueStore. If it's preferred that I make some things there protected I certainly can, there's just a trade-off that may mean more distinct child/wrapper types. --- toolchain/base/value_store.h | 32 +++- toolchain/check/context.cpp | 70 +++++---- toolchain/check/convert.cpp | 91 +++++------ toolchain/check/declaration_name_stack.cpp | 7 +- toolchain/check/handle_array.cpp | 2 +- toolchain/check/handle_call_expression.cpp | 4 +- toolchain/check/handle_class.cpp | 10 +- toolchain/check/handle_function.cpp | 22 +-- toolchain/check/handle_if_expression.cpp | 3 +- toolchain/check/handle_index.cpp | 8 +- toolchain/check/handle_let.cpp | 4 +- toolchain/check/handle_name.cpp | 19 +-- toolchain/check/handle_namespace.cpp | 2 +- toolchain/check/handle_operator.cpp | 20 +-- toolchain/check/handle_paren.cpp | 4 +- toolchain/check/handle_pattern_binding.cpp | 2 +- toolchain/check/handle_statement.cpp | 7 +- toolchain/check/handle_struct.cpp | 2 +- toolchain/check/handle_variable.cpp | 2 +- toolchain/check/node_block_stack.cpp | 6 +- toolchain/check/node_block_stack.h | 2 +- toolchain/check/pending_block.h | 12 +- toolchain/lower/file_context.cpp | 23 +-- toolchain/lower/function_context.cpp | 4 +- toolchain/lower/function_context.h | 4 +- toolchain/lower/handle.cpp | 48 +++--- toolchain/sem_ir/BUILD | 25 ++- toolchain/sem_ir/file.cpp | 65 ++++---- toolchain/sem_ir/file.h | 167 +++------------------ toolchain/sem_ir/formatter.cpp | 39 ++--- toolchain/sem_ir/value_stores.h | 151 +++++++++++++++++++ 31 files changed, 476 insertions(+), 381 deletions(-) create mode 100644 toolchain/sem_ir/value_stores.h diff --git a/toolchain/base/value_store.h b/toolchain/base/value_store.h index 20c4a133bc05..95b89f3927ba 100644 --- a/toolchain/base/value_store.h +++ b/toolchain/base/value_store.h @@ -5,6 +5,8 @@ #ifndef CARBON_TOOLCHAIN_BASE_VALUE_STORE_H_ #define CARBON_TOOLCHAIN_BASE_VALUE_STORE_H_ +#include + #include "common/check.h" #include "common/ostream.h" #include "llvm/ADT/APInt.h" @@ -44,7 +46,7 @@ class Real : public Printable { // Corresponds to an integer value represented by an APInt. struct IntegerId : public IndexBase, public Printable { - using IndexedType = llvm::APInt; + using IndexedType = const llvm::APInt; static const IntegerId Invalid; using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { @@ -56,7 +58,7 @@ constexpr IntegerId IntegerId::Invalid(IntegerId::InvalidIndex); // Corresponds to a Real value. struct RealId : public IndexBase, public Printable { - using IndexedType = Real; + using IndexedType = const Real; static const RealId Invalid; using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { @@ -68,7 +70,7 @@ constexpr RealId RealId::Invalid(RealId::InvalidIndex); // Corresponds to a StringRef. struct StringId : public IndexBase, public Printable { - using IndexedType = std::string; + using IndexedType = const std::string; static const StringId Invalid; using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { @@ -78,10 +80,19 @@ struct StringId : public IndexBase, public Printable { }; constexpr StringId StringId::Invalid(StringId::InvalidIndex); +namespace Internal { +// Used as a parent class for non-printable types. This is just for +// std::conditional, not as an API. +class ValueStoreNotPrintable {}; +} // namespace Internal + // A simple wrapper for accumulating values, providing IDs to later retrieve the // value. This does not do deduplication. template -class ValueStore : public Printable> { +class ValueStore + : public std::conditional, ValueT>, + Printable>, + Internal::ValueStoreNotPrintable> { public: // Stores the value and returns an ID to reference it. auto Add(ValueT value) -> IdT { @@ -91,6 +102,13 @@ class ValueStore : public Printable> { return id; } + // Adds a default constructed value and returns an ID to reference it. + auto AddDefaultValue() -> IdT { + auto id = IdT(values_.size()); + values_.resize(id.index + 1); + return id; + } + // Returns a mutable value for an ID. auto Get(IdT id) -> ValueT& { CARBON_CHECK(id.index >= 0) << id.index; @@ -103,6 +121,10 @@ class ValueStore : public Printable> { return values_[id.index]; } + // Reserves space. + auto Reserve(size_t size) -> void { values_.reserve(size); } + + // These are to support printable structures, and are not guaranteed. auto Print(llvm::raw_ostream& out) const -> void { Print(out, 0); } auto Print(llvm::raw_ostream& out, int indent) const -> void { for (const auto& value : values_) { @@ -115,7 +137,7 @@ class ValueStore : public Printable> { auto size() const -> int { return values_.size(); } private: - llvm::SmallVector values_; + llvm::SmallVector> values_; }; // Storage for StringRefs. The caller is responsible for ensuring storage is diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 3a09f9b12459..5a18ac27310f 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -75,7 +75,7 @@ auto Context::DiagnoseDuplicateName(Parse::Node parse_node, "Duplicate name being declared in the same scope."); CARBON_DIAGNOSTIC(NameDeclarationPrevious, Note, "Name is previously declared here."); - auto prev_def = semantics_ir_->GetNode(prev_def_id); + auto prev_def = semantics_ir_->nodes().Get(prev_def_id); emitter_->Build(parse_node, NameDeclarationDuplicate) .Note(prev_def.parse_node(), NameDeclarationPrevious) .Emit(); @@ -94,8 +94,9 @@ auto Context::NoteIncompleteClass(SemIR::ClassId class_id, CARBON_DIAGNOSTIC(ClassForwardDeclaredHere, Note, "Class was forward declared here."); const auto& class_info = semantics_ir().classes().Get(class_id); - builder.Note(semantics_ir().GetNode(class_info.declaration_id).parse_node(), - ClassForwardDeclaredHere); + builder.Note( + semantics_ir().nodes().Get(class_info.declaration_id).parse_node(), + ClassForwardDeclaredHere); } auto Context::AddNameToLookup(Parse::Node name_node, StringId name_id, @@ -124,7 +125,7 @@ auto Context::LookupName(Parse::Node parse_node, StringId name_id, // TODO: Check for ambiguous lookups. return it->second.back(); } else { - const auto& scope = semantics_ir_->GetNameScope(scope_id); + const auto& scope = semantics_ir_->name_scopes().Get(scope_id); auto it = scope.find(name_id); if (it == scope.end()) { if (print_diagnostics) { @@ -155,8 +156,9 @@ auto Context::PopScope() -> void { } auto Context::FollowNameReferences(SemIR::NodeId node_id) -> SemIR::NodeId { - while (auto name_ref = - semantics_ir().GetNode(node_id).TryAs()) { + while ( + auto name_ref = + semantics_ir().nodes().Get(node_id).TryAs()) { node_id = name_ref->value_id; } return node_id; @@ -169,7 +171,7 @@ static auto AddDominatedBlockAndBranchImpl(Context& context, if (!context.node_block_stack().is_current_block_reachable()) { return SemIR::NodeBlockId::Unreachable; } - auto block_id = context.semantics_ir().AddNodeBlockId(); + auto block_id = context.semantics_ir().node_blocks().AddDefaultValue(); context.AddNode(BranchNode{parse_node, block_id, args...}); return block_id; } @@ -201,7 +203,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks) for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) { if (node_block_stack().is_current_block_reachable()) { if (new_block_id == SemIR::NodeBlockId::Unreachable) { - new_block_id = semantics_ir().AddNodeBlockId(); + new_block_id = semantics_ir().node_blocks().AddDefaultValue(); } AddNode(SemIR::Branch{parse_node, new_block_id}); } @@ -219,7 +221,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( for (auto arg_id : block_args) { if (node_block_stack().is_current_block_reachable()) { if (new_block_id == SemIR::NodeBlockId::Unreachable) { - new_block_id = semantics_ir().AddNodeBlockId(); + new_block_id = semantics_ir().node_blocks().AddDefaultValue(); } AddNode(SemIR::BranchWithArg{parse_node, new_block_id, arg_id}); } @@ -229,7 +231,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( // Acquire the result value. SemIR::TypeId result_type_id = - semantics_ir().GetNode(*block_args.begin()).type_id(); + semantics_ir().nodes().Get(*block_args.begin()).type_id(); return AddNode(SemIR::BlockArg{parse_node, result_type_id, new_block_id}); } @@ -245,7 +247,8 @@ auto Context::AddCurrentCodeBlockToFunction() -> void { auto function_id = semantics_ir() - .GetNodeAs(return_scope_stack().back()) + .nodes() + .GetAs(return_scope_stack().back()) .function_id; semantics_ir() .functions() @@ -264,7 +267,7 @@ auto Context::is_current_position_reachable() -> bool { if (block_contents.empty()) { return true; } - const auto& last_node = semantics_ir().GetNode(block_contents.back()); + const auto& last_node = semantics_ir().nodes().Get(block_contents.back()); return last_node.kind().terminator_kind() != SemIR::TerminatorKind::Terminator; } @@ -343,7 +346,7 @@ class TypeCompleter { } auto node_id = context_.semantics_ir().GetTypeAllowBuiltinTypes(type_id); - auto node = context_.semantics_ir().GetNode(node_id); + auto node = context_.semantics_ir().nodes().Get(node_id); auto old_work_list_size = work_list_.size(); @@ -397,16 +400,17 @@ class TypeCompleter { break; case SemIR::StructType::Kind: - for (auto field_id : context_.semantics_ir().GetNodeBlock( + for (auto field_id : context_.semantics_ir().node_blocks().Get( type_node.As().fields_id)) { Push(context_.semantics_ir() - .GetNodeAs(field_id) + .nodes() + .GetAs(field_id) .field_type_id); } break; case SemIR::TupleType::Kind: - for (auto element_type_id : context_.semantics_ir().GetTypeBlock( + for (auto element_type_id : context_.semantics_ir().type_blocks().Get( type_node.As().elements_id)) { Push(element_type_id); } @@ -475,7 +479,8 @@ class TypeCompleter { -> SemIR::ValueRepresentation { auto xref_node = context_.semantics_ir() .GetCrossReferenceIR(xref.ir_id) - .GetNode(xref.node_id); + .nodes() + .Get(xref.node_id); // The canonical description of a type should only have cross-references // for entities owned by another File, such as builtins, which are owned @@ -510,7 +515,8 @@ class TypeCompleter { SemIR::StructType struct_type) const -> SemIR::ValueRepresentation { // TODO: Share code with tuples. - auto fields = context_.semantics_ir().GetNodeBlock(struct_type.fields_id); + auto fields = + context_.semantics_ir().node_blocks().Get(struct_type.fields_id); if (fields.empty()) { return MakeEmptyRepresentation(struct_type.parse_node); } @@ -522,7 +528,8 @@ class TypeCompleter { bool same_as_object_rep = true; for (auto field_id : fields) { auto field = - context_.semantics_ir().GetNodeAs(field_id); + context_.semantics_ir().nodes().GetAs( + field_id); auto field_value_rep = GetNestedValueRepresentation(field.field_type_id); if (field_value_rep.type_id != field.field_type_id) { same_as_object_rep = false; @@ -537,7 +544,7 @@ class TypeCompleter { ? type_id : context_.CanonicalizeStructType( struct_type.parse_node, - context_.semantics_ir().AddNodeBlock(value_rep_fields)); + context_.semantics_ir().node_blocks().Add(value_rep_fields)); if (fields.size() == 1) { // The value representation for a struct with a single field is a // struct containing the value representation of the field. @@ -554,7 +561,7 @@ class TypeCompleter { -> SemIR::ValueRepresentation { // TODO: Share code with structs. auto elements = - context_.semantics_ir().GetTypeBlock(tuple_type.elements_id); + context_.semantics_ir().type_blocks().Get(tuple_type.elements_id); if (elements.empty()) { return MakeEmptyRepresentation(tuple_type.parse_node); } @@ -784,19 +791,19 @@ static auto ProfileType(Context& semantics_context, SemIR::Node node, canonical_id.AddInteger(node.As().pointee_id.index); break; case SemIR::StructType::Kind: { - auto fields = semantics_context.semantics_ir().GetNodeBlock( + auto fields = semantics_context.semantics_ir().node_blocks().Get( node.As().fields_id); for (const auto& field_id : fields) { - auto field = - semantics_context.semantics_ir().GetNodeAs( - field_id); + auto field = semantics_context.semantics_ir() + .nodes() + .GetAs(field_id); canonical_id.AddInteger(field.name_id.index); canonical_id.AddInteger(field.field_type_id.index); } break; } case SemIR::TupleType::Kind: - ProfileTupleType(semantics_context.semantics_ir().GetTypeBlock( + ProfileTupleType(semantics_context.semantics_ir().type_blocks().Get( node.As().elements_id), canonical_id); break; @@ -822,7 +829,7 @@ auto Context::CanonicalizeType(SemIR::NodeId node_id) -> SemIR::TypeId { return it->second; } - auto node = semantics_ir_->GetNode(node_id); + auto node = semantics_ir_->nodes().Get(node_id); auto profile_node = [&](llvm::FoldingSetNodeID& canonical_id) { ProfileType(*this, node, canonical_id); }; @@ -845,8 +852,9 @@ auto Context::CanonicalizeTupleType(Parse::Node parse_node, ProfileTupleType(type_ids, canonical_id); }; auto make_tuple_node = [&] { - return AddNode(SemIR::TupleType{parse_node, SemIR::TypeId::TypeType, - semantics_ir_->AddTypeBlock(type_ids)}); + return AddNode( + SemIR::TupleType{parse_node, SemIR::TypeId::TypeType, + semantics_ir_->type_blocks().Add(type_ids)}); }; return CanonicalizeTypeImpl(SemIR::TupleType::Kind, profile_tuple, make_tuple_node); @@ -868,8 +876,8 @@ auto Context::GetPointerType(Parse::Node parse_node, } auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId { - SemIR::Node type_node = - semantics_ir_->GetNode(semantics_ir_->GetTypeAllowBuiltinTypes(type_id)); + SemIR::Node type_node = semantics_ir_->nodes().Get( + semantics_ir_->GetTypeAllowBuiltinTypes(type_id)); if (auto const_type = type_node.TryAs()) { return const_type->inner_id; } diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index ac6c73267616..7e071e4f7957 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -24,7 +24,7 @@ namespace Carbon::Check { static auto FindReturnSlotForInitializer(SemIR::File& semantics_ir, SemIR::NodeId init_id) -> SemIR::NodeId { - SemIR::Node init = semantics_ir.GetNode(init_id); + SemIR::Node init = semantics_ir.nodes().Get(init_id); switch (init.kind()) { default: CARBON_FATAL() << "Initialization from unexpected node " << init; @@ -46,12 +46,12 @@ static auto FindReturnSlotForInitializer(SemIR::File& semantics_ir, .has_return_slot()) { return SemIR::NodeId::Invalid; } - return semantics_ir.GetNodeBlock(call.args_id).back(); + return semantics_ir.node_blocks().Get(call.args_id).back(); } case SemIR::ArrayInit::Kind: { - return semantics_ir - .GetNodeBlock(init.As().inits_and_return_slot_id) + return semantics_ir.node_blocks() + .Get(init.As().inits_and_return_slot_id) .back(); } } @@ -64,11 +64,11 @@ static auto MarkInitializerFor(SemIR::File& semantics_ir, SemIR::NodeId init_id, auto return_slot_id = FindReturnSlotForInitializer(semantics_ir, init_id); if (return_slot_id.is_valid()) { // Replace the temporary in the return slot with a reference to our target. - CARBON_CHECK(semantics_ir.GetNode(return_slot_id).kind() == + CARBON_CHECK(semantics_ir.nodes().Get(return_slot_id).kind() == SemIR::TemporaryStorage::Kind) << "Return slot for initializer does not contain a temporary; " << "initialized multiple times? Have " - << semantics_ir.GetNode(return_slot_id); + << semantics_ir.nodes().Get(return_slot_id); target_block.MergeReplacing(return_slot_id, target_id); } } @@ -84,12 +84,12 @@ static auto FinalizeTemporary(Context& context, SemIR::NodeId init_id, auto return_slot_id = FindReturnSlotForInitializer(semantics_ir, init_id); if (return_slot_id.is_valid()) { // The return slot should already have a materialized temporary in it. - CARBON_CHECK(semantics_ir.GetNode(return_slot_id).kind() == + CARBON_CHECK(semantics_ir.nodes().Get(return_slot_id).kind() == SemIR::TemporaryStorage::Kind) << "Return slot for initializer does not contain a temporary; " << "initialized multiple times? Have " - << semantics_ir.GetNode(return_slot_id); - auto init = semantics_ir.GetNode(init_id); + << semantics_ir.nodes().Get(return_slot_id); + auto init = semantics_ir.nodes().Get(init_id); return context.AddNode(SemIR::Temporary{init.parse_node(), init.type_id(), return_slot_id, init_id}); } @@ -104,7 +104,7 @@ static auto FinalizeTemporary(Context& context, SemIR::NodeId init_id, // TODO: Consider using an invalid ID to mean that we immediately // materialize and initialize a temporary, rather than two separate // nodes. - auto init = semantics_ir.GetNode(init_id); + auto init = semantics_ir.nodes().Get(init_id); auto temporary_id = context.AddNode( SemIR::TemporaryStorage{init.parse_node(), init.type_id()}); return context.AddNode(SemIR::Temporary{init.parse_node(), init.type_id(), @@ -205,20 +205,20 @@ class CopyOnWriteBlock { CopyOnWriteBlock(SemIR::File& file, SemIR::NodeBlockId source_id, size_t size) : file_(file), source_id_(source_id) { if (!source_id_.is_valid()) { - id_ = file_.AddUninitializedNodeBlock(size); + id_ = file_.node_blocks().AddUninitialized(size); } } auto id() -> SemIR::NodeBlockId const { return id_; } auto Set(int i, SemIR::NodeId value) -> void { - if (source_id_.is_valid() && file_.GetNodeBlock(id_)[i] == value) { + if (source_id_.is_valid() && file_.node_blocks().Get(id_)[i] == value) { return; } if (id_ == source_id_) { - id_ = file_.AddNodeBlock(file_.GetNodeBlock(source_id_)); + id_ = file_.node_blocks().Add(file_.node_blocks().Get(source_id_)); } - file_.GetNodeBlock(id_)[i] = value; + file_.node_blocks().Get(id_)[i] = value; } private: @@ -235,16 +235,17 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto tuple_elem_types = semantics_ir.GetTypeBlock(tuple_type.elements_id); + auto tuple_elem_types = + semantics_ir.type_blocks().Get(tuple_type.elements_id); - auto value = semantics_ir.GetNode(value_id); + auto value = semantics_ir.nodes().Get(value_id); // If we're initializing from a tuple literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; if (auto tuple_literal = value.TryAs()) { - literal_elems = semantics_ir.GetNodeBlock(tuple_literal->elements_id); + literal_elems = semantics_ir.node_blocks().Get(tuple_literal->elements_id); } else { value_id = MaterializeIfInitializing(context, value_id); } @@ -305,9 +306,9 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, target_block->InsertHere(); inits.push_back(return_slot_id); - return context.AddNode(SemIR::ArrayInit{value.parse_node(), target.type_id, - value_id, - semantics_ir.AddNodeBlock(inits)}); + return context.AddNode( + SemIR::ArrayInit{value.parse_node(), target.type_id, value_id, + semantics_ir.node_blocks().Add(inits)}); } // Performs a conversion from a tuple to a tuple type. Does not perform a @@ -317,10 +318,10 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto src_elem_types = semantics_ir.GetTypeBlock(src_type.elements_id); - auto dest_elem_types = semantics_ir.GetTypeBlock(dest_type.elements_id); + auto src_elem_types = semantics_ir.type_blocks().Get(src_type.elements_id); + auto dest_elem_types = semantics_ir.type_blocks().Get(dest_type.elements_id); - auto value = semantics_ir.GetNode(value_id); + auto value = semantics_ir.nodes().Get(value_id); // If we're initializing from a tuple literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the @@ -329,7 +330,7 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type, auto literal_elems_id = SemIR::NodeBlockId::Invalid; if (auto tuple_literal = value.TryAs()) { literal_elems_id = tuple_literal->elements_id; - literal_elems = semantics_ir.GetNodeBlock(literal_elems_id); + literal_elems = semantics_ir.node_blocks().Get(literal_elems_id); } else { value_id = MaterializeIfInitializing(context, value_id); } @@ -391,10 +392,10 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto src_elem_fields = semantics_ir.GetNodeBlock(src_type.fields_id); - auto dest_elem_fields = semantics_ir.GetNodeBlock(dest_type.fields_id); + auto src_elem_fields = semantics_ir.node_blocks().Get(src_type.fields_id); + auto dest_elem_fields = semantics_ir.node_blocks().Get(dest_type.fields_id); - auto value = semantics_ir.GetNode(value_id); + auto value = semantics_ir.nodes().Get(value_id); // If we're initializing from a struct literal, we will use its elements // directly. Otherwise, materialize a temporary if needed and index into the @@ -403,7 +404,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, auto literal_elems_id = SemIR::NodeBlockId::Invalid; if (auto struct_literal = value.TryAs()) { literal_elems_id = struct_literal->elements_id; - literal_elems = semantics_ir.GetNodeBlock(literal_elems_id); + literal_elems = semantics_ir.node_blocks().Get(literal_elems_id); } else { value_id = MaterializeIfInitializing(context, value_id); } @@ -441,9 +442,9 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, for (auto [i, src_field_id, dest_field_id] : llvm::enumerate(src_elem_fields, dest_elem_fields)) { auto src_field = - semantics_ir.GetNodeAs(src_field_id); + semantics_ir.nodes().GetAs(src_field_id); auto dest_field = - semantics_ir.GetNodeAs(dest_field_id); + semantics_ir.nodes().GetAs(dest_field_id); if (src_field.name_id != dest_field.name_id) { CARBON_DIAGNOSTIC( StructInitFieldNameMismatch, Error, @@ -502,9 +503,9 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto value = semantics_ir.GetNode(value_id); + auto value = semantics_ir.nodes().Get(value_id); auto value_type_id = value.type_id(); - auto target_type_node = semantics_ir.GetNode( + auto target_type_node = semantics_ir.nodes().Get( semantics_ir.GetTypeAllowBuiltinTypes(target.type_id)); // Various forms of implicit conversion are supported as builtin conversions, @@ -546,7 +547,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, // A tuple (T1, T2, ..., Tn) converts to (U1, U2, ..., Un) if each Ti // converts to Ui. if (auto target_tuple_type = target_type_node.TryAs()) { - auto value_type_node = semantics_ir.GetNode( + auto value_type_node = semantics_ir.nodes().Get( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); if (auto src_tuple_type = value_type_node.TryAs()) { return ConvertTupleToTuple(context, *src_tuple_type, *target_tuple_type, @@ -559,7 +560,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, // (p(1), ..., p(n)) is a permutation of (1, ..., n) and each Ti converts // to Ui. if (auto target_struct_type = target_type_node.TryAs()) { - auto value_type_node = semantics_ir.GetNode( + auto value_type_node = semantics_ir.nodes().Get( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); if (auto src_struct_type = value_type_node.TryAs()) { return ConvertStructToStruct(context, *src_struct_type, @@ -569,7 +570,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, // A tuple (T1, T2, ..., Tn) converts to [T; n] if each Ti converts to T. if (auto target_array_type = target_type_node.TryAs()) { - auto value_type_node = semantics_ir.GetNode( + auto value_type_node = semantics_ir.nodes().Get( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); if (auto src_tuple_type = value_type_node.TryAs()) { return ConvertTupleToArray(context, *src_tuple_type, *target_array_type, @@ -583,7 +584,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, if (auto tuple_literal = value.TryAs()) { llvm::SmallVector type_ids; for (auto tuple_node_id : - semantics_ir.GetNodeBlock(tuple_literal->elements_id)) { + semantics_ir.node_blocks().Get(tuple_literal->elements_id)) { // TODO: This call recurses back into conversion. Switch to an // iterative approach. type_ids.push_back( @@ -615,7 +616,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, // Start by making sure both sides are valid. If any part is invalid, the // result is invalid and we shouldn't error. - if (semantics_ir.GetNode(expr_id).type_id() == SemIR::TypeId::Error || + if (semantics_ir.nodes().Get(expr_id).type_id() == SemIR::TypeId::Error || target.type_id == SemIR::TypeId::Error) { return SemIR::NodeId::BuiltinError; } @@ -627,7 +628,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, // namespace names, and allow use of functions as values. CARBON_DIAGNOSTIC(UseOfNonExpressionAsValue, Error, "Expression cannot be used as a value."); - context.emitter().Emit(semantics_ir.GetNode(expr_id).parse_node(), + context.emitter().Emit(semantics_ir.nodes().Get(expr_id).parse_node(), UseOfNonExpressionAsValue); return SemIR::NodeId::BuiltinError; } @@ -661,7 +662,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, // If the types don't match at this point, we can't perform the conversion. // TODO: Look for an ImplicitAs impl. - SemIR::Node expr = semantics_ir.GetNode(expr_id); + SemIR::Node expr = semantics_ir.nodes().Get(expr_id); if (expr.type_id() != target.type_id) { CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error, "Cannot implicitly convert from `{0}` to `{1}`.", @@ -742,14 +743,14 @@ auto Initialize(Context& context, Parse::Node parse_node, return Convert( context, parse_node, value_id, {.kind = ConversionTarget::Initializer, - .type_id = context.semantics_ir().GetNode(target_id).type_id(), + .type_id = context.semantics_ir().nodes().Get(target_id).type_id(), .init_id = target_id, .init_block = &target_block}); } auto ConvertToValueExpression(Context& context, SemIR::NodeId expr_id) -> SemIR::NodeId { - auto expr = context.semantics_ir().GetNode(expr_id); + auto expr = context.semantics_ir().nodes().Get(expr_id); return Convert(context, expr.parse_node(), expr_id, {.kind = ConversionTarget::Value, .type_id = expr.type_id()}); } @@ -757,7 +758,7 @@ auto ConvertToValueExpression(Context& context, SemIR::NodeId expr_id) auto ConvertToValueOrReferenceExpression(Context& context, SemIR::NodeId expr_id) -> SemIR::NodeId { - auto expr = context.semantics_ir().GetNode(expr_id); + auto expr = context.semantics_ir().nodes().Get(expr_id); return Convert( context, expr.parse_node(), expr_id, {.kind = ConversionTarget::ValueOrReference, .type_id = expr.type_id()}); @@ -789,8 +790,8 @@ auto ConvertCallArgs(Context& context, Parse::Node call_parse_node, return true; } - auto arg_refs = context.semantics_ir().GetNodeBlock(arg_refs_id); - auto param_refs = context.semantics_ir().GetNodeBlock(param_refs_id); + auto arg_refs = context.semantics_ir().node_blocks().Get(arg_refs_id); + auto param_refs = context.semantics_ir().node_blocks().Get(param_refs_id); if (has_return_slot) { // There's no entry in the parameter block for the return slot, so ignore @@ -834,7 +835,7 @@ auto ConvertCallArgs(Context& context, Parse::Node call_parse_node, for (auto [i, value_id, param_ref] : llvm::enumerate(arg_refs, param_refs)) { diag_param_index = i; - auto as_type_id = context.semantics_ir().GetNode(param_ref).type_id(); + auto as_type_id = context.semantics_ir().nodes().Get(param_ref).type_id(); // TODO: Convert to the proper expression category. For now, we assume // parameters are all `let` bindings. value_id = diff --git a/toolchain/check/declaration_name_stack.cpp b/toolchain/check/declaration_name_stack.cpp index febe44081be9..26094a4b1154 100644 --- a/toolchain/check/declaration_name_stack.cpp +++ b/toolchain/check/declaration_name_stack.cpp @@ -65,7 +65,7 @@ auto DeclarationNameStack::LookupOrAddName(NameContext name_context, } else { // TODO: Reject unless the scope is a namespace scope or the name is // unqualified. - bool success = context_->semantics_ir().AddNameScopeEntry( + bool success = context_->semantics_ir().name_scopes().AddEntry( name_context.target_scope_id, name_context.unresolved_name_id, target_id); CARBON_CHECK(success) @@ -122,7 +122,7 @@ auto DeclarationNameStack::UpdateScopeIfNeeded(NameContext& name_context) // This will only be reached for resolved nodes. We update the target // scope based on the resolved type. auto resolved_node = - context_->semantics_ir().GetNode(name_context.resolved_node_id); + context_->semantics_ir().nodes().Get(name_context.resolved_node_id); switch (resolved_node.kind()) { case SemIR::ClassDeclaration::Kind: { const auto& class_info = context_->semantics_ir().classes().Get( @@ -166,7 +166,8 @@ auto DeclarationNameStack::CanResolveQualifier(NameContext& name_context, // Because more qualifiers were found, we diagnose that the earlier // qualifier didn't resolve to a scoped entity. if (auto class_decl = context_->semantics_ir() - .GetNode(name_context.resolved_node_id) + .nodes() + .Get(name_context.resolved_node_id) .TryAs()) { CARBON_DIAGNOSTIC(QualifiedDeclarationInIncompleteClassScope, Error, "Cannot declare a member of incomplete class `{0}`.", diff --git a/toolchain/check/handle_array.cpp b/toolchain/check/handle_array.cpp index ddb96c802445..94c024d9e9dd 100644 --- a/toolchain/check/handle_array.cpp +++ b/toolchain/check/handle_array.cpp @@ -34,7 +34,7 @@ auto HandleArrayExpression(Context& context, Parse::Node parse_node) -> bool { context.node_stack() .PopAndDiscardSoloParseNode(); auto element_type_node_id = context.node_stack().PopExpression(); - auto bound_node = context.semantics_ir().GetNode(bound_node_id); + auto bound_node = context.semantics_ir().nodes().Get(bound_node_id); if (auto literal = bound_node.TryAs()) { const auto& bound_value = context.semantics_ir().integers().Get(literal->integer_id); diff --git a/toolchain/check/handle_call_expression.cpp b/toolchain/check/handle_call_expression.cpp index ed7674759bdf..54d53003d764 100644 --- a/toolchain/check/handle_call_expression.cpp +++ b/toolchain/check/handle_call_expression.cpp @@ -17,8 +17,8 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { auto [call_expr_parse_node, callee_id] = context.node_stack() .PopWithParseNode(); - auto callee_node = - context.semantics_ir().GetNode(context.FollowNameReferences(callee_id)); + auto callee_node = context.semantics_ir().nodes().Get( + context.FollowNameReferences(callee_id)); auto function_name = callee_node.TryAs(); if (!function_name) { // TODO: Work on error. diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index ebc0b94bcd95..a6d0b29e6539 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -35,7 +35,8 @@ static auto BuildClassDeclaration(Context& context) name_context, class_decl_id); if (existing_id.is_valid()) { if (auto existing_class_decl = context.semantics_ir() - .GetNode(existing_id) + .nodes() + .Get(existing_id) .TryAs()) { // This is a redeclaration of an existing class. class_decl.class_id = existing_class_decl->class_id; @@ -69,7 +70,7 @@ static auto BuildClassDeclaration(Context& context) } // Write the class ID into the ClassDeclaration. - context.semantics_ir().ReplaceNode(class_decl_id, class_decl); + context.semantics_ir().nodes().Set(class_decl_id, class_decl); return {class_decl.class_id, class_decl_id}; } @@ -95,13 +96,14 @@ auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node) .Build(parse_node, ClassRedefinition, context.semantics_ir().strings().Get(class_info.name_id)) .Note(context.semantics_ir() - .GetNode(class_info.definition_id) + .nodes() + .Get(class_info.definition_id) .parse_node(), ClassPreviousDefinition) .Emit(); } else { class_info.definition_id = class_decl_id; - class_info.scope_id = context.semantics_ir().AddNameScope(); + class_info.scope_id = context.semantics_ir().name_scopes().Add(); // TODO: Introduce `Self`. } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 9cf3fae6362b..1b80e7375e68 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -30,7 +30,7 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) context.node_stack().PopWithParseNode(); auto return_node_copy = return_node; return_type_id = - context.semantics_ir().GetNode(return_storage_id).type_id(); + context.semantics_ir().nodes().Get(return_storage_id).type_id(); if (!context.TryToCompleteType(return_type_id, [&] { CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error, @@ -69,7 +69,8 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) if (existing_id.is_valid()) { if (auto existing_function_decl = context.semantics_ir() - .GetNode(existing_id) + .nodes() + .Get(existing_id) .TryAs()) { // This is a redeclaration of an existing function. function_decl.function_id = existing_function_decl->function_id; @@ -104,11 +105,11 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) } // Write the function ID into the FunctionDeclaration. - context.semantics_ir().ReplaceNode(function_decl_id, function_decl); + context.semantics_ir().nodes().Set(function_decl_id, function_decl); if (SemIR::IsEntryPoint(context.semantics_ir(), function_decl.function_id)) { // TODO: Update this once valid signatures for the entry point are decided. - if (!context.semantics_ir().GetNodeBlock(param_refs_id).empty() || + if (!context.semantics_ir().node_blocks().Get(param_refs_id).empty() || (return_slot_id.is_valid() && return_type_id != context.GetBuiltinType(SemIR::BuiltinKind::BoolType) && @@ -172,9 +173,11 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node) context.emitter() .Build(parse_node, FunctionRedefinition, context.semantics_ir().strings().Get(function.name_id)) - .Note( - context.semantics_ir().GetNode(function.definition_id).parse_node(), - FunctionPreviousDefinition) + .Note(context.semantics_ir() + .nodes() + .Get(function.definition_id) + .parse_node(), + FunctionPreviousDefinition) .Emit(); } else { function.definition_id = decl_id; @@ -188,8 +191,9 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node) // Bring the parameters into scope. for (auto param_id : - context.semantics_ir().GetNodeBlock(function.param_refs_id)) { - auto param = context.semantics_ir().GetNodeAs(param_id); + context.semantics_ir().node_blocks().Get(function.param_refs_id)) { + auto param = + context.semantics_ir().nodes().GetAs(param_id); // The parameter types need to be complete. context.TryToCompleteType(param.type_id, [&] { diff --git a/toolchain/check/handle_if_expression.cpp b/toolchain/check/handle_if_expression.cpp index bf786f4c4774..0bf4adf216dc 100644 --- a/toolchain/check/handle_if_expression.cpp +++ b/toolchain/check/handle_if_expression.cpp @@ -57,7 +57,8 @@ auto HandleIfExpressionElse(Context& context, Parse::Node parse_node) -> bool { // Convert the `else` value to the `then` value's type, and finish the `else` // block. // TODO: Find a common type, and convert both operands to it instead. - auto result_type_id = context.semantics_ir().GetNode(then_value_id).type_id(); + auto result_type_id = + context.semantics_ir().nodes().Get(then_value_id).type_id(); else_value_id = ConvertToValueOfType(context, else_node, else_value_id, result_type_id); diff --git a/toolchain/check/handle_index.cpp b/toolchain/check/handle_index.cpp index 5ffffcb3467b..183e42fce8f5 100644 --- a/toolchain/check/handle_index.cpp +++ b/toolchain/check/handle_index.cpp @@ -40,13 +40,13 @@ static auto ValidateIntegerLiteralBound(Context& context, auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { auto index_node_id = context.node_stack().PopExpression(); - auto index_node = context.semantics_ir().GetNode(index_node_id); + auto index_node = context.semantics_ir().nodes().Get(index_node_id); auto operand_node_id = context.node_stack().PopExpression(); operand_node_id = ConvertToValueOrReferenceExpression(context, operand_node_id); - auto operand_node = context.semantics_ir().GetNode(operand_node_id); + auto operand_node = context.semantics_ir().nodes().Get(operand_node_id); auto operand_type_id = operand_node.type_id(); - auto operand_type_node = context.semantics_ir().GetNode( + auto operand_type_node = context.semantics_ir().nodes().Get( context.semantics_ir().GetTypeAllowBuiltinTypes(operand_type_id)); switch (operand_type_node.kind()) { @@ -88,7 +88,7 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { case SemIR::TupleType::Kind: { SemIR::TypeId element_type_id = SemIR::TypeId::Error; if (auto index_literal = index_node.TryAs()) { - auto type_block = context.semantics_ir().GetTypeBlock( + auto type_block = context.semantics_ir().type_blocks().Get( operand_type_node.As().elements_id); if (const auto* index_val = ValidateIntegerLiteralBound( context, parse_node, operand_node, *index_literal, diff --git a/toolchain/check/handle_let.cpp b/toolchain/check/handle_let.cpp index 320dae294d35..b7a56dc53d24 100644 --- a/toolchain/check/handle_let.cpp +++ b/toolchain/check/handle_let.cpp @@ -16,7 +16,7 @@ auto HandleLetDeclaration(Context& context, Parse::Node parse_node) -> bool { .PopAndDiscardSoloParseNode(); // Convert the value to match the type of the pattern. - auto pattern = context.semantics_ir().GetNode(pattern_id); + auto pattern = context.semantics_ir().nodes().Get(pattern_id); value_id = ConvertToValueOfType(context, parse_node, value_id, pattern.type_id()); @@ -27,7 +27,7 @@ auto HandleLetDeclaration(Context& context, Parse::Node parse_node) -> bool { CARBON_CHECK(!bind_name.value_id.is_valid()) << "Binding should not already have a value!"; bind_name.value_id = value_id; - context.semantics_ir().ReplaceNode(pattern_id, bind_name); + context.semantics_ir().nodes().Set(pattern_id, bind_name); context.node_block_stack().AddNodeId(pattern_id); // Add the name of the binding to the current scope. diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index f074a8164fef..618209cc80d3 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -14,7 +14,7 @@ namespace Carbon::Check { static auto GetAsNameScope(Context& context, SemIR::NodeId base_id) -> std::optional { auto base = - context.semantics_ir().GetNode(context.FollowNameReferences(base_id)); + context.semantics_ir().nodes().Get(context.FollowNameReferences(base_id)); if (auto base_as_namespace = base.TryAs()) { return base_as_namespace->name_scope_id; } @@ -26,7 +26,7 @@ static auto GetAsNameScope(Context& context, SemIR::NodeId base_id) "Member access into incomplete class `{0}`.", std::string); auto builder = context.emitter().Build( - context.semantics_ir().GetNode(base_id).parse_node(), + context.semantics_ir().nodes().Get(base_id).parse_node(), QualifiedExpressionInIncompleteClassScope, context.semantics_ir().StringifyTypeExpression(base_id, true)); context.NoteIncompleteClass(base_as_class->class_id, builder); @@ -49,7 +49,7 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) ? context.LookupName(parse_node, name_id, *name_scope_id, /*print_diagnostics=*/true) : SemIR::NodeId::BuiltinError; - auto node = context.semantics_ir().GetNode(node_id); + auto node = context.semantics_ir().nodes().Get(node_id); // TODO: Track that this node was named within `base_id`. context.AddNodeAndPush( parse_node, @@ -59,19 +59,20 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) // Materialize a temporary for the base expression if necessary. base_id = ConvertToValueOrReferenceExpression(context, base_id); - auto base_type_id = context.semantics_ir().GetNode(base_id).type_id(); + auto base_type_id = context.semantics_ir().nodes().Get(base_id).type_id(); - auto base_type = context.semantics_ir().GetNode( + auto base_type = context.semantics_ir().nodes().Get( context.semantics_ir().GetTypeAllowBuiltinTypes(base_type_id)); switch (base_type.kind()) { case SemIR::StructType::Kind: { - auto refs = context.semantics_ir().GetNodeBlock( + auto refs = context.semantics_ir().node_blocks().Get( base_type.As().fields_id); // TODO: Do we need to optimize this with a lookup table for O(1)? for (auto [i, ref_id] : llvm::enumerate(refs)) { auto field = - context.semantics_ir().GetNodeAs(ref_id); + context.semantics_ir().nodes().GetAs( + ref_id); if (name_id == field.name_id) { context.AddNodeAndPush( parse_node, SemIR::StructAccess{parse_node, field.field_type_id, @@ -124,7 +125,7 @@ auto HandleNameExpression(Context& context, Parse::Node parse_node) -> bool { auto value_id = context.LookupName(parse_node, name_id, SemIR::NameScopeId::Invalid, /*print_diagnostics=*/true); - auto value = context.semantics_ir().GetNode(value_id); + auto value = context.semantics_ir().nodes().Get(value_id); // If lookup finds a class declaration, the value is its `Self` type. if (auto class_decl = value.TryAs()) { @@ -133,7 +134,7 @@ auto HandleNameExpression(Context& context, Parse::Node parse_node) -> bool { .classes() .Get(class_decl->class_id) .self_type_id); - value = context.semantics_ir().GetNode(value_id); + value = context.semantics_ir().nodes().Get(value_id); } CARBON_CHECK(value.kind().value_kind() == SemIR::NodeValueKind::Typed); diff --git a/toolchain/check/handle_namespace.cpp b/toolchain/check/handle_namespace.cpp index 8929a733b9f6..74593588e1c4 100644 --- a/toolchain/check/handle_namespace.cpp +++ b/toolchain/check/handle_namespace.cpp @@ -17,7 +17,7 @@ auto HandleNamespace(Context& context, Parse::Node parse_node) -> bool { auto name_context = context.declaration_name_stack().Pop(); auto namespace_id = context.AddNode(SemIR::Namespace{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType), - context.semantics_ir().AddNameScope()}); + context.semantics_ir().name_scopes().Add()}); context.declaration_name_stack().AddNameToLookup(name_context, namespace_id); return true; } diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 478c2fe82c5b..d002a9b01c77 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -19,13 +19,13 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { // very trivial check of validity on the operation. lhs_id = ConvertToValueOfType( context, parse_node, lhs_id, - context.semantics_ir().GetNode(rhs_id).type_id()); + context.semantics_ir().nodes().Get(rhs_id).type_id()); rhs_id = ConvertToValueExpression(context, rhs_id); context.AddNodeAndPush( parse_node, SemIR::BinaryOperatorAdd{ - parse_node, context.semantics_ir().GetNode(lhs_id).type_id(), + parse_node, context.semantics_ir().nodes().Get(lhs_id).type_id(), lhs_id, rhs_id}); return true; @@ -48,7 +48,7 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { context.AddNodeAndPush( parse_node, SemIR::BlockArg{parse_node, - context.semantics_ir().GetNode(rhs_id).type_id(), + context.semantics_ir().nodes().Get(rhs_id).type_id(), resume_block_id}); return true; } @@ -127,7 +127,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { parse_node, context.GetPointerType( parse_node, - context.semantics_ir().GetNode(value_id).type_id()), + context.semantics_ir().nodes().Get(value_id).type_id()), value_id}); return true; } @@ -136,7 +136,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { // `const (const T)` is probably not what the developer intended. // TODO: Detect `const (const T)*` and suggest moving the `*` inside the // parentheses. - if (context.semantics_ir().GetNode(value_id).kind() == + if (context.semantics_ir().nodes().Get(value_id).kind() == SemIR::ConstType::Kind) { CARBON_DIAGNOSTIC(RepeatedConst, Warning, "`const` applied repeatedly to the same type has no " @@ -155,15 +155,16 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { context.AddNodeAndPush( parse_node, SemIR::UnaryOperatorNot{ - parse_node, context.semantics_ir().GetNode(value_id).type_id(), + parse_node, + context.semantics_ir().nodes().Get(value_id).type_id(), value_id}); return true; case Lex::TokenKind::Star: { value_id = ConvertToValueExpression(context, value_id); auto type_id = context.GetUnqualifiedType( - context.semantics_ir().GetNode(value_id).type_id()); - auto type_node = context.semantics_ir().GetNode( + context.semantics_ir().nodes().Get(value_id).type_id()); + auto type_node = context.semantics_ir().nodes().Get( context.semantics_ir().GetTypeAllowBuiltinTypes(type_id)); auto result_type_id = SemIR::TypeId::Error; if (auto pointer_type = type_node.TryAs()) { @@ -200,7 +201,8 @@ auto HandleShortCircuitOperand(Context& context, Parse::Node parse_node) // Convert the condition to `bool`. auto cond_value_id = context.node_stack().PopExpression(); cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id); - auto bool_type_id = context.semantics_ir().GetNode(cond_value_id).type_id(); + auto bool_type_id = + context.semantics_ir().nodes().Get(cond_value_id).type_id(); // Compute the branch value: the condition for `and`, inverted for `or`. auto token = context.parse_tree().node_token(parse_node); diff --git a/toolchain/check/handle_paren.cpp b/toolchain/check/handle_paren.cpp index f61f5234a962..163307e28d52 100644 --- a/toolchain/check/handle_paren.cpp +++ b/toolchain/check/handle_paren.cpp @@ -40,11 +40,11 @@ auto HandleTupleLiteral(Context& context, Parse::Node parse_node) -> bool { context.node_stack() .PopAndDiscardSoloParseNode< Parse::NodeKind::ParenExpressionOrTupleLiteralStart>(); - const auto& node_block = context.semantics_ir().GetNodeBlock(refs_id); + const auto& node_block = context.semantics_ir().node_blocks().Get(refs_id); llvm::SmallVector type_ids; type_ids.reserve(node_block.size()); for (auto node : node_block) { - type_ids.push_back(context.semantics_ir().GetNode(node).type_id()); + type_ids.push_back(context.semantics_ir().nodes().Get(node).type_id()); } auto type_id = context.CanonicalizeTupleType(parse_node, std::move(type_ids)); diff --git a/toolchain/check/handle_pattern_binding.cpp b/toolchain/check/handle_pattern_binding.cpp index 13d222d66a8a..3954db4ef716 100644 --- a/toolchain/check/handle_pattern_binding.cpp +++ b/toolchain/check/handle_pattern_binding.cpp @@ -71,7 +71,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { // the `let` pattern before we see the initializer. context.node_stack().Push( parse_node, - context.semantics_ir().AddNodeInNoBlock(SemIR::BindName{ + context.semantics_ir().nodes().AddInNoBlock(SemIR::BindName{ name_node, cast_type_id, name_id, SemIR::NodeId::Invalid})); break; diff --git a/toolchain/check/handle_statement.cpp b/toolchain/check/handle_statement.cpp index 195d456397e7..8322d5687280 100644 --- a/toolchain/check/handle_statement.cpp +++ b/toolchain/check/handle_statement.cpp @@ -14,7 +14,7 @@ static auto HandleDiscardedExpression(Context& context, SemIR::NodeId expr_id) -> void { // If we discard an initializing expression, convert it to a value or // reference so that it has something to initialize. - auto expr = context.semantics_ir().GetNode(expr_id); + auto expr = context.semantics_ir().nodes().Get(expr_id); Convert(context, expr.parse_node(), expr_id, {.kind = ConversionTarget::Discarded, .type_id = expr.type_id()}); @@ -29,8 +29,9 @@ auto HandleExpressionStatement(Context& context, Parse::Node /*parse_node*/) auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { CARBON_CHECK(!context.return_scope_stack().empty()); - auto fn_node = context.semantics_ir().GetNodeAs( - context.return_scope_stack().back()); + auto fn_node = + context.semantics_ir().nodes().GetAs( + context.return_scope_stack().back()); const auto& callable = context.semantics_ir().functions().Get(fn_node.function_id); diff --git a/toolchain/check/handle_struct.cpp b/toolchain/check/handle_struct.cpp index 248a7ce53890..95fb058fcf28 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -46,7 +46,7 @@ auto HandleStructFieldValue(Context& context, Parse::Node parse_node) -> bool { // Store the name for the type. context.args_type_info_stack().AddNode(SemIR::StructTypeField{ parse_node, name_id, - context.semantics_ir().GetNode(value_node_id).type_id()}); + context.semantics_ir().nodes().Get(value_node_id).type_id()}); // Push the value back on the stack as an argument. context.node_stack().Push(parse_node, value_node_id); diff --git a/toolchain/check/handle_variable.cpp b/toolchain/check/handle_variable.cpp index 18c30d122eac..5b75d06b2d15 100644 --- a/toolchain/check/handle_variable.cpp +++ b/toolchain/check/handle_variable.cpp @@ -24,7 +24,7 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node) // Extract the name binding. SemIR::NodeId var_id = context.node_stack().Pop(); - auto var = context.semantics_ir().GetNodeAs(var_id); + auto var = context.semantics_ir().nodes().GetAs(var_id); // Form a corresponding name in the current context, and bind the name to the // variable. diff --git a/toolchain/check/node_block_stack.cpp b/toolchain/check/node_block_stack.cpp index 07b190a34491..42683cdd24d2 100644 --- a/toolchain/check/node_block_stack.cpp +++ b/toolchain/check/node_block_stack.cpp @@ -27,7 +27,7 @@ auto NodeBlockStack::PeekOrAdd(int depth) -> SemIR::NodeBlockId { int index = size() - depth - 1; auto& slot = stack_[index]; if (!slot.id.is_valid()) { - slot.id = semantics_ir_->AddNodeBlockId(); + slot.id = semantics_ir_->node_blocks().AddDefaultValue(); } return slot.id; } @@ -40,9 +40,9 @@ auto NodeBlockStack::Pop() -> SemIR::NodeBlockId { // Finalize the block. if (!back.content.empty() && back.id != SemIR::NodeBlockId::Unreachable) { if (back.id.is_valid()) { - semantics_ir_->SetNodeBlock(back.id, back.content); + semantics_ir_->node_blocks().Set(back.id, back.content); } else { - back.id = semantics_ir_->AddNodeBlock(back.content); + back.id = semantics_ir_->node_blocks().Add(back.content); } } diff --git a/toolchain/check/node_block_stack.h b/toolchain/check/node_block_stack.h index b7b67a8c4b05..36e55c9bf18a 100644 --- a/toolchain/check/node_block_stack.h +++ b/toolchain/check/node_block_stack.h @@ -48,7 +48,7 @@ class NodeBlockStack { // Adds the given node to the block at the top of the stack and returns its // ID. auto AddNode(SemIR::Node node) -> SemIR::NodeId { - auto node_id = semantics_ir_->AddNodeInNoBlock(node); + auto node_id = semantics_ir_->nodes().AddInNoBlock(node); AddNodeId(node_id); return node_id; } diff --git a/toolchain/check/pending_block.h b/toolchain/check/pending_block.h index 78f2f7f7067a..c8e75be4dc74 100644 --- a/toolchain/check/pending_block.h +++ b/toolchain/check/pending_block.h @@ -40,7 +40,7 @@ class PendingBlock { }; auto AddNode(SemIR::Node node) -> SemIR::NodeId { - auto node_id = context_.semantics_ir().AddNodeInNoBlock(node); + auto node_id = context_.semantics_ir().nodes().AddInNoBlock(node); nodes_.push_back(node_id); return node_id; } @@ -56,26 +56,26 @@ class PendingBlock { // Replace the node at target_id with the nodes in this block. The new value // for target_id should be value_id. auto MergeReplacing(SemIR::NodeId target_id, SemIR::NodeId value_id) -> void { - auto value = context_.semantics_ir().GetNode(value_id); + auto value = context_.semantics_ir().nodes().Get(value_id); // There are three cases here: if (nodes_.empty()) { // 1) The block is empty. Replace `target_id` with an empty splice // pointing at `value_id`. - context_.semantics_ir().ReplaceNode( + context_.semantics_ir().nodes().Set( target_id, SemIR::SpliceBlock{value.parse_node(), value.type_id(), SemIR::NodeBlockId::Empty, value_id}); } else if (nodes_.size() == 1 && nodes_[0] == value_id) { // 2) The block is {value_id}. Replace `target_id` with the node referred // to by `value_id`. This is intended to be the common case. - context_.semantics_ir().ReplaceNode(target_id, value); + context_.semantics_ir().nodes().Set(target_id, value); } else { // 3) Anything else: splice it into the IR, replacing `target_id`. - context_.semantics_ir().ReplaceNode( + context_.semantics_ir().nodes().Set( target_id, SemIR::SpliceBlock{value.parse_node(), value.type_id(), - context_.semantics_ir().AddNodeBlock(nodes_), + context_.semantics_ir().node_blocks().Add(nodes_), value_id}); } diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index cedd619a5127..84c64fb9010a 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -62,7 +62,7 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) -> llvm::Function* { const auto& function = semantics_ir().functions().Get(function_id); const bool has_return_slot = function.return_slot_id.is_valid(); - auto param_refs = semantics_ir().GetNodeBlock(function.param_refs_id); + auto param_refs = semantics_ir().node_blocks().Get(function.param_refs_id); SemIR::InitializingRepresentation return_rep = function.return_type_id.is_valid() @@ -85,7 +85,7 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) param_node_ids.push_back(function.return_slot_id); } for (auto param_ref_id : param_refs) { - auto param_type_id = semantics_ir().GetNode(param_ref_id).type_id(); + auto param_type_id = semantics_ir().nodes().Get(param_ref_id).type_id(); switch (auto value_rep = SemIR::GetValueRepresentation(semantics_ir(), param_type_id); value_rep.kind) { @@ -134,7 +134,7 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) llvm_context(), GetType(function.return_type_id))); } else { arg.setName(semantics_ir().strings().Get( - semantics_ir().GetNodeAs(node_id).name_id)); + semantics_ir().nodes().GetAs(node_id).name_id)); } } @@ -159,7 +159,7 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) // TODO: This duplicates the mapping between semantics nodes and LLVM // function parameters that was already computed in BuildFunctionDeclaration. // We should only do that once. - auto param_refs = semantics_ir().GetNodeBlock(function.param_refs_id); + auto param_refs = semantics_ir().node_blocks().Get(function.param_refs_id); int param_index = 0; if (has_return_slot) { function_lowering.SetLocal(function.return_slot_id, @@ -167,7 +167,7 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) ++param_index; } for (auto param_ref_id : param_refs) { - auto param_type_id = semantics_ir().GetNode(param_ref_id).type_id(); + auto param_type_id = semantics_ir().nodes().Get(param_ref_id).type_id(); if (SemIR::GetValueRepresentation(semantics_ir(), param_type_id).kind == SemIR::ValueRepresentation::None) { function_lowering.SetLocal( @@ -219,7 +219,7 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* { break; } - auto node = semantics_ir_->GetNode(node_id); + auto node = semantics_ir_->nodes().Get(node_id); switch (node.kind()) { case SemIR::ArrayType::Kind: { auto array_type = node.As(); @@ -232,12 +232,13 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* { case SemIR::PointerType::Kind: return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0); case SemIR::StructType::Kind: { - auto fields = - semantics_ir_->GetNodeBlock(node.As().fields_id); + auto fields = semantics_ir_->node_blocks().Get( + node.As().fields_id); llvm::SmallVector subtypes; subtypes.reserve(fields.size()); for (auto field_id : fields) { - auto field = semantics_ir_->GetNodeAs(field_id); + auto field = + semantics_ir_->nodes().GetAs(field_id); // TODO: Handle recursive types. The restriction for builtins prevents // recursion while still letting them cache. CARBON_CHECK(field.field_type_id.index < SemIR::BuiltinKind::ValidCount) @@ -251,8 +252,8 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* { // can be collectively replaced with LLVM's void, particularly around // function returns. LLVM doesn't allow declaring variables with a void // type, so that may require significant special casing. - auto elements = - semantics_ir_->GetTypeBlock(node.As().elements_id); + auto elements = semantics_ir_->type_blocks().Get( + node.As().elements_id); llvm::SmallVector subtypes; subtypes.reserve(elements.size()); for (auto element_id : elements) { diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 2ed355b839b8..fb68e07ea145 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -38,8 +38,8 @@ auto FunctionContext::TryToReuseBlock(SemIR::NodeBlockId block_id, } auto FunctionContext::LowerBlock(SemIR::NodeBlockId block_id) -> void { - for (const auto& node_id : semantics_ir().GetNodeBlock(block_id)) { - auto node = semantics_ir().GetNode(node_id); + for (const auto& node_id : semantics_ir().node_blocks().Get(block_id)) { + auto node = semantics_ir().nodes().Get(node_id); CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n"; // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 1daa2438d347..3248dcb9f05b 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -48,7 +48,7 @@ class FunctionContext { auto it = locals_.find(node_id); CARBON_CHECK(it != locals_.end()) << "Missing local: " << node_id << " " - << semantics_ir().GetNode(node_id); + << semantics_ir().nodes().Get(node_id); return it->second; } @@ -56,7 +56,7 @@ class FunctionContext { auto SetLocal(SemIR::NodeId node_id, llvm::Value* value) { bool added = locals_.insert({node_id, value}).second; CARBON_CHECK(added) << "Duplicate local insert: " << node_id << " " - << semantics_ir().GetNode(node_id); + << semantics_ir().nodes().Get(node_id); } // Gets a callable's function. diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 6d75fe81e262..3308bd117a07 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -24,8 +24,8 @@ auto HandleAddressOf(FunctionContext& context, SemIR::NodeId node_id, auto HandleArrayIndex(FunctionContext& context, SemIR::NodeId node_id, SemIR::ArrayIndex node) -> void { auto* array_value = context.GetLocal(node.array_id); - auto* llvm_type = - context.GetType(context.semantics_ir().GetNode(node.array_id).type_id()); + auto* llvm_type = context.GetType( + context.semantics_ir().nodes().Get(node.array_id).type_id()); llvm::Value* indexes[2] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(context.llvm_context()), 0), context.GetLocal(node.index_id)}; @@ -37,15 +37,17 @@ auto HandleArrayIndex(FunctionContext& context, SemIR::NodeId node_id, auto HandleArrayInit(FunctionContext& context, SemIR::NodeId node_id, SemIR::ArrayInit node) -> void { // The result of initialization is the return slot of the initializer. - context.SetLocal( - node_id, context.GetLocal(context.semantics_ir() - .GetNodeBlock(node.inits_and_return_slot_id) - .back())); + context.SetLocal(node_id, + context.GetLocal(context.semantics_ir() + .node_blocks() + .Get(node.inits_and_return_slot_id) + .back())); } auto HandleAssign(FunctionContext& context, SemIR::NodeId /*node_id*/, SemIR::Assign node) -> void { - auto storage_type_id = context.semantics_ir().GetNode(node.lhs_id).type_id(); + auto storage_type_id = + context.semantics_ir().nodes().Get(node.lhs_id).type_id(); context.FinishInitialization(storage_type_id, node.lhs_id, node.rhs_id); } @@ -99,7 +101,7 @@ auto HandleBranchWithArg(FunctionContext& context, SemIR::NodeId /*node_id*/, SemIR::BranchWithArg node) -> void { llvm::Value* arg = context.GetLocal(node.arg_id); SemIR::TypeId arg_type_id = - context.semantics_ir().GetNode(node.arg_id).type_id(); + context.semantics_ir().nodes().Get(node.arg_id).type_id(); // Opportunistically avoid creating a BasicBlock that contains just a branch. // We only do this for a block that we know will only have a single @@ -133,7 +135,7 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, std::vector args; llvm::ArrayRef arg_ids = - context.semantics_ir().GetNodeBlock(node.args_id); + context.semantics_ir().node_blocks().Get(node.args_id); if (SemIR::GetInitializingRepresentation(context.semantics_ir(), node.type_id) .has_return_slot()) { @@ -142,7 +144,7 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, } for (auto arg_id : arg_ids) { - auto arg_type_id = context.semantics_ir().GetNode(arg_id).type_id(); + auto arg_type_id = context.semantics_ir().nodes().Get(arg_id).type_id(); if (SemIR::GetValueRepresentation(context.semantics_ir(), arg_type_id) .kind != SemIR::ValueRepresentation::None) { args.push_back(context.GetLocal(arg_id)); @@ -181,7 +183,8 @@ auto HandleFunctionDeclaration(FunctionContext& /*context*/, auto HandleInitializeFrom(FunctionContext& context, SemIR::NodeId /*node_id*/, SemIR::InitializeFrom node) -> void { - auto storage_type_id = context.semantics_ir().GetNode(node.dest_id).type_id(); + auto storage_type_id = + context.semantics_ir().nodes().Get(node.dest_id).type_id(); context.FinishInitialization(storage_type_id, node.dest_id, node.src_id); } @@ -202,7 +205,7 @@ auto HandleNameReference(FunctionContext& context, SemIR::NodeId node_id, return; } - auto target = context.semantics_ir().GetNode(node.value_id); + auto target = context.semantics_ir().nodes().Get(node.value_id); if (auto function_decl = target.TryAs()) { context.SetLocal(node_id, context.GetFunction(function_decl->function_id)); } else { @@ -250,7 +253,7 @@ auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/, SemIR::ReturnExpression node) -> void { switch (SemIR::GetInitializingRepresentation( context.semantics_ir(), - context.semantics_ir().GetNode(node.expr_id).type_id()) + context.semantics_ir().nodes().Get(node.expr_id).type_id()) .kind) { case SemIR::InitializingRepresentation::None: case SemIR::InitializingRepresentation::InPlace: @@ -283,7 +286,7 @@ static auto GetStructOrTupleElement(FunctionContext& context, SemIR::NodeId aggr_node_id, unsigned idx, SemIR::TypeId result_type_id, llvm::Twine name) -> llvm::Value* { - auto aggr_node = context.semantics_ir().GetNode(aggr_node_id); + auto aggr_node = context.semantics_ir().nodes().Get(aggr_node_id); auto* aggr_value = context.GetLocal(aggr_node_id); switch (SemIR::GetExpressionCategory(context.semantics_ir(), aggr_node_id)) { @@ -338,15 +341,16 @@ static auto GetStructOrTupleElement(FunctionContext& context, auto HandleStructAccess(FunctionContext& context, SemIR::NodeId node_id, SemIR::StructAccess node) -> void { auto struct_type_id = - context.semantics_ir().GetNode(node.struct_id).type_id(); + context.semantics_ir().nodes().Get(node.struct_id).type_id(); // Get type information for member names. - auto fields = context.semantics_ir().GetNodeBlock( + auto fields = context.semantics_ir().node_blocks().Get( context.semantics_ir() - .GetNodeAs( + .nodes() + .GetAs( context.semantics_ir().types().Get(struct_type_id).node_id) .fields_id); - auto field = context.semantics_ir().GetNodeAs( + auto field = context.semantics_ir().nodes().GetAs( fields[node.index.index]); auto member_name = context.semantics_ir().strings().Get(field.name_id); @@ -379,7 +383,7 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context, return llvm::PoisonValue::get(context.GetType(value_rep.type_id)); case SemIR::ValueRepresentation::Copy: { - auto refs = context.semantics_ir().GetNodeBlock(refs_id); + auto refs = context.semantics_ir().node_blocks().Get(refs_id); CARBON_CHECK(refs.size() == 1) << "Unexpected size for aggregate with by-copy value representation"; // TODO: Remove the LLVM StructType wrapper in this case, so we don't @@ -400,7 +404,7 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context, context.builder().CreateAlloca(llvm_value_rep_type, /*ArraySize=*/nullptr, name); for (auto [i, ref] : - llvm::enumerate(context.semantics_ir().GetNodeBlock(refs_id))) { + llvm::enumerate(context.semantics_ir().node_blocks().Get(refs_id))) { context.builder().CreateStore( context.GetLocal(ref), context.builder().CreateStructGEP(llvm_value_rep_type, alloca, i)); @@ -458,8 +462,8 @@ auto HandleTupleAccess(FunctionContext& context, SemIR::NodeId node_id, auto HandleTupleIndex(FunctionContext& context, SemIR::NodeId node_id, SemIR::TupleIndex node) -> void { - auto index_node = - context.semantics_ir().GetNodeAs(node.index_id); + auto index_node = context.semantics_ir().nodes().GetAs( + node.index_id); auto index = context.semantics_ir() .integers() .Get(index_node.integer_id) diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index b8ffbdfad2fd..074bce5e8d36 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -47,13 +47,13 @@ cc_library( srcs = ["node.cpp"], hdrs = ["node.h"], deps = [ + ":builtin_kind", + ":node_kind", "//common:check", "//common:ostream", "//common:struct_reflection", "//toolchain/base:index_base", "//toolchain/parse:tree", - "//toolchain/sem_ir:builtin_kind", - "//toolchain/sem_ir:node_kind", "@llvm-project//llvm:Support", ], ) @@ -63,11 +63,12 @@ cc_library( srcs = ["file.cpp"], hdrs = ["file.h"], deps = [ + ":builtin_kind", + ":node", + ":node_kind", + ":value_stores", "//common:check", "//toolchain/base:value_store", - "//toolchain/sem_ir:builtin_kind", - "//toolchain/sem_ir:node", - "//toolchain/sem_ir:node_kind", "@llvm-project//llvm:Support", ], ) @@ -77,10 +78,10 @@ cc_library( srcs = ["formatter.cpp"], hdrs = ["formatter.h"], deps = [ + ":file", + ":node_kind", "//toolchain/lex:tokenized_buffer", "//toolchain/parse:tree", - "//toolchain/sem_ir:file", - "//toolchain/sem_ir:node_kind", "@llvm-project//llvm:Support", ], ) @@ -95,6 +96,16 @@ cc_library( ], ) +cc_library( + name = "value_stores", + hdrs = ["value_stores.h"], + deps = [ + ":node", + "//toolchain/base:value_store", + "@llvm-project//llvm:Support", + ], +) + cc_test( name = "typed_nodes_test", size = "small", diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 81dcfb74eb55..2a6ae8cc58d9 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -6,6 +6,7 @@ #include "common/check.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Sequence.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/base/value_store.h" #include "toolchain/sem_ir/builtin_kind.h" @@ -45,18 +46,21 @@ File::File(SharedValueStores& value_stores) filename_(""), // Builtins are always the first IR, even when self-referential. cross_reference_irs_({this}), - // Default entry for NodeBlockId::Empty. - node_blocks_(1) { - nodes_.reserve(BuiltinKind::ValidCount); + type_blocks_(allocator_), + node_blocks_(allocator_) { + // Default entry for NodeBlockId::Empty. + node_blocks_.AddDefaultValue(); + + nodes_.Reserve(BuiltinKind::ValidCount); // Error uses a self-referential type so that it's not accidentally treated as // a normal type. Every other builtin is a type, including the // self-referential TypeType. -#define CARBON_SEM_IR_BUILTIN_KIND(Name, ...) \ - nodes_.push_back(Builtin{BuiltinKind::Name == BuiltinKind::Error \ - ? TypeId::Error \ - : TypeId::TypeType, \ - BuiltinKind::Name}); +#define CARBON_SEM_IR_BUILTIN_KIND(Name, ...) \ + nodes_.AddInNoBlock(Builtin{BuiltinKind::Name == BuiltinKind::Error \ + ? TypeId::Error \ + : TypeId::TypeType, \ + BuiltinKind::Name}); #include "toolchain/sem_ir/builtin_kind.def" CARBON_CHECK(nodes_.size() == BuiltinKind::ValidCount) @@ -70,18 +74,21 @@ File::File(SharedValueStores& value_stores, std::string filename, filename_(std::move(filename)), // Builtins are always the first IR. cross_reference_irs_({builtins}), - // Default entry for NodeBlockId::Empty. - node_blocks_(1) { + type_blocks_(allocator_), + node_blocks_(allocator_) { CARBON_CHECK(builtins != nullptr); CARBON_CHECK(builtins->cross_reference_irs_[0] == builtins) << "Not called with builtins!"; + // Default entry for NodeBlockId::Empty. + node_blocks_.AddDefaultValue(); + // Copy builtins over. - nodes_.reserve(BuiltinKind::ValidCount); + nodes_.Reserve(BuiltinKind::ValidCount); static constexpr auto BuiltinIR = CrossReferenceIRId(0); - for (auto [i, node] : llvm::enumerate(builtins->nodes_)) { + for (auto [i, node] : llvm::enumerate(builtins->nodes_.array_ref())) { // We can reuse builtin type IDs because they're special-cased values. - nodes_.push_back( + nodes_.AddInNoBlock( CrossReference{node.type_id(), BuiltinIR, SemIR::NodeId(i)}); } } @@ -97,8 +104,9 @@ auto File::Verify() const -> ErrorOr { for (const Function& function : functions_.array_ref()) { for (NodeBlockId block_id : function.body_block_ids) { TerminatorKind prior_kind = TerminatorKind::NotTerminator; - for (NodeId node_id : GetNodeBlock(block_id)) { - TerminatorKind node_kind = GetNode(node_id).kind().terminator_kind(); + for (NodeId node_id : node_blocks().Get(block_id)) { + TerminatorKind node_kind = + nodes().Get(node_id).kind().terminator_kind(); if (prior_kind == TerminatorKind::Terminator) { return Error(llvm::formatv("Node {0} in block {1} follows terminator", node_id, block_id)); @@ -143,12 +151,13 @@ static auto PrintList( } // PrintBlock is only used for vectors. -template +template static auto PrintBlock(llvm::raw_ostream& out, llvm::StringLiteral block_name, - const llvm::SmallVector& blocks) { + const BlockValueStoreT& blocks) { out.indent(BaseIndent); out << block_name << ": [\n"; - for (const auto& block : blocks) { + for (int index : llvm::seq(blocks.size())) { + auto block = blocks.Get(BlockIdT(index)); out.indent(BaseIndent + IndentStep); out << "[\n"; @@ -172,15 +181,15 @@ auto File::Print(llvm::raw_ostream& out, bool include_builtins) const -> void { PrintList(out, "functions", functions_.array_ref()); PrintList(out, "classes", classes_.array_ref()); PrintList(out, "types", types_.array_ref()); - PrintBlock(out, "type_blocks", type_blocks_); + PrintBlock(out, "type_blocks", type_blocks_); - llvm::ArrayRef nodes = nodes_; + auto nodes = nodes_.array_ref(); if (!include_builtins) { nodes = nodes.drop_front(BuiltinKind::ValidCount); } PrintList(out, "nodes", nodes); - PrintBlock(out, "node_blocks", node_blocks_); + PrintBlock(out, "node_blocks", node_blocks_); } // Map a node kind representing a type into an integer describing the @@ -289,7 +298,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, continue; } - auto node = GetNode(step.node_id); + auto node = nodes().Get(step.node_id); // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { @@ -318,7 +327,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, // Add parentheses if required. auto inner_type_node_id = GetTypeAllowBuiltinTypes(node.As().inner_id); - if (GetTypePrecedence(GetNode(inner_type_node_id).kind()) < + if (GetTypePrecedence(nodes().Get(inner_type_node_id).kind()) < GetTypePrecedence(node.kind())) { out << "("; steps.push_back(step.Next()); @@ -345,7 +354,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, break; } case StructType::Kind: { - auto refs = GetNodeBlock(node.As().fields_id); + auto refs = node_blocks().Get(node.As().fields_id); if (refs.empty()) { out << "{}"; break; @@ -370,7 +379,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, break; } case TupleType::Kind: { - auto refs = GetTypeBlock(node.As().elements_id); + auto refs = type_blocks().Get(node.As().elements_id); if (refs.empty()) { out << "()"; break; @@ -445,10 +454,10 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, // For `{}` or any tuple type, we've printed a non-type expression, so add a // conversion to type `type` if it's not implied by the context. if (!in_type_context) { - auto outer_node = GetNode(outer_node_id); + auto outer_node = nodes().Get(outer_node_id); if (outer_node.Is() || (outer_node.Is() && - GetNodeBlock(outer_node.As().fields_id).empty())) { + node_blocks().Get(outer_node.As().fields_id).empty())) { out << " as type"; } } @@ -460,7 +469,7 @@ auto GetExpressionCategory(const File& file, NodeId node_id) -> ExpressionCategory { const File* ir = &file; while (true) { - auto node = ir->GetNode(node_id); + auto node = ir->nodes().Get(node_id); // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 3758d0968e5e..84b8b91684bf 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -5,13 +5,13 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_FILE_H_ #define CARBON_TOOLCHAIN_SEM_IR_FILE_H_ -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/FormatVariadic.h" #include "toolchain/base/value_store.h" #include "toolchain/sem_ir/node.h" +#include "toolchain/sem_ir/value_stores.h" namespace Carbon::SemIR { @@ -145,7 +145,7 @@ class File : public Printable { // Returns array bound value from the bound node. auto GetArrayBoundValue(NodeId bound_id) const -> uint64_t { return integers() - .Get(GetNodeAs(bound_id).integer_id) + .Get(nodes().GetAs(bound_id).integer_id) .getZExtValue(); } @@ -154,104 +154,6 @@ class File : public Printable { return *cross_reference_irs_[xref_id.index]; } - // Adds a name scope, returning an ID to reference it. - auto AddNameScope() -> NameScopeId { - NameScopeId name_scopes_id(name_scopes_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(name_scopes_id.index >= 0); - name_scopes_.resize(name_scopes_id.index + 1); - return name_scopes_id; - } - - // Adds an entry to a name scope. Returns true on success, false on - // duplicates. - auto AddNameScopeEntry(NameScopeId scope_id, StringId name_id, - NodeId target_id) -> bool { - return name_scopes_[scope_id.index].insert({name_id, target_id}).second; - } - - // Returns the requested name scope. - auto GetNameScope(NameScopeId scope_id) const - -> const llvm::DenseMap& { - return name_scopes_[scope_id.index]; - } - - // Adds a node to the node list, returning an ID to reference the node. Note - // that this doesn't add the node to any node block. Check::Context::AddNode - // or NodeBlockStack::AddNode should usually be used instead, to add the node - // to the current block. - auto AddNodeInNoBlock(Node node) -> NodeId { - NodeId node_id(nodes_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(node_id.index >= 0); - nodes_.push_back(node); - return node_id; - } - - // Overwrites a given node with a new value. - auto ReplaceNode(NodeId node_id, Node node) -> void { - nodes_[node_id.index] = node; - } - - // Returns the requested node. - auto GetNode(NodeId node_id) const -> Node { return nodes_[node_id.index]; } - - // Returns the requested node, which is known to have the specified type. - template - auto GetNodeAs(NodeId node_id) const -> NodeT { - return GetNode(node_id).As(); - } - - // Reserves and returns a node block ID. The contents of the node block - // should be specified by calling SetNodeBlock, or by pushing the ID onto the - // NodeBlockStack. - auto AddNodeBlockId() -> NodeBlockId { - NodeBlockId id(node_blocks_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(id.index >= 0); - node_blocks_.push_back({}); - return id; - } - - // Sets the contents of an empty node block to the given content. - auto SetNodeBlock(NodeBlockId block_id, llvm::ArrayRef content) - -> void { - CARBON_CHECK(block_id != NodeBlockId::Unreachable); - CARBON_CHECK(node_blocks_[block_id.index].empty()) - << "node block content set more than once"; - node_blocks_[block_id.index] = AllocateCopy(content); - } - - // Adds a node block with the given content, returning an ID to reference it. - auto AddNodeBlock(llvm::ArrayRef content) -> NodeBlockId { - NodeBlockId id(node_blocks_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(id.index >= 0); - node_blocks_.push_back(AllocateCopy(content)); - return id; - } - - // Adds a node block of the given size. - auto AddUninitializedNodeBlock(size_t size) -> NodeBlockId { - NodeBlockId id(node_blocks_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(id.index >= 0); - node_blocks_.push_back(AllocateUninitialized(size)); - return id; - } - - // Returns the requested node block. - auto GetNodeBlock(NodeBlockId block_id) const -> llvm::ArrayRef { - CARBON_CHECK(block_id != NodeBlockId::Unreachable); - return node_blocks_[block_id.index]; - } - - // Returns the requested node block. - auto GetNodeBlock(NodeBlockId block_id) -> llvm::MutableArrayRef { - CARBON_CHECK(block_id != NodeBlockId::Unreachable); - return node_blocks_[block_id.index]; - } - // Marks a type as complete, and sets its value representation. auto CompleteType(TypeId object_type_id, ValueRepresentation value_representation) -> void { @@ -296,26 +198,9 @@ class File : public Printable { // Gets the pointee type of the given type, which must be a pointer type. auto GetPointeeType(TypeId pointer_id) const -> TypeId { - return GetNodeAs(types().Get(pointer_id).node_id).pointee_id; - } - - // Adds a type block with the given content, returning an ID to reference it. - auto AddTypeBlock(llvm::ArrayRef content) -> TypeBlockId { - TypeBlockId id(type_blocks_.size()); - // TODO: Return failure on overflow instead of crashing. - CARBON_CHECK(id.index >= 0); - type_blocks_.push_back(AllocateCopy(content)); - return id; - } - - // Returns the requested type block. - auto GetTypeBlock(TypeBlockId block_id) const -> llvm::ArrayRef { - return type_blocks_[block_id.index]; - } - - // Returns the requested type block. - auto GetTypeBlock(TypeBlockId block_id) -> llvm::MutableArrayRef { - return type_blocks_[block_id.index]; + return nodes() + .GetAs(types().Get(pointer_id).node_id) + .pointee_id; } // Produces a string version of a type. If `in_type_context` is false, an @@ -352,11 +237,20 @@ class File : public Printable { } auto classes() -> ValueStore& { return classes_; } auto classes() const -> const ValueStore& { return classes_; } + auto name_scopes() -> NameScopeStore& { return name_scopes_; } + auto name_scopes() const -> const NameScopeStore& { return name_scopes_; } auto types() -> ValueStore& { return types_; } auto types() const -> const ValueStore& { return types_; } - - auto nodes_size() const -> int { return nodes_.size(); } - auto node_blocks_size() const -> int { return node_blocks_.size(); } + auto type_blocks() -> BlockValueStore& { + return type_blocks_; + } + auto type_blocks() const -> const BlockValueStore& { + return type_blocks_; + } + auto nodes() -> NodeStore& { return nodes_; } + auto nodes() const -> const NodeStore& { return nodes_; } + auto node_blocks() -> NodeBlockStore& { return node_blocks_; } + auto node_blocks() const -> const NodeBlockStore& { return node_blocks_; } // A list of types that were completed in this file, in the order in which // they were completed. Earlier types in this list cannot contain instances of @@ -377,25 +271,6 @@ class File : public Printable { auto filename() const -> llvm::StringRef { return filename_; } private: - // Allocates an uninitialized array using our slab allocator. - template - auto AllocateUninitialized(std::size_t size) -> llvm::MutableArrayRef { - // We're not going to run a destructor, so ensure that's OK. - static_assert(std::is_trivially_destructible_v); - - T* storage = - static_cast(allocator_.Allocate(size * sizeof(T), alignof(T))); - return llvm::MutableArrayRef(storage, size); - } - - // Allocates a copy of the given data using our slab allocator. - template - auto AllocateCopy(llvm::ArrayRef data) -> llvm::MutableArrayRef { - auto result = AllocateUninitialized(data.size()); - std::uninitialized_copy(data.begin(), data.end(), result.begin()); - return result; - } - bool has_errors_ = false; // Shared, compile-scoped values. @@ -420,7 +295,7 @@ class File : public Printable { llvm::SmallVector cross_reference_irs_; // Storage for name scopes. - llvm::SmallVector> name_scopes_; + NameScopeStore name_scopes_; // Descriptions of types used in this file. ValueStore types_; @@ -430,15 +305,15 @@ class File : public Printable { // Type blocks within the IR. These reference entries in types_. Storage for // the data is provided by allocator_. - llvm::SmallVector> type_blocks_; + BlockValueStore type_blocks_; // All nodes. The first entries will always be cross-references to builtins, // at indices matching BuiltinKind ordering. - llvm::SmallVector nodes_; + NodeStore nodes_; // Node blocks within the IR. These reference entries in nodes_. Storage for // the data is provided by allocator_. - llvm::SmallVector> node_blocks_; + NodeBlockStore node_blocks_; // The top node block ID. NodeBlockId top_node_block_id_ = NodeBlockId::Invalid; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 50495f815dfa..3cf41ffda383 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -36,8 +36,8 @@ class NodeNamer { : tokenized_buffer_(tokenized_buffer), parse_tree_(parse_tree), semantics_ir_(semantics_ir) { - nodes.resize(semantics_ir.nodes_size()); - labels.resize(semantics_ir.node_blocks_size()); + nodes.resize(semantics_ir.nodes().size()); + labels.resize(semantics_ir.node_blocks().size()); scopes.resize(1 + semantics_ir.functions().size() + semantics_ir.classes().size()); @@ -62,7 +62,7 @@ class NodeNamer { nodes[fn.return_slot_id.index] = { fn_scope, GetScopeInfo(fn_scope).nodes.AllocateName( - *this, semantics_ir.GetNode(fn.return_slot_id).parse_node(), + *this, semantics_ir.nodes().Get(fn.return_slot_id).parse_node(), "return")}; } if (!fn.body_block_ids.empty()) { @@ -288,9 +288,9 @@ class NodeNamer { } if (parse_node == Parse::Node::Invalid) { - if (const auto& block = semantics_ir_.GetNodeBlock(block_id); + if (const auto& block = semantics_ir_.node_blocks().Get(block_id); !block.empty()) { - parse_node = semantics_ir_.GetNode(block.front()).parse_node(); + parse_node = semantics_ir_.nodes().Get(block.front()).parse_node(); } } @@ -379,12 +379,12 @@ class NodeNamer { Scope& scope = GetScopeInfo(scope_idx); // Use bound names where available. Otherwise, assign a backup name. - for (auto node_id : semantics_ir_.GetNodeBlock(block_id)) { + for (auto node_id : semantics_ir_.node_blocks().Get(block_id)) { if (!node_id.is_valid()) { continue; } - auto node = semantics_ir_.GetNode(node_id); + auto node = semantics_ir_.nodes().Get(node_id); auto add_node_name = [&](std::string name) { nodes[node_id.index] = {scope_idx, scope.nodes.AllocateName( *this, node.parse_node(), name)}; @@ -534,7 +534,8 @@ class Formatter { llvm::SaveAndRestore function_scope(scope_, node_namer_.GetScopeFor(id)); llvm::ListSeparator sep; - for (const NodeId param_id : semantics_ir_.GetNodeBlock(fn.param_refs_id)) { + for (const NodeId param_id : + semantics_ir_.node_blocks().Get(fn.param_refs_id)) { out_ << sep; if (!param_id.is_valid()) { out_ << "invalid"; @@ -542,7 +543,7 @@ class Formatter { } FormatNodeName(param_id); out_ << ": "; - FormatType(semantics_ir_.GetNode(param_id).type_id()); + FormatType(semantics_ir_.nodes().Get(param_id).type_id()); } out_ << ")"; if (fn.return_type_id.is_valid()) { @@ -577,7 +578,7 @@ class Formatter { return; } - for (const NodeId node_id : semantics_ir_.GetNodeBlock(block_id)) { + for (const NodeId node_id : semantics_ir_.node_blocks().Get(block_id)) { FormatInstruction(node_id); } } @@ -587,7 +588,7 @@ class Formatter { // Name scopes aren't kept in any particular order. Sort the entries before // we print them for stability and consistency. llvm::SmallVector> entries; - for (auto [name_id, node_id] : semantics_ir_.GetNameScope(id)) { + for (auto [name_id, node_id] : semantics_ir_.name_scopes().Get(id)) { entries.push_back({node_id, name_id}); } llvm::sort(entries, @@ -609,7 +610,7 @@ class Formatter { return; } - FormatInstruction(node_id, semantics_ir_.GetNode(node_id)); + FormatInstruction(node_id, semantics_ir_.nodes().Get(node_id)); } auto FormatInstruction(NodeId node_id, Node node) -> void { @@ -719,7 +720,7 @@ class Formatter { FormatArg(node.tuple_id); llvm::ArrayRef inits_and_return_slot = - semantics_ir_.GetNodeBlock(node.inits_and_return_slot_id); + semantics_ir_.node_blocks().Get(node.inits_and_return_slot_id); auto inits = inits_and_return_slot.drop_back(1); auto return_slot_id = inits_and_return_slot.back(); @@ -737,7 +738,7 @@ class Formatter { out_ << " "; FormatArg(node.callee_id); - llvm::ArrayRef args = semantics_ir_.GetNodeBlock(node.args_id); + llvm::ArrayRef args = semantics_ir_.node_blocks().Get(node.args_id); bool has_return_slot = GetInitializingRepresentation(semantics_ir_, node.type_id) @@ -775,7 +776,7 @@ class Formatter { auto FormatInstructionRHS(SpliceBlock node) -> void { FormatArgs(node.result_id); out_ << " {"; - if (!semantics_ir_.GetNodeBlock(node.block_id).empty()) { + if (!semantics_ir_.node_blocks().Get(node.block_id).empty()) { out_ << "\n"; indent_ += 2; FormatCodeBlock(node.block_id); @@ -792,9 +793,9 @@ class Formatter { auto FormatInstructionRHS(StructType node) -> void { out_ << " {"; llvm::ListSeparator sep; - for (auto field_id : semantics_ir_.GetNodeBlock(node.fields_id)) { + for (auto field_id : semantics_ir_.node_blocks().Get(node.fields_id)) { out_ << sep << "."; - auto field = semantics_ir_.GetNodeAs(field_id); + auto field = semantics_ir_.nodes().GetAs(field_id); FormatString(field.name_id); out_ << ": "; FormatType(field.field_type_id); @@ -836,7 +837,7 @@ class Formatter { auto FormatArg(NodeBlockId id) -> void { out_ << '('; llvm::ListSeparator sep; - for (auto node_id : semantics_ir_.GetNodeBlock(id)) { + for (auto node_id : semantics_ir_.node_blocks().Get(id)) { out_ << sep; FormatArg(node_id); } @@ -861,7 +862,7 @@ class Formatter { auto FormatArg(TypeBlockId id) -> void { out_ << '('; llvm::ListSeparator sep; - for (auto type_id : semantics_ir_.GetTypeBlock(id)) { + for (auto type_id : semantics_ir_.type_blocks().Get(id)) { out_ << sep; FormatArg(type_id); } diff --git a/toolchain/sem_ir/value_stores.h b/toolchain/sem_ir/value_stores.h new file mode 100644 index 000000000000..e1980325efcc --- /dev/null +++ b/toolchain/sem_ir/value_stores.h @@ -0,0 +1,151 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_SEM_IR_VALUE_STORES_H_ +#define CARBON_TOOLCHAIN_SEM_IR_VALUE_STORES_H_ + +#include "llvm/ADT/DenseMap.h" +#include "toolchain/base/value_store.h" +#include "toolchain/sem_ir/node.h" + +namespace Carbon::SemIR { + +// Provides a ValueStore wrapper for an API specific to nodes. +class NodeStore { + public: + // Adds a node to the node list, returning an ID to reference the node. Note + // that this doesn't add the node to any node block. Check::Context::AddNode + // or NodeBlockStack::AddNode should usually be used instead, to add the node + // to the current block. + auto AddInNoBlock(Node node) -> NodeId { return values_.Add(node); } + + // Returns the requested node. + auto Get(NodeId node_id) const -> Node { return values_.Get(node_id); } + + // Returns the requested node, which is known to have the specified type. + template + auto GetAs(NodeId node_id) const -> NodeT { + return Get(node_id).As(); + } + + // Overwrites a given node with a new value. + auto Set(NodeId node_id, Node node) -> void { values_.Get(node_id) = node; } + + // Reserves space. + auto Reserve(size_t size) -> void { values_.Reserve(size); } + + auto array_ref() const -> llvm::ArrayRef { return values_.array_ref(); } + auto size() const -> int { return values_.size(); } + + private: + ValueStore values_; +}; + +// Provides a ValueStore wrapper for an API specific to name scopes. +class NameScopeStore { + public: + // Adds a name scope, returning an ID to reference it. + auto Add() -> NameScopeId { return values_.AddDefaultValue(); } + + // Adds an entry to a name scope. Returns true on success, false on + // duplicates. + auto AddEntry(NameScopeId scope_id, StringId name_id, NodeId target_id) + -> bool { + return values_.Get(scope_id).insert({name_id, target_id}).second; + } + + // Returns the requested name scope. + auto Get(NameScopeId scope_id) const + -> const llvm::DenseMap& { + return values_.Get(scope_id); + } + + private: + ValueStore> values_; +}; + +// Provides a block-based ValueStore, which uses slab allocation of added +// blocks. This allows references to values to outlast vector resizes that might +// otherwise invalidate references. +// +// BlockValueStore is used as-is, but there are also children that expose the +// protected members for type-specific functionality. +template +class BlockValueStore { + public: + explicit BlockValueStore(llvm::BumpPtrAllocator& allocator) + : allocator_(&allocator) {} + + // Adds a block with the given content, returning an ID to reference it. + auto Add(llvm::ArrayRef content) -> IdT { + return values_.Add(AllocateCopy(content)); + } + + // Returns the requested block. + auto Get(IdT id) const -> llvm::ArrayRef { return values_.Get(id); } + + // Returns the requested block. + auto Get(IdT id) -> llvm::MutableArrayRef { return values_.Get(id); } + + auto size() const -> int { return values_.size(); } + + protected: + // Reserves and returns a block ID. The contents of the block + // should be specified by calling Set, or similar. + auto AddDefaultValue() -> NodeBlockId { return values_.AddDefaultValue(); } + + // Adds an uninitialized block of the given size. + auto AddUninitialized(size_t size) -> NodeBlockId { + return values_.Add(AllocateUninitialized(size)); + } + + // Sets the contents of an empty block to the given content. + auto Set(NodeBlockId block_id, llvm::ArrayRef content) -> void { + CARBON_CHECK(Get(block_id).empty()) + << "node block content set more than once"; + values_.Get(block_id) = AllocateCopy(content); + } + + private: + // Allocates an uninitialized array using our slab allocator. + auto AllocateUninitialized(std::size_t size) + -> llvm::MutableArrayRef { + // We're not going to run a destructor, so ensure that's OK. + static_assert(std::is_trivially_destructible_v); + + auto storage = static_cast( + allocator_->Allocate(size * sizeof(ValueT), alignof(ValueT))); + return llvm::MutableArrayRef(storage, size); + } + + // Allocates a copy of the given data using our slab allocator. + auto AllocateCopy(llvm::ArrayRef data) + -> llvm::MutableArrayRef { + auto result = AllocateUninitialized(data.size()); + std::uninitialized_copy(data.begin(), data.end(), result.begin()); + return result; + } + + llvm::BumpPtrAllocator* allocator_; + ValueStore> values_; +}; + +// Adapts BlockValueStore for node blocks. +class NodeBlockStore : public BlockValueStore { + public: + using BaseType = BlockValueStore; + + using BaseType::AddDefaultValue; + using BaseType::AddUninitialized; + using BaseType::BaseType; + + auto Set(NodeBlockId block_id, llvm::ArrayRef content) -> void { + CARBON_CHECK(block_id != NodeBlockId::Unreachable); + BlockValueStore::Set(block_id, content); + } +}; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_VALUE_STORES_H_