diff --git a/Cargo.lock b/Cargo.lock index 64bd75c8..4076ae9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,7 +561,6 @@ dependencies = [ "diagnostics", "documenter", "hir_format", - "index_map", "lexer", "lo_ast", "lowerer", diff --git a/compiler/documenter/src/format.rs b/compiler/documenter/src/format.rs index af3adbfd..f20b9312 100644 --- a/compiler/documenter/src/format.rs +++ b/compiler/documenter/src/format.rs @@ -3,7 +3,7 @@ use super::{ node::{Attributable, Element}, }; use hir::DeclarationIndex; -use hir_format::{ComponentExt, Display, SessionExt}; +use hir_format::{Display, SessionExt as _}; use joinery::JoinableIterator; use session::Session; use std::fmt::Write; @@ -257,10 +257,8 @@ impl<'a> Formatter<'a> { } fn module_url_fragment(&self, index: DeclarationIndex) -> String { - let component = self.session.component_of(index); - - let mut segments = component.local_index_to_path_segments(index.local_unchecked()); - segments.push_front(component.name().into_inner()); + let mut segments = self.session.index_to_path_segments(index); + segments.push_front(self.session.component_of(index).name().into_inner()); format!( "{}{}/index.html", diff --git a/compiler/documenter/src/lib.rs b/compiler/documenter/src/lib.rs index d7ee9b51..bc6b6c69 100644 --- a/compiler/documenter/src/lib.rs +++ b/compiler/documenter/src/lib.rs @@ -11,12 +11,11 @@ use crossbeam::thread::Scope; use derivation::Elements; use diagnostics::error::Result; use hir::{Attribute, AttributeName, Attributes, BareAttribute}; -use hir_format::ComponentExt; +use hir_format::SessionExt; use joinery::JoinableIterator; use lexer::word::Word; use node::{Attributable, Document, Element, Node, VoidElement}; use session::{ - component::{DeclarationIndexExt, IdentifierExt}, package::{ManifestPath, Package}, Context, Session, OUTPUT_FOLDER_NAME, }; @@ -203,11 +202,7 @@ impl<'a, 'scope> Documenter<'a, 'scope> { // incorrect on top of that!) let path = self .session - .component() - .local_index_with_root_to_extern_path( - index.local(self.session).unwrap(), - component_name.to_owned(), - ); + .index_with_root_to_extern_path(index, component_name.to_owned()); write!( search_index, @@ -534,10 +529,10 @@ impl<'a, 'scope> Documenter<'a, 'scope> { } fn add_module_page(&self, module: &hir::Module, attributes: &Attributes) -> Page { - let index = module.binder.local_declaration_index(self.session).unwrap(); + let index = module.binder.declaration_index().unwrap(); let component_name = self.session.component().name(); - let mut segments = self.session.component().local_index_to_path_segments(index); + let mut segments = self.session.index_to_path_segments(index); segments.push_front(component_name.into_inner()); let page_depth = segments.len(); let url_prefix = format!("./{}", "../".repeat(page_depth)); @@ -571,7 +566,7 @@ impl<'a, 'scope> Documenter<'a, 'scope> { { let mut heading = Element::new("h1"); - heading.add_child(match index == self.session.component().root_local() { + heading.add_child(match index.is_root() { true => "Component", false => "Module", }); @@ -613,8 +608,7 @@ impl<'a, 'scope> Documenter<'a, 'scope> { let title = self .session - .component() - .local_index_with_root_to_extern_path(index, component_name.to_string()); + .index_with_root_to_extern_path(index, component_name.to_string()); Page { path: segments diff --git a/compiler/driver/Cargo.toml b/compiler/driver/Cargo.toml index 48634beb..6c9db7eb 100644 --- a/compiler/driver/Cargo.toml +++ b/compiler/driver/Cargo.toml @@ -19,7 +19,6 @@ derivation = { path = "../../project/library/derivation" } diagnostics = { path = "../diagnostics" } documenter = { path = "../documenter" } hir_format = { path = "../hir_format" } -index_map = { path = "../../project/library/index_map" } lexer = { path = "../lexer" } lo_ast = { path = "../lo_ast" } lowerer = { path = "../lowerer" } diff --git a/compiler/driver/src/cli.rs b/compiler/driver/src/cli.rs index 9af1e055..af3fd3c0 100644 --- a/compiler/driver/src/cli.rs +++ b/compiler/driver/src/cli.rs @@ -14,10 +14,6 @@ use utility::{ paint::{paint_to_string, AnsiColor, ColorChoice}, }; -// @Task update the color scheme of the `-Zhelp` output from clap-3-style to clap-4-style -// i.e. from upper-case section titles and the use of yellow & green to -// bold & underlined section titles and the use of bold letters - pub(crate) fn arguments() -> Result<(Command, GlobalOptions)> { let short_version = format!( "{} ({} {})", @@ -134,7 +130,7 @@ pub(crate) fn arguments() -> Result<(Command, GlobalOptions)> { .action(ArgAction::Append) .help("Set an unstable option. See ‘-Z help’ for details"); - // @Task use `try_get_matches` (no real block, just def an error type and smh exit with code 2 instead of 1 on error) + // @Task use `try_get_matches` (just define an error type and smh exit with code 2 instead of 1 on error) let matches = clap::Command::new("lushui") .bin_name("lushui") .version(short_version) @@ -238,6 +234,8 @@ pub(crate) fn arguments() -> Result<(Command, GlobalOptions)> { .required(true) .help("The path to the Recnot file"), ), + clap::Command::new(subcommand::TREE) + .about("Display a tree visualization of a dependency graph"), ]) .get_matches(); @@ -341,6 +339,7 @@ pub(crate) fn arguments() -> Result<(Command, GlobalOptions)> { (subcommand::RECNOT, matches) => Command::Recnot { path: matches.get_one(argument::PATH).cloned().unwrap(), }, + (subcommand::TREE, _matches) => Command::Tree, _ => unreachable!(), }; @@ -359,6 +358,7 @@ mod subcommand { pub(super) const RUN: &str = "run"; #[cfg(feature = "lsp")] pub(super) const SERVE: &str = "serve"; + pub(super) const TREE: &str = "tree"; } mod argument { @@ -396,16 +396,17 @@ pub(crate) enum Command { mode: BuildMode, options: FileBuildOptions, }, - #[cfg(feature = "lsp")] - Serve, - Explain, CreatePackage { mode: PackageCreationMode, options: PackageCreationOptions, }, + Explain, Recnot { path: PathBuf, }, + #[cfg(feature = "lsp")] + Serve, + Tree, } pub(crate) struct GlobalOptions { diff --git a/compiler/driver/src/lib.rs b/compiler/driver/src/lib.rs index 80a48156..11d56533 100644 --- a/compiler/driver/src/lib.rs +++ b/compiler/driver/src/lib.rs @@ -10,15 +10,10 @@ use ast::Render; use cli::{Backend, BuildMode, Command, PassRestriction}; use diagnostics::{error::Result, reporter::ErasedReportedError, Diagnostic, ErrorCode, Reporter}; use hir_format::Display as _; -use index_map::IndexMap; use lo_ast::Display as _; use package::{find_package, resolve_file, resolve_package}; use resolver::ProgramEntryExt; -use session::{ - package::ManifestPath, - unit::{BuildUnit, ComponentType}, - Context, Session, -}; +use session::{package::ManifestPath, unit::ComponentType, Context, Session}; use span::SourceMap; use std::{ borrow::Cow, @@ -32,7 +27,7 @@ use std::{ use utility::{ default, displayed, paint::{epaint, paint, AnsiColor, ColorChoice}, - ComponentIndex, FormatError, PROGRAM_ENTRY, + FormatError, PROGRAM_ENTRY, }; mod cli; @@ -78,15 +73,13 @@ fn execute_command( map: &Arc>, reporter: Reporter, ) -> Result { - use Command::*; - match command { - BuildPackage { mode, options } => { - let (components, mut context) = match &options.path { + Command::BuildPackage { mode, options } => { + let mut context = match &options.path { Some(path) => { - // intentionally not `!path.is_dir()` to exclude broken symlinks + // Intentionally not `!path.is_dir()` to exclude broken symlinks. if path.is_file() { - // give a more useful diagnostic than the generic "could not load" one + // Give a more useful diagnostic than the generic "could not load" one. return Err(Diagnostic::error() .path(path.clone()) .message("the path does not refer to a folder") @@ -106,7 +99,7 @@ fn execute_command( let current_folder_path = match std::env::current_dir() { Ok(path) => path, Err(error) => { - // @Task improve message + // FIXME: Improve this diagnostic. return Err(Diagnostic::error() .message("could not read the current folder") .note(error.format()) @@ -129,18 +122,12 @@ fn execute_command( } }; - build_units( - components, - &mode, - &options.general, - global_options, - &mut context, - ) + build_components(&mode, &options.general, global_options, &mut context) } - BuildFile { mode, options } => { - // intentionally not `!path.is_file()` to exclude broken symlinks + Command::BuildFile { mode, options } => { + // Intentionally not `!path.is_file()` to exclude broken symlinks. if options.path.is_dir() { - // give a more useful diagnostic than the generic "could not load" one + // Give a more useful diagnostic than the generic “could not load” one. return Err(Diagnostic::error() .path(options.path.clone()) .message("the path does not refer to a file") @@ -154,7 +141,7 @@ fn execute_command( .report(&reporter)); } - let (components, mut context) = resolve_file( + let mut context = resolve_file( &options.path, None, options.component_type.unwrap_or(ComponentType::Executable), @@ -163,16 +150,49 @@ fn execute_command( reporter, )?; - build_units( - components, - &mode, - &options.general, - global_options, - &mut context, - ) + build_components(&mode, &options.general, global_options, &mut context) + } + // FIXME: Support package paths. + // FIXME: Add filter options + // FIXME: Use `https://lib.rs/crates/termtree` + Command::Tree => { + let current_folder_path = match std::env::current_dir() { + Ok(path) => path, + Err(error) => { + // FIXME: Improve this diagnostic. + return Err(Diagnostic::error() + .message("could not read the current folder") + .note(error.format()) + .report(&reporter)); + } + }; + + let Some(path) = find_package(¤t_folder_path) else { + return Err(Diagnostic::error() + .message("neither the current folder nor any of its parents is a package") + .note(format!( + "none of the folders contain a package manifest file named ‘{}’", + ManifestPath::FILE_NAME, + )) + .report(&reporter)); + }; + let _context = resolve_package(path, &default(), map, reporter)?; + + // let index = context.root_component().index; + // let unit = &units[index]; + // eprintln!( + // "index={:?} name={:?} path={:?}", + // index, unit.name, unit.path + // ); + + // for dependency in &unit.dependencies { + // eprintln!(">> {dependency:?}"); + // } + + Ok(()) } #[cfg(feature = "lsp")] - Serve => { + Command::Serve => { tokio::runtime::Builder::new_multi_thread() .enable_all() .build() @@ -180,10 +200,10 @@ fn execute_command( .block_on(server::serve(map.clone())); Ok(()) } - Explain => Err(Diagnostic::error() + Command::Explain => Err(Diagnostic::error() .message("the subcommand ‘explain’ is not implemented yet") .report(&reporter)), - CreatePackage { mode, options } => match mode { + Command::CreatePackage { mode, options } => match mode { cli::PackageCreationMode::Initialize => Err(Diagnostic::error() .message("the subcommand ‘initialize’ is not implemented yet") .report(&reporter)), @@ -191,21 +211,19 @@ fn execute_command( create::create_package(package_name, &options, &reporter) } }, - Recnot { path } => check_recnot_file(&path, map, &reporter), + Command::Recnot { path } => check_recnot_file(&path, map, &reporter), } } -fn build_units( - units: IndexMap, +fn build_components( mode: &cli::BuildMode, options: &cli::BuildOptions, global_options: &cli::GlobalOptions, context: &mut Context, ) -> Result { - for unit in units.into_values() { - context.at(unit, |unit, session| { - build_unit(unit, mode, options, global_options, session) - })?; + for component in context.components().keys() { + let mut session = Session::new(component, context); + build_component(mode, options, global_options, &mut session)?; } if let BuildMode::Document { options } = mode @@ -224,8 +242,7 @@ fn build_units( } #[allow(clippy::needless_pass_by_value)] // by design -fn build_unit( - unit: BuildUnit, +fn build_component( mode: &cli::BuildMode, options: &cli::BuildOptions, global_options: &cli::GlobalOptions, @@ -234,19 +251,25 @@ fn build_unit( // @Task abstract over this as print_status_report and report w/ label="Running" for // root component if mode==Run (in addition to initial "Building") if !global_options.quiet { + let label = match mode { + BuildMode::Check => "Checking", + BuildMode::Compile { .. } | BuildMode::Run { .. } => "Building", + // FIXME: This shouldn't be printed for non-root components and `--no-deps`. + BuildMode::Document { .. } => "Documenting", + }; + paint( |stdout| { - let label = match mode { - BuildMode::Check => "Checking", - BuildMode::Compile { .. } | BuildMode::Run { .. } => "Building", - // FIXME: This shouldn't be printed for non-root components and `--no-deps`. - BuildMode::Document { .. } => "Documenting", - }; stdout.set(AnsiColor::Green.on_default().bold())?; write!(stdout, " {label} ")?; stdout.unset()?; - writeln!(stdout, "{} ({})", unit.name, unit.path.bare.display()) + writeln!( + stdout, + "{} ({})", + session.component().name, + session.component().path.bare.display() + ) }, global_options.color, ) @@ -254,7 +277,8 @@ fn build_unit( } macro restriction_point($restriction:ident) { - if unit.is_root(&session) && options.pass_restriction == Some(PassRestriction::$restriction) + if session.is_root_component() + && options.pass_restriction == Some(PassRestriction::$restriction) { return Ok(()); } @@ -274,20 +298,21 @@ fn build_unit( eprintln!("Execution times by pass:"); } - let path = unit.path.as_deref(); + let path = session.component().path.as_deref(); // @Task don't unconditionally halt execution on failure here but continue (with tainted health) // and mark the component as "erroneous" (not yet implemented) so we can print more errors. // "Erroneous" components should not lead to further errors in the name resolver etc. let file = session .map() - .load(path.bare, Some(unit.index)) + .load(path.bare, Some(session.component().index)) .map_err(|error| { use std::fmt::Write; let mut message = format!( "could not load the {} component ‘{}’", - unit.type_, unit.name, + session.component().type_, + session.component().name, ); if let Some(package) = session.package() { write!(message, " in package ‘{}’", session[package].name).unwrap(); @@ -308,7 +333,7 @@ fn build_unit( let tokens = syntax::lex(file, session); }; - if unit.is_root(session) && options.emit_tokens { + if session.is_root_component() && options.emit_tokens { for token in &tokens.tokens { eprintln!("{token:?}"); } @@ -322,7 +347,7 @@ fn build_unit( let component_root = syntax::parse_root_module_file(tokens, file, session)?; } - if unit.is_root(session) && options.emit_ast { + if session.is_root_component() && options.emit_ast { epaint( |painter| component_root.render(default(), painter), global_options.color, @@ -335,7 +360,7 @@ fn build_unit( // @Task get rid of this! move into session / component of root package! let lowering_options = lowerer::Options { - internal_features_enabled: options.internals || unit.is_core_library(session), + internal_features_enabled: options.internals || session.is_core_library(), keep_documentation_comments: matches!(mode, BuildMode::Document { .. }), }; @@ -346,7 +371,7 @@ fn build_unit( lowerer::lower_file(component_root, lowering_options, session)?; } - if unit.is_root(session) && options.emit_lo_ast { + if session.is_root_component() && options.emit_lo_ast { eprintln!("{}", displayed(|f| component_root.write(f))); } @@ -359,10 +384,10 @@ fn build_unit( resolver::resolve_declarations(component_root, session)?; } - if unit.is_root(session) && options.emit_hir { + if session.is_root_component() && options.emit_hir { eprintln!("{}", displayed(|f| component_root.write(session, f))); } - if unit.is_root(session) && options.emit_untyped_bindings { + if session.is_root_component() && options.emit_untyped_bindings { eprintln!("{}", displayed(|f| session.component().write(session, f))); } @@ -374,18 +399,20 @@ fn build_unit( typer::check(&component_root, session)?; } - if unit.is_root(session) && options.emit_bindings { + if session.is_root_component() && options.emit_bindings { eprintln!("{}", displayed(|f| session.component().write(session, f))); } // @Task move out of main.rs // @Update @Task move into respective backends: LLVM and interpreter! - if unit.type_ == ComponentType::Executable && session.look_up_program_entry().is_none() { + if session.component().type_ == ComponentType::Executable + && session.look_up_program_entry().is_none() + { return Err(Diagnostic::error() .code(ErrorCode::E050) .message(format!( "the component ‘{}’ does not contain a ‘{PROGRAM_ENTRY}’ function in its root module", - unit.name + session.component().name )) .unlabeled_span(&session.shared_map()[file]) .report(session.reporter())); @@ -393,8 +420,8 @@ fn build_unit( match &mode { BuildMode::Run { options } => { - if unit.is_root(session) { - if unit.type_ != ComponentType::Executable { + if session.is_root_component() { + if session.component().type_ != ComponentType::Executable { // @Question code? // @Note I don't like this code here at all, it's hacky and not principled! // @Question why should the message differ?? @@ -442,7 +469,7 @@ fn build_unit( } #[cfg(feature = "cranelift")] Backend::Cranelift => { - if !unit.is_root(session) { + if !session.is_root_component() { return Err(Diagnostic::error() .message("extern components cannot be built yet with the Cranelift backend") .report(session.reporter())); @@ -459,7 +486,7 @@ fn build_unit( } #[cfg(feature = "llvm")] Backend::Llvm => { - if !unit.is_root(session) { + if !session.is_root_component() { return Err(Diagnostic::error() .message("extern components cannot be built yet with the LLVM backend") .report(session.reporter())); @@ -477,7 +504,7 @@ fn build_unit( }, BuildMode::Document { options } => { // @Bug leads to broken links, @Task the documenter has to handle this itself - if options.no_dependencies && !unit.is_root(session) { + if options.no_dependencies && !session.is_root_component() { return Ok(()); } diff --git a/compiler/hir/src/identifier.rs b/compiler/hir/src/identifier.rs index 72ec943d..4fda446c 100644 --- a/compiler/hir/src/identifier.rs +++ b/compiler/hir/src/identifier.rs @@ -149,6 +149,8 @@ impl fmt::Debug for Index { pub struct DeclarationIndex(u64); impl DeclarationIndex { + pub const ROOT: Self = Self(0); + pub fn new(component_index: ComponentIndex, local_index: LocalDeclarationIndex) -> Self { Self((u64::from(component_index.0) << LocalDeclarationIndex::BIT_WIDTH) | local_index.0) } @@ -161,6 +163,10 @@ impl DeclarationIndex { pub fn local_unchecked(self) -> LocalDeclarationIndex { LocalDeclarationIndex(self.0 & LocalDeclarationIndex::MAX) } + + pub fn is_root(self) -> bool { + self == Self::ROOT + } } impl fmt::Debug for DeclarationIndex { @@ -183,11 +189,17 @@ impl LocalDeclarationIndex { const BIT_WIDTH: u32 = 48; const MAX: u64 = 2_u64.pow(Self::BIT_WIDTH) - 1; - pub fn new(index: u64) -> Self { + pub const ROOT: Self = Self(0); + + pub const fn new(index: u64) -> Self { assert!(index < Self::MAX); Self(index) } + + pub fn is_root(self) -> bool { + self == Self::ROOT + } } impl fmt::Debug for LocalDeclarationIndex { diff --git a/compiler/hir_format/src/lib.rs b/compiler/hir_format/src/lib.rs index d866870c..ecdb63f2 100644 --- a/compiler/hir_format/src/lib.rs +++ b/compiler/hir_format/src/lib.rs @@ -4,7 +4,7 @@ use joinery::JoinableIterator; use lexer::{token::INDENTATION, CharExt}; use session::{ - component::{Component, DeclarationIndexExt, LocalDeclarationIndexExt}, + component::{ComponentMetadata, DeclarationIndexExt, LocalDeclarationIndexExt}, Session, }; use std::{collections::VecDeque, fmt}; @@ -466,6 +466,23 @@ pub trait SessionExt { fn index_to_path(&self, index: hir::DeclarationIndex) -> String; fn local_index_to_path(&self, index: hir::LocalDeclarationIndex) -> String; + + fn index_with_root_to_path(&self, index: hir::DeclarationIndex, root: String) -> String; + + /// XXX The textual representation of the path to the given binding relative to a component root + /// prefixed with name of the corresponding component. + /// + /// Rephrased, it returns a path that could be used in any dependent components (reverse dependencies) + /// to refer to the binding ignoring exposure as long as one would prepend the path hanger `extern`. + /// + /// # Example Output + /// + /// * `core.nat.Nat` (`core` referring to a component) + /// * `json.parse` (`json` referring to a component) + fn index_with_root_to_extern_path(&self, index: hir::DeclarationIndex, root: String) -> String; + + // @Task add documentation + fn index_to_path_segments(&self, index: hir::DeclarationIndex) -> VecDeque; } impl SessionExt for Session<'_> { @@ -479,75 +496,39 @@ impl SessionExt for Session<'_> { } fn index_to_path(&self, index: hir::DeclarationIndex) -> String { - let root = ast::BareHanger::Topmost.name().to_owned(); - - self.component().index_with_root_to_path(index, root, self) + self.index_with_root_to_path(index, ast::BareHanger::Topmost.name().to_owned()) } // @Question can we rewrite this function to not rely on Session? fn local_index_to_path(&self, index: hir::LocalDeclarationIndex) -> String { self.index_to_path(index.global(self)) } -} - -pub trait ComponentExt { - fn index_with_root_to_path( - &self, - index: hir::DeclarationIndex, - root: String, - session: &Session<'_>, - ) -> String; - /// The textual representation of the path to the given binding relative to a component root - /// prefixed with name of the corresponding component. - /// - /// Rephrased, it returns a path that could be used in any dependent components (reverse dependencies) - /// to refer to the binding ignoring exposure as long as one would prepend the path hanger `extern`. - /// - /// # Example Output - /// - /// * `core.nat.Nat` (`core` referring to a component) - /// * `json.parse` (`json` referring to a component) - fn local_index_with_root_to_extern_path( - &self, - index: hir::LocalDeclarationIndex, - root: String, - ) -> String; - - // @Task add documentation - fn local_index_to_path_segments(&self, index: hir::LocalDeclarationIndex) -> VecDeque; -} - -impl ComponentExt for Component { - fn index_with_root_to_path( - &self, - index: hir::DeclarationIndex, - root: String, - session: &Session<'_>, - ) -> String { - match index.local(self) { - Some(index) => self.local_index_with_root_to_extern_path(index, root), - None => { - let component = &session.look_up_component(index.component()); - let root = format!("{}.{}", ast::BareHanger::Extern.name(), component.name()); - - component.index_with_root_to_path(index, root, session) - } + fn index_with_root_to_path(&self, index: hir::DeclarationIndex, root: String) -> String { + if index.is_local(self) { + self.index_with_root_to_extern_path(index, root) + } else { + let component = index.component(); + let root = format!( + "{}.{}", + ast::BareHanger::Extern.name(), + self[component].name(), + ); + + self.index_with_root_to_path(index, root) } } - fn local_index_with_root_to_extern_path( - &self, - index: hir::LocalDeclarationIndex, - root: String, - ) -> String { + fn index_with_root_to_extern_path(&self, index: hir::DeclarationIndex, root: String) -> String { + // FIXME let entity = &self[index]; // @Task rewrite this recursive approach to an iterative one! let Some(parent) = entity.parent else { return root; }; - let mut parent_path = self.local_index_with_root_to_extern_path(parent, root); + let mut parent_path = + self.index_with_root_to_extern_path(parent.global(index.component()), root); let parent_is_symbol = parent_path.chars().next_back().unwrap().is_symbol(); @@ -565,32 +546,33 @@ impl ComponentExt for Component { parent_path } - fn local_index_to_path_segments( - &self, - mut index: hir::LocalDeclarationIndex, - ) -> VecDeque { + // FIXME: is this correct for non-local indices? + fn index_to_path_segments(&self, mut index: hir::DeclarationIndex) -> VecDeque { let mut segments = VecDeque::new(); + // FIXME while let Some(parent) = self[index].parent { + // FIXME segments.push_front(self[index].source.bare()); - index = parent; + index = parent.global(index.component()); } segments } } -impl Display for Component { - fn write(&self, session: &Session<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for ComponentMetadata { + fn write(&self, _session: &Session<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "{} ({:?})", self.name(), self.index())?; writeln!(f, " bindings:")?; - for (index, entity) in &self.bindings { - write!(f, " {index:?}: ")?; - entity.write(session, f)?; - writeln!(f)?; - } + // FIXME + // for (index, entity) in &self.bindings { + // write!(f, " {index:?}: ")?; + // entity.write(session, f)?; + // writeln!(f)?; + // } Ok(()) } diff --git a/compiler/hir_format/src/test.rs b/compiler/hir_format/src/test.rs index 8b791197..bb16bc2c 100644 --- a/compiler/hir_format/src/test.rs +++ b/compiler/hir_format/src/test.rs @@ -5,7 +5,7 @@ use hir::{ LocalDeclarationIndex, Number, Text, }; use session::{ - component::{Component, IdentifierExt, LocalDeclarationIndexExt}, + component::{ComponentMetadata, IdentifierExt, LocalDeclarationIndexExt}, Context, Session, }; use span::Span; @@ -35,10 +35,10 @@ fn assert_format(expected: &str, actual: &Expression, session: &Session<'_>) { } } -trait ComponentExt { +trait SessionExt { fn add(&mut self, name: &str, kind: EntityKind) -> Identifier; - fn add_below( + fn add_inside( &mut self, name: &str, kind: EntityKind, @@ -46,12 +46,12 @@ trait ComponentExt { ) -> Identifier; } -impl ComponentExt for Component { +impl SessionExt for Session<'_> { fn add(&mut self, name: &str, kind: EntityKind) -> Identifier { - self.add_below(name, kind, self.root_local()) + self.add_inside(name, kind, ComponentMetadata::ROOT) } - fn add_below( + fn add_inside( &mut self, name: &str, kind: EntityKind, @@ -65,8 +65,8 @@ impl ComponentExt for Component { attributes: default(), kind, }; - let index = self.bindings.insert(entity); - Identifier::new(index.global(self), identifier) + let index = self.context.bindings[self.component].insert(entity); + Identifier::new(index.global(&*self), identifier) } } @@ -79,20 +79,18 @@ fn parameter(name: &str) -> Identifier { #[test] fn pi_type_application_argument() { - let mut component = Component::mock(); - let array = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let array = session .add("Array", EntityKind::untyped_data_type()) .to_item(); - let int = component + let int = session .add("Int", EntityKind::untyped_data_type()) .to_item(); - let type_ = component + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "topmost.Array topmost.Int -> topmost.Type", &Expression::bare( @@ -117,14 +115,12 @@ fn pi_type_application_argument() { #[test] fn pi_type_named_parameter() { - let mut component = Component::mock(); - let array = component.add("Array", EntityKind::untyped_data_type()); - let int = component.add("Int", EntityKind::untyped_data_type()); - let container = component.add("Container", EntityKind::untyped_data_type()); - let alpha = parameter("alpha"); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let array = session.add("Array", EntityKind::untyped_data_type()); + let int = session.add("Int", EntityKind::untyped_data_type()); + let container = session.add("Container", EntityKind::untyped_data_type()); + let alpha = parameter("alpha"); assert_format( "For (alpha: topmost.Array topmost.Int) -> topmost.Container alpha", @@ -157,14 +153,12 @@ fn pi_type_named_parameter() { #[test] fn pi_type_implicit_parameter() { - let mut component = Component::mock(); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "For '(whatever: topmost.Type) -> topmost.Type", &Expression::bare( @@ -183,14 +177,12 @@ fn pi_type_implicit_parameter() { /// Compare with [`pi_type_two_curried_arguments`]. #[test] fn pi_type_higher_order_argument() { - let mut component = Component::mock(); - let int = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let int = session .add("Int", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "(topmost.Int -> topmost.Int) -> topmost.Int", &Expression::bare( @@ -217,20 +209,18 @@ fn pi_type_higher_order_argument() { /// Compare with [`pi_type_higher_order_argument`]. #[test] fn pi_type_two_curried_arguments() { - let mut component = Component::mock(); - let int = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let int = session .add("Int", EntityKind::untyped_data_type()) .to_item(); - let text = component + let text = session .add("Text", EntityKind::untyped_data_type()) .to_item(); - let type_ = component + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "topmost.Int -> topmost.Text -> topmost.Type", &Expression::bare( @@ -257,15 +247,13 @@ fn pi_type_two_curried_arguments() { /// Compare with [`lambda_pi_type_body`]. #[test] fn pi_type_lambda_domain() { - let mut component = Component::mock(); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); let x = parameter("x"); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "(for x => x) -> topmost.Type", &Expression::bare( @@ -292,15 +280,13 @@ fn pi_type_lambda_domain() { #[test] fn application_three_curried_arguments() { - let mut component = Component::mock(); - let beta = component.add("beta", EntityKind::UntypedFunction); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let beta = session.add("beta", EntityKind::UntypedFunction); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "alpha topmost.beta (gamma topmost.Type) 0", &Expression::bare( @@ -339,12 +325,10 @@ fn application_three_curried_arguments() { /// Compare with [`application_lambda_argument`]. #[test] fn application_lambda_last_argument() { - let mut component = Component::mock(); - let take = component.add("take", EntityKind::UntypedFunction); - let it = parameter("it"); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let take = session.add("take", EntityKind::UntypedFunction); + let it = parameter("it"); // we might want to format this special case as `topmost.take for it => it` in the future assert_format( @@ -374,12 +358,10 @@ fn application_lambda_last_argument() { /// Compare with [`application_lambda_last_argument`]. #[test] fn application_lambda_argument() { - let mut component = Component::mock(); - let take = component.add("take", EntityKind::UntypedFunction); - let it = parameter("it"); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let take = session.add("take", EntityKind::UntypedFunction); + let it = parameter("it"); assert_format( r#"topmost.take (for it => it) "who""#, @@ -414,15 +396,13 @@ fn application_lambda_argument() { #[test] fn application_implicit_argument() { - let mut component = Component::mock(); - let identity = component.add("identity", EntityKind::UntypedFunction); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let identity = session.add("identity", EntityKind::UntypedFunction); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( r"topmost.identity 'topmost.Type", &Expression::bare( @@ -439,12 +419,10 @@ fn application_implicit_argument() { #[test] fn application_complex_implicit_argument() { - let mut component = Component::mock(); - let identity = component.add("identity", EntityKind::UntypedFunction); - let text = component.add("Text", EntityKind::untyped_data_type()); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let identity = session.add("identity", EntityKind::UntypedFunction); + let text = session.add("Text", EntityKind::untyped_data_type()); assert_format( r"topmost.identity '(prepare topmost.Text)", @@ -470,7 +448,7 @@ fn application_complex_implicit_argument() { #[test] fn application_intrinsic_application_callee() { let mut context = Context::mock(); - let session = Session::new(Component::mock(), &mut context); + let session = Session::mock(&mut context); assert_format( "eta 10 omicron", @@ -494,11 +472,9 @@ fn application_intrinsic_application_callee() { #[test] fn lambda_body_type_annotation() { - let mut component = Component::mock(); - let output = component.add("Output", EntityKind::untyped_data_type()); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let output = session.add("Output", EntityKind::untyped_data_type()); assert_format( "for input: topmost.Output => 0", @@ -518,16 +494,14 @@ fn lambda_body_type_annotation() { #[test] fn lambda_parameter_type_annotation_body_type_annotation() { - let mut component = Component::mock(); - let input = component.add("Input", EntityKind::untyped_data_type()); - let output = component.add("Output", EntityKind::untyped_data_type()); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let input = session.add("Input", EntityKind::untyped_data_type()); + let output = session.add("Output", EntityKind::untyped_data_type()); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "for (input: topmost.Input): topmost.Output => topmost.Type", &Expression::bare( @@ -546,14 +520,12 @@ fn lambda_parameter_type_annotation_body_type_annotation() { #[test] fn lambda_implicit_parameter() { - let mut component = Component::mock(); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "for '(Input: topmost.Type) => topmost.Type", &Expression::bare( @@ -572,10 +544,9 @@ fn lambda_implicit_parameter() { #[test] fn lambda_implicit_unannotated_parameter() { - let a = parameter("a"); - let mut context = Context::mock(); - let session = Session::new(Component::mock(), &mut context); + let session = Session::mock(&mut context); + let a = parameter("a"); assert_format( "for 'A => for a => a", @@ -605,15 +576,13 @@ fn lambda_implicit_unannotated_parameter() { /// Compare with [`pi_type_lambda_domain`]. #[test] fn lambda_pi_type_body() { - let mut component = Component::mock(); - let type_ = component + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let type_ = session .add("Type", EntityKind::untyped_data_type()) .to_item(); let x = parameter("x"); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( r"for x => x -> topmost.Type", &Expression::bare( @@ -640,11 +609,9 @@ fn lambda_pi_type_body() { #[test] fn intrinsic_application_no_arguments() { - let mut component = Component::mock(); - let add = component.add("add", EntityKind::UntypedFunction); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let add = session.add("add", EntityKind::UntypedFunction); assert_format( "add", @@ -661,11 +628,9 @@ fn intrinsic_application_no_arguments() { #[test] fn intrinsic_application_two_arguments() { - let mut component = Component::mock(); - let add = component.add("add", EntityKind::UntypedFunction); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); + let mut session = Session::mock(&mut context); + let add = session.add("add", EntityKind::UntypedFunction); assert_format( "add (add 1 3000) 0", @@ -695,7 +660,7 @@ fn intrinsic_application_two_arguments() { #[test] fn attributes() { let mut context = Context::mock(); - let session = Session::new(Component::mock(), &mut context); + let session = Session::mock(&mut context); assert_format( "== @static @unsafe 3 @static (increment 1)", @@ -736,48 +701,50 @@ fn attributes() { #[test] fn path() { - let mut component = Component::mock(); - let overarching = component.add("overarching", EntityKind::module()); - let middle = component.add_below( + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let overarching = session.add("overarching", EntityKind::module()); + let middle = session.add_inside( "middle", EntityKind::module(), - overarching.local_declaration_index(&component).unwrap(), + overarching + .local_declaration_index(session.component()) + .unwrap(), ); - let sink = component.add_below( + let sink = session.add_inside( "sink", EntityKind::UntypedFunction, - middle.local_declaration_index(&component).unwrap(), + middle.local_declaration_index(session.component()).unwrap(), ); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format("topmost.overarching.middle.sink", &sink.to_item(), &session); } #[test] fn path_identifier_symbol_symbol_identifier_segments() { - let mut component = Component::mock(); - let overarching = component.add("overarching", EntityKind::module()); - let noisy = component.add_below( + let mut context = Context::mock(); + let mut session = Session::mock(&mut context); + let overarching = session.add("overarching", EntityKind::module()); + let noisy = session.add_inside( "&/.~##", EntityKind::module(), - overarching.local_declaration_index(&component).unwrap(), + overarching + .local_declaration_index(session.component()) + .unwrap(), ); - let zickzack = component.add_below( + let zickzack = session.add_inside( "^^^", EntityKind::module(), - noisy.local_declaration_index(&component).unwrap(), + noisy.local_declaration_index(session.component()).unwrap(), ); - let sink = component.add_below( + let sink = session.add_inside( "sink", EntityKind::UntypedFunction, - zickzack.local_declaration_index(&component).unwrap(), + zickzack + .local_declaration_index(session.component()) + .unwrap(), ); - let mut context = Context::mock(); - let session = Session::new(component, &mut context); - assert_format( "topmost.overarching.&/.~## . ^^^ .sink", &sink.to_item(), diff --git a/compiler/package/src/lib.rs b/compiler/package/src/lib.rs index 8c6158c1..20c5219e 100644 --- a/compiler/package/src/lib.rs +++ b/compiler/package/src/lib.rs @@ -11,8 +11,9 @@ use lexer::word::Word; use manifest::{DependencyDeclaration, DependencyProvider, PackageManifest, PackageProfile}; use recnot::Record; use session::{ + component::ComponentMetadata, package::{ManifestPath, Package, PossiblyUnresolvedComponent::*, CORE_PACKAGE_NAME}, - unit::{BuildUnit, ComponentType}, + unit::ComponentType, Context, }; use span::{SourceMap, Spanned, WeaklySpanned}; @@ -47,7 +48,7 @@ pub fn resolve_package( filter: &ComponentFilter, map: &Arc>, reporter: Reporter, -) -> Result<(IndexMap, Context)> { +) -> Result { let mut queue = BuildQueue::new(map, reporter); queue.resolve_package(path, filter)?; Ok(queue.finalize()) @@ -61,7 +62,7 @@ pub fn resolve_file( no_core: bool, map: &Arc>, reporter: Reporter, -) -> Result<(IndexMap, Context)> { +) -> Result { let mut queue = BuildQueue::new(map, reporter); queue.resolve_file(path, content, type_, no_core)?; Ok(queue.finalize()) @@ -86,9 +87,10 @@ impl PackageExt for Package { } } +// FIXME: Don't use any HashMaps here, they're unordered struct BuildQueue { /// The components which have not been built yet. - components: IndexMap, + components: IndexMap, packages: HashMap, /// The mapping from component to corresponding package. component_packages: HashMap, @@ -204,18 +206,18 @@ impl BuildQueue { } let component = self.components.insert_with(|index| { - BuildUnit { - name: name.bare, + ComponentMetadata::new( + name.bare, index, // @Beacon @Temporary new_unchecked @Bug its use is incorrect! @Task canonicalize (I guess?) - path: component.bare.path.as_ref().map(|relative_path| { + component.bare.path.as_ref().map(|relative_path| { CanonicalPathBuf::new_unchecked( manifest_path.folder().join(relative_path), ) }), - type_: type_.bare, + type_.bare, dependencies, - } + ) }); self.register_package_component(manifest_path, name.bare, component); @@ -310,14 +312,14 @@ impl BuildQueue { } self.components.insert_with(|index| { - BuildUnit { + ComponentMetadata::new( name, index, // @Task don't use bare - path: Spanned::bare(file_path), + Spanned::bare(file_path), type_, dependencies, - } + ) }); Ok(()) @@ -587,12 +589,14 @@ impl BuildQueue { let dependencies = self.resolve_dependencies(name, manifest_path, library.dependencies.as_ref())?; - let library = self.components.insert_with(|index| BuildUnit { - name, - index, - path: library_component_path, - type_: library.type_.bare, - dependencies, + let library = self.components.insert_with(|index| { + ComponentMetadata::new( + name, + index, + library_component_path, + library.type_.bare, + dependencies, + ) }); self.register_package_component(manifest_path, name, library); @@ -702,20 +706,24 @@ impl BuildQueue { } } - fn finalize(self) -> (IndexMap, Context) { + // FIXME: simplify + fn finalize(self) -> Context { // @Task Support the existence of more than one root component // dependending on a component filter provided by the user - let root = self.components.last().unwrap(); + let root = self + .components + .last() + .expect("failed to obtain root component"); + let root = root.outline(); - let context = Context::new( + Context::new( + self.components, self.packages, self.component_packages, - root.outline(), + root, &self.map, self.reporter, - ); - - (self.components, context) + ) } fn shared_map(&self) -> RwLockReadGuard<'_, SourceMap> { @@ -765,7 +773,7 @@ fn parse_component_name_from_file_path(path: &Path, reporter: &Reporter) -> Resu } impl Index for BuildQueue { - type Output = BuildUnit; + type Output = ComponentMetadata; fn index(&self, index: ComponentIndex) -> &Self::Output { &self.components[index] diff --git a/compiler/resolver/src/lib.rs b/compiler/resolver/src/lib.rs index b9b60c58..d97b26f4 100644 --- a/compiler/resolver/src/lib.rs +++ b/compiler/resolver/src/lib.rs @@ -24,11 +24,11 @@ use hir::{ AttributeName, Attributes, DeBruijnIndex, DeclarationIndex, Entity, EntityKind, Exposure, ExposureReach, Identifier, Index, LocalDeclarationIndex, PartiallyResolvedPath, }; -use hir_format::{Display, SessionExt}; +use hir_format::{Display, SessionExt as _}; use lexer::word::Word; use lo_ast::DeBruijnLevel; use session::{ - component::{Component, DeclarationIndexExt, IdentifierExt, LocalDeclarationIndexExt}, + component::{ComponentMetadata, DeclarationIndexExt, IdentifierExt, LocalDeclarationIndexExt}, Session, }; use span::{Span, Spanned, Spanning}; @@ -175,7 +175,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { Some(module), )?; - let binder = Identifier::new(index.global(self.session), function.binder); + let binder = Identifier::new(index.global(&*self.session), function.binder); if let Some(intrinsic) = declaration.attributes.get::<{ AttributeName::Intrinsic }>() @@ -207,7 +207,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { Some(module), )?; - let binder = Identifier::new(index.global(self.session), type_.binder); + let binder = Identifier::new(index.global(&*self.session), type_.binder); let known = declaration.attributes.get::<{ AttributeName::Known }>(); if let Some(known) = known @@ -293,7 +293,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { Some(namespace), )?; - let binder = Identifier::new(index.global(self.session), constructor.binder); + let binder = Identifier::new(index.global(&*self.session), constructor.binder); // @Task support `@(known name)` on constructors if let Some(known) = known @@ -376,28 +376,27 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { binding: EntityKind, namespace: Option, ) -> Result { - if let Some(namespace) = namespace { - if let Some(index) = self.session[namespace] + if let Some(namespace) = namespace + && let Some(index) = self.session[namespace] .namespace() .unwrap() .binders .iter() - .map(|&index| index.local(self.session).unwrap()) + .map(|&index| index.local(&*self.session).unwrap()) .find(|&index| self.session[index].source == binder) - { - let previous = &self.session.component().bindings[index].source; - - self.naming_conflicts - .entry(index) - .or_insert_with(|| smallvec![previous.span()]) - .push(binder.span()); - - // @Beacon @Bug that's not how new_unchecked - // is supposed to be used! get rid of the ERE here! it's a lie! - return Err(DefinitionError::ConflictingDefinition( - ErasedReportedError::new_unchecked(), - )); - } + { + let previous = &self.session[index].source; + + self.naming_conflicts + .entry(index) + .or_insert_with(|| smallvec![previous.span()]) + .push(binder.span()); + + // @Beacon @Bug that's not how new_unchecked + // is supposed to be used! get rid of the ERE here! it's a lie! + return Err(DefinitionError::ConflictingDefinition( + ErasedReportedError::new_unchecked(), + )); } let index = self.session.define(Entity { @@ -409,7 +408,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { }); if let Some(namespace) = namespace { - let index = index.global(self.session); + let index = index.global(&*self.session); self.session[namespace] .namespace_mut() .unwrap() @@ -543,9 +542,9 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { Some(module) => self .as_ref() .reobtain_resolved_identifier::(submodule.binder, module) - .local(self.session) + .local(&*self.session) .unwrap(), - None => self.session.component().root_local(), + None => ComponentMetadata::ROOT, }; let declarations = submodule @@ -564,7 +563,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { declaration.attributes, declaration.span, hir::Module { - binder: Identifier::new(index.global(self.session), submodule.binder), + binder: Identifier::new(index.global(&*self.session), submodule.binder), file: submodule.file, declarations, } @@ -593,7 +592,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { let mut partially_resolved_use_bindings = HashMap::default(); for (&index, item) in &self.partially_resolved_use_bindings { - let namespace = item.target.namespace.global(self.session); + let namespace = item.target.namespace.global(&*self.session); match self.as_ref().resolve_path::( &item.target.path, @@ -617,7 +616,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { PartiallyResolvedUseBinding { // unwrap: The binder should always be local since external components // always contain resolved bindings. - binder: binder.local(self.session).unwrap(), + binder: binder.local(&*self.session).unwrap(), target: item.target.clone(), }, ); @@ -655,10 +654,10 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { } fn resolve_exposure_reaches(&mut self) { - for (index, entity) in &self.session.component().bindings { + for (index, entity) in &self.session.context.bindings[self.session.component] { if let Exposure::Restricted(exposure) = &entity.exposure { // unwrap: root always has Exposure::Unrestricted, it won't reach this branch - let definition_site_namespace = entity.parent.unwrap().global(self.session); + let definition_site_namespace = entity.parent.unwrap().global(&*self.session); if let Err(error) = self .as_ref() @@ -675,9 +674,7 @@ impl<'sess, 'ctx> ResolverMut<'sess, 'ctx> { { let target = &self.session[target_index]; - if entity - .exposure - .compare(&target.exposure, self.session.component()) + if entity.exposure.compare(&target.exposure, self.session) == Some(Ordering::Greater) { let error = Diagnostic::error() @@ -1502,7 +1499,7 @@ impl<'a> Resolver<'a> { .into()); }; - let component = &self.session.look_up_component(component); + let component = &self.session[component]; let root = component.root(); return match &*path.segments { @@ -1649,7 +1646,7 @@ impl<'a> Resolver<'a> { definition_site_namespace.global(self.session), )?; - if !self.session.component().is_allowed_to_access( + if !self.session.is_allowed_to_access( origin_namespace, definition_site_namespace.global(self.session), reach, @@ -1715,7 +1712,6 @@ impl<'a> Resolver<'a> { let reach_is_ancestor = self .session - .component() .some_ancestor_equals(definition_site_namespace, reach); if !reach_is_ancestor { @@ -2164,7 +2160,7 @@ enum IdentifierUsage { Unqualified, } -trait ComponentExt { +trait SessionExt { fn some_ancestor_equals(&self, index: DeclarationIndex, namespace: DeclarationIndex) -> bool; /// Indicate if the definition-site namespace can be accessed from the given namespace. @@ -2179,8 +2175,7 @@ trait ComponentExt { } } -impl ComponentExt for Component { - // @Beacon @Question shouldn't `index` be a LocalDeclarationIndex? +impl SessionExt for Session<'_> { fn some_ancestor_equals( &self, mut index: DeclarationIndex, @@ -2202,11 +2197,11 @@ impl ComponentExt for Component { // @Task better name trait ExposureCompare { - fn compare(&self, other: &Self, component: &Component) -> Option; + fn compare(&self, other: &Self, session: &Session<'_>) -> Option; } impl ExposureCompare for Exposure { - fn compare(&self, other: &Self, component: &Component) -> Option { + fn compare(&self, other: &Self, session: &Session<'_>) -> Option { use Exposure::*; match (self, other) { @@ -2216,24 +2211,24 @@ impl ExposureCompare for Exposure { (Restricted(this), Restricted(other)) => { let this = this.lock().unwrap(); let other = other.lock().unwrap(); - this.compare(&other, component) + this.compare(&other, session) } } } } impl ExposureCompare for ExposureReach { - fn compare(&self, other: &Self, component: &Component) -> Option { + fn compare(&self, other: &Self, session: &Session<'_>) -> Option { Some(match (self, other) { (&ExposureReach::Resolved(this), &ExposureReach::Resolved(other)) => { - let this = this.global(component); - let other = other.global(component); + let this = this.global(session); + let other = other.global(session); if this == other { Ordering::Equal - } else if component.some_ancestor_equals(other, this) { + } else if session.some_ancestor_equals(other, this) { Ordering::Greater - } else if component.some_ancestor_equals(this, other) { + } else if session.some_ancestor_equals(this, other) { Ordering::Less } else { return None; diff --git a/compiler/server/src/lib.rs b/compiler/server/src/lib.rs index 7831c9fe..2774cbfa 100644 --- a/compiler/server/src/lib.rs +++ b/compiler/server/src/lib.rs @@ -12,14 +12,9 @@ use index_map::IndexMap; use package::resolve_file; use resolver::ProgramEntryExt; use session::Context; -use session::{ - component::Component, - unit::{BuildUnit, ComponentType}, - Session, -}; +use session::{component::ComponentMetadata, unit::ComponentType, Session}; use std::{ collections::BTreeSet, - mem, path::Path, sync::{Arc, RwLock}, }; @@ -100,7 +95,7 @@ impl Server { Reporter::buffer(diagnostics.clone()), ); - let diagnostics = mem::take(&mut *diagnostics.lock().unwrap()); + let diagnostics = std::mem::take(&mut *diagnostics.lock().unwrap()); self.report(diagnostics, uri, version).await; } @@ -252,7 +247,7 @@ fn check_file( map: &Arc>, reporter: Reporter, ) -> Result<(Context, HashMap)> { - let (units, mut context) = resolve_file( + let mut context = resolve_file( path, Some(content), ComponentType::Executable, @@ -263,10 +258,10 @@ fn check_file( let mut component_roots = HashMap::default(); - for unit in units.into_values() { - let index = unit.index; - let root = context.at(unit, build_unit)?; - component_roots.insert(index, root); + for component in context.components().keys() { + let mut session = Session::new(component, &mut context); + let root = build_component(&mut session)?; + component_roots.insert(component, root); } Ok((context, component_roots)) @@ -274,28 +269,31 @@ fn check_file( // @Task keep going even with errors! #[allow(clippy::needless_pass_by_value)] // by design -fn build_unit(unit: BuildUnit, session: &mut Session<'_>) -> Result { +fn build_component(session: &mut Session<'_>) -> Result { // let content = component.content.take(); // @Beacon @Beacon @Beacon @Temporary let content = Some(std::sync::Arc::new(String::new())); - let path = unit.path.as_deref(); + let path = session.component().path.as_deref(); // @Beacon @Task this shouldm't need to be that ugly!!! let file = match content { - Some(content) => session - .map() - .add(path.bare.to_owned(), content, Some(unit.index)), + Some(content) => session.map().add( + path.bare.to_owned(), + content, + Some(session.component().index), + ), None => { session .map() - .load(path.bare, Some(unit.index)) + .load(path.bare, Some(session.component().index)) .map_err(|error| { use std::fmt::Write; let mut message = format!( "could not load the {} component ‘{}’", - unit.type_, unit.name + session.component().type_, + session.component().name ); if let Some(package) = session.package() { @@ -319,7 +317,7 @@ fn build_unit(unit: BuildUnit, session: &mut Session<'_>) -> Result) -> Result; /// A sealed container of modules regarded as one unit. -pub struct Component { - name: Word, - index: ComponentIndex, - // @Task document this! @Note this is used by the lang-server which gets the document content by the client - // and which should not open the file at the given path to avoid TOC-TOU bugs / data races - pub content: Option>, +// FIXME: make most fields private again + use accessors +pub struct ComponentMetadata { + pub name: Word, + pub index: ComponentIndex, + // FIXME: path to what?? + pub path: Spanned, + pub type_: ComponentType, dependencies: HashMap, - /// All bindings inside of the component. - /// - /// The first item has to be the root module. - pub bindings: IndexMap, } // @Beacon @Beacon @Beacon @Beacon @Beacon @Beacon @Task improve naming scheme around "root" (root component vs component root vs …) -impl Component { +impl ComponentMetadata { + /// The root module / the component root as a local index. + pub const ROOT: LocalDeclarationIndex = LocalDeclarationIndex::ROOT; + pub fn new( name: Word, index: ComponentIndex, - content: Option>, + path: Spanned, + type_: ComponentType, dependencies: HashMap, ) -> Self { Self { name, index, - content, + path, + type_, dependencies, - bindings: default(), } } #[cfg(feature = "test")] pub fn mock() -> Self { - use hir::Exposure; - use span::Spanned; + // use hir::Exposure; + use std::path::PathBuf; const NAME: &str = "test"; let name = Word::new_unchecked(NAME.into()); + // FIXME: this gross! + let path = Spanned::bare(CanonicalPathBuf::new_unchecked(PathBuf::new())); - let mut component = Self::new(name, ComponentIndex(0), None, HashMap::default()); - component.bindings.insert(Entity { - source: Spanned::bare(name).into(), - parent: None, - exposure: Exposure::Unrestricted, - kind: hir::EntityKind::module(), - attributes: default(), - }); + let component = Self::new( + name, + ComponentIndex(0), + path, + ComponentType::Library, + HashMap::default(), + ); + // FIXME: + // component.bindings.insert(Entity { + // source: Spanned::bare(name).into(), + // parent: None, + // exposure: Exposure::Unrestricted, + // kind: hir::EntityKind::module(), + // attributes: default(), + // }); component } @@ -67,13 +82,7 @@ impl Component { /// The root module / the component root. pub fn root(&self) -> DeclarationIndex { - self.root_local().global(self) - } - - /// The root module / the component root as a local index. - #[allow(clippy::unused_self)] // leads to a more legible API - pub fn root_local(&self) -> LocalDeclarationIndex { - LocalDeclarationIndex::new(0) + Self::ROOT.global(self) } pub fn dependencies(&self) -> &HashMap { @@ -88,28 +97,15 @@ impl Component { } } -impl std::ops::Index for Component { - type Output = Entity; - - #[track_caller] - fn index(&self, index: LocalDeclarationIndex) -> &Self::Output { - &self.bindings[index] - } -} - -impl std::ops::IndexMut for Component { - #[track_caller] - fn index_mut(&mut self, index: LocalDeclarationIndex) -> &mut Self::Output { - &mut self.bindings[index] - } -} - pub trait IdentifierExt { fn local_declaration_index(&self, component: &C) -> Option; } -impl IdentifierExt for hir::Identifier { - fn local_declaration_index(&self, component: &Component) -> Option { +impl IdentifierExt for hir::Identifier { + fn local_declaration_index( + &self, + component: &ComponentMetadata, + ) -> Option { self.declaration_index()?.local(component) } } @@ -122,22 +118,32 @@ impl IdentifierExt> for hir::Identifier { pub trait DeclarationIndexExt { #[allow(clippy::wrong_self_convention)] // false positive IMO, @Task report - fn is_local(self, context: &C) -> bool; + fn is_local(self, context: C) -> bool; - fn local(self, context: &C) -> Option; + fn local(self, context: C) -> Option; } -impl DeclarationIndexExt for DeclarationIndex { - fn is_local(self, component: &Component) -> bool { - self.component() == component.index() +impl DeclarationIndexExt for DeclarationIndex { + fn is_local(self, component: ComponentIndex) -> bool { + self.component() == component } - fn local(self, component: &Component) -> Option { + fn local(self, component: ComponentIndex) -> Option { self.is_local(component).then(|| self.local_unchecked()) } } -impl DeclarationIndexExt> for DeclarationIndex { +impl DeclarationIndexExt<&ComponentMetadata> for DeclarationIndex { + fn is_local(self, component: &ComponentMetadata) -> bool { + self.is_local(component.index) + } + + fn local(self, component: &ComponentMetadata) -> Option { + self.local(component.index) + } +} + +impl DeclarationIndexExt<&Session<'_>> for DeclarationIndex { fn is_local(self, session: &Session<'_>) -> bool { self.is_local(session.component()) } @@ -148,16 +154,22 @@ impl DeclarationIndexExt> for DeclarationIndex { } pub trait LocalDeclarationIndexExt { - fn global(self, context: &C) -> DeclarationIndex; + fn global(self, context: C) -> DeclarationIndex; +} + +impl LocalDeclarationIndexExt for LocalDeclarationIndex { + fn global(self, component: ComponentIndex) -> DeclarationIndex { + DeclarationIndex::new(component, self) + } } -impl LocalDeclarationIndexExt for LocalDeclarationIndex { - fn global(self, component: &Component) -> DeclarationIndex { - DeclarationIndex::new(component.index(), self) +impl LocalDeclarationIndexExt<&ComponentMetadata> for LocalDeclarationIndex { + fn global(self, component: &ComponentMetadata) -> DeclarationIndex { + self.global(component.index()) } } -impl LocalDeclarationIndexExt> for LocalDeclarationIndex { +impl LocalDeclarationIndexExt<&Session<'_>> for LocalDeclarationIndex { fn global(self, session: &Session<'_>) -> DeclarationIndex { self.global(session.component()) } diff --git a/compiler/session/src/lib.rs b/compiler/session/src/lib.rs index f18cdcc0..def06319 100644 --- a/compiler/session/src/lib.rs +++ b/compiler/session/src/lib.rs @@ -1,11 +1,9 @@ #![feature(decl_macro, lazy_cell)] -use component::{Component, DeclarationIndexExt}; +use component::{Bindings, ComponentMetadata, DeclarationIndexExt}; use diagnostics::{error::Result, Diagnostic, Reporter}; -use hir::{ - special::{self, Bindings}, - DeclarationIndex, LocalDeclarationIndex, -}; +use hir::{special, DeclarationIndex, LocalDeclarationIndex}; +use index_map::IndexMap; use lexer::word::Word; use package::{ManifestPath, Package}; use span::{SourceMap, Span}; @@ -13,6 +11,7 @@ use std::{ ops::{Index, IndexMut}, sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; +use unit::ComponentType; use utility::{default, ComponentIndex, HashMap}; pub mod component; @@ -23,12 +22,22 @@ pub mod unit; pub const OUTPUT_FOLDER_NAME: &str = "build"; pub struct Session<'ctx> { - component: Component, - context: &'ctx mut Context, + // @Temporary pub + pub component: ComponentIndex, + // @Temporary pub + pub context: &'ctx mut Context, } impl<'ctx> Session<'ctx> { - pub fn new(component: Component, context: &'ctx mut Context) -> Self { + #[cfg(feature = "test")] + pub fn mock(context: &'ctx mut Context) -> Self { + Self { + component: ComponentIndex(0), + context, + } + } + + pub fn new(component: ComponentIndex, context: &'ctx mut Context) -> Self { Self { component, context } } @@ -45,7 +54,7 @@ impl<'ctx> Session<'ctx> { } pub fn package(&self) -> Option { - self.context.package_of(self.component.index()) + self.context.package_of(self.component) } pub fn in_root_package(&self, component: ComponentIndex) -> bool { @@ -53,8 +62,22 @@ impl<'ctx> Session<'ctx> { .map_or(false, |package| self.root_package() == Some(package)) } + // FIXME: can this be simplified now? + pub fn is_core_library(&self) -> bool { + self.package_of(self.component).map_or(false, |package| { + self[package].is_core() + && self.context.components[self.component].type_ == ComponentType::Library + }) + } + + // FIXME: can this be simplified now? + // FIXME: bad name + pub fn is_root_component(&self) -> bool { + self.component == self.context.root_component.index + } + pub fn define(&mut self, entity: hir::Entity) -> LocalDeclarationIndex { - self.component.bindings.insert(entity) + self.context.bindings[self.component].insert(entity) } pub fn parent_of(&self, index: DeclarationIndex) -> Option { @@ -64,16 +87,12 @@ impl<'ctx> Session<'ctx> { )) } - // @Task make this an Index::index fn again - pub fn look_up_component(&self, index: ComponentIndex) -> &Component { - &self.context.components[&index] - } - - pub fn component_of(&self, index: DeclarationIndex) -> &Component { - if index.is_local(&self.component) { - &self.component + // FIXME: how is this used??? this is no longer correct in the local case (bindings is empty) + pub fn component_of(&self, index: DeclarationIndex) -> &ComponentMetadata { + if index.is_local(self.component) { + &self.context.components[self.component] } else { - self.look_up_component(index.component()) + &self[index.component()] } } @@ -91,7 +110,11 @@ impl<'ctx> Session<'ctx> { binder, match style { Implicit { namespace } => Implicit { - namespace: namespace.map(|namespace| self.component[namespace].source.bare()), + namespace: namespace.map(|namespace| { + self.context.bindings[self.component][namespace] + .source + .bare() + }), }, Explicit { name } => Explicit { name }, }, @@ -112,11 +135,11 @@ impl<'ctx> Session<'ctx> { .ok_or_else(|| error::missing_binding(special, user).report(self.reporter())) } - pub fn component(&self) -> &Component { - &self.component + pub fn component(&self) -> &ComponentMetadata { + &self.context.components[self.component] } - pub fn specials(&self) -> &Bindings { + pub fn specials(&self) -> &special::Bindings { &self.context.specials } @@ -145,14 +168,23 @@ impl Index for Session<'_> { } } +impl Index for Session<'_> { + type Output = ComponentMetadata; + + fn index(&self, index: ComponentIndex) -> &Self::Output { + &self.context.components[index] + } +} + impl Index for Session<'_> { type Output = hir::Entity; fn index(&self, index: DeclarationIndex) -> &Self::Output { - match index.local(&self.component) { - Some(index) => &self.component[index], - None => &self.context[index], - } + // match index.local(self.component) { + // Some(index) => &self.bindings[index], + // None => &self.context[index], + // } + &self.context.bindings[index.component()][index.local_unchecked()] } } @@ -160,19 +192,19 @@ impl Index for Session<'_> { type Output = hir::Entity; fn index(&self, index: LocalDeclarationIndex) -> &Self::Output { - &self.component[index] + &self.context.bindings[self.component][index] } } impl IndexMut for Session<'_> { fn index_mut(&mut self, index: LocalDeclarationIndex) -> &mut Self::Output { - &mut self.component[index] + &mut self.context.bindings[self.component][index] } } +// FIXME: Don't use any HashMaps here, they're unordered! pub struct Context { - /// The components which have already been built in this session. - components: HashMap, + components: IndexMap, /// The packages whose components have not necessarily been built yet in this session but are about to. packages: HashMap, /// The mapping from component to corresponding package. @@ -180,25 +212,31 @@ pub struct Context { component_packages: HashMap, // @Task support multiple root components (depending on a user-supplied component filter) root_component: ComponentOutline, + // @Temporary pub + pub bindings: IndexMap, /// Intrinsic and known bindings. // @Temporary pub - pub specials: Bindings, + pub specials: special::Bindings, map: Arc>, reporter: Reporter, } impl Context { pub fn new( + components: IndexMap, packages: HashMap, component_packages: HashMap, root_component: ComponentOutline, map: &Arc>, reporter: Reporter, ) -> Self { + let bindings = IndexMap::bare(vec![Bindings::default(); components.len()]); + Self { - components: default(), + components, packages, component_packages, + bindings, root_component, specials: default(), map: map.clone(), @@ -213,6 +251,7 @@ impl Context { let map: Arc> = default(); + // FIXME: empty units/components is incorrect technically speaking Self { components: default(), packages: default(), @@ -221,36 +260,13 @@ impl Context { name: Word::new_unchecked("test".into()), index: ComponentIndex::new(0), }, + bindings: default(), specials: default(), map: map.clone(), reporter: Reporter::stderr(ColorChoice::Auto).with_map(map), } } - // @Task find a better name! - pub fn at( - &mut self, - mut unit: unit::BuildUnit, - handler: impl FnOnce(unit::BuildUnit, &mut Session<'_>) -> T, - ) -> T { - let component = Component::new( - unit.name, - unit.index, - None, - std::mem::take(&mut unit.dependencies), - ); - let mut session = Session::new(component, self); - - let result = handler(unit, &mut session); - - session - .context - .components - .insert(session.component.index(), session.component); - - result - } - pub fn root_package(&self) -> Option { self.package_of(self.root_component.index) } @@ -263,6 +279,10 @@ impl Context { self.component_packages.get(&component).copied() } + pub fn components(&self) -> &IndexMap { + &self.components + } + pub fn reporter(&self) -> &Reporter { &self.reporter } @@ -280,7 +300,7 @@ impl Index for Context { type Output = hir::Entity; fn index(&self, index: DeclarationIndex) -> &Self::Output { - &self.components[&index.component()][index.local_unchecked()] + &self.bindings[index.component()][index.local_unchecked()] } } diff --git a/compiler/session/src/unit.rs b/compiler/session/src/unit.rs index f99b720b..9b45dfb7 100644 --- a/compiler/session/src/unit.rs +++ b/compiler/session/src/unit.rs @@ -1,46 +1,9 @@ // @Question can we move this into `package`? -use crate::{ComponentOutline, Session}; +// FIXME: remove this file + use derivation::{Elements, FromStr, Str}; -use lexer::word::Word; -use span::Spanned; use std::fmt; -use utility::{path::CanonicalPathBuf, ComponentIndex, HashMap}; - -// @Beacon @Beacon @Beacon @Task rename BuildUnit again to sth containing "Component" - -pub struct BuildUnit { - pub name: Word, - pub index: ComponentIndex, - // @Task make this a PathBuf (?) - pub path: Spanned, - pub type_: ComponentType, - pub dependencies: HashMap, -} - -impl BuildUnit { - // @Temporary - pub fn outline(&self) -> ComponentOutline { - ComponentOutline { - name: self.name, - index: self.index, - } - } - - /// Test if this component is the standard library `core`. - // @Temporary - pub fn is_core_library(&self, session: &Session<'_>) -> bool { - session.package_of(self.index).map_or(false, |package| { - session[package].is_core() && self.type_ == ComponentType::Library - }) - } - - // @Beacon @Beacon @Beacon @Task store in a BuildUnit whether it is a root or not! - // and then remove this method and the root_component in Session - pub fn is_root(&self, session: &Session<'_>) -> bool { - self.index == session.context.root_component.index - } -} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Elements, FromStr, Str)] #[format(dash_case)] diff --git a/compiler/typer/src/lib.rs b/compiler/typer/src/lib.rs index 69d37346..a7265791 100644 --- a/compiler/typer/src/lib.rs +++ b/compiler/typer/src/lib.rs @@ -336,7 +336,7 @@ impl<'sess, 'ctx> Typer<'sess, 'ctx> { let function = self .session .specials() - .get(index.global(self.session)) + .get(index.global(&*self.session)) .unwrap(); let special::Binding::Function(function) = function else { unreachable!() diff --git a/project/library/index_map/src/lib.rs b/project/library/index_map/src/lib.rs index 40cc29d8..6d7d0102 100644 --- a/project/library/index_map/src/lib.rs +++ b/project/library/index_map/src/lib.rs @@ -111,7 +111,7 @@ impl IndexMap { self.values.iter_mut().enumerate().map(map_entry) } - pub fn indices(&self) -> impl Iterator { + pub fn keys(&self) -> KeysIntoIter { (0..self.len()).map(I::new) } } @@ -208,9 +208,11 @@ fn map_entry((index, value): (usize, T)) -> (I, T) { (I::new(index), value) } -pub type IntoIter = impl Iterator; -pub type Iter<'a, I: Index, T: 'a> = impl Iterator; -pub type IterMut<'a, I: Index, T: 'a> = impl Iterator; +pub type IntoIter = impl DoubleEndedIterator; +pub type Iter<'a, I: Index, T: 'a> = impl DoubleEndedIterator; +pub type IterMut<'a, I: Index, T: 'a> = impl DoubleEndedIterator; + +pub type KeysIntoIter = impl DoubleEndedIterator; pub trait Index { fn new(index: usize) -> Self;