Rename SemIR::Node to SemIR::Inst (#3355)

And generally replace "node" by "inst" in the code and "instruction" in
comments.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
josh11b
2023-11-02 17:58:30 +00:00
committed by GitHub
co-authored by Chandler Carruth
parent 35c2142392
commit 7edfd8e02a
63 changed files with 2136 additions and 2115 deletions
+4 -4
View File
@@ -23,7 +23,7 @@ cc_library(
"//common:vlog",
"//toolchain/parse:node_kind",
"//toolchain/parse:tree",
"//toolchain/sem_ir:node",
"//toolchain/sem_ir:inst",
"@llvm-project//llvm:Support",
],
)
@@ -35,7 +35,7 @@ cc_library(
"context.cpp",
"convert.cpp",
"declaration_name_stack.cpp",
"node_block_stack.cpp",
"inst_block_stack.cpp",
] +
# Glob handler files to avoid missing any.
glob([
@@ -66,8 +66,8 @@ cc_library(
"//toolchain/sem_ir:builtin_kind",
"//toolchain/sem_ir:entry_point",
"//toolchain/sem_ir:file",
"//toolchain/sem_ir:node",
"//toolchain/sem_ir:node_kind",
"//toolchain/sem_ir:inst",
"//toolchain/sem_ir:inst_kind",
"@llvm-project//llvm:Support",
],
)
+2 -2
View File
@@ -27,7 +27,7 @@ auto CheckParseTree(SharedValueStores& value_stores,
[&](llvm::raw_ostream& output) { context.PrintForStackDump(output); });
// Add a block for the Parse::Tree.
context.node_block_stack().Push();
context.inst_block_stack().Push();
context.PushScope();
// Loops over all nodes in the tree. On some errors, this may return early,
@@ -51,7 +51,7 @@ auto CheckParseTree(SharedValueStores& value_stores,
}
// Pop information for the file-level scope.
sem_ir.set_top_node_block_id(context.node_block_stack().Pop());
sem_ir.set_top_inst_block_id(context.inst_block_stack().Pop());
context.PopScope();
context.VerifyOnFinish();
+155 -155
View File
@@ -15,7 +15,7 @@
#include "toolchain/lex/tokenized_buffer.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Check {
@@ -29,15 +29,15 @@ Context::Context(const Lex::TokenizedBuffer& tokens, DiagnosticEmitter& emitter,
sem_ir_(&sem_ir),
vlog_stream_(vlog_stream),
node_stack_(parse_tree, vlog_stream),
node_block_stack_("node_block_stack_", sem_ir, vlog_stream),
inst_block_stack_("inst_block_stack_", sem_ir, vlog_stream),
params_or_args_stack_("params_or_args_stack_", sem_ir, vlog_stream),
args_type_info_stack_("args_type_info_stack_", sem_ir, vlog_stream),
declaration_name_stack_(this) {
// Inserts the "Error" and "Type" types as "used types" so that
// canonicalization can skip them. We don't emit either for lowering.
canonical_types_.insert({SemIR::NodeId::BuiltinError, SemIR::TypeId::Error});
canonical_types_.insert({SemIR::InstId::BuiltinError, SemIR::TypeId::Error});
canonical_types_.insert(
{SemIR::NodeId::BuiltinTypeType, SemIR::TypeId::TypeType});
{SemIR::InstId::BuiltinTypeType, SemIR::TypeId::TypeType});
}
auto Context::TODO(Parse::Node parse_node, std::string label) -> bool {
@@ -54,28 +54,28 @@ auto Context::VerifyOnFinish() -> void {
// node_stack_ will still contain top-level entities.
CARBON_CHECK(name_lookup_.empty()) << name_lookup_.size();
CARBON_CHECK(scope_stack_.empty()) << scope_stack_.size();
CARBON_CHECK(node_block_stack_.empty()) << node_block_stack_.size();
CARBON_CHECK(inst_block_stack_.empty()) << inst_block_stack_.size();
CARBON_CHECK(params_or_args_stack_.empty()) << params_or_args_stack_.size();
}
auto Context::AddNode(SemIR::Node node) -> SemIR::NodeId {
auto node_id = node_block_stack_.AddNode(node);
CARBON_VLOG() << "AddNode: " << node << "\n";
return node_id;
auto Context::AddInst(SemIR::Inst inst) -> SemIR::InstId {
auto inst_id = inst_block_stack_.AddInst(inst);
CARBON_VLOG() << "AddInst: " << inst << "\n";
return inst_id;
}
auto Context::AddNodeAndPush(Parse::Node parse_node, SemIR::Node node) -> void {
auto node_id = AddNode(node);
node_stack_.Push(parse_node, node_id);
auto Context::AddInstAndPush(Parse::Node parse_node, SemIR::Inst inst) -> void {
auto inst_id = AddInst(inst);
node_stack_.Push(parse_node, inst_id);
}
auto Context::DiagnoseDuplicateName(Parse::Node parse_node,
SemIR::NodeId prev_def_id) -> void {
SemIR::InstId prev_def_id) -> void {
CARBON_DIAGNOSTIC(NameDeclarationDuplicate, Error,
"Duplicate name being declared in the same scope.");
CARBON_DIAGNOSTIC(NameDeclarationPrevious, Note,
"Name is previously declared here.");
auto prev_def = nodes().Get(prev_def_id);
auto prev_def = insts().Get(prev_def_id);
emitter_->Build(parse_node, NameDeclarationDuplicate)
.Note(prev_def.parse_node(), NameDeclarationPrevious)
.Emit();
@@ -97,16 +97,16 @@ auto Context::NoteIncompleteClass(SemIR::ClassId class_id,
const auto& class_info = classes().Get(class_id);
CARBON_CHECK(!class_info.is_defined()) << "Class is not incomplete";
if (class_info.definition_id.is_valid()) {
builder.Note(nodes().Get(class_info.definition_id).parse_node(),
builder.Note(insts().Get(class_info.definition_id).parse_node(),
ClassIncompleteWithinDefinition);
} else {
builder.Note(nodes().Get(class_info.declaration_id).parse_node(),
builder.Note(insts().Get(class_info.declaration_id).parse_node(),
ClassForwardDeclaredHere);
}
}
auto Context::AddNameToLookup(Parse::Node name_node, StringId name_id,
SemIR::NodeId target_id) -> void {
SemIR::InstId target_id) -> void {
if (current_scope().names.insert(name_id).second) {
name_lookup_[name_id].push_back(target_id);
} else {
@@ -116,14 +116,14 @@ auto Context::AddNameToLookup(Parse::Node name_node, StringId name_id,
auto Context::LookupName(Parse::Node parse_node, StringId name_id,
SemIR::NameScopeId scope_id, bool print_diagnostics)
-> SemIR::NodeId {
-> SemIR::InstId {
if (scope_id == SemIR::NameScopeId::Invalid) {
auto it = name_lookup_.find(name_id);
if (it == name_lookup_.end()) {
if (print_diagnostics) {
DiagnoseNameNotFound(parse_node, name_id);
}
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
CARBON_CHECK(!it->second.empty())
<< "Should have been erased: " << strings().Get(name_id);
@@ -137,17 +137,17 @@ auto Context::LookupName(Parse::Node parse_node, StringId name_id,
if (print_diagnostics) {
DiagnoseNameNotFound(parse_node, name_id);
}
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
return it->second;
}
}
auto Context::PushScope(SemIR::NodeId scope_node_id,
auto Context::PushScope(SemIR::InstId scope_inst_id,
SemIR::NameScopeId scope_id) -> void {
scope_stack_.push_back(
{.scope_node_id = scope_node_id, .scope_id = scope_id});
{.scope_inst_id = scope_inst_id, .scope_id = scope_id});
}
auto Context::PopScope() -> void {
@@ -163,34 +163,34 @@ auto Context::PopScope() -> void {
}
}
auto Context::FollowNameReferences(SemIR::NodeId node_id) -> SemIR::NodeId {
while (auto name_ref = nodes().Get(node_id).TryAs<SemIR::NameReference>()) {
node_id = name_ref->value_id;
auto Context::FollowNameReferences(SemIR::InstId inst_id) -> SemIR::InstId {
while (auto name_ref = insts().Get(inst_id).TryAs<SemIR::NameReference>()) {
inst_id = name_ref->value_id;
}
return node_id;
return inst_id;
}
auto Context::GetConstantValue(SemIR::NodeId node_id) -> SemIR::NodeId {
// TODO: The constant value of a node should be computed as we build the
// node, or at least cached once computed.
auto Context::GetConstantValue(SemIR::InstId inst_id) -> SemIR::InstId {
// TODO: The constant value of an instruction should be computed as we build
// the instruction, or at least cached once computed.
while (true) {
auto node = nodes().Get(node_id);
switch (node.kind()) {
auto inst = insts().Get(inst_id);
switch (inst.kind()) {
case SemIR::NameReference::Kind:
node_id = node.As<SemIR::NameReference>().value_id;
inst_id = inst.As<SemIR::NameReference>().value_id;
break;
case SemIR::BindName::Kind:
node_id = node.As<SemIR::BindName>().value_id;
inst_id = inst.As<SemIR::BindName>().value_id;
break;
case SemIR::Field::Kind:
case SemIR::FunctionDeclaration::Kind:
return node_id;
return inst_id;
default:
// TODO: Handle the remaining cases.
return SemIR::NodeId::Invalid;
return SemIR::InstId::Invalid;
}
}
}
@@ -198,30 +198,30 @@ auto Context::GetConstantValue(SemIR::NodeId node_id) -> SemIR::NodeId {
template <typename BranchNode, typename... Args>
static auto AddDominatedBlockAndBranchImpl(Context& context,
Parse::Node parse_node, Args... args)
-> SemIR::NodeBlockId {
if (!context.node_block_stack().is_current_block_reachable()) {
return SemIR::NodeBlockId::Unreachable;
-> SemIR::InstBlockId {
if (!context.inst_block_stack().is_current_block_reachable()) {
return SemIR::InstBlockId::Unreachable;
}
auto block_id = context.node_blocks().AddDefaultValue();
context.AddNode(BranchNode{parse_node, block_id, args...});
auto block_id = context.inst_blocks().AddDefaultValue();
context.AddInst(BranchNode{parse_node, block_id, args...});
return block_id;
}
auto Context::AddDominatedBlockAndBranch(Parse::Node parse_node)
-> SemIR::NodeBlockId {
-> SemIR::InstBlockId {
return AddDominatedBlockAndBranchImpl<SemIR::Branch>(*this, parse_node);
}
auto Context::AddDominatedBlockAndBranchWithArg(Parse::Node parse_node,
SemIR::NodeId arg_id)
-> SemIR::NodeBlockId {
SemIR::InstId arg_id)
-> SemIR::InstBlockId {
return AddDominatedBlockAndBranchImpl<SemIR::BranchWithArg>(*this, parse_node,
arg_id);
}
auto Context::AddDominatedBlockAndBranchIf(Parse::Node parse_node,
SemIR::NodeId cond_id)
-> SemIR::NodeBlockId {
SemIR::InstId cond_id)
-> SemIR::InstBlockId {
return AddDominatedBlockAndBranchImpl<SemIR::BranchIf>(*this, parse_node,
cond_id);
}
@@ -230,73 +230,73 @@ auto Context::AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks)
-> void {
CARBON_CHECK(num_blocks >= 2) << "no convergence";
SemIR::NodeBlockId new_block_id = SemIR::NodeBlockId::Unreachable;
SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
for ([[maybe_unused]] auto _ : llvm::seq(num_blocks)) {
if (node_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::NodeBlockId::Unreachable) {
new_block_id = node_blocks().AddDefaultValue();
if (inst_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::InstBlockId::Unreachable) {
new_block_id = inst_blocks().AddDefaultValue();
}
AddNode(SemIR::Branch{parse_node, new_block_id});
AddInst(SemIR::Branch{parse_node, new_block_id});
}
node_block_stack().Pop();
inst_block_stack().Pop();
}
node_block_stack().Push(new_block_id);
inst_block_stack().Push(new_block_id);
}
auto Context::AddConvergenceBlockWithArgAndPush(
Parse::Node parse_node, std::initializer_list<SemIR::NodeId> block_args)
-> SemIR::NodeId {
Parse::Node parse_node, std::initializer_list<SemIR::InstId> block_args)
-> SemIR::InstId {
CARBON_CHECK(block_args.size() >= 2) << "no convergence";
SemIR::NodeBlockId new_block_id = SemIR::NodeBlockId::Unreachable;
SemIR::InstBlockId new_block_id = SemIR::InstBlockId::Unreachable;
for (auto arg_id : block_args) {
if (node_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::NodeBlockId::Unreachable) {
new_block_id = node_blocks().AddDefaultValue();
if (inst_block_stack().is_current_block_reachable()) {
if (new_block_id == SemIR::InstBlockId::Unreachable) {
new_block_id = inst_blocks().AddDefaultValue();
}
AddNode(SemIR::BranchWithArg{parse_node, new_block_id, arg_id});
AddInst(SemIR::BranchWithArg{parse_node, new_block_id, arg_id});
}
node_block_stack().Pop();
inst_block_stack().Pop();
}
node_block_stack().Push(new_block_id);
inst_block_stack().Push(new_block_id);
// Acquire the result value.
SemIR::TypeId result_type_id = nodes().Get(*block_args.begin()).type_id();
return AddNode(SemIR::BlockArg{parse_node, result_type_id, new_block_id});
SemIR::TypeId result_type_id = insts().Get(*block_args.begin()).type_id();
return AddInst(SemIR::BlockArg{parse_node, result_type_id, new_block_id});
}
// Add the current code block to the enclosing function.
auto Context::AddCurrentCodeBlockToFunction() -> void {
CARBON_CHECK(!node_block_stack().empty()) << "no current code block";
CARBON_CHECK(!inst_block_stack().empty()) << "no current code block";
CARBON_CHECK(!return_scope_stack().empty()) << "no current function";
if (!node_block_stack().is_current_block_reachable()) {
if (!inst_block_stack().is_current_block_reachable()) {
// Don't include unreachable blocks in the function.
return;
}
auto function_id =
nodes()
insts()
.GetAs<SemIR::FunctionDeclaration>(return_scope_stack().back())
.function_id;
functions()
.Get(function_id)
.body_block_ids.push_back(node_block_stack().PeekOrAdd());
.body_block_ids.push_back(inst_block_stack().PeekOrAdd());
}
auto Context::is_current_position_reachable() -> bool {
if (!node_block_stack().is_current_block_reachable()) {
if (!inst_block_stack().is_current_block_reachable()) {
return false;
}
// Our current position is at the end of a reachable block. That position is
// reachable unless the previous instruction is a terminator instruction.
auto block_contents = node_block_stack().PeekCurrentBlockContents();
auto block_contents = inst_block_stack().PeekCurrentBlockContents();
if (block_contents.empty()) {
return true;
}
const auto& last_node = nodes().Get(block_contents.back());
return last_node.kind().terminator_kind() !=
const auto& last_inst = insts().Get(block_contents.back());
return last_inst.kind().terminator_kind() !=
SemIR::TerminatorKind::Terminator;
}
@@ -312,11 +312,11 @@ auto Context::ParamOrArgEndNoPop(Parse::NodeKind start_kind) -> void {
}
}
auto Context::ParamOrArgPop() -> SemIR::NodeBlockId {
auto Context::ParamOrArgPop() -> SemIR::InstBlockId {
return params_or_args_stack_.Pop();
}
auto Context::ParamOrArgEnd(Parse::NodeKind start_kind) -> SemIR::NodeBlockId {
auto Context::ParamOrArgEnd(Parse::NodeKind start_kind) -> SemIR::InstBlockId {
ParamOrArgEndNoPop(start_kind);
return ParamOrArgPop();
}
@@ -373,14 +373,14 @@ class TypeCompleter {
return true;
}
auto node_id = context_.sem_ir().GetTypeAllowBuiltinTypes(type_id);
auto node = context_.nodes().Get(node_id);
auto inst_id = context_.sem_ir().GetTypeAllowBuiltinTypes(type_id);
auto inst = context_.insts().Get(inst_id);
auto old_work_list_size = work_list_.size();
switch (phase) {
case Phase::AddNestedIncompleteTypes:
if (!AddNestedIncompleteTypes(node)) {
if (!AddNestedIncompleteTypes(inst)) {
return false;
}
CARBON_CHECK(work_list_.size() >= old_work_list_size)
@@ -390,7 +390,7 @@ class TypeCompleter {
break;
case Phase::BuildValueRepresentation: {
auto value_rep = BuildValueRepresentation(type_id, node);
auto value_rep = BuildValueRepresentation(type_id, inst);
context_.sem_ir().CompleteType(type_id, value_rep);
CARBON_CHECK(old_work_list_size == work_list_.size())
<< "BuildValueRepresentation should not change work items";
@@ -419,18 +419,18 @@ class TypeCompleter {
return true;
}
// Adds any types nested within `type_node` that need to be complete for
// `type_node` to be complete to our work list.
auto AddNestedIncompleteTypes(SemIR::Node type_node) -> bool {
switch (type_node.kind()) {
// Adds any types nested within `type_inst` that need to be complete for
// `type_inst` to be complete to our work list.
auto AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool {
switch (type_inst.kind()) {
case SemIR::ArrayType::Kind:
Push(type_node.As<SemIR::ArrayType>().element_type_id);
Push(type_inst.As<SemIR::ArrayType>().element_type_id);
break;
case SemIR::StructType::Kind:
for (auto field_id : context_.node_blocks().Get(
type_node.As<SemIR::StructType>().fields_id)) {
Push(context_.nodes()
for (auto field_id : context_.inst_blocks().Get(
type_inst.As<SemIR::StructType>().fields_id)) {
Push(context_.insts()
.GetAs<SemIR::StructTypeField>(field_id)
.field_type_id);
}
@@ -438,13 +438,13 @@ class TypeCompleter {
case SemIR::TupleType::Kind:
for (auto element_type_id : context_.type_blocks().Get(
type_node.As<SemIR::TupleType>().elements_id)) {
type_inst.As<SemIR::TupleType>().elements_id)) {
Push(element_type_id);
}
break;
case SemIR::ClassType::Kind: {
auto class_type = type_node.As<SemIR::ClassType>();
auto class_type = type_inst.As<SemIR::ClassType>();
auto& class_info = context_.classes().Get(class_type.class_id);
if (!class_info.is_defined()) {
if (diagnoser_) {
@@ -459,7 +459,7 @@ class TypeCompleter {
}
case SemIR::ConstType::Kind:
Push(type_node.As<SemIR::ConstType>().inner_id);
Push(type_inst.As<SemIR::ConstType>().inner_id);
break;
default:
@@ -516,21 +516,21 @@ class TypeCompleter {
auto BuildCrossReferenceValueRepresentation(SemIR::TypeId type_id,
SemIR::CrossReference xref) const
-> SemIR::ValueRepresentation {
auto xref_node = context_.sem_ir()
auto xref_inst = context_.sem_ir()
.GetCrossReferenceIR(xref.ir_id)
.nodes()
.Get(xref.node_id);
.insts()
.Get(xref.inst_id);
// The canonical description of a type should only have cross-references
// for entities owned by another File, such as builtins, which are owned
// by the prelude, and named entities like classes and interfaces, which
// we don't support yet.
CARBON_CHECK(xref_node.kind() == SemIR::Builtin::Kind)
<< "TODO: Handle other kinds of node cross-references";
CARBON_CHECK(xref_inst.kind() == SemIR::Builtin::Kind)
<< "TODO: Handle other kinds of inst cross-references";
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (xref_node.As<SemIR::Builtin>().builtin_kind) {
switch (xref_inst.As<SemIR::Builtin>().builtin_kind) {
case SemIR::BuiltinKind::TypeType:
case SemIR::BuiltinKind::Error:
case SemIR::BuiltinKind::Invalid:
@@ -578,23 +578,23 @@ class TypeCompleter {
SemIR::StructType struct_type) const
-> SemIR::ValueRepresentation {
// TODO: Share more code with tuples.
auto fields = context_.node_blocks().Get(struct_type.fields_id);
auto fields = context_.inst_blocks().Get(struct_type.fields_id);
if (fields.empty()) {
return MakeEmptyRepresentation(struct_type.parse_node);
}
// Find the value representation for each field, and construct a struct
// of value representations.
llvm::SmallVector<SemIR::NodeId> value_rep_fields;
llvm::SmallVector<SemIR::InstId> value_rep_fields;
value_rep_fields.reserve(fields.size());
bool same_as_object_rep = true;
for (auto field_id : fields) {
auto field = context_.nodes().GetAs<SemIR::StructTypeField>(field_id);
auto field = context_.insts().GetAs<SemIR::StructTypeField>(field_id);
auto field_value_rep = GetNestedValueRepresentation(field.field_type_id);
if (field_value_rep.type_id != field.field_type_id) {
same_as_object_rep = false;
field.field_type_id = field_value_rep.type_id;
field_id = context_.AddNode(field);
field_id = context_.AddInst(field);
}
value_rep_fields.push_back(field_id);
}
@@ -603,7 +603,7 @@ class TypeCompleter {
? type_id
: context_.CanonicalizeStructType(
struct_type.parse_node,
context_.node_blocks().Add(value_rep_fields));
context_.inst_blocks().Add(value_rep_fields));
return BuildStructOrTupleValueRepresentation(
struct_type.parse_node, fields.size(), value_rep, same_as_object_rep);
}
@@ -640,16 +640,16 @@ class TypeCompleter {
// Builds and returns the value representation for the given type. All nested
// types, as found by AddNestedIncompleteTypes, are known to be complete.
auto BuildValueRepresentation(SemIR::TypeId type_id, SemIR::Node node) const
auto BuildValueRepresentation(SemIR::TypeId type_id, SemIR::Inst inst) const
-> SemIR::ValueRepresentation {
// TODO: This can emit new SemIR nodes. Consider emitting them into a
// dedicated file-scope node block where possible, or somewhere else that
// better reflects the definition of the type, rather than wherever the
// TODO: This can emit new SemIR instructions. Consider emitting them into a
// dedicated file-scope instruction block where possible, or somewhere else
// that better reflects the definition of the type, rather than wherever the
// type happens to first be required to be complete.
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (node.kind()) {
switch (inst.kind()) {
case SemIR::AddressOf::Kind:
case SemIR::ArrayIndex::Kind:
case SemIR::ArrayInit::Kind:
@@ -697,28 +697,28 @@ class TypeCompleter {
case SemIR::ValueAsReference::Kind:
case SemIR::ValueOfInitializer::Kind:
case SemIR::VarStorage::Kind:
CARBON_FATAL() << "Type refers to non-type node " << node;
CARBON_FATAL() << "Type refers to non-type inst " << inst;
case SemIR::CrossReference::Kind:
return BuildCrossReferenceValueRepresentation(
type_id, node.As<SemIR::CrossReference>());
type_id, inst.As<SemIR::CrossReference>());
case SemIR::ArrayType::Kind: {
// For arrays, it's convenient to always use a pointer representation,
// even when the array has zero or one element, in order to support
// indexing.
return MakePointerRepresentation(
node.parse_node(), type_id,
inst.parse_node(), type_id,
SemIR::ValueRepresentation::ObjectAggregate);
}
case SemIR::StructType::Kind:
return BuildStructTypeValueRepresentation(type_id,
node.As<SemIR::StructType>());
inst.As<SemIR::StructType>());
case SemIR::TupleType::Kind:
return BuildTupleTypeValueRepresentation(type_id,
node.As<SemIR::TupleType>());
inst.As<SemIR::TupleType>());
case SemIR::ClassType::Kind:
// The value representation for a class is a pointer to the object
@@ -726,9 +726,9 @@ class TypeCompleter {
// TODO: Support customized value representations for classes.
// TODO: Pick a better value representation when possible.
return MakePointerRepresentation(
node.parse_node(),
inst.parse_node(),
context_.classes()
.Get(node.As<SemIR::ClassType>().class_id)
.Get(inst.As<SemIR::ClassType>().class_id)
.object_representation_id,
SemIR::ValueRepresentation::ObjectAggregate);
@@ -743,7 +743,7 @@ class TypeCompleter {
// The value representation of `const T` is the same as that of `T`.
// Objects are not modifiable through their value representations.
return GetNestedValueRepresentation(
node.As<SemIR::ConstType>().inner_id);
inst.As<SemIR::ConstType>().inner_id);
}
}
@@ -774,9 +774,9 @@ auto Context::TryToCompleteType(
}
auto Context::CanonicalizeTypeImpl(
SemIR::NodeKind kind,
SemIR::InstKind kind,
llvm::function_ref<void(llvm::FoldingSetNodeID& canonical_id)> profile_type,
llvm::function_ref<SemIR::NodeId()> make_node) -> SemIR::TypeId {
llvm::function_ref<SemIR::InstId()> make_inst) -> SemIR::TypeId {
llvm::FoldingSetNodeID canonical_id;
kind.Profile(canonical_id);
profile_type(canonical_id);
@@ -788,14 +788,14 @@ auto Context::CanonicalizeTypeImpl(
return node->type_id();
}
auto node_id = make_node();
auto type_id = types().Add({.node_id = node_id});
CARBON_CHECK(canonical_types_.insert({node_id, type_id}).second);
auto inst_id = make_inst();
auto type_id = types().Add({.inst_id = inst_id});
CARBON_CHECK(canonical_types_.insert({inst_id, type_id}).second);
type_node_storage_.push_back(
std::make_unique<TypeNode>(canonical_id, type_id));
// In a debug build, check that our insertion position is still valid. It
// could have been invalidated by a misbehaving `make_node`.
// could have been invalidated by a misbehaving `make_inst`.
CARBON_DCHECK([&] {
void* check_insert_pos;
auto* check_node = canonical_type_nodes_.FindNodeOrInsertPos(
@@ -816,45 +816,45 @@ static auto ProfileTupleType(llvm::ArrayRef<SemIR::TypeId> type_ids,
}
// Compute a fingerprint for a type, for use as a key in a folding set.
static auto ProfileType(Context& semantics_context, SemIR::Node node,
static auto ProfileType(Context& semantics_context, SemIR::Inst inst,
llvm::FoldingSetNodeID& canonical_id) -> void {
switch (node.kind()) {
switch (inst.kind()) {
case SemIR::ArrayType::Kind: {
auto array_type = node.As<SemIR::ArrayType>();
auto array_type = inst.As<SemIR::ArrayType>();
canonical_id.AddInteger(
semantics_context.sem_ir().GetArrayBoundValue(array_type.bound_id));
canonical_id.AddInteger(array_type.element_type_id.index);
break;
}
case SemIR::Builtin::Kind:
canonical_id.AddInteger(node.As<SemIR::Builtin>().builtin_kind.AsInt());
canonical_id.AddInteger(inst.As<SemIR::Builtin>().builtin_kind.AsInt());
break;
case SemIR::ClassType::Kind:
canonical_id.AddInteger(node.As<SemIR::ClassType>().class_id.index);
canonical_id.AddInteger(inst.As<SemIR::ClassType>().class_id.index);
break;
case SemIR::CrossReference::Kind: {
// TODO: Cross-references should be canonicalized by looking at their
// target rather than treating them as new unique types.
auto xref = node.As<SemIR::CrossReference>();
auto xref = inst.As<SemIR::CrossReference>();
canonical_id.AddInteger(xref.ir_id.index);
canonical_id.AddInteger(xref.node_id.index);
canonical_id.AddInteger(xref.inst_id.index);
break;
}
case SemIR::ConstType::Kind:
canonical_id.AddInteger(
semantics_context
.GetUnqualifiedType(node.As<SemIR::ConstType>().inner_id)
.GetUnqualifiedType(inst.As<SemIR::ConstType>().inner_id)
.index);
break;
case SemIR::PointerType::Kind:
canonical_id.AddInteger(node.As<SemIR::PointerType>().pointee_id.index);
canonical_id.AddInteger(inst.As<SemIR::PointerType>().pointee_id.index);
break;
case SemIR::StructType::Kind: {
auto fields = semantics_context.node_blocks().Get(
node.As<SemIR::StructType>().fields_id);
auto fields = semantics_context.inst_blocks().Get(
inst.As<SemIR::StructType>().fields_id);
for (const auto& field_id : fields) {
auto field =
semantics_context.nodes().GetAs<SemIR::StructTypeField>(field_id);
semantics_context.insts().GetAs<SemIR::StructTypeField>(field_id);
canonical_id.AddInteger(field.name_id.index);
canonical_id.AddInteger(field.field_type_id.index);
}
@@ -862,49 +862,49 @@ static auto ProfileType(Context& semantics_context, SemIR::Node node,
}
case SemIR::TupleType::Kind:
ProfileTupleType(semantics_context.type_blocks().Get(
node.As<SemIR::TupleType>().elements_id),
inst.As<SemIR::TupleType>().elements_id),
canonical_id);
break;
case SemIR::UnboundFieldType::Kind: {
auto unbound_field_type = node.As<SemIR::UnboundFieldType>();
auto unbound_field_type = inst.As<SemIR::UnboundFieldType>();
canonical_id.AddInteger(unbound_field_type.class_type_id.index);
canonical_id.AddInteger(unbound_field_type.field_type_id.index);
break;
}
default:
CARBON_FATAL() << "Unexpected type node " << node;
CARBON_FATAL() << "Unexpected type inst " << inst;
}
}
auto Context::CanonicalizeTypeAndAddNodeIfNew(SemIR::Node node)
auto Context::CanonicalizeTypeAndAddInstIfNew(SemIR::Inst inst)
-> SemIR::TypeId {
auto profile_node = [&](llvm::FoldingSetNodeID& canonical_id) {
ProfileType(*this, node, canonical_id);
ProfileType(*this, inst, canonical_id);
};
auto make_node = [&] { return AddNode(node); };
return CanonicalizeTypeImpl(node.kind(), profile_node, make_node);
auto make_inst = [&] { return AddInst(inst); };
return CanonicalizeTypeImpl(inst.kind(), profile_node, make_inst);
}
auto Context::CanonicalizeType(SemIR::NodeId node_id) -> SemIR::TypeId {
node_id = FollowNameReferences(node_id);
auto Context::CanonicalizeType(SemIR::InstId inst_id) -> SemIR::TypeId {
inst_id = FollowNameReferences(inst_id);
auto it = canonical_types_.find(node_id);
auto it = canonical_types_.find(inst_id);
if (it != canonical_types_.end()) {
return it->second;
}
auto node = nodes().Get(node_id);
auto inst = insts().Get(inst_id);
auto profile_node = [&](llvm::FoldingSetNodeID& canonical_id) {
ProfileType(*this, node, canonical_id);
ProfileType(*this, inst, canonical_id);
};
auto make_node = [&] { return node_id; };
return CanonicalizeTypeImpl(node.kind(), profile_node, make_node);
auto make_inst = [&] { return inst_id; };
return CanonicalizeTypeImpl(inst.kind(), profile_node, make_inst);
}
auto Context::CanonicalizeStructType(Parse::Node parse_node,
SemIR::NodeBlockId refs_id)
SemIR::InstBlockId refs_id)
-> SemIR::TypeId {
return CanonicalizeTypeAndAddNodeIfNew(
return CanonicalizeTypeAndAddInstIfNew(
SemIR::StructType{parse_node, SemIR::TypeId::TypeType, refs_id});
}
@@ -915,17 +915,17 @@ auto Context::CanonicalizeTupleType(Parse::Node parse_node,
auto profile_tuple = [&](llvm::FoldingSetNodeID& canonical_id) {
ProfileTupleType(type_ids, canonical_id);
};
auto make_tuple_node = [&] {
return AddNode(SemIR::TupleType{parse_node, SemIR::TypeId::TypeType,
auto make_tuple_inst = [&] {
return AddInst(SemIR::TupleType{parse_node, SemIR::TypeId::TypeType,
type_blocks().Add(type_ids)});
};
return CanonicalizeTypeImpl(SemIR::TupleType::Kind, profile_tuple,
make_tuple_node);
make_tuple_inst);
}
auto Context::GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId {
CARBON_CHECK(kind != SemIR::BuiltinKind::Invalid);
auto type_id = CanonicalizeType(SemIR::NodeId::ForBuiltin(kind));
auto type_id = CanonicalizeType(SemIR::InstId::ForBuiltin(kind));
// To keep client code simpler, complete builtin types before returning them.
bool complete = TryToCompleteType(type_id);
CARBON_CHECK(complete) << "Failed to complete builtin type";
@@ -934,14 +934,14 @@ auto Context::GetBuiltinType(SemIR::BuiltinKind kind) -> SemIR::TypeId {
auto Context::GetPointerType(Parse::Node parse_node,
SemIR::TypeId pointee_type_id) -> SemIR::TypeId {
return CanonicalizeTypeAndAddNodeIfNew(
return CanonicalizeTypeAndAddInstIfNew(
SemIR::PointerType{parse_node, SemIR::TypeId::TypeType, pointee_type_id});
}
auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId {
SemIR::Node type_node =
nodes().Get(sem_ir_->GetTypeAllowBuiltinTypes(type_id));
if (auto const_type = type_node.TryAs<SemIR::ConstType>()) {
SemIR::Inst type_inst =
insts().Get(sem_ir_->GetTypeAllowBuiltinTypes(type_id));
if (auto const_type = type_inst.TryAs<SemIR::ConstType>()) {
return const_type->inner_id;
}
return type_id;
@@ -949,7 +949,7 @@ auto Context::GetUnqualifiedType(SemIR::TypeId type_id) -> SemIR::TypeId {
auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
node_stack_.PrintForStackDump(output);
node_block_stack_.PrintForStackDump(output);
inst_block_stack_.PrintForStackDump(output);
params_or_args_stack_.PrintForStackDump(output);
args_type_info_stack_.PrintForStackDump(output);
}
+84 -83
View File
@@ -14,7 +14,7 @@
#include "toolchain/check/node_stack.h"
#include "toolchain/parse/tree.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -26,8 +26,8 @@ class Context {
// A scope in which `break` and `continue` can be used.
struct BreakContinueScope {
SemIR::NodeBlockId break_target;
SemIR::NodeBlockId continue_target;
SemIR::InstBlockId break_target;
SemIR::InstBlockId continue_target;
};
// Stores references for work.
@@ -41,25 +41,25 @@ class Context {
// Runs verification that the processing cleanly finished.
auto VerifyOnFinish() -> void;
// Adds a node to the current block, returning the produced ID.
auto AddNode(SemIR::Node node) -> SemIR::NodeId;
// Adds an instruction to the current block, returning the produced ID.
auto AddInst(SemIR::Inst inst) -> SemIR::InstId;
// Pushes a parse tree node onto the stack, storing the SemIR::Node as the
// Pushes a parse tree node onto the stack, storing the SemIR::Inst as the
// result.
auto AddNodeAndPush(Parse::Node parse_node, SemIR::Node node) -> void;
auto AddInstAndPush(Parse::Node parse_node, SemIR::Inst inst) -> void;
// Adds a name to name lookup. Prints a diagnostic for name conflicts.
auto AddNameToLookup(Parse::Node name_node, StringId name_id,
SemIR::NodeId target_id) -> void;
SemIR::InstId target_id) -> void;
// Performs name lookup in a specified scope, returning the referenced node.
// If scope_id is invalid, uses the current contextual scope.
// Performs name lookup in a specified scope, returning the referenced
// instruction. If scope_id is invalid, uses the current contextual scope.
auto LookupName(Parse::Node parse_node, StringId name_id,
SemIR::NameScopeId scope_id, bool print_diagnostics)
-> SemIR::NodeId;
-> SemIR::InstId;
// Prints a diagnostic for a duplicate name.
auto DiagnoseDuplicateName(Parse::Node parse_node, SemIR::NodeId prev_def_id)
auto DiagnoseDuplicateName(Parse::Node parse_node, SemIR::InstId prev_def_id)
-> void;
// Prints a diagnostic for a missing name.
@@ -70,7 +70,7 @@ class Context {
-> void;
// Pushes a new scope onto scope_stack_.
auto PushScope(SemIR::NodeId scope_node_id = SemIR::NodeId::Invalid,
auto PushScope(SemIR::InstId scope_inst_id = SemIR::InstId::Invalid,
SemIR::NameScopeId scope_id = SemIR::NameScopeId::Invalid)
-> void;
@@ -84,55 +84,56 @@ class Context {
// Returns the current scope, if it is of the specified kind. Otherwise,
// returns nullopt.
template <typename NodeT>
auto GetCurrentScopeAs() -> std::optional<NodeT> {
auto current_scope_node_id = current_scope().scope_node_id;
if (!current_scope_node_id.is_valid()) {
template <typename InstT>
auto GetCurrentScopeAs() -> std::optional<InstT> {
auto current_scope_inst_id = current_scope().scope_inst_id;
if (!current_scope_inst_id.is_valid()) {
return std::nullopt;
}
return nodes().Get(current_scope_node_id).TryAs<NodeT>();
return insts().Get(current_scope_inst_id).TryAs<InstT>();
}
// Follows NameReference nodes to find the value named by a given node.
auto FollowNameReferences(SemIR::NodeId node_id) -> SemIR::NodeId;
// Follows NameReference instructions to find the value named by a given
// instruction.
auto FollowNameReferences(SemIR::InstId inst_id) -> SemIR::InstId;
// Gets the constant value of the given node, if it has one.
auto GetConstantValue(SemIR::NodeId node_id) -> SemIR::NodeId;
// Gets the constant value of the given instruction, if it has one.
auto GetConstantValue(SemIR::InstId inst_id) -> SemIR::InstId;
// Adds a `Branch` node branching to a new node block, and returns the ID of
// the new block. All paths to the branch target must go through the current
// block, though not necessarily through this branch.
auto AddDominatedBlockAndBranch(Parse::Node parse_node) -> SemIR::NodeBlockId;
// Adds a `Branch` instruction branching to a new instruction block, and
// returns the ID of the new block. All paths to the branch target must go
// through the current block, though not necessarily through this branch.
auto AddDominatedBlockAndBranch(Parse::Node parse_node) -> SemIR::InstBlockId;
// Adds a `Branch` node branching to a new node block with a value, and
// Adds a `Branch` instruction branching to a new instruction block with a
// value, and returns the ID of the new block. All paths to the branch target
// must go through the current block.
auto AddDominatedBlockAndBranchWithArg(Parse::Node parse_node,
SemIR::InstId arg_id)
-> SemIR::InstBlockId;
// Adds a `BranchIf` instruction branching to a new instruction block, and
// returns the ID of the new block. All paths to the branch target must go
// through the current block.
auto AddDominatedBlockAndBranchWithArg(Parse::Node parse_node,
SemIR::NodeId arg_id)
-> SemIR::NodeBlockId;
// Adds a `BranchIf` node branching to a new node block, and returns the ID
// of the new block. All paths to the branch target must go through the
// current block.
auto AddDominatedBlockAndBranchIf(Parse::Node parse_node,
SemIR::NodeId cond_id)
-> SemIR::NodeBlockId;
SemIR::InstId cond_id)
-> SemIR::InstBlockId;
// Handles recovergence of control flow. Adds branches from the top
// `num_blocks` on the node block stack to a new block, pops the existing
// blocks, and pushes the new block onto the node block stack.
// `num_blocks` on the instruction block stack to a new block, pops the
// existing blocks, and pushes the new block onto the instruction block stack.
auto AddConvergenceBlockAndPush(Parse::Node parse_node, int num_blocks)
-> void;
// Handles recovergence of control flow with a result value. Adds branches
// from the top few blocks on the node block stack to a new block, pops the
// existing blocks, and pushes the new block onto the node block stack. The
// number of blocks popped is the size of `block_args`, and the corresponding
// result values are the elements of `block_args`. Returns a node referring
// to the result value.
// from the top few blocks on the instruction block stack to a new block, pops
// the existing blocks, and pushes the new block onto the instruction block
// stack. The number of blocks popped is the size of `block_args`, and the
// corresponding result values are the elements of `block_args`. Returns an
// instruction referring to the result value.
auto AddConvergenceBlockWithArgAndPush(
Parse::Node parse_node,
std::initializer_list<SemIR::NodeId> blocks_and_args) -> SemIR::NodeId;
std::initializer_list<SemIR::InstId> blocks_and_args) -> SemIR::InstId;
// Add the current code block to the enclosing function.
auto AddCurrentCodeBlockToFunction() -> void;
@@ -140,8 +141,8 @@ class Context {
// Returns whether the current position in the current block is reachable.
auto is_current_position_reachable() -> bool;
// Canonicalizes a type which is tracked as a single node.
auto CanonicalizeType(SemIR::NodeId node_id) -> SemIR::TypeId;
// Canonicalizes a type which is tracked as a single instruction.
auto CanonicalizeType(SemIR::InstId inst_id) -> SemIR::TypeId;
// Handles canonicalization of struct types. This may create a new struct type
// when it has a new structure, or reference an existing struct type when it
@@ -151,7 +152,7 @@ class Context {
// name conflicts or other diagnostics during creation, which can use the
// parse node.
auto CanonicalizeStructType(Parse::Node parse_node,
SemIR::NodeBlockId refs_id) -> SemIR::TypeId;
SemIR::InstBlockId refs_id) -> SemIR::TypeId;
// Handles canonicalization of tuple types. This may create a new tuple type
// if the `type_ids` doesn't match an existing tuple type.
@@ -197,16 +198,16 @@ class Context {
// Pops the current parameter or argument list. Should only be called after
// `ParamOrArgEndNoPop`.
auto ParamOrArgPop() -> SemIR::NodeBlockId;
auto ParamOrArgPop() -> SemIR::InstBlockId;
// Detects whether there's an entry to push. Pops and returns the argument
// list. This is the same as `ParamOrArgEndNoPop` followed by `ParamOrArgPop`.
auto ParamOrArgEnd(Parse::NodeKind start_kind) -> SemIR::NodeBlockId;
auto ParamOrArgEnd(Parse::NodeKind start_kind) -> SemIR::InstBlockId;
// Saves a parameter from the top block in node_stack_ to the top block in
// params_or_args_stack_.
auto ParamOrArgSave(SemIR::NodeId node_id) -> void {
params_or_args_stack_.AddNodeId(node_id);
auto ParamOrArgSave(SemIR::InstId inst_id) -> void {
params_or_args_stack_.AddInstId(inst_id);
}
// Prints information for a stack dump.
@@ -222,17 +223,17 @@ class Context {
auto node_stack() -> NodeStack& { return node_stack_; }
auto node_block_stack() -> NodeBlockStack& { return node_block_stack_; }
auto inst_block_stack() -> InstBlockStack& { return inst_block_stack_; }
auto params_or_args_stack() -> NodeBlockStack& {
auto params_or_args_stack() -> InstBlockStack& {
return params_or_args_stack_;
}
auto args_type_info_stack() -> NodeBlockStack& {
auto args_type_info_stack() -> InstBlockStack& {
return args_type_info_stack_;
}
auto return_scope_stack() -> llvm::SmallVector<SemIR::NodeId>& {
auto return_scope_stack() -> llvm::SmallVector<SemIR::InstId>& {
return return_scope_stack_;
}
@@ -264,9 +265,9 @@ class Context {
-> SemIR::BlockValueStore<SemIR::TypeBlockId, SemIR::TypeId>& {
return sem_ir().type_blocks();
}
auto nodes() -> SemIR::NodeStore& { return sem_ir().nodes(); }
auto node_blocks() -> SemIR::NodeBlockStore& {
return sem_ir().node_blocks();
auto insts() -> SemIR::InstStore& { return sem_ir().insts(); }
auto inst_blocks() -> SemIR::InstBlockStore& {
return sem_ir().inst_blocks();
}
private:
@@ -285,13 +286,13 @@ class Context {
// An entry in scope_stack_.
struct ScopeStackEntry {
// The node associated with this entry, if any. This can be one of:
// The instruction associated with this entry, if any. This can be one of:
//
// - A `ClassDeclaration`, for a class definition scope.
// - A `FunctionDeclaration`, for the outermost scope in a function
// definition.
// - Invalid, for any other scope.
SemIR::NodeId scope_node_id;
SemIR::InstId scope_inst_id;
// The name scope associated with this entry, if any.
SemIR::NameScopeId scope_id;
@@ -310,19 +311,19 @@ class Context {
// type. The ID should be distinct for all distinct type values with the same
// `kind`.
//
// `make_node()` is called to obtain a `SemIR::NodeId` that describes the
// `make_inst()` is called to obtain a `SemIR::InstId` that describes the
// type. It is only called if the type does not already exist, so can be used
// to lazily build the `SemIR::Node`. `make_node()` is not permitted to
// to lazily build the `SemIR::Inst`. `make_inst()` is not permitted to
// directly or indirectly canonicalize any types.
auto CanonicalizeTypeImpl(
SemIR::NodeKind kind,
SemIR::InstKind kind,
llvm::function_ref<void(llvm::FoldingSetNodeID& canonical_id)>
profile_type,
llvm::function_ref<SemIR::NodeId()> make_node) -> SemIR::TypeId;
llvm::function_ref<SemIR::InstId()> make_inst) -> SemIR::TypeId;
// Forms a canonical type ID for a type. If the type is new, adds the node to
// the current block.
auto CanonicalizeTypeAndAddNodeIfNew(SemIR::Node node) -> SemIR::TypeId;
// Forms a canonical type ID for a type. If the type is new, adds the
// instruction to the current block.
auto CanonicalizeTypeAndAddInstIfNew(SemIR::Inst inst) -> SemIR::TypeId;
auto current_scope() -> ScopeStackEntry& { return scope_stack_.back(); }
auto current_scope() const -> const ScopeStackEntry& {
@@ -347,24 +348,24 @@ class Context {
// The stack during Build. Will contain file-level parse nodes on return.
NodeStack node_stack_;
// The stack of node blocks being used for general IR generation.
NodeBlockStack node_block_stack_;
// The stack of instruction blocks being used for general IR generation.
InstBlockStack inst_block_stack_;
// The stack of node blocks being used for per-element tracking of nodes in
// parameter and argument node blocks. Versus node_block_stack_, an element
// will have 1 or more nodes in blocks in node_block_stack_, but only ever 1
// node in blocks here.
NodeBlockStack params_or_args_stack_;
// The stack of instruction blocks being used for per-element tracking of
// instructions in parameter and argument instruction blocks. Versus
// inst_block_stack_, an element will have 1 or more instructions in blocks in
// inst_block_stack_, but only ever 1 instruction in blocks here.
InstBlockStack params_or_args_stack_;
// The stack of node blocks being used for type information while processing
// arguments. This is used in parallel with params_or_args_stack_. It's
// currently only used for struct literals, where we need to track names
// The stack of instruction blocks being used for type information while
// processing arguments. This is used in parallel with params_or_args_stack_.
// It's currently only used for struct literals, where we need to track names
// for a type separate from the literal arguments.
NodeBlockStack args_type_info_stack_;
InstBlockStack args_type_info_stack_;
// A stack of return scopes; i.e., targets for `return`. Inside a function,
// this will be a FunctionDeclaration.
llvm::SmallVector<SemIR::NodeId> return_scope_stack_;
llvm::SmallVector<SemIR::InstId> return_scope_stack_;
// A stack of `break` and `continue` targets.
llvm::SmallVector<BreakContinueScope> break_continue_stack_;
@@ -381,11 +382,11 @@ class Context {
// reference.
//
// Names which no longer have lookup results are erased.
llvm::DenseMap<StringId, llvm::SmallVector<SemIR::NodeId>> name_lookup_;
llvm::DenseMap<StringId, llvm::SmallVector<SemIR::InstId>> name_lookup_;
// Cache of the mapping from nodes to types, to avoid recomputing the folding
// set ID.
llvm::DenseMap<SemIR::NodeId, SemIR::TypeId> canonical_types_;
// Cache of the mapping from instructions to types, to avoid recomputing the
// folding set ID.
llvm::DenseMap<SemIR::InstId, SemIR::TypeId> canonical_types_;
// Tracks the canonical representation of types that have been defined.
llvm::FoldingSet<TypeNode> canonical_type_nodes_;
+179 -179
View File
@@ -13,7 +13,7 @@
#include "toolchain/diagnostics/diagnostic_kind.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Check {
@@ -22,12 +22,12 @@ namespace Carbon::Check {
// there is no return slot, because the initialization is not performed in
// place.
static auto FindReturnSlotForInitializer(SemIR::File& sem_ir,
SemIR::NodeId init_id)
-> SemIR::NodeId {
SemIR::Node init = sem_ir.nodes().Get(init_id);
SemIR::InstId init_id)
-> SemIR::InstId {
SemIR::Inst init = sem_ir.insts().Get(init_id);
switch (init.kind()) {
default:
CARBON_FATAL() << "Initialization from unexpected node " << init;
CARBON_FATAL() << "Initialization from unexpected inst " << init;
case SemIR::StructInit::Kind:
case SemIR::TupleInit::Kind:
@@ -44,13 +44,13 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir,
auto call = init.As<SemIR::Call>();
if (!SemIR::GetInitializingRepresentation(sem_ir, call.type_id)
.has_return_slot()) {
return SemIR::NodeId::Invalid;
return SemIR::InstId::Invalid;
}
return sem_ir.node_blocks().Get(call.args_id).back();
return sem_ir.inst_blocks().Get(call.args_id).back();
}
case SemIR::ArrayInit::Kind: {
return sem_ir.node_blocks()
return sem_ir.inst_blocks()
.Get(init.As<SemIR::ArrayInit>().inits_and_return_slot_id)
.back();
}
@@ -58,17 +58,17 @@ static auto FindReturnSlotForInitializer(SemIR::File& sem_ir,
}
// Marks the initializer `init_id` as initializing `target_id`.
static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::NodeId init_id,
SemIR::NodeId target_id,
static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::InstId init_id,
SemIR::InstId target_id,
PendingBlock& target_block) -> void {
auto return_slot_id = FindReturnSlotForInitializer(sem_ir, init_id);
if (return_slot_id.is_valid()) {
// Replace the temporary in the return slot with a reference to our target.
CARBON_CHECK(sem_ir.nodes().Get(return_slot_id).kind() ==
CARBON_CHECK(sem_ir.insts().Get(return_slot_id).kind() ==
SemIR::TemporaryStorage::Kind)
<< "Return slot for initializer does not contain a temporary; "
<< "initialized multiple times? Have "
<< sem_ir.nodes().Get(return_slot_id);
<< sem_ir.insts().Get(return_slot_id);
target_block.MergeReplacing(return_slot_id, target_id);
}
}
@@ -77,44 +77,44 @@ static auto MarkInitializerFor(SemIR::File& sem_ir, SemIR::NodeId init_id,
// expression described by `init_id`, and returns the location of the
// temporary. If `discarded` is `true`, the result is discarded, and no
// temporary will be created if possible; if no temporary is created, the
// return value will be `SemIR::NodeId::Invalid`.
static auto FinalizeTemporary(Context& context, SemIR::NodeId init_id,
bool discarded) -> SemIR::NodeId {
// return value will be `SemIR::InstId::Invalid`.
static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
bool discarded) -> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto return_slot_id = FindReturnSlotForInitializer(sem_ir, init_id);
if (return_slot_id.is_valid()) {
// The return slot should already have a materialized temporary in it.
CARBON_CHECK(sem_ir.nodes().Get(return_slot_id).kind() ==
CARBON_CHECK(sem_ir.insts().Get(return_slot_id).kind() ==
SemIR::TemporaryStorage::Kind)
<< "Return slot for initializer does not contain a temporary; "
<< "initialized multiple times? Have "
<< sem_ir.nodes().Get(return_slot_id);
auto init = sem_ir.nodes().Get(init_id);
return context.AddNode(SemIR::Temporary{init.parse_node(), init.type_id(),
<< sem_ir.insts().Get(return_slot_id);
auto init = sem_ir.insts().Get(init_id);
return context.AddInst(SemIR::Temporary{init.parse_node(), init.type_id(),
return_slot_id, init_id});
}
if (discarded) {
// Don't invent a temporary that we're going to discard.
return SemIR::NodeId::Invalid;
return SemIR::InstId::Invalid;
}
// The initializer has no return slot, but we want to produce a temporary
// object. Materialize one now.
// TODO: Consider using an invalid ID to mean that we immediately
// materialize and initialize a temporary, rather than two separate
// nodes.
auto init = sem_ir.nodes().Get(init_id);
auto temporary_id = context.AddNode(
// instructions.
auto init = sem_ir.insts().Get(init_id);
auto temporary_id = context.AddInst(
SemIR::TemporaryStorage{init.parse_node(), init.type_id()});
return context.AddNode(SemIR::Temporary{init.parse_node(), init.type_id(),
return context.AddInst(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
// an initializing expression.
static auto MaterializeIfInitializing(Context& context, SemIR::NodeId expr_id)
-> SemIR::NodeId {
static auto MaterializeIfInitializing(Context& context, SemIR::InstId expr_id)
-> SemIR::InstId {
if (GetExpressionCategory(context.sem_ir(), expr_id) ==
SemIR::ExpressionCategory::Initializing) {
return FinalizeTemporary(context, expr_id, /*discarded=*/false);
@@ -122,23 +122,23 @@ static auto MaterializeIfInitializing(Context& context, SemIR::NodeId expr_id)
return expr_id;
}
// Creates and adds a node to perform element access into an aggregate.
template <typename AccessNodeT, typename NodeBlockT>
static auto MakeElemAccessNode(Context& context, Parse::Node parse_node,
SemIR::NodeId aggregate_id,
SemIR::TypeId elem_type_id, NodeBlockT& block,
// Creates and adds an instruction to perform element access into an aggregate.
template <typename AccessInstT, typename InstBlockT>
static auto MakeElemAccessInst(Context& context, Parse::Node parse_node,
SemIR::InstId aggregate_id,
SemIR::TypeId elem_type_id, InstBlockT& block,
std::size_t i) {
if constexpr (std::is_same_v<AccessNodeT, SemIR::ArrayIndex>) {
// 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{
if constexpr (std::is_same_v<AccessInstT, SemIR::ArrayIndex>) {
// TODO: Add a new instruction kind for indexing an array at a constant
// index so that we don't need an integer literal instruction here, and
// remove this special case.
auto index_id = block.AddInst(SemIR::IntegerLiteral{
parse_node, context.GetBuiltinType(SemIR::BuiltinKind::IntegerType),
context.sem_ir().integers().Add(llvm::APInt(32, i))});
return block.AddNode(
AccessNodeT{parse_node, elem_type_id, aggregate_id, index_id});
return block.AddInst(
AccessInstT{parse_node, elem_type_id, aggregate_id, index_id});
} else {
return block.AddNode(AccessNodeT{parse_node, elem_type_id, aggregate_id,
return block.AddInst(AccessInstT{parse_node, elem_type_id, aggregate_id,
SemIR::MemberIndex(i)});
}
}
@@ -147,29 +147,29 @@ static auto MakeElemAccessNode(Context& context, Parse::Node parse_node,
// another aggregate.
//
// For the source: `src_id` is the source aggregate, `src_elem_type` is the
// element type, `i` is the index, and `SourceAccessNodeT` is the kind of node
// used to access the source element.
// element type, `i` is the index, and `SourceAccessInstT` is the kind of
// instruction used to access the source element.
//
// For the target: `kind` is the kind of conversion or initialization,
// `target_elem_type` is the element type. For initialization, `target_id` is
// the destination, `target_block` is a pending block for target location
// calculations that will be spliced as the return slot of the initializer if
// necessary, `i` is the index, and `TargetAccessNodeT` is the kind of node
// used to access the destination element.
template <typename SourceAccessNodeT, typename TargetAccessNodeT>
// necessary, `i` is the index, and `TargetAccessInstT` is the kind of
// instruction used to access the destination element.
template <typename SourceAccessInstT, typename TargetAccessInstT>
static auto ConvertAggregateElement(
Context& context, Parse::Node parse_node, SemIR::NodeId src_id,
Context& context, Parse::Node parse_node, SemIR::InstId src_id,
SemIR::TypeId src_elem_type,
llvm::ArrayRef<SemIR::NodeId> src_literal_elems,
ConversionTarget::Kind kind, SemIR::NodeId target_id,
llvm::ArrayRef<SemIR::InstId> src_literal_elems,
ConversionTarget::Kind kind, SemIR::InstId target_id,
SemIR::TypeId target_elem_type, PendingBlock* target_block, std::size_t i) {
// Compute the location of the source element. This goes into the current code
// block, not into the target block.
// TODO: Ideally we would discard this node if it's unused.
// TODO: Ideally we would discard this instruction if it's unused.
auto src_elem_id =
!src_literal_elems.empty()
? src_literal_elems[i]
: MakeElemAccessNode<SourceAccessNodeT>(context, parse_node, src_id,
: MakeElemAccessInst<SourceAccessInstT>(context, parse_node, src_id,
src_elem_type, context, i);
// If we're performing a conversion rather than an initialization, we won't
@@ -180,9 +180,9 @@ static auto ConvertAggregateElement(
}
// Compute the location of the target element and initialize it.
PendingBlock::DiscardUnusedNodesScope scope(target_block);
PendingBlock::DiscardUnusedInstsScope scope(target_block);
target.init_block = target_block;
target.init_id = MakeElemAccessNode<TargetAccessNodeT>(
target.init_id = MakeElemAccessInst<TargetAccessInstT>(
context, parse_node, target_id, target_elem_type, *target_block, i);
return Convert(context, parse_node, src_elem_id, target);
}
@@ -202,29 +202,29 @@ class CopyOnWriteBlock {
// Constructs the block. If `source_id` is valid, it is used as the initial
// value of the block. Otherwise, uninitialized storage for `size` elements
// is allocated.
CopyOnWriteBlock(SemIR::File& file, SemIR::NodeBlockId source_id, size_t size)
CopyOnWriteBlock(SemIR::File& file, SemIR::InstBlockId source_id, size_t size)
: file_(file), source_id_(source_id) {
if (!source_id_.is_valid()) {
id_ = file_.node_blocks().AddUninitialized(size);
id_ = file_.inst_blocks().AddUninitialized(size);
}
}
auto id() -> SemIR::NodeBlockId const { return id_; }
auto id() -> SemIR::InstBlockId const { return id_; }
auto Set(int i, SemIR::NodeId value) -> void {
if (source_id_.is_valid() && file_.node_blocks().Get(id_)[i] == value) {
auto Set(int i, SemIR::InstId value) -> void {
if (source_id_.is_valid() && file_.inst_blocks().Get(id_)[i] == value) {
return;
}
if (id_ == source_id_) {
id_ = file_.node_blocks().Add(file_.node_blocks().Get(source_id_));
id_ = file_.inst_blocks().Add(file_.inst_blocks().Get(source_id_));
}
file_.node_blocks().Get(id_)[i] = value;
file_.inst_blocks().Get(id_)[i] = value;
}
private:
SemIR::File& file_;
SemIR::NodeBlockId source_id_;
SemIR::NodeBlockId id_ = source_id_;
SemIR::InstBlockId source_id_;
SemIR::InstBlockId id_ = source_id_;
};
} // namespace
@@ -232,19 +232,19 @@ class CopyOnWriteBlock {
// final conversion to the requested expression category.
static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
SemIR::ArrayType array_type,
SemIR::NodeId value_id, ConversionTarget target)
-> SemIR::NodeId {
SemIR::InstId value_id, ConversionTarget target)
-> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto tuple_elem_types = sem_ir.type_blocks().Get(tuple_type.elements_id);
auto value = sem_ir.nodes().Get(value_id);
auto value = sem_ir.insts().Get(value_id);
// If we're initializing from a tuple literal, we will use its elements
// directly. Otherwise, materialize a temporary if needed and index into the
// result.
llvm::ArrayRef<SemIR::NodeId> literal_elems;
llvm::ArrayRef<SemIR::InstId> literal_elems;
if (auto tuple_literal = value.TryAs<SemIR::TupleLiteral>()) {
literal_elems = sem_ir.node_blocks().Get(tuple_literal->elements_id);
literal_elems = sem_ir.inst_blocks().Get(tuple_literal->elements_id);
} else {
value_id = MaterializeIfInitializing(context, value_id);
}
@@ -265,7 +265,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
? ArrayInitFromExpressionArgCountMismatch
: ArrayInitFromLiteralArgCountMismatch,
array_bound, tuple_elem_types.size());
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
PendingBlock target_block_storage(context);
@@ -274,9 +274,9 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
// Arrays are always initialized in-place. Allocate a temporary as the
// destination for the array initialization if we weren't given one.
SemIR::NodeId return_slot_id = target.init_id;
SemIR::InstId return_slot_id = target.init_id;
if (!target.init_id.is_valid()) {
return_slot_id = target_block->AddNode(
return_slot_id = target_block->AddInst(
SemIR::TemporaryStorage{value.parse_node(), target.type_id});
}
@@ -284,7 +284,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
// tuple.
// TODO: Annotate diagnostics coming from here with the array element index,
// if initializing from a tuple literal.
llvm::SmallVector<SemIR::NodeId> inits;
llvm::SmallVector<SemIR::InstId> inits;
inits.reserve(array_bound + 1);
for (auto [i, src_type_id] : llvm::enumerate(tuple_elem_types)) {
// TODO: This call recurses back into conversion. Switch to an iterative
@@ -294,8 +294,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
context, value.parse_node(), value_id, src_type_id, literal_elems,
ConversionTarget::FullInitializer, return_slot_id,
array_type.element_type_id, target_block, i);
if (init_id == SemIR::NodeId::BuiltinError) {
return SemIR::NodeId::BuiltinError;
if (init_id == SemIR::InstId::BuiltinError) {
return SemIR::InstId::BuiltinError;
}
inits.push_back(init_id);
}
@@ -305,31 +305,31 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
target_block->InsertHere();
inits.push_back(return_slot_id);
return context.AddNode(SemIR::ArrayInit{value.parse_node(), target.type_id,
return context.AddInst(SemIR::ArrayInit{value.parse_node(), target.type_id,
value_id,
sem_ir.node_blocks().Add(inits)});
sem_ir.inst_blocks().Add(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 src_type,
SemIR::TupleType dest_type,
SemIR::NodeId value_id, ConversionTarget target)
-> SemIR::NodeId {
SemIR::InstId value_id, ConversionTarget target)
-> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto src_elem_types = sem_ir.type_blocks().Get(src_type.elements_id);
auto dest_elem_types = sem_ir.type_blocks().Get(dest_type.elements_id);
auto value = sem_ir.nodes().Get(value_id);
auto value = sem_ir.insts().Get(value_id);
// If we're initializing from a tuple literal, we will use its elements
// directly. Otherwise, materialize a temporary if needed and index into the
// result.
llvm::ArrayRef<SemIR::NodeId> literal_elems;
auto literal_elems_id = SemIR::NodeBlockId::Invalid;
llvm::ArrayRef<SemIR::InstId> literal_elems;
auto literal_elems_id = SemIR::InstBlockId::Invalid;
if (auto tuple_literal = value.TryAs<SemIR::TupleLiteral>()) {
literal_elems_id = tuple_literal->elements_id;
literal_elems = sem_ir.node_blocks().Get(literal_elems_id);
literal_elems = sem_ir.inst_blocks().Get(literal_elems_id);
} else {
value_id = MaterializeIfInitializing(context, value_id);
}
@@ -342,7 +342,7 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
size_t, size_t);
context.emitter().Emit(value.parse_node(), TupleInitElementCountMismatch,
dest_elem_types.size(), src_elem_types.size());
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// If we're forming an initializer, then we want an initializer for each
@@ -369,16 +369,16 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
ConvertAggregateElement<SemIR::TupleAccess, SemIR::TupleAccess>(
context, value.parse_node(), value_id, src_type_id, literal_elems,
inner_kind, target.init_id, dest_type_id, target.init_block, i);
if (init_id == SemIR::NodeId::BuiltinError) {
return SemIR::NodeId::BuiltinError;
if (init_id == SemIR::InstId::BuiltinError) {
return SemIR::InstId::BuiltinError;
}
new_block.Set(i, init_id);
}
return is_init ? context.AddNode(SemIR::TupleInit{value.parse_node(),
return is_init ? context.AddInst(SemIR::TupleInit{value.parse_node(),
target.type_id, value_id,
new_block.id()})
: context.AddNode(SemIR::TupleValue{value.parse_node(),
: context.AddInst(SemIR::TupleValue{value.parse_node(),
target.type_id, value_id,
new_block.id()});
}
@@ -387,22 +387,22 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
// final conversion to the requested expression category.
static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
SemIR::StructType dest_type,
SemIR::NodeId value_id,
ConversionTarget target) -> SemIR::NodeId {
SemIR::InstId value_id,
ConversionTarget target) -> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto src_elem_fields = sem_ir.node_blocks().Get(src_type.fields_id);
auto dest_elem_fields = sem_ir.node_blocks().Get(dest_type.fields_id);
auto src_elem_fields = sem_ir.inst_blocks().Get(src_type.fields_id);
auto dest_elem_fields = sem_ir.inst_blocks().Get(dest_type.fields_id);
auto value = sem_ir.nodes().Get(value_id);
auto value = sem_ir.insts().Get(value_id);
// If we're initializing from a struct literal, we will use its elements
// directly. Otherwise, materialize a temporary if needed and index into the
// result.
llvm::ArrayRef<SemIR::NodeId> literal_elems;
auto literal_elems_id = SemIR::NodeBlockId::Invalid;
llvm::ArrayRef<SemIR::InstId> literal_elems;
auto literal_elems_id = SemIR::InstBlockId::Invalid;
if (auto struct_literal = value.TryAs<SemIR::StructLiteral>()) {
literal_elems_id = struct_literal->elements_id;
literal_elems = sem_ir.node_blocks().Get(literal_elems_id);
literal_elems = sem_ir.inst_blocks().Get(literal_elems_id);
} else {
value_id = MaterializeIfInitializing(context, value_id);
}
@@ -417,7 +417,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
size_t, size_t);
context.emitter().Emit(value.parse_node(), StructInitElementCountMismatch,
dest_elem_fields.size(), src_elem_fields.size());
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// Prepare to look up fields in the source by index.
@@ -425,7 +425,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
if (src_type.fields_id != dest_type.fields_id) {
for (auto [i, field_id] : llvm::enumerate(src_elem_fields)) {
auto [it, added] = src_field_indexes.insert(
{context.nodes().GetAs<SemIR::StructTypeField>(field_id).name_id, i});
{context.insts().GetAs<SemIR::StructTypeField>(field_id).name_id, i});
CARBON_CHECK(added) << "Duplicate field in source structure";
}
}
@@ -448,7 +448,7 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
CopyOnWriteBlock new_block(sem_ir, literal_elems_id, src_elem_fields.size());
for (auto [i, dest_field_id] : llvm::enumerate(dest_elem_fields)) {
auto dest_field =
sem_ir.nodes().GetAs<SemIR::StructTypeField>(dest_field_id);
sem_ir.insts().GetAs<SemIR::StructTypeField>(dest_field_id);
// Find the matching source field.
auto src_field_index = i;
@@ -474,11 +474,11 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
sem_ir.StringifyType(target.type_id),
sem_ir.strings().Get(dest_field.name_id));
}
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
src_field_index = src_field_it->second;
}
auto src_field = sem_ir.nodes().GetAs<SemIR::StructTypeField>(
auto src_field = sem_ir.insts().GetAs<SemIR::StructTypeField>(
src_elem_fields[src_field_index]);
// TODO: This call recurses back into conversion. Switch to an iterative
@@ -488,16 +488,16 @@ static auto ConvertStructToStruct(Context& context, SemIR::StructType src_type,
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, src_field_index);
if (init_id == SemIR::NodeId::BuiltinError) {
return SemIR::NodeId::BuiltinError;
if (init_id == SemIR::InstId::BuiltinError) {
return SemIR::InstId::BuiltinError;
}
new_block.Set(i, init_id);
}
return is_init ? context.AddNode(SemIR::StructInit{value.parse_node(),
return is_init ? context.AddInst(SemIR::StructInit{value.parse_node(),
target.type_id, value_id,
new_block.id()})
: context.AddNode(SemIR::StructValue{value.parse_node(),
: context.AddInst(SemIR::StructValue{value.parse_node(),
target.type_id, value_id,
new_block.id()});
}
@@ -525,13 +525,13 @@ static bool IsValidExpressionCategoryForConversionTarget(
}
static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id,
ConversionTarget target) -> SemIR::NodeId {
SemIR::InstId value_id,
ConversionTarget target) -> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto value = sem_ir.nodes().Get(value_id);
auto value = sem_ir.insts().Get(value_id);
auto value_type_id = value.type_id();
auto target_type_node =
sem_ir.nodes().Get(sem_ir.GetTypeAllowBuiltinTypes(target.type_id));
auto target_type_inst =
sem_ir.insts().Get(sem_ir.GetTypeAllowBuiltinTypes(target.type_id));
// Various forms of implicit conversion are supported as builtin conversions,
// either in addition to or instead of `impl`s of `ImplicitAs` in the Carbon
@@ -582,7 +582,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// The initializer produces an object representation by copy, and the
// value representation is a copy of the object representation, so we
// already have a value of the right form.
return context.AddNode(
return context.AddInst(
SemIR::ValueOfInitializer{parse_node, value_type_id, value_id});
}
}
@@ -590,10 +590,10 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// A tuple (T1, T2, ..., Tn) converts to (U1, U2, ..., Un) if each Ti
// converts to Ui.
if (auto target_tuple_type = target_type_node.TryAs<SemIR::TupleType>()) {
auto value_type_node =
sem_ir.nodes().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_tuple_type = value_type_node.TryAs<SemIR::TupleType>()) {
if (auto target_tuple_type = target_type_inst.TryAs<SemIR::TupleType>()) {
auto value_type_inst =
sem_ir.insts().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_tuple_type = value_type_inst.TryAs<SemIR::TupleType>()) {
return ConvertTupleToTuple(context, *src_tuple_type, *target_tuple_type,
value_id, target);
}
@@ -603,20 +603,20 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// {.f_p(1): U_p(1), .f_p(2): U_p(2), ..., .f_p(n): U_p(n)} if
// (p(1), ..., p(n)) is a permutation of (1, ..., n) and each Ti converts
// to Ui.
if (auto target_struct_type = target_type_node.TryAs<SemIR::StructType>()) {
auto value_type_node =
sem_ir.nodes().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_struct_type = value_type_node.TryAs<SemIR::StructType>()) {
if (auto target_struct_type = target_type_inst.TryAs<SemIR::StructType>()) {
auto value_type_inst =
sem_ir.insts().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_struct_type = value_type_inst.TryAs<SemIR::StructType>()) {
return ConvertStructToStruct(context, *src_struct_type,
*target_struct_type, value_id, target);
}
}
// A tuple (T1, T2, ..., Tn) converts to [T; n] if each Ti converts to T.
if (auto target_array_type = target_type_node.TryAs<SemIR::ArrayType>()) {
auto value_type_node =
sem_ir.nodes().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_tuple_type = value_type_node.TryAs<SemIR::TupleType>()) {
if (auto target_array_type = target_type_inst.TryAs<SemIR::ArrayType>()) {
auto value_type_inst =
sem_ir.insts().Get(sem_ir.GetTypeAllowBuiltinTypes(value_type_id));
if (auto src_tuple_type = value_type_inst.TryAs<SemIR::TupleType>()) {
return ConvertTupleToArray(context, *src_tuple_type, *target_array_type,
value_id, target);
}
@@ -627,12 +627,12 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// TODO: This should apply even for non-literal tuples.
if (auto tuple_literal = value.TryAs<SemIR::TupleLiteral>()) {
llvm::SmallVector<SemIR::TypeId> type_ids;
for (auto tuple_node_id :
sem_ir.node_blocks().Get(tuple_literal->elements_id)) {
for (auto tuple_inst_id :
sem_ir.inst_blocks().Get(tuple_literal->elements_id)) {
// TODO: This call recurses back into conversion. Switch to an
// iterative approach.
type_ids.push_back(
ExpressionAsType(context, parse_node, tuple_node_id));
ExpressionAsType(context, parse_node, tuple_inst_id));
}
auto tuple_type_id =
context.CanonicalizeTupleType(parse_node, std::move(type_ids));
@@ -644,7 +644,7 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// of type `{}`.
if (auto struct_literal = value.TryAs<SemIR::StructLiteral>();
struct_literal &&
struct_literal->elements_id == SemIR::NodeBlockId::Empty) {
struct_literal->elements_id == SemIR::InstBlockId::Empty) {
value_id = sem_ir.GetTypeAllowBuiltinTypes(value_type_id);
}
}
@@ -655,12 +655,12 @@ static auto PerformBuiltinConversion(Context& context, Parse::Node parse_node,
// Given a value expression, form a corresponding initializer that copies from
// that value, if it is possible to do so.
static auto PerformCopy(Context& context, SemIR::NodeId expr_id)
-> SemIR::NodeId {
auto expr = context.nodes().Get(expr_id);
static auto PerformCopy(Context& context, SemIR::InstId expr_id)
-> SemIR::InstId {
auto expr = context.insts().Get(expr_id);
auto type_id = expr.type_id();
if (type_id == SemIR::TypeId::Error) {
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// TODO: Directly track on the value representation whether it's a copy of
@@ -680,19 +680,19 @@ static auto PerformCopy(Context& context, SemIR::NodeId expr_id)
"Cannot copy value of type `{0}`.", std::string);
context.emitter().Emit(expr.parse_node(), CopyOfUncopyableType,
context.sem_ir().StringifyType(type_id));
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
ConversionTarget target) -> SemIR::NodeId {
auto Convert(Context& context, Parse::Node parse_node, SemIR::InstId expr_id,
ConversionTarget target) -> SemIR::InstId {
auto& sem_ir = context.sem_ir();
auto orig_expr_id = expr_id;
// Start by making sure both sides are valid. If any part is invalid, the
// result is invalid and we shouldn't error.
if (sem_ir.nodes().Get(expr_id).type_id() == SemIR::TypeId::Error ||
if (sem_ir.insts().Get(expr_id).type_id() == SemIR::TypeId::Error ||
target.type_id == SemIR::TypeId::Error) {
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
if (SemIR::GetExpressionCategory(sem_ir, expr_id) ==
@@ -702,9 +702,9 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
// namespace names, and allow use of functions as values.
CARBON_DIAGNOSTIC(UseOfNonExpressionAsValue, Error,
"Expression cannot be used as a value.");
context.emitter().Emit(sem_ir.nodes().Get(expr_id).parse_node(),
context.emitter().Emit(sem_ir.insts().Get(expr_id).parse_node(),
UseOfNonExpressionAsValue);
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// We can only perform initialization for complete types.
@@ -725,19 +725,19 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
: IncompleteTypeInConversion,
context.sem_ir().StringifyType(target.type_id, true));
})) {
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// Check whether any builtin conversion applies.
expr_id = PerformBuiltinConversion(context, parse_node, expr_id, target);
if (expr_id == SemIR::NodeId::BuiltinError) {
if (expr_id == SemIR::InstId::BuiltinError) {
return expr_id;
}
// If the types don't match at this point, we can't perform the conversion.
// TODO: Look for an `ImplicitAs` impl, or an `As` impl in the case where
// `target.kind == ConversionTarget::ExplicitAs`.
SemIR::Node expr = sem_ir.nodes().Get(expr_id);
SemIR::Inst expr = sem_ir.insts().Get(expr_id);
if (expr.type_id() != target.type_id) {
CARBON_DIAGNOSTIC(ImplicitAsConversionFailure, Error,
"Cannot implicitly convert from `{0}` to `{1}`.",
@@ -753,7 +753,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
sem_ir.StringifyType(expr.type_id()),
sem_ir.StringifyType(target.type_id))
.Emit();
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
// For `as`, don't perform any value category conversions. In particular, an
@@ -770,7 +770,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
<< " after builtin conversions";
case SemIR::ExpressionCategory::Error:
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
case SemIR::ExpressionCategory::Initializing:
if (target.is_initializer()) {
@@ -803,7 +803,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
// If we have a reference and don't want one, form a value binding.
// TODO: Support types with custom value representations.
expr_id = context.AddNode(
expr_id = context.AddInst(
SemIR::BindValue{expr.parse_node(), expr.type_id(), expr_id});
// We now have a value expression.
[[fallthrough]];
@@ -822,7 +822,7 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
SemIR::GetInitializingRepresentation(sem_ir, target.type_id);
init_rep.kind == SemIR::InitializingRepresentation::ByCopy) {
target.init_block->InsertHere();
expr_id = context.AddNode(SemIR::InitializeFrom{
expr_id = context.AddInst(SemIR::InitializeFrom{
parse_node, target.type_id, expr_id, target.init_id});
}
}
@@ -831,49 +831,49 @@ auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId expr_id,
}
auto Initialize(Context& context, Parse::Node parse_node,
SemIR::NodeId target_id, SemIR::NodeId value_id)
-> SemIR::NodeId {
SemIR::InstId target_id, SemIR::InstId value_id)
-> SemIR::InstId {
PendingBlock target_block(context);
return Convert(context, parse_node, value_id,
{.kind = ConversionTarget::Initializer,
.type_id = context.sem_ir().nodes().Get(target_id).type_id(),
.type_id = context.sem_ir().insts().Get(target_id).type_id(),
.init_id = target_id,
.init_block = &target_block});
}
auto ConvertToValueExpression(Context& context, SemIR::NodeId expr_id)
-> SemIR::NodeId {
auto expr = context.sem_ir().nodes().Get(expr_id);
auto ConvertToValueExpression(Context& context, SemIR::InstId expr_id)
-> SemIR::InstId {
auto expr = context.sem_ir().insts().Get(expr_id);
return Convert(context, expr.parse_node(), expr_id,
{.kind = ConversionTarget::Value, .type_id = expr.type_id()});
}
auto ConvertToValueOrReferenceExpression(Context& context,
SemIR::NodeId expr_id)
-> SemIR::NodeId {
auto expr = context.sem_ir().nodes().Get(expr_id);
SemIR::InstId expr_id)
-> SemIR::InstId {
auto expr = context.sem_ir().insts().Get(expr_id);
return Convert(
context, expr.parse_node(), expr_id,
{.kind = ConversionTarget::ValueOrReference, .type_id = expr.type_id()});
}
auto ConvertToValueOfType(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id, SemIR::TypeId type_id)
-> SemIR::NodeId {
SemIR::InstId value_id, SemIR::TypeId type_id)
-> SemIR::InstId {
return Convert(context, parse_node, value_id,
{.kind = ConversionTarget::Value, .type_id = type_id});
}
auto ConvertToBoolValue(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id) -> SemIR::NodeId {
SemIR::InstId value_id) -> SemIR::InstId {
return ConvertToValueOfType(
context, parse_node, value_id,
context.GetBuiltinType(SemIR::BuiltinKind::BoolType));
}
auto ConvertForExplicitAs(Context& context, Parse::Node as_node,
SemIR::NodeId value_id, SemIR::TypeId type_id)
-> SemIR::NodeId {
SemIR::InstId value_id, SemIR::TypeId type_id)
-> SemIR::InstId {
return Convert(context, as_node, value_id,
{.kind = ConversionTarget::ExplicitAs, .type_id = type_id});
}
@@ -883,8 +883,8 @@ CARBON_DIAGNOSTIC(InCallToFunction, Note, "Calling function declared here.");
// Convert the object argument in a method call to match the `self` parameter.
static auto ConvertSelf(Context& context, Parse::Node call_parse_node,
Parse::Node callee_parse_node,
SemIR::SelfParameter self_param, SemIR::NodeId self_id)
-> SemIR::NodeId {
SemIR::SelfParameter self_param, SemIR::InstId self_id)
-> SemIR::InstId {
if (!self_id.is_valid()) {
CARBON_DIAGNOSTIC(MissingObjectInMethodCall, Error,
"Missing object argument in method call.");
@@ -892,7 +892,7 @@ static auto ConvertSelf(Context& context, Parse::Node call_parse_node,
.Build(call_parse_node, MissingObjectInMethodCall)
.Note(callee_parse_node, InCallToFunction)
.Emit();
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
DiagnosticAnnotationScope annotate_diagnostics(
@@ -912,7 +912,7 @@ static auto ConvertSelf(Context& context, Parse::Node call_parse_node,
if (self_param.is_addr_self.index) {
self_or_addr_id =
ConvertToValueOrReferenceExpression(context, self_or_addr_id);
auto self = context.nodes().Get(self_or_addr_id);
auto self = context.insts().Get(self_or_addr_id);
switch (SemIR::GetExpressionCategory(context.sem_ir(), self_id)) {
case SemIR::ExpressionCategory::Error:
case SemIR::ExpressionCategory::DurableReference:
@@ -922,9 +922,9 @@ static auto ConvertSelf(Context& context, Parse::Node call_parse_node,
CARBON_DIAGNOSTIC(AddrSelfIsNonReference, Error,
"`addr self` method cannot be invoked on a value.");
context.emitter().Emit(call_parse_node, AddrSelfIsNonReference);
return SemIR::NodeId::BuiltinError;
return SemIR::InstId::BuiltinError;
}
self_or_addr_id = context.AddNode(SemIR::AddressOf{
self_or_addr_id = context.AddInst(SemIR::AddressOf{
self.parse_node(),
context.GetPointerType(self.parse_node(), self.type_id()),
self_or_addr_id});
@@ -935,15 +935,15 @@ static auto ConvertSelf(Context& context, Parse::Node call_parse_node,
}
auto ConvertCallArgs(Context& context, Parse::Node call_parse_node,
SemIR::NodeId self_id,
llvm::ArrayRef<SemIR::NodeId> arg_refs,
SemIR::NodeId return_storage_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_refs,
SemIR::InstId return_storage_id,
Parse::Node callee_parse_node,
SemIR::NodeBlockId implicit_param_refs_id,
SemIR::NodeBlockId param_refs_id) -> SemIR::NodeBlockId {
SemIR::InstBlockId implicit_param_refs_id,
SemIR::InstBlockId param_refs_id) -> SemIR::InstBlockId {
auto implicit_param_refs =
context.sem_ir().node_blocks().Get(implicit_param_refs_id);
auto param_refs = context.sem_ir().node_blocks().Get(param_refs_id);
context.sem_ir().inst_blocks().Get(implicit_param_refs_id);
auto param_refs = context.sem_ir().inst_blocks().Get(param_refs_id);
// If sizes mismatch, fail early.
if (arg_refs.size() != param_refs.size()) {
@@ -956,28 +956,28 @@ auto ConvertCallArgs(Context& context, Parse::Node call_parse_node,
param_refs.size())
.Note(callee_parse_node, InCallToFunction)
.Emit();
return SemIR::NodeBlockId::Invalid;
return SemIR::InstBlockId::Invalid;
}
// Start building a block to hold the converted arguments.
llvm::SmallVector<SemIR::NodeId> args;
llvm::SmallVector<SemIR::InstId> args;
args.reserve(implicit_param_refs.size() + param_refs.size() +
return_storage_id.is_valid());
// Check implicit parameters.
for (auto implicit_param_id : implicit_param_refs) {
auto param = context.nodes().Get(implicit_param_id);
auto param = context.insts().Get(implicit_param_id);
if (auto self_param = param.TryAs<SemIR::SelfParameter>()) {
auto converted_self_id = ConvertSelf(
context, call_parse_node, callee_parse_node, *self_param, self_id);
if (converted_self_id == SemIR::NodeId::BuiltinError) {
return SemIR::NodeBlockId::Invalid;
if (converted_self_id == SemIR::InstId::BuiltinError) {
return SemIR::InstBlockId::Invalid;
}
args.push_back(converted_self_id);
} else {
// TODO: Form argument values for implicit parameters.
context.TODO(call_parse_node, "Call with implicit parameters");
return SemIR::NodeBlockId::Invalid;
return SemIR::InstBlockId::Invalid;
}
}
@@ -995,13 +995,13 @@ auto ConvertCallArgs(Context& context, Parse::Node call_parse_node,
for (auto [i, arg_id, param_id] : llvm::enumerate(arg_refs, param_refs)) {
diag_param_index = i;
auto param_type_id = context.sem_ir().nodes().Get(param_id).type_id();
auto param_type_id = context.sem_ir().insts().Get(param_id).type_id();
// TODO: Convert to the proper expression category. For now, we assume
// parameters are all `let` bindings.
auto converted_arg_id =
ConvertToValueOfType(context, call_parse_node, arg_id, param_type_id);
if (converted_arg_id == SemIR::NodeId::BuiltinError) {
return SemIR::NodeBlockId::Invalid;
if (converted_arg_id == SemIR::InstId::BuiltinError) {
return SemIR::InstBlockId::Invalid;
}
args.push_back(converted_arg_id);
@@ -1012,11 +1012,11 @@ auto ConvertCallArgs(Context& context, Parse::Node call_parse_node,
args.push_back(return_storage_id);
}
return context.node_blocks().Add(args);
return context.inst_blocks().Add(args);
}
auto ExpressionAsType(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id) -> SemIR::TypeId {
SemIR::InstId value_id) -> SemIR::TypeId {
return context.CanonicalizeType(ConvertToValueOfType(
context, parse_node, value_id, SemIR::TypeId::TypeType));
}
+21 -21
View File
@@ -8,7 +8,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/pending_block.h"
#include "toolchain/parse/tree.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -39,7 +39,7 @@ struct ConversionTarget {
// The target type for the conversion.
SemIR::TypeId type_id;
// For an initializer, the object being initialized.
SemIR::NodeId init_id = SemIR::NodeId::Invalid;
SemIR::InstId init_id = SemIR::InstId::Invalid;
// For an initializer, a block of pending instructions that are needed to
// form the value of `target_id`, and that can be discarded if no
// initialization is needed.
@@ -52,54 +52,54 @@ struct ConversionTarget {
};
// Convert a value to another type and expression category.
auto Convert(Context& context, Parse::Node parse_node, SemIR::NodeId value_id,
ConversionTarget target) -> SemIR::NodeId;
auto Convert(Context& context, Parse::Node parse_node, SemIR::InstId value_id,
ConversionTarget target) -> SemIR::InstId;
// Performs initialization of `target_id` from `value_id`. Returns the
// possibly-converted initializing expression, which should be assigned to the
// target using a suitable node for the kind of initialization.
auto Initialize(Context& context, Parse::Node parse_node,
SemIR::NodeId target_id, SemIR::NodeId value_id)
-> SemIR::NodeId;
SemIR::InstId target_id, SemIR::InstId value_id)
-> SemIR::InstId;
// Convert the given expression to a value expression of the same type.
auto ConvertToValueExpression(Context& context, SemIR::NodeId expr_id)
-> SemIR::NodeId;
auto ConvertToValueExpression(Context& context, SemIR::InstId expr_id)
-> SemIR::InstId;
// Convert the given expression to a value or reference expression of the same
// type.
auto ConvertToValueOrReferenceExpression(Context& context,
SemIR::NodeId expr_id)
-> SemIR::NodeId;
SemIR::InstId expr_id)
-> SemIR::InstId;
// Converts `value_id` to a value expression of type `type_id`.
auto ConvertToValueOfType(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id, SemIR::TypeId type_id)
-> SemIR::NodeId;
SemIR::InstId value_id, SemIR::TypeId type_id)
-> SemIR::InstId;
// Converts `value_id` to a value expression of type `bool`.
auto ConvertToBoolValue(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id) -> SemIR::NodeId;
SemIR::InstId value_id) -> SemIR::InstId;
// Converts `value_id` to type `type_id` for an `as` expression.
auto ConvertForExplicitAs(Context& context, Parse::Node as_node,
SemIR::NodeId value_id, SemIR::TypeId type_id)
-> SemIR::NodeId;
SemIR::InstId value_id, SemIR::TypeId type_id)
-> SemIR::InstId;
// Implicitly converts a set of arguments to match the parameter types in a
// function call. Returns a block containing the converted implicit and explicit
// argument values.
auto ConvertCallArgs(Context& context, Parse::Node call_parse_node,
SemIR::NodeId self_id,
llvm::ArrayRef<SemIR::NodeId> arg_refs,
SemIR::NodeId return_storage_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_refs,
SemIR::InstId return_storage_id,
Parse::Node callee_parse_node,
SemIR::NodeBlockId implicit_param_refs_id,
SemIR::NodeBlockId param_refs_id) -> SemIR::NodeBlockId;
SemIR::InstBlockId implicit_param_refs_id,
SemIR::InstBlockId param_refs_id) -> SemIR::InstBlockId;
// Converts an expression for use as a type.
auto ExpressionAsType(Context& context, Parse::Node parse_node,
SemIR::NodeId value_id) -> SemIR::TypeId;
SemIR::InstId value_id) -> SemIR::TypeId;
} // namespace Carbon::Check
+23 -22
View File
@@ -43,12 +43,12 @@ auto DeclarationNameStack::Pop() -> NameContext {
}
auto DeclarationNameStack::LookupOrAddName(NameContext name_context,
SemIR::NodeId target_id)
-> SemIR::NodeId {
SemIR::InstId target_id)
-> SemIR::InstId {
switch (name_context.state) {
case NameContext::State::Error:
// The name is invalid and a diagnostic has already been emitted.
return SemIR::NodeId::Invalid;
return SemIR::InstId::Invalid;
case NameContext::State::Empty:
CARBON_FATAL() << "Name is missing, not expected to call AddNameToLookup "
@@ -56,7 +56,7 @@ auto DeclarationNameStack::LookupOrAddName(NameContext name_context,
case NameContext::State::Resolved:
case NameContext::State::ResolvedNonScope:
return name_context.resolved_node_id;
return name_context.resolved_inst_id;
case NameContext::State::Unresolved:
if (name_context.target_scope_id == SemIR::NameScopeId::Invalid) {
@@ -73,15 +73,15 @@ auto DeclarationNameStack::LookupOrAddName(NameContext name_context,
<< name_context.unresolved_name_id << " in "
<< name_context.target_scope_id;
}
return SemIR::NodeId::Invalid;
return SemIR::InstId::Invalid;
}
}
auto DeclarationNameStack::AddNameToLookup(NameContext name_context,
SemIR::NodeId target_id) -> void {
auto existing_node_id = LookupOrAddName(name_context, target_id);
if (existing_node_id.is_valid()) {
context_->DiagnoseDuplicateName(name_context.parse_node, existing_node_id);
SemIR::InstId target_id) -> void {
auto existing_inst_id = LookupOrAddName(name_context, target_id);
if (existing_inst_id.is_valid()) {
context_->DiagnoseDuplicateName(name_context.parse_node, existing_inst_id);
}
}
@@ -95,22 +95,23 @@ auto DeclarationNameStack::ApplyNameQualifierTo(NameContext& name_context,
StringId name_id) -> void {
if (CanResolveQualifier(name_context, parse_node)) {
// For identifier nodes, we need to perform a lookup on the identifier.
// This means the input node_id is actually a string ID.
// This means the input instruction name_id is actually a string ID.
//
// TODO: This doesn't perform the right kind of lookup. We will find names
// from enclosing lexical scopes here, in the case where `target_scope_id`
// is invalid.
auto resolved_node_id = context_->LookupName(
auto resolved_inst_id = context_->LookupName(
name_context.parse_node, name_id, name_context.target_scope_id,
/*print_diagnostics=*/false);
if (resolved_node_id == SemIR::NodeId::BuiltinError) {
// Invalid indicates an unresolved node. Store it and return.
if (resolved_inst_id == SemIR::InstId::BuiltinError) {
// Invalid indicates an unresolved instruction. Store it and return.
name_context.state = NameContext::State::Unresolved;
name_context.unresolved_name_id = name_id;
return;
} else {
// Store the resolved node and continue for the target scope update.
name_context.resolved_node_id = resolved_node_id;
// Store the resolved instruction and continue for the target scope
// update.
name_context.resolved_inst_id = resolved_inst_id;
}
UpdateScopeIfNeeded(name_context);
@@ -119,13 +120,13 @@ auto DeclarationNameStack::ApplyNameQualifierTo(NameContext& name_context,
auto DeclarationNameStack::UpdateScopeIfNeeded(NameContext& name_context)
-> void {
// This will only be reached for resolved nodes. We update the target
// This will only be reached for resolved instructions. We update the target
// scope based on the resolved type.
auto resolved_node = context_->nodes().Get(name_context.resolved_node_id);
switch (resolved_node.kind()) {
auto resolved_inst = context_->insts().Get(name_context.resolved_inst_id);
switch (resolved_inst.kind()) {
case SemIR::ClassDeclaration::Kind: {
const auto& class_info = context_->classes().Get(
resolved_node.As<SemIR::ClassDeclaration>().class_id);
resolved_inst.As<SemIR::ClassDeclaration>().class_id);
if (class_info.is_defined()) {
name_context.state = NameContext::State::Resolved;
name_context.target_scope_id = class_info.scope_id;
@@ -137,7 +138,7 @@ auto DeclarationNameStack::UpdateScopeIfNeeded(NameContext& name_context)
case SemIR::Namespace::Kind:
name_context.state = NameContext::State::Resolved;
name_context.target_scope_id =
resolved_node.As<SemIR::Namespace>().name_scope_id;
resolved_inst.As<SemIR::Namespace>().name_scope_id;
break;
default:
name_context.state = NameContext::State::ResolvedNonScope;
@@ -163,8 +164,8 @@ auto DeclarationNameStack::CanResolveQualifier(NameContext& name_context,
case NameContext::State::ResolvedNonScope: {
// Because more qualifiers were found, we diagnose that the earlier
// qualifier didn't resolve to a scoped entity.
if (auto class_decl = context_->nodes()
.Get(name_context.resolved_node_id)
if (auto class_decl = context_->insts()
.Get(name_context.resolved_inst_id)
.TryAs<SemIR::ClassDeclaration>()) {
CARBON_DIAGNOSTIC(QualifiedDeclarationInIncompleteClassScope, Error,
"Cannot declare a member of incomplete class `{0}`.",
+12 -12
View File
@@ -7,7 +7,7 @@
#include "llvm/ADT/SmallVector.h"
#include "toolchain/parse/tree.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -55,11 +55,11 @@ class DeclarationNameStack {
// A context that has not processed any parts of the qualifier.
Empty,
// A node ID has been resolved, whether through an identifier or
// An instruction ID has been resolved, whether through an identifier or
// expression. This provided a new scope, such as a type.
Resolved,
// A node ID has been resolved, whether through an identifier or
// An instruction ID has been resolved, whether through an identifier or
// expression. It did not provide a new scope, so must be the final part,
// such as an out-of-line function definition.
ResolvedNonScope,
@@ -85,7 +85,7 @@ class DeclarationNameStack {
union {
// The ID of a resolved qualifier, including both identifiers and
// expressions. Invalid indicates resolution failed.
SemIR::NodeId resolved_node_id = SemIR::NodeId::Invalid;
SemIR::InstId resolved_inst_id = SemIR::InstId::Invalid;
// The ID of an unresolved identifier.
StringId unresolved_name_id;
@@ -100,7 +100,7 @@ class DeclarationNameStack {
// Pops the current declaration name processing, returning the final context
// for adding the name to lookup. This also pops the final name node from the
// node stack, which will be applied to the declaration name if appropriate.
// name stack, which will be applied to the declaration name if appropriate.
auto Pop() -> NameContext;
// Creates and returns a name context corresponding to declaring an
@@ -110,24 +110,24 @@ class DeclarationNameStack {
auto MakeUnqualifiedName(Parse::Node parse_node, StringId name_id)
-> NameContext;
// Applies a Name from the node stack to the top of the declaration name
// Applies a Name from the name stack to the top of the declaration name
// stack.
auto ApplyNameQualifier(Parse::Node parse_node, StringId name_id) -> void;
// Adds a name to name lookup. Prints a diagnostic for name conflicts.
auto AddNameToLookup(NameContext name_context, SemIR::NodeId target_id)
auto AddNameToLookup(NameContext name_context, SemIR::InstId target_id)
-> void;
// Adds a name to name lookup, or returns the existing node if this name has
// already been declared in this scope.
auto LookupOrAddName(NameContext name_context, SemIR::NodeId target_id)
-> SemIR::NodeId;
// Adds a name to name lookup, or returns the existing instruction if this
// name has already been declared in this scope.
auto LookupOrAddName(NameContext name_context, SemIR::InstId target_id)
-> SemIR::InstId;
private:
// Returns a name context corresponding to an empty name.
auto MakeEmptyNameContext() -> NameContext;
// Applies a Name from the node stack to given name context.
// Applies a Name from the name stack to given name context.
auto ApplyNameQualifierTo(NameContext& name_context, Parse::Node parse_node,
StringId name_id) -> void;
+9 -9
View File
@@ -5,7 +5,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Check {
@@ -30,26 +30,26 @@ auto HandleArrayExpression(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "HandleArrayExpressionWithoutBounds");
}
auto bound_node_id = context.node_stack().PopExpression();
auto bound_inst_id = context.node_stack().PopExpression();
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::ArrayExpressionSemi>();
auto element_type_node_id = context.node_stack().PopExpression();
auto bound_node = context.nodes().Get(bound_node_id);
if (auto literal = bound_node.TryAs<SemIR::IntegerLiteral>()) {
auto element_type_inst_id = context.node_stack().PopExpression();
auto bound_inst = context.insts().Get(bound_inst_id);
if (auto literal = bound_inst.TryAs<SemIR::IntegerLiteral>()) {
const auto& bound_value = context.integers().Get(literal->integer_id);
// TODO: Produce an error if the array type is too large.
if (bound_value.getActiveBits() <= 64) {
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::ArrayType{
parse_node, SemIR::TypeId::TypeType, bound_node_id,
ExpressionAsType(context, parse_node, element_type_node_id)});
parse_node, SemIR::TypeId::TypeType, bound_inst_id,
ExpressionAsType(context, parse_node, element_type_inst_id)});
return true;
}
}
CARBON_DIAGNOSTIC(InvalidArrayExpression, Error, "Invalid array expression.");
context.emitter().Emit(parse_node, InvalidArrayExpression);
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
return true;
}
+10 -10
View File
@@ -5,7 +5,7 @@
#include "llvm/ADT/ScopeExit.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -22,7 +22,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool {
auto diagnose_not_callable = [&, call_expr_parse_node = call_expr_parse_node,
callee_id = callee_id] {
auto callee_type_id = context.nodes().Get(callee_id).type_id();
auto callee_type_id = context.insts().Get(callee_id).type_id();
if (callee_type_id != SemIR::TypeId::Error) {
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
"Value of type `{0}` is not callable.", std::string);
@@ -30,15 +30,15 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool {
call_expr_parse_node, CallToNonCallable,
context.sem_ir().StringifyType(callee_type_id, true));
}
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
return true;
};
// For a method call, pick out the `self` value.
auto function_callee_id = callee_id;
SemIR::NodeId self_id = SemIR::NodeId::Invalid;
SemIR::InstId self_id = SemIR::InstId::Invalid;
if (auto bound_method =
context.nodes().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
context.insts().Get(callee_id).TryAs<SemIR::BoundMethod>()) {
self_id = bound_method->object_id;
function_callee_id = bound_method->function_id;
}
@@ -49,7 +49,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool {
return diagnose_not_callable();
}
auto function_decl =
context.nodes().Get(function_decl_id).TryAs<SemIR::FunctionDeclaration>();
context.insts().Get(function_decl_id).TryAs<SemIR::FunctionDeclaration>();
if (!function_decl) {
return diagnose_not_callable();
}
@@ -64,11 +64,11 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool {
}
// If there is a return slot, build storage for the result.
SemIR::NodeId return_storage_id = SemIR::NodeId::Invalid;
SemIR::InstId return_storage_id = SemIR::InstId::Invalid;
if (callable.return_slot_id.is_valid()) {
// Tentatively put storage for a temporary in the function's return slot.
// This will be replaced if necessary when we perform initialization.
return_storage_id = context.AddNode(
return_storage_id = context.AddInst(
SemIR::TemporaryStorage{call_expr_parse_node, callable.return_type_id});
}
@@ -78,10 +78,10 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool {
context.params_or_args_stack().PeekCurrentBlockContents(),
return_storage_id, function_decl->parse_node,
callable.implicit_param_refs_id, callable.param_refs_id);
auto call_node_id = context.AddNode(
auto call_inst_id = context.AddInst(
SemIR::Call{call_expr_parse_node, type_id, callee_id, converted_args_id});
context.node_stack().Push(parse_node, call_node_id);
context.node_stack().Push(parse_node, call_inst_id);
return true;
}
+15 -14
View File
@@ -8,9 +8,9 @@
namespace Carbon::Check {
auto HandleClassIntroducer(Context& context, Parse::Node parse_node) -> bool {
// Create a node block to hold the nodes created as part of the class
// signature, such as generic parameters.
context.node_block_stack().Push();
// Create an instruction block to hold the instructions created as part of the
// class signature, such as generic parameters.
context.inst_block_stack().Push();
// Push the bracketing node.
context.node_stack().Push(parse_node);
// A name should always follow.
@@ -19,24 +19,24 @@ auto HandleClassIntroducer(Context& context, Parse::Node parse_node) -> bool {
}
static auto BuildClassDeclaration(Context& context)
-> std::tuple<SemIR::ClassId, SemIR::NodeId> {
-> std::tuple<SemIR::ClassId, SemIR::InstId> {
auto name_context = context.declaration_name_stack().Pop();
auto class_keyword =
context.node_stack()
.PopForSoloParseNode<Parse::NodeKind::ClassIntroducer>();
auto decl_block_id = context.node_block_stack().Pop();
auto decl_block_id = context.inst_block_stack().Pop();
// Add the class declaration.
auto class_decl = SemIR::ClassDeclaration{
class_keyword, SemIR::ClassId::Invalid, decl_block_id};
auto class_decl_id = context.AddNode(class_decl);
auto class_decl_id = context.AddInst(class_decl);
// Check whether this is a redeclaration.
auto existing_id = context.declaration_name_stack().LookupOrAddName(
name_context, class_decl_id);
if (existing_id.is_valid()) {
if (auto existing_class_decl =
context.nodes().Get(existing_id).TryAs<SemIR::ClassDeclaration>()) {
context.insts().Get(existing_id).TryAs<SemIR::ClassDeclaration>()) {
// This is a redeclaration of an existing class.
class_decl.class_id = existing_class_decl->class_id;
} else {
@@ -62,13 +62,13 @@ static auto BuildClassDeclaration(Context& context)
// Build the `Self` type.
auto& class_info = context.classes().Get(class_decl.class_id);
class_info.self_type_id =
context.CanonicalizeType(context.AddNode(SemIR::ClassType{
context.CanonicalizeType(context.AddInst(SemIR::ClassType{
class_keyword, context.GetBuiltinType(SemIR::BuiltinKind::TypeType),
class_decl.class_id}));
}
// Write the class ID into the ClassDeclaration.
context.nodes().Set(class_decl_id, class_decl);
context.insts().Set(class_decl_id, class_decl);
return {class_decl.class_id, class_decl_id};
}
@@ -93,7 +93,7 @@ auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node)
context.emitter()
.Build(parse_node, ClassRedefinition,
context.strings().Get(class_info.name_id))
.Note(context.nodes().Get(class_info.definition_id).parse_node(),
.Note(context.insts().Get(class_info.definition_id).parse_node(),
ClassPreviousDefinition)
.Emit();
} else {
@@ -114,7 +114,7 @@ auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node)
Lex::TokenKind::SelfTypeIdentifier.fixed_spelling()),
context.sem_ir().GetTypeAllowBuiltinTypes(class_info.self_type_id));
context.node_block_stack().Push();
context.inst_block_stack().Push();
context.node_stack().Push(parse_node, class_id);
context.args_type_info_stack().Push();
@@ -125,8 +125,9 @@ auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node)
// var v: if true then i32 else f64;
// }
//
// We may need to track a list of node blocks here, as we do for a function.
class_info.body_block_id = context.node_block_stack().PeekOrAdd();
// We may need to track a list of instruction blocks here, as we do for a
// function.
class_info.body_block_id = context.inst_block_stack().PeekOrAdd();
return true;
}
@@ -134,7 +135,7 @@ auto HandleClassDefinition(Context& context, Parse::Node parse_node) -> bool {
auto fields_id = context.args_type_info_stack().Pop();
auto class_id =
context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
context.PopScope();
// The class type is now fully defined.
+27 -27
View File
@@ -12,7 +12,7 @@ namespace Carbon::Check {
// handles the common logic shared by function declaration syntax and function
// definition syntax.
static auto BuildFunctionDeclaration(Context& context, bool is_definition)
-> std::pair<SemIR::FunctionId, SemIR::NodeId> {
-> std::pair<SemIR::FunctionId, SemIR::InstId> {
// TODO: This contains the IR block for the parameters and return type. At
// present, it's just loose, but it's not strictly required for parameter
// refs; we should either stop constructing it completely or, if it turns out
@@ -20,16 +20,16 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition)
// nowhere clear to emit, so changing storage would require addressing that
// problem. For comparison with function calls, the IR needs to be emitted
// prior to the call.
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
auto return_type_id = SemIR::TypeId::Invalid;
auto return_slot_id = SemIR::NodeId::Invalid;
auto return_slot_id = SemIR::InstId::Invalid;
if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) ==
Parse::NodeKind::ReturnType) {
auto [return_node, return_storage_id] =
context.node_stack().PopWithParseNode<Parse::NodeKind::ReturnType>();
auto return_node_copy = return_node;
return_type_id = context.nodes().Get(return_storage_id).type_id();
return_type_id = context.insts().Get(return_storage_id).type_id();
if (!context.TryToCompleteType(return_type_id, [&] {
CARBON_DIAGNOSTIC(IncompleteTypeInFunctionReturnType, Error,
@@ -49,12 +49,12 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition)
}
}
SemIR::NodeBlockId param_refs_id =
SemIR::InstBlockId param_refs_id =
context.node_stack().Pop<Parse::NodeKind::ParameterList>();
SemIR::NodeBlockId implicit_param_refs_id =
SemIR::InstBlockId implicit_param_refs_id =
context.node_stack()
.PopIf<Parse::NodeKind::ImplicitParameterList>()
.value_or(SemIR::NodeBlockId::Empty);
.value_or(SemIR::InstBlockId::Empty);
auto name_context = context.declaration_name_stack().Pop();
auto fn_node =
context.node_stack()
@@ -64,14 +64,14 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition)
auto function_decl = SemIR::FunctionDeclaration{
fn_node, context.GetBuiltinType(SemIR::BuiltinKind::FunctionType),
SemIR::FunctionId::Invalid};
auto function_decl_id = context.AddNode(function_decl);
auto function_decl_id = context.AddInst(function_decl);
// Check whether this is a redeclaration.
auto existing_id = context.declaration_name_stack().LookupOrAddName(
name_context, function_decl_id);
if (existing_id.is_valid()) {
if (auto existing_function_decl =
context.nodes()
context.insts()
.Get(existing_id)
.TryAs<SemIR::FunctionDeclaration>()) {
// This is a redeclaration of an existing function.
@@ -109,12 +109,12 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition)
}
// Write the function ID into the FunctionDeclaration.
context.nodes().Set(function_decl_id, function_decl);
context.insts().Set(function_decl_id, function_decl);
if (SemIR::IsEntryPoint(context.sem_ir(), function_decl.function_id)) {
// TODO: Update this once valid signatures for the entry point are decided.
if (!context.node_blocks().Get(implicit_param_refs_id).empty() ||
!context.node_blocks().Get(param_refs_id).empty() ||
if (!context.inst_blocks().Get(implicit_param_refs_id).empty() ||
!context.inst_blocks().Get(param_refs_id).empty() ||
(return_slot_id.is_valid() &&
return_type_id !=
context.GetBuiltinType(SemIR::BuiltinKind::BoolType) &&
@@ -149,12 +149,12 @@ 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.AddInst(SemIR::Return{parse_node});
}
}
context.PopScope();
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
context.return_scope_stack().pop_back();
return true;
}
@@ -175,7 +175,7 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node)
context.emitter()
.Build(parse_node, FunctionRedefinition,
context.strings().Get(function.name_id))
.Note(context.nodes().Get(function.definition_id).parse_node(),
.Note(context.insts().Get(function.definition_id).parse_node(),
FunctionPreviousDefinition)
.Emit();
} else {
@@ -184,15 +184,15 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node)
// Create the function scope and the entry block.
context.return_scope_stack().push_back(decl_id);
context.node_block_stack().Push();
context.inst_block_stack().Push();
context.PushScope(decl_id);
context.AddCurrentCodeBlockToFunction();
// Bring the implicit and explicit parameters into scope.
for (auto param_id : llvm::concat<SemIR::NodeId>(
context.node_blocks().Get(function.implicit_param_refs_id),
context.node_blocks().Get(function.param_refs_id))) {
auto param = context.nodes().Get(param_id);
for (auto param_id : llvm::concat<SemIR::InstId>(
context.inst_blocks().Get(function.implicit_param_refs_id),
context.inst_blocks().Get(function.param_refs_id))) {
auto param = context.insts().Get(param_id);
// The parameter types need to be complete.
context.TryToCompleteType(param.type_id(), [&] {
@@ -227,9 +227,9 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node)
auto HandleFunctionIntroducer(Context& context, Parse::Node parse_node)
-> bool {
// Create a node block to hold the nodes created as part of the function
// signature, such as parameter and return types.
context.node_block_stack().Push();
// Create an instruction block to hold the instructions created as part of the
// function signature, such as parameter and return types.
context.inst_block_stack().Push();
// Push the bracketing node.
context.node_stack().Push(parse_node);
// A name should always follow.
@@ -239,11 +239,11 @@ auto HandleFunctionIntroducer(Context& context, Parse::Node parse_node)
auto HandleReturnType(Context& context, Parse::Node parse_node) -> bool {
// Propagate the type expression.
auto [type_parse_node, type_node_id] =
auto [type_parse_node, type_inst_id] =
context.node_stack().PopExpressionWithParseNode();
auto type_id = ExpressionAsType(context, type_parse_node, type_node_id);
// TODO: Use a dedicated node rather than VarStorage here.
context.AddNodeAndPush(
auto type_id = ExpressionAsType(context, type_parse_node, type_inst_id);
// TODO: Use a dedicated instruction rather than VarStorage here.
context.AddInstAndPush(
parse_node,
SemIR::VarStorage{parse_node, type_id, context.strings().Add("return")});
return true;
+4 -4
View File
@@ -20,8 +20,8 @@ auto HandleIfExpressionIf(Context& context, Parse::Node parse_node) -> bool {
auto else_block_id = context.AddDominatedBlockAndBranch(if_node);
// Start emitting the `then` block.
context.node_block_stack().Pop();
context.node_block_stack().Push(then_block_id);
context.inst_block_stack().Pop();
context.inst_block_stack().Push(then_block_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(if_node, else_block_id);
@@ -37,7 +37,7 @@ auto HandleIfExpressionThen(Context& context, Parse::Node parse_node) -> bool {
then_value_id = ConvertToValueExpression(context, then_value_id);
// Start emitting the `else` block.
context.node_block_stack().Push(else_block_id);
context.inst_block_stack().Push(else_block_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(parse_node, then_value_id);
@@ -57,7 +57,7 @@ auto HandleIfExpressionElse(Context& context, Parse::Node parse_node) -> bool {
// Convert the `else` value to the `then` value's type, and finish the `else`
// block.
// TODO: Find a common type, and convert both operands to it instead.
auto result_type_id = context.nodes().Get(then_value_id).type_id();
auto result_type_id = context.insts().Get(then_value_id).type_id();
else_value_id =
ConvertToValueOfType(context, else_node, else_value_id, result_type_id);
+7 -7
View File
@@ -4,7 +4,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -26,8 +26,8 @@ auto HandleIfCondition(Context& context, Parse::Node parse_node) -> bool {
auto else_block_id = context.AddDominatedBlockAndBranch(parse_node);
// Start emitting the `then` block.
context.node_block_stack().Pop();
context.node_block_stack().Push(then_block_id);
context.inst_block_stack().Pop();
context.inst_block_stack().Push(then_block_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(parse_node, else_block_id);
@@ -38,7 +38,7 @@ auto HandleIfStatementElse(Context& context, Parse::Node parse_node) -> bool {
auto else_block_id = context.node_stack().Pop<Parse::NodeKind::IfCondition>();
// Switch to emitting the `else` block.
context.node_block_stack().Push(else_block_id);
context.inst_block_stack().Push(else_block_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(parse_node);
@@ -53,9 +53,9 @@ 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.node_block_stack().Pop();
context.node_block_stack().Push(else_block_id);
context.AddInst(SemIR::Branch{parse_node, else_block_id});
context.inst_block_stack().Pop();
context.inst_block_stack().Push(else_block_id);
break;
}
+33 -33
View File
@@ -5,7 +5,7 @@
#include "llvm/ADT/APSInt.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Check {
@@ -20,10 +20,10 @@ auto HandleIndexExpressionStart(Context& /*context*/,
// the array or tuple size. Returns the index on success, or nullptr on failure.
static auto ValidateIntegerLiteralBound(Context& context,
Parse::Node parse_node,
SemIR::Node operand_node,
SemIR::IntegerLiteral index_node,
SemIR::Inst operand_inst,
SemIR::IntegerLiteral index_inst,
int size) -> const llvm::APInt* {
const auto& index_val = context.integers().Get(index_node.integer_id);
const auto& index_val = context.integers().Get(index_inst.integer_id);
if (index_val.uge(size)) {
CARBON_DIAGNOSTIC(IndexOutOfBounds, Error,
"Index `{0}` is past the end of `{1}`.", llvm::APSInt,
@@ -31,49 +31,49 @@ static auto ValidateIntegerLiteralBound(Context& context,
context.emitter().Emit(
parse_node, IndexOutOfBounds,
llvm::APSInt(index_val, /*isUnsigned=*/true),
context.sem_ir().StringifyType(operand_node.type_id()));
context.sem_ir().StringifyType(operand_inst.type_id()));
return nullptr;
}
return &index_val;
}
auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool {
auto index_node_id = context.node_stack().PopExpression();
auto index_node = context.nodes().Get(index_node_id);
auto operand_node_id = context.node_stack().PopExpression();
operand_node_id =
ConvertToValueOrReferenceExpression(context, operand_node_id);
auto operand_node = context.nodes().Get(operand_node_id);
auto operand_type_id = operand_node.type_id();
auto operand_type_node = context.nodes().Get(
auto index_inst_id = context.node_stack().PopExpression();
auto index_inst = context.insts().Get(index_inst_id);
auto operand_inst_id = context.node_stack().PopExpression();
operand_inst_id =
ConvertToValueOrReferenceExpression(context, operand_inst_id);
auto operand_inst = context.insts().Get(operand_inst_id);
auto operand_type_id = operand_inst.type_id();
auto operand_type_inst = context.insts().Get(
context.sem_ir().GetTypeAllowBuiltinTypes(operand_type_id));
switch (operand_type_node.kind()) {
switch (operand_type_inst.kind()) {
case SemIR::ArrayType::Kind: {
auto array_type = operand_type_node.As<SemIR::ArrayType>();
auto array_type = operand_type_inst.As<SemIR::ArrayType>();
// We can check whether integers are in-bounds, although it doesn't affect
// the IR for an array.
if (auto index_literal = index_node.TryAs<SemIR::IntegerLiteral>();
if (auto index_literal = index_inst.TryAs<SemIR::IntegerLiteral>();
index_literal &&
!ValidateIntegerLiteralBound(
context, parse_node, operand_node, *index_literal,
context, parse_node, operand_inst, *index_literal,
context.sem_ir().GetArrayBoundValue(array_type.bound_id))) {
index_node_id = SemIR::NodeId::BuiltinError;
index_inst_id = SemIR::InstId::BuiltinError;
}
auto cast_index_id = ConvertToValueOfType(
context, index_node.parse_node(), index_node_id,
context, index_inst.parse_node(), index_inst_id,
context.GetBuiltinType(SemIR::BuiltinKind::IntegerType));
auto array_cat =
SemIR::GetExpressionCategory(context.sem_ir(), operand_node_id);
SemIR::GetExpressionCategory(context.sem_ir(), operand_inst_id);
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_inst_id = context.AddInst(SemIR::ValueAsReference{
parse_node, operand_type_id, operand_inst_id});
}
auto elem_id = context.AddNode(
auto elem_id = context.AddInst(
SemIR::ArrayIndex{parse_node, array_type.element_type_id,
operand_node_id, cast_index_id});
operand_inst_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.
@@ -86,25 +86,25 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool {
}
case SemIR::TupleType::Kind: {
SemIR::TypeId element_type_id = SemIR::TypeId::Error;
if (auto index_literal = index_node.TryAs<SemIR::IntegerLiteral>()) {
if (auto index_literal = index_inst.TryAs<SemIR::IntegerLiteral>()) {
auto type_block = context.type_blocks().Get(
operand_type_node.As<SemIR::TupleType>().elements_id);
operand_type_inst.As<SemIR::TupleType>().elements_id);
if (const auto* index_val = ValidateIntegerLiteralBound(
context, parse_node, operand_node, *index_literal,
context, parse_node, operand_inst, *index_literal,
type_block.size())) {
element_type_id = type_block[index_val->getZExtValue()];
} else {
index_node_id = SemIR::NodeId::BuiltinError;
index_inst_id = SemIR::InstId::BuiltinError;
}
} else if (index_node.type_id() != SemIR::TypeId::Error) {
} else if (index_inst.type_id() != SemIR::TypeId::Error) {
CARBON_DIAGNOSTIC(TupleIndexIntegerLiteral, Error,
"Tuples indices must be integer literals.");
context.emitter().Emit(parse_node, TupleIndexIntegerLiteral);
index_node_id = SemIR::NodeId::BuiltinError;
index_inst_id = SemIR::InstId::BuiltinError;
}
context.AddNodeAndPush(parse_node,
context.AddInstAndPush(parse_node,
SemIR::TupleIndex{parse_node, element_type_id,
operand_node_id, index_node_id});
operand_inst_id, index_inst_id});
return true;
}
default: {
@@ -114,7 +114,7 @@ auto HandleIndexExpression(Context& context, Parse::Node parse_node) -> bool {
context.emitter().Emit(parse_node, TypeNotIndexable,
context.sem_ir().StringifyType(operand_type_id));
}
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
return true;
}
}
+5 -5
View File
@@ -4,19 +4,19 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
auto HandleLetDeclaration(Context& context, Parse::Node parse_node) -> bool {
auto value_id = context.node_stack().PopExpression();
SemIR::NodeId pattern_id =
SemIR::InstId pattern_id =
context.node_stack().Pop<Parse::NodeKind::PatternBinding>();
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::LetIntroducer>();
// Convert the value to match the type of the pattern.
auto pattern = context.nodes().Get(pattern_id);
auto pattern = context.insts().Get(pattern_id);
value_id =
ConvertToValueOfType(context, parse_node, value_id, pattern.type_id());
@@ -27,8 +27,8 @@ auto HandleLetDeclaration(Context& context, Parse::Node parse_node) -> bool {
CARBON_CHECK(!bind_name.value_id.is_valid())
<< "Binding should not already have a value!";
bind_name.value_id = value_id;
context.nodes().Set(pattern_id, bind_name);
context.node_block_stack().AddNodeId(pattern_id);
context.insts().Set(pattern_id, bind_name);
context.inst_block_stack().AddInstId(pattern_id);
// Add the name of the binding to the current scope.
context.AddNameToLookup(pattern.parse_node(), bind_name.name_id, pattern_id);
+9 -9
View File
@@ -11,7 +11,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
switch (auto token_kind = context.tokens().GetKind(token)) {
case Lex::TokenKind::False:
case Lex::TokenKind::True: {
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::BoolLiteral{
parse_node, context.GetBuiltinType(SemIR::BuiltinKind::BoolType),
@@ -20,7 +20,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
break;
}
case Lex::TokenKind::IntegerLiteral: {
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::IntegerLiteral{
parse_node,
@@ -29,7 +29,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
break;
}
case Lex::TokenKind::RealLiteral: {
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::RealLiteral{
parse_node,
@@ -39,7 +39,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
}
case Lex::TokenKind::StringLiteral: {
auto id = context.tokens().GetStringLiteral(token);
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::StringLiteral{
parse_node,
@@ -47,11 +47,11 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
break;
}
case Lex::TokenKind::Type: {
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinTypeType);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinTypeType);
break;
}
case Lex::TokenKind::Bool: {
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinBoolType);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinBoolType);
break;
}
case Lex::TokenKind::IntegerTypeLiteral: {
@@ -59,7 +59,7 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
if (text != "i32") {
return context.TODO(parse_node, "Currently only i32 is allowed");
}
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinIntegerType);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinIntegerType);
break;
}
case Lex::TokenKind::FloatingPointTypeLiteral: {
@@ -68,11 +68,11 @@ auto HandleLiteral(Context& context, Parse::Node parse_node) -> bool {
return context.TODO(parse_node, "Currently only f64 is allowed");
}
context.node_stack().Push(parse_node,
SemIR::NodeId::BuiltinFloatingPointType);
SemIR::InstId::BuiltinFloatingPointType);
break;
}
case Lex::TokenKind::StringTypeLiteral: {
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinStringType);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinStringType);
break;
}
default: {
+13 -13
View File
@@ -20,11 +20,11 @@ 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.AddInst(SemIR::Branch{parse_node, stack.back().break_target});
}
context.node_block_stack().Pop();
context.node_block_stack().PushUnreachable();
context.inst_block_stack().Pop();
context.inst_block_stack().PushUnreachable();
return true;
}
@@ -41,11 +41,11 @@ 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.AddInst(SemIR::Branch{parse_node, stack.back().continue_target});
}
context.node_block_stack().Pop();
context.node_block_stack().PushUnreachable();
context.inst_block_stack().Pop();
context.inst_block_stack().PushUnreachable();
return true;
}
@@ -71,10 +71,10 @@ auto HandleWhileConditionStart(Context& context, Parse::Node parse_node)
// if the current block is empty; this ensures that the loop always has a
// preheader block.
auto loop_header_id = context.AddDominatedBlockAndBranch(parse_node);
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
// Start emitting the loop header block.
context.node_block_stack().Push(loop_header_id);
context.inst_block_stack().Push(loop_header_id);
context.AddCurrentCodeBlockToFunction();
context.node_stack().Push(parse_node, loop_header_id);
@@ -91,10 +91,10 @@ auto HandleWhileCondition(Context& context, Parse::Node parse_node) -> bool {
auto loop_body_id =
context.AddDominatedBlockAndBranchIf(parse_node, cond_value_id);
auto loop_exit_id = context.AddDominatedBlockAndBranch(parse_node);
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
// Start emitting the loop body.
context.node_block_stack().Push(loop_body_id);
context.inst_block_stack().Push(loop_body_id);
context.AddCurrentCodeBlockToFunction();
context.break_continue_stack().push_back(
{.break_target = loop_exit_id, .continue_target = loop_header_id});
@@ -111,11 +111,11 @@ 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.node_block_stack().Pop();
context.AddInst(SemIR::Branch{parse_node, loop_header_id});
context.inst_block_stack().Pop();
// Start emitting the loop exit block.
context.node_block_stack().Push(loop_exit_id);
context.inst_block_stack().Push(loop_exit_id);
context.AddCurrentCodeBlockToFunction();
return true;
}
+47 -46
View File
@@ -6,15 +6,15 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/lex/token_kind.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
// Returns the name scope corresponding to base_id, or nullopt if not a scope.
// On invalid scopes, prints a diagnostic and still returns the scope.
static auto GetAsNameScope(Context& context, SemIR::NodeId base_id)
static auto GetAsNameScope(Context& context, SemIR::InstId base_id)
-> std::optional<SemIR::NameScopeId> {
auto base = context.nodes().Get(context.FollowNameReferences(base_id));
auto base = context.insts().Get(context.FollowNameReferences(base_id));
if (auto base_as_namespace = base.TryAs<SemIR::Namespace>()) {
return base_as_namespace->name_scope_id;
}
@@ -25,7 +25,7 @@ static auto GetAsNameScope(Context& context, SemIR::NodeId base_id)
"Member access into incomplete class `{0}`.",
std::string);
auto builder = context.emitter().Build(
context.nodes().Get(base_id).parse_node(),
context.insts().Get(base_id).parse_node(),
QualifiedExpressionInIncompleteClassScope,
context.sem_ir().StringifyTypeExpression(base_id, true));
context.NoteIncompleteClass(base_as_class->class_id, builder);
@@ -36,20 +36,20 @@ static auto GetAsNameScope(Context& context, SemIR::NodeId base_id)
return std::nullopt;
}
// Given a node produced by a name lookup, get the value to use for that result
// in an expression.
// Given an instruction produced by a name lookup, get the value to use for that
// result in an expression.
static auto GetExpressionValueForLookupResult(Context& context,
SemIR::NodeId lookup_result_id)
-> SemIR::NodeId {
SemIR::InstId lookup_result_id)
-> SemIR::InstId {
// If lookup finds a class declaration, the value is its `Self` type.
auto lookup_result = context.nodes().Get(lookup_result_id);
auto lookup_result = context.insts().Get(lookup_result_id);
if (auto class_decl = lookup_result.TryAs<SemIR::ClassDeclaration>()) {
return context.sem_ir().GetTypeAllowBuiltinTypes(
context.classes().Get(class_decl->class_id).self_type_id);
}
// Anything else should be a typed value already.
CARBON_CHECK(lookup_result.kind().value_kind() == SemIR::NodeValueKind::Typed)
CARBON_CHECK(lookup_result.kind().value_kind() == SemIR::InstValueKind::Typed)
<< "Unexpected kind for lookup result";
return lookup_result_id;
}
@@ -62,39 +62,39 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
// If the base is a name scope, such as a class or namespace, perform lookup
// into that scope.
if (auto name_scope_id = GetAsNameScope(context, base_id)) {
auto node_id = name_scope_id->is_valid()
auto inst_id = name_scope_id->is_valid()
? context.LookupName(parse_node, name_id, *name_scope_id,
/*print_diagnostics=*/true)
: SemIR::NodeId::BuiltinError;
node_id = GetExpressionValueForLookupResult(context, node_id);
auto node = context.nodes().Get(node_id);
// TODO: Track that this node was named within `base_id`.
context.AddNodeAndPush(
: SemIR::InstId::BuiltinError;
inst_id = GetExpressionValueForLookupResult(context, inst_id);
auto inst = context.insts().Get(inst_id);
// TODO: Track that this instruction was named within `base_id`.
context.AddInstAndPush(
parse_node,
SemIR::NameReference{parse_node, node.type_id(), name_id, node_id});
SemIR::NameReference{parse_node, inst.type_id(), name_id, inst_id});
return true;
}
// If the base isn't a scope, it must have a complete type.
auto base_type_id = context.nodes().Get(base_id).type_id();
auto base_type_id = context.insts().Get(base_id).type_id();
if (!context.TryToCompleteType(base_type_id, [&] {
CARBON_DIAGNOSTIC(IncompleteTypeInMemberAccess, Error,
"Member access into object of incomplete type `{0}`.",
std::string);
return context.emitter().Build(
context.nodes().Get(base_id).parse_node(),
context.insts().Get(base_id).parse_node(),
IncompleteTypeInMemberAccess,
context.sem_ir().StringifyType(base_type_id, true));
})) {
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
return true;
}
// Materialize a temporary for the base expression if necessary.
base_id = ConvertToValueOrReferenceExpression(context, base_id);
base_type_id = context.nodes().Get(base_id).type_id();
base_type_id = context.insts().Get(base_id).type_id();
auto base_type = context.nodes().Get(
auto base_type = context.insts().Get(
context.sem_ir().GetTypeAllowBuiltinTypes(base_type_id));
switch (base_type.kind()) {
@@ -108,24 +108,24 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
member_id = GetExpressionValueForLookupResult(context, member_id);
// Perform instance binding if we found an instance member.
auto member_type_id = context.nodes().Get(member_id).type_id();
auto member_type_node = context.nodes().Get(
auto member_type_id = context.insts().Get(member_id).type_id();
auto member_type_inst = context.insts().Get(
context.sem_ir().GetTypeAllowBuiltinTypes(member_type_id));
if (auto unbound_field_type =
member_type_node.TryAs<SemIR::UnboundFieldType>()) {
member_type_inst.TryAs<SemIR::UnboundFieldType>()) {
// TODO: Check that the unbound field type describes a member of this
// class. Perform a conversion of the base if necessary.
// Find the named field and build a field access expression.
auto field_id = context.GetConstantValue(member_id);
CARBON_CHECK(field_id.is_valid())
<< "Non-constant value " << context.nodes().Get(member_id)
<< "Non-constant value " << context.insts().Get(member_id)
<< " of unbound field type";
auto field = context.nodes().Get(field_id).TryAs<SemIR::Field>();
auto field = context.insts().Get(field_id).TryAs<SemIR::Field>();
CARBON_CHECK(field)
<< "Unexpected value " << context.nodes().Get(field_id)
<< "Unexpected value " << context.insts().Get(field_id)
<< " for field name expression";
auto access_id = context.AddNode(SemIR::ClassFieldAccess{
auto access_id = context.AddInst(SemIR::ClassFieldAccess{
parse_node, unbound_field_type->field_type_id, base_id,
field->index});
if (SemIR::GetExpressionCategory(context.sem_ir(), base_id) ==
@@ -147,19 +147,19 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
// Find the named function and check whether it's an instance method.
auto function_name_id = context.GetConstantValue(member_id);
CARBON_CHECK(function_name_id.is_valid())
<< "Non-constant value " << context.nodes().Get(member_id)
<< "Non-constant value " << context.insts().Get(member_id)
<< " of function type";
auto function_decl = context.nodes()
auto function_decl = context.insts()
.Get(function_name_id)
.TryAs<SemIR::FunctionDeclaration>();
CARBON_CHECK(function_decl)
<< "Unexpected value " << context.nodes().Get(function_name_id)
<< "Unexpected value " << context.insts().Get(function_name_id)
<< " of function type";
auto& function = context.functions().Get(function_decl->function_id);
for (auto param_id :
context.node_blocks().Get(function.implicit_param_refs_id)) {
if (context.nodes().Get(param_id).Is<SemIR::SelfParameter>()) {
context.AddNodeAndPush(
context.inst_blocks().Get(function.implicit_param_refs_id)) {
if (context.insts().Get(param_id).Is<SemIR::SelfParameter>()) {
context.AddInstAndPush(
parse_node,
SemIR::BoundMethod{
parse_node,
@@ -172,19 +172,19 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
// For a non-instance member, the result is that member.
// TODO: Track that this was named within `base_id`.
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::NameReference{parse_node, member_type_id, name_id, member_id});
return true;
}
case SemIR::StructType::Kind: {
auto refs = context.node_blocks().Get(
auto refs = context.inst_blocks().Get(
base_type.As<SemIR::StructType>().fields_id);
// TODO: Do we need to optimize this with a lookup table for O(1)?
for (auto [i, ref_id] : llvm::enumerate(refs)) {
auto field = context.nodes().GetAs<SemIR::StructTypeField>(ref_id);
auto field = context.insts().GetAs<SemIR::StructTypeField>(ref_id);
if (name_id == field.name_id) {
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node, SemIR::StructAccess{parse_node, field.field_type_id,
base_id, SemIR::MemberIndex(i)});
return true;
@@ -214,7 +214,7 @@ auto HandleMemberAccessExpression(Context& context, Parse::Node parse_node)
}
// Should only be reached on error.
context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError);
context.node_stack().Push(parse_node, SemIR::InstId::BuiltinError);
return true;
}
@@ -237,9 +237,10 @@ auto HandleNameExpression(Context& context, Parse::Node parse_node) -> bool {
auto value_id =
context.LookupName(parse_node, name_id, SemIR::NameScopeId::Invalid,
/*print_diagnostics=*/true);
value_id = GetExpressionValueForLookupResult(context, value_id);
auto value = context.nodes().Get(value_id);
context.AddNodeAndPush(
auto value = context.insts().Get(value_id);
context.AddInstAndPush(
parse_node,
SemIR::NameReference{parse_node, value.type_id(), name_id, value_id});
return true;
@@ -287,8 +288,8 @@ auto HandleSelfTypeNameExpression(Context& context, Parse::Node parse_node)
auto value_id =
context.LookupName(parse_node, name_id, SemIR::NameScopeId::Invalid,
/*print_diagnostics=*/true);
auto value = context.nodes().Get(value_id);
context.AddNodeAndPush(
auto value = context.insts().Get(value_id);
context.AddInstAndPush(
parse_node,
SemIR::NameReference{parse_node, value.type_id(), name_id, value_id});
return true;
@@ -308,8 +309,8 @@ auto HandleSelfValueNameExpression(Context& context, Parse::Node parse_node)
auto value_id =
context.LookupName(parse_node, name_id, SemIR::NameScopeId::Invalid,
/*print_diagnostics=*/true);
auto value = context.nodes().Get(value_id);
context.AddNodeAndPush(
auto value = context.insts().Get(value_id);
context.AddInstAndPush(
parse_node,
SemIR::NameReference{parse_node, value.type_id(), name_id, value_id});
return true;
+2 -2
View File
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/check/context.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -15,7 +15,7 @@ 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.AddInst(SemIR::Namespace{
parse_node, context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
context.name_scopes().Add()});
context.declaration_name_stack().AddNameToLookup(name_context, namespace_id);
+29 -29
View File
@@ -18,12 +18,12 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool {
// TODO: This should search for a compatible interface. For now, it's a
// very trivial check of validity on the operation.
lhs_id = ConvertToValueOfType(context, parse_node, lhs_id,
context.nodes().Get(rhs_id).type_id());
context.insts().Get(rhs_id).type_id());
rhs_id = ConvertToValueExpression(context, rhs_id);
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node, SemIR::BinaryOperatorAdd{
parse_node, context.nodes().Get(lhs_id).type_id(),
parse_node, context.insts().Get(lhs_id).type_id(),
lhs_id, rhs_id});
return true;
@@ -36,16 +36,16 @@ auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool {
// When the second operand is evaluated, the result of `and` and `or` is
// its value.
auto resume_block_id = context.node_block_stack().PeekOrAdd(/*depth=*/1);
context.AddNode(
auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1);
context.AddInst(
SemIR::BranchWithArg{parse_node, resume_block_id, rhs_id});
context.node_block_stack().Pop();
context.inst_block_stack().Pop();
context.AddCurrentCodeBlockToFunction();
// Collect the result from either the first or second operand.
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::BlockArg{parse_node, context.nodes().Get(rhs_id).type_id(),
SemIR::BlockArg{parse_node, context.insts().Get(rhs_id).type_id(),
resume_block_id});
return true;
}
@@ -68,7 +68,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.AddInst(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
@@ -89,7 +89,7 @@ auto HandlePostfixOperator(Context& context, Parse::Node parse_node) -> bool {
switch (auto token_kind = context.tokens().GetKind(token)) {
case Lex::TokenKind::Star: {
auto inner_type_id = ExpressionAsType(context, parse_node, value_id);
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node, SemIR::PointerType{parse_node, SemIR::TypeId::TypeType,
inner_type_id});
return true;
@@ -124,12 +124,12 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool {
context.emitter().Emit(parse_node, AddressOfNonReference);
break;
}
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::AddressOf{
parse_node,
context.GetPointerType(parse_node,
context.nodes().Get(value_id).type_id()),
context.insts().Get(value_id).type_id()),
value_id});
return true;
}
@@ -138,14 +138,14 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool {
// `const (const T)` is probably not what the developer intended.
// TODO: Detect `const (const T)*` and suggest moving the `*` inside the
// parentheses.
if (context.nodes().Get(value_id).kind() == SemIR::ConstType::Kind) {
if (context.insts().Get(value_id).kind() == SemIR::ConstType::Kind) {
CARBON_DIAGNOSTIC(RepeatedConst, Warning,
"`const` applied repeatedly to the same type has no "
"additional effect.");
context.emitter().Emit(parse_node, RepeatedConst);
}
auto inner_type_id = ExpressionAsType(context, parse_node, value_id);
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::ConstType{parse_node, SemIR::TypeId::TypeType, inner_type_id});
return true;
@@ -153,20 +153,20 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool {
case Lex::TokenKind::Not:
value_id = ConvertToBoolValue(context, parse_node, value_id);
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::UnaryOperatorNot{
parse_node, context.nodes().Get(value_id).type_id(), value_id});
parse_node, context.insts().Get(value_id).type_id(), value_id});
return true;
case Lex::TokenKind::Star: {
value_id = ConvertToValueExpression(context, value_id);
auto type_id =
context.GetUnqualifiedType(context.nodes().Get(value_id).type_id());
auto type_node = context.nodes().Get(
context.GetUnqualifiedType(context.insts().Get(value_id).type_id());
auto type_inst = context.insts().Get(
context.sem_ir().GetTypeAllowBuiltinTypes(type_id));
auto result_type_id = SemIR::TypeId::Error;
if (auto pointer_type = type_node.TryAs<SemIR::PointerType>()) {
if (auto pointer_type = type_inst.TryAs<SemIR::PointerType>()) {
result_type_id = pointer_type->pointee_id;
} else if (type_id != SemIR::TypeId::Error) {
CARBON_DIAGNOSTIC(
@@ -185,7 +185,7 @@ auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool {
}
builder.Emit();
}
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node, SemIR::Dereference{parse_node, result_type_id, value_id});
return true;
}
@@ -200,23 +200,23 @@ auto HandleShortCircuitOperand(Context& context, Parse::Node parse_node)
// Convert the condition to `bool`.
auto cond_value_id = context.node_stack().PopExpression();
cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id);
auto bool_type_id = context.nodes().Get(cond_value_id).type_id();
auto bool_type_id = context.insts().Get(cond_value_id).type_id();
// Compute the branch value: the condition for `and`, inverted for `or`.
auto token = context.parse_tree().node_token(parse_node);
SemIR::NodeId branch_value_id = SemIR::NodeId::Invalid;
auto short_circuit_result_id = SemIR::NodeId::Invalid;
SemIR::InstId branch_value_id = SemIR::InstId::Invalid;
auto short_circuit_result_id = SemIR::InstId::Invalid;
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{
short_circuit_result_id = context.AddInst(SemIR::BoolLiteral{
parse_node, bool_type_id, SemIR::BoolValue::False});
break;
case Lex::TokenKind::Or:
branch_value_id = context.AddNode(
branch_value_id = context.AddInst(
SemIR::UnaryOperatorNot{parse_node, bool_type_id, cond_value_id});
short_circuit_result_id = context.AddNode(
short_circuit_result_id = context.AddInst(
SemIR::BoolLiteral{parse_node, bool_type_id, SemIR::BoolValue::True});
break;
@@ -232,9 +232,9 @@ auto HandleShortCircuitOperand(Context& context, Parse::Node parse_node)
// Push the resumption and the right-hand side blocks, and start emitting the
// right-hand operand.
context.node_block_stack().Pop();
context.node_block_stack().Push(end_block_id);
context.node_block_stack().Push(rhs_block_id);
context.inst_block_stack().Pop();
context.inst_block_stack().Push(end_block_id);
context.inst_block_stack().Push(rhs_block_id);
context.AddCurrentCodeBlockToFunction();
// Put the condition back on the stack for HandleInfixOperator.
+5 -5
View File
@@ -40,16 +40,16 @@ auto HandleTupleLiteral(Context& context, Parse::Node parse_node) -> bool {
context.node_stack()
.PopAndDiscardSoloParseNode<
Parse::NodeKind::ParenExpressionOrTupleLiteralStart>();
const auto& node_block = context.node_blocks().Get(refs_id);
const auto& inst_block = context.inst_blocks().Get(refs_id);
llvm::SmallVector<SemIR::TypeId> type_ids;
type_ids.reserve(node_block.size());
for (auto node : node_block) {
type_ids.push_back(context.nodes().Get(node).type_id());
type_ids.reserve(inst_block.size());
for (auto inst : inst_block) {
type_ids.push_back(context.insts().Get(inst).type_id());
}
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.AddInst(SemIR::TupleLiteral{parse_node, type_id, refs_id});
context.node_stack().Push(parse_node, value_id);
return true;
}
+19 -19
View File
@@ -4,7 +4,7 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -12,9 +12,9 @@ auto HandleAddress(Context& context, Parse::Node parse_node) -> bool {
auto self_param_id =
context.node_stack().Peek<Parse::NodeKind::PatternBinding>();
if (auto self_param =
context.nodes().Get(self_param_id).TryAs<SemIR::SelfParameter>()) {
context.insts().Get(self_param_id).TryAs<SemIR::SelfParameter>()) {
self_param->is_addr_self = SemIR::BoolValue::True;
context.nodes().Set(self_param_id, *self_param);
context.insts().Set(self_param_id, *self_param);
} else {
CARBON_DIAGNOSTIC(AddrOnNonSelfParameter, Error,
"`addr` can only be applied to a `self` parameter");
@@ -45,7 +45,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
"`self` can only be declared in an implicit parameter list");
context.emitter().Emit(parse_node, SelfOutsideImplicitParameterList);
}
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::SelfParameter{*self_node, cast_type_id,
/*is_addr_self=*/SemIR::BoolValue::False});
@@ -58,8 +58,8 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
auto [name_node, name_id] =
context.node_stack().PopWithParseNode<Parse::NodeKind::Name>();
// Allocate a node of the appropriate kind, linked to the name for error
// locations.
// Allocate an instruction of the appropriate kind, linked to the name for
// error locations.
switch (auto context_parse_node_kind = context.parse_tree().node_kind(
context.node_stack().PeekParseNode())) {
case Parse::NodeKind::VariableIntroducer: {
@@ -77,29 +77,29 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
})) {
cast_type_id = SemIR::TypeId::Error;
}
SemIR::NodeId value_id = SemIR::NodeId::Invalid;
SemIR::InstId value_id = SemIR::InstId::Invalid;
SemIR::TypeId value_type_id = cast_type_id;
if (enclosing_class_decl) {
auto& class_info =
context.classes().Get(enclosing_class_decl->class_id);
auto field_type_node_id = context.AddNode(SemIR::UnboundFieldType{
auto field_type_inst_id = context.AddInst(SemIR::UnboundFieldType{
parse_node, context.GetBuiltinType(SemIR::BuiltinKind::TypeType),
class_info.self_type_id, cast_type_id});
value_type_id = context.CanonicalizeType(field_type_node_id);
value_id = context.AddNode(
value_type_id = context.CanonicalizeType(field_type_inst_id);
value_id = context.AddInst(
SemIR::Field{parse_node, value_type_id, name_id,
SemIR::MemberIndex(context.args_type_info_stack()
.PeekCurrentBlockContents()
.size())});
// Add a corresponding field to the object representation of the class.
context.args_type_info_stack().AddNode(
context.args_type_info_stack().AddInst(
SemIR::StructTypeField{parse_node, name_id, cast_type_id});
} else {
value_id = context.AddNode(
value_id = context.AddInst(
SemIR::VarStorage{name_node, value_type_id, name_id});
}
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::BindName{name_node, value_type_id, name_id, value_id});
break;
@@ -109,9 +109,9 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
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(
context.AddInstAndPush(
parse_node, SemIR::Parameter{name_node, cast_type_id, name_id});
// TODO: Create a `BindName` node.
// TODO: Create a `BindName` instruction.
break;
case Parse::NodeKind::LetIntroducer:
@@ -125,14 +125,14 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool {
})) {
cast_type_id = SemIR::TypeId::Error;
}
// Create the node, but don't add it to a block until after we've formed
// its initializer.
// Create the instruction, but don't add it to a block until after we've
// formed its initializer.
// TODO: For general pattern parsing, we'll need to create a block to hold
// the `let` pattern before we see the initializer.
context.node_stack().Push(
parse_node,
context.nodes().AddInNoBlock(SemIR::BindName{
name_node, cast_type_id, name_id, SemIR::NodeId::Invalid}));
context.insts().AddInNoBlock(SemIR::BindName{
name_node, cast_type_id, name_id, SemIR::InstId::Invalid}));
break;
default:
+11 -11
View File
@@ -4,17 +4,17 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
// TODO: Find a better home for this. We'll likely need it for more than just
// expression statements.
static auto HandleDiscardedExpression(Context& context, SemIR::NodeId expr_id)
static auto HandleDiscardedExpression(Context& context, SemIR::InstId expr_id)
-> void {
// If we discard an initializing expression, convert it to a value or
// reference so that it has something to initialize.
auto expr = context.nodes().Get(expr_id);
auto expr = context.insts().Get(expr_id);
Convert(context, expr.parse_node(), expr_id,
{.kind = ConversionTarget::Discarded, .type_id = expr.type_id()});
@@ -29,9 +29,9 @@ auto HandleExpressionStatement(Context& context, Parse::Node /*parse_node*/)
auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool {
CARBON_CHECK(!context.return_scope_stack().empty());
auto fn_node = context.nodes().GetAs<SemIR::FunctionDeclaration>(
auto fn_inst = context.insts().GetAs<SemIR::FunctionDeclaration>(
context.return_scope_stack().back());
const auto& callable = context.functions().Get(fn_node.function_id);
const auto& callable = context.functions().Get(fn_inst.function_id);
if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) ==
Parse::NodeKind::ReturnStatementStart) {
@@ -48,7 +48,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool {
.Emit();
}
context.AddNode(SemIR::Return{parse_node});
context.AddInst(SemIR::Return{parse_node});
} else {
auto arg = context.node_stack().PopExpression();
context.node_stack()
@@ -62,7 +62,7 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool {
"There was no return type provided.");
context.emitter()
.Build(parse_node, ReturnStatementDisallowExpression)
.Note(fn_node.parse_node, ReturnStatementImplicitNote)
.Note(fn_inst.parse_node, ReturnStatementImplicitNote)
.Emit();
} else if (callable.return_slot_id.is_valid()) {
arg = Initialize(context, parse_node, callable.return_slot_id, arg);
@@ -71,14 +71,14 @@ auto HandleReturnStatement(Context& context, Parse::Node parse_node) -> bool {
callable.return_type_id);
}
context.AddNode(SemIR::ReturnExpression{parse_node, arg});
context.AddInst(SemIR::ReturnExpression{parse_node, arg});
}
// Switch to a new, unreachable, empty node block. This typically won't
// Switch to a new, unreachable, empty instruction block. This typically won't
// contain any semantics IR, but it can do if there are statements following
// the `return` statement.
context.node_block_stack().Pop();
context.node_block_stack().PushUnreachable();
context.inst_block_stack().Pop();
context.inst_block_stack().PushUnreachable();
return true;
}
+8 -8
View File
@@ -28,7 +28,7 @@ auto HandleStructFieldType(Context& context, Parse::Node parse_node) -> bool {
auto [name_node, name_id] =
context.node_stack().PopWithParseNode<Parse::NodeKind::Name>();
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node, SemIR::StructTypeField{name_node, name_id, cast_type_id});
return true;
}
@@ -39,16 +39,16 @@ auto HandleStructFieldUnknown(Context& context, Parse::Node parse_node)
}
auto HandleStructFieldValue(Context& context, Parse::Node parse_node) -> bool {
auto [value_parse_node, value_node_id] =
auto [value_parse_node, value_inst_id] =
context.node_stack().PopExpressionWithParseNode();
StringId name_id = context.node_stack().Pop<Parse::NodeKind::Name>();
// Store the name for the type.
context.args_type_info_stack().AddNode(SemIR::StructTypeField{
parse_node, name_id, context.nodes().Get(value_node_id).type_id()});
context.args_type_info_stack().AddInst(SemIR::StructTypeField{
parse_node, name_id, context.insts().Get(value_inst_id).type_id()});
// Push the value back on the stack as an argument.
context.node_stack().Push(parse_node, value_node_id);
context.node_stack().Push(parse_node, value_inst_id);
return true;
}
@@ -65,7 +65,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.AddInst(SemIR::StructLiteral{parse_node, type_id, refs_id});
context.node_stack().Push(parse_node, value_id);
return true;
}
@@ -94,10 +94,10 @@ auto HandleStructTypeLiteral(Context& context, Parse::Node parse_node) -> bool {
// This is only used for value literals.
context.args_type_info_stack().Pop();
CARBON_CHECK(refs_id != SemIR::NodeBlockId::Empty)
CARBON_CHECK(refs_id != SemIR::InstBlockId::Empty)
<< "{} is handled by StructLiteral.";
context.AddNodeAndPush(
context.AddInstAndPush(
parse_node,
SemIR::StructType{parse_node, SemIR::TypeId::TypeType, refs_id});
return true;
+6 -6
View File
@@ -4,14 +4,14 @@
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
auto HandleVariableDeclaration(Context& context, Parse::Node parse_node)
-> bool {
// Handle the optional initializer.
auto init_id = SemIR::NodeId::Invalid;
auto init_id = SemIR::InstId::Invalid;
bool has_init =
context.parse_tree().node_kind(context.node_stack().PeekParseNode()) !=
Parse::NodeKind::PatternBinding;
@@ -23,7 +23,7 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node)
// Extract the name binding.
auto value_id = context.node_stack().Pop<Parse::NodeKind::PatternBinding>();
if (auto bind_name = context.nodes().Get(value_id).TryAs<SemIR::BindName>()) {
if (auto bind_name = context.insts().Get(value_id).TryAs<SemIR::BindName>()) {
// Form a corresponding name in the current context, and bind the name to
// the variable.
context.declaration_name_stack().AddNameToLookup(
@@ -35,11 +35,11 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node)
// If there was an initializer, assign it to the storage.
if (has_init) {
if (context.nodes().Get(value_id).Is<SemIR::VarStorage>()) {
if (context.insts().Get(value_id).Is<SemIR::VarStorage>()) {
init_id = Initialize(context, parse_node, value_id, init_id);
// TODO: Consider using different node kinds for assignment versus
// TODO: Consider using different instruction kinds for assignment versus
// initialization.
context.AddNode(SemIR::Assign{parse_node, value_id, init_id});
context.AddInst(SemIR::Assign{parse_node, value_id, init_id});
} else {
// TODO: In a class scope, we should instead save the initializer
// somewhere so that we can use it as a default.
@@ -2,16 +2,15 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/check/node_block_stack.h"
#include "common/vlog.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/check/node_block_stack.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
auto NodeBlockStack::Push(SemIR::NodeBlockId id) -> void {
auto InstBlockStack::Push(SemIR::InstBlockId id) -> void {
CARBON_VLOG() << name_ << " Push " << size_ << "\n";
CARBON_CHECK(size_ < (1 << 20))
<< "Excessive stack size: likely infinite loop";
@@ -22,44 +21,44 @@ auto NodeBlockStack::Push(SemIR::NodeBlockId id) -> void {
++size_;
}
auto NodeBlockStack::PeekOrAdd(int depth) -> SemIR::NodeBlockId {
auto InstBlockStack::PeekOrAdd(int depth) -> SemIR::InstBlockId {
CARBON_CHECK(size() > depth) << "no such block";
int index = size() - depth - 1;
auto& slot = stack_[index];
if (!slot.id.is_valid()) {
slot.id = sem_ir_->node_blocks().AddDefaultValue();
slot.id = sem_ir_->inst_blocks().AddDefaultValue();
}
return slot.id;
}
auto NodeBlockStack::Pop() -> SemIR::NodeBlockId {
auto InstBlockStack::Pop() -> SemIR::InstBlockId {
CARBON_CHECK(!empty()) << "no current block";
--size_;
auto& back = stack_[size_];
// Finalize the block.
if (!back.content.empty() && back.id != SemIR::NodeBlockId::Unreachable) {
if (!back.content.empty() && back.id != SemIR::InstBlockId::Unreachable) {
if (back.id.is_valid()) {
sem_ir_->node_blocks().Set(back.id, back.content);
sem_ir_->inst_blocks().Set(back.id, back.content);
} else {
back.id = sem_ir_->node_blocks().Add(back.content);
back.id = sem_ir_->inst_blocks().Add(back.content);
}
}
CARBON_VLOG() << name_ << " Pop " << size_ << ": " << back.id << "\n";
if (!back.id.is_valid()) {
return SemIR::NodeBlockId::Empty;
return SemIR::InstBlockId::Empty;
}
return back.id;
}
auto NodeBlockStack::PopAndDiscard() -> void {
auto InstBlockStack::PopAndDiscard() -> void {
CARBON_CHECK(!empty()) << "no current block";
--size_;
CARBON_VLOG() << name_ << " PopAndDiscard " << size_ << "\n";
}
auto NodeBlockStack::PrintForStackDump(llvm::raw_ostream& output) const
auto InstBlockStack::PrintForStackDump(llvm::raw_ostream& output) const
-> void {
output << name_ << ":\n";
for (const auto& [i, entry] : llvm::enumerate(stack_)) {
+38 -36
View File
@@ -7,66 +7,68 @@
#include "llvm/ADT/SmallVector.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
// A stack of node blocks that are currently being constructed in a Context. The
// contents of the node blocks are stored here until the node block is popped
// from the stack, at which point they are transferred into the SemIR::File for
// long-term storage.
// A stack of instruction blocks that are currently being constructed in a
// Context. The contents of the instruction blocks are stored here until the
// instruction block is popped from the stack, at which point they are
// transferred into the SemIR::File for long-term storage.
//
// All pushes and pops will be vlogged.
class NodeBlockStack {
class InstBlockStack {
public:
explicit NodeBlockStack(llvm::StringLiteral name, SemIR::File& sem_ir,
explicit InstBlockStack(llvm::StringLiteral name, SemIR::File& sem_ir,
llvm::raw_ostream* vlog_stream)
: name_(name), sem_ir_(&sem_ir), vlog_stream_(vlog_stream) {}
// Pushes an existing node block.
auto Push(SemIR::NodeBlockId id) -> void;
// Pushes an existing instruction block.
auto Push(SemIR::InstBlockId id) -> void;
// Pushes a new node block. It will be invalid unless PeekOrAdd is called in
// order to support lazy allocation.
auto Push() -> void { Push(SemIR::NodeBlockId::Invalid); }
// Pushes a new instruction block. It will be invalid unless PeekOrAdd is
// called in order to support lazy allocation.
auto Push() -> void { Push(SemIR::InstBlockId::Invalid); }
// Pushes a new unreachable code block.
auto PushUnreachable() -> void { Push(SemIR::NodeBlockId::Unreachable); }
auto PushUnreachable() -> void { Push(SemIR::InstBlockId::Unreachable); }
// Returns the ID of the top node block, allocating one if necessary. If
// `depth` is specified, returns the node at `depth` levels from the top of
// the stack instead of the top block, where the top block is at depth 0.
auto PeekOrAdd(int depth = 0) -> SemIR::NodeBlockId;
// Returns the ID of the top instruction block, allocating one if necessary.
// If `depth` is specified, returns the instruction at `depth` levels from the
// top of the stack instead of the top block, where the top block is at depth
// 0.
auto PeekOrAdd(int depth = 0) -> SemIR::InstBlockId;
// Pops the top node block. This will always return a valid node block;
// SemIR::NodeBlockId::Empty is returned if one wasn't allocated.
auto Pop() -> SemIR::NodeBlockId;
// Pops the top instruction block. This will always return a valid instruction
// block; SemIR::InstBlockId::Empty is returned if one wasn't allocated.
auto Pop() -> SemIR::InstBlockId;
// Pops the top node block, and discards it if it hasn't had an ID allocated.
// Pops the top instruction block, and discards it if it hasn't had an ID
// allocated.
auto PopAndDiscard() -> void;
// Adds the given node to the block at the top of the stack and returns its
// ID.
auto AddNode(SemIR::Node node) -> SemIR::NodeId {
auto node_id = sem_ir_->nodes().AddInNoBlock(node);
AddNodeId(node_id);
return node_id;
// Adds the given instruction to the block at the top of the stack and returns
// its ID.
auto AddInst(SemIR::Inst inst) -> SemIR::InstId {
auto inst_id = sem_ir_->insts().AddInNoBlock(inst);
AddInstId(inst_id);
return inst_id;
}
// Adds the given node ID to the block at the top of the stack.
auto AddNodeId(SemIR::NodeId node_id) -> void {
// Adds the given instruction ID to the block at the top of the stack.
auto AddInstId(SemIR::InstId inst_id) -> void {
CARBON_CHECK(!empty()) << "no current block";
stack_[size_ - 1].content.push_back(node_id);
stack_[size_ - 1].content.push_back(inst_id);
}
// Returns whether the current block is statically reachable.
auto is_current_block_reachable() -> bool {
return size_ != 0 &&
stack_[size_ - 1].id != SemIR::NodeBlockId::Unreachable;
stack_[size_ - 1].id != SemIR::InstBlockId::Unreachable;
}
// Returns a view of the contents of the top node block on the stack.
auto PeekCurrentBlockContents() -> llvm::ArrayRef<SemIR::NodeId> {
// Returns a view of the contents of the top instruction block on the stack.
auto PeekCurrentBlockContents() -> llvm::ArrayRef<SemIR::InstId> {
CARBON_CHECK(!empty()) << "no current block";
return stack_[size_ - 1].content;
}
@@ -84,18 +86,18 @@ class NodeBlockStack {
// reallocation.
StackEntry() { content.reserve(32); }
auto Reset(SemIR::NodeBlockId new_id) {
auto Reset(SemIR::InstBlockId new_id) {
id = new_id;
content.clear();
}
// The block ID, if one has been allocated, Invalid if no block has been
// allocated, or Unreachable if this block is known to be unreachable.
SemIR::NodeBlockId id = SemIR::NodeBlockId::Invalid;
SemIR::InstBlockId id = SemIR::InstBlockId::Invalid;
// The content of the block. Stored as a vector rather than as a SmallVector
// to reduce the cost of resizing `stack_` and performing swaps.
std::vector<SemIR::NodeId> content;
std::vector<SemIR::InstId> content;
};
// A name for debugging.
+3 -3
View File
@@ -5,7 +5,7 @@
#include "toolchain/check/node_stack.h"
#include "llvm/ADT/STLExtras.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
@@ -17,8 +17,8 @@ auto NodeStack::PrintForStackDump(llvm::raw_ostream& output) const -> void {
if (parse_node_kind == Parse::NodeKind::PatternBinding) {
output << " -> " << entry.name_id;
} else {
if (entry.node_id.is_valid()) {
output << " -> " << entry.node_id;
if (entry.inst_id.is_valid()) {
output << " -> " << entry.inst_id;
}
}
output << "\n";
+40 -38
View File
@@ -11,11 +11,13 @@
#include "llvm/ADT/SmallVector.h"
#include "toolchain/parse/node_kind.h"
#include "toolchain/parse/tree.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
// Wraps the stack of nodes for Context.
// Wraps the stack of parse nodes for Context. Each parse node can have an
// associated id of some kind (instruction, instruction block, function, class,
// ...).
//
// All pushes and pops will be vlogged.
//
@@ -23,7 +25,7 @@ namespace Carbon::Check {
//
// - If receiving a pop_parse_kind, verify that the parse_node being popped is
// of pop_parse_kind.
// - Validates presence of node_id based on whether it's a solo
// - Validates presence of inst_id based on whether it's a solo
// parse_node.
//
// These should be assumed API constraints unless otherwise mentioned on a
@@ -44,7 +46,7 @@ class NodeStack {
<< parse_tree_->node_kind(parse_node) << " -> <none>\n";
CARBON_CHECK(stack_.size() < (1 << 20))
<< "Excessive stack size: likely infinite loop";
stack_.push_back(Entry(parse_node, SemIR::NodeId::Invalid));
stack_.push_back(Entry(parse_node, SemIR::InstId::Invalid));
}
// Pushes a parse tree node onto the stack with an ID.
@@ -70,12 +72,12 @@ class NodeStack {
}
// Pops the top of the stack without any verification.
auto PopAndIgnore() -> void { PopEntry<SemIR::NodeId>(); }
auto PopAndIgnore() -> void { PopEntry<SemIR::InstId>(); }
// Pops the top of the stack and returns the parse_node.
template <Parse::NodeKind::RawEnumType RequiredParseKind>
auto PopForSoloParseNode() -> Parse::Node {
Entry back = PopEntry<SemIR::NodeId>();
Entry back = PopEntry<SemIR::InstId>();
RequireIdKind(Parse::NodeKind::Create(RequiredParseKind),
IdKind::SoloParseNode);
RequireParseKind<RequiredParseKind>(back.parse_node);
@@ -100,8 +102,8 @@ class NodeStack {
// Pops an expression from the top of the stack and returns the parse_node and
// the ID.
auto PopExpressionWithParseNode() -> std::pair<Parse::Node, SemIR::NodeId> {
return PopWithParseNode<SemIR::NodeId>();
auto PopExpressionWithParseNode() -> std::pair<Parse::Node, SemIR::InstId> {
return PopWithParseNode<SemIR::InstId>();
}
// Pops the top of the stack and returns the parse_node and the ID.
@@ -109,13 +111,13 @@ class NodeStack {
auto PopWithParseNode() -> auto {
constexpr IdKind RequiredIdKind =
ParseNodeKindToIdKind(Parse::NodeKind::Create(RequiredParseKind));
if constexpr (RequiredIdKind == IdKind::NodeId) {
auto back = PopWithParseNode<SemIR::NodeId>();
if constexpr (RequiredIdKind == IdKind::InstId) {
auto back = PopWithParseNode<SemIR::InstId>();
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
if constexpr (RequiredIdKind == IdKind::NodeBlockId) {
auto back = PopWithParseNode<SemIR::NodeBlockId>();
if constexpr (RequiredIdKind == IdKind::InstBlockId) {
auto back = PopWithParseNode<SemIR::InstBlockId>();
RequireParseKind<RequiredParseKind>(back.first);
return back;
}
@@ -145,8 +147,8 @@ class NodeStack {
}
// Pops an expression from the top of the stack and returns the ID.
// Expressions map multiple Parse::NodeKinds to SemIR::NodeId always.
auto PopExpression() -> SemIR::NodeId {
// Expressions map multiple Parse::NodeKinds to SemIR::InstId always.
auto PopExpression() -> SemIR::InstId {
return PopExpressionWithParseNode().second;
}
@@ -166,7 +168,7 @@ class NodeStack {
return std::nullopt;
}
// Peeks at the parse_node of the given depth in the stack, or by default the
// Peeks at the parse node of the given depth in the stack, or by default the
// top node.
auto PeekParseNode() const -> Parse::Node { return stack_.back().parse_node; }
@@ -178,11 +180,11 @@ class NodeStack {
RequireParseKind<RequiredParseKind>(back.parse_node);
constexpr IdKind RequiredIdKind =
ParseNodeKindToIdKind(Parse::NodeKind::Create(RequiredParseKind));
if constexpr (RequiredIdKind == IdKind::NodeId) {
return back.id<SemIR::NodeId>();
if constexpr (RequiredIdKind == IdKind::InstId) {
return back.id<SemIR::InstId>();
}
if constexpr (RequiredIdKind == IdKind::NodeBlockId) {
return back.id<SemIR::NodeBlockId>();
if constexpr (RequiredIdKind == IdKind::InstBlockId) {
return back.id<SemIR::InstBlockId>();
}
if constexpr (RequiredIdKind == IdKind::FunctionId) {
return back.id<SemIR::FunctionId>();
@@ -210,8 +212,8 @@ class NodeStack {
private:
// Possible associated ID types.
enum class IdKind : int8_t {
NodeId,
NodeBlockId,
InstId,
InstBlockId,
FunctionId,
ClassId,
StringId,
@@ -224,10 +226,10 @@ class NodeStack {
// An entry in stack_.
struct Entry {
explicit Entry(Parse::Node parse_node, SemIR::NodeId node_id)
: parse_node(parse_node), node_id(node_id) {}
explicit Entry(Parse::Node parse_node, SemIR::NodeBlockId node_block_id)
: parse_node(parse_node), node_block_id(node_block_id) {}
explicit Entry(Parse::Node parse_node, SemIR::InstId inst_id)
: parse_node(parse_node), inst_id(inst_id) {}
explicit Entry(Parse::Node parse_node, SemIR::InstBlockId inst_block_id)
: parse_node(parse_node), inst_block_id(inst_block_id) {}
explicit Entry(Parse::Node parse_node, SemIR::FunctionId function_id)
: parse_node(parse_node), function_id(function_id) {}
explicit Entry(Parse::Node parse_node, SemIR::ClassId class_id)
@@ -240,11 +242,11 @@ class NodeStack {
// Returns the appropriate ID basaed on type.
template <typename T>
auto id() -> T& {
if constexpr (std::is_same<T, SemIR::NodeId>()) {
return node_id;
if constexpr (std::is_same<T, SemIR::InstId>()) {
return inst_id;
}
if constexpr (std::is_same<T, SemIR::NodeBlockId>()) {
return node_block_id;
if constexpr (std::is_same<T, SemIR::InstBlockId>()) {
return inst_block_id;
}
if constexpr (std::is_same<T, SemIR::FunctionId>()) {
return function_id;
@@ -260,7 +262,7 @@ class NodeStack {
}
}
// The node associated with the stack entry.
// The parse node associated with the stack entry.
Parse::Node parse_node;
// The entries will evaluate as invalid if and only if they're a solo
@@ -269,8 +271,8 @@ class NodeStack {
// A discriminator isn't needed because the caller can determine which field
// is used based on the Parse::NodeKind.
union {
SemIR::NodeId node_id;
SemIR::NodeBlockId node_block_id;
SemIR::InstId inst_id;
SemIR::InstBlockId inst_block_id;
SemIR::FunctionId function_id;
SemIR::ClassId class_id;
StringId name_id;
@@ -305,14 +307,14 @@ class NodeStack {
case Parse::NodeKind::StructFieldType:
case Parse::NodeKind::StructTypeLiteral:
case Parse::NodeKind::TupleLiteral:
return IdKind::NodeId;
return IdKind::InstId;
case Parse::NodeKind::IfCondition:
case Parse::NodeKind::IfExpressionIf:
case Parse::NodeKind::ImplicitParameterList:
case Parse::NodeKind::ParameterList:
case Parse::NodeKind::WhileCondition:
case Parse::NodeKind::WhileConditionStart:
return IdKind::NodeBlockId;
return IdKind::InstBlockId;
case Parse::NodeKind::FunctionDefinitionStart:
return IdKind::FunctionId;
case Parse::NodeKind::ClassDefinitionStart:
@@ -344,11 +346,11 @@ class NodeStack {
// ParseNodeKindToIdKind.
template <typename IdT>
static constexpr auto IdTypeToIdKind() -> IdKind {
if constexpr (std::is_same_v<IdT, SemIR::NodeId>) {
return IdKind::NodeId;
if constexpr (std::is_same_v<IdT, SemIR::InstId>) {
return IdKind::InstId;
}
if constexpr (std::is_same_v<IdT, SemIR::NodeBlockId>) {
return IdKind::NodeBlockId;
if constexpr (std::is_same_v<IdT, SemIR::InstBlockId>) {
return IdKind::InstBlockId;
}
if constexpr (std::is_same_v<IdT, SemIR::FunctionId>) {
return IdKind::FunctionId;
+31 -31
View File
@@ -19,18 +19,18 @@ class PendingBlock {
PendingBlock(const PendingBlock&) = delete;
PendingBlock& operator=(const PendingBlock&) = delete;
// A scope in which we will tentatively add nodes to a pending block. If we
// leave the scope without inserting or merging the block, nodes added after
// this point will be removed again.
class DiscardUnusedNodesScope {
// A scope in which we will tentatively add instructions to a pending block.
// If we leave the scope without inserting or merging the block, instructions
// added after this point will be removed again.
class DiscardUnusedInstsScope {
public:
// If `block` is not null, enters the scope. If `block` is null, this object
// has no effect.
DiscardUnusedNodesScope(PendingBlock* block)
: block_(block), size_(block ? block->nodes_.size() : 0) {}
~DiscardUnusedNodesScope() {
if (block_ && block_->nodes_.size() > size_) {
block_->nodes_.truncate(size_);
DiscardUnusedInstsScope(PendingBlock* block)
: block_(block), size_(block ? block->insts_.size() : 0) {}
~DiscardUnusedInstsScope() {
if (block_ && block_->insts_.size() > size_) {
block_->insts_.truncate(size_);
}
}
@@ -39,52 +39,52 @@ class PendingBlock {
size_t size_;
};
auto AddNode(SemIR::Node node) -> SemIR::NodeId {
auto node_id = context_.nodes().AddInNoBlock(node);
nodes_.push_back(node_id);
return node_id;
auto AddInst(SemIR::Inst inst) -> SemIR::InstId {
auto inst_id = context_.insts().AddInNoBlock(inst);
insts_.push_back(inst_id);
return inst_id;
}
// Insert the pending block of code at the current position.
auto InsertHere() -> void {
for (auto id : nodes_) {
context_.node_block_stack().AddNodeId(id);
for (auto id : insts_) {
context_.inst_block_stack().AddInstId(id);
}
nodes_.clear();
insts_.clear();
}
// Replace the node at target_id with the nodes in this block. The new value
// for target_id should be value_id.
auto MergeReplacing(SemIR::NodeId target_id, SemIR::NodeId value_id) -> void {
auto value = context_.nodes().Get(value_id);
// Replace the instruction at target_id with the instructions in this block.
// The new value for target_id should be value_id.
auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id) -> void {
auto value = context_.insts().Get(value_id);
// There are three cases here:
if (nodes_.empty()) {
if (insts_.empty()) {
// 1) The block is empty. Replace `target_id` with an empty splice
// pointing at `value_id`.
context_.nodes().Set(
context_.insts().Set(
target_id, SemIR::SpliceBlock{value.parse_node(), value.type_id(),
SemIR::NodeBlockId::Empty, value_id});
} else if (nodes_.size() == 1 && nodes_[0] == value_id) {
// 2) The block is {value_id}. Replace `target_id` with the node referred
// to by `value_id`. This is intended to be the common case.
context_.nodes().Set(target_id, value);
SemIR::InstBlockId::Empty, value_id});
} else if (insts_.size() == 1 && insts_[0] == value_id) {
// 2) The block is {value_id}. Replace `target_id` with the instruction
// referred to by `value_id`. This is intended to be the common case.
context_.insts().Set(target_id, value);
} else {
// 3) Anything else: splice it into the IR, replacing `target_id`.
context_.nodes().Set(
context_.insts().Set(
target_id,
SemIR::SpliceBlock{value.parse_node(), value.type_id(),
context_.node_blocks().Add(nodes_), value_id});
context_.inst_blocks().Add(insts_), value_id});
}
// Prepare to stash more pending instructions.
nodes_.clear();
insts_.clear();
}
private:
Context& context_;
llvm::SmallVector<SemIR::NodeId> nodes_;
llvm::SmallVector<SemIR::InstId> insts_;
};
} // namespace Carbon::Check
+11 -11
View File
@@ -14,16 +14,16 @@
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types: {}
// CHECK:STDOUT: type_blocks: {}
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: nodeTypeType: {kind: CrossReference, arg0: ir0, arg1: nodeTypeType, type: typeTypeType}
// CHECK:STDOUT: nodeError: {kind: CrossReference, arg0: ir0, arg1: nodeError, type: typeError}
// CHECK:STDOUT: nodeBoolType: {kind: CrossReference, arg0: ir0, arg1: nodeBoolType, type: typeTypeType}
// CHECK:STDOUT: nodeIntegerType: {kind: CrossReference, arg0: ir0, arg1: nodeIntegerType, type: typeTypeType}
// CHECK:STDOUT: nodeFloatingPointType: {kind: CrossReference, arg0: ir0, arg1: nodeFloatingPointType, type: typeTypeType}
// CHECK:STDOUT: nodeStringType: {kind: CrossReference, arg0: ir0, arg1: nodeStringType, type: typeTypeType}
// CHECK:STDOUT: nodeFunctionType: {kind: CrossReference, arg0: ir0, arg1: nodeFunctionType, type: typeTypeType}
// CHECK:STDOUT: nodeBoundMethodType: {kind: CrossReference, arg0: ir0, arg1: nodeBoundMethodType, type: typeTypeType}
// CHECK:STDOUT: nodeNamespaceType: {kind: CrossReference, arg0: ir0, arg1: nodeNamespaceType, type: typeTypeType}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: instTypeType: {kind: CrossReference, arg0: ir0, arg1: instTypeType, type: typeTypeType}
// CHECK:STDOUT: instError: {kind: CrossReference, arg0: ir0, arg1: instError, type: typeError}
// CHECK:STDOUT: instBoolType: {kind: CrossReference, arg0: ir0, arg1: instBoolType, type: typeTypeType}
// CHECK:STDOUT: instIntegerType: {kind: CrossReference, arg0: ir0, arg1: instIntegerType, type: typeTypeType}
// CHECK:STDOUT: instFloatingPointType: {kind: CrossReference, arg0: ir0, arg1: instFloatingPointType, type: typeTypeType}
// CHECK:STDOUT: instStringType: {kind: CrossReference, arg0: ir0, arg1: instStringType, type: typeTypeType}
// CHECK:STDOUT: instFunctionType: {kind: CrossReference, arg0: ir0, arg1: instFunctionType, type: typeTypeType}
// CHECK:STDOUT: instBoundMethodType: {kind: CrossReference, arg0: ir0, arg1: instBoundMethodType, type: typeTypeType}
// CHECK:STDOUT: instNamespaceType: {kind: CrossReference, arg0: ir0, arg1: instNamespaceType, type: typeTypeType}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: ...
@@ -22,17 +22,17 @@ fn B() {}
// CHECK:STDOUT: function0: {name: str0, param_refs: block0, body: [block1]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type0: {inst: instFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type_blocks: {}
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: node+1: {kind: Return}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: inst+1: {kind: Return}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+1
// CHECK:STDOUT: 0: inst+1
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: ...
// CHECK:STDOUT:
// CHECK:STDOUT: file "a.carbon" {
@@ -51,17 +51,17 @@ fn B() {}
// CHECK:STDOUT: function0: {name: str0, param_refs: block0, body: [block1]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type0: {inst: instFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type_blocks: {}
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: node+1: {kind: Return}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: inst+1: {kind: Return}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+1
// CHECK:STDOUT: 0: inst+1
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: ...
// CHECK:STDOUT:
// CHECK:STDOUT: file "b.carbon" {
+14 -14
View File
@@ -22,17 +22,17 @@ fn B() {}
// CHECK:STDOUT: function0: {name: str0, param_refs: block0, body: [block1]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type0: {inst: instFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type_blocks: {}
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: node+1: {kind: Return}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: inst+1: {kind: Return}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+1
// CHECK:STDOUT: 0: inst+1
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: ...
// CHECK:STDOUT: ---
// CHECK:STDOUT: filename: b.carbon
@@ -42,15 +42,15 @@ fn B() {}
// CHECK:STDOUT: function0: {name: str0, param_refs: block0, body: [block1]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type0: {inst: instFunctionType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type_blocks: {}
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: node+1: {kind: Return}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: FunctionDeclaration, arg0: function0, type: type0}
// CHECK:STDOUT: inst+1: {kind: Return}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+1
// CHECK:STDOUT: 0: inst+1
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: ...
+52 -52
View File
@@ -17,15 +17,15 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: cross_reference_irs_size: 1
// CHECK:STDOUT: functions:
// CHECK:STDOUT: function0: {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
// CHECK:STDOUT: function0: {name: str0, param_refs: block1, return_type: type3, return_slot: inst+4, body: [block4]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
// CHECK:STDOUT: type2: {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
// CHECK:STDOUT: type3: {node: node+3, value_rep: {kind: pointer, type: type4}}
// CHECK:STDOUT: type4: {node: node+5, value_rep: {kind: copy, type: type4}}
// CHECK:STDOUT: type5: {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
// CHECK:STDOUT: type0: {inst: instIntegerType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {inst: inst+1, value_rep: {kind: unknown, type: type<invalid>}}
// CHECK:STDOUT: type2: {inst: instFloatingPointType, value_rep: {kind: copy, type: type2}}
// CHECK:STDOUT: type3: {inst: inst+3, value_rep: {kind: pointer, type: type4}}
// CHECK:STDOUT: type4: {inst: inst+5, value_rep: {kind: copy, type: type4}}
// CHECK:STDOUT: type5: {inst: instFunctionType, value_rep: {kind: copy, type: type5}}
// CHECK:STDOUT: type_blocks:
// CHECK:STDOUT: typeBlock0:
// CHECK:STDOUT: 0: typeTypeType
@@ -33,59 +33,59 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: typeBlock1:
// CHECK:STDOUT: 0: type0
// CHECK:STDOUT: 1: type2
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: Parameter, arg0: str1, type: type0}
// CHECK:STDOUT: node+1: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
// CHECK:STDOUT: node+2: {kind: TupleLiteral, arg0: block2, type: type1}
// CHECK:STDOUT: node+3: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
// CHECK:STDOUT: node+4: {kind: VarStorage, arg0: str2, type: type3}
// CHECK:STDOUT: node+5: {kind: PointerType, arg0: type3, type: typeTypeType}
// CHECK:STDOUT: node+6: {kind: FunctionDeclaration, arg0: function0, type: type5}
// CHECK:STDOUT: node+7: {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
// CHECK:STDOUT: node+8: {kind: IntegerLiteral, arg0: int3, type: type0}
// CHECK:STDOUT: node+9: {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
// CHECK:STDOUT: node+10: {kind: RealLiteral, arg0: real0, type: type2}
// CHECK:STDOUT: node+11: {kind: TupleLiteral, arg0: block5, type: type3}
// CHECK:STDOUT: node+12: {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
// CHECK:STDOUT: node+13: {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
// CHECK:STDOUT: node+14: {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
// CHECK:STDOUT: node+15: {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
// CHECK:STDOUT: node+16: {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
// CHECK:STDOUT: node+17: {kind: ReturnExpression, arg0: node+16}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: Parameter, arg0: str1, type: type0}
// CHECK:STDOUT: inst+1: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
// CHECK:STDOUT: inst+2: {kind: TupleLiteral, arg0: block2, type: type1}
// CHECK:STDOUT: inst+3: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
// CHECK:STDOUT: inst+4: {kind: VarStorage, arg0: str2, type: type3}
// CHECK:STDOUT: inst+5: {kind: PointerType, arg0: type3, type: typeTypeType}
// CHECK:STDOUT: inst+6: {kind: FunctionDeclaration, arg0: function0, type: type5}
// CHECK:STDOUT: inst+7: {kind: NameReference, arg0: str1, arg1: inst+0, type: type0}
// CHECK:STDOUT: inst+8: {kind: IntegerLiteral, arg0: int3, type: type0}
// CHECK:STDOUT: inst+9: {kind: BinaryOperatorAdd, arg0: inst+7, arg1: inst+8, type: type0}
// CHECK:STDOUT: inst+10: {kind: RealLiteral, arg0: real0, type: type2}
// CHECK:STDOUT: inst+11: {kind: TupleLiteral, arg0: block5, type: type3}
// CHECK:STDOUT: inst+12: {kind: TupleAccess, arg0: inst+4, arg1: member0, type: type0}
// CHECK:STDOUT: inst+13: {kind: InitializeFrom, arg0: inst+9, arg1: inst+12, type: type0}
// CHECK:STDOUT: inst+14: {kind: TupleAccess, arg0: inst+4, arg1: member1, type: type2}
// CHECK:STDOUT: inst+15: {kind: InitializeFrom, arg0: inst+10, arg1: inst+14, type: type2}
// CHECK:STDOUT: inst+16: {kind: TupleInit, arg0: inst+11, arg1: block6, type: type3}
// CHECK:STDOUT: inst+17: {kind: ReturnExpression, arg0: inst+16}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: nodeIntegerType
// CHECK:STDOUT: 1: nodeFloatingPointType
// CHECK:STDOUT: 0: instIntegerType
// CHECK:STDOUT: 1: instFloatingPointType
// CHECK:STDOUT: block3:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 1: node+1
// CHECK:STDOUT: 2: node+2
// CHECK:STDOUT: 3: node+3
// CHECK:STDOUT: 4: node+4
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: 1: inst+1
// CHECK:STDOUT: 2: inst+2
// CHECK:STDOUT: 3: inst+3
// CHECK:STDOUT: 4: inst+4
// CHECK:STDOUT: block4:
// CHECK:STDOUT: 0: node+7
// CHECK:STDOUT: 1: node+8
// CHECK:STDOUT: 2: node+9
// CHECK:STDOUT: 3: node+10
// CHECK:STDOUT: 4: node+11
// CHECK:STDOUT: 5: node+12
// CHECK:STDOUT: 6: node+13
// CHECK:STDOUT: 7: node+14
// CHECK:STDOUT: 8: node+15
// CHECK:STDOUT: 9: node+16
// CHECK:STDOUT: 10: node+17
// CHECK:STDOUT: 0: inst+7
// CHECK:STDOUT: 1: inst+8
// CHECK:STDOUT: 2: inst+9
// CHECK:STDOUT: 3: inst+10
// CHECK:STDOUT: 4: inst+11
// CHECK:STDOUT: 5: inst+12
// CHECK:STDOUT: 6: inst+13
// CHECK:STDOUT: 7: inst+14
// CHECK:STDOUT: 8: inst+15
// CHECK:STDOUT: 9: inst+16
// CHECK:STDOUT: 10: inst+17
// CHECK:STDOUT: block5:
// CHECK:STDOUT: 0: node+9
// CHECK:STDOUT: 1: node+10
// CHECK:STDOUT: 0: inst+9
// CHECK:STDOUT: 1: inst+10
// CHECK:STDOUT: block6:
// CHECK:STDOUT: 0: node+13
// CHECK:STDOUT: 1: node+15
// CHECK:STDOUT: 0: inst+13
// CHECK:STDOUT: 1: inst+15
// CHECK:STDOUT: block7:
// CHECK:STDOUT: 0: node+5
// CHECK:STDOUT: 1: node+6
// CHECK:STDOUT: 0: inst+5
// CHECK:STDOUT: 1: inst+6
// CHECK:STDOUT: ...
// CHECK:STDOUT:
// CHECK:STDOUT: file "raw_and_textual_ir.carbon" {
+52 -52
View File
@@ -17,15 +17,15 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: sem_ir:
// CHECK:STDOUT: cross_reference_irs_size: 1
// CHECK:STDOUT: functions:
// CHECK:STDOUT: function0: {name: str0, param_refs: block1, return_type: type3, return_slot: node+4, body: [block4]}
// CHECK:STDOUT: function0: {name: str0, param_refs: block1, return_type: type3, return_slot: inst+4, body: [block4]}
// CHECK:STDOUT: classes: {}
// CHECK:STDOUT: types:
// CHECK:STDOUT: type0: {node: nodeIntegerType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {node: node+1, value_rep: {kind: unknown, type: type<invalid>}}
// CHECK:STDOUT: type2: {node: nodeFloatingPointType, value_rep: {kind: copy, type: type2}}
// CHECK:STDOUT: type3: {node: node+3, value_rep: {kind: pointer, type: type4}}
// CHECK:STDOUT: type4: {node: node+5, value_rep: {kind: copy, type: type4}}
// CHECK:STDOUT: type5: {node: nodeFunctionType, value_rep: {kind: copy, type: type5}}
// CHECK:STDOUT: type0: {inst: instIntegerType, value_rep: {kind: copy, type: type0}}
// CHECK:STDOUT: type1: {inst: inst+1, value_rep: {kind: unknown, type: type<invalid>}}
// CHECK:STDOUT: type2: {inst: instFloatingPointType, value_rep: {kind: copy, type: type2}}
// CHECK:STDOUT: type3: {inst: inst+3, value_rep: {kind: pointer, type: type4}}
// CHECK:STDOUT: type4: {inst: inst+5, value_rep: {kind: copy, type: type4}}
// CHECK:STDOUT: type5: {inst: instFunctionType, value_rep: {kind: copy, type: type5}}
// CHECK:STDOUT: type_blocks:
// CHECK:STDOUT: typeBlock0:
// CHECK:STDOUT: 0: typeTypeType
@@ -33,57 +33,57 @@ fn Foo(n: i32) -> (i32, f64) {
// CHECK:STDOUT: typeBlock1:
// CHECK:STDOUT: 0: type0
// CHECK:STDOUT: 1: type2
// CHECK:STDOUT: nodes:
// CHECK:STDOUT: node+0: {kind: Parameter, arg0: str1, type: type0}
// CHECK:STDOUT: node+1: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
// CHECK:STDOUT: node+2: {kind: TupleLiteral, arg0: block2, type: type1}
// CHECK:STDOUT: node+3: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
// CHECK:STDOUT: node+4: {kind: VarStorage, arg0: str2, type: type3}
// CHECK:STDOUT: node+5: {kind: PointerType, arg0: type3, type: typeTypeType}
// CHECK:STDOUT: node+6: {kind: FunctionDeclaration, arg0: function0, type: type5}
// CHECK:STDOUT: node+7: {kind: NameReference, arg0: str1, arg1: node+0, type: type0}
// CHECK:STDOUT: node+8: {kind: IntegerLiteral, arg0: int3, type: type0}
// CHECK:STDOUT: node+9: {kind: BinaryOperatorAdd, arg0: node+7, arg1: node+8, type: type0}
// CHECK:STDOUT: node+10: {kind: RealLiteral, arg0: real0, type: type2}
// CHECK:STDOUT: node+11: {kind: TupleLiteral, arg0: block5, type: type3}
// CHECK:STDOUT: node+12: {kind: TupleAccess, arg0: node+4, arg1: member0, type: type0}
// CHECK:STDOUT: node+13: {kind: InitializeFrom, arg0: node+9, arg1: node+12, type: type0}
// CHECK:STDOUT: node+14: {kind: TupleAccess, arg0: node+4, arg1: member1, type: type2}
// CHECK:STDOUT: node+15: {kind: InitializeFrom, arg0: node+10, arg1: node+14, type: type2}
// CHECK:STDOUT: node+16: {kind: TupleInit, arg0: node+11, arg1: block6, type: type3}
// CHECK:STDOUT: node+17: {kind: ReturnExpression, arg0: node+16}
// CHECK:STDOUT: node_blocks:
// CHECK:STDOUT: insts:
// CHECK:STDOUT: inst+0: {kind: Parameter, arg0: str1, type: type0}
// CHECK:STDOUT: inst+1: {kind: TupleType, arg0: typeBlock0, type: typeTypeType}
// CHECK:STDOUT: inst+2: {kind: TupleLiteral, arg0: block2, type: type1}
// CHECK:STDOUT: inst+3: {kind: TupleType, arg0: typeBlock1, type: typeTypeType}
// CHECK:STDOUT: inst+4: {kind: VarStorage, arg0: str2, type: type3}
// CHECK:STDOUT: inst+5: {kind: PointerType, arg0: type3, type: typeTypeType}
// CHECK:STDOUT: inst+6: {kind: FunctionDeclaration, arg0: function0, type: type5}
// CHECK:STDOUT: inst+7: {kind: NameReference, arg0: str1, arg1: inst+0, type: type0}
// CHECK:STDOUT: inst+8: {kind: IntegerLiteral, arg0: int3, type: type0}
// CHECK:STDOUT: inst+9: {kind: BinaryOperatorAdd, arg0: inst+7, arg1: inst+8, type: type0}
// CHECK:STDOUT: inst+10: {kind: RealLiteral, arg0: real0, type: type2}
// CHECK:STDOUT: inst+11: {kind: TupleLiteral, arg0: block5, type: type3}
// CHECK:STDOUT: inst+12: {kind: TupleAccess, arg0: inst+4, arg1: member0, type: type0}
// CHECK:STDOUT: inst+13: {kind: InitializeFrom, arg0: inst+9, arg1: inst+12, type: type0}
// CHECK:STDOUT: inst+14: {kind: TupleAccess, arg0: inst+4, arg1: member1, type: type2}
// CHECK:STDOUT: inst+15: {kind: InitializeFrom, arg0: inst+10, arg1: inst+14, type: type2}
// CHECK:STDOUT: inst+16: {kind: TupleInit, arg0: inst+11, arg1: block6, type: type3}
// CHECK:STDOUT: inst+17: {kind: ReturnExpression, arg0: inst+16}
// CHECK:STDOUT: inst_blocks:
// CHECK:STDOUT: block0: {}
// CHECK:STDOUT: block1:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: block2:
// CHECK:STDOUT: 0: nodeIntegerType
// CHECK:STDOUT: 1: nodeFloatingPointType
// CHECK:STDOUT: 0: instIntegerType
// CHECK:STDOUT: 1: instFloatingPointType
// CHECK:STDOUT: block3:
// CHECK:STDOUT: 0: node+0
// CHECK:STDOUT: 1: node+1
// CHECK:STDOUT: 2: node+2
// CHECK:STDOUT: 3: node+3
// CHECK:STDOUT: 4: node+4
// CHECK:STDOUT: 0: inst+0
// CHECK:STDOUT: 1: inst+1
// CHECK:STDOUT: 2: inst+2
// CHECK:STDOUT: 3: inst+3
// CHECK:STDOUT: 4: inst+4
// CHECK:STDOUT: block4:
// CHECK:STDOUT: 0: node+7
// CHECK:STDOUT: 1: node+8
// CHECK:STDOUT: 2: node+9
// CHECK:STDOUT: 3: node+10
// CHECK:STDOUT: 4: node+11
// CHECK:STDOUT: 5: node+12
// CHECK:STDOUT: 6: node+13
// CHECK:STDOUT: 7: node+14
// CHECK:STDOUT: 8: node+15
// CHECK:STDOUT: 9: node+16
// CHECK:STDOUT: 10: node+17
// CHECK:STDOUT: 0: inst+7
// CHECK:STDOUT: 1: inst+8
// CHECK:STDOUT: 2: inst+9
// CHECK:STDOUT: 3: inst+10
// CHECK:STDOUT: 4: inst+11
// CHECK:STDOUT: 5: inst+12
// CHECK:STDOUT: 6: inst+13
// CHECK:STDOUT: 7: inst+14
// CHECK:STDOUT: 8: inst+15
// CHECK:STDOUT: 9: inst+16
// CHECK:STDOUT: 10: inst+17
// CHECK:STDOUT: block5:
// CHECK:STDOUT: 0: node+9
// CHECK:STDOUT: 1: node+10
// CHECK:STDOUT: 0: inst+9
// CHECK:STDOUT: 1: inst+10
// CHECK:STDOUT: block6:
// CHECK:STDOUT: 0: node+13
// CHECK:STDOUT: 1: node+15
// CHECK:STDOUT: 0: inst+13
// CHECK:STDOUT: 1: inst+15
// CHECK:STDOUT: block7:
// CHECK:STDOUT: 0: node+5
// CHECK:STDOUT: 1: node+6
// CHECK:STDOUT: 0: inst+5
// CHECK:STDOUT: 1: inst+6
// CHECK:STDOUT: ...
+4 -4
View File
@@ -8,10 +8,10 @@
// NOAUTOUPDATE
// SET-CHECK-SUBSET
// CHECK:STDERR: Node Push 0: FunctionIntroducer -> <none>
// CHECK:STDERR: AddNode: {kind: FunctionDeclaration, arg0: {{.*}}, type: type{{[0-9]+}}}
// CHECK:STDERR: node_block_stack_ Push 1
// CHECK:STDERR: AddNode: {kind: Return}
// CHECK:STDERR: node_block_stack_ Pop 1: block{{[0-9]+}}
// CHECK:STDERR: AddInst: {kind: FunctionDeclaration, arg0: {{.*}}, type: type{{[0-9]+}}}
// CHECK:STDERR: inst_block_stack_ Push 1
// CHECK:STDERR: AddInst: {kind: Return}
// CHECK:STDERR: inst_block_stack_ Pop 1: block{{[0-9]+}}
fn Foo() {
return;
+2 -2
View File
@@ -39,8 +39,8 @@ fn Class.H() {}
// CHECK:STDOUT: %H: <function> = fn_decl @H
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .F = <unexpected noderef 11>
// CHECK:STDOUT: .H = <unexpected noderef 12>
// CHECK:STDOUT: .F = <unexpected instref 11>
// CHECK:STDOUT: .H = <unexpected instref 12>
// CHECK:STDOUT: .G = %G
// CHECK:STDOUT: }
// CHECK:STDOUT:
+1 -1
View File
@@ -32,7 +32,7 @@ fn G(o: Outer) {
// CHECK:STDOUT: %.loc10: type = struct_type {.n: i32}
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Inner = <unexpected noderef 11>
// CHECK:STDOUT: .Inner = <unexpected instref 11>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Inner {
+2 -2
View File
@@ -43,8 +43,8 @@ cc_library(
"//common:vlog",
"//toolchain/sem_ir:entry_point",
"//toolchain/sem_ir:file",
"//toolchain/sem_ir:node",
"//toolchain/sem_ir:node_kind",
"//toolchain/sem_ir:inst",
"//toolchain/sem_ir:inst_kind",
"@llvm-project//llvm:Core",
"@llvm-project//llvm:Support",
],
+33 -33
View File
@@ -10,7 +10,7 @@
#include "toolchain/lower/function_context.h"
#include "toolchain/sem_ir/entry_point.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Lower {
@@ -35,7 +35,7 @@ auto FileContext::Run() -> std::unique_ptr<llvm::Module> {
// used.
types_.resize(sem_ir_->types().size());
for (auto type_id : sem_ir_->complete_types()) {
types_[type_id.index] = BuildType(sem_ir_->types().Get(type_id).node_id);
types_[type_id.index] = BuildType(sem_ir_->types().Get(type_id).inst_id);
}
// Lower function declarations.
@@ -61,8 +61,8 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id)
const auto& function = sem_ir().functions().Get(function_id);
const bool has_return_slot = function.return_slot_id.is_valid();
auto implicit_param_refs =
sem_ir().node_blocks().Get(function.implicit_param_refs_id);
auto param_refs = sem_ir().node_blocks().Get(function.param_refs_id);
sem_ir().inst_blocks().Get(function.implicit_param_refs_id);
auto param_refs = sem_ir().inst_blocks().Get(function.param_refs_id);
SemIR::InitializingRepresentation return_rep =
function.return_type_id.is_valid()
@@ -73,22 +73,22 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id)
CARBON_CHECK(return_rep.has_return_slot() == has_return_slot);
llvm::SmallVector<llvm::Type*> param_types;
// TODO: Consider either storing `param_node_ids` somewhere so that we can
// TODO: Consider either storing `param_inst_ids` somewhere so that we can
// reuse it from `BuildFunctionDefinition` and when building calls, or factor
// out a mechanism to compute the mapping between parameters and arguments on
// demand.
llvm::SmallVector<SemIR::NodeId> param_node_ids;
llvm::SmallVector<SemIR::InstId> param_inst_ids;
auto max_llvm_params =
has_return_slot + implicit_param_refs.size() + param_refs.size();
param_types.reserve(max_llvm_params);
param_node_ids.reserve(max_llvm_params);
param_inst_ids.reserve(max_llvm_params);
if (has_return_slot) {
param_types.push_back(GetType(function.return_type_id)->getPointerTo());
param_node_ids.push_back(function.return_slot_id);
param_inst_ids.push_back(function.return_slot_id);
}
for (auto param_ref_id :
llvm::concat<const SemIR::NodeId>(implicit_param_refs, param_refs)) {
auto param_type_id = sem_ir().nodes().Get(param_ref_id).type_id();
llvm::concat<const SemIR::InstId>(implicit_param_refs, param_refs)) {
auto param_type_id = sem_ir().insts().Get(param_ref_id).type_id();
switch (auto value_rep =
SemIR::GetValueRepresentation(sem_ir(), param_type_id);
value_rep.kind) {
@@ -101,7 +101,7 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id)
case SemIR::ValueRepresentation::Custom:
case SemIR::ValueRepresentation::Pointer:
param_types.push_back(GetType(value_rep.type_id));
param_node_ids.push_back(param_ref_id);
param_inst_ids.push_back(param_ref_id);
break;
}
}
@@ -129,17 +129,17 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id)
mangled_name, llvm_module());
// Set up parameters and the return slot.
for (auto [node_id, arg] :
llvm::zip_equal(param_node_ids, llvm_function->args())) {
auto node = sem_ir().nodes().Get(node_id);
if (node_id == function.return_slot_id) {
for (auto [inst_id, arg] :
llvm::zip_equal(param_inst_ids, llvm_function->args())) {
auto inst = sem_ir().insts().Get(inst_id);
if (inst_id == function.return_slot_id) {
arg.setName("return");
arg.addAttr(llvm::Attribute::getWithStructRetType(
llvm_context(), GetType(function.return_type_id)));
} else if (node.Is<SemIR::SelfParameter>()) {
} else if (inst.Is<SemIR::SelfParameter>()) {
arg.setName("self");
} else {
arg.setName(sem_ir().strings().Get(node.As<SemIR::Parameter>().name_id));
arg.setName(sem_ir().strings().Get(inst.As<SemIR::Parameter>().name_id));
}
}
@@ -161,12 +161,12 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
const bool has_return_slot = function.return_slot_id.is_valid();
// Add parameters to locals.
// TODO: This duplicates the mapping between semantics nodes and LLVM
// TODO: This duplicates the mapping between sem_ir instructions and LLVM
// function parameters that was already computed in BuildFunctionDeclaration.
// We should only do that once.
auto implicit_param_refs =
sem_ir().node_blocks().Get(function.implicit_param_refs_id);
auto param_refs = sem_ir().node_blocks().Get(function.param_refs_id);
sem_ir().inst_blocks().Get(function.implicit_param_refs_id);
auto param_refs = sem_ir().inst_blocks().Get(function.param_refs_id);
int param_index = 0;
if (has_return_slot) {
function_lowering.SetLocal(function.return_slot_id,
@@ -174,8 +174,8 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
++param_index;
}
for (auto param_ref_id :
llvm::concat<const SemIR::NodeId>(implicit_param_refs, param_refs)) {
auto param_type_id = sem_ir().nodes().Get(param_ref_id).type_id();
llvm::concat<const SemIR::InstId>(implicit_param_refs, param_refs)) {
auto param_type_id = sem_ir().insts().Get(param_ref_id).type_id();
if (SemIR::GetValueRepresentation(sem_ir(), param_type_id).kind ==
SemIR::ValueRepresentation::None) {
function_lowering.SetLocal(
@@ -206,8 +206,8 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id)
}
}
auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
switch (node_id.index) {
auto FileContext::BuildType(SemIR::InstId inst_id) -> llvm::Type* {
switch (inst_id.index) {
case SemIR::BuiltinKind::FloatingPointType.AsInt():
// TODO: Handle different sizes.
return llvm::Type::getDoubleTy(*llvm_context_);
@@ -228,10 +228,10 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
break;
}
auto node = sem_ir_->nodes().Get(node_id);
switch (node.kind()) {
auto inst = sem_ir_->insts().Get(inst_id);
switch (inst.kind()) {
case SemIR::ArrayType::Kind: {
auto array_type = node.As<SemIR::ArrayType>();
auto array_type = inst.As<SemIR::ArrayType>();
return llvm::ArrayType::get(
GetType(array_type.element_type_id),
sem_ir_->GetArrayBoundValue(array_type.bound_id));
@@ -239,21 +239,21 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
case SemIR::ClassType::Kind: {
auto object_representation_id =
sem_ir_->classes()
.Get(node.As<SemIR::ClassType>().class_id)
.Get(inst.As<SemIR::ClassType>().class_id)
.object_representation_id;
return GetType(object_representation_id);
}
case SemIR::ConstType::Kind:
return GetType(node.As<SemIR::ConstType>().inner_id);
return GetType(inst.As<SemIR::ConstType>().inner_id);
case SemIR::PointerType::Kind:
return llvm::PointerType::get(*llvm_context_, /*AddressSpace=*/0);
case SemIR::StructType::Kind: {
auto fields =
sem_ir_->node_blocks().Get(node.As<SemIR::StructType>().fields_id);
sem_ir_->inst_blocks().Get(inst.As<SemIR::StructType>().fields_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(fields.size());
for (auto field_id : fields) {
auto field = sem_ir_->nodes().GetAs<SemIR::StructTypeField>(field_id);
auto field = sem_ir_->insts().GetAs<SemIR::StructTypeField>(field_id);
// TODO: Handle recursive types. The restriction for builtins prevents
// recursion while still letting them cache.
CARBON_CHECK(field.field_type_id.index < SemIR::BuiltinKind::ValidCount)
@@ -268,7 +268,7 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
// function returns. LLVM doesn't allow declaring variables with a void
// type, so that may require significant special casing.
auto elements =
sem_ir_->type_blocks().Get(node.As<SemIR::TupleType>().elements_id);
sem_ir_->type_blocks().Get(inst.As<SemIR::TupleType>().elements_id);
llvm::SmallVector<llvm::Type*> subtypes;
subtypes.reserve(elements.size());
for (auto element_id : elements) {
@@ -281,7 +281,7 @@ auto FileContext::BuildType(SemIR::NodeId node_id) -> llvm::Type* {
return llvm::StructType::get(*llvm_context_);
}
default: {
CARBON_FATAL() << "Cannot use node as type: " << node_id << " " << node;
CARBON_FATAL() << "Cannot use inst as type: " << inst_id << " " << inst;
}
}
}
+4 -4
View File
@@ -9,7 +9,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Lower {
@@ -60,9 +60,9 @@ class FileContext {
// declaration with no definition, does nothing.
auto BuildFunctionDefinition(SemIR::FunctionId function_id) -> void;
// Builds the type for the given node, which should then be cached by the
// caller.
auto BuildType(SemIR::NodeId node_id) -> llvm::Type*;
// Builds the type for the given instruction, which should then be cached by
// the caller.
auto BuildType(SemIR::InstId inst_id) -> llvm::Type*;
// Returns the empty LLVM struct type used to represent the type `type`.
auto GetTypeType() -> llvm::StructType* {
+17 -17
View File
@@ -17,7 +17,7 @@ FunctionContext::FunctionContext(FileContext& file_context,
builder_(file_context.llvm_context()),
vlog_stream_(vlog_stream) {}
auto FunctionContext::GetBlock(SemIR::NodeBlockId block_id)
auto FunctionContext::GetBlock(SemIR::InstBlockId block_id)
-> llvm::BasicBlock* {
llvm::BasicBlock*& entry = blocks_[block_id];
if (!entry) {
@@ -26,7 +26,7 @@ auto FunctionContext::GetBlock(SemIR::NodeBlockId block_id)
return entry;
}
auto FunctionContext::TryToReuseBlock(SemIR::NodeBlockId block_id,
auto FunctionContext::TryToReuseBlock(SemIR::InstBlockId block_id,
llvm::BasicBlock* block) -> bool {
if (!blocks_.insert({block_id, block}).second) {
return false;
@@ -37,23 +37,23 @@ auto FunctionContext::TryToReuseBlock(SemIR::NodeBlockId block_id,
return true;
}
auto FunctionContext::LowerBlock(SemIR::NodeBlockId block_id) -> void {
for (const auto& node_id : sem_ir().node_blocks().Get(block_id)) {
auto node = sem_ir().nodes().Get(node_id);
CARBON_VLOG() << "Lowering " << node_id << ": " << node << "\n";
auto FunctionContext::LowerBlock(SemIR::InstBlockId block_id) -> void {
for (const auto& inst_id : sem_ir().inst_blocks().Get(block_id)) {
auto inst = sem_ir().insts().Get(inst_id);
CARBON_VLOG() << "Lowering " << inst_id << ": " << inst << "\n";
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (node.kind()) {
#define CARBON_SEM_IR_NODE_KIND(Name) \
switch (inst.kind()) {
#define CARBON_SEM_IR_INST_KIND(Name) \
case SemIR::Name::Kind: \
Handle##Name(*this, node_id, node.As<SemIR::Name>()); \
Handle##Name(*this, inst_id, inst.As<SemIR::Name>()); \
break;
#include "toolchain/sem_ir/node_kind.def"
}
}
}
auto FunctionContext::GetBlockArg(SemIR::NodeBlockId block_id,
auto FunctionContext::GetBlockArg(SemIR::InstBlockId block_id,
SemIR::TypeId type_id) -> llvm::PHINode* {
llvm::BasicBlock* block = GetBlock(block_id);
@@ -78,8 +78,8 @@ auto FunctionContext::CreateSyntheticBlock() -> llvm::BasicBlock* {
return synthetic_block_;
}
auto FunctionContext::GetLocalOrGlobal(SemIR::NodeId node_id) -> llvm::Value* {
auto target = sem_ir().nodes().Get(node_id);
auto FunctionContext::GetLocalOrGlobal(SemIR::InstId inst_id) -> llvm::Value* {
auto target = sem_ir().insts().Get(inst_id);
if (auto function_decl = target.TryAs<SemIR::FunctionDeclaration>()) {
return GetFunction(function_decl->function_id);
}
@@ -87,12 +87,12 @@ auto FunctionContext::GetLocalOrGlobal(SemIR::NodeId node_id) -> llvm::Value* {
return GetTypeAsValue();
}
// TODO: Handle other kinds of name references to globals.
return GetLocal(node_id);
return GetLocal(inst_id);
}
auto FunctionContext::FinishInitialization(SemIR::TypeId type_id,
SemIR::NodeId dest_id,
SemIR::NodeId source_id) -> void {
SemIR::InstId dest_id,
SemIR::InstId source_id) -> void {
switch (SemIR::GetInitializingRepresentation(sem_ir(), type_id).kind) {
case SemIR::InitializingRepresentation::None:
case SemIR::InitializingRepresentation::InPlace:
@@ -103,8 +103,8 @@ auto FunctionContext::FinishInitialization(SemIR::TypeId type_id,
}
}
auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::NodeId source_id,
SemIR::NodeId dest_id) -> void {
auto FunctionContext::CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id,
SemIR::InstId dest_id) -> void {
switch (auto rep = SemIR::GetValueRepresentation(sem_ir(), type_id);
rep.kind) {
case SemIR::ValueRepresentation::Unknown:
+29 -29
View File
@@ -10,7 +10,7 @@
#include "llvm/IR/Module.h"
#include "toolchain/lower/file_context.h"
#include "toolchain/sem_ir/file.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Lower {
@@ -23,44 +23,44 @@ class FunctionContext {
// Returns a basic block corresponding to the start of the given semantics
// block, and enqueues it for emission.
auto GetBlock(SemIR::NodeBlockId block_id) -> llvm::BasicBlock*;
auto GetBlock(SemIR::InstBlockId block_id) -> llvm::BasicBlock*;
// If we have not yet allocated a `BasicBlock` for this `block_id`, set it to
// `block`, and enqueue `block_id` for emission. Returns whether we set the
// block.
auto TryToReuseBlock(SemIR::NodeBlockId block_id, llvm::BasicBlock* block)
auto TryToReuseBlock(SemIR::InstBlockId block_id, llvm::BasicBlock* block)
-> bool;
// Builds LLVM IR for the sequence of instructions in `block_id`.
auto LowerBlock(SemIR::NodeBlockId block_id) -> void;
auto LowerBlock(SemIR::InstBlockId block_id) -> void;
// Returns a phi node corresponding to the block argument of the given basic
// block.
auto GetBlockArg(SemIR::NodeBlockId block_id, SemIR::TypeId type_id)
auto GetBlockArg(SemIR::InstBlockId block_id, SemIR::TypeId type_id)
-> llvm::PHINode*;
// Returns a local (versus global) value for the given node.
auto GetLocal(SemIR::NodeId node_id) -> llvm::Value* {
// Returns a local (versus global) value for the given instruction.
auto GetLocal(SemIR::InstId inst_id) -> llvm::Value* {
// All builtins are types, with the same empty lowered value.
if (node_id.index < SemIR::BuiltinKind::ValidCount) {
if (inst_id.index < SemIR::BuiltinKind::ValidCount) {
return GetTypeAsValue();
}
auto it = locals_.find(node_id);
auto it = locals_.find(inst_id);
CARBON_CHECK(it != locals_.end())
<< "Missing local: " << node_id << " " << sem_ir().nodes().Get(node_id);
<< "Missing local: " << inst_id << " " << sem_ir().insts().Get(inst_id);
return it->second;
}
// Sets the value for the given node.
auto SetLocal(SemIR::NodeId node_id, llvm::Value* value) {
bool added = locals_.insert({node_id, value}).second;
CARBON_CHECK(added) << "Duplicate local insert: " << node_id << " "
<< sem_ir().nodes().Get(node_id);
// Sets the value for the given instruction.
auto SetLocal(SemIR::InstId inst_id, llvm::Value* value) {
bool added = locals_.insert({inst_id, value}).second;
CARBON_CHECK(added) << "Duplicate local insert: " << inst_id << " "
<< sem_ir().insts().Get(inst_id);
}
// Returns a value for the given node, which might not be local.
auto GetLocalOrGlobal(SemIR::NodeId node_id) -> llvm::Value*;
// Returns a value for the given instruction, which might not be local.
auto GetLocalOrGlobal(SemIR::InstId inst_id) -> llvm::Value*;
// Gets a callable's function.
auto GetFunction(SemIR::FunctionId function_id) -> llvm::Function* {
@@ -77,7 +77,7 @@ class FunctionContext {
return file_context_->GetTypeAsValue();
}
// Create a synthetic block that corresponds to no SemIR::NodeBlockId. Such
// Create a synthetic block that corresponds to no SemIR::InstBlockId. Such
// a block should only ever have a single predecessor, and is used when we
// need multiple `llvm::BasicBlock`s to model the linear control flow in a
// single SemIR::File block.
@@ -92,8 +92,8 @@ class FunctionContext {
// initialization of `dest_id` from that initializer. This is a no-op if the
// initialization was performed in-place, and otherwise performs a store or a
// copy.
auto FinishInitialization(SemIR::TypeId type_id, SemIR::NodeId dest_id,
SemIR::NodeId init_id) -> void;
auto FinishInitialization(SemIR::TypeId type_id, SemIR::InstId dest_id,
SemIR::InstId init_id) -> void;
auto llvm_context() -> llvm::LLVMContext& {
return file_context_->llvm_context();
@@ -106,8 +106,8 @@ class FunctionContext {
// Emits a value copy for type `type_id` from `source_id` to `dest_id`.
// `source_id` must produce a value representation for `type_id`, and
// `dest_id` must be a pointer to a `type_id` object.
auto CopyValue(SemIR::TypeId type_id, SemIR::NodeId source_id,
SemIR::NodeId dest_id) -> void;
auto CopyValue(SemIR::TypeId type_id, SemIR::InstId source_id,
SemIR::InstId dest_id) -> void;
// Context for the overall lowering process.
FileContext* file_context_;
@@ -121,22 +121,22 @@ class FunctionContext {
llvm::raw_ostream* vlog_stream_;
// Maps a function's SemIR::File blocks to lowered blocks.
llvm::DenseMap<SemIR::NodeBlockId, llvm::BasicBlock*> blocks_;
llvm::DenseMap<SemIR::InstBlockId, llvm::BasicBlock*> blocks_;
// The synthetic block we most recently created. May be null if there is no
// such block.
llvm::BasicBlock* synthetic_block_ = nullptr;
// Maps a function's SemIR::File nodes to lowered values.
// Maps a function's SemIR::File instructions to lowered values.
// TODO: Handle nested scopes. Right now this is just cleared at the end of
// every block.
llvm::DenseMap<SemIR::NodeId, llvm::Value*> locals_;
llvm::DenseMap<SemIR::InstId, llvm::Value*> locals_;
};
// Declare handlers for each SemIR::File node.
#define CARBON_SEM_IR_NODE_KIND(Name) \
auto Handle##Name(FunctionContext& context, SemIR::NodeId node_id, \
SemIR::Name node) \
// Declare handlers for each SemIR::File instruction.
#define CARBON_SEM_IR_INST_KIND(Name) \
auto Handle##Name(FunctionContext& context, SemIR::InstId inst_id, \
SemIR::Name inst) \
->void;
#include "toolchain/sem_ir/node_kind.def"
+185 -185
View File
@@ -5,109 +5,109 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include "toolchain/lower/function_context.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::Lower {
auto HandleCrossReference(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/, SemIR::CrossReference node)
SemIR::InstId /*inst_id*/, SemIR::CrossReference inst)
-> void {
CARBON_FATAL() << "TODO: Add support: " << node;
CARBON_FATAL() << "TODO: Add support: " << inst;
}
auto HandleAddressOf(FunctionContext& context, SemIR::NodeId node_id,
SemIR::AddressOf node) -> void {
context.SetLocal(node_id, context.GetLocal(node.lvalue_id));
auto HandleAddressOf(FunctionContext& context, SemIR::InstId inst_id,
SemIR::AddressOf inst) -> void {
context.SetLocal(inst_id, context.GetLocal(inst.lvalue_id));
}
auto HandleArrayIndex(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ArrayIndex node) -> void {
auto* array_value = context.GetLocal(node.array_id);
auto HandleArrayIndex(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ArrayIndex inst) -> void {
auto* array_value = context.GetLocal(inst.array_id);
auto* llvm_type =
context.GetType(context.sem_ir().nodes().Get(node.array_id).type_id());
context.GetType(context.sem_ir().insts().Get(inst.array_id).type_id());
llvm::Value* indexes[2] = {
llvm::ConstantInt::get(llvm::Type::getInt32Ty(context.llvm_context()), 0),
context.GetLocal(node.index_id)};
context.SetLocal(node_id,
context.GetLocal(inst.index_id)};
context.SetLocal(inst_id,
context.builder().CreateInBoundsGEP(llvm_type, array_value,
indexes, "array.index"));
}
auto HandleArrayInit(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ArrayInit node) -> void {
auto HandleArrayInit(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ArrayInit inst) -> void {
// The result of initialization is the return slot of the initializer.
context.SetLocal(node_id,
context.SetLocal(inst_id,
context.GetLocal(context.sem_ir()
.node_blocks()
.Get(node.inits_and_return_slot_id)
.inst_blocks()
.Get(inst.inits_and_return_slot_id)
.back()));
}
auto HandleAssign(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::Assign node) -> void {
auto storage_type_id = context.sem_ir().nodes().Get(node.lhs_id).type_id();
context.FinishInitialization(storage_type_id, node.lhs_id, node.rhs_id);
auto HandleAssign(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::Assign inst) -> void {
auto storage_type_id = context.sem_ir().insts().Get(inst.lhs_id).type_id();
context.FinishInitialization(storage_type_id, inst.lhs_id, inst.rhs_id);
}
auto HandleBinaryOperatorAdd(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::BinaryOperatorAdd node) -> void {
CARBON_FATAL() << "TODO: Add support: " << node;
SemIR::InstId /*inst_id*/,
SemIR::BinaryOperatorAdd inst) -> void {
CARBON_FATAL() << "TODO: Add support: " << inst;
}
auto HandleBindName(FunctionContext& context, SemIR::NodeId node_id,
SemIR::BindName node) -> void {
context.SetLocal(node_id, context.GetLocal(node.value_id));
auto HandleBindName(FunctionContext& context, SemIR::InstId inst_id,
SemIR::BindName inst) -> void {
context.SetLocal(inst_id, context.GetLocal(inst.value_id));
}
auto HandleBlockArg(FunctionContext& context, SemIR::NodeId node_id,
SemIR::BlockArg node) -> void {
context.SetLocal(node_id, context.GetBlockArg(node.block_id, node.type_id));
auto HandleBlockArg(FunctionContext& context, SemIR::InstId inst_id,
SemIR::BlockArg inst) -> void {
context.SetLocal(inst_id, context.GetBlockArg(inst.block_id, inst.type_id));
}
auto HandleBoolLiteral(FunctionContext& context, SemIR::NodeId node_id,
SemIR::BoolLiteral node) -> void {
auto HandleBoolLiteral(FunctionContext& context, SemIR::InstId inst_id,
SemIR::BoolLiteral inst) -> void {
llvm::Value* v =
llvm::ConstantInt::get(context.builder().getInt1Ty(), node.value.index);
context.SetLocal(node_id, v);
llvm::ConstantInt::get(context.builder().getInt1Ty(), inst.value.index);
context.SetLocal(inst_id, v);
}
auto HandleBoundMethod(FunctionContext& context, SemIR::NodeId node_id,
SemIR::BoundMethod node) -> void {
auto HandleBoundMethod(FunctionContext& context, SemIR::InstId inst_id,
SemIR::BoundMethod inst) -> void {
// Propagate just the function; the object is separately provided to the
// enclosing call as an implicit argument.
context.SetLocal(node_id, context.GetLocalOrGlobal(node.function_id));
context.SetLocal(inst_id, context.GetLocalOrGlobal(inst.function_id));
}
auto HandleBranch(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::Branch node) -> void {
auto HandleBranch(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::Branch inst) -> void {
// Opportunistically avoid creating a BasicBlock that contains just a branch.
// TODO: Don't do this if it would remove a loop preheader block.
llvm::BasicBlock* block = context.builder().GetInsertBlock();
if (block->empty() && context.TryToReuseBlock(node.target_id, block)) {
if (block->empty() && context.TryToReuseBlock(inst.target_id, block)) {
// Reuse this block as the branch target.
} else {
context.builder().CreateBr(context.GetBlock(node.target_id));
context.builder().CreateBr(context.GetBlock(inst.target_id));
}
context.builder().ClearInsertionPoint();
}
auto HandleBranchIf(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::BranchIf node) -> void {
llvm::Value* cond = context.GetLocal(node.cond_id);
llvm::BasicBlock* then_block = context.GetBlock(node.target_id);
auto HandleBranchIf(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::BranchIf inst) -> void {
llvm::Value* cond = context.GetLocal(inst.cond_id);
llvm::BasicBlock* then_block = context.GetBlock(inst.target_id);
llvm::BasicBlock* else_block = context.CreateSyntheticBlock();
context.builder().CreateCondBr(cond, then_block, else_block);
context.builder().SetInsertPoint(else_block);
}
auto HandleBranchWithArg(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::BranchWithArg node) -> void {
llvm::Value* arg = context.GetLocal(node.arg_id);
auto HandleBranchWithArg(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::BranchWithArg inst) -> void {
llvm::Value* arg = context.GetLocal(inst.arg_id);
SemIR::TypeId arg_type_id =
context.sem_ir().nodes().Get(node.arg_id).type_id();
context.sem_ir().insts().Get(inst.arg_id).type_id();
// Opportunistically avoid creating a BasicBlock that contains just a branch.
// We only do this for a block that we know will only have a single
@@ -116,41 +116,41 @@ auto HandleBranchWithArg(FunctionContext& context, SemIR::NodeId /*node_id*/,
llvm::BasicBlock* block = context.builder().GetInsertBlock();
llvm::BasicBlock* phi_predecessor = block;
if (block->empty() && context.IsCurrentSyntheticBlock(block) &&
context.TryToReuseBlock(node.target_id, block)) {
context.TryToReuseBlock(inst.target_id, block)) {
// Reuse this block as the branch target.
phi_predecessor = block->getSinglePredecessor();
CARBON_CHECK(phi_predecessor)
<< "Synthetic block did not have a single predecessor";
} else {
context.builder().CreateBr(context.GetBlock(node.target_id));
context.builder().CreateBr(context.GetBlock(inst.target_id));
}
context.GetBlockArg(node.target_id, arg_type_id)
context.GetBlockArg(inst.target_id, arg_type_id)
->addIncoming(arg, phi_predecessor);
context.builder().ClearInsertionPoint();
}
auto HandleBuiltin(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::Builtin node) -> void {
CARBON_FATAL() << "TODO: Add support: " << node;
auto HandleBuiltin(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::Builtin inst) -> void {
CARBON_FATAL() << "TODO: Add support: " << inst;
}
auto HandleCall(FunctionContext& context, SemIR::NodeId node_id,
SemIR::Call node) -> void {
auto* callee = llvm::cast<llvm::Function>(context.GetLocal(node.callee_id));
auto HandleCall(FunctionContext& context, SemIR::InstId inst_id,
SemIR::Call inst) -> void {
auto* callee = llvm::cast<llvm::Function>(context.GetLocal(inst.callee_id));
std::vector<llvm::Value*> args;
llvm::ArrayRef<SemIR::NodeId> arg_ids =
context.sem_ir().node_blocks().Get(node.args_id);
llvm::ArrayRef<SemIR::InstId> arg_ids =
context.sem_ir().inst_blocks().Get(inst.args_id);
if (SemIR::GetInitializingRepresentation(context.sem_ir(), node.type_id)
if (SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
.has_return_slot()) {
args.push_back(context.GetLocal(arg_ids.back()));
arg_ids = arg_ids.drop_back();
}
for (auto arg_id : arg_ids) {
auto arg_type_id = context.sem_ir().nodes().Get(arg_id).type_id();
auto arg_type_id = context.sem_ir().insts().Get(arg_id).type_id();
if (SemIR::GetValueRepresentation(context.sem_ir(), arg_type_id).kind !=
SemIR::ValueRepresentation::None) {
args.push_back(context.GetLocal(arg_id));
@@ -158,7 +158,7 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id,
}
auto* call = context.builder().CreateCall(callee, args);
context.SetLocal(node_id, call);
context.SetLocal(inst_id, call);
// Name the call's result the same as the callee.
// TODO: Is this a helpful name?
@@ -168,8 +168,8 @@ auto HandleCall(FunctionContext& context, SemIR::NodeId node_id,
}
auto HandleClassDeclaration(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::ClassDeclaration /*node*/) -> void {
SemIR::InstId /*inst_id*/,
SemIR::ClassDeclaration /*inst*/) -> void {
// No action to perform.
}
@@ -177,14 +177,14 @@ auto HandleClassDeclaration(FunctionContext& /*context*/,
// index. Depending on the expression category and value representation of the
// aggregate input, this will either produce a value or a reference.
static auto GetAggregateElement(FunctionContext& context,
SemIR::NodeId aggr_node_id,
SemIR::InstId aggr_inst_id,
SemIR::MemberIndex idx,
SemIR::TypeId result_type_id, llvm::Twine name)
-> llvm::Value* {
auto aggr_node = context.sem_ir().nodes().Get(aggr_node_id);
auto* aggr_value = context.GetLocal(aggr_node_id);
auto aggr_inst = context.sem_ir().insts().Get(aggr_inst_id);
auto* aggr_value = context.GetLocal(aggr_inst_id);
switch (SemIR::GetExpressionCategory(context.sem_ir(), aggr_node_id)) {
switch (SemIR::GetExpressionCategory(context.sem_ir(), aggr_inst_id)) {
case SemIR::ExpressionCategory::Error:
case SemIR::ExpressionCategory::NotExpression:
case SemIR::ExpressionCategory::Initializing:
@@ -193,7 +193,7 @@ static auto GetAggregateElement(FunctionContext& context,
case SemIR::ExpressionCategory::Value: {
auto value_rep =
SemIR::GetValueRepresentation(context.sem_ir(), aggr_node.type_id());
SemIR::GetValueRepresentation(context.sem_ir(), aggr_inst.type_id());
CARBON_CHECK(value_rep.aggregate_kind !=
SemIR::ValueRepresentation::NotAggregate)
<< "aggregate type should have aggregate value representation";
@@ -237,7 +237,7 @@ static auto GetAggregateElement(FunctionContext& context,
case SemIR::ExpressionCategory::DurableReference:
case SemIR::ExpressionCategory::EphemeralReference: {
// Just locate the aggregate element.
auto* aggr_type = context.GetType(aggr_node.type_id());
auto* aggr_type = context.GetType(aggr_inst.type_id());
return context.builder().CreateStructGEP(aggr_type, aggr_value, idx.index,
name);
}
@@ -247,24 +247,24 @@ static auto GetAggregateElement(FunctionContext& context,
static auto GetStructFieldName(FunctionContext& context,
SemIR::TypeId struct_type_id,
SemIR::MemberIndex index) -> llvm::StringRef {
auto fields = context.sem_ir().node_blocks().Get(
auto fields = context.sem_ir().inst_blocks().Get(
context.sem_ir()
.nodes()
.insts()
.GetAs<SemIR::StructType>(
context.sem_ir().types().Get(struct_type_id).node_id)
context.sem_ir().types().Get(struct_type_id).inst_id)
.fields_id);
auto field = context.sem_ir().nodes().GetAs<SemIR::StructTypeField>(
auto field = context.sem_ir().insts().GetAs<SemIR::StructTypeField>(
fields[index.index]);
return context.sem_ir().strings().Get(field.name_id);
}
auto HandleClassFieldAccess(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ClassFieldAccess node) -> void {
auto HandleClassFieldAccess(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ClassFieldAccess inst) -> void {
// Find the class that we're performing access into.
auto class_type_id = context.sem_ir().nodes().Get(node.base_id).type_id();
auto class_type_id = context.sem_ir().insts().Get(inst.base_id).type_id();
auto class_id =
context.sem_ir()
.nodes()
.insts()
.GetAs<SemIR::ClassType>(
context.sem_ir().GetTypeAllowBuiltinTypes(class_type_id))
.class_id;
@@ -273,97 +273,97 @@ auto HandleClassFieldAccess(FunctionContext& context, SemIR::NodeId node_id,
// Translate the class field access into a struct access on the object
// representation.
context.SetLocal(
node_id,
inst_id,
GetAggregateElement(
context, node.base_id, node.index, node.type_id,
context, inst.base_id, inst.index, inst.type_id,
GetStructFieldName(context, class_info.object_representation_id,
node.index)));
inst.index)));
}
auto HandleDereference(FunctionContext& context, SemIR::NodeId node_id,
SemIR::Dereference node) -> void {
context.SetLocal(node_id, context.GetLocal(node.pointer_id));
auto HandleDereference(FunctionContext& context, SemIR::InstId inst_id,
SemIR::Dereference inst) -> void {
context.SetLocal(inst_id, context.GetLocal(inst.pointer_id));
}
auto HandleField(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::Field /*node*/) -> void {
auto HandleField(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::Field /*inst*/) -> void {
// No action to perform.
}
auto HandleFunctionDeclaration(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::FunctionDeclaration node) -> void {
SemIR::InstId /*inst_id*/,
SemIR::FunctionDeclaration inst) -> void {
CARBON_FATAL()
<< "Should not be encountered. If that changes, we may want to change "
"higher-level logic to skip them rather than calling this. "
<< node;
<< inst;
}
auto HandleInitializeFrom(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::InitializeFrom node) -> void {
auto storage_type_id = context.sem_ir().nodes().Get(node.dest_id).type_id();
context.FinishInitialization(storage_type_id, node.dest_id, node.src_id);
auto HandleInitializeFrom(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::InitializeFrom inst) -> void {
auto storage_type_id = context.sem_ir().insts().Get(inst.dest_id).type_id();
context.FinishInitialization(storage_type_id, inst.dest_id, inst.src_id);
}
auto HandleIntegerLiteral(FunctionContext& context, SemIR::NodeId node_id,
SemIR::IntegerLiteral node) -> void {
const llvm::APInt& i = context.sem_ir().integers().Get(node.integer_id);
auto HandleIntegerLiteral(FunctionContext& context, SemIR::InstId inst_id,
SemIR::IntegerLiteral inst) -> void {
const llvm::APInt& i = context.sem_ir().integers().Get(inst.integer_id);
// TODO: This won't offer correct semantics, but seems close enough for now.
llvm::Value* v =
llvm::ConstantInt::get(context.builder().getInt32Ty(), i.getZExtValue());
context.SetLocal(node_id, v);
context.SetLocal(inst_id, v);
}
auto HandleNameReference(FunctionContext& context, SemIR::NodeId node_id,
SemIR::NameReference node) -> void {
auto type_node_id = context.sem_ir().GetTypeAllowBuiltinTypes(node.type_id);
if (type_node_id == SemIR::NodeId::BuiltinNamespaceType) {
auto HandleNameReference(FunctionContext& context, SemIR::InstId inst_id,
SemIR::NameReference inst) -> void {
auto type_inst_id = context.sem_ir().GetTypeAllowBuiltinTypes(inst.type_id);
if (type_inst_id == SemIR::InstId::BuiltinNamespaceType) {
return;
}
context.SetLocal(node_id, context.GetLocalOrGlobal(node.value_id));
context.SetLocal(inst_id, context.GetLocalOrGlobal(inst.value_id));
}
auto HandleNamespace(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::Namespace node) -> void {
auto HandleNamespace(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::Namespace inst) -> void {
CARBON_FATAL()
<< "Should not be encountered. If that changes, we may want to change "
"higher-level logic to skip them rather than calling this. "
<< node;
<< inst;
}
auto HandleNoOp(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::NoOp /*node*/) -> void {
auto HandleNoOp(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::NoOp /*inst*/) -> void {
// No action to take.
}
auto HandleParameter(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::Parameter /*node*/) -> void {
auto HandleParameter(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::Parameter /*inst*/) -> void {
CARBON_FATAL() << "Parameters should be lowered by `BuildFunctionDefinition`";
}
auto HandleRealLiteral(FunctionContext& context, SemIR::NodeId node_id,
SemIR::RealLiteral node) -> void {
const Real& real = context.sem_ir().reals().Get(node.real_id);
auto HandleRealLiteral(FunctionContext& context, SemIR::InstId inst_id,
SemIR::RealLiteral inst) -> void {
const Real& real = context.sem_ir().reals().Get(inst.real_id);
// TODO: This will probably have overflow issues, and should be fixed.
double val =
real.mantissa.getZExtValue() *
std::pow((real.is_decimal ? 10 : 2), real.exponent.getSExtValue());
llvm::APFloat llvm_val(val);
context.SetLocal(node_id, llvm::ConstantFP::get(
context.SetLocal(inst_id, llvm::ConstantFP::get(
context.builder().getDoubleTy(), llvm_val));
}
auto HandleReturn(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::Return /*node*/) -> void {
auto HandleReturn(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::Return /*inst*/) -> void {
context.builder().CreateRetVoid();
}
auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/,
SemIR::ReturnExpression node) -> void {
auto HandleReturnExpression(FunctionContext& context, SemIR::InstId /*inst_id*/,
SemIR::ReturnExpression inst) -> void {
switch (SemIR::GetInitializingRepresentation(
context.sem_ir(),
context.sem_ir().nodes().Get(node.expr_id).type_id())
context.sem_ir().insts().Get(inst.expr_id).type_id())
.kind) {
case SemIR::InitializingRepresentation::None:
case SemIR::InitializingRepresentation::InPlace:
@@ -372,41 +372,41 @@ auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/,
return;
case SemIR::InitializingRepresentation::ByCopy:
// The expression produces the value representation for the type.
context.builder().CreateRet(context.GetLocal(node.expr_id));
context.builder().CreateRet(context.GetLocal(inst.expr_id));
return;
}
}
auto HandleSelfParameter(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::SelfParameter /*node*/) -> void {
SemIR::InstId /*inst_id*/,
SemIR::SelfParameter /*inst*/) -> void {
CARBON_FATAL() << "Parameters should be lowered by `BuildFunctionDefinition`";
}
auto HandleSpliceBlock(FunctionContext& context, SemIR::NodeId node_id,
SemIR::SpliceBlock node) -> void {
context.LowerBlock(node.block_id);
context.SetLocal(node_id, context.GetLocal(node.result_id));
auto HandleSpliceBlock(FunctionContext& context, SemIR::InstId inst_id,
SemIR::SpliceBlock inst) -> void {
context.LowerBlock(inst.block_id);
context.SetLocal(inst_id, context.GetLocal(inst.result_id));
}
auto HandleStringLiteral(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/, SemIR::StringLiteral node)
SemIR::InstId /*inst_id*/, SemIR::StringLiteral inst)
-> void {
CARBON_FATAL() << "TODO: Add support: " << node;
CARBON_FATAL() << "TODO: Add support: " << inst;
}
auto HandleStructAccess(FunctionContext& context, SemIR::NodeId node_id,
SemIR::StructAccess node) -> void {
auto struct_type_id = context.sem_ir().nodes().Get(node.struct_id).type_id();
auto HandleStructAccess(FunctionContext& context, SemIR::InstId inst_id,
SemIR::StructAccess inst) -> void {
auto struct_type_id = context.sem_ir().insts().Get(inst.struct_id).type_id();
context.SetLocal(
node_id, GetAggregateElement(
context, node.struct_id, node.index, node.type_id,
GetStructFieldName(context, struct_type_id, node.index)));
inst_id, GetAggregateElement(
context, inst.struct_id, inst.index, inst.type_id,
GetStructFieldName(context, struct_type_id, inst.index)));
}
auto HandleStructLiteral(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::StructLiteral /*node*/) -> void {
SemIR::InstId /*inst_id*/,
SemIR::StructLiteral /*inst*/) -> void {
// A StructLiteral should always be converted to a StructInit or StructValue
// if its value is needed.
}
@@ -415,7 +415,7 @@ auto HandleStructLiteral(FunctionContext& /*context*/,
// contents of `refs_id`.
auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
SemIR::TypeId type_id,
SemIR::NodeBlockId refs_id,
SemIR::InstBlockId refs_id,
llvm::Twine name) -> llvm::Value* {
auto value_rep = SemIR::GetValueRepresentation(context.sem_ir(), type_id);
switch (value_rep.kind) {
@@ -427,7 +427,7 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
return llvm::PoisonValue::get(context.GetType(value_rep.type_id));
case SemIR::ValueRepresentation::Copy: {
auto refs = context.sem_ir().node_blocks().Get(refs_id);
auto refs = context.sem_ir().inst_blocks().Get(refs_id);
CARBON_CHECK(refs.size() == 1)
<< "Unexpected size for aggregate with by-copy value representation";
// TODO: Remove the LLVM StructType wrapper in this case, so we don't
@@ -447,7 +447,7 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
context.builder().CreateAlloca(llvm_value_rep_type,
/*ArraySize=*/nullptr, name);
for (auto [i, ref] :
llvm::enumerate(context.sem_ir().node_blocks().Get(refs_id))) {
llvm::enumerate(context.sem_ir().inst_blocks().Get(refs_id))) {
context.builder().CreateStore(
context.GetLocal(ref),
context.builder().CreateStructGEP(llvm_value_rep_type, alloca, i));
@@ -461,107 +461,107 @@ auto EmitStructOrTupleValueRepresentation(FunctionContext& context,
}
}
auto HandleStructInit(FunctionContext& context, SemIR::NodeId node_id,
SemIR::StructInit node) -> void {
auto* llvm_type = context.GetType(node.type_id);
auto HandleStructInit(FunctionContext& context, SemIR::InstId inst_id,
SemIR::StructInit inst) -> void {
auto* llvm_type = context.GetType(inst.type_id);
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), node.type_id)
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
.kind) {
case SemIR::InitializingRepresentation::None:
case SemIR::InitializingRepresentation::InPlace:
// TODO: Add a helper to poison a value slot.
context.SetLocal(node_id, llvm::PoisonValue::get(llvm_type));
context.SetLocal(inst_id, llvm::PoisonValue::get(llvm_type));
break;
case SemIR::InitializingRepresentation::ByCopy: {
context.SetLocal(
node_id, EmitStructOrTupleValueRepresentation(
context, node.type_id, node.elements_id, "struct.init"));
inst_id, EmitStructOrTupleValueRepresentation(
context, inst.type_id, inst.elements_id, "struct.init"));
break;
}
}
}
auto HandleStructValue(FunctionContext& context, SemIR::NodeId node_id,
SemIR::StructValue node) -> void {
context.SetLocal(node_id,
auto HandleStructValue(FunctionContext& context, SemIR::InstId inst_id,
SemIR::StructValue inst) -> void {
context.SetLocal(inst_id,
EmitStructOrTupleValueRepresentation(
context, node.type_id, node.elements_id, "struct"));
context, inst.type_id, inst.elements_id, "struct"));
}
auto HandleStructTypeField(FunctionContext& /*context*/,
SemIR::NodeId /*node_id*/,
SemIR::StructTypeField /*node*/) -> void {
SemIR::InstId /*inst_id*/,
SemIR::StructTypeField /*inst*/) -> void {
// No action to take.
}
auto HandleTupleAccess(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TupleAccess node) -> void {
context.SetLocal(node_id,
GetAggregateElement(context, node.tuple_id, node.index,
node.type_id, "tuple.elem"));
auto HandleTupleAccess(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TupleAccess inst) -> void {
context.SetLocal(inst_id,
GetAggregateElement(context, inst.tuple_id, inst.index,
inst.type_id, "tuple.elem"));
}
auto HandleTupleIndex(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TupleIndex node) -> void {
auto index_node =
context.sem_ir().nodes().GetAs<SemIR::IntegerLiteral>(node.index_id);
auto HandleTupleIndex(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TupleIndex inst) -> void {
auto index_inst =
context.sem_ir().insts().GetAs<SemIR::IntegerLiteral>(inst.index_id);
auto index =
context.sem_ir().integers().Get(index_node.integer_id).getZExtValue();
context.SetLocal(node_id, GetAggregateElement(context, node.tuple_id,
context.sem_ir().integers().Get(index_inst.integer_id).getZExtValue();
context.SetLocal(inst_id, GetAggregateElement(context, inst.tuple_id,
SemIR::MemberIndex(index),
node.type_id, "tuple.index"));
inst.type_id, "tuple.index"));
}
auto HandleTupleLiteral(FunctionContext& /*context*/, SemIR::NodeId /*node_id*/,
SemIR::TupleLiteral /*node*/) -> void {
auto HandleTupleLiteral(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
SemIR::TupleLiteral /*inst*/) -> void {
// A TupleLiteral should always be converted to a TupleInit or TupleValue if
// its value is needed.
}
auto HandleTupleInit(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TupleInit node) -> void {
auto* llvm_type = context.GetType(node.type_id);
auto HandleTupleInit(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TupleInit inst) -> void {
auto* llvm_type = context.GetType(inst.type_id);
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), node.type_id)
switch (SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
.kind) {
case SemIR::InitializingRepresentation::None:
case SemIR::InitializingRepresentation::InPlace:
// TODO: Add a helper to poison a value slot.
context.SetLocal(node_id, llvm::PoisonValue::get(llvm_type));
context.SetLocal(inst_id, llvm::PoisonValue::get(llvm_type));
break;
case SemIR::InitializingRepresentation::ByCopy: {
context.SetLocal(
node_id, EmitStructOrTupleValueRepresentation(
context, node.type_id, node.elements_id, "tuple.init"));
inst_id, EmitStructOrTupleValueRepresentation(
context, inst.type_id, inst.elements_id, "tuple.init"));
break;
}
}
}
auto HandleTupleValue(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TupleValue node) -> void {
auto HandleTupleValue(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TupleValue inst) -> void {
context.SetLocal(
node_id, EmitStructOrTupleValueRepresentation(context, node.type_id,
node.elements_id, "tuple"));
inst_id, EmitStructOrTupleValueRepresentation(context, inst.type_id,
inst.elements_id, "tuple"));
}
auto HandleUnaryOperatorNot(FunctionContext& context, SemIR::NodeId node_id,
SemIR::UnaryOperatorNot node) -> void {
auto HandleUnaryOperatorNot(FunctionContext& context, SemIR::InstId inst_id,
SemIR::UnaryOperatorNot inst) -> void {
context.SetLocal(
node_id, context.builder().CreateNot(context.GetLocal(node.operand_id)));
inst_id, context.builder().CreateNot(context.GetLocal(inst.operand_id)));
}
auto HandleVarStorage(FunctionContext& context, SemIR::NodeId node_id,
SemIR::VarStorage node) -> void {
auto HandleVarStorage(FunctionContext& context, SemIR::InstId inst_id,
SemIR::VarStorage inst) -> void {
// TODO: Eventually this name will be optional, and we'll want to provide
// something like `var` as a default. However, that's not possible right now
// so cannot be tested.
auto name = context.sem_ir().strings().Get(node.name_id);
auto* alloca = context.builder().CreateAlloca(context.GetType(node.type_id),
auto name = context.sem_ir().strings().Get(inst.name_id);
auto* alloca = context.builder().CreateAlloca(context.GetType(inst.type_id),
/*ArraySize=*/nullptr, name);
context.SetLocal(node_id, alloca);
context.SetLocal(inst_id, alloca);
}
} // namespace Carbon::Lower
+28 -28
View File
@@ -7,10 +7,10 @@
namespace Carbon::Lower {
auto HandleBindValue(FunctionContext& context, SemIR::NodeId node_id,
SemIR::BindValue node) -> void {
auto HandleBindValue(FunctionContext& context, SemIR::InstId inst_id,
SemIR::BindValue inst) -> void {
switch (auto rep =
SemIR::GetValueRepresentation(context.sem_ir(), node.type_id);
SemIR::GetValueRepresentation(context.sem_ir(), inst.type_id);
rep.kind) {
case SemIR::ValueRepresentation::Unknown:
CARBON_FATAL()
@@ -19,56 +19,56 @@ auto HandleBindValue(FunctionContext& context, SemIR::NodeId node_id,
// Nothing should use this value, but StubReference needs a value to
// propagate.
// TODO: Remove this now the StubReferences are gone.
context.SetLocal(node_id,
llvm::PoisonValue::get(context.GetType(node.type_id)));
context.SetLocal(inst_id,
llvm::PoisonValue::get(context.GetType(inst.type_id)));
break;
case SemIR::ValueRepresentation::Copy:
context.SetLocal(node_id, context.builder().CreateLoad(
context.GetType(node.type_id),
context.GetLocal(node.value_id)));
context.SetLocal(inst_id, context.builder().CreateLoad(
context.GetType(inst.type_id),
context.GetLocal(inst.value_id)));
break;
case SemIR::ValueRepresentation::Pointer:
context.SetLocal(node_id, context.GetLocal(node.value_id));
context.SetLocal(inst_id, context.GetLocal(inst.value_id));
break;
case SemIR::ValueRepresentation::Custom:
CARBON_FATAL() << "TODO: Add support for BindValue with custom value rep";
}
}
auto HandleTemporary(FunctionContext& context, SemIR::NodeId node_id,
SemIR::Temporary node) -> void {
context.FinishInitialization(node.type_id, node.storage_id, node.init_id);
context.SetLocal(node_id, context.GetLocal(node.storage_id));
auto HandleTemporary(FunctionContext& context, SemIR::InstId inst_id,
SemIR::Temporary inst) -> void {
context.FinishInitialization(inst.type_id, inst.storage_id, inst.init_id);
context.SetLocal(inst_id, context.GetLocal(inst.storage_id));
}
auto HandleTemporaryStorage(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TemporaryStorage node) -> void {
context.SetLocal(node_id,
context.builder().CreateAlloca(context.GetType(node.type_id),
auto HandleTemporaryStorage(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TemporaryStorage inst) -> void {
context.SetLocal(inst_id,
context.builder().CreateAlloca(context.GetType(inst.type_id),
nullptr, "temp"));
}
auto HandleValueAsReference(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ValueAsReference node) -> void {
CARBON_CHECK(SemIR::GetExpressionCategory(context.sem_ir(), node.value_id) ==
auto HandleValueAsReference(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ValueAsReference inst) -> void {
CARBON_CHECK(SemIR::GetExpressionCategory(context.sem_ir(), inst.value_id) ==
SemIR::ExpressionCategory::Value);
CARBON_CHECK(
SemIR::GetValueRepresentation(context.sem_ir(), node.type_id).kind ==
SemIR::GetValueRepresentation(context.sem_ir(), inst.type_id).kind ==
SemIR::ValueRepresentation::Pointer);
context.SetLocal(node_id, context.GetLocal(node.value_id));
context.SetLocal(inst_id, context.GetLocal(inst.value_id));
}
auto HandleValueOfInitializer(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ValueOfInitializer node) -> void {
CARBON_CHECK(SemIR::GetExpressionCategory(context.sem_ir(), node.init_id) ==
auto HandleValueOfInitializer(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ValueOfInitializer inst) -> void {
CARBON_CHECK(SemIR::GetExpressionCategory(context.sem_ir(), inst.init_id) ==
SemIR::ExpressionCategory::Initializing);
CARBON_CHECK(
SemIR::GetValueRepresentation(context.sem_ir(), node.type_id).kind ==
SemIR::GetValueRepresentation(context.sem_ir(), inst.type_id).kind ==
SemIR::ValueRepresentation::Copy);
CARBON_CHECK(
SemIR::GetInitializingRepresentation(context.sem_ir(), node.type_id)
SemIR::GetInitializingRepresentation(context.sem_ir(), inst.type_id)
.kind == SemIR::InitializingRepresentation::ByCopy);
context.SetLocal(node_id, context.GetLocal(node.init_id));
context.SetLocal(inst_id, context.GetLocal(inst.init_id));
}
} // namespace Carbon::Lower
+21 -21
View File
@@ -6,39 +6,39 @@
namespace Carbon::Lower {
auto HandleArrayType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ArrayType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleArrayType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ArrayType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleClassType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ClassType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleClassType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ClassType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleConstType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::ConstType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleConstType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::ConstType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandlePointerType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::PointerType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandlePointerType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::PointerType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleStructType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::StructType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleStructType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::StructType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleTupleType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::TupleType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleTupleType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::TupleType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
auto HandleUnboundFieldType(FunctionContext& context, SemIR::NodeId node_id,
SemIR::UnboundFieldType /*node*/) -> void {
context.SetLocal(node_id, context.GetTypeAsValue());
auto HandleUnboundFieldType(FunctionContext& context, SemIR::InstId inst_id,
SemIR::UnboundFieldType /*inst*/) -> void {
context.SetLocal(inst_id, context.GetTypeAsValue());
}
} // namespace Carbon::Lower
+13 -13
View File
@@ -26,11 +26,11 @@ cc_library(
)
cc_library(
name = "node_kind",
name = "inst_kind",
srcs = ["node_kind.cpp"],
hdrs = [
"node_kind.h",
"typed_nodes.h",
"typed_insts.h",
],
textual_hdrs = ["node_kind.def"],
deps = [
@@ -43,12 +43,12 @@ cc_library(
)
cc_library(
name = "node",
srcs = ["node.cpp"],
hdrs = ["node.h"],
name = "inst",
srcs = ["inst.cpp"],
hdrs = ["inst.h"],
deps = [
":builtin_kind",
":node_kind",
":inst_kind",
"//common:check",
"//common:ostream",
"//common:struct_reflection",
@@ -64,8 +64,8 @@ cc_library(
hdrs = ["file.h"],
deps = [
":builtin_kind",
":node",
":node_kind",
":inst",
":inst_kind",
":value_stores",
"//common:check",
"//toolchain/base:value_store",
@@ -80,7 +80,7 @@ cc_library(
hdrs = ["formatter.h"],
deps = [
":file",
":node_kind",
":inst_kind",
"//toolchain/lex:tokenized_buffer",
"//toolchain/parse:tree",
"@llvm-project//llvm:Support",
@@ -101,7 +101,7 @@ cc_library(
name = "value_stores",
hdrs = ["value_stores.h"],
deps = [
":node",
":inst",
"//toolchain/base:value_store",
"//toolchain/base:yaml",
"@llvm-project//llvm:Support",
@@ -109,12 +109,12 @@ cc_library(
)
cc_test(
name = "typed_nodes_test",
name = "typed_insts_test",
size = "small",
srcs = ["typed_nodes_test.cpp"],
deps = [
":node",
":node_kind",
":inst",
":inst_kind",
"//testing/base:gtest_main",
"@com_google_googletest//:gtest",
],
+1 -1
View File
@@ -40,7 +40,7 @@
// This has a deliberately self-referential type.
CARBON_SEM_IR_BUILTIN_KIND(TypeType, "type")
// Used when a semantic error has been detected, and a SemanticNodeId is still
// Used when a semantic error has been detected, and a SemIR InstId is still
// required. For example, when there is a type checking issue, this will be used
// in the type_id. It's typically used as a cue that semantic checking doesn't
// need to issue further diagnostics.
+1 -1
View File
@@ -30,7 +30,7 @@ class BuiltinKind : public CARBON_ENUM_BASE(BuiltinKind) {
// constant.
static const uint8_t ValidCount;
// Support conversion to and from an int32_t for SemanticNode storage.
// Support conversion to and from an int32_t for SemIR instruction storage.
using EnumBase::AsInt;
using EnumBase::FromInt;
};
+90 -89
View File
@@ -10,7 +10,7 @@
#include "toolchain/base/value_store.h"
#include "toolchain/base/yaml.h"
#include "toolchain/sem_ir/builtin_kind.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/node_kind.h"
namespace Carbon::SemIR {
@@ -38,7 +38,7 @@ auto ValueRepresentation::Print(llvm::raw_ostream& out) const -> void {
}
auto TypeInfo::Print(llvm::raw_ostream& out) const -> void {
out << "{node: " << node_id << ", value_rep: " << value_representation << "}";
out << "{inst: " << inst_id << ", value_rep: " << value_representation << "}";
}
File::File(SharedValueStores& value_stores)
@@ -47,25 +47,25 @@ File::File(SharedValueStores& value_stores)
// Builtins are always the first IR, even when self-referential.
cross_reference_irs_({this}),
type_blocks_(allocator_),
node_blocks_(allocator_) {
// Default entry for NodeBlockId::Empty.
node_blocks_.AddDefaultValue();
inst_blocks_(allocator_) {
// Default entry for InstBlockId::Empty.
inst_blocks_.AddDefaultValue();
nodes_.Reserve(BuiltinKind::ValidCount);
insts_.Reserve(BuiltinKind::ValidCount);
// Error uses a self-referential type so that it's not accidentally treated as
// a normal type. Every other builtin is a type, including the
// self-referential TypeType.
#define CARBON_SEM_IR_BUILTIN_KIND(Name, ...) \
nodes_.AddInNoBlock(Builtin{BuiltinKind::Name == BuiltinKind::Error \
insts_.AddInNoBlock(Builtin{BuiltinKind::Name == BuiltinKind::Error \
? TypeId::Error \
: TypeId::TypeType, \
BuiltinKind::Name});
#include "toolchain/sem_ir/builtin_kind.def"
CARBON_CHECK(nodes_.size() == BuiltinKind::ValidCount)
CARBON_CHECK(insts_.size() == BuiltinKind::ValidCount)
<< "Builtins should produce " << BuiltinKind::ValidCount
<< " nodes, actual: " << nodes_.size();
<< " insts, actual: " << insts_.size();
}
File::File(SharedValueStores& value_stores, std::string filename,
@@ -75,21 +75,21 @@ File::File(SharedValueStores& value_stores, std::string filename,
// Builtins are always the first IR.
cross_reference_irs_({builtins}),
type_blocks_(allocator_),
node_blocks_(allocator_) {
inst_blocks_(allocator_) {
CARBON_CHECK(builtins != nullptr);
CARBON_CHECK(builtins->cross_reference_irs_[0] == builtins)
<< "Not called with builtins!";
// Default entry for NodeBlockId::Empty.
node_blocks_.AddDefaultValue();
// Default entry for InstBlockId::Empty.
inst_blocks_.AddDefaultValue();
// Copy builtins over.
nodes_.Reserve(BuiltinKind::ValidCount);
insts_.Reserve(BuiltinKind::ValidCount);
static constexpr auto BuiltinIR = CrossReferenceIRId(0);
for (auto [i, node] : llvm::enumerate(builtins->nodes_.array_ref())) {
for (auto [i, inst] : llvm::enumerate(builtins->insts_.array_ref())) {
// We can reuse builtin type IDs because they're special-cased values.
nodes_.AddInNoBlock(
CrossReference{node.type_id(), BuiltinIR, SemIR::NodeId(i)});
insts_.AddInNoBlock(
CrossReference{inst.type_id(), BuiltinIR, SemIR::InstId(i)});
}
}
@@ -102,22 +102,22 @@ auto File::Verify() const -> ErrorOr<Success> {
// Check that every code block has a terminator sequence that appears at the
// end of the block.
for (const Function& function : functions_.array_ref()) {
for (NodeBlockId block_id : function.body_block_ids) {
for (InstBlockId block_id : function.body_block_ids) {
TerminatorKind prior_kind = TerminatorKind::NotTerminator;
for (NodeId node_id : node_blocks().Get(block_id)) {
TerminatorKind node_kind =
nodes().Get(node_id).kind().terminator_kind();
for (InstId inst_id : inst_blocks().Get(block_id)) {
TerminatorKind inst_kind =
insts().Get(inst_id).kind().terminator_kind();
if (prior_kind == TerminatorKind::Terminator) {
return Error(llvm::formatv("Node {0} in block {1} follows terminator",
node_id, block_id));
return Error(llvm::formatv("Inst {0} in block {1} follows terminator",
inst_id, block_id));
}
if (prior_kind > node_kind) {
if (prior_kind > inst_kind) {
return Error(
llvm::formatv("Non-terminator node {0} in block {1} follows "
llvm::formatv("Non-terminator inst {0} in block {1} follows "
"terminator sequence",
node_id, block_id));
inst_id, block_id));
}
prior_kind = node_kind;
prior_kind = inst_kind;
}
if (prior_kind != TerminatorKind::Terminator) {
return Error(llvm::formatv("No terminator in block {0}", block_id));
@@ -125,8 +125,8 @@ auto File::Verify() const -> ErrorOr<Success> {
}
}
// TODO: Check that a node only references other nodes that are either global
// or that dominate it.
// TODO: Check that an instruction only references other instructions that are
// either global or that dominate it.
return Success();
}
@@ -141,25 +141,25 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping {
map.Add("classes", classes_.OutputYaml());
map.Add("types", types_.OutputYaml());
map.Add("type_blocks", type_blocks_.OutputYaml());
map.Add("nodes",
map.Add("insts",
Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
int start =
include_builtins ? 0 : BuiltinKind::ValidCount;
for (int i : llvm::seq(start, nodes_.size())) {
auto id = NodeId(i);
for (int i : llvm::seq(start, insts_.size())) {
auto id = InstId(i);
map.Add(PrintToString(id),
Yaml::OutputScalar(nodes_.Get(id)));
Yaml::OutputScalar(insts_.Get(id)));
}
}));
map.Add("node_blocks", node_blocks_.OutputYaml());
map.Add("inst_blocks", inst_blocks_.OutputYaml());
}));
});
}
// Map a node kind representing a type into an integer describing the
// Map an instruction kind representing a type into an integer describing the
// precedence of that type's syntax. Higher numbers correspond to higher
// precedence.
static auto GetTypePrecedence(NodeKind kind) -> int {
static auto GetTypePrecedence(InstKind kind) -> int {
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (kind) {
@@ -228,7 +228,7 @@ static auto GetTypePrecedence(NodeKind kind) -> int {
case ValueAsReference::Kind:
case ValueOfInitializer::Kind:
case VarStorage::Kind:
CARBON_FATAL() << "GetTypePrecedence for non-type node kind " << kind;
CARBON_FATAL() << "GetTypePrecedence for non-type inst kind " << kind;
}
}
@@ -238,47 +238,47 @@ auto File::StringifyType(TypeId type_id, bool in_type_context) const
in_type_context);
}
auto File::StringifyTypeExpression(NodeId outer_node_id,
auto File::StringifyTypeExpression(InstId outer_inst_id,
bool in_type_context) const -> std::string {
std::string str;
llvm::raw_string_ostream out(str);
struct Step {
// The node to print.
NodeId node_id;
// The index into node_id to print. Not used by all types.
// The instruction to print.
InstId inst_id;
// The index into inst_id to print. Not used by all types.
int index = 0;
auto Next() const -> Step {
return {.node_id = node_id, .index = index + 1};
return {.inst_id = inst_id, .index = index + 1};
}
};
llvm::SmallVector<Step> steps = {{.node_id = outer_node_id}};
llvm::SmallVector<Step> steps = {{.inst_id = outer_inst_id}};
while (!steps.empty()) {
auto step = steps.pop_back_val();
if (!step.node_id.is_valid()) {
if (!step.inst_id.is_valid()) {
out << "<invalid type>";
continue;
}
// Builtins have designated labels.
if (step.node_id.index < BuiltinKind::ValidCount) {
out << BuiltinKind::FromInt(step.node_id.index).label();
if (step.inst_id.index < BuiltinKind::ValidCount) {
out << BuiltinKind::FromInt(step.inst_id.index).label();
continue;
}
auto node = nodes().Get(step.node_id);
auto inst = insts().Get(step.inst_id);
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (node.kind()) {
switch (inst.kind()) {
case ArrayType::Kind: {
auto array = node.As<ArrayType>();
auto array = inst.As<ArrayType>();
if (step.index == 0) {
out << "[";
steps.push_back(step.Next());
steps.push_back(
{.node_id = GetTypeAllowBuiltinTypes(array.element_type_id)});
{.inst_id = GetTypeAllowBuiltinTypes(array.element_type_id)});
} else if (step.index == 1) {
out << "; " << GetArrayBoundValue(array.bound_id) << "]";
}
@@ -286,7 +286,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
}
case ClassType::Kind: {
auto class_name_id =
classes().Get(node.As<ClassType>().class_id).name_id;
classes().Get(inst.As<ClassType>().class_id).name_id;
out << strings().Get(class_name_id);
break;
}
@@ -295,36 +295,36 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
out << "const ";
// Add parentheses if required.
auto inner_type_node_id =
GetTypeAllowBuiltinTypes(node.As<ConstType>().inner_id);
if (GetTypePrecedence(nodes().Get(inner_type_node_id).kind()) <
GetTypePrecedence(node.kind())) {
auto inner_type_inst_id =
GetTypeAllowBuiltinTypes(inst.As<ConstType>().inner_id);
if (GetTypePrecedence(insts().Get(inner_type_inst_id).kind()) <
GetTypePrecedence(inst.kind())) {
out << "(";
steps.push_back(step.Next());
}
steps.push_back({.node_id = inner_type_node_id});
steps.push_back({.inst_id = inner_type_inst_id});
} else if (step.index == 1) {
out << ")";
}
break;
}
case NameReference::Kind: {
out << strings().Get(node.As<NameReference>().name_id);
out << strings().Get(inst.As<NameReference>().name_id);
break;
}
case PointerType::Kind: {
if (step.index == 0) {
steps.push_back(step.Next());
steps.push_back({.node_id = GetTypeAllowBuiltinTypes(
node.As<PointerType>().pointee_id)});
steps.push_back({.inst_id = GetTypeAllowBuiltinTypes(
inst.As<PointerType>().pointee_id)});
} else if (step.index == 1) {
out << "*";
}
break;
}
case StructType::Kind: {
auto refs = node_blocks().Get(node.As<StructType>().fields_id);
auto refs = inst_blocks().Get(inst.As<StructType>().fields_id);
if (refs.empty()) {
out << "{}";
break;
@@ -338,18 +338,18 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
}
steps.push_back(step.Next());
steps.push_back({.node_id = refs[step.index]});
steps.push_back({.inst_id = refs[step.index]});
break;
}
case StructTypeField::Kind: {
auto field = node.As<StructTypeField>();
auto field = inst.As<StructTypeField>();
out << "." << strings().Get(field.name_id) << ": ";
steps.push_back(
{.node_id = GetTypeAllowBuiltinTypes(field.field_type_id)});
{.inst_id = GetTypeAllowBuiltinTypes(field.field_type_id)});
break;
}
case TupleType::Kind: {
auto refs = type_blocks().Get(node.As<TupleType>().elements_id);
auto refs = type_blocks().Get(inst.As<TupleType>().elements_id);
if (refs.empty()) {
out << "()";
break;
@@ -368,15 +368,15 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
}
steps.push_back(step.Next());
steps.push_back(
{.node_id = GetTypeAllowBuiltinTypes(refs[step.index])});
{.inst_id = GetTypeAllowBuiltinTypes(refs[step.index])});
break;
}
case UnboundFieldType::Kind: {
if (step.index == 0) {
out << "<unbound field of class ";
steps.push_back(step.Next());
steps.push_back({.node_id = GetTypeAllowBuiltinTypes(
node.As<UnboundFieldType>().class_type_id)});
steps.push_back({.inst_id = GetTypeAllowBuiltinTypes(
inst.As<UnboundFieldType>().class_type_id)});
} else {
out << ">";
}
@@ -429,10 +429,10 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
case ValueAsReference::Kind:
case ValueOfInitializer::Kind:
case VarStorage::Kind:
// We don't need to handle stringification for nodes that don't show up
// in errors, but make it clear what's going on so that it's clearer
// when stringification is needed.
out << "<cannot stringify " << step.node_id << ">";
// We don't need to handle stringification for instructions that don't
// show up in errors, but make it clear what's going on so that it's
// clearer when stringification is needed.
out << "<cannot stringify " << step.inst_id << ">";
break;
}
}
@@ -440,10 +440,10 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
// For `{}` or any tuple type, we've printed a non-type expression, so add a
// conversion to type `type` if it's not implied by the context.
if (!in_type_context) {
auto outer_node = nodes().Get(outer_node_id);
if (outer_node.Is<TupleType>() ||
(outer_node.Is<StructType>() &&
node_blocks().Get(outer_node.As<StructType>().fields_id).empty())) {
auto outer_inst = insts().Get(outer_inst_id);
if (outer_inst.Is<TupleType>() ||
(outer_inst.Is<StructType>() &&
inst_blocks().Get(outer_inst.As<StructType>().fields_id).empty())) {
out << " as type";
}
}
@@ -451,18 +451,19 @@ auto File::StringifyTypeExpression(NodeId outer_node_id,
return str;
}
auto GetExpressionCategory(const File& file, NodeId node_id)
auto GetExpressionCategory(const File& file, InstId inst_id)
-> ExpressionCategory {
const File* ir = &file;
// The overall expression category if the current node is a value expression.
// The overall expression category if the current instruction is a value
// expression.
ExpressionCategory value_category = ExpressionCategory::Value;
while (true) {
auto node = ir->nodes().Get(node_id);
auto inst = ir->insts().Get(inst_id);
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (node.kind()) {
switch (inst.kind()) {
case Assign::Kind:
case Branch::Kind:
case BranchIf::Kind:
@@ -478,14 +479,14 @@ auto GetExpressionCategory(const File& file, NodeId node_id)
return ExpressionCategory::NotExpression;
case CrossReference::Kind: {
auto xref = node.As<CrossReference>();
auto xref = inst.As<CrossReference>();
ir = &ir->GetCrossReferenceIR(xref.ir_id);
node_id = xref.node_id;
inst_id = xref.inst_id;
continue;
}
case NameReference::Kind: {
node_id = node.As<NameReference>().value_id;
inst_id = inst.As<NameReference>().value_id;
continue;
}
@@ -514,24 +515,24 @@ auto GetExpressionCategory(const File& file, NodeId node_id)
return value_category;
case Builtin::Kind: {
if (node.As<Builtin>().builtin_kind == BuiltinKind::Error) {
if (inst.As<Builtin>().builtin_kind == BuiltinKind::Error) {
return ExpressionCategory::Error;
}
return value_category;
}
case BindName::Kind: {
node_id = node.As<BindName>().value_id;
inst_id = inst.As<BindName>().value_id;
continue;
}
case ArrayIndex::Kind: {
node_id = node.As<ArrayIndex>().array_id;
inst_id = inst.As<ArrayIndex>().array_id;
continue;
}
case ClassFieldAccess::Kind: {
node_id = node.As<ClassFieldAccess>().base_id;
inst_id = inst.As<ClassFieldAccess>().base_id;
// A value of class type is a pointer to an object representation.
// Therefore, if the base is a value, the result is an ephemeral
// reference.
@@ -540,22 +541,22 @@ auto GetExpressionCategory(const File& file, NodeId node_id)
}
case StructAccess::Kind: {
node_id = node.As<StructAccess>().struct_id;
inst_id = inst.As<StructAccess>().struct_id;
continue;
}
case TupleAccess::Kind: {
node_id = node.As<TupleAccess>().tuple_id;
inst_id = inst.As<TupleAccess>().tuple_id;
continue;
}
case TupleIndex::Kind: {
node_id = node.As<TupleIndex>().tuple_id;
inst_id = inst.As<TupleIndex>().tuple_id;
continue;
}
case SpliceBlock::Kind: {
node_id = node.As<SpliceBlock>().result_id;
inst_id = inst.As<SpliceBlock>().result_id;
continue;
}
+56 -54
View File
@@ -11,7 +11,7 @@
#include "llvm/Support/FormatVariadic.h"
#include "toolchain/base/value_store.h"
#include "toolchain/base/yaml.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/value_stores.h"
namespace Carbon::SemIR {
@@ -39,11 +39,11 @@ struct Function : public Printable<Function> {
StringId name_id;
// The definition, if the function has been defined or is currently being
// defined. This is a FunctionDeclaration.
NodeId definition_id = NodeId::Invalid;
// A block containing a single reference node per implicit parameter.
NodeBlockId implicit_param_refs_id;
// A block containing a single reference node per parameter.
NodeBlockId param_refs_id;
InstId definition_id = InstId::Invalid;
// A block containing a single reference instruction per implicit parameter.
InstBlockId implicit_param_refs_id;
// A block containing a single reference instruction per parameter.
InstBlockId param_refs_id;
// The return type. This will be invalid if the return type wasn't specified.
TypeId return_type_id;
// The storage for the return value, which is a reference expression whose
@@ -51,11 +51,11 @@ struct Function : public Printable<Function> {
// doesn't have a return slot. If this is valid, a call to the function is
// expected to have an additional final argument corresponding to the return
// slot.
NodeId return_slot_id;
InstId return_slot_id;
// A list of the statically reachable code blocks in the body of the
// function, in lexical order. The first block is the entry block. This will
// be empty for declarations that don't have a visible definition.
llvm::SmallVector<NodeBlockId> body_block_ids = {};
llvm::SmallVector<InstBlockId> body_block_ids = {};
};
// A class.
@@ -77,17 +77,17 @@ struct Class : public Printable<Class> {
// The class type, which is the type of `Self` in the class definition.
TypeId self_type_id;
// The first declaration of the class. This is a ClassDeclaration.
NodeId declaration_id = NodeId::Invalid;
InstId declaration_id = InstId::Invalid;
// The following members are set at the `{` of the class definition.
// The definition of the class. This is a ClassDeclaration.
NodeId definition_id = NodeId::Invalid;
InstId definition_id = InstId::Invalid;
// The class scope.
NameScopeId scope_id = NameScopeId::Invalid;
// The first block of the class body.
// TODO: Handle control flow in the class body, such as if-expressions.
NodeBlockId body_block_id = NodeBlockId::Invalid;
InstBlockId body_block_id = InstBlockId::Invalid;
// The following members are set at the `}` of the class definition.
@@ -152,8 +152,8 @@ struct ValueRepresentation : public Printable<ValueRepresentation> {
struct TypeInfo : public Printable<TypeInfo> {
auto Print(llvm::raw_ostream& out) const -> void;
// The node that defines this type.
NodeId node_id;
// The instruction that defines this type.
InstId inst_id;
// The value representation for this type. Will be `Unknown` if the type is
// not complete.
ValueRepresentation value_representation = ValueRepresentation();
@@ -183,10 +183,10 @@ class File : public Printable<File> {
}
auto OutputYaml(bool include_builtins) const -> Yaml::OutputMapping;
// Returns array bound value from the bound node.
auto GetArrayBoundValue(NodeId bound_id) const -> uint64_t {
// Returns array bound value from the bound instruction.
auto GetArrayBoundValue(InstId bound_id) const -> uint64_t {
return integers()
.Get(nodes().GetAs<IntegerLiteral>(bound_id).integer_id)
.Get(insts().GetAs<IntegerLiteral>(bound_id).integer_id)
.getZExtValue();
}
@@ -209,15 +209,15 @@ class File : public Printable<File> {
complete_types_.push_back(object_type_id);
}
auto GetTypeAllowBuiltinTypes(TypeId type_id) const -> NodeId {
auto GetTypeAllowBuiltinTypes(TypeId type_id) const -> InstId {
if (type_id == TypeId::TypeType) {
return NodeId::BuiltinTypeType;
return InstId::BuiltinTypeType;
} else if (type_id == TypeId::Error) {
return NodeId::BuiltinError;
return InstId::BuiltinError;
} else if (type_id == TypeId::Invalid) {
return NodeId::Invalid;
return InstId::Invalid;
} else {
return types().Get(type_id).node_id;
return types().Get(type_id).inst_id;
}
}
@@ -239,8 +239,8 @@ class File : public Printable<File> {
// Gets the pointee type of the given type, which must be a pointer type.
auto GetPointeeType(TypeId pointer_id) const -> TypeId {
return nodes()
.GetAs<PointerType>(types().Get(pointer_id).node_id)
return insts()
.GetAs<PointerType>(types().Get(pointer_id).inst_id)
.pointee_id;
}
@@ -251,9 +251,9 @@ class File : public Printable<File> {
auto StringifyType(TypeId type_id, bool in_type_context = false) const
-> std::string;
// Same as `StringifyType`, but starting with a node representing a type
// expression rather than a canonical type.
auto StringifyTypeExpression(NodeId outer_node_id,
// Same as `StringifyType`, but starting with an instruction representing a
// type expression rather than a canonical type.
auto StringifyTypeExpression(InstId outer_inst_id,
bool in_type_context = false) const
-> std::string;
@@ -289,10 +289,10 @@ class File : public Printable<File> {
auto type_blocks() const -> const BlockValueStore<TypeBlockId, TypeId>& {
return type_blocks_;
}
auto nodes() -> NodeStore& { return nodes_; }
auto nodes() const -> const NodeStore& { return nodes_; }
auto node_blocks() -> NodeBlockStore& { return node_blocks_; }
auto node_blocks() const -> const NodeBlockStore& { return node_blocks_; }
auto insts() -> InstStore& { return insts_; }
auto insts() const -> const InstStore& { return insts_; }
auto inst_blocks() -> InstBlockStore& { return inst_blocks_; }
auto inst_blocks() const -> const InstBlockStore& { return inst_blocks_; }
// A list of types that were completed in this file, in the order in which
// they were completed. Earlier types in this list cannot contain instances of
@@ -301,9 +301,9 @@ class File : public Printable<File> {
return complete_types_;
}
auto top_node_block_id() const -> NodeBlockId { return top_node_block_id_; }
auto set_top_node_block_id(NodeBlockId block_id) -> void {
top_node_block_id_ = block_id;
auto top_inst_block_id() const -> InstBlockId { return top_inst_block_id_; }
auto set_top_inst_block_id(InstBlockId block_id) -> void {
top_inst_block_id_ = block_id;
}
// Returns true if there were errors creating the semantics IR.
@@ -318,7 +318,7 @@ class File : public Printable<File> {
// Shared, compile-scoped values.
SharedValueStores* value_stores_;
// Slab allocator, used to allocate node and type blocks.
// Slab allocator, used to allocate instruction and type blocks.
llvm::BumpPtrAllocator allocator_;
// The associated filename.
@@ -333,7 +333,7 @@ class File : public Printable<File> {
// Related IRs. There will always be at least 2 entries, the builtin IR (used
// for references of builtins) followed by the current IR (used for references
// crossing node blocks).
// crossing instruction blocks).
llvm::SmallVector<const File*> cross_reference_irs_;
// Storage for name scopes.
@@ -349,38 +349,40 @@ class File : public Printable<File> {
// the data is provided by allocator_.
BlockValueStore<TypeBlockId, TypeId> type_blocks_;
// All nodes. The first entries will always be cross-references to builtins,
// at indices matching BuiltinKind ordering.
NodeStore nodes_;
// All instructions. The first entries will always be cross-references to
// builtins, at indices matching BuiltinKind ordering.
InstStore insts_;
// Node blocks within the IR. These reference entries in nodes_. Storage for
// the data is provided by allocator_.
NodeBlockStore node_blocks_;
// Instruction blocks within the IR. These reference entries in
// insts_. Storage for the data is provided by allocator_.
InstBlockStore inst_blocks_;
// The top node block ID.
NodeBlockId top_node_block_id_ = NodeBlockId::Invalid;
// The top instruction block ID.
InstBlockId top_inst_block_id_ = InstBlockId::Invalid;
};
// The expression category of a semantics node. See /docs/design/values.md for
// details.
// The expression category of a sem_ir instruction. See /docs/design/values.md
// for details.
enum class ExpressionCategory : int8_t {
// This node does not correspond to an expression, and as such has no
// This instruction does not correspond to an expression, and as such has no
// category.
NotExpression,
// The category of this node is not known due to an error.
// The category of this instruction is not known due to an error.
Error,
// This node represents a value expression.
// This instruction represents a value expression.
Value,
// This node represents a durable reference expression, that denotes an
// This instruction represents a durable reference expression, that denotes an
// object that outlives the current full expression context.
DurableReference,
// This node represents an ephemeral reference expression, that denotes an
// This instruction represents an ephemeral reference expression, that denotes
// an
// object that does not outlive the current full expression context.
EphemeralReference,
// This node represents an initializing expression, that describes how to
// This instruction represents an initializing expression, that describes how
// to
// initialize an object.
Initializing,
// This node represents a syntactic combination of expressions that are
// This instruction represents a syntactic combination of expressions that are
// permitted to have different expression categories. This is used for tuple
// and struct literals, where the subexpressions for different elements can
// have different categories.
@@ -388,8 +390,8 @@ enum class ExpressionCategory : int8_t {
Last = Mixed
};
// Returns the expression category for a node.
auto GetExpressionCategory(const File& file, NodeId node_id)
// Returns the expression category for an instruction.
auto GetExpressionCategory(const File& file, InstId inst_id)
-> ExpressionCategory;
// Returns information about the value representation to use for a type.
+168 -167
View File
@@ -14,14 +14,14 @@
namespace Carbon::SemIR {
namespace {
// Assigns names to nodes, blocks, and scopes in the Semantics IR.
// Assigns names to instructions, blocks, and scopes in the Semantics IR.
//
// TODOs / future work ideas:
// - Add a documentation file for the textual format and link to the
// naming section here.
// - Consider representing literals as just `literal` in the IR and using the
// type to distinguish.
class NodeNamer {
class InstNamer {
public:
// int32_t matches the input value size.
// NOLINTNEXTLINE(performance-enum-size)
@@ -31,18 +31,18 @@ class NodeNamer {
};
static_assert(sizeof(ScopeIndex) == sizeof(FunctionId));
NodeNamer(const Lex::TokenizedBuffer& tokenized_buffer,
InstNamer(const Lex::TokenizedBuffer& tokenized_buffer,
const Parse::Tree& parse_tree, const File& sem_ir)
: tokenized_buffer_(tokenized_buffer),
parse_tree_(parse_tree),
sem_ir_(sem_ir) {
nodes.resize(sem_ir.nodes().size());
labels.resize(sem_ir.node_blocks().size());
insts.resize(sem_ir.insts().size());
labels.resize(sem_ir.inst_blocks().size());
scopes.resize(1 + sem_ir.functions().size() + sem_ir.classes().size());
// Build the file scope.
GetScopeInfo(ScopeIndex::File).name = globals.AddNameUnchecked("file");
CollectNamesInBlock(ScopeIndex::File, sem_ir.top_node_block_id());
CollectNamesInBlock(ScopeIndex::File, sem_ir.top_inst_block_id());
// Build each function scope.
for (auto [i, fn] : llvm::enumerate(sem_ir.functions().array_ref())) {
@@ -57,10 +57,10 @@ class NodeNamer {
CollectNamesInBlock(fn_scope, fn.implicit_param_refs_id);
CollectNamesInBlock(fn_scope, fn.param_refs_id);
if (fn.return_slot_id.is_valid()) {
nodes[fn.return_slot_id.index] = {
insts[fn.return_slot_id.index] = {
fn_scope,
GetScopeInfo(fn_scope).nodes.AllocateName(
*this, sem_ir.nodes().Get(fn.return_slot_id).parse_node(),
GetScopeInfo(fn_scope).insts.AllocateName(
*this, sem_ir.insts().Get(fn.return_slot_id).parse_node(),
"return")};
}
if (!fn.body_block_ids.empty()) {
@@ -118,30 +118,31 @@ class NodeNamer {
return GetScopeInfo(GetScopeFor(class_id)).name.str();
}
// Returns the IR name to use for a node, when referenced from a given scope.
auto GetNameFor(ScopeIndex scope_idx, NodeId node_id) -> std::string {
if (!node_id.is_valid()) {
// Returns the IR name to use for an instruction, when referenced from a given
// scope.
auto GetNameFor(ScopeIndex scope_idx, InstId inst_id) -> std::string {
if (!inst_id.is_valid()) {
return "invalid";
}
// Check for a builtin.
if (node_id.index < BuiltinKind::ValidCount) {
return BuiltinKind::FromInt(node_id.index).label().str();
if (inst_id.index < BuiltinKind::ValidCount) {
return BuiltinKind::FromInt(inst_id.index).label().str();
}
auto& [node_scope, node_name] = nodes[node_id.index];
if (!node_name) {
auto& [inst_scope, inst_name] = insts[inst_id.index];
if (!inst_name) {
// This should not happen in valid IR.
return "<unexpected noderef " + llvm::itostr(node_id.index) + ">";
return "<unexpected instref " + llvm::itostr(inst_id.index) + ">";
}
if (node_scope == scope_idx) {
return node_name.str().str();
if (inst_scope == scope_idx) {
return inst_name.str().str();
}
return (GetScopeInfo(node_scope).name.str() + "." + node_name.str()).str();
return (GetScopeInfo(inst_scope).name.str() + "." + inst_name.str()).str();
}
// Returns the IR name to use for a label, when referenced from a given scope.
auto GetLabelFor(ScopeIndex scope_idx, NodeBlockId block_id) -> std::string {
auto GetLabelFor(ScopeIndex scope_idx, InstBlockId block_id) -> std::string {
if (!block_id.is_valid()) {
return "!invalid";
}
@@ -149,7 +150,7 @@ class NodeNamer {
auto& [label_scope, label_name] = labels[block_id.index];
if (!label_name) {
// This should not happen in valid IR.
return "<unexpected nodeblockref " + llvm::itostr(block_id.index) + ">";
return "<unexpected instblockref " + llvm::itostr(block_id.index) + ">";
}
if (label_scope == scope_idx) {
return label_name.str().str();
@@ -203,10 +204,10 @@ class NodeNamer {
return Name(allocated.insert({name, NameResult()}).first);
}
auto AllocateName(const NodeNamer& namer, Parse::Node node,
auto AllocateName(const InstNamer& namer, Parse::Node node,
std::string name = "") -> Name {
// The best (shortest) name for this node so far, and the current name
// for it.
// The best (shortest) name for this instruction so far, and the current
// name for it.
Name best;
Name current;
@@ -217,8 +218,8 @@ class NodeNamer {
if (!added) {
if (mark_ambiguous) {
// This name was allocated for a different node. Mark it as
// ambiguous and keep looking for a name for this node.
// This name was allocated for a different instruction. Mark it as
// ambiguous and keep looking for a name for this instruction.
new_name.SetAmbiguous();
}
} else {
@@ -269,7 +270,7 @@ class NodeNamer {
// A named scope that contains named entities.
struct Scope {
Namespace::Name name;
Namespace nodes = {.prefix = "%"};
Namespace insts = {.prefix = "%"};
Namespace labels = {.prefix = "!"};
};
@@ -277,7 +278,7 @@ class NodeNamer {
return scopes[static_cast<int>(scope_idx)];
}
auto AddBlockLabel(ScopeIndex scope_idx, NodeBlockId block_id,
auto AddBlockLabel(ScopeIndex scope_idx, InstBlockId block_id,
std::string name = "",
Parse::Node parse_node = Parse::Node::Invalid) -> void {
if (!block_id.is_valid() || labels[block_id.index].second) {
@@ -285,9 +286,9 @@ class NodeNamer {
}
if (parse_node == Parse::Node::Invalid) {
if (const auto& block = sem_ir_.node_blocks().Get(block_id);
if (const auto& block = sem_ir_.inst_blocks().Get(block_id);
!block.empty()) {
parse_node = sem_ir_.nodes().Get(block.front()).parse_node();
parse_node = sem_ir_.insts().Get(block.front()).parse_node();
}
}
@@ -296,14 +297,14 @@ class NodeNamer {
*this, parse_node, std::move(name))};
}
// Finds and adds a suitable block label for the given semantics node that
// Finds and adds a suitable block label for the given SemIR instruction that
// represents some kind of branch.
auto AddBlockLabel(ScopeIndex scope_idx, NodeBlockId block_id, Node node)
auto AddBlockLabel(ScopeIndex scope_idx, InstBlockId block_id, Inst inst)
-> void {
llvm::StringRef name;
switch (parse_tree_.node_kind(node.parse_node())) {
switch (parse_tree_.node_kind(inst.parse_node())) {
case Parse::NodeKind::IfExpressionIf:
switch (node.kind()) {
switch (inst.kind()) {
case BranchIf::Kind:
name = "if.expr.then";
break;
@@ -319,7 +320,7 @@ class NodeNamer {
break;
case Parse::NodeKind::IfCondition:
switch (node.kind()) {
switch (inst.kind()) {
case BranchIf::Kind:
name = "if.then";
break;
@@ -336,9 +337,9 @@ class NodeNamer {
break;
case Parse::NodeKind::ShortCircuitOperand: {
bool is_rhs = node.Is<BranchIf>();
bool is_rhs = inst.Is<BranchIf>();
bool is_and = tokenized_buffer_.GetKind(parse_tree_.node_token(
node.parse_node())) == Lex::TokenKind::And;
inst.parse_node())) == Lex::TokenKind::And;
name = is_and ? (is_rhs ? "and.rhs" : "and.result")
: (is_rhs ? "or.rhs" : "or.result");
break;
@@ -349,11 +350,11 @@ class NodeNamer {
break;
case Parse::NodeKind::WhileCondition:
switch (node.kind()) {
case NodeKind::BranchIf:
switch (inst.kind()) {
case InstKind::BranchIf:
name = "while.body";
break;
case NodeKind::Branch:
case InstKind::Branch:
name = "while.done";
break;
default:
@@ -365,10 +366,10 @@ class NodeNamer {
break;
}
AddBlockLabel(scope_idx, block_id, name.str(), node.parse_node());
AddBlockLabel(scope_idx, block_id, name.str(), inst.parse_node());
}
auto CollectNamesInBlock(ScopeIndex scope_idx, NodeBlockId block_id) -> void {
auto CollectNamesInBlock(ScopeIndex scope_idx, InstBlockId block_id) -> void {
if (!block_id.is_valid()) {
return;
}
@@ -376,73 +377,73 @@ class NodeNamer {
Scope& scope = GetScopeInfo(scope_idx);
// Use bound names where available. Otherwise, assign a backup name.
for (auto node_id : sem_ir_.node_blocks().Get(block_id)) {
if (!node_id.is_valid()) {
for (auto inst_id : sem_ir_.inst_blocks().Get(block_id)) {
if (!inst_id.is_valid()) {
continue;
}
auto node = sem_ir_.nodes().Get(node_id);
auto add_node_name = [&](std::string name) {
nodes[node_id.index] = {scope_idx, scope.nodes.AllocateName(
*this, node.parse_node(), name)};
auto inst = sem_ir_.insts().Get(inst_id);
auto add_inst_name = [&](std::string name) {
insts[inst_id.index] = {scope_idx, scope.insts.AllocateName(
*this, inst.parse_node(), name)};
};
auto add_node_name_id = [&](StringId name_id,
auto add_inst_name_id = [&](StringId name_id,
llvm::StringRef suffix = "") {
if (name_id.is_valid()) {
add_node_name((sem_ir_.strings().Get(name_id).str() + suffix).str());
add_inst_name((sem_ir_.strings().Get(name_id).str() + suffix).str());
} else {
add_node_name(suffix.str());
add_inst_name(suffix.str());
}
};
switch (node.kind()) {
switch (inst.kind()) {
case Branch::Kind: {
AddBlockLabel(scope_idx, node.As<Branch>().target_id, node);
AddBlockLabel(scope_idx, inst.As<Branch>().target_id, inst);
break;
}
case BranchIf::Kind: {
AddBlockLabel(scope_idx, node.As<BranchIf>().target_id, node);
AddBlockLabel(scope_idx, inst.As<BranchIf>().target_id, inst);
break;
}
case BranchWithArg::Kind: {
AddBlockLabel(scope_idx, node.As<BranchWithArg>().target_id, node);
AddBlockLabel(scope_idx, inst.As<BranchWithArg>().target_id, inst);
break;
}
case SpliceBlock::Kind: {
CollectNamesInBlock(scope_idx, node.As<SpliceBlock>().block_id);
CollectNamesInBlock(scope_idx, inst.As<SpliceBlock>().block_id);
break;
}
case BindName::Kind: {
add_node_name_id(node.As<BindName>().name_id);
add_inst_name_id(inst.As<BindName>().name_id);
continue;
}
case FunctionDeclaration::Kind: {
add_node_name_id(sem_ir_.functions()
.Get(node.As<FunctionDeclaration>().function_id)
add_inst_name_id(sem_ir_.functions()
.Get(inst.As<FunctionDeclaration>().function_id)
.name_id);
continue;
}
case ClassType::Kind: {
add_node_name_id(
sem_ir_.classes().Get(node.As<ClassType>().class_id).name_id);
add_inst_name_id(
sem_ir_.classes().Get(inst.As<ClassType>().class_id).name_id);
continue;
}
case NameReference::Kind: {
add_node_name_id(node.As<NameReference>().name_id, ".ref");
add_inst_name_id(inst.As<NameReference>().name_id, ".ref");
continue;
}
case Parameter::Kind: {
add_node_name_id(node.As<Parameter>().name_id);
add_inst_name_id(inst.As<Parameter>().name_id);
continue;
}
case SelfParameter::Kind: {
add_node_name(node.As<SelfParameter>().is_addr_self.index
add_inst_name(inst.As<SelfParameter>().is_addr_self.index
? "self.addr"
: "self");
continue;
}
case VarStorage::Kind: {
add_node_name_id(node.As<VarStorage>().name_id, ".var");
add_inst_name_id(inst.As<VarStorage>().name_id, ".var");
continue;
}
default: {
@@ -451,8 +452,8 @@ class NodeNamer {
}
// Sequentially number all remaining values.
if (node.kind().value_kind() != NodeValueKind::None) {
add_node_name("");
if (inst.kind().value_kind() != InstValueKind::None) {
add_inst_name("");
}
}
}
@@ -462,7 +463,7 @@ class NodeNamer {
const File& sem_ir_;
Namespace globals = {.prefix = "@"};
std::vector<std::pair<ScopeIndex, Namespace::Name>> nodes;
std::vector<std::pair<ScopeIndex, Namespace::Name>> insts;
std::vector<std::pair<ScopeIndex, Namespace::Name>> labels;
std::vector<Scope> scopes;
};
@@ -476,17 +477,17 @@ class Formatter {
llvm::raw_ostream& out)
: sem_ir_(sem_ir),
out_(out),
node_namer_(tokenized_buffer, parse_tree, sem_ir) {}
inst_namer_(tokenized_buffer, parse_tree, sem_ir) {}
auto Format() -> void {
out_ << "file \"" << sem_ir_.filename() << "\" {\n";
// TODO: Include information from the `package` declaration, once we
// fully support it.
// TODO: Handle the case where there are multiple top-level node blocks.
// For example, there may be branching in the initializer of a global or a
// type expression.
if (auto block_id = sem_ir_.top_node_block_id(); block_id.is_valid()) {
llvm::SaveAndRestore file_scope(scope_, NodeNamer::ScopeIndex::File);
// TODO: Handle the case where there are multiple top-level instruction
// blocks. For example, there may be branching in the initializer of a
// global or a type expression.
if (auto block_id = sem_ir_.top_inst_block_id(); block_id.is_valid()) {
llvm::SaveAndRestore file_scope(scope_, InstNamer::ScopeIndex::File);
FormatCodeBlock(block_id);
}
out_ << "}\n";
@@ -506,7 +507,7 @@ class Formatter {
out_ << "\nclass ";
FormatClassName(id);
llvm::SaveAndRestore class_scope(scope_, node_namer_.GetScopeFor(id));
llvm::SaveAndRestore class_scope(scope_, inst_namer_.GetScopeFor(id));
if (class_info.scope_id.is_valid()) {
out_ << " {\n";
@@ -525,9 +526,9 @@ class Formatter {
out_ << "\nfn ";
FormatFunctionName(id);
llvm::SaveAndRestore function_scope(scope_, node_namer_.GetScopeFor(id));
llvm::SaveAndRestore function_scope(scope_, inst_namer_.GetScopeFor(id));
if (fn.implicit_param_refs_id != SemIR::NodeBlockId::Empty) {
if (fn.implicit_param_refs_id != SemIR::InstBlockId::Empty) {
out_ << "[";
FormatParameterList(fn.implicit_param_refs_id);
out_ << "]";
@@ -540,7 +541,7 @@ class Formatter {
if (fn.return_type_id.is_valid()) {
out_ << " -> ";
if (fn.return_slot_id.is_valid()) {
FormatNodeName(fn.return_slot_id);
FormatInstName(fn.return_slot_id);
out_ << ": ";
}
FormatType(fn.return_type_id);
@@ -564,27 +565,27 @@ class Formatter {
}
}
auto FormatParameterList(NodeBlockId param_refs_id) -> void {
auto FormatParameterList(InstBlockId param_refs_id) -> void {
llvm::ListSeparator sep;
for (const NodeId param_id : sem_ir_.node_blocks().Get(param_refs_id)) {
for (const InstId param_id : sem_ir_.inst_blocks().Get(param_refs_id)) {
out_ << sep;
if (!param_id.is_valid()) {
out_ << "invalid";
continue;
}
FormatNodeName(param_id);
FormatInstName(param_id);
out_ << ": ";
FormatType(sem_ir_.nodes().Get(param_id).type_id());
FormatType(sem_ir_.insts().Get(param_id).type_id());
}
}
auto FormatCodeBlock(NodeBlockId block_id) -> void {
auto FormatCodeBlock(InstBlockId block_id) -> void {
if (!block_id.is_valid()) {
return;
}
for (const NodeId node_id : sem_ir_.node_blocks().Get(block_id)) {
FormatInstruction(node_id);
for (const InstId inst_id : sem_ir_.inst_blocks().Get(block_id)) {
FormatInstruction(inst_id);
}
}
@@ -592,39 +593,39 @@ class Formatter {
llvm::StringRef prefix) -> void {
// Name scopes aren't kept in any particular order. Sort the entries before
// we print them for stability and consistency.
llvm::SmallVector<std::pair<NodeId, StringId>> entries;
for (auto [name_id, node_id] : sem_ir_.name_scopes().Get(id)) {
entries.push_back({node_id, name_id});
llvm::SmallVector<std::pair<InstId, StringId>> entries;
for (auto [name_id, inst_id] : sem_ir_.name_scopes().Get(id)) {
entries.push_back({inst_id, name_id});
}
llvm::sort(entries,
[](auto a, auto b) { return a.first.index < b.first.index; });
llvm::ListSeparator sep(separator);
for (auto [node_id, name_id] : entries) {
for (auto [inst_id, name_id] : entries) {
out_ << sep << prefix;
FormatString(name_id);
out_ << " = ";
FormatNodeName(node_id);
FormatInstName(inst_id);
}
}
auto FormatInstruction(NodeId node_id) -> void {
if (!node_id.is_valid()) {
auto FormatInstruction(InstId inst_id) -> void {
if (!inst_id.is_valid()) {
Indent();
out_ << "invalid\n";
return;
}
FormatInstruction(node_id, sem_ir_.nodes().Get(node_id));
FormatInstruction(inst_id, sem_ir_.insts().Get(inst_id));
}
auto FormatInstruction(NodeId node_id, Node node) -> void {
auto FormatInstruction(InstId inst_id, Inst inst) -> void {
// clang warns on unhandled enum values; clang-tidy is incorrect here.
// NOLINTNEXTLINE(bugprone-switch-missing-default-case)
switch (node.kind()) {
#define CARBON_SEM_IR_NODE_KIND(NodeT) \
case NodeT::Kind: \
FormatInstruction(node_id, node.As<NodeT>()); \
switch (inst.kind()) {
#define CARBON_SEM_IR_INST_KIND(InstT) \
case InstT::Kind: \
FormatInstruction(inst_id, inst.As<InstT>()); \
break;
#include "toolchain/sem_ir/node_kind.def"
}
@@ -632,21 +633,21 @@ class Formatter {
auto Indent() -> void { out_.indent(indent_); }
template <typename NodeT>
auto FormatInstruction(NodeId node_id, NodeT node) -> void {
template <typename InstT>
auto FormatInstruction(InstId inst_id, InstT inst) -> void {
Indent();
FormatInstructionLHS(node_id, node);
out_ << NodeT::Kind.ir_name();
FormatInstructionRHS(node);
FormatInstructionLHS(inst_id, inst);
out_ << InstT::Kind.ir_name();
FormatInstructionRHS(inst);
out_ << "\n";
}
auto FormatInstructionLHS(NodeId node_id, Node node) -> void {
switch (node.kind().value_kind()) {
case NodeValueKind::Typed:
FormatNodeName(node_id);
auto FormatInstructionLHS(InstId inst_id, Inst inst) -> void {
switch (inst.kind().value_kind()) {
case InstValueKind::Typed:
FormatInstName(inst_id);
out_ << ": ";
switch (GetExpressionCategory(sem_ir_, node_id)) {
switch (GetExpressionCategory(sem_ir_, inst_id)) {
case ExpressionCategory::NotExpression:
case ExpressionCategory::Error:
case ExpressionCategory::Value:
@@ -660,99 +661,99 @@ class Formatter {
out_ << "init ";
break;
}
FormatType(node.type_id());
FormatType(inst.type_id());
out_ << " = ";
break;
case NodeValueKind::None:
case InstValueKind::None:
break;
}
}
template <typename NodeT>
auto FormatInstructionRHS(NodeT node) -> void {
template <typename InstT>
auto FormatInstructionRHS(InstT inst) -> void {
// By default, an instruction has a comma-separated argument list.
using Info = TypedNodeArgsInfo<NodeT>;
using Info = TypedInstArgsInfo<InstT>;
if constexpr (Info::NumArgs == 2) {
FormatArgs(Info::template Get<0>(node), Info::template Get<1>(node));
FormatArgs(Info::template Get<0>(inst), Info::template Get<1>(inst));
} else if constexpr (Info::NumArgs == 1) {
FormatArgs(Info::template Get<0>(node));
FormatArgs(Info::template Get<0>(inst));
} else {
FormatArgs();
}
}
auto FormatInstructionRHS(BlockArg node) -> void {
auto FormatInstructionRHS(BlockArg inst) -> void {
out_ << " ";
FormatLabel(node.block_id);
FormatLabel(inst.block_id);
}
auto FormatInstruction(NodeId /*node_id*/, BranchIf node) -> void {
auto FormatInstruction(InstId /*inst_id*/, BranchIf inst) -> void {
if (!in_terminator_sequence_) {
Indent();
}
out_ << "if ";
FormatNodeName(node.cond_id);
FormatInstName(inst.cond_id);
out_ << " " << Branch::Kind.ir_name() << " ";
FormatLabel(node.target_id);
FormatLabel(inst.target_id);
out_ << " else ";
in_terminator_sequence_ = true;
}
auto FormatInstruction(NodeId /*node_id*/, BranchWithArg node) -> void {
auto FormatInstruction(InstId /*inst_id*/, BranchWithArg inst) -> void {
if (!in_terminator_sequence_) {
Indent();
}
out_ << BranchWithArg::Kind.ir_name() << " ";
FormatLabel(node.target_id);
FormatLabel(inst.target_id);
out_ << "(";
FormatNodeName(node.arg_id);
FormatInstName(inst.arg_id);
out_ << ")\n";
in_terminator_sequence_ = false;
}
auto FormatInstruction(NodeId /*node_id*/, Branch node) -> void {
auto FormatInstruction(InstId /*inst_id*/, Branch inst) -> void {
if (!in_terminator_sequence_) {
Indent();
}
out_ << Branch::Kind.ir_name() << " ";
FormatLabel(node.target_id);
FormatLabel(inst.target_id);
out_ << "\n";
in_terminator_sequence_ = false;
}
auto FormatInstructionRHS(ArrayInit node) -> void {
auto FormatInstructionRHS(ArrayInit inst) -> void {
out_ << " ";
FormatArg(node.tuple_id);
FormatArg(inst.tuple_id);
llvm::ArrayRef<NodeId> inits_and_return_slot =
sem_ir_.node_blocks().Get(node.inits_and_return_slot_id);
llvm::ArrayRef<InstId> inits_and_return_slot =
sem_ir_.inst_blocks().Get(inst.inits_and_return_slot_id);
auto inits = inits_and_return_slot.drop_back(1);
auto return_slot_id = inits_and_return_slot.back();
out_ << ", (";
llvm::ListSeparator sep;
for (auto node_id : inits) {
for (auto inst_id : inits) {
out_ << sep;
FormatArg(node_id);
FormatArg(inst_id);
}
out_ << ')';
FormatReturnSlot(return_slot_id);
}
auto FormatInstructionRHS(Call node) -> void {
auto FormatInstructionRHS(Call inst) -> void {
out_ << " ";
FormatArg(node.callee_id);
FormatArg(inst.callee_id);
if (!node.args_id.is_valid()) {
if (!inst.args_id.is_valid()) {
out_ << "(<invalid>)";
return;
}
llvm::ArrayRef<NodeId> args = sem_ir_.node_blocks().Get(node.args_id);
llvm::ArrayRef<InstId> args = sem_ir_.inst_blocks().Get(inst.args_id);
bool has_return_slot =
GetInitializingRepresentation(sem_ir_, node.type_id).has_return_slot();
NodeId return_slot_id = NodeId::Invalid;
GetInitializingRepresentation(sem_ir_, inst.type_id).has_return_slot();
InstId return_slot_id = InstId::Invalid;
if (has_return_slot) {
return_slot_id = args.back();
args = args.drop_back();
@@ -760,9 +761,9 @@ class Formatter {
llvm::ListSeparator sep;
out_ << '(';
for (auto node_id : args) {
for (auto inst_id : args) {
out_ << sep;
FormatArg(node_id);
FormatArg(inst_id);
}
out_ << ')';
@@ -771,24 +772,24 @@ class Formatter {
}
}
auto FormatInstructionRHS(InitializeFrom node) -> void {
FormatArgs(node.src_id);
FormatReturnSlot(node.dest_id);
auto FormatInstructionRHS(InitializeFrom inst) -> void {
FormatArgs(inst.src_id);
FormatReturnSlot(inst.dest_id);
}
auto FormatInstructionRHS(CrossReference node) -> void {
auto FormatInstructionRHS(CrossReference inst) -> void {
// TODO: Figure out a way to make this meaningful. We'll need some way to
// name cross-reference IRs, perhaps by the node ID of the import?
out_ << " " << node.ir_id << "." << node.node_id;
// name cross-reference IRs, perhaps by the instruction ID of the import?
out_ << " " << inst.ir_id << "." << inst.inst_id;
}
auto FormatInstructionRHS(SpliceBlock node) -> void {
FormatArgs(node.result_id);
auto FormatInstructionRHS(SpliceBlock inst) -> void {
FormatArgs(inst.result_id);
out_ << " {";
if (!sem_ir_.node_blocks().Get(node.block_id).empty()) {
if (!sem_ir_.inst_blocks().Get(inst.block_id).empty()) {
out_ << "\n";
indent_ += 2;
FormatCodeBlock(node.block_id);
FormatCodeBlock(inst.block_id);
indent_ -= 2;
Indent();
}
@@ -796,15 +797,15 @@ class Formatter {
}
// StructTypeFields are formatted as part of their StructType.
auto FormatInstruction(NodeId /*node_id*/, StructTypeField /*node*/) -> void {
auto FormatInstruction(InstId /*inst_id*/, StructTypeField /*inst*/) -> void {
}
auto FormatInstructionRHS(StructType node) -> void {
auto FormatInstructionRHS(StructType inst) -> void {
out_ << " {";
llvm::ListSeparator sep;
for (auto field_id : sem_ir_.node_blocks().Get(node.fields_id)) {
for (auto field_id : sem_ir_.inst_blocks().Get(inst.fields_id)) {
out_ << sep << ".";
auto field = sem_ir_.nodes().GetAs<StructTypeField>(field_id);
auto field = sem_ir_.insts().GetAs<StructTypeField>(field_id);
FormatString(field.name_id);
out_ << ": ";
FormatType(field.field_type_id);
@@ -841,14 +842,14 @@ class Formatter {
out_ << '}';
}
auto FormatArg(NodeId id) -> void { FormatNodeName(id); }
auto FormatArg(InstId id) -> void { FormatInstName(id); }
auto FormatArg(NodeBlockId id) -> void {
auto FormatArg(InstBlockId id) -> void {
out_ << '(';
llvm::ListSeparator sep;
for (auto node_id : sem_ir_.node_blocks().Get(id)) {
for (auto inst_id : sem_ir_.inst_blocks().Get(id)) {
out_ << sep;
FormatArg(node_id);
FormatArg(inst_id);
}
out_ << ')';
}
@@ -878,27 +879,27 @@ class Formatter {
out_ << ')';
}
auto FormatReturnSlot(NodeId dest_id) -> void {
auto FormatReturnSlot(InstId dest_id) -> void {
out_ << " to ";
FormatArg(dest_id);
}
auto FormatNodeName(NodeId id) -> void {
out_ << node_namer_.GetNameFor(scope_, id);
auto FormatInstName(InstId id) -> void {
out_ << inst_namer_.GetNameFor(scope_, id);
}
auto FormatLabel(NodeBlockId id) -> void {
out_ << node_namer_.GetLabelFor(scope_, id);
auto FormatLabel(InstBlockId id) -> void {
out_ << inst_namer_.GetLabelFor(scope_, id);
}
auto FormatString(StringId id) -> void { out_ << sem_ir_.strings().Get(id); }
auto FormatFunctionName(FunctionId id) -> void {
out_ << node_namer_.GetNameFor(id);
out_ << inst_namer_.GetNameFor(id);
}
auto FormatClassName(ClassId id) -> void {
out_ << node_namer_.GetNameFor(id);
out_ << inst_namer_.GetNameFor(id);
}
auto FormatType(TypeId id) -> void {
@@ -912,8 +913,8 @@ class Formatter {
private:
const File& sem_ir_;
llvm::raw_ostream& out_;
NodeNamer node_namer_;
NodeNamer::ScopeIndex scope_ = NodeNamer::ScopeIndex::None;
InstNamer inst_namer_;
InstNamer::ScopeIndex scope_ = InstNamer::ScopeIndex::None;
bool in_terminator_sequence_ = false;
int indent_ = 2;
};
+30 -30
View File
@@ -14,24 +14,24 @@
namespace Carbon::SemIR {
// The ID of a node.
struct NodeId : public IndexBase, public Printable<NodeId> {
// An explicitly invalid node ID.
static const NodeId Invalid;
// The ID of an instruction.
struct InstId : public IndexBase, public Printable<InstId> {
// An explicitly invalid instruction ID.
static const InstId Invalid;
// Builtin node IDs.
#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) static const NodeId Builtin##Name;
// Builtin instruction IDs.
#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) static const InstId 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());
// Returns the cross-reference instruction ID for a builtin. This relies on
// File guarantees for builtin cross-reference placement.
static constexpr auto ForBuiltin(BuiltinKind kind) -> InstId {
return InstId(kind.AsInt());
}
using IndexBase::IndexBase;
auto Print(llvm::raw_ostream& out) const -> void {
out << "node";
out << "inst";
if (!is_valid()) {
IndexBase::Print(out);
} else if (index < BuiltinKind::ValidCount) {
@@ -44,11 +44,11 @@ struct NodeId : public IndexBase, public Printable<NodeId> {
}
};
constexpr NodeId NodeId::Invalid = NodeId(NodeId::InvalidIndex);
constexpr InstId InstId::Invalid = InstId(InstId::InvalidIndex);
#define CARBON_SEM_IR_BUILTIN_KIND_NAME(Name) \
constexpr NodeId NodeId::Builtin##Name = \
NodeId::ForBuiltin(BuiltinKind::Name);
constexpr InstId InstId::Builtin##Name = \
InstId::ForBuiltin(BuiltinKind::Name);
#include "toolchain/sem_ir/builtin_kind.def"
// The ID of a function.
@@ -127,16 +127,16 @@ struct NameScopeId : public IndexBase, public Printable<NameScopeId> {
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;
// The ID of an instruction block.
struct InstBlockId : public IndexBase, public Printable<InstBlockId> {
// All File instances must provide the 0th instruction block as empty.
static const InstBlockId Empty;
// An explicitly invalid ID.
static const NodeBlockId Invalid;
static const InstBlockId Invalid;
// An ID for unreachable code.
static const NodeBlockId Unreachable;
static const InstBlockId Unreachable;
using IndexBase::IndexBase;
auto Print(llvm::raw_ostream& out) const -> void {
@@ -149,13 +149,13 @@ struct NodeBlockId : public IndexBase, public Printable<NodeBlockId> {
}
};
constexpr NodeBlockId NodeBlockId::Empty = NodeBlockId(0);
constexpr NodeBlockId NodeBlockId::Invalid =
NodeBlockId(NodeBlockId::InvalidIndex);
constexpr NodeBlockId NodeBlockId::Unreachable =
NodeBlockId(NodeBlockId::InvalidIndex - 1);
constexpr InstBlockId InstBlockId::Empty = InstBlockId(0);
constexpr InstBlockId InstBlockId::Invalid =
InstBlockId(InstBlockId::InvalidIndex);
constexpr InstBlockId InstBlockId::Unreachable =
InstBlockId(InstBlockId::InvalidIndex - 1);
// The ID of a node block.
// The ID of a type.
struct TypeId : public IndexBase, public Printable<TypeId> {
// The builtin TypeType.
static const TypeId TypeType;
@@ -205,10 +205,10 @@ struct MemberIndex : public IndexBase, public Printable<MemberIndex> {
// Support use of Id types as DenseMap/DenseSet keys.
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NodeBlockId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeBlockId> {};
struct llvm::DenseMapInfo<Carbon::SemIR::InstBlockId>
: public Carbon::IndexMapInfo<Carbon::SemIR::InstBlockId> {};
template <>
struct llvm::DenseMapInfo<Carbon::SemIR::NodeId>
: public Carbon::IndexMapInfo<Carbon::SemIR::NodeId> {};
struct llvm::DenseMapInfo<Carbon::SemIR::InstId>
: public Carbon::IndexMapInfo<Carbon::SemIR::InstId> {};
#endif // CARBON_TOOLCHAIN_SEM_IR_IDS_H_
@@ -2,11 +2,11 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::SemIR {
auto Node::Print(llvm::raw_ostream& out) const -> void {
auto Inst::Print(llvm::raw_ostream& out) const -> void {
out << "{kind: " << kind_;
auto print_args = [&](auto info) {
@@ -22,9 +22,9 @@ auto Node::Print(llvm::raw_ostream& out) const -> void {
// 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) \
#define CARBON_SEM_IR_INST_KIND(Name) \
case Name::Kind: \
print_args(TypedNodeArgsInfo<Name>()); \
print_args(TypedInstArgsInfo<Name>()); \
break;
#include "toolchain/sem_ir/node_kind.def"
}
@@ -2,8 +2,8 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_TOOLCHAIN_SEM_IR_NODE_H_
#define CARBON_TOOLCHAIN_SEM_IR_NODE_H_
#ifndef CARBON_TOOLCHAIN_SEM_IR_INST_H_
#define CARBON_TOOLCHAIN_SEM_IR_INST_H_
#include <cstdint>
@@ -14,98 +14,101 @@
#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"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
// 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 {
// Data about the arguments of a typed instruction, to aid in type erasure. The
// `KindT` parameter is used to check that `TypedInst` is a typed instruction.
template <typename TypedInst,
const InstKind::Definition& KindT = TypedInst::Kind>
struct TypedInstArgsInfo {
// A corresponding std::tuple<...> type.
using Tuple = decltype(StructReflection::AsTuple(std::declval<TypedNode>()));
using Tuple = decltype(StructReflection::AsTuple(std::declval<TypedInst>()));
static constexpr int FirstArgField =
HasParseNode<TypedNode> + HasTypeId<TypedNode>;
HasParseNode<TypedInst> + HasTypeId<TypedInst>;
static constexpr int NumArgs = std::tuple_size_v<Tuple> - FirstArgField;
static_assert(NumArgs <= 2,
"Unsupported: typed node has more than two data fields");
"Unsupported: typed inst has more than two data fields");
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));
static auto Get(TypedInst inst) -> ArgType<N> {
return std::get<FirstArgField + N>(StructReflection::AsTuple(inst));
}
};
// 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:
// A type-erased representation of a SemIR instruction, that may be constructed
// from the specific kinds of instruction defined in `typed_insts.h`. This
// provides access to common fields present on most or all kinds of
// instructions:
//
// - `parse_node` for error placement.
// - `kind` for run-time logic when the input Kind is unknown.
// - `type_id` for quick type checking.
//
// In addition, kind-specific data can be accessed by casting to the specific
// kind of node:
// kind of instruction:
//
// - 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,
// - Use `inst.kind()` or `Is<TypedInst>` to determine what kind of instruction
// it is.
// - Cast to a specific type using `inst.As<TypedInst>()`
// - Using the wrong kind in `inst.As<TypedInst>()` is a programming error,
// and will CHECK-fail in debug modes (opt may too, but it's not an API
// guarantee).
// - Use `node.TryAs<TypedNode>()` to safely access type-specific node data
// where the node's kind is not known.
class Node : public Printable<Node> {
// - Use `inst.TryAs<TypedInst>()` to safely access type-specific instruction
// data
// where the instruction's kind is not known.
class Inst : public Printable<Inst> {
public:
template <typename TypedNode, typename Info = TypedNodeArgsInfo<TypedNode>>
template <typename TypedInst, typename Info = TypedInstArgsInfo<TypedInst>>
// NOLINTNEXTLINE(google-explicit-constructor)
Node(TypedNode typed_node)
Inst(TypedInst typed_inst)
: parse_node_(Parse::Node::Invalid),
kind_(TypedNode::Kind),
kind_(TypedInst::Kind),
type_id_(TypeId::Invalid),
arg0_(NodeId::InvalidIndex),
arg1_(NodeId::InvalidIndex) {
if constexpr (HasParseNode<TypedNode>) {
parse_node_ = typed_node.parse_node;
arg0_(InstId::InvalidIndex),
arg1_(InstId::InvalidIndex) {
if constexpr (HasParseNode<TypedInst>) {
parse_node_ = typed_inst.parse_node;
}
if constexpr (HasTypeId<TypedNode>) {
type_id_ = typed_node.type_id;
if constexpr (HasTypeId<TypedInst>) {
type_id_ = typed_inst.type_id;
}
if constexpr (Info::NumArgs > 0) {
arg0_ = ToRaw(Info::template Get<0>(typed_node));
arg0_ = ToRaw(Info::template Get<0>(typed_inst));
}
if constexpr (Info::NumArgs > 1) {
arg1_ = ToRaw(Info::template Get<1>(typed_node));
arg1_ = ToRaw(Info::template Get<1>(typed_inst));
}
}
// Returns whether this node has the specified type.
template <typename TypedNode>
// Returns whether this instruction has the specified type.
template <typename TypedInst>
auto Is() const -> bool {
return kind() == TypedNode::Kind;
return kind() == TypedInst::Kind;
}
// Casts this node to the given typed node, which must match the node's kind,
// and returns the typed node.
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;
// Casts this instruction to the given typed instruction, which must match the
// instruction's kind, and returns the typed instruction.
template <typename TypedInst, typename Info = TypedInstArgsInfo<TypedInst>>
auto As() const -> TypedInst {
CARBON_CHECK(Is<TypedInst>()) << "Casting inst of kind " << kind()
<< " to wrong kind " << TypedInst::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...};
if constexpr (HasParseNode<TypedInst>) {
return TypedInst{parse_node(), type_id_and_args...};
} else {
return TypedNode{type_id_and_args...};
return TypedInst{type_id_and_args...};
}
};
auto build_with_args = [&](auto... args) {
if constexpr (HasTypeId<TypedNode>) {
if constexpr (HasTypeId<TypedInst>) {
return build_with_type_id_and_args(type_id(), args...);
} else {
return build_with_type_id_and_args(args...);
@@ -124,30 +127,30 @@ class Node : public Printable<Node> {
}
}
// If this node is the given kind, returns a typed node, otherwise returns
// nullopt.
template <typename TypedNode>
auto TryAs() const -> std::optional<TypedNode> {
if (Is<TypedNode>()) {
return As<TypedNode>();
// If this instruction is the given kind, returns a typed instruction,
// otherwise returns nullopt.
template <typename TypedInst>
auto TryAs() const -> std::optional<TypedInst> {
if (Is<TypedInst>()) {
return As<TypedInst>();
} else {
return std::nullopt;
}
}
auto parse_node() const -> Parse::Node { return parse_node_; }
auto kind() const -> NodeKind { return kind_; }
auto kind() const -> InstKind { return kind_; }
// Gets the type of the value produced by evaluating this node.
// Gets the type of the value produced by evaluating this instruction.
auto type_id() const -> TypeId { return type_id_; }
auto Print(llvm::raw_ostream& out) const -> void;
private:
friend class NodeTestHelper;
friend class InstTestHelper;
// Raw constructor, used for testing.
explicit Node(NodeKind kind, Parse::Node parse_node, TypeId type_id,
explicit Inst(InstKind kind, Parse::Node parse_node, TypeId type_id,
int32_t arg0, int32_t arg1)
: parse_node_(parse_node),
kind_(kind),
@@ -172,7 +175,7 @@ class Node : public Printable<Node> {
}
Parse::Node parse_node_;
NodeKind kind_;
InstKind kind_;
TypeId type_id_;
// Use `As` to access arg0 and arg1.
@@ -181,18 +184,18 @@ class Node : public Printable<Node> {
};
// TODO: This is currently 20 bytes because we sometimes have 2 arguments for a
// pair of Nodes. However, NodeKind is 1 byte; if args
// were 3.5 bytes, we could potentially shrink Node by 4 bytes. This
// pair of Insts. However, InstKind is 1 byte; if args
// were 3.5 bytes, we could potentially shrink Inst by 4 bytes. This
// may be worth investigating further.
static_assert(sizeof(Node) == 20, "Unexpected Node size");
static_assert(sizeof(Inst) == 20, "Unexpected Inst size");
// 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);
// Typed instructions can be printed by converting them to instructions.
template <typename TypedInst, typename = TypedInstArgsInfo<TypedInst>>
inline llvm::raw_ostream& operator<<(llvm::raw_ostream& out, TypedInst inst) {
Inst(inst).Print(out);
return out;
}
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_NODE_H_
#endif // CARBON_TOOLCHAIN_SEM_IR_INST_H_
+11 -11
View File
@@ -4,35 +4,35 @@
#include "toolchain/sem_ir/node_kind.h"
#include "toolchain/sem_ir/typed_nodes.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
CARBON_DEFINE_ENUM_CLASS_NAMES(NodeKind) = {
#define CARBON_SEM_IR_NODE_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
CARBON_DEFINE_ENUM_CLASS_NAMES(InstKind) = {
#define CARBON_SEM_IR_INST_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name)
#include "toolchain/sem_ir/node_kind.def"
};
auto NodeKind::ir_name() const -> llvm::StringLiteral {
auto InstKind::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(Name) \
HasTypeId<SemIR::Name> ? NodeValueKind::Typed : NodeValueKind::None,
auto InstKind::value_kind() const -> InstValueKind {
static constexpr InstValueKind Table[] = {
#define CARBON_SEM_IR_INST_KIND(Name) \
HasTypeId<SemIR::Name> ? InstValueKind::Typed : InstValueKind::None,
#include "toolchain/sem_ir/node_kind.def"
};
return Table[AsInt()];
}
auto NodeKind::terminator_kind() const -> TerminatorKind {
auto InstKind::terminator_kind() const -> TerminatorKind {
return definition().terminator_kind();
}
auto NodeKind::definition() const -> const Definition& {
auto InstKind::definition() const -> const Definition& {
static constexpr const Definition* Table[] = {
#define CARBON_SEM_IR_NODE_KIND(Name) &SemIR::Name::Kind,
#define CARBON_SEM_IR_INST_KIND(Name) &SemIR::Name::Kind,
#include "toolchain/sem_ir/node_kind.def"
};
return *Table[AsInt()];
+62 -62
View File
@@ -8,70 +8,70 @@
// at the end of this file.
//
// 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_INST_KIND(Name)
// Invoked for each kind of semantic instruction.
#ifndef CARBON_SEM_IR_NODE_KIND
#ifndef CARBON_SEM_IR_INST_KIND
#error "Must define the x-macro to use this file."
#endif
// 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(BoundMethod)
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(ClassFieldAccess)
CARBON_SEM_IR_NODE_KIND(ClassType)
CARBON_SEM_IR_NODE_KIND(ConstType)
CARBON_SEM_IR_NODE_KIND(CrossReference)
CARBON_SEM_IR_NODE_KIND(Dereference)
CARBON_SEM_IR_NODE_KIND(Field)
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(SelfParameter)
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(UnboundFieldType)
CARBON_SEM_IR_NODE_KIND(ValueAsReference)
CARBON_SEM_IR_NODE_KIND(ValueOfInitializer)
CARBON_SEM_IR_NODE_KIND(VarStorage)
// For each instruction kind declared here there is a matching definition in
// `typed_insts.h`.
CARBON_SEM_IR_INST_KIND(AddressOf)
CARBON_SEM_IR_INST_KIND(ArrayIndex)
CARBON_SEM_IR_INST_KIND(ArrayInit)
CARBON_SEM_IR_INST_KIND(ArrayType)
CARBON_SEM_IR_INST_KIND(Assign)
CARBON_SEM_IR_INST_KIND(BinaryOperatorAdd)
CARBON_SEM_IR_INST_KIND(BindName)
CARBON_SEM_IR_INST_KIND(BindValue)
CARBON_SEM_IR_INST_KIND(BlockArg)
CARBON_SEM_IR_INST_KIND(BoolLiteral)
CARBON_SEM_IR_INST_KIND(BoundMethod)
CARBON_SEM_IR_INST_KIND(Branch)
CARBON_SEM_IR_INST_KIND(BranchIf)
CARBON_SEM_IR_INST_KIND(BranchWithArg)
CARBON_SEM_IR_INST_KIND(Builtin)
CARBON_SEM_IR_INST_KIND(Call)
CARBON_SEM_IR_INST_KIND(ClassDeclaration)
CARBON_SEM_IR_INST_KIND(ClassFieldAccess)
CARBON_SEM_IR_INST_KIND(ClassType)
CARBON_SEM_IR_INST_KIND(ConstType)
CARBON_SEM_IR_INST_KIND(CrossReference)
CARBON_SEM_IR_INST_KIND(Dereference)
CARBON_SEM_IR_INST_KIND(Field)
CARBON_SEM_IR_INST_KIND(FunctionDeclaration)
CARBON_SEM_IR_INST_KIND(InitializeFrom)
CARBON_SEM_IR_INST_KIND(IntegerLiteral)
CARBON_SEM_IR_INST_KIND(NameReference)
CARBON_SEM_IR_INST_KIND(Namespace)
CARBON_SEM_IR_INST_KIND(NoOp)
CARBON_SEM_IR_INST_KIND(Parameter)
CARBON_SEM_IR_INST_KIND(PointerType)
CARBON_SEM_IR_INST_KIND(RealLiteral)
CARBON_SEM_IR_INST_KIND(ReturnExpression)
CARBON_SEM_IR_INST_KIND(Return)
CARBON_SEM_IR_INST_KIND(SelfParameter)
CARBON_SEM_IR_INST_KIND(SpliceBlock)
CARBON_SEM_IR_INST_KIND(StringLiteral)
CARBON_SEM_IR_INST_KIND(StructAccess)
CARBON_SEM_IR_INST_KIND(StructInit)
CARBON_SEM_IR_INST_KIND(StructLiteral)
CARBON_SEM_IR_INST_KIND(StructTypeField)
CARBON_SEM_IR_INST_KIND(StructType)
CARBON_SEM_IR_INST_KIND(StructValue)
CARBON_SEM_IR_INST_KIND(TemporaryStorage)
CARBON_SEM_IR_INST_KIND(Temporary)
CARBON_SEM_IR_INST_KIND(TupleAccess)
CARBON_SEM_IR_INST_KIND(TupleIndex)
CARBON_SEM_IR_INST_KIND(TupleInit)
CARBON_SEM_IR_INST_KIND(TupleLiteral)
CARBON_SEM_IR_INST_KIND(TupleType)
CARBON_SEM_IR_INST_KIND(TupleValue)
CARBON_SEM_IR_INST_KIND(UnaryOperatorNot)
CARBON_SEM_IR_INST_KIND(UnboundFieldType)
CARBON_SEM_IR_INST_KIND(ValueAsReference)
CARBON_SEM_IR_INST_KIND(ValueOfInitializer)
CARBON_SEM_IR_INST_KIND(VarStorage)
#undef CARBON_SEM_IR_NODE_KIND
#undef CARBON_SEM_IR_INST_KIND
+45 -42
View File
@@ -12,102 +12,105 @@
namespace Carbon::SemIR {
// Whether a node produces or represents a value, and if so, what kind of value.
enum class NodeValueKind : int8_t {
// This node doesn't produce a value, and shouldn't be referenced by other
// nodes.
// Whether an instruction produces or represents a value, and if so, what kind
// of value.
enum class InstValueKind : int8_t {
// This instruction doesn't produce a value, and shouldn't be referenced by
// other
// instructions.
None,
// This node represents an expression or expression-like construct that
// This instruction represents an expression or expression-like construct that
// produces a value of the type indicated by its `type_id` field.
Typed,
};
// Whether a node is a terminator or part of the terminator sequence. The nodes
// in a block appear in the order NotTerminator, then TerminatorSequence, then
// Terminator, which is also the numerical order of these values.
// Whether an instruction is a terminator or part of the terminator sequence.
// The instructions in a block appear in the order NotTerminator, then
// TerminatorSequence, then Terminator, which is also the numerical order of
// these values.
enum class TerminatorKind : int8_t {
// This node is not a terminator.
// This instruction is not a terminator.
NotTerminator,
// This node is not itself a terminator, but forms part of a terminator
// This instruction is not itself a terminator, but forms part of a terminator
// sequence.
TerminatorSequence,
// This node is a terminator.
// This instruction is a terminator.
Terminator,
};
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) {
#define CARBON_SEM_IR_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
CARBON_DEFINE_RAW_ENUM_CLASS(InstKind, uint8_t) {
#define CARBON_SEM_IR_INST_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
#include "toolchain/sem_ir/node_kind.def"
};
class NodeKind : public CARBON_ENUM_BASE(NodeKind) {
class InstKind : public CARBON_ENUM_BASE(InstKind) {
public:
#define CARBON_SEM_IR_NODE_KIND(Name) CARBON_ENUM_CONSTANT_DECLARATION(Name)
#define CARBON_SEM_IR_INST_KIND(Name) CARBON_ENUM_CONSTANT_DECLARATION(Name)
#include "toolchain/sem_ir/node_kind.def"
using EnumBase::Create;
// Returns the name to use for this node kind in Semantics IR.
// Returns the name to use for this instruction kind in Semantics IR.
[[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;
// Returns whether this kind of instruction is expected to produce a value.
[[nodiscard]] auto value_kind() const -> InstValueKind;
// Returns whether this node kind is a code block terminator, such as an
// unconditional branch instruction, or part of the termination sequence,
// Returns whether this instruction kind is a code block terminator, such as
// an unconditional branch instruction, or part of the termination sequence,
// such as a conditional branch instruction. The termination sequence of a
// code block appears after all other instructions, and ends with a
// terminator instruction.
[[nodiscard]] auto terminator_kind() const -> TerminatorKind;
// Compute a fingerprint for this node kind, allowing its use as part of the
// key in a `FoldingSet`.
// Compute a fingerprint for this instruction 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`.
// Provides a definition for this instruction kind. Should only be called
// once, to construct the kind as part of defining it in `typed_insts.h`.
constexpr auto Define(llvm::StringLiteral ir_name,
TerminatorKind terminator_kind =
TerminatorKind::NotTerminator) const -> Definition;
private:
// Looks up the definition for this node kind.
// Looks up the definition for this instruction kind.
[[nodiscard]] auto definition() const -> const Definition&;
};
#define CARBON_SEM_IR_NODE_KIND(Name) \
CARBON_ENUM_CONSTANT_DEFINITION(NodeKind, Name)
#define CARBON_SEM_IR_INST_KIND(Name) \
CARBON_ENUM_CONSTANT_DEFINITION(InstKind, Name)
#include "toolchain/sem_ir/node_kind.def"
// We expect the node kind to fit compactly into 8 bits.
static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!");
// We expect the instruction kind to fit compactly into 8 bits.
static_assert(sizeof(InstKind) == 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 {
// A definition of an instruction kind. This is an InstKind 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
// instruction kind, specifically `TypedInst::Kind`. Use `InstKind` instead as a
// thin wrapper around an instruction kind index.
class InstKind::Definition : public InstKind {
public:
// Returns the name to use for this node kind in Semantics IR.
// Returns the name to use for this instruction 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().
// Returns whether this instruction kind is a code block terminator. See
// InstKind::terminator_kind().
[[nodiscard]] constexpr auto terminator_kind() const -> TerminatorKind {
return terminator_kind_;
}
private:
friend class NodeKind;
friend class InstKind;
constexpr Definition(NodeKind kind, llvm::StringLiteral ir_name,
constexpr Definition(InstKind kind, llvm::StringLiteral ir_name,
TerminatorKind terminator_kind)
: NodeKind(kind), ir_name_(ir_name), terminator_kind_(terminator_kind) {}
: InstKind(kind), ir_name_(ir_name), terminator_kind_(terminator_kind) {}
// Not copyable.
Definition(const Definition&) = delete;
@@ -117,7 +120,7 @@ class NodeKind::Definition : public NodeKind {
TerminatorKind terminator_kind_;
};
constexpr auto NodeKind::Define(llvm::StringLiteral ir_name,
constexpr auto InstKind::Define(llvm::StringLiteral ir_name,
TerminatorKind terminator_kind) const
-> Definition {
return Definition(*this, ir_name, terminator_kind);
@@ -2,55 +2,55 @@
// 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_
#ifndef CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_
#define CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_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.
// Representations for specific kinds of instructions.
//
// 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.
// - Optionally, a `Parse::Node parse_node;` member, for instructions with an
// associated location. Almost all instructions 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 instructions that produce a
// value. This includes instructions 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.
// specified above, when present. When converting to a `SemIR::Inst`, they will
// become the parse node and type associated with the type-erased instruction.
//
// 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.
// with a particular member of the `InstKind` enumeration. This `Kind`
// declaration also defines the instruction kind by calling `InstKind::Define`
// and specifying additional information about the instruction kind. This
// information is available through the member functions of the `InstKind` value
// declared in `inst_kind.h`, and includes the name used in textual IR and
// whether the instruction is a terminator instruction.
namespace Carbon::SemIR {
struct AddressOf {
static constexpr auto Kind = NodeKind::AddressOf.Define("address_of");
static constexpr auto Kind = InstKind::AddressOf.Define("address_of");
Parse::Node parse_node;
TypeId type_id;
NodeId lvalue_id;
InstId lvalue_id;
};
struct ArrayIndex {
static constexpr auto Kind = NodeKind::ArrayIndex.Define("array_index");
static constexpr auto Kind = InstKind::ArrayIndex.Define("array_index");
Parse::Node parse_node;
TypeId type_id;
NodeId array_id;
NodeId index_id;
InstId array_id;
InstId index_id;
};
// Initializes an array from a tuple. `tuple_id` is the source tuple
@@ -58,20 +58,20 @@ struct ArrayIndex {
// element, plus a final element that is the return slot for the
// initialization.
struct ArrayInit {
static constexpr auto Kind = NodeKind::ArrayInit.Define("array_init");
static constexpr auto Kind = InstKind::ArrayInit.Define("array_init");
Parse::Node parse_node;
TypeId type_id;
NodeId tuple_id;
NodeBlockId inits_and_return_slot_id;
InstId tuple_id;
InstBlockId inits_and_return_slot_id;
};
struct ArrayType {
static constexpr auto Kind = NodeKind::ArrayType.Define("array_type");
static constexpr auto Kind = InstKind::ArrayType.Define("array_type");
Parse::Node parse_node;
TypeId type_id;
NodeId bound_id;
InstId bound_id;
TypeId element_type_id;
};
@@ -79,50 +79,50 @@ struct ArrayType {
// `rhs_id`. This finishes initialization of `lhs_id` in the same way as
// `InitializeFrom`.
struct Assign {
static constexpr auto Kind = NodeKind::Assign.Define("assign");
static constexpr auto Kind = InstKind::Assign.Define("assign");
Parse::Node parse_node;
// Assignments are statements, and so have no type.
NodeId lhs_id;
NodeId rhs_id;
InstId lhs_id;
InstId rhs_id;
};
struct BinaryOperatorAdd {
static constexpr auto Kind = NodeKind::BinaryOperatorAdd.Define("add");
static constexpr auto Kind = InstKind::BinaryOperatorAdd.Define("add");
Parse::Node parse_node;
TypeId type_id;
NodeId lhs_id;
NodeId rhs_id;
InstId lhs_id;
InstId rhs_id;
};
struct BindName {
static constexpr auto Kind = NodeKind::BindName.Define("bind_name");
static constexpr auto Kind = InstKind::BindName.Define("bind_name");
Parse::Node parse_node;
TypeId type_id;
StringId name_id;
NodeId value_id;
InstId value_id;
};
struct BindValue {
static constexpr auto Kind = NodeKind::BindValue.Define("bind_value");
static constexpr auto Kind = InstKind::BindValue.Define("bind_value");
Parse::Node parse_node;
TypeId type_id;
NodeId value_id;
InstId value_id;
};
struct BlockArg {
static constexpr auto Kind = NodeKind::BlockArg.Define("block_arg");
static constexpr auto Kind = InstKind::BlockArg.Define("block_arg");
Parse::Node parse_node;
TypeId type_id;
NodeBlockId block_id;
InstBlockId block_id;
};
struct BoolLiteral {
static constexpr auto Kind = NodeKind::BoolLiteral.Define("bool_literal");
static constexpr auto Kind = InstKind::BoolLiteral.Define("bool_literal");
Parse::Node parse_node;
TypeId type_id;
@@ -132,48 +132,48 @@ struct BoolLiteral {
// A bound method, that combines a function with the value to use for its
// `self` parameter, such as `object.MethodName`.
struct BoundMethod {
static constexpr auto Kind = NodeKind::BoundMethod.Define("bound_method");
static constexpr auto Kind = InstKind::BoundMethod.Define("bound_method");
Parse::Node parse_node;
TypeId type_id;
// The object argument in the bound method, which will be used to initialize
// `self`, or whose address will be used to initialize `self` for an `addr
// self` parameter.
NodeId object_id;
NodeId function_id;
InstId object_id;
InstId function_id;
};
struct Branch {
static constexpr auto Kind =
NodeKind::Branch.Define("br", TerminatorKind::Terminator);
InstKind::Branch.Define("br", TerminatorKind::Terminator);
Parse::Node parse_node;
// Branches don't produce a value, so have no type.
NodeBlockId target_id;
InstBlockId target_id;
};
struct BranchIf {
static constexpr auto Kind =
NodeKind::BranchIf.Define("br", TerminatorKind::TerminatorSequence);
InstKind::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;
InstBlockId target_id;
InstId cond_id;
};
struct BranchWithArg {
static constexpr auto Kind =
NodeKind::BranchWithArg.Define("br", TerminatorKind::Terminator);
InstKind::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;
InstBlockId target_id;
InstId arg_id;
};
struct Builtin {
static constexpr auto Kind = NodeKind::Builtin.Define("builtin");
static constexpr auto Kind = InstKind::Builtin.Define("builtin");
// Builtins don't have a parse node associated with them.
TypeId type_id;
@@ -181,21 +181,21 @@ struct Builtin {
};
struct Call {
static constexpr auto Kind = NodeKind::Call.Define("call");
static constexpr auto Kind = InstKind::Call.Define("call");
Parse::Node parse_node;
TypeId type_id;
NodeId callee_id;
InstId callee_id;
// The arguments block contains IDs for the following arguments, in order:
// - The argument for each implicit parameter.
// - The argument for each explicit parameter.
// - The argument for the return slot, if present.
NodeBlockId args_id;
InstBlockId args_id;
};
struct ClassDeclaration {
static constexpr auto Kind =
NodeKind::ClassDeclaration.Define("class_declaration");
InstKind::ClassDeclaration.Define("class_declaration");
Parse::Node parse_node;
// No type: a class declaration is not itself a value. The name of a class
@@ -205,21 +205,21 @@ struct ClassDeclaration {
ClassId class_id;
// The declaration block, containing the class name's qualifiers and the
// class's generic parameters.
NodeBlockId decl_block_id;
InstBlockId decl_block_id;
};
struct ClassFieldAccess {
static constexpr auto Kind =
NodeKind::ClassFieldAccess.Define("class_field_access");
InstKind::ClassFieldAccess.Define("class_field_access");
Parse::Node parse_node;
TypeId type_id;
NodeId base_id;
InstId base_id;
MemberIndex index;
};
struct ClassType {
static constexpr auto Kind = NodeKind::ClassType.Define("class_type");
static constexpr auto Kind = InstKind::ClassType.Define("class_type");
Parse::Node parse_node;
TypeId type_id;
@@ -228,7 +228,7 @@ struct ClassType {
};
struct ConstType {
static constexpr auto Kind = NodeKind::ConstType.Define("const_type");
static constexpr auto Kind = InstKind::ConstType.Define("const_type");
Parse::Node parse_node;
TypeId type_id;
@@ -237,28 +237,28 @@ struct ConstType {
// A cross-reference between IRs.
struct CrossReference {
static constexpr auto Kind = NodeKind::CrossReference.Define("xref");
static constexpr auto Kind = InstKind::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.
// No parse node: an instruction's parse tree node must refer to a node in the
// current parse tree. This cannot use the cross-referenced instruction's
// parse tree node because it will be in a different parse tree.
TypeId type_id;
CrossReferenceIRId ir_id;
NodeId node_id;
InstId inst_id;
};
struct Dereference {
static constexpr auto Kind = NodeKind::Dereference.Define("dereference");
static constexpr auto Kind = InstKind::Dereference.Define("dereference");
Parse::Node parse_node;
TypeId type_id;
NodeId pointer_id;
InstId pointer_id;
};
// A field in a class, of the form `var field: field_type;`. The type of the
// `Field` node is an `UnboundFieldType`.
// `Field` instruction is an `UnboundFieldType`.
struct Field {
static constexpr auto Kind = NodeKind::Field.Define("field");
static constexpr auto Kind = InstKind::Field.Define("field");
Parse::Node parse_node;
TypeId type_id;
@@ -267,7 +267,7 @@ struct Field {
};
struct FunctionDeclaration {
static constexpr auto Kind = NodeKind::FunctionDeclaration.Define("fn_decl");
static constexpr auto Kind = InstKind::FunctionDeclaration.Define("fn_decl");
Parse::Node parse_node;
TypeId type_id;
@@ -279,16 +279,16 @@ struct FunctionDeclaration {
// whose initialization is not in-place.
struct InitializeFrom {
static constexpr auto Kind =
NodeKind::InitializeFrom.Define("initialize_from");
InstKind::InitializeFrom.Define("initialize_from");
Parse::Node parse_node;
TypeId type_id;
NodeId src_id;
NodeId dest_id;
InstId src_id;
InstId dest_id;
};
struct IntegerLiteral {
static constexpr auto Kind = NodeKind::IntegerLiteral.Define("int_literal");
static constexpr auto Kind = InstKind::IntegerLiteral.Define("int_literal");
Parse::Node parse_node;
TypeId type_id;
@@ -296,16 +296,16 @@ struct IntegerLiteral {
};
struct NameReference {
static constexpr auto Kind = NodeKind::NameReference.Define("name_reference");
static constexpr auto Kind = InstKind::NameReference.Define("name_reference");
Parse::Node parse_node;
TypeId type_id;
StringId name_id;
NodeId value_id;
InstId value_id;
};
struct Namespace {
static constexpr auto Kind = NodeKind::Namespace.Define("namespace");
static constexpr auto Kind = InstKind::Namespace.Define("namespace");
Parse::Node parse_node;
TypeId type_id;
@@ -313,14 +313,14 @@ struct Namespace {
};
struct NoOp {
static constexpr auto Kind = NodeKind::NoOp.Define("no_op");
static constexpr auto Kind = InstKind::NoOp.Define("no_op");
Parse::Node parse_node;
// This node doesn't produce a value, so has no type.
// This instruction doesn't produce a value, so has no type.
};
struct Parameter {
static constexpr auto Kind = NodeKind::Parameter.Define("parameter");
static constexpr auto Kind = InstKind::Parameter.Define("parameter");
Parse::Node parse_node;
TypeId type_id;
@@ -328,7 +328,7 @@ struct Parameter {
};
struct PointerType {
static constexpr auto Kind = NodeKind::PointerType.Define("ptr_type");
static constexpr auto Kind = InstKind::PointerType.Define("ptr_type");
Parse::Node parse_node;
TypeId type_id;
@@ -336,7 +336,7 @@ struct PointerType {
};
struct RealLiteral {
static constexpr auto Kind = NodeKind::RealLiteral.Define("real_literal");
static constexpr auto Kind = InstKind::RealLiteral.Define("real_literal");
Parse::Node parse_node;
TypeId type_id;
@@ -345,7 +345,7 @@ struct RealLiteral {
struct Return {
static constexpr auto Kind =
NodeKind::Return.Define("return", TerminatorKind::Terminator);
InstKind::Return.Define("return", TerminatorKind::Terminator);
Parse::Node parse_node;
// This is a statement, so has no type.
@@ -353,15 +353,15 @@ struct Return {
struct ReturnExpression {
static constexpr auto Kind =
NodeKind::ReturnExpression.Define("return", TerminatorKind::Terminator);
InstKind::ReturnExpression.Define("return", TerminatorKind::Terminator);
Parse::Node parse_node;
// This is a statement, so has no type.
NodeId expr_id;
InstId expr_id;
};
struct SelfParameter {
static constexpr auto Kind = NodeKind::SelfParameter.Define("self_parameter");
static constexpr auto Kind = InstKind::SelfParameter.Define("self_parameter");
static constexpr llvm::StringLiteral Name = "self";
Parse::Node parse_node;
@@ -370,16 +370,16 @@ struct SelfParameter {
};
struct SpliceBlock {
static constexpr auto Kind = NodeKind::SpliceBlock.Define("splice_block");
static constexpr auto Kind = InstKind::SpliceBlock.Define("splice_block");
Parse::Node parse_node;
TypeId type_id;
NodeBlockId block_id;
NodeId result_id;
InstBlockId block_id;
InstId result_id;
};
struct StringLiteral {
static constexpr auto Kind = NodeKind::StringLiteral.Define("string_literal");
static constexpr auto Kind = InstKind::StringLiteral.Define("string_literal");
Parse::Node parse_node;
TypeId type_id;
@@ -387,113 +387,114 @@ struct StringLiteral {
};
struct StructAccess {
static constexpr auto Kind = NodeKind::StructAccess.Define("struct_access");
static constexpr auto Kind = InstKind::StructAccess.Define("struct_access");
Parse::Node parse_node;
TypeId type_id;
NodeId struct_id;
InstId struct_id;
MemberIndex index;
};
struct StructInit {
static constexpr auto Kind = NodeKind::StructInit.Define("struct_init");
static constexpr auto Kind = InstKind::StructInit.Define("struct_init");
Parse::Node parse_node;
TypeId type_id;
NodeId src_id;
NodeBlockId elements_id;
InstId src_id;
InstBlockId elements_id;
};
struct StructLiteral {
static constexpr auto Kind = NodeKind::StructLiteral.Define("struct_literal");
static constexpr auto Kind = InstKind::StructLiteral.Define("struct_literal");
Parse::Node parse_node;
TypeId type_id;
NodeBlockId elements_id;
InstBlockId elements_id;
};
struct StructType {
static constexpr auto Kind = NodeKind::StructType.Define("struct_type");
static constexpr auto Kind = InstKind::StructType.Define("struct_type");
Parse::Node parse_node;
TypeId type_id;
NodeBlockId fields_id;
InstBlockId fields_id;
};
struct StructTypeField {
static constexpr auto Kind =
NodeKind::StructTypeField.Define("struct_type_field");
InstKind::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.
// This instruction 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");
static constexpr auto Kind = InstKind::StructValue.Define("struct_value");
Parse::Node parse_node;
TypeId type_id;
NodeId src_id;
NodeBlockId elements_id;
InstId src_id;
InstBlockId elements_id;
};
struct Temporary {
static constexpr auto Kind = NodeKind::Temporary.Define("temporary");
static constexpr auto Kind = InstKind::Temporary.Define("temporary");
Parse::Node parse_node;
TypeId type_id;
NodeId storage_id;
NodeId init_id;
InstId storage_id;
InstId init_id;
};
struct TemporaryStorage {
static constexpr auto Kind =
NodeKind::TemporaryStorage.Define("temporary_storage");
InstKind::TemporaryStorage.Define("temporary_storage");
Parse::Node parse_node;
TypeId type_id;
};
struct TupleAccess {
static constexpr auto Kind = NodeKind::TupleAccess.Define("tuple_access");
static constexpr auto Kind = InstKind::TupleAccess.Define("tuple_access");
Parse::Node parse_node;
TypeId type_id;
NodeId tuple_id;
InstId tuple_id;
MemberIndex index;
};
struct TupleIndex {
static constexpr auto Kind = NodeKind::TupleIndex.Define("tuple_index");
static constexpr auto Kind = InstKind::TupleIndex.Define("tuple_index");
Parse::Node parse_node;
TypeId type_id;
NodeId tuple_id;
NodeId index_id;
InstId tuple_id;
InstId index_id;
};
struct TupleInit {
static constexpr auto Kind = NodeKind::TupleInit.Define("tuple_init");
static constexpr auto Kind = InstKind::TupleInit.Define("tuple_init");
Parse::Node parse_node;
TypeId type_id;
NodeId src_id;
NodeBlockId elements_id;
InstId src_id;
InstBlockId elements_id;
};
struct TupleLiteral {
static constexpr auto Kind = NodeKind::TupleLiteral.Define("tuple_literal");
static constexpr auto Kind = InstKind::TupleLiteral.Define("tuple_literal");
Parse::Node parse_node;
TypeId type_id;
NodeBlockId elements_id;
InstBlockId elements_id;
};
struct TupleType {
static constexpr auto Kind = NodeKind::TupleType.Define("tuple_type");
static constexpr auto Kind = InstKind::TupleType.Define("tuple_type");
Parse::Node parse_node;
TypeId type_id;
@@ -501,20 +502,20 @@ struct TupleType {
};
struct TupleValue {
static constexpr auto Kind = NodeKind::TupleValue.Define("tuple_value");
static constexpr auto Kind = InstKind::TupleValue.Define("tuple_value");
Parse::Node parse_node;
TypeId type_id;
NodeId src_id;
NodeBlockId elements_id;
InstId src_id;
InstBlockId elements_id;
};
struct UnaryOperatorNot {
static constexpr auto Kind = NodeKind::UnaryOperatorNot.Define("not");
static constexpr auto Kind = InstKind::UnaryOperatorNot.Define("not");
Parse::Node parse_node;
TypeId type_id;
NodeId operand_id;
InstId operand_id;
};
// The type of an expression naming an unbound field, such as `Class.field`.
@@ -522,7 +523,7 @@ struct UnaryOperatorNot {
// such as `instance.(Class.field)`.
struct UnboundFieldType {
static constexpr auto Kind =
NodeKind::UnboundFieldType.Define("unbound_field_type");
InstKind::UnboundFieldType.Define("unbound_field_type");
Parse::Node parse_node;
TypeId type_id;
@@ -534,24 +535,24 @@ struct UnboundFieldType {
struct ValueAsReference {
static constexpr auto Kind =
NodeKind::ValueAsReference.Define("value_as_reference");
InstKind::ValueAsReference.Define("value_as_reference");
Parse::Node parse_node;
TypeId type_id;
NodeId value_id;
InstId value_id;
};
struct ValueOfInitializer {
static constexpr auto Kind =
NodeKind::ValueOfInitializer.Define("value_of_initializer");
InstKind::ValueOfInitializer.Define("value_of_initializer");
Parse::Node parse_node;
TypeId type_id;
NodeId init_id;
InstId init_id;
};
struct VarStorage {
static constexpr auto Kind = NodeKind::VarStorage.Define("var");
static constexpr auto Kind = InstKind::VarStorage.Define("var");
Parse::Node parse_node;
TypeId type_id;
@@ -572,4 +573,4 @@ constexpr bool HasTypeId<T, decltype(&T::type_id)> = true;
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_TYPED_NODES_H_
#endif // CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_
+71 -70
View File
@@ -2,21 +2,20 @@
// 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"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::SemIR {
// A friend of `SemIR::Node` that is used to pierce the abstraction.
class NodeTestHelper {
// A friend of `SemIR::Inst` that is used to pierce the abstraction.
class InstTestHelper {
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);
static auto MakeInst(InstKind inst_kind, Parse::Node parse_node,
TypeId type_id, int32_t arg0, int32_t arg1) -> Inst {
return Inst(inst_kind, parse_node, type_id, arg0, arg1);
}
};
@@ -25,120 +24,122 @@ class NodeTestHelper {
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);
// Check that each instruction kind defines a Kind member using the correct
// InstKind enumerator.
#define CARBON_SEM_IR_INST_KIND(Name) \
static_assert(Name::Kind == InstKind::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
// Form a list of all typed instruction types. Use `TypesExceptFirst` and a
// leading `void` to handle the problem that we only want N-1 commas in this
// list.
using TypedInstTypes = TypesExceptFirst<void
#define CARBON_SEM_IR_INST_KIND(Name) , Name
#include "toolchain/sem_ir/node_kind.def"
>;
// Set up the test fixture.
template <typename TypedNode>
class TypedNodeTest : public testing::Test {};
template <typename TypedInst>
class TypedInstTest : public testing::Test {};
TYPED_TEST_SUITE(TypedNodeTest, TypedNodeTypes);
TYPED_TEST_SUITE(TypedInstTest, TypedInstTypes);
TYPED_TEST(TypedNodeTest, CommonFieldOrder) {
using TypedNode = TypeParam;
TYPED_TEST(TypedInstTest, CommonFieldOrder) {
using TypedInst = TypeParam;
Node node = NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1),
Inst inst = InstTestHelper::MakeInst(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));
EXPECT_EQ(inst.kind(), TypeParam::Kind);
EXPECT_EQ(inst.parse_node(), Parse::Node(1));
EXPECT_EQ(inst.type_id(), TypeId(2));
TypedNode typed = node.As<TypedNode>();
if constexpr (HasParseNode<TypedNode>) {
TypedInst typed = inst.As<TypedInst>();
if constexpr (HasParseNode<TypedInst>) {
EXPECT_EQ(typed.parse_node, Parse::Node(1));
}
if constexpr (HasTypeId<TypedNode>) {
if constexpr (HasTypeId<TypedInst>) {
EXPECT_EQ(typed.type_id, TypeId(2));
}
}
TYPED_TEST(TypedNodeTest, RoundTrip) {
using TypedNode = TypeParam;
TYPED_TEST(TypedInstTest, RoundTrip) {
using TypedInst = TypeParam;
Node node1 = NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1),
Inst inst1 = InstTestHelper::MakeInst(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));
EXPECT_EQ(inst1.kind(), TypeParam::Kind);
EXPECT_EQ(inst1.parse_node(), Parse::Node(1));
EXPECT_EQ(inst1.type_id(), TypeId(2));
TypedNode typed1 = node1.As<TypedNode>();
Node node2 = typed1;
TypedInst typed1 = inst1.As<TypedInst>();
Inst inst2 = typed1;
EXPECT_EQ(node1.kind(), node2.kind());
if constexpr (HasParseNode<TypedNode>) {
EXPECT_EQ(node1.parse_node(), node2.parse_node());
EXPECT_EQ(inst1.kind(), inst2.kind());
if constexpr (HasParseNode<TypedInst>) {
EXPECT_EQ(inst1.parse_node(), inst2.parse_node());
}
if constexpr (HasTypeId<TypedNode>) {
EXPECT_EQ(node1.type_id(), node2.type_id());
if constexpr (HasTypeId<TypedInst>) {
EXPECT_EQ(inst1.type_id(), inst2.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);
// If the typed instruction has no padding, we should get exactly the same
// thing if we convert back from an instruction.
TypedInst typed2 = inst2.As<TypedInst>();
if constexpr (std::has_unique_object_representations_v<TypedInst>) {
EXPECT_EQ(std::memcmp(&typed1, &typed2, sizeof(TypedInst)), 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);
// The original instruction might not be identical after one round trip,
// because the fields not carried by the typed instruction are lost. But they
// should be stable if we round-trip again.
Inst inst3 = typed2;
if constexpr (std::has_unique_object_representations_v<Inst>) {
EXPECT_EQ(std::memcmp(&inst2, &inst3, sizeof(Inst)), 0);
}
}
TYPED_TEST(TypedNodeTest, StructLayout) {
using TypedNode = TypeParam;
TYPED_TEST(TypedInstTest, StructLayout) {
using TypedInst = TypeParam;
TypedNode typed =
NodeTestHelper::MakeNode(TypeParam::Kind, Parse::Node(1), TypeId(2), 3, 4)
.template As<TypedNode>();
TypedInst typed =
InstTestHelper::MakeInst(TypeParam::Kind, Parse::Node(1), TypeId(2), 3, 4)
.template As<TypedInst>();
// Check that the memory representation of the typed node is what we expect.
// Check that the memory representation of the typed instruction 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>) {
if constexpr (HasParseNode<TypedInst>) {
fields[field++] = 1;
}
if constexpr (HasTypeId<TypedNode>) {
if constexpr (HasTypeId<TypedInst>) {
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);
ASSERT_LE(sizeof(TypedInst), sizeof(fields));
// We can only do this check if the typed instruction has no padding.
if constexpr (std::has_unique_object_representations_v<TypedInst>) {
EXPECT_EQ(std::memcmp(&fields, &typed, sizeof(TypedInst)), 0);
}
}
TYPED_TEST(TypedNodeTest, NodeKindMatches) {
using TypedNode = TypeParam;
TYPED_TEST(TypedInstTest, InstKindMatches) {
using TypedInst = TypeParam;
// TypedNode::Kind is a NodeKind::Definition that extends NodeKind, but
// TypedInst::Kind is an InstKind::Definition that extends InstKind, but
// has different definitions of the `ir_name()` and `terminator_kind()`
// methods. Here we test that values returned by the two different versions
// of those functions match.
NodeKind as_kind = TypedNode::Kind;
EXPECT_EQ(TypedNode::Kind.ir_name(), as_kind.ir_name());
EXPECT_EQ(TypedNode::Kind.terminator_kind(), as_kind.terminator_kind());
InstKind as_kind = TypedInst::Kind;
EXPECT_EQ(TypedInst::Kind.ir_name(), as_kind.ir_name());
EXPECT_EQ(TypedInst::Kind.terminator_kind(), as_kind.terminator_kind());
}
} // namespace
+33 -31
View File
@@ -8,39 +8,41 @@
#include "llvm/ADT/DenseMap.h"
#include "toolchain/base/value_store.h"
#include "toolchain/base/yaml.h"
#include "toolchain/sem_ir/node.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::SemIR {
// Provides a ValueStore wrapper for an API specific to nodes.
class NodeStore {
// Provides a ValueStore wrapper for an API specific to instructions.
class InstStore {
public:
// Adds a node to the node list, returning an ID to reference the node. Note
// that this doesn't add the node to any node block. Check::Context::AddNode
// or NodeBlockStack::AddNode should usually be used instead, to add the node
// to the current block.
auto AddInNoBlock(Node node) -> NodeId { return values_.Add(node); }
// Adds an instruction to the instruction list, returning an ID to reference
// the instruction. Note that this doesn't add the instruction to any
// instruction block. Check::Context::AddInst or InstBlockStack::AddInst
// should usually be used instead, to add the instruction to the current
// block.
auto AddInNoBlock(Inst inst) -> InstId { return values_.Add(inst); }
// Returns the requested node.
auto Get(NodeId node_id) const -> Node { return values_.Get(node_id); }
// Returns the requested instruction.
auto Get(InstId inst_id) const -> Inst { return values_.Get(inst_id); }
// Returns the requested node, which is known to have the specified type.
template <typename NodeT>
auto GetAs(NodeId node_id) const -> NodeT {
return Get(node_id).As<NodeT>();
// Returns the requested instruction, which is known to have the specified
// type.
template <typename InstT>
auto GetAs(InstId inst_id) const -> InstT {
return Get(inst_id).As<InstT>();
}
// Overwrites a given node with a new value.
auto Set(NodeId node_id, Node node) -> void { values_.Get(node_id) = node; }
// Overwrites a given instruction with a new value.
auto Set(InstId inst_id, Inst inst) -> void { values_.Get(inst_id) = inst; }
// Reserves space.
auto Reserve(size_t size) -> void { values_.Reserve(size); }
auto array_ref() const -> llvm::ArrayRef<Node> { return values_.array_ref(); }
auto array_ref() const -> llvm::ArrayRef<Inst> { return values_.array_ref(); }
auto size() const -> int { return values_.size(); }
private:
ValueStore<NodeId, Node> values_;
ValueStore<InstId, Inst> values_;
};
// Provides a ValueStore wrapper for an API specific to name scopes.
@@ -51,19 +53,19 @@ class NameScopeStore {
// Adds an entry to a name scope. Returns true on success, false on
// duplicates.
auto AddEntry(NameScopeId scope_id, StringId name_id, NodeId target_id)
auto AddEntry(NameScopeId scope_id, StringId name_id, InstId target_id)
-> bool {
return values_.Get(scope_id).insert({name_id, target_id}).second;
}
// Returns the requested name scope.
auto Get(NameScopeId scope_id) const
-> const llvm::DenseMap<StringId, NodeId>& {
-> const llvm::DenseMap<StringId, InstId>& {
return values_.Get(scope_id);
}
private:
ValueStore<NameScopeId, llvm::DenseMap<StringId, NodeId>> values_;
ValueStore<NameScopeId, llvm::DenseMap<StringId, InstId>> values_;
};
// Provides a block-based ValueStore, which uses slab allocation of added
@@ -109,17 +111,17 @@ class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT, ValueT>> {
protected:
// Reserves and returns a block ID. The contents of the block
// should be specified by calling Set, or similar.
auto AddDefaultValue() -> NodeBlockId { return values_.AddDefaultValue(); }
auto AddDefaultValue() -> InstBlockId { return values_.AddDefaultValue(); }
// Adds an uninitialized block of the given size.
auto AddUninitialized(size_t size) -> NodeBlockId {
auto AddUninitialized(size_t size) -> InstBlockId {
return values_.Add(AllocateUninitialized(size));
}
// Sets the contents of an empty block to the given content.
auto Set(NodeBlockId block_id, llvm::ArrayRef<NodeId> content) -> void {
auto Set(InstBlockId block_id, llvm::ArrayRef<InstId> content) -> void {
CARBON_CHECK(Get(block_id).empty())
<< "node block content set more than once";
<< "inst block content set more than once";
values_.Get(block_id) = AllocateCopy(content);
}
@@ -147,18 +149,18 @@ class BlockValueStore : public Yaml::Printable<BlockValueStore<IdT, ValueT>> {
ValueStore<IdT, llvm::MutableArrayRef<ValueT>> values_;
};
// Adapts BlockValueStore for node blocks.
class NodeBlockStore : public BlockValueStore<NodeBlockId, NodeId> {
// Adapts BlockValueStore for instruction blocks.
class InstBlockStore : public BlockValueStore<InstBlockId, InstId> {
public:
using BaseType = BlockValueStore<NodeBlockId, NodeId>;
using BaseType = BlockValueStore<InstBlockId, InstId>;
using BaseType::AddDefaultValue;
using BaseType::AddUninitialized;
using BaseType::BaseType;
auto Set(NodeBlockId block_id, llvm::ArrayRef<NodeId> content) -> void {
CARBON_CHECK(block_id != NodeBlockId::Unreachable);
BlockValueStore<NodeBlockId, NodeId>::Set(block_id, content);
auto Set(InstBlockId block_id, llvm::ArrayRef<InstId> content) -> void {
CARBON_CHECK(block_id != InstBlockId::Unreachable);
BlockValueStore<InstBlockId, InstId>::Set(block_id, content);
}
};
+15 -15
View File
@@ -38,14 +38,14 @@ TEST(SemIRTest, YAML) {
d.RunCommand(
{"compile", "--phase=check", "--dump-raw-sem-ir", "test.carbon"});
// Matches the ID of a node. The numbers may change because of builtin
// Matches the ID of an instruction. The numbers may change because of builtin
// cross-references, so this code is only doing loose structural checks.
auto integer_id = Yaml::Scalar(MatchesRegex(R"(int\d+)"));
auto node_id = Yaml::Scalar(MatchesRegex(R"(node\+\d+)"));
auto node_builtin = Yaml::Scalar(MatchesRegex(R"(node\w+)"));
auto inst_id = Yaml::Scalar(MatchesRegex(R"(inst\+\d+)"));
auto inst_builtin = Yaml::Scalar(MatchesRegex(R"(inst\w+)"));
auto type_id = Yaml::Scalar(MatchesRegex(R"(type\d+)"));
auto type_builtin = Pair(
type_id, Yaml::Mapping(ElementsAre(Pair("node", node_builtin),
type_id, Yaml::Mapping(ElementsAre(Pair("inst", inst_builtin),
Pair("value_rep", Yaml::Mapping(_)))));
auto file = Yaml::Mapping(ElementsAre(
@@ -54,30 +54,30 @@ TEST(SemIRTest, YAML) {
Pair("classes", Yaml::Mapping(SizeIs(0))),
Pair("types", Yaml::Mapping(Each(type_builtin))),
Pair("type_blocks", Yaml::Mapping(IsEmpty())),
Pair("nodes",
Pair("insts",
Yaml::Mapping(AllOf(
Each(Key(node_id)),
Each(Key(inst_id)),
// kind is required, other parts are optional.
Each(Pair(_, Yaml::Mapping(Contains(Pair("kind", _))))),
// A 0-arg node.
// A 0-arg instruction.
Contains(
Pair(_, Yaml::Mapping(ElementsAre(Pair("kind", "Return"))))),
// A 1-arg node.
// A 1-arg instruction.
Contains(Pair(
_, Yaml::Mapping(ElementsAre(Pair("kind", "IntegerLiteral"),
Pair("arg0", integer_id),
Pair("type", type_id))))),
// A 2-arg node.
// A 2-arg instruction.
Contains(Pair(
_, Yaml::Mapping(ElementsAre(Pair("kind", "Assign"),
Pair("arg0", node_id),
Pair("arg1", node_id)))))))),
// This production has only two node blocks.
Pair("node_blocks",
Pair("arg0", inst_id),
Pair("arg1", inst_id)))))))),
// This production has only two instruction blocks.
Pair("inst_blocks",
Yaml::Mapping(ElementsAre(
Pair("block0", Yaml::Mapping(IsEmpty())),
Pair("block1", Yaml::Mapping(Each(Pair(_, node_id)))),
Pair("block2", Yaml::Mapping(Each(Pair(_, node_id)))))))));
Pair("block1", Yaml::Mapping(Each(Pair(_, inst_id)))),
Pair("block2", Yaml::Mapping(Each(Pair(_, inst_id)))))))));
auto root = Yaml::Sequence(ElementsAre(Yaml::Mapping(
ElementsAre(Pair("filename", "test.carbon"), Pair("sem_ir", file)))));