mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:20:11 +01:00
Improve backtrace for lowering crashes. (#5651)
Factor out the logic for mapping from a `LocId` into a diagnostic
location from check into sem_ir so it can be reused by lowering. Include
the function and instruction being lowered in the pretty stack trace.
Example stack trace:
```carbon
2. filename: examples/sieve.carbon
3. core/prelude/types/int.carbon:213:3: lowering function Core.Op(Core.IntLiteral as Core.ImplicitAs(i32))
fn Op[addr self: Self*](other: Self) = "int.sadd_assign";
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4. core/prelude/operators/arithmetic.carbon:22:27: lowering call
fn Op[addr self: Self*](other: Other);
^~~~~~~~~~~~
```
This commit is contained in:
@@ -285,6 +285,7 @@ cc_library(
|
||||
"//toolchain/lex:token_index",
|
||||
"//toolchain/parse:tree",
|
||||
"//toolchain/sem_ir:absolute_node_id",
|
||||
"//toolchain/sem_ir:diagnostic_loc_converter",
|
||||
"//toolchain/sem_ir:file",
|
||||
"//toolchain/sem_ir:stringify",
|
||||
"//toolchain/sem_ir:typed_insts",
|
||||
|
||||
@@ -377,7 +377,7 @@ auto CheckUnit::ProcessNodeIds() -> bool {
|
||||
const auto& tree = tree_and_subtrees_getter_();
|
||||
auto converted = tree.NodeToDiagnosticLoc(node_id, /*token_only=*/false);
|
||||
converted.loc.FormatLocation(output);
|
||||
output << "checking " << context_.parse_tree().node_kind(node_id) << "\n";
|
||||
output << "Checking " << context_.parse_tree().node_kind(node_id) << "\n";
|
||||
// Crash output has a tab indent; try to indent slightly past that.
|
||||
converted.loc.FormatSnippet(output, /*indent=*/10);
|
||||
});
|
||||
|
||||
@@ -19,8 +19,12 @@ namespace Carbon::Check {
|
||||
auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
|
||||
ContextFnT context_fn) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
auto converted =
|
||||
ConvertLocImpl(loc_id.loc_id(), loc_id.is_token_only(), context_fn);
|
||||
auto [imports, converted] = loc_converter_.ConvertWithImports(
|
||||
loc_id.loc_id(), loc_id.is_token_only());
|
||||
for (const auto& import : imports) {
|
||||
CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
|
||||
context_fn(import.loc, InImport);
|
||||
}
|
||||
|
||||
// Use the token when possible, but -1 is the default value.
|
||||
auto last_offset = -1;
|
||||
@@ -40,73 +44,6 @@ auto DiagnosticEmitter::ConvertLoc(LocIdForDiagnostics loc_id,
|
||||
return converted;
|
||||
}
|
||||
|
||||
auto DiagnosticEmitter::ConvertLocImpl(SemIR::LocId loc_id, bool is_token_only,
|
||||
ContextFnT context_fn) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
|
||||
SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
|
||||
|
||||
auto final_node_id = absolute_node_ids.pop_back_val();
|
||||
for (const auto& absolute_node_id : absolute_node_ids) {
|
||||
if (!absolute_node_id.node_id().has_value()) {
|
||||
// TODO: Add an "In implicit import of prelude." note for the case where
|
||||
// we don't have a location.
|
||||
continue;
|
||||
}
|
||||
// TODO: Include the name of the imported library in the diagnostic.
|
||||
auto diag_loc =
|
||||
ConvertLocInFile(absolute_node_id, is_token_only, context_fn);
|
||||
AddInImport(diag_loc.loc, context_fn);
|
||||
}
|
||||
|
||||
return ConvertLocInFile(final_node_id, is_token_only, context_fn);
|
||||
}
|
||||
|
||||
auto DiagnosticEmitter::ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id,
|
||||
bool token_only,
|
||||
ContextFnT context_fn) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
if (absolute_node_id.check_ir_id() == SemIR::CheckIRId::Cpp) {
|
||||
// Special handling of Clang source locations.
|
||||
CARBON_CHECK(sem_ir_->import_cpps().size() > 0);
|
||||
// TODO: Use information on the specific C++ import extract from Clang error
|
||||
// message and propagated here instead of using first C++ import
|
||||
// arbitrarily.
|
||||
Parse::NodeId import_node_id =
|
||||
sem_ir_->import_cpps().values().begin()->node_id;
|
||||
AddInImport(ConvertLocInCarbonFile(sem_ir_->check_ir_id(), import_node_id,
|
||||
/*token_only=*/false)
|
||||
.loc,
|
||||
context_fn);
|
||||
|
||||
clang::SourceLocation clang_loc = sem_ir_->clang_source_locs().Get(
|
||||
absolute_node_id.clang_source_loc_id());
|
||||
|
||||
CARBON_CHECK(sem_ir_->cpp_ast());
|
||||
clang::PresumedLoc presumed_loc =
|
||||
sem_ir_->cpp_ast()->getSourceManager().getPresumedLoc(clang_loc);
|
||||
|
||||
return Diagnostics::ConvertedLoc{
|
||||
.loc = {.filename = presumed_loc.getFilename(),
|
||||
.line_number = static_cast<int32_t>(presumed_loc.getLine())},
|
||||
// TODO: Set `last_byte_offset` based on the `import Cpp` location.
|
||||
.last_byte_offset = 0};
|
||||
}
|
||||
|
||||
return ConvertLocInCarbonFile(absolute_node_id.check_ir_id(),
|
||||
absolute_node_id.node_id(), token_only);
|
||||
}
|
||||
|
||||
auto DiagnosticEmitter::ConvertLocInCarbonFile(SemIR::CheckIRId check_ir_id,
|
||||
Parse::NodeId node_id,
|
||||
bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
CARBON_CHECK(check_ir_id != SemIR::CheckIRId::Cpp);
|
||||
const auto& tree_and_subtrees =
|
||||
tree_and_subtrees_getters_[check_ir_id.index]();
|
||||
return tree_and_subtrees.NodeToDiagnosticLoc(node_id, token_only);
|
||||
}
|
||||
|
||||
auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
|
||||
if (auto* library_name_id = llvm::any_cast<SemIR::LibraryNameId>(&arg)) {
|
||||
std::string library_name;
|
||||
@@ -178,10 +115,4 @@ auto DiagnosticEmitter::ConvertArg(llvm::Any arg) const -> llvm::Any {
|
||||
return DiagnosticEmitterBase::ConvertArg(arg);
|
||||
}
|
||||
|
||||
auto DiagnosticEmitter::AddInImport(Diagnostics::Loc loc, ContextFnT context_fn)
|
||||
-> void {
|
||||
CARBON_DIAGNOSTIC(InImport, LocationInfo, "in import");
|
||||
context_fn(loc, InImport);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/lex/token_index.h"
|
||||
#include "toolchain/parse/tree_and_subtrees.h"
|
||||
#include "toolchain/sem_ir/absolute_node_id.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
@@ -24,8 +21,8 @@ class DiagnosticEmitter : public DiagnosticEmitterBase {
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
const SemIR::File* sem_ir)
|
||||
: DiagnosticEmitterBase(consumer),
|
||||
tree_and_subtrees_getters_(tree_and_subtrees_getters),
|
||||
sem_ir_(sem_ir) {}
|
||||
sem_ir_(sem_ir),
|
||||
loc_converter_(tree_and_subtrees_getters, sem_ir) {}
|
||||
|
||||
// If a byte offset is past the current last byte offset, advances forward.
|
||||
// Earlier offsets are ignored.
|
||||
@@ -47,31 +44,12 @@ class DiagnosticEmitter : public DiagnosticEmitterBase {
|
||||
-> Diagnostics::ConvertedLoc override;
|
||||
|
||||
private:
|
||||
// Implements `ConvertLoc`, but without `last_token_` applied.
|
||||
auto ConvertLocImpl(SemIR::LocId loc_id, bool is_token_only,
|
||||
ContextFnT context_fn) const -> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts an `absolute_node_id` in either a Carbon file or C++ import to a
|
||||
// diagnostic location.
|
||||
auto ConvertLocInFile(SemIR::AbsoluteNodeId absolute_node_id, bool token_only,
|
||||
ContextFnT context_fn) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts a `node_id` corresponding to a specific sem_ir to a diagnostic
|
||||
// location.
|
||||
auto ConvertLocInCarbonFile(SemIR::CheckIRId check_ir_id,
|
||||
Parse::NodeId node_id, bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Adds `in import` note.
|
||||
static auto AddInImport(Diagnostics::Loc loc, ContextFnT context_fn) -> void;
|
||||
|
||||
// Converters for each SemIR.
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
|
||||
|
||||
// The current SemIR being processed.
|
||||
const SemIR::File* sem_ir_;
|
||||
|
||||
// Converter for locations.
|
||||
SemIR::DiagnosticLocConverter loc_converter_;
|
||||
|
||||
// The last token encountered during processing.
|
||||
Lex::TokenIndex last_token_ = Lex::TokenIndex::None;
|
||||
};
|
||||
|
||||
@@ -708,13 +708,11 @@ auto CompilationUnit::RunLower() -> void {
|
||||
// TODO: Consider disabling instruction naming by default if we're not
|
||||
// producing textual LLVM IR.
|
||||
SemIR::InstNamer inst_namer(&*sem_ir_);
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>> subtrees;
|
||||
if (options_->include_debug_info) {
|
||||
subtrees = cache_->tree_and_subtrees_getters();
|
||||
}
|
||||
module_ = Lower::LowerToLLVM(*llvm_context_, driver_env_->fs, subtrees,
|
||||
input_filename_, *sem_ir_, &inst_namer,
|
||||
vlog_stream_);
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> subtrees =
|
||||
cache_->tree_and_subtrees_getters();
|
||||
module_ = Lower::LowerToLLVM(
|
||||
*llvm_context_, driver_env_->fs, options_->include_debug_info, subtrees,
|
||||
input_filename_, *sem_ir_, &inst_namer, vlog_stream_);
|
||||
});
|
||||
if (vlog_stream_) {
|
||||
CARBON_VLOG("*** llvm::Module ***\n");
|
||||
@@ -846,7 +844,7 @@ auto CompilationUnit::LogCall(llvm::StringLiteral logging_label,
|
||||
llvm::StringLiteral timing_label,
|
||||
llvm::function_ref<auto()->void> fn) -> void {
|
||||
PrettyStackTraceFunction trace_file([&](llvm::raw_ostream& out) {
|
||||
out << "filename: " << input_filename_ << "\n";
|
||||
out << "Filename: " << input_filename_ << "\n";
|
||||
});
|
||||
CARBON_VLOG("*** {0}: {1} ***\n", logging_label, input_filename_);
|
||||
Timings::ScopedTiming timing(timings_ ? &*timings_ : nullptr, timing_label);
|
||||
|
||||
@@ -53,12 +53,15 @@ cc_library(
|
||||
"//common:vlog",
|
||||
"//toolchain/base:fixed_size_value_store",
|
||||
"//toolchain/base:kind_switch",
|
||||
"//toolchain/base:pretty_stack_trace_function",
|
||||
"//toolchain/parse:tree",
|
||||
"//toolchain/sem_ir:absolute_node_id",
|
||||
"//toolchain/sem_ir:diagnostic_loc_converter",
|
||||
"//toolchain/sem_ir:entry_point",
|
||||
"//toolchain/sem_ir:expr_info",
|
||||
"//toolchain/sem_ir:file",
|
||||
"//toolchain/sem_ir:inst_namer",
|
||||
"//toolchain/sem_ir:stringify",
|
||||
"//toolchain/sem_ir:typed_insts",
|
||||
"@llvm-project//clang:ast",
|
||||
"@llvm-project//clang:basic",
|
||||
|
||||
@@ -13,21 +13,20 @@
|
||||
|
||||
namespace Carbon::Lower {
|
||||
|
||||
Context::Context(llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
tree_and_subtrees_getters_for_debug_info,
|
||||
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream)
|
||||
Context::Context(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream)
|
||||
: llvm_context_(&llvm_context),
|
||||
llvm_module_(std::make_unique<llvm::Module>(module_name, llvm_context)),
|
||||
file_system_(std::move(fs)),
|
||||
di_builder_(*llvm_module_),
|
||||
di_compile_unit_(
|
||||
tree_and_subtrees_getters_for_debug_info
|
||||
want_debug_info
|
||||
? BuildDICompileUnit(module_name, *llvm_module_, di_builder_)
|
||||
: nullptr),
|
||||
tree_and_subtrees_getters_for_debug_info_(
|
||||
tree_and_subtrees_getters_for_debug_info),
|
||||
tree_and_subtrees_getters_(tree_and_subtrees_getters),
|
||||
vlog_stream_(vlog_stream) {}
|
||||
|
||||
auto Context::GetFileContext(const SemIR::File* file,
|
||||
@@ -79,8 +78,7 @@ auto Context::BuildDICompileUnit(llvm::StringRef module_name,
|
||||
|
||||
auto Context::GetLocForDI(SemIR::AbsoluteNodeId abs_node_id) -> LocForDI {
|
||||
const auto& tree_and_subtrees =
|
||||
(*tree_and_subtrees_getters_for_debug_info_)[abs_node_id.check_ir_id()
|
||||
.index]();
|
||||
tree_and_subtrees_getters()[abs_node_id.check_ir_id().index]();
|
||||
const auto& tokens = tree_and_subtrees.tree().tokens();
|
||||
|
||||
if (abs_node_id.node_id().has_value()) {
|
||||
|
||||
@@ -41,11 +41,11 @@ class Context {
|
||||
SemIR::SpecificId specific_id;
|
||||
};
|
||||
|
||||
explicit Context(llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
tree_and_subtrees_getters_for_debug_info,
|
||||
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream);
|
||||
explicit Context(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
llvm::StringRef module_name, llvm::raw_ostream* vlog_stream);
|
||||
|
||||
// Gets or creates the `FileContext` for a given SemIR file. If an
|
||||
// `inst_namer` is specified the first time this is called for a file, it will
|
||||
@@ -95,6 +95,10 @@ class Context {
|
||||
}
|
||||
auto di_builder() -> llvm::DIBuilder& { return di_builder_; }
|
||||
auto di_compile_unit() -> llvm::DICompileUnit* { return di_compile_unit_; }
|
||||
auto tree_and_subtrees_getters()
|
||||
-> llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> {
|
||||
return tree_and_subtrees_getters_;
|
||||
}
|
||||
|
||||
auto printf_int_format_string() -> llvm::Value* {
|
||||
return printf_int_format_string_;
|
||||
@@ -128,9 +132,8 @@ class Context {
|
||||
// The DICompileUnit, if any - null implies debug info is not being emitted.
|
||||
llvm::DICompileUnit* di_compile_unit_;
|
||||
|
||||
// The trees are only provided when debug info should be emitted.
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
tree_and_subtrees_getters_for_debug_info_;
|
||||
// Parse trees. Used for debug information and crash diagnostics.
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
|
||||
|
||||
// The optional vlog stream.
|
||||
llvm::raw_ostream* vlog_stream_;
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/base/pretty_stack_trace_function.h"
|
||||
#include "toolchain/lower/constant.h"
|
||||
#include "toolchain/lower/function_context.h"
|
||||
#include "toolchain/lower/mangler.h"
|
||||
#include "toolchain/sem_ir/absolute_node_id.h"
|
||||
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
|
||||
#include "toolchain/sem_ir/entry_point.h"
|
||||
#include "toolchain/sem_ir/expr_info.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
@@ -33,6 +35,7 @@
|
||||
#include "toolchain/sem_ir/inst_categories.h"
|
||||
#include "toolchain/sem_ir/inst_kind.h"
|
||||
#include "toolchain/sem_ir/pattern.h"
|
||||
#include "toolchain/sem_ir/stringify.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Lower {
|
||||
@@ -757,6 +760,26 @@ auto FileContext::BuildFunctionBody(SemIR::FunctionId function_id,
|
||||
FileContext& definition_context,
|
||||
const SemIR::Function& definition_function)
|
||||
-> void {
|
||||
// On crash, report the function we were lowering.
|
||||
PrettyStackTraceFunction stack_trace_entry([&](llvm::raw_ostream& output) {
|
||||
SemIR::DiagnosticLocConverter converter(
|
||||
context().tree_and_subtrees_getters(), &sem_ir());
|
||||
auto converted =
|
||||
converter.Convert(SemIR::LocId(declaration_function.definition_id),
|
||||
/*token_only=*/false);
|
||||
converted.loc.FormatLocation(output);
|
||||
output << "Lowering function ";
|
||||
if (specific_id.has_value()) {
|
||||
output << SemIR::StringifySpecific(sem_ir(), specific_id);
|
||||
} else {
|
||||
output << SemIR::StringifyConstantInst(
|
||||
sem_ir(), declaration_function.definition_id);
|
||||
}
|
||||
output << "\n";
|
||||
// Crash output has a tab indent; try to indent slightly past that.
|
||||
converted.loc.FormatSnippet(output, /*indent=*/10);
|
||||
});
|
||||
|
||||
// Note that `definition_function` is potentially from a different SemIR::File
|
||||
// than the one that this file context represents. Any lowering done for
|
||||
// values derived from `definition_function` should use `definition_context`
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "common/vlog.h"
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/base/pretty_stack_trace_function.h"
|
||||
#include "toolchain/sem_ir/diagnostic_loc_converter.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/generic.h"
|
||||
|
||||
@@ -55,7 +57,25 @@ auto FunctionContext::TryToReuseBlock(SemIR::InstBlockId block_id,
|
||||
}
|
||||
|
||||
auto FunctionContext::LowerBlockContents(SemIR::InstBlockId block_id) -> void {
|
||||
auto inst_id_for_stack_trace = SemIR::InstId::None;
|
||||
|
||||
// On crash, report the instruction we were lowering.
|
||||
PrettyStackTraceFunction stack_trace_entry([&](llvm::raw_ostream& output) {
|
||||
SemIR::DiagnosticLocConverter converter(
|
||||
file_context_->context().tree_and_subtrees_getters(), &sem_ir());
|
||||
auto converted = converter.Convert(SemIR::LocId(inst_id_for_stack_trace),
|
||||
/*token_only=*/false);
|
||||
converted.loc.FormatLocation(output);
|
||||
// TODO: Format SemIR for the instruction we were lowering?
|
||||
output << "Lowering "
|
||||
<< sem_ir().insts().Get(inst_id_for_stack_trace).kind().ir_name()
|
||||
<< "\n";
|
||||
// Crash output has a tab indent; try to indent slightly past that.
|
||||
converted.loc.FormatSnippet(output, /*indent=*/10);
|
||||
});
|
||||
|
||||
for (auto inst_id : sem_ir().inst_blocks().Get(block_id)) {
|
||||
inst_id_for_stack_trace = inst_id;
|
||||
LowerInst(inst_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,15 @@
|
||||
|
||||
namespace Carbon::Lower {
|
||||
|
||||
auto LowerToLLVM(llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
tree_and_subtrees_getters_for_debug_info,
|
||||
llvm::StringRef module_name, const SemIR::File& sem_ir,
|
||||
const SemIR::InstNamer* inst_namer,
|
||||
llvm::raw_ostream* vlog_stream)
|
||||
auto LowerToLLVM(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
llvm::StringRef module_name, const SemIR::File& sem_ir,
|
||||
const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream)
|
||||
-> std::unique_ptr<llvm::Module> {
|
||||
Context context(llvm_context, std::move(fs),
|
||||
tree_and_subtrees_getters_for_debug_info, module_name,
|
||||
vlog_stream);
|
||||
Context context(llvm_context, std::move(fs), want_debug_info,
|
||||
tree_and_subtrees_getters, module_name, vlog_stream);
|
||||
context.GetFileContext(&sem_ir, inst_namer).LowerDefinitions();
|
||||
return std::move(context).Finalize();
|
||||
}
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
namespace Carbon::Lower {
|
||||
|
||||
// Lowers SemIR to LLVM IR.
|
||||
auto LowerToLLVM(llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
||||
std::optional<llvm::ArrayRef<Parse::GetTreeAndSubtreesFn>>
|
||||
tree_and_subtrees_getters_for_debug_info,
|
||||
llvm::StringRef module_name, const SemIR::File& sem_ir,
|
||||
const SemIR::InstNamer* inst_namer,
|
||||
llvm::raw_ostream* vlog_stream)
|
||||
auto LowerToLLVM(
|
||||
llvm::LLVMContext& llvm_context,
|
||||
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs, bool want_debug_info,
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
llvm::StringRef module_name, const SemIR::File& sem_ir,
|
||||
const SemIR::InstNamer* inst_namer, llvm::raw_ostream* vlog_stream)
|
||||
-> std::unique_ptr<llvm::Module>;
|
||||
|
||||
} // namespace Carbon::Lower
|
||||
|
||||
@@ -228,6 +228,20 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "diagnostic_loc_converter",
|
||||
srcs = ["diagnostic_loc_converter.cpp"],
|
||||
hdrs = ["diagnostic_loc_converter.h"],
|
||||
deps = [
|
||||
":absolute_node_id",
|
||||
":file",
|
||||
":typed_insts",
|
||||
"//toolchain/diagnostics:diagnostic_emitter",
|
||||
"//toolchain/parse:tree",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "dump",
|
||||
srcs = ["dump.cpp"],
|
||||
|
||||
@@ -8,14 +8,21 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// Notes an import on the diagnostic. For `Cpp` imports, returns true. Otherwise
|
||||
// updates cursors to point at the imported IR and returns false.
|
||||
// Follows an imported instruction location to find the sequence of import
|
||||
// locations and the ultimately imported location.
|
||||
static auto FollowImportRef(
|
||||
llvm::SmallVector<AbsoluteNodeId>& absolute_node_ids,
|
||||
const File*& cursor_ir, InstId& cursor_inst_id,
|
||||
ImportIRInstId import_ir_inst_id) -> bool {
|
||||
auto import_ir_inst = cursor_ir->import_ir_insts().Get(import_ir_inst_id);
|
||||
if (import_ir_inst.ir_id() == ImportIRId::Cpp) {
|
||||
CARBON_CHECK(cursor_ir->import_cpps().size() > 0);
|
||||
// TODO: Decompose the Clang source location to determine which C++ import
|
||||
// made this location available, and use the location of that import instead
|
||||
// of arbitrarily using the first C++ import.
|
||||
absolute_node_ids.push_back(
|
||||
AbsoluteNodeId(cursor_ir->check_ir_id(),
|
||||
cursor_ir->import_cpps().values().begin()->node_id));
|
||||
absolute_node_ids.push_back(
|
||||
AbsoluteNodeId(import_ir_inst.clang_source_loc_id()));
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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/sem_ir/diagnostic_loc_converter.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
auto DiagnosticLocConverter::ConvertWithImports(LocId loc_id,
|
||||
bool token_only) const
|
||||
-> LocAndImports {
|
||||
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
|
||||
SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
|
||||
auto final_node_id = absolute_node_ids.pop_back_val();
|
||||
|
||||
// Convert the final location.
|
||||
LocAndImports result = {.loc = Convert(final_node_id, token_only)};
|
||||
|
||||
// Convert the import locations.
|
||||
for (const auto& absolute_node_id : absolute_node_ids) {
|
||||
if (!absolute_node_id.node_id().has_value()) {
|
||||
// TODO: Add an `ImportLoc` pointing at the prelude for the case where
|
||||
// we don't have a location.
|
||||
continue;
|
||||
}
|
||||
result.imports.push_back({.loc = Convert(absolute_node_id, false).loc});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto DiagnosticLocConverter::Convert(LocId loc_id, bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
|
||||
SemIR::GetAbsoluteNodeId(sem_ir_, loc_id);
|
||||
return Convert(absolute_node_ids.back(), token_only);
|
||||
}
|
||||
|
||||
auto DiagnosticLocConverter::Convert(SemIR::AbsoluteNodeId absolute_node_id,
|
||||
bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
if (absolute_node_id.check_ir_id() == SemIR::CheckIRId::Cpp) {
|
||||
return Convert(absolute_node_id.clang_source_loc_id());
|
||||
}
|
||||
|
||||
return Convert(absolute_node_id.check_ir_id(), absolute_node_id.node_id(),
|
||||
token_only);
|
||||
}
|
||||
|
||||
auto DiagnosticLocConverter::Convert(SemIR::CheckIRId check_ir_id,
|
||||
Parse::NodeId node_id,
|
||||
bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
CARBON_CHECK(check_ir_id != SemIR::CheckIRId::Cpp);
|
||||
const auto& tree_and_subtrees =
|
||||
tree_and_subtrees_getters_[check_ir_id.index]();
|
||||
return tree_and_subtrees.NodeToDiagnosticLoc(node_id, token_only);
|
||||
}
|
||||
|
||||
auto DiagnosticLocConverter::Convert(ClangSourceLocId clang_source_loc_id) const
|
||||
-> Diagnostics::ConvertedLoc {
|
||||
clang::SourceLocation clang_loc =
|
||||
sem_ir_->clang_source_locs().Get(clang_source_loc_id);
|
||||
|
||||
CARBON_CHECK(sem_ir_->cpp_ast());
|
||||
clang::PresumedLoc presumed_loc =
|
||||
sem_ir_->cpp_ast()->getSourceManager().getPresumedLoc(clang_loc);
|
||||
unsigned offset =
|
||||
sem_ir_->cpp_ast()->getSourceManager().getDecomposedLoc(clang_loc).second;
|
||||
|
||||
return Diagnostics::ConvertedLoc{
|
||||
.loc = {.filename = presumed_loc.getFilename(),
|
||||
.line_number = static_cast<int32_t>(presumed_loc.getLine())},
|
||||
.last_byte_offset = static_cast<int32_t>(offset)};
|
||||
}
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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_SEM_IR_DIAGNOSTIC_LOC_CONVERTER_H_
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_DIAGNOSTIC_LOC_CONVERTER_H_
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/parse/tree_and_subtrees.h"
|
||||
#include "toolchain/sem_ir/absolute_node_id.h"
|
||||
#include "toolchain/sem_ir/file.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// Converter from compact location information into a diagnostic location
|
||||
// describing a filename, line location, and potentially a sequence of imports.
|
||||
// Such diagnostics locations are used to render user-facing diagnostics and
|
||||
// also locations for stack trace in crash diagnostics.
|
||||
class DiagnosticLocConverter {
|
||||
public:
|
||||
// Information about an import within which the location was found.
|
||||
struct ImportLoc {
|
||||
Diagnostics::Loc loc;
|
||||
// TODO: Include the name of the imported library in this information so it
|
||||
// can be included in the diagnostic.
|
||||
};
|
||||
|
||||
// Information about a location that has been converted from a LocId to a
|
||||
// diagnostic location.
|
||||
struct LocAndImports {
|
||||
llvm::SmallVector<ImportLoc> imports;
|
||||
Diagnostics::ConvertedLoc loc;
|
||||
};
|
||||
|
||||
// `sem_ir` must not be null.
|
||||
explicit DiagnosticLocConverter(
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters,
|
||||
const File* sem_ir)
|
||||
: tree_and_subtrees_getters_(tree_and_subtrees_getters),
|
||||
sem_ir_(sem_ir) {}
|
||||
|
||||
// Converts the given location into a sequence of import locations and a final
|
||||
// diagnostic location.
|
||||
auto ConvertWithImports(LocId loc_id, bool token_only) const -> LocAndImports;
|
||||
|
||||
// Converts the given location into a diagnostic location.
|
||||
auto Convert(LocId loc_id, bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts an `absolute_node_id` in either a Carbon file or C++ import to a
|
||||
// diagnostic location.
|
||||
auto Convert(AbsoluteNodeId absolute_node_id, bool token_only) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts a `node_id` corresponding to a specific check IR to a diagnostic
|
||||
// location.
|
||||
auto Convert(CheckIRId check_ir_id, Parse::NodeId node_id,
|
||||
bool token_only) const -> Diagnostics::ConvertedLoc;
|
||||
|
||||
// Converts a location pointing into C++ code to a diagnostic location.
|
||||
auto Convert(ClangSourceLocId clang_source_loc_id) const
|
||||
-> Diagnostics::ConvertedLoc;
|
||||
|
||||
private:
|
||||
// Converters for each SemIR.
|
||||
llvm::ArrayRef<Parse::GetTreeAndSubtreesFn> tree_and_subtrees_getters_;
|
||||
|
||||
// The current SemIR being processed.
|
||||
const File* sem_ir_;
|
||||
};
|
||||
|
||||
} // namespace Carbon::SemIR
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_SEM_IR_DIAGNOSTIC_LOC_CONVERTER_H_
|
||||
Reference in New Issue
Block a user