mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
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:
+13
-13
@@ -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
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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_ << "}";
|
||||
}
|
||||
|
||||
@@ -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_
|
||||
@@ -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
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user