difftreelog
style fix clippy warnings
in: master
30 files changed
bindings/jrsonnet-web/src/lib.rsdiffbeforeafterboth--- a/bindings/jrsonnet-web/src/lib.rs
+++ b/bindings/jrsonnet-web/src/lib.rs
@@ -65,6 +65,11 @@
}
fn unwrap_val_ref(value: &JsValue) -> Result<<WasmVal as RefFromWasmAbi>::Anchor, JsValue> {
+ #[allow(
+ clippy::cast_sign_loss,
+ clippy::cast_possible_truncation,
+ reason = "defined to be u32"
+ )]
let ptr = get(value, &JsValue::from_str("__wbg_ptr"))
.ok()
.and_then(|v| v.as_f64())
@@ -371,14 +376,14 @@
impl WasmArrValue {
#[wasm_bindgen(getter)]
pub fn length(&self) -> u32 {
- self.arr.len()
+ self.arr.len32()
}
pub fn at(&self, index: u32) -> Result<Option<WasmVal>, JsValue> {
let result = self.state.as_ref().map_or_else(
- || self.arr.get(index),
+ || self.arr.get32(index),
|state| {
let _guard = state.try_enter();
- self.arr.get(index)
+ self.arr.get32(index)
},
);
result
bindings/jsonnet/src/lib.rsdiffbeforeafterboth--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -66,14 +66,12 @@
#[cfg(target_family = "unix")]
{
use std::os::unix::ffi::OsStrExt;
- let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");
- str
+ CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it")
}
#[cfg(not(target_family = "unix"))]
{
let str = input.as_os_str().to_str().expect("bad utf-8");
- let cstr = CString::new(str).expect("input has NUL inside");
- cstr
+ CString::new(str).expect("input has NUL inside")
}
}
cmds/jrb/src/main.rsdiffbeforeafterboth--- a/cmds/jrb/src/main.rs
+++ b/cmds/jrb/src/main.rs
@@ -104,6 +104,7 @@
}
}
+#[allow(clippy::too_many_lines)]
fn main() {
tracing_subscriber::fmt().init();
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/analyze.rs
+++ b/crates/jrsonnet-evaluator/src/analyze.rs
@@ -28,7 +28,10 @@
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
-use crate::error::{format_found, suggest_names};
+use crate::{
+ arr::arridx,
+ error::{format_found, suggest_names},
+};
#[derive(Debug, Clone, Copy)]
#[must_use]
@@ -658,7 +661,7 @@
stack: &'s mut AnalysisStack,
bomb: DropBomb,
}
-impl<'s> PendingBody<'s> {
+impl PendingBody<'_> {
/// After the body is processed, drop the frame's locals and emit any
/// "unused local" warnings.
fn finish(self) {
@@ -704,7 +707,7 @@
.drain(closures.first_in_frame.idx()..)
.collect();
for (i, def) in drained.iter().enumerate().rev() {
- let id = LocalId(closures.first_in_frame.0 + i as u32);
+ let id = LocalId(closures.first_in_frame.0 + arridx(i));
let stack_locals = stack
.local_by_name
.get_mut(&def.name)
@@ -819,7 +822,7 @@
let (this_refs, rest) = refs.split_at(*refs_len);
refs = rest;
let start = next_id;
- next_id += *dest_count as u32;
+ next_id += arridx(*dest_count);
Closure {
references: this_refs,
ids: start..next_id,
@@ -1043,7 +1046,7 @@
}
fn next_local_id(&self) -> LocalId {
- LocalId(self.local_defs.len() as u32)
+ LocalId(arridx(self.local_defs.len()))
}
fn report_error(&mut self, msg: impl Into<String>, span: Option<Span>) {
@@ -1565,7 +1568,7 @@
let mut pending = alloc.finish();
let mut l_binds: Vec<LBind> = Vec::with_capacity(binds.len());
- for (bind, destruct) in binds.iter().zip(destructs.into_iter()) {
+ for (bind, destruct) in binds.iter().zip(destructs) {
let mut value_taint = AnalysisResult::default();
let (value_shape, value) = pending
.stack
@@ -1608,7 +1611,7 @@
let mut pending = alloc.finish();
let mut l_params: Vec<LParam> = Vec::with_capacity(params.exprs.len());
- for (p, destruct) in params.exprs.iter().zip(param_destructs.into_iter()) {
+ for (p, destruct) in params.exprs.iter().zip(param_destructs) {
let mut value_taint = AnalysisResult::default();
let default = p.default.as_ref().map_or_else(
|| None,
crates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -2,6 +2,7 @@
any::Any,
fmt::{self},
num::NonZeroU32,
+ ops::{Bound, RangeBounds},
rc::Rc,
};
@@ -104,20 +105,37 @@
Self::new(RangeArray::new_inclusive(a, b))
}
+ #[inline]
#[must_use]
- pub fn slice(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {
+ pub fn slice(self, range: impl RangeBounds<usize>) -> Self {
+ fn map_bound(start: bool, bound: Bound<&usize>) -> Option<i32> {
+ match bound {
+ Bound::Included(&v) => Some(i32::try_from(v).unwrap_or(i32::MAX)),
+ Bound::Excluded(&v) => Some(
+ i32::try_from(v)
+ .unwrap_or(i32::MAX)
+ .saturating_add(if start { 1 } else { -1 }),
+ ),
+ Bound::Unbounded => None,
+ }
+ }
+ self.slice32(
+ map_bound(true, range.start_bound()),
+ map_bound(false, range.end_bound()),
+ None,
+ )
+ }
+
+ #[must_use]
+ pub fn slice32(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {
let get_idx = |pos: Option<i32>, len: u32, default| match pos {
- #[expect(
- clippy::cast_sign_loss,
- reason = "abs value is used, len is limited to u31"
- )]
Some(v) if v < 0 => len.saturating_add_signed(v),
#[expect(clippy::cast_sign_loss, reason = "abs value is used")]
Some(v) => (v as u32).min(len),
None => default,
};
- let index = get_idx(index, self.len(), 0);
- let end = get_idx(end, self.len(), self.len());
+ let index = get_idx(index, self.len32(), 0);
+ let end = get_idx(end, self.len32(), self.len32());
let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0"));
if index >= end {
@@ -126,24 +144,29 @@
Self::new(SliceArray {
inner: self,
- #[expect(clippy::cast_possible_truncation, reason = "len is limited to u31")]
- from: index as u32,
- #[expect(clippy::cast_possible_truncation, reason = "len is limited to u31")]
- to: end as u32,
+ from: index,
+ to: end,
step: step.get(),
})
}
/// Array length.
- pub fn len(&self) -> u32 {
- self.0.len()
+ #[inline]
+ pub fn len32(&self) -> u32 {
+ self.0.len32()
}
+ pub fn len(&self) -> usize {
+ self.len32() as usize
+ }
+
/// Is array contains no elements?
+ #[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
+ #[inline]
pub fn is_cheap(&self) -> bool {
self.0.is_cheap()
}
@@ -151,24 +174,37 @@
/// Get array element by index, evaluating it, if it is lazy.
///
/// Returns `None` on out-of-bounds condition.
- pub fn get(&self, index: u32) -> Result<Option<Val>> {
- self.0.get(index)
+ #[inline]
+ pub fn get32(&self, index: u32) -> Result<Option<Val>> {
+ self.0.get32(index)
+ }
+
+ pub fn get(&self, index: usize) -> Result<Option<Val>> {
+ let Ok(i) = u32::try_from(index) else {
+ return Ok(None);
+ };
+ self.get32(i)
}
/// Get array element by index, without evaluation.
///
/// Returns `None` on out-of-bounds condition.
- pub fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
- self.0.get_lazy(index)
+ #[inline]
+ pub fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
+ self.0.get_lazy32(index)
+ }
+
+ pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+ u32::try_from(index).ok().and_then(|i| self.get_lazy32(i))
}
pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {
- (0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))
+ (0..self.len32()).map(|i| self.get32(i).transpose().expect("length checked"))
}
/// Iterate over elements, returning lazy values.
pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {
- (0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))
+ (0..self.len32()).map(|i| self.get_lazy32(i).expect("length checked"))
}
/// Return a reversed view on current array.
@@ -201,3 +237,18 @@
Self::new(iter.into_iter().collect::<Vec<_>>())
}
}
+
+/// Checks that the usize does not exceed 4g with debug assertions enabled
+/// Should only be used on values that can't reasonably exceed this value
+#[inline]
+pub(crate) fn arridx(i: usize) -> u32 {
+ #[allow(
+ clippy::cast_possible_truncation,
+ reason = "array indexes never exceed 4g"
+ )]
+ if cfg!(debug_assertions) {
+ u32::try_from(i).expect("4g hard limit")
+ } else {
+ i as u32
+ }
+}
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -9,7 +9,7 @@
use jrsonnet_gcmodule::{Cc, Trace};
use jrsonnet_interner::{IBytes, IStr};
-use super::ArrValue;
+use super::{ArrValue, arridx};
use crate::{
Context, Error, ObjValue, Result, Thunk, Val,
analyze::{ClosureShape, LExpr},
@@ -21,12 +21,12 @@
};
pub trait ArrayLike: Any + Trace + Debug {
- fn len(&self) -> u32;
+ fn len32(&self) -> u32;
fn is_empty(&self) -> bool {
- self.len() == 0
+ self.len32() == 0
}
- fn get(&self, index: u32) -> Result<Option<Val>>;
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>>;
+ fn get32(&self, index: u32) -> Result<Option<Val>>;
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>>;
fn is_cheap(&self) -> bool {
false
@@ -40,15 +40,15 @@
where
T: Any + Trace + Debug + ArrayCheap,
{
- fn len(&self) -> u32 {
+ fn len32(&self) -> u32 {
<T as ArrayCheap>::len(self)
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
Ok(<T as ArrayCheap>::get(self, index))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
<T as ArrayCheap>::get(self, index).map(Thunk::evaluated)
}
@@ -80,16 +80,16 @@
}
}
impl ArrayLike for SliceArray {
- fn len(&self) -> u32 {
+ fn len32(&self) -> u32 {
(self.to - self.from).div_ceil(self.step)
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
- self.inner.get(self.map_idx(index))
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
+ self.inner.get32(self.map_idx(index))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
- self.inner.get_lazy(self.map_idx(index))
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
+ self.inner.get_lazy32(self.map_idx(index))
}
fn is_cheap(&self) -> bool {
@@ -99,7 +99,7 @@
impl ArrayCheap for IBytes {
fn len(&self) -> u32 {
- self.as_slice().len() as u32
+ arridx(self.as_slice().len())
}
fn get(&self, index: u32) -> Option<Val> {
self.as_slice()
@@ -132,11 +132,11 @@
}
}
impl ArrayLike for ExprArray {
- fn len(&self) -> u32 {
- self.cached.borrow().len() as u32
+ fn len32(&self) -> u32 {
+ arridx(self.cached.borrow().len())
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
- if index >= self.len() {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
+ if index >= self.len32() {
return Ok(None);
}
match &self.cached.borrow()[index as usize] {
@@ -157,7 +157,7 @@
self.cached.borrow_mut()[index as usize] = ArrayThunk::Computed(new_value.clone());
Ok(Some(new_value))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
#[derive(Trace)]
struct ExprArrThunk {
expr: ExprArray,
@@ -168,13 +168,13 @@
fn get(&self) -> Result<Self::Output> {
self.expr
- .get(self.index)
+ .get32(self.index)
.transpose()
.expect("index checked")
}
}
- if index >= self.len() {
+ if index >= self.len32() {
return None;
}
match &self.cached.borrow()[index as usize] {
@@ -202,8 +202,8 @@
}
impl ExtendedArray {
pub fn new(a: ArrValue, b: ArrValue) -> Option<Self> {
- let a_len = a.len();
- let b_len = b.len();
+ let a_len = a.len32();
+ let b_len = b.len32();
let len = a_len.checked_add(b_len)?;
Some(Self {
a,
@@ -251,22 +251,22 @@
}
}
impl ArrayLike for ExtendedArray {
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
if self.split > index {
- self.a.get(index)
+ self.a.get32(index)
} else {
- self.b.get(index - self.split)
+ self.b.get32(index - self.split)
}
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
if self.split > index {
- self.a.get_lazy(index)
+ self.a.get_lazy32(index)
} else {
- self.b.get_lazy(index - self.split)
+ self.b.get_lazy32(index - self.split)
}
}
- fn len(&self) -> u32 {
+ fn len32(&self) -> u32 {
self.len
}
@@ -280,18 +280,18 @@
T: IntoUntyped + Trace + fmt::Debug,
for<'a> &'a T: IntoUntyped,
{
- fn len(&self) -> u32 {
+ fn len32(&self) -> u32 {
self.as_slice().len().try_into().unwrap_or(u32::MAX)
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
let Some(elem) = self.as_slice().get(index as usize) else {
return Ok(None);
};
IntoUntyped::into_untyped(elem).map(Some)
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
let elem = self.as_slice().get(index as usize)?;
Some(IntoUntyped::into_lazy_untyped(elem))
}
@@ -343,16 +343,16 @@
#[derive(Debug, Trace)]
pub struct ReverseArray(pub ArrValue);
impl ArrayLike for ReverseArray {
- fn len(&self) -> u32 {
- self.0.len()
+ fn len32(&self) -> u32 {
+ self.0.len32()
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
- self.0.get(self.0.len() - index - 1)
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
+ self.0.get32(self.0.len32() - index - 1)
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
- self.0.get_lazy(self.0.len() - index - 1)
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
+ self.0.get_lazy32(self.0.len32() - index - 1)
}
fn is_cheap(&self) -> bool {
@@ -374,7 +374,7 @@
}
impl MappedArray {
pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {
- let len = inner.len();
+ let len = inner.len32();
Self {
inner,
cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len as usize])),
@@ -389,12 +389,12 @@
}
}
impl ArrayLike for MappedArray {
- fn len(&self) -> u32 {
- self.cached.borrow().len() as u32
+ fn len32(&self) -> u32 {
+ arridx(self.cached.borrow().len())
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
- if index >= self.len() {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
+ if index >= self.len32() {
return Ok(None);
}
match &self.cached.borrow()[index as usize] {
@@ -413,7 +413,7 @@
let val = self
.inner
- .get(index)
+ .get32(index)
.transpose()
.expect("index checked")
.and_then(|r| self.evaluate(index, r));
@@ -428,7 +428,7 @@
self.cached.borrow_mut()[index as usize] = ArrayThunk::Computed(new_value.clone());
Ok(Some(new_value))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
#[derive(Trace)]
struct MappedArrayThunk {
arr: MappedArray,
@@ -438,11 +438,14 @@
type Output = Val;
fn get(&self) -> Result<Self::Output> {
- self.arr.get(self.index).transpose().expect("index checked")
+ self.arr
+ .get32(self.index)
+ .transpose()
+ .expect("index checked")
}
}
- if index >= self.len() {
+ if index >= self.len32() {
return None;
}
match &self.cached.borrow()[index as usize] {
@@ -471,12 +474,12 @@
}
}
impl ArrayLike for MakeArray {
- fn len(&self) -> u32 {
- self.cached.borrow().len() as u32
+ fn len32(&self) -> u32 {
+ arridx(self.cached.borrow().len())
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
- if index >= self.len() {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
+ if index >= self.len32() {
return Ok(None);
}
match &self.cached.borrow()[index as usize] {
@@ -493,7 +496,7 @@
unreachable!()
};
- let val = self.mapper.call(index as u32);
+ let val = self.mapper.call(index);
let new_value = match val {
Ok(v) => v,
@@ -505,7 +508,7 @@
self.cached.borrow_mut()[index as usize] = ArrayThunk::Computed(new_value.clone());
Ok(Some(new_value))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
#[derive(Trace)]
struct MakeArrayThunk {
arr: MakeArray,
@@ -515,11 +518,14 @@
type Output = Val;
fn get(&self) -> Result<Self::Output> {
- self.arr.get(self.index).transpose().expect("index checked")
+ self.arr
+ .get32(self.index)
+ .transpose()
+ .expect("index checked")
}
}
- if index >= self.len() {
+ if index >= self.len32() {
return None;
}
match &self.cached.borrow()[index as usize] {
@@ -543,7 +549,7 @@
}
impl RepeatedArray {
pub fn new(data: ArrValue, repeats: u32) -> Option<Self> {
- let total_len = data.len().checked_mul(repeats)?;
+ let total_len = data.len32().checked_mul(repeats)?;
Some(Self {
data,
repeats,
@@ -554,25 +560,25 @@
if index > self.total_len {
return None;
}
- Some(index % self.data.len())
+ Some(index % self.data.len32())
}
}
impl ArrayLike for RepeatedArray {
- fn len(&self) -> u32 {
+ fn len32(&self) -> u32 {
self.total_len
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
let Some(idx) = self.map_idx(index) else {
return Ok(None);
};
- self.data.get(idx)
+ self.data.get32(idx)
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
let idx = self.map_idx(index)?;
- self.data.get_lazy(idx)
+ self.data.get_lazy32(idx)
}
fn is_cheap(&self) -> bool {
@@ -593,18 +599,18 @@
}
impl ArrayLike for PickObjectValues {
- fn len(&self) -> u32 {
- self.keys.len() as u32
+ fn len32(&self) -> u32 {
+ arridx(self.keys.len())
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
let Some(key) = self.keys.as_slice().get(index as usize) else {
return Ok(None);
};
Ok(Some(self.obj.get_or_bail(key.clone())?))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
let key = self.keys.as_slice().get(index as usize)?;
Some(self.obj.get_lazy_or_bail(key.clone()))
}
@@ -633,11 +639,11 @@
}
impl ArrayLike for PickObjectKeyValues {
- fn len(&self) -> u32 {
- self.keys.len() as u32
+ fn len32(&self) -> u32 {
+ arridx(self.keys.len())
}
- fn get(&self, index: u32) -> Result<Option<Val>> {
+ fn get32(&self, index: u32) -> Result<Option<Val>> {
let Some(key) = self.keys.as_slice().get(index as usize) else {
return Ok(None);
};
@@ -650,7 +656,7 @@
))
}
- fn get_lazy(&self, index: u32) -> Option<Thunk<Val>> {
+ fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {
let key = self.keys.as_slice().get(index as usize)?;
// Nothing can fail in the key part, yet value is still
// lazy-evaluated
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -122,7 +122,7 @@
pub fn enter(self, sup_this: SupThis, build: impl FnOnce(&LocalsFrame, &Context)) -> Context {
let locals = LocalsFrame::new_once(self.n_locals);
let val = Context(Cc::new(ContextInternal {
- captures: self.captures.clone(),
+ captures: self.captures,
locals,
sup_this: Some(sup_this),
}));
crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
@@ -97,7 +97,7 @@
let value_ctx = inner_ctx
.pack_captures_sup_this(self.frame_shape)
.enter(|fill, ctx| {
- fill_letrec_binds(fill, &ctx, self.locals);
+ fill_letrec_binds(fill, ctx, self.locals);
});
evaluate_field_member_static(self.builder, inner_ctx, value_ctx, self.field)
}
@@ -336,6 +336,7 @@
Ok(())
}
+#[allow(clippy::too_many_lines)]
fn evaluate_compspecs(
ctx: Context,
specs: &[LCompSpec],
@@ -381,7 +382,7 @@
for (i, item) in arr.iter().enumerate() {
let item = item?;
let inner_ctx = ctx.pack_captures_sup_this(frame_shape).enter(|fill, ctx| {
- destruct(dst, fill, Thunk::evaluated(item), &ctx);
+ destruct(dst, fill, Thunk::evaluated(item), ctx);
});
evaluate_compspecs(
inner_ctx,
crates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -5,7 +5,7 @@
use crate::{
Context, LocalsFrame, PackedContext, Result, SupThis, Thunk, Unbound, Val,
analyze::{
- ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LExpr, LLocalExpr, LocalSlot,
+ ClosureShape, LBind, LDestruct, LDestructField, LDestructRest, LLocalExpr, LocalSlot,
},
bail,
evaluate::evaluate,
@@ -19,7 +19,7 @@
fill: &LocalsFrame,
value: Thunk<Val>,
- a_ctx: &Context,
+ ctx: &Context,
) {
let min_len = start.len() + end.len();
let has_rest = rest.is_some();
@@ -29,14 +29,14 @@
bail!("expected array");
};
if !has_rest {
- if arr.len() as usize != min_len {
- bail!("expected {} elements, got {}", min_len, arr.len())
+ if arr.len() != min_len {
+ bail!("expected {} elements, got {}", min_len, arr.len32())
}
- } else if (arr.len() as usize) < min_len {
+ } else if arr.len() < min_len {
bail!(
"expected at least {} elements, but array was only {}",
min_len,
- arr.len()
+ arr.len32()
)
}
Ok(arr)
@@ -47,13 +47,13 @@
destruct(
d,
fill,
- Thunk!(move || Ok(full.evaluate()?.get(i as u32)?.expect("length is checked"))),
- a_ctx,
+ Thunk!(move || Ok(full.evaluate()?.get(i)?.expect("length is checked"))),
+ ctx,
);
}
- let start_len = start.len() as u32;
- let end_len = end.len() as u32;
+ let start_len = start.len();
+ let end_len = end.len();
if let Some(LDestructRest::Keep(slot)) = rest {
let full = full.clone();
@@ -62,11 +62,7 @@
Thunk!(move || {
let full = full.evaluate()?;
let to = full.len() - end_len;
- Ok(Val::Arr(full.slice(
- Some(start_len as i32),
- Some(to as i32),
- None,
- )))
+ Ok(Val::Arr(full.slice(start_len..to)))
}),
);
}
@@ -79,10 +75,10 @@
Thunk!(move || {
let full = full.evaluate()?;
Ok(full
- .get(full.len() - end_len + i as u32)?
+ .get(full.len() - end_len + i)?
.expect("length is checked"))
}),
- a_ctx,
+ ctx,
);
}
}
@@ -94,7 +90,7 @@
fill: &LocalsFrame,
value: Thunk<Val>,
- a_ctx: &Context,
+ ctx: &Context,
) {
use jrsonnet_interner::IStr;
use rustc_hash::FxHashSet;
@@ -118,7 +114,7 @@
}
}
if !has_rest {
- let len = obj.len();
+ let len = obj.len32();
if len as usize > field_names.len() {
bail!("too many fields, and rest not found");
}
@@ -142,10 +138,11 @@
for field in fields {
let field_name = field.name.clone();
- let default_thunk: Option<Thunk<Val>> = field
- .default
- .as_ref()
- .map(|(shape, expr)| build_b_thunk(a_ctx, shape, expr.clone()));
+ let default_thunk: Option<Thunk<Val>> = field.default.as_ref().map(|(shape, expr)| {
+ let expr = expr.clone();
+ let env = Context::enter_using(ctx, shape);
+ Thunk!(move || evaluate(env, &expr))
+ });
let field_full = full.clone();
let value_thunk = Thunk!(move || {
@@ -157,7 +154,7 @@
});
if let Some(into) = &field.into {
- destruct(into, fill, value_thunk, a_ctx);
+ destruct(into, fill, value_thunk, ctx);
} else {
unreachable!("analyzer lowers object-destruct shorthands into `into`");
}
@@ -177,21 +174,18 @@
#[cfg(feature = "exp-destruct")]
LDestruct::Object { fields, rest } => destruct_object(fields, rest.as_ref(), fill, value, a_ctx),
}
-}
-
-pub fn build_b_thunk(a_ctx: &Context, shape: &ClosureShape, expr: Rc<LExpr>) -> Thunk<Val> {
- let env = Context::enter_using(a_ctx, shape);
- Thunk!(move || evaluate(env, &expr))
-}
-pub fn build_b_thunk_uno(a_ctx: &Context, shape: Rc<(ClosureShape, LExpr)>) -> Thunk<Val> {
- let env = Context::enter_using(a_ctx, &shape.0);
- Thunk!(move || evaluate(env, &shape.1))
}
pub fn fill_letrec_binds(fill: &LocalsFrame, ctx: &Context, binds: &[LBind]) {
for bind in binds {
- let value_thunk = build_b_thunk(ctx, &bind.value_shape, bind.value.clone());
- destruct(&bind.destruct, fill, value_thunk, ctx);
+ let expr = bind.value.clone();
+ let env = Context::enter_using(ctx, &bind.value_shape);
+ destruct(
+ &bind.destruct,
+ fill,
+ Thunk!(move || evaluate(env, &expr)),
+ ctx,
+ );
}
}
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -7,7 +7,7 @@
use self::{
compspec::{evaluate_arr_comp, evaluate_obj_comp},
- destructure::{build_b_thunk_uno, evaluate_local_expr, evaluate_locals_unbound},
+ destructure::{evaluate_local_expr, evaluate_locals_unbound},
operator::evaluate_binary_op_special,
};
use crate::{
@@ -115,6 +115,7 @@
}
}
+#[allow(clippy::too_many_lines)]
pub fn evaluate(ctx: Context, expr: &LExpr) -> Result<Val> {
Ok(match expr {
LExpr::Null => Val::Null,
@@ -218,7 +219,7 @@
BoundedUsize::from_untyped(v).description("slice step value")
})
.transpose()?;
- Val::from(indexable.slice(start, end, step)?)
+ Val::from(indexable.slice32(start, end, step)?)
}
LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super().ok_or(NoSuperFound)?),
LExpr::Import {
@@ -320,6 +321,7 @@
)
}
+#[allow(clippy::too_many_lines)]
fn evaluate_index(ctx: Context, indexable: &LExpr, parts: &[LIndexPart]) -> Result<Val> {
let mut parts = parts.iter();
let mut indexable = if matches!(indexable, LExpr::Super) {
@@ -394,17 +396,17 @@
if n.fract() > f64::EPSILON {
bail!(FractionalIndex)
}
- let len = arr.len();
+ let len = arr.len32();
if n < 0.0 || n > f64::from(len) {
bail!(ArrayBoundsError(n, len));
}
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
- reason = "n is checked positive"
+ reason = "n is checked range"
)]
let i = n as u32;
- arr.get(i)
+ arr.get32(i)
.with_description_src(loc, || format!("element <{i}> access"))?
.ok_or_else(|| ArrayBoundsError(n, len))?
}
@@ -507,12 +509,13 @@
return Ok(());
};
- let thunk = build_b_thunk_uno(&value_ctx, value.clone());
+ let env = Context::enter_using(&value_ctx, &value.0);
+ let value = value.clone();
builder
.field(name)
.with_add(*plus)
.with_visibility(*visibility)
- .try_thunk(thunk)?;
+ .try_thunk(Thunk!(move || evaluate(env, &value.1)))?;
Ok(())
}
crates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -11,12 +11,10 @@
prepared::{PreparedCall, parse_prepared_builtin_call},
};
use crate::{
- PackedContextSupThis, Result, Thunk, Val,
+ Context, PackedContextSupThis, Result, Thunk, Val,
analyze::LFunction,
- evaluate::{
- destructure::{build_b_thunk, destruct},
- ensure_sufficient_stack, evaluate, evaluate_trivial,
- },
+ arr::arridx,
+ evaluate::{destructure::destruct, ensure_sufficient_stack, evaluate, evaluate_trivial},
function::builtin::BuiltinFunc,
};
@@ -83,7 +81,7 @@
&self.func.params[param_idx].destruct,
fill,
thunk.clone(),
- &ctx,
+ ctx,
);
}
for &(param_idx, arg_idx) in prepared.named() {
@@ -91,15 +89,22 @@
&self.func.params[param_idx].destruct,
fill,
named[arg_idx].clone(),
- &ctx,
+ ctx,
);
}
for ¶m_idx in prepared.defaults() {
let param = &self.func.params[param_idx];
let (shape, expr) = param.default.as_ref().expect("default exists");
- let thunk = build_b_thunk(&ctx, shape, expr.clone());
- destruct(¶m.destruct, fill, thunk, &ctx);
+ let expr = expr.clone();
+ let env = Context::enter_using(ctx, shape);
+
+ destruct(
+ ¶m.destruct,
+ fill,
+ Thunk!(move || evaluate(env, &expr)),
+ ctx,
+ );
}
});
@@ -152,8 +157,8 @@
}
}
/// Amount of non-default required arguments
- pub fn params_len(&self) -> u32 {
- self.params().iter().filter(|p| !p.has_default()).count() as u32
+ pub fn params_len32(&self) -> u32 {
+ arridx(self.params().iter().filter(|p| !p.has_default()).count())
}
/// Function name, as defined in code.
pub fn name(&self) -> IStr {
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -182,7 +182,7 @@
#[cfg(feature = "exp-bigint")]
Self::BigInt(b) => b.serialize(serializer),
Self::Arr(arr) => {
- let mut seq = serializer.serialize_seq(Some(arr.len() as usize))?;
+ let mut seq = serializer.serialize_seq(Some(arr.len()))?;
for (i, element) in arr.iter().enumerate() {
let mut serde_error = None;
in_description_frame(
@@ -203,7 +203,7 @@
seq.end()
}
Self::Obj(obj) => {
- let mut map = serializer.serialize_map(Some(obj.len() as usize))?;
+ let mut map = serializer.serialize_map(Some(obj.len32() as usize))?;
for (field, value) in obj.iter(
#[cfg(feature = "exp-preserve-order")]
true,
crates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth1use std::{2 any::Any,3 cell::{Cell, RefCell},4 clone::Clone,5 cmp::Reverse,6 collections::hash_map::Entry,7 fmt::{self, Debug},8 hash::{Hash, Hasher},9 num::Saturating,10 ops::ControlFlow,11};1213use educe::Educe;14use jrsonnet_gcmodule::{Acyclic, Cc, Trace, Weak, cc_dyn};15use jrsonnet_interner::IStr;16use jrsonnet_ir::Span;17use rustc_hash::{FxHashMap, FxHashSet};1819mod oop;2021pub use jrsonnet_ir::Visibility;22pub use oop::ObjValueBuilder;2324use crate::{25 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,26 arr::{PickObjectKeyValues, PickObjectValues},27 bail,28 error::{ErrorKind::*, suggest_object_fields},29 evaluate::operator::evaluate_add_op,30 identity_hash,31 val::{ArrValue, ThunkValue},32};3334#[cfg(not(feature = "exp-preserve-order"))]35pub mod ordering {36 #![allow(37 // This module works as stub for preserve-order feature38 clippy::unused_self,39 )]4041 use jrsonnet_gcmodule::Trace;4243 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]44 pub struct FieldIndex(());45 impl FieldIndex {46 pub fn absolute(_v: u32) -> Self {47 Self(())48 }49 #[must_use]50 pub const fn next(self) -> Self {51 Self(())52 }53 }5455 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]56 pub struct SuperDepth(());57 impl SuperDepth {58 pub(super) fn deepen(self) {}59 }60}6162#[cfg(feature = "exp-preserve-order")]63pub mod ordering {64 use jrsonnet_gcmodule::Trace;6566 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]67 pub struct FieldIndex(u32);68 impl FieldIndex {69 pub fn absolute(v: u32) -> Self {70 Self(v)71 }72 #[must_use]73 pub fn next(self) -> Self {74 Self(self.0 + 1)75 }76 }7778 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]79 pub struct SuperDepth(u32);80 impl SuperDepth {81 pub(super) fn deepen(&mut self) {82 self.0 += 1;83 }84 }85}8687use ordering::{FieldIndex, SuperDepth};8889#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]90pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);91impl FieldSortKey {92 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {93 Self(Reverse(depth), index)94 }95}9697// 0 - add98// 12 - visibility99#[derive(Clone, Copy, Acyclic)]100pub struct ObjFieldFlags(u8);101impl ObjFieldFlags {102 fn new(add: bool, visibility: Visibility) -> Self {103 let mut v = 0;104 if add {105 v |= 1;106 }107 v |= match visibility {108 Visibility::Normal => 0b000,109 Visibility::Hidden => 0b010,110 Visibility::Unhide => 0b100,111 };112 Self(v)113 }114 pub fn add(&self) -> bool {115 self.0 & 1 != 0116 }117 pub fn visibility(&self) -> Visibility {118 match (self.0 & 0b110) >> 1 {119 0b00 => Visibility::Normal,120 0b01 => Visibility::Hidden,121 0b10 => Visibility::Unhide,122 _ => unreachable!(),123 }124 }125}126impl Debug for ObjFieldFlags {127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {128 f.debug_struct("ObjFieldFlags")129 .field("add", &self.add())130 .field("visibility", &self.visibility())131 .finish()132 }133}134135#[allow(clippy::module_name_repetitions)]136#[derive(Debug, Trace)]137pub struct ObjMember {138 flags: ObjFieldFlags,139 pub invoke: MaybeUnbound,140 pub location: Option<Span>,141}142143cc_dyn!(CcObjectAssertion, ObjectAssertion);144pub trait ObjectAssertion: Trace {145 fn run(&self, sup_this: SupThis) -> Result<()>;146}147148// Field => This149150#[derive(Trace, Debug)]151enum CacheValue {152 Cached(Result<Option<Val>>),153 Pending,154}155156pub type EnumFieldsHandler<'a> =157 dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;158159#[derive(Debug)]160pub enum EnumFields {161 Normal(Visibility),162 Omit(Skip),163}164165#[derive(Trace, Clone)]166pub enum GetFor {167 // Return value168 Final(Val),169 // Continue iterating over cores, add current value to sum stack170 SuperPlus(Val),171 // Ignore the field value, stop at this layer instead172 Omit(#[trace(skip)] Skip),173 NotFound,174}175176#[derive(Acyclic, Clone)]177pub enum FieldVisibility {178 Found(Visibility),179 Omit(Skip),180 NotFound,181}182183#[derive(Acyclic, Clone)]184pub enum HasFieldIncludeHidden {185 Exists,186 NotFound,187 Omit(Skip),188}189190type Skip = Saturating<usize>;191192pub trait ObjectCore: Trace + Any + Debug {193 // If callback returns false, iteration stops, and this call returns false.194 fn enum_fields_core(195 &self,196 super_depth: &mut SuperDepth,197 handler: &mut EnumFieldsHandler<'_>,198 ) -> bool;199200 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;201202 fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor>;203 fn field_visibility_core(&self, field: IStr) -> FieldVisibility;204205 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;206}207208#[derive(Clone, Trace)]209pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);210impl Debug for WeakObjValue {211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {212 f.debug_tuple("WeakObjValue").finish()213 }214}215216impl PartialEq for WeakObjValue {217 fn eq(&self, other: &Self) -> bool {218 Weak::ptr_eq(&self.0, &other.0)219 }220}221222impl Eq for WeakObjValue {}223impl Hash for WeakObjValue {224 fn hash<H: Hasher>(&self, hasher: &mut H) {225 // Safety: usize is POD226 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };227 hasher.write_usize(addr);228 }229}230231cc_dyn!(232 #[derive(Clone, Debug)]233 CcObjectCore, ObjectCore,234 pub fn new() {...}235);236237#[derive(Trace, Educe)]238#[educe(Debug)]239struct ObjValueInner {240 cores: Vec<CcObjectCore>,241 assertions_ran: Cell<bool>,242 has_assertions: bool,243 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,244}245246thread_local! {247 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();248}249fn is_asserting(obj: &ObjValue) -> bool {250 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))251}252/// Returns false if already asserting253fn start_asserting(obj: &ObjValue) -> bool {254 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))255}256fn finish_asserting(obj: &ObjValue) {257 RUNNING_ASSERTIONS.with_borrow_mut(|v| {258 let r = v.remove(obj);259 debug_assert!(260 r,261 "finish_asserting was called before start_asserting or twice"262 );263 });264}265266thread_local! {267 static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {268 cores: vec![],269 assertions_ran: Cell::new(true),270 has_assertions: false,271 value_cache: RefCell::default(),272 }))273}274275#[allow(clippy::module_name_repetitions)]276#[derive(Clone, Trace, Debug, Educe)]277#[educe(PartialEq, Hash, Eq)]278pub struct ObjValue(279 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,280);281282impl ObjValue {283 pub fn empty() -> Self {284 EMPTY_OBJ.with(Clone::clone)285 }286 pub fn is_empty(&self) -> bool {287 self.0.cores.is_empty() || self.len() == 0288 }289}290291#[derive(Trace, Debug)]292pub(crate) struct StandaloneSuperCore {293 sup: CoreIdx,294 this: ObjValue,295}296impl ObjectCore for StandaloneSuperCore {297 fn enum_fields_core(298 &self,299 super_depth: &mut SuperDepth,300 handler: &mut EnumFieldsHandler<'_>,301 ) -> bool {302 self.this.enum_fields_idx(super_depth, handler, self.sup)303 }304305 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {306 if self.this.has_field_include_hidden_idx(name, self.sup) {307 HasFieldIncludeHidden::Exists308 } else {309 HasFieldIncludeHidden::NotFound310 }311 }312313 fn get_for_core(&self, key: IStr, _sup_this: SupThis, omit_only: bool) -> Result<GetFor> {314 if omit_only {315 return Ok(GetFor::NotFound);316 }317 let v = self.this.get_idx(key, self.sup)?;318 Ok(v.map_or(GetFor::NotFound, GetFor::Final))319 }320321 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {322 self.this323 .field_visibility_idx(field, self.sup)324 .map_or(FieldVisibility::NotFound, FieldVisibility::Found)325 }326327 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {328 self.this.run_assertions()329 }330}331332#[derive(Debug, Acyclic)]333struct OmitFieldsCore {334 omit: FxHashSet<IStr>,335 prev_layers: usize,336}337impl ObjectCore for OmitFieldsCore {338 fn enum_fields_core(339 &self,340 super_depth: &mut SuperDepth,341 handler: &mut EnumFieldsHandler<'_>,342 ) -> bool {343 let mut fi = FieldIndex::default();344 for f in &self.omit {345 if handler(346 *super_depth,347 fi,348 f.clone(),349 EnumFields::Omit(Saturating(self.prev_layers)),350 ) == ControlFlow::Break(())351 {352 return false;353 }354 fi = fi.next();355 }356 true357 }358359 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {360 if self.omit.contains(&name) {361 return HasFieldIncludeHidden::Omit(Saturating(self.prev_layers));362 }363 HasFieldIncludeHidden::NotFound364 }365366 fn get_for_core(&self, key: IStr, _sup_this: SupThis, _omit_only: bool) -> Result<GetFor> {367 if self.omit.contains(&key) {368 return Ok(GetFor::Omit(Saturating(self.prev_layers)));369 }370 Ok(GetFor::NotFound)371 }372373 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {374 if self.omit.contains(&field) {375 return FieldVisibility::Omit(Saturating(self.prev_layers));376 }377 FieldVisibility::NotFound378 }379380 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {381 Ok(())382 }383}384385#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]386struct CoreIdx {387 idx: usize,388}389impl CoreIdx {390 fn super_exists(self) -> bool {391 self.idx != 0392 }393}394#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]395pub struct SupThis {396 sup: CoreIdx,397 this: ObjValue,398}399impl SupThis {400 /// Create a `SupThis` for a freshly constructed object (no super).401 pub fn new(this: ObjValue) -> Self {402 Self {403 sup: CoreIdx {404 idx: this.0.cores.len(),405 },406 this,407 }408 }409 pub fn has_super(&self) -> bool {410 self.sup.super_exists()411 }412 /// Implementation of `"field" in super` operation,413 /// works faster than standalone super path.414 ///415 /// In case of no `super` existence, returns false.416 pub fn field_in_super(&self, field: IStr) -> bool {417 self.this.has_field_include_hidden_idx(field, self.sup)418 }419 /// Implementation of `super.field` operation,420 /// works faster than standalone super path.421 ///422 /// In case of no `super` existence, returns `NoSuperFound`423 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {424 if !self.sup.super_exists() {425 bail!(NoSuperFound);426 }427 self.this.get_idx(field, self.sup)428 }429 /// `super` with `self` overriden for top-level lookups.430 /// Exists when super appears outside of `super.field`/`"field" in super` expressions431 /// Exclusive to jrsonnet.432 ///433 /// Returns None if no `super` found434 pub fn standalone_super(&self) -> Option<ObjValue> {435 if !self.sup.super_exists() {436 return None;437 }438 let mut out = ObjValue::builder();439 out.extend_with_core(StandaloneSuperCore {440 sup: self.sup,441 this: self.this.clone(),442 });443 Some(out.build())444 }445 pub fn this(&self) -> &ObjValue {446 &self.this447 }448 pub fn downgrade(self) -> WeakSupThis {449 WeakSupThis {450 sup: self.sup,451 this: self.this.downgrade(),452 }453 }454}455#[derive(Trace, PartialEq, Eq, Hash, Debug)]456pub struct WeakSupThis {457 sup: CoreIdx,458 this: WeakObjValue,459}460461impl ObjValue {462 pub fn builder() -> ObjValueBuilder {463 ObjValueBuilder::new()464 }465 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {466 ObjValueBuilder::with_capacity(capacity)467 }468 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {469 let mut out = ObjValueBuilder::with_capacity(1);470 out.with_super(self);471 let mut member = out.field(key);472 if value.flags.add() {473 member = member.add();474 }475 if let Some(loc) = value.location {476 member = member.with_location(loc);477 }478 let _ = member479 .with_visibility(value.flags.visibility())480 .binding(value.invoke);481 out.build()482 }483 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {484 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())485 }486487 pub fn extend(&mut self) -> ObjValueBuilder {488 let mut out = ObjValueBuilder::new();489 out.with_super(self.clone());490 out491 }492493 #[must_use]494 pub fn extend_from(&self, sup: Self) -> Self {495 let mut cores = Vec::with_capacity(sup.0.cores.len() + self.0.cores.len());496 cores.extend(sup.0.cores.iter().cloned());497 cores.extend(self.0.cores.iter().cloned());498499 let has_assertions = sup.0.has_assertions || self.0.has_assertions;500 ObjValue(Cc::new(ObjValueInner {501 cores,502 value_cache: RefCell::default(),503 assertions_ran: Cell::new(!has_assertions),504 has_assertions,505 }))506 }507 // #[must_use]508 // pub fn with_this(&self, this: Self) -> Self {509 // self.0.with_this(self.clone(), this)510 // }511 /// Returns amount of visible object fields512 /// If object only contains hidden fields - may return zero.513 pub fn len(&self) -> u32 {514 self.fields_visibility()515 .values()516 .filter(|d| d.visible())517 .count() as u32518 }519 /// For each field, calls callback.520 /// If callback returns false - ends iteration prematurely.521 ///522 /// Returns false if ended prematurely523 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {524 let mut super_depth = SuperDepth::default();525 self.enum_fields_idx(526 &mut super_depth,527 handler,528 CoreIdx {529 idx: self.0.cores.len(),530 },531 )532 }533534 fn iter_cores(&self, idx: CoreIdx) -> impl Iterator<Item = &CcObjectCore> {535 self.0.cores.iter().take(idx.idx).rev()536 }537 fn iter_cores_enumerate(&self, idx: CoreIdx) -> impl Iterator<Item = (CoreIdx, &CcObjectCore)> {538 self.0539 .cores540 .iter()541 .take(idx.idx)542 .enumerate()543 .rev()544 .map(|(idx, o)| (CoreIdx { idx }, o))545 }546547 fn enum_fields_idx(548 &self,549 super_depth: &mut SuperDepth,550 handler: &mut EnumFieldsHandler<'_>,551 idx: CoreIdx,552 ) -> bool {553 for core in self.iter_cores(idx) {554 if !core.0.enum_fields_core(super_depth, handler) {555 return false;556 }557 super_depth.deepen();558 }559 true560 }561562 pub fn has_field_include_hidden(&self, name: IStr) -> bool {563 self.has_field_include_hidden_idx(564 name,565 CoreIdx {566 idx: self.0.cores.len(),567 },568 )569 }570 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {571 let mut skip = Saturating(0usize);572 for ele in self.iter_cores(core) {573 match ele.0.has_field_include_hidden_core(name.clone()) {574 HasFieldIncludeHidden::Exists => {575 if skip.0 == 0 {576 return true;577 }578 }579 HasFieldIncludeHidden::Omit(new_skip) => {580 // +1 including this core581 skip = skip.max(new_skip + Saturating(1));582 }583 HasFieldIncludeHidden::NotFound => {}584 }585 skip -= 1;586 }587 false588 }589 pub fn has_field(&self, name: IStr) -> bool {590 match self.field_visibility(name) {591 Some(Visibility::Unhide | Visibility::Normal) => true,592 Some(Visibility::Hidden) | None => false,593 }594 }595 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {596 if include_hidden {597 self.has_field_include_hidden(name)598 } else {599 self.has_field(name)600 }601 }602 pub fn get(&self, key: IStr) -> Result<Option<Val>> {603 self.get_idx(604 key,605 CoreIdx {606 idx: self.0.cores.len(),607 },608 )609 }610611 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {612 let cache_key = (key.clone(), core);613 {614 let mut cache = self.0.value_cache.borrow_mut();615 // entry_ref candidate?616 match cache.entry(cache_key.clone()) {617 Entry::Occupied(v) => match v.get() {618 CacheValue::Cached(v) => return v.clone(),619 CacheValue::Pending => {620 if !is_asserting(self) {621 bail!(InfiniteRecursionDetected);622 }623 }624 },625 Entry::Vacant(v) => {626 v.insert(CacheValue::Pending);627 }628 };629 }630 let result = self.get_idx_uncached(key, core);631 {632 let mut cache = self.0.value_cache.borrow_mut();633 cache.insert(cache_key, CacheValue::Cached(result.clone()));634 }635 result636 }637 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {638 self.run_assertions()?;639 let mut first_add = None;640 let mut add_stack: Vec<Val> = Vec::new();641 let mut skip = Saturating(0);642 for (sup, core) in self.iter_cores_enumerate(core) {643 let sup_this = SupThis {644 sup,645 this: self.clone(),646 };647 match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {648 GetFor::Final(val) if first_add.is_none() => {649 if skip.0 == 0 {650 return Ok(Some(val));651 }652 }653 GetFor::Final(val) => {654 if skip.0 == 0 {655 add_stack.push(val);656 break;657 }658 }659 GetFor::SuperPlus(val) => {660 if skip.0 == 0 {661 if first_add.is_none() {662 first_add = Some(val);663 } else {664 add_stack.push(val);665 }666 }667 }668 GetFor::Omit(new_skip) => {669 skip = skip.max(new_skip + Saturating(1));670 }671 GetFor::NotFound => {}672 }673 skip -= 1;674 }675 let Some(first) = first_add else {676 if add_stack.is_empty() {677 return Ok(None);678 }679 return Ok(Some(add_stack.pop().expect("single element on stack")));680 };681 if add_stack.is_empty() {682 return Ok(Some(first));683 }684 add_stack.insert(0, first);685 let mut values = add_stack.into_iter().rev();686 let init = values.next().expect("at least 2 elements");687688 values689 .try_fold(init, |a, b| evaluate_add_op(&a, &b))690 .map(Some)691 }692693 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {694 let Some(value) = self.get(key.clone())? else {695 let suggestions = suggest_object_fields(self, key.clone());696 bail!(NoSuchField(key, suggestions))697 };698 Ok(value)699 }700701 fn field_visibility(&self, field: IStr) -> Option<Visibility> {702 self.field_visibility_idx(703 field,704 CoreIdx {705 idx: self.0.cores.len(),706 },707 )708 }709 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {710 let mut exists = false;711 let mut skip = Saturating(0usize);712 for ele in self.iter_cores(core) {713 let vis = ele.0.field_visibility_core(field.clone());714 match vis {715 FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {716 if skip.0 == 0 {717 return Some(vis);718 }719 }720 FieldVisibility::Found(Visibility::Normal) => {721 if skip.0 == 0 {722 exists = true;723 }724 }725 FieldVisibility::NotFound => {}726 FieldVisibility::Omit(new_skip) => {727 // +1 including this core728 skip = skip.max(new_skip + Saturating(1));729 }730 }731 skip -= 1;732 }733 exists.then_some(Visibility::Normal)734 }735736 pub fn run_assertions(&self) -> Result<()> {737 if self.0.assertions_ran.get() {738 return Ok(());739 }740 if !start_asserting(self) {741 return Ok(());742 }743 for (idx, ele) in self.0.cores.iter().enumerate() {744 let sup_this = SupThis {745 sup: CoreIdx { idx },746 this: self.clone(),747 };748 ele.0.run_assertions_core(sup_this).inspect_err(|_e| {749 finish_asserting(self);750 })?;751 }752 finish_asserting(self);753 self.0.assertions_ran.set(true);754 Ok(())755 }756757 pub fn iter(758 &self,759 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,760 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {761 let fields = self.fields(762 #[cfg(feature = "exp-preserve-order")]763 preserve_order,764 );765 fields.into_iter().map(|field| {766 (767 field.clone(),768 self.get(field)769 .map(|opt| opt.expect("iterating over keys, field exists")),770 )771 })772 }773 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {774 #[derive(Trace)]775 struct ObjFieldThunk {776 obj: ObjValue,777 key: IStr,778 }779 impl ThunkValue for ObjFieldThunk {780 type Output = Val;781782 fn get(&self) -> Result<Self::Output> {783 self.obj784 .get(self.key.clone())785 .transpose()786 .expect("field existence checked")787 }788 }789790 if !self.has_field_ex(key.clone(), true) {791 return None;792 }793794 Some(Thunk::new(ObjFieldThunk {795 obj: self.clone(),796 key,797 }))798 }799 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {800 #[derive(Trace)]801 struct ObjFieldThunk {802 obj: ObjValue,803 key: IStr,804 }805 impl ThunkValue for ObjFieldThunk {806 type Output = Val;807808 fn get(&self) -> Result<Self::Output> {809 self.obj.get_or_bail(self.key.clone())810 }811 }812813 Thunk::new(ObjFieldThunk {814 obj: self.clone(),815 key,816 })817 }818819 #[allow(dead_code, reason = "used in object ...rest destructuring")]820 pub(crate) fn as_standalone(&self) -> StandaloneSuperCore {821 StandaloneSuperCore {822 sup: CoreIdx {823 idx: self.0.cores.len(),824 },825 this: self.clone(),826 }827 }828 pub fn ptr_eq(a: &Self, b: &Self) -> bool {829 Cc::ptr_eq(&a.0, &b.0)830 }831 pub fn downgrade(self) -> WeakObjValue {832 WeakObjValue(self.0.downgrade())833 }834}835836#[derive(Debug)]837struct FieldVisibilityData {838 omitted_until: Saturating<usize>,839 exists_visible: Option<Visibility>,840 #[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]841 key: FieldSortKey,842}843impl FieldVisibilityData {844 fn visible(&self) -> bool {845 self.exists_visible846 .expect("non-existing fields shall be dropped at the end of fn fields_visibility()")847 .is_visible()848 }849 #[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]850 fn sort_key(&self) -> FieldSortKey {851 self.key852 }853}854855impl ObjValue {856 fn fields_visibility(&self) -> FxHashMap<IStr, FieldVisibilityData> {857 let mut out = FxHashMap::default();858859 let mut super_depth = SuperDepth::default();860 let mut omit_index = Saturating(0);861 for core in self.0.cores.iter().rev() {862 core.0863 .enum_fields_core(&mut super_depth, &mut |depth, index, name, visibility| {864 let entry = out.entry(name);865 let data = entry.or_insert_with(|| FieldVisibilityData {866 exists_visible: None,867 key: FieldSortKey::new(depth, index),868 omitted_until: omit_index,869 });870 match visibility {871 EnumFields::Omit(new_skip) => {872 // +1 including this core873 data.omitted_until = data874 .omitted_until875 .max(omit_index + new_skip + Saturating(1));876 }877 EnumFields::Normal(Visibility::Normal) => {878 if data.omitted_until <= omit_index && data.exists_visible.is_none() {879 data.exists_visible = Some(Visibility::Normal);880 }881 }882 EnumFields::Normal(Visibility::Hidden) => {883 if data.omitted_until <= omit_index {884 data.exists_visible = Some(match data.exists_visible {885 // We're iterating in reverse, later unhide is preserved886 Some(Visibility::Unhide) => Visibility::Unhide,887 _ => Visibility::Hidden,888 });889 }890 }891 EnumFields::Normal(Visibility::Unhide) => {892 if data.omitted_until <= omit_index {893 data.exists_visible = Some(match data.exists_visible {894 // We're iterating in reverse, later hide is preserved895 Some(Visibility::Hidden) => Visibility::Hidden,896 _ => Visibility::Unhide,897 });898 }899 }900 }901 ControlFlow::Continue(())902 });903904 super_depth.deepen();905 omit_index += 1;906 }907908 out.retain(|_, v| v.exists_visible.is_some());909910 out911 }912 pub fn fields_with_visibility(913 &self,914 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,915 ) -> Vec<(IStr, Visibility)> {916 #[cfg(feature = "exp-preserve-order")]917 if preserve_order {918 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self919 .fields_visibility()920 .into_iter()921 .enumerate()922 .map(|(idx, (k, d))| {923 (924 (925 k,926 d.exists_visible.expect("non-existing fields filtered out"),927 ),928 (d.sort_key(), idx),929 )930 })931 .unzip();932 keys.sort_unstable_by_key(|v| v.0);933 for i in 0..fields.len() {934 let x = fields[i].clone();935 let mut j = i;936 loop {937 let k = keys[j].1;938 keys[j].1 = j;939 if k == i {940 break;941 }942 fields[j] = fields[k].clone();943 j = k;944 }945 fields[j] = x;946 }947 return fields;948 }949 let mut fields: Vec<_> = self950 .fields_visibility()951 .into_iter()952 .map(|(k, d)| {953 (954 k,955 d.exists_visible.expect("non-existing fields filtered out"),956 )957 })958 .collect();959 fields.sort_unstable_by(|a, b| a.0.cmp(&b.0));960 fields961 }962 pub fn fields_ex(963 &self,964 include_hidden: bool,965 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,966 ) -> Vec<IStr> {967 #[cfg(feature = "exp-preserve-order")]968 if preserve_order {969 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self970 .fields_visibility()971 .into_iter()972 .filter(|(_, d)| include_hidden || d.visible())973 .enumerate()974 .map(|(idx, (k, d))| (k, (d.sort_key(), idx)))975 .unzip();976 keys.sort_unstable_by_key(|v| v.0);977 // Reorder in-place by resulting indexes978 for i in 0..fields.len() {979 let x = fields[i].clone();980 let mut j = i;981 loop {982 let k = keys[j].1;983 keys[j].1 = j;984 if k == i {985 break;986 }987 fields[j] = fields[k].clone();988 j = k;989 }990 fields[j] = x;991 }992 return fields;993 }994995 let mut fields: Vec<_> = self996 .fields_visibility()997 .into_iter()998 .filter(|(_, d)| include_hidden || d.visible())999 .map(|(k, _)| k)1000 .collect();1001 fields.sort_unstable();1002 fields1003 }1004 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {1005 self.fields_ex(1006 false,1007 #[cfg(feature = "exp-preserve-order")]1008 preserve_order,1009 )1010 }1011 pub fn values_ex(1012 &self,1013 include_hidden: bool,1014 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1015 ) -> ArrValue {1016 ArrValue::new(PickObjectValues::new(1017 self.clone(),1018 self.fields_ex(1019 include_hidden,1020 #[cfg(feature = "exp-preserve-order")]1021 preserve_order,1022 ),1023 ))1024 }1025 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {1026 self.values_ex(1027 false,1028 #[cfg(feature = "exp-preserve-order")]1029 preserve_order,1030 )1031 }1032 pub fn key_values_ex(1033 &self,1034 include_hidden: bool,1035 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1036 ) -> ArrValue {1037 ArrValue::new(PickObjectKeyValues::new(1038 self.clone(),1039 self.fields_ex(1040 include_hidden,1041 #[cfg(feature = "exp-preserve-order")]1042 preserve_order,1043 ),1044 ))1045 }1046 pub fn key_values(1047 &self,1048 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1049 ) -> ArrValue {1050 self.key_values_ex(1051 false,1052 #[cfg(feature = "exp-preserve-order")]1053 preserve_order,1054 )1055 }1056}10571058#[allow(clippy::module_name_repetitions)]1059#[must_use = "value not added unless binding() was called"]1060pub struct ObjMemberBuilder<Kind> {1061 kind: Kind,1062 name: IStr,1063 add: bool,1064 visibility: Visibility,1065 original_index: FieldIndex,1066 location: Option<Span>,1067}10681069#[allow(clippy::missing_const_for_fn)]1070impl<Kind> ObjMemberBuilder<Kind> {1071 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {1072 Self {1073 kind,1074 name,1075 original_index,1076 add: false,1077 visibility: Visibility::Normal,1078 location: None,1079 }1080 }10811082 pub const fn with_add(mut self, add: bool) -> Self {1083 self.add = add;1084 self1085 }1086 pub fn add(self) -> Self {1087 self.with_add(true)1088 }1089 pub fn with_visibility(mut self, visibility: Visibility) -> Self {1090 self.visibility = visibility;1091 self1092 }1093 pub fn hide(self) -> Self {1094 self.with_visibility(Visibility::Hidden)1095 }1096 pub fn with_location(mut self, location: Span) -> Self {1097 self.location = Some(location);1098 self1099 }1100 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, FieldIndex, ObjMember) {1101 (1102 self.kind,1103 self.name,1104 self.original_index,1105 ObjMember {1106 flags: ObjFieldFlags::new(self.add, self.visibility),1107 invoke: binding,1108 location: self.location,1109 },1110 )1111 }1112}11131114pub struct ExtendBuilder<'v>(&'v mut ObjValue);1115impl ObjMemberBuilder<ExtendBuilder<'_>> {1116 pub fn value(self, value: impl Into<Val>) {1117 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1118 }1119 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1120 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1121 }1122 pub fn binding(self, binding: MaybeUnbound) {1123 let (receiver, name, _, member) = self.build_member(binding);1124 let new = receiver.0.clone();1125 *receiver.0 = new.extend_with_raw_member(name, member);1126 }1127}1use std::{2 any::Any,3 cell::{Cell, RefCell},4 clone::Clone,5 cmp::Reverse,6 collections::hash_map::Entry,7 fmt::{self, Debug},8 hash::{Hash, Hasher},9 num::Saturating,10 ops::ControlFlow,11};1213use educe::Educe;14use jrsonnet_gcmodule::{Acyclic, Cc, Trace, Weak, cc_dyn};15use jrsonnet_interner::IStr;16use jrsonnet_ir::Span;17use rustc_hash::{FxHashMap, FxHashSet};1819mod oop;2021pub use jrsonnet_ir::Visibility;22pub use oop::ObjValueBuilder;2324use crate::{25 CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val,26 arr::{PickObjectKeyValues, PickObjectValues, arridx},27 bail,28 error::{ErrorKind::*, suggest_object_fields},29 evaluate::operator::evaluate_add_op,30 identity_hash,31 val::{ArrValue, ThunkValue},32};3334#[cfg(not(feature = "exp-preserve-order"))]35pub mod ordering {36 #![allow(37 // This module works as stub for preserve-order feature38 clippy::unused_self,39 )]4041 use jrsonnet_gcmodule::Trace;4243 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]44 pub struct FieldIndex(());45 impl FieldIndex {46 pub fn absolute(_v: u32) -> Self {47 Self(())48 }49 #[must_use]50 pub const fn next(self) -> Self {51 Self(())52 }53 }5455 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]56 pub struct SuperDepth(());57 impl SuperDepth {58 pub(super) fn deepen(self) {}59 }60}6162#[cfg(feature = "exp-preserve-order")]63pub mod ordering {64 use jrsonnet_gcmodule::Trace;6566 #[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]67 pub struct FieldIndex(u32);68 impl FieldIndex {69 pub fn absolute(v: u32) -> Self {70 Self(v)71 }72 #[must_use]73 pub fn next(self) -> Self {74 Self(self.0 + 1)75 }76 }7778 #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug)]79 pub struct SuperDepth(u32);80 impl SuperDepth {81 pub(super) fn deepen(&mut self) {82 self.0 += 1;83 }84 }85}8687use ordering::{FieldIndex, SuperDepth};8889#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]90pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);91impl FieldSortKey {92 pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {93 Self(Reverse(depth), index)94 }95}9697// 0 - add98// 12 - visibility99#[derive(Clone, Copy, Acyclic)]100pub struct ObjFieldFlags(u8);101impl ObjFieldFlags {102 fn new(add: bool, visibility: Visibility) -> Self {103 let mut v = 0;104 if add {105 v |= 1;106 }107 v |= match visibility {108 Visibility::Normal => 0b000,109 Visibility::Hidden => 0b010,110 Visibility::Unhide => 0b100,111 };112 Self(v)113 }114 pub fn add(&self) -> bool {115 self.0 & 1 != 0116 }117 pub fn visibility(&self) -> Visibility {118 match (self.0 & 0b110) >> 1 {119 0b00 => Visibility::Normal,120 0b01 => Visibility::Hidden,121 0b10 => Visibility::Unhide,122 _ => unreachable!(),123 }124 }125}126impl Debug for ObjFieldFlags {127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {128 f.debug_struct("ObjFieldFlags")129 .field("add", &self.add())130 .field("visibility", &self.visibility())131 .finish()132 }133}134135#[allow(clippy::module_name_repetitions)]136#[derive(Debug, Trace)]137pub struct ObjMember {138 flags: ObjFieldFlags,139 pub invoke: MaybeUnbound,140 pub location: Option<Span>,141}142143cc_dyn!(CcObjectAssertion, ObjectAssertion);144pub trait ObjectAssertion: Trace {145 fn run(&self, sup_this: SupThis) -> Result<()>;146}147148// Field => This149150#[derive(Trace, Debug)]151enum CacheValue {152 Cached(Result<Option<Val>>),153 Pending,154}155156pub type EnumFieldsHandler<'a> =157 dyn FnMut(SuperDepth, FieldIndex, IStr, EnumFields) -> ControlFlow<()> + 'a;158159#[derive(Debug)]160pub enum EnumFields {161 Normal(Visibility),162 Omit(Skip),163}164165#[derive(Trace, Clone)]166pub enum GetFor {167 // Return value168 Final(Val),169 // Continue iterating over cores, add current value to sum stack170 SuperPlus(Val),171 // Ignore the field value, stop at this layer instead172 Omit(#[trace(skip)] Skip),173 NotFound,174}175176#[derive(Acyclic, Clone)]177pub enum FieldVisibility {178 Found(Visibility),179 Omit(Skip),180 NotFound,181}182183#[derive(Acyclic, Clone)]184pub enum HasFieldIncludeHidden {185 Exists,186 NotFound,187 Omit(Skip),188}189190type Skip = Saturating<usize>;191192pub trait ObjectCore: Trace + Any + Debug {193 // If callback returns false, iteration stops, and this call returns false.194 fn enum_fields_core(195 &self,196 super_depth: &mut SuperDepth,197 handler: &mut EnumFieldsHandler<'_>,198 ) -> bool;199200 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden;201202 fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor>;203 fn field_visibility_core(&self, field: IStr) -> FieldVisibility;204205 fn run_assertions_core(&self, sup_this: SupThis) -> Result<()>;206}207208#[derive(Clone, Trace)]209pub struct WeakObjValue(#[trace(skip)] Weak<ObjValueInner>);210impl Debug for WeakObjValue {211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {212 f.debug_tuple("WeakObjValue").finish()213 }214}215216impl PartialEq for WeakObjValue {217 fn eq(&self, other: &Self) -> bool {218 Weak::ptr_eq(&self.0, &other.0)219 }220}221222impl Eq for WeakObjValue {}223impl Hash for WeakObjValue {224 fn hash<H: Hasher>(&self, hasher: &mut H) {225 // Safety: usize is POD226 let addr = unsafe { *std::ptr::addr_of!(self.0).cast() };227 hasher.write_usize(addr);228 }229}230231cc_dyn!(232 #[derive(Clone, Debug)]233 CcObjectCore, ObjectCore,234 pub fn new() {...}235);236237#[derive(Trace, Educe)]238#[educe(Debug)]239struct ObjValueInner {240 cores: Vec<CcObjectCore>,241 assertions_ran: Cell<bool>,242 has_assertions: bool,243 value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,244}245246thread_local! {247 static RUNNING_ASSERTIONS: RefCell<FxHashSet<ObjValue>> = RefCell::default();248}249fn is_asserting(obj: &ObjValue) -> bool {250 RUNNING_ASSERTIONS.with_borrow(|v| v.contains(obj))251}252/// Returns false if already asserting253fn start_asserting(obj: &ObjValue) -> bool {254 RUNNING_ASSERTIONS.with_borrow_mut(|v| v.insert(obj.clone()))255}256fn finish_asserting(obj: &ObjValue) {257 RUNNING_ASSERTIONS.with_borrow_mut(|v| {258 let r = v.remove(obj);259 debug_assert!(260 r,261 "finish_asserting was called before start_asserting or twice"262 );263 });264}265266thread_local! {267 static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {268 cores: vec![],269 assertions_ran: Cell::new(true),270 has_assertions: false,271 value_cache: RefCell::default(),272 }))273}274275#[allow(clippy::module_name_repetitions)]276#[derive(Clone, Trace, Debug, Educe)]277#[educe(PartialEq, Hash, Eq)]278pub struct ObjValue(279 #[educe(PartialEq(method(Cc::ptr_eq)), Hash(method(identity_hash)))] Cc<ObjValueInner>,280);281282impl ObjValue {283 pub fn empty() -> Self {284 EMPTY_OBJ.with(Clone::clone)285 }286 pub fn is_empty(&self) -> bool {287 self.0.cores.is_empty() || self.len() == 0288 }289}290291#[derive(Trace, Debug)]292pub(crate) struct StandaloneSuperCore {293 sup: CoreIdx,294 this: ObjValue,295}296impl ObjectCore for StandaloneSuperCore {297 fn enum_fields_core(298 &self,299 super_depth: &mut SuperDepth,300 handler: &mut EnumFieldsHandler<'_>,301 ) -> bool {302 self.this.enum_fields_idx(super_depth, handler, self.sup)303 }304305 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {306 if self.this.has_field_include_hidden_idx(name, self.sup) {307 HasFieldIncludeHidden::Exists308 } else {309 HasFieldIncludeHidden::NotFound310 }311 }312313 fn get_for_core(&self, key: IStr, _sup_this: SupThis, omit_only: bool) -> Result<GetFor> {314 if omit_only {315 return Ok(GetFor::NotFound);316 }317 let v = self.this.get_idx(key, self.sup)?;318 Ok(v.map_or(GetFor::NotFound, GetFor::Final))319 }320321 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {322 self.this323 .field_visibility_idx(field, self.sup)324 .map_or(FieldVisibility::NotFound, FieldVisibility::Found)325 }326327 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {328 self.this.run_assertions()329 }330}331332#[derive(Debug, Acyclic)]333struct OmitFieldsCore {334 omit: FxHashSet<IStr>,335 prev_layers: usize,336}337impl ObjectCore for OmitFieldsCore {338 fn enum_fields_core(339 &self,340 super_depth: &mut SuperDepth,341 handler: &mut EnumFieldsHandler<'_>,342 ) -> bool {343 let mut fi = FieldIndex::default();344 for f in &self.omit {345 if handler(346 *super_depth,347 fi,348 f.clone(),349 EnumFields::Omit(Saturating(self.prev_layers)),350 ) == ControlFlow::Break(())351 {352 return false;353 }354 fi = fi.next();355 }356 true357 }358359 fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {360 if self.omit.contains(&name) {361 return HasFieldIncludeHidden::Omit(Saturating(self.prev_layers));362 }363 HasFieldIncludeHidden::NotFound364 }365366 fn get_for_core(&self, key: IStr, _sup_this: SupThis, _omit_only: bool) -> Result<GetFor> {367 if self.omit.contains(&key) {368 return Ok(GetFor::Omit(Saturating(self.prev_layers)));369 }370 Ok(GetFor::NotFound)371 }372373 fn field_visibility_core(&self, field: IStr) -> FieldVisibility {374 if self.omit.contains(&field) {375 return FieldVisibility::Omit(Saturating(self.prev_layers));376 }377 FieldVisibility::NotFound378 }379380 fn run_assertions_core(&self, _sup_this: SupThis) -> Result<()> {381 Ok(())382 }383}384385#[derive(Hash, PartialEq, Eq, Trace, Clone, Copy, Debug)]386struct CoreIdx {387 idx: usize,388}389impl CoreIdx {390 fn super_exists(self) -> bool {391 self.idx != 0392 }393}394#[derive(Trace, Clone, PartialEq, Eq, Hash, Debug)]395pub struct SupThis {396 sup: CoreIdx,397 this: ObjValue,398}399impl SupThis {400 /// Create a `SupThis` for a freshly constructed object (no super).401 pub fn new(this: ObjValue) -> Self {402 Self {403 sup: CoreIdx {404 idx: this.0.cores.len(),405 },406 this,407 }408 }409 pub fn has_super(&self) -> bool {410 self.sup.super_exists()411 }412 /// Implementation of `"field" in super` operation,413 /// works faster than standalone super path.414 ///415 /// In case of no `super` existence, returns false.416 pub fn field_in_super(&self, field: IStr) -> bool {417 self.this.has_field_include_hidden_idx(field, self.sup)418 }419 /// Implementation of `super.field` operation,420 /// works faster than standalone super path.421 ///422 /// In case of no `super` existence, returns `NoSuperFound`423 pub fn get_super(&self, field: IStr) -> Result<Option<Val>> {424 if !self.sup.super_exists() {425 bail!(NoSuperFound);426 }427 self.this.get_idx(field, self.sup)428 }429 /// `super` with `self` overriden for top-level lookups.430 /// Exists when super appears outside of `super.field`/`"field" in super` expressions431 /// Exclusive to jrsonnet.432 ///433 /// Returns None if no `super` found434 pub fn standalone_super(&self) -> Option<ObjValue> {435 if !self.sup.super_exists() {436 return None;437 }438 let mut out = ObjValue::builder();439 out.extend_with_core(StandaloneSuperCore {440 sup: self.sup,441 this: self.this.clone(),442 });443 Some(out.build())444 }445 pub fn this(&self) -> &ObjValue {446 &self.this447 }448 pub fn downgrade(self) -> WeakSupThis {449 WeakSupThis {450 sup: self.sup,451 this: self.this.downgrade(),452 }453 }454}455#[derive(Trace, PartialEq, Eq, Hash, Debug)]456pub struct WeakSupThis {457 sup: CoreIdx,458 this: WeakObjValue,459}460461impl ObjValue {462 pub fn builder() -> ObjValueBuilder {463 ObjValueBuilder::new()464 }465 pub fn builder_with_capacity(capacity: usize) -> ObjValueBuilder {466 ObjValueBuilder::with_capacity(capacity)467 }468 pub(crate) fn extend_with_raw_member(self, key: IStr, value: ObjMember) -> Self {469 let mut out = ObjValueBuilder::with_capacity(1);470 out.with_super(self);471 let mut member = out.field(key);472 if value.flags.add() {473 member = member.add();474 }475 if let Some(loc) = value.location {476 member = member.with_location(loc);477 }478 let _ = member479 .with_visibility(value.flags.visibility())480 .binding(value.invoke);481 out.build()482 }483 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {484 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())485 }486487 pub fn extend(&mut self) -> ObjValueBuilder {488 let mut out = ObjValueBuilder::new();489 out.with_super(self.clone());490 out491 }492493 #[must_use]494 pub fn extend_from(&self, sup: Self) -> Self {495 let mut cores = Vec::with_capacity(sup.0.cores.len() + self.0.cores.len());496 cores.extend(sup.0.cores.iter().cloned());497 cores.extend(self.0.cores.iter().cloned());498499 let has_assertions = sup.0.has_assertions || self.0.has_assertions;500 ObjValue(Cc::new(ObjValueInner {501 cores,502 value_cache: RefCell::default(),503 assertions_ran: Cell::new(!has_assertions),504 has_assertions,505 }))506 }507 // #[must_use]508 // pub fn with_this(&self, this: Self) -> Self {509 // self.0.with_this(self.clone(), this)510 // }511 /// Returns amount of visible object fields512 /// If object only contains hidden fields - may return zero.513 pub fn len(&self) -> usize {514 self.fields_visibility()515 .values()516 .filter(|d| d.visible())517 .count()518 }519 pub fn len32(&self) -> u32 {520 arridx(self.len())521 }522 /// For each field, calls callback.523 /// If callback returns false - ends iteration prematurely.524 ///525 /// Returns false if ended prematurely526 pub fn enum_fields(&self, handler: &mut EnumFieldsHandler<'_>) -> bool {527 let mut super_depth = SuperDepth::default();528 self.enum_fields_idx(529 &mut super_depth,530 handler,531 CoreIdx {532 idx: self.0.cores.len(),533 },534 )535 }536537 fn iter_cores(&self, idx: CoreIdx) -> impl Iterator<Item = &CcObjectCore> {538 self.0.cores.iter().take(idx.idx).rev()539 }540 fn iter_cores_enumerate(&self, idx: CoreIdx) -> impl Iterator<Item = (CoreIdx, &CcObjectCore)> {541 self.0542 .cores543 .iter()544 .take(idx.idx)545 .enumerate()546 .rev()547 .map(|(idx, o)| (CoreIdx { idx }, o))548 }549550 fn enum_fields_idx(551 &self,552 super_depth: &mut SuperDepth,553 handler: &mut EnumFieldsHandler<'_>,554 idx: CoreIdx,555 ) -> bool {556 for core in self.iter_cores(idx) {557 if !core.0.enum_fields_core(super_depth, handler) {558 return false;559 }560 super_depth.deepen();561 }562 true563 }564565 pub fn has_field_include_hidden(&self, name: IStr) -> bool {566 self.has_field_include_hidden_idx(567 name,568 CoreIdx {569 idx: self.0.cores.len(),570 },571 )572 }573 fn has_field_include_hidden_idx(&self, name: IStr, core: CoreIdx) -> bool {574 let mut skip = Saturating(0usize);575 for ele in self.iter_cores(core) {576 match ele.0.has_field_include_hidden_core(name.clone()) {577 HasFieldIncludeHidden::Exists => {578 if skip.0 == 0 {579 return true;580 }581 }582 HasFieldIncludeHidden::Omit(new_skip) => {583 // +1 including this core584 skip = skip.max(new_skip + Saturating(1));585 }586 HasFieldIncludeHidden::NotFound => {}587 }588 skip -= 1;589 }590 false591 }592 pub fn has_field(&self, name: IStr) -> bool {593 match self.field_visibility(name) {594 Some(Visibility::Unhide | Visibility::Normal) => true,595 Some(Visibility::Hidden) | None => false,596 }597 }598 pub fn has_field_ex(&self, name: IStr, include_hidden: bool) -> bool {599 if include_hidden {600 self.has_field_include_hidden(name)601 } else {602 self.has_field(name)603 }604 }605 pub fn get(&self, key: IStr) -> Result<Option<Val>> {606 self.get_idx(607 key,608 CoreIdx {609 idx: self.0.cores.len(),610 },611 )612 }613614 fn get_idx(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {615 let cache_key = (key.clone(), core);616 {617 let mut cache = self.0.value_cache.borrow_mut();618 // entry_ref candidate?619 match cache.entry(cache_key.clone()) {620 Entry::Occupied(v) => match v.get() {621 CacheValue::Cached(v) => return v.clone(),622 CacheValue::Pending => {623 if !is_asserting(self) {624 bail!(InfiniteRecursionDetected);625 }626 }627 },628 Entry::Vacant(v) => {629 v.insert(CacheValue::Pending);630 }631 }632 }633 let result = self.get_idx_uncached(key, core);634 {635 let mut cache = self.0.value_cache.borrow_mut();636 cache.insert(cache_key, CacheValue::Cached(result.clone()));637 }638 result639 }640 fn get_idx_uncached(&self, key: IStr, core: CoreIdx) -> Result<Option<Val>> {641 self.run_assertions()?;642 let mut first_add = None;643 let mut add_stack: Vec<Val> = Vec::new();644 let mut skip = Saturating(0);645 for (sup, core) in self.iter_cores_enumerate(core) {646 let sup_this = SupThis {647 sup,648 this: self.clone(),649 };650 match core.0.get_for_core(key.clone(), sup_this, skip.0 != 0)? {651 GetFor::Final(val) if first_add.is_none() => {652 if skip.0 == 0 {653 return Ok(Some(val));654 }655 }656 GetFor::Final(val) => {657 if skip.0 == 0 {658 add_stack.push(val);659 break;660 }661 }662 GetFor::SuperPlus(val) => {663 if skip.0 == 0 {664 if first_add.is_none() {665 first_add = Some(val);666 } else {667 add_stack.push(val);668 }669 }670 }671 GetFor::Omit(new_skip) => {672 skip = skip.max(new_skip + Saturating(1));673 }674 GetFor::NotFound => {}675 }676 skip -= 1;677 }678 let Some(first) = first_add else {679 if add_stack.is_empty() {680 return Ok(None);681 }682 return Ok(Some(add_stack.pop().expect("single element on stack")));683 };684 if add_stack.is_empty() {685 return Ok(Some(first));686 }687 add_stack.insert(0, first);688 let mut values = add_stack.into_iter().rev();689 let init = values.next().expect("at least 2 elements");690691 values692 .try_fold(init, |a, b| evaluate_add_op(&a, &b))693 .map(Some)694 }695696 pub fn get_or_bail(&self, key: IStr) -> Result<Val> {697 let Some(value) = self.get(key.clone())? else {698 let suggestions = suggest_object_fields(self, key.clone());699 bail!(NoSuchField(key, suggestions))700 };701 Ok(value)702 }703704 fn field_visibility(&self, field: IStr) -> Option<Visibility> {705 self.field_visibility_idx(706 field,707 CoreIdx {708 idx: self.0.cores.len(),709 },710 )711 }712 fn field_visibility_idx(&self, field: IStr, core: CoreIdx) -> Option<Visibility> {713 let mut exists = false;714 let mut skip = Saturating(0usize);715 for ele in self.iter_cores(core) {716 let vis = ele.0.field_visibility_core(field.clone());717 match vis {718 FieldVisibility::Found(vis @ (Visibility::Unhide | Visibility::Hidden)) => {719 if skip.0 == 0 {720 return Some(vis);721 }722 }723 FieldVisibility::Found(Visibility::Normal) => {724 if skip.0 == 0 {725 exists = true;726 }727 }728 FieldVisibility::NotFound => {}729 FieldVisibility::Omit(new_skip) => {730 // +1 including this core731 skip = skip.max(new_skip + Saturating(1));732 }733 }734 skip -= 1;735 }736 exists.then_some(Visibility::Normal)737 }738739 pub fn run_assertions(&self) -> Result<()> {740 if self.0.assertions_ran.get() {741 return Ok(());742 }743 if !start_asserting(self) {744 return Ok(());745 }746 for (idx, ele) in self.0.cores.iter().enumerate() {747 let sup_this = SupThis {748 sup: CoreIdx { idx },749 this: self.clone(),750 };751 ele.0.run_assertions_core(sup_this).inspect_err(|_e| {752 finish_asserting(self);753 })?;754 }755 finish_asserting(self);756 self.0.assertions_ran.set(true);757 Ok(())758 }759760 pub fn iter(761 &self,762 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,763 ) -> impl Iterator<Item = (IStr, Result<Val>)> + '_ {764 let fields = self.fields(765 #[cfg(feature = "exp-preserve-order")]766 preserve_order,767 );768 fields.into_iter().map(|field| {769 (770 field.clone(),771 self.get(field)772 .map(|opt| opt.expect("iterating over keys, field exists")),773 )774 })775 }776 pub fn get_lazy(&self, key: IStr) -> Option<Thunk<Val>> {777 #[derive(Trace)]778 struct ObjFieldThunk {779 obj: ObjValue,780 key: IStr,781 }782 impl ThunkValue for ObjFieldThunk {783 type Output = Val;784785 fn get(&self) -> Result<Self::Output> {786 self.obj787 .get(self.key.clone())788 .transpose()789 .expect("field existence checked")790 }791 }792793 if !self.has_field_ex(key.clone(), true) {794 return None;795 }796797 Some(Thunk::new(ObjFieldThunk {798 obj: self.clone(),799 key,800 }))801 }802 pub fn get_lazy_or_bail(&self, key: IStr) -> Thunk<Val> {803 #[derive(Trace)]804 struct ObjFieldThunk {805 obj: ObjValue,806 key: IStr,807 }808 impl ThunkValue for ObjFieldThunk {809 type Output = Val;810811 fn get(&self) -> Result<Self::Output> {812 self.obj.get_or_bail(self.key.clone())813 }814 }815816 Thunk::new(ObjFieldThunk {817 obj: self.clone(),818 key,819 })820 }821822 #[allow(dead_code, reason = "used in object ...rest destructuring")]823 pub(crate) fn as_standalone(&self) -> StandaloneSuperCore {824 StandaloneSuperCore {825 sup: CoreIdx {826 idx: self.0.cores.len(),827 },828 this: self.clone(),829 }830 }831 pub fn ptr_eq(a: &Self, b: &Self) -> bool {832 Cc::ptr_eq(&a.0, &b.0)833 }834 pub fn downgrade(self) -> WeakObjValue {835 WeakObjValue(self.0.downgrade())836 }837}838839#[derive(Debug)]840struct FieldVisibilityData {841 omitted_until: Saturating<usize>,842 exists_visible: Option<Visibility>,843 #[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]844 key: FieldSortKey,845}846impl FieldVisibilityData {847 fn visible(&self) -> bool {848 self.exists_visible849 .expect("non-existing fields shall be dropped at the end of fn fields_visibility()")850 .is_visible()851 }852 #[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]853 fn sort_key(&self) -> FieldSortKey {854 self.key855 }856}857858impl ObjValue {859 fn fields_visibility(&self) -> FxHashMap<IStr, FieldVisibilityData> {860 let mut out = FxHashMap::default();861862 let mut super_depth = SuperDepth::default();863 let mut omit_index = Saturating(0);864 for core in self.0.cores.iter().rev() {865 core.0866 .enum_fields_core(&mut super_depth, &mut |depth, index, name, visibility| {867 let entry = out.entry(name);868 let data = entry.or_insert_with(|| FieldVisibilityData {869 exists_visible: None,870 key: FieldSortKey::new(depth, index),871 omitted_until: omit_index,872 });873 match visibility {874 EnumFields::Omit(new_skip) => {875 // +1 including this core876 data.omitted_until = data877 .omitted_until878 .max(omit_index + new_skip + Saturating(1));879 }880 EnumFields::Normal(Visibility::Normal) => {881 if data.omitted_until <= omit_index && data.exists_visible.is_none() {882 data.exists_visible = Some(Visibility::Normal);883 }884 }885 EnumFields::Normal(Visibility::Hidden) => {886 if data.omitted_until <= omit_index {887 data.exists_visible = Some(match data.exists_visible {888 // We're iterating in reverse, later unhide is preserved889 Some(Visibility::Unhide) => Visibility::Unhide,890 _ => Visibility::Hidden,891 });892 }893 }894 EnumFields::Normal(Visibility::Unhide) => {895 if data.omitted_until <= omit_index {896 data.exists_visible = Some(match data.exists_visible {897 // We're iterating in reverse, later hide is preserved898 Some(Visibility::Hidden) => Visibility::Hidden,899 _ => Visibility::Unhide,900 });901 }902 }903 }904 ControlFlow::Continue(())905 });906907 super_depth.deepen();908 omit_index += 1;909 }910911 out.retain(|_, v| v.exists_visible.is_some());912913 out914 }915 pub fn fields_with_visibility(916 &self,917 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,918 ) -> Vec<(IStr, Visibility)> {919 #[cfg(feature = "exp-preserve-order")]920 if preserve_order {921 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self922 .fields_visibility()923 .into_iter()924 .enumerate()925 .map(|(idx, (k, d))| {926 (927 (928 k,929 d.exists_visible.expect("non-existing fields filtered out"),930 ),931 (d.sort_key(), idx),932 )933 })934 .unzip();935 keys.sort_unstable_by_key(|v| v.0);936 for i in 0..fields.len() {937 let x = fields[i].clone();938 let mut j = i;939 loop {940 let k = keys[j].1;941 keys[j].1 = j;942 if k == i {943 break;944 }945 fields[j] = fields[k].clone();946 j = k;947 }948 fields[j] = x;949 }950 return fields;951 }952 let mut fields: Vec<_> = self953 .fields_visibility()954 .into_iter()955 .map(|(k, d)| {956 (957 k,958 d.exists_visible.expect("non-existing fields filtered out"),959 )960 })961 .collect();962 fields.sort_unstable_by(|a, b| a.0.cmp(&b.0));963 fields964 }965 pub fn fields_ex(966 &self,967 include_hidden: bool,968 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,969 ) -> Vec<IStr> {970 #[cfg(feature = "exp-preserve-order")]971 if preserve_order {972 let (mut fields, mut keys): (Vec<_>, Vec<_>) = self973 .fields_visibility()974 .into_iter()975 .filter(|(_, d)| include_hidden || d.visible())976 .enumerate()977 .map(|(idx, (k, d))| (k, (d.sort_key(), idx)))978 .unzip();979 keys.sort_unstable_by_key(|v| v.0);980 // Reorder in-place by resulting indexes981 for i in 0..fields.len() {982 let x = fields[i].clone();983 let mut j = i;984 loop {985 let k = keys[j].1;986 keys[j].1 = j;987 if k == i {988 break;989 }990 fields[j] = fields[k].clone();991 j = k;992 }993 fields[j] = x;994 }995 return fields;996 }997998 let mut fields: Vec<_> = self999 .fields_visibility()1000 .into_iter()1001 .filter(|(_, d)| include_hidden || d.visible())1002 .map(|(k, _)| k)1003 .collect();1004 fields.sort_unstable();1005 fields1006 }1007 pub fn fields(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Vec<IStr> {1008 self.fields_ex(1009 false,1010 #[cfg(feature = "exp-preserve-order")]1011 preserve_order,1012 )1013 }1014 pub fn values_ex(1015 &self,1016 include_hidden: bool,1017 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1018 ) -> ArrValue {1019 ArrValue::new(PickObjectValues::new(1020 self.clone(),1021 self.fields_ex(1022 include_hidden,1023 #[cfg(feature = "exp-preserve-order")]1024 preserve_order,1025 ),1026 ))1027 }1028 pub fn values(&self, #[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> ArrValue {1029 self.values_ex(1030 false,1031 #[cfg(feature = "exp-preserve-order")]1032 preserve_order,1033 )1034 }1035 pub fn key_values_ex(1036 &self,1037 include_hidden: bool,1038 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1039 ) -> ArrValue {1040 ArrValue::new(PickObjectKeyValues::new(1041 self.clone(),1042 self.fields_ex(1043 include_hidden,1044 #[cfg(feature = "exp-preserve-order")]1045 preserve_order,1046 ),1047 ))1048 }1049 pub fn key_values(1050 &self,1051 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,1052 ) -> ArrValue {1053 self.key_values_ex(1054 false,1055 #[cfg(feature = "exp-preserve-order")]1056 preserve_order,1057 )1058 }1059}10601061#[allow(clippy::module_name_repetitions)]1062#[must_use = "value not added unless binding() was called"]1063pub struct ObjMemberBuilder<Kind> {1064 kind: Kind,1065 name: IStr,1066 add: bool,1067 visibility: Visibility,1068 original_index: FieldIndex,1069 location: Option<Span>,1070}10711072#[allow(clippy::missing_const_for_fn)]1073impl<Kind> ObjMemberBuilder<Kind> {1074 pub(crate) fn new(kind: Kind, name: IStr, original_index: FieldIndex) -> Self {1075 Self {1076 kind,1077 name,1078 original_index,1079 add: false,1080 visibility: Visibility::Normal,1081 location: None,1082 }1083 }10841085 pub const fn with_add(mut self, add: bool) -> Self {1086 self.add = add;1087 self1088 }1089 pub fn add(self) -> Self {1090 self.with_add(true)1091 }1092 pub fn with_visibility(mut self, visibility: Visibility) -> Self {1093 self.visibility = visibility;1094 self1095 }1096 pub fn hide(self) -> Self {1097 self.with_visibility(Visibility::Hidden)1098 }1099 pub fn with_location(mut self, location: Span) -> Self {1100 self.location = Some(location);1101 self1102 }1103 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, FieldIndex, ObjMember) {1104 (1105 self.kind,1106 self.name,1107 self.original_index,1108 ObjMember {1109 flags: ObjFieldFlags::new(self.add, self.visibility),1110 invoke: binding,1111 location: self.location,1112 },1113 )1114 }1115}11161117pub struct ExtendBuilder<'v>(&'v mut ObjValue);1118impl ObjMemberBuilder<ExtendBuilder<'_>> {1119 pub fn value(self, value: impl Into<Val>) {1120 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value.into())));1121 }1122 pub fn bindable(self, bindable: impl Unbound<Bound = Val>) {1123 self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)));1124 }1125 pub fn binding(self, binding: MaybeUnbound) {1126 let (receiver, name, _, member) = self.build_member(binding);1127 let new = receiver.0.clone();1128 *receiver.0 = new.extend_with_raw_member(name, member);1129 }1130}crates/jrsonnet-evaluator/src/stack.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/stack.rs
+++ b/crates/jrsonnet-evaluator/src/stack.rs
@@ -11,7 +11,7 @@
struct NightlyLocalKey<T>(pub T);
#[cfg(nightly)]
impl<T> NightlyLocalKey<T> {
- #[inline(always)]
+ #[inline]
fn with<U>(&self, v: impl FnOnce(&T) -> U) -> U {
v(&self.0)
}
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -197,7 +197,7 @@
w = align
)?;
} else {
- write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;
+ write!(out, "{:<p$}{}", "", el.desc, p = self.padding)?;
}
}
Ok(())
@@ -258,6 +258,7 @@
}
#[cfg(feature = "explaining-traces")]
impl TraceFormat for HiDocFormat {
+ #[allow(clippy::too_many_lines)]
fn write_trace(&self, out: &mut dyn fmt::Write, error: &Error) -> Result<(), fmt::Error> {
struct ResetData {
loc: Span,
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -637,7 +637,7 @@
}
<Self as Typed>::TYPE.check(&value)?;
// Any::downcast_ref::<ByteArray>(&a);
- let mut out = Vec::with_capacity(a.len() as usize);
+ let mut out = Vec::with_capacity(a.len());
for e in a.iter() {
let r = e?;
out.push(u8::from_untyped(r)?);
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -277,7 +277,7 @@
/// For strings, will create a copy of specified interval.
///
/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.
- pub fn slice(
+ pub fn slice32(
self,
index: Option<i32>,
end: Option<i32>,
@@ -321,7 +321,7 @@
.into(),
))
}
- Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice(
+ Self::Arr(arr) => Ok(Self::Arr(arr.clone().slice32(
index,
end,
#[expect(
@@ -658,7 +658,7 @@
if ArrValue::ptr_eq(a, b) {
return Ok(true);
}
- if a.len() != b.len() {
+ if a.len32() != b.len32() {
return Ok(false);
}
for (a, b) in a.iter().zip(b.iter()) {
crates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-formatter/src/lib.rs
+++ b/crates/jrsonnet-formatter/src/lib.rs
@@ -477,8 +477,7 @@
&mut out,
);
- let mut compspecs = compspecs.into_iter().peekable();
- while let Some(mem) = compspecs.next() {
+ for mem in compspecs {
if mem.should_start_with_newline {
p!(out, nl);
}
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -273,19 +273,15 @@
Expr::ArrComp(Box::new(expr), specs)
}
pub rule number_expr(s: &ParserSettings) -> Expr
- = n:number() {? if let Some(n) = NumValue::new(n) {
- Ok(Expr::Num(n))
- } else {
- Err("!!!numbers are finite")
- }}
+ = n:number() {? NumValue::new(n).map_or_else(|| Err("!!!numbers are finite"), |n| Ok(Expr::Num(n)))}
rule spanned<T: Acyclic>(x: rule<T>, s: &ParserSettings) -> Spanned<T>
- = a:position!() n:x() b:position!() { Spanned::new(n, Span(s.source.clone(), a as u32, b as u32)) }
+ = a:position!() n:x() b:position!() { Spanned::new(n, Span(s.source.clone(), codeidx(a), codeidx(b))) }
pub rule var_expr(s: &ParserSettings) -> Expr
= n:spanned(<id()>, s) { Expr::Var(n) }
pub rule id_loc(s: &ParserSettings) -> Spanned<Expr>
- = a:position!() n:id() b:position!() { Spanned::new(Expr::Str(n), Span(s.source.clone(), a as u32,b as u32)) }
+ = a:position!() n:id() b:position!() { Spanned::new(Expr::Str(n), Span(s.source.clone(), codeidx(a), codeidx(b))) }
pub rule if_then_else_expr(s: &ParserSettings) -> Expr
= cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse(Box::new(IfElse{
cond,
@@ -421,6 +417,10 @@
}
}
+fn codeidx(i: usize) -> u32 {
+ u32::try_from(i).expect("code has 4g hard limit")
+}
+
pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
pub fn parse(str: &str, settings: &ParserSettings) -> Result<Expr, ParseError> {
jsonnet_parser::jsonnet(str, settings)
@@ -428,7 +428,10 @@
/// Used for importstr values
pub fn string_to_expr(str: IStr, settings: &ParserSettings) -> Spanned<Expr> {
let len = str.len();
- Spanned::new(Expr::Str(str), Span(settings.source.clone(), 0, len as u32))
+ Spanned::new(
+ Expr::Str(str),
+ Span(settings.source.clone(), 0, codeidx(len)),
+ )
}
#[cfg(test)]
crates/jrsonnet-pkg/src/install/accessor.rsdiffbeforeafterboth--- a/crates/jrsonnet-pkg/src/install/accessor.rs
+++ b/crates/jrsonnet-pkg/src/install/accessor.rs
@@ -66,6 +66,10 @@
Ok(Some(out))
}
#[allow(clippy::significant_drop_tightening, reason = "false-positive")]
+ #[allow(
+ clippy::iter_not_returning_iterator,
+ reason = "idk for a better name, it is still inner iteration"
+ )]
pub fn iter<E>(
&self,
subdir: &SubDir,
crates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -226,12 +226,12 @@
self.nth_at(0, kind)
}
pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
- if n == 0 {
- if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {
- let kinds = kinds.with(kind);
- self.expected_syntax_tracking_state
- .set(ExpectedSyntax::Unnamed(kinds));
- }
+ if n == 0
+ && let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get()
+ {
+ let kinds = kinds.with(kind);
+ self.expected_syntax_tracking_state
+ .set(ExpectedSyntax::Unnamed(kinds));
}
self.nth(n) == kind
}
crates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -52,7 +52,7 @@
step: Option<Option<BoundedUsize<1, { i32::MAX as usize }>>>,
) -> Result<Val> {
indexable
- .slice(index.flatten(), end.flatten(), step.flatten())
+ .slice32(index.flatten(), end.flatten(), step.flatten())
.map(Val::from)
}
@@ -204,14 +204,14 @@
let item = item?.clone();
if let Val::Arr(items) = item {
if !first {
- out.reserve(joiner_items.len() as usize);
+ out.reserve(joiner_items.len());
// TODO: extend
for item in joiner_items.iter() {
out.push(item?);
}
}
first = false;
- out.reserve(items.len() as usize);
+ out.reserve(items.len());
for item in items.iter() {
out.push(item?);
}
@@ -372,10 +372,10 @@
#[builtin]
pub fn builtin_remove_at(arr: ArrValue, at: i32) -> Result<ArrValue> {
- let newArrLeft = arr.clone().slice(None, Some(at), None);
- let newArrRight = arr.slice(Some(at + 1), None, None);
+ let newArrLeft = arr.clone().slice32(None, Some(at), None);
+ let newArrRight = arr.slice32(Some(at + 1), None, None);
- Ok(ArrValue::extended(newArrLeft, newArrRight).ok_or_else(|| error!("array is too large"))?)
+ ArrValue::extended(newArrLeft, newArrRight).ok_or_else(|| error!("array is too large"))
}
#[builtin]
crates/jrsonnet-stdlib/src/manifest/xml.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/xml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/xml.rs
@@ -44,15 +44,15 @@
bail!("JSONML value should have tag (array length should be >=1)");
}
let tag = String::from_untyped(
- arr.get(0)
+ arr.get32(0)
.description("getting JSONML tag")?
.expect("length checked"),
)
.description("parsing JSONML tag")?;
- let (has_attrs, attrs) = if arr.len() >= 2 {
+ let (has_attrs, attrs) = if arr.len32() >= 2 {
let maybe_attrs = arr
- .get(1)
+ .get32(1)
.with_description(|| "getting JSONML attrs")?
.expect("length checked");
if let Val::Obj(attrs) = maybe_attrs {
@@ -68,13 +68,7 @@
attrs,
children: in_description_frame(
|| "parsing children".to_owned(),
- || {
- FromUntyped::from_untyped(Val::Arr(arr.slice(
- Some(if has_attrs { 2 } else { 1 }),
- None,
- None,
- )))
- },
+ || FromUntyped::from_untyped(Val::Arr(arr.slice(if has_attrs { 2 } else { 1 }..))),
)?,
})
}
crates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -16,10 +16,10 @@
pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> u32 {
use Either4::*;
match x {
- A(x) => x.chars().count() as u32,
- B(x) => x.len(),
- C(x) => x.len(),
- D(f) => f.params_len(),
+ A(x) => u32::try_from(x.chars().count()).expect("4g limit"),
+ B(x) => x.len32(),
+ C(x) => x.len32(),
+ D(f) => f.params_len32(),
}
}
@@ -102,7 +102,7 @@
} else if b.len() == a.len() {
return equals(&Val::Arr(a), &Val::Arr(b));
}
- for (a, b) in a.iter().take(b.len() as usize).zip(b.iter()) {
+ for (a, b) in a.iter().take(b.len()).zip(b.iter()) {
let a = a?;
let b = b?;
if !equals(&a, &b)? {
@@ -127,7 +127,7 @@
return equals(&Val::Arr(a), &Val::Arr(b));
}
let a_len = a.len();
- for (a, b) in a.iter().skip((a_len - b.len()) as usize).zip(b.iter()) {
+ for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {
let a = a?;
let b = b?;
if !equals(&a, &b)? {
crates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -8,13 +8,13 @@
#[allow(non_snake_case)]
pub fn builtin_set_member(x: Thunk<Val>, arr: ArrValue, #[default] keyF: KeyF) -> Result<bool> {
let mut low = 0;
- let mut high = arr.len();
+ let mut high = arr.len32();
let x = keyF.eval(x)?;
while low < high {
let middle = u32::midpoint(high, low);
- let comp = keyF.eval(arr.get_lazy(middle).expect("in bounds"))?;
+ let comp = keyF.eval(arr.get_lazy32(middle).expect("in bounds"))?;
match Val::try_cmp(&comp, &x)? {
Ordering::Less => low = middle + 1,
Ordering::Equal => return Ok(true),
crates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -69,7 +69,7 @@
fn sort_keyf(values: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {
// Slow path, user provided key getter
- let mut vk = Vec::with_capacity(values.len() as usize);
+ let mut vk = Vec::with_capacity(values.len());
for value in values.iter_lazy() {
vk.push((value.clone(), keyf.eval(value)?));
}
@@ -137,7 +137,7 @@
fn uniq_keyf(arr: ArrValue, keyf: KeyF) -> Result<Vec<Thunk<Val>>> {
let mut out = Vec::new();
- let last_value = arr.get_lazy(0).unwrap();
+ let last_value = arr.get_lazy32(0).unwrap();
let mut last_key = keyf.eval(last_value.clone())?;
out.push(last_value);
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -103,10 +103,8 @@
Self::BoundedNumber(a, b) => write!(
f,
"BoundedNumber<{}, {}>",
- a.map(|e| e.to_string())
- .unwrap_or_else(|| "open".to_owned()),
- b.map(|e| e.to_string())
- .unwrap_or_else(|| "open".to_owned())
+ a.map_or_else(|| "open".to_owned(), |e| e.to_string()),
+ b.map_or_else(|| "open".to_owned(), |e| e.to_string())
)?,
Self::ArrayRef(a) => print_array(a, f)?,
Self::Array(a) => print_array(a, f)?,
tests/tests/cpp_test_suite.rsdiffbeforeafterboth--- a/tests/tests/cpp_test_suite.rs
+++ b/tests/tests/cpp_test_suite.rs
@@ -60,7 +60,7 @@
let _entered = s.enter();
let trace_format = CompactFormat {
- resolver: resolver.clone(),
+ resolver,
max_trace: 20,
padding: 4,
};
xtask/src/bench.rsdiffbeforeafterboth--- a/xtask/src/bench.rs
+++ b/xtask/src/bench.rs
@@ -91,6 +91,10 @@
let start = Instant::now();
let child = cmd.spawn()?;
+ #[allow(
+ clippy::cast_possible_wrap,
+ reason = "it is signed, but libc didn't set unsigned for it"
+ )]
let pid = child.id() as libc::pid_t;
// We'll reap via wait4 ourselves; don't let std touch this handle again.
mem::forget(child);
@@ -133,10 +137,10 @@
);
eprintln!(
" max_rss: {} ± {} KiB [{}..{}]",
- r.max_rss_kib.mean as i64,
- r.max_rss_kib.stddev as i64,
- r.max_rss_kib.min as i64,
- r.max_rss_kib.max as i64,
+ r.max_rss_kib.mean.trunc(),
+ r.max_rss_kib.stddev.trunc(),
+ r.max_rss_kib.min.trunc(),
+ r.max_rss_kib.max.trunc(),
);
Ok(())
}
xtask/src/sourcegen/mod.rsdiffbeforeafterboth--- a/xtask/src/sourcegen/mod.rs
+++ b/xtask/src/sourcegen/mod.rs
@@ -113,6 +113,7 @@
Ok(())
}
+#[allow(clippy::too_many_lines)]
fn generate_syntax_kinds(kinds: &KindsSrc, grammar: &AstSrc, lexer: bool) -> Result<String> {
let t_macros = kinds.tokens().filter_map(TokenKind::expand_t_macros);
let token_kinds = kinds.tokens().map(|t| t.expand_kind(lexer));