Skip to content

Commit 431b900

Browse files
authored
Merge pull request RustPython#5716 from arihant2math/codegen-panic-reduction
Cleaner panic output for codegen errors
2 parents 301c32d + 397a196 commit 431b900

4 files changed

Lines changed: 115 additions & 26 deletions

File tree

‎compiler/codegen/src/compile.rs‎

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@ use rustpython_compiler_core::{
4141
};
4242
use rustpython_compiler_source::SourceCode;
4343
// use rustpython_parser_core::source_code::{LineNumber, SourceLocation};
44+
use crate::error::InternalError;
4445
use std::borrow::Cow;
4546

47+
pub(crate) type InternalResult<T> = Result<T, InternalError>;
4648
type CompileResult<T> = Result<T, CodegenError>;
4749

4850
#[derive(PartialEq, Eq, Clone, Copy)]
@@ -210,6 +212,54 @@ macro_rules! emit {
210212
};
211213
}
212214

215+
fn eprint_location(zelf: &Compiler<'_>) {
216+
let start = zelf
217+
.source_code
218+
.source_location(zelf.current_source_range.start());
219+
let end = zelf
220+
.source_code
221+
.source_location(zelf.current_source_range.end());
222+
eprintln!(
223+
"LOCATION: {} from {}:{} to {}:{}",
224+
zelf.source_code.path.to_owned(),
225+
start.row,
226+
start.column,
227+
end.row,
228+
end.column
229+
);
230+
}
231+
232+
/// Better traceback for internal error
233+
fn unwrap_internal<T>(zelf: &Compiler<'_>, r: InternalResult<T>) -> T {
234+
if let Err(ref r_err) = r {
235+
eprintln!("=== CODEGEN PANIC INFO ===");
236+
eprintln!("This IS an internal error: {}", r_err);
237+
eprint_location(zelf);
238+
eprintln!("=== END PANIC INFO ===");
239+
}
240+
r.unwrap()
241+
}
242+
243+
fn compiler_unwrap_option<T>(zelf: &Compiler<'_>, o: Option<T>) -> T {
244+
if o.is_none() {
245+
eprintln!("=== CODEGEN PANIC INFO ===");
246+
eprintln!("This IS an internal error, an option was unwrapped during codegen");
247+
eprint_location(zelf);
248+
eprintln!("=== END PANIC INFO ===");
249+
}
250+
o.unwrap()
251+
}
252+
253+
// fn compiler_result_unwrap<T, E: std::fmt::Debug>(zelf: &Compiler<'_>, result: Result<T, E>) -> T {
254+
// if result.is_err() {
255+
// eprintln!("=== CODEGEN PANIC INFO ===");
256+
// eprintln!("This IS an internal error, an result was unwrapped during codegen");
257+
// eprint_location(zelf);
258+
// eprintln!("=== END PANIC INFO ===");
259+
// }
260+
// result.unwrap()
261+
// }
262+
213263
/// The pattern context holds information about captured names and jump targets.
214264
#[derive(Clone)]
215265
pub struct PatternContext {
@@ -372,10 +422,9 @@ impl Compiler<'_> {
372422
fn pop_code_object(&mut self) -> CodeObject {
373423
let table = self.pop_symbol_table();
374424
assert!(table.sub_tables.is_empty());
375-
self.code_stack
376-
.pop()
377-
.unwrap()
378-
.finalize_code(self.opts.optimize)
425+
let pop = self.code_stack.pop();
426+
let stack_top = compiler_unwrap_option(self, pop);
427+
unwrap_internal(self, stack_top.finalize_code(self.opts.optimize))
379428
}
380429

381430
// could take impl Into<Cow<str>>, but everything is borrowed from ast structs; we never
@@ -482,7 +531,8 @@ impl Compiler<'_> {
482531
self.current_block().instructions.pop(); // pop Instruction::Pop
483532
}
484533
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
485-
let store_inst = self.current_block().instructions.pop().unwrap(); // pop Instruction::Store
534+
let pop_instructions = self.current_block().instructions.pop();
535+
let store_inst = compiler_unwrap_option(self, pop_instructions); // pop Instruction::Store
486536
emit!(self, Instruction::Duplicate);
487537
self.current_block().instructions.push(store_inst);
488538
}
@@ -540,8 +590,11 @@ impl Compiler<'_> {
540590
self.check_forbidden_name(&name, usage)?;
541591

