Skip to content

Commit 8f3814e

Browse files
committed
Package management in the evaluator
1 parent 07cd719 commit 8f3814e

File tree

17 files changed

+425
-45
lines changed

17 files changed

+425
-45
lines changed

Cargo.lock

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ansi_term = "0.12"
3737
anyhow = "1.0"
3838
assert_cmd = "2.0.11"
3939
assert_matches = "1.5.0"
40+
base16 = "0.2.1"
4041
bincode = "1.3.3"
4142
clap = "4.3"
4243
clap_complete = "4.3.2"

core/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ cxx-build = { workspace = true, optional = true }
3434
pkg-config = { workspace = true, optional = true }
3535

3636
[dependencies]
37+
base16.workspace = true
3738
lalrpop-util.workspace = true
3839
regex.workspace = true
3940
simple-counter.workspace = true
@@ -79,6 +80,7 @@ tree-sitter-nickel = { workspace = true, optional = true }
7980

8081
metrics = { workspace = true, optional = true }
8182
strsim = "0.10.0"
83+
directories.workspace = true
8284

8385
[dev-dependencies]
8486
pretty_assertions.workspace = true

core/src/cache.rs

+44-11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::eval::cache::Cache as EvalCache;
55
use crate::eval::Closure;
66
#[cfg(feature = "nix-experimental")]
77
use crate::nix_ffi;
8+
use crate::package::{self, LockedPackageSource, ResolvedLockFile};
89
use crate::parser::{lexer::Lexer, ErrorTolerantParser};
910
use crate::position::TermPos;
1011
use crate::program::FieldPath;
@@ -75,6 +76,7 @@ impl InputFormat {
7576
/// is, the operations that have been performed on this term) is stored in an [EntryState].
7677
#[derive(Debug, Clone)]
7778
pub struct Cache {
79+
// TODO: associate packages to file ids
7880
/// The content of the program sources plus imports.
7981
files: Files<String>,
8082
file_paths: HashMap<FileId, SourcePath>,
@@ -86,6 +88,10 @@ pub struct Cache {
8688
rev_imports: HashMap<FileId, HashSet<FileId>>,
8789
/// The table storing parsed terms corresponding to the entries of the file database.
8890
terms: HashMap<FileId, TermEntry>,
91+
/// A table mapping FileIds to the package that they belong to.
92+
///
93+
/// Path dependencies have already been canonicalized to absolute paths.
94+
package: HashMap<FileId, LockedPackageSource>,
8995
/// The list of ids corresponding to the stdlib modules
9096
stdlib_ids: Option<HashMap<StdlibModule, FileId>>,
9197
/// The inferred type of wildcards for each `FileId`.
@@ -94,6 +100,8 @@ pub struct Cache {
94100
error_tolerance: ErrorTolerance,
95101
import_paths: Vec<PathBuf>,
96102

103+
lock_file: ResolvedLockFile,
104+
97105
#[cfg(debug_assertions)]
98106
/// Skip loading the stdlib, used for debugging purpose
99107
pub skip_stdlib: bool,
@@ -338,9 +346,11 @@ impl Cache {
338346
wildcards: HashMap::new(),
339347
imports: HashMap::new(),
340348
rev_imports: HashMap::new(),
349+
package: HashMap::new(),
341350
stdlib_ids: None,
342351
error_tolerance,
343352
import_paths: Vec::new(),
353+
lock_file: ResolvedLockFile::default(),
344354

345355
#[cfg(debug_assertions)]
346356
skip_stdlib: false,
@@ -354,6 +364,10 @@ impl Cache {
354364
self.import_paths.extend(paths.map(PathBuf::from));
355365
}
356366

367+
pub fn set_lock_file(&mut self, lock_file: ResolvedLockFile) {
368+
self.lock_file = lock_file;
369+
}
370+
357371
/// Same as [Self::add_file], but assume that the path is already normalized, and take the
358372
/// timestamp as a parameter.
359373
fn add_file_(&mut self, path: PathBuf, timestamp: SystemTime) -> io::Result<FileId> {
@@ -1313,6 +1327,7 @@ pub trait ImportResolver {
13131327
&mut self,
13141328
path: &OsStr,
13151329
parent: Option<FileId>,
1330+
pkg: Option<&package::Name>,
13161331
pos: &TermPos,
13171332
) -> Result<(ResolvedTerm, FileId), ImportError>;
13181333

@@ -1327,18 +1342,30 @@ impl ImportResolver for Cache {
13271342
&mut self,
13281343
path: &OsStr,
13291344
parent: Option<FileId>,
1345+
pkg: Option<&package::Name>,
13301346
pos: &TermPos,
13311347
) -> Result<(ResolvedTerm, FileId), ImportError> {
1332-
// `parent` is the file that did the import. We first look in its containing directory.
1333-
let mut parent_path = parent
1334-
.and_then(|p| self.get_path(p))
1335-
.map(PathBuf::from)
1336-
.unwrap_or_default();
1337-
parent_path.pop();
1338-
1339-
let possible_parents: Vec<PathBuf> = std::iter::once(parent_path)
1340-
.chain(self.import_paths.iter().cloned())
1341-
.collect();
1348+
let (possible_parents, pkg_id) = if let Some(pkg) = pkg {
1349+
let pkg_id = self
1350+
.lock_file
1351+
.get(parent.and_then(|p| self.package.get(&p)), pkg, pos)?;
1352+
(vec![pkg_id.local_path()], Some(pkg_id.clone()))
1353+
} else {
1354+
// `parent` is the file that did the import. We first look in its containing directory, followed by
1355+
// the directories in the import path.
1356+
let mut parent_path = parent
1357+
.and_then(|p| self.get_path(p))
1358+
.map(PathBuf::from)
1359+
.unwrap_or_default();
1360+
parent_path.pop();
1361+
1362+
(
1363+
std::iter::once(parent_path)
1364+
.chain(self.import_paths.iter().cloned())
1365+
.collect(),
1366+
None,
1367+
)
1368+
};
13421369

13431370
// Try to import from all possibilities, taking the first one that succeeds.
13441371
let (id_op, path_buf) = possible_parents
@@ -1374,6 +1401,10 @@ impl ImportResolver for Cache {
13741401
self.parse_multi(file_id, format)
13751402
.map_err(|err| ImportError::ParseErrors(err, *pos))?;
13761403

1404+
if let Some(pkg_id) = pkg_id {
1405+
self.package.insert(file_id, pkg_id);
1406+
}
1407+
13771408
Ok((result, file_id))
13781409
}
13791410

@@ -1414,7 +1445,7 @@ pub fn normalize_path(path: impl Into<PathBuf>) -> std::io::Result<PathBuf> {
14141445
/// [`std::fs::canonicalize`] can be hard to use correctly, since it can often
14151446
/// fail, or on Windows returns annoying device paths. This is a problem Cargo
14161447
/// needs to improve on.
1417-
fn normalize_abs_path(path: &Path) -> PathBuf {
1448+
pub fn normalize_abs_path(path: &Path) -> PathBuf {
14181449
use std::path::Component;
14191450

14201451
let mut components = path.components().peekable();
@@ -1461,6 +1492,7 @@ pub mod resolvers {
14611492
&mut self,
14621493
_path: &OsStr,
14631494
_parent: Option<FileId>,
1495+
_pkg: Option<&package::Name>,
14641496
_pos: &TermPos,
14651497
) -> Result<(ResolvedTerm, FileId), ImportError> {
14661498
panic!("cache::resolvers: dummy resolver should not have been invoked");
@@ -1502,6 +1534,7 @@ pub mod resolvers {
15021534
&mut self,
15031535
path: &OsStr,
15041536
_parent: Option<FileId>,
1537+
_pkg: Option<&package::Name>,
15051538
pos: &TermPos,
15061539
) -> Result<(ResolvedTerm, FileId), ImportError> {
15071540
let file_id = self

core/src/error/mod.rs

+44
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! Define error types for different phases of the execution, together with functions to generate a
44
//! [codespan](https://crates.io/crates/codespan-reporting) diagnostic from them.
5+
56
pub use codespan::{FileId, Files};
67
pub use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle};
78

@@ -18,6 +19,7 @@ use crate::{
1819
ty_path::{self, PathSpan},
1920
MergeKind, MergeLabel,
2021
},
22+
package::{LockedPackageSource, Name},
2123
parser::{
2224
self,
2325
error::{InvalidRecordTypeError, LexicalError, ParseError as InternalParseError},
@@ -558,6 +560,8 @@ pub enum ParseError {
558560
/// An error occurring during the resolution of an import.
559561
#[derive(Debug, PartialEq, Eq, Clone)]
560562
pub enum ImportError {
563+
/// An unexpected internal error.
564+
InternalError { msg: String, pos: TermPos },
561565
/// An IO error occurred during an import.
562566
IOError(
563567
/* imported file */ String,
@@ -569,6 +573,15 @@ pub enum ImportError {
569573
/* error */ ParseErrors,
570574
/* import position */ TermPos,
571575
),
576+
/// A package dependency was not found.
577+
MissingDependency {
578+
/// The package that tried to import the missing dependency, if there was one.
579+
/// This will be `None` if the missing dependency was from the top-level
580+
parent: Option<Box<(Name, LockedPackageSource)>>,
581+
/// The name of the package that could not be resolved.
582+
missing: Name,
583+
pos: TermPos,
584+
},
572585
}
573586

574587
#[derive(Debug, PartialEq, Clone)]
@@ -2527,6 +2540,17 @@ impl IntoDiagnostics<FileId> for ImportError {
25272540
stdlib_ids: Option<&Vec<FileId>>,
25282541
) -> Vec<Diagnostic<FileId>> {
25292542
match self {
2543+
ImportError::InternalError { msg, pos } => {
2544+
let labels = pos
2545+
.as_opt_ref()
2546+
.map(|span| vec![primary(span).with_message("here")])
2547+
.unwrap_or_default();
2548+
2549+
vec![Diagnostic::error()
2550+
.with_message(format!("internal error: {msg}"))
2551+
.with_labels(labels)
2552+
.with_notes(vec![String::from(INTERNAL_ERROR_MSG)])]
2553+
}
25302554
ImportError::IOError(path, error, span_opt) => {
25312555
let labels = span_opt
25322556
.as_opt_ref()
@@ -2552,6 +2576,26 @@ impl IntoDiagnostics<FileId> for ImportError {
25522576

25532577
diagnostic
25542578
}
2579+
ImportError::MissingDependency {
2580+
parent,
2581+
missing,
2582+
pos,
2583+
} => {
2584+
let labels = pos
2585+
.as_opt_ref()
2586+
.map(|span| vec![primary(span).with_message("imported here")])
2587+
.unwrap_or_default();
2588+
let msg = if let Some((parent_name, parent_source)) = parent.as_deref() {
2589+
format!(
2590+
"unknown package {missing}, imported from package {parent_name} at {}",
2591+
parent_source.local_path().display()
2592+
)
2593+
} else {
2594+
format!("unknown package {missing}")
2595+
};
2596+
2597+
vec![Diagnostic::error().with_message(msg).with_labels(labels)]
2598+
}
25552599
}
25562600
}
25572601
}

core/src/eval/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ impl<R: ImportResolver, C: Cache> VirtualMachine<R, C> {
785785
));
786786
}
787787
}
788-
Term::Import(path) => {
788+
Term::Import(path, _pkg) => {
789789
return Err(EvalError::InternalError(
790790
format!("Unresolved import ({})", path.to_string_lossy()),
791791
pos,
@@ -1159,7 +1159,7 @@ pub fn subst<C: Cache>(
11591159
| v @ Term::Lbl(_)
11601160
| v @ Term::SealingKey(_)
11611161
| v @ Term::Enum(_)
1162-
| v @ Term::Import(_)
1162+
| v @ Term::Import(..)
11631163
| v @ Term::ResolvedImport(_)
11641164
// We could recurse here, because types can contain terms which would then be subject to
11651165
// substitution. Not recursing should be fine, though, because a type in term position

core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod identifier;
99
pub mod label;
1010
#[cfg(feature = "nix-experimental")]
1111
pub mod nix_ffi;
12+
pub mod package;
1213
pub mod parser;
1314
pub mod position;
1415
pub mod pretty;

0 commit comments

Comments
 (0)