From e63d14419f5cc2ea1f0d9e9221062c2c8d40fe31 Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Wed, 12 Oct 2022 23:27:09 -0400 Subject: feat(tvix/eval): Record formals on lambda In preparation for both implementing the `functionArgs` builtin and adding support for validating closed formals, record information about the formal arguments to a function *on the Lambda itself*. This may seem a little odd for the purposes of just closed formal checking, but is something we have to have anyway for builtins.functionArgs so I figured I'd do it this way to kill both birds with one stone. Change-Id: Ie3770a607bf352a1eb395c79ca29bb25d5978cd8 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7001 Reviewed-by: tazjin Tested-by: BuildkiteCI --- tvix/eval/src/compiler/mod.rs | 31 ++++++++++++++++++++----------- tvix/eval/src/value/function.rs | 16 ++++++++++++++-- tvix/eval/src/value/mod.rs | 1 + tvix/eval/src/value/string.rs | 7 +++++++ 4 files changed, 42 insertions(+), 13 deletions(-) (limited to 'tvix/eval') diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index b7d2d88530de..259431842956 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -30,7 +30,7 @@ use crate::errors::{Error, ErrorKind, EvalResult}; use crate::observer::CompilerObserver; use crate::opcode::{CodeIdx, Count, JumpOffset, OpCode, UpvalueIdx}; use crate::spans::ToSpan; -use crate::value::{Closure, Lambda, Thunk, Value}; +use crate::value::{Closure, Formals, Lambda, Thunk, Value}; use crate::warnings::{EvalWarning, WarningKind}; use self::scope::{LocalIdx, LocalPosition, Scope, Upvalue, UpvalueKind}; @@ -775,7 +775,7 @@ impl Compiler<'_> { /// many arguments are provided. This is done by emitting a /// special instruction that checks the set of keys from a /// constant containing the expected keys. - fn compile_param_pattern(&mut self, pattern: &ast::Pattern) { + fn compile_param_pattern(&mut self, pattern: &ast::Pattern) -> Formals { let span = self.span_for(pattern); let set_idx = match pattern.pat_bind() { Some(name) => self.declare_local(&name, name.ident().unwrap().to_string()), @@ -792,12 +792,15 @@ impl Compiler<'_> { // then finalise any necessary recursion into the scope. let mut entries: Vec<(LocalIdx, ast::PatEntry)> = vec![]; let mut indices: Vec = vec![]; + let mut arguments = HashMap::default(); for entry in pattern.pat_entries() { let ident = entry.ident().unwrap(); let idx = self.declare_local(&ident, ident.to_string()); + let has_default = entry.default().is_some(); entries.push((idx, entry)); indices.push(idx); + arguments.insert(ident.into(), has_default); } // For each of the bindings, push the set on the stack and @@ -836,15 +839,22 @@ impl Compiler<'_> { // TODO: strictly check if all keys have been consumed if // there is no ellipsis. - if pattern.ellipsis_token().is_none() { + let ellipsis = pattern.ellipsis_token().is_some(); + if !ellipsis { self.emit_warning(pattern, WarningKind::NotImplemented("closed formals")); } + + Formals { + arguments, + ellipsis, + } } fn compile_lambda(&mut self, slot: LocalIdx, node: &ast::Lambda) { - // Compile the function itself - match node.param().unwrap() { - ast::Param::Pattern(pat) => self.compile_param_pattern(&pat), + // Compile the function itself, recording its formal arguments (if any) + // for later use + let formals = match node.param().unwrap() { + ast::Param::Pattern(pat) => Some(self.compile_param_pattern(&pat)), ast::Param::IdentParam(param) => { let name = param @@ -857,10 +867,12 @@ impl Compiler<'_> { let idx = self.declare_local(¶m, &name); self.scope_mut().mark_initialised(idx); + None } - } + }; self.compile(slot, &node.body().unwrap()); + self.context_mut().lambda.formals = formals; } fn thunk(&mut self, outer_slot: LocalIdx, node: &N, content: F) @@ -1005,10 +1017,7 @@ impl Compiler<'_> { /// several operations related to attribute sets, where /// identifiers are used as string keys. fn emit_literal_ident(&mut self, ident: &ast::Ident) { - self.emit_constant( - Value::String(ident.ident_token().unwrap().text().into()), - ident, - ); + self.emit_constant(Value::String(ident.clone().into()), ident); } /// Patch the jump instruction at the given index, setting its diff --git a/tvix/eval/src/value/function.rs b/tvix/eval/src/value/function.rs index 0923e1b1cba9..e58aab19c0e7 100644 --- a/tvix/eval/src/value/function.rs +++ b/tvix/eval/src/value/function.rs @@ -1,6 +1,7 @@ //! This module implements the runtime representation of functions. use std::{ cell::{Ref, RefCell, RefMut}, + collections::HashMap, rc::Rc, }; @@ -9,6 +10,17 @@ use crate::{ upvalues::{UpvalueCarrier, Upvalues}, }; +use super::NixString; + +#[derive(Clone, Debug, PartialEq)] +pub(crate) struct Formals { + /// Map from argument name, to whether that argument is required + pub(crate) arguments: HashMap, + + /// Do the formals of this function accept extra arguments + pub(crate) ellipsis: bool, +} + /// The opcodes for a thunk or closure, plus the number of /// non-executable opcodes which are allowed after an OpClosure or /// OpThunk referencing it. At runtime `Lambda` is usually wrapped @@ -16,7 +28,6 @@ use crate::{ /// quite large). #[derive(Debug, PartialEq)] pub struct Lambda { - // name: Option, pub(crate) chunk: Chunk, /// Number of upvalues which the code in this Lambda closes @@ -24,14 +35,15 @@ pub struct Lambda { /// runtime. Information about the variables is emitted using /// data-carrying opcodes (see [`OpCode::DataLocalIdx`]). pub(crate) upvalue_count: usize, + pub(crate) formals: Option, } impl Lambda { pub fn new_anonymous() -> Self { Lambda { - // name: None, chunk: Default::default(), upvalue_count: 0, + formals: None, } } diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index a1216452fbb5..78f4f5de67a4 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -20,6 +20,7 @@ use crate::opcode::StackIdx; use crate::vm::VM; pub use attrs::NixAttrs; pub use builtin::Builtin; +pub(crate) use function::Formals; pub use function::{Closure, Lambda}; pub use list::NixList; pub use path::canon_path; diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs index d146ee4cec95..4caef653f2cc 100644 --- a/tvix/eval/src/value/string.rs +++ b/tvix/eval/src/value/string.rs @@ -1,5 +1,6 @@ //! This module implements Nix language strings and their different //! backing implementations. +use rnix::ast; use smol_str::SmolStr; use std::ffi::OsStr; use std::hash::Hash; @@ -55,6 +56,12 @@ impl From for NixString { } } +impl From for NixString { + fn from(ident: ast::Ident) -> Self { + ident.ident_token().unwrap().text().into() + } +} + impl Hash for NixString { fn hash(&self, state: &mut H) { self.as_str().hash(state) -- cgit 1.4.1