Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: disallow returning constant values #2978

Merged
merged 11 commits into from
Oct 10, 2023
4 changes: 4 additions & 0 deletions compiler/noirc_errors/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,8 @@ impl Location {
pub fn new(span: Span, file: FileId) -> Self {
Self { span, file }
}

pub fn dummy() -> Self {
Self { span: Span::single_char(0), file: FileId::dummy() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ impl<'block> BrilligBlock<'block> {
self.create_block_label_for_current_function(*destination_block),
);
}
TerminatorInstruction::Return { return_values } => {
TerminatorInstruction::Return { return_values, .. } => {
let return_registers: Vec<_> = return_values
.iter()
.flat_map(|value_id| {
Expand Down
14 changes: 11 additions & 3 deletions compiler/noirc_evaluator/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub enum InternalError {
UndeclaredAcirVar { call_stack: CallStack },
#[error("ICE: Expected {expected:?}, found {found:?}")]
UnExpected { expected: String, found: String, call_stack: CallStack },
#[error("Returning a constant value is not allowed")]
ReturnConstant { call_stack: CallStack },
}

impl RuntimeError {
Expand All @@ -79,7 +81,8 @@ impl RuntimeError {
| InternalError::MissingArg { call_stack, .. }
| InternalError::NotAConstant { call_stack, .. }
| InternalError::UndeclaredAcirVar { call_stack }
| InternalError::UnExpected { call_stack, .. },
| InternalError::UnExpected { call_stack, .. }
| InternalError::ReturnConstant { call_stack, .. },
)
| RuntimeError::FailedConstraint { call_stack, .. }
| RuntimeError::IndexOutOfBounds { call_stack, .. }
Expand All @@ -96,16 +99,21 @@ impl RuntimeError {
impl From<RuntimeError> for FileDiagnostic {
fn from(error: RuntimeError) -> FileDiagnostic {
let call_stack = vecmap(error.call_stack(), |location| *location);
let diagnostic = error.into_diagnostic();
let file_id = call_stack.last().map(|location| location.file).unwrap_or_default();

let diagnostic = error.into_diagnostic();
diagnostic.in_file(file_id).with_call_stack(call_stack)
}
}

impl RuntimeError {
fn into_diagnostic(self) -> Diagnostic {
match self {
RuntimeError::InternalError(InternalError::ReturnConstant { ref call_stack }) => {
let message = self.to_string();
let location =
call_stack.back().expect("Expected RuntimeError to have a location");
Diagnostic::simple_error(message, "constant value".to_string(), location.span)
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
}
RuntimeError::InternalError(cause) => {
Diagnostic::simple_error(
"Internal Consistency Evaluators Errors: \n
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ impl AcirContext {
}
}

/// True if the given AcirVar refers to a constant value
pub(crate) fn is_constant(&self, var: &AcirVar) -> bool {
matches!(self.vars[var], AcirVarData::Const(_))
}

/// Adds a new Variable to context whose value will
/// be constrained to be the negation of `var`.
///
Expand Down
20 changes: 12 additions & 8 deletions compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1248,15 +1248,20 @@ impl Context {
terminator: &TerminatorInstruction,
dfg: &DataFlowGraph,
) -> Result<(), InternalError> {
let return_values = match terminator {
TerminatorInstruction::Return { return_values } => return_values,
let (return_values, call_stack) = match terminator {
TerminatorInstruction::Return { return_values, call_stack } => {
(return_values, call_stack)
}
_ => unreachable!("ICE: Program must have a singular return"),
};

// The return value may or may not be an array reference. Calling `flatten_value_list`
// will expand the array if there is one.
let return_acir_vars = self.flatten_value_list(return_values, dfg);
for acir_var in return_acir_vars {
if self.acir_context.is_constant(&acir_var) {
return Err(InternalError::ReturnConstant { call_stack: call_stack.clone() });
}
self.acir_context.return_var(acir_var)?;
}
Ok(())
Expand Down Expand Up @@ -1869,6 +1874,7 @@ mod tests {

use crate::{
brillig::Brillig,
errors::{InternalError, RuntimeError},
ssa::{
function_builder::FunctionBuilder,
ir::{function::RuntimeType, map::Id, types::Type},
Expand Down Expand Up @@ -1897,12 +1903,10 @@ mod tests {
let ssa = builder.finish();

let context = Context::new();
let mut acir = context.convert_ssa(ssa, Brillig::default(), &HashMap::default()).unwrap();

let expected_opcodes =
vec![Opcode::Arithmetic(&Expression::one() - &Expression::from(Witness(1)))];
assert_eq!(acir.take_opcodes(), expected_opcodes);
assert_eq!(acir.return_witnesses, vec![Witness(1)]);
let acir = context
.convert_ssa(ssa, Brillig::default(), &HashMap::default())
.expect_err("Return constant value");
assert!(matches!(acir, RuntimeError::InternalError(InternalError::ReturnConstant { .. })));
}
}
//
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ impl FunctionBuilder {

/// Terminate the current block with a return instruction
pub(crate) fn terminate_with_return(&mut self, return_values: Vec<ValueId>) {
self.terminate_block_with(TerminatorInstruction::Return { return_values });
let call_stack = self.call_stack.clone();
self.terminate_block_with(TerminatorInstruction::Return { return_values, call_stack });
}

/// Returns a ValueId pointing to the given function or imports the function
Expand Down
9 changes: 8 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/basic_block.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{
dfg::CallStack,
instruction::{InstructionId, TerminatorInstruction},
map::Id,
value::ValueId,
Expand Down Expand Up @@ -117,7 +118,13 @@ impl BasicBlock {
/// being kept, which is likely unwanted.
pub(crate) fn take_terminator(&mut self) -> TerminatorInstruction {
let terminator = self.terminator.as_mut().expect("Expected block to have a terminator");
std::mem::replace(terminator, TerminatorInstruction::Return { return_values: Vec::new() })
std::mem::replace(
terminator,
TerminatorInstruction::Return {
return_values: Vec::new(),
call_stack: CallStack::new(),
},
)
}

/// Return the jmp arguments, if any, of this block's TerminatorInstruction.
Expand Down
20 changes: 15 additions & 5 deletions compiler/noirc_evaluator/src/ssa/ir/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ impl ControlFlowGraph {

#[cfg(test)]
mod tests {
use crate::ssa::ir::{instruction::TerminatorInstruction, map::Id, types::Type};
use crate::ssa::ir::{
dfg::CallStack, instruction::TerminatorInstruction, map::Id, types::Type,
};

use super::{super::function::Function, ControlFlowGraph};

Expand All @@ -140,7 +142,10 @@ mod tests {
let func_id = Id::test_new(0);
let mut func = Function::new("func".into(), func_id);
let block_id = func.entry_block();
func.dfg[block_id].set_terminator(TerminatorInstruction::Return { return_values: vec![] });
func.dfg[block_id].set_terminator(TerminatorInstruction::Return {
return_values: vec![],
call_stack: CallStack::new(),
});

ControlFlowGraph::with_function(&func);
}
Expand Down Expand Up @@ -173,7 +178,10 @@ mod tests {
then_destination: block1_id,
else_destination: block2_id,
});
func.dfg[block2_id].set_terminator(TerminatorInstruction::Return { return_values: vec![] });
func.dfg[block2_id].set_terminator(TerminatorInstruction::Return {
return_values: vec![],
call_stack: CallStack::new(),
});

let mut cfg = ControlFlowGraph::with_function(&func);

Expand Down Expand Up @@ -218,8 +226,10 @@ mod tests {
// return ()
// }
let ret_block_id = func.dfg.make_block();
func.dfg[ret_block_id]
.set_terminator(TerminatorInstruction::Return { return_values: vec![] });
func.dfg[ret_block_id].set_terminator(TerminatorInstruction::Return {
return_values: vec![],
call_stack: CallStack::new(),
});
func.dfg[block2_id].set_terminator(TerminatorInstruction::Jmp {
destination: ret_block_id,
arguments: vec![],
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ mod tests {
function_builder::FunctionBuilder,
ir::{
basic_block::BasicBlockId,
dfg::CallStack,
dom::DominatorTree,
function::{Function, RuntimeType},
instruction::TerminatorInstruction,
Expand All @@ -265,7 +266,7 @@ mod tests {
let block0_id = func.entry_block();
func.dfg.set_block_terminator(
block0_id,
TerminatorInstruction::Return { return_values: vec![] },
TerminatorInstruction::Return { return_values: vec![], call_stack: CallStack::new() },
);
let mut dom_tree = DominatorTree::with_function(&func);
assert!(dom_tree.dominates(block0_id, block0_id));
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl Function {
let mut function_return_values = None;
for block in blocks {
let terminator = self.dfg[block].terminator();
if let Some(TerminatorInstruction::Return { return_values }) = terminator {
if let Some(TerminatorInstruction::Return { return_values, .. }) = terminator {
function_return_values = Some(return_values);
break;
}
Expand Down
13 changes: 7 additions & 6 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ pub(crate) enum TerminatorInstruction {
/// unconditionally jump to a single exit block with the return values
/// as the block arguments. Then the exit block can terminate in a return
/// instruction returning these values.
Return { return_values: Vec<ValueId> },
Return { return_values: Vec<ValueId>, call_stack: CallStack },
}

impl TerminatorInstruction {
Expand All @@ -557,9 +557,10 @@ impl TerminatorInstruction {
arguments: vecmap(arguments, |value| f(*value)),
call_stack: call_stack.clone(),
},
Return { return_values } => {
Return { return_values: vecmap(return_values, |value| f(*value)) }
}
Return { return_values, call_stack } => Return {
return_values: vecmap(return_values, |value| f(*value)),
call_stack: call_stack.clone(),
},
}
}

Expand All @@ -575,7 +576,7 @@ impl TerminatorInstruction {
*argument = f(*argument);
}
}
Return { return_values } => {
Return { return_values, .. } => {
for return_value in return_values {
*return_value = f(*return_value);
}
Expand All @@ -595,7 +596,7 @@ impl TerminatorInstruction {
f(*argument);
}
}
Return { return_values } => {
Return { return_values, .. } => {
for return_value in return_values {
f(*return_value);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub(crate) fn display_terminator(
else_destination
)
}
Some(TerminatorInstruction::Return { return_values }) => {
Some(TerminatorInstruction::Return { return_values, .. }) => {
writeln!(f, " return {}", value_list(function, return_values))
}
None => writeln!(f, " (no terminator instruction)"),
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ mod test {
assert_eq!(block.instructions().len(), 0);

match block.terminator() {
Some(TerminatorInstruction::Return { return_values }) => {
Some(TerminatorInstruction::Return { return_values, .. }) => {
let value = main
.dfg
.get_numeric_constant(return_values[0])
Expand Down Expand Up @@ -278,7 +278,7 @@ mod test {
assert_ne!(new_add_instr_result, v1);

let return_value_id = match entry_block.unwrap_terminator() {
TerminatorInstruction::Return { return_values } => return_values[0],
TerminatorInstruction::Return { return_values, .. } => return_values[0],
_ => unreachable!(),
};
let return_element = match &main.dfg[return_value_id] {
Expand Down
10 changes: 6 additions & 4 deletions compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,13 @@ impl<'f> Context<'f> {
let arguments = vecmap(arguments.clone(), |value| self.inserter.resolve(value));
self.inline_block(destination, &arguments)
}
TerminatorInstruction::Return { return_values } => {
TerminatorInstruction::Return { return_values, call_stack } => {
let call_stack = call_stack.clone();
let return_values =
vecmap(return_values.clone(), |value| self.inserter.resolve(value));
let new_return = TerminatorInstruction::Return { return_values, call_stack };
let entry = self.inserter.function.entry_block();
let new_return = TerminatorInstruction::Return { return_values };

self.inserter.function.dfg.set_block_terminator(entry, new_return);
block
}
Expand Down Expand Up @@ -1079,7 +1081,7 @@ mod test {

let main = ssa.main();
let ret = match main.dfg[main.entry_block()].terminator() {
Some(TerminatorInstruction::Return { return_values }) => return_values[0],
Some(TerminatorInstruction::Return { return_values, .. }) => return_values[0],
_ => unreachable!(),
};

Expand Down Expand Up @@ -1442,7 +1444,7 @@ mod test {

// The return value should be 200, not 310
match main.dfg[main.entry_block()].terminator() {
Some(TerminatorInstruction::Return { return_values }) => {
Some(TerminatorInstruction::Return { return_values, .. }) => {
match main.dfg.get_numeric_constant(return_values[0]) {
Some(constant) => {
let value = constant.to_u128();
Expand Down
11 changes: 8 additions & 3 deletions compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,10 +480,15 @@ impl<'function> PerFunctionContext<'function> {
}
None
}
TerminatorInstruction::Return { return_values } => {
TerminatorInstruction::Return { return_values, call_stack } => {
let return_values = vecmap(return_values, |value| self.translate_value(*value));
if self.inlining_entry {
self.context.builder.terminate_with_return(return_values.clone());
let mut new_call_stack = self.context.call_stack.clone();
new_call_stack.append(call_stack.clone());
self.context
.builder
.set_call_stack(new_call_stack)
.terminate_with_return(return_values.clone());
}
// Note that `translate_block` would take us back to the point at which the
// inlining of this source block began. Since additional blocks may have been
Expand Down Expand Up @@ -686,7 +691,7 @@ mod test {
let b6 = &main.dfg[b6_id];

match b6.terminator() {
Some(TerminatorInstruction::Return { return_values }) => {
Some(TerminatorInstruction::Return { return_values, .. }) => {
assert_eq!(return_values.len(), 1);
let value = main
.dfg
Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ impl<'f> PerFunctionContext<'f> {
}
}
}
TerminatorInstruction::Return { return_values } => {
TerminatorInstruction::Return { return_values, .. } => {
// Removing all `last_stores` for each returned reference is more important here
// than setting them all to ReferenceValue::Unknown since no other block should
// have a block with a Return terminator as a predecessor anyway.
Expand Down Expand Up @@ -449,7 +449,7 @@ mod tests {
assert_eq!(count_stores(block_id, &func.dfg), 0);

let ret_val_id = match func.dfg[block_id].terminator().unwrap() {
TerminatorInstruction::Return { return_values } => return_values.first().unwrap(),
TerminatorInstruction::Return { return_values, .. } => return_values.first().unwrap(),
_ => unreachable!(),
};
assert_eq!(func.dfg[*ret_val_id], func.dfg[two]);
Expand Down Expand Up @@ -485,7 +485,7 @@ mod tests {
assert_eq!(count_stores(block_id, &func.dfg), 1);

let ret_val_id = match func.dfg[block_id].terminator().unwrap() {
TerminatorInstruction::Return { return_values } => return_values.first().unwrap(),
TerminatorInstruction::Return { return_values, .. } => return_values.first().unwrap(),
_ => unreachable!(),
};
assert_eq!(func.dfg[*ret_val_id], func.dfg[one]);
Expand Down Expand Up @@ -518,7 +518,7 @@ mod tests {
assert_eq!(instructions.len(), 2);

let ret_val_id = match func.dfg[block_id].terminator().unwrap() {
TerminatorInstruction::Return { return_values } => *return_values.first().unwrap(),
TerminatorInstruction::Return { return_values, .. } => *return_values.first().unwrap(),
_ => unreachable!(),
};

Expand Down
Loading
Loading