mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:24:42 +01:00
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:
co-authored by
Chandler Carruth
parent
35c2142392
commit
7edfd8e02a
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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}`.",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_)) {
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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_
|
||||
@@ -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()];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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)))));
|
||||
|
||||
Reference in New Issue
Block a user