feat(tvix/eval): Give names to builtin arguments
Refactor the arguments of a Builtin to be a vec of a new BuiltinArgument struct, which contains the old strictness boolean and also a static `name` str - this is automatically determined via the ident for the corresponding function argument in the proc-macro case, and passed in in the cases where we're still manually calling Builtin::new. Currently this name is unused, but in the future this can be used as part of a documentation system for builtins. Change-Id: Ib9dadb15b69bf8c9ea1983a4f4f197294a2394a6 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7204 Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
This commit is contained in:
		
							parent
							
								
									dad07a8bc0
								
							
						
					
					
						commit
						a1015ba1d7
					
				
					 6 changed files with 109 additions and 55 deletions
				
			
		|  | @ -5,7 +5,9 @@ use proc_macro2::Span; | ||||||
| use quote::{quote_spanned, ToTokens}; | use quote::{quote_spanned, ToTokens}; | ||||||
| use syn::parse::Parse; | use syn::parse::Parse; | ||||||
| use syn::spanned::Spanned; | use syn::spanned::Spanned; | ||||||
| use syn::{parse_macro_input, parse_quote, FnArg, Ident, Item, ItemMod, LitStr, PatType}; | use syn::{ | ||||||
|  |     parse_macro_input, parse_quote, FnArg, Ident, Item, ItemMod, LitStr, Pat, PatIdent, PatType, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| struct BuiltinArgs { | struct BuiltinArgs { | ||||||
|     name: LitStr, |     name: LitStr, | ||||||
|  | @ -89,31 +91,54 @@ pub fn builtins(_args: TokenStream, item: TokenStream) -> TokenStream { | ||||||
|                     .into(); |                     .into(); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 let strictness = f |                 let builtin_arguments = f | ||||||
|                     .sig |                     .sig | ||||||
|                     .inputs |                     .inputs | ||||||
|                     .iter_mut() |                     .iter_mut() | ||||||
|                     .skip(1) |                     .skip(1) | ||||||
|                     .map(|input| { |                     .map(|arg| { | ||||||
|                         let mut lazy = false; |                         let mut strict = true; | ||||||
|                         if let FnArg::Typed(PatType { attrs, .. }) = input { |                         let name = match arg { | ||||||
|                             attrs.retain(|attr| { |                             FnArg::Receiver(_) => { | ||||||
|                                 attr.path.get_ident().into_iter().any(|id| { |                                 return Err(quote_spanned!(arg.span() => { | ||||||
|                                     if id == "lazy" { |                                     compile_error!("Unexpected receiver argument in builtin") | ||||||
|                                         lazy = true; |                                 })) | ||||||
|                                         false |                             } | ||||||
|                                     } else { |                             FnArg::Typed(PatType { attrs, pat, .. }) => { | ||||||
|                                         true |                                 attrs.retain(|attr| { | ||||||
|                                     } |                                     attr.path.get_ident().into_iter().any(|id| { | ||||||
|                                 }) |                                         if id == "lazy" { | ||||||
|                             }); |                                             strict = false; | ||||||
|                         } |                                             false | ||||||
|                         !lazy |                                         } else { | ||||||
|  |                                             true | ||||||
|  |                                         } | ||||||
|  |                                     }) | ||||||
|  |                                 }); | ||||||
|  |                                 match pat.as_ref() { | ||||||
|  |                                     Pat::Ident(PatIdent { ident, .. }) => ident.to_string(), | ||||||
|  |                                     _ => "unknown".to_string(), | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         Ok(quote_spanned!(arg.span() => { | ||||||
|  |                             crate::internal::BuiltinArgument { | ||||||
|  |                                 strict: #strict, | ||||||
|  |                                 name: #name, | ||||||
|  |                             } | ||||||
|  |                         })) | ||||||
|                     }) |                     }) | ||||||
|                     .collect::<Vec<_>>(); |                     .collect::<Result<Vec<_>, _>>(); | ||||||
|  | 
 | ||||||
|  |                 let builtin_arguments = match builtin_arguments { | ||||||
|  |                     Ok(args) => args, | ||||||
|  |                     Err(err) => return err.into(), | ||||||
|  |                 }; | ||||||
| 
 | 
 | ||||||
|                 let fn_name = f.sig.ident.clone(); |                 let fn_name = f.sig.ident.clone(); | ||||||
|                 let args = (0..(f.sig.inputs.len() - 1)) |                 let num_args = f.sig.inputs.len() - 1; | ||||||
|  |                 let args = (0..num_args) | ||||||
|                     .map(|n| Ident::new(&format!("arg_{n}"), Span::call_site())) |                     .map(|n| Ident::new(&format!("arg_{n}"), Span::call_site())) | ||||||
|                     .collect::<Vec<_>>(); |                     .collect::<Vec<_>>(); | ||||||
|                 let mut reversed_args = args.clone(); |                 let mut reversed_args = args.clone(); | ||||||
|  | @ -122,7 +147,7 @@ pub fn builtins(_args: TokenStream, item: TokenStream) -> TokenStream { | ||||||
|                 builtins.push(quote_spanned! { builtin_attr.span() => { |                 builtins.push(quote_spanned! { builtin_attr.span() => { | ||||||
|                     crate::internal::Builtin::new( |                     crate::internal::Builtin::new( | ||||||
|                         #name, |                         #name, | ||||||
|                         &[#(#strictness),*], |                         &[#(#builtin_arguments),*], | ||||||
|                         |mut args: Vec<crate::Value>, vm: &mut crate::internal::VM| { |                         |mut args: Vec<crate::Value>, vm: &mut crate::internal::VM| { | ||||||
|                             #(let #reversed_args = args.pop().unwrap();)* |                             #(let #reversed_args = args.pop().unwrap();)* | ||||||
|                             #fn_name(vm, #(#args),*) |                             #fn_name(vm, #(#args),*) | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ use crate::{ | ||||||
|     compiler::GlobalsMap, |     compiler::GlobalsMap, | ||||||
|     errors::ErrorKind, |     errors::ErrorKind, | ||||||
|     observer::NoOpObserver, |     observer::NoOpObserver, | ||||||
|     value::{Builtin, NixAttrs, Thunk}, |     value::{Builtin, BuiltinArgument, NixAttrs, Thunk}, | ||||||
|     vm::VM, |     vm::VM, | ||||||
|     SourceCode, Value, |     SourceCode, Value, | ||||||
| }; | }; | ||||||
|  | @ -111,7 +111,10 @@ pub fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builti | ||||||
| 
 | 
 | ||||||
|     Builtin::new( |     Builtin::new( | ||||||
|         "import", |         "import", | ||||||
|         &[true], |         &[BuiltinArgument { | ||||||
|  |             strict: true, | ||||||
|  |             name: "path", | ||||||
|  |         }], | ||||||
|         move |mut args: Vec<Value>, vm: &mut VM| { |         move |mut args: Vec<Value>, vm: &mut VM| { | ||||||
|             let mut path = super::coerce_value_to_path(&args.pop().unwrap(), vm)?; |             let mut path = super::coerce_value_to_path(&args.pop().unwrap(), vm)?; | ||||||
|             if path.is_dir() { |             if path.is_dir() { | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| 
 | 
 | ||||||
| use crate::compiler::{GlobalsMap, GlobalsMapFunc}; | use crate::compiler::{GlobalsMap, GlobalsMapFunc}; | ||||||
| use crate::source::SourceCode; | use crate::source::SourceCode; | ||||||
|  | use crate::value::BuiltinArgument; | ||||||
| use std::cmp::{self, Ordering}; | use std::cmp::{self, Ordering}; | ||||||
| use std::collections::{BTreeMap, HashMap, HashSet}; | use std::collections::{BTreeMap, HashMap, HashSet}; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
|  | @ -907,7 +908,16 @@ fn placeholders() -> Vec<Builtin> { | ||||||
|     vec![ |     vec![ | ||||||
|         Builtin::new( |         Builtin::new( | ||||||
|             "addErrorContext", |             "addErrorContext", | ||||||
|             &[false, false], |             &[ | ||||||
|  |                 BuiltinArgument { | ||||||
|  |                     strict: false, | ||||||
|  |                     name: "context", | ||||||
|  |                 }, | ||||||
|  |                 BuiltinArgument { | ||||||
|  |                     strict: false, | ||||||
|  |                     name: "value", | ||||||
|  |                 }, | ||||||
|  |             ], | ||||||
|             |mut args: Vec<Value>, vm: &mut VM| { |             |mut args: Vec<Value>, vm: &mut VM| { | ||||||
|                 vm.emit_warning(WarningKind::NotImplemented("builtins.addErrorContext")); |                 vm.emit_warning(WarningKind::NotImplemented("builtins.addErrorContext")); | ||||||
|                 Ok(args.pop().unwrap()) |                 Ok(args.pop().unwrap()) | ||||||
|  | @ -915,7 +925,10 @@ fn placeholders() -> Vec<Builtin> { | ||||||
|         ), |         ), | ||||||
|         Builtin::new( |         Builtin::new( | ||||||
|             "unsafeDiscardStringContext", |             "unsafeDiscardStringContext", | ||||||
|             &[true], |             &[BuiltinArgument { | ||||||
|  |                 strict: true, | ||||||
|  |                 name: "s", | ||||||
|  |             }], | ||||||
|             |mut args: Vec<Value>, vm: &mut VM| { |             |mut args: Vec<Value>, vm: &mut VM| { | ||||||
|                 vm.emit_warning(WarningKind::NotImplemented( |                 vm.emit_warning(WarningKind::NotImplemented( | ||||||
|                     "builtins.unsafeDiscardStringContext", |                     "builtins.unsafeDiscardStringContext", | ||||||
|  | @ -923,28 +936,35 @@ fn placeholders() -> Vec<Builtin> { | ||||||
|                 Ok(args.pop().unwrap()) |                 Ok(args.pop().unwrap()) | ||||||
|             }, |             }, | ||||||
|         ), |         ), | ||||||
|         Builtin::new("derivation", &[true], |args: Vec<Value>, vm: &mut VM| { |         Builtin::new( | ||||||
|             vm.emit_warning(WarningKind::NotImplemented("builtins.derivation")); |             "derivation", | ||||||
|  |             &[BuiltinArgument { | ||||||
|  |                 strict: true, | ||||||
|  |                 name: "attrs", | ||||||
|  |             }], | ||||||
|  |             |args: Vec<Value>, vm: &mut VM| { | ||||||
|  |                 vm.emit_warning(WarningKind::NotImplemented("builtins.derivation")); | ||||||
| 
 | 
 | ||||||
|             // We do not implement derivations yet, so this function sets mock
 |                 // We do not implement derivations yet, so this function sets mock
 | ||||||
|             // values on the fields that a real derivation would contain.
 |                 // values on the fields that a real derivation would contain.
 | ||||||
|             //
 |                 //
 | ||||||
|             // Crucially this means we do not yet *validate* the values either.
 |                 // Crucially this means we do not yet *validate* the values either.
 | ||||||
|             let attrs = unwrap_or_clone_rc(args[0].to_attrs()?); |                 let attrs = unwrap_or_clone_rc(args[0].to_attrs()?); | ||||||
|             let attrs = attrs.update(NixAttrs::from_map(BTreeMap::from([ |                 let attrs = attrs.update(NixAttrs::from_map(BTreeMap::from([ | ||||||
|                 ( |                     ( | ||||||
|                     "outPath".into(), |                         "outPath".into(), | ||||||
|                     "/nix/store/00000000000000000000000000000000-mock".into(), |                         "/nix/store/00000000000000000000000000000000-mock".into(), | ||||||
|                 ), |                     ), | ||||||
|                 ( |                     ( | ||||||
|                     "drvPath".into(), |                         "drvPath".into(), | ||||||
|                     "/nix/store/00000000000000000000000000000000-mock.drv".into(), |                         "/nix/store/00000000000000000000000000000000-mock.drv".into(), | ||||||
|                 ), |                     ), | ||||||
|                 ("type".into(), "derivation".into()), |                     ("type".into(), "derivation".into()), | ||||||
|             ]))); |                 ]))); | ||||||
| 
 | 
 | ||||||
|             Ok(Value::Attrs(Rc::new(attrs))) |                 Ok(Value::Attrs(Rc::new(attrs))) | ||||||
|         }), |             }, | ||||||
|  |         ), | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
| // we set TVIX_CURRENT_SYSTEM in build.rs
 | // we set TVIX_CURRENT_SYSTEM in build.rs
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ pub use crate::vm::run_lambda; | ||||||
| /// Internal-only parts of `tvix-eval`, exported for use in macros, but not part of the public
 | /// Internal-only parts of `tvix-eval`, exported for use in macros, but not part of the public
 | ||||||
| /// interface of the crate.
 | /// interface of the crate.
 | ||||||
| pub mod internal { | pub mod internal { | ||||||
|     pub use crate::value::Builtin; |     pub use crate::value::{Builtin, BuiltinArgument}; | ||||||
|     pub use crate::vm::VM; |     pub use crate::vm::VM; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,14 @@ use std::{ | ||||||
| pub trait BuiltinFn: Fn(Vec<Value>, &mut VM) -> Result<Value, ErrorKind> {} | pub trait BuiltinFn: Fn(Vec<Value>, &mut VM) -> Result<Value, ErrorKind> {} | ||||||
| impl<F: Fn(Vec<Value>, &mut VM) -> Result<Value, ErrorKind>> BuiltinFn for F {} | impl<F: Fn(Vec<Value>, &mut VM) -> Result<Value, ErrorKind>> BuiltinFn for F {} | ||||||
| 
 | 
 | ||||||
|  | /// Description of a single argument passed to a builtin
 | ||||||
|  | pub struct BuiltinArgument { | ||||||
|  |     /// Whether the argument should be forced before the underlying builtin function is called
 | ||||||
|  |     pub strict: bool, | ||||||
|  |     /// The name of the argument, to be used in docstrings and error messages
 | ||||||
|  |     pub name: &'static str, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Represents a single built-in function which directly executes Rust
 | /// Represents a single built-in function which directly executes Rust
 | ||||||
| /// code that operates on a Nix value.
 | /// code that operates on a Nix value.
 | ||||||
| ///
 | ///
 | ||||||
|  | @ -40,10 +48,8 @@ impl<F: Fn(Vec<Value>, &mut VM) -> Result<Value, ErrorKind>> BuiltinFn for F {} | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct Builtin { | pub struct Builtin { | ||||||
|     name: &'static str, |     name: &'static str, | ||||||
|     /// Array reference that describes how many arguments there are (usually 1
 |     /// Array of arguments to the builtin.
 | ||||||
|     /// or 2) and whether they need to be forced. `true` causes the
 |     arguments: &'static [BuiltinArgument], | ||||||
|     /// corresponding argument to be forced before `func` is called.
 |  | ||||||
|     strict_args: &'static [bool], |  | ||||||
|     func: Rc<dyn BuiltinFn>, |     func: Rc<dyn BuiltinFn>, | ||||||
| 
 | 
 | ||||||
|     /// Partially applied function arguments.
 |     /// Partially applied function arguments.
 | ||||||
|  | @ -53,12 +59,12 @@ pub struct Builtin { | ||||||
| impl Builtin { | impl Builtin { | ||||||
|     pub fn new<F: BuiltinFn + 'static>( |     pub fn new<F: BuiltinFn + 'static>( | ||||||
|         name: &'static str, |         name: &'static str, | ||||||
|         strict_args: &'static [bool], |         arguments: &'static [BuiltinArgument], | ||||||
|         func: F, |         func: F, | ||||||
|     ) -> Self { |     ) -> Self { | ||||||
|         Builtin { |         Builtin { | ||||||
|             name, |             name, | ||||||
|             strict_args, |             arguments, | ||||||
|             func: Rc::new(func), |             func: Rc::new(func), | ||||||
|             partials: vec![], |             partials: vec![], | ||||||
|         } |         } | ||||||
|  | @ -74,9 +80,9 @@ impl Builtin { | ||||||
|     pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> { |     pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> { | ||||||
|         self.partials.push(arg); |         self.partials.push(arg); | ||||||
| 
 | 
 | ||||||
|         if self.partials.len() == self.strict_args.len() { |         if self.partials.len() == self.arguments.len() { | ||||||
|             for (idx, force) in self.strict_args.iter().enumerate() { |             for (idx, BuiltinArgument { strict, .. }) in self.arguments.iter().enumerate() { | ||||||
|                 if *force { |                 if *strict { | ||||||
|                     self.partials[idx].force(vm)?; |                     self.partials[idx].force(vm)?; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ use crate::errors::ErrorKind; | ||||||
| use crate::opcode::StackIdx; | use crate::opcode::StackIdx; | ||||||
| use crate::vm::VM; | use crate::vm::VM; | ||||||
| pub use attrs::NixAttrs; | pub use attrs::NixAttrs; | ||||||
| pub use builtin::Builtin; | pub use builtin::{Builtin, BuiltinArgument}; | ||||||
| pub(crate) use function::Formals; | pub(crate) use function::Formals; | ||||||
| pub use function::{Closure, Lambda}; | pub use function::{Closure, Lambda}; | ||||||
| pub use list::NixList; | pub use list::NixList; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue