• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.44
/crates/steel-core/src/values/functions.rs
1
#![allow(unused)]
2

3
use std::{
4
    cell::{Cell, RefCell},
5
    collections::HashMap,
6
    convert::TryFrom,
7
    hash::Hasher,
8
    sync::Arc,
9
};
10

11
use fxhash::FxHashSet;
12

13
use crate::{
14
    core::{instructions::DenseInstruction, opcode::OpCode},
15
    gc::{
16
        shared::{MutContainer, ShareableMut, StandardShared},
17
        Gc, Shared, SharedMut,
18
    },
19
    parser::{parser::SyntaxObjectId, span::Span},
20
    rvals::{
21
        from_serializable_value, into_serializable_value, AsRefSteelVal, Custom, FunctionSignature,
22
        HeapSerializer, IntoSteelVal, MutFunctionSignature, SerializableSteelVal, SteelString,
23
    },
24
    steel_vm::{
25
        register_fn::SendSyncStatic,
26
        vm::{BlockMetadata, BlockPattern, BuiltInSignature},
27
    },
28
    // values::contracts::ContractedFunction,
29
    SteelErr,
30
    SteelVal,
31
};
32

33
use super::{
34
    closed::{Heap, HeapRef},
35
    structs::UserDefinedStruct,
36
};
37

38
// pub(crate) enum Function {
39
//     BoxedFunction(BoxedFunctionSignature),
40
//     Closure(Gc<ByteCodeLambda>),
41
//     FuncV(FunctionSignature),
42
//     ContractedFunction(Gc<ContractedFunction>),
43
//     MutFuncV(MutFunctionSignature),
44
//     Builtin(BuiltInSignature),
45
// }
46

47
// Keep track of this metadata table for getting the docs associated
48
// with a given function?
49
pub struct LambdaMetadataTable {
50
    fn_ptr_table: HashMap<usize, SteelString>,
51
}
52

53
impl Custom for LambdaMetadataTable {}
54

55
impl LambdaMetadataTable {
56
    pub fn new() -> Self {
4✔
57
        Self {
58
            fn_ptr_table: HashMap::new(),
4✔
59
        }
60
    }
61

62
    pub fn add(&mut self, function: SteelVal, doc: SteelString) {
2,349✔
63
        match function {
2,349✔
64
            SteelVal::Closure(b) => {
2,349✔
65
                self.fn_ptr_table.insert(b.id as _, doc);
66
            }
67
            SteelVal::BoxedFunction(b) => {
×
68
                self.fn_ptr_table.insert(Gc::as_ptr(&b) as usize, doc);
×
69
            }
70
            _ => {}
×
71
        }
72
    }
73

74
    pub fn get(&self, function: SteelVal) -> Option<SteelString> {
×
75
        match function {
×
76
            SteelVal::Closure(b) => self.fn_ptr_table.get(&(b.id as _)).cloned(),
×
77
            SteelVal::BoxedFunction(b) => {
×
78
                self.fn_ptr_table.get(&(Gc::as_ptr(&b) as usize)).cloned()
×
79
            }
80
            _ => None,
×
81
        }
82
    }
83

84
    // TODO: This will need to get called in other places
85
    pub fn collect_garbage(&mut self, keep_set: impl Iterator<Item = usize>) {
×
86
        let set = keep_set.collect::<std::collections::HashSet<_>>();
×
87

88
        self.fn_ptr_table.retain(|k, _| set.contains(k));
×
89
    }
90
}
91

92
#[cfg(feature = "inline-captures")]
93
const INLINE_CAPTURE_SIZE: usize = 3;
94

95
#[cfg(not(feature = "inline-captures"))]
96
pub type CaptureVec = Vec<SteelVal>;
97

98
#[cfg(feature = "inline-captures")]
99
pub type CaptureVec = smallvec::SmallVec<[SteelVal; INLINE_CAPTURE_SIZE]>;
100

