diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 5ed3672e2953..70b7b73b7dc1 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -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)", diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index 620068e2cddf..bf31d3f95949 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -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 { - public: - explicit SemIRDiagnosticConverter( - const llvm::DenseMap* - 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 { - 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(); - 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()) { - 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(&arg)) { - return sem_ir_->names().GetFormatted(*name_id).str(); - } - if (auto* type_id = llvm::any_cast(&arg)) { - return sem_ir_->StringifyType(*type_id); - } - if (auto* typed_int = llvm::any_cast(&arg)) { - return llvm::APSInt(typed_int->value, - !sem_ir_->types().IsSignedInt(typed_int->type)); - } - return DiagnosticConverter::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* - 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* - node_converters, + llvm::MutableArrayRef 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& unit_infos) + llvm::MutableArrayRef unit_infos) -> llvm::DenseMap { llvm::DenseMap api_map; for (auto& unit_info : unit_infos) { @@ -1137,8 +1017,11 @@ auto CheckParseTrees(llvm::MutableArrayRef units, bool prelude_import, // UnitInfo is big due to its SmallVectors, so we default to 0 on the stack. llvm::SmallVector unit_infos; unit_infos.reserve(units.size()); + llvm::SmallVector 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 api_map = @@ -1183,14 +1066,12 @@ auto CheckParseTrees(llvm::MutableArrayRef units, bool prelude_import, } } - llvm::DenseMap 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(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 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); } } } diff --git a/toolchain/check/sem_ir_diagnostic_converter.cpp b/toolchain/check/sem_ir_diagnostic_converter.cpp new file mode 100644 index 000000000000..d4a4a4740c0e --- /dev/null +++ b/toolchain/check/sem_ir_diagnostic_converter.cpp @@ -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 { + 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(); + 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()) { + 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(&arg)) { + return sem_ir_->names().GetFormatted(*name_id).str(); + } + if (auto* type_id = llvm::any_cast(&arg)) { + return sem_ir_->StringifyType(*type_id); + } + if (auto* typed_int = llvm::any_cast(&arg)) { + return llvm::APSInt(typed_int->value, + !sem_ir_->types().IsSignedInt(typed_int->type)); + } + return DiagnosticConverter::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 diff --git a/toolchain/check/sem_ir_diagnostic_converter.h b/toolchain/check/sem_ir_diagnostic_converter.h new file mode 100644 index 000000000000..34743ebd0781 --- /dev/null +++ b/toolchain/check/sem_ir_diagnostic_converter.h @@ -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 { + public: + explicit SemIRDiagnosticConverter( + llvm::ArrayRef 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 node_converters_; + + // The current SemIR being processed. + const SemIR::File* sem_ir_; +}; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_SEM_IR_DIAGNOSTIC_CONVERTER_H_