mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:40:11 +01:00
203 lines
7.5 KiB
C++
203 lines
7.5 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
|
|
|
|
#ifndef CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_
|
|
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "common/map.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "toolchain/check/check.h"
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/diagnostic_emitter.h"
|
|
#include "toolchain/parse/node_ids.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
struct UnitAndImports;
|
|
|
|
// A file's imports corresponding to a single package, for
|
|
// `UnitAndImports::package_imports`.
|
|
struct PackageImports {
|
|
// A given import within the file, with its destination.
|
|
struct Import {
|
|
Parse::Tree::PackagingNames names;
|
|
UnitAndImports* unit_info;
|
|
};
|
|
|
|
// Use the constructor so that the SmallVector is only constructed
|
|
// as-needed.
|
|
explicit PackageImports(PackageNameId package_id,
|
|
Parse::AnyPackagingDeclId node_id)
|
|
: package_id(package_id), node_id(node_id) {}
|
|
|
|
// The identifier of the imported package.
|
|
PackageNameId package_id;
|
|
// The first `package` or `import` declaration in the file, which declared the
|
|
// package's identifier (even if the import failed). Used for associating
|
|
// diagnostics not specific to a single import.
|
|
Parse::AnyPackagingDeclId node_id;
|
|
// The associated `import` instruction. Has a value after a file is checked.
|
|
SemIR::InstId import_decl_id = SemIR::InstId::None;
|
|
// Whether there's an import that failed to load.
|
|
bool has_load_error = false;
|
|
// The list of valid imports.
|
|
llvm::SmallVector<Import> imports;
|
|
};
|
|
|
|
// Contains information accumulated while checking a `Unit` (primarily import
|
|
// information), in addition to the `Unit` itself.
|
|
struct UnitAndImports {
|
|
// Converts a `NodeId` to a diagnostic location for `UnitAndImports`.
|
|
class NodeEmitter : public Diagnostics::Emitter<Parse::NodeId> {
|
|
public:
|
|
explicit NodeEmitter(Diagnostics::Consumer* consumer,
|
|
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter)
|
|
: Emitter(consumer),
|
|
tree_and_subtrees_getter_(tree_and_subtrees_getter) {}
|
|
|
|
protected:
|
|
auto ConvertLoc(Parse::NodeId node_id, ContextFnT /*context_fn*/) const
|
|
-> Diagnostics::ConvertedLoc override {
|
|
return tree_and_subtrees_getter_().NodeToDiagnosticLoc(
|
|
node_id, /*token_only=*/false);
|
|
}
|
|
|
|
private:
|
|
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_;
|
|
};
|
|
|
|
explicit UnitAndImports(Unit* unit,
|
|
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter)
|
|
: unit(unit),
|
|
err_tracker(*unit->consumer),
|
|
emitter(&err_tracker, tree_and_subtrees_getter) {}
|
|
|
|
auto parse_tree() -> const Parse::Tree& { return unit->sem_ir->parse_tree(); }
|
|
auto source() -> const SourceBuffer& {
|
|
return parse_tree().tokens().source();
|
|
}
|
|
|
|
Unit* unit;
|
|
|
|
// Emitter information.
|
|
Diagnostics::ErrorTrackingConsumer err_tracker;
|
|
NodeEmitter emitter;
|
|
|
|
// List of the outgoing imports. If a package includes unavailable library
|
|
// imports, it has an entry with has_load_error set. Invalid imports (for
|
|
// example, `import Main;`) aren't added because they won't add identifiers to
|
|
// name lookup.
|
|
llvm::SmallVector<PackageImports> package_imports;
|
|
|
|
// A map of the package names to the outgoing imports above.
|
|
Map<PackageNameId, int32_t> package_imports_map;
|
|
|
|
// List of the `import Cpp` imports.
|
|
llvm::SmallVector<Parse::Tree::PackagingNames> cpp_imports;
|
|
|
|
// The remaining number of imports which must be checked before this unit can
|
|
// be processed.
|
|
int32_t imports_remaining = 0;
|
|
|
|
// A list of incoming imports. This will be empty for `impl` files, because
|
|
// imports only touch `api` files.
|
|
llvm::SmallVector<UnitAndImports*> incoming_imports;
|
|
|
|
// The corresponding `api` unit if this is an `impl` file. The entry should
|
|
// also be in the corresponding `PackageImports`.
|
|
UnitAndImports* api_for_impl = nullptr;
|
|
|
|
// Whether the unit has been checked.
|
|
bool is_checked = false;
|
|
};
|
|
|
|
// Handles checking of a single unit. Requires that all dependencies have been
|
|
// checked.
|
|
//
|
|
// This mainly splits out the single-unit logic from the higher level cross-unit
|
|
// logic in check.cpp.
|
|
class CheckUnit {
|
|
public:
|
|
// `unit_and_imports` and `tree_and_subtrees_getters` must be non-null.
|
|
// `vlog_stream` is optional.
|
|
explicit CheckUnit(
|
|
UnitAndImports* unit_and_imports,
|
|
const Parse::GetTreeAndSubtreesStore* tree_and_subtrees_getters,
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
|
llvm::LLVMContext* llvm_context,
|
|
std::shared_ptr<clang::CompilerInvocation> clang_invocation,
|
|
llvm::raw_ostream* vlog_stream);
|
|
|
|
// Produces and checks the IR for the provided unit.
|
|
auto Run() -> void;
|
|
|
|
private:
|
|
using CheckIRIdToIntStore = FixedSizeValueStore<SemIR::CheckIRId, int>;
|
|
|
|
// Add imports to the root block.
|
|
auto InitPackageScopeAndImports() -> void;
|
|
|
|
// Collects direct imports, for CollectTransitiveImports.
|
|
auto CollectDirectImports(llvm::SmallVector<SemIR::ImportIR>& results,
|
|
CheckIRIdToIntStore& ir_to_result_index,
|
|
SemIR::InstId import_decl_id,
|
|
const PackageImports& imports, bool is_local)
|
|
-> void;
|
|
|
|
// Collects transitive imports, handling deduplication. These will be unified
|
|
// between local_imports and api_imports.
|
|
auto CollectTransitiveImports(SemIR::InstId import_decl_id,
|
|
const PackageImports* local_imports,
|
|
const PackageImports* api_imports)
|
|
-> llvm::SmallVector<SemIR::ImportIR>;
|
|
|
|
// Imports the current package.
|
|
auto ImportCurrentPackage(SemIR::InstId package_inst_id,
|
|
SemIR::TypeId namespace_type_id) -> void;
|
|
|
|
// Imports all other Carbon packages (excluding the current package).
|
|
auto ImportOtherPackages(SemIR::TypeId namespace_type_id) -> void;
|
|
|
|
// Checks that each required declaration is available. This applies for
|
|
// declarations that should exist in an owning library, for which an extern
|
|
// declaration exists that assigns ownership to the current API.
|
|
auto CheckRequiredDeclarations() -> void;
|
|
|
|
// Checks that each required definition is available. If the definition can be
|
|
// generated by resolving a specific, does so, otherwise emits a diagnostic
|
|
// for each declaration in context.definitions_required_by_decl() and
|
|
// context.definitions_required_by_use that doesn't have a definition.
|
|
auto CheckRequiredDefinitions() -> void;
|
|
|
|
// Re-run every impl lookup with a concrete result and make sure they find the
|
|
// same witnesses.
|
|
auto CheckPoisonedConcreteImplLookupQueries() -> void;
|
|
|
|
// Look for `impl` declarations that are invalid.
|
|
auto CheckImpls() -> void;
|
|
|
|
// Does work after processing the parse tree, such as finishing the IR and
|
|
// checking for missing contents.
|
|
auto FinishRun() -> void;
|
|
|
|
// Loops over all nodes in the tree. On some errors, this may return early,
|
|
// for example if an unrecoverable state is encountered.
|
|
auto ProcessNodeIds() -> bool;
|
|
|
|
UnitAndImports* unit_and_imports_;
|
|
Parse::GetTreeAndSubtreesFn tree_and_subtrees_getter_;
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs_;
|
|
llvm::LLVMContext* llvm_context_;
|
|
std::shared_ptr<clang::CompilerInvocation> clang_invocation_;
|
|
|
|
DiagnosticEmitter emitter_;
|
|
Context context_;
|
|
};
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_CHECK_UNIT_H_
|