diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 404110306ee8..b9b6e859aa50 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -168,7 +168,7 @@ static auto AddDominatedBlockAndBranchImpl(Context& context, return SemIR::NodeBlockId::Unreachable; } auto block_id = context.semantics_ir().AddNodeBlockId(); - context.AddNode(BranchNode(parse_node, block_id, args...)); + context.AddNode(BranchNode{parse_node, block_id, args...}); return block_id; } @@ -201,7 +201,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::Branch(parse_node, new_block_id)); + AddNode(SemIR::Branch{parse_node, new_block_id}); } node_block_stack().Pop(); } @@ -219,7 +219,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( if (new_block_id == SemIR::NodeBlockId::Unreachable) { new_block_id = semantics_ir().AddNodeBlockId(); } - AddNode(SemIR::BranchWithArg(parse_node, new_block_id, arg_id)); + AddNode(SemIR::BranchWithArg{parse_node, new_block_id, arg_id}); } node_block_stack().Pop(); } @@ -228,7 +228,7 @@ auto Context::AddConvergenceBlockWithArgAndPush( // Acquire the result value. SemIR::TypeId result_type_id = semantics_ir().GetNode(*block_args.begin()).type_id(); - return AddNode(SemIR::BlockArg(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. @@ -389,7 +389,7 @@ class TypeCompleter { type_node.As().fields_id)) { Push(context_.semantics_ir() .GetNodeAs(field_id) - .type_id); + .field_type_id); } break; @@ -511,10 +511,10 @@ class TypeCompleter { for (auto field_id : fields) { auto field = context_.semantics_ir().GetNodeAs(field_id); - auto field_value_rep = GetNestedValueRepresentation(field.type_id); - if (field_value_rep.type_id != field.type_id) { + auto field_value_rep = GetNestedValueRepresentation(field.field_type_id); + if (field_value_rep.type_id != field.field_type_id) { same_as_object_rep = false; - field.type_id = field_value_rep.type_id; + field.field_type_id = field_value_rep.type_id; field_id = context_.AddNode(field); } value_rep_fields.push_back(field_id); @@ -779,7 +779,7 @@ static auto ProfileType(Context& semantics_context, SemIR::Node node, semantics_context.semantics_ir().GetNodeAs( field_id); canonical_id.AddInteger(field.name_id.index); - canonical_id.AddInteger(field.type_id.index); + canonical_id.AddInteger(field.field_type_id.index); } break; } @@ -822,7 +822,7 @@ auto Context::CanonicalizeStructType(Parse::Node parse_node, SemIR::NodeBlockId refs_id) -> SemIR::TypeId { return CanonicalizeTypeAndAddNodeIfNew( - SemIR::StructType(parse_node, SemIR::TypeId::TypeType, refs_id)); + SemIR::StructType{parse_node, SemIR::TypeId::TypeType, refs_id}); } auto Context::CanonicalizeTupleType(Parse::Node parse_node, @@ -833,8 +833,8 @@ auto Context::CanonicalizeTupleType(Parse::Node parse_node, ProfileTupleType(type_ids, canonical_id); }; auto make_tuple_node = [&] { - return AddNode(SemIR::TupleType(parse_node, SemIR::TypeId::TypeType, - semantics_ir_->AddTypeBlock(type_ids))); + return AddNode(SemIR::TupleType{parse_node, SemIR::TypeId::TypeType, + semantics_ir_->AddTypeBlock(type_ids)}); }; return CanonicalizeTypeImpl(SemIR::TupleType::Kind, profile_tuple, make_tuple_node); @@ -852,7 +852,7 @@ auto Context::GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId { auto Context::GetPointerType(Parse::Node parse_node, SemIR::TypeId pointee_type_id) -> SemIR::TypeId { return CanonicalizeTypeAndAddNodeIfNew( - SemIR::PointerType(parse_node, SemIR::TypeId::TypeType, pointee_type_id)); + SemIR::PointerType{parse_node, SemIR::TypeId::TypeType, pointee_type_id}); } auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId { diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 76cdfea373dc..ac6c73267616 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -90,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::Temporary(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) { @@ -106,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::TemporaryStorage(init.parse_node(), init.type_id())); - return context.AddNode(SemIR::Temporary(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 @@ -132,14 +132,14 @@ static auto MakeElemAccessNode(Context& context, Parse::Node parse_node, // 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::IntegerLiteral( + auto index_id = block.AddNode(SemIR::IntegerLiteral{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::IntegerType), - context.semantics_ir().integers().Add(llvm::APInt(32, i)))); + context.semantics_ir().integers().Add(llvm::APInt(32, i))}); return block.AddNode( - AccessNodeT(parse_node, elem_type_id, aggregate_id, index_id)); + AccessNodeT{parse_node, elem_type_id, aggregate_id, index_id}); } else { - return block.AddNode(AccessNodeT(parse_node, elem_type_id, aggregate_id, - SemIR::MemberIndex(i))); + return block.AddNode(AccessNodeT{parse_node, elem_type_id, aggregate_id, + SemIR::MemberIndex(i)}); } } @@ -230,9 +230,8 @@ 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::TupleType::Data tuple_type, - SemIR::ArrayType::Data array_type, +static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, + SemIR::ArrayType array_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); @@ -278,7 +277,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::NodeId return_slot_id = target.init_id; if (!target.init_id.is_valid()) { return_slot_id = target_block->AddNode( - SemIR::TemporaryStorage(value.parse_node(), target.type_id)); + SemIR::TemporaryStorage{value.parse_node(), target.type_id}); } // Initialize each element of the array from the corresponding element of the @@ -306,16 +305,15 @@ static auto ConvertTupleToArray(Context& context, target_block->InsertHere(); inits.push_back(return_slot_id); - return context.AddNode(SemIR::ArrayInit(value.parse_node(), target.type_id, + return context.AddNode(SemIR::ArrayInit{value.parse_node(), target.type_id, value_id, - semantics_ir.AddNodeBlock(inits))); + 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::TupleType::Data src_type, - SemIR::TupleType::Data dest_type, +static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type, + SemIR::TupleType dest_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); @@ -378,19 +376,18 @@ static auto ConvertTupleToTuple(Context& context, new_block.Set(i, init_id); } - return is_init ? context.AddNode(SemIR::TupleInit(value.parse_node(), + 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(), + new_block.id()}) + : context.AddNode(SemIR::TupleValue{value.parse_node(), target.type_id, value_id, - new_block.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::StructType::Data src_type, - SemIR::StructType::Data dest_type, +static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type, + SemIR::StructType dest_type, SemIR::NodeId value_id, ConversionTarget target) -> SemIR::NodeId { auto& semantics_ir = context.semantics_ir(); @@ -464,8 +461,8 @@ static auto ConvertStructToStruct(Context& context, // approach. 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, + context, value.parse_node(), value_id, src_field.field_type_id, + literal_elems, inner_kind, target.init_id, dest_field.field_type_id, target.init_block, i); if (init_id == SemIR::NodeId::BuiltinError) { return SemIR::NodeId::BuiltinError; @@ -473,12 +470,12 @@ static auto ConvertStructToStruct(Context& context, new_block.Set(i, init_id); } - return is_init ? context.AddNode(SemIR::StructInit(value.parse_node(), + 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(), + new_block.id()}) + : context.AddNode(SemIR::StructValue{value.parse_node(), target.type_id, value_id, - new_block.id())); + new_block.id()}); } // Returns whether `category` is a valid expression category to produce as a @@ -715,7 +712,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id, target.kind != ConversionTarget::Discarded) { // TODO: Support types with custom value representations. expr_id = context.AddNode( - SemIR::BindValue(expr.parse_node(), expr.type_id(), expr_id)); + SemIR::BindValue{expr.parse_node(), expr.type_id(), expr_id}); } break; } @@ -730,8 +727,8 @@ 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::InitializeFrom( - parse_node, target.type_id, expr_id, target.init_id)); + expr_id = context.AddNode(SemIR::InitializeFrom{ + parse_node, target.type_id, expr_id, target.init_id}); } } diff --git a/toolchain/check/handle_array.cpp b/toolchain/check/handle_array.cpp index b6625ba3af9c..ddb96c802445 100644 --- a/toolchain/check/handle_array.cpp +++ b/toolchain/check/handle_array.cpp @@ -42,9 +42,9 @@ auto HandleArrayExpression(Context& context, Parse::Node parse_node) -> bool { if (bound_value.getActiveBits() <= 64) { context.AddNodeAndPush( parse_node, - SemIR::ArrayType( + SemIR::ArrayType{ parse_node, SemIR::TypeId::TypeType, bound_node_id, - ExpressionAsType(context, parse_node, element_type_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 4d3f26168353..196d3b378041 100644 --- a/toolchain/check/handle_call_expression.cpp +++ b/toolchain/check/handle_call_expression.cpp @@ -43,7 +43,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { // 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::TemporaryStorage(call_expr_parse_node, callable.return_type_id)); + SemIR::TemporaryStorage{call_expr_parse_node, callable.return_type_id}); context.ParamOrArgSave(temp_id); } @@ -57,7 +57,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { } auto call_node_id = context.AddNode( - SemIR::Call(call_expr_parse_node, type_id, callee_id, refs_id)); + SemIR::Call{call_expr_parse_node, type_id, callee_id, refs_id}); context.node_stack().Push(parse_node, call_node_id); return true; diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index b69a8e53dea8..34db8e4d439f 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -27,8 +27,8 @@ static auto BuildClassDeclaration(Context& context) // Add the class declaration. auto class_decl = - SemIR::ClassDeclaration(class_keyword, SemIR::TypeId::TypeType, - SemIR::ClassId::Invalid, decl_block_id); + SemIR::ClassDeclaration{class_keyword, SemIR::TypeId::TypeType, + SemIR::ClassId::Invalid, decl_block_id}; auto class_decl_id = context.AddNode(class_decl); // Check whether this is a redeclaration. diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 05a8d342e12c..0ef92e12c5de 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -58,9 +58,9 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) .PopForSoloParseNode(); // Add the function declaration. - auto function_decl = SemIR::FunctionDeclaration( + auto function_decl = SemIR::FunctionDeclaration{ fn_node, context.GetBuiltinType(SemIR::BuiltinKind::FunctionType), - SemIR::FunctionId::Invalid); + SemIR::FunctionId::Invalid}; auto function_decl_id = context.AddNode(function_decl); // Check whether this is a redeclaration. @@ -145,7 +145,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::Return(parse_node)); + context.AddNode(SemIR::Return{parse_node}); } } @@ -228,8 +228,8 @@ auto HandleReturnType(Context& context, Parse::Node parse_node) -> bool { // TODO: Use a dedicated node rather than VarStorage here. context.AddNodeAndPush( parse_node, - SemIR::VarStorage(parse_node, type_id, - context.semantics_ir().strings().Add("return"))); + SemIR::VarStorage{parse_node, type_id, + context.semantics_ir().strings().Add("return")}); return true; } diff --git a/toolchain/check/handle_if_statement.cpp b/toolchain/check/handle_if_statement.cpp index c8250e4e0418..1c98a45e2ae4 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::Branch(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 8532c7d78a3f..5ffffcb3467b 100644 --- a/toolchain/check/handle_index.cpp +++ b/toolchain/check/handle_index.cpp @@ -69,12 +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::ValueAsReference( - parse_node, operand_type_id, operand_node_id)); + operand_node_id = context.AddNode(SemIR::ValueAsReference{ + parse_node, operand_type_id, operand_node_id}); } auto elem_id = context.AddNode( - SemIR::ArrayIndex(parse_node, array_type.element_type_id, - operand_node_id, cast_index_id)); + 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. @@ -104,8 +104,8 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool { index_node_id = SemIR::NodeId::BuiltinError; } context.AddNodeAndPush(parse_node, - SemIR::TupleIndex(parse_node, element_type_id, - operand_node_id, index_node_id)); + SemIR::TupleIndex{parse_node, element_type_id, + operand_node_id, index_node_id}); return true; } default: { diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index 17634dd48cea..14c6fd0adc2e 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -13,37 +13,37 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool { case Lex::TokenKind::True: { context.AddNodeAndPush( parse_node, - SemIR::BoolLiteral( + SemIR::BoolLiteral{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::BoolType), token_kind == Lex::TokenKind::True ? SemIR::BoolValue::True - : SemIR::BoolValue::False)); + : SemIR::BoolValue::False}); break; } case Lex::TokenKind::IntegerLiteral: { context.AddNodeAndPush( parse_node, - SemIR::IntegerLiteral( + SemIR::IntegerLiteral{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::IntegerType), - context.tokens().GetIntegerLiteral(token))); + context.tokens().GetIntegerLiteral(token)}); break; } case Lex::TokenKind::RealLiteral: { context.AddNodeAndPush( parse_node, - SemIR::RealLiteral( + SemIR::RealLiteral{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::FloatingPointType), - context.tokens().GetRealLiteral(token))); + context.tokens().GetRealLiteral(token)}); break; } case Lex::TokenKind::StringLiteral: { auto id = context.tokens().GetStringLiteral(token); context.AddNodeAndPush( parse_node, - SemIR::StringLiteral( + SemIR::StringLiteral{ parse_node, - context.GetBuiltinType(SemIR::BuiltinKind::StringType), id)); + context.GetBuiltinType(SemIR::BuiltinKind::StringType), id}); break; } case Lex::TokenKind::Type: { diff --git a/toolchain/check/handle_loop_statement.cpp b/toolchain/check/handle_loop_statement.cpp index 41e2caf36974..923f4427d32e 100644 --- a/toolchain/check/handle_loop_statement.cpp +++ b/toolchain/check/handle_loop_statement.cpp @@ -20,7 +20,7 @@ auto HandleBreakStatementStart(Context& context, Parse::Node parse_node) "`break` can only be used in a loop."); context.emitter().Emit(parse_node, BreakOutsideLoop); } else { - context.AddNode(SemIR::Branch(parse_node, stack.back().break_target)); + context.AddNode(SemIR::Branch{parse_node, stack.back().break_target}); } context.node_block_stack().Pop(); @@ -41,7 +41,7 @@ auto HandleContinueStatementStart(Context& context, Parse::Node parse_node) "`continue` can only be used in a loop."); context.emitter().Emit(parse_node, ContinueOutsideLoop); } else { - context.AddNode(SemIR::Branch(parse_node, stack.back().continue_target)); + context.AddNode(SemIR::Branch{parse_node, stack.back().continue_target}); } context.node_block_stack().Pop(); @@ -111,7 +111,7 @@ auto HandleWhileStatement(Context& context, Parse::Node parse_node) -> bool { context.break_continue_stack().pop_back(); // Add the loop backedge. - context.AddNode(SemIR::Branch(parse_node, loop_header_id)); + context.AddNode(SemIR::Branch{parse_node, loop_header_id}); context.node_block_stack().Pop(); // Start emitting the loop exit block. diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 363daf011d61..9484c84b6ed7 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -52,7 +52,7 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) // TODO: Track that this node was named within `base_id`. context.AddNodeAndPush( parse_node, - SemIR::NameReference(parse_node, node.type_id(), name_id, node_id)); + SemIR::NameReference{parse_node, node.type_id(), name_id, node_id}); return true; } @@ -73,8 +73,8 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node) context.semantics_ir().GetNodeAs(ref_id); if (name_id == field.name_id) { context.AddNodeAndPush( - parse_node, SemIR::StructAccess(parse_node, field.type_id, - base_id, SemIR::MemberIndex(i))); + parse_node, SemIR::StructAccess{parse_node, field.field_type_id, + base_id, SemIR::MemberIndex(i)}); return true; } } @@ -128,7 +128,7 @@ auto HandleNameExpression(Context& context, Parse::Node parse_node) -> bool { CARBON_CHECK(value.kind().value_kind() == SemIR::NodeValueKind::Typed); context.AddNodeAndPush( parse_node, - SemIR::NameReference(parse_node, value.type_id(), name_id, value_id)); + SemIR::NameReference{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 16ddfc00cb8b..8929a733b9f6 100644 --- a/toolchain/check/handle_namespace.cpp +++ b/toolchain/check/handle_namespace.cpp @@ -15,9 +15,9 @@ 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::Namespace( + auto namespace_id = context.AddNode(SemIR::Namespace{ parse_node, context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType), - context.semantics_ir().AddNameScope())); + 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 2bc7d95c8ab5..478c2fe82c5b 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -24,9 +24,9 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { context.AddNodeAndPush( parse_node, - SemIR::BinaryOperatorAdd( + SemIR::BinaryOperatorAdd{ parse_node, context.semantics_ir().GetNode(lhs_id).type_id(), - lhs_id, rhs_id)); + lhs_id, rhs_id}); return true; case Lex::TokenKind::And: @@ -40,16 +40,16 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool { // its value. auto resume_block_id = context.node_block_stack().PeekOrAdd(/*depth=*/1); context.AddNode( - SemIR::BranchWithArg(parse_node, resume_block_id, rhs_id)); + 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::BlockArg(parse_node, + SemIR::BlockArg{parse_node, context.semantics_ir().GetNode(rhs_id).type_id(), - resume_block_id)); + resume_block_id}); return true; } case Lex::TokenKind::Equal: { @@ -65,7 +65,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::Assign(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 @@ -87,8 +87,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::PointerType(parse_node, SemIR::TypeId::TypeType, - inner_type_id)); + parse_node, SemIR::PointerType{parse_node, SemIR::TypeId::TypeType, + inner_type_id}); return true; } @@ -123,12 +123,12 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { } context.AddNodeAndPush( parse_node, - SemIR::AddressOf( + SemIR::AddressOf{ parse_node, context.GetPointerType( parse_node, context.semantics_ir().GetNode(value_id).type_id()), - value_id)); + value_id}); return true; } @@ -146,7 +146,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { auto inner_type_id = ExpressionAsType(context, parse_node, value_id); context.AddNodeAndPush( parse_node, - SemIR::ConstType(parse_node, SemIR::TypeId::TypeType, inner_type_id)); + SemIR::ConstType{parse_node, SemIR::TypeId::TypeType, inner_type_id}); return true; } @@ -154,9 +154,9 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { value_id = ConvertToBoolValue(context, parse_node, value_id); context.AddNodeAndPush( parse_node, - SemIR::UnaryOperatorNot( + SemIR::UnaryOperatorNot{ parse_node, context.semantics_ir().GetNode(value_id).type_id(), - value_id)); + value_id}); return true; case Lex::TokenKind::Star: { @@ -186,7 +186,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool { builder.Emit(); } context.AddNodeAndPush( - parse_node, SemIR::Dereference(parse_node, result_type_id, value_id)); + parse_node, SemIR::Dereference{parse_node, result_type_id, value_id}); return true; } @@ -209,15 +209,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::BoolLiteral( - parse_node, bool_type_id, SemIR::BoolValue::False)); + 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::UnaryOperatorNot(parse_node, bool_type_id, cond_value_id)); + 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)); + 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 9a8662e3b8b2..f61f5234a962 100644 --- a/toolchain/check/handle_paren.cpp +++ b/toolchain/check/handle_paren.cpp @@ -49,7 +49,7 @@ 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::TupleLiteral(parse_node, type_id, refs_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 962415fa79fa..13d222d66a8a 100644 --- a/toolchain/check/handle_pattern_binding.cpp +++ b/toolchain/check/handle_pattern_binding.cpp @@ -44,14 +44,14 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { cast_type_id = SemIR::TypeId::Error; } context.AddNodeAndPush( - parse_node, SemIR::VarStorage(name_node, cast_type_id, name_id)); + parse_node, SemIR::VarStorage{name_node, cast_type_id, name_id}); break; case Parse::NodeKind::ParameterListStart: // Parameters can have incomplete types in a function declaration, but not // in a function definition. We don't know which kind we have here. context.AddNodeAndPush( - parse_node, SemIR::Parameter(name_node, cast_type_id, name_id)); + parse_node, SemIR::Parameter{name_node, cast_type_id, name_id}); break; case Parse::NodeKind::LetIntroducer: @@ -71,8 +71,8 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { // the `let` pattern before we see the initializer. context.node_stack().Push( parse_node, - context.semantics_ir().AddNodeInNoBlock(SemIR::BindName( - name_node, cast_type_id, name_id, SemIR::NodeId::Invalid))); + 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 f996f5a2762e..26479ca067eb 100644 --- a/toolchain/check/handle_statement.cpp +++ b/toolchain/check/handle_statement.cpp @@ -49,7 +49,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { .Emit(); } - context.AddNode(SemIR::Return(parse_node)); + context.AddNode(SemIR::Return{parse_node}); } else { auto arg = context.node_stack().PopExpression(); context.node_stack() @@ -72,7 +72,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool { callable.return_type_id); } - context.AddNode(SemIR::ReturnExpression(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 b7d223893e3c..248a7ce53890 100644 --- a/toolchain/check/handle_struct.cpp +++ b/toolchain/check/handle_struct.cpp @@ -29,7 +29,7 @@ auto HandleStructFieldType(Context& context, Parse::Node parse_node) -> bool { context.node_stack().PopWithParseNode(); context.AddNodeAndPush( - parse_node, SemIR::StructTypeField(name_node, name_id, cast_type_id)); + parse_node, SemIR::StructTypeField{name_node, name_id, cast_type_id}); return true; } @@ -44,9 +44,9 @@ auto HandleStructFieldValue(Context& context, Parse::Node parse_node) -> bool { StringId name_id = context.node_stack().Pop(); // Store the name for the type. - context.args_type_info_stack().AddNode(SemIR::StructTypeField( + context.args_type_info_stack().AddNode(SemIR::StructTypeField{ parse_node, name_id, - context.semantics_ir().GetNode(value_node_id).type_id())); + context.semantics_ir().GetNode(value_node_id).type_id()}); // Push the value back on the stack as an argument. context.node_stack().Push(parse_node, value_node_id); @@ -66,7 +66,7 @@ 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::StructLiteral(parse_node, type_id, refs_id)); + context.AddNode(SemIR::StructLiteral{parse_node, type_id, refs_id}); context.node_stack().Push(parse_node, value_id); return true; } @@ -100,7 +100,7 @@ auto HandleStructTypeLiteral(Context& context, Parse::Node parse_node) -> bool { context.AddNodeAndPush( parse_node, - SemIR::StructType(parse_node, SemIR::TypeId::TypeType, refs_id)); + 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 c2ea5a07207b..18c30d122eac 100644 --- a/toolchain/check/handle_variable.cpp +++ b/toolchain/check/handle_variable.cpp @@ -41,7 +41,7 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node) init_id = Initialize(context, parse_node, var_id, init_id); // TODO: Consider using different node kinds for assignment versus // initialization. - context.AddNode(SemIR::Assign(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 736a8a0c677d..78f2f7f7067a 100644 --- a/toolchain/check/pending_block.h +++ b/toolchain/check/pending_block.h @@ -64,8 +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::SpliceBlock(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. @@ -74,9 +74,9 @@ class PendingBlock { // 3) Anything else: splice it into the IR, replacing `target_id`. context_.semantics_ir().ReplaceNode( target_id, - SemIR::SpliceBlock(value.parse_node(), value.type_id(), + SemIR::SpliceBlock{value.parse_node(), value.type_id(), context_.semantics_ir().AddNodeBlock(nodes_), - value_id)); + value_id}); } // Prepare to stash more pending instructions. diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 5b02ff536852..cb09bb536753 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -238,9 +238,9 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* { 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.field_type_id.index < SemIR::BuiltinKind::ValidCount) + << field.field_type_id; + subtypes.push_back(GetType(field.field_type_id)); } return llvm::StructType::get(*llvm_context_, subtypes); } diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 2def577d9234..4bdb744383cd 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -14,13 +14,29 @@ cc_library( deps = ["//common:enum_base"], ) +cc_library( + name = "ids", + hdrs = ["ids.h"], + deps = [ + "//common:ostream", + "//toolchain/base:index_base", + "//toolchain/sem_ir:builtin_kind", + ], +) + cc_library( name = "node_kind", srcs = ["node_kind.cpp"], - hdrs = ["node_kind.h"], + hdrs = [ + "node_kind.h", + "typed_nodes.h", + ], textual_hdrs = ["node_kind.def"], deps = [ "//common:enum_base", + "//toolchain/parse:tree", + "//toolchain/sem_ir:builtin_kind", + "//toolchain/sem_ir:ids", "@llvm-project//llvm:Support", ], ) @@ -78,6 +94,18 @@ cc_library( ], ) +cc_test( + name = "typed_nodes_test", + size = "small", + srcs = ["typed_nodes_test.cpp"], + deps = [ + ":node", + ":node_kind", + "//testing/base:gtest_main", + "@com_google_googletest//:gtest", + ], +) + cc_test( name = "yaml_test", size = "small", diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 98bb5eb15238..370128d10802 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -53,10 +53,10 @@ File::File(SharedValueStores& value_stores) // a normal type. Every other builtin is a type, including the // self-referential TypeType. #define CARBON_SEM_IR_BUILTIN_KIND(Name, ...) \ - nodes_.push_back(Builtin(BuiltinKind::Name == BuiltinKind::Error \ + nodes_.push_back(Builtin{BuiltinKind::Name == BuiltinKind::Error \ ? TypeId::Error \ : TypeId::TypeType, \ - BuiltinKind::Name)); + BuiltinKind::Name}); #include "toolchain/sem_ir/builtin_kind.def" CARBON_CHECK(nodes_.size() == BuiltinKind::ValidCount) @@ -82,7 +82,7 @@ File::File(SharedValueStores& value_stores, std::string filename, for (auto [i, node] : llvm::enumerate(builtins->nodes_)) { // We can reuse builtin type IDs because they're special-cased values. nodes_.push_back( - CrossReference(node.type_id(), BuiltinIR, SemIR::NodeId(i))); + CrossReference{node.type_id(), BuiltinIR, SemIR::NodeId(i)}); } } @@ -374,7 +374,8 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, case StructTypeField::Kind: { auto field = node.As(); out << "." << strings().Get(field.name_id) << ": "; - steps.push_back({.node_id = GetTypeAllowBuiltinTypes(field.type_id)}); + steps.push_back( + {.node_id = GetTypeAllowBuiltinTypes(field.field_type_id)}); break; } case TupleType::Kind: { diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index a0645c7280e3..e1745cf043f0 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -667,7 +667,14 @@ class Formatter { template auto FormatInstructionRHS(NodeT node) -> void { // By default, an instruction has a comma-separated argument list. - std::apply([&](auto... args) { FormatArgs(args...); }, node.args_tuple()); + using Info = TypedNodeArgsInfo; + if constexpr (Info::NumArgs == 2) { + FormatArgs(Info::template Get<0>(node), Info::template Get<1>(node)); + } else if constexpr (Info::NumArgs == 1) { + FormatArgs(Info::template Get<0>(node)); + } else { + FormatArgs(); + } } auto FormatInstructionRHS(BlockArg node) -> void { @@ -792,7 +799,7 @@ class Formatter { auto field = semantics_ir_.GetNodeAs(field_id); FormatString(field.name_id); out_ << ": "; - FormatType(field.type_id); + FormatType(field.field_type_id); } out_ << "}"; } diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h new file mode 100644 index 000000000000..38039f8c7634 --- /dev/null +++ b/toolchain/sem_ir/ids.h @@ -0,0 +1,213 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_SEM_IR_IDS_H_ +#define CARBON_TOOLCHAIN_SEM_IR_IDS_H_ + +#include + +#include "common/ostream.h" +#include "toolchain/base/index_base.h" +#include "toolchain/sem_ir/builtin_kind.h" + +namespace Carbon::SemIR { + +// The ID of a node. +struct NodeId : public IndexBase, public Printable { + // An explicitly invalid node ID. + static const NodeId Invalid; + +// Builtin node IDs. +#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) static const NodeId Builtin##Name; +#include "toolchain/sem_ir/builtin_kind.def" + + // Returns the cross-reference node ID for a builtin. This relies on File + // guarantees for builtin cross-reference placement. + static constexpr auto ForBuiltin(BuiltinKind kind) -> NodeId { + return NodeId(kind.AsInt()); + } + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "node"; + if (!is_valid()) { + IndexBase::Print(out); + } else if (index < BuiltinKind::ValidCount) { + out << BuiltinKind::FromInt(index); + } else { + // Use the `+` as a small reminder that this is a delta, rather than an + // absolute index. + out << "+" << index - BuiltinKind::ValidCount; + } + } +}; + +constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex); + +#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) \ + constexpr NodeId NodeId::Builtin##Name = \ + NodeId::ForBuiltin(BuiltinKind::Name); +#include "toolchain/sem_ir/builtin_kind.def" + +// The ID of a function. +struct FunctionId : public IndexBase, public Printable { + // An explicitly invalid function ID. + static const FunctionId Invalid; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "function"; + IndexBase::Print(out); + } +}; + +constexpr FunctionId FunctionId::Invalid = FunctionId(FunctionId::InvalidIndex); + +// The ID of a class. +struct ClassId : public IndexBase, public Printable { + // An explicitly invalid class ID. + static const ClassId Invalid; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "class"; + IndexBase::Print(out); + } +}; + +constexpr ClassId ClassId::Invalid = ClassId(ClassId::InvalidIndex); + +// The ID of a cross-referenced IR. +struct CrossReferenceIRId : public IndexBase, + public Printable { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "ir"; + IndexBase::Print(out); + } +}; + +// A boolean value. +struct BoolValue : public IndexBase, public Printable { + static const BoolValue False; + static const BoolValue True; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + switch (index) { + case 0: + out << "false"; + break; + case 1: + out << "true"; + break; + default: + CARBON_FATAL() << "Invalid bool value " << index; + } + } +}; + +constexpr BoolValue BoolValue::False = BoolValue(0); +constexpr BoolValue BoolValue::True = BoolValue(1); + +// The ID of a name scope. +struct NameScopeId : public IndexBase, public Printable { + // An explicitly invalid ID. + static const NameScopeId Invalid; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "name_scope"; + IndexBase::Print(out); + } +}; + +constexpr NameScopeId NameScopeId::Invalid = + NameScopeId(NameScopeId::InvalidIndex); + +// The ID of a node block. +struct NodeBlockId : public IndexBase, public Printable { + // All File instances must provide the 0th node block as empty. + static const NodeBlockId Empty; + + // An explicitly invalid ID. + static const NodeBlockId Invalid; + + // An ID for unreachable code. + static const NodeBlockId Unreachable; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + if (index == Unreachable.index) { + out << "unreachable"; + } else { + out << "block"; + IndexBase::Print(out); + } + } +}; + +constexpr NodeBlockId NodeBlockId::Empty = NodeBlockId(0); +constexpr NodeBlockId NodeBlockId::Invalid = + NodeBlockId(NodeBlockId::InvalidIndex); +constexpr NodeBlockId NodeBlockId::Unreachable = + NodeBlockId(NodeBlockId::InvalidIndex - 1); + +// The ID of a node block. +struct TypeId : public IndexBase, public Printable { + // The builtin TypeType. + static const TypeId TypeType; + + // The builtin Error. + static const TypeId Error; + + // An explicitly invalid ID. + static const TypeId Invalid; + + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "type"; + if (index == TypeType.index) { + out << "TypeType"; + } else if (index == Error.index) { + out << "Error"; + } else { + IndexBase::Print(out); + } + } +}; + +constexpr TypeId TypeId::TypeType = TypeId(TypeId::InvalidIndex - 2); +constexpr TypeId TypeId::Error = TypeId(TypeId::InvalidIndex - 1); +constexpr TypeId TypeId::Invalid = TypeId(TypeId::InvalidIndex); + +// The ID of a type block. +struct TypeBlockId : public IndexBase, public Printable { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "typeBlock"; + IndexBase::Print(out); + } +}; + +// An index for member access, for structs and tuples. +struct MemberIndex : public IndexBase, public Printable { + using IndexBase::IndexBase; + auto Print(llvm::raw_ostream& out) const -> void { + out << "member"; + IndexBase::Print(out); + } +}; + +} // namespace Carbon::SemIR + +// Support use of Id types as DenseMap/DenseSet keys. +template <> +struct llvm::DenseMapInfo + : public Carbon::IndexMapInfo {}; +template <> +struct llvm::DenseMapInfo + : public Carbon::IndexMapInfo {}; + +#endif // CARBON_TOOLCHAIN_SEM_IR_IDS_H_ diff --git a/toolchain/sem_ir/node.cpp b/toolchain/sem_ir/node.cpp index c7e4088813b9..746aea8d289c 100644 --- a/toolchain/sem_ir/node.cpp +++ b/toolchain/sem_ir/node.cpp @@ -9,17 +9,22 @@ namespace Carbon::SemIR { auto Node::Print(llvm::raw_ostream& out) const -> void { out << "{kind: " << kind_; - auto print_args = [&](auto... args) { - int n = 0; - ((out << ", arg" << n++ << ": " << args), ...); + auto print_args = [&](auto info) { + using Info = decltype(info); + if constexpr (Info::NumArgs > 0) { + out << ", arg0: " << FromRaw>(arg0_); + } + if constexpr (Info::NumArgs > 1) { + out << ", arg1: " << FromRaw>(arg1_); + } }; // clang warns on unhandled enum values; clang-tidy is incorrect here. // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (kind_) { -#define CARBON_SEM_IR_NODE_KIND(Name) \ - case Name::Kind: \ - std::apply(print_args, As().args_tuple()); \ +#define CARBON_SEM_IR_NODE_KIND(Name) \ + case Name::Kind: \ + print_args(TypedNodeArgsInfo()); \ break; #include "toolchain/sem_ir/node_kind.def" } diff --git a/toolchain/sem_ir/node.h b/toolchain/sem_ir/node.h index 09eb23933697..9881506c425b 100644 --- a/toolchain/sem_ir/node.h +++ b/toolchain/sem_ir/node.h @@ -14,515 +14,122 @@ #include "toolchain/parse/tree.h" #include "toolchain/sem_ir/builtin_kind.h" #include "toolchain/sem_ir/node_kind.h" +#include "toolchain/sem_ir/typed_nodes.h" namespace Carbon::SemIR { -// The ID of a node. -struct NodeId : public IndexBase, public Printable { - // An explicitly invalid node ID. - static const NodeId Invalid; +// Data about the arguments of a typed node, to aid in type erasure. The `KindT` +// parameter is used to check that `TypedNode` is a typed node. +template +struct TypedNodeArgsInfo { + // A corresponding std::tuple<...> type. + using Tuple = decltype(StructReflection::AsTuple(std::declval())); -// Builtin node IDs. -#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) static const NodeId Builtin##Name; -#include "toolchain/sem_ir/builtin_kind.def" + static constexpr int FirstArgField = + HasParseNode + HasTypeId; - // Returns the cross-reference node ID for a builtin. This relies on File - // guarantees for builtin cross-reference placement. - static constexpr auto ForBuiltin(BuiltinKind kind) -> NodeId { - return NodeId(kind.AsInt()); - } + static constexpr int NumArgs = std::tuple_size_v - FirstArgField; + static_assert(NumArgs <= 2, + "Unsupported: typed node has more than two data fields"); - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "node"; - if (!is_valid()) { - IndexBase::Print(out); - } else if (index < BuiltinKind::ValidCount) { - out << BuiltinKind::FromInt(index); - } else { - // Use the `+` as a small reminder that this is a delta, rather than an - // absolute index. - out << "+" << index - BuiltinKind::ValidCount; - } + template + using ArgType = std::tuple_element_t; + + template + static auto Get(TypedNode node) -> ArgType { + return std::get(StructReflection::AsTuple(node)); } }; -constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex); - -#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) \ - constexpr NodeId NodeId::Builtin##Name = \ - NodeId::ForBuiltin(BuiltinKind::Name); -#include "toolchain/sem_ir/builtin_kind.def" - -// The ID of a function. -struct FunctionId : public IndexBase, public Printable { - // An explicitly invalid function ID. - static const FunctionId Invalid; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "function"; - IndexBase::Print(out); - } -}; - -constexpr FunctionId FunctionId::Invalid = FunctionId(FunctionId::InvalidIndex); - -// The ID of a class. -struct ClassId : public IndexBase, public Printable { - // An explicitly invalid class ID. - static const ClassId Invalid; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "class"; - IndexBase::Print(out); - } -}; - -constexpr ClassId ClassId::Invalid = ClassId(ClassId::InvalidIndex); - -// The ID of a cross-referenced IR. -struct CrossReferenceIRId : public IndexBase, - public Printable { - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "ir"; - IndexBase::Print(out); - } -}; - -// A boolean value. -struct BoolValue : public IndexBase, public Printable { - static const BoolValue False; - static const BoolValue True; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - switch (index) { - case 0: - out << "false"; - break; - case 1: - out << "true"; - break; - default: - CARBON_FATAL() << "Invalid bool value " << index; - } - } -}; - -constexpr BoolValue BoolValue::False = BoolValue(0); -constexpr BoolValue BoolValue::True = BoolValue(1); - -// The ID of a name scope. -struct NameScopeId : public IndexBase, public Printable { - // An explicitly invalid ID. - static const NameScopeId Invalid; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "name_scope"; - IndexBase::Print(out); - } -}; - -constexpr NameScopeId NameScopeId::Invalid = - NameScopeId(NameScopeId::InvalidIndex); - -// The ID of a node block. -struct NodeBlockId : public IndexBase, public Printable { - // All File instances must provide the 0th node block as empty. - static const NodeBlockId Empty; - - // An explicitly invalid ID. - static const NodeBlockId Invalid; - - // An ID for unreachable code. - static const NodeBlockId Unreachable; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - if (index == Unreachable.index) { - out << "unreachable"; - } else { - out << "block"; - IndexBase::Print(out); - } - } -}; - -constexpr NodeBlockId NodeBlockId::Empty = NodeBlockId(0); -constexpr NodeBlockId NodeBlockId::Invalid = - NodeBlockId(NodeBlockId::InvalidIndex); -constexpr NodeBlockId NodeBlockId::Unreachable = - NodeBlockId(NodeBlockId::InvalidIndex - 1); - -// The ID of a node block. -struct TypeId : public IndexBase, public Printable { - // The builtin TypeType. - static const TypeId TypeType; - - // The builtin Error. - static const TypeId Error; - - // An explicitly invalid ID. - static const TypeId Invalid; - - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "type"; - if (index == TypeType.index) { - out << "TypeType"; - } else if (index == Error.index) { - out << "Error"; - } else { - IndexBase::Print(out); - } - } -}; - -constexpr TypeId TypeId::TypeType = TypeId(TypeId::InvalidIndex - 2); -constexpr TypeId TypeId::Error = TypeId(TypeId::InvalidIndex - 1); -constexpr TypeId TypeId::Invalid = TypeId(TypeId::InvalidIndex); - -// The ID of a type block. -struct TypeBlockId : public IndexBase, public Printable { - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "typeBlock"; - IndexBase::Print(out); - } -}; - -// An index for member access, for structs and tuples. -struct MemberIndex : public IndexBase, public Printable { - using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "member"; - IndexBase::Print(out); - } -}; - -// 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 { - NodeId callee_id; - NodeBlockId args_id; -}; - -struct ClassDeclaration { - ClassId class_id; - // The declaration block, containing the class name's qualifiers and the - // class's generic parameters. - NodeBlockId decl_block_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 { - 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 Namespace { - 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: +// A type-erased representation of a SemIR node, that may be constructed from +// the specific kinds of node defined in `typed_nodes.h`. This provides access +// to common fields present on most or all kinds of nodes: // // - `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. // -// To create a specific kind of `Node`, use the appropriate `TypedNode` -// constructor. A `TypedNode` implicitly converts to a `Node`. +// In addition, kind-specific data can be accessed by casting to the specific +// kind of node: // -// Given a `Node`, you may: -// -// - Access non-Kind-specific members like `Print`. -// - Use `node.kind()` or `Is` to determine what kind of node it is. -// - 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 +// - Use `node.kind()` or `Is` to determine what kind of node it is. +// - Cast to a specific type using `node.As()` +// - 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. +// - Use `node.TryAs()` to safely access type-specific node data +// where the node's kind is not known. class Node : public Printable { public: - 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()) {} + template > + // NOLINTNEXTLINE(google-explicit-constructor) + Node(TypedNode typed_node) + : parse_node_(Parse::Node::Invalid), + kind_(TypedNode::Kind), + type_id_(TypeId::Invalid), + arg0_(NodeId::InvalidIndex), + arg1_(NodeId::InvalidIndex) { + if constexpr (HasParseNode) { + parse_node_ = typed_node.parse_node; + } + if constexpr (HasTypeId) { + type_id_ = typed_node.type_id; + } + if constexpr (Info::NumArgs > 0) { + arg0_ = ToRaw(Info::template Get<0>(typed_node)); + } + if constexpr (Info::NumArgs > 1) { + arg1_ = ToRaw(Info::template Get<1>(typed_node)); + } + } // Returns whether this node has the specified type. - template + template auto Is() const -> bool { - return kind() == Typed::Kind; + return kind() == TypedNode::Kind; } // 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(Is()) << "Casting node of kind " << kind() - << " to wrong kind " << Typed::Kind; - return Typed::FromRawData(parse_node_, type_id_, arg0_, arg1_); + template > + auto As() const -> TypedNode { + CARBON_CHECK(Is()) << "Casting node of kind " << kind() + << " to wrong kind " << TypedNode::Kind; + auto build_with_type_id_and_args = [&](auto... type_id_and_args) { + if constexpr (HasParseNode) { + return TypedNode{parse_node(), type_id_and_args...}; + } else { + return TypedNode{type_id_and_args...}; + } + }; + + auto build_with_args = [&](auto... args) { + if constexpr (HasTypeId) { + return build_with_type_id_and_args(type_id(), args...); + } else { + return build_with_type_id_and_args(args...); + } + }; + + if constexpr (Info::NumArgs == 0) { + return build_with_args(); + } else if constexpr (Info::NumArgs == 1) { + return build_with_args( + FromRaw>(arg0_)); + } else if constexpr (Info::NumArgs == 2) { + return build_with_args( + FromRaw>(arg0_), + FromRaw>(arg1_)); + } } // If this node is the given kind, returns a typed node, otherwise returns // nullopt. - template - auto TryAs() const -> std::optional { - if (Is()) { - return As(); + template + auto TryAs() const -> std::optional { + if (Is()) { + return As(); } else { return std::nullopt; } @@ -537,15 +144,33 @@ class Node : public Printable { auto Print(llvm::raw_ostream& out) const -> void; private: - explicit Node(Parse::Node parse_node, NodeKind kind, TypeId type_id, - int32_t arg0 = NodeId::InvalidIndex, - int32_t arg1 = NodeId::InvalidIndex) + friend class NodeTestHelper; + + // Raw constructor, used for testing. + explicit Node(NodeKind kind, Parse::Node parse_node, TypeId type_id, + int32_t arg0, int32_t arg1) : parse_node_(parse_node), kind_(kind), type_id_(type_id), arg0_(arg0), arg1_(arg1) {} + // Convert a field to its raw representation, used as `arg0_` / `arg1_`. + static constexpr auto ToRaw(IndexBase base) -> int32_t { return base.index; } + static constexpr auto ToRaw(BuiltinKind kind) -> int32_t { + return kind.AsInt(); + } + + // Convert a field from its raw representation. + template + static constexpr auto FromRaw(int32_t raw) -> T { + return T(raw); + } + template <> + constexpr auto FromRaw(int32_t raw) -> BuiltinKind { + return BuiltinKind::FromInt(raw); + } + Parse::Node parse_node_; NodeKind kind_; TypeId type_id_; @@ -561,245 +186,13 @@ class Node : public Printable { // may be worth investigating further. static_assert(sizeof(Node) == 20, "Unexpected Node size"); -namespace NodeInternals { -template -struct TypedNodeImpl; +// Typed nodes can be printed by converting them to nodes. +template > +inline llvm::raw_ostream& operator<<(llvm::raw_ostream& out, TypedNode node) { + Node(node).Print(out); + return out; } -// 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_SEM_IR_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 -// HasTypeBase or HasNoTypeBase. -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 - } // namespace Carbon::SemIR -// Support use of Id types as DenseMap/DenseSet keys. -template <> -struct llvm::DenseMapInfo - : public Carbon::IndexMapInfo {}; -template <> -struct llvm::DenseMapInfo - : public Carbon::IndexMapInfo {}; - #endif // CARBON_TOOLCHAIN_SEM_IR_NODE_H_ diff --git a/toolchain/sem_ir/node_kind.cpp b/toolchain/sem_ir/node_kind.cpp index eda9dbf70250..1800fceee16e 100644 --- a/toolchain/sem_ir/node_kind.cpp +++ b/toolchain/sem_ir/node_kind.cpp @@ -4,6 +4,8 @@ #include "toolchain/sem_ir/node_kind.h" +#include "toolchain/sem_ir/typed_nodes.h" + namespace Carbon::SemIR { CARBON_DEFINE_ENUM_CLASS_NAMES(NodeKind) = { @@ -11,30 +13,29 @@ CARBON_DEFINE_ENUM_CLASS_NAMES(NodeKind) = { #include "toolchain/sem_ir/node_kind.def" }; -// Returns the name to use for this node kind in Semantics IR. -[[nodiscard]] auto NodeKind::ir_name() const -> llvm::StringRef { - static constexpr llvm::StringRef Table[] = { -#define CARBON_SEM_IR_NODE_KIND_WITH_IR_NAME(Name, IR_Name) IR_Name, -#include "toolchain/sem_ir/node_kind.def" - }; - return Table[AsInt()]; +auto NodeKind::ir_name() const -> llvm::StringLiteral { + return definition().ir_name(); } auto NodeKind::value_kind() const -> NodeValueKind { static constexpr NodeValueKind Table[] = { -#define CARBON_SEM_IR_NODE_KIND_WITH_VALUE_KIND(Name, Kind) NodeValueKind::Kind, +#define CARBON_SEM_IR_NODE_KIND(Name) \ + HasTypeId ? NodeValueKind::Typed : NodeValueKind::None, #include "toolchain/sem_ir/node_kind.def" }; return Table[AsInt()]; } auto NodeKind::terminator_kind() const -> TerminatorKind { - static constexpr TerminatorKind Table[] = { -#define CARBON_SEM_IR_NODE_KIND_WITH_TERMINATOR_KIND(Name, Kind) \ - TerminatorKind::Kind, + return definition().terminator_kind(); +} + +auto NodeKind::definition() const -> const Definition& { + static constexpr const Definition* Table[] = { +#define CARBON_SEM_IR_NODE_KIND(Name) &SemIR::Name::Kind, #include "toolchain/sem_ir/node_kind.def" }; - return Table[AsInt()]; + return *Table[AsInt()]; } } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/node_kind.def b/toolchain/sem_ir/node_kind.def index 4978fa0a5b43..fa7f8ce1d98d 100644 --- a/toolchain/sem_ir/node_kind.def +++ b/toolchain/sem_ir/node_kind.def @@ -7,101 +7,64 @@ // inclusion to expand to the desired output. Macro definitions are cleaned up // at the end of this file. // -// Exactly one of these macros should be defined before including this header: +// This macro should be defined before including this header: // - CARBON_SEM_IR_NODE_KIND(Name) // Invoked for each kind of semantic node. -// - CARBON_SEM_IR_NODE_KIND_WITH_VALUE_KIND(Name, TypeFieldKind) -// Invoked for each kind of semantic node, along with information about -// whether the node produces a value, and if so, what kind of value. -// - CARBON_SEM_IR_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind) -// Invoked for each kind of semantic node, along with information about -// whether the node is a terminator node. -// - CARBON_SEM_IR_NODE_KIND_WITH_IR_NAME(Name, IRName) -// Invoked for each kind of semantic node, along with the name that is used -// to denote this node in textual Semantics IR. -#if defined(CARBON_SEM_IR_NODE_KIND) -#define CARBON_SEM_IR_NODE_KIND_IMPL(Name, IRName, ValueKind, TerminatorKind) \ - CARBON_SEM_IR_NODE_KIND(Name) -#elif defined(CARBON_SEM_IR_NODE_KIND_WITH_VALUE_KIND) -#define CARBON_SEM_IR_NODE_KIND_IMPL(Name, IRName, ValueKind, TerminatorKind) \ - CARBON_SEM_IR_NODE_KIND_WITH_VALUE_KIND(Name, ValueKind) -#elif defined(CARBON_SEM_IR_NODE_KIND_WITH_TERMINATOR_KIND) -#define CARBON_SEM_IR_NODE_KIND_IMPL(Name, IRName, ValueKind, TerminatorKind) \ - CARBON_SEM_IR_NODE_KIND_WITH_TERMINATOR_KIND(Name, TerminatorKind) -#elif defined(CARBON_SEM_IR_NODE_KIND_WITH_IR_NAME) -#define CARBON_SEM_IR_NODE_KIND_IMPL(Name, IRName, ValueKind, TerminatorKind) \ - CARBON_SEM_IR_NODE_KIND_WITH_IR_NAME(Name, IRName) -#else +#ifndef CARBON_SEM_IR_NODE_KIND #error "Must define the x-macro to use this file." #endif -// A cross-reference between IRs. -CARBON_SEM_IR_NODE_KIND_IMPL(CrossReference, "xref", Typed, NotTerminator) - -CARBON_SEM_IR_NODE_KIND_IMPL(AddressOf, "address_of", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ArrayIndex, "array_index", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ArrayInit, "array_init", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ArrayType, "array_type", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Assign, "assign", None, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BinaryOperatorAdd, "add", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BindName, "bind_name", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BindValue, "bind_value", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BlockArg, "block_arg", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BoolLiteral, "bool_literal", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Branch, "br", None, Terminator) -CARBON_SEM_IR_NODE_KIND_IMPL(BranchIf, "br", None, TerminatorSequence) -CARBON_SEM_IR_NODE_KIND_IMPL(BranchWithArg, "br", None, Terminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Builtin, "builtin", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Call, "call", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ClassDeclaration, "class_declaration", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ConstType, "const_type", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Dereference, "dereference", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(FunctionDeclaration, "fn_decl", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(InitializeFrom, "initialize_from", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(IntegerLiteral, "int_literal", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(NameReference, "name_reference", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Namespace, "namespace", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(NoOp, "no_op", None, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Parameter, "parameter", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(PointerType, "ptr_type", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(RealLiteral, "real_literal", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ReturnExpression, "return", None, Terminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Return, "return", None, Terminator) -CARBON_SEM_IR_NODE_KIND_IMPL(SpliceBlock, "splice_block", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StringLiteral, "string_literal", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructAccess, "struct_access", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructInit, "struct_init", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructLiteral, "struct_literal", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructTypeField, "struct_type_field", None, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructType, "struct_type", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(StructValue, "struct_value", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TemporaryStorage, "temporary_storage", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(Temporary, "temporary", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleAccess, "tuple_access", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleIndex, "tuple_index", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleInit, "tuple_init", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleLiteral, "tuple_literal", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleType, "tuple_type", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(TupleValue, "tuple_value", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(UnaryOperatorNot, "not", Typed, NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(ValueAsReference, "value_as_reference", Typed, - NotTerminator) -CARBON_SEM_IR_NODE_KIND_IMPL(VarStorage, "var", Typed, NotTerminator) +// For each node kind declared here there is a matching definition in +// `typed.nodes.h`. +CARBON_SEM_IR_NODE_KIND(AddressOf) +CARBON_SEM_IR_NODE_KIND(ArrayIndex) +CARBON_SEM_IR_NODE_KIND(ArrayInit) +CARBON_SEM_IR_NODE_KIND(ArrayType) +CARBON_SEM_IR_NODE_KIND(Assign) +CARBON_SEM_IR_NODE_KIND(BinaryOperatorAdd) +CARBON_SEM_IR_NODE_KIND(BindName) +CARBON_SEM_IR_NODE_KIND(BindValue) +CARBON_SEM_IR_NODE_KIND(BlockArg) +CARBON_SEM_IR_NODE_KIND(BoolLiteral) +CARBON_SEM_IR_NODE_KIND(Branch) +CARBON_SEM_IR_NODE_KIND(BranchIf) +CARBON_SEM_IR_NODE_KIND(BranchWithArg) +CARBON_SEM_IR_NODE_KIND(Builtin) +CARBON_SEM_IR_NODE_KIND(Call) +CARBON_SEM_IR_NODE_KIND(ClassDeclaration) +CARBON_SEM_IR_NODE_KIND(ConstType) +CARBON_SEM_IR_NODE_KIND(CrossReference) +CARBON_SEM_IR_NODE_KIND(Dereference) +CARBON_SEM_IR_NODE_KIND(FunctionDeclaration) +CARBON_SEM_IR_NODE_KIND(InitializeFrom) +CARBON_SEM_IR_NODE_KIND(IntegerLiteral) +CARBON_SEM_IR_NODE_KIND(NameReference) +CARBON_SEM_IR_NODE_KIND(Namespace) +CARBON_SEM_IR_NODE_KIND(NoOp) +CARBON_SEM_IR_NODE_KIND(Parameter) +CARBON_SEM_IR_NODE_KIND(PointerType) +CARBON_SEM_IR_NODE_KIND(RealLiteral) +CARBON_SEM_IR_NODE_KIND(ReturnExpression) +CARBON_SEM_IR_NODE_KIND(Return) +CARBON_SEM_IR_NODE_KIND(SpliceBlock) +CARBON_SEM_IR_NODE_KIND(StringLiteral) +CARBON_SEM_IR_NODE_KIND(StructAccess) +CARBON_SEM_IR_NODE_KIND(StructInit) +CARBON_SEM_IR_NODE_KIND(StructLiteral) +CARBON_SEM_IR_NODE_KIND(StructTypeField) +CARBON_SEM_IR_NODE_KIND(StructType) +CARBON_SEM_IR_NODE_KIND(StructValue) +CARBON_SEM_IR_NODE_KIND(TemporaryStorage) +CARBON_SEM_IR_NODE_KIND(Temporary) +CARBON_SEM_IR_NODE_KIND(TupleAccess) +CARBON_SEM_IR_NODE_KIND(TupleIndex) +CARBON_SEM_IR_NODE_KIND(TupleInit) +CARBON_SEM_IR_NODE_KIND(TupleLiteral) +CARBON_SEM_IR_NODE_KIND(TupleType) +CARBON_SEM_IR_NODE_KIND(TupleValue) +CARBON_SEM_IR_NODE_KIND(UnaryOperatorNot) +CARBON_SEM_IR_NODE_KIND(ValueAsReference) +CARBON_SEM_IR_NODE_KIND(VarStorage) #undef CARBON_SEM_IR_NODE_KIND -#undef CARBON_SEM_IR_NODE_KIND_WITH_VALUE_KIND -#undef CARBON_SEM_IR_NODE_KIND_WITH_TERMINATOR_KIND -#undef CARBON_SEM_IR_NODE_KIND_WITH_IR_NAME -#undef CARBON_SEM_IR_NODE_KIND_IMPL diff --git a/toolchain/sem_ir/node_kind.h b/toolchain/sem_ir/node_kind.h index 53ddc51191fe..24db767269c9 100644 --- a/toolchain/sem_ir/node_kind.h +++ b/toolchain/sem_ir/node_kind.h @@ -48,7 +48,7 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) { using EnumBase::Create; // Returns the name to use for this node kind in Semantics IR. - [[nodiscard]] auto ir_name() const -> llvm::StringRef; + [[nodiscard]] auto ir_name() const -> llvm::StringLiteral; // Returns whether this kind of node is expected to produce a value. [[nodiscard]] auto value_kind() const -> NodeValueKind; @@ -63,6 +63,18 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) { // Compute a fingerprint for this node kind, allowing its use as part of the // key in a `FoldingSet`. void Profile(llvm::FoldingSetNodeID& id) { id.AddInteger(AsInt()); } + + class Definition; + + // Provides a definition for this node kind. Should only be called once, to + // construct the kind as part of defining it in `typed_nodes.h`. + constexpr auto Define(llvm::StringLiteral ir_name, + TerminatorKind terminator_kind = + TerminatorKind::NotTerminator) const -> Definition; + + private: + // Looks up the definition for this node kind. + [[nodiscard]] auto definition() const -> const Definition&; }; #define CARBON_SEM_IR_NODE_KIND(Name) \ @@ -72,6 +84,45 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) { // We expect the node kind to fit compactly into 8 bits. static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!"); +// A definition of a node kind. This is a NodeKind value, plus ancillary data +// such as the name to use for the node kind in LLVM IR. These are not +// copyable, and only one instance of this type is expected to exist per node +// kind, specifically `TypedNode::Kind`. Use `NodeKind` instead as a thin +// wrapper around a node kind index. +class NodeKind::Definition : public NodeKind { + public: + // Returns the name to use for this node kind in Semantics IR. + [[nodiscard]] constexpr auto ir_name() const -> llvm::StringLiteral { + return ir_name_; + } + + // Returns whether this node kind is a code block terminator. See + // NodeKind::terminator_kind(). + [[nodiscard]] constexpr auto terminator_kind() const -> TerminatorKind { + return terminator_kind_; + } + + private: + friend class NodeKind; + + constexpr Definition(NodeKind kind, llvm::StringLiteral ir_name, + TerminatorKind terminator_kind) + : NodeKind(kind), ir_name_(ir_name), terminator_kind_(terminator_kind) {} + + // Not copyable. + Definition(const Definition&) = delete; + Definition& operator=(const Definition&) = delete; + + llvm::StringLiteral ir_name_; + TerminatorKind terminator_kind_; +}; + +constexpr auto NodeKind::Define(llvm::StringLiteral ir_name, + TerminatorKind terminator_kind) const + -> Definition { + return Definition(*this, ir_name, terminator_kind); +} + } // namespace Carbon::SemIR #endif // CARBON_TOOLCHAIN_SEM_IR_NODE_KIND_H_ diff --git a/toolchain/sem_ir/typed_nodes.h b/toolchain/sem_ir/typed_nodes.h new file mode 100644 index 000000000000..83218f1e20b1 --- /dev/null +++ b/toolchain/sem_ir/typed_nodes.h @@ -0,0 +1,491 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_SEM_IR_TYPED_NODES_H_ +#define CARBON_TOOLCHAIN_SEM_IR_TYPED_NODES_H_ + +#include "toolchain/parse/tree.h" +#include "toolchain/sem_ir/builtin_kind.h" +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/node_kind.h" + +// Representations for specific kinds of nodes. +// +// Each type should be a struct with up to four members: +// +// - Optionally, a `Parse::Node parse_node;` member, for nodes with an +// associated location. Almost all nodes should have this, with exceptions +// being things that are generated internally, without any relation to source +// syntax, such as predeclared builtins. +// - Optionally, a `TypeId type_id;` member, for nodes that produce a value. +// This includes nodes that produce an abstract value, such as a `Namespace`, +// for which a placeholder type should be used. +// - Up to two `[...]Id` members describing the contents of the struct. +// +// The field names here matter -- the first two fields must have the names +// specified above, when present. When converting to a `SemIR::Node`, they will +// become the parse node and type associated with the type-erased node. +// +// In addition, each type provides a constant `Kind` that associates the type +// with a particular member of the `NodeKind` enumeration. This `Kind` +// declaration also defines the node kind by calling `NodeKind::Define` and +// specifying additional information about the node kind. This information is +// available through the member functions of the `NodeKind` value declared in +// `node_kind.h`, and includes the name used in textual IR and whether the node +// is a terminator instruction. +namespace Carbon::SemIR { + +struct AddressOf { + static constexpr auto Kind = NodeKind::AddressOf.Define("address_of"); + + Parse::Node parse_node; + TypeId type_id; + NodeId lvalue_id; +}; + +struct ArrayIndex { + static constexpr auto Kind = NodeKind::ArrayIndex.Define("array_index"); + + Parse::Node parse_node; + TypeId type_id; + 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 { + static constexpr auto Kind = NodeKind::ArrayInit.Define("array_init"); + + Parse::Node parse_node; + TypeId type_id; + NodeId tuple_id; + NodeBlockId inits_and_return_slot_id; +}; + +struct ArrayType { + static constexpr auto Kind = NodeKind::ArrayType.Define("array_type"); + + Parse::Node parse_node; + TypeId type_id; + 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 { + static constexpr auto Kind = NodeKind::Assign.Define("assign"); + + Parse::Node parse_node; + // Assignments are statements, and so have no type. + NodeId lhs_id; + NodeId rhs_id; +}; + +struct BinaryOperatorAdd { + static constexpr auto Kind = NodeKind::BinaryOperatorAdd.Define("add"); + + Parse::Node parse_node; + TypeId type_id; + NodeId lhs_id; + NodeId rhs_id; +}; + +struct BindName { + static constexpr auto Kind = NodeKind::BindName.Define("bind_name"); + + Parse::Node parse_node; + TypeId type_id; + StringId name_id; + NodeId value_id; +}; + +struct BindValue { + static constexpr auto Kind = NodeKind::BindValue.Define("bind_value"); + + Parse::Node parse_node; + TypeId type_id; + NodeId value_id; +}; + +struct BlockArg { + static constexpr auto Kind = NodeKind::BlockArg.Define("block_arg"); + + Parse::Node parse_node; + TypeId type_id; + NodeBlockId block_id; +}; + +struct BoolLiteral { + static constexpr auto Kind = NodeKind::BoolLiteral.Define("bool_literal"); + + Parse::Node parse_node; + TypeId type_id; + BoolValue value; +}; + +struct Branch { + static constexpr auto Kind = + NodeKind::Branch.Define("br", TerminatorKind::Terminator); + + Parse::Node parse_node; + // Branches don't produce a value, so have no type. + NodeBlockId target_id; +}; + +struct BranchIf { + static constexpr auto Kind = + NodeKind::BranchIf.Define("br", TerminatorKind::TerminatorSequence); + + Parse::Node parse_node; + // Branches don't produce a value, so have no type. + NodeBlockId target_id; + NodeId cond_id; +}; + +struct BranchWithArg { + static constexpr auto Kind = + NodeKind::BranchWithArg.Define("br", TerminatorKind::Terminator); + + Parse::Node parse_node; + // Branches don't produce a value, so have no type. + NodeBlockId target_id; + NodeId arg_id; +}; + +struct Builtin { + static constexpr auto Kind = NodeKind::Builtin.Define("builtin"); + + // Builtins don't have a parse node associated with them. + TypeId type_id; + BuiltinKind builtin_kind; +}; + +struct Call { + static constexpr auto Kind = NodeKind::Call.Define("call"); + + Parse::Node parse_node; + TypeId type_id; + NodeId callee_id; + NodeBlockId args_id; +}; + +struct ClassDeclaration { + static constexpr auto Kind = + NodeKind::ClassDeclaration.Define("class_declaration"); + + Parse::Node parse_node; + TypeId type_id; + ClassId class_id; + // The declaration block, containing the class name's qualifiers and the + // class's generic parameters. + NodeBlockId decl_block_id; +}; + +struct ConstType { + static constexpr auto Kind = NodeKind::ConstType.Define("const_type"); + + Parse::Node parse_node; + TypeId type_id; + TypeId inner_id; +}; + +// A cross-reference between IRs. +struct CrossReference { + static constexpr auto Kind = NodeKind::CrossReference.Define("xref"); + + // No parse 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. + TypeId type_id; + CrossReferenceIRId ir_id; + NodeId node_id; +}; + +struct Dereference { + static constexpr auto Kind = NodeKind::Dereference.Define("dereference"); + + Parse::Node parse_node; + TypeId type_id; + NodeId pointer_id; +}; + +struct FunctionDeclaration { + static constexpr auto Kind = NodeKind::FunctionDeclaration.Define("fn_decl"); + + Parse::Node parse_node; + TypeId type_id; + 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 { + static constexpr auto Kind = + NodeKind::InitializeFrom.Define("initialize_from"); + + Parse::Node parse_node; + TypeId type_id; + NodeId src_id; + NodeId dest_id; +}; + +struct IntegerLiteral { + static constexpr auto Kind = NodeKind::IntegerLiteral.Define("int_literal"); + + Parse::Node parse_node; + TypeId type_id; + IntegerId integer_id; +}; + +struct NameReference { + static constexpr auto Kind = NodeKind::NameReference.Define("name_reference"); + + Parse::Node parse_node; + TypeId type_id; + StringId name_id; + NodeId value_id; +}; + +struct Namespace { + static constexpr auto Kind = NodeKind::Namespace.Define("namespace"); + + Parse::Node parse_node; + TypeId type_id; + NameScopeId name_scope_id; +}; + +struct NoOp { + static constexpr auto Kind = NodeKind::NoOp.Define("no_op"); + + Parse::Node parse_node; + // This node doesn't produce a value, so has no type. +}; + +struct Parameter { + static constexpr auto Kind = NodeKind::Parameter.Define("parameter"); + + Parse::Node parse_node; + TypeId type_id; + StringId name_id; +}; + +struct PointerType { + static constexpr auto Kind = NodeKind::PointerType.Define("ptr_type"); + + Parse::Node parse_node; + TypeId type_id; + TypeId pointee_id; +}; + +struct RealLiteral { + static constexpr auto Kind = NodeKind::RealLiteral.Define("real_literal"); + + Parse::Node parse_node; + TypeId type_id; + RealId real_id; +}; + +struct Return { + static constexpr auto Kind = + NodeKind::Return.Define("return", TerminatorKind::Terminator); + + Parse::Node parse_node; + // This is a statement, so has no type. +}; + +struct ReturnExpression { + static constexpr auto Kind = + NodeKind::ReturnExpression.Define("return", TerminatorKind::Terminator); + + Parse::Node parse_node; + // This is a statement, so has no type. + NodeId expr_id; +}; + +struct SpliceBlock { + static constexpr auto Kind = NodeKind::SpliceBlock.Define("splice_block"); + + Parse::Node parse_node; + TypeId type_id; + NodeBlockId block_id; + NodeId result_id; +}; + +struct StringLiteral { + static constexpr auto Kind = NodeKind::StringLiteral.Define("string_literal"); + + Parse::Node parse_node; + TypeId type_id; + StringId string_id; +}; + +struct StructAccess { + static constexpr auto Kind = NodeKind::StructAccess.Define("struct_access"); + + Parse::Node parse_node; + TypeId type_id; + NodeId struct_id; + MemberIndex index; +}; + +struct StructInit { + static constexpr auto Kind = NodeKind::StructInit.Define("struct_init"); + + Parse::Node parse_node; + TypeId type_id; + NodeId src_id; + NodeBlockId elements_id; +}; + +struct StructLiteral { + static constexpr auto Kind = NodeKind::StructLiteral.Define("struct_literal"); + + Parse::Node parse_node; + TypeId type_id; + NodeBlockId elements_id; +}; + +struct StructType { + static constexpr auto Kind = NodeKind::StructType.Define("struct_type"); + + Parse::Node parse_node; + TypeId type_id; + NodeBlockId fields_id; +}; + +struct StructTypeField { + static constexpr auto Kind = + NodeKind::StructTypeField.Define("struct_type_field"); + + Parse::Node parse_node; + // This node is an implementation detail of `StructType`, and doesn't produce + // a value, so has no type, even though it declares a field with a type. + StringId name_id; + TypeId field_type_id; +}; + +struct StructValue { + static constexpr auto Kind = NodeKind::StructValue.Define("struct_value"); + + Parse::Node parse_node; + TypeId type_id; + NodeId src_id; + NodeBlockId elements_id; +}; + +struct Temporary { + static constexpr auto Kind = NodeKind::Temporary.Define("temporary"); + + Parse::Node parse_node; + TypeId type_id; + NodeId storage_id; + NodeId init_id; +}; + +struct TemporaryStorage { + static constexpr auto Kind = + NodeKind::TemporaryStorage.Define("temporary_storage"); + + Parse::Node parse_node; + TypeId type_id; +}; + +struct TupleAccess { + static constexpr auto Kind = NodeKind::TupleAccess.Define("tuple_access"); + + Parse::Node parse_node; + TypeId type_id; + NodeId tuple_id; + MemberIndex index; +}; + +struct TupleIndex { + static constexpr auto Kind = NodeKind::TupleIndex.Define("tuple_index"); + + Parse::Node parse_node; + TypeId type_id; + NodeId tuple_id; + NodeId index_id; +}; + +struct TupleInit { + static constexpr auto Kind = NodeKind::TupleInit.Define("tuple_init"); + + Parse::Node parse_node; + TypeId type_id; + NodeId src_id; + NodeBlockId elements_id; +}; + +struct TupleLiteral { + static constexpr auto Kind = NodeKind::TupleLiteral.Define("tuple_literal"); + + Parse::Node parse_node; + TypeId type_id; + NodeBlockId elements_id; +}; + +struct TupleType { + static constexpr auto Kind = NodeKind::TupleType.Define("tuple_type"); + + Parse::Node parse_node; + TypeId type_id; + TypeBlockId elements_id; +}; + +struct TupleValue { + static constexpr auto Kind = NodeKind::TupleValue.Define("tuple_value"); + + Parse::Node parse_node; + TypeId type_id; + NodeId src_id; + NodeBlockId elements_id; +}; + +struct UnaryOperatorNot { + static constexpr auto Kind = NodeKind::UnaryOperatorNot.Define("not"); + + Parse::Node parse_node; + TypeId type_id; + NodeId operand_id; +}; + +struct ValueAsReference { + static constexpr auto Kind = + NodeKind::ValueAsReference.Define("value_as_reference"); + + Parse::Node parse_node; + TypeId type_id; + NodeId value_id; +}; + +struct VarStorage { + static constexpr auto Kind = NodeKind::VarStorage.Define("var"); + + Parse::Node parse_node; + TypeId type_id; + StringId name_id; +}; + +// HasParseNode is true if T has a `Parse::Node parse_node` field. +template +constexpr bool HasParseNode = false; +template +constexpr bool HasParseNode = true; + +// HasTypeId is true if T has a `TypeId type_id` field. +template +constexpr bool HasTypeId = false; +template +constexpr bool HasTypeId = true; + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_TYPED_NODES_H_ diff --git a/toolchain/sem_ir/typed_nodes_test.cpp b/toolchain/sem_ir/typed_nodes_test.cpp new file mode 100644 index 000000000000..bdabe99cd171 --- /dev/null +++ b/toolchain/sem_ir/typed_nodes_test.cpp @@ -0,0 +1,133 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/sem_ir/typed_nodes.h" + +#include +#include + +#include "toolchain/sem_ir/node.h" + +namespace Carbon::SemIR { + +// A friend of `SemIR::Node` that is used to pierce the abstraction. +class NodeTestHelper { + public: + static auto MakeNode(NodeKind node_kind, Parse::Node parse_node, + TypeId type_id, int32_t arg0, int32_t arg1) -> Node { + return Node(node_kind, parse_node, type_id, arg0, arg1); + } +}; + +} // namespace Carbon::SemIR + +namespace Carbon::SemIR { +namespace { + +// Check that each node kind defines a Kind member using the correct NodeKind +// enumerator. +#define CARBON_SEM_IR_NODE_KIND(Name) \ + static_assert(Name::Kind == NodeKind::Name); +#include "toolchain/sem_ir/node_kind.def" + +template +using TypesExceptFirst = ::testing::Types; + +// Form a list of all typed node types. Use `TypesExceptFirst` and a leading +// `void` to handle the problem that we only want N-1 commas in this list. +using TypedNodeTypes = TypesExceptFirst; + +// Set up the test fixture. +template +class TypedNodeTest : public testing::Test {}; + +TYPED_TEST_SUITE(TypedNodeTest, TypedNodeTypes); + +TYPED_TEST(TypedNodeTest, CommonFieldOrder) { + using TypedNode = TypeParam; + + Node node = NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1), + TypeId(2), 3, 4); + EXPECT_EQ(node.kind(), TypeParam::Kind); + EXPECT_EQ(node.parse_node(), Parse::Node(1)); + EXPECT_EQ(node.type_id(), TypeId(2)); + + TypedNode typed = node.As(); + if constexpr (HasParseNode) { + EXPECT_EQ(typed.parse_node, Parse::Node(1)); + } + if constexpr (HasTypeId) { + EXPECT_EQ(typed.type_id, TypeId(2)); + } +} + +TYPED_TEST(TypedNodeTest, RoundTrip) { + using TypedNode = TypeParam; + + Node node1 = NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1), + TypeId(2), 3, 4); + EXPECT_EQ(node1.kind(), TypeParam::Kind); + EXPECT_EQ(node1.parse_node(), Parse::Node(1)); + EXPECT_EQ(node1.type_id(), TypeId(2)); + + TypedNode typed1 = node1.As(); + Node node2 = typed1; + + EXPECT_EQ(node1.kind(), node2.kind()); + if constexpr (HasParseNode) { + EXPECT_EQ(node1.parse_node(), node2.parse_node()); + } + if constexpr (HasTypeId) { + EXPECT_EQ(node1.type_id(), node2.type_id()); + } + + // If the typed node has no padding, we should get exactly the same thing + // if we convert back from a node. + TypedNode typed2 = node2.As(); + if constexpr (std::has_unique_object_representations_v) { + EXPECT_EQ(std::memcmp(&typed1, &typed2, sizeof(TypedNode)), 0); + } + + // The original node might not be identical after one round trip, because the + // fields not carried by the typed node are lost. But they should be stable + // if we round-trip again. + Node node3 = typed2; + if constexpr (std::has_unique_object_representations_v) { + EXPECT_EQ(std::memcmp(&node2, &node3, sizeof(Node)), 0); + } +} + +TYPED_TEST(TypedNodeTest, StructLayout) { + using TypedNode = TypeParam; + + TypedNode typed = + NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1), TypeId(2), 3, 4) + .template As(); + + // Check that the memory representation of the typed node is what we expect. + // TODO: Struct layout is not guaranteed, and this test could fail in some + // build environment. If so, we should disable it. + int32_t fields[4] = {}; + int field = 0; + if constexpr (HasParseNode) { + fields[field++] = 1; + } + if constexpr (HasTypeId) { + fields[field++] = 2; + } + fields[field++] = 3; + fields[field++] = 4; + + ASSERT_LE(sizeof(TypedNode), sizeof(fields)); + // We can only do this check if the typed node has no padding. + if constexpr (std::has_unique_object_representations_v) { + EXPECT_EQ(std::memcmp(&fields, &typed, sizeof(TypedNode)), 0); + } +} + +} // namespace +} // namespace Carbon::SemIR