fix(tvix/eval): Inline List.sort_by, and propagate errors
In order to correctly propagate errors in the comparator passed to builtins.sort, we need to do all the sorting in a context where we can short-circuit return `Value`s (because catchables are Values on the `Ok` side of the Result , not `Err`s). Unfortunately this means we have to *inline* the List `sort_by` implementation into the builtin_sort function - fortunately this is the only place that was called so this is relatively low cost. This does that, and adds the requisite `try_value!` invocation to allow us to propagate comparator errors here. As before, this doesn't include tests, primarily since those are coming in the next commit. Change-Id: I8453c3aa2cd82299eae89828e2a2bb118da4cd48 Reviewed-on: https://cl.tvl.fyi/c/depot/+/10754 Tested-by: BuildkiteCI Reviewed-by: raitobezarius <tvl@lahfa.xyz>
This commit is contained in:
		
							parent
							
								
									ddb7bc8d18
								
							
						
					
					
						commit
						8446cd1c8b
					
				
					 2 changed files with 45 additions and 53 deletions
				
			
		| 
						 | 
					@ -87,7 +87,7 @@ mod pure_builtins {
 | 
				
			||||||
    use itertools::Itertools;
 | 
					    use itertools::Itertools;
 | 
				
			||||||
    use os_str_bytes::OsStringBytes;
 | 
					    use os_str_bytes::OsStringBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::{value::PointerEquality, NixContext, NixContextElement};
 | 
					    use crate::{value::PointerEquality, AddContext, NixContext, NixContextElement};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1223,8 +1223,50 @@ mod pure_builtins {
 | 
				
			||||||
    #[builtin("sort")]
 | 
					    #[builtin("sort")]
 | 
				
			||||||
    async fn builtin_sort(co: GenCo, comparator: Value, list: Value) -> Result<Value, ErrorKind> {
 | 
					    async fn builtin_sort(co: GenCo, comparator: Value, list: Value) -> Result<Value, ErrorKind> {
 | 
				
			||||||
        let list = list.to_list()?;
 | 
					        let list = list.to_list()?;
 | 
				
			||||||
        let sorted = list.sort_by(&co, comparator).await?;
 | 
					        let mut len = list.len();
 | 
				
			||||||
        Ok(Value::List(sorted))
 | 
					        let mut data = list.into_inner();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Asynchronous sorting algorithm in which the comparator can make use of
 | 
				
			||||||
 | 
					        // VM requests (required as `builtins.sort` uses comparators written in
 | 
				
			||||||
 | 
					        // Nix).
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // This is a simple, optimised bubble sort implementation. The choice of
 | 
				
			||||||
 | 
					        // algorithm is constrained by the comparator in Nix not being able to
 | 
				
			||||||
 | 
					        // yield equality, and us being unable to use the standard library
 | 
				
			||||||
 | 
					        // implementation of sorting (which is a lot longer, but a lot more
 | 
				
			||||||
 | 
					        // efficient) here.
 | 
				
			||||||
 | 
					        // TODO(amjoseph): Investigate potential impl in Nix code, or Tvix bytecode.
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            let mut new_len = 0;
 | 
				
			||||||
 | 
					            for i in 1..len {
 | 
				
			||||||
 | 
					                if try_value!(
 | 
				
			||||||
 | 
					                    generators::request_force(
 | 
				
			||||||
 | 
					                        &co,
 | 
				
			||||||
 | 
					                        generators::request_call_with(
 | 
				
			||||||
 | 
					                            &co,
 | 
				
			||||||
 | 
					                            comparator.clone(),
 | 
				
			||||||
 | 
					                            [data[i].clone(), data[i - 1].clone()],
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        .await,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    .await
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .as_bool()
 | 
				
			||||||
 | 
					                .context("evaluating comparator in `builtins.sort`")?
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    data.swap(i, i - 1);
 | 
				
			||||||
 | 
					                    new_len = i;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if new_len == 0 {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            len = new_len;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Value::List(data.into()))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[builtin("splitVersion")]
 | 
					    #[builtin("splitVersion")]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,11 +6,6 @@ use imbl::{vector, Vector};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use serde::Deserialize;
 | 
					use serde::Deserialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::generators;
 | 
					 | 
				
			||||||
use crate::generators::GenCo;
 | 
					 | 
				
			||||||
use crate::AddContext;
 | 
					 | 
				
			||||||
use crate::ErrorKind;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use super::thunk::ThunkSet;
 | 
					use super::thunk::ThunkSet;
 | 
				
			||||||
use super::TotalDisplay;
 | 
					use super::TotalDisplay;
 | 
				
			||||||
use super::Value;
 | 
					use super::Value;
 | 
				
			||||||
| 
						 | 
					@ -78,51 +73,6 @@ impl NixList {
 | 
				
			||||||
    pub fn from_vec(vs: Vec<Value>) -> Self {
 | 
					    pub fn from_vec(vs: Vec<Value>) -> Self {
 | 
				
			||||||
        Self(Rc::new(Vector::from_iter(vs)))
 | 
					        Self(Rc::new(Vector::from_iter(vs)))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Asynchronous sorting algorithm in which the comparator can make use of
 | 
					 | 
				
			||||||
    /// VM requests (required as `builtins.sort` uses comparators written in
 | 
					 | 
				
			||||||
    /// Nix).
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This is a simple, optimised bubble sort implementation. The choice of
 | 
					 | 
				
			||||||
    /// algorithm is constrained by the comparator in Nix not being able to
 | 
					 | 
				
			||||||
    /// yield equality, and us being unable to use the standard library
 | 
					 | 
				
			||||||
    /// implementation of sorting (which is a lot longer, but a lot more
 | 
					 | 
				
			||||||
    /// efficient) here.
 | 
					 | 
				
			||||||
    // TODO(amjoseph): Investigate potential impl in Nix code, or Tvix bytecode.
 | 
					 | 
				
			||||||
    pub async fn sort_by(self, co: &GenCo, cmp: Value) -> Result<Self, ErrorKind> {
 | 
					 | 
				
			||||||
        let mut len = self.len();
 | 
					 | 
				
			||||||
        let mut data = self.into_inner();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        loop {
 | 
					 | 
				
			||||||
            let mut new_len = 0;
 | 
					 | 
				
			||||||
            for i in 1..len {
 | 
					 | 
				
			||||||
                if generators::request_force(
 | 
					 | 
				
			||||||
                    co,
 | 
					 | 
				
			||||||
                    generators::request_call_with(
 | 
					 | 
				
			||||||
                        co,
 | 
					 | 
				
			||||||
                        cmp.clone(),
 | 
					 | 
				
			||||||
                        [data[i].clone(), data[i - 1].clone()],
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    .await,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                .await
 | 
					 | 
				
			||||||
                .as_bool()
 | 
					 | 
				
			||||||
                .context("evaluating comparator in `builtins.sort`")?
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    data.swap(i, i - 1);
 | 
					 | 
				
			||||||
                    new_len = i;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if new_len == 0 {
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            len = new_len;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(data.into())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl IntoIterator for NixList {
 | 
					impl IntoIterator for NixList {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue