git.delta.rocks / jrsonnet / refs/commits / 5fc1275a239d

difftreelog

fix prepare with extra named arguments

onzopuokYaroslav Bolyukin2026-04-25parent: #340c467.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -431,7 +431,6 @@
 	Ok(match value {
 		Val::Func(f) => {
 			let name = f.name();
-			let prepare = PreparedFuncVal::new(f, args.unnamed.len(), &args.names)?;
 			let unnamed = args
 				.unnamed
 				.iter()
@@ -444,6 +443,8 @@
 				.cloned()
 				.map(|un| evaluate_thunk(ctx.clone(), un, tailstrict))
 				.collect::<Result<Vec<_>>>()?;
+			let prepare = PreparedFuncVal::new(f, args.unnamed.len(), &args.names)
+				.with_description_src(loc, || format!("function <{name}> call"))?;
 			let body = || prepare.call(loc, &unnamed, &named);
 			if tailstrict {
 				body()?
modifiedcrates/jrsonnet-evaluator/src/function/prepared.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function/prepared.rs
1use std::rc::Rc;23use jrsonnet_gcmodule::{Acyclic, Trace};4use jrsonnet_ir::{ExprParams, IStr, function::FunctionSignature};5use rustc_hash::{FxHashMap, FxHashSet};67use super::{CallLocation, FuncVal};8use crate::{9	Context, ContextBuilder, Pending, Result, Thunk, Val, bail, destructure::destruct,10	error::ErrorKind::*, evaluate_named_param, gc::WithCapacityExt,11};1213#[derive(Debug, Trace, Clone)]14pub struct PreparedFuncVal {15	fun: FuncVal,16	prepared: Rc<PreparedCall>,17}1819impl PreparedFuncVal {20	pub fn new(fun: FuncVal, unnamed: usize, named: &[IStr]) -> Result<Self> {21		let prepared = prepare_call(fun.params(), unnamed, named)?;22		Ok(Self {23			fun,24			prepared: Rc::new(prepared),25		})26	}27	pub fn call(28		&self,29		loc: CallLocation<'_>,30		unnamed: &[Thunk<Val>],31		named: &[Thunk<Val>],32	) -> Result<Val> {33		self.fun34			.evaluate_prepared(&self.prepared, loc, unnamed, named, false)35	}36}3738#[derive(Acyclic, Debug)]39pub struct PreparedCall {40	// Param, named input.41	named: Vec<(usize, usize)>,42	defaults: Vec<usize>,43}4445pub fn prepare_call(46	params: FunctionSignature,47	unnamed: usize,48	named: &[IStr],49) -> Result<PreparedCall> {50	if unnamed > params.len() {51		bail!(TooManyArgsFunctionHas(params.len(), params))52	}5354	let expected_defaults = params.len() - unnamed - named.len();55	let mut ops = PreparedCall {56		named: Vec::with_capacity(named.len()),57		defaults: Vec::with_capacity(expected_defaults),58	};5960	// FIXME: bitmask61	let mut passed: FxHashSet<usize> = (0..unnamed).collect();6263	for (input_id, name) in named.iter().enumerate() {64		// FIXME: O(n) for arg existence check65		let Some(param_idx) = params.iter().position(|p| p.name() == name) else {66			bail!(UnknownFunctionParameter(name.clone()));67		};68		if !passed.insert(param_idx) {69			bail!(BindingParameterASecondTime(name.clone()));70		}71		ops.named.push((param_idx, input_id));72	}7374	if named.len() + unnamed < params.len() {75		let mut defaults = 0;7677		for (param_id, param) in params78			.iter()79			.enumerate()80			.skip(unnamed)81			.filter(|p| p.1.has_default())82		{83			// Skip already passed parameters84			if !param.name().is_anonymous() && passed.contains(&param_id) {85				continue;86			}87			defaults += 1;8889			ops.defaults.push(param_id);90		}9192		// Some args still weren't filled93		if defaults != expected_defaults {94			for param in params.iter().skip(unnamed) {95				let mut found = false;96				for name in named {97					if param.name() == name {98						found = true;99					}100				}101				if !found {102					bail!(FunctionParameterNotBoundInCall(103						param.name().clone(),104						params105					));106				}107			}108			unreachable!();109		}110	}111112	Ok(ops)113}114pub fn parse_prepared_function_call(115	body_ctx: Context,116	prepared: &PreparedCall,117	params: &ExprParams,118	unnamed: &[Thunk<Val>],119	named: &[Thunk<Val>],120) -> Result<Context> {121	let mut passed_args = FxHashMap::with_capacity(params.binds_len());122123	let destruct_ctx = Pending::new();124125	for (param_idx, unnamed) in unnamed.iter().enumerate() {126		destruct(127			&params.exprs[param_idx].destruct,128			unnamed.clone(),129			destruct_ctx.clone(),130			&mut passed_args,131		)?;132	}133134	for (param_idx, arg_idx) in prepared.named.iter().copied() {135		destruct(136			&params.exprs[param_idx].destruct,137			named[arg_idx].clone(),138			destruct_ctx.clone(),139			&mut passed_args,140		)?;141	}142143	if prepared.defaults.is_empty() {144		let body_ctx = body_ctx145			.extend_bindings(passed_args)146			.into_future(destruct_ctx);147		Ok(body_ctx)148	} else {149		let fctx = Context::new_future();150		let mut defaults = FxHashMap::with_capacity(params.binds_len() - passed_args.len());151		for param_idx in prepared.defaults.iter().copied() {152			// let param = params.0.rc_idx(param_idx);153			destruct(154				&params.exprs[param_idx].destruct,155				{156					let ctx = fctx.clone();157					let params = params.clone();158					Thunk!(move || {159						let param = &params.exprs[param_idx];160						let name = param.destruct.name();161						let value = param.default.as_ref().expect("default exists");162						evaluate_named_param(ctx.unwrap(), value, name)163					})164				},165				fctx.clone(),166				&mut defaults,167			)?;168		}169170		let mut ctx = ContextBuilder::extend(body_ctx);171		ctx.binds(passed_args);172		ctx.binds(defaults);173		Ok(ctx.build().into_future(fctx).into_future(destruct_ctx))174	}175}176pub fn parse_prepared_builtin_call(177	prepared: &PreparedCall,178	params: FunctionSignature,179	unnamed: &[Thunk<Val>],180	named: &[Thunk<Val>],181) -> Vec<Option<Thunk<Val>>> {182	let mut passed_args = vec![None; params.len()];183184	for (param_idx, unnamed) in unnamed.iter().enumerate() {185		passed_args[param_idx] = Some(unnamed.clone());186	}187188	for (param_idx, arg_idx) in prepared.named.iter().copied() {189		passed_args[param_idx] = Some(named[arg_idx].clone());190	}191192	passed_args193}