Be a little bit more helpful on register uninitialized errors, fix a bug with drain on multiple pipelines
This commit is contained in:
parent
4cf6bd6fb0
commit
4343d1d80c
|
@ -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);
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user