Files
carbon-lang/toolchain/check/pending_block.h
T
Richard SmithandJon Ross-Perkins 891c7d8368 Enforce that the parse node for an instruction has the kind specified in the instruction definition (#4264)
Remove `ReusingLoc` and add enforcement that even for imported
locations, the kind of the parse node for an instruction matches the
kind specified in the instruction definition.

Change the node kind for a few instructions to `NodeId`:

- A couple of instructions had a typed node but could be created
implicitly with any node as part of a builtin implicit conversion. This
happened for `AddrOf`, `ArrayIndex`, and `Deref`.
- A bunch of instructions had `InvalidNodeId` as their associated parse
node kind but were actually always created with a location.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2024-08-29 21:10:14 +00:00

102 lines
3.5 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_
#define CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_
#include "llvm/ADT/SmallVector.h"
#include "toolchain/check/context.h"
#include "toolchain/sem_ir/inst.h"
namespace Carbon::Check {
// A block of code that contains pending instructions that might be needed but
// that haven't been inserted yet.
class PendingBlock {
public:
explicit PendingBlock(Context& context) : context_(context) {}
PendingBlock(const PendingBlock&) = delete;
auto operator=(const PendingBlock&) -> PendingBlock& = delete;
// 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.
explicit DiscardUnusedInstsScope(PendingBlock* block)
: block_(block), size_(block ? block->insts_.size() : 0) {}
~DiscardUnusedInstsScope() {
if (block_ && block_->insts_.size() > size_) {
block_->insts_.truncate(size_);
}
}
private:
PendingBlock* block_;
size_t size_;
};
template <typename InstT, typename LocT>
auto AddInst(LocT loc_id, InstT inst) -> SemIR::InstId {
auto inst_id = context_.AddInstInNoBlock(loc_id, 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 : insts_) {
context_.inst_block_stack().AddInstId(id);
}
insts_.clear();
}
// 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().GetWithLocId(value_id);
// There are three cases here:
if (insts_.empty()) {
// 1) The block is empty. Replace `target_id` with an empty splice
// pointing at `value_id`.
context_.ReplaceLocIdAndInstBeforeConstantUse(
target_id,
SemIR::LocIdAndInst(
value.loc_id,
SemIR::SpliceBlock{.type_id = value.inst.type_id(),
.block_id = SemIR::InstBlockId::Empty,
.result_id = 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_.ReplaceLocIdAndInstBeforeConstantUse(target_id, value);
} else {
// 3) Anything else: splice it into the IR, replacing `target_id`.
context_.ReplaceLocIdAndInstBeforeConstantUse(
target_id,
SemIR::LocIdAndInst(
value.loc_id,
SemIR::SpliceBlock{.type_id = value.inst.type_id(),
.block_id = context_.inst_blocks().Add(insts_),
.result_id = value_id}));
}
// Prepare to stash more pending instructions.
insts_.clear();
}
private:
Context& context_;
llvm::SmallVector<SemIR::InstId> insts_;
};
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_PENDING_BLOCK_H_