101
#[derive(Clone, Debug)]
102
pub struct ByteCodeLambda {
103
    pub(crate) id: u32,
104
    /// body of the function with identifiers yet to be bound
105
    #[cfg(feature = "dynamic")]
106
    pub(crate) body_exp: RefCell<Shared<[DenseInstruction]>>,
107

108
    #[cfg(not(feature = "dynamic"))]
109
    pub(crate) body_exp: StandardShared<[DenseInstruction]>,
110

111
    pub(crate) arity: u16,
112

113
    #[cfg(feature = "dynamic")]
114
    call_count: Cell<usize>,
115

116
    pub(crate) is_multi_arity: bool,
117

118
    // Store... some amount inline?
119
    // pub(crate) captures: Vec<SteelVal>,
120
    pub(crate) captures: CaptureVec,
121

122
    // pub(crate) captures: Box<[SteelVal]>
123
    #[cfg(feature = "dynamic")]
124
    pub(crate) blocks: RefCell<Vec<(BlockPattern, BlockMetadata)>>,
125

126
    // This is a little suspicious, but it should give us the necessary information to attach a struct of metadata
127
    #[cfg(feature = "sync")]
128
    contract: SharedMut<Option<Gc<UserDefinedStruct>>>,
129

130
    #[cfg(not(feature = "sync"))]
131
    contract: MutContainer<Option<Gc<UserDefinedStruct>>>,
132
}
133

134
impl PartialEq for ByteCodeLambda {
135
    fn eq(&self, other: &Self) -> bool {
100✔
136
        // self.body_exp == other.body_exp &&
137
        self.arity == other.arity && self.id == other.id
162✔
138
    }
139
}
140

141
impl Eq for ByteCodeLambda {}
142

143
impl std::hash::Hash for ByteCodeLambda {
144
    fn hash<H: Hasher>(&self, state: &mut H) {
1,701✔
145
        self.id.hash(state);
5,103✔
146
        // self.body_exp.as_ptr().hash(state);
147
        self.arity.hash(state);
5,103✔
148

149
        // self.sub_expression_env.as_ptr().hash(state);
150
    }
151
}
152

153
// Can this be moved across threads? What does it cost to execute a closure in another thread?
154
// Engine instances be deep cloned?
155
pub struct SerializedLambda {
156
    pub id: u32,
157
    pub body_exp: Vec<DenseInstruction>,
158
    pub arity: usize,
159
    pub is_multi_arity: bool,
160
    // TODO: Go ahead and create a ThreadSafeSteelVal where we will just deep clone everything, move
161
    // it across the thread, and reconstruct on the other side.
162
    pub captures: Vec<SerializableSteelVal>,
163
}
164

165
#[derive(Clone)]
166
pub struct SerializedLambdaPrototype {
167
    pub id: u32,
168
    pub body_exp: Vec<DenseInstruction>,
169
    pub arity: usize,
170
    pub is_multi_arity: bool,
171
}
172

173
#[derive(Clone, PartialEq, Eq)]
174
pub struct RootedInstructions {
175
    #[cfg(feature = "rooted-instructions")]
176
    inner: *const [DenseInstruction],
177
    #[cfg(not(feature = "rooted-instructions"))]
178
    inner: StandardShared<[DenseInstruction]>,
179
}
180

181
#[cfg(feature = "rooted-instructions")]
182
impl Copy for RootedInstructions {}
183

184
// TODO: Come back to this
185
unsafe impl Send for RootedInstructions {}
186
unsafe impl Sync for RootedInstructions {}
187

188
impl RootedInstructions {
189
    pub fn new(instructions: StandardShared<[DenseInstruction]>) -> Self {
120,679✔
190
        Self {
191
            #[cfg(feature = "rooted-instructions")]
192
            inner: StandardShared::as_ptr(&instructions),
120,679✔
193
            #[cfg(not(feature = "rooted-instructions"))]
194
            inner: instructions,
195
        }
196
    }
197
}
198

199
impl std::fmt::Debug for RootedInstructions {
200
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
201
        write!(f, "{:?}", self.inner)
×
202
    }
203
}
204

205
impl std::ops::Deref for RootedInstructions {
206
    type Target = [DenseInstruction];
207

208
    fn deref(&self) -> &Self::Target {
2,147,483,647✔
209
        #[cfg(feature = "rooted-instructions")]
210
        unsafe {
211
            &(*self.inner)
2,147,483,647✔
212
        }
213

214
        #[cfg(not(feature = "rooted-instructions"))]
215
        &self.inner
216
    }
217
}
218

