Skip to content

Commit 570d50c

Browse files
authored
no_std for common, pylib, codegen (RustPython#7056)
* `std` feature for common - Gate OS-dependent modules behind `#[cfg(feature = "std")]` - Replace `std::f64` with `core::f64` in float_ops - Replace `std::process::abort` with panic in refcount - Remove `thread_local` from levenshtein (stack buffer) - Split static_cell into threading/non_threading/no_std * `std` for codegen * `no_std` for pylib
1 parent c974b77 commit 570d50c

10 files changed

Lines changed: 139 additions & 57 deletions

File tree

crates/codegen/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ rust-version.workspace = true
88
repository.workspace = true
99
license.workspace = true
1010

11+
[features]
12+
default = ["std"]
13+
std = ["thiserror/std", "itertools/use_std"]
14+
1115
[dependencies]
1216
rustpython-compiler-core = { workspace = true }
1317
rustpython-literal = {workspace = true }

crates/codegen/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Compile a Python AST or source code into bytecode consumable by RustPython.
2+
#![cfg_attr(not(feature = "std"), no_std)]
23
#![doc(html_logo_url = "https://raw.githubusercontent.com/RustPython/RustPython/main/logo.png")]
34
#![doc(html_root_url = "https://docs.rs/rustpython-compiler/")]
45

crates/codegen/src/symboltable.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,8 @@ fn drop_class_free(symbol_table: &mut SymbolTable, newfree: &mut IndexSet<String
296296
type SymbolMap = IndexMap<String, Symbol>;
297297

298298
mod stack {
299+
use alloc::vec::Vec;
299300
use core::ptr::NonNull;
300-
use std::panic;
301301
pub struct StackStack<T> {
302302
v: Vec<NonNull<T>>,
303303
}
@@ -309,14 +309,30 @@ mod stack {
309309
impl<T> StackStack<T> {
310310
/// Appends a reference to this stack for the duration of the function `f`. When `f`
311311
/// returns, the reference will be popped off the stack.
312+
#[cfg(feature = "std")]
312313
pub fn with_append<F, R>(&mut self, x: &mut T, f: F) -> R
313314
where
314315
F: FnOnce(&mut Self) -> R,
315316
{
316317
self.v.push(x.into());
317-
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| f(self)));
318+
let res = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| f(self)));
318319
self.v.pop();
319-
res.unwrap_or_else(|x| panic::resume_unwind(x))
320+
res.unwrap_or_else(|x| std::panic::resume_unwind(x))
321+
}
322+
323+
/// Appends a reference to this stack for the duration of the function `f`. When `f`
324+
/// returns, the reference will be popped off the stack.
325+
///
326+
/// Without std, panic cleanup is not guaranteed (no catch_unwind).
327+
#[cfg(not(feature = "std"))]
328+
pub fn with_append<F, R>(&mut self, x: &mut T, f: F) -> R
329+
where
330+
F: FnOnce(&mut Self) -> R,
331+
{
332+
self.v.push(x.into());
333+
let result = f(self);
334+
self.v.pop();
335+
result
320336
}
321337

322338
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> + '_ {

crates/common/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ repository.workspace = true
99
license.workspace = true
1010

1111
[features]
12+
default = ["std"]
13+
std = []
1214
threading = ["parking_lot"]
1315
wasm_js = ["getrandom/wasm_js"]
1416

crates/common/src/float_ops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use core::f64;
12
use malachite_bigint::{BigInt, ToBigInt};
23
use num_traits::{Float, Signed, ToPrimitive, Zero};
3-
use std::f64;
44

55
pub const fn decompose_float(value: f64) -> (f64, i32) {
66
if 0.0 == value {

crates/common/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! A crate to hold types and functions common to all rustpython components.
22
3+
#![cfg_attr(not(feature = "std"), no_std)]
4+
35
extern crate alloc;
46

57
#[macro_use]
@@ -10,24 +12,25 @@ pub mod atomic;
1012
pub mod borrow;
1113
pub mod boxvec;
1214
pub mod cformat;
13-
#[cfg(any(unix, windows, target_os = "wasi"))]
15+
#[cfg(all(feature = "std", any(unix, windows, target_os = "wasi")))]
1416
pub mod crt_fd;
1517
pub mod encodings;
16-
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
18+
#[cfg(all(feature = "std", any(not(target_arch = "wasm32"), target_os = "wasi")))]
1719
pub mod fileutils;
1820
pub mod float_ops;
1921
pub mod format;
2022
pub mod hash;
2123
pub mod int;
2224
pub mod linked_list;
2325
pub mod lock;
26+
#[cfg(feature = "std")]
2427
pub mod os;
2528
pub mod rand;
2629
pub mod rc;
2730
pub mod refcount;
2831
pub mod static_cell;
2932
pub mod str;
30-
#[cfg(windows)]
33+
#[cfg(all(feature = "std", windows))]
3134
pub mod windows;
3235

3336
pub use rustpython_wtf8 as wtf8;

crates/common/src/refcount.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
use crate::atomic::{Ordering::*, PyAtomic, Radium};
22

3+
#[inline(never)]
4+
#[cold]
5+
fn refcount_overflow() -> ! {
6+
#[cfg(feature = "std")]
7+
std::process::abort();
8+
#[cfg(not(feature = "std"))]
9+
core::panic!("refcount overflow");
10+
}
11+
312
/// from alloc::sync
413
/// A soft limit on the amount of references that may be made to an `Arc`.
514
///
@@ -36,7 +45,7 @@ impl RefCount {
3645
let old_size = self.strong.fetch_add(1, Relaxed);
3746

3847
if old_size & Self::MASK == Self::MASK {
39-
std::process::abort();
48+
refcount_overflow();
4049
}
4150
}
4251

@@ -46,7 +55,7 @@ impl RefCount {
4655
let old_size = self.strong.fetch_add(n, Relaxed);
4756

4857
if old_size & Self::MASK > Self::MASK - n {
49-
std::process::abort();
58+
refcount_overflow();
5059
}
5160
}
5261

crates/common/src/static_cell.rs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,53 @@
1-
#[cfg(not(feature = "threading"))]
1+
#[cfg(feature = "threading")]
2+
mod threading {
3+
use crate::lock::OnceCell;
4+
5+
pub struct StaticCell<T: 'static> {
6+
inner: OnceCell<T>,
7+
}
8+
9+
impl<T> StaticCell<T> {
10+
#[doc(hidden)]
11+
pub const fn _from_once_cell(inner: OnceCell<T>) -> Self {
12+
Self { inner }
13+
}
14+
15+
pub fn get(&'static self) -> Option<&'static T> {
16+
self.inner.get()
17+
}
18+
19+
pub fn set(&'static self, value: T) -> Result<(), T> {
20+
self.inner.set(value)
21+
}
22+
23+
pub fn get_or_init<F>(&'static self, f: F) -> &'static T
24+
where
25+
F: FnOnce() -> T,
26+
{
27+
self.inner.get_or_init(f)
28+
}
29+
30+
pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
31+
where
32+
F: FnOnce() -> Result<T, E>,
33+
{
34+
self.inner.get_or_try_init(f)
35+
}
36+
}
37+
38+
#[macro_export]
39+
macro_rules! static_cell {
40+
($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => {
41+
$($(#[$attr])*
42+
$vis static $name: $crate::static_cell::StaticCell<$t> =
43+
$crate::static_cell::StaticCell::_from_once_cell($crate::lock::OnceCell::new());)+
44+
};
45+
}
46+
}
47+
#[cfg(feature = "threading")]
48+
pub use threading::*;
49+
50+
#[cfg(all(not(feature = "threading"), feature = "std"))]
251
mod non_threading {
352
use crate::lock::OnceCell;
453
use std::thread::LocalKey;
@@ -22,12 +71,10 @@ mod non_threading {
2271
}
2372

2473
pub fn set(&'static self, value: T) -> Result<(), T> {
25-
// thread-safe because it's a unsync::OnceCell
2674
self.inner.with(|x| {
2775
if x.get().is_some() {
2876
Err(value)
2977
} else {
30-
// will never fail
3178
let _ = x.set(leak(value));
3279
Ok(())
3380
}
@@ -65,43 +112,51 @@ mod non_threading {
65112
};
66113
}
67114
}
68-
#[cfg(not(feature = "threading"))]
115+
#[cfg(all(not(feature = "threading"), feature = "std"))]
69116
pub use non_threading::*;
70117

71-
#[cfg(feature = "threading")]
72-
mod threading {
118+
// Same as `threading` variant, but wraps unsync::OnceCell with Sync.
119+
#[cfg(all(not(feature = "threading"), not(feature = "std")))]
120+
mod no_std {
73121
use crate::lock::OnceCell;
74122

123+
// unsync::OnceCell is !Sync, but without std there can be no threads.
124+
struct SyncOnceCell<T>(OnceCell<T>);
125+
// SAFETY: Without std, threading is impossible.
126+
unsafe impl<T> Sync for SyncOnceCell<T> {}
127+
75128
pub struct StaticCell<T: 'static> {
76-
inner: OnceCell<T>,
129+
inner: SyncOnceCell<T>,
77130
}
78131

79132
impl<T> StaticCell<T> {
80133
#[doc(hidden)]
81134
pub const fn _from_once_cell(inner: OnceCell<T>) -> Self {
82-
Self { inner }
135+
Self {
136+
inner: SyncOnceCell(inner),
137+
}
83138
}
84139

85140
pub fn get(&'static self) -> Option<&'static T> {
86-
self.inner.get()
141+
self.inner.0.get()
87142
}
88143

89144
pub fn set(&'static self, value: T) -> Result<(), T> {
90-
self.inner.set(value)
145+
self.inner.0.set(value)
91146
}
92147

93148
pub fn get_or_init<F>(&'static self, f: F) -> &'static T
94149
where
95150
F: FnOnce() -> T,
96151
{
97-
self.inner.get_or_init(f)
152+
self.inner.0.get_or_init(f)
98153
}
99154

100155
pub fn get_or_try_init<F, E>(&'static self, f: F) -> Result<&'static T, E>
101156
where
102157
F: FnOnce() -> Result<T, E>,
103158
{
104-
self.inner.get_or_try_init(f)
159+
self.inner.0.get_or_try_init(f)
105160
}
106161
}
107162

@@ -114,5 +169,5 @@ mod threading {
114169
};
115170
}
116171
}
117-
#[cfg(feature = "threading")]
118-
pub use threading::*;
172+
#[cfg(all(not(feature = "threading"), not(feature = "std")))]
173+
pub use no_std::*;

crates/common/src/str.rs

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,6 @@ impl fmt::Display for UnicodeEscapeCodepoint {
466466
}
467467

468468
pub mod levenshtein {
469-
use core::cell::RefCell;
470-
use std::thread_local;
471-
472469
pub const MOVE_COST: usize = 2;
473470
const CASE_COST: usize = 1;
474471
const MAX_STRING_SIZE: usize = 40;
@@ -490,13 +487,6 @@ pub mod levenshtein {
490487
}
491488

492489
pub fn levenshtein_distance(a: &[u8], b: &[u8], max_cost: usize) -> usize {
493-
thread_local! {
494-
#[allow(clippy::declare_interior_mutable_const, reason = "thread-local scratch buffer uses const initializer with RefCell")]
495-
static BUFFER: RefCell<[usize; MAX_STRING_SIZE]> = const {
496-
RefCell::new([0usize; MAX_STRING_SIZE])
497-
};
498-
}
499-
500490
if a == b {
501491
return 0;
502492
}
@@ -535,33 +525,33 @@ pub mod levenshtein {
535525
return max_cost + 1;
536526
}
537527

538-
BUFFER.with_borrow_mut(|buffer| {
539-
for (i, x) in buffer.iter_mut().take(a_end).enumerate() {
540-
*x = (i + 1) * MOVE_COST;
541-
}
528+
let mut buffer = [0usize; MAX_STRING_SIZE];
542529

543-
let mut result = 0usize;
544-
for (b_index, b_code) in b_bytes[b_begin..(b_begin + b_end)].iter().enumerate() {
545-
result = b_index * MOVE_COST;
546-
let mut distance = result;
547-
let mut minimum = usize::MAX;
548-
for (a_index, a_code) in a_bytes[a_begin..(a_begin + a_end)].iter().enumerate() {
549-
let substitute = distance + substitution_cost(*b_code, *a_code);
550-
distance = buffer[a_index];
551-
let insert_delete = usize::min(result, distance) + MOVE_COST;
552-
result = usize::min(insert_delete, substitute);
553-
554-
buffer[a_index] = result;
555-
if result < minimum {
556-
minimum = result;
557-
}
558-
}
559-
if minimum > max_cost {
560-
return max_cost + 1;
530+
for (i, x) in buffer.iter_mut().take(a_end).enumerate() {
531+
*x = (i + 1) * MOVE_COST;
532+
}
533+
534+
let mut result = 0usize;
535+
for (b_index, b_code) in b_bytes[b_begin..(b_begin + b_end)].iter().enumerate() {
536+
result = b_index * MOVE_COST;
537+
let mut distance = result;
538+
let mut minimum = usize::MAX;
539+
for (a_index, a_code) in a_bytes[a_begin..(a_begin + a_end)].iter().enumerate() {
540+
let substitute = distance + substitution_cost(*b_code, *a_code);
541+
distance = buffer[a_index];
542+
let insert_delete = usize::min(result, distance) + MOVE_COST;
543+
result = usize::min(insert_delete, substitute);
544+
545+
buffer[a_index] = result;
546+
if result < minimum {
547+
minimum = result;
561548
}
562549
}
563-
result
564-
})
550+
if minimum > max_cost {
551+
return max_cost + 1;
552+
}
553+
}
554+
result
565555
}
566556
}
567557

crates/pylib/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//! common way to use this crate is to just add the `"freeze-stdlib"` feature to `rustpython-vm`,
33
//! in order to automatically include the python part of the standard library into the binary.
44
5+
#![no_std]
6+
57
// windows needs to read the symlink out of `Lib` as git turns it into a text file,
68
// so build.rs sets this env var
79
pub const LIB_PATH: &str = match option_env!("win_lib_path") {

0 commit comments

Comments
 (0)