Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .sampo/changesets/constant-fold-fixed-point.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
cargo/nash-codegen: patch
cargo/nash-plutus: patch
---

Run constant folding, unused-parameter removal and cleanup until unchanged without optimizer resource or output-growth limits. Evaluate all pure representable builtin calls directly and return errors instead of panicking for out-of-range constant indices and constructor tags. Correct whole-byte left shifts and handle arbitrary-size shift, rotate and list-drop inputs.
8 changes: 8 additions & 0 deletions .sampo/changesets/o1-application-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
cargo/nash-ir: patch
cargo/nash-codegen: patch
---

Fuse adjacent application stages in O1, clean up generated UPLC bindings, and
pack value application spines with native Case/Constr while preserving traces,
failure and termination.
6 changes: 6 additions & 0 deletions .sampo/changesets/o1-constant-folding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
cargo/nash-ir: patch
cargo/nash-codegen: patch
---

Enable bounded constant builtin evaluation in O1. Fold saturated literal calls through ANF bindings and repeat cleanup, preserving traces and runtime failures. Keep O0 and explicit comptime policies unchanged.
6 changes: 6 additions & 0 deletions .sampo/changesets/optimizer-static-fixed-point.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
cargo/nash-codegen: patch
cargo/nash-ir: patch
---

Revisit static recursive parameter lifting after cleanup and recursive pruning so O1 reaches the same closed program in one invocation. Preserve atomic operands when lifting all-static oversaturated recursive calls and check optimizer idempotence across executable fixtures.
8 changes: 8 additions & 0 deletions .sampo/changesets/pair-access-and-cancellation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
cargo/nash-solve: minor
cargo/nash-codegen: minor
cargo/nash-ir: patch
cargo/nash-report: patch
---

Support read-only `.fst` and `.snd` field access and accessor functions for known builtin pair types, including aliases. Lower access directly to the pair builtins. Enable the restricted O1 pair-case rewrite only when constructor inverse cleanup removes the introduced projection, preserving strict producer evaluation.
9 changes: 9 additions & 0 deletions .sampo/changesets/plutus-v3-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
cargo/nash-config: minor
cargo/nash-codegen: minor
cargo/nash-test: minor
cargo/nash-driver: minor
cargo/nash-cli: minor
---

Remove the Plutus V1 and V2 targets. Nash compiles validators against the V3 script context only, so a V1 or V2 language tag produced an invalid validator. The `plutusVersion` project setting, the `--plutus-version` option, `nash_config::PlutusVersion`, `assemble_core_for_version`, `TestProgram::plutus_version` and the version parameters of `assemble_core_with_options` and `compile_tests*` are gone. Script hashes always use the V3 language tag.
5 changes: 5 additions & 0 deletions .sampo/changesets/post-anf-folding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
cargo/nash-codegen: patch
---

Run constructor folding and representation cancellation only in the post-ANF cleanup loops, following generated bindings and opportunities exposed by inlining. Remove the early direct-expression passes.
6 changes: 6 additions & 0 deletions .sampo/changesets/recursive-unused-parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
cargo/nash-ir: patch
cargo/nash-codegen: patch
---

Remove unused self-recursive and mutual parameters in O1 while preserving strict argument order, forwarding dependencies, type views, and delayed worker execution.
8 changes: 8 additions & 0 deletions .sampo/changesets/silent-o2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
cargo/nash-config: minor
cargo/nash-codegen: minor
cargo/nash-driver: minor
cargo/nash-cli: minor
---

Add explicit O2 compilation for build and test. O2 requires silent settings, removes user and compiler traces, and discards trace message computations even when they fail or diverge before applying the O1 optimizer. Reject compact, verbose and compiler tracing with O2. Keep O1 as the default.
6 changes: 4 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Done:
- [x] `nash check`
- [x] UPLC runtime (`nash-plutus`): conformance suite passes

Implementation plans (Plan 08 is in progress; Plans 09 and 10 are complete):
Implementation plans (Plans 08, 09 and 10 are complete):

