From c7a9e29a8914fb2353c0a4ae37249e7895584892 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 10 Oct 2023 22:39:59 -0700 Subject: [PATCH] Add typed nodes to SemIR. (#3280) Replace `SemIR::Node::GetAsFoo` and `SemIR::Node::Foo::Make` with `SemIR::Foo` class that represents a particular kind of node, with named fields. Rename `SemIR::IntegerLiteral` and `SemIR::RealLiteral` to `IntegerValue` / `RealValue` to better reflect their purpose and avoid a name collision with the corresponding `SemIR` node kinds. Remove `NodeKind::Invalid` and the `SemIR::Node` default constructor entirely, as they were not used for anything. --- common/BUILD | 16 + common/struct_reflection.h | 162 ++++ common/struct_reflection_test.cpp | 97 +++ toolchain/check/context.cpp | 92 +- toolchain/check/convert.cpp | 224 ++--- toolchain/check/declaration_name_stack.cpp | 3 +- toolchain/check/handle_array.cpp | 8 +- toolchain/check/handle_call_expression.cpp | 13 +- toolchain/check/handle_function.cpp | 17 +- toolchain/check/handle_if_statement.cpp | 2 +- toolchain/check/handle_index.cpp | 40 +- toolchain/check/handle_let.cpp | 13 +- toolchain/check/handle_literal.cpp | 12 +- toolchain/check/handle_name.cpp | 29 +- toolchain/check/handle_namespace.cpp | 4 +- toolchain/check/handle_operator.cpp | 43 +- toolchain/check/handle_paren.cpp | 4 +- toolchain/check/handle_pattern_binding.cpp | 10 +- toolchain/check/handle_statement.cpp | 12 +- toolchain/check/handle_struct.cpp | 16 +- toolchain/check/handle_variable.cpp | 7 +- toolchain/check/pending_block.h | 11 +- .../testdata/basics/builtin_nodes.carbon | 4 +- .../multifile_raw_and_textual_ir.carbon | 8 +- .../testdata/basics/multifile_raw_ir.carbon | 8 +- .../testdata/basics/raw_and_textual_ir.carbon | 4 +- toolchain/check/testdata/basics/raw_ir.carbon | 4 +- toolchain/lower/file_context.cpp | 34 +- toolchain/lower/function_context.cpp | 6 +- toolchain/lower/function_context.h | 2 +- toolchain/lower/handle.cpp | 286 +++---- .../lower/handle_expression_category.cpp | 39 +- toolchain/lower/handle_type.cpp | 10 +- toolchain/sem_ir/BUILD | 1 + toolchain/sem_ir/file.cpp | 102 +-- toolchain/sem_ir/file.h | 48 +- toolchain/sem_ir/file_test.cpp | 4 +- toolchain/sem_ir/formatter.cpp | 172 ++-- toolchain/sem_ir/node.cpp | 26 +- toolchain/sem_ir/node.h | 804 ++++++++++++------ toolchain/sem_ir/node_kind.def | 2 - 41 files changed, 1445 insertions(+), 954 deletions(-) create mode 100644 common/struct_reflection.h create mode 100644 common/struct_reflection_test.cpp diff --git a/common/BUILD b/common/BUILD index 20e84374bc6b..f337eedd8696 100644 --- a/common/BUILD +++ b/common/BUILD @@ -153,6 +153,22 @@ cc_test( ], ) +cc_library( + name = "struct_reflection", + hdrs = ["struct_reflection.h"], +) + +cc_test( + name = "struct_reflection_test", + size = "small", + srcs = ["struct_reflection_test.cpp"], + deps = [ + ":struct_reflection", + "//testing/base:gtest_main", + "@com_google_googletest//:gtest", + ], +) + cc_library( name = "vlog", srcs = ["vlog_internal.h"], diff --git a/common/struct_reflection.h b/common/struct_reflection.h new file mode 100644 index 000000000000..560cb5bb1574 --- /dev/null +++ b/common/struct_reflection.h @@ -0,0 +1,162 @@ +// 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_COMMON_STRUCT_REFLECTION_H_ +#define CARBON_COMMON_STRUCT_REFLECTION_H_ + +// Reflection support for simple struct types. +// +// Example usage: +// +// ``` +// struct A { int x; std::string y; }; +// +// A a; +// std::tuple t = StructReflection::AsTuple(a); +// ``` +// +// Limitations: +// +// - Only simple aggregate structs are supported. Types with base classes, +// non-public data members, constructors, or virtual functions are not +// supported. +// - Structs with more than 5 fields are not supported. This limit is easy to +// increase if needed, but removing it entirely is hard. +// - Structs containing a reference to the same type are not supported. + +#include +#include + +namespace Carbon::StructReflection { + +namespace Internal { + +// A type that can be converted to any field type within type T. +template +struct AnyField { + template + operator FieldT&() const; + template + operator FieldT&&() const; + + // Don't allow conversion to T itself. This ensures we don't match against a + // copy or move constructor. + operator T&() const = delete; + operator T&&() const = delete; +}; + +// The detection mechanism below intentionally misses field initializers. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + +// Detector for whether we can list-initialize T from the given list of fields. +template +constexpr bool CanListInitialize(decltype(T{Fields()...})*) { + return true; +} +template +constexpr bool CanListInitialize(...) { + return false; +} + +#pragma clang diagnostic pop + +// Simple detector to find the number of data fields in a struct. This proceeds +// in two passes: +// +// 1) Add AnyFields until we can initialize T from our list of initializers. +// 2) Add more AnyFields until we can't initialize any more. +template +constexpr auto CountFields() -> int { + if constexpr (CanListInitialize(0)) { + return CountFields>(); + } else if constexpr (AnyWorkedSoFar) { + static_assert(sizeof...(Fields) <= 5, + "Unsupported: too many fields in struct"); + return sizeof...(Fields) - 1; + } else if constexpr (sizeof...(Fields) > 32) { + // If we go too far without finding a working initializer, something + // probably went wrong with our calculation. Bail out before we recurse too + // deeply. + static_assert(sizeof...(Fields) <= 32, + "Internal error, could not count fields in struct"); + } else { + return CountFields>(); + } +} + +// Utility to access fields by index. +template +struct FieldAccessor; + +template <> +struct FieldAccessor<0> { + template + static auto Get(T& /*value*/) -> auto { + return std::tuple<>(); + } +}; + +template <> +struct FieldAccessor<1> { + template + static auto Get(T& value) -> auto { + auto& [field0] = value; + return std::tuple(field0); + } +}; + +template <> +struct FieldAccessor<2> { + template + static auto Get(T& value) -> auto { + auto& [field0, field1] = value; + return std::tuple(field0, field1); + } +}; + +template <> +struct FieldAccessor<3> { + template + static auto Get(T& value) -> auto { + auto& [field0, field1, field2] = value; + return std::tuple( + field0, field1, field2); + } +}; + +template <> +struct FieldAccessor<4> { + template + static auto Get(T& value) -> auto { + auto& [field0, field1, field2, field3] = value; + return std::tuple(field0, field1, field2, field3); + } +}; + +template <> +struct FieldAccessor<5> { + template + static auto Get(T& value) -> auto { + auto& [field0, field1, field2, field3, field4] = value; + return std::tuple( + field0, field1, field2, field3, field4); + } +}; + +} // namespace Internal + +// Get the fields of the struct `T` as a tuple. +template +auto AsTuple(T value) -> auto { + // We use aggregate initialization to detect the number of fields. + static_assert(std::is_aggregate_v, "Only aggregates are supported"); + return Internal::FieldAccessor()>::Get(value); +} + +} // namespace Carbon::StructReflection + +#endif // CARBON_COMMON_STRUCT_REFLECTION_H_ diff --git a/common/struct_reflection_test.cpp b/common/struct_reflection_test.cpp new file mode 100644 index 000000000000..ec53639a61f0 --- /dev/null +++ b/common/struct_reflection_test.cpp @@ -0,0 +1,97 @@ +// 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 "common/struct_reflection.h" + +#include + +namespace Carbon::StructReflection { +namespace { + +struct ZeroFields {}; + +struct OneField { + int x; +}; + +struct TwoFields { + int x; + int y; +}; + +struct ReferenceField { + int& ref; +}; + +struct NoDefaultConstructor { + NoDefaultConstructor(int n) : v(n) {} + int v; +}; + +struct OneFieldNoDefaultConstructor { + NoDefaultConstructor x; +}; + +struct TwoFieldsNoDefaultConstructor { + NoDefaultConstructor x; + NoDefaultConstructor y; +}; + +TEST(StructReflectionTest, CanListInitialize) { + { + using Type = OneField; + using Field = Internal::AnyField; + static_assert(Internal::CanListInitialize(0)); + static_assert(Internal::CanListInitialize(0)); + static_assert(!Internal::CanListInitialize(0)); + } + + { + using Type = OneFieldNoDefaultConstructor; + using Field = Internal::AnyField; + static_assert(!Internal::CanListInitialize(0)); + static_assert(Internal::CanListInitialize(0)); + static_assert(!Internal::CanListInitialize(0)); + } +} + +TEST(StructReflectionTest, CountFields) { + static_assert(Internal::CountFields() == 0); + static_assert(Internal::CountFields() == 1); + static_assert(Internal::CountFields() == 2); + static_assert(Internal::CountFields() == 1); + static_assert(Internal::CountFields() == 1); +} + +TEST(StructReflectionTest, EmptyStruct) { + std::tuple<> fields = AsTuple(ZeroFields()); + static_cast(fields); +} + +TEST(StructReflectionTest, OneField) { + std::tuple fields = AsTuple(OneField{.x = 1}); + EXPECT_EQ(std::get<0>(fields), 1); +} + +TEST(StructReflectionTest, TwoField) { + std::tuple fields = AsTuple(TwoFields{.x = 1, .y = 2}); + EXPECT_EQ(std::get<0>(fields), 1); + EXPECT_EQ(std::get<1>(fields), 2); +} + +TEST(StructReflectionTest, NoDefaultConstructor) { + std::tuple fields = + AsTuple(TwoFieldsNoDefaultConstructor{.x = 1, .y = 2}); + EXPECT_EQ(std::get<0>(fields).v, 1); + EXPECT_EQ(std::get<1>(fields).v, 2); +} + +TEST(StructReflectionTest, ReferenceField) { + int n = 0; + std::tuple fields = AsTuple(ReferenceField{.ref = n}); + EXPECT_EQ(&std::get<0>(fields), &n); +} + +} // namespace +} // namespace Carbon::StructReflection diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 4f1121e9097b..50adee0b5bfd 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -149,13 +149,11 @@ auto Context::FollowNameReferences(SemIR::NodeId node_id) -> SemIR::NodeId { auto node = semantics_ir().GetNode(node_id); switch (node.kind()) { case SemIR::NodeKind::NameReference: { - auto [name_id, value_id] = node.GetAsNameReference(); - node_id = value_id; + node_id = node.As().value_id; break; } case SemIR::NodeKind::NameReferenceUntyped: { - auto [name_id, value_id] = node.GetAsNameReferenceUntyped(); - node_id = value_id; + node_id = node.As().value_id; break; } default: @@ -172,27 +170,27 @@ static auto AddDominatedBlockAndBranchImpl(Context& context, return SemIR::NodeBlockId::Unreachable; } auto block_id = context.semantics_ir().AddNodeBlockId(); - context.AddNode(BranchNode::Make(parse_node, block_id, args...)); + context.AddNode(BranchNode(parse_node, block_id, args...)); return block_id; } auto Context::AddDominatedBlockAndBranch(Parse::Node parse_node) -> SemIR::NodeBlockId { - return AddDominatedBlockAndBranchImpl(*this, parse_node); + return AddDominatedBlockAndBranchImpl(*this, parse_node); } auto Context::AddDominatedBlockAndBranchWithArg(Parse::Node parse_node, SemIR::NodeId arg_id) -> SemIR::NodeBlockId { - return AddDominatedBlockAndBranchImpl( - *this, parse_node, arg_id); + return AddDominatedBlockAndBranchImpl(*this, parse_node, + arg_id); } auto Context::AddDominatedBlockAndBranchIf(Parse::Node parse_node, SemIR::NodeId cond_id) -> SemIR::NodeBlockId { - return AddDominatedBlockAndBranchImpl( - *this, parse_node, cond_id); + return AddDominatedBlockAndBranchImpl(*this, parse_node, + cond_id); } auto Context::AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks) @@ -205,7 +203,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks) if (new_block_id == SemIR::NodeBlockId::Unreachable) { new_block_id = semantics_ir().AddNodeBlockId(); } - AddNode(SemIR::Node::Branch::Make(parse_node, new_block_id)); + AddNode(SemIR::Branch(parse_node, new_block_id)); } node_block_stack().Pop(); } @@ -223,8 +221,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( if (new_block_id == SemIR::NodeBlockId::Unreachable) { new_block_id = semantics_ir().AddNodeBlockId(); } - AddNode( - SemIR::Node::BranchWithArg::Make(parse_node, new_block_id, arg_id)); + AddNode(SemIR::BranchWithArg(parse_node, new_block_id, arg_id)); } node_block_stack().Pop(); } @@ -233,8 +230,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( // Acquire the result value. SemIR::TypeId result_type_id = semantics_ir().GetNode(*block_args.begin()).type_id(); - return AddNode( - SemIR::Node::BlockArg::Make(parse_node, result_type_id, new_block_id)); + return AddNode(SemIR::BlockArg(parse_node, result_type_id, new_block_id)); } // Add the current code block to the enclosing function. @@ -247,9 +243,10 @@ auto Context::AddCurrentCodeBlockToFunction() -> void { return; } - auto function_id = semantics_ir() - .GetNode(return_scope_stack().back()) - .GetAsFunctionDeclaration(); + auto function_id = + semantics_ir() + .GetNodeAs(return_scope_stack().back()) + .function_id; semantics_ir() .GetFunction(function_id) .body_block_ids.push_back(node_block_stack().PeekOrAdd()); @@ -339,45 +336,49 @@ static auto ProfileType(Context& semantics_context, SemIR::Node node, llvm::FoldingSetNodeID& canonical_id) -> void { switch (node.kind()) { case SemIR::NodeKind::ArrayType: { - auto [bound_id, element_type_id] = node.GetAsArrayType(); + auto array_type = node.As(); canonical_id.AddInteger( - semantics_context.semantics_ir().GetArrayBoundValue(bound_id)); - canonical_id.AddInteger(element_type_id.index); + semantics_context.semantics_ir().GetArrayBoundValue( + array_type.bound_id)); + canonical_id.AddInteger(array_type.element_type_id.index); break; } case SemIR::NodeKind::Builtin: - canonical_id.AddInteger(node.GetAsBuiltin().AsInt()); + canonical_id.AddInteger(node.As().builtin_kind.AsInt()); break; case SemIR::NodeKind::CrossReference: { // TODO: Cross-references should be canonicalized by looking at their // target rather than treating them as new unique types. - auto [xref_id, node_id] = node.GetAsCrossReference(); - canonical_id.AddInteger(xref_id.index); - canonical_id.AddInteger(node_id.index); + auto xref = node.As(); + canonical_id.AddInteger(xref.ir_id.index); + canonical_id.AddInteger(xref.node_id.index); break; } case SemIR::NodeKind::ConstType: canonical_id.AddInteger( - semantics_context.GetUnqualifiedType(node.GetAsConstType()).index); + semantics_context + .GetUnqualifiedType(node.As().inner_id) + .index); break; case SemIR::NodeKind::PointerType: - canonical_id.AddInteger(node.GetAsPointerType().index); + canonical_id.AddInteger(node.As().pointee_id.index); break; case SemIR::NodeKind::StructType: { - auto refs = - semantics_context.semantics_ir().GetNodeBlock(node.GetAsStructType()); - for (const auto& ref_id : refs) { - auto ref = semantics_context.semantics_ir().GetNode(ref_id); - auto [name_id, type_id] = ref.GetAsStructTypeField(); - canonical_id.AddInteger(name_id.index); - canonical_id.AddInteger(type_id.index); + auto fields = semantics_context.semantics_ir().GetNodeBlock( + node.As().fields_id); + for (const auto& field_id : fields) { + auto field = + semantics_context.semantics_ir().GetNodeAs( + field_id); + canonical_id.AddInteger(field.name_id.index); + canonical_id.AddInteger(field.type_id.index); } break; } case SemIR::NodeKind::TupleType: - ProfileTupleType( - semantics_context.semantics_ir().GetTypeBlock(node.GetAsTupleType()), - canonical_id); + ProfileTupleType(semantics_context.semantics_ir().GetTypeBlock( + node.As().elements_id), + canonical_id); break; default: CARBON_FATAL() << "Unexpected type node " << node; @@ -410,8 +411,8 @@ auto Context::CanonicalizeType(SemIR::NodeId node_id) -> SemIR::TypeId { auto Context::CanonicalizeStructType(Parse::Node parse_node, SemIR::NodeBlockId refs_id) -> SemIR::TypeId { - return CanonicalizeTypeAndAddNodeIfNew(SemIR::Node::StructType::Make( - parse_node, SemIR::TypeId::TypeType, refs_id)); + return CanonicalizeTypeAndAddNodeIfNew( + SemIR::StructType(parse_node, SemIR::TypeId::TypeType, refs_id)); } auto Context::CanonicalizeTupleType(Parse::Node parse_node, @@ -422,9 +423,8 @@ auto Context::CanonicalizeTupleType(Parse::Node parse_node, ProfileTupleType(type_ids, canonical_id); }; auto make_tuple_node = [&] { - return AddNode( - SemIR::Node::TupleType::Make(parse_node, SemIR::TypeId::TypeType, - semantics_ir_->AddTypeBlock(type_ids))); + return AddNode(SemIR::TupleType(parse_node, SemIR::TypeId::TypeType, + semantics_ir_->AddTypeBlock(type_ids))); }; return CanonicalizeTypeImpl(SemIR::NodeKind::TupleType, profile_tuple, make_tuple_node); @@ -432,15 +432,15 @@ auto Context::CanonicalizeTupleType(Parse::Node parse_node, auto Context::GetPointerType(Parse::Node parse_node, SemIR::TypeId pointee_type_id) -> SemIR::TypeId { - return CanonicalizeTypeAndAddNodeIfNew(SemIR::Node::PointerType::Make( - parse_node, SemIR::TypeId::TypeType, pointee_type_id)); + return CanonicalizeTypeAndAddNodeIfNew( + SemIR::PointerType(parse_node, SemIR::TypeId::TypeType, pointee_type_id)); } auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId { SemIR::Node type_node = semantics_ir_->GetNode(semantics_ir_->GetTypeAllowBuiltinTypes(type_id)); - if (type_node.kind() == SemIR::NodeKind::ConstType) { - return type_node.GetAsConstType(); + if (auto const_type = type_node.TryAs()) { + return const_type->inner_id; } return type_id; } diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index c22fd0af1517..3cfc2c2f9c72 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -37,21 +37,22 @@ static auto FindReturnSlotForInitializer(SemIR::File& semantics_ir, "filled in properly"; case SemIR::NodeKind::InitializeFrom: { - auto [src_id, dest_id] = init.GetAsInitializeFrom(); - return dest_id; + return init.As().dest_id; } case SemIR::NodeKind::Call: { - auto [refs_id, callee_id] = init.GetAsCall(); - if (!semantics_ir.GetFunction(callee_id).return_slot_id.is_valid()) { + auto call = init.As(); + if (!semantics_ir.GetFunction(call.function_id) + .return_slot_id.is_valid()) { return SemIR::NodeId::Invalid; } - return semantics_ir.GetNodeBlock(refs_id).back(); + return semantics_ir.GetNodeBlock(call.args_id).back(); } case SemIR::NodeKind::ArrayInit: { - auto [src_id, refs_id] = init.GetAsArrayInit(); - return semantics_ir.GetNodeBlock(refs_id).back(); + return semantics_ir + .GetNodeBlock(init.As().inits_and_return_slot_id) + .back(); } } } @@ -89,8 +90,8 @@ static auto FinalizeTemporary(Context& context, SemIR::NodeId init_id, << "initialized multiple times? Have " << semantics_ir.GetNode(return_slot_id); auto init = semantics_ir.GetNode(init_id); - return context.AddNode(SemIR::Node::Temporary::Make( - init.parse_node(), init.type_id(), return_slot_id, init_id)); + return context.AddNode(SemIR::Temporary(init.parse_node(), init.type_id(), + return_slot_id, init_id)); } if (discarded) { @@ -105,9 +106,9 @@ static auto FinalizeTemporary(Context& context, SemIR::NodeId init_id, // nodes. auto init = semantics_ir.GetNode(init_id); auto temporary_id = context.AddNode( - SemIR::Node::TemporaryStorage::Make(init.parse_node(), init.type_id())); - return context.AddNode(SemIR::Node::Temporary::Make( - init.parse_node(), init.type_id(), temporary_id, init_id)); + SemIR::TemporaryStorage(init.parse_node(), init.type_id())); + return context.AddNode(SemIR::Temporary(init.parse_node(), init.type_id(), + temporary_id, init_id)); } // Materialize a temporary to hold the result of the given expression if it is @@ -127,18 +128,18 @@ static auto MakeElemAccessNode(Context& context, Parse::Node parse_node, SemIR::NodeId aggregate_id, SemIR::TypeId elem_type_id, NodeBlockT& block, std::size_t i) { - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { // TODO: Add a new node kind for indexing an array at a constant index // so that we don't need an integer literal node here, and remove this // special case. - auto index_id = block.AddNode(SemIR::Node::IntegerLiteral::Make( + auto index_id = block.AddNode(SemIR::IntegerLiteral( parse_node, context.CanonicalizeType(SemIR::NodeId::BuiltinIntegerType), - context.semantics_ir().AddIntegerLiteral(llvm::APInt(32, i)))); + context.semantics_ir().AddInteger(llvm::APInt(32, i)))); return block.AddNode( - AccessNodeT::Make(parse_node, elem_type_id, aggregate_id, index_id)); + AccessNodeT(parse_node, elem_type_id, aggregate_id, index_id)); } else { - return block.AddNode(AccessNodeT::Make( - parse_node, elem_type_id, aggregate_id, SemIR::MemberIndex(i))); + return block.AddNode(AccessNodeT(parse_node, elem_type_id, aggregate_id, + SemIR::MemberIndex(i))); } } @@ -229,13 +230,13 @@ class CopyOnWriteBlock { // Performs a conversion from a tuple to an array type. Does not perform a // final conversion to the requested expression category. -static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, - SemIR::Node array_type, SemIR::NodeId value_id, - ConversionTarget target) -> SemIR::NodeId { +static auto ConvertTupleToArray(Context& context, + SemIR::TupleType::Data tuple_type, + SemIR::ArrayType::Data array_type, + SemIR::NodeId value_id, ConversionTarget target) + -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto [array_bound_id, element_type_id] = array_type.GetAsArrayType(); - auto tuple_elem_types_id = tuple_type.GetAsTupleType(); - const auto& tuple_elem_types = semantics_ir.GetTypeBlock(tuple_elem_types_id); + auto tuple_elem_types = semantics_ir.GetTypeBlock(tuple_type.elements_id); auto value = semantics_ir.GetNode(value_id); @@ -243,14 +244,14 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; - if (value.kind() == SemIR::NodeKind::TupleLiteral) { - literal_elems = semantics_ir.GetNodeBlock(value.GetAsTupleLiteral()); + if (auto tuple_literal = value.TryAs()) { + literal_elems = semantics_ir.GetNodeBlock(tuple_literal->elements_id); } else { value_id = MaterializeIfInitializing(context, value_id); } // Check that the tuple is the right size. - uint64_t array_bound = semantics_ir.GetArrayBoundValue(array_bound_id); + uint64_t array_bound = semantics_ir.GetArrayBoundValue(array_type.bound_id); if (tuple_elem_types.size() != array_bound) { CARBON_DIAGNOSTIC( ArrayInitFromLiteralArgCountMismatch, Error, @@ -276,8 +277,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, // destination for the array initialization if we weren't given one. SemIR::NodeId return_slot_id = target.init_id; if (!target.init_id.is_valid()) { - return_slot_id = target_block->AddNode(SemIR::Node::TemporaryStorage::Make( - value.parse_node(), target.type_id)); + return_slot_id = target_block->AddNode( + SemIR::TemporaryStorage(value.parse_node(), target.type_id)); } // Initialize each element of the array from the corresponding element of the @@ -289,11 +290,11 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, for (auto [i, src_type_id] : llvm::enumerate(tuple_elem_types)) { // TODO: This call recurses back into conversion. Switch to an iterative // approach. - auto init_id = ConvertAggregateElement( - context, value.parse_node(), value_id, src_type_id, literal_elems, - ConversionTarget::FullInitializer, return_slot_id, element_type_id, - target_block, i); + auto init_id = + ConvertAggregateElement( + context, value.parse_node(), value_id, src_type_id, literal_elems, + ConversionTarget::FullInitializer, return_slot_id, + array_type.element_type_id, target_block, i); if (init_id == SemIR::NodeId::BuiltinError) { return SemIR::NodeId::BuiltinError; } @@ -305,19 +306,21 @@ static auto ConvertTupleToArray(Context& context, SemIR::Node tuple_type, target_block->InsertHere(); inits.push_back(return_slot_id); - return context.AddNode( - SemIR::Node::ArrayInit::Make(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.AddNodeBlock(inits))); } // Performs a conversion from a tuple to a tuple type. Does not perform a // final conversion to the requested expression category. -static auto ConvertTupleToTuple(Context& context, SemIR::Node src_type, - SemIR::Node dest_type, SemIR::NodeId value_id, - ConversionTarget target) -> SemIR::NodeId { +static auto ConvertTupleToTuple(Context& context, + SemIR::TupleType::Data src_type, + SemIR::TupleType::Data dest_type, + SemIR::NodeId value_id, ConversionTarget target) + -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto src_elem_types = semantics_ir.GetTypeBlock(src_type.GetAsTupleType()); - auto dest_elem_types = semantics_ir.GetTypeBlock(dest_type.GetAsTupleType()); + auto src_elem_types = semantics_ir.GetTypeBlock(src_type.elements_id); + auto dest_elem_types = semantics_ir.GetTypeBlock(dest_type.elements_id); auto value = semantics_ir.GetNode(value_id); @@ -325,8 +328,10 @@ static auto ConvertTupleToTuple(Context& context, SemIR::Node src_type, // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; - if (value.kind() == SemIR::NodeKind::TupleLiteral) { - literal_elems = semantics_ir.GetNodeBlock(value.GetAsTupleLiteral()); + 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); } else { value_id = MaterializeIfInitializing(context, value_id); } @@ -357,42 +362,40 @@ static auto ConvertTupleToTuple(Context& context, SemIR::Node src_type, // Initialize each element of the destination from the corresponding element // of the source. // TODO: Annotate diagnostics coming from here with the element index. - CopyOnWriteBlock new_block(semantics_ir, - value.kind() == SemIR::NodeKind::TupleLiteral - ? value.GetAsTupleLiteral() - : SemIR::NodeBlockId::Invalid, + CopyOnWriteBlock new_block(semantics_ir, literal_elems_id, src_elem_types.size()); for (auto [i, src_type_id, dest_type_id] : llvm::enumerate(src_elem_types, dest_elem_types)) { // TODO: This call recurses back into conversion. Switch to an iterative // approach. - auto init_id = ConvertAggregateElement( - context, value.parse_node(), value_id, src_type_id, literal_elems, - inner_kind, target.init_id, dest_type_id, target.init_block, i); + auto init_id = + ConvertAggregateElement( + context, value.parse_node(), value_id, src_type_id, literal_elems, + inner_kind, target.init_id, dest_type_id, target.init_block, i); if (init_id == SemIR::NodeId::BuiltinError) { return SemIR::NodeId::BuiltinError; } new_block.Set(i, init_id); } - return context.AddNode( - is_init - ? SemIR::Node::TupleInit::Make(value.parse_node(), target.type_id, - value_id, new_block.id()) - : SemIR::Node::TupleValue::Make(value.parse_node(), target.type_id, - value_id, new_block.id())); + return is_init ? context.AddNode(SemIR::TupleInit(value.parse_node(), + target.type_id, value_id, + new_block.id())) + : context.AddNode(SemIR::TupleValue(value.parse_node(), + target.type_id, value_id, + new_block.id())); } // Performs a conversion from a struct to a struct type. Does not perform a // final conversion to the requested expression category. -static auto ConvertStructToStruct(Context& context, SemIR::Node src_type, - SemIR::Node dest_type, SemIR::NodeId value_id, +static auto ConvertStructToStruct(Context& context, + SemIR::StructType::Data src_type, + SemIR::StructType::Data dest_type, + SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); - auto src_elem_fields = semantics_ir.GetNodeBlock(src_type.GetAsStructType()); - auto dest_elem_fields = - semantics_ir.GetNodeBlock(dest_type.GetAsStructType()); + auto src_elem_fields = semantics_ir.GetNodeBlock(src_type.fields_id); + auto dest_elem_fields = semantics_ir.GetNodeBlock(dest_type.fields_id); auto value = semantics_ir.GetNode(value_id); @@ -400,8 +403,10 @@ static auto ConvertStructToStruct(Context& context, SemIR::Node src_type, // directly. Otherwise, materialize a temporary if needed and index into the // result. llvm::ArrayRef literal_elems; - if (value.kind() == SemIR::NodeKind::StructLiteral) { - literal_elems = semantics_ir.GetNodeBlock(value.GetAsStructLiteral()); + 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); } else { value_id = MaterializeIfInitializing(context, value_id); } @@ -434,47 +439,45 @@ static auto ConvertStructToStruct(Context& context, SemIR::Node src_type, // Initialize each element of the destination from the corresponding element // of the source. // TODO: Annotate diagnostics coming from here with the element index. - CopyOnWriteBlock new_block(semantics_ir, - value.kind() == SemIR::NodeKind::StructLiteral - ? value.GetAsStructLiteral() - : SemIR::NodeBlockId::Invalid, + CopyOnWriteBlock new_block(semantics_ir, literal_elems_id, src_elem_fields.size()); for (auto [i, src_field_id, dest_field_id] : llvm::enumerate(src_elem_fields, dest_elem_fields)) { - auto [src_name_id, src_type_id] = - semantics_ir.GetNode(src_field_id).GetAsStructTypeField(); - auto [dest_name_id, dest_type_id] = - semantics_ir.GetNode(dest_field_id).GetAsStructTypeField(); - if (src_name_id != dest_name_id) { + auto src_field = + semantics_ir.GetNodeAs(src_field_id); + auto dest_field = + semantics_ir.GetNodeAs(dest_field_id); + if (src_field.name_id != dest_field.name_id) { CARBON_DIAGNOSTIC( StructInitFieldNameMismatch, Error, "Mismatched names for field {0} in struct initialization: " "source has field name `{1}`, destination has field name `{2}`.", size_t, llvm::StringRef, llvm::StringRef); context.emitter().Emit(value.parse_node(), StructInitFieldNameMismatch, - i + 1, semantics_ir.GetString(src_name_id), - semantics_ir.GetString(dest_name_id)); + i + 1, semantics_ir.GetString(src_field.name_id), + semantics_ir.GetString(dest_field.name_id)); return SemIR::NodeId::BuiltinError; } // TODO: This call recurses back into conversion. Switch to an iterative // approach. - auto init_id = ConvertAggregateElement( - context, value.parse_node(), value_id, src_type_id, literal_elems, - inner_kind, target.init_id, dest_type_id, target.init_block, i); + auto init_id = + ConvertAggregateElement( + context, value.parse_node(), value_id, src_field.type_id, + literal_elems, inner_kind, target.init_id, dest_field.type_id, + target.init_block, i); if (init_id == SemIR::NodeId::BuiltinError) { return SemIR::NodeId::BuiltinError; } new_block.Set(i, init_id); } - return context.AddNode( - is_init - ? SemIR::Node::StructInit::Make(value.parse_node(), target.type_id, - value_id, new_block.id()) - : SemIR::Node::StructValue::Make(value.parse_node(), target.type_id, - value_id, new_block.id())); + return is_init ? context.AddNode(SemIR::StructInit(value.parse_node(), + target.type_id, value_id, + new_block.id())) + : context.AddNode(SemIR::StructValue(value.parse_node(), + target.type_id, value_id, + new_block.id())); } // Returns whether `category` is a valid expression category to produce as a @@ -544,11 +547,11 @@ 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 (target_type_node.kind() == SemIR::NodeKind::TupleType) { + if (auto target_tuple_type = target_type_node.TryAs()) { auto value_type_node = semantics_ir.GetNode( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); - if (value_type_node.kind() == SemIR::NodeKind::TupleType) { - return ConvertTupleToTuple(context, value_type_node, target_type_node, + if (auto src_tuple_type = value_type_node.TryAs()) { + return ConvertTupleToTuple(context, *src_tuple_type, *target_tuple_type, value_id, target); } } @@ -557,21 +560,21 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, // {.f_p(1): U_p(1), .f_p(2): U_p(2), ..., .f_p(n): U_p(n)} if // (p(1), ..., p(n)) is a permutation of (1, ..., n) and each Ti converts // to Ui. - if (target_type_node.kind() == SemIR::NodeKind::StructType) { + if (auto target_struct_type = target_type_node.TryAs()) { auto value_type_node = semantics_ir.GetNode( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); - if (value_type_node.kind() == SemIR::NodeKind::StructType) { - return ConvertStructToStruct(context, value_type_node, target_type_node, - value_id, target); + if (auto src_struct_type = value_type_node.TryAs()) { + return ConvertStructToStruct(context, *src_struct_type, + *target_struct_type, value_id, target); } } // A tuple (T1, T2, ..., Tn) converts to [T; n] if each Ti converts to T. - if (target_type_node.kind() == SemIR::NodeKind::ArrayType) { + if (auto target_array_type = target_type_node.TryAs()) { auto value_type_node = semantics_ir.GetNode( semantics_ir.GetTypeAllowBuiltinTypes(value_type_id)); - if (value_type_node.kind() == SemIR::NodeKind::TupleType) { - return ConvertTupleToArray(context, value_type_node, target_type_node, + if (auto src_tuple_type = value_type_node.TryAs()) { + return ConvertTupleToArray(context, *src_tuple_type, *target_array_type, value_id, target); } } @@ -579,18 +582,14 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, if (target.type_id == SemIR::TypeId::TypeType) { // A tuple of types converts to type `type`. // TODO: This should apply even for non-literal tuples. - if (value.kind() == SemIR::NodeKind::TupleLiteral) { - auto tuple_block_id = value.GetAsTupleLiteral(); + if (auto tuple_literal = value.TryAs()) { llvm::SmallVector type_ids; - // If it is empty tuple type, we don't fetch anything. - if (tuple_block_id != SemIR::NodeBlockId::Empty) { - const auto& tuple_block = semantics_ir.GetNodeBlock(tuple_block_id); - for (auto tuple_node_id : tuple_block) { - // TODO: This call recurses back into conversion. Switch to an - // iterative approach. - type_ids.push_back( - ExpressionAsType(context, parse_node, tuple_node_id)); - } + for (auto tuple_node_id : + semantics_ir.GetNodeBlock(tuple_literal->elements_id)) { + // TODO: This call recurses back into conversion. Switch to an + // iterative approach. + type_ids.push_back( + ExpressionAsType(context, parse_node, tuple_node_id)); } auto tuple_type_id = context.CanonicalizeTupleType(parse_node, std::move(type_ids)); @@ -600,8 +599,9 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node, // `{}` converts to `{} as type`. // TODO: This conversion should also be performed for a non-literal value // of type `{}`. - if (value.kind() == SemIR::NodeKind::StructLiteral && - value.GetAsStructLiteral() == SemIR::NodeBlockId::Empty) { + if (auto struct_literal = value.TryAs(); + struct_literal && + struct_literal->elements_id == SemIR::NodeBlockId::Empty) { value_id = semantics_ir.GetTypeAllowBuiltinTypes(value_type_id); } } @@ -692,8 +692,8 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, if (target.kind != ConversionTarget::ValueOrReference && target.kind != ConversionTarget::Discarded) { // TODO: Support types with custom value representations. - expr_id = context.AddNode(SemIR::Node::BindValue::Make( - expr.parse_node(), expr.type_id(), expr_id)); + expr_id = context.AddNode( + SemIR::BindValue(expr.parse_node(), expr.type_id(), expr_id)); } break; } @@ -708,7 +708,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, SemIR::GetInitializingRepresentation(semantics_ir, target.type_id); init_rep.kind == SemIR::InitializingRepresentation::ByCopy) { target.init_block->InsertHere(); - expr_id = context.AddNode(SemIR::Node::InitializeFrom::Make( + expr_id = context.AddNode(SemIR::InitializeFrom( parse_node, target.type_id, expr_id, target.init_id)); } } diff --git a/toolchain/check/declaration_name_stack.cpp b/toolchain/check/declaration_name_stack.cpp index 406c2e2b780e..60c01530ce29 100644 --- a/toolchain/check/declaration_name_stack.cpp +++ b/toolchain/check/declaration_name_stack.cpp @@ -120,7 +120,8 @@ auto DeclarationNameStack::UpdateScopeIfNeeded(NameContext& name_context) switch (resolved_node.kind()) { case SemIR::NodeKind::Namespace: name_context.state = NameContext::State::Resolved; - name_context.target_scope_id = resolved_node.GetAsNamespace(); + name_context.target_scope_id = + resolved_node.As().name_scope_id; break; default: name_context.state = NameContext::State::ResolvedNonScope; diff --git a/toolchain/check/handle_array.cpp b/toolchain/check/handle_array.cpp index 925c174b8812..32215c3ef895 100644 --- a/toolchain/check/handle_array.cpp +++ b/toolchain/check/handle_array.cpp @@ -35,14 +35,14 @@ auto HandleArrayExpression(Context& context, Parse::Node parse_node) -> bool { .PopAndDiscardSoloParseNode(); auto element_type_node_id = context.node_stack().PopExpression(); auto bound_node = context.semantics_ir().GetNode(bound_node_id); - if (bound_node.kind() == SemIR::NodeKind::IntegerLiteral) { - auto bound_value = context.semantics_ir().GetIntegerLiteral( - bound_node.GetAsIntegerLiteral()); + if (auto literal = bound_node.TryAs()) { + const auto& bound_value = + context.semantics_ir().GetInteger(literal->integer_id); // TODO: Produce an error if the array type is too large. if (bound_value.getActiveBits() <= 64) { context.AddNodeAndPush( parse_node, - SemIR::Node::ArrayType::Make( + SemIR::ArrayType( parse_node, SemIR::TypeId::TypeType, bound_node_id, ExpressionAsType(context, parse_node, element_type_node_id))); return true; diff --git a/toolchain/check/handle_call_expression.cpp b/toolchain/check/handle_call_expression.cpp index d920aabd2f0c..a1d4cd9f3c52 100644 --- a/toolchain/check/handle_call_expression.cpp +++ b/toolchain/check/handle_call_expression.cpp @@ -19,7 +19,8 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { .PopWithParseNode(); auto name_node = context.semantics_ir().GetNode(context.FollowNameReferences(name_id)); - if (name_node.kind() != SemIR::NodeKind::FunctionDeclaration) { + auto function_name = name_node.TryAs(); + if (!function_name) { // TODO: Work on error. context.TODO(parse_node, "Not a callable name"); context.node_stack().Push(parse_node, name_id); @@ -27,7 +28,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { return true; } - auto function_id = name_node.GetAsFunctionDeclaration(); + auto function_id = function_name->function_id; const auto& callable = context.semantics_ir().GetFunction(function_id); // For functions with an implicit return type, the return type is the empty @@ -41,8 +42,8 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { if (callable.return_slot_id.is_valid()) { // Tentatively put storage for a temporary in the function's return slot. // This will be replaced if necessary when we perform initialization. - auto temp_id = context.AddNode(SemIR::Node::TemporaryStorage::Make( - call_expr_parse_node, callable.return_type_id)); + auto temp_id = context.AddNode( + SemIR::TemporaryStorage(call_expr_parse_node, callable.return_type_id)); context.ParamOrArgSave(temp_id); } @@ -55,8 +56,8 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { return true; } - auto call_node_id = context.AddNode(SemIR::Node::Call::Make( - call_expr_parse_node, type_id, refs_id, function_id)); + auto call_node_id = context.AddNode( + SemIR::Call(call_expr_parse_node, type_id, refs_id, function_id)); context.node_stack().Push(parse_node, call_node_id); return true; diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index eaf2428c30f5..5f6f3244b2c4 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -57,8 +57,8 @@ static auto BuildFunctionDeclaration(Context& context) .return_type_id = return_type_id, .return_slot_id = return_slot_id, .body_block_ids = {}}); - auto decl_id = context.AddNode( - SemIR::Node::FunctionDeclaration::Make(fn_node, function_id)); + auto decl_id = + context.AddNode(SemIR::FunctionDeclaration(fn_node, function_id)); context.declaration_name_stack().AddNameToLookup(name_context, decl_id); if (SemIR::IsEntryPoint(context.semantics_ir(), function_id)) { @@ -100,7 +100,7 @@ auto HandleFunctionDefinition(Context& context, Parse::Node parse_node) "Missing `return` at end of function with declared return type."); context.emitter().Emit(parse_node, MissingReturnStatement); } else { - context.AddNode(SemIR::Node::Return::Make(parse_node)); + context.AddNode(SemIR::Return(parse_node)); } } @@ -123,11 +123,10 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node) context.AddCurrentCodeBlockToFunction(); // Bring the parameters into scope. - for (auto ref_id : + for (auto param_id : context.semantics_ir().GetNodeBlock(function.param_refs_id)) { - auto ref = context.semantics_ir().GetNode(ref_id); - auto name_id = ref.GetAsParameter(); - context.AddNameToLookup(ref.parse_node(), name_id, ref_id); + auto param = context.semantics_ir().GetNodeAs(param_id); + context.AddNameToLookup(param.parse_node, param.name_id, param_id); } context.node_stack().Push(parse_node, function_id); @@ -154,8 +153,8 @@ auto HandleReturnType(Context& context, Parse::Node parse_node) -> bool { // TODO: Use a dedicated node rather than VarStorage here. context.AddNodeAndPush( parse_node, - SemIR::Node::VarStorage::Make( - parse_node, type_id, context.semantics_ir().AddString("return"))); + SemIR::VarStorage(parse_node, type_id, + context.semantics_ir().AddString("return"))); return true; } diff --git a/toolchain/check/handle_if_statement.cpp b/toolchain/check/handle_if_statement.cpp index 0e025e3e52bc..c8250e4e0418 100644 --- a/toolchain/check/handle_if_statement.cpp +++ b/toolchain/check/handle_if_statement.cpp @@ -53,7 +53,7 @@ auto HandleIfStatement(Context& context, Parse::Node parse_node) -> bool { // block. auto else_block_id = context.node_stack().Pop(); - context.AddNode(SemIR::Node::Branch::Make(parse_node, else_block_id)); + context.AddNode(SemIR::Branch(parse_node, else_block_id)); context.node_block_stack().Pop(); context.node_block_stack().Push(else_block_id); break; diff --git a/toolchain/check/handle_index.cpp b/toolchain/check/handle_index.cpp index 3426a8f45a41..6b3c5a64ee71 100644 --- a/toolchain/check/handle_index.cpp +++ b/toolchain/check/handle_index.cpp @@ -21,10 +21,10 @@ auto HandleIndexExpressionStart(Context& /*context*/, static auto ValidateIntegerLiteralBound(Context& context, Parse::Node parse_node, SemIR::Node operand_node, - SemIR::Node index_node, int size) - -> const llvm::APInt* { - const auto& index_val = context.semantics_ir().GetIntegerLiteral( - index_node.GetAsIntegerLiteral()); + SemIR::IntegerLiteral index_node, + int size) -> const llvm::APInt* { + const auto& index_val = + context.semantics_ir().GetInteger(index_node.integer_id); if (index_val.uge(size)) { CARBON_DIAGNOSTIC(IndexOutOfBounds, Error, "Index `{0}` is past the end of `{1}`.", llvm::APSInt, @@ -51,13 +51,14 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { switch (operand_type_node.kind()) { case SemIR::NodeKind::ArrayType: { - auto [bound_id, element_type_id] = operand_type_node.GetAsArrayType(); + auto array_type = operand_type_node.As(); // We can check whether integers are in-bounds, although it doesn't affect // the IR for an array. - if (index_node.kind() == SemIR::NodeKind::IntegerLiteral && + if (auto index_literal = index_node.TryAs(); + index_literal && !ValidateIntegerLiteralBound( - context, parse_node, operand_node, index_node, - context.semantics_ir().GetArrayBoundValue(bound_id))) { + context, parse_node, operand_node, *index_literal, + context.semantics_ir().GetArrayBoundValue(array_type.bound_id))) { index_node_id = SemIR::NodeId::BuiltinError; } auto cast_index_id = ConvertToValueOfType( @@ -68,11 +69,12 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { if (array_cat == SemIR::ExpressionCategory::Value) { // If the operand is an array value, convert it to an ephemeral // reference to an array so we can perform a primitive indexing into it. - operand_node_id = context.AddNode(SemIR::Node::ValueAsReference::Make( + operand_node_id = context.AddNode(SemIR::ValueAsReference( parse_node, operand_type_id, operand_node_id)); } - auto elem_id = context.AddNode(SemIR::Node::ArrayIndex::Make( - parse_node, element_type_id, operand_node_id, cast_index_id)); + auto elem_id = context.AddNode( + SemIR::ArrayIndex(parse_node, array_type.element_type_id, + operand_node_id, cast_index_id)); if (array_cat != SemIR::ExpressionCategory::DurableReference) { // Indexing a durable reference gives a durable reference expression. // Indexing anything else gives a value expression. @@ -85,12 +87,12 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { } case SemIR::NodeKind::TupleType: { SemIR::TypeId element_type_id = SemIR::TypeId::Error; - if (index_node.kind() == SemIR::NodeKind::IntegerLiteral) { + if (auto index_literal = index_node.TryAs()) { auto type_block = context.semantics_ir().GetTypeBlock( - operand_type_node.GetAsTupleType()); - if (const auto* index_val = - ValidateIntegerLiteralBound(context, parse_node, operand_node, - index_node, type_block.size())) { + operand_type_node.As().elements_id); + if (const auto* index_val = ValidateIntegerLiteralBound( + context, parse_node, operand_node, *index_literal, + type_block.size())) { element_type_id = type_block[index_val->getZExtValue()]; } else { index_node_id = SemIR::NodeId::BuiltinError; @@ -101,9 +103,9 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { context.emitter().Emit(parse_node, TupleIndexIntegerLiteral); index_node_id = SemIR::NodeId::BuiltinError; } - context.AddNodeAndPush(parse_node, SemIR::Node::TupleIndex::Make( - parse_node, element_type_id, - operand_node_id, index_node_id)); + context.AddNodeAndPush(parse_node, + SemIR::TupleIndex(parse_node, element_type_id, + operand_node_id, index_node_id)); return true; } default: { diff --git a/toolchain/check/handle_let.cpp b/toolchain/check/handle_let.cpp index 25fc8be22489..320dae294d35 100644 --- a/toolchain/check/handle_let.cpp +++ b/toolchain/check/handle_let.cpp @@ -22,17 +22,16 @@ auto HandleLetDeclaration(Context& context, Parse::Node parse_node) -> bool { // Update the binding with its value and add it to the current block, after // the computation of the value. - auto [name_id, absent_value_id] = pattern.GetAsBindName(); - CARBON_CHECK(!absent_value_id.is_valid()) + // TODO: Support other kinds of pattern here. + auto bind_name = pattern.As(); + CARBON_CHECK(!bind_name.value_id.is_valid()) << "Binding should not already have a value!"; - context.semantics_ir().ReplaceNode( - pattern_id, - SemIR::Node::BindName::Make(pattern.parse_node(), pattern.type_id(), - name_id, value_id)); + bind_name.value_id = value_id; + context.semantics_ir().ReplaceNode(pattern_id, bind_name); context.node_block_stack().AddNodeId(pattern_id); // Add the name of the binding to the current scope. - context.AddNameToLookup(pattern.parse_node(), name_id, pattern_id); + context.AddNameToLookup(pattern.parse_node(), bind_name.name_id, pattern_id); return true; } diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index f8f9f98f5269..6698f4822c84 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -13,7 +13,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool { case Lex::TokenKind::True: { context.AddNodeAndPush( parse_node, - SemIR::Node::BoolLiteral::Make( + SemIR::BoolLiteral( parse_node, context.CanonicalizeType(SemIR::NodeId::BuiltinBoolType), token_kind == Lex::TokenKind::True ? SemIR::BoolValue::True @@ -21,24 +21,24 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool { break; } case Lex::TokenKind::IntegerLiteral: { - auto id = context.semantics_ir().AddIntegerLiteral( + auto id = context.semantics_ir().AddInteger( context.tokens().GetIntegerLiteral(token)); context.AddNodeAndPush( parse_node, - SemIR::Node::IntegerLiteral::Make( + SemIR::IntegerLiteral( parse_node, context.CanonicalizeType(SemIR::NodeId::BuiltinIntegerType), id)); break; } case Lex::TokenKind::RealLiteral: { auto token_value = context.tokens().GetRealLiteral(token); - auto id = context.semantics_ir().AddRealLiteral( + auto id = context.semantics_ir().AddReal( {.mantissa = token_value.mantissa, .exponent = token_value.exponent, .is_decimal = token_value.is_decimal}); context.AddNodeAndPush( parse_node, - SemIR::Node::RealLiteral::Make( + SemIR::RealLiteral( parse_node, context.CanonicalizeType(SemIR::NodeId::BuiltinFloatingPointType), id)); @@ -49,7 +49,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool { context.tokens().GetStringLiteral(token)); context.AddNodeAndPush( parse_node, - SemIR::Node::StringLiteral::Make( + SemIR::StringLiteral( parse_node, context.CanonicalizeType(SemIR::NodeId::BuiltinStringType), id)); break; diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 8b7db384dcf5..5cf24200c068 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -17,10 +17,10 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) auto base = context.semantics_ir().GetNode(context.FollowNameReferences(base_id)); - if (base.kind() == SemIR::NodeKind::Namespace) { + if (auto namespc = base.TryAs()) { // For a namespace, just resolve the name. auto node_id = - context.LookupName(parse_node, name_id, base.GetAsNamespace(), + context.LookupName(parse_node, name_id, namespc->name_scope_id, /*print_diagnostics=*/true); context.node_stack().Push(parse_node, node_id); return true; @@ -34,16 +34,15 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) switch (base_type.kind()) { case SemIR::NodeKind::StructType: { - auto refs = - context.semantics_ir().GetNodeBlock(base_type.GetAsStructType()); + auto refs = context.semantics_ir().GetNodeBlock( + 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 ref = context.semantics_ir().GetNode(ref_id); - if (auto [field_name_id, field_type_id] = ref.GetAsStructTypeField(); - name_id == field_name_id) { + auto field = + context.semantics_ir().GetNodeAs(ref_id); + if (name_id == field.name_id) { context.AddNodeAndPush( - parse_node, - SemIR::Node::StructAccess::Make(parse_node, field_type_id, + parse_node, SemIR::StructAccess(parse_node, field.type_id, base_id, SemIR::MemberIndex(i))); return true; } @@ -97,15 +96,15 @@ auto HandleNameExpression(Context& context, Parse::Node parse_node) -> bool { auto value = context.semantics_ir().GetNode(value_id); if (value.kind().value_kind() == SemIR::NodeValueKind::Typed) { // This is a reference to a name binding that has a value and a type. - context.AddNodeAndPush(parse_node, - SemIR::Node::NameReference::Make( - parse_node, value.type_id(), name_id, value_id)); + context.AddNodeAndPush( + parse_node, + SemIR::NameReference(parse_node, value.type_id(), name_id, value_id)); } else { // This is something like a namespace name, that can be found by name lookup // but isn't a first-class value with a type. - context.AddNodeAndPush(parse_node, - SemIR::Node::NameReferenceUntyped::Make( - parse_node, value.type_id(), name_id, value_id)); + context.AddNodeAndPush( + parse_node, SemIR::NameReferenceUntyped(parse_node, value.type_id(), + name_id, value_id)); } return true; } diff --git a/toolchain/check/handle_namespace.cpp b/toolchain/check/handle_namespace.cpp index ced59723a8a6..c6f6851a9e20 100644 --- a/toolchain/check/handle_namespace.cpp +++ b/toolchain/check/handle_namespace.cpp @@ -15,8 +15,8 @@ auto HandleNamespaceStart(Context& context, Parse::Node /*parse_node*/) auto HandleNamespace(Context& context, Parse::Node parse_node) -> bool { auto name_context = context.declaration_name_stack().Pop(); - auto namespace_id = context.AddNode(SemIR::Node::Namespace::Make( - parse_node, context.semantics_ir().AddNameScope())); + auto namespace_id = context.AddNode( + SemIR::Namespace(parse_node, context.semantics_ir().AddNameScope())); 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 382afe3c5a4f..1496df96e333 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -24,7 +24,7 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { context.AddNodeAndPush( parse_node, - SemIR::Node::BinaryOperatorAdd::Make( + SemIR::BinaryOperatorAdd( parse_node, context.semantics_ir().GetNode(lhs_id).type_id(), lhs_id, rhs_id)); return true; @@ -39,17 +39,17 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { // When the second operand is evaluated, the result of `and` and `or` is // its value. auto resume_block_id = context.node_block_stack().PeekOrAdd(/*depth=*/1); - context.AddNode(SemIR::Node::BranchWithArg::Make( - parse_node, resume_block_id, rhs_id)); + context.AddNode( + SemIR::BranchWithArg(parse_node, resume_block_id, rhs_id)); context.node_block_stack().Pop(); context.AddCurrentCodeBlockToFunction(); // Collect the result from either the first or second operand. context.AddNodeAndPush( parse_node, - SemIR::Node::BlockArg::Make( - parse_node, context.semantics_ir().GetNode(rhs_id).type_id(), - resume_block_id)); + SemIR::BlockArg(parse_node, + context.semantics_ir().GetNode(rhs_id).type_id(), + resume_block_id)); return true; } case Lex::TokenKind::Equal: { @@ -63,7 +63,7 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { // TODO: Destroy the old value before reinitializing. This will require // building the destruction code before we build the RHS subexpression. rhs_id = Initialize(context, parse_node, lhs_id, rhs_id); - context.AddNode(SemIR::Node::Assign::Make(parse_node, lhs_id, rhs_id)); + context.AddNode(SemIR::Assign(parse_node, lhs_id, rhs_id)); // We model assignment as an expression, so we need to push a value for // it, even though it doesn't produce a value. // TODO: Consider changing our parse tree to model assignment as a @@ -85,8 +85,8 @@ auto HandlePostfixOperator(Context& context, Parse::Node parse_node) -> bool { case Lex::TokenKind::Star: { auto inner_type_id = ExpressionAsType(context, parse_node, value_id); context.AddNodeAndPush( - parse_node, SemIR::Node::PointerType::Make( - parse_node, SemIR::TypeId::TypeType, inner_type_id)); + parse_node, SemIR::PointerType(parse_node, SemIR::TypeId::TypeType, + inner_type_id)); return true; } @@ -120,7 +120,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { } context.AddNodeAndPush( parse_node, - SemIR::Node::AddressOf::Make( + SemIR::AddressOf( parse_node, context.GetPointerType( parse_node, @@ -142,8 +142,8 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { } auto inner_type_id = ExpressionAsType(context, parse_node, value_id); context.AddNodeAndPush( - parse_node, SemIR::Node::ConstType::Make( - parse_node, SemIR::TypeId::TypeType, inner_type_id)); + parse_node, + SemIR::ConstType(parse_node, SemIR::TypeId::TypeType, inner_type_id)); return true; } @@ -151,7 +151,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { value_id = ConvertToBoolValue(context, parse_node, value_id); context.AddNodeAndPush( parse_node, - SemIR::Node::UnaryOperatorNot::Make( + SemIR::UnaryOperatorNot( parse_node, context.semantics_ir().GetNode(value_id).type_id(), value_id)); return true; @@ -162,8 +162,8 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { auto type_node = context.semantics_ir().GetNode( context.semantics_ir().GetTypeAllowBuiltinTypes(type_id)); auto result_type_id = SemIR::TypeId::Error; - if (type_node.kind() == SemIR::NodeKind::PointerType) { - result_type_id = type_node.GetAsPointerType(); + if (auto pointer_type = type_node.TryAs()) { + result_type_id = pointer_type->pointee_id; } else { CARBON_DIAGNOSTIC( DereferenceOfNonPointer, Error, @@ -183,8 +183,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { } value_id = ConvertToValueExpression(context, value_id); context.AddNodeAndPush( - parse_node, - SemIR::Node::Dereference::Make(parse_node, result_type_id, value_id)); + parse_node, SemIR::Dereference(parse_node, result_type_id, value_id)); return true; } @@ -207,15 +206,15 @@ auto HandleShortCircuitOperand(Context& context, Parse::Node parse_node) switch (auto token_kind = context.tokens().GetKind(token)) { case Lex::TokenKind::And: branch_value_id = cond_value_id; - short_circuit_result_id = context.AddNode(SemIR::Node::BoolLiteral::Make( + short_circuit_result_id = context.AddNode(SemIR::BoolLiteral( parse_node, bool_type_id, SemIR::BoolValue::False)); break; case Lex::TokenKind::Or: - branch_value_id = context.AddNode(SemIR::Node::UnaryOperatorNot::Make( - parse_node, bool_type_id, cond_value_id)); - short_circuit_result_id = context.AddNode(SemIR::Node::BoolLiteral::Make( - parse_node, bool_type_id, SemIR::BoolValue::True)); + branch_value_id = context.AddNode( + SemIR::UnaryOperatorNot(parse_node, bool_type_id, cond_value_id)); + short_circuit_result_id = context.AddNode( + SemIR::BoolLiteral(parse_node, bool_type_id, SemIR::BoolValue::True)); break; default: diff --git a/toolchain/check/handle_paren.cpp b/toolchain/check/handle_paren.cpp index ce61d277c703..9a8662e3b8b2 100644 --- a/toolchain/check/handle_paren.cpp +++ b/toolchain/check/handle_paren.cpp @@ -48,8 +48,8 @@ auto HandleTupleLiteral(Context& context, Parse::Node parse_node) -> bool { } auto type_id = context.CanonicalizeTupleType(parse_node, std::move(type_ids)); - auto value_id = context.AddNode( - SemIR::Node::TupleLiteral::Make(parse_node, type_id, refs_id)); + auto value_id = + context.AddNode(SemIR::TupleLiteral(parse_node, type_id, refs_id)); context.node_stack().Push(parse_node, value_id); return true; } diff --git a/toolchain/check/handle_pattern_binding.cpp b/toolchain/check/handle_pattern_binding.cpp index 9596b88e6726..d4fce2eef958 100644 --- a/toolchain/check/handle_pattern_binding.cpp +++ b/toolchain/check/handle_pattern_binding.cpp @@ -32,12 +32,12 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { switch (auto context_parse_node_kind = context.parse_tree().node_kind( context.node_stack().PeekParseNode())) { case Parse::NodeKind::VariableIntroducer: - context.AddNodeAndPush(parse_node, SemIR::Node::VarStorage::Make( - name_node, cast_type_id, name_id)); + context.AddNodeAndPush( + parse_node, SemIR::VarStorage(name_node, cast_type_id, name_id)); break; case Parse::NodeKind::ParameterListStart: - context.AddNodeAndPush(parse_node, SemIR::Node::Parameter::Make( - name_node, cast_type_id, name_id)); + context.AddNodeAndPush( + parse_node, SemIR::Parameter(name_node, cast_type_id, name_id)); break; case Parse::NodeKind::LetIntroducer: // Create the node, but don't add it to a block until after we've formed @@ -46,7 +46,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::Node::BindName::Make( + context.semantics_ir().AddNodeInNoBlock(SemIR::BindName( name_node, cast_type_id, name_id, SemIR::NodeId::Invalid))); break; default: diff --git a/toolchain/check/handle_statement.cpp b/toolchain/check/handle_statement.cpp index 8744ca9f0855..f996f5a2762e 100644 --- a/toolchain/check/handle_statement.cpp +++ b/toolchain/check/handle_statement.cpp @@ -29,10 +29,10 @@ auto HandleExpressionStatement(Context& context, Parse::Node /*parse_node*/) auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { CARBON_CHECK(!context.return_scope_stack().empty()); - const auto& fn_node = - context.semantics_ir().GetNode(context.return_scope_stack().back()); + auto fn_node = context.semantics_ir().GetNodeAs( + context.return_scope_stack().back()); const auto& callable = - context.semantics_ir().GetFunction(fn_node.GetAsFunctionDeclaration()); + context.semantics_ir().GetFunction(fn_node.function_id); if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == Parse::NodeKind::ReturnStatementStart) { @@ -49,7 +49,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { .Emit(); } - context.AddNode(SemIR::Node::Return::Make(parse_node)); + context.AddNode(SemIR::Return(parse_node)); } else { auto arg = context.node_stack().PopExpression(); context.node_stack() @@ -63,7 +63,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { "There was no return type provided."); context.emitter() .Build(parse_node, ReturnStatementDisallowExpression) - .Note(fn_node.parse_node(), ReturnStatementImplicitNote) + .Note(fn_node.parse_node, ReturnStatementImplicitNote) .Emit(); } else if (callable.return_slot_id.is_valid()) { arg = Initialize(context, parse_node, callable.return_slot_id, arg); @@ -72,7 +72,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { callable.return_type_id); } - context.AddNode(SemIR::Node::ReturnExpression::Make(parse_node, arg)); + context.AddNode(SemIR::ReturnExpression(parse_node, arg)); } // Switch to a new, unreachable, empty node block. This typically won't diff --git a/toolchain/check/handle_struct.cpp b/toolchain/check/handle_struct.cpp index e29a0c534a8d..81ce42245673 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -28,8 +28,8 @@ auto HandleStructFieldType(Context& context, Parse::Node parse_node) -> bool { auto [name_node, name_id] = context.node_stack().PopWithParseNode(); - context.AddNodeAndPush(parse_node, SemIR::Node::StructTypeField::Make( - name_node, name_id, cast_type_id)); + context.AddNodeAndPush( + parse_node, SemIR::StructTypeField(name_node, name_id, cast_type_id)); return true; } @@ -44,7 +44,7 @@ auto HandleStructFieldValue(Context& context, Parse::Node parse_node) -> bool { SemIR::StringId name_id = context.node_stack().Pop(); // Store the name for the type. - context.args_type_info_stack().AddNode(SemIR::Node::StructTypeField::Make( + context.args_type_info_stack().AddNode(SemIR::StructTypeField( parse_node, name_id, context.semantics_ir().GetNode(value_node_id).type_id())); @@ -65,8 +65,8 @@ auto HandleStructLiteral(Context& context, Parse::Node parse_node) -> bool { auto type_id = context.CanonicalizeStructType(parse_node, type_block_id); - auto value_id = context.AddNode( - SemIR::Node::StructLiteral::Make(parse_node, type_id, refs_id)); + auto value_id = + context.AddNode(SemIR::StructLiteral(parse_node, type_id, refs_id)); context.node_stack().Push(parse_node, value_id); return true; } @@ -98,9 +98,9 @@ auto HandleStructTypeLiteral(Context& context, Parse::Node parse_node) -> bool { CARBON_CHECK(refs_id != SemIR::NodeBlockId::Empty) << "{} is handled by StructLiteral."; - context.AddNodeAndPush(parse_node, - SemIR::Node::StructType::Make( - parse_node, SemIR::TypeId::TypeType, refs_id)); + context.AddNodeAndPush( + parse_node, + SemIR::StructType(parse_node, SemIR::TypeId::TypeType, refs_id)); return true; } diff --git a/toolchain/check/handle_variable.cpp b/toolchain/check/handle_variable.cpp index 5280d5c075c8..1f885eb345ef 100644 --- a/toolchain/check/handle_variable.cpp +++ b/toolchain/check/handle_variable.cpp @@ -24,15 +24,14 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node) // Get the storage and add it to name lookup. SemIR::NodeId var_id = context.node_stack().Pop(); - auto var = context.semantics_ir().GetNode(var_id); - auto name_id = var.GetAsVarStorage(); - context.AddNameToLookup(var.parse_node(), name_id, var_id); + auto var = context.semantics_ir().GetNodeAs(var_id); + context.AddNameToLookup(var.parse_node, var.name_id, var_id); // If there was an initializer, assign it to storage. if (has_init) { init_id = Initialize(context, parse_node, var_id, init_id); // TODO: Consider using different node kinds for assignment versus // initialization. - context.AddNode(SemIR::Node::Assign::Make(parse_node, var_id, init_id)); + context.AddNode(SemIR::Assign(parse_node, var_id, init_id)); } context.node_stack() diff --git a/toolchain/check/pending_block.h b/toolchain/check/pending_block.h index 5aa638539648..736a8a0c677d 100644 --- a/toolchain/check/pending_block.h +++ b/toolchain/check/pending_block.h @@ -64,9 +64,8 @@ class PendingBlock { // 1) The block is empty. Replace `target_id` with an empty splice // pointing at `value_id`. context_.semantics_ir().ReplaceNode( - target_id, - SemIR::Node::SpliceBlock::Make(value.parse_node(), value.type_id(), - SemIR::NodeBlockId::Empty, value_id)); + 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. @@ -75,9 +74,9 @@ class PendingBlock { // 3) Anything else: splice it into the IR, replacing `target_id`. context_.semantics_ir().ReplaceNode( target_id, - SemIR::Node::SpliceBlock::Make( - value.parse_node(), value.type_id(), - context_.semantics_ir().AddNodeBlock(nodes_), value_id)); + SemIR::SpliceBlock(value.parse_node(), value.type_id(), + context_.semantics_ir().AddNodeBlock(nodes_), + value_id)); } // Prepare to stash more pending instructions. diff --git a/toolchain/check/testdata/basics/builtin_nodes.carbon b/toolchain/check/testdata/basics/builtin_nodes.carbon index 9c693f23c955..8f6235272ee4 100644 --- a/toolchain/check/testdata/basics/builtin_nodes.carbon +++ b/toolchain/check/testdata/basics/builtin_nodes.carbon @@ -11,9 +11,9 @@ // CHECK:STDOUT: - cross_reference_irs_size: 1 // CHECK:STDOUT: functions: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ // CHECK:STDOUT: ] diff --git a/toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon index c9142680e902..a5d932086518 100644 --- a/toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/multifile_raw_and_textual_ir.carbon @@ -20,9 +20,9 @@ fn B() {} // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ // CHECK:STDOUT: A, @@ -60,9 +60,9 @@ fn B() {} // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ // CHECK:STDOUT: B, diff --git a/toolchain/check/testdata/basics/multifile_raw_ir.carbon b/toolchain/check/testdata/basics/multifile_raw_ir.carbon index 0a2b3f1dce0a..94155c5010a1 100644 --- a/toolchain/check/testdata/basics/multifile_raw_ir.carbon +++ b/toolchain/check/testdata/basics/multifile_raw_ir.carbon @@ -20,9 +20,9 @@ fn B() {} // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ // CHECK:STDOUT: A, @@ -51,9 +51,9 @@ fn B() {} // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block0, body: [block1]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ // CHECK:STDOUT: B, diff --git a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon index 089817b94b6a..740765583560 100644 --- a/toolchain/check/testdata/basics/raw_and_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_and_textual_ir.carbon @@ -18,10 +18,10 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: 2, // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: {mantissa: 34, exponent: -1, is_decimal: 1}, // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ diff --git a/toolchain/check/testdata/basics/raw_ir.carbon b/toolchain/check/testdata/basics/raw_ir.carbon index 75368e3f87d5..a0bd51bcd6e7 100644 --- a/toolchain/check/testdata/basics/raw_ir.carbon +++ b/toolchain/check/testdata/basics/raw_ir.carbon @@ -18,10 +18,10 @@ fn Foo(n: i32) -> (i32, f64) { // CHECK:STDOUT: functions: [ // CHECK:STDOUT: {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}, // CHECK:STDOUT: ] -// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: integers: [ // CHECK:STDOUT: 2, // CHECK:STDOUT: ] -// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: reals: [ // CHECK:STDOUT: {mantissa: 34, exponent: -1, is_decimal: 1}, // CHECK:STDOUT: ] // CHECK:STDOUT: strings: [ diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 77875fda4e44..c5af4a7a7f2a 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -132,7 +132,7 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) llvm_context(), GetType(function.return_type_id))); } else { arg.setName(semantics_ir().GetString( - semantics_ir().GetNode(node_id).GetAsParameter())); + semantics_ir().GetNodeAs(node_id).name_id)); } } @@ -208,26 +208,27 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* { auto node = semantics_ir_->GetNode(node_id); switch (node.kind()) { case SemIR::NodeKind::ArrayType: { - auto [bound_node_id, type_id] = node.GetAsArrayType(); + auto array_type = node.As(); return llvm::ArrayType::get( - GetType(type_id), semantics_ir_->GetArrayBoundValue(bound_node_id)); + GetType(array_type.element_type_id), + semantics_ir_->GetArrayBoundValue(array_type.bound_id)); } case SemIR::NodeKind::ConstType: - return GetType(node.GetAsConstType()); + return GetType(node.As().inner_id); case SemIR::NodeKind::PointerType: return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0); case SemIR::NodeKind::StructType: { - auto refs = semantics_ir_->GetNodeBlock(node.GetAsStructType()); + auto fields = + semantics_ir_->GetNodeBlock(node.As().fields_id); llvm::SmallVector subtypes; - subtypes.reserve(refs.size()); - for (auto ref_id : refs) { - auto [field_name_id, field_type_id] = - semantics_ir_->GetNode(ref_id).GetAsStructTypeField(); + subtypes.reserve(fields.size()); + for (auto field_id : fields) { + auto field = semantics_ir_->GetNodeAs(field_id); // TODO: Handle recursive types. The restriction for builtins prevents // recursion while still letting them cache. - CARBON_CHECK(field_type_id.index < SemIR::BuiltinKind::ValidCount) - << field_type_id; - subtypes.push_back(GetType(field_type_id)); + CARBON_CHECK(field.type_id.index < SemIR::BuiltinKind::ValidCount) + << field.type_id; + subtypes.push_back(GetType(field.type_id)); } return llvm::StructType::get(*llvm_context_, subtypes); } @@ -236,11 +237,12 @@ 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 refs = semantics_ir_->GetTypeBlock(node.GetAsTupleType()); + auto elements = + semantics_ir_->GetTypeBlock(node.As().elements_id); llvm::SmallVector subtypes; - subtypes.reserve(refs.size()); - for (auto ref_id : refs) { - subtypes.push_back(GetType(ref_id)); + subtypes.reserve(elements.size()); + for (auto element_id : elements) { + subtypes.push_back(GetType(element_id)); } return llvm::StructType::get(*llvm_context_, subtypes); } diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 320136235010..fc428d6e1138 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -44,9 +44,9 @@ auto FunctionContext::LowerBlock(SemIR::NodeBlockId block_id) -> void { // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { -#define CARBON_SEMANTICS_NODE_KIND(Name) \ - case SemIR::NodeKind::Name: \ - Handle##Name(*this, node_id, node); \ +#define CARBON_SEMANTICS_NODE_KIND(Name) \ + case SemIR::NodeKind::Name: \ + Handle##Name(*this, node_id, node.As()); \ break; #include "toolchain/sem_ir/node_kind.def" } diff --git a/toolchain/lower/function_context.h b/toolchain/lower/function_context.h index 5bba15aec949..fbf17065ef35 100644 --- a/toolchain/lower/function_context.h +++ b/toolchain/lower/function_context.h @@ -133,7 +133,7 @@ class FunctionContext { // Declare handlers for each SemIR::File node. #define CARBON_SEMANTICS_NODE_KIND(Name) \ auto Handle##Name(FunctionContext& context, SemIR::NodeId node_id, \ - SemIR::Node node) \ + SemIR::Name node) \ ->void; #include "toolchain/sem_ir/node_kind.def" diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index f4c38d1f99bb..77684204f3bd 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -9,106 +9,95 @@ namespace Carbon::Lower { -auto HandleInvalid(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, - SemIR::Node /*node*/) -> void { - llvm_unreachable("never in actual IR"); -} - auto HandleCrossReference(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node node) -> void { + SemIR::NodeId /*node_id*/, SemIR::CrossReference node) + -> void { CARBON_FATAL() << "TODO: Add support: " << node; } auto HandleAddressOf(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal(node_id, context.GetLocal(node.GetAsAddressOf())); + SemIR::AddressOf node) -> void { + context.SetLocal(node_id, context.GetLocal(node.lvalue_id)); } auto HandleArrayIndex(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [array_node_id, index_node_id] = node.GetAsArrayIndex(); - auto* array_value = context.GetLocal(array_node_id); + SemIR::ArrayIndex node) -> void { + auto* array_value = context.GetLocal(node.array_id); auto* llvm_type = - context.GetType(context.semantics_ir().GetNode(array_node_id).type_id()); + context.GetType(context.semantics_ir().GetNode(node.array_id).type_id()); llvm::Value* indexes[2] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(context.llvm_context()), 0), - context.GetLocal(index_node_id)}; + context.GetLocal(node.index_id)}; context.SetLocal(node_id, context.builder().CreateInBoundsGEP(llvm_type, array_value, indexes, "array.index")); } auto HandleArrayInit(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [src_id, refs_id] = node.GetAsArrayInit(); + 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(refs_id).back())); + node_id, context.GetLocal(context.semantics_ir() + .GetNodeBlock(node.inits_and_return_slot_id) + .back())); } auto HandleAssign(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - auto [storage_id, value_id] = node.GetAsAssign(); - auto storage_type_id = context.semantics_ir().GetNode(storage_id).type_id(); - context.FinishInitialization(storage_type_id, storage_id, value_id); + SemIR::Assign node) -> void { + auto storage_type_id = context.semantics_ir().GetNode(node.lhs_id).type_id(); + context.FinishInitialization(storage_type_id, node.lhs_id, node.rhs_id); } auto HandleBinaryOperatorAdd(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node node) - -> void { + SemIR::NodeId /*node_id*/, + SemIR::BinaryOperatorAdd node) -> void { CARBON_FATAL() << "TODO: Add support: " << node; } auto HandleBindName(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [name_id, value_id] = node.GetAsBindName(); - context.SetLocal(node_id, context.GetLocal(value_id)); + SemIR::BindName node) -> void { + context.SetLocal(node_id, context.GetLocal(node.value_id)); } auto HandleBlockArg(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - SemIR::NodeBlockId block_id = node.GetAsBlockArg(); - context.SetLocal(node_id, context.GetBlockArg(block_id, node.type_id())); + SemIR::BlockArg node) -> void { + context.SetLocal(node_id, context.GetBlockArg(node.block_id, node.type_id)); } auto HandleBoolLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - llvm::Value* v = llvm::ConstantInt::get(context.builder().getInt1Ty(), - node.GetAsBoolLiteral().index); + SemIR::BoolLiteral node) -> void { + llvm::Value* v = + llvm::ConstantInt::get(context.builder().getInt1Ty(), node.value.index); context.SetLocal(node_id, v); } auto HandleBranch(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - SemIR::NodeBlockId target_block_id = node.GetAsBranch(); - + SemIR::Branch node) -> void { // Opportunistically avoid creating a BasicBlock that contains just a branch. llvm::BasicBlock* block = context.builder().GetInsertBlock(); - if (block->empty() && context.TryToReuseBlock(target_block_id, block)) { + if (block->empty() && context.TryToReuseBlock(node.target_id, block)) { // Reuse this block as the branch target. } else { - context.builder().CreateBr(context.GetBlock(target_block_id)); + context.builder().CreateBr(context.GetBlock(node.target_id)); } context.builder().ClearInsertionPoint(); } auto HandleBranchIf(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - auto [target_block_id, cond_id] = node.GetAsBranchIf(); - llvm::Value* cond = context.GetLocal(cond_id); - llvm::BasicBlock* then_block = context.GetBlock(target_block_id); + SemIR::BranchIf node) -> void { + llvm::Value* cond = context.GetLocal(node.cond_id); + llvm::BasicBlock* then_block = context.GetBlock(node.target_id); llvm::BasicBlock* else_block = context.CreateSyntheticBlock(); context.builder().CreateCondBr(cond, then_block, else_block); context.builder().SetInsertPoint(else_block); } auto HandleBranchWithArg(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - auto [target_block_id, arg_id] = node.GetAsBranchWithArg(); - llvm::Value* arg = context.GetLocal(arg_id); - SemIR::TypeId arg_type_id = context.semantics_ir().GetNode(arg_id).type_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(); // 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 @@ -117,45 +106,44 @@ auto HandleBranchWithArg(FunctionContext& context, SemIR::NodeId /*node_id*/, llvm::BasicBlock* block = context.builder().GetInsertBlock(); llvm::BasicBlock* phi_predecessor = block; if (block->empty() && context.IsCurrentSyntheticBlock(block) && - context.TryToReuseBlock(target_block_id, block)) { + context.TryToReuseBlock(node.target_id, block)) { // Reuse this block as the branch target. phi_predecessor = block->getSinglePredecessor(); CARBON_CHECK(phi_predecessor) << "Synthetic block did not have a single predecessor"; } else { - context.builder().CreateBr(context.GetBlock(target_block_id)); + context.builder().CreateBr(context.GetBlock(node.target_id)); } - context.GetBlockArg(target_block_id, arg_type_id) + context.GetBlockArg(node.target_id, arg_type_id) ->addIncoming(arg, phi_predecessor); context.builder().ClearInsertionPoint(); } auto HandleBuiltin(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { + SemIR::Builtin node) -> void { CARBON_FATAL() << "TODO: Add support: " << node; } auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [refs_id, function_id] = node.GetAsCall(); - auto* llvm_function = context.GetFunction(function_id); - const auto& function = context.semantics_ir().GetFunction(function_id); + SemIR::Call node) -> void { + auto* llvm_function = context.GetFunction(node.function_id); + const auto& function = context.semantics_ir().GetFunction(node.function_id); std::vector args; llvm::ArrayRef arg_ids = - context.semantics_ir().GetNodeBlock(refs_id); + context.semantics_ir().GetNodeBlock(node.args_id); if (function.return_slot_id.is_valid()) { args.push_back(context.GetLocal(arg_ids.back())); arg_ids = arg_ids.drop_back(); } - for (auto ref_id : arg_ids) { - auto arg_type_id = context.semantics_ir().GetNode(ref_id).type_id(); + for (auto arg_id : arg_ids) { + auto arg_type_id = context.semantics_ir().GetNode(arg_id).type_id(); if (SemIR::GetValueRepresentation(context.semantics_ir(), arg_type_id) .kind != SemIR::ValueRepresentation::None) { - args.push_back(context.GetLocal(ref_id)); + args.push_back(context.GetLocal(arg_id)); } } @@ -165,7 +153,7 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, // StubReference needs a value to propagate. // TODO: Remove this now the StubReferences are gone. context.SetLocal(node_id, - llvm::PoisonValue::get(context.GetType(node.type_id()))); + llvm::PoisonValue::get(context.GetType(node.type_id))); } else { context.SetLocal(node_id, context.builder().CreateCall(llvm_function, args, @@ -174,13 +162,13 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id, } auto HandleDereference(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal(node_id, context.GetLocal(node.GetAsDereference())); + SemIR::Dereference node) -> void { + context.SetLocal(node_id, context.GetLocal(node.pointer_id)); } auto HandleFunctionDeclaration(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node node) - -> void { + SemIR::NodeId /*node_id*/, + SemIR::FunctionDeclaration node) -> void { CARBON_FATAL() << "Should not be encountered. If that changes, we may want to change " "higher-level logic to skip them rather than calling this. " @@ -188,16 +176,14 @@ auto HandleFunctionDeclaration(FunctionContext& /*context*/, } auto HandleInitializeFrom(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - auto [init_id, storage_id] = node.GetAsInitializeFrom(); - auto storage_type_id = context.semantics_ir().GetNode(storage_id).type_id(); - context.FinishInitialization(storage_type_id, storage_id, init_id); + SemIR::InitializeFrom node) -> void { + auto storage_type_id = context.semantics_ir().GetNode(node.dest_id).type_id(); + context.FinishInitialization(storage_type_id, node.dest_id, node.src_id); } auto HandleIntegerLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - llvm::APInt i = - context.semantics_ir().GetIntegerLiteral(node.GetAsIntegerLiteral()); + SemIR::IntegerLiteral node) -> void { + const llvm::APInt& i = context.semantics_ir().GetInteger(node.integer_id); // TODO: This won't offer correct semantics, but seems close enough for now. llvm::Value* v = llvm::ConstantInt::get(context.builder().getInt32Ty(), i.getZExtValue()); @@ -205,36 +191,34 @@ auto HandleIntegerLiteral(FunctionContext& context, SemIR::NodeId node_id, } auto HandleNameReference(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [name_id, value_id] = node.GetAsNameReference(); - context.SetLocal(node_id, context.GetLocal(value_id)); + SemIR::NameReference node) -> void { + context.SetLocal(node_id, context.GetLocal(node.value_id)); } auto HandleNameReferenceUntyped(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node /*node*/) - -> void { + SemIR::NodeId /*node_id*/, + SemIR::NameReferenceUntyped /*node*/) -> void { // No action to take: untyped name references don't hold a value. } auto HandleNamespace(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, - SemIR::Node /*node*/) -> void { + SemIR::Namespace /*node*/) -> void { // No action to take. } auto HandleNoOp(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, - SemIR::Node /*node*/) -> void { + SemIR::NoOp /*node*/) -> void { // No action to take. } auto HandleParameter(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/, - SemIR::Node /*node*/) -> void { + SemIR::Parameter /*node*/) -> void { CARBON_FATAL() << "Parameters should be lowered by `BuildFunctionDefinition`"; } auto HandleRealLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - SemIR::RealLiteral real = - context.semantics_ir().GetRealLiteral(node.GetAsRealLiteral()); + SemIR::RealLiteral node) -> void { + const SemIR::Real& real = context.semantics_ir().GetReal(node.real_id); // TODO: This will probably have overflow issues, and should be fixed. double val = real.mantissa.getZExtValue() * @@ -245,16 +229,15 @@ auto HandleRealLiteral(FunctionContext& context, SemIR::NodeId node_id, } auto HandleReturn(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node /*node*/) -> void { + SemIR::Return /*node*/) -> void { context.builder().CreateRetVoid(); } auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/, - SemIR::Node node) -> void { - SemIR::NodeId expr_id = node.GetAsReturnExpression(); + SemIR::ReturnExpression node) -> void { switch (SemIR::GetInitializingRepresentation( context.semantics_ir(), - context.semantics_ir().GetNode(expr_id).type_id()) + context.semantics_ir().GetNode(node.expr_id).type_id()) .kind) { case SemIR::InitializingRepresentation::None: case SemIR::InitializingRepresentation::InPlace: @@ -263,20 +246,20 @@ auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/, return; case SemIR::InitializingRepresentation::ByCopy: // The expression produces the value representation for the type. - context.builder().CreateRet(context.GetLocal(expr_id)); + context.builder().CreateRet(context.GetLocal(node.expr_id)); return; } } auto HandleSpliceBlock(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [block_id, result_id] = node.GetAsSpliceBlock(); - context.LowerBlock(block_id); - context.SetLocal(node_id, context.GetLocal(result_id)); + SemIR::SpliceBlock node) -> void { + context.LowerBlock(node.block_id); + context.SetLocal(node_id, context.GetLocal(node.result_id)); } auto HandleStringLiteral(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node node) -> void { + SemIR::NodeId /*node_id*/, SemIR::StringLiteral node) + -> void { CARBON_FATAL() << "TODO: Add support: " << node; } @@ -326,32 +309,31 @@ static auto GetStructOrTupleElement(FunctionContext& context, } auto HandleStructAccess(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [struct_id, member_index] = node.GetAsStructAccess(); - auto struct_type_id = context.semantics_ir().GetNode(struct_id).type_id(); + SemIR::StructAccess node) -> void { + auto struct_type_id = + context.semantics_ir().GetNode(node.struct_id).type_id(); // Get type information for member names. - auto type_refs = context.semantics_ir().GetNodeBlock( + auto fields = context.semantics_ir().GetNodeBlock( context.semantics_ir() - .GetNode(context.semantics_ir().GetType(struct_type_id)) - .GetAsStructType()); - auto [field_name_id, field_type_id] = - context.semantics_ir() - .GetNode(type_refs[member_index.index]) - .GetAsStructTypeField(); - auto member_name = context.semantics_ir().GetString(field_name_id); + .GetNodeAs( + context.semantics_ir().GetType(struct_type_id)) + .fields_id); + auto field = context.semantics_ir().GetNodeAs( + fields[node.index.index]); + auto member_name = context.semantics_ir().GetString(field.name_id); - context.SetLocal( - node_id, GetStructOrTupleElement(context, struct_id, member_index.index, - node.type_id(), member_name)); + context.SetLocal(node_id, GetStructOrTupleElement(context, node.struct_id, + node.index.index, + node.type_id, member_name)); } auto HandleStructLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { + SemIR::StructLiteral node) -> void { // A StructLiteral should always be converted to a StructInit or StructValue // if its value is needed. context.SetLocal(node_id, - llvm::PoisonValue::get(context.GetType(node.type_id()))); + llvm::PoisonValue::get(context.GetType(node.type_id))); } // Emits the value representation for a struct or tuple whose elements are the @@ -399,12 +381,12 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context, } auto HandleStructInit(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto* llvm_type = context.GetType(node.type_id()); + SemIR::StructInit node) -> void { + auto* llvm_type = context.GetType(node.type_id); - switch (SemIR::GetInitializingRepresentation(context.semantics_ir(), - node.type_id()) - .kind) { + switch ( + SemIR::GetInitializingRepresentation(context.semantics_ir(), node.type_id) + .kind) { case SemIR::InitializingRepresentation::None: case SemIR::InitializingRepresentation::InPlace: // TODO: Add a helper to poison a value slot. @@ -412,63 +394,60 @@ auto HandleStructInit(FunctionContext& context, SemIR::NodeId node_id, break; case SemIR::InitializingRepresentation::ByCopy: { - auto [struct_literal_id, refs_id] = node.GetAsStructInit(); - context.SetLocal(node_id, - EmitStructOrTupleValueRepresentation( - context, node.type_id(), refs_id, "struct.init")); + context.SetLocal( + node_id, EmitStructOrTupleValueRepresentation( + context, node.type_id, node.elements_id, "struct.init")); break; } } } auto HandleStructValue(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [struct_literal_id, refs_id] = node.GetAsStructValue(); - context.SetLocal(node_id, EmitStructOrTupleValueRepresentation( - context, node.type_id(), refs_id, "struct")); + SemIR::StructValue node) -> void { + context.SetLocal(node_id, + EmitStructOrTupleValueRepresentation( + context, node.type_id, node.elements_id, "struct")); } auto HandleStructTypeField(FunctionContext& /*context*/, - SemIR::NodeId /*node_id*/, SemIR::Node /*node*/) - -> void { + SemIR::NodeId /*node_id*/, + SemIR::StructTypeField /*node*/) -> void { // No action to take. } auto HandleTupleAccess(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [tuple_node_id, index] = node.GetAsTupleAccess(); - context.SetLocal(node_id, - GetStructOrTupleElement(context, tuple_node_id, index.index, - node.type_id(), "tuple.elem")); + SemIR::TupleAccess node) -> void { + context.SetLocal( + node_id, GetStructOrTupleElement(context, node.tuple_id, node.index.index, + node.type_id, "tuple.elem")); } auto HandleTupleIndex(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [tuple_node_id, index_node_id] = node.GetAsTupleIndex(); - auto index_node = context.semantics_ir().GetNode(index_node_id); - const auto index = context.semantics_ir() - .GetIntegerLiteral(index_node.GetAsIntegerLiteral()) - .getZExtValue(); + SemIR::TupleIndex node) -> void { + auto index_node = + context.semantics_ir().GetNodeAs(node.index_id); + auto index = + context.semantics_ir().GetInteger(index_node.integer_id).getZExtValue(); context.SetLocal(node_id, - GetStructOrTupleElement(context, tuple_node_id, index, - node.type_id(), "tuple.index")); + GetStructOrTupleElement(context, node.tuple_id, index, + node.type_id, "tuple.index")); } auto HandleTupleLiteral(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { + SemIR::TupleLiteral node) -> void { // A TupleLiteral should always be converted to a TupleInit or TupleValue if // its value is needed. context.SetLocal(node_id, - llvm::PoisonValue::get(context.GetType(node.type_id()))); + llvm::PoisonValue::get(context.GetType(node.type_id))); } auto HandleTupleInit(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto* llvm_type = context.GetType(node.type_id()); + SemIR::TupleInit node) -> void { + auto* llvm_type = context.GetType(node.type_id); - switch (SemIR::GetInitializingRepresentation(context.semantics_ir(), - node.type_id()) - .kind) { + switch ( + SemIR::GetInitializingRepresentation(context.semantics_ir(), node.type_id) + .kind) { case SemIR::InitializingRepresentation::None: case SemIR::InitializingRepresentation::InPlace: // TODO: Add a helper to poison a value slot. @@ -476,35 +455,34 @@ auto HandleTupleInit(FunctionContext& context, SemIR::NodeId node_id, break; case SemIR::InitializingRepresentation::ByCopy: { - auto [struct_literal_id, refs_id] = node.GetAsTupleInit(); context.SetLocal( - node_id, EmitStructOrTupleValueRepresentation(context, node.type_id(), - refs_id, "tuple.init")); + node_id, EmitStructOrTupleValueRepresentation( + context, node.type_id, node.elements_id, "tuple.init")); break; } } } auto HandleTupleValue(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [struct_literal_id, refs_id] = node.GetAsTupleValue(); - context.SetLocal(node_id, EmitStructOrTupleValueRepresentation( - context, node.type_id(), refs_id, "tuple")); + SemIR::TupleValue node) -> void { + context.SetLocal( + node_id, EmitStructOrTupleValueRepresentation(context, node.type_id, + node.elements_id, "tuple")); } auto HandleUnaryOperatorNot(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal(node_id, context.builder().CreateNot(context.GetLocal( - node.GetAsUnaryOperatorNot()))); + SemIR::UnaryOperatorNot node) -> void { + context.SetLocal( + node_id, context.builder().CreateNot(context.GetLocal(node.operand_id))); } auto HandleVarStorage(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { + SemIR::VarStorage node) -> void { // TODO: Eventually this name will be optional, and we'll want to provide // something like `var` as a default. However, that's not possible right now // so cannot be tested. - auto name = context.semantics_ir().GetString(node.GetAsVarStorage()); - auto* alloca = context.builder().CreateAlloca(context.GetType(node.type_id()), + auto name = context.semantics_ir().GetString(node.name_id); + auto* alloca = context.builder().CreateAlloca(context.GetType(node.type_id), /*ArraySize=*/nullptr, name); context.SetLocal(node_id, alloca); } diff --git a/toolchain/lower/handle_expression_category.cpp b/toolchain/lower/handle_expression_category.cpp index 8c8faa1c91e6..c7e645edbbe0 100644 --- a/toolchain/lower/handle_expression_category.cpp +++ b/toolchain/lower/handle_expression_category.cpp @@ -8,24 +8,24 @@ namespace Carbon::Lower { auto HandleBindValue(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { + SemIR::BindValue node) -> void { switch (auto rep = SemIR::GetValueRepresentation(context.semantics_ir(), - node.type_id()); + node.type_id); rep.kind) { case SemIR::ValueRepresentation::None: // Nothing should use this value, but StubReference needs a value to // propagate. // TODO: Remove this now the StubReferences are gone. context.SetLocal(node_id, - llvm::PoisonValue::get(context.GetType(node.type_id()))); + llvm::PoisonValue::get(context.GetType(node.type_id))); break; case SemIR::ValueRepresentation::Copy: context.SetLocal(node_id, context.builder().CreateLoad( - context.GetType(node.type_id()), - context.GetLocal(node.GetAsBindValue()))); + context.GetType(node.type_id), + context.GetLocal(node.value_id))); break; case SemIR::ValueRepresentation::Pointer: - context.SetLocal(node_id, context.GetLocal(node.GetAsBindValue())); + context.SetLocal(node_id, context.GetLocal(node.value_id)); break; case SemIR::ValueRepresentation::Custom: CARBON_FATAL() << "TODO: Add support for BindValue with custom value rep"; @@ -33,28 +33,27 @@ auto HandleBindValue(FunctionContext& context, SemIR::NodeId node_id, } auto HandleTemporary(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - auto [temporary_id, init_id] = node.GetAsTemporary(); - context.FinishInitialization(node.type_id(), temporary_id, init_id); - context.SetLocal(node_id, context.GetLocal(temporary_id)); + SemIR::Temporary node) -> void { + context.FinishInitialization(node.type_id, node.storage_id, node.init_id); + context.SetLocal(node_id, context.GetLocal(node.storage_id)); } auto HandleTemporaryStorage(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - context.SetLocal( - node_id, context.builder().CreateAlloca(context.GetType(node.type_id()), - nullptr, "temp")); + SemIR::TemporaryStorage node) -> void { + context.SetLocal(node_id, + context.builder().CreateAlloca(context.GetType(node.type_id), + nullptr, "temp")); } auto HandleValueAsReference(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node node) -> void { - CARBON_CHECK(SemIR::GetExpressionCategory(context.semantics_ir(), - node.GetAsValueAsReference()) == - SemIR::ExpressionCategory::Value); + SemIR::ValueAsReference node) -> void { CARBON_CHECK( - SemIR::GetValueRepresentation(context.semantics_ir(), node.type_id()) + SemIR::GetExpressionCategory(context.semantics_ir(), node.value_id) == + SemIR::ExpressionCategory::Value); + CARBON_CHECK( + SemIR::GetValueRepresentation(context.semantics_ir(), node.type_id) .kind == SemIR::ValueRepresentation::Pointer); - context.SetLocal(node_id, context.GetLocal(node.GetAsValueAsReference())); + context.SetLocal(node_id, context.GetLocal(node.value_id)); } } // namespace Carbon::Lower diff --git a/toolchain/lower/handle_type.cpp b/toolchain/lower/handle_type.cpp index ffb4bc1548f6..4418fe6fe3dc 100644 --- a/toolchain/lower/handle_type.cpp +++ b/toolchain/lower/handle_type.cpp @@ -7,27 +7,27 @@ namespace Carbon::Lower { auto HandleArrayType(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node /*node*/) -> void { + SemIR::ArrayType /*node*/) -> void { context.SetLocal(node_id, context.GetTypeAsValue()); } auto HandleConstType(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node /*node*/) -> void { + SemIR::ConstType /*node*/) -> void { context.SetLocal(node_id, context.GetTypeAsValue()); } auto HandlePointerType(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node /*node*/) -> void { + SemIR::PointerType /*node*/) -> void { context.SetLocal(node_id, context.GetTypeAsValue()); } auto HandleStructType(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node /*node*/) -> void { + SemIR::StructType /*node*/) -> void { context.SetLocal(node_id, context.GetTypeAsValue()); } auto HandleTupleType(FunctionContext& context, SemIR::NodeId node_id, - SemIR::Node /*node*/) -> void { + SemIR::TupleType /*node*/) -> void { context.SetLocal(node_id, context.GetTypeAsValue()); } diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 50e764d07abd..94625ca51337 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -32,6 +32,7 @@ cc_library( deps = [ "//common:check", "//common:ostream", + "//common:struct_reflection", "//toolchain/base:index_base", "//toolchain/parse:tree", "//toolchain/sem_ir:builtin_kind", diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 4f377e7f61b4..8923aca0da32 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -24,11 +24,11 @@ File::File() // 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_SEMANTICS_BUILTIN_KIND(Name, ...) \ - nodes_.push_back(Node::Builtin::Make(BuiltinKind::Name, \ - BuiltinKind::Name == BuiltinKind::Error \ - ? TypeId::Error \ - : TypeId::TypeType)); +#define CARBON_SEMANTICS_BUILTIN_KIND(Name, ...) \ + nodes_.push_back(Builtin(BuiltinKind::Name == BuiltinKind::Error \ + ? TypeId::Error \ + : TypeId::TypeType, \ + BuiltinKind::Name)); #include "toolchain/sem_ir/builtin_kind.def" CARBON_CHECK(nodes_.size() == BuiltinKind::ValidCount) @@ -51,8 +51,8 @@ File::File(std::string filename, const File* builtins) static constexpr auto BuiltinIR = CrossReferenceIRId(0); for (auto [i, node] : llvm::enumerate(builtins->nodes_)) { // We can reuse builtin type IDs because they're special-cased values. - nodes_.push_back(Node::CrossReference::Make(node.type_id(), BuiltinIR, - SemIR::NodeId(i))); + nodes_.push_back( + CrossReference(node.type_id(), BuiltinIR, SemIR::NodeId(i))); } } @@ -150,13 +150,13 @@ auto File::Print(llvm::raw_ostream& out, bool include_builtins) const -> void { << "\n"; PrintList(out, "functions", functions_); - // Integer literals are an APInt, and default to a signed print, but the - // ZExtValue print is correct. - PrintList(out, "integer_literals", integer_literals_, + // Integer values are APInts, and default to a signed print, but we currently + // treat them as unsigned. + PrintList(out, "integers", integers_, [](llvm::raw_ostream& out, const llvm::APInt& val) { val.print(out, /*isSigned=*/false); }); - PrintList(out, "real_literals", real_literals_); + PrintList(out, "reals", reals_); PrintList(out, "strings", strings_); PrintList(out, "types", types_); PrintBlock(out, "type_blocks", type_blocks_); @@ -210,7 +210,6 @@ static auto GetTypePrecedence(NodeKind kind) -> int { case NodeKind::FunctionDeclaration: case NodeKind::InitializeFrom: case NodeKind::IntegerLiteral: - case NodeKind::Invalid: case NodeKind::NameReference: case NodeKind::NameReferenceUntyped: case NodeKind::Namespace: @@ -276,13 +275,14 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { case NodeKind::ArrayType: { - auto [bound_id, type_id] = node.GetAsArrayType(); + auto array = node.As(); if (step.index == 0) { out << "["; steps.push_back(step.Next()); - steps.push_back({.node_id = GetTypeAllowBuiltinTypes(type_id)}); + steps.push_back( + {.node_id = GetTypeAllowBuiltinTypes(array.element_type_id)}); } else if (step.index == 1) { - out << "; " << GetArrayBoundValue(bound_id) << "]"; + out << "; " << GetArrayBoundValue(array.bound_id) << "]"; } break; } @@ -292,7 +292,7 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const // Add parentheses if required. auto inner_type_node_id = - GetTypeAllowBuiltinTypes(node.GetAsConstType()); + GetTypeAllowBuiltinTypes(node.As().inner_id); if (GetTypePrecedence(GetNode(inner_type_node_id).kind()) < GetTypePrecedence(node.kind())) { out << "("; @@ -308,15 +308,15 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const case NodeKind::PointerType: { if (step.index == 0) { steps.push_back(step.Next()); - steps.push_back( - {.node_id = GetTypeAllowBuiltinTypes(node.GetAsPointerType())}); + steps.push_back({.node_id = GetTypeAllowBuiltinTypes( + node.As().pointee_id)}); } else if (step.index == 1) { out << "*"; } break; } case NodeKind::StructType: { - auto refs = GetNodeBlock(node.GetAsStructType()); + auto refs = GetNodeBlock(node.As().fields_id); if (refs.empty()) { out << "{}"; break; @@ -334,13 +334,13 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const break; } case NodeKind::StructTypeField: { - auto [name_id, type_id] = node.GetAsStructTypeField(); - out << "." << GetString(name_id) << ": "; - steps.push_back({.node_id = GetTypeAllowBuiltinTypes(type_id)}); + auto field = node.As(); + out << "." << GetString(field.name_id) << ": "; + steps.push_back({.node_id = GetTypeAllowBuiltinTypes(field.type_id)}); break; } case NodeKind::TupleType: { - auto refs = GetTypeBlock(node.GetAsTupleType()); + auto refs = GetTypeBlock(node.As().elements_id); if (refs.empty()) { out << "()"; break; @@ -410,8 +410,6 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const // when stringification is needed. out << ""; break; - case NodeKind::Invalid: - llvm_unreachable("NodeKind::Invalid is never used."); } } @@ -421,7 +419,7 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const auto outer_node = GetNode(outer_node_id); if (outer_node.kind() == NodeKind::TupleType || (outer_node.kind() == NodeKind::StructType && - GetNodeBlock(outer_node.GetAsStructType()).empty())) { + GetNodeBlock(outer_node.As().fields_id).empty())) { out << " as type"; } } @@ -437,7 +435,6 @@ auto GetExpressionCategory(const File& file, NodeId node_id) // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { - case NodeKind::Invalid: case NodeKind::Assign: case NodeKind::Branch: case NodeKind::BranchIf: @@ -452,15 +449,14 @@ auto GetExpressionCategory(const File& file, NodeId node_id) return ExpressionCategory::NotExpression; case NodeKind::CrossReference: { - auto [xref_id, xref_node_id] = node.GetAsCrossReference(); - ir = &ir->GetCrossReferenceIR(xref_id); - node_id = xref_node_id; + auto xref = node.As(); + ir = &ir->GetCrossReferenceIR(xref.ir_id); + node_id = xref.node_id; continue; } case NodeKind::NameReference: { - auto [name_id, value_id] = node.GetAsNameReference(); - node_id = value_id; + node_id = node.As().value_id; continue; } @@ -485,38 +481,32 @@ auto GetExpressionCategory(const File& file, NodeId node_id) return ExpressionCategory::Value; case NodeKind::BindName: { - auto [name_id, value_id] = node.GetAsBindName(); - node_id = value_id; + node_id = node.As().value_id; continue; } case NodeKind::ArrayIndex: { - auto [base_id, index_id] = node.GetAsArrayIndex(); - node_id = base_id; + node_id = node.As().array_id; continue; } case NodeKind::StructAccess: { - auto [base_id, member_index] = node.GetAsStructAccess(); - node_id = base_id; + node_id = node.As().struct_id; continue; } case NodeKind::TupleAccess: { - auto [base_id, index_id] = node.GetAsTupleAccess(); - node_id = base_id; + node_id = node.As().tuple_id; continue; } case NodeKind::TupleIndex: { - auto [base_id, index_id] = node.GetAsTupleIndex(); - node_id = base_id; + node_id = node.As().tuple_id; continue; } case NodeKind::SpliceBlock: { - auto [block_id, result_id] = node.GetAsSpliceBlock(); - node_id = result_id; + node_id = node.As().result_id; continue; } @@ -569,7 +559,6 @@ auto GetValueRepresentation(const File& file, TypeId type_id) case NodeKind::FunctionDeclaration: case NodeKind::InitializeFrom: case NodeKind::IntegerLiteral: - case NodeKind::Invalid: case NodeKind::NameReference: case NodeKind::NameReferenceUntyped: case NodeKind::Namespace: @@ -597,15 +586,14 @@ auto GetValueRepresentation(const File& file, TypeId type_id) CARBON_FATAL() << "Type refers to non-type node " << node; case NodeKind::CrossReference: { - auto [xref_id, xref_node_id] = node.GetAsCrossReference(); - ir = &ir->GetCrossReferenceIR(xref_id); - node_id = xref_node_id; + auto xref = node.As(); + ir = &ir->GetCrossReferenceIR(xref.ir_id); + node_id = xref.node_id; continue; } case NodeKind::SpliceBlock: { - auto [block_id, result_id] = node.GetAsSpliceBlock(); - node_id = result_id; + node_id = node.As().result_id; continue; } @@ -616,16 +604,15 @@ auto GetValueRepresentation(const File& file, TypeId type_id) return {.kind = ValueRepresentation::Pointer, .type = type_id}; case NodeKind::StructType: { - const auto& fields = ir->GetNodeBlock(node.GetAsStructType()); + const auto& fields = ir->GetNodeBlock(node.As().fields_id); if (fields.empty()) { // An empty struct has an empty representation. return {.kind = ValueRepresentation::None, .type = TypeId::Invalid}; } if (fields.size() == 1) { // A struct with one field has the same representation as its field. - auto [field_name_id, field_type_id] = - ir->GetNode(fields.front()).GetAsStructTypeField(); - node_id = ir->GetTypeAllowBuiltinTypes(field_type_id); + node_id = ir->GetTypeAllowBuiltinTypes( + ir->GetNode(fields.front()).As().type_id); continue; } // For any other struct, use a pointer representation. @@ -633,7 +620,8 @@ auto GetValueRepresentation(const File& file, TypeId type_id) } case NodeKind::TupleType: { - const auto& elements = ir->GetTypeBlock(node.GetAsTupleType()); + const auto& elements = + ir->GetTypeBlock(node.As().elements_id); if (elements.empty()) { // An empty tuple has an empty representation. return {.kind = ValueRepresentation::None, .type = TypeId::Invalid}; @@ -650,7 +638,7 @@ auto GetValueRepresentation(const File& file, TypeId type_id) case NodeKind::Builtin: // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) - switch (node.GetAsBuiltin()) { + switch (node.As().builtin_kind) { case BuiltinKind::TypeType: case BuiltinKind::Error: case BuiltinKind::Invalid: @@ -670,7 +658,7 @@ auto GetValueRepresentation(const File& file, TypeId type_id) return {.kind = ValueRepresentation::Copy, .type = type_id}; case NodeKind::ConstType: - node_id = ir->GetTypeAllowBuiltinTypes(node.GetAsConstType()); + node_id = ir->GetTypeAllowBuiltinTypes(node.As().inner_id); continue; } } diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index b39abbfe2f38..3ece8e22998b 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -51,7 +51,9 @@ struct Function : public Printable { llvm::SmallVector body_block_ids; }; -struct RealLiteral : public Printable { +// TODO: Replace this with a Rational type, per the design: +// docs/design/expressions/literals.md +struct Real : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{mantissa: "; mantissa.print(out, /*isSigned=*/false); @@ -91,7 +93,7 @@ class File : public Printable { // Returns array bound value from the bound node. auto GetArrayBoundValue(NodeId bound_id) const -> uint64_t { - return GetIntegerLiteral(GetNode(bound_id).GetAsIntegerLiteral()) + return GetInteger(GetNodeAs(bound_id).integer_id) .getZExtValue(); } @@ -119,18 +121,18 @@ class File : public Printable { return functions_[function_id.index]; } - // Adds an integer literal, returning an ID to reference it. - auto AddIntegerLiteral(llvm::APInt integer_literal) -> IntegerLiteralId { - IntegerLiteralId id(integer_literals_.size()); + // Adds an integer value, returning an ID to reference it. + auto AddInteger(llvm::APInt integer) -> IntegerId { + IntegerId id(integers_.size()); // TODO: Return failure on overflow instead of crashing. CARBON_CHECK(id.index >= 0); - integer_literals_.push_back(integer_literal); + integers_.push_back(integer); return id; } - // Returns the requested integer literal. - auto GetIntegerLiteral(IntegerLiteralId int_id) const -> const llvm::APInt& { - return integer_literals_[int_id.index]; + // Returns the requested integer value. + auto GetInteger(IntegerId int_id) const -> const llvm::APInt& { + return integers_[int_id.index]; } // Adds a name scope, returning an ID to reference it. @@ -175,6 +177,12 @@ class File : public Printable { // 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. @@ -225,18 +233,18 @@ class File : public Printable { return node_blocks_[block_id.index]; } - // Adds a real literal, returning an ID to reference it. - auto AddRealLiteral(RealLiteral real_literal) -> RealLiteralId { - RealLiteralId id(real_literals_.size()); + // Adds a real value, returning an ID to reference it. + auto AddReal(Real real) -> RealId { + RealId id(reals_.size()); // TODO: Return failure on overflow instead of crashing. CARBON_CHECK(id.index >= 0); - real_literals_.push_back(real_literal); + reals_.push_back(real); return id; } - // Returns the requested real literal. - auto GetRealLiteral(RealLiteralId int_id) const -> const RealLiteral& { - return real_literals_[int_id.index]; + // Returns the requested real value. + auto GetReal(RealId real_id) const -> const Real& { + return reals_[real_id.index]; } // Adds an string, returning an ID to reference it. @@ -372,14 +380,14 @@ class File : public Printable { // crossing node blocks). llvm::SmallVector cross_reference_irs_; - // Storage for integer literals. - llvm::SmallVector integer_literals_; + // Storage for integer values. + llvm::SmallVector integers_; // Storage for name scopes. llvm::SmallVector> name_scopes_; - // Storage for real literals. - llvm::SmallVector real_literals_; + // Storage for real values. + llvm::SmallVector reals_; // Storage for strings. strings_ provides a list of allocated strings, while // string_to_id_ provides a mapping to identify strings. diff --git a/toolchain/sem_ir/file_test.cpp b/toolchain/sem_ir/file_test.cpp index 7a14b794f18f..d1950c6c4c33 100644 --- a/toolchain/sem_ir/file_test.cpp +++ b/toolchain/sem_ir/file_test.cpp @@ -47,8 +47,8 @@ TEST(SemIRTest, YAML) { auto file = Yaml::Sequence(ElementsAre(Yaml::Mapping(ElementsAre( Pair("cross_reference_irs_size", "1"), Pair("functions", Yaml::Sequence(SizeIs(1))), - Pair("integer_literals", Yaml::Sequence(ElementsAre("0"))), - Pair("real_literals", Yaml::Sequence(IsEmpty())), + Pair("integers", Yaml::Sequence(ElementsAre("0"))), + Pair("reals", Yaml::Sequence(IsEmpty())), Pair("strings", Yaml::Sequence(ElementsAre("F", "x"))), Pair("types", Yaml::Sequence(ElementsAre(node_builtin))), Pair("type_blocks", Yaml::Sequence(IsEmpty())), diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index f2eeec858342..84afd9d303e2 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -350,55 +350,54 @@ class NodeNamer { switch (node.kind()) { case NodeKind::Branch: { - auto dest_id = node.GetAsBranch(); - AddBlockLabel(scope_idx, dest_id, node); + AddBlockLabel(scope_idx, node.As().target_id, node); break; } case NodeKind::BranchIf: { - auto [dest_id, cond_id] = node.GetAsBranchIf(); - AddBlockLabel(scope_idx, dest_id, node); + AddBlockLabel(scope_idx, node.As().target_id, node); break; } case NodeKind::BranchWithArg: { - auto [dest_id, arg_id] = node.GetAsBranchWithArg(); - AddBlockLabel(scope_idx, dest_id, node); + AddBlockLabel(scope_idx, node.As().target_id, node); break; } case NodeKind::SpliceBlock: { - auto [block_id, result_id] = node.GetAsSpliceBlock(); - CollectNamesInBlock(scope_idx, block_id); + CollectNamesInBlock(scope_idx, node.As().block_id); break; } case NodeKind::BindName: { - auto [name_id, value_id] = node.GetAsBindName(); - add_node_name_id(name_id); + add_node_name_id(node.As().name_id); continue; } case NodeKind::FunctionDeclaration: { add_node_name_id( - semantics_ir_.GetFunction(node.GetAsFunctionDeclaration()) + semantics_ir_ + .GetFunction(node.As().function_id) .name_id); continue; } case NodeKind::NameReference: { - auto [name_id, value_id] = node.GetAsNameReference(); - add_node_name(semantics_ir_.GetString(name_id).str() + ".ref"); + add_node_name( + semantics_ir_.GetString(node.As().name_id).str() + + ".ref"); continue; } case NodeKind::NameReferenceUntyped: { - auto [name_id, value_id] = node.GetAsNameReferenceUntyped(); - add_node_name(semantics_ir_.GetString(name_id).str() + ".ref"); + add_node_name( + semantics_ir_.GetString(node.As().name_id) + .str() + + ".ref"); continue; } case NodeKind::Parameter: { - add_node_name_id(node.GetAsParameter()); + add_node_name_id(node.As().name_id); continue; } case NodeKind::VarStorage: { // TODO: Eventually this name will be optional, and we'll want to // provide something like `var` as a default. However, that's not // possible right now so cannot be tested. - add_node_name_id(node.GetAsVarStorage()); + add_node_name_id(node.As().name_id); continue; } default: { @@ -515,7 +514,7 @@ class Formatter { auto FormatInstruction(NodeId node_id) -> void { if (!node_id.is_valid()) { Indent(); - out_ << NodeKind::Invalid.ir_name() << "\n"; + out_ << "invalid\n"; return; } @@ -526,9 +525,9 @@ class Formatter { // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (node.kind()) { -#define CARBON_SEMANTICS_NODE_KIND(Name) \ - case NodeKind::Name: \ - FormatInstruction(node_id, node); \ +#define CARBON_SEMANTICS_NODE_KIND(Kind) \ + case NodeKind::Kind: \ + FormatInstruction(node_id, node.As()); \ break; #include "toolchain/sem_ir/node_kind.def" } @@ -536,12 +535,12 @@ class Formatter { auto Indent() -> void { out_.indent(indent_); } - template - auto FormatInstruction(NodeId node_id, Node node) -> void { + template + auto FormatInstruction(NodeId node_id, NodeT node) -> void { Indent(); FormatInstructionLHS(node_id, node); - out_ << node.kind().ir_name(); - FormatInstructionRHS(node); + out_ << NodeT::Kind.ir_name(); + FormatInstructionRHS(node); out_ << "\n"; } @@ -575,68 +574,59 @@ class Formatter { } } - template - auto FormatInstructionRHS(Node node) -> void { + template + auto FormatInstructionRHS(NodeT node) -> void { // By default, an instruction has a comma-separated argument list. - FormatArgs(Kind::Get(node)); + std::apply([&](auto... args) { FormatArgs(args...); }, node.args_tuple()); } - template <> - auto FormatInstructionRHS(Node node) -> void { + auto FormatInstructionRHS(BlockArg node) -> void { out_ << " "; - FormatLabel(node.GetAsBlockArg()); + FormatLabel(node.block_id); } - template <> - auto FormatInstruction(NodeId /*node_id*/, Node node) - -> void { + auto FormatInstruction(NodeId /*node_id*/, BranchIf node) -> void { if (!in_terminator_sequence_) { Indent(); } - auto [label_id, cond_id] = node.GetAsBranchIf(); out_ << "if "; - FormatNodeName(cond_id); + FormatNodeName(node.cond_id); out_ << " " << NodeKind::Branch.ir_name() << " "; - FormatLabel(label_id); + FormatLabel(node.target_id); out_ << " else "; in_terminator_sequence_ = true; } - template <> - auto FormatInstruction(NodeId /*node_id*/, Node node) - -> void { + auto FormatInstruction(NodeId /*node_id*/, BranchWithArg node) -> void { if (!in_terminator_sequence_) { Indent(); } - auto [label_id, arg_id] = node.GetAsBranchWithArg(); out_ << NodeKind::BranchWithArg.ir_name() << " "; - FormatLabel(label_id); + FormatLabel(node.target_id); out_ << "("; - FormatNodeName(arg_id); + FormatNodeName(node.arg_id); out_ << ")\n"; in_terminator_sequence_ = false; } - template <> - auto FormatInstruction(NodeId /*node_id*/, Node node) -> void { + auto FormatInstruction(NodeId /*node_id*/, Branch node) -> void { if (!in_terminator_sequence_) { Indent(); } out_ << NodeKind::Branch.ir_name() << " "; - FormatLabel(node.GetAsBranch()); + FormatLabel(node.target_id); out_ << "\n"; in_terminator_sequence_ = false; } - template <> - auto FormatInstructionRHS(Node node) -> void { + auto FormatInstructionRHS(ArrayInit node) -> void { out_ << " "; - auto [src_id, refs_id] = node.GetAsArrayInit(); - FormatArg(src_id); + FormatArg(node.tuple_id); - llvm::ArrayRef refs = semantics_ir_.GetNodeBlock(refs_id); - auto inits = refs.drop_back(1); - auto return_slot_id = refs.back(); + llvm::ArrayRef inits_and_return_slot = + semantics_ir_.GetNodeBlock(node.inits_and_return_slot_id); + auto inits = inits_and_return_slot.drop_back(1); + auto return_slot_id = inits_and_return_slot.back(); out_ << ", ("; llvm::ListSeparator sep; @@ -648,16 +638,14 @@ class Formatter { FormatReturnSlot(return_slot_id); } - template <> - auto FormatInstructionRHS(Node node) -> void { + auto FormatInstructionRHS(Call node) -> void { out_ << " "; - auto [args_id, callee_id] = node.GetAsCall(); - FormatArg(callee_id); + FormatArg(node.function_id); - llvm::ArrayRef args = semantics_ir_.GetNodeBlock(args_id); + llvm::ArrayRef args = semantics_ir_.GetNodeBlock(node.args_id); bool has_return_slot = - semantics_ir_.GetFunction(callee_id).return_slot_id.is_valid(); + semantics_ir_.GetFunction(node.function_id).return_slot_id.is_valid(); NodeId return_slot_id = NodeId::Invalid; if (has_return_slot) { return_slot_id = args.back(); @@ -677,30 +665,24 @@ class Formatter { } } - template <> - auto FormatInstructionRHS(Node node) -> void { - auto [src_id, dest_id] = node.GetAsInitializeFrom(); - FormatArgs(src_id); - FormatReturnSlot(dest_id); + auto FormatInstructionRHS(InitializeFrom node) -> void { + FormatArgs(node.src_id); + FormatReturnSlot(node.dest_id); } - template <> - auto FormatInstructionRHS(Node node) -> void { + auto FormatInstructionRHS(CrossReference node) -> void { // TODO: Figure out a way to make this meaningful. We'll need some way to // name cross-reference IRs, perhaps by the node ID of the import? - auto [xref_id, node_id] = node.GetAsCrossReference(); - out_ << " " << xref_id << "." << node_id; + out_ << " " << node.ir_id << "." << node.node_id; } - template <> - auto FormatInstructionRHS(Node node) -> void { - auto [block_id, result_id] = node.GetAsSpliceBlock(); - FormatArgs(result_id); + auto FormatInstructionRHS(SpliceBlock node) -> void { + FormatArgs(node.result_id); out_ << " {"; - if (!semantics_ir_.GetNodeBlock(block_id).empty()) { + if (!semantics_ir_.GetNodeBlock(node.block_id).empty()) { out_ << "\n"; indent_ += 2; - FormatCodeBlock(block_id); + FormatCodeBlock(node.block_id); indent_ -= 2; Indent(); } @@ -708,39 +690,29 @@ class Formatter { } // StructTypeFields are formatted as part of their StructType. - template <> - auto FormatInstruction(NodeId /*node_id*/, - Node /*node*/) -> void {} + auto FormatInstruction(NodeId /*node_id*/, StructTypeField /*node*/) -> void { + } - template <> - auto FormatInstructionRHS(Node node) -> void { + auto FormatInstructionRHS(StructType node) -> void { out_ << " {"; llvm::ListSeparator sep; - for (auto field_id : semantics_ir_.GetNodeBlock(node.GetAsStructType())) { + for (auto field_id : semantics_ir_.GetNodeBlock(node.fields_id)) { out_ << sep << "."; - auto [field_name_id, field_type_id] = - semantics_ir_.GetNode(field_id).GetAsStructTypeField(); - FormatString(field_name_id); + auto field = semantics_ir_.GetNodeAs(field_id); + FormatString(field.name_id); out_ << ": "; - FormatType(field_type_id); + FormatType(field.type_id); } out_ << "}"; } - auto FormatArgs(Node::NoArgs /*unused*/) -> void {} + auto FormatArgs() -> void {} - template - auto FormatArgs(Arg1 arg) -> void { + template + auto FormatArgs(Args... args) -> void { out_ << ' '; - FormatArg(arg); - } - - template - auto FormatArgs(std::pair args) -> void { - out_ << ' '; - FormatArg(args.first); - out_ << ","; - FormatArgs(args.second); + llvm::ListSeparator sep; + ((out_ << sep, FormatArg(args)), ...); } auto FormatArg(BoolValue v) -> void { out_ << v; } @@ -749,8 +721,8 @@ class Formatter { auto FormatArg(FunctionId id) -> void { FormatFunctionName(id); } - auto FormatArg(IntegerLiteralId id) -> void { - semantics_ir_.GetIntegerLiteral(id).print(out_, /*isSigned=*/false); + auto FormatArg(IntegerId id) -> void { + semantics_ir_.GetInteger(id).print(out_, /*isSigned=*/false); } auto FormatArg(MemberIndex index) -> void { out_ << index; } @@ -790,9 +762,9 @@ class Formatter { out_ << ')'; } - auto FormatArg(RealLiteralId id) -> void { + auto FormatArg(RealId id) -> void { // TODO: Format with a `.` when the exponent is near zero. - const auto& real = semantics_ir_.GetRealLiteral(id); + const auto& real = semantics_ir_.GetReal(id); real.mantissa.print(out_, /*isSigned=*/false); out_ << (real.is_decimal ? 'e' : 'p') << real.exponent; } diff --git a/toolchain/sem_ir/node.cpp b/toolchain/sem_ir/node.cpp index e2c9a8bfb74b..32ce35468afb 100644 --- a/toolchain/sem_ir/node.cpp +++ b/toolchain/sem_ir/node.cpp @@ -6,28 +6,20 @@ namespace Carbon::SemIR { -static auto PrintArgs(llvm::raw_ostream& /*out*/, - const Node::NoArgs /*no_args*/) -> void {} - -template -static auto PrintArgs(llvm::raw_ostream& out, T arg) -> void { - out << ", arg0: " << arg; -} - -template -static auto PrintArgs(llvm::raw_ostream& out, std::pair args) -> void { - PrintArgs(out, args.first); - out << ", arg1: " << args.second; -} - auto Node::Print(llvm::raw_ostream& out) const -> void { out << "{kind: " << kind_; + + auto print_args = [&](auto... args) { + int n = 0; + ((out << ", arg" << n++ << ": " << args), ...); + }; + // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (kind_) { -#define CARBON_SEMANTICS_NODE_KIND(Name) \ - case NodeKind::Name: \ - PrintArgs(out, GetAs##Name()); \ +#define CARBON_SEMANTICS_NODE_KIND(Name) \ + case NodeKind::Name: \ + std::apply(print_args, As().args_tuple()); \ break; #include "toolchain/sem_ir/node_kind.def" } diff --git a/toolchain/sem_ir/node.h b/toolchain/sem_ir/node.h index 5fef8edd311e..d5adbf72b934 100644 --- a/toolchain/sem_ir/node.h +++ b/toolchain/sem_ir/node.h @@ -9,6 +9,7 @@ #include "common/check.h" #include "common/ostream.h" +#include "common/struct_reflection.h" #include "toolchain/base/index_base.h" #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/builtin_kind.h" @@ -91,8 +92,8 @@ struct BoolValue : public IndexBase, public Printable { constexpr BoolValue BoolValue::False = BoolValue(0); constexpr BoolValue BoolValue::True = BoolValue(1); -// The ID of an integer literal. -struct IntegerLiteralId : public IndexBase, public Printable { +// The ID of an integer value. +struct IntegerId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "int"; @@ -143,8 +144,8 @@ constexpr NodeBlockId NodeBlockId::Invalid = constexpr NodeBlockId NodeBlockId::Unreachable = NodeBlockId(NodeBlockId::InvalidIndex - 1); -// The ID of a real literal. -struct RealLiteralId : public IndexBase, public Printable { +// The ID of a real number value. +struct RealId : public IndexBase, public Printable { using IndexBase::IndexBase; auto Print(llvm::raw_ostream& out) const -> void { out << "real"; @@ -207,271 +208,323 @@ struct MemberIndex : public IndexBase, public Printable { } }; +// Data storage for the operands of each kind of node. +// +// For each node kind declared in `node_kinds.def`, a struct here with the same +// name describes the kind-specific storage for that node. A node kind can +// store up to two IDs. +// +// A typed node also has: +// +// - An injected `Parse::Node parse_node;` field, unless it specifies +// `using HasParseNode = std::false_type;`, and +// - An injected `TypeId type_id;` field, unless it specifies +// `using HasTypeId = std::false_type;`. +namespace NodeData { +struct AddressOf { + NodeId lvalue_id; +}; + +struct ArrayIndex { + NodeId array_id; + NodeId index_id; +}; + +// Initializes an array from a tuple. `tuple_id` is the source tuple +// expression. `inits_and_return_slot_id` contains one initializer per array +// element, plus a final element that is the return slot for the +// initialization. +struct ArrayInit { + NodeId tuple_id; + NodeBlockId inits_and_return_slot_id; +}; + +struct ArrayType { + NodeId bound_id; + TypeId element_type_id; +}; + +// Performs a source-level initialization or assignment of `lhs_id` from +// `rhs_id`. This finishes initialization of `lhs_id` in the same way as +// `InitializeFrom`. +struct Assign { + using HasType = std::false_type; + + NodeId lhs_id; + NodeId rhs_id; +}; + +struct BinaryOperatorAdd { + NodeId lhs_id; + NodeId rhs_id; +}; + +struct BindName { + StringId name_id; + NodeId value_id; +}; + +struct BindValue { + NodeId value_id; +}; + +struct BlockArg { + NodeBlockId block_id; +}; + +struct BoolLiteral { + BoolValue value; +}; + +struct Branch { + using HasType = std::false_type; + + NodeBlockId target_id; +}; + +struct BranchIf { + using HasType = std::false_type; + + NodeBlockId target_id; + NodeId cond_id; +}; + +struct BranchWithArg { + using HasType = std::false_type; + + NodeBlockId target_id; + NodeId arg_id; +}; + +struct Builtin { + // Builtins don't have a parse node associated with them. + using HasParseNode = std::false_type; + + BuiltinKind builtin_kind; +}; + +struct Call { + NodeBlockId args_id; + FunctionId function_id; +}; + +struct ConstType { + TypeId inner_id; +}; + +struct CrossReference { + // A node's parse tree node must refer to a node in the current parse tree. + // This cannot use the cross-referenced node's parse tree node because it + // will be in a different parse tree. + using HasParseNode = std::false_type; + + CrossReferenceIRId ir_id; + NodeId node_id; +}; + +struct Dereference { + NodeId pointer_id; +}; + +struct FunctionDeclaration { + using HasType = std::false_type; + + FunctionId function_id; +}; + +// Finalizes the initialization of `dest_id` from the initializer expression +// `src_id`, by performing a final copy from source to destination, for types +// whose initialization is not in-place. +struct InitializeFrom { + NodeId src_id; + NodeId dest_id; +}; + +struct IntegerLiteral { + IntegerId integer_id; +}; + +struct NameReference { + StringId name_id; + NodeId value_id; +}; + +struct NameReferenceUntyped { + StringId name_id; + NodeId value_id; +}; + +struct Namespace { + using HasType = std::false_type; + + NameScopeId name_scope_id; +}; + +struct NoOp { + using HasType = std::false_type; +}; + +struct Parameter { + StringId name_id; +}; + +struct PointerType { + TypeId pointee_id; +}; + +struct RealLiteral { + RealId real_id; +}; + +struct Return { + using HasType = std::false_type; +}; + +struct ReturnExpression { + using HasType = std::false_type; + + NodeId expr_id; +}; + +struct SpliceBlock { + NodeBlockId block_id; + NodeId result_id; +}; + +struct StringLiteral { + StringId string_id; +}; + +struct StructAccess { + NodeId struct_id; + MemberIndex index; +}; + +struct StructInit { + NodeId src_id; + NodeBlockId elements_id; +}; + +struct StructLiteral { + NodeBlockId elements_id; +}; + +struct StructType { + NodeBlockId fields_id; +}; + +struct StructTypeField { + using HasType = std::false_type; + + StringId name_id; + TypeId type_id; +}; + +struct StructValue { + NodeId src_id; + NodeBlockId elements_id; +}; + +struct Temporary { + NodeId storage_id; + NodeId init_id; +}; + +struct TemporaryStorage {}; + +struct TupleAccess { + NodeId tuple_id; + MemberIndex index; +}; + +struct TupleIndex { + NodeId tuple_id; + NodeId index_id; +}; + +struct TupleInit { + NodeId src_id; + NodeBlockId elements_id; +}; + +struct TupleLiteral { + NodeBlockId elements_id; +}; + +struct TupleType { + TypeBlockId elements_id; +}; + +struct TupleValue { + NodeId src_id; + NodeBlockId elements_id; +}; + +struct UnaryOperatorNot { + NodeId operand_id; +}; + +struct ValueAsReference { + NodeId value_id; +}; + +struct VarStorage { + StringId name_id; +}; +} // namespace NodeData + +template +struct TypedNode; + // The standard structure for Node. This is trying to provide a minimal // amount of information for a node: // -// - parse_node for error placement. -// - kind for run-time logic when the input Kind is unknown. -// - type_id for quick type checking. +// - `parse_node` for error placement. +// - `kind` for run-time logic when the input Kind is unknown. +// - `type_id` for quick type checking. // - Up to two Kind-specific members. // // For each Kind in NodeKind, a typical flow looks like: // -// - Create a `Node` using `Node::Kind::Make()` +// - Create a specific kind of `Node` using the appropriate `TypedNode` +// constructor. // - Access cross-Kind members using `node.type_id()` and similar. -// - Access Kind-specific members using `node.GetAsKind()`, which depending on -// the number of members will return one of NoArgs, a single value, or a -// `std::pair` of values. -// - Using the wrong `node.GetAsKind()` is a programming error, and should -// CHECK-fail in debug modes (opt may too, but it's not an API guarantee). -// -// Internally, each Kind uses the `Factory*` types to provide a boilerplate -// `Make` and `Get` methods. +// - Access Kind-specific members using `node.As()`, which produces a +// `TypedNode` with type-specific members, including `parse_node` and +// `type_id` for nodes that have associated parse nodes and types. +// - Using the wrong kind in `node.As()` is a programming error, and +// will CHECK-fail in debug modes (opt may too, but it's not an API +// guarantee). +// - Use `node.TryAs()` to safely access type-specific node data where +// the node's kind is not known. class Node : public Printable { public: - struct NoArgs {}; + template + /*implicit*/ + Node(TypedNode typed_node) + : Node(typed_node.parse_node_or_invalid(), NodeKind::Create(Kind), + typed_node.type_id_or_invalid(), typed_node.arg0_or_invalid(), + typed_node.arg1_or_invalid()) {} - // Factory base classes are private, then used for public classes. This class - // has two public and two private sections to prevent accidents. - private: - // Provides Make and Get to support 0, 1, or 2 arguments for a Node. - // These are protected so that child factories can opt in to what pieces they - // want to use. - template - class FactoryBase { - protected: - static auto Make(Parse::Node parse_node, TypeId type_id, - ArgTypes... arg_ids) -> Node { - return Node(parse_node, NodeKind::Create(Kind), type_id, - arg_ids.index...); + // Casts this node to the given typed node, which must match the node's kind, + // and returns the typed node. + template + auto As() const -> Typed { + CARBON_CHECK(kind() == Typed::Kind) << "Casting node of kind " << kind() + << " to wrong kind " << Typed::Kind; + return Typed::FromRawData(parse_node_, type_id_, arg0_, arg1_); + } + + // If this node is the given kind, returns a typed node, otherwise returns + // nullopt. + template + auto TryAs() const -> std::optional { + if (kind() == Typed::Kind) { + return As(); + } else { + return std::nullopt; } - - static auto Get(Node node) { - struct Unused {}; - return GetImpl(node); - } - - private: - // GetImpl handles the different return types based on ArgTypes. - template - static auto GetImpl(Node node) -> std::pair { - CARBON_CHECK(node.kind() == Kind); - return {Arg0Type(node.arg0_), Arg1Type(node.arg1_)}; - } - template - static auto GetImpl(Node node) -> Arg0Type { - CARBON_CHECK(node.kind() == Kind); - return Arg0Type(node.arg0_); - } - template - static auto GetImpl(Node node) -> NoArgs { - CARBON_CHECK(node.kind() == Kind); - return NoArgs(); - } - }; - - // Provide Get along with a Make that requires a type. - template - class Factory : public FactoryBase { - public: - using FactoryBase::Make; - using FactoryBase::Get; - }; - - // Provides Get along with a Make that assumes the node doesn't produce a - // typed value. - template - class FactoryNoType : public FactoryBase { - public: - static auto Make(Parse::Node parse_node, ArgTypes... args) { - return FactoryBase::Make(parse_node, TypeId::Invalid, - args...); - } - using FactoryBase::Get; - }; - - public: - // Invalid is in the NodeKind enum, but should never be used. - class Invalid { - public: - static auto Get(Node /*node*/) -> Node::NoArgs { - CARBON_FATAL() << "Invalid access"; - } - }; - - using AddressOf = Node::Factory; - - using ArrayIndex = - Factory; - - // Initializes an array from a tuple. `tuple_id` is the source tuple - // expression. `refs_id` contains one initializer per array element, plus a - // final element that is the return slot for the initialization. - using ArrayInit = Factory; - - using ArrayType = Node::Factory; - - // Performs a source-level initialization or assignment of `lhs_id` from - // `rhs_id`. This finishes initialization of `lhs_id` in the same way as - // `InitializeFrom`. - using Assign = Node::FactoryNoType; - - using BinaryOperatorAdd = Node::Factory; - - using BindName = - Factory; - - using BindValue = Factory; - - using BlockArg = Factory; - - using BoolLiteral = Factory; - - using Branch = FactoryNoType; - - using BranchIf = FactoryNoType; - - using BranchWithArg = - FactoryNoType; - - class Builtin { - public: - static auto Make(BuiltinKind builtin_kind, TypeId type_id) -> Node { - // Builtins won't have a Parse::Tree node associated, so we provide the - // default invalid one. - // This can't use the standard Make function because of the `AsInt()` cast - // instead of `.index`. - return Node(Parse::Node::Invalid, NodeKind::Builtin, type_id, - builtin_kind.AsInt()); - } - static auto Get(Node node) -> BuiltinKind { - return BuiltinKind::FromInt(node.arg0_); - } - }; - - using Call = Factory; - - using ConstType = Factory; - - class CrossReference - : public FactoryBase { - public: - static auto Make(TypeId type_id, CrossReferenceIRId ir_id, NodeId node_id) - -> Node { - // A node's parse tree node must refer to a node in the current parse - // tree. This cannot use the cross-referenced node's parse tree node - // because it will be in a different parse tree. - return FactoryBase::Make(Parse::Node::Invalid, type_id, ir_id, node_id); - } - using FactoryBase::Get; - }; - - using Dereference = Factory; - - using FunctionDeclaration = - FactoryNoType; - - // Finalizes the initialization of `dest_id` from the initializer expression - // `src_id`, by performing a final copy from source to destination, for types - // whose initialization is not in-place. - using InitializeFrom = - Factory; - - using IntegerLiteral = - Factory; - - using NameReference = Factory; - - using NameReferenceUntyped = - Factory; - - using Namespace = - FactoryNoType; - - using NoOp = FactoryNoType; - - using Parameter = Factory; - - using PointerType = Factory; - - using RealLiteral = Factory; - - using Return = FactoryNoType; - - using ReturnExpression = - FactoryNoType; - - using SpliceBlock = Factory; - - using StringLiteral = - Factory; - - using StructAccess = Factory; - - using StructInit = Factory; - - using StructLiteral = - Factory; - - using StructType = Factory; - - using StructTypeField = - FactoryNoType; - - using StructValue = Factory; - - using Temporary = - Factory; - - using TemporaryStorage = Factory; - - using TupleAccess = Factory; - - using TupleIndex = - Factory; - - using TupleInit = Factory; - - using TupleLiteral = Factory; - - using TupleType = Factory; - - using TupleValue = Factory; - - using UnaryOperatorNot = - Factory; - - using ValueAsReference = - Factory; - - using VarStorage = Factory; - - explicit Node() - : Node(Parse::Node::Invalid, NodeKind::Invalid, TypeId::Invalid) {} - - // Provide `node.GetAsKind()` as an instance method for all kinds, essentially - // an alias for`Node::Kind::Get(node)`. -#define CARBON_SEMANTICS_NODE_KIND(Name) \ - auto GetAs##Name() const { return Name::Get(*this); } -#include "toolchain/sem_ir/node_kind.def" + } auto parse_node() const -> Parse::Node { return parse_node_; } auto kind() const -> NodeKind { return kind_; } @@ -482,10 +535,6 @@ class Node : public Printable { auto Print(llvm::raw_ostream& out) const -> void; private: - // Builtins have peculiar construction, so they are a friend rather than using - // a factory base class. - friend struct NodeForBuiltin; - explicit Node(Parse::Node parse_node, NodeKind kind, TypeId type_id, int32_t arg0 = NodeId::InvalidIndex, int32_t arg1 = NodeId::InvalidIndex) @@ -499,7 +548,7 @@ class Node : public Printable { NodeKind kind_; TypeId type_id_; - // Use GetAsKind to access arg0 and arg1. + // Use `As` to access arg0 and arg1. int32_t arg0_; int32_t arg1_; }; @@ -510,6 +559,237 @@ class Node : public Printable { // may be worth investigating further. static_assert(sizeof(Node) == 20, "Unexpected Node size"); +namespace NodeInternals { +template +struct TypedNodeImpl; +} + +// Representation of a specific kind of node. This has the following public +// data members: +// +// - A `parse_node` member for nodes with an associated parse node. +// - A `type_id` member for nodes with an associated type. +// - Each member from the `NodeData` struct, above. +// +// A `TypedNode` can be constructed by passing its fields in order: +// +// - First, the `parse_node`, for nodes with a location, +// - Then, the `type_id`, for nodes with a type, +// - Then, each field of the `NodeData` struct above. +template +struct TypedNode : NodeInternals::TypedNodeImpl, + Printable> { + static constexpr NodeKind Kind = NodeKind::Create(KindT); + using Data = DataT; + + // Members from base classes, repeated here to make the API of this class + // easier to understand. +#if 0 + // From HasParseNodeBase, unless `DataT::HasParseNode` is `false_type`. + Parse::Node parse_node; + + // From HasTypeBase, unless `DataT::HasType` is `false_type`. + TypeId type_id; + + // Up to two operand types and names, from `DataT`. + IdType1 id_1; + IdType2 id_2; + + // Construct the node from its elements. For any omitted fields, the + // parameter is removed here. Constructor is inherited from TypedNodeBase. + TypedNode(Parse::Node parse_node, TypeId type_id, IdType1 id_1, IdType2 id_2); + + // Returns the operands of the node. + auto args() const -> DataT; + + // Returns the operands of the node as a tuple of up to two operands. + auto args_tuple() const -> std::tuple; +#endif + + using NodeInternals::TypedNodeImpl::TypedNodeImpl; + + static auto FromRawData(Parse::Node parse_node, TypeId type_id, int32_t arg0, + int32_t arg1) -> TypedNode { + return TypedNode(TypedNode::FromParseNode(parse_node), + TypedNode::FromTypeId(type_id), + TypedNode::FromRawArgs(arg0, arg1)); + } + + auto Print(llvm::raw_ostream& out) const -> void { Node(*this).Print(out); } +}; + +// Declare type names for each specific kind of node. +#define CARBON_SEMANTICS_NODE_KIND(Name) \ + using Name = TypedNode; +#include "toolchain/sem_ir/node_kind.def" + +// Implementation details for typed nodes. +namespace NodeInternals { +template +using GetHasParseNode = typename T::HasParseNode; +template +using GetHasType = typename T::HasType; + +// Apply Getter, or provide Default if it doesn't exist. +template typename Getter, typename Default, + typename Void = void> +struct GetWithDefaultImpl { + using Result = Default; +}; +template typename Getter, typename Default> +struct GetWithDefaultImpl>> { + using Result = Getter; +}; +template typename Getter, typename Default> +using GetWithDefault = typename GetWithDefaultImpl::Result; + +// Base class for nodes that have a `parse_node` field. +struct HasParseNodeBase { + Parse::Node parse_node; + + static auto FromParseNode(Parse::Node parse_node) -> HasParseNodeBase { + return {.parse_node = parse_node}; + } + + auto parse_node_or_invalid() const -> Parse::Node { return parse_node; } +}; + +// Base class for nodes that have no `parse_node` field. +struct HasNoParseNodeBase { + static auto FromParseNode(Parse::Node /*parse_node*/) -> HasNoParseNodeBase { + return {}; + } + + auto parse_node_or_invalid() const -> Parse::Node { + return Parse::Node::Invalid; + } +}; + +// ParseNodeBase holds the `parse_node` field if the node has a parse tree +// node, and is either HasParseNodeBase or HasNoParseNodeBase. +template +using ParseNodeBase = + std::conditional_t{}, + HasParseNodeBase, HasNoParseNodeBase>; + +// Base class for nodes that have a `type_id` field. +struct HasTypeBase { + TypeId type_id; + + static auto FromTypeId(TypeId type_id) -> HasTypeBase { + return {.type_id = type_id}; + } + + auto type_id_or_invalid() const -> TypeId { return type_id; } +}; + +// Base class for nodes that have no `type_id` field. +struct HasNoTypeBase { + static auto FromTypeId(TypeId /*type_id*/) -> HasNoTypeBase { return {}; } + + auto type_id_or_invalid() const -> TypeId { return TypeId::Invalid; } +}; + +// TypeBase holds the `type_id` field if the node has a type, and is either +// TypedNodeBase or UntypedNodeBase. +template +using TypeBase = + std::conditional_t{}, + HasTypeBase, HasNoTypeBase>; + +// Convert a field from its raw representation. +template +constexpr auto FromRaw(int32_t raw) -> T { + return T(raw); +} +template <> +constexpr auto FromRaw(int32_t raw) -> BuiltinKind { + return BuiltinKind::FromInt(raw); +} + +// Convert a field to its raw representation. +constexpr auto ToRaw(IndexBase base) -> int32_t { return base.index; } +constexpr auto ToRaw(BuiltinKind kind) -> int32_t { return kind.AsInt(); } + +template +using FieldTypes = decltype(StructReflection::AsTuple(std::declval())); + +// Base class for nodes that contains the node data. +template > +struct DataBase; + +template +struct DataBase> : T { + static_assert(sizeof...(Fields) <= 2, "Too many fields in node data"); + + static auto FromRawArgs(decltype(ToRaw(std::declval()))... args, ...) + -> DataBase { + return {FromRaw(args)...}; + } + + // Returns the operands of the node. + auto args() const -> T { return *this; } + + // Returns the operands of the node as a tuple. + auto args_tuple() const -> auto { + return StructReflection::AsTuple(static_cast(*this)); + } + + auto arg0_or_invalid() const -> auto { + if constexpr (sizeof...(Fields) >= 1) { + return ToRaw(std::get<0>(args_tuple())); + } else { + return NodeId::InvalidIndex; + } + } + + auto arg1_or_invalid() const -> auto { + if constexpr (sizeof...(Fields) >= 2) { + return ToRaw(std::get<1>(args_tuple())); + } else { + return NodeId::InvalidIndex; + } + } +}; + +template +struct TypedNodeBase; + +// A helper base class that produces a constructor with one correctly-typed +// parameter for each struct field. +template +struct TypedNodeBase, + std::tuple, std::tuple> + : ParseNodeBase, TypeBase, DataBase { + // Braced initialization of base classes confuses clang-format. + // clang-format off + constexpr TypedNodeBase(ParseNodeFields... parse_node_fields, + TypeFields... type_fields, DataFields... data_fields) + : ParseNodeBase{parse_node_fields...}, + TypeBase{type_fields...}, + DataBase{data_fields...} { + } + // clang-format on + + constexpr TypedNodeBase(ParseNodeBase parse_node_base, + TypeBase type_base, DataBase data_base) + : ParseNodeBase(parse_node_base), + TypeBase(type_base), + DataBase(data_base) {} +}; + +template +using MakeTypedNodeBase = + TypedNodeBase>, + FieldTypes>, FieldTypes>; + +template +struct TypedNodeImpl : MakeTypedNodeBase { + using MakeTypedNodeBase::MakeTypedNodeBase; +}; +} // namespace NodeInternals + // Provides base support for use of Id types as DenseMap/DenseSet keys. // Instantiated below. template diff --git a/toolchain/sem_ir/node_kind.def b/toolchain/sem_ir/node_kind.def index d59040bc0b38..4af0ceec0286 100644 --- a/toolchain/sem_ir/node_kind.def +++ b/toolchain/sem_ir/node_kind.def @@ -40,8 +40,6 @@ #error "Must define the x-macro to use this file." #endif -CARBON_SEMANTICS_NODE_KIND_IMPL(Invalid, "invalid", None, NotTerminator) - // A cross-reference between IRs. CARBON_SEMANTICS_NODE_KIND_IMPL(CrossReference, "xref", Typed, NotTerminator)