git.delta.rocks / jrsonnet / refs/commits / c7fb88801c20

difftreelog

feat FuncDesc helper for default context

Yaroslav Bolyukin2022-04-17parent: #efea837.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/function.rs
1use crate::{2	error::{Error::*, LocError},3	evaluate, evaluate_named,4	gc::TraceBox,5	throw,6	typed::Typed,7	Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17	context: Context,18	expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21	fn get(self: Box<Self>) -> Result<Val> {22		evaluate(self.context, &self.expr)23	}24}2526pub trait ArgLike {27	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;28}29impl ArgLike for &LocExpr {30	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {31		Ok(if tailstrict {32			LazyVal::new_resolved(evaluate(ctx, self)?)33		} else {34			LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {35				context: ctx,36				expr: (*self).clone(),37			})))38		})39	}40}41impl<T> ArgLike for T42where43	T: Typed + Clone,44	Val: TryFrom<T, Error = LocError>,45{46	fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {47		let val: Val = Val::try_from(self.clone())?;48		Ok(LazyVal::new_resolved(val))49	}50}51pub enum TlaArg {52	String(IStr),53	Code(LocExpr),54	Val(Val),55}56impl ArgLike for TlaArg {57	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {58		match self {59			TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),60			TlaArg::Code(code) => Ok(if tailstrict {61				LazyVal::new_resolved(evaluate(ctx, code)?)62			} else {63				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {64					context: ctx,65					expr: code.clone(),66				})))67			}),68			TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),69		}70	}71}7273pub trait ArgsLike {74	fn unnamed_len(&self) -> usize;75	fn unnamed_iter(76		&self,77		ctx: Context,78		tailstrict: bool,79		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,80	) -> Result<()>;81	fn named_iter(82		&self,83		ctx: Context,84		tailstrict: bool,85		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,86	) -> Result<()>;87	fn named_names(&self, handler: &mut dyn FnMut(&IStr));88}8990impl ArgsLike for ArgsDesc {91	fn unnamed_len(&self) -> usize {92		self.unnamed.len()93	}9495	fn unnamed_iter(96		&self,97		ctx: Context,98		tailstrict: bool,99		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,100	) -> Result<()> {101		for (id, arg) in self.unnamed.iter().enumerate() {102			handler(103				id,104				if tailstrict {105					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)106				} else {107					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {108						context: ctx.clone(),109						expr: arg.clone(),110					})))111				},112			)?;113		}114		Ok(())115	}116117	fn named_iter(118		&self,119		ctx: Context,120		tailstrict: bool,121		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,122	) -> Result<()> {123		for (name, arg) in self.named.iter() {124			handler(125				name,126				if tailstrict {127					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)128				} else {129					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {130						context: ctx.clone(),131						expr: arg.clone(),132					})))133				},134			)?;135		}136		Ok(())137	}138139	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {140		for (name, _) in self.named.iter() {141			handler(name)142		}143	}144}145146impl<A: ArgLike> ArgsLike for [(IStr, A)] {147	fn unnamed_len(&self) -> usize {148		0149	}150151	fn unnamed_iter(152		&self,153		_ctx: Context,154		_tailstrict: bool,155		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,156	) -> Result<()> {157		Ok(())158	}159160	fn named_iter(161		&self,162		ctx: Context,163		tailstrict: bool,164		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,165	) -> Result<()> {166		for (name, val) in self.iter() {167			handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;168		}169		Ok(())170	}171172	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {173		for (name, _) in self.iter() {174			handler(name);175		}176	}177}178179impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {180	fn unnamed_len(&self) -> usize {181		0182	}183184	fn unnamed_iter(185		&self,186		_ctx: Context,187		_tailstrict: bool,188		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,189	) -> Result<()> {190		Ok(())191	}192193	fn named_iter(194		&self,195		ctx: Context,196		tailstrict: bool,197		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,198	) -> Result<()> {199		for (name, value) in self.iter() {200			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;201		}202		Ok(())203	}204205	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {206		for (name, _) in self.iter() {207			handler(name);208		}209	}210}211212impl<A: ArgLike> ArgsLike for [A] {213	fn unnamed_len(&self) -> usize {214		self.len()215	}216217	fn unnamed_iter(218		&self,219		ctx: Context,220		tailstrict: bool,221		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,222	) -> Result<()> {223		for (i, arg) in self.iter().enumerate() {224			handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;225		}226		Ok(())227	}228229	fn named_iter(230		&self,231		_ctx: Context,232		_tailstrict: bool,233		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,234	) -> Result<()> {235		Ok(())236	}237238	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}239}240impl<A: ArgLike> ArgsLike for &[A] {241	fn unnamed_len(&self) -> usize {242		(*self).unnamed_len()243	}244245	fn unnamed_iter(246		&self,247		ctx: Context,248		tailstrict: bool,249		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,250	) -> Result<()> {251		(*self).unnamed_iter(ctx, tailstrict, handler)252	}253254	fn named_iter(255		&self,256		ctx: Context,257		tailstrict: bool,258		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,259	) -> Result<()> {260		(*self).named_iter(ctx, tailstrict, handler)261	}262263	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {264		(*self).named_names(handler)265	}266}267268/// Creates correct [context](Context) for function body evaluation returning error on invalid call.269///270/// ## Parameters271/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)272/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)273/// * `params`: function parameters' definition274/// * `args`: passed function arguments275/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily276pub fn parse_function_call(277	ctx: Context,278	body_ctx: Context,279	params: &ParamsDesc,280	args: &dyn ArgsLike,281	tailstrict: bool,282) -> Result<Context> {283	let mut passed_args = GcHashMap::with_capacity(params.len());284	if args.unnamed_len() > params.len() {285		throw!(TooManyArgsFunctionHas(params.len()))286	}287288	let mut filled_args = 0;289290	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {291		let name = params[id].0.clone();292		passed_args.insert(name, arg);293		filled_args += 1;294		Ok(())295	})?;296297	args.named_iter(ctx, tailstrict, &mut |name, value| {298		// FIXME: O(n) for arg existence check299		if !params.iter().any(|p| &p.0 == name) {300			throw!(UnknownFunctionParameter((name as &str).to_owned()));301		}302		if passed_args.insert(name.clone(), value).is_some() {303			throw!(BindingParameterASecondTime(name.clone()));304		}305		filled_args += 1;306		Ok(())307	})?;308309	if filled_args < params.len() {310		// Some args are unset, but maybe we have defaults for them311		// Default values should be created in newly created context312		let future_context = FutureWrapper::<Context>::new();313		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);314315		for param in params.iter().filter(|p| p.1.is_some()) {316			if passed_args.contains_key(&param.0.clone()) {317				continue;318			}319			#[derive(Trace)]320			struct LazyNamedBinding {321				future_context: FutureWrapper<Context>,322				name: IStr,323				value: LocExpr,324			}325			impl LazyValValue for LazyNamedBinding {326				fn get(self: Box<Self>) -> Result<Val> {327					evaluate_named(self.future_context.unwrap(), &self.value, self.name)328				}329			}330			LazyVal::new(TraceBox(Box::new(LazyNamedBinding {331				future_context: future_context.clone(),332				name: param.0.clone(),333				value: param.1.clone().unwrap(),334			})));335336			defaults.insert(337				param.0.clone(),338				LazyVal::new(TraceBox(Box::new(LazyNamedBinding {339					future_context: future_context.clone(),340					name: param.0.clone(),341					value: param.1.clone().unwrap(),342				}))),343			);344			filled_args += 1;345		}346347		// Some args still wasn't filled348		if filled_args != params.len() {349			for param in params.iter().skip(args.unnamed_len()) {350				let mut found = false;351				args.named_names(&mut |name| {352					if name == &param.0 {353						found = true;354					}355				});356				if !found {357					throw!(FunctionParameterNotBoundInCall(param.0.clone()));358				}359			}360			unreachable!();361		}362363		Ok(body_ctx364			.extend(passed_args, None, None, None)365			.extend_bound(defaults)366			.into_future(future_context))367	} else {368		let body_ctx = body_ctx.extend(passed_args, None, None, None);369		Ok(body_ctx)370	}371}372373type BuiltinParamName = Cow<'static, str>;374375#[derive(Clone, Trace)]376pub struct BuiltinParam {377	pub name: BuiltinParamName,378	pub has_default: bool,379}380381/// Do not implement it directly, instead use #[builtin] macro382pub trait Builtin: Trace {383	fn name(&self) -> &str;384	fn params(&self) -> &[BuiltinParam];385	fn call(386		&self,387		context: Context,388		loc: Option<&ExprLocation>,389		args: &dyn ArgsLike,390	) -> Result<Val>;391}392393pub trait StaticBuiltin: Builtin + Send + Sync394where395	Self: 'static,396{397	// In impl, to make it object safe:398	// const INST: &'static Self;399}400401/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead402///403/// ## Parameters404/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)405/// * `params`: function parameters' definition406/// * `args`: passed function arguments407/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily408pub fn parse_builtin_call(409	ctx: Context,410	params: &[BuiltinParam],411	args: &dyn ArgsLike,412	tailstrict: bool,413) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {414	let mut passed_args = GcHashMap::with_capacity(params.len());415	if args.unnamed_len() > params.len() {416		throw!(TooManyArgsFunctionHas(params.len()))417	}418419	let mut filled_args = 0;420421	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {422		let name = params[id].name.clone();423		passed_args.insert(name, arg);424		filled_args += 1;425		Ok(())426	})?;427428	args.named_iter(ctx, tailstrict, &mut |name, arg| {429		// FIXME: O(n) for arg existence check430		let p = params431			.iter()432			.find(|p| p.name == name as &str)433			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;434		if passed_args.insert(p.name.clone(), arg).is_some() {435			throw!(BindingParameterASecondTime(name.clone()));436		}437		filled_args += 1;438		Ok(())439	})?;440441	if filled_args < params.len() {442		for param in params.iter().filter(|p| p.has_default) {443			if passed_args.contains_key(&param.name) {444				continue;445			}446			filled_args += 1;447		}448449		// Some args still wasn't filled450		if filled_args != params.len() {451			for param in params.iter().skip(args.unnamed_len()) {452				let mut found = false;453				args.named_names(&mut |name| {454					if name as &str == &param.name as &str {455						found = true;456					}457				});458				if !found {459					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));460				}461			}462			unreachable!();463		}464	}465	Ok(passed_args)466}
after · crates/jrsonnet-evaluator/src/function.rs
1use crate::{2	error::{Error::*, LocError},3	evaluate, evaluate_named,4	gc::TraceBox,5	throw,6	typed::Typed,7	Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val,8};9use gcmodule::Trace;10use jrsonnet_interner::IStr;11pub use jrsonnet_macros::builtin;12use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};13use std::{borrow::Cow, collections::HashMap, convert::TryFrom};1415#[derive(Trace)]16struct EvaluateLazyVal {17	context: Context,18	expr: LocExpr,19}20impl LazyValValue for EvaluateLazyVal {21	fn get(self: Box<Self>) -> Result<Val> {22		evaluate(self.context, &self.expr)23	}24}2526#[derive(Trace)]27struct EvaluateNamedLazyVal {28	future_context: FutureWrapper<Context>,29	name: IStr,30	value: LocExpr,31}32impl LazyValValue for EvaluateNamedLazyVal {33	fn get(self: Box<Self>) -> Result<Val> {34		evaluate_named(self.future_context.unwrap(), &self.value, self.name)35	}36}3738pub trait ArgLike {39	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;40}41impl ArgLike for &LocExpr {42	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {43		Ok(if tailstrict {44			LazyVal::new_resolved(evaluate(ctx, self)?)45		} else {46			LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {47				context: ctx,48				expr: (*self).clone(),49			})))50		})51	}52}53impl<T> ArgLike for T54where55	T: Typed + Clone,56	Val: TryFrom<T, Error = LocError>,57{58	fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {59		let val: Val = Val::try_from(self.clone())?;60		Ok(LazyVal::new_resolved(val))61	}62}63pub enum TlaArg {64	String(IStr),65	Code(LocExpr),66	Val(Val),67}68impl ArgLike for TlaArg {69	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {70		match self {71			TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),72			TlaArg::Code(code) => Ok(if tailstrict {73				LazyVal::new_resolved(evaluate(ctx, code)?)74			} else {75				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {76					context: ctx,77					expr: code.clone(),78				})))79			}),80			TlaArg::Val(val) => Ok(LazyVal::new_resolved(val.clone())),81		}82	}83}8485pub trait ArgsLike {86	fn unnamed_len(&self) -> usize;87	fn unnamed_iter(88		&self,89		ctx: Context,90		tailstrict: bool,91		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,92	) -> Result<()>;93	fn named_iter(94		&self,95		ctx: Context,96		tailstrict: bool,97		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,98	) -> Result<()>;99	fn named_names(&self, handler: &mut dyn FnMut(&IStr));100}101102impl ArgsLike for ArgsDesc {103	fn unnamed_len(&self) -> usize {104		self.unnamed.len()105	}106107	fn unnamed_iter(108		&self,109		ctx: Context,110		tailstrict: bool,111		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,112	) -> Result<()> {113		for (id, arg) in self.unnamed.iter().enumerate() {114			handler(115				id,116				if tailstrict {117					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)118				} else {119					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {120						context: ctx.clone(),121						expr: arg.clone(),122					})))123				},124			)?;125		}126		Ok(())127	}128129	fn named_iter(130		&self,131		ctx: Context,132		tailstrict: bool,133		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,134	) -> Result<()> {135		for (name, arg) in self.named.iter() {136			handler(137				name,138				if tailstrict {139					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)140				} else {141					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {142						context: ctx.clone(),143						expr: arg.clone(),144					})))145				},146			)?;147		}148		Ok(())149	}150151	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {152		for (name, _) in self.named.iter() {153			handler(name)154		}155	}156}157158impl<A: ArgLike> ArgsLike for [(IStr, A)] {159	fn unnamed_len(&self) -> usize {160		0161	}162163	fn unnamed_iter(164		&self,165		_ctx: Context,166		_tailstrict: bool,167		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,168	) -> Result<()> {169		Ok(())170	}171172	fn named_iter(173		&self,174		ctx: Context,175		tailstrict: bool,176		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,177	) -> Result<()> {178		for (name, val) in self.iter() {179			handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;180		}181		Ok(())182	}183184	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {185		for (name, _) in self.iter() {186			handler(name);187		}188	}189}190191impl<A: ArgLike> ArgsLike for HashMap<IStr, A> {192	fn unnamed_len(&self) -> usize {193		0194	}195196	fn unnamed_iter(197		&self,198		_ctx: Context,199		_tailstrict: bool,200		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,201	) -> Result<()> {202		Ok(())203	}204205	fn named_iter(206		&self,207		ctx: Context,208		tailstrict: bool,209		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,210	) -> Result<()> {211		for (name, value) in self.iter() {212			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;213		}214		Ok(())215	}216217	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {218		for (name, _) in self.iter() {219			handler(name);220		}221	}222}223224impl<A: ArgLike> ArgsLike for [A] {225	fn unnamed_len(&self) -> usize {226		self.len()227	}228229	fn unnamed_iter(230		&self,231		ctx: Context,232		tailstrict: bool,233		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,234	) -> Result<()> {235		for (i, arg) in self.iter().enumerate() {236			handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;237		}238		Ok(())239	}240241	fn named_iter(242		&self,243		_ctx: Context,244		_tailstrict: bool,245		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,246	) -> Result<()> {247		Ok(())248	}249250	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}251}252impl<A: ArgLike> ArgsLike for &[A] {253	fn unnamed_len(&self) -> usize {254		(*self).unnamed_len()255	}256257	fn unnamed_iter(258		&self,259		ctx: Context,260		tailstrict: bool,261		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,262	) -> Result<()> {263		(*self).unnamed_iter(ctx, tailstrict, handler)264	}265266	fn named_iter(267		&self,268		ctx: Context,269		tailstrict: bool,270		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,271	) -> Result<()> {272		(*self).named_iter(ctx, tailstrict, handler)273	}274275	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {276		(*self).named_names(handler)277	}278}279280/// Creates correct [context](Context) for function body evaluation returning error on invalid call.281///282/// ## Parameters283/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)284/// * `body_ctx`: used for default parameter values' execution and for body execution (if set)285/// * `params`: function parameters' definition286/// * `args`: passed function arguments287/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily288pub fn parse_function_call(289	ctx: Context,290	body_ctx: Context,291	params: &ParamsDesc,292	args: &dyn ArgsLike,293	tailstrict: bool,294) -> Result<Context> {295	let mut passed_args = GcHashMap::with_capacity(params.len());296	if args.unnamed_len() > params.len() {297		throw!(TooManyArgsFunctionHas(params.len()))298	}299300	let mut filled_args = 0;301302	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {303		let name = params[id].0.clone();304		passed_args.insert(name, arg);305		filled_args += 1;306		Ok(())307	})?;308309	args.named_iter(ctx, tailstrict, &mut |name, value| {310		// FIXME: O(n) for arg existence check311		if !params.iter().any(|p| &p.0 == name) {312			throw!(UnknownFunctionParameter((name as &str).to_owned()));313		}314		if passed_args.insert(name.clone(), value).is_some() {315			throw!(BindingParameterASecondTime(name.clone()));316		}317		filled_args += 1;318		Ok(())319	})?;320321	if filled_args < params.len() {322		// Some args are unset, but maybe we have defaults for them323		// Default values should be created in newly created context324		let future_context = Context::new_future();325		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);326327		for param in params.iter().filter(|p| p.1.is_some()) {328			if passed_args.contains_key(&param.0.clone()) {329				continue;330			}331			LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {332				future_context: future_context.clone(),333				name: param.0.clone(),334				value: param.1.clone().unwrap(),335			})));336337			defaults.insert(338				param.0.clone(),339				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {340					future_context: future_context.clone(),341					name: param.0.clone(),342					value: param.1.clone().unwrap(),343				}))),344			);345			filled_args += 1;346		}347348		// Some args still wasn't filled349		if filled_args != params.len() {350			for param in params.iter().skip(args.unnamed_len()) {351				let mut found = false;352				args.named_names(&mut |name| {353					if name == &param.0 {354						found = true;355					}356				});357				if !found {358					throw!(FunctionParameterNotBoundInCall(param.0.clone()));359				}360			}361			unreachable!();362		}363364		Ok(body_ctx365			.extend(passed_args, None, None, None)366			.extend_bound(defaults)367			.into_future(future_context))368	} else {369		let body_ctx = body_ctx.extend(passed_args, None, None, None);370		Ok(body_ctx)371	}372}373374type BuiltinParamName = Cow<'static, str>;375376#[derive(Clone, Trace)]377pub struct BuiltinParam {378	pub name: BuiltinParamName,379	pub has_default: bool,380}381382/// Do not implement it directly, instead use #[builtin] macro383pub trait Builtin: Trace {384	fn name(&self) -> &str;385	fn params(&self) -> &[BuiltinParam];386	fn call(387		&self,388		context: Context,389		loc: Option<&ExprLocation>,390		args: &dyn ArgsLike,391	) -> Result<Val>;392}393394pub trait StaticBuiltin: Builtin + Send + Sync395where396	Self: 'static,397{398	// In impl, to make it object safe:399	// const INST: &'static Self;400}401402/// You shouldn't probally use this function, use jrsonnet_macros::builtin instead403///404/// ## Parameters405/// * `ctx`: used for passed argument expressions' execution and for body execution (if `body_ctx` is not set)406/// * `params`: function parameters' definition407/// * `args`: passed function arguments408/// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily409pub fn parse_builtin_call(410	ctx: Context,411	params: &[BuiltinParam],412	args: &dyn ArgsLike,413	tailstrict: bool,414) -> Result<GcHashMap<BuiltinParamName, LazyVal>> {415	let mut passed_args = GcHashMap::with_capacity(params.len());416	if args.unnamed_len() > params.len() {417		throw!(TooManyArgsFunctionHas(params.len()))418	}419420	let mut filled_args = 0;421422	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {423		let name = params[id].name.clone();424		passed_args.insert(name, arg);425		filled_args += 1;426		Ok(())427	})?;428429	args.named_iter(ctx, tailstrict, &mut |name, arg| {430		// FIXME: O(n) for arg existence check431		let p = params432			.iter()433			.find(|p| p.name == name as &str)434			.ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;435		if passed_args.insert(p.name.clone(), arg).is_some() {436			throw!(BindingParameterASecondTime(name.clone()));437		}438		filled_args += 1;439		Ok(())440	})?;441442	if filled_args < params.len() {443		for param in params.iter().filter(|p| p.has_default) {444			if passed_args.contains_key(&param.name) {445				continue;446			}447			filled_args += 1;448		}449450		// Some args still wasn't filled451		if filled_args != params.len() {452			for param in params.iter().skip(args.unnamed_len()) {453				let mut found = false;454				args.named_names(&mut |name| {455					if name as &str == &param.name as &str {456						found = true;457					}458				});459				if !found {460					throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));461				}462			}463			unreachable!();464		}465	}466	Ok(passed_args)467}468469/// Creates Context, which has all argument default values applied470/// and with unbound values causing error to be returned471pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {472	let ctx = Context::new_future();473474	let mut bindings = GcHashMap::new();475476	#[derive(Trace)]477	struct DependsOnUnbound(IStr);478	impl LazyValValue for DependsOnUnbound {479		fn get(self: Box<Self>) -> Result<Val> {480			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())481		}482	}483484	for param in params.iter() {485		if let Some(v) = &param.1 {486			bindings.insert(487				param.0.clone(),488				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {489					future_context: ctx.clone(),490					name: param.0.clone(),491					value: v.clone(),492				}))),493			);494		} else {495			bindings.insert(496				param.0.clone(),497				LazyVal::new(TraceBox(Box::new(DependsOnUnbound(param.0.clone())))),498			);499		}500	}501502	body_ctx.extend(bindings, None, None, None).into_future(ctx)503}
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -5,7 +5,9 @@
 	cc_ptr_eq,
 	error::{Error::*, LocError},
 	evaluate,