542592
let symbol_table = self.symbol_table_stack.last().unwrap();
543-
let symbol = symbol_table.lookup(name.as_ref()).unwrap_or_else(||
544-
unreachable!("the symbol '{name}' should be present in the symbol table, even when it is undefined in python."),
593+
let symbol = unwrap_internal(
594+
self,
595+
symbol_table
596+
.lookup(name.as_ref())
597+
.ok_or_else(|| InternalError::MissingSymbol(name.to_string())),
545598
);
546599
let info = self.code_stack.last_mut().unwrap();
547600
let mut cache = &mut info.name_cache;
@@ -1476,12 +1529,12 @@ impl Compiler<'_> {
14761529
}
14771530
for var in &*code.freevars {
14781531
let table = self.symbol_table_stack.last().unwrap();
1479-
let symbol = table.lookup(var).unwrap_or_else(|| {
1480-
panic!(
1481-
"couldn't look up var {} in {} in {}",
1482-
var, code.obj_name, self.source_code.path
1483-
)
1484-
});
1532+
let symbol = unwrap_internal(
1533+
self,
1534+
table
1535+
.lookup(var)
1536+
.ok_or_else(|| InternalError::MissingSymbol(var.to_owned())),
1537+
);
14851538
let parent_code = self.code_stack.last().unwrap();
14861539
let vars = match symbol.scope {
14871540
SymbolScope::Free => &parent_code.freevar_cache,
@@ -1564,8 +1617,11 @@ impl Compiler<'_> {
15641617

15651618
// Check if the class is declared global
15661619
let symbol_table = self.symbol_table_stack.last().unwrap();
1567-
let symbol = symbol_table.lookup(name.as_ref()).expect(
1568-
"The symbol must be present in the symbol table, even when it is undefined in python.",
1620+
let symbol = unwrap_internal(
1621+
self,
1622+
symbol_table
1623+
.lookup(name.as_ref())
1624+
.ok_or_else(|| InternalError::MissingSymbol(name.to_owned())),
15691625
);
15701626
let mut global_path_prefix = Vec::new();
15711627
if symbol.scope == SymbolScope::GlobalExplicit {

‎compiler/codegen/src/error.rs‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ impl fmt::Display for CodegenError {
3434
}
3535
}
3636

37+
#[derive(Debug)]
38+
#[non_exhaustive]
39+
pub enum InternalError {
40+
StackOverflow,
41+
StackUnderflow,
42+
MissingSymbol(String),
43+
}
44+
45+
impl Display for InternalError {
46+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47+
match self {
48+
Self::StackOverflow => write!(f, "stack overflow"),
49+
Self::StackUnderflow => write!(f, "stack underflow"),
50+
Self::MissingSymbol(s) => write!(
51+
f,
52+
"The symbol '{s}' must be present in the symbol table, even when it is undefined in python."
53+
),
54+
}
55+
}
56+
}
57+
3758
#[derive(Debug)]
3859
#[non_exhaustive]
3960
pub enum CodegenErrorType {

‎compiler/codegen/src/ir.rs‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::ops;
22

33
use crate::IndexSet;
4+
use crate::error::InternalError;
45
use ruff_source_file::{OneIndexed, SourceLocation};
56
use rustpython_compiler_core::bytecode::{
67
CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label, OpArg,
@@ -82,12 +83,12 @@ pub struct CodeInfo {
8283
pub freevar_cache: IndexSet<String>,
8384
}
8485
impl CodeInfo {
85-
pub fn finalize_code(mut self, optimize: u8) -> CodeObject {
86+
pub fn finalize_code(mut self, optimize: u8) -> crate::InternalResult<CodeObject> {
8687
if optimize > 0 {
8788
self.dce();
8889
}
8990

90-
let max_stackdepth = self.max_stackdepth();
91+
let max_stackdepth = self.max_stackdepth()?;
9192
let cell2arg = self.cell2arg();
9293

9394
let CodeInfo {
@@ -154,7 +155,7 @@ impl CodeInfo {
154155
locations.clear()
155156
}
156157

157-
CodeObject {
158+
Ok(CodeObject {
158159
flags,
159160
posonlyarg_count,
160161
arg_count,
@@ -172,7 +173,7 @@ impl CodeInfo {
172173
cellvars: cellvar_cache.into_iter().collect(),
173174
freevars: freevar_cache.into_iter().collect(),
174175
cell2arg,
175-
}
176+
})
176177
}
177178

178179
fn cell2arg(&self) -> Option<Box<[i32]>> {
@@ -219,7 +220,7 @@ impl CodeInfo {
219220
}
220221
}
221222

222-
fn max_stackdepth(&self) -> u32 {
223+
fn max_stackdepth(&self) -> crate::InternalResult<u32> {
223224
let mut maxdepth = 0u32;
224225
let mut stack = Vec::with_capacity(self.blocks.len());
225226
let mut start_depths = vec![u32::MAX; self.blocks.len()];
@@ -244,10 +245,13 @@ impl CodeInfo {
244245
let instr_display = instr.display(display_arg, self);
245246
eprint!("{instr_display}: {depth} {effect:+} => ");
246247
}
247-
if effect < 0 && depth < effect.unsigned_abs() {
248-
panic!("The stack will underflow at {depth} with {effect} effect on {instr:?}");
249-
}
250-
let new_depth = depth.checked_add_signed(effect).unwrap();
248+
let new_depth = depth.checked_add_signed(effect).ok_or({
249+
if effect < 0 {
250+
InternalError::StackUnderflow
251+
} else {
252+
InternalError::StackOverflow
253+
}
254+
})?;
251255
if DEBUG {
252256
eprintln!("{new_depth}");
253257
}
@@ -264,7 +268,13 @@ impl CodeInfo {
264268
)
265269
{
266270
let effect = instr.stack_effect(ins.arg, true);
267-
let target_depth = depth.checked_add_signed(effect).unwrap();
271+
let target_depth = depth.checked_add_signed(effect).ok_or({
272+
if effect < 0 {
273+
InternalError::StackUnderflow
274+
} else {
275+
InternalError::StackOverflow
276+
}
277+
})?;
268278
if target_depth > maxdepth {
269279
maxdepth = target_depth
270280
}
@@ -280,7 +290,7 @@ impl CodeInfo {
280290
if DEBUG {
281291
eprintln!("DONE: {maxdepth}");
282292
}
283-
maxdepth
293+
Ok(maxdepth)
284294
}
285295
}
286296

‎compiler/codegen/src/lib.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ mod unparse;
1818
pub use compile::CompileOpts;
1919
use ruff_python_ast::Expr;
2020

21+
pub(crate) use compile::InternalResult;
22+
2123
pub trait ToPythonName {
2224
/// Returns a short name for the node suitable for use in error messages.
2325
fn python_name(&self) -> &'static str;

0 commit comments

Comments
 (0)