Refactor SemIRDiagnosticConverter out of check.cpp (#4039)

This is mostly just moving the code out. It also changes node_converters
from a DenseMap to a SmallVector, taking advantage of CheckIRId.
This commit is contained in:
Jon Ross-Perkins
2024-06-07 18:19:51 +00:00
committed by GitHub
parent b8373334c6
commit 3ade5bd8f3
4 changed files with 216 additions and 163 deletions
+48 -34
View File
@@ -13,40 +13,6 @@ filegroup(
data = glob(["testdata/**/*.carbon"]),
)
cc_library(
name = "node_stack",
srcs = ["node_stack.cpp"],
hdrs = ["node_stack.h"],
deps = [
"//common:check",
"//common:ostream",
"//common:vlog",
"//toolchain/parse:node_kind",
"//toolchain/parse:tree",
"//toolchain/sem_ir:ids",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "scope_stack",
srcs = ["scope_stack.cpp"],
hdrs = [
"lexical_lookup.h",
"scope_index.h",
"scope_stack.h",
],
deps = [
"//common:check",
"//common:ostream",
"//common:vlog",
"//toolchain/base:index_base",
"//toolchain/sem_ir:file",
"//toolchain/sem_ir:ids",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "context",
srcs = [
@@ -124,6 +90,7 @@ cc_library(
":member_access",
":operator",
":pointer_dereference",
":sem_ir_diagnostic_converter",
"//common:check",
"//common:error",
"//common:ostream",
@@ -238,6 +205,21 @@ cc_library(
],
)
cc_library(
name = "node_stack",
srcs = ["node_stack.cpp"],
hdrs = ["node_stack.h"],
deps = [
"//common:check",
"//common:ostream",
"//common:vlog",
"//toolchain/parse:node_kind",
"//toolchain/parse:tree",
"//toolchain/sem_ir:ids",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "operator",
srcs = ["operator.cpp"],
@@ -270,6 +252,38 @@ cc_library(
],
)
cc_library(
name = "scope_stack",
srcs = ["scope_stack.cpp"],
hdrs = [
"lexical_lookup.h",
"scope_index.h",
"scope_stack.h",
],
deps = [
"//common:check",
"//common:ostream",
"//common:vlog",
"//toolchain/base:index_base",
"//toolchain/sem_ir:file",
"//toolchain/sem_ir:ids",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "sem_ir_diagnostic_converter",
srcs = ["sem_ir_diagnostic_converter.cpp"],
hdrs = ["sem_ir_diagnostic_converter.h"],
deps = [
":context",
"//toolchain/diagnostics:diagnostic_emitter",
"//toolchain/parse:tree_node_diagnostic_converter",
"//toolchain/sem_ir:file",
"@llvm-project//llvm:Support",
],
)
glob_sh_run(
args = [
"$(location //toolchain/driver:carbon)",
+10 -129
View File
@@ -17,6 +17,7 @@
#include "toolchain/check/handle.h"
#include "toolchain/check/import.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/sem_ir_diagnostic_converter.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/lex/token_kind.h"
@@ -29,125 +30,7 @@
namespace Carbon::Check {
// Handles the transformation of a SemIRLoc to a DiagnosticLoc.
//
// TODO: Move this to diagnostic_helpers.cpp.
class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLoc> {
public:
explicit SemIRDiagnosticConverter(
const llvm::DenseMap<const SemIR::File*, Parse::NodeLocConverter*>*
node_converters,
const SemIR::File* sem_ir)
: node_converters_(node_converters), sem_ir_(sem_ir) {}
// Converts an instruction's location to a diagnostic location, which will be
// the underlying line of code. Adds context for any imports used in the
// current SemIR to get to the underlying code.
auto ConvertLoc(SemIRLoc loc, ContextFnT context_fn) const
-> DiagnosticLoc override {
// Cursors for the current IR and instruction in that IR.
const auto* cursor_ir = sem_ir_;
auto cursor_inst_id = SemIR::InstId::Invalid;
// Notes an import on the diagnostic and updates cursors to point at the
// imported IR.
auto follow_import_ref = [&](SemIR::ImportIRInstId import_ir_inst_id) {
auto import_ir_inst = cursor_ir->import_ir_insts().Get(import_ir_inst_id);
const auto& import_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id);
auto context_loc = ConvertLocInFile(cursor_ir, import_ir.node_id,
loc.token_only, context_fn);
CARBON_DIAGNOSTIC(InImport, Note, "In import.");
context_fn(context_loc, InImport);
cursor_ir = import_ir.sem_ir;
cursor_inst_id = import_ir_inst.inst_id;
};
// If the location is is an import, follows it and returns nullopt.
// Otherwise, it's a parse node, so return the final location.
auto handle_loc = [&](SemIR::LocId loc_id) -> std::optional<DiagnosticLoc> {
if (loc_id.is_import_ir_inst_id()) {
follow_import_ref(loc_id.import_ir_inst_id());
return std::nullopt;
} else {
// Parse nodes always refer to the current IR.
return ConvertLocInFile(cursor_ir, loc_id.node_id(), loc.token_only,
context_fn);
}
};
// Handle the base location.
if (loc.is_inst_id) {
cursor_inst_id = loc.inst_id;
} else {
if (auto diag_loc = handle_loc(loc.loc_id)) {
return *diag_loc;
}
CARBON_CHECK(cursor_inst_id.is_valid()) << "Should have been set";
}
while (true) {
if (cursor_inst_id.is_valid()) {
auto cursor_inst = cursor_ir->insts().Get(cursor_inst_id);
if (auto bind_ref = cursor_inst.TryAs<SemIR::ExportDecl>();
bind_ref && bind_ref->value_id.is_valid()) {
cursor_inst_id = bind_ref->value_id;
continue;
}
// If the parse node is valid, use it for the location.
if (auto loc_id = cursor_ir->insts().GetLocId(cursor_inst_id);
loc_id.is_valid()) {
if (auto diag_loc = handle_loc(loc_id)) {
return *diag_loc;
}
continue;
}
// If a namespace has an instruction for an import, switch to looking at
// it.
if (auto ns = cursor_inst.TryAs<SemIR::Namespace>()) {
if (ns->import_id.is_valid()) {
cursor_inst_id = ns->import_id;
continue;
}
}
}
// Invalid parse node but not an import; just nothing to point at.
return ConvertLocInFile(cursor_ir, Parse::NodeId::Invalid, loc.token_only,
context_fn);
}
}
auto ConvertArg(llvm::Any arg) const -> llvm::Any override {
if (auto* name_id = llvm::any_cast<SemIR::NameId>(&arg)) {
return sem_ir_->names().GetFormatted(*name_id).str();
}
if (auto* type_id = llvm::any_cast<SemIR::TypeId>(&arg)) {
return sem_ir_->StringifyType(*type_id);
}
if (auto* typed_int = llvm::any_cast<TypedInt>(&arg)) {
return llvm::APSInt(typed_int->value,
!sem_ir_->types().IsSignedInt(typed_int->type));
}
return DiagnosticConverter<SemIRLoc>::ConvertArg(arg);
}
private:
auto ConvertLocInFile(const SemIR::File* sem_ir, Parse::NodeId node_id,
bool token_only, ContextFnT context_fn) const
-> DiagnosticLoc {
auto it = node_converters_->find(sem_ir);
CARBON_CHECK(it != node_converters_->end());
return it->second->ConvertLoc(Parse::NodeLoc(node_id, token_only),
context_fn);
}
const llvm::DenseMap<const SemIR::File*, Parse::NodeLocConverter*>*
node_converters_;
const SemIR::File* sem_ir_;
};
namespace {
struct UnitInfo {
// A given import within the file, with its destination.
struct Import {
@@ -211,6 +94,7 @@ struct UnitInfo {
// also be in the corresponding `PackageImports`.
UnitInfo* api_for_impl = nullptr;
};
} // namespace
// Collects transitive imports, handling deduplication.
static auto CollectTransitiveImports(const UnitInfo::PackageImports& imports,
@@ -816,18 +700,14 @@ static auto ProcessNodeIds(Context& context, llvm::raw_ostream* vlog_stream,
// Produces and checks the IR for the provided Parse::Tree.
static auto CheckParseTree(
llvm::DenseMap<const SemIR::File*, Parse::NodeLocConverter*>*
node_converters,
llvm::MutableArrayRef<Parse::NodeLocConverter*> node_converters,
UnitInfo& unit_info, int total_ir_count, llvm::raw_ostream* vlog_stream)
-> void {
unit_info.unit->sem_ir->emplace(
unit_info.check_ir_id, *unit_info.unit->value_stores,
unit_info.unit->tokens->source().filename().str());
// For ease-of-access.
SemIR::File& sem_ir = **unit_info.unit->sem_ir;
CARBON_CHECK(node_converters->insert({&sem_ir, &unit_info.converter}).second);
SemIRDiagnosticConverter converter(node_converters, &sem_ir);
Context::DiagnosticEmitter emitter(converter, unit_info.err_tracker);
Context context(*unit_info.unit->tokens, emitter, *unit_info.unit->parse_tree,
@@ -1044,7 +924,7 @@ static auto TrackImport(
// related to the packaging because the strings are loaded as part of getting
// the ImportKey (which we then do for `impl` files too).
static auto BuildApiMapAndDiagnosePackaging(
llvm::SmallVector<UnitInfo, 0>& unit_infos)
llvm::MutableArrayRef<UnitInfo> unit_infos)
-> llvm::DenseMap<ImportKey, UnitInfo*> {
llvm::DenseMap<ImportKey, UnitInfo*> api_map;
for (auto& unit_info : unit_infos) {
@@ -1137,8 +1017,11 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
// UnitInfo is big due to its SmallVectors, so we default to 0 on the stack.
llvm::SmallVector<UnitInfo, 0> unit_infos;
unit_infos.reserve(units.size());
llvm::SmallVector<Parse::NodeLocConverter*> node_converters;
node_converters.reserve(units.size());
for (auto [i, unit] : llvm::enumerate(units)) {
unit_infos.emplace_back(SemIR::CheckIRId(i), unit);
node_converters.push_back(&unit_infos.back().converter);
}
llvm::DenseMap<ImportKey, UnitInfo*> api_map =
@@ -1183,14 +1066,12 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
}
}
llvm::DenseMap<const SemIR::File*, Parse::NodeLocConverter*> node_converters;
// Check everything with no dependencies. Earlier entries with dependencies
// will be checked as soon as all their dependencies have been checked.
for (int check_index = 0;
check_index < static_cast<int>(ready_to_check.size()); ++check_index) {
auto* unit_info = ready_to_check[check_index];
CheckParseTree(&node_converters, *unit_info, units.size(), vlog_stream);
CheckParseTree(node_converters, *unit_info, units.size(), vlog_stream);
for (auto* incoming_import : unit_info->incoming_imports) {
--incoming_import->imports_remaining;
if (incoming_import->imports_remaining == 0) {
@@ -1237,7 +1118,7 @@ auto CheckParseTrees(llvm::MutableArrayRef<Unit> units, bool prelude_import,
// incomplete imports.
for (auto& unit_info : unit_infos) {
if (unit_info.imports_remaining > 0) {
CheckParseTree(&node_converters, unit_info, units.size(), vlog_stream);
CheckParseTree(node_converters, unit_info, units.size(), vlog_stream);
}
}
}
@@ -0,0 +1,109 @@
// 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/sem_ir_diagnostic_converter.h"
namespace Carbon::Check {
auto SemIRDiagnosticConverter::ConvertLoc(SemIRLoc loc,
ContextFnT context_fn) const
-> DiagnosticLoc {
// Cursors for the current IR and instruction in that IR.
const auto* cursor_ir = sem_ir_;
auto cursor_inst_id = SemIR::InstId::Invalid;
// Notes an import on the diagnostic and updates cursors to point at the
// imported IR.
auto follow_import_ref = [&](SemIR::ImportIRInstId import_ir_inst_id) {
auto import_ir_inst = cursor_ir->import_ir_insts().Get(import_ir_inst_id);
const auto& import_ir = cursor_ir->import_irs().Get(import_ir_inst.ir_id);
auto context_loc = ConvertLocInFile(cursor_ir, import_ir.node_id,
loc.token_only, context_fn);
CARBON_DIAGNOSTIC(InImport, Note, "In import.");
context_fn(context_loc, InImport);
cursor_ir = import_ir.sem_ir;
cursor_inst_id = import_ir_inst.inst_id;
};
// If the location is is an import, follows it and returns nullopt.
// Otherwise, it's a parse node, so return the final location.
auto handle_loc = [&](SemIR::LocId loc_id) -> std::optional<DiagnosticLoc> {
if (loc_id.is_import_ir_inst_id()) {
follow_import_ref(loc_id.import_ir_inst_id());
return std::nullopt;
} else {
// Parse nodes always refer to the current IR.
return ConvertLocInFile(cursor_ir, loc_id.node_id(), loc.token_only,
context_fn);
}
};
// Handle the base location.
if (loc.is_inst_id) {
cursor_inst_id = loc.inst_id;
} else {
if (auto diag_loc = handle_loc(loc.loc_id)) {
return *diag_loc;
}
CARBON_CHECK(cursor_inst_id.is_valid()) << "Should have been set";
}
while (true) {
if (cursor_inst_id.is_valid()) {
auto cursor_inst = cursor_ir->insts().Get(cursor_inst_id);
if (auto bind_ref = cursor_inst.TryAs<SemIR::ExportDecl>();
bind_ref && bind_ref->value_id.is_valid()) {
cursor_inst_id = bind_ref->value_id;
continue;
}
// If the parse node is valid, use it for the location.
if (auto loc_id = cursor_ir->insts().GetLocId(cursor_inst_id);
loc_id.is_valid()) {
if (auto diag_loc = handle_loc(loc_id)) {
return *diag_loc;
}
continue;
}
// If a namespace has an instruction for an import, switch to looking at
// it.
if (auto ns = cursor_inst.TryAs<SemIR::Namespace>()) {
if (ns->import_id.is_valid()) {
cursor_inst_id = ns->import_id;
continue;
}
}
}
// Invalid parse node but not an import; just nothing to point at.
return ConvertLocInFile(cursor_ir, Parse::NodeId::Invalid, loc.token_only,
context_fn);
}
}
auto SemIRDiagnosticConverter::ConvertArg(llvm::Any arg) const -> llvm::Any {
if (auto* name_id = llvm::any_cast<SemIR::NameId>(&arg)) {
return sem_ir_->names().GetFormatted(*name_id).str();
}
if (auto* type_id = llvm::any_cast<SemIR::TypeId>(&arg)) {
return sem_ir_->StringifyType(*type_id);
}
if (auto* typed_int = llvm::any_cast<TypedInt>(&arg)) {
return llvm::APSInt(typed_int->value,
!sem_ir_->types().IsSignedInt(typed_int->type));
}
return DiagnosticConverter<SemIRLoc>::ConvertArg(arg);
}
auto SemIRDiagnosticConverter::ConvertLocInFile(const SemIR::File* sem_ir,
Parse::NodeId node_id,
bool token_only,
ContextFnT context_fn) const
-> DiagnosticLoc {
return node_converters_[sem_ir->check_ir_id().index]->ConvertLoc(
Parse::NodeLoc(node_id, token_only), context_fn);
}
} // namespace Carbon::Check
@@ -0,0 +1,49 @@
// 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_SEM_IR_DIAGNOSTIC_CONVERTER_H_
#define CARBON_TOOLCHAIN_CHECK_SEM_IR_DIAGNOSTIC_CONVERTER_H_
#include "llvm/ADT/ArrayRef.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/diagnostics/diagnostic_converter.h"
#include "toolchain/parse/tree_node_diagnostic_converter.h"
#include "toolchain/sem_ir/file.h"
namespace Carbon::Check {
// Handles the transformation of a SemIRLoc to a DiagnosticLoc.
class SemIRDiagnosticConverter : public DiagnosticConverter<SemIRLoc> {
public:
explicit SemIRDiagnosticConverter(
llvm::ArrayRef<Parse::NodeLocConverter*> node_converters,
const SemIR::File* sem_ir)
: node_converters_(node_converters), sem_ir_(sem_ir) {}
// Converts an instruction's location to a diagnostic location, which will be
// the underlying line of code. Adds context for any imports used in the
// current SemIR to get to the underlying code.
auto ConvertLoc(SemIRLoc loc, ContextFnT context_fn) const
-> DiagnosticLoc override;
// Implements argument conversions for supported check-phase arguments.
auto ConvertArg(llvm::Any arg) const -> llvm::Any override;
private:
// Converts a node_id corresponding to a specific sem_ir to a diagnostic
// location.
auto ConvertLocInFile(const SemIR::File* sem_ir, Parse::NodeId node_id,
bool token_only, ContextFnT context_fn) const
-> DiagnosticLoc;
// Converters for each SemIR.
llvm::ArrayRef<Parse::NodeLocConverter*> node_converters_;
// The current SemIR being processed.
const SemIR::File* sem_ir_;
};
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_SEM_IR_DIAGNOSTIC_CONVERTER_H_