mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
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:
1 parent
866f7ca812
commit
c6e254fbdc
14 files changed
+364
-490
No files matched your search
Generated
+1
@@ -3553,6 +3553,7 @@ dependencies = [
|
||||
"lz4_flex",
|
||||
"malachite-bigint",
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"rustpython-ruff_source_file",
|
||||
"rustpython-wtf8",
|
||||
]
|
||||
|
||||
+165
-229
@@ -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
@@ -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] {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>);
|
||||
|
||||
|
||||
@@ -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,6 +1,4 @@
|
||||
/*! Python `attribute` descriptor class. (PyGetSet)
|
||||
|
||||
*/
|
||||
//! Python `attribute` descriptor class. (PyGetSet)
|
||||
use crate::{
|
||||
Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
convert::ToPyResult,
|
||||
|
||||
@@ -254,6 +254,7 @@ impl PyMethodDef {
|
||||
all_methods
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
const fn const_copy(&self) -> Self {
|
||||
Self {
|
||||
name: self.name,
|
||||
|
||||
+15
-5
@@ -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)
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ pub enum ReadlineResult {
|
||||
Line(String),
|
||||
Eof,
|
||||
Interrupt,
|
||||
Io(std::io::Error),
|
||||
Io(io::Error),
|
||||
#[cfg(unix)]
|
||||
OsError(String),
|
||||
Other(OtherError),
|
||||
|
||||
Reference in new issue
Block a user