- [x] 01 Syntax: `'a` type vars, little/Big names, `trait`/`impl`, `=>` contexts, representation annotations, `validator module`, `tests` block, `do`, attributes, `name!()`, `comptime`, `assert`/`fail`/`todo`/`trace`; drop `Float`/`Char`/record extension types — [plans/01-syntax.md](plans/01-syntax.md)
- [x] 02 Kinds: original declaration checking and representation rules (engine replaced by the follow-up) — [plans/02-kinds.md](plans/02-kinds.md)
Expand All @@ -63,7 +63,7 @@ Implementation plans (Plan 08 is in progress; Plans 09 and 10 are complete):
- [x] 05 Exhaustiveness (`Nitpick/PatternMatches` port) — [plans/05-nitpick.md](plans/05-nitpick.md)
- [x] 06 Diagnostics (`nash-report`, concise source labels, stable codes, JSON/LSP) — [plans/06-diagnostics.md](plans/06-diagnostics.md); [concise diagnostics refactor](plans/diagnostics-refactor.md) complete
- [x] 07 Codegen: Core IR, monomorphization, decision trees, recursion, Data casts, UPLC lowering — [plans/07-codegen.md](plans/07-codegen.md)
- [ ] 08 Optimizer: Single ANF normalization and optimization before recursion rewrite, inlining, builtin force caching, DCE, case-of-known-ctor/constant folding — [plans/08-optimizer.md](plans/08-optimizer.md) (Retained scope of Chunks 1–8 accepted; representation cancellation and O0/O1 build/test/config integration available; recursive unused-parameter removal, Chunks 9–10 and final composition review open)
- [x] 08 Optimizer: Single ANF normalization and optimization before recursion rewrite, inlining, builtin force caching, DCE, case-of-known-ctor/constant folding — [plans/08-optimizer.md](plans/08-optimizer.md) (Retained scope complete: recursive unused parameters, unbudgeted constant folding, joint lifting/cleanup fixed point, restricted pair cancellation, O0/O1 build/test/config integration, composition/convergence validation, adjacent application fusion, native value-call packing and late UPLC binding cleanup)
- [x] 09 Validators + `nash build` — [plans/09-validators-build.md](plans/09-validators-build.md)
- [x] 10 Testing: `tests` block, props, generators, shrinking, power-assert, `nash test` — [plans/10-testing.md](plans/10-testing.md)
- [ ] 11 Macros + comptime — [plans/11-macros-comptime.md](plans/11-macros-comptime.md); includes explicit integer-dispatch AST and positional library macro (chunk 13), reusable call/binding/case-shaped macro inputs required in chunk 14 (library semantics deferred)
Expand All @@ -73,6 +73,8 @@ Implementation plans (Plan 08 is in progress; Plans 09 and 10 are complete):

- [ ] 14 Explicit Big constructor tags: preserve declared Data tags; little tags remain consecutive from zero — [plans/14-explicit-big-constructor-tags.md](plans/14-explicit-big-constructor-tags.md)

- [ ] 15 Silent O2 and application regrouping: silent O2 implemented, including removal of failing/diverging trace messages; broader grouping/motion pending; trace-preserving candidates remain O1 — [plans/15-o2-optimizer.md](plans/15-o2-optimizer.md) (partial)

Later: LSP features, web playground, package registry (pubgrub), TypeScript codegen.

