derive Copy where possible (#6844)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Made many lightweight/internal types trivially copyable to simplify
value handling across the codebase.
* Added a workspace lint to surface missing copy implementations as
warnings.
  * Extended buffer support with explicit retain/release hooks.

* **Bug Fixes**
* Improved diagnostics and debug output for thread/lock acquisition
scenarios.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Jeong, YunWon
2026-01-23 15:24:17 +00:00
committed by GitHub
39 changed files with 89 additions and 58 deletions

View File

@@ -222,6 +222,7 @@ wasm-bindgen = "0.2.106"
unsafe_code = "allow"
unsafe_op_in_unsafe_fn = "deny"
elided_lifetimes_in_paths = "warn"
missing_copy_implementations = "warn"
[workspace.lints.clippy]
# alloc_instead_of_core = "warn"

View File

@@ -113,13 +113,14 @@ struct Compiler {
in_annotation: bool,
}
#[derive(Clone, Copy)]
enum DoneWithFuture {
No,
DoneWithDoc,
Yes,
}
#[derive(Debug, Clone)]
#[derive(Clone, Copy, Debug)]
pub struct CompileOpts {
/// How optimized the bytecode output should be; any optimize > 0 does
/// not emit assert statements

View File

@@ -3,7 +3,7 @@ use core::fmt::Display;
use rustpython_compiler_core::SourceLocation;
use thiserror::Error;
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum PatternUnreachableReason {
NameCapture,
Wildcard,

View File

@@ -85,7 +85,7 @@ impl ops::IndexMut<BlockIdx> for Vec<Block> {
}
}
#[derive(Debug, Clone)]
#[derive(Clone, Copy, Debug)]
pub struct InstructionInfo {
pub instr: AnyInstruction,
pub arg: OpArg,
@@ -95,8 +95,8 @@ pub struct InstructionInfo {
pub except_handler: Option<ExceptHandlerInfo>,
}
/// Exception handler information for an instruction
#[derive(Debug, Clone)]
/// Exception handler information for an instruction.
#[derive(Clone, Copy, Debug)]
pub struct ExceptHandlerInfo {
/// Block to jump to when exception occurs
pub handler_block: BlockIdx,
@@ -742,13 +742,13 @@ fn generate_exception_table(blocks: &[Block], block_to_index: &[u32]) -> Box<[u8
// instr_size includes EXTENDED_ARG instructions
let instr_size = instr.arg.instr_size() as u32;
match (&current_entry, &instr.except_handler) {
match (&current_entry, instr.except_handler) {
// No current entry, no handler - nothing to do
(None, None) => {}
// No current entry, handler starts - begin new entry
(None, Some(handler)) => {
current_entry = Some((handler.clone(), instr_index));
current_entry = Some((handler, instr_index));
}
// Current entry exists, same handler - continue
@@ -767,7 +767,7 @@ fn generate_exception_table(blocks: &[Block], block_to_index: &[u32]) -> Box<[u8
curr_handler.stack_depth as u16,
curr_handler.preserve_lasti,
));
current_entry = Some((handler.clone(), instr_index));
current_entry = Some((handler, instr_index));
}
// Current entry exists, no handler - finish current entry

View File

@@ -758,7 +758,7 @@ impl SymbolTableAnalyzer {
}
}
#[derive(Debug, Clone)]
#[derive(Clone, Copy, Debug)]
enum SymbolUsage {
Global,
Nonlocal,

View File

@@ -14,7 +14,7 @@ use rustpython_literal::{float, format::Case};
use crate::wtf8::{CodePoint, Wtf8, Wtf8Buf};
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CFormatErrorType {
UnmatchedKeyParentheses,
MissingModuloSign,
@@ -27,7 +27,7 @@ pub enum CFormatErrorType {
// also contains how many chars the parsing function consumed
pub type ParsingError = (CFormatErrorType, usize);
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CFormatError {
pub typ: CFormatErrorType, // FIXME
pub index: usize,

View File

@@ -262,6 +262,7 @@ pub mod errors {
use super::*;
use core::fmt::Write;
#[derive(Clone, Copy)]
pub struct Strict;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for Strict {
@@ -286,6 +287,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct Ignore;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for Ignore {
@@ -310,6 +312,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct Replace;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for Replace {
@@ -338,6 +341,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct XmlCharRefReplace;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for XmlCharRefReplace {
@@ -358,6 +362,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct BackslashReplace;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for BackslashReplace {
@@ -394,6 +399,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct NameReplace;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for NameReplace {
@@ -422,6 +428,7 @@ pub mod errors {
}
}
#[derive(Clone, Copy)]
pub struct SurrogateEscape;
impl<Ctx: EncodeContext> EncodeErrorHandler<Ctx> for SurrogateEscape {

View File

@@ -46,7 +46,7 @@ pub mod windows {
pub const SECS_BETWEEN_EPOCHS: i64 = 11644473600; // Seconds between 1.1.1601 and 1.1.1970
#[derive(Default)]
#[derive(Clone, Copy, Default)]
pub struct StatStruct {
pub st_dev: libc::c_ulong,
pub st_ino: u64,
@@ -256,6 +256,7 @@ pub mod windows {
m as _
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct FILE_STAT_BASIC_INFORMATION {
pub FileId: i64,
@@ -275,8 +276,9 @@ pub mod windows {
pub FileId128: [u64; 2],
}
#[repr(C)]
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(C)]
pub enum FILE_INFO_BY_NAME_CLASS {
FileStatByNameInfo,
FileStatLxByNameInfo,

View File

@@ -110,7 +110,7 @@ impl FormatParse for FormatSign {
}
}
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FormatGrouping {
Comma,
Underscore,
@@ -136,7 +136,7 @@ impl From<&FormatGrouping> for char {
}
}
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FormatType {
String,
Binary,
@@ -199,7 +199,7 @@ impl FormatParse for FormatType {
}
}
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FormatSpec {
conversion: Option<FormatConversion>,
fill: Option<CodePoint>,
@@ -845,7 +845,7 @@ impl Deref for AsciiStr<'_> {
}
}
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FormatSpecError {
DecimalDigitsTooMany,
PrecisionTooBig,
@@ -862,7 +862,7 @@ pub enum FormatSpecError {
NotImplemented(char, &'static str),
}
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FormatParseError {
UnmatchedBracket,
MissingStartBracket,

View File

@@ -25,6 +25,7 @@ pub const SEED_BITS: usize = core::mem::size_of::<u64>() * 2 * 8;
// pub const CUTOFF: usize = 7;
#[derive(Clone, Copy)]
pub struct HashSecret {
k0: u64,
k1: u64,

View File

@@ -29,7 +29,7 @@ pub fn float_to_ratio(value: f64) -> Option<(BigInt, BigInt)> {
})
}
#[derive(Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BytesToIntError {
InvalidLiteral { base: u32 },
InvalidBase,

View File

@@ -201,10 +201,14 @@ fn deadlock(lock_kind: &str, ty: &str) -> ! {
panic!("deadlock: tried to {lock_kind}lock a Cell{ty} twice")
}
#[derive(Clone, Copy)]
pub struct SingleThreadId(());
unsafe impl GetThreadId for SingleThreadId {
const INIT: Self = Self(());
fn nonzero_thread_id(&self) -> NonZero<usize> {
NonZero::new(1).unwrap()
// Safety: This is constant.
unsafe { NonZero::new_unchecked(1) }
}
}

View File

@@ -121,19 +121,23 @@ impl<R: RawMutex, G: GetThreadId, T: ?Sized> ThreadMutex<R, G, T> {
}
}
}
// Whether ThreadMutex::try_lock failed because the mutex was already locked on another thread or
// on the current thread
#[derive(Clone, Copy)]
pub enum TryLockThreadError {
/// Failed to lock because mutex was already locked on another thread.
Other,
/// Failed to lock because mutex was already locked on current thread.
Current,
}
struct LockedPlaceholder(&'static str);
impl fmt::Debug for LockedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.0)
}
}
impl<R: RawMutex, G: GetThreadId, T: ?Sized + fmt::Debug> fmt::Debug for ThreadMutex<R, G, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_lock() {

View File

@@ -449,6 +449,7 @@ pub fn to_ascii(value: &str) -> AsciiString {
unsafe { AsciiString::from_ascii_unchecked(ascii) }
}
#[derive(Clone, Copy)]
pub struct UnicodeEscapeCodepoint(pub CodePoint);
impl fmt::Display for UnicodeEscapeCodepoint {

View File

@@ -32,7 +32,7 @@ mod oparg;
/// Exception table entry for zero-cost exception handling
/// Format: (start, size, target, depth<<1|lasti)
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ExceptionTableEntry {
/// Start instruction offset (inclusive)
pub start: u32,
@@ -47,7 +47,7 @@ pub struct ExceptionTableEntry {
}
impl ExceptionTableEntry {
pub fn new(start: u32, end: u32, target: u32, depth: u16, push_lasti: bool) -> Self {
pub const fn new(start: u32, end: u32, target: u32, depth: u16, push_lasti: bool) -> Self {
Self {
start,
end,

View File

@@ -6,7 +6,7 @@ use rustpython_wtf8::Wtf8;
pub const FORMAT_VERSION: u32 = 5;
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum MarshalError {
/// Unexpected End Of File
Eof,
@@ -42,6 +42,7 @@ impl core::error::Error for MarshalError {}
type Result<T, E = MarshalError> = core::result::Result<T, E>;
#[derive(Clone, Copy)]
#[repr(u8)]
enum Type {
// Null = b'0',

View File

@@ -22,7 +22,7 @@ impl core::str::FromStr for Mode {
}
/// Returned when a given mode is not valid.
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct ModeParseError;
impl core::fmt::Display for ModeParseError {

View File

@@ -14,9 +14,10 @@
use bitflags::bitflags;
pub const SRE_MAGIC: usize = 20230612;
#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]
#[repr(u32)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(num_enum::TryFromPrimitive, Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SreOpcode {
FAILURE = 0,
SUCCESS = 1,
@@ -63,9 +64,9 @@ pub enum SreOpcode {
RANGE_UNI_IGNORE = 42,
}
#[derive(num_enum::TryFromPrimitive, Debug, PartialEq, Eq)]
#[repr(u32)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(num_enum::TryFromPrimitive, Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SreAtCode {
BEGINNING = 0,
BEGINNING_LINE = 1,
@@ -81,9 +82,9 @@ pub enum SreAtCode {
UNI_NON_BOUNDARY = 11,
}
#[derive(num_enum::TryFromPrimitive, Debug)]
#[repr(u32)]
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(num_enum::TryFromPrimitive, Clone, Copy, Debug)]
#[repr(u32)]
pub enum SreCatCode {
DIGIT = 0,
NOT_DIGIT = 1,
@@ -120,6 +121,7 @@ bitflags! {
}
bitflags! {
#[derive(Clone, Copy)]
pub struct SreInfo: u32 {
const PREFIX = 1;
const LITERAL = 2;

View File

@@ -16,13 +16,13 @@ pub struct SplitArgs<T: TryFromObject> {
maxsplit: isize,
}
#[derive(FromArgs)]
#[derive(Clone, Copy, FromArgs)]
pub struct SplitLinesArgs {
#[pyarg(any, default = false)]
pub keepends: bool,
}
#[derive(FromArgs)]
#[derive(Clone, Copy, FromArgs)]
pub struct ExpandTabsArgs {
#[pyarg(any, default = 8)]
tabsize: isize,

View File

@@ -168,7 +168,7 @@ impl Representable for PyMethodDescriptor {
}
}
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum MemberKind {
Bool = 14,
ObjectEx = 16,
@@ -176,11 +176,13 @@ pub enum MemberKind {
pub type MemberSetterFunc = Option<fn(&VirtualMachine, PyObjectRef, PySetterValue) -> PyResult<()>>;
#[derive(Clone, Copy)]
pub enum MemberGetter {
Getter(fn(&VirtualMachine, PyObjectRef) -> PyResult),
Offset(usize),
}
#[derive(Clone, Copy)]
pub enum MemberSetter {
Setter(MemberSetterFunc),
Offset(usize),

View File

@@ -9,7 +9,7 @@ use crate::{
};
#[pyclass(module = false, name = "module")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct PyModuleDef {
// pub index: usize,
pub name: &'static PyStrInterned,
@@ -26,7 +26,7 @@ pub type ModuleCreate =
fn(&VirtualMachine, &PyObject, &'static PyModuleDef) -> PyResult<PyRef<PyModule>>;
pub type ModuleExec = fn(&VirtualMachine, &Py<PyModule>) -> PyResult<()>;
#[derive(Default)]
#[derive(Clone, Copy, Default)]
pub struct PyModuleSlots {
pub create: Option<ModuleCreate>,
pub exec: Option<ModuleExec>,
@@ -83,7 +83,7 @@ impl PyModuleDef {
#[allow(clippy::new_without_default)] // avoid Default implementation
#[pyclass(module = false, name = "module")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct PyModule {
// PyObject *md_dict;
pub def: Option<&'static PyModuleDef>,

View File

@@ -14,8 +14,8 @@ use crate::{
///
/// SimpleNamespace(**kwargs)
#[pyclass(module = "types", name = "SimpleNamespace")]
#[derive(Debug, Default)]
pub struct PyNamespace {}
#[derive(Copy, Clone, Debug, Default)]
pub struct PyNamespace;
impl PyPayload for PyNamespace {
#[inline]

View File

@@ -18,7 +18,7 @@ use itertools::Itertools;
/// When called, it accepts no arguments and returns a new featureless
/// instance that has no instance attributes and cannot be given any.
#[pyclass(module = false, name = "object")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct PyBaseObject;
impl PyPayload for PyBaseObject {

View File

@@ -9,7 +9,7 @@ use crate::{
};
#[pyclass(module = false, name = "NoneType")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct PyNone;
impl PyPayload for PyNone {
@@ -75,7 +75,7 @@ impl AsNumber for PyNone {
}
#[pyclass(module = false, name = "NotImplementedType")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct PyNotImplemented;
impl PyPayload for PyNotImplemented {

View File

@@ -301,7 +301,7 @@ impl Representable for PySlice {
}
#[pyclass(module = false, name = "EllipsisType")]
#[derive(Debug)]
#[derive(Copy, Clone, Debug)]
pub struct PyEllipsis;
impl PyPayload for PyEllipsis {

View File

@@ -24,7 +24,7 @@ impl PyPayload for PyZip {
}
}
#[derive(FromArgs)]
#[derive(Clone, Copy, FromArgs)]
pub struct PyZipNewArgs {
#[pyarg(named, optional)]
strict: OptionalArg<bool>,

View File

@@ -124,7 +124,7 @@ struct DictEntry<T> {
}
static_assertions::assert_eq_size!(DictEntry<PyObjectRef>, Option<DictEntry<PyObjectRef>>);
#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DictSize {
indices_size: usize,
pub entries_size: usize,

View File

@@ -63,7 +63,7 @@ macro_rules! define_methods {
};
}
#[derive(Clone)]
#[derive(Clone, Copy)]
pub struct PyMethodDef {
pub name: &'static str, // TODO: interned
pub func: &'static dyn PyNativeFn,
@@ -270,7 +270,7 @@ impl core::fmt::Debug for PyMethodDef {
// This is not a part of CPython API.
// But useful to support dynamically generated methods
#[pyclass(name, module = false, ctx = "method_def")]
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub struct HeapMethodDef {
method: PyMethodDef,
}

View File

@@ -14,7 +14,7 @@ use num_traits::PrimInt;
/// method, this method will first be called to convert the object into a float.
/// If `__complex__()` is not defined then it falls back to `__float__()`. If
/// `__float__()` is not defined it falls back to `__index__()`.
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(transparent)]
pub struct ArgIntoComplex {
value: Complex64,
@@ -52,7 +52,7 @@ impl TryFromObject for ArgIntoComplex {
/// If the object is not a Python floating point object but has a `__float__()`
/// method, this method will first be called to convert the object into a float.
/// If `__float__()` is not defined then it falls back to `__index__()`.
#[derive(Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(transparent)]
pub struct ArgIntoFloat {
value: f64,
@@ -95,7 +95,7 @@ impl TryFromObject for ArgIntoFloat {
/// By default an object is considered true unless its class defines either a
/// `__bool__()` method that returns False or a `__len__()` method that returns
/// zero, when called with the object.
#[derive(Debug, Default, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ArgIntoBool {
value: bool,
}

View File

@@ -14,6 +14,7 @@ use alloc::borrow::Cow;
use core::{fmt::Debug, ops::Range};
use itertools::Itertools;
#[derive(Clone, Copy)]
pub struct BufferMethods {
pub obj_bytes: fn(&PyBuffer) -> BorrowedValue<'_, [u8]>,
pub obj_bytes_mut: fn(&PyBuffer) -> BorrowedValueMut<'_, [u8]>,

View File

@@ -48,7 +48,7 @@ impl PyMappingSlots {
}
#[allow(clippy::type_complexity)]
#[derive(Default)]
#[derive(Clone, Copy, Default)]
pub struct PyMappingMethods {
pub length: Option<fn(PyMapping<'_>, &VirtualMachine) -> PyResult<usize>>,
pub subscript: Option<fn(PyMapping<'_>, &PyObject, &VirtualMachine) -> PyResult>,

View File

@@ -70,7 +70,7 @@ impl PySequenceSlots {
}
#[allow(clippy::type_complexity)]
#[derive(Default)]
#[derive(Clone, Copy, Default)]
pub struct PySequenceMethods {
pub length: Option<fn(PySequence<'_>, &VirtualMachine) -> PyResult<usize>>,
pub concat: Option<fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult>,

View File

@@ -252,6 +252,7 @@ impl<T: Clone> SliceableSequenceOp for [T] {
}
}
#[derive(Clone, Copy)]
pub enum SequenceIndex {
Int(isize),
Slice(SaturatedSlice),
@@ -403,6 +404,7 @@ impl SaturatedSlice {
}
}
#[derive(Clone, Copy)]
pub struct SaturatedSliceIter {
index: isize,
step: isize,

View File

@@ -25,6 +25,7 @@ mod decl {
#[pyattr(name = "version")]
use marshal::FORMAT_VERSION;
#[derive(Clone, Copy)]
pub struct DumpError;
impl marshal::Dumpable for PyObjectRef {

View File

@@ -35,7 +35,7 @@ impl crate::convert::IntoPyException for rustix::io::Errno {
}
#[allow(dead_code)]
#[derive(FromArgs, Default)]
#[derive(FromArgs, Default, Copy, Clone)]
pub struct TargetIsDirectory {
#[pyarg(any, default = false)]
pub(crate) target_is_directory: bool,

View File

@@ -988,9 +988,9 @@ pub(crate) mod typevar {
#[pyattr]
#[pyclass(name = "Generic", module = "typing")]
#[derive(Debug, PyPayload)]
#[derive(Copy, Clone, Debug, PyPayload)]
#[allow(dead_code)]
pub struct Generic {}
pub struct Generic;
#[pyclass(flags(BASETYPE))]
impl Generic {

View File

@@ -49,7 +49,7 @@ pub(crate) mod decl {
}
#[pyclass(no_attr, name = "NoDefaultType", module = "typing")]
#[derive(Debug, PyPayload)]
#[derive(Clone, Copy, Debug, PyPayload)]
pub struct NoDefault;
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]

View File

@@ -102,6 +102,7 @@ pub(crate) fn weak_vm(vm: &VirtualMachine) -> Weak<StoredVirtualMachine> {
STORED_VMS.with_borrow(|vms| Rc::downgrade(vms.get(id).expect("VirtualMachine is not valid")))
}
#[derive(Clone, Copy)]
#[wasm_bindgen(js_name = vmStore)]
pub struct VMStore;

View File

@@ -60,7 +60,7 @@ fn main() -> Result<(), lexopt::Error> {
for script in &scripts {
if script.exists() && script.is_file() {
let res = display_script(script, mode, opts.clone(), expand_code_objects);
let res = display_script(script, mode, opts, expand_code_objects);
if let Err(e) = res {
error!("Error while compiling {script:?}: {e}");
}