1use std::cell::{Cell, RefCell};
8use std::collections::{HashSet, HashMap, VecDeque};
9use std::path::PathBuf;
10
11use rnix::ast::{self, AstToken, HasEntry, InterpolPart};
12use rowan::ast::AstNode;
13
14use crate::builtins;
15use crate::value::*;
16
17thread_local! { static EVAL_DEPTH: Cell<usize> = const { Cell::new(0) }; }
18
19
20thread_local! {
29 static CURRENT_SOURCE_ID: Cell<u32> = const { Cell::new(0) };
30}
31
32thread_local! {
40 static EVAL_FILE_STACK: RefCell<Vec<PathBuf>> = const { RefCell::new(Vec::new()) };
41 static NIX_TRACE_STACK: RefCell<Vec<NixTraceFrame>> = const { RefCell::new(Vec::new()) };
45}
46
47#[derive(Debug, Clone)]
57pub enum NixTraceFrame {
58 Eager {
62 file: Option<String>,
63 description: String,
64 },
65 Lambda {
75 closure_env: Env,
76 current_file: Option<PathBuf>,
77 },
78}
79
80fn strip_source_prefix(p: &std::path::Path) -> String {
83 let s = p.display().to_string();
84 s.rsplit_once("-source/")
85 .map_or_else(|| p.display().to_string(), |(_, tail)| tail.to_string())
86}
87
88impl NixTraceFrame {
89 fn file(&self) -> Option<String> {
92 match self {
93 NixTraceFrame::Eager { file, .. } => file.clone(),
94 NixTraceFrame::Lambda { current_file, .. } => {
95 current_file.as_deref().map(strip_source_prefix)
96 }
97 }
98 }
99
100 fn description(&self) -> String {
105 self.to_string()
106 }
107}
108
109impl std::fmt::Display for NixTraceFrame {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 match self {
115 NixTraceFrame::Eager { description, .. } => f.write_str(description),
116 NixTraceFrame::Lambda { closure_env, .. } => {
117 let file = closure_env.eval_file().map(|p| strip_source_prefix(p));
118 write!(
119 f,
120 "while calling function defined in {}",
121 file.as_deref().unwrap_or("<eval>")
122 )
123 }
124 }
125 }
126}
127
128fn push_nix_trace(desc: impl Into<String>) -> NixTraceGuard {
130 let frame = NixTraceFrame::Eager {
131 file: current_eval_file().map(|p| {
132 p.display().to_string()
133 .rsplit_once("-source/")
134 .map_or_else(|| p.display().to_string(), |(_, s)| s.to_string())
135 }),
136 description: desc.into(),
137 };
138 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
139 NixTraceGuard
140}
141
142fn push_nix_trace_lambda(closure_env: &Env) -> NixTraceGuard {
148 let frame = NixTraceFrame::Lambda {
149 closure_env: closure_env.clone(),
150 current_file: current_eval_file(),
151 };
152 NIX_TRACE_STACK.with(|s| s.borrow_mut().push(frame));
153 NixTraceGuard
154}
155
156struct NixTraceGuard;
157impl Drop for NixTraceGuard {
158 fn drop(&mut self) {
159 NIX_TRACE_STACK.with(|s| s.borrow_mut().pop());
160 }
161}
162
163pub fn attach_trace(err: EvalError) -> EvalError {
165 NIX_TRACE_STACK.with(|s| {
166 let stack = s.borrow();
167 if stack.is_empty() {
168 return err;
169 }
170 let max_frames = std::env::var("SUI_M26_MAXFRAMES").ok()
171 .and_then(|s| s.parse::<usize>().ok()).unwrap_or(15);
172 let mut trace = format!("{err}");
173 for (i, frame) in stack.iter().rev().take(max_frames).enumerate() {
174 let file = frame.file();
175 let loc = file.as_deref().unwrap_or("<eval>");
176 trace.push_str(&format!("\n {} ({loc})", frame.description()));
177 if i + 1 >= max_frames && stack.len() > max_frames {
178 trace.push_str(&format!("\n ... ({} more frames)", stack.len() - max_frames));
179 }
180 }
181 match err {
184 EvalError::Throw(_) => EvalError::Throw(trace),
185 EvalError::AssertionFailed(_) => EvalError::AssertionFailed(trace),
186 _ => EvalError::TypeError(trace),
187 }
188 })
189}
190
191#[must_use]
194pub fn current_eval_dir() -> Option<PathBuf> {
195 EVAL_FILE_STACK.with(|s| s.borrow().last().and_then(|p| p.parent().map(PathBuf::from)))
196}
197
198pub fn push_eval_file(file: PathBuf) -> EvalFileGuard {
202 EVAL_FILE_STACK.with(|s| s.borrow_mut().push(file));
203 EvalFileGuard
204}
205
206#[must_use]
209pub fn current_eval_file() -> Option<PathBuf> {
210 EVAL_FILE_STACK.with(|s| s.borrow().last().cloned())
211}
212
213
214pub fn eval_file_stack_snapshot() -> Vec<String> {
216 EVAL_FILE_STACK.with(|s| {
217 s.borrow().iter().map(|p| {
218 let s = p.display().to_string();
219 s.rsplit_once("-source/").map_or(s.clone(), |(_, r)| r.to_string())
220 }).collect()
221 })
222}
223
224pub(crate) fn eval_file_ctx() -> String {
227 current_eval_file()
228 .map(|p| format!(", in '{}'", p.display()))
229 .unwrap_or_default()
230}
231
232pub struct EvalFileGuard;
234
235impl Drop for EvalFileGuard {
236 fn drop(&mut self) {
237 EVAL_FILE_STACK.with(|s| {
238 s.borrow_mut().pop();
239 });
240 }
241}
242
243pub fn push_source_id(id: u32) -> SourceIdGuard {
249 let prev = CURRENT_SOURCE_ID.with(|s| {
250 let old = s.get();
251 s.set(id);
252 old
253 });
254 SourceIdGuard(prev)
255}
256
257pub struct SourceIdGuard(u32);
259
260impl Drop for SourceIdGuard {
261 fn drop(&mut self) {
262 CURRENT_SOURCE_ID.with(|s| s.set(self.0));
263 }
264}
265
266pub fn normalize_path(path: &std::path::Path) -> std::path::PathBuf {
279 crate::path::normalize(path)
280}
281
282thread_local! {
290 static PURE_MODE: Cell<bool> = const { Cell::new(false) };
291}
292
293pub fn set_pure_mode(pure: bool) {
295 PURE_MODE.with(|p| p.set(pure));
296}
297
298#[must_use]
300pub fn is_pure_mode() -> bool {
301 PURE_MODE.with(Cell::get)
302}
303
304#[cfg(test)]
320const MAX_EVAL_DEPTH: usize = 2_048;
321#[cfg(not(test))]
322const MAX_EVAL_DEPTH: usize = usize::MAX;
323
324struct DepthGuard;
330
331const PROMOTION_RUNAWAY_EVAL_DEPTH: usize = 500;
348
349impl DepthGuard {
350 #[inline(always)]
351 fn enter() -> Result<Self, EvalError> {
352 EVAL_DEPTH.with(|d| {
353 let depth = d.get();
354 if MAX_EVAL_DEPTH != usize::MAX && depth > MAX_EVAL_DEPTH {
355 return Err(EvalError::InfiniteRecursion(
356 "eval depth exceeded".into(),
357 ));
358 }
359 if depth > PROMOTION_RUNAWAY_EVAL_DEPTH
360 && crate::value::promotion_occurred()
361 {
362 return Err(EvalError::InfiniteRecursion(
363 "overlay-fixpoint promotion runaway (eval depth exceeded)".into(),
364 ));
365 }
366 d.set(depth + 1);
367 Ok(DepthGuard)
368 })
369 }
370}
371
372impl Drop for DepthGuard {
373 #[inline(always)]
374 fn drop(&mut self) {
375 EVAL_DEPTH.with(|d| d.set(d.get().saturating_sub(1)));
376 }
377}
378
379fn collect_referenced_names(expr: &ast::Expr) -> HashSet<String> {
396 let mut names = HashSet::new();
397 for node in expr.syntax().descendants() {
398 if let Some(ident) = ast::Ident::cast(node) {
399 names.insert(ident_text(&ident));
400 }
401 }
402 names
403}
404
405fn compute_needed_bindings(
419 body: &ast::Expr,
420 binding_info: &[(String, Option<ast::Expr>)], ) -> HashSet<String> {
422 let body_refs = collect_referenced_names(body);
424
425 let mut all_names: HashSet<String> = HashSet::with_capacity(binding_info.len());
427 let mut deps: HashMap<String, HashSet<String>> = HashMap::with_capacity(binding_info.len());
428
429 for (name, value_expr) in binding_info {
430 all_names.insert(name.clone());
431 if let Some(expr) = value_expr {
432 deps.insert(name.clone(), collect_referenced_names(expr));
433 }
434 }
435
436 let mut needed: HashSet<String> = body_refs.intersection(&all_names).cloned().collect();
438 let mut queue: VecDeque<String> = needed.iter().cloned().collect();
439
440 while let Some(name) = queue.pop_front() {
441 if let Some(name_deps) = deps.get(&name) {
442 for dep in name_deps {
443 if all_names.contains(dep) && needed.insert(dep.clone()) {
444 queue.push_back(dep.clone());
445 }
446 }
447 }
448 }
449
450 needed
451}
452
453#[must_use = "evaluation result should be used"]
455pub fn eval(input: &str) -> Result<Value, EvalError> {
456 eval_with_file(input, None)
457}
458
459thread_local! {
461 static EVAL_NESTING: Cell<usize> = const { Cell::new(0) };
462}
463
464pub fn eval_with_file(input: &str, file: Option<std::path::PathBuf>) -> Result<Value, EvalError> {
471 let nesting = EVAL_NESTING.with(|n| {
472 let v = n.get();
473 n.set(v + 1);
474 v
475 });
476 if nesting == 0 {
477 crate::perf::init();
478 crate::perf::start();
479 crate::trace::init_trace();
480 clear_ident_cache();
483 crate::resolve_env::clear();
487 }
506 let parse = rnix::Root::parse(input);
507 if !parse.errors().is_empty() {
508 let msgs: Vec<String> = parse.errors().iter().map(|e| e.to_string()).collect();
509 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
510 return Err(EvalError::ParseError(msgs.join("; ")));
511 }
512
513 let src_id = next_source_id();
517 if crate::resolve_env::enabled() {
524 let table = sui_resolve::resolve(&parse.tree());
525 crate::resolve_env::populate(src_id, &table);
526 }
527 crate::pos::register_source(file.as_deref(), input);
533 let prev_src_id = CURRENT_SOURCE_ID.with(|s| {
534 let old = s.get();
535 s.set(src_id);
536 old
537 });
538
539 let root = parse.tree();
540 let expr = match root.expr() {
541 Some(e) => e,
542 None => {
543 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
544 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
545 return Err(EvalError::ParseError("empty expression".to_string()));
546 }
547 };
548 let mut env = Env::new();
549 env.set_eval_file(file);
550 env.set_source_id(src_id);
555 builtins::register(&mut env);
556 let result = eval_expr(&expr, &env).map_err(|e| attach_trace(e))?;
557 let final_result = force_value(&result).map_err(|e| attach_trace(e));
559 CURRENT_SOURCE_ID.with(|s| s.set(prev_src_id));
561 EVAL_NESTING.with(|n| n.set(n.get().saturating_sub(1)));
562 if nesting == 0 {
563 crate::perf::report();
564 }
565 final_result
566}
567
568#[inline(always)]
576pub fn force_concrete(value: &Value) -> Result<Concrete, EvalError> {
581 value.demand()
582}
583
584pub fn force_value(value: &Value) -> Result<Value, EvalError> {
588 crate::perf::inc(crate::perf::Counter::ForceValue);
589 if !matches!(value, Value::Thunk(_)) {
592 return Ok(value.clone());
593 }
594 let mut v = value.clone();
609 let mut depth = 0u32;
610 loop {
611 match v {
612 Value::Thunk(ref thunk) => {
613 v = force_thunk(thunk)?;
614 depth += 1;
615 if depth > 100 {
616 return Err(EvalError::InfiniteRecursion(
617 "force_value: thunk chain exceeded depth 100 (cycle or runaway lazy wrap)".into(),
618 ));
619 }
620 }
621 _ => return Ok(v),
622 }
623 }
624}
625
626pub fn force_value_tracked(value: &Value, site: &str) -> Result<Value, EvalError> {
628 crate::perf::inc(crate::perf::Counter::ForceValue);
629 if let Value::Thunk(thunk) = value {
630 FORCE_SITES.with(|sites| {
631 *sites.borrow_mut().entry(site.to_string()).or_insert(0) += 1;
632 });
633 force_thunk(thunk)
634 } else {
635 Ok(value.clone())
636 }
637}
638
639thread_local! {
640 static FORCE_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
641 std::cell::RefCell::new(std::collections::HashMap::new());
642 static APPLY_SITES: std::cell::RefCell<std::collections::HashMap<String, u64>> =
643 std::cell::RefCell::new(std::collections::HashMap::new());
644}
645
646pub fn dump_force_sites() {
648 FORCE_SITES.with(|sites| {
649 let sites = sites.borrow();
650 let mut sorted: Vec<_> = sites.iter().collect();
651 sorted.sort_by(|a, b| b.1.cmp(a.1));
652 eprintln!("[force-sites] top thunk force call sites:");
653 for (site, count) in sorted.iter().take(10) {
654 eprintln!(" {count:>8} {site}");
655 }
656 });
657 APPLY_SITES.with(|sites| {
658 let sites = sites.borrow();
659 let mut sorted: Vec<_> = sites.iter().collect();
660 sorted.sort_by(|a, b| b.1.cmp(a.1));
661 eprintln!("[apply-sites] top lambda call sites by source file:");
662 for (site, count) in sorted.iter().take(15) {
663 let short = site.rsplit_once("-source/").map_or(site.as_str(), |(_,s)| s);
665 eprintln!(" {count:>8} {short}");
666 }
667 });
668}
669
670fn force_thunk(thunk: &Thunk) -> Result<Value, EvalError> {
674 if let Some(cached) = thunk.peek() {
676 crate::perf::inc(crate::perf::Counter::ThunkHit);
677 return Ok(cached.clone().into_value());
678 }
679 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
680 thunk.force(&|expr, env| eval_expr(expr, env))
686 })
687}
688
689fn referenced_idents(value_expr: &ast::Expr) -> HashSet<SmolStr> {
756 use rnix::SyntaxKind;
757 let perf_on = crate::perf::enabled();
763 let t0 = if perf_on {
764 Some(std::time::Instant::now())
765 } else {
766 None
767 };
768 crate::perf::inc(crate::perf::Counter::SelfRecWalkCalls);
769 let mut nodes_walked: u64 = 0;
770 let mut set: HashSet<SmolStr> = HashSet::new();
771 for node in value_expr.syntax().descendants() {
772 nodes_walked += 1;
773 if node.kind() == SyntaxKind::NODE_IDENT
774 && node
775 .parent()
776 .is_none_or(|p| p.kind() != SyntaxKind::NODE_ATTRPATH)
777 && let Some(i) = ast::Ident::cast(node)
778 {
779 set.insert(SmolStr::from(ident_text(&i).as_str()));
780 }
781 }
782 crate::perf::add(crate::perf::Counter::SelfRecWalkNodes, nodes_walked);
783 if let Some(t0) = t0 {
784 crate::trace::add_self_rec_walk_nanos(t0.elapsed().as_nanos());
785 }
786 set
787}
788
789fn is_self_recursive_binding(value_expr: &ast::Expr, name: &str) -> bool {
793 referenced_idents(value_expr).contains(name)
794}
795
796fn maybe_thunk(
797 expr: &ast::Expr,
798 env: &Env,
799 is_rec: bool,
800 defined_so_far: Option<&HashSet<String>>,
801) -> Value {
802 match expr {
803 ast::Expr::Literal(lit) => eval_literal(lit).unwrap_or_else(|_| {
805 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
806 }),
807 ast::Expr::Ident(ident) if !is_rec => {
814 let sym = {
824 let src_id = env.source_id();
825 let offset = u32::from(ident.syntax().text_range().start());
826 crate::value::intern_cached_with(src_id, offset, || {
827 crate::value::intern(&ident_text(ident))
828 })
829 };
830 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
832 "true" => Some(Value::Bool(true)),
833 "false" => Some(Value::Bool(false)),
834 "null" => Some(Value::Null),
835 _ => None,
836 }) {
837 return kw;
838 }
839 {
840 {
841 if let Some(v) = env.lookup_fast(sym, "") {
845 return v;
846 }
847 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
850 return Value::Thunk(Thunk::new_with_ident(
851 SmolStr::from(ident_text(ident).as_str()),
852 scope_cache,
853 scope_value,
854 env.clone(),
855 ));
856 }
857 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
858 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
859 }
860 }
861 }
862 ast::Expr::Ident(ident) if is_rec => {
866 let name = ident_text(ident);
867 match name.as_str() {
868 "true" => Value::Bool(true),
869 "false" => Value::Bool(false),
870 "null" => Value::Null,
871 _ => {
872 if defined_so_far.map_or(false, |d| d.contains(&name)) {
875 env.lookup(&name).unwrap_or_else(|| {
876 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
877 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
878 })
879 } else {
880 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeIdent);
882 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
883 }
884 }
885 }
886 }
887 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
892 let text = crate::path::canon_abs(&p.syntax().text().to_string());
898 Value::Path(Box::new(SmolStr::from(text.as_str())))
899 }
900 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
901 let text = p.syntax().text().to_string();
902 Value::Path(Box::new(SmolStr::from(text.as_str())))
903 }
904 ast::Expr::Str(st) if !str_has_interpolation(st) => {
917 eval_str(st, env).unwrap_or_else(|_| {
918 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
919 })
920 }
921 ast::Expr::Lambda(lam) if !is_rec => {
925 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
926 Value::Lambda(Rc::new(Closure {
927 param,
928 body,
929 env: env.clone(),
930 }))
931 } else {
932 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
933 }
934 }
935 _ => {
946 crate::perf::inc(crate::perf::Counter::ThunkSiteMaybeOther);
947 if crate::perf::enabled() {
948 let kind = match expr {
949 ast::Expr::Select(_) => "Select",
950 ast::Expr::Apply(_) => "Apply",
951 ast::Expr::BinOp(_) => "BinOp",
952 ast::Expr::IfElse(_) => "IfElse",
953 ast::Expr::Str(_) => "Str",
954 ast::Expr::List(_) => "List",
955 ast::Expr::With(_) => "With",
956 ast::Expr::Assert(_) => "Assert",
957 ast::Expr::HasAttr(_) => "HasAttr",
958 ast::Expr::UnaryOp(_) => "UnaryOp",
959 ast::Expr::Paren(_) => "Paren",
960 ast::Expr::LetIn(_) => "LetIn",
961 ast::Expr::AttrSet(_) => "AttrSet",
962 ast::Expr::Ident(_) => "Ident(rec)",
963 ast::Expr::Lambda(_) => "Lambda(rec)",
964 ast::Expr::LegacyLet(_) => "LegacyLet",
965 ast::Expr::PathAbs(_)
966 | ast::Expr::PathHome(_)
967 | ast::Expr::PathRel(_)
968 | ast::Expr::PathSearch(_) => "Path(interp)",
969 _ => "Other",
970 };
971 crate::trace::inc_maybe_other_kind(kind);
972 }
973 Value::Thunk(Thunk::new_suspended(expr.clone(), env.clone()))
974 }
975 }
976}
977
978#[inline(always)]
989pub fn eval_expr(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
990 match expr {
993 ast::Expr::Ident(ident) => {
994 crate::perf::inc(crate::perf::Counter::EvalExpr);
995 if crate::perf::enabled() {
996 crate::perf::inc(crate::perf::Counter::ExprIdent);
997 }
998 if crate::resolve_env::enabled() {
1011 let src_id = CURRENT_SOURCE_ID.with(std::cell::Cell::get);
1012 let offset = u32::from(ident.syntax().text_range().start());
1013 if let sui_resolve::Resolution::Lexical { sym } =
1014 crate::resolve_env::resolution_for(src_id, offset)
1015 {
1016 if let Some(v) = env.lookup_lexical_sym(sym) {
1017 return Ok(v);
1018 }
1019 }
1020 }
1022 let sym = {
1056 let src_id = env.source_id();
1057 let offset = u32::from(ident.syntax().text_range().start());
1058 crate::value::intern_cached_with(src_id, offset, || {
1059 crate::value::intern(&ident_text(ident))
1060 })
1061 };
1062 if let Some(kw) = crate::value::with_resolved(sym, |s| match s {
1066 "true" => Some(Value::Bool(true)),
1067 "false" => Some(Value::Bool(false)),
1068 "null" => Some(Value::Null),
1069 _ => None,
1070 }) {
1071 return Ok(kw);
1072 }
1073 return {
1074 {
1075 if let Some(v) = env.lookup_fast(sym, "") {
1079 Ok(v)
1080 } else {
1081 let name = ident_text(ident);
1082 let fresh = crate::value::intern(name.as_str());
1101 if fresh != sym {
1102 if let Some(v) = env.lookup_fast(fresh, name.as_str()) {
1103 return Ok(v);
1104 }
1105 }
1106 if env.with_scope_count() > 0 {
1107 if let Some((scope_cache, scope_value)) = env.innermost_with_scope() {
1111 Ok(Value::Thunk(Thunk::new_with_ident(
1112 SmolStr::from(name.as_str()),
1113 scope_cache,
1114 scope_value,
1115 env.clone(),
1116 )))
1117 } else if crate::value::in_promise_eval() {
1118 Ok(Value::Null)
1128 } else {
1129 Err(EvalError::UndefinedVar(
1130 format!("'{name}'{}", eval_file_ctx()),
1131 ))
1132 }
1133 } else {
1134 if let Ok(dbg_var) = std::env::var("SUI_DEBUG_VAR") {
1135 if dbg_var == name || dbg_var == "*" {
1136 eprintln!(
1137 "[sui-debug] UndefinedVar '{name}' in {}\n\
1138 [sui-debug] env bindings ({} total): {:?}\n\
1139 [sui-debug] with_scopes: {}",
1140 eval_file_ctx(),
1141 env.binding_count(),
1142 env.binding_names_preview(20),
1143 env.with_scope_count(),
1144 );
1145 }
1146 }
1147 if crate::value::in_promise_eval() {
1148 return Ok(Value::Null);
1151 }
1152 Err(EvalError::UndefinedVar(
1153 format!("'{name}'{}", eval_file_ctx()),
1154 ))
1155 }
1156 }
1157 }
1158 };
1159 }
1160 ast::Expr::Literal(lit) => {
1161 crate::perf::inc(crate::perf::Counter::EvalExpr);
1162 if crate::perf::enabled() {
1163 crate::perf::inc(crate::perf::Counter::ExprLiteral);
1164 }
1165 return eval_literal(lit);
1166 }
1167 ast::Expr::Paren(p) => {
1168 if let Some(inner) = p.expr() {
1169 return eval_expr(&inner, env);
1170 }
1171 }
1172 ast::Expr::Root(r) => {
1173 if let Some(inner) = r.expr() {
1174 return eval_expr(&inner, env);
1175 }
1176 }
1177 ast::Expr::Lambda(lam) => {
1179 crate::perf::inc(crate::perf::Counter::EvalExpr);
1180 if crate::perf::enabled() {
1181 crate::perf::inc(crate::perf::Counter::ExprLambda);
1182 }
1183 if let (Some(param), Some(body)) = (lam.param(), lam.body()) {
1184 return Ok(Value::Lambda(Rc::new(Closure {
1185 param,
1186 body,
1187 env: env.clone(),
1188 })));
1189 }
1190 }
1191 _ => {}
1192 }
1193 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || {
1195 eval_expr_inner(expr, env)
1196 })
1197}
1198
1199fn eval_expr_inner(expr: &ast::Expr, env: &Env) -> Result<Value, EvalError> {
1207 let mut cur_expr = expr.clone();
1210 let mut cur_env = env.clone();
1211
1212 loop {
1213 crate::perf::inc(crate::perf::Counter::EvalExpr);
1214 if crate::perf::enabled() {
1216 use crate::perf::Counter;
1217 let c = match &cur_expr {
1218 ast::Expr::Ident(_) => Counter::ExprIdent,
1219 ast::Expr::Literal(_) => Counter::ExprLiteral,
1220 ast::Expr::Str(_) => Counter::ExprStr,
1221 ast::Expr::List(_) => Counter::ExprList,
1222 ast::Expr::AttrSet(_) => Counter::ExprAttrs,
1223 ast::Expr::Select(_) => Counter::ExprSelect,
1224 ast::Expr::Apply(_) => Counter::ExprApply,
1225 ast::Expr::LetIn(_) => Counter::ExprLetIn,
1226 ast::Expr::IfElse(_) => Counter::ExprIfElse,
1227 ast::Expr::With(_) => Counter::ExprWith,
1228 ast::Expr::Lambda(_) => Counter::ExprLambda,
1229 ast::Expr::BinOp(_) => Counter::ExprBinOp,
1230 ast::Expr::HasAttr(_) => Counter::ExprHasAttr,
1231 ast::Expr::UnaryOp(_) => Counter::ExprUnaryOp,
1232 ast::Expr::Assert(_) => Counter::ExprAssert,
1233 ast::Expr::PathAbs(_) | ast::Expr::PathRel(_)
1234 | ast::Expr::PathHome(_) | ast::Expr::PathSearch(_) => Counter::ExprPath,
1235 _ => Counter::ExprOther,
1236 };
1237 crate::perf::inc(c);
1238 }
1239 let _guard = DepthGuard::enter()?;
1240 let env = &cur_env;
1241 match &cur_expr {
1242 ast::Expr::Literal(lit) => return eval_literal(lit),
1243
1244 ast::Expr::Str(s) => return eval_str(s, env),
1245
1246 ast::Expr::PathAbs(p) => {
1247 let parts = p.parts();
1250 if parts_have_interpolation(&parts) {
1251 return eval_interpol_path_parts(&parts, PathKind::Abs, env);
1252 }
1253 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1256 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1257 }
1258 ast::Expr::PathRel(p) => {
1259 let parts = p.parts();
1270 if parts_have_interpolation(&parts) {
1271 return eval_interpol_path_parts(&parts, PathKind::Rel, env);
1272 }
1273 let text = p.syntax().text().to_string();
1274 let resolved = if let Some(dir) = current_eval_dir() {
1275 let joined = dir.join(&text);
1276 let norm = normalize_path(&joined);
1280 crate::path::dematerialize(&norm)
1290 .to_string_lossy()
1291 .into_owned()
1292 } else {
1293 text.clone()
1294 };
1295 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1296 }
1297 ast::Expr::PathHome(p) => {
1298 let parts = p.parts();
1299 if parts_have_interpolation(&parts) {
1300 return eval_interpol_path_parts(&parts, PathKind::Home, env);
1301 }
1302 let text = p.syntax().text().to_string();
1303 return Ok(Value::Path(Box::new(SmolStr::from(text.as_str()))));
1304 }
1305 ast::Expr::PathSearch(p) => {
1306 let text = p.syntax().text().to_string();
1311 let inner = text
1312 .strip_prefix('<')
1313 .and_then(|s| s.strip_suffix('>'))
1314 .unwrap_or(&text);
1315 if let Some(resolved) = crate::builtins::resolve_search_path(inner) {
1316 return Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))));
1317 }
1318 return Err(EvalError::Throw(
1322 format!("search path '{text}' not in NIX_PATH"),
1323 ));
1324 }
1325
1326 ast::Expr::Ident(ident) => {
1327 let name = ident_text(ident);
1328 return match name.as_str() {
1329 "true" => Ok(Value::Bool(true)),
1330 "false" => Ok(Value::Bool(false)),
1331 "null" => Ok(Value::Null),
1332 _ => {
1333 env.lookup(&name)
1334 .ok_or_else(|| EvalError::UndefinedVar(
1335 format!("'{name}'{}", eval_file_ctx()),
1336 ))
1337 }
1338 };
1339 }
1340
1341 ast::Expr::List(list) => {
1342 let values: Vec<Value> = list.items()
1347 .map(|e| maybe_thunk(&e, env, false, None))
1348 .collect();
1349 return Ok(Value::list(values));
1350 }
1351
1352 ast::Expr::AttrSet(set) => return eval_attrset(set, env),
1353
1354 ast::Expr::Select(sel) => return eval_select(sel, env),
1355
1356 ast::Expr::HasAttr(ha) => return eval_has_attr(ha, env),
1357
1358 ast::Expr::UnaryOp(op) => return eval_unary_op(op, env),
1359
1360 ast::Expr::BinOp(binop) => {
1361 let lhs_expr = binop
1362 .lhs()
1363 .ok_or_else(|| EvalError::ParseError("binop missing lhs".to_string()))?;
1364 let rhs_expr = binop
1365 .rhs()
1366 .ok_or_else(|| EvalError::ParseError("binop missing rhs".to_string()))?;
1367 let kind = binop
1368 .operator()
1369 .ok_or_else(|| EvalError::ParseError("binop missing operator".to_string()))?;
1370 return eval_binop(kind, &lhs_expr, &rhs_expr, env);
1371 }
1372
1373 ast::Expr::Apply(app) => return eval_apply(app, env),
1374
1375 ast::Expr::IfElse(ie) => {
1376 let cond = ie
1377 .condition()
1378 .ok_or_else(|| EvalError::ParseError("if missing condition".to_string()))?;
1379 let body = ie
1380 .body()
1381 .ok_or_else(|| EvalError::ParseError("if missing then body".to_string()))?;
1382 let else_body = ie
1383 .else_body()
1384 .ok_or_else(|| EvalError::ParseError("if missing else body".to_string()))?;
1385 if force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1386 cur_expr = body;
1387 } else {
1388 cur_expr = else_body;
1389 }
1390 continue;
1392 }
1393
1394 ast::Expr::Assert(assert) => {
1395 let cond = assert
1396 .condition()
1397 .ok_or_else(|| EvalError::ParseError("assert missing condition".to_string()))?;
1398 let body = assert
1399 .body()
1400 .ok_or_else(|| EvalError::ParseError("assert missing body".to_string()))?;
1401 if !force_concrete(&eval_expr(&cond, env)?)?.as_bool()? {
1402 return Err(EvalError::AssertionFailed(eval_file_ctx()));
1403 }
1404 cur_expr = body;
1405 continue;
1406 }
1407
1408 ast::Expr::With(with) => {
1409 let ns = with
1410 .namespace()
1411 .ok_or_else(|| EvalError::ParseError("with missing namespace".to_string()))?;
1412 let body = with
1413 .body()
1414 .ok_or_else(|| EvalError::ParseError("with missing body".to_string()))?;
1415 let scope_val = maybe_thunk(&ns, env, false, None);
1441 let new_env = env.child().with_scope(scope_val);
1442 cur_expr = body;
1443 cur_env = new_env;
1444 continue;
1445 }
1446
1447 ast::Expr::LetIn(letin) => {
1448 let mut new_env = env.child();
1449
1450 let mut thunks: Vec<(String, Thunk)> = Vec::new();
1453
1454 let mut defined_so_far: HashSet<String> = HashSet::new();
1458
1459 let mut dotted_attrs: NixAttrs = NixAttrs::new();
1463
1464 let let_scope_names: HashSet<String> = {
1470 let mut s = HashSet::new();
1471 for entry in letin.entries() {
1472 match entry {
1473 ast::Entry::AttrpathValue(apv) => {
1474 if let Some(attrpath) = apv.attrpath() {
1475 if let Some(first) = attrpath.attrs().next() {
1476 if let Ok(name) = eval_attr(&first, env) {
1477 s.insert(name);
1478 }
1479 }
1480 }
1481 }
1482 ast::Entry::Inherit(inherit) => {
1483 for attr in inherit.attrs() {
1484 if let Ok(name) = eval_attr(&attr, env) {
1485 s.insert(name);
1486 }
1487 }
1488 }
1489 }
1490 }
1491 s
1492 };
1493
1494 for entry in letin.entries() {
1495 match entry {
1496 ast::Entry::AttrpathValue(ref apv) => {
1497 let attrpath = apv.attrpath().ok_or_else(|| {
1498 EvalError::ParseError("binding missing attrpath".to_string())
1499 })?;
1500 let value_expr = apv.value().ok_or_else(|| {
1501 EvalError::ParseError("binding missing value".to_string())
1502 })?;
1503 let mut path_keys: Vec<String> = attrpath
1504 .attrs()
1505 .map(|a| eval_attr(&a, env))
1506 .collect::<Result<_, _>>()?;
1507 if path_keys.len() == 1 {
1508 let key = path_keys.pop().unwrap();
1509 let referenced = referenced_idents(&value_expr);
1532 let in_mutual_cycle = std::iter::once(&key)
1533 .chain(let_scope_names.iter())
1534 .any(|n| referenced.contains(n.as_str()));
1535 let value = if in_mutual_cycle {
1536 Value::Thunk(Thunk::new_suspended_recursive(
1537 value_expr.clone(),
1538 env.clone(),
1539 ))
1540 } else {
1541 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
1542 };
1543 new_env.bind(key.clone(), value.clone());
1544 if let Value::Thunk(t) = &value {
1545 thunks.push((key.clone(), t.clone()));
1546 }
1547 defined_so_far.insert(key);
1548 } else if path_keys.len() > 1 {
1549 let key = path_keys[0].clone();
1554 let value = build_nested_attr_thunk(
1555 &path_keys[1..],
1556 &value_expr,
1557 env,
1558 &mut thunks,
1559 );
1560 merge_nested_insert(&mut dotted_attrs, key, value);
1561 }
1562 }
1563 ast::Entry::Inherit(ref inherit) => {
1564 if let Some(from) = inherit.from() {
1565 let source_expr = from.expr().ok_or_else(|| {
1566 EvalError::ParseError(
1567 "inherit from missing expr".to_string(),
1568 )
1569 })?;
1570 let source_thunk = Thunk::new_suspended(
1575 source_expr, env.clone(),
1576 );
1577 for attr in inherit.attrs() {
1578 let name = eval_attr(&attr, env)?;
1579 let thunk = Thunk::new_inherit_select(
1580 source_thunk.clone(),
1581 name.clone(),
1582 );
1583 new_env.bind(name.clone(), Value::Thunk(thunk.clone()));
1584 thunks.push((name, thunk));
1585 }
1586 } else {
1587 for attr in inherit.attrs() {
1592 let name = eval_attr(&attr, env)?;
1593 let value = env.lookup(&name).ok_or_else(|| {
1594 EvalError::UndefinedVar(
1595 format!("'{name}'{}", eval_file_ctx()),
1596 )
1597 })?;
1598 new_env.bind(name, value);
1599 }
1600 }
1601 }
1602 }
1603 }
1604
1605 for (key, value) in dotted_attrs.iter() {
1610 new_env.bind(key.clone(), value.clone());
1611 }
1612
1613 for (_key, thunk) in &thunks {
1616 thunk.update_env(&new_env);
1617 }
1618
1619 let body = letin
1620 .body()
1621 .ok_or_else(|| EvalError::ParseError("let missing body".to_string()))?;
1622 cur_expr = body;
1623 cur_env = new_env;
1624 continue;
1625 }
1626
1627 ast::Expr::Lambda(lam) => {
1628 let param = lam
1629 .param()
1630 .ok_or_else(|| EvalError::ParseError("lambda missing param".to_string()))?;
1631 let body = lam
1632 .body()
1633 .ok_or_else(|| EvalError::ParseError("lambda missing body".to_string()))?;
1634 return Ok(Value::Lambda(Rc::new(Closure {
1635 param,
1636 body,
1637 env: env.clone(),
1638 })));
1639 }
1640
1641 ast::Expr::Paren(p) => {
1642 let inner = p
1643 .expr()
1644 .ok_or_else(|| EvalError::ParseError("paren missing expr".to_string()))?;
1645 cur_expr = inner;
1646 continue;
1647 }
1648
1649 ast::Expr::Root(r) => {
1650 let inner = r
1651 .expr()
1652 .ok_or_else(|| EvalError::ParseError("root missing expr".to_string()))?;
1653 cur_expr = inner;
1654 continue;
1655 }
1656
1657 ast::Expr::LegacyLet(ll) => {
1658 let mut new_env = env.child();
1659 eval_entries(ll, &mut new_env)?;
1660 return new_env
1662 .lookup("body")
1663 .ok_or_else(|| EvalError::AttrNotFound(
1664 format!("'body' in legacy let{}", eval_file_ctx()),
1665 ));
1666 }
1667
1668 ast::Expr::CurPos(_) => return Err(EvalError::NotImplemented("__curPos".to_string())),
1669 ast::Expr::Error(_) => return Err(EvalError::ParseError("parse error node".to_string())),
1670 } } }
1673
1674fn eval_literal(lit: &ast::Literal) -> Result<Value, EvalError> {
1675 use ast::LiteralKind;
1676 match lit.kind() {
1677 LiteralKind::Integer(tok) => {
1678 let n = tok
1679 .value()
1680 .map_err(|e| EvalError::ParseError(format!("invalid integer: {e}")))?;
1681 Ok(Value::Int(n))
1682 }
1683 LiteralKind::Float(tok) => {
1684 let f = tok
1685 .value()
1686 .map_err(|e| EvalError::ParseError(format!("invalid float: {e}")))?;
1687 Ok(Value::Float(f))
1688 }
1689 LiteralKind::Uri(tok) => Ok(Value::string(tok.syntax().text().to_string())),
1690 }
1691}
1692
1693enum TraverseResult {
1695 Found(Value),
1697 Missing(String),
1699 NotAttrs(Value),
1701}
1702
1703fn traverse_attrpath(
1708 base: Value,
1709 attrpath: &rnix::ast::Attrpath,
1710 env: &Env,
1711) -> Result<TraverseResult, EvalError> {
1712 let attrs: Vec<_> = attrpath.attrs().collect();
1713 let mut value = base;
1714 for (i, attr) in attrs.iter().enumerate() {
1715 let key = eval_attr(attr, env)?;
1716 let forced = force_value(&value)?;
1718 match forced {
1719 Value::Attrs(ref a) => match a.get(&key) {
1720 Some(v) => {
1721 if i < attrs.len() - 1 {
1722 value = force_value(v)?;
1724 } else {
1725 value = v.clone();
1728 }
1729 }
1730 None => return Ok(TraverseResult::Missing(key)),
1731 },
1732 _ => return Ok(TraverseResult::NotAttrs(forced)),
1733 }
1734 }
1735 Ok(TraverseResult::Found(value))
1736}
1737
1738fn eval_select(sel: &ast::Select, env: &Env) -> Result<Value, EvalError> {
1739 crate::perf::inc(crate::perf::Counter::Select);
1740 let base_expr = sel.expr().ok_or_else(|| {
1741 EvalError::ParseError("select missing expression".to_string())
1742 })?;
1743 let base_result = eval_expr(&base_expr, env)
1752 .and_then(|v| force_concrete(&v).map(Concrete::into_value));
1753 let base = match base_result {
1754 Ok(v) => v,
1755 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1756 return eval_expr(&sel.default_expr().expect("checked"), env);
1757 }
1758 Err(e) => return Err(e),
1759 };
1760 let base_type = base.type_name();
1761 let attrpath = sel.attrpath().ok_or_else(|| {
1762 EvalError::ParseError("select missing attrpath".to_string())
1763 })?;
1764 let bridge_active = std::env::var_os("SUI_BLACKHOLE_AS_EMPTY_ATTRS").is_some()
1786 || std::env::var_os("SUI_BLACKHOLE_AS_NULL").is_some();
1787 let traversal = traverse_attrpath(base, &attrpath, env);
1788 match traversal {
1789 Ok(TraverseResult::Found(v)) => Ok(v),
1790 Ok(TraverseResult::Missing(key)) => {
1791 if let Some(def) = sel.default_expr() {
1792 eval_expr(&def, env)
1793 } else if bridge_active {
1794 if std::env::var_os("SUI_M26_SELTRACE").is_some() {
1795 let path: Vec<String> = sel.attrpath().map(|ap|
1796 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1797 ).unwrap_or_default();
1798 eprintln!("[M26 SEL-MISS→null] base_type={base_type} path={path:?} missing-key={key}{}", eval_file_ctx());
1799 }
1800 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1801 let path: Vec<String> = sel.attrpath().map(|ap|
1802 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1803 ).unwrap_or_default();
1804 if path.iter().any(|p| p.contains(&filt)) {
1805 return Err(EvalError::type_error(format!(
1806 "M26-HARDSOFTEN path={path:?} key={key}"
1807 )));
1808 }
1809 }
1810 Ok(Value::Null)
1811 } else {
1812 Err(EvalError::AttrNotFound(
1813 format!("'{key}'{}", eval_file_ctx()),
1814 ))
1815 }
1816 }
1817 Ok(TraverseResult::NotAttrs(forced)) => {
1818 if let Some(def) = sel.default_expr() {
1824 eval_expr(&def, env)
1825 } else if bridge_active {
1826 if let Ok(filt) = std::env::var("SUI_M26_HARDSOFTEN") {
1827 let path: Vec<String> = sel.attrpath().map(|ap|
1828 ap.attrs().map(|a| a.syntax().text().to_string()).collect()
1829 ).unwrap_or_default();
1830 if path.iter().any(|p| p.contains(&filt)) {
1831 return Err(EvalError::type_error(format!(
1832 "M26-HARDSOFTEN-NOTATTRS path={path:?} base_type={base_type}"
1833 )));
1834 }
1835 }
1836 return Ok(Value::Null);
1837 } else {
1838 if std::env::var("SUI_DEBUG_SELECT").is_ok() {
1839 let path: Vec<String> = sel.attrpath().map(|ap|
1840 ap.attrs().filter_map(|a| match a {
1841 ast::Attr::Ident(i) => Some(i.to_string()),
1842 ast::Attr::Str(s) => Some(format!("\"{}\"", s.syntax().text())),
1843 ast::Attr::Dynamic(_) => Some("<dyn>".into()),
1844 }).collect()
1845 ).unwrap_or_default();
1846 let dbg = format!("{:?}", forced);
1847 let truncated = if dbg.len() > 200 { format!("{}…", &dbg[..200]) } else { dbg };
1848 eprintln!("[SUI_DEBUG_SELECT] base_type={base_type} path={path:?} base={truncated}{}", eval_file_ctx());
1849 }
1850 Err(attach_trace(EvalError::type_error(
1851 format!("cannot select from {base_type}"),
1852 )))
1853 }
1854 }
1855 Err(EvalError::InfiniteRecursion(_)) if sel.default_expr().is_some() => {
1860 eval_expr(&sel.default_expr().expect("checked"), env)
1861 }
1862 Err(e) => Err(e),
1863 }
1864}
1865
1866fn eval_has_attr(ha: &ast::HasAttr, env: &Env) -> Result<Value, EvalError> {
1868 let base_expr = ha.expr().ok_or_else(|| {
1869 EvalError::ParseError("hasattr missing expression".to_string())
1870 })?;
1871 let base = force_concrete(&eval_expr(&base_expr, env)?)?.into_value();
1872 let attrpath = ha.attrpath().ok_or_else(|| {
1873 EvalError::ParseError("hasattr missing attrpath".to_string())
1874 })?;
1875 match traverse_attrpath(base, &attrpath, env)? {
1876 TraverseResult::Found(_) => Ok(Value::Bool(true)),
1877 TraverseResult::Missing(_) | TraverseResult::NotAttrs(_) => Ok(Value::Bool(false)),
1878 }
1879}
1880
1881fn eval_unary_op(op: &ast::UnaryOp, env: &Env) -> Result<Value, EvalError> {
1882 let inner = op
1883 .expr()
1884 .ok_or_else(|| EvalError::ParseError("unary op missing expr".to_string()))?;
1885 let val = force_value(&eval_expr(&inner, env)?)?;
1886 let kind = op
1887 .operator()
1888 .ok_or_else(|| EvalError::ParseError("unary op missing operator".to_string()))?;
1889 match kind {
1890 ast::UnaryOpKind::Negate => match val {
1891 Value::Int(n) => Ok(Value::Int(-n)),
1892 Value::Float(f) => Ok(Value::Float(-f)),
1893 _ => Err(EvalError::type_error(
1894 format!("cannot negate {}", val.type_name()),
1895 )),
1896 },
1897 ast::UnaryOpKind::Invert => Ok(Value::Bool(!val.as_bool()?)),
1898 }
1899}
1900
1901#[inline]
1912pub(crate) fn builtin_takes_lazy_arg(name: &str) -> bool {
1913 matches!(
1914 name,
1915 "tryEval" | "addErrorContext<partial>" | "seq<partial>" | "deepSeq<partial>" | "foldl'<p1>"
1916 )
1917}
1918
1919fn eval_apply(app: &ast::Apply, env: &Env) -> Result<Value, EvalError> {
1920 let func_expr = app
1921 .lambda()
1922 .ok_or_else(|| EvalError::ParseError("apply missing function".to_string()))?;
1923 let arg_expr = app
1924 .argument()
1925 .ok_or_else(|| EvalError::ParseError("apply missing argument".to_string()))?;
1926 let func = force_value(&eval_expr(&func_expr, env)?)?;
1927 let arg = match &func {
1935 Value::Lambda(_) => {
1936 if let Some(v) = eval_pure_constant_arg(&arg_expr) {
1945 v
1946 } else {
1947 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1948 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1949 }
1950 }
1951 Value::Builtin(b) if builtin_takes_lazy_arg(&b.name) => {
1952 crate::perf::inc(crate::perf::Counter::ThunkSiteApplyArg);
1957 Value::Thunk(Thunk::new_suspended(arg_expr.clone(), env.clone()))
1958 }
1959 _ => eval_expr(&arg_expr, env)?,
1960 };
1961 apply(func, arg)
1962}
1963
1964fn eval_pure_constant_arg(arg_expr: &ast::Expr) -> Option<Value> {
1979 match arg_expr {
1980 ast::Expr::Literal(lit) => eval_literal(lit).ok(),
1981 ast::Expr::Str(st) if !str_has_interpolation(st) => {
1982 eval_str(st, &Env::new()).ok()
1984 }
1985 ast::Expr::PathAbs(p) if !parts_have_interpolation(&p.parts()) => {
1986 let text = crate::path::canon_abs(&p.syntax().text().to_string());
1987 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1988 }
1989 ast::Expr::PathHome(p) if !parts_have_interpolation(&p.parts()) => {
1990 let text = p.syntax().text().to_string();
1991 Some(Value::Path(Box::new(SmolStr::from(text.as_str()))))
1992 }
1993 _ => None,
1994 }
1995}
1996
1997fn eval_str(s: &ast::Str, env: &Env) -> Result<Value, EvalError> {
1998 let mut result = String::new();
1999 let mut ctx = StringContext::new();
2000 for part in s.normalized_parts() {
2001 match part {
2002 InterpolPart::Literal(text) => result.push_str(&text),
2003 InterpolPart::Interpolation(interpol) => {
2004 let expr = interpol.expr().ok_or_else(|| {
2005 EvalError::ParseError("interpolation missing expr".to_string())
2006 })?;
2007 let val = force_value(&eval_expr(&expr, env)?)?;
2008 let (s, c) = val.coerce_to_string_copy_to_store()?;
2013 result.push_str(&s);
2014 ctx.merge(&c);
2015 }
2016 }
2017 }
2018 Ok(Value::String(Rc::new(NixString::with_context(result, ctx))))
2019}
2020
2021fn parts_have_interpolation(parts: &[InterpolPart<rnix::ast::PathContent>]) -> bool {
2025 parts
2026 .iter()
2027 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2028}
2029
2030fn str_has_interpolation(s: &ast::Str) -> bool {
2034 s.normalized_parts()
2035 .iter()
2036 .any(|p| matches!(p, InterpolPart::Interpolation(_)))
2037}
2038
2039fn eval_interpol_path_parts(
2054 parts: &[InterpolPart<rnix::ast::PathContent>],
2055 kind: PathKind,
2056 env: &Env,
2057) -> Result<Value, EvalError> {
2058 let mut text = String::new();
2059 for part in parts {
2060 match part {
2061 InterpolPart::Literal(content) => text.push_str(content.text()),
2062 InterpolPart::Interpolation(interpol) => {
2063 let expr = interpol.expr().ok_or_else(|| {
2064 EvalError::ParseError("path interpolation missing expr".to_string())
2065 })?;
2066 let val = force_value(&eval_expr(&expr, env)?)?;
2067 let (s, _ctx) = val.coerce_to_string()?;
2071 text.push_str(&s);
2072 }
2073 }
2074 }
2075 let resolved = match kind {
2076 PathKind::Rel => {
2079 if let Some(dir) = current_eval_dir() {
2080 let norm = normalize_path(&dir.join(&text));
2081 crate::path::dematerialize(&norm).to_string_lossy().into_owned()
2090 } else {
2091 text
2095 }
2096 }
2097 PathKind::Abs => crate::path::canon_abs(&text),
2105 PathKind::Home => normalize_path(std::path::Path::new(&text))
2108 .to_string_lossy()
2109 .into_owned(),
2110 };
2111 Ok(Value::Path(Box::new(SmolStr::from(resolved.as_str()))))
2112}
2113
2114#[derive(Clone, Copy)]
2117enum PathKind {
2118 Abs,
2119 Rel,
2120 Home,
2121}
2122
2123fn eval_attr(attr: &ast::Attr, env: &Env) -> Result<String, EvalError> {
2126 eval_attr_maybe_null(attr, env)?
2127 .ok_or_else(|| EvalError::TypeError("null dynamic attribute name".into()))
2128}
2129
2130fn eval_attr_maybe_null(attr: &ast::Attr, env: &Env) -> Result<Option<String>, EvalError> {
2133 match attr {
2134 ast::Attr::Ident(ident) => Ok(Some(ident_text(ident))),
2135 ast::Attr::Dynamic(dyn_) => {
2136 let expr = dyn_
2137 .expr()
2138 .ok_or_else(|| EvalError::ParseError("dynamic attr missing expr".to_string()))?;
2139 let val = force_value(&eval_expr(&expr, env)?)?;
2140 if val == Value::Null {
2143 return Ok(None);
2144 }
2145 Ok(Some(val.as_string()?.to_string()))
2146 }
2147 ast::Attr::Str(s) => {
2148 let val = eval_str(s, env)?;
2149 Ok(Some(val.as_string()?.to_string()))
2150 }
2151 }
2152}
2153
2154fn ident_text(ident: &ast::Ident) -> String {
2156 match ident.ident_token() {
2164 Some(tok) => tok.text().to_string(),
2165 None => ident.syntax().text().to_string(),
2166 }
2167}
2168
2169fn static_attr_offset(attr: &ast::Attr) -> Option<u32> {
2176 let node = match attr {
2177 ast::Attr::Ident(i) => i.syntax(),
2178 ast::Attr::Str(s) => s.syntax(),
2179 ast::Attr::Dynamic(_) => return None,
2180 };
2181 Some(u32::from(node.text_range().start()))
2182}
2183
2184fn attach_attrset_positions(set: &ast::AttrSet, attrs: &mut NixAttrs, env: &Env) {
2191 let mut table = crate::pos::AttrPositions::new(current_eval_file());
2198 for entry in set.entries() {
2199 if let ast::Entry::AttrpathValue(apv) = entry {
2200 let Some(attrpath) = apv.attrpath() else { continue };
2201 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2202 if path_attrs.len() != 1 {
2207 continue;
2208 }
2209 let Some(offset) = static_attr_offset(&path_attrs[0]) else { continue };
2210 if let Ok(Some(name)) = eval_attr_maybe_null(&path_attrs[0], env) {
2213 table.insert(intern(&name), offset);
2214 }
2215 }
2216 }
2217 if !table.is_empty() {
2218 attrs.set_positions(std::rc::Rc::new(table));
2219 }
2220}
2221
2222fn eval_attrset(set: &ast::AttrSet, env: &Env) -> Result<Value, EvalError> {
2223 crate::perf::inc(crate::perf::Counter::Attrset);
2224 let mut attrs = NixAttrs::new();
2225 let is_rec = set.rec_token().is_some();
2226
2227 if is_rec {
2228 let mut rec_env = env.child();
2229 let mut thunks: Vec<(String, Thunk)> = Vec::new();
2230
2231 let mut defined_so_far: HashSet<String> = HashSet::new();
2235
2236 let mut dotted_attrs: NixAttrs = NixAttrs::new();
2242
2243 for entry in set.entries() {
2245 match entry {
2246 ast::Entry::AttrpathValue(apv) => {
2247 let attrpath = apv.attrpath().ok_or_else(|| {
2248 EvalError::ParseError("binding missing attrpath".to_string())
2249 })?;
2250 let value_expr = apv.value().ok_or_else(|| {
2251 EvalError::ParseError("binding missing value".to_string())
2252 })?;
2253 let mut path_keys: Vec<String> = attrpath
2254 .attrs()
2255 .filter_map(|a| eval_attr_maybe_null(&a, env).transpose())
2256 .collect::<Result<_, _>>()?;
2257 if path_keys.is_empty() { continue; }
2259 if path_keys.len() == 1 {
2260 let key = path_keys.pop().unwrap();
2261 let referenced = referenced_idents(&value_expr);
2278 let is_recursive_binding = referenced.contains(key.as_str())
2279 || defined_so_far
2280 .iter()
2281 .any(|n| referenced.contains(n.as_str()));
2282 let value = if is_recursive_binding {
2283 Value::Thunk(Thunk::new_suspended_recursive(
2284 value_expr.clone(),
2285 env.clone(),
2286 ))
2287 } else {
2288 maybe_thunk(&value_expr, env, true, Some(&defined_so_far))
2294 };
2295 rec_env.bind(key.clone(), value.clone());
2296 attrs.insert(key.clone(), value.clone());
2297 if let Value::Thunk(t) = &value {
2298 thunks.push((key.clone(), t.clone()));
2299 }
2300 defined_so_far.insert(key);
2301 } else {
2302 let key = path_keys[0].clone();
2306 let value =
2307 build_nested_attr_thunk(&path_keys[1..], &value_expr, env, &mut thunks);
2308 merge_nested_insert(&mut dotted_attrs, key, value);
2309 }
2310 }
2311 ast::Entry::Inherit(inherit) => {
2312 eval_inherit(&inherit, env, &mut attrs, Some(&mut rec_env), Some(&mut thunks))?;
2313 }
2314 }
2315 }
2316
2317 for (key, value) in dotted_attrs.iter() {
2322 attrs.insert(key.clone(), value.clone());
2323 rec_env.bind(key.clone(), value.clone());
2324 }
2325
2326 for (_key, thunk) in &thunks {
2329 thunk.update_env(&rec_env);
2330 }
2331 } else {
2332 for entry in set.entries() {
2333 match entry {
2334 ast::Entry::AttrpathValue(apv) => {
2335 let attrpath = apv.attrpath().ok_or_else(|| {
2336 EvalError::ParseError("binding missing attrpath".to_string())
2337 })?;
2338 let value_expr = apv.value().ok_or_else(|| {
2339 EvalError::ParseError("binding missing value".to_string())
2340 })?;
2341 let path_attrs: Vec<ast::Attr> = attrpath.attrs().collect();
2342 let tail_is_dynamic =
2352 path_attrs.len() > 1 && attrs_have_dynamic(&path_attrs[1..]);
2353 let head_key = match eval_attr_maybe_null(&path_attrs[0], env)? {
2354 Some(k) => k,
2355 None => continue,
2357 };
2358 if tail_is_dynamic && attrs.get(&head_key).is_none() {
2359 let value =
2360 build_deferred_tail_attr(&path_attrs[1..], &value_expr, env);
2361 attrs.insert(head_key, value);
2362 continue;
2363 }
2364 if tail_is_dynamic {
2378 if let Some(existing) = attrs.get(&head_key).cloned() {
2379 let merged = merge_deferred_dynamic_tail(
2380 existing,
2381 &path_attrs[1..],
2382 &value_expr,
2383 env,
2384 )?;
2385 attrs.insert(head_key, merged);
2386 continue;
2387 }
2388 }
2389 let mut path_keys: Vec<String> = {
2392 let mut v = Vec::with_capacity(path_attrs.len());
2393 v.push(head_key);
2394 let mut skip = false;
2395 for a in &path_attrs[1..] {
2396 match eval_attr_maybe_null(a, env)? {
2397 Some(k) => v.push(k),
2398 None => { skip = true; break; }
2399 }
2400 }
2401 if skip { v.clear(); }
2402 v
2403 };
2404 if path_keys.is_empty() { continue; }
2406 if path_keys.len() == 1 {
2407 let key = path_keys.pop().unwrap();
2408 let value = maybe_thunk(&value_expr, env, false, None);
2411 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2436 let existing = attrs.get(&key).cloned().unwrap();
2437 let forced_existing = force_value(&existing)?;
2438 attrs.insert(key.clone(), forced_existing);
2439 }
2440 if matches!(attrs.get(&key), Some(Value::Attrs(_))) {
2441 let forced = force_value(&value)?;
2442 merge_nested_insert(&mut attrs, key, forced);
2443 } else {
2444 attrs.insert(key, value);
2445 }
2446 } else {
2447 let key = path_keys[0].clone();
2448 let value = build_nested_attr(&path_keys[1..], &value_expr, env)?;
2449 if matches!(attrs.get(&key), Some(Value::Thunk(_))) {
2463 let existing = attrs.get(&key).cloned().unwrap();
2464 let forced = force_value(&existing)?;
2465 attrs.insert(key.clone(), forced);
2466 }
2467 merge_nested_insert(&mut attrs, key, value);
2468 }
2469 }
2470 ast::Entry::Inherit(inherit) => {
2471 eval_inherit(&inherit, env, &mut attrs, None, None)?;
2472 }
2473 }
2474 }
2475 }
2476
2477 attach_attrset_positions(set, &mut attrs, env);
2483
2484 Ok(Value::Attrs(Rc::new(attrs)))
2485}
2486
2487fn eval_inherit(
2488 inherit: &ast::Inherit,
2489 env: &Env,
2490 attrs: &mut NixAttrs,
2491 bind_env: Option<&mut Env>,
2492 mut thunks: Option<&mut Vec<(String, Thunk)>>,
2493) -> Result<(), EvalError> {
2494 if let Some(from) = inherit.from() {
2495 let source_expr = from
2515 .expr()
2516 .ok_or_else(|| EvalError::ParseError("inherit from missing expr".to_string()))?;
2517 let source_thunk = Thunk::new_suspended(source_expr, env.clone());
2521 let mut be = bind_env;
2522 for attr in inherit.attrs() {
2523 let name = eval_attr(&attr, env)?;
2524 let thunk = Thunk::new_inherit_select(source_thunk.clone(), name.clone());
2525 let value = Value::Thunk(thunk.clone());
2526 attrs.insert(name.clone(), value.clone());
2527 if let Some(ref mut e) = be {
2528 e.bind(name.clone(), value);
2529 }
2530 if let Some(ref mut t) = thunks {
2531 t.push((name, thunk));
2532 }
2533 }
2534 } else {
2535 let mut be = bind_env;
2551 for attr in inherit.attrs() {
2552 let name = eval_attr(&attr, env)?;
2553 let sym = crate::value::intern(&name);
2554 let value = if let Some(v) = env.lookup_fast(sym, &name) {
2555 v
2556 } else if let Some((scope_cache, scope_value)) =
2557 env.innermost_with_scope()
2558 {
2559 Value::Thunk(Thunk::new_with_ident(
2560 SmolStr::from(name.as_str()),
2561 scope_cache,
2562 scope_value,
2563 env.clone(),
2564 ))
2565 } else {
2566 return Err(EvalError::UndefinedVar(format!(
2567 "'{name}'{}",
2568 eval_file_ctx()
2569 )));
2570 };
2571 attrs.insert(name.clone(), value.clone());
2572 if let Some(ref mut e) = be {
2573 e.bind(name, value);
2574 }
2575 }
2576 }
2577 Ok(())
2578}
2579
2580fn build_nested_attr(
2581 path: &[String],
2582 expr: &ast::Expr,
2583 env: &Env,
2584) -> Result<Value, EvalError> {
2585 if path.is_empty() {
2586 return Ok(maybe_thunk(expr, env, false, None));
2591 }
2592 let key = path[0].clone();
2593 let inner = build_nested_attr(&path[1..], expr, env)?;
2594 let mut attrs = NixAttrs::new();
2595 attrs.insert(key, inner);
2596 Ok(Value::Attrs(Rc::new(attrs)))
2597}
2598
2599fn attr_is_dynamic(attr: &ast::Attr) -> bool {
2620 match attr {
2621 ast::Attr::Dynamic(_) => true,
2622 ast::Attr::Str(s) => s
2625 .normalized_parts()
2626 .iter()
2627 .any(|p| matches!(p, InterpolPart::Interpolation(_))),
2628 ast::Attr::Ident(_) => false,
2629 }
2630}
2631
2632fn attrs_have_dynamic(attrs: &[ast::Attr]) -> bool {
2640 attrs.iter().any(attr_is_dynamic)
2641}
2642
2643fn build_deferred_tail_attr(
2656 tail: &[ast::Attr],
2657 value_expr: &ast::Expr,
2658 env: &Env,
2659) -> Value {
2660 let tail: Vec<ast::Attr> = tail.to_vec();
2661 let value_expr = value_expr.clone();
2662 let env = env.clone();
2663 Value::Thunk(Thunk::new_native(move || {
2664 build_tail_attrs_now(&tail, &value_expr, &env)
2665 }))
2666}
2667
2668fn build_tail_attrs_now(
2689 tail: &[ast::Attr],
2690 value_expr: &ast::Expr,
2691 env: &Env,
2692) -> Result<Value, EvalError> {
2693 if tail.is_empty() {
2694 return Ok(maybe_thunk(value_expr, env, false, None));
2695 }
2696 if std::env::var_os("SUI_M26_TAILTRACE").is_some() {
2697 let t: String = tail[0].syntax().text().to_string().chars().take(40).collect();
2698 eprintln!("[M26 TAIL-RESOLVE] forcing dynamic tail key `{t}`");
2699 if attrs_have_dynamic(&tail[..1]) {
2700 crate::trace::dump_force_stack_ids();
2701 }
2702 }
2703 let key = match eval_attr_maybe_null(&tail[0], env)? {
2704 Some(k) => k,
2705 None => return Ok(Value::Attrs(Rc::new(NixAttrs::new()))),
2708 };
2709 let inner = if tail.len() == 1 {
2715 maybe_thunk(value_expr, env, false, None)
2716 } else {
2717 build_deferred_tail_attr(&tail[1..], value_expr, env)
2718 };
2719 let mut attrs = NixAttrs::new();
2720 attrs.insert(key, inner);
2721 Ok(Value::Attrs(Rc::new(attrs)))
2722}
2723
2724fn merge_deferred_dynamic_tail(
2742 existing: Value,
2743 tail: &[ast::Attr],
2744 value_expr: &ast::Expr,
2745 env: &Env,
2746) -> Result<Value, EvalError> {
2747 debug_assert!(!tail.is_empty());
2750
2751 if attr_is_dynamic(&tail[0]) {
2756 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2757 return Ok(lazy_overlay_merge(existing, deferred));
2758 }
2759
2760 let key = match eval_attr_maybe_null(&tail[0], env)? {
2763 Some(k) => k,
2764 None => return Ok(existing),
2765 };
2766
2767 let existing_forced = force_value(&existing)?;
2771 let mut base = match existing_forced {
2772 Value::Attrs(a) => (*a).clone(),
2773 _ => {
2778 let deferred = build_deferred_tail_attr(tail, value_expr, env);
2779 return Ok(deferred);
2780 }
2781 };
2782
2783 let child_existing = base.get(&key).cloned();
2785 let new_child = match child_existing {
2786 Some(child) if tail.len() > 1 => {
2787 merge_deferred_dynamic_tail(child, &tail[1..], value_expr, env)?
2789 }
2790 Some(child) => {
2791 let leaf = maybe_thunk(value_expr, env, false, None);
2794 lazy_overlay_merge(child, leaf)
2795 }
2796 None if tail.len() > 1 => {
2797 build_deferred_tail_attr(&tail[1..], value_expr, env)
2801 }
2802 None => maybe_thunk(value_expr, env, false, None),
2803 };
2804 base.insert(key, new_child);
2805 Ok(Value::Attrs(Rc::new(base)))
2806}
2807
2808fn lazy_overlay_merge(left: Value, right: Value) -> Value {
2815 match (&left, &right) {
2816 (Value::Attrs(la), Value::Attrs(_)) => {
2817 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2818 let mut merged = (**la).clone();
2819 if let Value::Attrs(ra) = &right {
2820 for (k, v) in ra.iter_unsorted() {
2824 merge_nested_insert(&mut merged, k.clone(), v.clone());
2825 }
2826 }
2827 Value::Attrs(Rc::new(merged))
2828 }
2829 _ => {
2830 Value::Thunk(Thunk::new_native(move || {
2834 let lf = force_value(&left)?;
2835 let rf = force_value(&right)?;
2836 let la = lf.as_attrs()?;
2837 let ra = rf.as_attrs()?;
2838 crate::perf::inc(crate::perf::Counter::SlashDeferredTailClone);
2839 let mut merged = (*la).clone();
2840 for (k, v) in ra.iter_unsorted() {
2841 merge_nested_insert(&mut merged, k.clone(), v.clone());
2842 }
2843 Ok(Value::Attrs(Rc::new(merged)))
2844 }))
2845 }
2846 }
2847}
2848
2849fn build_nested_attr_thunk(
2857 path: &[String],
2858 expr: &ast::Expr,
2859 env: &Env,
2860 thunks: &mut Vec<(String, Thunk)>,
2861) -> Value {
2862 if path.is_empty() {
2863 let thunk = Thunk::new_suspended(expr.clone(), env.clone());
2864 let val = Value::Thunk(thunk.clone());
2865 thunks.push((String::new(), thunk));
2866 return val;
2867 }
2868 let key = path[0].clone();
2869 let inner = build_nested_attr_thunk(&path[1..], expr, env, thunks);
2870 let mut attrs = NixAttrs::new();
2871 attrs.insert(key, inner);
2872 Value::Attrs(Rc::new(attrs))
2873}
2874
2875fn merge_nested_insert(target: &mut NixAttrs, key: String, value: Value) {
2882 let existing = match target.get(&key) {
2886 Some(e) => e.clone(),
2887 None => {
2888 target.insert(key, value);
2889 return;
2890 }
2891 };
2892 let value = match value {
2916 Value::Thunk(_) => match force_value(&value) {
2917 Ok(v @ Value::Attrs(_)) => v,
2918 _ => value,
2919 },
2920 other => other,
2921 };
2922 if !matches!(value, Value::Attrs(_)) {
2923 target.insert(key, value);
2924 return;
2925 }
2926 let existing_concrete = match &existing {
2929 Value::Attrs(_) => existing.clone(),
2930 Value::Thunk(_) => match force_value(&existing) {
2931 Ok(v @ Value::Attrs(_)) => v,
2932 _ => {
2933 target.insert(key, value);
2934 return;
2935 }
2936 },
2937 _ => {
2938 target.insert(key, value);
2939 return;
2940 }
2941 };
2942 let mut existing_attrs = match existing_concrete {
2946 Value::Attrs(a) => (*a).clone(),
2947 _ => unreachable!(),
2948 };
2949 let new_attrs = match value {
2950 Value::Attrs(ref a) => a,
2951 _ => unreachable!(),
2952 };
2953 for (k, v) in new_attrs.iter_unsorted() {
2954 merge_nested_insert(&mut existing_attrs, k.clone(), v.clone());
2955 }
2956 target.insert(key, Value::Attrs(Rc::new(existing_attrs)));
2957}
2958
2959fn eval_entries<N: HasEntry + AstNode>(node: &N, env: &mut Env) -> Result<(), EvalError> {
2961 for entry in node.entries() {
2962 match entry {
2963 ast::Entry::AttrpathValue(apv) => {
2964 let attrpath = apv.attrpath().ok_or_else(|| {
2965 EvalError::ParseError("binding missing attrpath".to_string())
2966 })?;
2967 let value_expr = apv.value().ok_or_else(|| {
2968 EvalError::ParseError("binding missing value".to_string())
2969 })?;
2970 let mut path_keys: Vec<String> = attrpath
2971 .attrs()
2972 .map(|a| eval_attr(&a, env))
2973 .collect::<Result<_, _>>()?;
2974 if path_keys.len() == 1 {
2975 let key = path_keys.pop().unwrap();
2976 let value = eval_expr(&value_expr, env)?;
2977 env.bind(key, value);
2978 }
2979 }
2981 ast::Entry::Inherit(inherit) => {
2982 if let Some(from) = inherit.from() {
2983 let source_expr = from.expr().ok_or_else(|| {
2984 EvalError::ParseError("inherit from missing expr".to_string())
2985 })?;
2986 let source = force_value(&eval_expr(&source_expr, env)?)?;
2987 let source_attrs = source.as_attrs()?;
2988 for attr in inherit.attrs() {
2989 let name = eval_attr(&attr, env)?;
2990 let value = source_attrs
2991 .get(&name)
2992 .cloned()
2993 .ok_or_else(|| EvalError::AttrNotFound(
2994 format!("'{name}' in inherit{}", eval_file_ctx()),
2995 ))?;
2996 env.bind(name, value);
2997 }
2998 } else {
2999 for attr in inherit.attrs() {
3000 let name = eval_attr(&attr, env)?;
3001 let value = env
3002 .lookup(&name)
3003 .ok_or_else(|| EvalError::UndefinedVar(
3004 format!("'{name}'{}", eval_file_ctx()),
3005 ))?;
3006 env.bind(name, value);
3007 }
3008 }
3009 }
3010 }
3011 }
3012 Ok(())
3013}
3014
3015fn eval_binop(
3016 op: ast::BinOpKind,
3017 lhs: &ast::Expr,
3018 rhs: &ast::Expr,
3019 env: &Env,
3020) -> Result<Value, EvalError> {
3021 match op {
3023 ast::BinOpKind::And => {
3024 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3025 if !l {
3026 return Ok(Value::Bool(false));
3027 }
3028 return eval_expr(rhs, env);
3029 }
3030 ast::BinOpKind::Or => {
3031 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3032 if l {
3033 return Ok(Value::Bool(true));
3034 }
3035 return eval_expr(rhs, env);
3036 }
3037 ast::BinOpKind::Implication => {
3038 let l = force_value(&eval_expr(lhs, env)?)?.as_bool()?;
3039 if !l {
3040 return Ok(Value::Bool(true));
3041 }
3042 return eval_expr(rhs, env);
3043 }
3044 _ => {}
3045 }
3046
3047 let lc = force_concrete(&eval_expr(lhs, env)?)?;
3048 let rc = force_concrete(&eval_expr(rhs, env)?)?;
3049 let l = lc.into_value();
3056 let r = rc.into_value();
3057
3058 match op {
3059 ast::BinOpKind::Add => match (&l, &r) {
3060 (Value::Int(a), Value::Int(b)) => a
3061 .checked_add(*b)
3062 .map(Value::Int)
3063 .ok_or_else(|| int_overflow("adding", *a, '+', *b)),
3064 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
3065 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
3066 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(a + *b as f64)),
3067 (Value::String(a), Value::String(b)) => {
3068 let mut ctx = a.context.clone();
3069 ctx.merge(&b.context);
3070 let mut s = String::with_capacity(a.chars.len() + b.chars.len());
3078 s.push_str(&a.chars);
3079 s.push_str(&b.chars);
3080 Ok(Value::String(Rc::new(NixString::with_context(s, ctx))))
3081 }
3082 (Value::Path(a), Value::String(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}{}", b.chars).as_str())))),
3083 (Value::Path(a), Value::Path(b)) => Ok(Value::Path(Box::new(SmolStr::from(format!("{a}/{b}").as_str())))),
3084 (Value::Attrs(_), _) | (_, Value::Attrs(_)) => {
3086 let (ls, lctx) = l.coerce_to_string()?;
3087 let (rs, rctx) = r.coerce_to_string()?;
3088 let mut ctx = lctx;
3089 ctx.merge(&rctx);
3090 Ok(Value::String(Rc::new(NixString::with_context(
3091 format!("{ls}{rs}"),
3092 ctx,
3093 ))))
3094 }
3095 _ => Err(EvalError::op_type("add", l.type_name(), r.type_name())),
3096 },
3097 ast::BinOpKind::Sub => num_op(
3098 &l,
3099 &r,
3100 |a, b| a.checked_sub(b),
3101 |a, b| a - b,
3102 |a, b| int_overflow("subtracting", a, '-', b),
3103 ),
3104 ast::BinOpKind::Mul => num_op(
3105 &l,
3106 &r,
3107 |a, b| a.checked_mul(b),
3108 |a, b| a * b,
3109 |a, b| int_overflow("multiplying", a, '*', b),
3110 ),
3111 ast::BinOpKind::Div => {
3112 let rhs_is_zero = match &r {
3121 Value::Int(0) => true,
3122 Value::Float(f) => *f == 0.0,
3123 _ => false,
3124 };
3125 if rhs_is_zero {
3126 return Err(EvalError::DivisionByZero);
3127 }
3128 num_op(
3129 &l,
3130 &r,
3131 |a, b| a.checked_div(b),
3132 |a, b| a / b,
3133 |a, b| int_overflow("dividing", a, '/', b),
3134 )
3135 }
3136 ast::BinOpKind::Equal => Ok(Value::Bool(l == r)),
3137 ast::BinOpKind::NotEqual => Ok(Value::Bool(l != r)),
3138 ast::BinOpKind::Less => compare(&l, &r, |o| o == std::cmp::Ordering::Less),
3139 ast::BinOpKind::LessOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Greater),
3140 ast::BinOpKind::More => compare(&l, &r, |o| o == std::cmp::Ordering::Greater),
3141 ast::BinOpKind::MoreOrEq => compare(&l, &r, |o| o != std::cmp::Ordering::Less),
3142 ast::BinOpKind::Update => {
3143 let la = l.to_attrs()?;
3144 let ra = r.to_attrs()?;
3145 Ok(Value::Attrs(Rc::new(la.overlay(ra))))
3147 }
3148 ast::BinOpKind::Concat => {
3149 crate::value::concat_lists(l, r.as_list()?)
3159 }
3160 ast::BinOpKind::And | ast::BinOpKind::Or | ast::BinOpKind::Implication => {
3161 unreachable!("handled above")
3162 }
3163 ast::BinOpKind::PipeRight | ast::BinOpKind::PipeLeft => {
3164 Err(EvalError::NotImplemented("pipe operators".to_string()))
3165 }
3166 }
3167}
3168
3169#[inline]
3174fn int_overflow(verb: &str, a: i64, sym: char, b: i64) -> EvalError {
3175 EvalError::Abort(format!("integer overflow in {verb} {a} {sym} {b}"))
3176}
3177
3178fn num_op(
3179 l: &Value,
3180 r: &Value,
3181 int_op: impl Fn(i64, i64) -> Option<i64>,
3182 float_op: impl Fn(f64, f64) -> f64,
3183 overflow: impl Fn(i64, i64) -> EvalError,
3184) -> Result<Value, EvalError> {
3185 match (l, r) {
3186 (Value::Int(a), Value::Int(b)) => {
3187 int_op(*a, *b).map(Value::Int).ok_or_else(|| overflow(*a, *b))
3188 }
3189 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(float_op(*a, *b))),
3190 (Value::Int(a), Value::Float(b)) => Ok(Value::Float(float_op(*a as f64, *b))),
3191 (Value::Float(a), Value::Int(b)) => Ok(Value::Float(float_op(*a, *b as f64))),
3192 _ => Err(EvalError::op_type("perform arithmetic on", l.type_name(), r.type_name())),
3193 }
3194}
3195
3196fn compare(
3197 l: &Value,
3198 r: &Value,
3199 pred: impl Fn(std::cmp::Ordering) -> bool,
3200) -> Result<Value, EvalError> {
3201 let ord = match (l, r) {
3202 (Value::Int(a), Value::Int(b)) => a.cmp(b),
3203 (Value::Float(a), Value::Float(b)) => {
3204 a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
3205 }
3206 (Value::Int(a), Value::Float(b)) => (*a as f64)
3207 .partial_cmp(b)
3208 .unwrap_or(std::cmp::Ordering::Equal),
3209 (Value::Float(a), Value::Int(b)) => a
3210 .partial_cmp(&(*b as f64))
3211 .unwrap_or(std::cmp::Ordering::Equal),
3212 (Value::String(a), Value::String(b)) => a.chars.cmp(&b.chars),
3213 _ => {
3214 return Err(EvalError::op_type("compare", l.type_name(), r.type_name()));
3215 }
3216 };
3217 Ok(Value::Bool(pred(ord)))
3218}
3219
3220pub fn apply_and_force(func: Value, arg: Value) -> Result<Value, EvalError> {
3234 force_value(&apply(func, arg)?)
3235}
3236
3237pub fn apply(func: Value, arg: Value) -> Result<Value, EvalError> {
3238 stacker::maybe_grow(64 * 1024, 2 * 1024 * 1024, || apply_inner(func, arg))
3239}
3240
3241fn apply_inner(func: Value, arg: Value) -> Result<Value, EvalError> {
3242 crate::perf::inc(crate::perf::Counter::Apply);
3243 let func = force_concrete(&func)?.into_value();
3244 match func {
3245 Value::Lambda(closure) => {
3246 if crate::perf::enabled() {
3248 APPLY_SITES.with(|sites| {
3249 let file = closure.env.eval_file()
3250 .map(|p| p.display().to_string())
3251 .unwrap_or_else(|| "<eval>".into());
3252 let param_name = match &closure.param {
3254 rnix::ast::Param::IdentParam(ip) => ip.ident().map(|i| ident_text(&i)).unwrap_or_default(),
3255 rnix::ast::Param::Pattern(pat) => {
3256 let mut names: Vec<String> = pat.pat_entries()
3257 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3258 .take(3)
3259 .collect();
3260 if pat.pat_entries().count() > 3 { names.push("...".to_string()); }
3261 format!("{{{}}}", names.join(","))
3262 }
3263 };
3264 let key = format!("{}:{}", file.rsplit_once("-source/").map_or(file.as_str(), |(_,s)| s), param_name);
3265 *sites.borrow_mut().entry(key).or_insert(0u64) += 1;
3266 });
3267 }
3268 let mut call_env = closure.env.child();
3269 let _file_guard = closure
3270 .env
3271 .eval_file()
3272 .cloned()
3273 .map(push_eval_file);
3274 let _trace = push_nix_trace_lambda(&closure.env);
3280 match &closure.param {
3281 rnix::ast::Param::IdentParam(_) => {
3282 bind_param(&closure.param, &arg, &mut call_env)?;
3285 }
3286 rnix::ast::Param::Pattern(_) => {
3287 let forced_arg = force_concrete(&arg)?.into_value();
3289 bind_param(&closure.param, &forced_arg, &mut call_env)?;
3290 }
3291 }
3292 eval_expr(&closure.body, &call_env)
3293 }
3294 Value::Builtin(b) => {
3295 let _trace = push_nix_trace(format!("while calling the '{}' builtin", b.name));
3296 if builtin_takes_lazy_arg(&b.name) {
3306 (b.func)(&[arg])
3307 } else {
3308 let forced_arg = force_value(&arg)?;
3309 (b.func)(&[forced_arg])
3310 }
3311 }
3312 Value::Attrs(ref attrs) => {
3313 if let Some(functor) = attrs.get("__functor") {
3314 let functor = force_value(functor)?;
3315 let partial = apply(functor, func.clone())?;
3317 apply(partial, arg)
3318 } else if crate::value::in_promise_eval() {
3319 Ok(Value::Null)
3324 } else {
3325 Err(EvalError::type_error(
3326 format!("cannot call {} (missing __functor){}", func.type_name(), eval_file_ctx()),
3327 ))
3328 }
3329 }
3330 _ if crate::value::in_promise_eval() => {
3331 Ok(Value::Null)
3336 }
3337 _ => Err(EvalError::type_error(
3338 format!("cannot call {}{}", func.type_name(), eval_file_ctx()),
3339 )),
3340 }
3341}
3342
3343fn bind_param(param: &ast::Param, arg: &Value, env: &mut Env) -> Result<(), EvalError> {
3344 match param {
3345 ast::Param::IdentParam(ip) => {
3346 let ident = ip
3347 .ident()
3348 .ok_or_else(|| EvalError::ParseError("ident param missing ident".to_string()))?;
3349 let name = ident_text(&ident);
3350 env.bind(name, arg.clone());
3351 }
3352 ast::Param::Pattern(pat) => {
3353 let attrs = arg.as_attrs()?;
3354
3355 if let Some(pat_bind) = pat.pat_bind()
3357 && let Some(ident) = pat_bind.ident()
3358 {
3359 let name = ident_text(&ident);
3360 env.bind(name, arg.clone());
3361 }
3362
3363 let has_ellipsis = pat.ellipsis_token().is_some();
3364 let entries: Vec<ast::PatEntry> = pat.pat_entries().collect();
3365
3366 let mut default_thunks: Vec<Thunk> = Vec::new();
3373
3374 for entry in &entries {
3375 let ident = entry.ident().ok_or_else(|| {
3376 EvalError::ParseError("pat entry missing ident".to_string())
3377 })?;
3378 let name = ident_text(&ident);
3379 let value = if let Some(v) = attrs.get(&name) {
3380 v.clone()
3381 } else if let Some(default_expr) = entry.default() {
3382 let thunk = Thunk::new_suspended(
3388 ast::Expr::cast(default_expr.syntax().clone()).unwrap(),
3389 env.clone(),
3390 );
3391 default_thunks.push(thunk.clone());
3392 Value::Thunk(thunk)
3393 } else {
3394 return Err(EvalError::type_error(
3395 format!("missing argument '{name}'{}", eval_file_ctx()),
3396 ));
3397 };
3398 env.bind(name, value);
3399 }
3400
3401 for thunk in &default_thunks {
3403 thunk.update_env(env);
3404 }
3405
3406 if !has_ellipsis {
3407 let entry_names: std::collections::HashSet<String> = entries
3408 .iter()
3409 .filter_map(|e| e.ident().map(|i| ident_text(&i)))
3410 .collect();
3411 for key in attrs.keys() {
3412 if !entry_names.contains(key.as_str()) {
3413 return Err(EvalError::type_error(
3414 format!("unexpected argument '{key}'{}", eval_file_ctx()),
3415 ));
3416 }
3417 }
3418 }
3419 }
3420 }
3421 Ok(())
3422}
3423
3424#[cfg(test)]
3425mod tests {
3426 use super::*;
3427
3428 fn ev(input: &str) -> Value {
3429 eval(input).unwrap()
3430 }
3431
3432 #[test]
3439 fn is_self_recursive_binding_ignores_attribute_names() {
3440 fn expr(s: &str) -> ast::Expr {
3441 rnix::Root::parse(s).tree().expr().expect("parse")
3442 }
3443 assert!(!is_self_recursive_binding(&expr("lhs.placeholder"), "placeholder"));
3445 assert!(!is_self_recursive_binding(&expr("{ placeholder = 1; }"), "placeholder"));
3446 assert!(!is_self_recursive_binding(
3447 &expr("if lhs.placeholder == rhs.placeholder then lhs.placeholder else null"),
3448 "placeholder",
3449 ));
3450 assert!(is_self_recursive_binding(&expr("placeholder + 1"), "placeholder"));
3452 assert!(is_self_recursive_binding(
3453 &expr("if placeholder then 1 else 2"),
3454 "placeholder"
3455 ));
3456 }
3457
3458 #[test]
3462 fn maybe_thunk_eager_constant_str_is_byte_identical() {
3463 fn expr(s: &str) -> ast::Expr {
3464 rnix::Root::parse(s).tree().expr().expect("parse")
3465 }
3466 let env = Env::new();
3467 let v = maybe_thunk(&expr(r#""abc""#), &env, false, None);
3469 assert!(matches!(v, Value::String(_)), "constant str should be eager, got {v:?}");
3470 assert_eq!(force_value(&v).unwrap(), Value::string("abc"));
3471 let vi = maybe_thunk(&expr(r#""a${b}c""#), &env, false, None);
3473 assert!(matches!(vi, Value::Thunk(_)), "interpolated str must stay thunked");
3474 }
3475
3476 #[test]
3480 fn eval_pure_constant_arg_classification() {
3481 fn expr(s: &str) -> ast::Expr {
3482 rnix::Root::parse(s).tree().expr().expect("parse")
3483 }
3484 assert!(eval_pure_constant_arg(&expr("42")).is_some());
3486 assert!(eval_pure_constant_arg(&expr("3.14")).is_some());
3487 assert!(eval_pure_constant_arg(&expr(r#""const""#)).is_some());
3488 assert!(eval_pure_constant_arg(&expr("/abs/path")).is_some());
3489 assert!(eval_pure_constant_arg(&expr(r#""a${b}c""#)).is_none(), "interpolated str");
3491 assert!(eval_pure_constant_arg(&expr("true")).is_none(), "bool is an ident");
3494 assert!(eval_pure_constant_arg(&expr("x")).is_none(), "ident (with-scope force)");
3495 assert!(eval_pure_constant_arg(&expr("a.b")).is_none(), "select (fixpoint)");
3496 assert!(eval_pure_constant_arg(&expr("f x")).is_none(), "apply (may throw)");
3497 assert!(eval_pure_constant_arg(&expr("1 + 1")).is_none(), "binop (may throw)");
3498 assert!(eval_pure_constant_arg(&expr("throw \"x\"")).is_none(), "throw stays lazy");
3499 }
3500
3501 #[test]
3505 fn ignored_throwing_arg_stays_lazy() {
3506 assert_eq!(ev(r#"(x: 7) (throw "boom")"#), Value::Int(7));
3507 assert_eq!(ev(r#"(x: 7) "const""#), Value::Int(7));
3509 assert_eq!(ev(r#"(x: x) "used""#), Value::string("used"));
3511 }
3512
3513 #[test]
3514 fn eval_int() { assert_eq!(ev("42"), Value::Int(42)); }
3515
3516 #[test]
3517 fn eval_float() { assert_eq!(ev("3.14"), Value::Float(3.14)); }
3518
3519 #[test]
3520 fn eval_string() { assert_eq!(ev(r#""hello""#), Value::string("hello")); }
3521
3522 #[test]
3523 fn eval_bool() { assert_eq!(ev("true"), Value::Bool(true)); }
3524
3525 #[test]
3526 fn eval_null() { assert_eq!(ev("null"), Value::Null); }
3527
3528 #[test]
3529 fn eval_arithmetic() {
3530 assert_eq!(ev("1 + 2"), Value::Int(3));
3531 assert_eq!(ev("10 - 3"), Value::Int(7));
3532 assert_eq!(ev("2 * 3"), Value::Int(6));
3533 assert_eq!(ev("10 / 3"), Value::Int(3));
3534 }
3535
3536 #[test]
3537 fn eval_precedence() {
3538 assert_eq!(ev("1 + 2 * 3"), Value::Int(7));
3539 assert_eq!(ev("(1 + 2) * 3"), Value::Int(9));
3540 }
3541
3542 #[test]
3543 fn eval_comparison() {
3544 assert_eq!(ev("1 == 1"), Value::Bool(true));
3545 assert_eq!(ev("1 == 2"), Value::Bool(false));
3546 assert_eq!(ev("1 < 2"), Value::Bool(true));
3547 assert_eq!(ev("2 <= 2"), Value::Bool(true));
3548 }
3549
3550 #[test]
3551 fn eval_logic() {
3552 assert_eq!(ev("true && false"), Value::Bool(false));
3553 assert_eq!(ev("true || false"), Value::Bool(true));
3554 assert_eq!(ev("!true"), Value::Bool(false));
3555 }
3556
3557 #[test]
3558 fn eval_string_concat() {
3559 assert_eq!(ev(r#""hello" + " " + "world""#), Value::string("hello world"));
3560 }
3561
3562 #[test]
3563 fn eval_if() {
3564 assert_eq!(ev("if true then 1 else 2"), Value::Int(1));
3565 assert_eq!(ev("if false then 1 else 2"), Value::Int(2));
3566 }
3567
3568 #[test]
3569 fn eval_let() {
3570 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
3571 assert_eq!(ev("let x = 1; y = 2; in x + y"), Value::Int(3));
3572 }
3573
3574 #[test]
3575 fn eval_let_dotted_simple() {
3576 assert_eq!(ev("let a.b = 1; a.c = 2; in a.b + a.c"), Value::Int(3));
3578 }
3579
3580 #[test]
3581 fn eval_let_dotted_deep() {
3582 assert_eq!(ev("let a.b.c = 1; in a.b.c"), Value::Int(1));
3584 }
3585
3586 #[test]
3587 fn eval_let_dotted_mixed() {
3588 assert_eq!(
3590 ev("let a.x = 1; b = 2; a.y = 3; in a.x + a.y + b"),
3591 Value::Int(6),
3592 );
3593 }
3594
3595 #[test]
3596 fn eval_let_dotted_produces_attrset() {
3597 let v = ev("let a.b = 1; a.c = 2; in a");
3599 if let Value::Attrs(attrs) = v {
3600 assert_eq!(attrs.get("b"), Some(&Value::Int(1)));
3601 assert_eq!(attrs.get("c"), Some(&Value::Int(2)));
3602 } else {
3603 panic!("expected Attrs, got {v:?}");
3604 }
3605 }
3606
3607 #[test]
3615 fn dynamic_inner_attr_key_is_lazy_on_sibling_read() {
3616 assert_eq!(
3618 ev(r#"let s = { a.${throw "KEYFORCED"} = 7; other = 9; }; in s.other"#),
3619 Value::Int(9),
3620 );
3621 }
3622
3623 #[test]
3624 fn dynamic_inner_attr_key_resolves_on_head_demand() {
3625 let v = ev(r#"let u = "bob"; s = { homes.${u} = 7; }; in s.homes"#);
3627 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3628 assert_eq!(attrs.get("bob"), Some(&Value::Int(7)));
3629 } else {
3630 panic!("expected Attrs");
3631 }
3632 }
3633
3634 #[test]
3635 fn dynamic_inner_attr_key_merges_with_static_sibling() {
3636 let v = ev(r#"let u = "x"; s = { a.${u} = 1; a.b = 2; }; in s.a"#);
3638 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3639 assert_eq!(attrs.get("x"), Some(&Value::Int(1)));
3640 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3641 } else {
3642 panic!("expected Attrs");
3643 }
3644 }
3645
3646 #[test]
3647 fn dynamic_inner_attr_key_null_skips_binding() {
3648 let v = ev(
3651 r#"let c = true; s = { a.${if c then null else "n"} = 5; b = 1; }; in s.b"#,
3652 );
3653 assert_eq!(v, Value::Int(1));
3654 }
3655
3656 #[test]
3662 fn interpolated_string_attr_key_is_lazy_on_sibling_read() {
3663 assert_eq!(
3664 ev(r#"let s = { a."p/${throw "KEYFORCED"}" = 7; other = 9; }; in s.other"#),
3665 Value::Int(9),
3666 );
3667 }
3668
3669 #[test]
3670 fn interpolated_string_attr_key_resolves_on_head_demand() {
3671 let v = ev(r#"let u = "bob"; s = { homes."u/${u}" = 7; }; in s.homes"#);
3673 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3674 assert_eq!(attrs.get("u/bob"), Some(&Value::Int(7)));
3675 } else {
3676 panic!("expected Attrs");
3677 }
3678 }
3679
3680 #[test]
3681 fn purely_literal_string_attr_key_stays_eager_static() {
3682 let v = ev(r#"let s = { a."foo bar" = 1; a.b = 2; }; in s.a"#);
3685 if let Value::Attrs(attrs) = force_value(&v).unwrap() {
3686 assert_eq!(attrs.get("foo bar"), Some(&Value::Int(1)));
3687 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3688 } else {
3689 panic!("expected Attrs");
3690 }
3691 }
3692
3693 #[test]
3696 fn dynamic_tail_key_under_colliding_head_is_lazy() {
3697 let v = ev(
3700 r#"let s = { sd.services.x = 1; sd.tmpfiles.${throw "KEYFORCED"}.d = 2; }; in s.sd.services.x"#,
3701 );
3702 assert_eq!(v, Value::Int(1));
3703 }
3704
3705 #[test]
3706 fn dynamic_tail_key_under_colliding_head_resolves_and_merges() {
3707 let v = ev(
3710 r#"let k = "z"; s = { sd.services.x = 1; sd.tmpfiles.${k}.d = 2; }; in s.sd"#,
3711 );
3712 let sd = force_value(&v).unwrap();
3713 if let Value::Attrs(sd_attrs) = &sd {
3714 let services = force_value(sd_attrs.get("services").unwrap()).unwrap();
3716 if let Value::Attrs(a) = &services {
3717 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3718 } else { panic!("expected services attrs"); }
3719 let tmpfiles = force_value(sd_attrs.get("tmpfiles").unwrap()).unwrap();
3721 if let Value::Attrs(a) = &tmpfiles {
3722 let z = force_value(a.get("z").unwrap()).unwrap();
3723 if let Value::Attrs(zd) = &z {
3724 assert_eq!(force_value(zd.get("d").unwrap()).unwrap(), Value::Int(2));
3725 } else { panic!("expected z attrs"); }
3726 } else { panic!("expected tmpfiles attrs"); }
3727 } else {
3728 panic!("expected sd attrs");
3729 }
3730 }
3731
3732 #[test]
3741 fn with_namespace_is_lazy_on_body_whnf() {
3742 let v = ev(r#"builtins.attrNames (with (throw "WITH-FORCED"); { a = 1; b = 2; })"#);
3743 if let Value::List(items) = force_value(&v).unwrap() {
3744 let names: Vec<String> = items
3745 .iter()
3746 .map(|i| match force_value(i).unwrap() {
3747 Value::String(s) => s.as_str().to_string(),
3748 other => panic!("expected string, got {}", other.type_name()),
3749 })
3750 .collect();
3751 assert_eq!(names, vec!["a".to_string(), "b".to_string()]);
3752 } else {
3753 panic!("expected list");
3754 }
3755 }
3756
3757 #[test]
3758 fn with_namespace_forces_only_on_fallthrough() {
3759 assert_eq!(ev(r#"with { x = 42; }; x"#), Value::Int(42));
3763 assert_eq!(ev(r#"let x = 7; in with (throw "NS"); x"#), Value::Int(7));
3766 }
3767
3768 #[test]
3779 fn dotted_fullset_leaf_deep_merges_with_deeper_sibling() {
3780 let v = ev(r#"{ o.a = { x = 1; }; o.a.y = 2; }.o.a"#);
3781 if let Value::Attrs(a) = force_value(&v).unwrap() {
3782 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3783 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3784 } else {
3785 panic!("expected attrs");
3786 }
3787 }
3788
3789 #[test]
3790 fn dotted_fullset_leaf_deep_merge_reverse_order() {
3791 let v = ev(r#"{ o.a.y = 2; o.a = { x = 1; }; }.o.a"#);
3794 if let Value::Attrs(a) = force_value(&v).unwrap() {
3795 assert_eq!(force_value(a.get("x").unwrap()).unwrap(), Value::Int(1));
3796 assert_eq!(force_value(a.get("y").unwrap()).unwrap(), Value::Int(2));
3797 } else {
3798 panic!("expected attrs");
3799 }
3800 }
3801
3802 #[test]
3803 fn dotted_fullset_leaf_merge_preserves_leaf_laziness() {
3804 assert_eq!(ev(r#"{ o.a = { x = throw "X-NEVER"; }; o.a.y = 2; }.o.a.y"#), Value::Int(2));
3808 }
3809
3810 #[test]
3811 fn eval_nested_let() {
3812 assert_eq!(ev("let a = 1; b = let c = 2; in c; in a + b"), Value::Int(3));
3813 }
3814
3815 #[test]
3816 fn eval_lambda() {
3817 assert_eq!(ev("(x: x + 1) 41"), Value::Int(42));
3818 }
3819
3820 #[test]
3821 fn eval_lambda_multi_arg() {
3822 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
3823 }
3824
3825 #[test]
3826 fn eval_list() {
3827 let v = ev("[1 2 3]");
3828 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
3829 }
3830
3831 #[test]
3832 fn eval_list_concat() {
3833 let v = ev("[1 2] ++ [3 4]");
3834 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
3835 }
3836
3837 #[test]
3838 fn eval_attrset() {
3839 let v = ev("{ a = 1; b = 2; }");
3840 if let Value::Attrs(attrs) = v {
3841 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3842 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
3843 } else {
3844 panic!("expected attrset");
3845 }
3846 }
3847
3848 #[test]
3849 fn eval_select() {
3850 assert_eq!(ev("{ a = 42; }.a"), Value::Int(42));
3851 }
3852
3853 #[test]
3854 fn eval_select_or() {
3855 assert_eq!(ev("{ a = 42; }.b or 0"), Value::Int(0));
3856 }
3857
3858 #[test]
3859 fn eval_has_attr() {
3860 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
3861 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
3862 }
3863
3864 #[test]
3865 fn eval_update() {
3866 let v = ev("{ a = 1; b = 2; } // { b = 3; c = 4; }");
3867 if let Value::Attrs(attrs) = v {
3868 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
3869 assert_eq!(attrs.get("b"), Some(&Value::Int(3)));
3870 assert_eq!(attrs.get("c"), Some(&Value::Int(4)));
3871 } else {
3872 panic!("expected attrset");
3873 }
3874 }
3875
3876 #[test]
3877 fn eval_with() {
3878 assert_eq!(ev("with { x = 42; }; x"), Value::Int(42));
3879 }
3880
3881 #[test]
3882 fn eval_assert() {
3883 assert_eq!(ev("assert true; 42"), Value::Int(42));
3884 assert!(eval("assert false; 42").is_err());
3885 }
3886
3887 #[test]
3888 fn eval_formals() {
3889 assert_eq!(ev("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3));
3890 }
3891
3892 #[test]
3893 fn eval_formals_default() {
3894 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 1; }"), Value::Int(11));
3895 }
3896
3897 #[test]
3898 fn eval_formals_ellipsis() {
3899 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; }"), Value::Int(1));
3900 }
3901
3902 #[test]
3903 fn eval_named_formals() {
3904 assert_eq!(ev("(args @ { a }: args.a) { a = 42; }"), Value::Int(42));
3905 }
3906
3907 #[test]
3908 fn eval_rec_attrset() {
3909 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
3910 }
3911
3912 #[test]
3913 fn eval_negation() {
3914 assert_eq!(ev("-42"), Value::Int(-42));
3915 }
3916
3917 #[test]
3918 fn eval_float_arithmetic() {
3919 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
3920 assert_eq!(ev("1 + 1.5"), Value::Float(2.5));
3921 }
3922
3923 #[test]
3924 fn eval_division_by_zero() {
3925 assert!(eval("1 / 0").is_err());
3926 }
3927
3928 #[test]
3929 fn eval_builtins_available() {
3930 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
3931 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
3932 }
3933
3934 #[test]
3935 fn eval_builtins_length() {
3936 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
3937 }
3938
3939 #[test]
3940 fn eval_builtins_head_tail() {
3941 assert_eq!(ev("builtins.head [1 2 3]"), Value::Int(1));
3942 assert_eq!(ev("builtins.length (builtins.tail [1 2 3])"), Value::Int(2));
3943 }
3944
3945 #[test]
3946 fn eval_builtins_add() {
3947 assert_eq!(ev("builtins.add 1 2"), Value::Int(3));
3948 }
3949
3950 #[test]
3951 fn eval_builtins_to_string() {
3952 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
3953 }
3954
3955 #[test]
3956 fn eval_implication() {
3957 assert_eq!(ev("false -> true"), Value::Bool(true));
3958 assert_eq!(ev("true -> false"), Value::Bool(false));
3959 assert_eq!(ev("true -> true"), Value::Bool(true));
3960 }
3961
3962 #[test]
3965 fn eval_error_undefined_variable() {
3966 let result = eval("nonexistent");
3967 assert!(result.is_err());
3968 let msg = format!("{}", result.unwrap_err());
3969 assert!(msg.contains("undefined variable"));
3970 }
3971
3972 #[test]
3973 fn eval_error_type_mismatch_arithmetic() {
3974 let result = eval(r#"1 + "hello""#);
3975 assert!(result.is_err());
3976 let msg = format!("{}", result.unwrap_err());
3977 assert!(msg.contains("cannot add") || msg.contains("type"));
3978 }
3979
3980 #[test]
3981 fn eval_error_unexpected_argument() {
3982 let result = eval("({ a }: a) { a = 1; b = 2; }");
3983 assert!(result.is_err());
3984 let msg = format!("{}", result.unwrap_err());
3985 assert!(msg.contains("unexpected argument"));
3986 }
3987
3988 #[test]
3989 fn eval_error_missing_required_argument() {
3990 let result = eval("({ a, b }: a + b) { a = 1; }");
3991 assert!(result.is_err());
3992 let msg = format!("{}", result.unwrap_err());
3993 assert!(msg.contains("missing argument"));
3994 }
3995
3996 #[test]
3997 fn eval_builtins_attr_names_sorted() {
3998 let v = ev("builtins.attrNames { z = 1; a = 2; m = 3; }");
3999 assert_eq!(
4001 v,
4002 Value::list(vec![
4003 Value::string("a"),
4004 Value::string("m"),
4005 Value::string("z"),
4006 ]),
4007 );
4008 }
4009
4010 #[test]
4011 fn eval_builtins_attr_values() {
4012 let v = ev("builtins.attrValues { a = 1; b = 2; }");
4013 assert_eq!(v, Value::list(vec![Value::Int(1), Value::Int(2)]));
4015 }
4016
4017 #[test]
4018 fn eval_builtins_is_null() {
4019 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
4020 assert_eq!(ev("builtins.isNull 1"), Value::Bool(false));
4021 }
4022
4023 #[test]
4024 fn eval_builtins_is_int() {
4025 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
4026 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
4027 }
4028
4029 #[test]
4030 fn eval_builtins_is_bool() {
4031 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
4032 assert_eq!(ev("builtins.isBool 0"), Value::Bool(false));
4033 }
4034
4035 #[test]
4036 fn eval_builtins_is_string() {
4037 assert_eq!(ev(r#"builtins.isString "hi""#), Value::Bool(true));
4038 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
4039 }
4040
4041 #[test]
4042 fn eval_builtins_is_list() {
4043 assert_eq!(ev("builtins.isList [1 2]"), Value::Bool(true));
4044 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
4045 }
4046
4047 #[test]
4048 fn eval_builtins_is_attrs() {
4049 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
4050 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
4051 }
4052
4053 #[test]
4054 fn eval_builtins_string_length() {
4055 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
4056 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
4057 }
4058
4059 #[test]
4060 fn eval_builtins_to_json_roundtrip() {
4061 assert_eq!(
4063 ev(r#"builtins.fromJSON (builtins.toJSON 42)"#),
4064 Value::Int(42),
4065 );
4066 assert_eq!(
4067 ev(r#"builtins.fromJSON (builtins.toJSON [1 2 3])"#),
4068 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4069 );
4070 }
4071
4072 #[test]
4073 fn eval_builtins_from_json() {
4074 assert_eq!(
4075 ev(r#"builtins.fromJSON "{\"a\": 1}""#),
4076 {
4077 let mut attrs = NixAttrs::new();
4078 attrs.insert("a".to_string(), Value::Int(1));
4079 Value::Attrs(Rc::new(attrs))
4080 },
4081 );
4082 assert_eq!(ev(r#"builtins.fromJSON "null""#), Value::Null);
4083 assert_eq!(ev(r#"builtins.fromJSON "true""#), Value::Bool(true));
4084 }
4085
4086 #[test]
4087 fn eval_nested_function_application() {
4088 assert_eq!(ev("(x: y: x + y) 1 2"), Value::Int(3));
4090 assert_eq!(ev("((x: y: x + y) 1) 2"), Value::Int(3));
4092 }
4093
4094 #[test]
4095 fn eval_recursive_let() {
4096 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4097 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4098 }
4099
4100 #[test]
4101 fn eval_string_comparison() {
4102 assert_eq!(ev(r#""a" < "b""#), Value::Bool(true));
4103 assert_eq!(ev(r#""b" < "a""#), Value::Bool(false));
4104 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4105 assert_eq!(ev(r#""abc" != "def""#), Value::Bool(true));
4106 }
4107
4108 #[test]
4109 fn eval_list_in_attrset() {
4110 let v = ev("{ x = [1 2 3]; }.x");
4111 assert_eq!(
4112 v,
4113 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4114 );
4115 }
4116
4117 #[test]
4118 fn eval_nested_attrset_select() {
4119 assert_eq!(ev("{ a = { b = 42; }; }.a.b"), Value::Int(42));
4120 }
4121
4122 #[test]
4123 fn eval_let_shadows_outer() {
4124 assert_eq!(
4125 ev("let x = 1; in let x = 2; in x"),
4126 Value::Int(2),
4127 );
4128 }
4129
4130 #[test]
4131 fn eval_with_provides_scope() {
4132 assert_eq!(
4134 ev("with { x = 42; y = 10; }; x + y"),
4135 Value::Int(52),
4136 );
4137 }
4138
4139 #[test]
4140 fn eval_list_equality() {
4141 assert_eq!(ev("[1 2] == [1 2]"), Value::Bool(true));
4142 assert_eq!(ev("[1 2] == [1 3]"), Value::Bool(false));
4143 }
4144
4145 #[test]
4146 fn eval_attrset_equality() {
4147 assert_eq!(ev("{ a = 1; } == { a = 1; }"), Value::Bool(true));
4148 assert_eq!(ev("{ a = 1; } == { a = 2; }"), Value::Bool(false));
4149 }
4150
4151 #[test]
4156 fn literal_int_large_zero_negative() {
4157 assert_eq!(ev("9223372036854775807"), Value::Int(i64::MAX));
4159 assert_eq!(ev("0"), Value::Int(0));
4161 assert_eq!(ev("-1"), Value::Int(-1));
4163 assert_eq!(ev("-999999"), Value::Int(-999999));
4164 }
4165
4166 #[test]
4167 fn literal_float_small_large() {
4168 assert_eq!(ev("0.001"), Value::Float(0.001));
4169 assert_eq!(ev("999999.999"), Value::Float(999999.999));
4170 assert_eq!(ev("1.0e3"), Value::Float(1000.0));
4172 assert_eq!(ev("1.5e2"), Value::Float(150.0));
4173 }
4174
4175 #[test]
4176 fn literal_string_empty_and_escapes() {
4177 assert_eq!(ev(r#""""#), Value::string(""));
4178 assert_eq!(ev(r#""hello\nworld""#), Value::string("hello\nworld"));
4180 assert_eq!(ev(r#""tab\there""#), Value::string("tab\there"));
4181 }
4182
4183 #[test]
4184 fn literal_multiline_string() {
4185 assert_eq!(
4187 ev("''hello''"),
4188 Value::string("hello"),
4189 );
4190 assert_eq!(
4192 ev("''\n line1\n line2\n''"),
4193 Value::string("line1\nline2\n"),
4194 );
4195 }
4196
4197 #[test]
4198 fn literal_paths() {
4199 assert_eq!(ev("./foo"), Value::Path(Box::new(SmolStr::from("./foo"))));
4201 assert_eq!(ev("/nix/store/abc"), Value::Path(Box::new(SmolStr::from("/nix/store/abc"))));
4203 assert_eq!(ev("~/myfile"), Value::Path(Box::new(SmolStr::from("~/myfile"))));
4205 }
4206
4207 #[test]
4217 fn interp_path_abs_splices_and_types_path() {
4218 let v = ev(r#"let x = "foo"; in /a/${x}/b"#);
4220 assert_eq!(v, Value::Path(Box::new(SmolStr::from("/a/foo/b"))));
4221 }
4222
4223 #[test]
4224 fn interp_path_abs_multi_and_slash_in_value() {
4225 assert_eq!(
4227 ev(r#"let a = "x"; b = "y/z"; in /p/${a}/${b}.nix"#),
4228 Value::Path(Box::new(SmolStr::from("/p/x/y/z.nix"))),
4229 );
4230 }
4231
4232 #[test]
4233 fn interp_path_abs_normalizes_double_slash_seam() {
4234 assert_eq!(
4237 ev(r#"/bar/${/tmp/foo}"#),
4238 Value::Path(Box::new(SmolStr::from("/bar/tmp/foo"))),
4239 );
4240 }
4241
4242 #[test]
4243 fn interp_path_rel_resolves_against_eval_dir() {
4244 let _g = push_eval_file(std::path::PathBuf::from("/tmp/example/default.nix"));
4248 assert_eq!(
4249 ev(r#"let x = "foo"; in ./${x}.nix"#),
4250 Value::Path(Box::new(SmolStr::from("/tmp/example/foo.nix"))),
4251 );
4252 }
4253
4254 #[test]
4255 fn interp_path_rel_no_eval_dir_keeps_relative_text() {
4256 assert_eq!(
4259 ev(r#"let x = "foo"; in ./${x}.nix"#),
4260 Value::Path(Box::new(SmolStr::from("./foo.nix"))),
4261 );
4262 }
4263
4264 #[test]
4265 fn interp_path_home_splices_leading_tilde_preserved() {
4266 assert_eq!(
4270 ev(r#"let x = "foo"; in ~/${x}/bar"#),
4271 Value::Path(Box::new(SmolStr::from("~/foo/bar"))),
4272 );
4273 }
4274
4275 #[test]
4276 fn interp_path_non_interpolated_still_raw() {
4277 assert_eq!(ev("/a/b/c"), Value::Path(Box::new(SmolStr::from("/a/b/c"))));
4280 assert_eq!(ev("~/plain"), Value::Path(Box::new(SmolStr::from("~/plain"))));
4281 }
4282
4283 #[test]
4284 fn literal_null_true_false_standalone() {
4285 assert_eq!(ev("null"), Value::Null);
4286 assert_eq!(ev("true"), Value::Bool(true));
4287 assert_eq!(ev("false"), Value::Bool(false));
4288 }
4289
4290 #[test]
4295 fn op_arithmetic_int() {
4296 assert_eq!(ev("100 + 200"), Value::Int(300));
4297 assert_eq!(ev("50 - 30"), Value::Int(20));
4298 assert_eq!(ev("7 * 8"), Value::Int(56));
4299 assert_eq!(ev("17 / 3"), Value::Int(5)); }
4301
4302 #[test]
4303 fn op_arithmetic_float() {
4304 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
4305 assert_eq!(ev("5.0 - 1.5"), Value::Float(3.5));
4306 assert_eq!(ev("2.0 * 3.0"), Value::Float(6.0));
4307 assert_eq!(ev("7.0 / 2.0"), Value::Float(3.5));
4308 }
4309
4310 #[test]
4311 fn op_arithmetic_mixed_int_float() {
4312 assert_eq!(ev("1 + 2.5"), Value::Float(3.5));
4314 assert_eq!(ev("2.5 + 1"), Value::Float(3.5));
4315 assert_eq!(ev("2 * 1.5"), Value::Float(3.0));
4317 assert_eq!(ev("5.5 - 2"), Value::Float(3.5));
4319 }
4320
4321 #[test]
4322 fn op_string_concat() {
4323 assert_eq!(ev(r#""foo" + "bar""#), Value::string("foobar"));
4324 assert_eq!(ev(r#""" + "x""#), Value::string("x"));
4325 assert_eq!(ev(r#""a" + "" + "b""#), Value::string("ab"));
4326 }
4327
4328 #[test]
4329 fn op_path_concat() {
4330 assert_eq!(ev(r#"./foo + "/bar""#), Value::Path(Box::new(SmolStr::from("./foo/bar"))));
4332 assert_eq!(ev("./a + ./b"), Value::Path(Box::new(SmolStr::from("./a/./b"))));
4334 }
4335
4336 #[test]
4337 fn op_comparison_ints() {
4338 assert_eq!(ev("1 < 2"), Value::Bool(true));
4339 assert_eq!(ev("2 < 1"), Value::Bool(false));
4340 assert_eq!(ev("2 > 1"), Value::Bool(true));
4341 assert_eq!(ev("1 > 2"), Value::Bool(false));
4342 assert_eq!(ev("2 <= 2"), Value::Bool(true));
4343 assert_eq!(ev("3 <= 2"), Value::Bool(false));
4344 assert_eq!(ev("2 >= 2"), Value::Bool(true));
4345 assert_eq!(ev("1 >= 2"), Value::Bool(false));
4346 }
4347
4348 #[test]
4349 fn op_comparison_floats() {
4350 assert_eq!(ev("1.5 < 2.5"), Value::Bool(true));
4351 assert_eq!(ev("2.5 > 1.5"), Value::Bool(true));
4352 assert_eq!(ev("1.5 <= 1.5"), Value::Bool(true));
4353 assert_eq!(ev("1.5 >= 1.5"), Value::Bool(true));
4354 }
4355
4356 #[test]
4357 fn op_comparison_strings() {
4358 assert_eq!(ev(r#""apple" < "banana""#), Value::Bool(true));
4359 assert_eq!(ev(r#""banana" > "apple""#), Value::Bool(true));
4360 assert_eq!(ev(r#""abc" == "abc""#), Value::Bool(true));
4361 assert_eq!(ev(r#""abc" != "xyz""#), Value::Bool(true));
4362 assert_eq!(ev(r#""abc" <= "abd""#), Value::Bool(true));
4363 assert_eq!(ev(r#""abc" >= "abb""#), Value::Bool(true));
4364 }
4365
4366 #[test]
4367 fn op_equality_various_types() {
4368 assert_eq!(ev("null == null"), Value::Bool(true));
4369 assert_eq!(ev("true == true"), Value::Bool(true));
4370 assert_eq!(ev("false == false"), Value::Bool(true));
4371 assert_eq!(ev("true == false"), Value::Bool(false));
4372 assert_eq!(ev("1 == 1"), Value::Bool(true));
4373 assert_eq!(ev("1 != 2"), Value::Bool(true));
4374 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
4376 assert_eq!(ev("null == false"), Value::Bool(false));
4377 }
4378
4379 #[test]
4380 fn op_logic_short_circuit() {
4381 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
4383 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
4385 }
4386
4387 #[test]
4388 fn op_logic_full() {
4389 assert_eq!(ev("true && true"), Value::Bool(true));
4390 assert_eq!(ev("true && false"), Value::Bool(false));
4391 assert_eq!(ev("false && true"), Value::Bool(false));
4392 assert_eq!(ev("false && false"), Value::Bool(false));
4393 assert_eq!(ev("true || true"), Value::Bool(true));
4394 assert_eq!(ev("true || false"), Value::Bool(true));
4395 assert_eq!(ev("false || true"), Value::Bool(true));
4396 assert_eq!(ev("false || false"), Value::Bool(false));
4397 assert_eq!(ev("!true"), Value::Bool(false));
4398 assert_eq!(ev("!false"), Value::Bool(true));
4399 }
4400
4401 #[test]
4402 fn op_implication_truth_table() {
4403 assert_eq!(ev("false -> false"), Value::Bool(true));
4405 assert_eq!(ev("false -> true"), Value::Bool(true));
4406 assert_eq!(ev("true -> true"), Value::Bool(true));
4408 assert_eq!(ev("true -> false"), Value::Bool(false));
4409 }
4410
4411 #[test]
4412 fn op_implication_short_circuit() {
4413 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
4415 }
4416
4417 #[test]
4418 fn op_update_merge() {
4419 let v = ev("{ a = 1; } // { b = 2; }");
4420 if let Value::Attrs(attrs) = v {
4421 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4422 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4423 } else {
4424 panic!("expected attrs");
4425 }
4426 }
4427
4428 #[test]
4429 fn op_update_right_wins() {
4430 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4431 }
4432
4433 #[test]
4434 fn op_list_concat() {
4435 assert_eq!(
4436 ev("[1 2] ++ [3 4]"),
4437 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]),
4438 );
4439 assert_eq!(ev("[] ++ [1]"), Value::list(vec![Value::Int(1)]));
4441 assert_eq!(ev("[1] ++ []"), Value::list(vec![Value::Int(1)]));
4442 }
4443
4444 #[test]
4445 fn op_has_attr_present_and_absent() {
4446 assert_eq!(ev("{ x = 1; y = 2; } ? x"), Value::Bool(true));
4447 assert_eq!(ev("{ x = 1; } ? z"), Value::Bool(false));
4448 assert_eq!(ev("{} ? anything"), Value::Bool(false));
4449 }
4450
4451 #[test]
4452 fn op_unary_negate() {
4453 assert_eq!(ev("-42"), Value::Int(-42));
4454 assert_eq!(ev("-3.14"), Value::Float(-3.14));
4455 assert_eq!(ev("- -5"), Value::Int(5));
4457 }
4458
4459 #[test]
4464 fn control_if_true_branch() {
4465 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
4466 }
4467
4468 #[test]
4469 fn control_if_false_branch() {
4470 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
4471 }
4472
4473 #[test]
4474 fn control_if_nested() {
4475 assert_eq!(
4476 ev("if true then (if false then 1 else 2) else 3"),
4477 Value::Int(2),
4478 );
4479 assert_eq!(
4480 ev("if false then 1 else (if true then 2 else 3)"),
4481 Value::Int(2),
4482 );
4483 }
4484
4485 #[test]
4486 fn control_assert_passing() {
4487 assert_eq!(ev("assert 1 == 1; 42"), Value::Int(42));
4488 assert_eq!(ev("assert true; true"), Value::Bool(true));
4489 }
4490
4491 #[test]
4492 fn control_assert_failing() {
4493 assert!(eval("assert false; 42").is_err());
4494 assert!(eval("assert 1 == 2; 42").is_err());
4495 }
4496
4497 #[test]
4498 fn control_with_basic_scope() {
4499 assert_eq!(ev("with { a = 1; b = 2; }; a + b"), Value::Int(3));
4500 }
4501
4502 #[test]
4503 fn control_with_lexical_precedence() {
4504 assert_eq!(
4506 ev("let x = 10; in with { x = 99; }; x"),
4507 Value::Int(10),
4508 );
4509 }
4510
4511 #[test]
4512 fn control_with_nested() {
4513 assert_eq!(
4514 ev("with { a = 1; }; with { b = 2; }; a + b"),
4515 Value::Int(3),
4516 );
4517 }
4518
4519 #[test]
4520 fn control_with_lazy_fix_self() {
4521 let result = eval(
4526 "let fix = f: let x = f x; in x; in fix (self: with self; { a = 1; b = a + 1; })"
4527 );
4528 assert!(result.is_ok(), "fix with self should work: {:?}", result);
4529 if let Ok(Value::Attrs(attrs)) = result {
4530 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4531 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4532 } else {
4533 panic!("expected Attrs, got {:?}", result);
4534 }
4535 }
4536
4537 #[test]
4538 fn control_with_lazy_fix_self_lib_pattern() {
4539 let result = eval(r#"
4542 let fix = f: let x = f x; in x;
4543 in (fix (self: with self; {
4544 lib = { version = "1.0"; };
4545 hello = "hello ${lib.version}";
4546 })).hello
4547 "#);
4548 assert!(result.is_ok(), "nixpkgs-style lib pattern: {:?}", result);
4549 assert_eq!(
4550 result.unwrap(),
4551 Value::String(Rc::new(NixString::plain("hello 1.0"))),
4552 );
4553 }
4554
4555 #[test]
4556 fn control_with_non_attrset_errors() {
4557 let result = eval("with 42; 1");
4559 assert_eq!(result.unwrap(), Value::Int(1));
4562 }
4563
4564 #[test]
4565 fn control_with_non_attrset_lookup_falls_through() {
4566 let result = eval("let x = 1; in with 42; x");
4569 assert_eq!(result.unwrap(), Value::Int(1));
4570 }
4571
4572 #[test]
4573 fn control_let_simple_and_multiple() {
4574 assert_eq!(ev("let x = 5; in x"), Value::Int(5));
4575 assert_eq!(ev("let x = 1; y = 2; z = 3; in x + y + z"), Value::Int(6));
4576 }
4577
4578 #[test]
4579 fn control_let_shadow_outer() {
4580 assert_eq!(
4581 ev("let x = 1; in let x = 2; in x"),
4582 Value::Int(2),
4583 );
4584 }
4585
4586 #[test]
4587 fn control_let_recursive_reference() {
4588 assert_eq!(ev("let a = 1; b = a + 1; in b"), Value::Int(2));
4589 assert_eq!(ev("let a = 1; b = a + 1; c = b + 1; in c"), Value::Int(3));
4590 }
4591
4592 #[test]
4593 fn control_nested_let_expression() {
4594 assert_eq!(
4595 ev("let a = let b = 1; in b; in a"),
4596 Value::Int(1),
4597 );
4598 assert_eq!(
4599 ev("let a = let b = 10; in b + 5; in a * 2"),
4600 Value::Int(30),
4601 );
4602 }
4603
4604 #[test]
4609 fn func_identity_lambda() {
4610 assert_eq!(ev("(x: x) 42"), Value::Int(42));
4611 assert_eq!(ev(r#"(x: x) "hello""#), Value::string("hello"));
4612 }
4613
4614 #[test]
4615 fn func_curried_two_args() {
4616 assert_eq!(ev("(x: y: x + y) 3 4"), Value::Int(7));
4617 }
4618
4619 #[test]
4620 fn func_curried_three_args() {
4621 assert_eq!(ev("(a: b: c: a + b + c) 1 2 3"), Value::Int(6));
4622 }
4623
4624 #[test]
4625 fn func_formals_basic() {
4626 assert_eq!(ev("({ a, b }: a + b) { a = 3; b = 7; }"), Value::Int(10));
4627 }
4628
4629 #[test]
4630 fn func_formals_with_defaults() {
4631 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; }"), Value::Int(15));
4632 assert_eq!(ev("({ a, b ? 10 }: a + b) { a = 5; b = 20; }"), Value::Int(25));
4634 }
4635
4636 #[test]
4637 fn func_formals_with_ellipsis() {
4638 assert_eq!(ev("({ a, ... }: a) { a = 1; b = 2; c = 3; }"), Value::Int(1));
4639 }
4640
4641 #[test]
4642 fn func_named_formals_at_before() {
4643 assert_eq!(
4645 ev("(args @ { a, b }: args.a + args.b) { a = 3; b = 4; }"),
4646 Value::Int(7),
4647 );
4648 }
4649
4650 #[test]
4651 fn func_named_formals_at_after() {
4652 assert_eq!(
4654 ev("({ a, b } @ args: args.a + args.b) { a = 10; b = 20; }"),
4655 Value::Int(30),
4656 );
4657 }
4658
4659 #[test]
4660 fn func_nested_application() {
4661 assert_eq!(ev("((x: y: x * y) 3) 4"), Value::Int(12));
4663 }
4664
4665 #[test]
4666 fn func_higher_order_map() {
4667 assert_eq!(
4668 ev("builtins.map (x: x * 2) [1 2 3]"),
4669 Value::list(vec![Value::Int(2), Value::Int(4), Value::Int(6)]),
4670 );
4671 }
4672
4673 #[test]
4674 fn func_higher_order_filter() {
4675 assert_eq!(
4676 ev("builtins.filter (x: x > 2) [1 2 3 4 5]"),
4677 Value::list(vec![Value::Int(3), Value::Int(4), Value::Int(5)]),
4678 );
4679 }
4680
4681 #[test]
4682 fn func_higher_order_foldl() {
4683 assert_eq!(
4685 ev("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4]"),
4686 Value::Int(10),
4687 );
4688 }
4689
4690 #[test]
4691 fn func_as_attrset_value() {
4692 assert_eq!(
4693 ev("let s = { f = x: x + 1; }; in s.f 5"),
4694 Value::Int(6),
4695 );
4696 }
4697
4698 #[test]
4699 fn func_immediate_application() {
4700 assert_eq!(ev("(x: x * x) 7"), Value::Int(49));
4701 }
4702
4703 #[test]
4704 fn func_in_let_binding() {
4705 assert_eq!(
4706 ev("let double = x: x * 2; in double 21"),
4707 Value::Int(42),
4708 );
4709 }
4710
4711 #[test]
4716 fn attrs_empty_set() {
4717 let v = ev("{}");
4718 if let Value::Attrs(attrs) = v {
4719 assert!(attrs.is_empty());
4720 } else {
4721 panic!("expected attrs");
4722 }
4723 }
4724
4725 #[test]
4726 fn attrs_simple() {
4727 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
4728 }
4729
4730 #[test]
4731 fn attrs_nested_access() {
4732 assert_eq!(ev("{ a = { b = { c = 42; }; }; }.a.b.c"), Value::Int(42));
4733 }
4734
4735 #[test]
4736 fn attrs_recursive_set() {
4737 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
4738 }
4739
4740 #[test]
4741 fn attrs_update_disjoint() {
4742 let v = ev("{ a = 1; } // { b = 2; }");
4743 if let Value::Attrs(attrs) = v {
4744 assert_eq!(attrs.len(), 2);
4745 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
4746 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
4747 } else {
4748 panic!("expected attrs");
4749 }
4750 }
4751
4752 #[test]
4753 fn attrs_update_override() {
4754 assert_eq!(ev("({ a = 1; } // { a = 2; }).a"), Value::Int(2));
4755 }
4756
4757 #[test]
4758 fn attrs_has_attr_operator() {
4759 assert_eq!(ev("{ a = 1; } ? a"), Value::Bool(true));
4760 assert_eq!(ev("{ a = 1; } ? b"), Value::Bool(false));
4761 }
4762
4763 #[test]
4764 fn attrs_select_with_default() {
4765 assert_eq!(ev("{ a = 1; }.a or 99"), Value::Int(1));
4766 assert_eq!(ev("{}.missing or 99"), Value::Int(99));
4767 assert_eq!(ev("{ a = 1; }.b or 42"), Value::Int(42));
4768 }
4769
4770 #[test]
4771 fn attrs_nested_attr_path_in_binding() {
4772 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
4774 }
4775
4776 #[test]
4777 fn attrs_inherit_from_scope() {
4778 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.x"), Value::Int(1));
4779 assert_eq!(ev("let x = 1; y = 2; in { inherit x y; }.y"), Value::Int(2));
4780 }
4781
4782 #[test]
4783 fn attrs_inherit_from_expr() {
4784 assert_eq!(
4785 ev("{ inherit ({ a = 42; b = 10; }) a; }.a"),
4786 Value::Int(42),
4787 );
4788 }
4789
4790 #[test]
4791 fn attrs_dynamic_attr_name() {
4792 assert_eq!(
4793 ev(r#"let name = "x"; in { ${name} = 42; }.x"#),
4794 Value::Int(42),
4795 );
4796 }
4797
4798 #[test]
4799 fn attrs_attr_names_sorted() {
4800 assert_eq!(
4801 ev("builtins.attrNames { z = 1; m = 2; a = 3; }"),
4802 Value::list(vec![
4803 Value::string("a"),
4804 Value::string("m"),
4805 Value::string("z"),
4806 ]),
4807 );
4808 }
4809
4810 #[test]
4811 fn attrs_attr_values_follow_key_order() {
4812 assert_eq!(
4814 ev("builtins.attrValues { c = 3; a = 1; b = 2; }"),
4815 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4816 );
4817 }
4818
4819 #[test]
4820 fn attrs_update_is_shallow() {
4821 assert_eq!(
4823 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a ? x"),
4824 Value::Bool(false),
4825 );
4826 assert_eq!(
4827 ev("({ a = { x = 1; }; } // { a = { y = 2; }; }).a.y"),
4828 Value::Int(2),
4829 );
4830 }
4831
4832 #[test]
4837 fn list_empty() {
4838 assert_eq!(ev("[]"), Value::list(vec![]));
4839 }
4840
4841 #[test]
4842 fn list_single_element() {
4843 assert_eq!(ev("[1]"), Value::list(vec![Value::Int(1)]));
4844 }
4845
4846 #[test]
4847 fn list_mixed_types() {
4848 assert_eq!(
4849 ev(r#"[1 "two" true null]"#),
4850 Value::list(vec![
4851 Value::Int(1),
4852 Value::string("two"),
4853 Value::Bool(true),
4854 Value::Null,
4855 ]),
4856 );
4857 }
4858
4859 #[test]
4860 fn list_nested() {
4861 assert_eq!(
4862 ev("[[1 2] [3 4]]"),
4863 Value::list(vec![
4864 Value::list(vec![Value::Int(1), Value::Int(2)]),
4865 Value::list(vec![Value::Int(3), Value::Int(4)]),
4866 ]),
4867 );
4868 }
4869
4870 #[test]
4871 fn list_concat_operator() {
4872 assert_eq!(
4873 ev("[1] ++ [2] ++ [3]"),
4874 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
4875 );
4876 }
4877
4878 #[test]
4879 fn list_builtins_length() {
4880 assert_eq!(ev("builtins.length [1 2 3]"), Value::Int(3));
4881 assert_eq!(ev("builtins.length []"), Value::Int(0));
4882 }
4883
4884 #[test]
4885 fn list_builtins_elem_at() {
4886 assert_eq!(ev("builtins.elemAt [10 20 30] 0"), Value::Int(10));
4887 assert_eq!(ev("builtins.elemAt [10 20 30] 1"), Value::Int(20));
4888 assert_eq!(ev("builtins.elemAt [10 20 30] 2"), Value::Int(30));
4889 }
4890
4891 #[test]
4892 fn list_equality() {
4893 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
4894 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
4895 assert_eq!(ev("[] == []"), Value::Bool(true));
4896 }
4897
4898 #[test]
4903 fn interp_simple_variable() {
4904 assert_eq!(
4905 ev(r#"let name = "world"; in "hello ${name}""#),
4906 Value::string("hello world"),
4907 );
4908 }
4909
4910 #[test]
4911 fn interp_nested_expression() {
4912 assert_eq!(
4913 ev(r#""result: ${builtins.toString (1 + 2)}""#),
4914 Value::string("result: 3"),
4915 );
4916 }
4917
4918 #[test]
4919 fn interp_int_coercion() {
4920 assert_eq!(
4922 ev(r#"let x = 42; in "count: ${builtins.toString x}""#),
4923 Value::string("count: 42"),
4924 );
4925 }
4926
4927 #[test]
4928 fn interp_multiple() {
4929 assert_eq!(
4930 ev(r#"let a = "foo"; b = "bar"; in "${a} and ${b}""#),
4931 Value::string("foo and bar"),
4932 );
4933 }
4934
4935 #[test]
4936 fn interp_in_let() {
4937 assert_eq!(
4938 ev(r#"let x = "world"; in "hello ${x}""#),
4939 Value::string("hello world"),
4940 );
4941 }
4942
4943 #[test]
4944 fn interp_empty_result() {
4945 assert_eq!(
4946 ev(r#"let x = ""; in "a${x}b""#),
4947 Value::string("ab"),
4948 );
4949 }
4950
4951 #[test]
4952 fn interp_path_in_string_context() {
4953 assert!(eval(r#""path: ${./foo-nonexistent-xyz}""#).is_err());
4959 }
4960
4961 #[test]
4962 fn interp_adjacent_interpolations() {
4963 assert_eq!(
4964 ev(r#"let a = "x"; b = "y"; in "${a}${b}""#),
4965 Value::string("xy"),
4966 );
4967 }
4968
4969 #[test]
4974 fn builtins_map_filter_foldl() {
4975 assert_eq!(
4977 ev("builtins.map (x: x + 10) [1 2 3]"),
4978 Value::list(vec![Value::Int(11), Value::Int(12), Value::Int(13)]),
4979 );
4980 assert_eq!(
4982 ev("builtins.filter (x: x > 1) [1 2 3]"),
4983 Value::list(vec![Value::Int(2), Value::Int(3)]),
4984 );
4985 assert_eq!(
4987 ev("builtins.foldl' (a: b: a * b) 1 [2 3 4]"),
4988 Value::Int(24),
4989 );
4990 }
4991
4992 #[test]
4993 fn builtins_map_attrs() {
4994 assert_eq!(
4995 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).a"),
4996 Value::Int(2),
4997 );
4998 assert_eq!(
4999 ev("(builtins.mapAttrs (name: value: value * 2) { a = 1; b = 2; }).b"),
5000 Value::Int(4),
5001 );
5002 }
5003
5004 #[test]
5005 fn builtins_list_to_attrs() {
5006 assert_eq!(
5007 ev(r#"(builtins.listToAttrs [{ name = "x"; value = 1; } { name = "y"; value = 2; }]).x"#),
5008 Value::Int(1),
5009 );
5010 }
5011
5012 #[test]
5013 fn builtins_list_to_attrs_duplicate_key_first_wins() {
5014 assert_eq!(
5023 ev(r#"(builtins.listToAttrs [{ name = "k"; value = 1; } { name = "k"; value = 2; }]).k"#),
5024 Value::Int(1),
5025 );
5026 }
5027
5028 #[test]
5029 fn builtins_concat_map() {
5030 assert_eq!(
5031 ev("builtins.concatMap (x: [x (x * 2)]) [1 2 3]"),
5032 Value::list(vec![
5033 Value::Int(1), Value::Int(2),
5034 Value::Int(2), Value::Int(4),
5035 Value::Int(3), Value::Int(6),
5036 ]),
5037 );
5038 }
5039
5040 #[test]
5041 fn builtins_concat_lists() {
5042 assert_eq!(
5043 ev("builtins.concatLists [[1 2] [3] [4 5]]"),
5044 Value::list(vec![
5045 Value::Int(1), Value::Int(2), Value::Int(3),
5046 Value::Int(4), Value::Int(5),
5047 ]),
5048 );
5049 }
5050
5051 #[test]
5052 fn builtins_concat_strings_sep() {
5053 assert_eq!(
5054 ev(r#"builtins.concatStringsSep ", " ["a" "b" "c"]"#),
5055 Value::string("a, b, c"),
5056 );
5057 assert_eq!(
5058 ev(r#"builtins.concatStringsSep "" ["x" "y"]"#),
5059 Value::string("xy"),
5060 );
5061 }
5062
5063 #[test]
5064 fn builtins_replace_strings() {
5065 assert_eq!(
5066 ev(r#"builtins.replaceStrings ["o"] ["0"] "foobar""#),
5067 Value::string("f00bar"),
5068 );
5069 assert_eq!(
5070 ev(r#"builtins.replaceStrings ["hello"] ["goodbye"] "hello world""#),
5071 Value::string("goodbye world"),
5072 );
5073 }
5074
5075 #[test]
5076 fn builtins_has_prefix_has_suffix() {
5077 assert_eq!(ev(r#"builtins.hasPrefix "he" "hello""#), Value::Bool(true));
5078 assert_eq!(ev(r#"builtins.hasPrefix "xx" "hello""#), Value::Bool(false));
5079 assert_eq!(ev(r#"builtins.hasSuffix "lo" "hello""#), Value::Bool(true));
5080 assert_eq!(ev(r#"builtins.hasSuffix "xx" "hello""#), Value::Bool(false));
5081 }
5082
5083 #[test]
5084 fn builtins_all_any() {
5085 assert_eq!(ev("builtins.all (x: x > 0) [1 2 3]"), Value::Bool(true));
5086 assert_eq!(ev("builtins.all (x: x > 1) [1 2 3]"), Value::Bool(false));
5087 assert_eq!(ev("builtins.any (x: x > 2) [1 2 3]"), Value::Bool(true));
5088 assert_eq!(ev("builtins.any (x: x > 5) [1 2 3]"), Value::Bool(false));
5089 }
5090
5091 #[test]
5092 fn builtins_sort() {
5093 assert_eq!(
5094 ev("builtins.sort (a: b: a < b) [3 1 2]"),
5095 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5096 );
5097 }
5098
5099 #[test]
5100 fn builtins_remove_attrs() {
5101 let v = ev(r#"builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b" "c"]"#);
5102 if let Value::Attrs(attrs) = v {
5103 assert_eq!(attrs.len(), 1);
5104 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
5105 assert!(attrs.get("b").is_none());
5106 } else {
5107 panic!("expected attrs");
5108 }
5109 }
5110
5111 #[test]
5112 fn builtins_intersect_attrs() {
5113 let v = ev("builtins.intersectAttrs { a = 1; b = 2; } { b = 20; c = 30; }");
5114 if let Value::Attrs(attrs) = v {
5115 assert_eq!(attrs.len(), 1);
5116 assert_eq!(attrs.get("b"), Some(&Value::Int(20)));
5118 } else {
5119 panic!("expected attrs");
5120 }
5121 }
5122
5123 #[test]
5124 fn builtins_type_of_all_types() {
5125 assert_eq!(ev("builtins.typeOf null"), Value::string("null"));
5126 assert_eq!(ev("builtins.typeOf true"), Value::string("bool"));
5127 assert_eq!(ev("builtins.typeOf 42"), Value::string("int"));
5128 assert_eq!(ev("builtins.typeOf 3.14"), Value::string("float"));
5129 assert_eq!(ev(r#"builtins.typeOf "hi""#), Value::string("string"));
5130 assert_eq!(ev("builtins.typeOf [1]"), Value::string("list"));
5131 assert_eq!(ev("builtins.typeOf {}"), Value::string("set"));
5132 assert_eq!(ev("builtins.typeOf (x: x)"), Value::string("lambda"));
5133 }
5134
5135 #[test]
5136 fn builtins_is_type_checks() {
5137 assert_eq!(ev("builtins.isNull null"), Value::Bool(true));
5138 assert_eq!(ev("builtins.isNull 0"), Value::Bool(false));
5139 assert_eq!(ev("builtins.isInt 42"), Value::Bool(true));
5140 assert_eq!(ev("builtins.isInt 3.14"), Value::Bool(false));
5141 assert_eq!(ev("builtins.isBool true"), Value::Bool(true));
5142 assert_eq!(ev("builtins.isBool 1"), Value::Bool(false));
5143 assert_eq!(ev(r#"builtins.isString "x""#), Value::Bool(true));
5144 assert_eq!(ev("builtins.isString 1"), Value::Bool(false));
5145 assert_eq!(ev("builtins.isList []"), Value::Bool(true));
5146 assert_eq!(ev("builtins.isList {}"), Value::Bool(false));
5147 assert_eq!(ev("builtins.isAttrs {}"), Value::Bool(true));
5148 assert_eq!(ev("builtins.isAttrs []"), Value::Bool(false));
5149 assert_eq!(ev("builtins.isFunction (x: x)"), Value::Bool(true));
5150 assert_eq!(ev("builtins.isFunction 1"), Value::Bool(false));
5151 assert_eq!(ev("builtins.isFloat 3.14"), Value::Bool(true));
5152 assert_eq!(ev("builtins.isFloat 1"), Value::Bool(false));
5153 }
5154
5155 #[test]
5156 fn builtins_to_json_from_json_roundtrip() {
5157 assert_eq!(ev("builtins.fromJSON (builtins.toJSON 42)"), Value::Int(42));
5159 assert_eq!(
5161 ev(r#"builtins.fromJSON (builtins.toJSON "hello")"#),
5162 Value::string("hello"),
5163 );
5164 assert_eq!(
5166 ev("builtins.fromJSON (builtins.toJSON [1 2 3])"),
5167 Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
5168 );
5169 assert_eq!(ev("builtins.fromJSON (builtins.toJSON null)"), Value::Null);
5171 assert_eq!(ev("builtins.fromJSON (builtins.toJSON true)"), Value::Bool(true));
5173 }
5174
5175 #[test]
5176 fn builtins_to_string_various() {
5177 assert_eq!(ev("builtins.toString 42"), Value::string("42"));
5178 assert_eq!(ev("builtins.toString true"), Value::string("1"));
5179 assert_eq!(ev("builtins.toString false"), Value::string(""));
5180 assert_eq!(ev("builtins.toString null"), Value::string(""));
5181 assert_eq!(ev(r#"builtins.toString "hello""#), Value::string("hello"));
5182 }
5183
5184 #[test]
5185 fn builtins_function_args() {
5186 let v = ev("builtins.functionArgs ({ a, b ? 1 }: a)");
5187 if let Value::Attrs(attrs) = v {
5188 assert_eq!(attrs.get("a"), Some(&Value::Bool(false))); assert_eq!(attrs.get("b"), Some(&Value::Bool(true))); } else {
5191 panic!("expected attrs");
5192 }
5193 }
5194
5195 #[test]
5196 fn builtins_gen_list() {
5197 assert_eq!(
5198 ev("builtins.genList (x: x * x) 5"),
5199 Value::list(vec![
5200 Value::Int(0), Value::Int(1), Value::Int(4),
5201 Value::Int(9), Value::Int(16),
5202 ]),
5203 );
5204 assert_eq!(ev("builtins.genList (x: x) 0"), Value::list(vec![]));
5205 }
5206
5207 #[test]
5208 fn builtins_elem() {
5209 assert_eq!(ev("builtins.elem 2 [1 2 3]"), Value::Bool(true));
5210 assert_eq!(ev("builtins.elem 5 [1 2 3]"), Value::Bool(false));
5211 assert_eq!(ev("builtins.elem 1 []"), Value::Bool(false));
5212 }
5213
5214 #[test]
5215 fn builtins_head_tail() {
5216 assert_eq!(ev("builtins.head [10 20 30]"), Value::Int(10));
5217 assert_eq!(
5218 ev("builtins.tail [10 20 30]"),
5219 Value::list(vec![Value::Int(20), Value::Int(30)]),
5220 );
5221 }
5222
5223 #[test]
5224 fn builtins_string_length() {
5225 assert_eq!(ev(r#"builtins.stringLength "hello""#), Value::Int(5));
5226 assert_eq!(ev(r#"builtins.stringLength """#), Value::Int(0));
5227 assert_eq!(ev(r#"builtins.stringLength "abc def""#), Value::Int(7));
5228 }
5229
5230 #[test]
5231 fn builtins_ceil_floor() {
5232 assert_eq!(ev("builtins.ceil 2.3"), Value::Int(3));
5233 assert_eq!(ev("builtins.ceil 2.0"), Value::Int(2));
5234 assert_eq!(ev("builtins.floor 2.9"), Value::Int(2));
5235 assert_eq!(ev("builtins.floor 2.0"), Value::Int(2));
5236 assert_eq!(ev("builtins.ceil 5"), Value::Int(5));
5238 assert_eq!(ev("builtins.floor 5"), Value::Int(5));
5239 }
5240
5241 #[test]
5242 fn builtins_try_eval() {
5243 let v = ev("builtins.tryEval 42");
5244 if let Value::Attrs(attrs) = v {
5245 assert_eq!(attrs.get("success"), Some(&Value::Bool(true)));
5246 assert_eq!(attrs.get("value"), Some(&Value::Int(42)));
5247 } else {
5248 panic!("expected attrs");
5249 }
5250 }
5251
5252 #[test]
5253 fn builtins_throw() {
5254 let result = eval(r#"builtins.throw "oops""#);
5255 assert!(result.is_err());
5256 let msg = format!("{}", result.unwrap_err());
5257 assert!(msg.contains("oops"));
5258 }
5259
5260 #[test]
5261 fn builtins_seq_deep_seq() {
5262 assert_eq!(ev("builtins.seq 1 42"), Value::Int(42));
5264 assert_eq!(ev("builtins.deepSeq [1 2 3] 99"), Value::Int(99));
5266 }
5267
5268 #[test]
5269 fn builtins_current_system() {
5270 let v = ev("builtins.currentSystem");
5271 if let Value::String(ns) = v {
5272 let s = &ns.chars;
5273 assert!(
5275 s == "aarch64-darwin"
5276 || s == "x86_64-darwin"
5277 || s == "aarch64-linux"
5278 || s == "x86_64-linux",
5279 "unexpected system: {s}",
5280 );
5281 } else {
5282 panic!("expected string");
5283 }
5284 }
5285
5286 #[test]
5291 fn pattern_mkif_like() {
5292 assert_eq!(
5294 ev("(if true then { x = 1; } else {}).x"),
5295 Value::Int(1),
5296 );
5297 let v = ev("if false then { x = 1; } else {}");
5298 if let Value::Attrs(attrs) = v {
5299 assert!(attrs.is_empty());
5300 } else {
5301 panic!("expected attrs");
5302 }
5303 }
5304
5305 #[test]
5306 fn pattern_optional_attrs() {
5307 assert_eq!(
5309 ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in (optionalAttrs true { a = 1; }).a"),
5310 Value::Int(1),
5311 );
5312 let v = ev("let optionalAttrs = cond: attrs: if cond then attrs else {}; in optionalAttrs false { a = 1; }");
5313 if let Value::Attrs(attrs) = v {
5314 assert!(attrs.is_empty());
5315 } else {
5316 panic!("expected attrs");
5317 }
5318 }
5319
5320 #[test]
5321 fn pattern_filter_attrs_via_remove() {
5322 assert_eq!(
5324 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]).a"#),
5325 Value::Int(1),
5326 );
5327 assert_eq!(
5328 ev(r#"(builtins.removeAttrs { a = 1; b = 2; c = 3; } ["b"]) ? b"#),
5329 Value::Bool(false),
5330 );
5331 }
5332
5333 #[test]
5334 fn pattern_override() {
5335 let v = ev(r#"
5337 let
5338 defaults = { debug = false; port = 8080; host = "localhost"; };
5339 overrides = { debug = true; port = 9090; };
5340 in defaults // overrides
5341 "#);
5342 if let Value::Attrs(attrs) = v {
5343 assert_eq!(attrs.get("debug"), Some(&Value::Bool(true)));
5344 assert_eq!(attrs.get("port"), Some(&Value::Int(9090)));
5345 assert_eq!(attrs.get("host"), Some(&Value::string("localhost")));
5346 } else {
5347 panic!("expected attrs");
5348 }
5349 }
5350
5351 #[test]
5352 fn pattern_functor() {
5353 assert_eq!(
5355 ev("let s = { __functor = self: x: self.value + x; value = 10; }; in s 5"),
5356 Value::Int(15),
5357 );
5358 }
5359
5360 #[test]
5361 fn pattern_platform_check() {
5362 let v = ev(r#"if builtins.currentSystem == "aarch64-darwin" then "arm" else "other""#);
5364 if let Value::String(_) = v {
5366 } else {
5368 panic!("expected string");
5369 }
5370 }
5371
5372 #[test]
5373 fn pattern_recursive_overlay_lambda_structure() {
5374 let v = ev("let overlay = self: super: { pkg = 42; }; in overlay {} {}");
5376 if let Value::Attrs(attrs) = v {
5377 assert_eq!(attrs.get("pkg"), Some(&Value::Int(42)));
5378 } else {
5379 panic!("expected attrs");
5380 }
5381 }
5382
5383 #[test]
5384 fn pattern_call_package_simplified() {
5385 assert_eq!(
5387 ev("let callPkg = f: f { lib = { id = x: x; }; }; lib = { id = x: x; }; in callPkg ({ lib }: lib.id 42)"),
5388 Value::Int(42),
5389 );
5390 }
5391
5392 #[test]
5393 fn pattern_derivation_like_attrset() {
5394 let v = ev(r#"{ type = "derivation"; name = "hello"; system = builtins.currentSystem; builder = "/bin/sh"; }"#);
5395 if let Value::Attrs(attrs) = v {
5396 assert_eq!(attrs.get("type"), Some(&Value::string("derivation")));
5397 assert_eq!(attrs.get("name"), Some(&Value::string("hello")));
5398 assert_eq!(attrs.get("builder"), Some(&Value::string("/bin/sh")));
5399 let system = force_value(attrs.get("system").unwrap()).unwrap();
5401 assert!(matches!(system, Value::String(_)), "expected string, got {system:?}");
5402 } else {
5403 panic!("expected attrs");
5404 }
5405 }
5406
5407 #[test]
5408 fn pattern_module_system_simplified() {
5409 assert_eq!(
5411 ev(r#"
5412 let
5413 eval = m: m { config = {}; lib = { mkDefault = x: x; }; };
5414 in eval ({ config, lib }: { result = lib.mkDefault 42; })
5415 "#),
5416 {
5417 let mut attrs = NixAttrs::new();
5418 attrs.insert("result".to_string(), Value::Int(42));
5419 Value::Attrs(Rc::new(attrs))
5420 },
5421 );
5422 }
5423
5424 #[test]
5429 fn error_undefined_variable() {
5430 let result = eval("nonexistent_var");
5431 assert!(result.is_err());
5432 let msg = format!("{}", result.unwrap_err());
5433 assert!(msg.contains("undefined variable") || msg.contains("nonexistent_var"));
5434 }
5435
5436 #[test]
5437 fn error_type_mismatch_arithmetic() {
5438 let result = eval(r#"1 + "hello""#);
5439 assert!(result.is_err());
5440 }
5441
5442 #[test]
5443 fn error_missing_attribute() {
5444 let result = eval("{}.nonexistent");
5445 assert!(result.is_err());
5446 let msg = format!("{}", result.unwrap_err());
5447 assert!(msg.contains("nonexistent") || msg.contains("not found"));
5448 }
5449
5450 #[test]
5451 fn error_division_by_zero() {
5452 assert!(eval("1 / 0").is_err());
5453 assert!(eval("100 / 0").is_err());
5454 }
5455
5456 #[test]
5457 fn error_missing_required_function_arg() {
5458 let result = eval("({ a, b }: a + b) { a = 1; }");
5459 assert!(result.is_err());
5460 let msg = format!("{}", result.unwrap_err());
5461 assert!(msg.contains("missing argument"));
5462 }
5463
5464 #[test]
5465 fn error_unexpected_function_arg() {
5466 let result = eval("({ a }: a) { a = 1; b = 2; }");
5467 assert!(result.is_err());
5468 let msg = format!("{}", result.unwrap_err());
5469 assert!(msg.contains("unexpected argument"));
5470 }
5471
5472 #[test]
5473 fn error_assertion_failure() {
5474 assert!(eval("assert false; 1").is_err());
5475 assert!(eval("assert 1 == 2; 1").is_err());
5476 }
5477
5478 #[test]
5479 fn error_infinite_recursion() {
5480 let result = eval("let x = x; in x");
5483 assert!(result.is_err());
5484 }
5485
5486 #[test]
5487 fn error_infinite_recursion_via_lambda() {
5488 let result = eval("let f = x: f x; in f 1");
5490 assert!(result.is_err());
5491 let msg = format!("{}", result.unwrap_err());
5492 assert!(
5493 msg.contains("infinite recursion") || msg.contains("eval depth") || msg.contains("undefined"),
5494 );
5495 }
5496
5497 #[test]
5502 fn integration_let_with_function_returning_attrset() {
5503 assert_eq!(
5504 ev("let mkPkg = name: { inherit name; version = 1; }; in (mkPkg \"hello\").name"),
5505 Value::string("hello"),
5506 );
5507 }
5508
5509 #[test]
5510 fn integration_chained_updates() {
5511 assert_eq!(
5512 ev("({ a = 1; } // { b = 2; } // { c = 3; }).c"),
5513 Value::Int(3),
5514 );
5515 }
5516
5517 #[test]
5518 fn integration_map_over_attrnames() {
5519 assert_eq!(
5521 ev(r#"
5522 let
5523 set = { a = 1; b = 2; };
5524 names = builtins.attrNames set;
5525 in builtins.length names
5526 "#),
5527 Value::Int(2),
5528 );
5529 }
5530
5531 #[test]
5532 fn integration_compose_functions() {
5533 assert_eq!(
5535 ev("let compose = f: g: x: f (g x); double = x: x * 2; inc = x: x + 1; in compose double inc 5"),
5536 Value::Int(12), );
5538 }
5539
5540 #[test]
5541 fn integration_recursive_list_building() {
5542 assert_eq!(
5544 ev("builtins.map (x: x * x) (builtins.genList (x: x + 1) 4)"),
5545 Value::list(vec![Value::Int(1), Value::Int(4), Value::Int(9), Value::Int(16)]),
5546 );
5547 }
5548
5549 #[test]
5550 fn integration_attrset_from_list() {
5551 let v = ev(r#"
5553 builtins.listToAttrs (builtins.map (x: { name = x; value = true; }) ["a" "b" "c"])
5554 "#);
5555 if let Value::Attrs(attrs) = v {
5556 assert_eq!(attrs.get("a"), Some(&Value::Bool(true)));
5557 assert_eq!(attrs.get("b"), Some(&Value::Bool(true)));
5558 assert_eq!(attrs.get("c"), Some(&Value::Bool(true)));
5559 } else {
5560 panic!("expected attrs");
5561 }
5562 }
5563
5564 #[test]
5565 fn integration_nested_with_and_let() {
5566 assert_eq!(
5567 ev("let x = 10; in with { y = 20; }; x + y"),
5568 Value::Int(30),
5569 );
5570 }
5571
5572 #[test]
5573 fn integration_complex_pattern_match() {
5574 assert_eq!(
5576 ev("(args @ { a, b ? 5, ... }: a + b + (if args ? c then args.c else 0)) { a = 1; c = 10; }"),
5577 Value::Int(16), );
5579 }
5580
5581 #[test]
5582 fn integration_substring() {
5583 assert_eq!(
5584 ev(r#"builtins.substring 0 5 "hello world""#),
5585 Value::string("hello"),
5586 );
5587 assert_eq!(
5588 ev(r#"builtins.substring 6 5 "hello world""#),
5589 Value::string("world"),
5590 );
5591 }
5592
5593 #[test]
5594 fn integration_has_attr_on_nested() {
5595 assert_eq!(ev("{ a = { b = 1; }; } ? a"), Value::Bool(true));
5597 assert_eq!(
5598 ev("({ a = { b = 1; }; }.a) ? b"),
5599 Value::Bool(true),
5600 );
5601 }
5602
5603 #[test]
5604 fn integration_cat_attrs() {
5605 assert_eq!(
5606 ev(r#"builtins.catAttrs "x" [{ x = 1; } { y = 2; } { x = 3; }]"#),
5607 Value::list(vec![Value::Int(1), Value::Int(3)]),
5608 );
5609 }
5610
5611 #[test]
5612 fn integration_get_attr_builtin() {
5613 assert_eq!(
5614 ev(r#"builtins.getAttr "a" { a = 42; b = 10; }"#),
5615 Value::Int(42),
5616 );
5617 }
5618
5619 #[test]
5620 fn integration_has_attr_builtin() {
5621 assert_eq!(
5622 ev(r#"builtins.hasAttr "a" { a = 1; }"#),
5623 Value::Bool(true),
5624 );
5625 assert_eq!(
5626 ev(r#"builtins.hasAttr "z" { a = 1; }"#),
5627 Value::Bool(false),
5628 );
5629 }
5630
5631 #[test]
5632 fn integration_is_path() {
5633 assert_eq!(ev("builtins.isPath ./foo"), Value::Bool(true));
5634 assert_eq!(ev("builtins.isPath 42"), Value::Bool(false));
5635 }
5636
5637 #[test]
5638 fn integration_builtins_trace() {
5639 assert_eq!(ev(r#"builtins.trace "debug msg" 42"#), Value::Int(42));
5641 }
5642
5643 #[test]
5644 fn integration_builtins_split() {
5645 assert_eq!(
5649 ev(r#"builtins.split "/" "a/b/c""#),
5650 Value::list(vec![
5651 Value::string("a"),
5652 Value::list(vec![]),
5653 Value::string("b"),
5654 Value::list(vec![]),
5655 Value::string("c"),
5656 ]),
5657 );
5658 assert_eq!(
5661 ev(r#"builtins.split "(/)" "a/b/c""#),
5662 Value::list(vec![
5663 Value::string("a"),
5664 Value::list(vec![Value::string("/")]),
5665 Value::string("b"),
5666 Value::list(vec![Value::string("/")]),
5667 Value::string("c"),
5668 ]),
5669 );
5670 }
5671
5672 #[test]
5673 fn integration_builtins_split_no_capture_groups() {
5674 assert_eq!(
5679 ev(r#"builtins.split "-" "aarch64-darwin""#),
5680 Value::list(vec![
5681 Value::string("aarch64"),
5682 Value::list(vec![]),
5683 Value::string("darwin"),
5684 ]),
5685 );
5686 }
5687
5688 #[test]
5689 fn integration_builtins_split_system_string_filter() {
5690 assert_eq!(
5693 ev(r#"builtins.filter builtins.isString (builtins.split "-" "aarch64-darwin")"#),
5694 Value::list(vec![
5695 Value::string("aarch64"),
5696 Value::string("darwin"),
5697 ]),
5698 );
5699 }
5700
5701 #[test]
5702 fn integration_deeply_nested_let() {
5703 assert_eq!(
5705 ev("let a = let b = let c = 10; in c * 2; in b + 1; in a"),
5706 Value::Int(21),
5707 );
5708 }
5709
5710 #[test]
5711 fn integration_if_in_attrset_value() {
5712 assert_eq!(
5713 ev("{ x = if true then 1 else 2; }.x"),
5714 Value::Int(1),
5715 );
5716 }
5717
5718 #[test]
5719 fn integration_lambda_in_list() {
5720 assert_eq!(
5722 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 0) 5"),
5723 Value::Int(6),
5724 );
5725 assert_eq!(
5726 ev("let fs = [(x: x + 1) (x: x * 2)]; in (builtins.elemAt fs 1) 5"),
5727 Value::Int(10),
5728 );
5729 }
5730
5731 #[test]
5732 fn integration_nixpkgs_lib_id() {
5733 assert_eq!(
5735 ev("let lib = { id = x: x; const = a: b: a; }; in lib.id 42"),
5736 Value::Int(42),
5737 );
5738 assert_eq!(
5739 ev("let lib = { id = x: x; const = a: b: a; }; in lib.const 1 2"),
5740 Value::Int(1),
5741 );
5742 }
5743
5744 #[test]
5745 fn integration_multiple_inherit() {
5746 assert_eq!(
5747 ev("let a = 1; b = 2; c = 3; in { inherit a b c; }.b"),
5748 Value::Int(2),
5749 );
5750 }
5751
5752 #[test]
5753 fn integration_rec_set_with_builtins() {
5754 assert_eq!(
5755 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
5756 Value::Int(5),
5757 );
5758 }
5759
5760 #[test]
5765 fn functor_simple_callable_attrset() {
5766 assert_eq!(
5767 ev("let s = { __functor = self: x: x + 1; }; in s 41"),
5768 Value::Int(42),
5769 );
5770 }
5771
5772 #[test]
5773 fn functor_with_self_reference() {
5774 assert_eq!(
5775 ev("let s = { __functor = self: x: self.base + x; base = 100; }; in s 23"),
5776 Value::Int(123),
5777 );
5778 }
5779
5780 #[test]
5781 fn functor_updated_attrset() {
5782 assert_eq!(
5784 ev(r#"
5785 let
5786 mk = { __functor = self: x: self.n + x; n = 0; };
5787 s = mk // { n = 50; };
5788 in s 7
5789 "#),
5790 Value::Int(57),
5791 );
5792 }
5793
5794 #[test]
5795 fn functor_error_on_non_callable_attrset() {
5796 let result = eval("let s = { a = 1; }; in s 5");
5798 assert!(result.is_err());
5799 }
5800
5801 #[test]
5806 fn to_string_protocol_in_interpolation() {
5807 assert_eq!(
5808 ev(r#"let s = { __toString = self: "world"; }; in "hello ${s}""#),
5809 Value::string("hello world"),
5810 );
5811 }
5812
5813 #[test]
5814 fn to_string_protocol_accesses_self() {
5815 assert_eq!(
5816 ev(r#"let s = { __toString = self: self.val; val = "abc"; }; in "${s}""#),
5817 Value::string("abc"),
5818 );
5819 }
5820
5821 #[test]
5822 fn to_string_protocol_via_builtin_to_string() {
5823 assert_eq!(
5824 ev(r#"builtins.toString { __toString = self: "via-builtin"; }"#),
5825 Value::string("via-builtin"),
5826 );
5827 }
5828
5829 #[test]
5830 fn to_string_protocol_attrset_without_toString_fails() {
5831 let result = eval(r#""${{}}"#);
5833 assert!(result.is_err());
5834 }
5835
5836 #[test]
5841 fn eval_builtins_concat_strings() {
5842 assert_eq!(
5843 ev(r#"builtins.concatStrings ["a" "b" "c"]"#),
5844 Value::string("abc"),
5845 );
5846 assert_eq!(
5847 ev(r#"builtins.concatStrings []"#),
5848 Value::string(""),
5849 );
5850 }
5851
5852 #[test]
5853 fn eval_builtins_partition() {
5854 let v = ev("builtins.partition (x: x > 3) [1 2 3 4 5]");
5855 if let Value::Attrs(a) = v {
5856 assert_eq!(a.get("right"), Some(&Value::list(vec![Value::Int(4), Value::Int(5)])));
5857 assert_eq!(a.get("wrong"), Some(&Value::list(vec![Value::Int(1), Value::Int(2), Value::Int(3)])));
5858 } else {
5859 panic!("expected attrs");
5860 }
5861 }
5862
5863 #[test]
5864 fn eval_builtins_group_by() {
5865 let v = ev(r#"builtins.groupBy (x: if x > 0 then "pos" else "neg") [1 (0 - 2) 3 (0 - 4)]"#);
5866 if let Value::Attrs(a) = v {
5867 assert_eq!(a.get("pos"), Some(&Value::list(vec![Value::Int(1), Value::Int(3)])));
5868 assert_eq!(a.get("neg"), Some(&Value::list(vec![Value::Int(-2), Value::Int(-4)])));
5869 } else {
5870 panic!("expected attrs");
5871 }
5872 }
5873
5874 #[test]
5875 fn eval_builtins_zip_attrs_with() {
5876 let v = ev("builtins.zipAttrsWith (n: vs: builtins.head vs) [{ a = 1; } { a = 2; b = 3; }]");
5877 if let Value::Attrs(a) = v {
5878 assert_eq!(a.get("a"), Some(&Value::Int(1)));
5879 assert_eq!(a.get("b"), Some(&Value::Int(3)));
5880 } else {
5881 panic!("expected attrs");
5882 }
5883 }
5884
5885 #[test]
5886 fn eval_builtins_compare_versions() {
5887 assert_eq!(ev(r#"builtins.compareVersions "2.0" "1.0""#), Value::Int(1));
5888 assert_eq!(ev(r#"builtins.compareVersions "1.0" "2.0""#), Value::Int(-1));
5889 assert_eq!(ev(r#"builtins.compareVersions "1.0" "1.0""#), Value::Int(0));
5890 }
5891
5892 #[test]
5893 fn eval_builtins_parse_drv_name() {
5894 let v = ev(r#"builtins.parseDrvName "nix-2.3.4""#);
5895 if let Value::Attrs(a) = v {
5896 assert_eq!(a.get("name"), Some(&Value::string("nix")));
5897 assert_eq!(a.get("version"), Some(&Value::string("2.3.4")));
5898 } else {
5899 panic!("expected attrs");
5900 }
5901 }
5902
5903 #[test]
5904 fn eval_builtins_base_name_of() {
5905 assert_eq!(
5906 ev(r#"builtins.baseNameOf "/foo/bar/baz""#),
5907 Value::string("baz"),
5908 );
5909 }
5910
5911 #[test]
5912 fn eval_builtins_dir_of() {
5913 assert_eq!(
5914 ev(r#"builtins.dirOf "/foo/bar/baz""#),
5915 Value::string("/foo/bar"),
5916 );
5917 }
5918
5919 #[test]
5920 fn eval_builtins_add_error_context() {
5921 assert_eq!(
5922 ev(r#"builtins.addErrorContext "some context" 42"#),
5923 Value::Int(42),
5924 );
5925 }
5926
5927 #[test]
5928 fn eval_builtins_abort() {
5929 let result = eval(r#"builtins.abort "fatal error""#);
5930 assert!(result.is_err());
5931 let msg = format!("{}", result.unwrap_err());
5932 assert!(msg.contains("fatal error"));
5933 }
5934
5935 #[test]
5940 fn indented_string_simple() {
5941 assert_eq!(ev("''hello''"), Value::string("hello"));
5942 }
5943
5944 #[test]
5945 fn indented_string_multiline_strips_indent() {
5946 assert_eq!(
5947 ev("''\n line1\n line2\n''"),
5948 Value::string("line1\nline2\n"),
5949 );
5950 }
5951
5952 #[test]
5953 fn indented_string_with_interpolation() {
5954 let code = "let x = \"world\"; in ''hello ${x}''";
5955 assert_eq!(
5956 ev(code),
5957 Value::string("hello world"),
5958 );
5959 }
5960
5961 #[test]
5962 fn indented_string_deeper_indent_preserved() {
5963 assert_eq!(
5965 ev("''\n a\n b\n''"),
5966 Value::string("a\n b\n"),
5967 );
5968 }
5969
5970 #[test]
5975 fn dynamic_attr_name_in_set() {
5976 assert_eq!(
5977 ev(r#"let key = "mykey"; in { ${key} = 42; }.mykey"#),
5978 Value::Int(42),
5979 );
5980 }
5981
5982 #[test]
5983 fn dynamic_attr_name_with_expression() {
5984 assert_eq!(
5985 ev(r#"let prefix = "foo"; in { ${"${prefix}bar"} = 1; }.foobar"#),
5986 Value::Int(1),
5987 );
5988 }
5989
5990 #[test]
5995 fn eval_builtins_match() {
5996 assert_eq!(
5997 ev(r#"builtins.match "([0-9]+)" "42""#),
5998 Value::list(vec![Value::string("42")]),
5999 );
6000 }
6001
6002 #[test]
6003 fn eval_builtins_hash_string() {
6004 let v = ev(r#"builtins.hashString "sha256" "hello""#);
6005 if let Value::String(ns) = v {
6006 assert_eq!(ns.chars.len(), 64);
6007 } else {
6008 panic!("expected string");
6009 }
6010 }
6011
6012 #[test]
6013 fn eval_builtins_import() {
6014 let dir = std::env::temp_dir();
6015 let path = dir.join("sui_eval_test_import_eval.nix");
6016 std::fs::write(&path, "42").unwrap();
6017 let expr = format!(r#"import "{}""#, path.display());
6018 let v = eval(&expr).unwrap();
6019 assert_eq!(v, Value::Int(42));
6020 std::fs::remove_file(&path).ok();
6021 }
6022
6023 #[test]
6024 fn eval_builtins_derivation() {
6025 let v = eval(r#"builtins.derivation { name = "test"; system = "x86_64-linux"; builder = "/bin/sh"; }"#).unwrap();
6026 if let Value::Attrs(a) = v {
6027 assert_eq!(a.get("type"), Some(&Value::string("derivation")));
6028 } else {
6029 panic!("expected attrs");
6030 }
6031 }
6032
6033 #[test]
6034 fn eval_mutual_recursive_let() {
6035 let v = eval("let a = { x = b; }; b = { y = a; }; in a.x.y");
6042 assert!(v.is_ok(), "mutual recursive let should not error: {v:?}");
6043 let val = v.unwrap();
6045 assert!(
6046 matches!(val, Value::Attrs(_)),
6047 "a.x.y should be an attrset, got: {val:?}",
6048 );
6049 }
6050
6051 #[test]
6052 fn eval_mutual_recursive_let_simple() {
6053 let v = eval("let a = b; b = 42; in a");
6055 assert!(v.is_ok());
6056 assert_eq!(v.unwrap(), Value::Int(42));
6059 }
6060
6061 #[test]
6062 fn eval_builtins_read_dir() {
6063 let dir = std::env::temp_dir().join("sui_eval_test_readdir_eval");
6064 let _ = std::fs::remove_dir_all(&dir);
6065 std::fs::create_dir_all(&dir).unwrap();
6066 std::fs::write(dir.join("a.txt"), "").unwrap();
6067 let expr = format!(r#"builtins.readDir "{}""#, dir.display());
6068 let v = eval(&expr).unwrap();
6069 if let Value::Attrs(a) = v {
6070 assert_eq!(a.get("a.txt"), Some(&Value::string("regular")));
6071 } else {
6072 panic!("expected attrs");
6073 }
6074 let _ = std::fs::remove_dir_all(&dir);
6075 }
6076
6077 #[test]
6082 fn thunk_basic_let() {
6083 assert_eq!(ev("let x = 1; in x"), Value::Int(1));
6085 }
6086
6087 #[test]
6088 fn thunk_forward_ref() {
6089 assert_eq!(ev("let a = b; b = 1; in a"), Value::Int(1));
6091 }
6092
6093 #[test]
6094 fn thunk_mutual_rec_attrset_in_let() {
6095 assert_eq!(ev("let a = { x = b; }; b = { y = 1; }; in a.x.y"), Value::Int(1));
6097 }
6098
6099 #[test]
6100 fn thunk_rec_attrset() {
6101 assert_eq!(ev("(rec { a = b; b = 1; }).a"), Value::Int(1));
6103 }
6104
6105 #[test]
6106 fn thunk_rec_attrset_chain() {
6107 assert_eq!(ev("(rec { a = 1; b = a + 1; c = b + 1; }).c"), Value::Int(3));
6109 }
6110
6111 #[test]
6112 fn thunk_fixpoint() {
6113 assert_eq!(
6115 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; })).b"),
6116 Value::Int(2),
6117 );
6118 }
6119
6120 #[test]
6121 fn thunk_blackhole_self_reference() {
6122 let result = eval("let x = x; in x");
6124 assert!(result.is_err());
6125 let msg = format!("{}", result.unwrap_err());
6126 assert!(
6127 msg.contains("infinite recursion") || msg.contains("blackhole"),
6128 "expected blackhole error, got: {msg}",
6129 );
6130 }
6131
6132 #[test]
6133 fn thunk_mutual_blackhole() {
6134 let result = eval("let a = b; b = a; in a");
6136 assert!(result.is_err());
6137 }
6138
6139 #[test]
6140 fn thunk_let_body_forces_correctly() {
6141 assert_eq!(ev("let a = 10; b = 20; in a + b"), Value::Int(30));
6143 }
6144
6145 #[test]
6146 fn thunk_only_forced_when_needed() {
6147 assert_eq!(ev("let bad = 1 / 0; good = 42; in good"), Value::Int(42));
6149 }
6150
6151 #[test]
6152 fn thunk_forward_ref_in_function_body() {
6153 assert_eq!(
6155 ev("let f = x: x + b; b = 10; in f 5"),
6156 Value::Int(15),
6157 );
6158 }
6159
6160 #[test]
6161 fn thunk_rec_set_self_ref_through_self() {
6162 assert_eq!(
6164 ev(r#"(rec { a = "hello"; b = builtins.stringLength a; }).b"#),
6165 Value::Int(5),
6166 );
6167 }
6168
6169 #[test]
6170 fn thunk_nested_let_forward_ref() {
6171 assert_eq!(
6173 ev("let a = b + 1; b = 2; in a"),
6174 Value::Int(3),
6175 );
6176 }
6177
6178 #[test]
6179 fn thunk_deep_chain() {
6180 assert_eq!(
6182 ev("let a = 1; b = a; c = b; d = c; e = d; in e"),
6183 Value::Int(1),
6184 );
6185 }
6186
6187 #[test]
6188 fn thunk_rec_set_fixpoint() {
6189 assert_eq!(
6191 ev("let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; c = self.b + 1; })).c"),
6192 Value::Int(3),
6193 );
6194 }
6195
6196 #[test]
6197 fn thunk_let_with_inherit() {
6198 assert_eq!(
6200 ev("let a = 1; in let inherit a; b = a + 1; in b"),
6201 Value::Int(2),
6202 );
6203 }
6204
6205 #[test]
6206 fn thunk_attrset_value_lazy() {
6207 assert_eq!(
6210 ev("let x = 42; in { a = x; }.a"),
6211 Value::Int(42),
6212 );
6213 }
6214
6215 #[test]
6216 fn thunk_unused_error_not_forced() {
6217 assert_eq!(
6219 ev(r#"let bad = builtins.throw "boom"; ok = 1; in ok"#),
6220 Value::Int(1),
6221 );
6222 }
6223
6224 #[test]
6225 fn thunk_rec_set_mutual_reference() {
6226 let v = ev("rec { a = { val = b.val + 1; }; b = { val = 10; }; }");
6228 if let Value::Attrs(attrs) = v {
6229 let a = attrs.get("a").unwrap();
6230 let a_forced = force_value(a).unwrap();
6231 if let Value::Attrs(a_attrs) = a_forced {
6232 assert_eq!(a_attrs.get("val"), Some(&Value::Int(11)));
6233 } else {
6234 panic!("expected attrs for a");
6235 }
6236 } else {
6237 panic!("expected attrs");
6238 }
6239 }
6240
6241 #[test]
6244 fn let_rec_self_reference_simple() {
6245 assert_eq!(
6246 ev("let x = 1; y = x + 1; in y"),
6247 Value::Int(2),
6248 );
6249 }
6250
6251 #[test]
6252 fn let_rec_self_reference_chain() {
6253 assert_eq!(
6254 ev("let a = 1; b = a + 1; c = b + 1; in c"),
6255 Value::Int(3),
6256 );
6257 }
6258
6259 #[test]
6260 fn let_rec_self_reference_with_function() {
6261 assert_eq!(
6262 ev("let f = x: x + 1; y = f 10; in y"),
6263 Value::Int(11),
6264 );
6265 }
6266
6267 #[test]
6268 fn let_rec_mutual_recursion_via_if() {
6269 assert_eq!(
6270 ev("let isEven = n: if n == 0 then true else isOdd (n - 1); isOdd = n: if n == 0 then false else isEven (n - 1); in isEven 4"),
6271 Value::Bool(true),
6272 );
6273 }
6274
6275 #[test]
6276 fn let_rec_forward_ref_in_list() {
6277 assert_eq!(
6278 ev("let xs = [a b]; a = 1; b = 2; in builtins.length xs"),
6279 Value::Int(2),
6280 );
6281 }
6282
6283 #[test]
6286 fn with_shadowing_let_wins_over_with() {
6287 assert_eq!(
6288 ev("let x = 1; in with { x = 2; }; x"),
6289 Value::Int(1),
6290 );
6291 }
6292
6293 #[test]
6294 fn with_shadowing_inner_with_wins() {
6295 assert_eq!(
6296 ev("with { x = 1; }; with { x = 2; }; x"),
6297 Value::Int(2),
6298 );
6299 }
6300
6301 #[test]
6302 fn with_shadowing_outer_provides_missing() {
6303 assert_eq!(
6304 ev("with { x = 1; y = 10; }; with { x = 2; }; x + y"),
6305 Value::Int(12),
6306 );
6307 }
6308
6309 #[test]
6310 fn with_shadowing_lambda_arg_wins() {
6311 assert_eq!(
6312 ev("(x: with { x = 99; }; x) 42"),
6313 Value::Int(42),
6314 );
6315 }
6316
6317 #[test]
6318 fn with_shadowing_nested_let_wins_over_with() {
6319 assert_eq!(
6320 ev("with { x = 1; }; let x = 2; in x"),
6321 Value::Int(2),
6322 );
6323 }
6324
6325 #[test]
6326 fn with_scope_dynamic_attrs() {
6327 assert_eq!(
6328 ev(r#"with { x = 1; y = 2; z = 3; }; x + y + z"#),
6329 Value::Int(6),
6330 );
6331 }
6332
6333 #[test]
6334 fn with_scope_over_lazy_thunk_chain_resolves() {
6335 assert_eq!(
6344 ev(r#"let outer = if true then (if true then { unix = 42; } else {}) else {};
6345 # force a two-deep lazy wrap of the with-head
6346 head = (x: x) ((y: y) outer);
6347 in with head; unix"#),
6348 Value::Int(42),
6349 );
6350 }
6351
6352 #[test]
6353 fn with_scope_head_from_deep_select_resolves() {
6354 assert_eq!(
6357 ev(r#"let a = { b = { c = { key = 7; }; }; }; in with a.b.c; key"#),
6358 Value::Int(7),
6359 );
6360 }
6361
6362 #[test]
6365 fn attrset_deep_merge_simple() {
6366 let v = ev("{ a.b = 1; a.c = 2; }");
6367 if let Value::Attrs(attrs) = v {
6368 let a = force_value(attrs.get("a").unwrap()).unwrap();
6369 if let Value::Attrs(inner) = a {
6370 assert_eq!(force_value(inner.get("b").unwrap()).unwrap(), Value::Int(1));
6371 assert_eq!(force_value(inner.get("c").unwrap()).unwrap(), Value::Int(2));
6372 } else {
6373 panic!("expected nested attrs");
6374 }
6375 } else {
6376 panic!("expected attrs");
6377 }
6378 }
6379
6380 #[test]
6381 fn attrset_deep_merge_three_levels() {
6382 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
6383 if let Value::Attrs(attrs) = v {
6384 let a = force_value(attrs.get("a").unwrap()).unwrap();
6385 if let Value::Attrs(a_inner) = a {
6386 let e = force_value(a_inner.get("e").unwrap()).unwrap();
6387 assert_eq!(e, Value::Int(3));
6388 let b = force_value(a_inner.get("b").unwrap()).unwrap();
6389 if let Value::Attrs(b_inner) = b {
6390 assert_eq!(force_value(b_inner.get("c").unwrap()).unwrap(), Value::Int(1));
6391 assert_eq!(force_value(b_inner.get("d").unwrap()).unwrap(), Value::Int(2));
6392 } else {
6393 panic!("expected nested attrs for b");
6394 }
6395 } else {
6396 panic!("expected nested attrs for a");
6397 }
6398 } else {
6399 panic!("expected attrs");
6400 }
6401 }
6402
6403 #[test]
6404 fn attrset_deep_merge_preserves_siblings() {
6405 assert_eq!(
6406 ev("{ a.x = 1; b = 2; a.y = 3; }.b"),
6407 Value::Int(2),
6408 );
6409 }
6410
6411 #[test]
6412 fn attrset_deep_merge_in_let() {
6413 let v = ev("let s = { a.b = 1; a.c = 2; }; in s.a.b + s.a.c");
6414 assert_eq!(v, Value::Int(3));
6415 }
6416
6417 #[test]
6418 fn attrset_deep_merge_fullset_then_dotted() {
6419 let v = ev("let s = { a = { x = 1; }; a.y = 2; }; in s.a.x + s.a.y");
6426 assert_eq!(v, Value::Int(3));
6427 let both = ev("let s = { a = { x = 1; }; a.y = 2; }; in [ s.a.x s.a.y ]");
6429 if let Value::List(items) = both {
6430 assert_eq!(force_value(&items[0]).unwrap(), Value::Int(1));
6431 assert_eq!(force_value(&items[1]).unwrap(), Value::Int(2));
6432 } else {
6433 panic!("expected list");
6434 }
6435 }
6436
6437 #[test]
6440 fn inherit_from_basic() {
6441 assert_eq!(
6442 ev("let s = { x = 1; y = 2; }; in let inherit (s) x y; in x + y"),
6443 Value::Int(3),
6444 );
6445 }
6446
6447 #[test]
6448 fn inherit_from_with_shadowing() {
6449 assert_eq!(
6450 ev("let x = 10; in let inherit ({ x = 20; }) x; in x"),
6451 Value::Int(20),
6452 );
6453 }
6454
6455 #[test]
6456 fn inherit_from_in_attrset() {
6457 let v = ev(r#"let s = { a = 1; b = 2; }; in { inherit (s) a b; c = 3; }"#);
6458 if let Value::Attrs(attrs) = v {
6459 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
6460 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
6461 assert_eq!(force_value(attrs.get("c").unwrap()).unwrap(), Value::Int(3));
6462 } else {
6463 panic!("expected attrs");
6464 }
6465 }
6466
6467 #[test]
6468 fn inherit_from_rec_set() {
6469 assert_eq!(
6470 ev("rec { inherit ({ x = 42; }) x; y = x; }.y"),
6471 Value::Int(42),
6472 );
6473 }
6474
6475 #[test]
6476 fn inherit_plain_from_scope() {
6477 assert_eq!(
6478 ev("let x = 1; in { inherit x; }.x"),
6479 Value::Int(1),
6480 );
6481 }
6482
6483 #[test]
6492 fn inherit_plain_from_with_scope_lazy() {
6493 assert_eq!(
6497 ev("let fix = f: let x = f x; in x;
6498 self = fix (self: with self; {
6499 a = use { inherit cp; };
6500 use = { cp }: cp 5;
6501 cp = x: x + 100;
6502 });
6503 in self.a"),
6504 Value::Int(105),
6505 );
6506 assert_eq!(
6508 ev("with { y = 7; }; { inherit y; }.y"),
6509 Value::Int(7),
6510 );
6511 }
6512
6513 #[test]
6514 fn inherit_multiple_from_expr() {
6515 assert_eq!(
6516 ev("let s = { a = 10; b = 20; c = 30; }; in let inherit (s) a b c; in a + b + c"),
6517 Value::Int(60),
6518 );
6519 }
6520
6521 #[test]
6524 fn interp_nested_attrset_access() {
6525 assert_eq!(
6526 ev(r#"let x = { a = "hello"; }; in "${x.a} world""#),
6527 Value::string("hello world"),
6528 );
6529 }
6530
6531 #[test]
6532 fn interp_with_let_expression() {
6533 assert_eq!(
6534 ev(r#""${let x = "inner"; in x}""#),
6535 Value::string("inner"),
6536 );
6537 }
6538
6539 #[test]
6540 fn interp_float_coercion() {
6541 assert_eq!(
6543 ev(r#""${toString 3.14}""#),
6544 Value::string("3.140000"),
6545 );
6546 }
6547
6548 #[test]
6551 fn compare_mixed_int_float() {
6552 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
6553 assert_eq!(ev("1.5 > 1"), Value::Bool(true));
6554 assert_eq!(ev("2.0 == 2"), Value::Bool(true));
6555 }
6556
6557 #[test]
6558 fn compare_string_lexicographic() {
6559 assert_eq!(ev(r#""abc" < "abd""#), Value::Bool(true));
6560 assert_eq!(ev(r#""abc" < "abc""#), Value::Bool(false));
6561 assert_eq!(ev(r#""abc" <= "abc""#), Value::Bool(true));
6562 }
6563
6564 #[test]
6567 fn update_empty_sets() {
6568 let v = ev("{} // {}");
6569 if let Value::Attrs(a) = v { assert!(a.is_empty()); } else { panic!(); }
6570 }
6571
6572 #[test]
6573 fn update_right_overrides_completely() {
6574 assert_eq!(
6575 ev("{ a = 1; b = 2; } // { a = 10; c = 30; }"),
6576 ev("{ a = 10; b = 2; c = 30; }"),
6577 );
6578 }
6579
6580 #[test]
6581 fn update_chained() {
6582 assert_eq!(
6583 ev("{ a = 1; } // { b = 2; } // { c = 3; }"),
6584 ev("{ a = 1; b = 2; c = 3; }"),
6585 );
6586 }
6587
6588 #[test]
6591 fn force_value_concrete_unchanged() {
6592 let v = Value::Int(42);
6593 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
6594 }
6595
6596 #[test]
6597 fn force_value_null() {
6598 assert_eq!(force_value(&Value::Null).unwrap(), Value::Null);
6599 }
6600
6601 #[test]
6604 fn eval_with_file_none() {
6605 let result = eval_with_file("1 + 2", None).unwrap();
6606 assert_eq!(result, Value::Int(3));
6607 }
6608
6609 #[test]
6612 fn error_type_mismatch_in_comparison() {
6613 let result = eval(r#"1 < "a""#);
6614 assert!(result.is_err());
6615 }
6616
6617 #[test]
6618 fn error_select_from_non_set() {
6619 let result = eval("42.x");
6620 assert!(result.is_err());
6621 }
6622
6623 #[test]
6624 fn error_call_non_function() {
6625 let result = eval("42 1");
6626 assert!(result.is_err());
6627 }
6628
6629 #[test]
6630 fn error_negate_string() {
6631 let result = eval(r#"-"hello""#);
6632 assert!(result.is_err());
6633 }
6634
6635 #[test]
6638 fn multiline_string_empty() {
6639 assert_eq!(ev("''''"), Value::string(""));
6640 }
6641
6642 #[test]
6643 fn multiline_string_with_trailing_newline() {
6644 let v = ev("''\n hello\n''");
6645 assert_eq!(v, Value::string("hello\n"));
6646 }
6647
6648 #[test]
6651 fn list_concat_empty_left() {
6652 assert_eq!(ev("[] ++ [1 2]"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6653 }
6654
6655 #[test]
6656 fn list_concat_empty_right() {
6657 assert_eq!(ev("[1 2] ++ []"), Value::list(vec![Value::Int(1), Value::Int(2)]));
6658 }
6659
6660 #[test]
6661 fn list_concat_both_empty() {
6662 assert_eq!(ev("[] ++ []"), Value::list(vec![]));
6663 }
6664
6665 #[test]
6668 fn formals_at_pattern_accessible() {
6669 assert_eq!(
6670 ev("({ x, ... } @ args: builtins.length (builtins.attrNames args)) { x = 1; y = 2; z = 3; }"),
6671 Value::Int(3),
6672 );
6673 }
6674
6675 #[test]
6676 fn formals_default_uses_other_arg() {
6677 assert_eq!(
6678 ev("({ x, y ? x + 1 }: y) { x = 10; }"),
6679 Value::Int(11),
6680 );
6681 }
6682
6683 #[test]
6684 fn formals_default_lazy_assert_false() {
6685 assert_eq!(
6689 ev("({ cpu, vendor ? assert false; null, kernel } @ args: if args ? vendor then vendor else \"inferred\") { cpu = \"x86_64\"; kernel = \"linux\"; }"),
6690 Value::String(Rc::new(NixString::plain("inferred"))),
6691 );
6692 }
6693
6694 #[test]
6695 fn formals_default_lazy_only_forced_when_accessed() {
6696 assert_eq!(
6698 ev("({ a, b ? 42 }: b) { a = 1; }"),
6699 Value::Int(42),
6700 );
6701 }
6702
6703 #[test]
6704 fn formals_ellipsis_ignores_extra() {
6705 assert_eq!(
6706 ev("({ x, ... }: x) { x = 1; y = 2; z = 3; }"),
6707 Value::Int(1),
6708 );
6709 }
6710
6711 #[test]
6714 fn pure_mode_roundtrip() {
6715 let was_pure = is_pure_mode();
6716 set_pure_mode(true);
6717 assert!(is_pure_mode());
6718 set_pure_mode(false);
6719 assert!(!is_pure_mode());
6720 set_pure_mode(was_pure);
6721 }
6722
6723 #[test]
6726 fn path_concat_with_string() {
6727 assert_eq!(
6728 ev(r#"/foo + "bar""#),
6729 Value::Path(Box::new(SmolStr::from("/foobar"))),
6730 );
6731 }
6732
6733 #[test]
6734 fn path_concat_with_path() {
6735 assert_eq!(
6736 ev("/foo + /bar"),
6737 Value::Path(Box::new(SmolStr::from("/foo//bar"))),
6738 );
6739 }
6740
6741 #[test]
6744 fn current_eval_dir_empty_when_no_file_pushed() {
6745 let snapshot = current_eval_dir();
6749 let _ = snapshot;
6751 }
6752
6753 #[test]
6754 fn push_eval_file_sets_current_dir() {
6755 let p = std::path::PathBuf::from("/tmp/example/file.nix");
6756 {
6757 let _g = push_eval_file(p.clone());
6758 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/tmp/example")));
6759 }
6760 }
6764
6765 #[test]
6766 fn push_eval_file_nested_stack() {
6767 let outer = std::path::PathBuf::from("/a/x.nix");
6768 let inner = std::path::PathBuf::from("/b/y.nix");
6769 {
6770 let _g_outer = push_eval_file(outer.clone());
6771 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6772 {
6773 let _g_inner = push_eval_file(inner.clone());
6774 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/b")));
6775 }
6776 assert_eq!(current_eval_dir(), Some(std::path::PathBuf::from("/a")));
6778 }
6779 }
6780
6781 #[test]
6784 fn error_undefined_var_includes_file_context() {
6785 let p = std::path::PathBuf::from("/nix/store/abc-default.nix");
6786 let _g = push_eval_file(p);
6787 let result = eval("nonexistent_xyz");
6788 let msg = format!("{}", result.unwrap_err());
6789 assert!(msg.contains("undefined variable"), "msg: {msg}");
6790 assert!(msg.contains("nonexistent_xyz"), "msg: {msg}");
6791 assert!(msg.contains("abc-default.nix"), "msg: {msg}");
6792 }
6793
6794 #[test]
6795 fn error_attr_not_found_includes_file_context() {
6796 let p = std::path::PathBuf::from("/nix/store/xyz-module.nix");
6797 let _g = push_eval_file(p);
6798 let result = eval("{}.missing_key");
6799 let msg = format!("{}", result.unwrap_err());
6800 assert!(msg.contains("not found") || msg.contains("missing_key"), "msg: {msg}");
6801 assert!(msg.contains("xyz-module.nix"), "msg: {msg}");
6802 }
6803
6804 #[test]
6805 fn error_assertion_failed_includes_file_context() {
6806 let p = std::path::PathBuf::from("/nix/store/test-assert.nix");
6807 let _g = push_eval_file(p);
6808 let result = eval("assert false; 1");
6809 let msg = format!("{}", result.unwrap_err());
6810 assert!(msg.contains("assertion failed"), "msg: {msg}");
6811 assert!(msg.contains("test-assert.nix"), "msg: {msg}");
6812 }
6813
6814 #[test]
6815 fn error_missing_argument_includes_file_context() {
6816 let p = std::path::PathBuf::from("/nix/store/func.nix");
6817 let _g = push_eval_file(p);
6818 let result = eval("({ a, b }: a) { a = 1; }");
6819 let msg = format!("{}", result.unwrap_err());
6820 assert!(msg.contains("missing argument"), "msg: {msg}");
6821 assert!(msg.contains("func.nix"), "msg: {msg}");
6822 }
6823
6824 #[test]
6825 fn error_cannot_call_includes_file_context() {
6826 let p = std::path::PathBuf::from("/nix/store/call.nix");
6827 let _g = push_eval_file(p);
6828 let result = eval("42 99");
6829 let msg = format!("{}", result.unwrap_err());
6830 assert!(msg.contains("cannot call"), "msg: {msg}");
6831 assert!(msg.contains("call.nix"), "msg: {msg}");
6832 }
6833
6834 #[test]
6835 fn error_without_file_has_no_in_prefix() {
6836 let result = eval("nonexistent_xyz");
6839 let msg = format!("{}", result.unwrap_err());
6840 assert!(msg.contains("undefined variable"), "msg: {msg}");
6841 assert!(!msg.contains(", in"), "msg should not contain file context: {msg}");
6842 }
6843
6844 #[test]
6847 fn pure_mode_set_get_independence() {
6848 let was = is_pure_mode();
6849 set_pure_mode(true);
6850 assert!(is_pure_mode());
6851 set_pure_mode(false);
6852 assert!(!is_pure_mode());
6853 set_pure_mode(was);
6854 }
6855
6856 #[test]
6859 fn eval_with_file_some_path_arithmetic() {
6860 let p = std::path::PathBuf::from("/tmp/imaginary.nix");
6861 let result = eval_with_file("1 + 2", Some(p)).unwrap();
6862 assert_eq!(result, Value::Int(3));
6863 }
6864
6865 #[test]
6873 fn unsafe_get_attr_pos_reports_file_and_offset_column() {
6874 let dir = tempfile::tempdir().unwrap();
6880 let file_body = "{ a = 1;\n b = 2; }\n";
6882 let f = dir.path().join("lit.nix");
6883 std::fs::write(&f, file_body).unwrap();
6884 let src = format!("builtins.unsafeGetAttrPos \"b\" (import {})", f.display());
6885 let v = eval(&src).unwrap();
6886 let attrs = match v { Value::Attrs(a) => a, other => panic!("expected attrs, got {other:?}") };
6887 assert_eq!(
6888 attrs.get("file").unwrap().as_string().unwrap(),
6889 f.to_string_lossy(),
6890 );
6891 assert_eq!(*attrs.get("line").unwrap(), Value::Int(1));
6892 let expected_col = (file_body.find("b = 2").unwrap() as i64) + 1;
6894 let col = match attrs.get("column").unwrap() { Value::Int(n) => *n, o => panic!("{o:?}") };
6895 assert_eq!(col, expected_col, "column must be the 1-based byte offset");
6896 }
6897
6898 #[test]
6899 fn unsafe_get_attr_pos_null_for_string_origin() {
6900 let v = eval("builtins.unsafeGetAttrPos \"a\" { a = 1; }").unwrap();
6902 assert_eq!(v, Value::Null);
6903 }
6904
6905 #[test]
6906 fn unsafe_get_attr_pos_null_for_missing_key() {
6907 let dir = tempfile::tempdir().unwrap();
6909 let f = dir.path().join("lit.nix");
6910 std::fs::write(&f, "{ a = 1; }\n").unwrap();
6911 let src = format!("builtins.unsafeGetAttrPos \"zzz\" (import {})", f.display());
6912 let v = eval(&src).unwrap();
6913 assert_eq!(v, Value::Null);
6914 }
6915
6916 #[test]
6919 fn interp_int_into_string() {
6920 assert_eq!(ev(r#""val=${toString 42}""#), Value::string("val=42"));
6922 }
6923
6924 #[test]
6925 fn interp_bool_true_becomes_one() {
6926 let v = ev(r#"let x = true; in "${builtins.toString x}""#);
6928 assert_eq!(v, Value::string("1"));
6929 }
6930
6931 #[test]
6932 fn interp_null_becomes_empty() {
6933 let v = ev(r#"let x = null; in "${builtins.toString x}""#);
6935 assert_eq!(v, Value::string(""));
6936 }
6937
6938 #[test]
6939 fn interp_attrset_without_to_string_errors() {
6940 let result = eval(r#"let s = { x = 1; }; in "${s}""#);
6942 assert!(result.is_err());
6943 }
6944
6945 #[test]
6946 fn interp_attrset_with_to_string_protocol() {
6947 let v = ev(r#""${{ __toString = self: "ok"; }}""#);
6949 assert_eq!(v, Value::string("ok"));
6950 }
6951
6952 #[test]
6955 fn eval_path_absolute_literal() {
6956 let v = ev("/tmp/foo");
6957 match v {
6958 Value::Path(p) => assert!(p.contains("/tmp/foo")),
6959 _ => panic!("expected Path"),
6960 }
6961 }
6962
6963 #[test]
6964 fn eval_path_home_literal() {
6965 let v = ev("~/foo.nix");
6966 match v {
6967 Value::Path(p) => assert!(p.contains("~/foo.nix") || p.ends_with("foo.nix")),
6968 _ => panic!("expected Path"),
6969 }
6970 }
6971
6972 #[test]
6975 fn path_search_unmatched_errors() {
6976 let saved = std::env::var("NIX_PATH").ok();
6979 unsafe {
6983 std::env::remove_var("NIX_PATH");
6984 }
6985 let result = eval("<this_should_not_resolve>");
6986 if let Some(v) = saved {
6987 unsafe {
6988 std::env::set_var("NIX_PATH", v);
6989 }
6990 }
6991 assert!(result.is_err());
6992 }
6993
6994 #[test]
6997 fn unary_negate_int() {
6998 assert_eq!(ev("-7"), Value::Int(-7));
6999 }
7000
7001 #[test]
7002 fn unary_negate_float() {
7003 assert_eq!(ev("-2.5"), Value::Float(-2.5));
7004 }
7005
7006 #[test]
7007 fn unary_invert_true() {
7008 assert_eq!(ev("!true"), Value::Bool(false));
7009 }
7010
7011 #[test]
7012 fn unary_invert_false() {
7013 assert_eq!(ev("!false"), Value::Bool(true));
7014 }
7015
7016 #[test]
7017 fn unary_negate_bool_errors() {
7018 let result = eval("-true");
7019 assert!(result.is_err());
7020 }
7021
7022 #[test]
7023 fn unary_invert_int_errors() {
7024 let result = eval("!42");
7025 assert!(result.is_err());
7026 }
7027
7028 #[test]
7031 fn binop_add_attrs_errors() {
7032 let result = eval("{a=1;} + {b=2;}");
7033 assert!(result.is_err());
7034 }
7035
7036 #[test]
7037 fn binop_sub_string_errors() {
7038 let result = eval(r#""a" - "b""#);
7039 assert!(result.is_err());
7040 }
7041
7042 #[test]
7043 fn binop_mul_string_errors() {
7044 let result = eval(r#""a" * "b""#);
7045 assert!(result.is_err());
7046 }
7047
7048 #[test]
7049 fn binop_div_string_errors() {
7050 let result = eval(r#""a" / "b""#);
7051 assert!(result.is_err());
7052 }
7053
7054 #[test]
7055 fn binop_compare_attrs_errors() {
7056 let result = eval("{a=1;} < {b=2;}");
7057 assert!(result.is_err());
7058 }
7059
7060 #[test]
7061 fn binop_div_float_by_zero_int() {
7062 let result = eval("1.0 / 0");
7066 let _ = result;
7069 }
7070
7071 #[test]
7072 fn binop_int_div_zero_is_division_by_zero() {
7073 let result = eval("5 / 0");
7074 match result {
7075 Err(EvalError::DivisionByZero) => {}
7076 other => panic!("expected DivisionByZero, got {other:?}"),
7077 }
7078 }
7079
7080 #[test]
7083 fn if_else_only_chosen_branch_evaluated_then() {
7084 assert_eq!(ev("if true then 42 else 1 / 0"), Value::Int(42));
7087 }
7088
7089 #[test]
7090 fn if_else_only_chosen_branch_evaluated_else() {
7091 assert_eq!(ev("if false then 1 / 0 else 99"), Value::Int(99));
7092 }
7093
7094 #[test]
7095 fn if_condition_must_be_bool() {
7096 let result = eval("if 1 then 1 else 2");
7097 assert!(result.is_err());
7098 }
7099
7100 #[test]
7101 fn if_condition_lazy_does_not_force_unused() {
7102 assert_eq!(
7105 ev("let bad = 1 / 0; in if true then 42 else bad"),
7106 Value::Int(42),
7107 );
7108 }
7109
7110 #[test]
7113 fn and_short_circuits_on_false() {
7114 assert_eq!(ev("false && (1 / 0 == 0)"), Value::Bool(false));
7116 }
7117
7118 #[test]
7119 fn or_short_circuits_on_true() {
7120 assert_eq!(ev("true || (1 / 0 == 0)"), Value::Bool(true));
7121 }
7122
7123 #[test]
7124 fn implication_short_circuits_on_false_lhs() {
7125 assert_eq!(ev("false -> (1 / 0 == 0)"), Value::Bool(true));
7127 }
7128
7129 #[test]
7132 fn lambda_fix_combinator_returns_attrset() {
7133 let v = ev(
7135 "let fix = f: let x = f x; in x; in
7136 (fix (self: { val = 1; double = self.val * 2; })).double",
7137 );
7138 assert_eq!(v, Value::Int(2));
7139 }
7140
7141 #[test]
7144 fn rec_attrset_self_reference() {
7145 let v = ev("(rec { a = b; b = 1; }).a");
7147 assert_eq!(v, Value::Int(1));
7148 }
7149
7150 #[test]
7151 fn rec_attrset_inherit_from_uses_outer_scope() {
7152 let v = ev(
7156 "let src = { a = 10; }; in
7157 rec {
7158 inherit (src) a;
7159 b = a + 1;
7160 }",
7161 );
7162 if let Value::Attrs(attrs) = v {
7163 let b = attrs.get("b").unwrap();
7164 let b_forced = force_value(b).unwrap();
7165 assert_eq!(b_forced, Value::Int(11));
7166 } else {
7167 panic!("expected attrs");
7168 }
7169 }
7170
7171 #[test]
7172 fn nonrec_attrset_no_self_reference() {
7173 let result = eval("({ a = 1; b = a + 1; }).b");
7176 assert!(result.is_err());
7177 }
7178
7179 #[test]
7182 fn dotted_binding_three_segments_then_sibling() {
7183 let v = ev("{ a.b.c = 1; a.b.d = 2; a.e = 3; }");
7184 if let Value::Attrs(attrs) = v {
7185 let a = attrs.get("a").unwrap();
7186 let a_forced = force_value(a).unwrap();
7187 if let Value::Attrs(a_attrs) = a_forced {
7188 let b = a_attrs.get("b").unwrap();
7189 let b_forced = force_value(b).unwrap();
7190 if let Value::Attrs(b_attrs) = b_forced {
7191 assert_eq!(force_value(b_attrs.get("c").unwrap()).unwrap(), Value::Int(1));
7192 assert_eq!(force_value(b_attrs.get("d").unwrap()).unwrap(), Value::Int(2));
7193 } else {
7194 panic!("expected b to be attrs");
7195 }
7196 assert_eq!(force_value(a_attrs.get("e").unwrap()).unwrap(), Value::Int(3));
7197 } else {
7198 panic!("expected a to be attrs");
7199 }
7200 } else {
7201 panic!("expected outer attrs");
7202 }
7203 }
7204
7205 #[test]
7208 fn rec_dotted_bindings_visible_to_siblings() {
7209 let v = ev("rec { types.openSB = 1; types.openCpu = 2; foo = types.openSB; }.foo");
7212 assert_eq!(v, Value::Int(1));
7213 }
7214
7215 #[test]
7216 fn rec_dotted_leaf_uses_rec_scope() {
7217 let v = ev("rec { types.a = f 1; f = x: x + 1; }.types.a");
7220 assert_eq!(v, Value::Int(2));
7221 }
7222
7223 #[test]
7224 fn rec_dotted_multiple_keys_merge() {
7225 let v = ev("rec { types.a = 1; types.b = 2; x = types; }.x");
7227 if let Value::Attrs(attrs) = v {
7228 assert_eq!(force_value(attrs.get("a").unwrap()).unwrap(), Value::Int(1));
7229 assert_eq!(force_value(attrs.get("b").unwrap()).unwrap(), Value::Int(2));
7230 } else {
7231 panic!("expected attrs");
7232 }
7233 }
7234
7235 #[test]
7236 fn rec_nixpkgs_parse_pattern() {
7237 let v = ev(r#"
7241 let
7242 mkOptionType = x: x;
7243 mergeOneOption = "merge";
7244 attrValues = builtins.attrValues;
7245 setType = name: value: { __type = name; } // value;
7246 mapAttrs = builtins.mapAttrs;
7247 enum = xs: mkOptionType { name = "enum"; check = x: builtins.elem x xs; };
7248 setTypes = type: mapAttrs (name: value: setType type.name ({ inherit name; } // value));
7249 in
7250 rec {
7251 types.openSB = mkOptionType { name = "sb"; merge = mergeOneOption; };
7252 types.significantByte = enum (attrValues significantBytes);
7253 significantBytes = setTypes types.openSB { bigEndian = {}; littleEndian = {}; };
7254 types.openCpuType = mkOptionType { name = "cpu-type"; };
7255 types.cpuType = enum (attrValues cpuTypes);
7256 cpuTypes = setTypes types.openCpuType { arm = { bits = 32; }; };
7257 }.types.openCpuType
7258 "#);
7259 if let Value::Attrs(attrs) = v {
7260 assert_eq!(
7261 force_value(attrs.get("name").unwrap()).unwrap(),
7262 Value::string("cpu-type")
7263 );
7264 } else {
7265 panic!("expected attrs");
7266 }
7267 }
7268
7269 #[test]
7270 fn let_dotted_leaf_uses_let_scope() {
7271 let v = ev("let a.x = f 1; f = x: x + 1; in a.x");
7273 assert_eq!(v, Value::Int(2));
7274 }
7275
7276 #[test]
7277 fn let_inherit_from_plus_dotted_overrides() {
7278 let v = ev(r#"
7284 let
7285 src = { types = { existing = true; }; };
7286 inherit (src) types;
7287 types.added = true;
7288 in types
7289 "#);
7290 if let Value::Attrs(attrs) = v {
7291 assert_eq!(
7293 force_value(attrs.get("added").unwrap()).unwrap(),
7294 Value::Bool(true)
7295 );
7296 assert!(attrs.get("existing").is_none());
7298 } else {
7299 panic!("expected attrs");
7300 }
7301 }
7302
7303 #[test]
7306 fn pattern_empty_no_args_no_ellipsis() {
7307 assert_eq!(ev("({}: 1) {}"), Value::Int(1));
7309 }
7310
7311 #[test]
7312 fn pattern_empty_with_ellipsis_accepts_extra() {
7313 assert_eq!(ev("({...}: 1) { a = 1; b = 2; }"), Value::Int(1));
7314 }
7315
7316 #[test]
7317 fn pattern_all_defaults() {
7318 assert_eq!(
7319 ev("({a ? 1, b ? 2}: a + b) {}"),
7320 Value::Int(3),
7321 );
7322 }
7323
7324 #[test]
7325 fn pattern_at_bind_before() {
7326 assert_eq!(ev("(args @ { x }: args.x) { x = 7; }"), Value::Int(7));
7328 }
7329
7330 #[test]
7331 fn pattern_at_bind_after() {
7332 assert_eq!(ev("({ x } @ args: args.x) { x = 7; }"), Value::Int(7));
7334 }
7335
7336 #[test]
7337 fn pattern_default_references_other_arg() {
7338 assert_eq!(ev("({a, b ? a + 1}: b) {a = 10;}"), Value::Int(11));
7340 }
7341
7342 #[test]
7343 fn pattern_required_missing_errors() {
7344 let result = eval("({ a, b }: a) { a = 1; }");
7345 assert!(result.is_err());
7346 }
7347
7348 #[test]
7349 fn pattern_unexpected_errors_without_ellipsis() {
7350 let result = eval("({ a }: a) { a = 1; b = 2; }");
7351 assert!(result.is_err());
7352 }
7353
7354 #[test]
7357 fn apply_int_errors() {
7358 let result = eval("42 5");
7359 assert!(result.is_err());
7360 }
7361
7362 #[test]
7363 fn apply_string_errors() {
7364 let result = eval(r#""hi" 5"#);
7365 assert!(result.is_err());
7366 }
7367
7368 #[test]
7369 fn apply_attrset_without_functor_errors() {
7370 let result = eval("{ x = 1; } 5");
7371 assert!(result.is_err());
7372 let msg = format!("{}", result.unwrap_err());
7373 assert!(msg.contains("__functor") || msg.contains("cannot call"));
7374 }
7375
7376 #[test]
7379 fn select_multi_segment_with_default() {
7380 assert_eq!(ev("{ a = { b = 1; }; }.a.c or 99"), Value::Int(99));
7382 }
7383
7384 #[test]
7385 fn select_from_int_errors() {
7386 let result = eval("(1).x");
7387 assert!(result.is_err());
7388 }
7389
7390 #[test]
7393 fn has_attr_on_non_set_returns_false() {
7394 assert_eq!(ev("1 ? x"), Value::Bool(false));
7396 }
7397
7398 #[test]
7399 fn has_attr_nested_path_present() {
7400 assert_eq!(ev("{ a = { b = 1; }; } ? a.b"), Value::Bool(true));
7401 }
7402
7403 #[test]
7404 fn has_attr_nested_path_missing() {
7405 assert_eq!(ev("{ a = { b = 1; }; } ? a.c"), Value::Bool(false));
7406 }
7407
7408 #[test]
7409 fn has_attr_intermediate_missing_returns_false() {
7410 assert_eq!(ev("{} ? a.b.c"), Value::Bool(false));
7411 }
7412
7413 #[test]
7416 fn list_with_function_value() {
7417 let v = ev("[(x: x + 1)]");
7418 if let Value::List(items) = v {
7419 assert_eq!(items.len(), 1);
7420 let forced = force_value(&items[0]).unwrap();
7422 assert!(matches!(forced, Value::Lambda(_)));
7423 } else {
7424 panic!("expected list");
7425 }
7426 }
7427
7428 #[test]
7431 fn inherit_unknown_name_errors() {
7432 let result = eval("let x = 1; in let inherit nonexistent; in nonexistent");
7433 assert!(result.is_err());
7434 }
7435
7436 #[test]
7439 fn string_concat_no_context_when_both_plain() {
7440 let v = ev(r#""abc" + "def""#);
7441 if let Value::String(ns) = v {
7442 assert_eq!(ns.chars, "abcdef");
7443 assert!(!ns.has_context());
7444 } else {
7445 panic!("expected string");
7446 }
7447 }
7448
7449 #[test]
7452 fn parens_around_expression() {
7453 assert_eq!(ev("(1 + 2)"), Value::Int(3));
7454 }
7455
7456 #[test]
7457 fn nested_parens() {
7458 assert_eq!(ev("(((42)))"), Value::Int(42));
7459 }
7460
7461 #[test]
7464 fn throw_propagates_as_error() {
7465 let result = eval(r#"builtins.throw "kaboom""#);
7466 match result {
7467 Err(EvalError::Throw(s)) => assert!(s.contains("kaboom")),
7468 other => panic!("expected Throw, got {other:?}"),
7469 }
7470 }
7471
7472 #[test]
7473 fn assert_failed_propagates_as_error() {
7474 let result = eval("assert false; 1");
7475 match result {
7476 Err(EvalError::AssertionFailed(_)) => {}
7477 other => panic!("expected AssertionFailed, got {other:?}"),
7478 }
7479 }
7480
7481 #[test]
7484 fn string_no_interp_yields_no_context() {
7485 let v = ev(r#""just literal""#);
7486 if let Value::String(ns) = v {
7487 assert!(!ns.has_context());
7488 } else {
7489 panic!("expected string");
7490 }
7491 }
7492
7493 #[test]
7502 fn interp_path_copies_to_store_byte_matches_cppnix() {
7503 let dir = std::env::temp_dir().join(format!("sui-r5-interp-{}", std::process::id()));
7504 let _ = std::fs::remove_dir_all(&dir);
7505 std::fs::create_dir_all(&dir).unwrap();
7506 let f = dir.join("data.txt");
7507 std::fs::write(&f, b"hello\n").unwrap();
7508 let expr = format!(r#""${{{}}}""#, f.display());
7509 let v = eval(&expr).unwrap();
7510 if let Value::String(ns) = v {
7511 assert_eq!(
7512 ns.chars.to_string(),
7513 "/nix/store/y9dmvfhip31hg8ia4njwjz9vfa3ndphr-data.txt",
7514 );
7515 assert!(ns.has_context());
7516 } else {
7517 panic!("expected string");
7518 }
7519 let _ = std::fs::remove_dir_all(&dir);
7520 }
7521
7522 #[test]
7531 fn parse_error_unbalanced_braces() {
7532 let result = eval("{ a = 1");
7533 assert!(result.is_err());
7534 let err = result.unwrap_err();
7535 assert!(matches!(err, EvalError::ParseError(_)));
7536 }
7537
7538 #[test]
7539 fn parse_error_dangling_let() {
7540 let result = eval("let in");
7541 assert!(result.is_err());
7542 }
7543
7544 #[test]
7545 fn parse_error_empty_input() {
7546 let result = eval("");
7547 assert!(result.is_err());
7548 }
7549
7550 #[test]
7553 fn float_int_subtraction() {
7554 assert_eq!(ev("3.5 - 1"), Value::Float(2.5));
7555 }
7556
7557 #[test]
7558 fn int_float_subtraction() {
7559 assert_eq!(ev("3 - 0.5"), Value::Float(2.5));
7560 }
7561
7562 #[test]
7563 fn float_float_division() {
7564 assert_eq!(ev("6.0 / 2.0"), Value::Float(3.0));
7565 }
7566
7567 #[test]
7568 fn int_float_multiplication() {
7569 assert_eq!(ev("3 * 2.5"), Value::Float(7.5));
7570 }
7571
7572 #[test]
7575 fn compare_int_float_less() {
7576 assert_eq!(ev("1 < 1.5"), Value::Bool(true));
7577 }
7578
7579 #[test]
7580 fn compare_float_int_more() {
7581 assert_eq!(ev("3.5 > 3"), Value::Bool(true));
7582 }
7583
7584 #[test]
7585 fn compare_equal_int_float() {
7586 assert_eq!(ev("3 <= 3.0"), Value::Bool(true));
7587 }
7588
7589 #[test]
7592 fn equal_lists_same() {
7593 assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
7594 }
7595
7596 #[test]
7597 fn equal_lists_diff_length() {
7598 assert_eq!(ev("[1 2] == [1 2 3]"), Value::Bool(false));
7599 }
7600
7601 #[test]
7602 fn not_equal_lists() {
7603 assert_eq!(ev("[1] != [2]"), Value::Bool(true));
7604 }
7605
7606 #[test]
7607 fn equal_attrsets_same() {
7608 assert_eq!(ev("{a = 1; b = 2;} == {b = 2; a = 1;}"), Value::Bool(true));
7609 }
7610
7611 #[test]
7618 fn lambda_self_equality_in_attrset() {
7619 assert_eq!(
7621 ev("let f = x: x; in { a = 1; inherit f; } == { a = 1; inherit f; }"),
7622 Value::Bool(true),
7623 );
7624 }
7625
7626 #[test]
7627 fn lambda_self_reference_attrset_equality() {
7628 assert_eq!(
7630 ev("let x = { a = 1; f = y: y; }; in x == x"),
7631 Value::Bool(true),
7632 );
7633 }
7634
7635 #[test]
7636 fn lambda_different_closures_not_equal() {
7637 assert_eq!(
7639 ev("{ f = x: x; } == { f = x: x; }"),
7640 Value::Bool(false),
7641 );
7642 }
7643
7644 #[test]
7645 fn lambda_ne_does_not_force_unused_branch() {
7646 assert_eq!(
7649 ev("let ls = { a = 1; f = x: x; }; in if ls != ls then builtins.throw \"bug\" else 42"),
7650 Value::Int(42),
7651 );
7652 }
7653
7654 #[test]
7657 fn force_value_through_thunk() {
7658 let root = rnix::Root::parse("1 + 2");
7659 let expr = root.tree().expr().unwrap();
7660 let thunk = Thunk::new_suspended(expr, Env::new());
7661 let val = Value::Thunk(thunk);
7662 assert_eq!(force_value(&val).unwrap(), Value::Int(3));
7663 }
7664
7665 #[test]
7668 fn try_eval_catches_thrown_error() {
7669 let v = ev(r#"(builtins.tryEval (builtins.throw "oops")).success"#);
7671 assert_eq!(v, Value::Bool(false));
7672 }
7673
7674 #[test]
7675 fn try_eval_returns_value_on_success() {
7676 let v = ev("(builtins.tryEval 42).value");
7677 assert_eq!(v, Value::Int(42));
7678 }
7679
7680 #[test]
7683 fn legacy_let_returns_body_attr() {
7684 assert_eq!(ev("let { x = 1; body = x + 41; }"), Value::Int(42));
7688 }
7689
7690 #[test]
7691 fn legacy_let_missing_body_errors() {
7692 let result = eval("let { x = 1; }");
7693 assert!(result.is_err());
7694 }
7695
7696 #[test]
7697 fn legacy_let_with_inherit_from_scope() {
7698 assert_eq!(
7699 ev("let outer = 5; in let { inherit outer; body = outer * 2; }"),
7700 Value::Int(10),
7701 );
7702 }
7703
7704 #[test]
7707 fn interp_with_string_concat_preserves_order() {
7708 assert_eq!(
7709 ev(r#"let a = "x"; b = "y"; in "${a}-${b}""#),
7710 Value::string("x-y"),
7711 );
7712 }
7713
7714 #[test]
7715 fn interp_only_literal_part() {
7716 assert_eq!(ev(r#""no interp here""#), Value::string("no interp here"));
7717 }
7718
7719 #[test]
7722 fn dynamic_attr_via_string_key_in_set() {
7723 assert_eq!(ev(r#"{ "a" = 1; }.a"#), Value::Int(1));
7725 }
7726
7727 #[test]
7728 fn dynamic_attr_via_interpolated_key() {
7729 let v = ev(r#"let k = "foo"; in { ${k} = 99; }.foo"#);
7730 assert_eq!(v, Value::Int(99));
7731 }
7732
7733 #[test]
7736 fn select_with_string_key() {
7737 let v = ev(r#"{ a = 42; }."a""#);
7738 assert_eq!(v, Value::Int(42));
7739 }
7740
7741 #[test]
7744 fn apply_attrset_with_functor_works() {
7745 let v = ev("let s = { __functor = self: x: x + 1; }; in s 5");
7746 assert_eq!(v, Value::Int(6));
7747 }
7748
7749 #[test]
7752 fn double_negate_int() {
7753 assert_eq!(ev("- (-5)"), Value::Int(5));
7754 }
7755
7756 #[test]
7759 fn inherit_in_let_makes_name_available() {
7760 assert_eq!(
7761 ev("let src = { a = 7; }; in let inherit (src) a; in a"),
7762 Value::Int(7),
7763 );
7764 }
7765
7766 #[test]
7769 fn path_plus_string_yields_path() {
7770 let v = ev(r#"/foo + "/bar""#);
7771 match v {
7772 Value::Path(p) => assert_eq!(&*p, "/foo/bar"),
7773 _ => panic!("expected path"),
7774 }
7775 }
7776
7777 #[test]
7780 fn attrset_value_not_forced_unless_selected() {
7781 assert_eq!(
7784 ev(r#"{ bad = builtins.throw "boom"; good = 42; }.good"#),
7785 Value::Int(42),
7786 );
7787 }
7788
7789 #[test]
7792 fn lambda_recursive_via_let() {
7793 assert_eq!(
7795 ev("let fact = n: if n == 0 then 1 else n * fact (n - 1); in fact 5"),
7796 Value::Int(120),
7797 );
7798 }
7799
7800 #[test]
7803 fn select_with_dynamic_key_via_var() {
7804 assert_eq!(ev(r#"let k = { x = 1; }; in k.x"#), Value::Int(1));
7807 }
7808
7809 #[test]
7812 fn compare_string_lex_greater_or_equal() {
7813 assert_eq!(ev(r#""b" >= "a""#), Value::Bool(true));
7814 assert_eq!(ev(r#""a" >= "a""#), Value::Bool(true));
7815 assert_eq!(ev(r#""a" >= "b""#), Value::Bool(false));
7816 }
7817
7818 #[test]
7821 fn equal_int_string_false() {
7822 assert_eq!(ev(r#"1 == "1""#), Value::Bool(false));
7823 }
7824
7825 #[test]
7826 fn equal_null_int_false() {
7827 assert_eq!(ev("null == 0"), Value::Bool(false));
7828 }
7829
7830 #[test]
7833 fn update_with_let_bound_operands() {
7834 assert_eq!(
7835 ev("let a = { x = 1; }; b = { y = 2; }; in (a // b).y"),
7836 Value::Int(2),
7837 );
7838 }
7839
7840 #[test]
7843 fn concat_lists_from_let() {
7844 assert_eq!(
7845 ev("let a = [1 2]; b = [3 4]; in builtins.length (a ++ b)"),
7846 Value::Int(4),
7847 );
7848 }
7849
7850 #[test]
7853 fn interp_list_coerces_with_spaces() {
7854 assert_eq!(
7857 ev(r#""${toString [1 2 3]}""#),
7858 Value::string("1 2 3"),
7859 );
7860 }
7861
7862 #[test]
7863 fn interp_list_directly_coerces() {
7864 assert_eq!(
7866 ev(r#""${[1 2]}""#),
7867 Value::string("1 2"),
7868 );
7869 }
7870
7871 #[test]
7874 fn interp_outpath_attrset() {
7875 assert_eq!(
7876 ev(r#"let x = { outPath = "/nix/store/abc"; }; in "${x}""#),
7877 Value::string("/nix/store/abc"),
7878 );
7879 }
7880
7881 #[test]
7882 fn interp_tostring_takes_priority_over_outpath() {
7883 assert_eq!(
7884 ev(r#"let x = { __toString = self: "custom"; outPath = "/ignored"; }; in "${x}""#),
7885 Value::string("custom"),
7886 );
7887 }
7888
7889 #[test]
7890 fn interp_derivation_coerces_to_outpath() {
7891 let result = eval(r#"
7893 let drv = builtins.derivation {
7894 name = "test";
7895 system = "x86_64-linux";
7896 builder = "/bin/sh";
7897 };
7898 in "${drv}"
7899 "#).unwrap();
7900 if let Value::String(s) = result {
7901 assert!(s.chars.starts_with("/nix/store/"), "got: {}", s.chars);
7902 } else {
7903 panic!("expected string");
7904 }
7905 }
7906
7907 #[test]
7910 fn interp_lambda_errors() {
7911 let result = eval(r#""${x: x}""#);
7912 assert!(result.is_err());
7913 }
7914
7915 #[test]
7918 fn force_value_int_returns_same() {
7919 let v = Value::Int(42);
7920 assert_eq!(force_value(&v).unwrap(), Value::Int(42));
7921 }
7922
7923 #[test]
7924 fn force_value_bool_returns_same() {
7925 let v = Value::Bool(true);
7926 assert_eq!(force_value(&v).unwrap(), Value::Bool(true));
7927 }
7928
7929 #[test]
7930 fn force_value_string_returns_same() {
7931 let v = Value::string("hello");
7932 assert_eq!(force_value(&v).unwrap(), Value::string("hello"));
7933 }
7934
7935 #[test]
7936 fn force_value_attrs_returns_same() {
7937 let mut a = NixAttrs::new();
7938 a.insert("x".to_string(), Value::Int(1));
7939 let v = Value::Attrs(Rc::new(a.clone()));
7940 assert_eq!(force_value(&v).unwrap(), Value::Attrs(Rc::new(a)));
7941 }
7942
7943 #[test]
7944 fn force_value_list_returns_same() {
7945 let v = Value::list(vec![Value::Int(1), Value::Int(2)]);
7946 assert_eq!(
7947 force_value(&v).unwrap(),
7948 Value::list(vec![Value::Int(1), Value::Int(2)]),
7949 );
7950 }
7951
7952 #[test]
7953 fn force_value_null_returns_null() {
7954 let v = Value::Null;
7955 assert_eq!(force_value(&v).unwrap(), Value::Null);
7956 }
7957
7958 #[test]
7959 fn force_value_evaluated_thunk_returns_cached() {
7960 let v = ev("let x = 1 + 2; in x");
7962 assert_eq!(v, Value::Int(3));
7963 assert_eq!(force_value(&v).unwrap(), Value::Int(3));
7965 }
7966
7967 #[test]
7970 fn tco_if_true_condition() {
7971 assert_eq!(ev("if true then 42 else 0"), Value::Int(42));
7972 }
7973
7974 #[test]
7975 fn tco_if_false_condition() {
7976 assert_eq!(ev("if false then 42 else 0"), Value::Int(0));
7977 }
7978
7979 #[test]
7980 fn tco_deeply_nested_if_else_chain() {
7981 let mut expr = String::from("150");
7984 for i in (1..150).rev() {
7985 expr = format!("if false then {} else {}", i, expr);
7986 }
7987 let v = ev(&expr);
7988 assert_eq!(v, Value::Int(150));
7989 }
7990
7991 #[test]
7992 fn tco_assert_true_passes_through() {
7993 assert_eq!(ev("assert true; 42"), Value::Int(42));
7994 }
7995
7996 #[test]
7997 fn tco_assert_false_throws_assertion_failed() {
7998 let result = eval("assert false; 42");
7999 assert!(result.is_err());
8000 let err = result.unwrap_err();
8001 assert!(
8002 matches!(err, EvalError::AssertionFailed(_)),
8003 "expected AssertionFailed, got: {err}",
8004 );
8005 }
8006
8007 #[test]
8008 fn tco_with_makes_scope_available() {
8009 assert_eq!(ev("with { x = 10; y = 20; }; x + y"), Value::Int(30));
8010 }
8011
8012 #[test]
8013 fn tco_let_in_creates_bindings() {
8014 assert_eq!(ev("let a = 5; in a"), Value::Int(5));
8015 }
8016
8017 #[test]
8018 fn tco_let_in_multiple_bindings() {
8019 assert_eq!(ev("let a = 1; b = 2; c = 3; in a + b + c"), Value::Int(6));
8020 }
8021
8022 #[test]
8025 fn eval_attrset_empty() {
8026 let v = ev("{}");
8027 if let Value::Attrs(attrs) = v {
8028 assert!(attrs.is_empty(), "expected empty attrset");
8029 } else {
8030 panic!("expected attrset, got {v:?}");
8031 }
8032 }
8033
8034 #[test]
8035 fn eval_attrset_simple_kv() {
8036 let v = ev("{ a = 1; b = 2; }");
8037 if let Value::Attrs(attrs) = v {
8038 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8039 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8040 } else {
8041 panic!("expected attrset, got {v:?}");
8042 }
8043 }
8044
8045 #[test]
8046 fn eval_attrset_recursive() {
8047 assert_eq!(ev("(rec { a = 1; b = a + 1; }).b"), Value::Int(2));
8048 assert_eq!(ev("(rec { a = 1; b = a + 1; }).a"), Value::Int(1));
8049 }
8050
8051 #[test]
8052 fn eval_attrset_inherit_from_scope() {
8053 assert_eq!(ev("let x = 1; in { inherit x; }.x"), Value::Int(1));
8054 }
8055
8056 #[test]
8057 fn eval_attrset_inherit_from_expr() {
8058 assert_eq!(
8059 ev("{ inherit (builtins) true; }.true"),
8060 Value::Bool(true),
8061 );
8062 }
8063
8064 #[test]
8065 fn eval_attrset_dotted_path() {
8066 assert_eq!(ev("{ a.b.c = 1; }.a.b.c"), Value::Int(1));
8067 }
8068
8069 #[test]
8070 fn eval_attrset_update_merge() {
8071 let v = ev("{ a = 1; } // { b = 2; }");
8072 if let Value::Attrs(attrs) = v {
8073 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8074 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8075 } else {
8076 panic!("expected attrset, got {v:?}");
8077 }
8078 }
8079
8080 #[test]
8083 fn eval_apply_simple_function() {
8084 assert_eq!(ev("(x: x + 1) 2"), Value::Int(3));
8085 }
8086
8087 #[test]
8088 fn eval_apply_pattern_destructuring() {
8089 assert_eq!(ev("({a, b}: a + b) { a = 1; b = 2; }"), Value::Int(3));
8090 }
8091
8092 #[test]
8093 fn eval_apply_default_arguments() {
8094 assert_eq!(ev("({a, b ? 0}: a + b) { a = 1; }"), Value::Int(1));
8095 }
8096
8097 #[test]
8098 fn eval_apply_ellipsis() {
8099 assert_eq!(ev("({a, ...}: a) { a = 1; b = 2; }"), Value::Int(1));
8100 }
8101
8102 #[test]
8105 fn eval_select_single_key() {
8106 assert_eq!(ev("{ a = 1; }.a"), Value::Int(1));
8107 }
8108
8109 #[test]
8110 fn eval_select_multi_level() {
8111 assert_eq!(ev("{ a.b = 1; }.a.b"), Value::Int(1));
8112 }
8113
8114 #[test]
8115 fn eval_select_with_or_default() {
8116 assert_eq!(ev("{}.a or 42"), Value::Int(42));
8117 }
8118
8119 #[test]
8120 fn eval_select_missing_key_without_default_throws() {
8121 let result = eval("{}.a");
8122 assert!(result.is_err());
8123 }
8124
8125 #[test]
8128 fn binop_add_ints() {
8129 assert_eq!(ev("1 + 2"), Value::Int(3));
8130 }
8131
8132 #[test]
8133 fn binop_sub_ints() {
8134 assert_eq!(ev("3 - 1"), Value::Int(2));
8135 }
8136
8137 #[test]
8138 fn binop_mul_ints() {
8139 assert_eq!(ev("2 * 3"), Value::Int(6));
8140 }
8141
8142 #[test]
8143 fn binop_div_ints() {
8144 assert_eq!(ev("6 / 2"), Value::Int(3));
8145 }
8146
8147 #[test]
8148 fn binop_float_arithmetic() {
8149 assert_eq!(ev("1.5 + 2.5"), Value::Float(4.0));
8150 }
8151
8152 #[test]
8153 fn binop_string_concat() {
8154 assert_eq!(
8155 ev(r#""hello" + " " + "world""#),
8156 Value::string("hello world"),
8157 );
8158 }
8159
8160 #[test]
8161 fn binop_list_concat() {
8162 assert_eq!(
8163 ev("[1 2] ++ [3 4]"),
8164 Value::list(vec![
8165 Value::Int(1),
8166 Value::Int(2),
8167 Value::Int(3),
8168 Value::Int(4),
8169 ]),
8170 );
8171 }
8172
8173 #[test]
8174 fn binop_attrset_update() {
8175 let v = ev("{ a = 1; } // { b = 2; }");
8176 if let Value::Attrs(attrs) = v {
8177 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
8178 assert_eq!(attrs.get("b"), Some(&Value::Int(2)));
8179 } else {
8180 panic!("expected attrset, got {v:?}");
8181 }
8182 }
8183
8184 #[test]
8185 fn binop_less_than() {
8186 assert_eq!(ev("1 < 2"), Value::Bool(true));
8187 assert_eq!(ev("2 < 1"), Value::Bool(false));
8188 }
8189
8190 #[test]
8191 fn binop_greater_than() {
8192 assert_eq!(ev("2 > 1"), Value::Bool(true));
8193 assert_eq!(ev("1 > 2"), Value::Bool(false));
8194 }
8195
8196 #[test]
8197 fn binop_equal() {
8198 assert_eq!(ev("1 == 1"), Value::Bool(true));
8199 assert_eq!(ev("1 == 2"), Value::Bool(false));
8200 }
8201
8202 #[test]
8203 fn binop_not_equal() {
8204 assert_eq!(ev("1 != 2"), Value::Bool(true));
8205 assert_eq!(ev("1 != 1"), Value::Bool(false));
8206 }
8207
8208 #[test]
8209 fn binop_logical_and() {
8210 assert_eq!(ev("true && false"), Value::Bool(false));
8211 assert_eq!(ev("true && true"), Value::Bool(true));
8212 }
8213
8214 #[test]
8215 fn binop_logical_or() {
8216 assert_eq!(ev("true || false"), Value::Bool(true));
8217 assert_eq!(ev("false || false"), Value::Bool(false));
8218 }
8219
8220 #[test]
8221 fn binop_logical_not() {
8222 assert_eq!(ev("!true"), Value::Bool(false));
8223 assert_eq!(ev("!false"), Value::Bool(true));
8224 }
8225
8226 #[test]
8227 fn binop_implication() {
8228 assert_eq!(ev("false -> true"), Value::Bool(true));
8229 assert_eq!(ev("false -> false"), Value::Bool(true));
8230 assert_eq!(ev("true -> true"), Value::Bool(true));
8231 assert_eq!(ev("true -> false"), Value::Bool(false));
8232 }
8233}