## Elm file mappings
Expand Down
83 changes: 81 additions & 2 deletions crates/nash-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod tests {
#[test]
fn optimization_flags() {
for command in ["build", "test"] {
for flag in ["-O0", "-O1"] {
for flag in ["-O0", "-O1", "-O2"] {
let cli = Cli::try_parse_from(["nash", command, flag]).unwrap();
let level = match cli.cmd {
crate::cmd::Cmd::Build(args) => args.optimize,
Expand All @@ -59,10 +59,89 @@ mod tests {
assert_eq!(u8::from(level.unwrap()), flag.as_bytes()[2] - b'0');
}
assert!(Cli::try_parse_from(["nash", command, "--optimize", "1"]).is_ok());
assert!(Cli::try_parse_from(["nash", command, "-O2"]).is_err());
assert!(Cli::try_parse_from(["nash", command, "-O3"]).is_err());
}
}

#[test]
fn o2_settings_merge_before_test_defaults() {
let mut rendered = Vec::new();
for command in ["build", "test"] {
for args in [
vec!["-O2"],
vec!["-O2", "--trace-level", "silent"],
vec!["-O2", "--trace-level", "compact"],
vec!["-O2", "--trace-level", "verbose"],
vec!["-O1"],
] {
for (setting, trace_level, compiler_traces) in [
("default", nash_config::TraceLevel::Silent, false),
("verbose project", nash_config::TraceLevel::Verbose, false),
("compiler traces", nash_config::TraceLevel::Silent, true),
] {
let cli = Cli::try_parse_from(
["nash", command].into_iter().chain(args.iter().copied()),
)
.unwrap();
let build = nash_config::Build {
trace_level,
compiler_traces,
trace_level_explicit: setting != "default",
..Default::default()
};
let resolved = match cli.cmd {
crate::cmd::Cmd::Build(args) => args.resolve_config(build),
crate::cmd::Cmd::Test(args) => args
.resolve_config(build)
.map(nash_config::Build::for_tests),
_ => unreachable!(),
};
rendered.push(format!(
"{command} {} ({setting}): {}",
args.join(" "),
match resolved {
Ok(config) => format!(
"O{} {:?}, compiler traces: {}",
u8::from(config.optimize),
config.trace_level,
config.compiler_traces
),
Err(error) => error.to_string(),
}
));
}
}
}
insta::with_settings!({omit_expression => true}, { insta::assert_snapshot!(rendered.join("\n")); });
}

#[test]
fn o2_compiler_trace_cli_overrides() {
let mut rows = Vec::new();
for flag in ["--compiler-traces", "--compiler-traces=false"] {
let cli = Cli::try_parse_from(["nash", "build", "-O2", flag]).unwrap();
let crate::cmd::Cmd::Build(args) = cli.cmd else {
unreachable!()
};
let result = args.resolve_config(nash_config::Build {
compiler_traces: true,
..Default::default()
});
rows.push(format!(
"{flag}: {}",
match result {
Ok(config) => format!(
"O{}; compiler traces: {}",
u8::from(config.optimize),
config.compiler_traces
),
Err(error) => error.to_string(),
}
));
}
insta::with_settings!({omit_expression => true}, { insta::assert_snapshot!(rows.join("\n")); });
}

#[test]
fn command_schema_is_consistent() {
Cli::command().debug_assert();
Expand Down
68 changes: 34 additions & 34 deletions crates/nash-cli/src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tokio::sync::Mutex;

#[derive(clap::Args)]
pub struct Args {
/// Optimization level: 0 (baseline) or 1 (preserves enabled traces).
/// Optimization level: 0 (baseline), 1 (preserves traces), or 2 (silent).
#[arg(short = 'O', long)]
pub optimize: Option<nash_config::OptimizationLevel>,
/// Path to the project.
Expand All @@ -17,9 +17,6 @@ pub struct Args {
/// Include user traces in the script.
#[arg(long, value_enum)]
pub trace_level: Option<TraceLevelArg>,
/// Ledger language target (protocol 11 compatibility).
#[arg(long, value_enum)]
pub plutus_version: Option<PlutusVersionArg>,
/// Include compiler traces for failed casts and pattern checks.
#[arg(long, num_args = 0..=1, require_equals = true, default_missing_value = "true")]
pub compiler_traces: Option<bool>,
Expand All @@ -32,16 +29,38 @@ pub enum TraceLevelArg {
Verbose,
}

#[derive(Clone, Copy, clap::ValueEnum)]
pub enum PlutusVersionArg {
V1,
V2,
V3,
}

impl Args {
pub(crate) fn resolve_config(
&self,
mut config: nash_config::Build,
) -> std::result::Result<nash_config::Build, nash_config::BuildConflict> {
if let Some(level) = self.optimize {
config.optimize = level;
}
if let Some(level) = self.trace_level {
config.trace_level_explicit = true;
config.trace_level = match level {
TraceLevelArg::Silent => nash_config::TraceLevel::Silent,
TraceLevelArg::Compact => nash_config::TraceLevel::Compact,
TraceLevelArg::Verbose => nash_config::TraceLevel::Verbose,
};
}
if let Some(enabled) = self.compiler_traces {
config.compiler_traces = enabled;
}
config.validate()?;
Ok(config)
}

pub async fn exec(self, color: bool) -> Result<()> {
let project = Project::load(&self.path).await.into_diagnostic()?;
// Reject conflicts even when a project/member contains no source roots.
self.resolve_config(project.config.build())
.into_diagnostic()?;
for member in &project.members {
self.resolve_config(member.config.build())
.into_diagnostic()?;
}
let db = Arc::new(Mutex::new(Database::new(FileSystemSource::new())));
let modules = project
.discover_modules_production(&*db.lock().await)
Expand Down Expand Up @@ -76,34 +95,15 @@ impl Args {
.to_file_path()
.expect("discovered source has a file URL");
let path = path.canonicalize().unwrap_or(path);
let mut config = member_settings
let config = member_settings
.iter()
.filter(|(directory, _)| path.starts_with(directory))
.max_by_key(|(directory, _)| directory.components().count())
.map_or_else(|| project.config.build(), |(_, config)| *config);
if let Some(version) = self.plutus_version {
config.plutus_version = match version {
PlutusVersionArg::V1 => nash_config::PlutusVersion::V1,
PlutusVersionArg::V2 => nash_config::PlutusVersion::V2,
PlutusVersionArg::V3 => nash_config::PlutusVersion::V3,
};
}
if let Some(level) = self.optimize {
config.optimize = level;
}
if let Some(level) = self.trace_level {
config.trace_level = match level {
TraceLevelArg::Silent => nash_config::TraceLevel::Silent,
TraceLevelArg::Compact => nash_config::TraceLevel::Compact,
TraceLevelArg::Verbose => nash_config::TraceLevel::Verbose,
};
}
if let Some(enabled) = self.compiler_traces {
config.compiler_traces = enabled;
}
(uri.clone(), config)
Ok((uri.clone(), self.resolve_config(config)?))
})
.collect();
.collect::<std::result::Result<_, nash_config::BuildConflict>>()
.into_diagnostic()?;
let (report, output) = build_with(db, &graph, &modules, move |solved| {
nash_driver::build::build_validators_matching_with(solved, |uri| {
configs.get(uri).copied()
Expand Down
Loading
Loading