General code nits (#8135)

* General nitpicks

* Move Compiler::constant_truthiness to a ConstantData method

* More code cleanup

* bytecode::CodeFlags -> CodeFlags

* More code clenaups
This commit is contained in:
Shahar Naveh authored and GitHub committed 2026-06-22 16:46:17 +09:00
1 parent 866f7ca812
commit c6e254fbdc
14 files changed
+364 -490

No files matched your search

Generated
+1
View File
@@ -3553,6 +3553,7 @@ dependencies = [
"lz4_flex",
"malachite-bigint",
"num-complex",
"num-traits",
"rustpython-ruff_source_file",
"rustpython-wtf8",
]
+165 -229
View File
@@ -24,13 +24,15 @@ use num_complex::Complex;
use num_traits::{Num, ToPrimitive, Zero};
use ruff_python_ast as ast;
use ruff_text_size::{Ranged, TextRange, TextSize};
use rustpython_compiler_core::{
Mode, OneIndexed, PositionEncoding, SourceFile, SourceLocation,
bytecode::{
self, AnyInstruction, AnyOpcode, Arg as OpArgMarker, BinaryOperator, BuildSliceArgCount,
CodeObject, ComparisonOperator, ConstantData, ConvertValueOparg, Instruction,
IntrinsicFunction1, Invert, LoadAttr, LoadSuperAttr, OpArg, OpArgType, PseudoInstruction,
SpecialMethod, UnpackExArgs, oparg,
CodeFlags, CodeObject, ComparisonOperator, ConstantData, ConvertValueOparg, Instruction,
IntrinsicFunction1, Invert, LoadAttr, LoadSuperAttr, MakeFunctionFlag, MakeFunctionFlags,
OpArg, OpArgType, Opcode, PseudoInstruction, PseudoOpcode, SpecialMethod, UnpackExArgs,
oparg,
},
};
use rustpython_wtf8::Wtf8Buf;
@@ -63,12 +65,9 @@ impl ExprExt for ast::Expr {
fn is_constant_slice(&self) -> bool {
match self {
Self::Slice(s) => {
let lower_const =
s.lower.is_none() || s.lower.as_deref().is_some_and(|e| e.is_constant());
let upper_const =
s.upper.is_none() || s.upper.as_deref().is_some_and(|e| e.is_constant());
let step_const =
s.step.is_none() || s.step.as_deref().is_some_and(|e| e.is_constant());
let lower_const = s.lower.as_deref().is_none_or(|e| e.is_constant());
let upper_const = s.upper.as_deref().is_none_or(|e| e.is_constant());
let step_const = s.step.as_deref().is_none_or(|e| e.is_constant());
lower_const && upper_const && step_const
}
_ => false,
@@ -458,22 +457,6 @@ enum CollectionType {
const STACK_USE_GUIDELINE: u32 = 30;
impl Compiler {
fn constant_truthiness(constant: &ConstantData) -> bool {
match constant {
ConstantData::Tuple { elements } | ConstantData::Frozenset { elements } => {
!elements.is_empty()
}
ConstantData::Integer { value } => !value.is_zero(),
ConstantData::Float { value } => *value != 0.0,
ConstantData::Complex { value } => value.re != 0.0 || value.im != 0.0,
ConstantData::Boolean { value } => *value,
ConstantData::Str { value } => !value.is_empty(),
ConstantData::Bytes { value } => !value.is_empty(),
ConstantData::Code { .. } | ConstantData::Slice { .. } | ConstantData::Ellipsis => true,
ConstantData::None => false,
}
}
fn new(opts: CompileOpts, source_file: SourceFile, code_name: &str) -> Self {
let module_code = ir::CodeInfo {
// CPython convention: top-level module / interactive /
@@ -484,7 +467,7 @@ impl Compiler {
// empty flags. frame.rs:725-731 then binds locals to globals
// for module/REPL frames whose `scope.locals` is None - the
// correct semantics for `exec(code, globals)` and module init.
flags: bytecode::CodeFlags::empty(),
flags: CodeFlags::empty(),
source_path: source_file.name().to_owned(),
private: None,
blocks: vec![ir::Block::default()],
@@ -1196,13 +1179,10 @@ impl Compiler {
let source_path = self.source_file.name().to_owned();
// Lookup symbol table entry using key (_PySymtable_Lookup)
let ste = match self.symbol_table_stack.get(key) {
Some(v) => v,
None => {
return Err(self.error(CodegenErrorType::SyntaxError(
"unknown symbol table entry".to_owned(),
)));
}
let Some(ste) = self.symbol_table_stack.get(key) else {
return Err(self.error(CodegenErrorType::SyntaxError(
"unknown symbol table entry".into(),
)));
};
// Use varnames from symbol table (already collected in definition order)
@@ -1273,16 +1253,21 @@ impl Compiler {
.collect()
})
.unwrap_or_default();
let mut free_names: Vec<_> = ste
let mut free_names = ste
.symbols
.iter()
.filter(|(_, s)| {
s.scope == SymbolScope::Free
|| (scope_type != CompilerScope::Class
&& s.flags.contains(SymbolFlags::FREE_CLASS))
|| (scope_type == CompilerScope::Class
&& s.flags.contains(SymbolFlags::FREE_CLASS)
&& self.has_enclosing_non_module_code_scope())
if s.scope == SymbolScope::Free {
return true;
}
let has_free_class = s.flags.contains(SymbolFlags::FREE_CLASS);
if scope_type == CompilerScope::Class {
has_free_class && self.has_enclosing_non_module_code_scope()
} else {
has_free_class
}
})
.filter(|(name, symbol)| {
if !matches!(
@@ -1294,7 +1279,8 @@ impl Compiler {
!(annotation_free_names.contains(*name) && symbol.flags.is_empty())
})
.map(|(name, _)| name.clone())
.collect();
.collect::<Vec<_>>();
free_names.sort();
for name in free_names {
freevar_cache.insert(name);
@@ -1302,28 +1288,23 @@ impl Compiler {
// Initialize u_metadata fields
let (mut flags, posonlyarg_count, arg_count, kwonlyarg_count) = match scope_type {
CompilerScope::Module => (bytecode::CodeFlags::empty(), 0, 0, 0),
CompilerScope::Class => (bytecode::CodeFlags::empty(), 0, 0, 0),
CompilerScope::Module => (CodeFlags::empty(), 0, 0, 0),
CompilerScope::Class => (CodeFlags::empty(), 0, 0, 0),
CompilerScope::Function | CompilerScope::AsyncFunction | CompilerScope::Lambda => (
bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED,
CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED,
0, // Will be set later in enter_function
0, // Will be set later in enter_function
0, // Will be set later in enter_function
),
CompilerScope::Comprehension => (
bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED,
CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED,
0,
1, // comprehensions take one argument (.0)
0,
),
CompilerScope::TypeParams => (
bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED,
0,
0,
0,
),
CompilerScope::TypeParams => (CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED, 0, 0, 0),
CompilerScope::Annotation => (
bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED,
CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED,
1, // format is positional-only
1, // annotation scope takes one argument (format)
0,
@@ -1331,7 +1312,7 @@ impl Compiler {
};
if ste.is_method {
flags |= bytecode::CodeFlags::METHOD;
flags |= CodeFlags::METHOD;
}
// CPython sets CO_NESTED from symtable's ste_nested, not merely
@@ -1347,12 +1328,12 @@ impl Compiler {
| CompilerScope::Annotation
| CompilerScope::TypeParams
) {
flags | bytecode::CodeFlags::NESTED
flags | CodeFlags::NESTED
} else {
flags
};
if self.future_annotations {
flags |= bytecode::CodeFlags::FUTURE_ANNOTATIONS;
flags |= CodeFlags::FUTURE_ANNOTATIONS;
}
// Get private name from parent scope
@@ -1436,10 +1417,7 @@ impl Compiler {
let except_handler = None;
self.cpython_cfg_builder_addop(ir::InstructionInfo {
instr: Instruction::Resume {
context: OpArgMarker::marker(),
}
.into(),
instr: Opcode::Resume.into(),
arg: OpArg::new(oparg::ResumeLocation::AtFuncStart.into()),
target: BlockIdx::NULL,
location,
@@ -1477,9 +1455,7 @@ impl Compiler {
// Preserve flags computed from the symbol-table context.
info.flags = flags
| (info.flags
& (bytecode::CodeFlags::NESTED
| bytecode::CodeFlags::METHOD
| bytecode::CodeFlags::FUTURE_ANNOTATIONS));
& (CodeFlags::NESTED | CodeFlags::METHOD | CodeFlags::FUTURE_ANNOTATIONS));
info.metadata.argcount = arg_count;
info.metadata.posonlyargcount = posonlyarg_count;
info.metadata.kwonlyargcount = kwonlyarg_count;
@@ -2074,7 +2050,7 @@ impl Compiler {
if self.future_annotations {
self.current_code_info()
.flags
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
.insert(CodeFlags::FUTURE_ANNOTATIONS);
}
// Module-level __conditional_annotations__ cell
@@ -2145,7 +2121,7 @@ impl Compiler {
if self.future_annotations {
self.current_code_info()
.flags
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
.insert(CodeFlags::FUTURE_ANNOTATIONS);
}
self.symbol_table_stack.push(symbol_table);
let module_start_loc = self.module_start_location(body);
@@ -2485,11 +2461,13 @@ impl Compiler {
let current_table = self.current_symbol_table();
if current_table.typ == CompilerScope::Class
&& !self.current_code_info().in_inlined_comp
&& ((usage == NameUsage::Load
&& (name == "__class__"
|| name == "__classdict__"
|| name == "__conditional_annotations__"))
|| (name == "__conditional_annotations__" && usage == NameUsage::Store))
&& matches!(
(usage, name.as_ref()),
(
NameUsage::Load,
"__class__" | "__classdict__" | "__conditional_annotations__"
) | (NameUsage::Store, "__conditional_annotations__")
)
{
Some(SymbolScope::Cell)
} else {
@@ -3150,7 +3128,7 @@ impl Compiler {
let code = self.exit_scope();
self.ctx = prev_ctx;
self.make_closure(code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(code, MakeFunctionFlags::new())?;
emit!(self, Instruction::PushNull);
emit!(self, Instruction::Call { argc: 0 });
} else {
@@ -3228,7 +3206,7 @@ impl Compiler {
}
self.push_output(
bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED,
CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED,
parameters.posonlyargs.len().to_u32(),
(parameters.posonlyargs.len() + parameters.args.len()).to_u32(),
parameters.kwonlyargs.len().to_u32(),
@@ -3246,11 +3224,11 @@ impl Compiler {
}
if let Some(name) = parameters.vararg.as_deref() {
self.current_code_info().flags |= bytecode::CodeFlags::VARARGS;
self.current_code_info().flags |= CodeFlags::VARARGS;
self.varname(name.name.as_str());
}
if let Some(name) = parameters.kwarg.as_deref() {
self.current_code_info().flags |= bytecode::CodeFlags::VARKEYWORDS;
self.current_code_info().flags |= CodeFlags::VARKEYWORDS;
self.varname(name.name.as_str());
}
@@ -3333,10 +3311,7 @@ impl Compiler {
self.ctx = prev_ctx;
self.set_source_range(expr_range);
self.make_closure(
code,
bytecode::MakeFunctionFlags::from([bytecode::MakeFunctionFlag::Defaults]),
)?;
self.make_closure(code, MakeFunctionFlags::from([MakeFunctionFlag::Defaults]))?;
Ok(())
}
@@ -3376,10 +3351,7 @@ impl Compiler {
let code = self.exit_scope();
self.ctx = prev_ctx;
self.set_source_range(alias_range);
self.make_closure(
code,
bytecode::MakeFunctionFlags::from([bytecode::MakeFunctionFlag::Defaults]),
)?;
self.make_closure(code, MakeFunctionFlags::from([MakeFunctionFlag::Defaults]))?;
Ok(())
}
@@ -4206,7 +4178,7 @@ impl Compiler {
parameters: &ast::Parameters,
loc: TextRange,
) -> CompileResult<bytecode::MakeFunctionFlags> {
let mut funcflags = bytecode::MakeFunctionFlags::new();
let mut funcflags = MakeFunctionFlags::new();
// Handle positional defaults
let defaults: Vec<_> = core::iter::empty()
@@ -4227,7 +4199,7 @@ impl Compiler {
count: defaults.len().to_u32()
}
);
funcflags.insert(bytecode::MakeFunctionFlag::Defaults);
funcflags.insert(MakeFunctionFlag::Defaults);
}
// Handle keyword-only defaults
@@ -4254,7 +4226,7 @@ impl Compiler {
count: kw_with_defaults.len().to_u32(),
}
);
funcflags.insert(bytecode::MakeFunctionFlag::KwOnlyDefaults);
funcflags.insert(MakeFunctionFlag::KwOnlyDefaults);
}
Ok(funcflags)
@@ -4275,7 +4247,7 @@ impl Compiler {
self.enter_function(name, parameters)?;
self.current_code_info()
.flags
.set(bytecode::CodeFlags::COROUTINE, is_async);
.set(CodeFlags::COROUTINE, is_async);
// Set up context
let prev_ctx = self.ctx;
@@ -4304,7 +4276,7 @@ impl Compiler {
.insert_full(ConstantData::Str {
value: (*doc).to_string().into(),
});
self.current_code_info().flags |= bytecode::CodeFlags::HAS_DOCSTRING;
self.current_code_info().flags |= CodeFlags::HAS_DOCSTRING;
}
let start_label = self.use_cpython_function_start_label();
@@ -4460,7 +4432,7 @@ impl Compiler {
// Make a closure from the code object
self.set_source_range(func_range);
self.make_closure(annotate_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(annotate_code, MakeFunctionFlags::new())?;
Ok(true)
}
@@ -4684,7 +4656,7 @@ impl Compiler {
// Make a closure from the code object
self.set_source_range(loc);
self.make_closure(annotate_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(annotate_code, MakeFunctionFlags::new())?;
// Store as __annotate_func__ for classes, __annotate__ for modules
let name = if parent_scope_type == CompilerScope::Class {
@@ -4737,10 +4709,10 @@ impl Compiler {
if is_generic {
// Count args to pass to type params scope
if funcflags.contains(&bytecode::MakeFunctionFlag::Defaults) {
if funcflags.contains(&MakeFunctionFlag::Defaults) {
num_typeparam_args += 1;
}
if funcflags.contains(&bytecode::MakeFunctionFlag::KwOnlyDefaults) {
if funcflags.contains(&MakeFunctionFlag::KwOnlyDefaults) {
num_typeparam_args += 1;
}
if num_typeparam_args == 2 {
@@ -4750,7 +4722,7 @@ impl Compiler {
// Enter type params scope
let type_params_name = format!("<generic parameters of {name}>");
self.push_output(
bytecode::CodeFlags::OPTIMIZED | bytecode::CodeFlags::NEWLOCALS,
CodeFlags::OPTIMIZED | CodeFlags::NEWLOCALS,
0,
num_typeparam_args,
0,
@@ -4767,13 +4739,13 @@ impl Compiler {
// Add parameter names to varnames for the type params scope
// These will be passed as arguments when the closure is called
let current_info = self.current_code_info();
if funcflags.contains(&bytecode::MakeFunctionFlag::Defaults) {
if funcflags.contains(&MakeFunctionFlag::Defaults) {
current_info
.metadata
.varnames
.insert(".defaults".to_owned());
}
if funcflags.contains(&bytecode::MakeFunctionFlag::KwOnlyDefaults) {
if funcflags.contains(&MakeFunctionFlag::KwOnlyDefaults) {
current_info
.metadata
.varnames
@@ -4791,9 +4763,9 @@ impl Compiler {
}
// Compile annotations as closure (PEP 649)
let mut annotations_flag = bytecode::MakeFunctionFlags::new();
let mut annotations_flag = MakeFunctionFlags::new();
if self.compile_annotations_closure(name, parameters, returns, def_source_range)? {
annotations_flag.insert(bytecode::MakeFunctionFlag::Annotate);
annotations_flag.insert(MakeFunctionFlag::Annotate);
}
// Compile function body
@@ -4834,7 +4806,7 @@ impl Compiler {
self.ctx = saved_ctx;
// Make closure for type params code
self.make_closure(type_params_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(type_params_code, MakeFunctionFlags::new())?;
if num_typeparam_args > 0 {
emit!(
@@ -4873,31 +4845,36 @@ impl Compiler {
/// Determines if a variable should be CELL or FREE type
// = get_ref_type
fn get_ref_type(&self, name: &str) -> Result<SymbolScope, CodegenErrorType> {
let table = self.symbol_table_stack.last().unwrap();
let table = self.current_symbol_table();
// Special handling for __class__, __classdict__, and __conditional_annotations__ in class scope
// This should only apply when we're actually IN a class body,
// not when we're in a method nested inside a class.
if table.typ == CompilerScope::Class
&& (name == "__class__"
|| name == "__classdict__"
|| name == "__conditional_annotations__")
&& matches!(
name,
"__class__" | "__classdict__" | "__conditional_annotations__"
)
{
return Ok(SymbolScope::Cell);
}
match table.lookup(name) {
Some(symbol) => match symbol.scope {
SymbolScope::Cell => Ok(SymbolScope::Cell),
SymbolScope::Free => Ok(SymbolScope::Free),
_ if symbol.flags.contains(SymbolFlags::FREE_CLASS) => Ok(SymbolScope::Free),
_ => Err(CodegenErrorType::SyntaxError(format!(
"get_ref_type: invalid scope for '{name}'"
))),
},
None => Err(CodegenErrorType::SyntaxError(format!(
let Some(symbol) = table.lookup(name) else {
return Err(CodegenErrorType::SyntaxError(format!(
"get_ref_type: cannot find symbol '{name}'"
))),
}
)));
};
Ok(match symbol.scope {
SymbolScope::Cell => SymbolScope::Cell,
SymbolScope::Free => SymbolScope::Free,
_ if symbol.flags.contains(SymbolFlags::FREE_CLASS) => SymbolScope::Free,
_ => {
return Err(CodegenErrorType::SyntaxError(format!(
"get_ref_type: invalid scope for '{name}'"
)));
}
})
}
/// Loads closure variables if needed and creates a function object
@@ -4990,57 +4967,57 @@ impl Compiler {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::Closure
flag: MakeFunctionFlag::Closure
}
);
}
// Set annotations if present
if flags.contains(&bytecode::MakeFunctionFlag::Annotations) {
if flags.contains(&MakeFunctionFlag::Annotations) {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::Annotations
flag: MakeFunctionFlag::Annotations
}
);
}
// Set __annotate__ closure if present (PEP 649)
if flags.contains(&bytecode::MakeFunctionFlag::Annotate) {
if flags.contains(&MakeFunctionFlag::Annotate) {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::Annotate
flag: MakeFunctionFlag::Annotate
}
);
}
// Set kwdefaults if present
if flags.contains(&bytecode::MakeFunctionFlag::KwOnlyDefaults) {
if flags.contains(&MakeFunctionFlag::KwOnlyDefaults) {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::KwOnlyDefaults
flag: MakeFunctionFlag::KwOnlyDefaults
}
);
}
// Set defaults if present
if flags.contains(&bytecode::MakeFunctionFlag::Defaults) {
if flags.contains(&MakeFunctionFlag::Defaults) {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::Defaults
flag: MakeFunctionFlag::Defaults
}
);
}
// Set type_params if present
if flags.contains(&bytecode::MakeFunctionFlag::TypeParams) {
if flags.contains(&MakeFunctionFlag::TypeParams) {
emit!(
self,
Instruction::SetFunctionAttribute {
flag: bytecode::MakeFunctionFlag::TypeParams
flag: MakeFunctionFlag::TypeParams
}
);
}
@@ -5302,7 +5279,7 @@ impl Compiler {
if is_generic {
let type_params_name = format!("<generic parameters of {name}>");
self.push_output(
bytecode::CodeFlags::OPTIMIZED | bytecode::CodeFlags::NEWLOCALS,
CodeFlags::OPTIMIZED | CodeFlags::NEWLOCALS,
0,
0,
0,
@@ -5345,7 +5322,7 @@ impl Compiler {
// Create the class body function with the .type_params closure
// captured through the class code object's freevars.
self.make_closure(class_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(class_code, MakeFunctionFlags::new())?;
self.emit_load_const(ConstantData::Str { value: name.into() });
// Create .generic_base after the class function and name are on the
@@ -5496,7 +5473,7 @@ impl Compiler {
// Execute the type params function
self.set_source_range(class_source_range);
self.make_closure(type_params_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(type_params_code, MakeFunctionFlags::new())?;
self.set_source_range(class_source_range);
emit!(self, Instruction::PushNull);
self.set_source_range(class_source_range);
@@ -5507,7 +5484,7 @@ impl Compiler {
emit!(self, Instruction::PushNull);
// Create class function with closure
self.make_closure(class_code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(class_code, MakeFunctionFlags::new())?;
self.emit_load_const(ConstantData::Str { value: name.into() });
if let Some(arguments) = arguments {
@@ -8225,12 +8202,12 @@ impl Compiler {
}
self.enter_function(&name, params)?;
let mut func_flags = bytecode::MakeFunctionFlags::new();
let mut func_flags = MakeFunctionFlags::new();
if have_defaults {
func_flags.insert(bytecode::MakeFunctionFlag::Defaults);
func_flags.insert(MakeFunctionFlag::Defaults);
}
if have_kwdefaults {
func_flags.insert(bytecode::MakeFunctionFlag::KwOnlyDefaults);
func_flags.insert(MakeFunctionFlag::KwOnlyDefaults);
}
// Set qualname for lambda
@@ -8270,12 +8247,7 @@ impl Compiler {
}) => {
self.compile_comprehension(
"<listcomp>",
Some(
Instruction::BuildList {
count: OpArgMarker::marker(),
}
.into(),
),
Some(Opcode::BuildList.into()),
generators,
&|compiler, collection_add_i| {
compiler.compile_comprehension_element(elt)?;
@@ -8303,12 +8275,7 @@ impl Compiler {
}) => {
self.compile_comprehension(
"<setcomp>",
Some(
Instruction::BuildSet {
count: OpArgMarker::marker(),
}
.into(),
),
Some(Opcode::BuildSet.into()),
generators,
&|compiler, collection_add_i| {
compiler.compile_comprehension_element(elt)?;
@@ -8337,12 +8304,7 @@ impl Compiler {
}) => {
self.compile_comprehension(
"<dictcomp>",
Some(
Instruction::BuildMap {
count: OpArgMarker::marker(),
}
.into(),
),
Some(Opcode::BuildMap.into()),
generators,
&|compiler, collection_add_i| {
// changed evaluation order for Py38 named expression PEP 572
@@ -9309,9 +9271,7 @@ impl Compiler {
if let Some(info) = self.code_stack.last_mut() {
info.flags = flags
| (info.flags
& (bytecode::CodeFlags::NESTED
| bytecode::CodeFlags::METHOD
| bytecode::CodeFlags::FUTURE_ANNOTATIONS));
& (CodeFlags::NESTED | CodeFlags::METHOD | CodeFlags::FUTURE_ANNOTATIONS));
info.metadata.argcount = arg_count;
info.metadata.posonlyargcount = posonlyarg_count;
info.metadata.kwonlyargcount = kwonlyarg_count;
@@ -9395,9 +9355,9 @@ impl Compiler {
in_async_scope: prev_ctx.in_async_scope || is_async,
};
let flags = bytecode::CodeFlags::NEWLOCALS | bytecode::CodeFlags::OPTIMIZED;
let flags = CodeFlags::NEWLOCALS | CodeFlags::OPTIMIZED;
let flags = if is_async {
flags | bytecode::CodeFlags::COROUTINE
flags | CodeFlags::COROUTINE
} else {
flags
};
@@ -9593,7 +9553,7 @@ impl Compiler {
// Create comprehension function with closure
self.set_source_range(comprehension_range);
self.make_closure(code, bytecode::MakeFunctionFlags::new())?;
self.make_closure(code, MakeFunctionFlags::new())?;
// Evaluate iterated item and get its iterator.
self.compile_comprehension_iter(outermost)?;
@@ -10092,18 +10052,22 @@ impl Compiler {
}
let instr = instr.into();
let opcode = AnyOpcode::from(instr);
debug_assert!(
!instr.is_assembler(),
"CPython codegen_addop_* must not emit assembler-only opcodes"
);
debug_assert!(
opcode.has_arg() || instr.has_target() || u32::from(arg) == 0,
"CPython _PyInstructionSequence_Addop requires either OPCODE_HAS_ARG, HAS_TARGET, or oparg == 0"
);
debug_assert!(
target == BlockIdx::NULL || instr.has_target(),
"CPython codegen_addop_j only accepts HAS_TARGET opcodes"
);
let range = self.current_source_range;
let source = self.source_file.to_source_code();
let location = source.source_location(range.start(), PositionEncoding::Utf8);
@@ -10187,6 +10151,7 @@ impl Compiler {
.blocks
.first_mut()
.expect("code unit must have an entry block");
debug_assert!(
entry
.used_instructions()
@@ -10200,14 +10165,11 @@ impl Compiler {
}),
"scope entry must start with a function-start RESUME"
);
debug_assert!(
!entry.used_instructions().iter().any(|info| matches!(
info.instr.real(),
Some(
Instruction::ReturnGenerator
| Instruction::MakeCell { .. }
| Instruction::CopyFreeVars { .. }
)
info.instr.real_opcode(),
Some(Opcode::ReturnGenerator | Opcode::MakeCell | Opcode::CopyFreeVars)
)),
"CPython inserts StopIteration cleanup before CFG prefix instructions"
);
@@ -10555,7 +10517,7 @@ impl Compiler {
}
(ast::UnaryOp::Not, ConstantData::Tuple { .. }) => return Ok(None),
(ast::UnaryOp::Not, value) => ConstantData::Boolean {
value: !Self::constant_truthiness(&value),
value: !value.truthiness(),
},
_ => return Ok(None),
}
@@ -10575,9 +10537,9 @@ impl Compiler {
let mut selected = first;
match op {
ast::BoolOp::Or => {
if !Self::constant_truthiness(&selected) {
if !selected.truthiness() {
for constant in iter {
let is_truthy = Self::constant_truthiness(&constant);
let is_truthy = constant.truthiness();
selected = constant;
if is_truthy {
break;
@@ -10586,9 +10548,9 @@ impl Compiler {
}
}
ast::BoolOp::And => {
if Self::constant_truthiness(&selected) {
if selected.truthiness() {
for constant in iter {
let is_truthy = Self::constant_truthiness(&constant);
let is_truthy = constant.truthiness();
selected = constant;
if !is_truthy {
break;
@@ -10914,12 +10876,17 @@ impl Compiler {
_ => {}
}
}
if !found_loop {
if is_break {
return Err(self.error_ranged(CodegenErrorType::InvalidBreak, range));
}
return Err(self.error_ranged(CodegenErrorType::InvalidContinue, range));
let err_type = if is_break {
CodegenErrorType::InvalidBreak
} else {
CodegenErrorType::InvalidContinue
};
return Err(self.error_ranged(err_type, range));
}
return Ok(());
}
@@ -10956,12 +10923,7 @@ impl Compiler {
} else {
self.set_source_range(range);
};
self.emit_jump_label(
PseudoInstruction::Jump {
delta: OpArgMarker::marker(),
},
target_label,
);
self.emit_jump_label(PseudoOpcode::Jump, target_label);
if unwind_loc.is_none() {
self.set_no_location();
}
@@ -11191,10 +11153,10 @@ impl Compiler {
let is_async = self.ctx.func == FunctionContext::AsyncFunction;
let flags = &mut self.current_code_info().flags;
if is_async {
flags.remove(bytecode::CodeFlags::COROUTINE);
flags.insert(bytecode::CodeFlags::ASYNC_GENERATOR);
flags.remove(CodeFlags::COROUTINE);
flags.insert(CodeFlags::ASYNC_GENERATOR);
} else {
flags.insert(bytecode::CodeFlags::GENERATOR);
flags.insert(CodeFlags::GENERATOR);
}
}
@@ -12238,10 +12200,8 @@ mod tests {
fn assert_scope_exit_locations(code: &CodeObject) {
for (instr, (location, _)) in code.instructions.iter().zip(code.locations.iter()) {
if matches!(
instr.op,
Instruction::ReturnValue
| Instruction::RaiseVarargs { .. }
| Instruction::Reraise { .. }
instr.op.into(),
Opcode::ReturnValue | Opcode::RaiseVarargs | Opcode::Reraise
) {
assert!(
location.line.get() > 0,
@@ -12439,7 +12399,7 @@ def f(x, y, z):
compiler
.current_code_info()
.flags
.set(bytecode::CodeFlags::COROUTINE, is_async);
.set(CodeFlags::COROUTINE, is_async);
let prev_ctx = compiler.ctx;
compiler.ctx = CompileContext {
@@ -14757,13 +14717,13 @@ def f():
for code in [method, async_method, lambda, genexpr] {
assert!(
code.flags.contains(bytecode::CodeFlags::METHOD),
code.flags.contains(CodeFlags::METHOD),
"class-scope function-like code should carry CO_METHOD like CPython 3.14, got {:?}",
code.flags
);
}
assert!(
!module_function.flags.contains(bytecode::CodeFlags::METHOD),
!module_function.flags.contains(CodeFlags::METHOD),
"module-scope function must not carry CO_METHOD"
);
}
@@ -14782,11 +14742,11 @@ class C:
let class_code = find_code(&code, "C").expect("missing class code");
let lambda = find_code(class_code, "<lambda>").expect("missing lambda code");
assert!(
lambda.flags.contains(bytecode::CodeFlags::NESTED),
lambda.flags.contains(CodeFlags::NESTED),
"lambda under inlined class comprehension should stay nested"
);
assert!(
!lambda.flags.contains(bytecode::CodeFlags::METHOD),
!lambda.flags.contains(CodeFlags::METHOD),
"CPython creates this lambda while the current symtable block is the comprehension, not the class"
);
}
@@ -14822,37 +14782,17 @@ async def ag():
let coroutine = find_code(&code, "c").expect("missing coroutine code");
let async_generator = find_code(&code, "ag").expect("missing async generator code");
assert!(generator.flags.contains(bytecode::CodeFlags::GENERATOR));
assert!(!generator.flags.contains(bytecode::CodeFlags::COROUTINE));
assert!(
!generator
.flags
.contains(bytecode::CodeFlags::ASYNC_GENERATOR)
);
assert!(generator.flags.contains(CodeFlags::GENERATOR));
assert!(!generator.flags.contains(CodeFlags::COROUTINE));
assert!(!generator.flags.contains(CodeFlags::ASYNC_GENERATOR));
assert!(coroutine.flags.contains(bytecode::CodeFlags::COROUTINE));
assert!(!coroutine.flags.contains(bytecode::CodeFlags::GENERATOR));
assert!(
!coroutine
.flags
.contains(bytecode::CodeFlags::ASYNC_GENERATOR)
);
assert!(coroutine.flags.contains(CodeFlags::COROUTINE));
assert!(!coroutine.flags.contains(CodeFlags::GENERATOR));
assert!(!coroutine.flags.contains(CodeFlags::ASYNC_GENERATOR));
assert!(
async_generator
.flags
.contains(bytecode::CodeFlags::ASYNC_GENERATOR)
);
assert!(
!async_generator
.flags
.contains(bytecode::CodeFlags::GENERATOR)
);
assert!(
!async_generator
.flags
.contains(bytecode::CodeFlags::COROUTINE)
);
assert!(async_generator.flags.contains(CodeFlags::ASYNC_GENERATOR));
assert!(!async_generator.flags.contains(CodeFlags::GENERATOR));
assert!(!async_generator.flags.contains(CodeFlags::COROUTINE));
}
#[test]
@@ -24093,15 +24033,11 @@ def f():
return C
",
);
assert!(code.flags.contains(bytecode::CodeFlags::FUTURE_ANNOTATIONS));
assert!(code.flags.contains(CodeFlags::FUTURE_ANNOTATIONS));
let f = find_code(&code, "f").expect("missing f code");
assert!(f.flags.contains(bytecode::CodeFlags::FUTURE_ANNOTATIONS));
assert!(f.flags.contains(CodeFlags::FUTURE_ANNOTATIONS));
let class_code = find_code(f, "C").expect("missing C code");
assert!(
class_code
.flags
.contains(bytecode::CodeFlags::FUTURE_ANNOTATIONS)
);
assert!(class_code.flags.contains(CodeFlags::FUTURE_ANNOTATIONS));
}
#[test]
@@ -24120,7 +24056,7 @@ def outer():
let class_annotate =
find_code(class_code, "__annotate__").expect("missing class annotation code");
assert!(
!class_annotate.flags.contains(bytecode::CodeFlags::NESTED),
!class_annotate.flags.contains(CodeFlags::NESTED),
"module-level class annotation scope should not be nested"
);
@@ -24129,7 +24065,7 @@ def outer():
let nested_annotate =
find_code(nested_class, "__annotate__").expect("missing nested annotation code");
assert!(
nested_annotate.flags.contains(bytecode::CodeFlags::NESTED),
nested_annotate.flags.contains(CodeFlags::NESTED),
"annotation scope under a nested class should be nested"
);
}
@@ -24144,25 +24080,25 @@ type A[T] = T
);
let outer_lambda = find_code(&code, "<lambda>").expect("missing outer lambda code");
assert!(
!outer_lambda.flags.contains(bytecode::CodeFlags::NESTED),
!outer_lambda.flags.contains(CodeFlags::NESTED),
"module-level lambda should not be nested"
);
let inner_lambda =
find_direct_child_code(outer_lambda, "<lambda>").expect("missing inner lambda code");
assert!(
inner_lambda.flags.contains(bytecode::CodeFlags::NESTED),
inner_lambda.flags.contains(CodeFlags::NESTED),
"lambda inside lambda should be nested"
);
let type_params =
find_code(&code, "<generic parameters of A>").expect("missing type params code");
assert!(
!type_params.flags.contains(bytecode::CodeFlags::NESTED),
!type_params.flags.contains(CodeFlags::NESTED),
"module-level type-parameter scope should not be nested"
);
let type_alias = find_direct_child_code(type_params, "A").expect("missing type alias code");
assert!(
type_alias.flags.contains(bytecode::CodeFlags::NESTED),
type_alias.flags.contains(CodeFlags::NESTED),
"type alias body inside type-parameter scope should be nested"
);
}
+112 -233
View File
@@ -9,10 +9,10 @@ use rustpython_wtf8::Wtf8Buf;
use rustpython_compiler_core::{
OneIndexed, SourceLocation,
bytecode::{
AnyInstruction, AnyOpcode, Arg, CO_FAST_ARG_KW, CO_FAST_ARG_POS, CO_FAST_ARG_VAR,
CO_FAST_CELL, CO_FAST_FREE, CO_FAST_HIDDEN, CO_FAST_LOCAL, CodeFlags, CodeObject, CodeUnit,
CodeUnits, ConstantData, InstrDisplayContext, Instruction, IntrinsicFunction1, OpArg,
OpArgByte, Opcode, PseudoInstruction, PseudoOpcode, PyCodeLocationInfoKind, oparg,
AnyInstruction, AnyOpcode, CO_FAST_ARG_KW, CO_FAST_ARG_POS, CO_FAST_ARG_VAR, CO_FAST_CELL,
CO_FAST_FREE, CO_FAST_HIDDEN, CO_FAST_LOCAL, CodeFlags, CodeObject, CodeUnit, CodeUnits,
ConstantData, InstrDisplayContext, Instruction, IntrinsicFunction1, OpArg, OpArgByte,
Opcode, PseudoInstruction, PseudoOpcode, PyCodeLocationInfoKind, oparg,
},
varint::{write_signed_varint, write_varint},
};
@@ -832,7 +832,7 @@ fn instr_size(instr: &InstructionInfo) -> usize {
}
/// pycore_opcode_metadata.h is_pseudo_target
fn is_pseudo_target(pseudo: PseudoOpcode, target: Opcode) -> bool {
const fn is_pseudo_target(pseudo: PseudoOpcode, target: Opcode) -> bool {
match pseudo {
PseudoOpcode::LoadClosure => matches!(target, Opcode::LoadFast),
PseudoOpcode::StoreFastMaybeNull => matches!(target, Opcode::StoreFast),
@@ -874,16 +874,11 @@ fn resolve_unconditional_jumps(
AnyInstruction::Pseudo(PseudoInstruction::Jump { .. }) => {
debug_assert!(is_pseudo_target(PseudoOpcode::Jump, Opcode::JumpForward));
debug_assert!(is_pseudo_target(PseudoOpcode::Jump, Opcode::JumpBackward));
if is_forward {
instr.instr = Instruction::JumpForward {
delta: Arg::marker(),
}
.into();
instr.instr = Opcode::JumpForward.into();
} else {
instr.instr = Instruction::JumpBackward {
delta: Arg::marker(),
}
.into();
instr.instr = Opcode::JumpBackward.into();
}
}
AnyInstruction::Pseudo(PseudoInstruction::JumpNoInterrupt { .. }) => {
@@ -896,15 +891,9 @@ fn resolve_unconditional_jumps(
Opcode::JumpBackwardNoInterrupt
));
if is_forward {
instr.instr = Instruction::JumpForward {
delta: Arg::marker(),
}
.into();
instr.instr = Opcode::JumpForward.into();
} else {
instr.instr = Instruction::JumpBackwardNoInterrupt {
delta: Arg::marker(),
}
.into();
instr.instr = Opcode::JumpBackwardNoInterrupt.into();
}
}
_ => {
@@ -1319,7 +1308,8 @@ impl Block {
&self.instructions[..self.instruction_used]
}
pub(crate) fn is_empty(&self) -> bool {
#[must_use]
pub(crate) const fn is_empty(&self) -> bool {
self.instruction_used == 0
}
}
@@ -1717,10 +1707,7 @@ impl CodeInfo {
&mut self.instr_sequence,
0,
InstructionInfo {
instr: PseudoInstruction::SetupCleanup {
delta: Arg::marker(),
}
.into(),
instr: PseudoOpcode::SetupCleanup.into(),
arg: instruction_sequence_label_oparg(handler_label),
target: BlockIdx::NULL,
location: SourceLocation::default(),
@@ -1851,7 +1838,6 @@ fn optimized_cfg_to_instruction_sequence(
}
impl CodeInfo {
#[allow(clippy::needless_range_loop)]
pub fn finalize_code(
mut self,
opts: &crate::compile::CompileOpts,
@@ -2016,7 +2002,7 @@ fn insert_prefix_instructions(
entry,
ncellsused,
InstructionInfo {
instr: Instruction::MakeCell { i: Arg::marker() }.into(),
instr: Opcode::MakeCell.into(),
arg: OpArg::new(oldindex as u32),
target: BlockIdx::NULL,
location: SourceLocation::default(),
@@ -2034,7 +2020,7 @@ fn insert_prefix_instructions(
entry,
0,
InstructionInfo {
instr: Instruction::CopyFreeVars { n: Arg::marker() }.into(),
instr: Opcode::CopyFreeVars.into(),
arg: OpArg::new(nfreevars as u32),
target: BlockIdx::NULL,
location: SourceLocation::default(),
@@ -2153,7 +2139,7 @@ fn eval_const_unaryop(
}
(ConstantData::Boolean { .. }, Instruction::UnaryInvert, None) => None,
(_, Instruction::UnaryNot, None) => Some(ConstantData::Boolean {
value: !constant_truthiness(operand),
value: !operand.truthiness(),
}),
(
ConstantData::Integer { value },
@@ -2183,22 +2169,6 @@ fn eval_const_unaryop(
}
}
fn constant_truthiness(constant: &ConstantData) -> bool {
match constant {
ConstantData::Tuple { elements } | ConstantData::Frozenset { elements } => {
!elements.is_empty()
}
ConstantData::Integer { value } => !value.is_zero(),
ConstantData::Float { value } => *value != 0.0,
ConstantData::Complex { value } => value.re != 0.0 || value.im != 0.0,
ConstantData::Boolean { value } => *value,
ConstantData::Str { value } => !value.is_empty(),
ConstantData::Bytes { value } => !value.is_empty(),
ConstantData::Code { .. } | ConstantData::Slice { .. } | ConstantData::Ellipsis => true,
ConstantData::None => false,
}
}
fn load_const_truthiness(
instr: Instruction,
arg: OpArg,
@@ -2207,7 +2177,7 @@ fn load_const_truthiness(
match instr {
Instruction::LoadConst { consti } => {
let constant = &metadata.consts[consti.get(arg).as_usize()];
Some(constant_truthiness(constant))
Some(constant.truthiness())
}
Instruction::LoadSmallInt { i } => Some(i.get(arg) != 0),
_ => None,
@@ -2234,10 +2204,7 @@ fn instr_make_load_const(
let const_idx = add_const(metadata, constant)?;
instr_set_op1(
instr,
Instruction::LoadConst {
consti: Arg::marker(),
}
.into(),
Opcode::LoadConst.into(),
OpArg::new(const_idx as u32),
);
Ok(())
@@ -2260,12 +2227,7 @@ fn fold_const_unaryop(
oparg::IntrinsicFunction1::UnaryPositive
) =>
{
(
Instruction::CallIntrinsic1 {
func: Arg::marker(),
},
Some(func.get(instr.arg)),
)
(Opcode::CallIntrinsic1.into(), Some(func.get(instr.arg)))
}
_ => return Ok(false),
};
@@ -2337,9 +2299,10 @@ fn fold_const_binop(
) -> crate::InternalResult<bool> {
use oparg::BinaryOperator as BinOp;
let Some(Instruction::BinaryOp { .. }) = block.instructions[i].instr.real() else {
let Some(Opcode::BinaryOp) = block.instructions[i].instr.real_opcode() else {
return Ok(false);
};
let Some(operand_indices) = (if let Some(start) = i.checked_sub(1) {
get_const_loading_instrs(block, start, 2)?
} else {
@@ -2347,18 +2310,22 @@ fn fold_const_binop(
}) else {
return Ok(false);
};
let op_raw = u32::from(block.instructions[i].arg);
let Ok(op) = BinOp::try_from(op_raw) else {
return Ok(false);
};
let left = get_const_value(metadata, &block.instructions[operand_indices[0]]);
let right = get_const_value(metadata, &block.instructions[operand_indices[1]]);
let (Some(left_val), Some(right_val)) = (left, right) else {
return Ok(false);
};
let Some(result_const) = eval_const_binop(&left_val, &right_val, op) else {
return Ok(false);
};
nop_out(block, &operand_indices);
instr_make_load_const(metadata, &mut block.instructions[i], result_const)?;
Ok(true)
@@ -2366,13 +2333,13 @@ fn fold_const_binop(
/// flowgraph.c loads_const
fn loads_const(info: &InstructionInfo) -> bool {
info.instr.has_const() || matches!(info.instr.real(), Some(Instruction::LoadSmallInt { .. }))
info.instr.has_const() || matches!(info.instr.real_opcode(), Some(Opcode::LoadSmallInt))
}
/// flowgraph.c get_const_value
fn get_const_value(metadata: &CodeUnitMetadata, info: &InstructionInfo) -> Option<ConstantData> {
match info.instr.real() {
Some(Instruction::LoadSmallInt { .. }) => {
match info.instr.real_opcode() {
Some(Opcode::LoadSmallInt) => {
let v = u32::from(info.arg) as i32;
Some(ConstantData::Integer {
value: BigInt::from(v),
@@ -3059,7 +3026,7 @@ fn fold_tuple_of_constants(
block: &mut Block,
i: usize,
) -> crate::InternalResult<bool> {
let Some(Instruction::BuildTuple { .. }) = block.instructions[i].instr.real() else {
let Some(Opcode::BuildTuple) = block.instructions[i].instr.real_opcode() else {
return Ok(false);
};
@@ -3237,16 +3204,11 @@ fn optimize_lists_and_sets(
nop_out(block, &operand_indices);
let build_instr = if is_list {
Instruction::BuildList {
count: Arg::marker(),
}
.into()
Opcode::BuildList
} else {
Instruction::BuildSet {
count: Arg::marker(),
}
.into()
};
Opcode::BuildSet
}
.into();
instr_set_op1(&mut block.instructions[i - 2], build_instr, OpArg::new(0));
block.instructions[i - 2].location = folded_loc;
block.instructions[i - 2].end_location = end_loc;
@@ -3254,10 +3216,7 @@ fn optimize_lists_and_sets(
instr_set_op1(
&mut block.instructions[i - 1],
Instruction::LoadConst {
consti: Arg::marker(),
}
.into(),
Opcode::LoadConst.into(),
OpArg::new(const_idx as u32),
);
@@ -3278,10 +3237,7 @@ fn optimize_lists_and_sets(
instr_set_op1(
&mut block.instructions[i],
Instruction::LoadConst {
consti: Arg::marker(),
}
.into(),
Opcode::LoadConst.into(),
OpArg::new(const_idx as u32),
);
Ok(true)
@@ -3338,21 +3294,21 @@ fn next_swappable_instruction(block: &Block, mut i: usize, lineno: i32) -> Optio
/// flowgraph.c swaptimize
fn swaptimize(block: &mut Block, ix: &mut usize) -> crate::InternalResult<()> {
debug_assert!(matches!(
block.instructions[*ix].instr.real(),
Some(Instruction::Swap { .. })
block.instructions[*ix].instr.real_opcode(),
Some(Opcode::Swap)
));
let mut depth = u32::from(block.instructions[*ix].arg) as usize;
let mut len = 1usize;
let mut more = false;
let limit = block.instruction_used - *ix;
while len < limit {
match block.instructions[*ix + len].instr.real() {
Some(Instruction::Swap { .. }) => {
match block.instructions[*ix + len].instr.real_opcode() {
Some(Opcode::Swap) => {
depth = depth.max(u32::from(block.instructions[*ix + len].arg) as usize);
more = true;
len += 1;
}
Some(Instruction::Nop) => {
Some(Opcode::Nop) => {
len += 1;
}
_ => break,
@@ -3377,7 +3333,7 @@ fn swaptimize(block: &mut Block, ix: &mut usize) -> crate::InternalResult<()> {
i = 0;
while i < len {
let info = &block.instructions[*ix + i];
if matches!(info.instr.real(), Some(Instruction::Swap { .. })) {
if matches!(info.instr.real_opcode(), Some(Opcode::Swap)) {
let oparg = u32::from(info.arg) as usize;
stack.swap(0, oparg - 1);
}
@@ -3421,15 +3377,15 @@ fn apply_static_swaps(block: &mut Block, mut i: isize) {
while i >= 0 {
let idx = i as usize;
debug_assert!(idx < block.instruction_used);
let swap_arg = match block.instructions[idx].instr.real() {
Some(Instruction::Swap { .. }) => u32::from(block.instructions[idx].arg),
Some(Instruction::Nop | Instruction::PopTop | Instruction::StoreFast { .. }) => {
let swap_arg = match block.instructions[idx].instr.real_opcode() {
Some(Opcode::Swap) => u32::from(block.instructions[idx].arg),
Some(Opcode::Nop | Opcode::PopTop | Opcode::StoreFast) => {
i -= 1;
continue;
}
_ if matches!(
block.instructions[idx].instr.pseudo(),
Some(PseudoInstruction::StoreFastMaybeNull { .. })
block.instructions[idx].instr.pseudo_opcode(),
Some(PseudoOpcode::StoreFastMaybeNull)
) =>
{
i -= 1;
@@ -3477,8 +3433,8 @@ fn apply_static_swaps_block(block: &mut Block) -> crate::InternalResult<()> {
let mut i = 0;
while i < block.instruction_used {
if matches!(
block.instructions[i].instr.real(),
Some(Instruction::Swap { .. })
block.instructions[i].instr.real_opcode(),
Some(Opcode::Swap)
) {
swaptimize(block, &mut i)?;
apply_static_swaps(block, i as isize);
@@ -3548,11 +3504,11 @@ fn basicblock_optimize_load_const(
let next_arg = next.arg;
if let Some(is_true) = load_const_truthiness(const_instr, const_arg, metadata) {
let const_jump = match (next.instr.real(), next.instr.pseudo()) {
(_, Some(PseudoInstruction::JumpIfTrue { .. })) => Some((true, false)),
(_, Some(PseudoInstruction::JumpIfFalse { .. })) => Some((false, false)),
(Some(Instruction::PopJumpIfTrue { .. }), _) => Some((true, true)),
(Some(Instruction::PopJumpIfFalse { .. }), _) => Some((false, true)),
let const_jump = match (next.instr.real_opcode(), next.instr.pseudo_opcode()) {
(_, Some(PseudoOpcode::JumpIfTrue)) => Some((true, false)),
(_, Some(PseudoOpcode::JumpIfFalse)) => Some((false, false)),
(Some(Opcode::PopJumpIfTrue), _) => Some((true, true)),
(Some(Opcode::PopJumpIfFalse), _) => Some((false, true)),
_ => None,
};
if let Some((jump_if_true, pops_condition)) = const_jump {
@@ -3560,10 +3516,7 @@ fn basicblock_optimize_load_const(
set_to_nop(&mut block.instructions[i]);
}
if is_true == jump_if_true {
block.instructions[i + 1].instr = PseudoInstruction::Jump {
delta: Arg::marker(),
}
.into();
block.instructions[i + 1].instr = PseudoOpcode::Jump.into();
} else {
set_to_nop(&mut block.instructions[i + 1]);
}
@@ -3624,13 +3577,9 @@ fn basicblock_optimize_load_const(
set_to_nop(&mut block.instructions[i]);
set_to_nop(&mut block.instructions[i + 1]);
block.instructions[jump_idx].instr = if invert {
Instruction::PopJumpIfNotNone {
delta: Arg::marker(),
}
Opcode::PopJumpIfNotNone
} else {
Instruction::PopJumpIfNone {
delta: Arg::marker(),
}
Opcode::PopJumpIfNone
}
.into();
i = jump_idx;
@@ -3648,10 +3597,7 @@ fn basicblock_optimize_load_const(
set_to_nop(&mut block.instructions[i]);
instr_set_op1(
&mut block.instructions[i + 1],
Instruction::LoadConst {
consti: Arg::marker(),
}
.into(),
Opcode::LoadConst.into(),
OpArg::new(const_idx as u32),
);
i += 1;
@@ -3729,8 +3675,7 @@ fn optimize_basic_block(
}
2 | 3 => {
set_to_nop(&mut blocks[bi].instructions[i]);
blocks[bi].instructions[i + 1].instr =
Instruction::Swap { i: Arg::marker() }.into();
blocks[bi].instructions[i + 1].instr = Opcode::Swap.into();
i += 1;
continue;
}
@@ -3925,7 +3870,6 @@ fn optimize_basic_block(
}
/// flowgraph.c remove_redundant_nops_and_pairs
#[allow(clippy::if_same_then_else, clippy::useless_let_if_seq)]
#[allow(clippy::unnecessary_wraps)]
fn remove_redundant_nops_and_pairs(blocks: &mut [Block]) -> crate::InternalResult<()> {
let mut done = false;
@@ -3947,29 +3891,21 @@ fn remove_redundant_nops_and_pairs(blocks: &mut [Block]) -> crate::InternalResul
instr = Some((block_idx, instr_idx));
let instr_info = blocks[block_idx.idx()].instructions[instr_idx];
let mut prev_opcode = None;
let mut prev_oparg = 0;
if let Some((prev_block, prev_instr_idx)) = prev_instr {
let prev_oparg = if let Some((prev_block, prev_instr_idx)) = prev_instr {
let prev_info = blocks[prev_block.idx()].instructions[prev_instr_idx];
prev_opcode = prev_info.instr.real();
prev_oparg = match prev_info.instr.real() {
prev_opcode = prev_info.instr.real_opcode();
match prev_info.instr.real() {
Some(Instruction::Copy { i }) => i.get(prev_info.arg),
_ => u32::from(prev_info.arg),
};
}
let opcode = instr_info.instr.real();
let mut is_redundant_pair = false;
if matches!(opcode, Some(Instruction::PopTop)) {
if matches!(
prev_opcode,
Some(Instruction::LoadConst { .. } | Instruction::LoadSmallInt { .. })
) {
is_redundant_pair = true;
} else if matches!(prev_opcode, Some(Instruction::Copy { .. }))
&& prev_oparg == 1
{
is_redundant_pair = true;
}
}
} else {
0
};
let opcode = instr_info.instr.real_opcode();
let is_redundant_pair = matches!(opcode, Some(Opcode::PopTop))
&& (matches!(prev_opcode, Some(Opcode::LoadConst | Opcode::LoadSmallInt))
|| (prev_oparg == 1 && matches!(prev_opcode, Some(Opcode::Copy))));
if is_redundant_pair {
let (prev_block, prev_instr_idx) =
@@ -3980,10 +3916,10 @@ fn remove_redundant_nops_and_pairs(blocks: &mut [Block]) -> crate::InternalResul
}
}
let mut instr_is_jump = false;
if let Some((instr_block, instr_idx)) = instr {
instr_is_jump = is_jump(&blocks[instr_block.idx()].instructions[instr_idx]);
}
let instr_is_jump = instr.is_some_and(|(instr_block, instr_idx)| {
is_jump(&blocks[instr_block.idx()].instructions[instr_idx])
});
let block = &blocks[block_idx.idx()];
if instr_is_jump || !bb_has_fallthrough(block) {
instr = None;
@@ -4327,18 +4263,13 @@ fn optimize_load_fast(blocks: &mut [Block]) -> crate::InternalResult<()> {
i += 1;
continue;
}
match info.instr.real() {
Some(Instruction::LoadFast { .. }) => {
info.instr = Instruction::LoadFastBorrow {
var_num: Arg::marker(),
}
.into();
match info.instr.real_opcode() {
Some(Opcode::LoadFast) => {
info.instr = Opcode::LoadFastBorrow.into();
}
Some(Instruction::LoadFastLoadFast { .. }) => {
info.instr = Instruction::LoadFastBorrowLoadFastBorrow {
var_nums: Arg::marker(),
}
.into();
Some(Opcode::LoadFastLoadFast) => {
info.instr = Opcode::LoadFastBorrowLoadFastBorrow.into();
}
_ => {}
}
@@ -4621,10 +4552,7 @@ fn insert_superinstructions(blocks: &mut [Block]) -> crate::InternalResult<usize
make_super_instruction(
&mut inst1[0],
&mut rest[0],
Instruction::LoadFastLoadFast {
var_nums: Arg::marker(),
}
.into(),
Opcode::LoadFastLoadFast.into(),
);
}
}
@@ -4634,10 +4562,7 @@ fn insert_superinstructions(blocks: &mut [Block]) -> crate::InternalResult<usize
make_super_instruction(
&mut inst1[0],
&mut rest[0],
Instruction::StoreFastLoadFast {
var_nums: Arg::marker(),
}
.into(),
Opcode::StoreFastLoadFast.into(),
);
}
Some(Instruction::StoreFast { .. }) => {
@@ -4645,10 +4570,7 @@ fn insert_superinstructions(blocks: &mut [Block]) -> crate::InternalResult<usize
make_super_instruction(
&mut inst1[0],
&mut rest[0],
Instruction::StoreFastStoreFast {
var_nums: Arg::marker(),
}
.into(),
Opcode::StoreFastStoreFast.into(),
);
}
_ => {}
@@ -4665,6 +4587,7 @@ fn insert_superinstructions(blocks: &mut [Block]) -> crate::InternalResult<usize
}
/// flowgraph.c LoadFastInstrFlag
#[derive(Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
enum LoadFastInstrFlag {
SupportKilled = 1,
@@ -4795,6 +4718,7 @@ fn stackdepth_push(
}
/// flowgraph.c stack_effects
#[derive(Clone, Copy, Eq, PartialEq)]
struct StackEffects {
net: i32,
}
@@ -5468,29 +5392,24 @@ fn convert_pseudo_conditional_jumps(blocks: &mut [Block]) -> crate::InternalResu
let instr = block.instructions[i];
let opcode = instr.instr;
if matches!(
opcode.pseudo(),
Some(PseudoInstruction::JumpIfFalse { .. } | PseudoInstruction::JumpIfTrue { .. })
opcode.pseudo_opcode(),
Some(PseudoOpcode::JumpIfFalse | PseudoOpcode::JumpIfTrue)
) {
debug_assert_eq!(i, block.instruction_used - 1);
block.instructions[i].instr =
if matches!(opcode.pseudo(), Some(PseudoInstruction::JumpIfFalse { .. })) {
Instruction::PopJumpIfFalse {
delta: Arg::marker(),
}
.into()
if matches!(opcode.pseudo_opcode(), Some(PseudoOpcode::JumpIfFalse)) {
Opcode::PopJumpIfFalse
} else {
Instruction::PopJumpIfTrue {
delta: Arg::marker(),
}
.into()
};
Opcode::PopJumpIfTrue
}
.into();
let location = instr.location;
let end_location = instr.end_location;
let except_handler = instr.except_handler;
let lineno_override = instr.lineno_override;
let copy = InstructionInfo {
instr: Instruction::Copy { i: Arg::marker() }.into(),
instr: Opcode::Copy.into(),
arg: OpArg::new(1),
target: BlockIdx::NULL,
location,
@@ -5502,7 +5421,7 @@ fn convert_pseudo_conditional_jumps(blocks: &mut [Block]) -> crate::InternalResu
i += 1;
let to_bool = InstructionInfo {
instr: Instruction::ToBool.into(),
instr: Opcode::ToBool.into(),
arg: OpArg::new(0),
target: BlockIdx::NULL,
location,
@@ -5552,7 +5471,7 @@ fn normalize_jumps_in_block(
return Ok(());
}
let reversed_opcode = match AnyOpcode::from(last_ins.instr).real() {
let reversed_opcode = match last_ins.instr.real_opcode() {
Some(Opcode::PopJumpIfNotNone) => Opcode::PopJumpIfNone.into(),
Some(Opcode::PopJumpIfNone) => Opcode::PopJumpIfNotNone.into(),
Some(Opcode::PopJumpIfFalse) => Opcode::PopJumpIfTrue.into(),
@@ -5770,10 +5689,7 @@ fn remove_redundant_nops(blocks: &mut [Block]) -> crate::InternalResult<usize> {
/// flowgraph.c no_redundant_nops
#[cfg(debug_assertions)]
fn no_redundant_nops(blocks: &mut [Block]) -> bool {
match remove_redundant_nops(blocks) {
Ok(0) => true,
Ok(_) | Err(_) => false,
}
matches!(remove_redundant_nops(blocks), Ok(0))
}
/// flowgraph.c remove_redundant_jumps
@@ -6745,10 +6661,7 @@ pub(crate) fn convert_pseudo_ops(blocks: &mut [Block]) -> crate::InternalResult<
PseudoOpcode::StoreFastMaybeNull,
Opcode::StoreFast
));
info.instr = Instruction::StoreFast {
var_num: Arg::marker(),
}
.into();
info.instr = Opcode::StoreFast.into();
}
}
block_idx = next;
@@ -6972,10 +6885,7 @@ mod tests {
assert!(except_stack_top(&stack, &blocks).is_none());
let setup = InstructionInfo {
instr: PseudoInstruction::SetupWith {
delta: Arg::marker(),
}
.into(),
instr: PseudoOpcode::SetupWith.into(),
arg: OpArg::new(0),
target: BlockIdx::new(1),
location: SourceLocation::default(),
@@ -7294,14 +7204,9 @@ mod tests {
#[test]
fn static_swaps_respect_cpython_no_location_line_boundary() {
let mut block = Block::default();
let mut swap = test_instr(Instruction::Swap { i: Arg::marker() }, 60);
let mut swap = test_instr(Opcode::Swap.into(), 60);
swap.arg = OpArg::new(2);
let mut store = test_instr(
Instruction::StoreFast {
var_num: Arg::marker(),
},
60,
);
let mut store = test_instr(Opcode::StoreFast.into(), 60);
store.arg = OpArg::new(0);
let mut pop = test_instr(Instruction::PopTop, 60);
pop.lineno_override = Some(NO_LOCATION_OVERRIDE);
@@ -7328,14 +7233,9 @@ mod tests {
));
let mut block = Block::default();
let mut swap = test_instr(Instruction::Swap { i: Arg::marker() }, 70);
let mut swap = test_instr(Opcode::Swap.into(), 70);
swap.arg = OpArg::new(2);
let mut store = test_instr(
Instruction::StoreFast {
var_num: Arg::marker(),
},
70,
);
let mut store = test_instr(Opcode::StoreFast.into(), 70);
store.arg = OpArg::new(0);
store.lineno_override = Some(NO_LOCATION_OVERRIDE);
let pop = test_instr(Instruction::PopTop, 71);
@@ -7348,32 +7248,24 @@ mod tests {
// Conversely, when the first swaperand has NO_LOCATION, CPython passes
// `-1` as the line filter and does not enforce a boundary.
assert!(matches!(
block.instructions[0].instr.real(),
Some(Instruction::Nop)
block.instructions[0].instr.real_opcode(),
Some(Opcode::Nop)
));
assert!(matches!(
block.instructions[1].instr.real(),
Some(Instruction::PopTop)
block.instructions[1].instr.real_opcode(),
Some(Opcode::PopTop)
));
assert!(matches!(
block.instructions[2].instr.real(),
Some(Instruction::StoreFast { .. })
block.instructions[2].instr.real_opcode(),
Some(Opcode::StoreFast)
));
}
#[test]
fn optimize_load_const_tracks_cpython_copy_of_load_const() {
let mut block = Block::default();
test_block_push(
&mut block,
test_instr(
Instruction::LoadConst {
consti: Arg::marker(),
},
80,
),
);
let mut copy = test_instr(Instruction::Copy { i: Arg::marker() }, 80);
test_block_push(&mut block, test_instr(Opcode::LoadConst.into(), 80));
let mut copy = test_instr(Opcode::Copy.into(), 80);
copy.arg = OpArg::new(1);
test_block_push(&mut block, copy);
test_block_push(&mut block, test_instr(Instruction::ToBool, 80));
@@ -7414,17 +7306,9 @@ mod tests {
#[test]
fn optimize_load_fast_records_no_input_opcode_ref_at_cpython_produced_index() {
let mut block = Block::default();
test_block_push(
&mut block,
test_instr(
Instruction::LoadFast {
var_num: Arg::marker(),
},
10,
),
);
test_block_push(&mut block, test_instr(Opcode::LoadFast.into(), 10));
test_block_push(&mut block, test_instr(Instruction::GetLen, 10));
let mut swap = test_instr(Instruction::Swap { i: Arg::marker() }, 10);
let mut swap = test_instr(Opcode::Swap.into(), 10);
swap.arg = OpArg::new(2);
test_block_push(&mut block, swap);
test_block_push(&mut block, test_instr(Instruction::PopTop, 10));
@@ -7472,12 +7356,7 @@ mod tests {
let mut mortal = test_instr(Instruction::Nop, 90);
mortal.instr = Opcode::LoadConstMortal.into();
mortal.arg = OpArg::new(right as u32);
let mut build = test_instr(
Instruction::BuildTuple {
count: Arg::marker(),
},
90,
);
let mut build = test_instr(Opcode::BuildTuple.into(), 90);
build.arg = OpArg::new(2);
let mut block = Block::default();
for info in [immortal, mortal, build] {
+1
View File
@@ -13,6 +13,7 @@ use rustpython_wtf8::{CodePoint, Wtf8, Wtf8Buf};
// use ruff_python_parser::{LexicalError, LexicalErrorType};
type LexicalError = Infallible;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum EscapedChar {
Literal(CodePoint),
Escape(char),
+1
View File
@@ -18,6 +18,7 @@ bitflagset = { workspace = true }
itertools = { workspace = true }
malachite-bigint = { workspace = true }
num-complex = { workspace = true }
num-traits = { workspace = true }
lz4_flex = { workspace = true }
+26
View File
@@ -17,6 +17,7 @@ use core::{
use itertools::Itertools;
use malachite_bigint::BigInt;
use num_complex::Complex64;
use num_traits::Zero;
use rustpython_wtf8::{Wtf8, Wtf8Buf};
pub use crate::bytecode::{
@@ -909,6 +910,31 @@ pub enum ConstantData {
Ellipsis,
}
impl ConstantData {
/// Whether or not python would return True/False for the given constant data.
///
/// ```py
/// bool(0) # False
/// bool(1) # True
/// bool([]) # False
/// bool(...) # True
/// ```
#[must_use]
pub fn truthiness(&self) -> bool {
match self {
Self::Tuple { elements } | Self::Frozenset { elements } => !elements.is_empty(),
Self::Integer { value } => !value.is_zero(),
Self::Float { value } => *value != 0.0,
Self::Complex { value } => value.re != 0.0 || value.im != 0.0,
Self::Boolean { value } => *value,
Self::Str { value } => !value.is_empty(),
Self::Bytes { value } => !value.is_empty(),
Self::Code { .. } | Self::Slice { .. } | Self::Ellipsis => true,
Self::None => false,
}
}
}
impl PartialEq for ConstantData {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
+12 -11
View File
@@ -13,11 +13,10 @@ use crate::{
pub struct ArgBytesLike(PyBuffer);
impl PyObject {
pub fn try_bytes_like<R>(
&self,
vm: &VirtualMachine,
f: impl FnOnce(&[u8]) -> R,
) -> PyResult<R> {
pub fn try_bytes_like<F, R>(&self, vm: &VirtualMachine, f: F) -> PyResult<R>
where
F: FnOnce(&[u8]) -> R,
{
let buffer = PyBuffer::try_from_borrowed_object(vm, self)?;
buffer
.as_contiguous()
@@ -25,11 +24,10 @@ impl PyObject {
.ok_or_else(|| vm.new_buffer_error("non-contiguous buffer is not a bytes-like object"))
}
pub fn try_rw_bytes_like<R>(
&self,
vm: &VirtualMachine,
f: impl FnOnce(&mut [u8]) -> R,
) -> PyResult<R> {
pub fn try_rw_bytes_like<F, R>(&self, vm: &VirtualMachine, f: F) -> PyResult<R>
where
F: FnOnce(&mut [u8]) -> R,
{
let buffer = PyBuffer::try_from_borrowed_object(vm, self)?;
buffer
.as_contiguous_mut()
@@ -207,7 +205,10 @@ impl ArgAsciiBuffer {
}
#[inline]
pub fn with_ref<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
pub fn with_ref<F, R>(&self, f: F) -> R
where
F: FnOnce(&[u8]) -> R,
{
match self {
Self::String(s) => f(s.as_bytes()),
Self::Buffer(buffer) => buffer.with_ref(f),
+5 -2
View File
@@ -11,8 +11,9 @@ pub trait PyNativeFn:
Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static
{
}
impl<F: Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static> PyNativeFn
for F
impl<F> PyNativeFn for F where
F: Fn(&VirtualMachine, FuncArgs) -> PyResult + PyThreadingConstraint + 'static
{
}
@@ -103,8 +104,10 @@ use sealed::PyNativeFnInternal;
#[doc(hidden)]
pub struct OwnedParam<T>(PhantomData<T>);
#[doc(hidden)]
pub struct BorrowedParam<T>(PhantomData<T>);
#[doc(hidden)]
pub struct RefParam<T>(PhantomData<T>);
+20 -4
View File
@@ -8,7 +8,11 @@ pub enum Either<A, B> {
B(B),
}
impl<A: Borrow<PyObject>, B: Borrow<PyObject>> Borrow<PyObject> for Either<A, B> {
impl<A, B> Borrow<PyObject> for Either<A, B>
where
A: Borrow<PyObject>,
B: Borrow<PyObject>,
{
#[inline(always)]
fn borrow(&self) -> &PyObject {
match self {
@@ -18,7 +22,11 @@ impl<A: Borrow<PyObject>, B: Borrow<PyObject>> Borrow<PyObject> for Either<A, B>
}
}
impl<A: AsRef<PyObject>, B: AsRef<PyObject>> AsRef<PyObject> for Either<A, B> {
impl<A, B> AsRef<PyObject> for Either<A, B>
where
A: AsRef<PyObject>,
B: AsRef<PyObject>,
{
#[inline(always)]
fn as_ref(&self) -> &PyObject {
match self {
@@ -28,7 +36,11 @@ impl<A: AsRef<PyObject>, B: AsRef<PyObject>> AsRef<PyObject> for Either<A, B> {
}
}
impl<A: Into<Self>, B: Into<Self>> From<Either<A, B>> for PyObjectRef {
impl<A, B> From<Either<A, B>> for PyObjectRef
where
A: Into<Self>,
B: Into<Self>,
{
#[inline(always)]
fn from(value: Either<A, B>) -> Self {
match value {
@@ -38,7 +50,11 @@ impl<A: Into<Self>, B: Into<Self>> From<Either<A, B>> for PyObjectRef {
}
}
impl<A: ToPyObject, B: ToPyObject> ToPyObject for Either<A, B> {
impl<A, B> ToPyObject for Either<A, B>
where
A: ToPyObject,
B: ToPyObject,
{
#[inline(always)]
fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
match self {
+1 -3
View File
@@ -1,6 +1,4 @@
/*! Python `attribute` descriptor class. (PyGetSet)
*/
//! Python `attribute` descriptor class. (PyGetSet)
use crate::{
Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
convert::ToPyResult,
+1
View File
@@ -254,6 +254,7 @@ impl PyMethodDef {
all_methods
}
#[must_use]
const fn const_copy(&self) -> Self {
Self {
name: self.name,
+15 -5
View File
@@ -1,14 +1,15 @@
use core::{fmt, ops};
use std::io;
use crate::{
PyObject, PyObjectRef, PyResult, VirtualMachine,
builtins::{PyBaseExceptionRef, PyBytes, PyStr},
common::ascii,
};
use alloc::fmt;
use core::ops;
use std::io;
pub trait Write {
type Error;
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Self::Error>;
}
@@ -24,10 +25,12 @@ impl<T> IoWriter<T> {
impl<T> ops::Deref for IoWriter<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> ops::DerefMut for IoWriter<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
@@ -39,6 +42,7 @@ where
W: io::Write,
{
type Error = io::Error;
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
<W as io::Write>::write_fmt(&mut self.0, args)
}
@@ -46,6 +50,7 @@ where
impl Write for String {
type Error = fmt::Error;
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
<Self as fmt::Write>::write_fmt(self, args)
}
@@ -55,8 +60,10 @@ pub struct PyWriter<'vm>(pub PyObjectRef, pub &'vm VirtualMachine);
impl Write for PyWriter<'_> {
type Error = PyBaseExceptionRef;
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Self::Error> {
let PyWriter(obj, vm) = self;
let Self(obj, vm) = self;
vm.call_method(obj, "write", (args.to_string(),)).map(drop)
}
}
@@ -70,6 +77,7 @@ pub fn file_readline(obj: &PyObject, size: Option<usize>, vm: &VirtualMachine) -
vec![vm.ctx.new_str(ascii!("EOF when reading a line")).into()],
)
};
let ret = match_class!(match ret {
s @ PyStr => {
// Use as_wtf8() to handle strings with surrogates (e.g., surrogateescape)
@@ -77,6 +85,7 @@ pub fn file_readline(obj: &PyObject, size: Option<usize>, vm: &VirtualMachine) -
if s_wtf8.is_empty() {
return Err(eof_err());
}
// '\n' is ASCII, so we can check bytes directly
if s_wtf8.as_bytes().last() == Some(&b'\n') {
let no_nl = &s_wtf8[..s_wtf8.len() - 1];
@@ -90,13 +99,14 @@ pub fn file_readline(obj: &PyObject, size: Option<usize>, vm: &VirtualMachine) -
if buf.is_empty() {
return Err(eof_err());
}
if buf.last() == Some(&b'\n') {
vm.ctx.new_bytes(buf[..buf.len() - 1].to_owned()).into()
} else {
b.into()
}
}
_ => return Err(vm.new_type_error("object.readline() returned non-string".to_owned())),
_ => return Err(vm.new_type_error("object.readline() returned non-string")),
});
Ok(ret)
}
+3 -2
View File
@@ -111,8 +111,9 @@ pub struct PyObjectDeserializer<'c> {
}
impl<'c> PyObjectDeserializer<'c> {
pub fn new(vm: &'c VirtualMachine) -> Self {
PyObjectDeserializer { vm }
#[must_use]
pub const fn new(vm: &'c VirtualMachine) -> Self {
Self { vm }
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ pub enum ReadlineResult {
Line(String),
Eof,
Interrupt,
Io(std::io::Error),
Io(io::Error),
#[cfg(unix)]
OsError(String),
Other(OtherError),