219
impl ByteCodeLambda {
220
    pub fn new(
123,961✔
221
        id: u32,
222
        body_exp: StandardShared<[DenseInstruction]>,
223
        arity: usize,
224
        is_multi_arity: bool,
225
        captures: CaptureVec,
226
    ) -> ByteCodeLambda {
227
        ByteCodeLambda {
228
            id,
229

230
            #[cfg(feature = "dynamic")]
231
            body_exp: RefCell::new(body_exp),
123,961✔
232
            #[cfg(not(feature = "dynamic"))]
233
            body_exp,
234

235
            arity: arity as u16,
247,922✔
236

237
            #[cfg(feature = "dynamic")]
238
            call_count: Cell::new(0),
123,961✔
239

240
            is_multi_arity,
241
            captures,
242

243
            #[cfg(feature = "sync")]
244
            contract: SharedMut::new(MutContainer::new(None)),
247,922✔
245

246
            #[cfg(not(feature = "sync"))]
247
            contract: MutContainer::new(None),
123,961✔
248

249
            #[cfg(feature = "dynamic")]
250
            blocks: RefCell::new(Vec::new()),
123,961✔
251
        }
252
    }
253

254
    pub(crate) fn from_serialized(heap: &mut HeapSerializer, value: SerializedLambda) -> Self {
×
255
        ByteCodeLambda::new(
256
            value.id,
×
257
            value.body_exp.into(),
×
258
            value.arity,
×
259
            value.is_multi_arity,
×
260
            value
×
261
                .captures
×
262
                .into_iter()
×
263
                .map(|x| from_serializable_value(heap, x))
×
264
                .collect(),
×
265
        )
266
    }
267

268
    pub fn rooted(instructions: StandardShared<[DenseInstruction]>) -> ByteCodeLambda {
60,793✔
269
        Self::new(
270
            SyntaxObjectId::fresh().into(),
121,586✔
271
            instructions,
60,793✔
272
            0,
273
            false,
274
            CaptureVec::default(),
60,793✔
275
        )
276
    }
277

278
    pub fn main(instructions: Vec<DenseInstruction>) -> ByteCodeLambda {
8✔
279
        Self::new(
280
            SyntaxObjectId::fresh().into(),
16✔
281
            instructions.into(),
16✔
282
            0,
283
            false,
284
            CaptureVec::default(),
8✔
285
        )
286
    }
287

288
    pub fn set_captures(&mut self, captures: CaptureVec) {
4,670,141✔
289
        self.captures = captures;
9,340,282✔
290
    }
291

292
    // TODO: The lifecycle of `RootedInstructions` should not be
293
    // beyond the scope of execution. This invariant should in
294
    // general hold - with the exception of continuations, which
295
    // should probably hold on to any functions that are contains
296
    // strongly - so there should be some kind of slot on the continuation
297
    // to hold on to a strong reference to each instruction set.
298
    pub(crate) fn body_exp(&self) -> RootedInstructions {
95,453,483✔
299
        // #[cfg(feature = "dynamic")]
300
        // return Shared::clone(&self.body_exp.borrow());
301

302
        // #[cfg(not(feature = "dynamic"))]
303
        // Shared::clone(&self.body_exp)
304

305
        #[cfg(not(feature = "rooted-instructions"))]
306
        return RootedInstructions {
307
            inner: StandardShared::clone(&self.body_exp),
308
        };
309

310
        #[cfg(feature = "rooted-instructions")]
311
        return RootedInstructions {
95,453,483✔
312
            inner: StandardShared::as_ptr(&self.body_exp),
95,453,483✔
313
        };
314
    }
315

316
    pub fn body_mut_exp(&mut self) -> StandardShared<[DenseInstruction]> {
×
317
        #[cfg(feature = "dynamic")]
318
        return StandardShared::clone(self.body_exp.get_mut());
319

320
        #[cfg(not(feature = "dynamic"))]
321
        StandardShared::clone(&self.body_exp)
×
322
    }
323

324
    // pub fn spans(&self) -> Rc<[Span]> {
325
    //     Rc::clone(&self.spans)
326
    // }
327

328
    // Get the starting index in the instruction set, and the new ID to associate with this
329
    // super instruction set.
330
    // Deep copy the old instruction set, update the new spot to have a dynamic super instruction
331
    // associated with it.
332
    #[cfg(feature = "dynamic")]
333
    pub fn update_to_super_instruction(
334
        &self,
335
        start: usize,
336
        super_instruction_id: usize,
337
    ) -> (DenseInstruction, Shared<[DenseInstruction]>) {
338
        let mut guard = self.body_exp.borrow_mut();
339
        let mut old: Box<[_]> = guard.iter().copied().collect();
340

341
        // set up the head instruction to get returned, we'll need it in the block first
342
        let head_instruction = old[start];
343

344
        // Point to the new super instruction
345
        old[start].op_code = OpCode::DynSuperInstruction;
346
        old[start].payload_size = super_instruction_id as _;
347
        *guard = old.into();
348
        (head_instruction, Shared::clone(&guard))
349
    }
350

351
    #[inline(always)]
UNCOV
352
    pub fn arity(&self) -> usize {
×
UNCOV
353
        self.arity as usize
×
354
    }
355

356
    #[inline(always)]
357
    pub fn is_multi_arity(&self) -> bool {
13,154✔
358
        self.is_multi_arity
13,154✔
359
    }
360

361
    // pub fn heap_allocated(&self) -> &RefCell<Vec<HeapRef<SteelVal>>> {
362
    //     &self.heap_allocated
363
    // }
364

365
    pub fn captures(&self) -> &[SteelVal] {
264,348,428✔
366
        &self.captures
264,348,428✔
367
    }
368

369
    #[cfg(feature = "dynamic")]
370
    #[inline(always)]
371
    pub fn increment_call_count(&self) {
×
372
        // self.call_count += 1;
373
        self.call_count.set(self.call_count.get() + 1);
×
374
    }
375

376
    #[cfg(feature = "dynamic")]
377
    pub fn call_count(&self) -> usize {
378
        self.call_count.get()
379
    }
380

381
    // pub fn set_cannot_be_compiled(&self) {
382
    //     self.cant_be_compiled.set(true)
383
    // }
384

385
    // pub fn has_attempted_to_be_compiled(&self) -> bool {
386
    //     self.cant_be_compiled.get()
387
    // }
388

389
    pub fn attach_contract_information(&self, steel_struct: Gc<UserDefinedStruct>) {
627✔
390
        #[cfg(feature = "sync")]
391
        {
392
            let mut guard = self.contract.write();
1,881✔
393

394
            *guard = Some(steel_struct);
627✔
395
        }
396

397
        #[cfg(not(feature = "sync"))]
398
        {
399
            let mut guard = self.contract.borrow_mut();
400

401
            *guard = Some(steel_struct);
402
        }
403
    }
404

405
    pub fn get_contract_information(&self) -> Option<SteelVal> {
59,241✔
406
        #[cfg(feature = "sync")]
407
        {
408
            self.contract
59,241✔
409
                .read()
410
                .as_ref()
411
                .map(|x| SteelVal::CustomStruct(x.clone()))
60,321✔
412
        }
413

414
        #[cfg(not(feature = "sync"))]
415
        {
416
            self.contract
417
                .borrow()
418
                .as_ref()
419
                .map(|x| SteelVal::CustomStruct(x.clone()))
420
        }
421
    }
422

423
    // pub fn mark_hot(&self) {
424
    //     self.is_hot.set(true)
425
    // }
426

427
    // pub(crate) fn mark_block_tail(&self, pattern: BlockPattern) {
428
    //     self.blocks.borrow_mut();
429
    // }
430

431
    // pub(crate) fn check_tail(&self, pattern: &BlockPattern) -> bool {
432
    //     self.blocks.borrow().contains(pattern)
433
    // }
434

435
    // pub(crate) fn block_tail(&self, block_pattern
436
}
437

