Change how CheckParseTrees receives NodeLocConverters (#4563)

This is really a set of closely related changes:

1) We really shouldn't be creating a Check::Unit for _Lower_. This fixes
that by storing the diagnostic converter for reuse.
2) Rather than passing in node converters to Check as their own array,
pass diagnostic converters as part of Check::Unit.
3) To support creating the converters early, pass SemIR::File
pre-constructed.
4) Since SemIR::File construction was used to track "checked", add
`is_checked` for that.
5) Clarifies a subtle edge case around `input_filename_` use with `-`.

Note the key consequence of this change, where I actually started, is
that `Check` only has one array of `Check::Unit` instead of receiving
`NodeLocConverter` as a separate array.
This commit is contained in:
Jon Ross-Perkins
2024-11-22 16:51:10 +00:00
committed by GitHub
parent 77689c4a72
commit 17272cf93c
10 changed files with 122 additions and 95 deletions
+67 -38
View File
@@ -395,7 +395,7 @@ class CompilationUnit {
// Parses tokens. Returns true on success.
auto RunParse() -> void {
CARBON_CHECK(tokens_);
CARBON_CHECK(tokens_, "Must call RunLex first");
LogCall("Parse::Parse", "parse", [&] {
parse_tree_ = Parse::Parse(*tokens_, *consumer_, vlog_stream_);
@@ -418,34 +418,49 @@ class CompilationUnit {
}
}
auto PreCheck() -> Parse::NodeLocConverter& {
CARBON_CHECK(parse_tree_, "Must call RunParse first");
get_parse_tree_and_subtrees_ = [this]() -> const Parse::TreeAndSubtrees& {
return this->GetParseTreeAndSubtrees();
};
node_converter_.emplace(&*tokens_, source_->filename(),
*get_parse_tree_and_subtrees_);
return *node_converter_;
}
// Returns information needed to check this unit.
auto GetCheckUnit() -> Check::Unit {
CARBON_CHECK(parse_tree_);
return {
.value_stores = &value_stores_,
.timings = &timings_,
.tokens = &*tokens_,
.parse_tree = &*parse_tree_,
.consumer = consumer_,
.get_parse_tree_and_subtrees = [&]() -> const Parse::TreeAndSubtrees& {
return GetParseTreeAndSubtrees();
},
.sem_ir = &sem_ir_};
auto GetCheckUnit(SemIR::CheckIRId check_ir_id,
llvm::ArrayRef<Parse::NodeLocConverter*> node_converters)
-> Check::Unit {
CARBON_CHECK(node_converter_, "Must call PreCheck first");
sem_ir_.emplace(check_ir_id, parse_tree_->packaging_decl(), value_stores_,
input_filename_);
if (mem_usage_) {
mem_usage_->Collect("sem_ir_", *sem_ir_);
}
sem_ir_converter_.emplace(node_converters, &*sem_ir_);
return {.consumer = consumer_,
.value_stores = &value_stores_,
.timings = &timings_,
.tokens = &*tokens_,
.parse_tree = &*parse_tree_,
.get_parse_tree_and_subtrees = *get_parse_tree_and_subtrees_,
.sem_ir = &*sem_ir_,
.node_converter = &*node_converter_,
.sem_ir_converter = &*sem_ir_converter_};
}
// Runs post-check logic. Returns true if checking succeeded for the IR.
auto PostCheck() -> void {
CARBON_CHECK(sem_ir_);
CARBON_CHECK(sem_ir_converter_, "Must call GetCheckUnit first");
// We've finished all steps that can produce diagnostics. Emit the
// diagnostics now, so that the developer sees them sooner and doesn't need
// to wait for code generation.
consumer_->Flush();
if (mem_usage_) {
mem_usage_->Collect("sem_ir_", *sem_ir_);
}
if (options_.dump_raw_sem_ir && IncludeInDumps()) {
CARBON_VLOG("*** Raw SemIR::File ***\n{0}\n", *sem_ir_);
sem_ir_->Print(driver_env_->output_stream, options_.builtin_sem_ir);
@@ -486,8 +501,8 @@ class CompilationUnit {
}
// Lower SemIR to LLVM IR.
auto RunLower(const Check::SemIRDiagnosticConverter& converter) -> void {
CARBON_CHECK(sem_ir_);
auto RunLower() -> void {
CARBON_CHECK(sem_ir_converter_, "Must call PostCheck first");
LogCall("Lower::LowerToLLVM", "lower", [&] {
llvm_context_ = std::make_unique<llvm::LLVMContext>();
@@ -495,8 +510,8 @@ class CompilationUnit {
// producing textual LLVM IR.
SemIR::InstNamer inst_namer(*tokens_, *parse_tree_, *sem_ir_);
module_ = Lower::LowerToLLVM(*llvm_context_, options_.include_debug_info,
converter, input_filename_, *sem_ir_,
&inst_namer, vlog_stream_);
*sem_ir_converter_, input_filename_,
*sem_ir_, &inst_namer, vlog_stream_);
});
if (vlog_stream_) {
CARBON_VLOG("*** llvm::Module ***\n");
@@ -511,7 +526,7 @@ class CompilationUnit {
}
auto RunCodeGen() -> void {
CARBON_CHECK(module_);
CARBON_CHECK(module_, "Must call RunLower first");
LogCall("CodeGen", "codegen", [&] { success_ = RunCodeGenHelper(); });
}
@@ -577,8 +592,8 @@ class CompilationUnit {
if (!source_->is_regular_file()) {
// Don't invent file names like `-.o` or `/dev/stdin.o`.
driver_env_->error_stream
<< "error: output file name must be specified for input '"
<< input_filename_ << "' that is not a regular file\n";
<< "error: output file name must be specified for input `"
<< input_filename_ << "` that is not a regular file\n";
return false;
}
output_filename = input_filename_;
@@ -654,6 +669,10 @@ class CompilationUnit {
DriverEnv* driver_env_;
SharedValueStores value_stores_;
const CompileOptions& options_;
// The input filename from the command line. For most diagnostics, we
// typically use `source_->filename()`, which includes a `-` -> `<stdin>`
// translation. However, logging and some diagnostics use the command line
// argument.
std::string input_filename_;
// Copied from driver_ for CARBON_VLOG.
@@ -675,6 +694,10 @@ class CompilationUnit {
std::optional<Lex::TokenizedBuffer> tokens_;
std::optional<Parse::Tree> parse_tree_;
std::optional<Parse::TreeAndSubtrees> parse_tree_and_subtrees_;
std::optional<std::function<const Parse::TreeAndSubtrees&()>>
get_parse_tree_and_subtrees_;
std::optional<Parse::NodeLocConverter> node_converter_;
std::optional<Check::SemIRDiagnosticConverter> sem_ir_converter_;
std::optional<SemIR::File> sem_ir_;
std::unique_ptr<llvm::LLVMContext> llvm_context_;
std::unique_ptr<llvm::Module> module_;
@@ -779,22 +802,30 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
return make_result();
}
// Check.
SharedValueStores builtin_value_stores;
llvm::SmallVector<Check::Unit> check_units;
// Pre-check assigns IR IDs and constructs node converters.
llvm::SmallVector<Parse::NodeLocConverter*> node_converters;
// This size may not match due to units that are missing source, but that's an
// error case and not worth extra work.
node_converters.reserve(units.size());
for (auto& unit : units) {
if (unit->has_source()) {
check_units.push_back(unit->GetCheckUnit());
node_converters.push_back(&unit->PreCheck());
}
}
llvm::SmallVector<Parse::NodeLocConverter> node_converters;
node_converters.reserve(check_units.size());
for (auto& unit : check_units) {
node_converters.emplace_back(unit.tokens, unit.tokens->source().filename(),
unit.get_parse_tree_and_subtrees);
// Gather Check::Units.
llvm::SmallVector<Check::Unit> check_units;
check_units.reserve(node_converters.size());
for (auto& unit : units) {
if (unit->has_source()) {
SemIR::CheckIRId check_ir_id(check_units.size());
check_units.push_back(unit->GetCheckUnit(check_ir_id, node_converters));
}
}
// Execute the actual checking.
CARBON_VLOG_TO(driver_env.vlog_stream, "*** Check::CheckParseTrees ***\n");
Check::CheckParseTrees(check_units, node_converters, options_.prelude_import,
Check::CheckParseTrees(check_units, options_.prelude_import,
driver_env.vlog_stream);
CARBON_VLOG_TO(driver_env.vlog_stream,
"*** Check::CheckParseTrees done ***\n");
@@ -816,9 +847,7 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
// Lower.
for (const auto& unit : units) {
Check::SemIRDiagnosticConverter converter(node_converters,
&**unit->GetCheckUnit().sem_ir);
unit->RunLower(converter);
unit->RunLower();
}
if (options_.phase == CompileOptions::Phase::Lower) {
return make_result();