Remove most of the metaprogramming in node.h in favor of listing all the members in the typed node structs. (#3310)

Split `node.h` into separate files for ID types (`id.h`) and for typed
nodes (`typed_nodes.h`). The per-node-kind data is now specified as part
of declaring the typed nodes, and is removed from the node kinds
x-macros, which now simply enumerate the node kinds.
This commit is contained in:
Richard Smith
2023-10-20 20:26:10 +00:00
committed by GitHub
parent e54deee525
commit a46e7dd967
31 changed files with 1240 additions and 957 deletions
+13 -13
View File
@@ -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<SemIR::StructType>().fields_id)) {
Push(context_.semantics_ir()
.GetNodeAs<SemIR::StructTypeField>(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<SemIR::StructTypeField>(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<SemIR::StructTypeField>(
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 {
+32 -35
View File
@@ -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<SemIR::StructAccess, SemIR::StructAccess>(
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});
}
}
+2 -2
View File
@@ -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;
}
}
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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.
+5 -5
View File
@@ -58,9 +58,9 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition)
.PopForSoloParseNode<Parse::NodeKind::FunctionIntroducer>();
// 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;
}
+1 -1
View File
@@ -53,7 +53,7 @@ auto HandleIfStatement(Context& context, Parse::Node parse_node) -> bool {
// block.
auto else_block_id =
context.node_stack().Pop<Parse::NodeKind::IfCondition>();
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;
+6 -6
View File
@@ -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: {
+8 -8
View File
@@ -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: {
+3 -3
View File
@@ -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.
+4 -4
View File
@@ -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<SemIR::StructTypeField>(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;
}
+2 -2
View File
@@ -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;
}
+18 -18
View File
@@ -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:
+1 -1
View File
@@ -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;
}
+4 -4
View File
@@ -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:
+2 -2
View File
@@ -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
+5 -5
View File
@@ -29,7 +29,7 @@ auto HandleStructFieldType(Context& context, Parse::Node parse_node) -> bool {
context.node_stack().PopWithParseNode<Parse::NodeKind::Name>();
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<Parse::NodeKind::Name>();
// 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;
}
+1 -1
View File
@@ -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()
+4 -4
View File
@@ -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.
+3 -3
View File
@@ -238,9 +238,9 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
auto field = semantics_ir_->GetNodeAs<SemIR::StructTypeField>(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);
}
+29 -1
View File
@@ -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",
+5 -4
View File
@@ -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<StructTypeField>();
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: {
+9 -2
View File
@@ -667,7 +667,14 @@ class Formatter {
template <typename NodeT>
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<NodeT>;
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<StructTypeField>(field_id);
FormatString(field.name_id);
out_ << ": ";
FormatType(field.type_id);
FormatType(field.field_type_id);
}
out_ << "}";
}
+213
View File
@@ -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 <cstdint>
#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<NodeId> {
// 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<FunctionId> {
// 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<ClassId> {
// 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<CrossReferenceIRId> {
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<BoolValue> {
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<NameScopeId> {
// 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<NodeBlockId> {
// 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<TypeId> {
// 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<TypeBlockId> {
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<MemberIndex> {
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<Carbon::SemIR::NodeBlockId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeBlockId> {};
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NodeId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeId> {};
#endif // CARBON_TOOLCHAIN_SEM_IR_IDS_H_
+11 -6
View File
@@ -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<typename Info::template ArgType<0>>(arg0_);
}
if constexpr (Info::NumArgs > 1) {
out << ", arg1: " << FromRaw<typename Info::template ArgType<1>>(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<SemIR::Name>().args_tuple()); \
#define CARBON_SEM_IR_NODE_KIND(Name) \
case Name::Kind: \
print_args(TypedNodeArgsInfo<Name>()); \
break;
#include "toolchain/sem_ir/node_kind.def"
}
+113 -720
View File
@@ -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<NodeId> {
// 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 <typename TypedNode,
const NodeKind::Definition& KindT = TypedNode::Kind>
struct TypedNodeArgsInfo {
// A corresponding std::tuple<...> type.
using Tuple = decltype(StructReflection::AsTuple(std::declval<TypedNode>()));
// 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<TypedNode> + HasTypeId<TypedNode>;
// 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<Tuple> - 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 <int N>
using ArgType = std::tuple_element_t<FirstArgField + N, Tuple>;
template <int N>
static auto Get(TypedNode node) -> ArgType<N> {
return std::get<FirstArgField + N>(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<FunctionId> {
// 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<ClassId> {
// 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<CrossReferenceIRId> {
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<BoolValue> {
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<NameScopeId> {
// 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<NodeBlockId> {
// 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<TypeId> {
// 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<TypeBlockId> {
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<MemberIndex> {
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 <NodeKind::RawEnumType KindT, typename DataT>
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<Kind>` to determine what kind of node it is.
// - Access Kind-specific members using `node.As<Kind>()`, 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<Kind>()` 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<TypedNode>` to determine what kind of node it is.
// - Cast to a specific type using `node.As<TypedNode>()`
// - Using the wrong kind in `node.As<TypedNode>()` is a programming error,
// and will CHECK-fail in debug modes (opt may too, but it's not an API
// guarantee).
// - Use `node.TryAs<Kind>()` to safely access type-specific node data where
// the node's kind is not known.
// - Use `node.TryAs<TypedNode>()` to safely access type-specific node data
// where the node's kind is not known.
class Node : public Printable<Node> {
public:
template <NodeKind::RawEnumType Kind, typename Data>
/*implicit*/
Node(TypedNode<Kind, Data> 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 <typename TypedNode, typename Info = TypedNodeArgsInfo<TypedNode>>
// 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<TypedNode>) {
parse_node_ = typed_node.parse_node;
}
if constexpr (HasTypeId<TypedNode>) {
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 <typename Typed>
template <typename TypedNode>
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 <typename Typed>
auto As() const -> Typed {
CARBON_CHECK(Is<Typed>()) << "Casting node of kind " << kind()
<< " to wrong kind " << Typed::Kind;
return Typed::FromRawData(parse_node_, type_id_, arg0_, arg1_);
template <typename TypedNode, typename Info = TypedNodeArgsInfo<TypedNode>>
auto As() const -> TypedNode {
CARBON_CHECK(Is<TypedNode>()) << "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<TypedNode>) {
return TypedNode{parse_node(), type_id_and_args...};
} else {
return TypedNode{type_id_and_args...};
}
};
auto build_with_args = [&](auto... args) {
if constexpr (HasTypeId<TypedNode>) {
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<typename Info::template ArgType<0>>(arg0_));
} else if constexpr (Info::NumArgs == 2) {
return build_with_args(
FromRaw<typename Info::template ArgType<0>>(arg0_),
FromRaw<typename Info::template ArgType<1>>(arg1_));
}
}
// If this node is the given kind, returns a typed node, otherwise returns
// nullopt.
template <typename Typed>
auto TryAs() const -> std::optional<Typed> {
if (Is<Typed>()) {
return As<Typed>();
template <typename TypedNode>
auto TryAs() const -> std::optional<TypedNode> {
if (Is<TypedNode>()) {
return As<TypedNode>();
} else {
return std::nullopt;
}
@@ -537,15 +144,33 @@ class Node : public Printable<Node> {
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 <typename T>
static constexpr auto FromRaw(int32_t raw) -> T {
return T(raw);
}
template <>
constexpr auto FromRaw<BuiltinKind>(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<Node> {
// may be worth investigating further.
static_assert(sizeof(Node) == 20, "Unexpected Node size");
namespace NodeInternals {
template <typename DataT>
struct TypedNodeImpl;
// Typed nodes can be printed by converting them to nodes.
template <typename TypedNode, typename = TypedNodeArgsInfo<TypedNode>>
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 <NodeKind::RawEnumType KindT, typename DataT>
struct TypedNode : NodeInternals::TypedNodeImpl<DataT>,
Printable<TypedNode<KindT, DataT>> {
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<IdType1, IdType2>;
#endif
using NodeInternals::TypedNodeImpl<DataT>::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<NodeKind::Name, NodeData::Name>;
#include "toolchain/sem_ir/node_kind.def"
// Implementation details for typed nodes.
namespace NodeInternals {
template <typename T>
using GetHasParseNode = typename T::HasParseNode;
template <typename T>
using GetHasType = typename T::HasType;
// Apply Getter<T>, or provide Default if it doesn't exist.
template <typename T, template <typename> typename Getter, typename Default,
typename Void = void>
struct GetWithDefaultImpl {
using Result = Default;
};
template <typename T, template <typename> typename Getter, typename Default>
struct GetWithDefaultImpl<T, Getter, Default, std::void_t<Getter<T>>> {
using Result = Getter<T>;
};
template <typename T, template <typename> typename Getter, typename Default>
using GetWithDefault = typename GetWithDefaultImpl<T, Getter, Default>::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<T> holds the `parse_node` field if the node has a parse tree
// node, and is either HasParseNodeBase or HasNoParseNodeBase.
template <typename T>
using ParseNodeBase =
std::conditional_t<GetWithDefault<T, GetHasParseNode, std::true_type>{},
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<T> holds the `type_id` field if the node has a type, and is either
// HasTypeBase or HasNoTypeBase.
template <typename T>
using TypeBase =
std::conditional_t<GetWithDefault<T, GetHasType, std::true_type>{},
HasTypeBase, HasNoTypeBase>;
// Convert a field from its raw representation.
template <typename T>
constexpr auto FromRaw(int32_t raw) -> T {
return T(raw);
}
template <>
constexpr auto FromRaw<BuiltinKind>(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 <typename T>
using FieldTypes = decltype(StructReflection::AsTuple(std::declval<T>()));
// Base class for nodes that contains the node data.
template <typename T, typename = FieldTypes<T>>
struct DataBase;
template <typename T, typename... Fields>
struct DataBase<T, std::tuple<Fields...>> : T {
static_assert(sizeof...(Fields) <= 2, "Too many fields in node data");
static auto FromRawArgs(decltype(ToRaw(std::declval<Fields>()))... args, ...)
-> DataBase {
return {FromRaw<Fields>(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<const T&>(*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 <typename, typename, typename, typename>
struct TypedNodeBase;
// A helper base class that produces a constructor with one correctly-typed
// parameter for each struct field.
template <typename DataT, typename... ParseNodeFields, typename... TypeFields,
typename... DataFields>
struct TypedNodeBase<DataT, std::tuple<ParseNodeFields...>,
std::tuple<TypeFields...>, std::tuple<DataFields...>>
: ParseNodeBase<DataT>, TypeBase<DataT>, DataBase<DataT> {
// Braced initialization of base classes confuses clang-format.
// clang-format off
constexpr TypedNodeBase(ParseNodeFields... parse_node_fields,
TypeFields... type_fields, DataFields... data_fields)
: ParseNodeBase<DataT>{parse_node_fields...},
TypeBase<DataT>{type_fields...},
DataBase<DataT>{data_fields...} {
}
// clang-format on
constexpr TypedNodeBase(ParseNodeBase<DataT> parse_node_base,
TypeBase<DataT> type_base, DataBase<DataT> data_base)
: ParseNodeBase<DataT>(parse_node_base),
TypeBase<DataT>(type_base),
DataBase<DataT>(data_base) {}
};
template <typename DataT>
using MakeTypedNodeBase =
TypedNodeBase<DataT, FieldTypes<ParseNodeBase<DataT>>,
FieldTypes<TypeBase<DataT>>, FieldTypes<DataT>>;
template <typename DataT>
struct TypedNodeImpl : MakeTypedNodeBase<DataT> {
using MakeTypedNodeBase<DataT>::MakeTypedNodeBase;
};
} // namespace NodeInternals
} // namespace Carbon::SemIR
// Support use of Id types as DenseMap/DenseSet keys.
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NodeBlockId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeBlockId> {};
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NodeId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeId> {};
#endif // CARBON_TOOLCHAIN_SEM_IR_NODE_H_
+13 -12
View File
@@ -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<SemIR::Name> ? 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
+53 -90
View File
@@ -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
+52 -1
View File
@@ -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_
+491
View File
@@ -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<T> is true if T has a `Parse::Node parse_node` field.
template <typename T, typename ParseNodeType = Parse::Node T::*>
constexpr bool HasParseNode = false;
template <typename T>
constexpr bool HasParseNode<T, decltype(&T::parse_node)> = true;
// HasTypeId<T> is true if T has a `TypeId type_id` field.
template <typename T, typename TypeIdType = TypeId T::*>
constexpr bool HasTypeId = false;
template <typename T>
constexpr bool HasTypeId<T, decltype(&T::type_id)> = true;
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_TYPED_NODES_H_
+133
View File
@@ -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 <gmock/gmock.h>
#include <gtest/gtest.h>
#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 <typename Ignored, typename... Types>
using TypesExceptFirst = ::testing::Types<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<void
#define CARBON_SEM_IR_NODE_KIND(Name) , Name
#include "toolchain/sem_ir/node_kind.def"
>;
// Set up the test fixture.
template <typename TypedNode>
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<TypedNode>();
if constexpr (HasParseNode<TypedNode>) {
EXPECT_EQ(typed.parse_node, Parse::Node(1));
}
if constexpr (HasTypeId<TypedNode>) {
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<TypedNode>();
Node node2 = typed1;
EXPECT_EQ(node1.kind(), node2.kind());
if constexpr (HasParseNode<TypedNode>) {
EXPECT_EQ(node1.parse_node(), node2.parse_node());
}
if constexpr (HasTypeId<TypedNode>) {
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<TypedNode>();
if constexpr (std::has_unique_object_representations_v<TypedNode>) {
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<Node>) {
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<TypedNode>();
// 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<TypedNode>) {
fields[field++] = 1;
}
if constexpr (HasTypeId<TypedNode>) {
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<TypedNode>) {
EXPECT_EQ(std::memcmp(&fields, &typed, sizeof(TypedNode)), 0);
}
}
} // namespace
} // namespace Carbon::SemIR