change the return keyword to return-early, use ShellError::Return :(

This commit is contained in:
Devyn Cairns 2024-07-09 05:09:25 -07:00
parent f50e22597c
commit 36faabd36d
6 changed files with 30 additions and 5 deletions

View File

@ -241,6 +241,7 @@ impl BlockBuilder {
Instruction::OnErrorInto { index: _, dst } => allocate(&[], &[*dst]), Instruction::OnErrorInto { index: _, dst } => allocate(&[], &[*dst]),
Instruction::PopErrorHandler => Ok(()), Instruction::PopErrorHandler => Ok(()),
Instruction::CheckExternalFailed { dst, src } => allocate(&[*src], &[*dst, *src]), Instruction::CheckExternalFailed { dst, src } => allocate(&[*src], &[*dst, *src]),
Instruction::ReturnEarly { src } => allocate(&[*src], &[]),
Instruction::Return { src } => allocate(&[*src], &[]), Instruction::Return { src } => allocate(&[*src], &[]),
}; };

View File

@ -844,7 +844,7 @@ pub(crate) fn compile_continue(
Ok(()) Ok(())
} }
/// Compile a call to `return` as a `return` instruction. /// Compile a call to `return` as a `return-early` instruction.
/// ///
/// This is not strictly necessary, but it is more efficient. /// This is not strictly necessary, but it is more efficient.
pub(crate) fn compile_return( pub(crate) fn compile_return(
@ -857,7 +857,7 @@ pub(crate) fn compile_return(
// Pseudocode: // Pseudocode:
// //
// %io_reg <- <arg_expr> // %io_reg <- <arg_expr>
// return %io_reg // return-early %io_reg
if let Some(arg_expr) = call.positional_nth(0) { if let Some(arg_expr) = call.positional_nth(0) {
compile_expression( compile_expression(
working_set, working_set,
@ -871,7 +871,9 @@ pub(crate) fn compile_return(
builder.load_empty(io_reg)?; builder.load_empty(io_reg)?;
} }
builder.push(Instruction::Return { src: io_reg }.into_spanned(call.head))?; // TODO: It would be nice if this could be `return` instead, but there is a little bit of
// behaviour remaining that still depends on `ShellError::Return`
builder.push(Instruction::ReturnEarly { src: io_reg }.into_spanned(call.head))?;
// io_reg is supposed to remain allocated // io_reg is supposed to remain allocated
builder.load_empty(io_reg)?; builder.load_empty(io_reg)?;

View File

@ -210,6 +210,14 @@ fn eval_ir_block_impl<D: DebugContext>(
)); ));
} }
} }
Err(
err @ (ShellError::Return { .. }
| ShellError::Continue { .. }
| ShellError::Break { .. }),
) => {
// These block control related errors should be passed through
return Err(err);
}
Err(err) => { Err(err) => {
if let Some(error_handler) = ctx.stack.error_handlers.pop(ctx.error_handler_base) { if let Some(error_handler) = ctx.stack.error_handlers.pop(ctx.error_handler_base) {
// If an error handler is set, branch there // If an error handler is set, branch there
@ -739,6 +747,13 @@ fn eval_instruction<D: DebugContext>(
ctx.put_reg(*dst, Value::bool(failed, *span).into_pipeline_data()); ctx.put_reg(*dst, Value::bool(failed, *span).into_pipeline_data());
Ok(Continue) Ok(Continue)
} }
Instruction::ReturnEarly { src } => {
let val = ctx.collect_reg(*src, *span)?;
Err(ShellError::Return {
span: *span,
value: Box::new(val),
})
}
Instruction::Return { src } => Ok(Return(*src)), Instruction::Return { src } => Ok(Return(*src)),
} }
} }

View File

@ -167,7 +167,7 @@ fn ir_call_to_extern_call(
} => { } => {
let name_arg = engine::Argument::Positional { let name_arg = engine::Argument::Positional {
span: *span, span: *span,
val: Value::string(known_external_option_name(&data, *name, *short), *span), val: Value::string(known_external_option_name(data, *name, *short), *span),
ast: None, ast: None,
}; };
extern_call.add_argument(stack, name_arg); extern_call.add_argument(stack, name_arg);
@ -182,7 +182,7 @@ fn ir_call_to_extern_call(
} => { } => {
let name_arg = engine::Argument::Positional { let name_arg = engine::Argument::Positional {
span: *span, span: *span,
val: Value::string(known_external_option_name(&data, *name, *short), *span), val: Value::string(known_external_option_name(data, *name, *short), *span),
ast: None, ast: None,
}; };
let val_arg = engine::Argument::Positional { let val_arg = engine::Argument::Positional {

View File

@ -240,6 +240,9 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
Instruction::CheckExternalFailed { dst, src } => { Instruction::CheckExternalFailed { dst, src } => {
write!(f, "{:WIDTH$} {dst}, {src}", "check-external-failed") write!(f, "{:WIDTH$} {dst}, {src}", "check-external-failed")
} }
Instruction::ReturnEarly { src } => {
write!(f, "{:WIDTH$} {src}", "return-early")
}
Instruction::Return { src } => { Instruction::Return { src } => {
write!(f, "{:WIDTH$} {src}", "return") write!(f, "{:WIDTH$} {src}", "return")
} }

View File

@ -248,6 +248,10 @@ pub enum Instruction {
/// Check if an external command failed. Boolean value into `dst`. `src` is preserved, but it /// Check if an external command failed. Boolean value into `dst`. `src` is preserved, but it
/// does require waiting for the command to exit. /// does require waiting for the command to exit.
CheckExternalFailed { dst: RegId, src: RegId }, CheckExternalFailed { dst: RegId, src: RegId },
/// Return early from the block, raising a `ShellError::Return` instead.
///
/// Collecting the value is unavoidable.
ReturnEarly { src: RegId },
/// Return from the block with the value in the register /// Return from the block with the value in the register
Return { src: RegId }, Return { src: RegId },
} }