mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
High level, replacing `Id::Invalid` with `Id::None` and `Id::is_valid` with `Id::has_value` for clarity, as discussed [here](https://discord.com/channels/655572317891461132/655578254970716160/1331664574545395794). The `IntId` refactoring is needed together with `AnyIdBase` because it's also used with `ValueStore`. Note, trying to be careful not to rewrite `EnumBase::InvalidIndex`, or `is_valid` in general (e.g., `IdKind::is_valid`). I've tried to sequence commits here: 1. Automatic replacements: - `((?:Id|Index)(?: |::|\(|Base(?:\(|::)))Invalid((?:Index)?\W)` -> `$1None$2` - `<invalid>` -> `<none>` - `InvalidNodeId` -> `NoneNodeId` - `/\*invalid\*/` -> `/*none*/` - `id((?:_|\(\))(?:\.|->))is_valid` -> `id$1has_value` 2. Manual edits: - In `int.h` and `int_test.cpp` - `IntT` has `is_value`, which I'm renaming to `is_embedded_value`. - Manual edits to comments in this file. - `AnyIdBase` and `IdBase` - Declaration of `is_valid` -> `has_value`, `InvalidIndex` -> `NoneIndex`. - In `ids.h` and `ids.cpp` - `is_valid` -> `has_value` - `// An explicitly invalid ID.` -> `// An ID with no value.`; similar for index - Various math on `InvalidIndex` -> `NoneIndex` - Various mentions of "valid" in comments - In `value_store.h`, for `IdT::Invalid`, plus one comment - In `impl.h` and `tokenized_buffer.h`, we had different initialization of `::None` values (versus `ids.h` syntax) that I fixed manually. - Spot checks to compile - Particularly where `is_valid` replacements didn't catch spots due to different naming. 3. Autoupdate tests 4. verbose.carbon (NOAUTOUPDATE) 5. Comment spot checks Note there are probably other mentions of "Invalid" that should be swept up, but I'd like to argue for merging and separating out remaining cleanup since this is so sweeping (and likely to hit merge conflicts from churn). We'll probably have lingering mentions of "invalid" for a bit regardless, just because there are uses of "invalid" in non-Id APIs.
175 lines
6.3 KiB
C++
175 lines
6.3 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
|
|
|
|
#include "toolchain/check/node_id_traversal.h"
|
|
|
|
#include "toolchain/check/handle.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
NodeIdTraversal::NodeIdTraversal(Context& context,
|
|
llvm::raw_ostream* vlog_stream)
|
|
: context_(context),
|
|
next_deferred_definition_(&context.parse_tree()),
|
|
worklist_(vlog_stream) {
|
|
auto range = context.parse_tree().postorder();
|
|
chunks_.push_back({.it = range.begin(),
|
|
.end = range.end(),
|
|
.next_definition = Parse::DeferredDefinitionIndex::None});
|
|
}
|
|
|
|
auto NodeIdTraversal::Next() -> std::optional<Parse::NodeId> {
|
|
while (true) {
|
|
// If we're checking deferred definitions, find the next definition we
|
|
// should check, restore its suspended state, and add a corresponding
|
|
// `Chunk` to the top of the chunk list.
|
|
if (chunks_.back().checking_deferred_definitions) {
|
|
std::visit(
|
|
[&](auto&& task) { PerformTask(std::forward<decltype(task)>(task)); },
|
|
worklist_.Pop());
|
|
continue;
|
|
}
|
|
|
|
// If we're not checking deferred definitions, produce the next parse node
|
|
// for this chunk. If we've run out of parse nodes, we're done with this
|
|
// chunk of the parse tree.
|
|
if (chunks_.back().it == chunks_.back().end) {
|
|
auto old_chunk = chunks_.pop_back_val();
|
|
|
|
// If we're out of chunks, then we're done entirely.
|
|
if (chunks_.empty()) {
|
|
worklist_.VerifyEmpty();
|
|
return std::nullopt;
|
|
}
|
|
|
|
next_deferred_definition_.SkipTo(old_chunk.next_definition);
|
|
continue;
|
|
}
|
|
|
|
auto node_id = *chunks_.back().it;
|
|
|
|
// If we've reached the start of a deferred definition, skip to the end of
|
|
// it, and track that we need to check it later.
|
|
if (node_id == next_deferred_definition_.start_id()) {
|
|
const auto& definition_info =
|
|
context_.parse_tree().deferred_definitions().Get(
|
|
next_deferred_definition_.index());
|
|
worklist_.SuspendFunctionAndPush(context_,
|
|
next_deferred_definition_.index(),
|
|
definition_info.start_id);
|
|
|
|
// Continue type-checking the parse tree after the end of the definition.
|
|
chunks_.back().it =
|
|
Parse::Tree::PostorderIterator(definition_info.definition_id) + 1;
|
|
next_deferred_definition_.SkipTo(definition_info.next_definition_index);
|
|
continue;
|
|
}
|
|
|
|
++chunks_.back().it;
|
|
return node_id;
|
|
}
|
|
}
|
|
|
|
// Determines whether this node kind is the start of a deferred definition
|
|
// scope.
|
|
static auto IsStartOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
|
|
switch (kind) {
|
|
case Parse::NodeKind::ClassDefinitionStart:
|
|
case Parse::NodeKind::ImplDefinitionStart:
|
|
case Parse::NodeKind::InterfaceDefinitionStart:
|
|
case Parse::NodeKind::NamedConstraintDefinitionStart:
|
|
// TODO: Mixins.
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Determines whether this node kind is the end of a deferred definition scope.
|
|
static auto IsEndOfDeferredDefinitionScope(Parse::NodeKind kind) -> bool {
|
|
switch (kind) {
|
|
case Parse::NodeKind::ClassDefinition:
|
|
case Parse::NodeKind::ImplDefinition:
|
|
case Parse::NodeKind::InterfaceDefinition:
|
|
case Parse::NodeKind::NamedConstraintDefinition:
|
|
// TODO: Mixins.
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// TODO: Investigate factoring out `IsStartOfDeferredDefinitionScope` and
|
|
// `IsEndOfDeferredDefinitionScope` in order to make `NodeIdTraversal`
|
|
// reusable.
|
|
auto NodeIdTraversal::Handle(Parse::NodeKind parse_kind) -> void {
|
|
// When we reach the start of a deferred definition scope, add a task to the
|
|
// worklist to check future skipped definitions in the new context.
|
|
if (IsStartOfDeferredDefinitionScope(parse_kind)) {
|
|
worklist_.PushEnterDeferredDefinitionScope(context_);
|
|
}
|
|
|
|
// When we reach the end of a deferred definition scope, add a task to the
|
|
// worklist to leave the scope. If this is not a nested scope, start
|
|
// checking the deferred definitions now.
|
|
if (IsEndOfDeferredDefinitionScope(parse_kind)) {
|
|
chunks_.back().checking_deferred_definitions =
|
|
worklist_.SuspendFinishedScopeAndPush(context_);
|
|
}
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::EnterDeferredDefinitionScope&& enter) -> void {
|
|
CARBON_CHECK(enter.suspended_name,
|
|
"Entering a scope with no suspension information.");
|
|
context_.decl_name_stack().Restore(std::move(*enter.suspended_name));
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::LeaveDeferredDefinitionScope&& leave) -> void {
|
|
if (!leave.in_deferred_definition_scope) {
|
|
// We're done with checking deferred definitions.
|
|
chunks_.back().checking_deferred_definitions = false;
|
|
}
|
|
context_.decl_name_stack().PopScope();
|
|
}
|
|
|
|
auto NodeIdTraversal::PerformTask(
|
|
DeferredDefinitionWorklist::CheckSkippedDefinition&& parse_definition)
|
|
-> void {
|
|
auto& [definition_index, suspended_fn] = parse_definition;
|
|
const auto& definition_info =
|
|
context_.parse_tree().deferred_definitions().Get(definition_index);
|
|
HandleFunctionDefinitionResume(context_, definition_info.start_id,
|
|
std::move(suspended_fn));
|
|
auto range = Parse::Tree::PostorderIterator::MakeRange(
|
|
definition_info.start_id, definition_info.definition_id);
|
|
chunks_.push_back({.it = range.begin() + 1,
|
|
.end = range.end(),
|
|
.next_definition = next_deferred_definition_.index()});
|
|
++definition_index.index;
|
|
next_deferred_definition_.SkipTo(definition_index);
|
|
}
|
|
|
|
NodeIdTraversal::NextDeferredDefinitionCache::NextDeferredDefinitionCache(
|
|
const Parse::Tree* tree)
|
|
: tree_(tree) {
|
|
SkipTo(Parse::DeferredDefinitionIndex(0));
|
|
}
|
|
|
|
// Set the specified deferred definition index as being the next one that
|
|
// will be encountered.
|
|
auto NodeIdTraversal::NextDeferredDefinitionCache::SkipTo(
|
|
Parse::DeferredDefinitionIndex next_index) -> void {
|
|
index_ = next_index;
|
|
if (static_cast<size_t>(index_.index) ==
|
|
tree_->deferred_definitions().size()) {
|
|
start_id_ = Parse::NodeId::None;
|
|
} else {
|
|
start_id_ = tree_->deferred_definitions().Get(index_).start_id;
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Check
|