-	function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},
+	function::{
+		parse_default_function_call, parse_function_call, ArgsLike, Builtin, StaticBuiltin,
+	},
 	gc::TraceBox,
 	throw, Context, ObjValue, Result,
 };
@@ -84,6 +86,22 @@
 	pub params: ParamsDesc,
 	pub body: LocExpr,
 }
+impl FuncDesc {
+	/// Create body context, but fill arguments without defaults with lazy error
+	pub fn default_body_context(&self) -> Context {
+		parse_default_function_call(self.ctx.clone(), &self.params)
+	}
+
+	/// Create context, with which body code will run
+	pub fn call_body_context(
+		&self,
+		call_ctx: Context,
+		args: &dyn ArgsLike,
+		tailstrict: bool,
+	) -> Result<Context> {
+		parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)
+	}
+}
 
 #[derive(Trace, Clone)]
 pub enum FuncVal {
@@ -140,16 +158,10 @@
 	) -> Result<Val> {
 		match self {
 			Self::Normal(func) => {
-				let ctx = parse_function_call(
-					call_ctx,
-					func.ctx.clone(),
-					&func.params,
-					args,
-					tailstrict,
-				)?;
-				evaluate(ctx, &func.body)
+				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;
+				evaluate(body_ctx, &func.body)
 			}
-			Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),
+			Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),
 			Self::Builtin(b) => b.call(call_ctx, loc, args),
 		}
 	}