438
pub fn attach_contract_struct(args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
627✔
439
    if let SteelVal::Closure(closure) = &args[0] {
1,254✔
440
        if let SteelVal::CustomStruct(s) = &args[1] {
627✔
441
            closure.attach_contract_information(s.clone());
442

443
            Ok(SteelVal::Void)
444
        } else {
445
            stop!(TypeMismatch => "attach-contract-struct! expects a struct in the second position")
×
446
        }
447
    } else {
448
        stop!(TypeMismatch => "attach-contract-struct! expects a function in the first position")
×
449
    }
450
}
451

452
pub fn get_contract(args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
650✔
453
    if let SteelVal::Closure(closure) = &args[0] {
1,110✔
454
        closure.get_contract_information().into_steelval()
455
    } else {
456
        Ok(SteelVal::BoolV(false))
190✔
457

458
        // stop!(TypeMismatch => "get-contract-struct! expects a function in the first position, found: {}", &args[0])
459
    }
460
}
461

462
#[derive(Clone)]
463
#[repr(C)]
464
pub enum StaticOrRcStr {
465
    Static(&'static str),
466
    Owned(Arc<String>),
467
}
468

469
/// This allows cloning the underlying closure, so we can send it across threads.
470
/// It does _not_ solve serializing closures fully, but it does mean we can move function
471
/// pointers across threads, which should be very helpful with spawning native threads.
472
// TODO: @Matt - Replace usage of BoxedDynFunction (and subsequent call sites) with this instead
473
// trait DynamicFunction: Send + Sync {
474
//     #[inline]
475
//     fn call(&self, args: &[SteelVal]) -> crate::rvals::Result<SteelVal>;
476
//     fn clone_box(&self) -> Box<dyn DynamicFunction>;
477
// }
478

479
// // Allow only the capturing of send + sync variables?
480
// impl<F: Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Clone + Send + Sync + 'static>
481
//     DynamicFunction for F
482
// {
483
//     fn call(&self, args: &[SteelVal]) -> crate::rvals::Result<SteelVal> {
484
//         (self)(args)
485
//     }
486

487
//     fn clone_box(&self) -> Box<dyn DynamicFunction> {
488
//         Box::new(self.clone())
489
//     }
490
// }
491

492
// impl Clone for Box<dyn DynamicFunction> {
493
//     fn clone(&self) -> Self {
494
//         self.clone_box()
495
//     }
496
// }
497

498
// pub enum MaybeSendSyncFunction {}
499

500
#[derive(Clone)]
501
#[repr(C)]
502
pub struct BoxedDynFunction {
503
    pub function:
504
        Arc<dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static>,
505
    pub name: Option<Arc<String>>,
506
    pub arity: Option<u32>,
507
}
508

509
impl BoxedDynFunction {
510
    // pub fn spawn_on_thread(self) {
511
    //     std::thread::spawn(move || self.function);
512
    // }
513

514
    pub(crate) fn new(
973✔
515
        function: Arc<
516
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
517
        >,
518
        name: Option<&str>,
519
        arity: Option<u32>,
520
    ) -> Self {
521
        BoxedDynFunction {
522
            function,
523
            name: name.map(|x| Arc::new(x.to_string())),
4,865✔
524
            arity,
525
        }
526
    }
527

528
    pub(crate) fn new_owned(
11,577✔
529
        function: Arc<
530
            dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static,
531
        >,
532
        name: Option<Arc<String>>,
533
        arity: Option<u32>,
534
    ) -> Self {
535
        BoxedDynFunction {
536
            function,
537
            name: name,
538
            arity,
539
        }
540
    }
541

542
    #[inline(always)]
543
    pub fn func(
278,327✔
544
        &self,
545
    ) -> &(dyn Fn(&[SteelVal]) -> crate::rvals::Result<SteelVal> + Send + Sync + 'static) {
546
        self.function.as_ref()
278,327✔
547
    }
548

549
    #[inline(always)]
550
    pub fn get_arity(&self) -> Option<u32> {
×
551
        self.arity
×
552
    }
553

554
    #[inline(always)]
555
    pub fn name(&self) -> Option<&str> {
×
556
        self.name.as_ref().map(|x| x.as_str())
×
557
    }
558
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc