Be a little bit more helpful on register uninitialized errors, fix a bug with drain on multiple pipelines

This commit is contained in:
Devyn Cairns 2024-07-05 20:43:40 -07:00
parent 4cf6bd6fb0
commit 4343d1d80c
No known key found for this signature in database
3 changed files with 138 additions and 104 deletions

View File

@ -96,133 +96,153 @@ impl BlockBuilder {
&mut self, &mut self,
instruction: Spanned<Instruction>, instruction: Spanned<Instruction>,
) -> Result<usize, CompileError> { ) -> Result<usize, CompileError> {
match &instruction.item { // Free read registers, and mark write registers.
//
// If a register is both read and written, it should be on both sides, so that we can verify
// that the register was in the right state beforehand.
let mut allocate = |read: &[RegId], write: &[RegId]| -> Result<(), CompileError> {
for reg in read {
self.free_register(*reg)?;
}
for reg in write {
self.mark_register(*reg)?;
}
Ok(())
};
let allocate_result = match &instruction.item {
Instruction::LoadLiteral { dst, lit } => { Instruction::LoadLiteral { dst, lit } => {
self.mark_register(*dst)?; allocate(&[], &[*dst]).and(
// Free any registers on the literal // Free any registers on the literal
match lit { match lit {
Literal::Range { Literal::Range {
start, start,
step, step,
end, end,
inclusion: _, inclusion: _,
} => { } => allocate(&[*start, *step, *end], &[]),
self.free_register(*start)?; Literal::Bool(_)
self.free_register(*step)?; | Literal::Int(_)
self.free_register(*end)?; | Literal::Float(_)
} | Literal::Filesize(_)
Literal::Bool(_) | Literal::Duration(_)
| Literal::Int(_) | Literal::Binary(_)
| Literal::Float(_) | Literal::Block(_)
| Literal::Filesize(_) | Literal::Closure(_)
| Literal::Duration(_) | Literal::RowCondition(_)
| Literal::Binary(_) | Literal::List { capacity: _ }
| Literal::Block(_) | Literal::Record { capacity: _ }
| Literal::Closure(_) | Literal::Filepath {
| Literal::RowCondition(_) val: _,
| Literal::List { capacity: _ } no_expand: _,
| Literal::Record { capacity: _ } }
| Literal::Filepath { | Literal::Directory {
val: _, val: _,
no_expand: _, no_expand: _,
} }
| Literal::Directory { | Literal::GlobPattern {
val: _, val: _,
no_expand: _, no_expand: _,
} }
| Literal::GlobPattern { | Literal::String(_)
val: _, | Literal::RawString(_)
no_expand: _, | Literal::CellPath(_)
} | Literal::Date(_)
| Literal::String(_) | Literal::Nothing => Ok(()),
| Literal::RawString(_) },
| Literal::CellPath(_) )
| Literal::Date(_)
| Literal::Nothing => (),
}
} }
Instruction::LoadValue { dst, val: _ } => self.mark_register(*dst)?, Instruction::LoadValue { dst, val: _ } => allocate(&[], &[*dst]),
Instruction::Move { dst, src } => { Instruction::Move { dst, src } => allocate(&[*src], &[*dst]),
self.free_register(*src)?; Instruction::Clone { dst, src } => allocate(&[*src], &[*dst, *src]),
self.mark_register(*dst)?; Instruction::Collect { src_dst } => allocate(&[*src_dst], &[*src_dst]),
} Instruction::Drop { src } => allocate(&[*src], &[]),
Instruction::Clone { dst, src: _ } => self.mark_register(*dst)?, Instruction::Drain { src } => allocate(&[*src], &[]),
Instruction::Collect { src_dst: _ } => (), Instruction::LoadVariable { dst, var_id: _ } => allocate(&[], &[*dst]),
Instruction::Drop { src } => self.free_register(*src)?, Instruction::StoreVariable { var_id: _, src } => allocate(&[*src], &[]),
Instruction::Drain { src } => self.free_register(*src)?, Instruction::LoadEnv { dst, key: _ } => allocate(&[], &[*dst]),
Instruction::LoadVariable { dst, var_id: _ } => self.mark_register(*dst)?, Instruction::LoadEnvOpt { dst, key: _ } => allocate(&[], &[*dst]),
Instruction::StoreVariable { var_id: _, src } => self.free_register(*src)?, Instruction::StoreEnv { key: _, src } => allocate(&[*src], &[]),
Instruction::LoadEnv { dst, key: _ } => self.mark_register(*dst)?, Instruction::NewCalleeStack => Ok(()),
Instruction::LoadEnvOpt { dst, key: _ } => self.mark_register(*dst)?, Instruction::CaptureVariable { var_id: _ } => Ok(()),
Instruction::StoreEnv { key: _, src } => self.free_register(*src)?, Instruction::PushVariable { var_id: _, src } => allocate(&[*src], &[]),
Instruction::NewCalleeStack => (), Instruction::PushPositional { src } => allocate(&[*src], &[]),
Instruction::CaptureVariable { var_id: _ } => (), Instruction::AppendRest { src } => allocate(&[*src], &[]),
Instruction::PushVariable { var_id: _, src } => self.free_register(*src)?, Instruction::PushFlag { name: _ } => Ok(()),
Instruction::PushPositional { src } => self.free_register(*src)?, Instruction::PushNamed { name: _, src } => allocate(&[*src], &[]),
Instruction::AppendRest { src } => self.free_register(*src)?, Instruction::PushParserInfo { name: _, info: _ } => Ok(()),
Instruction::PushFlag { name: _ } => (),
Instruction::PushNamed { name: _, src } => self.free_register(*src)?,
Instruction::PushParserInfo { name: _, info: _ } => (),
Instruction::RedirectOut { mode } | Instruction::RedirectErr { mode } => match mode { Instruction::RedirectOut { mode } | Instruction::RedirectErr { mode } => match mode {
RedirectMode::File { path, .. } => self.free_register(*path)?, RedirectMode::File { path, .. } => allocate(&[*path], &[]),
_ => (), _ => Ok(()),
}, },
Instruction::Call { Instruction::Call {
decl_id: _, decl_id: _,
src_dst: _, src_dst,
} => (), } => allocate(&[*src_dst], &[*src_dst]),
Instruction::StringAppend { src_dst: _, val } => self.free_register(*val)?, Instruction::StringAppend { src_dst, val } => allocate(&[*src_dst, *val], &[*src_dst]),
Instruction::GlobFrom { Instruction::GlobFrom {
src_dst: _, src_dst,
no_expand: _, no_expand: _,
} => (), } => allocate(&[*src_dst], &[*src_dst]),
Instruction::ListPush { src_dst: _, item } => self.free_register(*item)?, Instruction::ListPush { src_dst, item } => allocate(&[*src_dst, *item], &[*src_dst]),
Instruction::ListSpread { src_dst: _, items } => self.free_register(*items)?, Instruction::ListSpread { src_dst, items } => {
Instruction::RecordInsert { allocate(&[*src_dst, *items], &[*src_dst])
src_dst: _,
key,
val,
} => {
self.free_register(*key)?;
self.free_register(*val)?;
} }
Instruction::RecordSpread { src_dst: _, items } => self.free_register(*items)?, Instruction::RecordInsert { src_dst, key, val } => {
Instruction::Not { src_dst: _ } => (), allocate(&[*src_dst, *key, *val], &[*src_dst])
}
Instruction::RecordSpread { src_dst, items } => {
allocate(&[*src_dst, *items], &[*src_dst])
}
Instruction::Not { src_dst } => allocate(&[*src_dst], &[*src_dst]),
Instruction::BinaryOp { Instruction::BinaryOp {
lhs_dst: _, lhs_dst,
op: _, op: _,
rhs, rhs,
} => self.free_register(*rhs)?, } => allocate(&[*lhs_dst, *rhs], &[*lhs_dst]),
Instruction::FollowCellPath { src_dst: _, path } => self.free_register(*path)?, Instruction::FollowCellPath { src_dst, path } => {
Instruction::CloneCellPath { dst, src: _, path } => { allocate(&[*src_dst, *path], &[*src_dst])
self.mark_register(*dst)?; }
self.free_register(*path)?; Instruction::CloneCellPath { dst, src, path } => {
allocate(&[*src, *path], &[*src, *dst])
} }
Instruction::UpsertCellPath { Instruction::UpsertCellPath {
src_dst: _, src_dst,
path, path,
new_value, new_value,
} => { } => allocate(&[*src_dst, *path, *new_value], &[*src_dst]),
self.free_register(*path)?; Instruction::Jump { index: _ } => Ok(()),
self.free_register(*new_value)?; Instruction::BranchIf { cond, index: _ } => allocate(&[*cond], &[]),
}
Instruction::Jump { index: _ } => (),
Instruction::BranchIf { cond, index: _ } => self.free_register(*cond)?,
Instruction::Match { Instruction::Match {
pattern: _, pattern: _,
src: _, src,
index: _, index: _,
} => (), } => allocate(&[*src], &[*src]),
Instruction::Iterate { Instruction::Iterate {
dst, dst,
stream: _, stream,
end_index: _, end_index: _,
} => self.mark_register(*dst)?, } => allocate(&[*stream], &[*dst, *stream]),
Instruction::OnError { index: _ } => (), Instruction::OnError { index: _ } => Ok(()),
Instruction::OnErrorInto { index: _, dst } => self.mark_register(*dst)?, Instruction::OnErrorInto { index: _, dst } => allocate(&[], &[*dst]),
Instruction::PopErrorHandler => (), Instruction::PopErrorHandler => Ok(()),
Instruction::Return { src } => self.free_register(*src)?, Instruction::Return { src } => allocate(&[*src], &[]),
};
// Add more context to the error
match allocate_result {
Ok(()) => (),
Err(CompileError::RegisterUninitialized { reg_id, caller }) => {
return Err(CompileError::RegisterUninitializedWhilePushingInstruction {
reg_id,
caller,
instruction: format!("{:?}", instruction.item),
span: instruction.span,
});
}
Err(err) => return Err(err),
} }
let index = self.next_instruction_index(); let index = self.next_instruction_index();
self.instructions.push(instruction.item); self.instructions.push(instruction.item);
self.spans.push(instruction.span); self.spans.push(instruction.span);

View File

@ -83,6 +83,7 @@ fn compile_block(
if builder.is_allocated(out_reg) { if builder.is_allocated(out_reg) {
builder.push(Instruction::Drain { src: out_reg }.into_spanned(span))?; builder.push(Instruction::Drain { src: out_reg }.into_spanned(span))?;
} }
builder.load_empty(out_reg)?;
} }
} }
Ok(()) Ok(())

View File

@ -21,6 +21,19 @@ pub enum CompileError {
)] )]
RegisterUninitialized { reg_id: RegId, caller: String }, RegisterUninitialized { reg_id: RegId, caller: String },
#[error("Register {reg_id} was uninitialized when used, possibly reused.")]
#[diagnostic(
code(nu::compile::register_uninitialized),
help("this is a compiler bug. Please report it at https://github.com/nushell/nushell/issues/new\nfrom: {caller}"),
)]
RegisterUninitializedWhilePushingInstruction {
reg_id: RegId,
caller: String,
instruction: String,
#[label("while adding this instruction: {instruction}")]
span: Span,
},
#[error("Block contains too much string data: maximum 4 GiB exceeded.")] #[error("Block contains too much string data: maximum 4 GiB exceeded.")]
#[diagnostic( #[diagnostic(
code(nu::compile::data_overflow), code(nu::compile::data_overflow),