Skip to content

Commit

Permalink
Merge branch 'master' into debug-prototype-repl-easy
Browse files Browse the repository at this point in the history
  • Loading branch information
mverzilli committed Oct 13, 2023
2 parents f0b3ecf + af3d771 commit c61df1a
Show file tree
Hide file tree
Showing 321 changed files with 504 additions and 312 deletions.
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"jnoortheen.nix-ide",
"rust-lang.rust-analyzer",
"redhat.vscode-yaml",
"esbenp.prettier-vscode"
"esbenp.prettier-vscode",
// Spell checking
"streetsidesoftware.code-spell-checker",
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
Expand Down
26 changes: 5 additions & 21 deletions acvm-repo/acvm_js/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use acvm::{
acir::circuit::{Circuit, OpcodeLocation},
acir::circuit::Circuit,
pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM},
};
#[allow(deprecated)]
Expand Down Expand Up @@ -84,17 +84,13 @@ pub async fn execute_circuit_with_black_box_solver(
| OpcodeResolutionError::IndexOutOfBounds {
opcode_location: ErrorLocation::Resolved(opcode_location),
..
} => (
get_assert_message(&circuit.assert_messages, opcode_location),
Some(vec![*opcode_location]),
),
} => {
(circuit.get_assert_message(*opcode_location), Some(vec![*opcode_location]))
}
OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => {
let failing_opcode =
call_stack.last().expect("Brillig error call stacks cannot be empty");
(
get_assert_message(&circuit.assert_messages, failing_opcode),
Some(call_stack.clone()),
)
(circuit.get_assert_message(*failing_opcode), Some(call_stack.clone()))
}
_ => (None, None),
};
Expand All @@ -117,15 +113,3 @@ pub async fn execute_circuit_with_black_box_solver(
let witness_map = acvm.finalize();
Ok(witness_map.into())
}

// Searches the slice for `opcode_location`.
// This is functionality equivalent to .get on a map.
fn get_assert_message(
assert_messages: &[(OpcodeLocation, String)],
opcode_location: &OpcodeLocation,
) -> Option<String> {
assert_messages
.iter()
.find(|(loc, _)| loc == opcode_location)
.map(|(_, message)| message.clone())
}
17 changes: 14 additions & 3 deletions compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ pub(crate) struct AcirContext {
/// The vars object is an instance of the `TwoWayMap`, which provides a bidirectional mapping between `AcirVar` and `AcirVarData`.
vars: HashMap<AcirVar, AcirVarData>,

constant_witnesses: HashMap<FieldElement, Witness>,

/// An in-memory representation of ACIR.
///
/// This struct will progressively be populated
Expand Down Expand Up @@ -227,7 +229,16 @@ impl AcirContext {
/// Converts an [`AcirVar`] to a [`Witness`]
fn var_to_witness(&mut self, var: AcirVar) -> Result<Witness, InternalError> {
let expression = self.var_to_expression(var)?;
Ok(self.acir_ir.get_or_create_witness(&expression))
let witness = if let Some(constant) = expression.to_const() {
// Check if a witness has been assigned this value already, if so reuse it.
*self
.constant_witnesses
.entry(constant)
.or_insert_with(|| self.acir_ir.get_or_create_witness(&expression))
} else {
self.acir_ir.get_or_create_witness(&expression)
};
Ok(witness)
}

/// Converts an [`AcirVar`] to an [`Expression`]
Expand Down Expand Up @@ -487,11 +498,11 @@ impl AcirContext {
| (AcirVarData::Const(constant), AcirVarData::Witness(witness)) => {
let mut expr = Expression::default();
expr.push_addition_term(constant, witness);
self.add_data(AcirVarData::Expr(expr))
self.add_data(AcirVarData::from(expr))
}
(AcirVarData::Const(constant), AcirVarData::Expr(expr))
| (AcirVarData::Expr(expr), AcirVarData::Const(constant)) => {
self.add_data(AcirVarData::Expr(&expr * constant))
self.add_data(AcirVarData::from(&expr * constant))
}
(AcirVarData::Witness(lhs_witness), AcirVarData::Witness(rhs_witness)) => {
let mut expr = Expression::default();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,30 @@ impl GeneratedAcir {
//
// When the predicate is 0, the equation always passes.
// When the predicate is 1, the rhs must not be 0.
let rhs_is_zero = self.is_zero(rhs);
let rhs_is_not_zero = self.mul_with_witness(&rhs_is_zero.into(), predicate);
self.assert_is_zero(rhs_is_not_zero);
let rhs_is_nonzero_const = rhs.is_const() && !rhs.is_zero();
if !rhs_is_nonzero_const {
match predicate.to_const() {
Some(predicate) if predicate.is_zero() => {
// If predicate is known to be inactive, we don't need to lay down constraints.
}

Some(predicate) if predicate.is_one() => {
// If the predicate is known to be active, we simply assert that an inverse must exist.
// This implies that `rhs != 0`.
let unsafe_inverse = self.brillig_inverse(rhs.clone());
let rhs_has_inverse =
self.mul_with_witness(rhs, &unsafe_inverse.into()) - FieldElement::one();
self.assert_is_zero(rhs_has_inverse);
}

_ => {
// Otherwise we must handle both potential cases.
let rhs_is_zero = self.is_zero(rhs);
let rhs_is_not_zero = self.mul_with_witness(&rhs_is_zero.into(), predicate);
self.assert_is_zero(rhs_is_not_zero);
}
}
}

// maximum bit size for q and for [r and rhs]
let mut max_q_bits = max_bit_size;
Expand Down
15 changes: 12 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,19 @@ impl Instruction {

pub(crate) fn has_side_effects(&self, dfg: &DataFlowGraph) -> bool {
use Instruction::*;

match self {
Binary(_)
| Cast(_, _)
Binary(binary) => {
if matches!(binary.operator, BinaryOp::Div | BinaryOp::Mod) {
if let Some(rhs) = dfg.get_numeric_constant(binary.rhs) {
rhs == FieldElement::zero()
} else {
true
}
} else {
false
}
}
Cast(_, _)
| Not(_)
| Truncate { .. }
| Allocate
Expand Down
10 changes: 9 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,15 @@ fn operator_result_max_bit_size_to_truncate(
match op {
Add => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1),
Subtract => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1),
Multiply => Some(lhs_bit_size + rhs_bit_size),
Multiply => {
if lhs_bit_size == 1 || rhs_bit_size == 1 {
// Truncation is unnecessary as multiplication by a boolean value cannot cause an overflow.
None
} else {
Some(lhs_bit_size + rhs_bit_size)
}
}

ShiftLeft => {
if let Some(rhs_constant) = dfg.get_numeric_constant(rhs) {
// Happy case is that we know precisely by how many bits the the integer will
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum ExpressionKind {
Variable(Path),
Tuple(Vec<Expression>),
Lambda(Box<Lambda>),
Parenthesized(Box<Expression>),
Error,
}

Expand Down Expand Up @@ -479,6 +480,7 @@ impl Display for ExpressionKind {
write!(f, "({})", elements.join(", "))
}
Lambda(lambda) => lambda.fmt(f),
Parenthesized(subexpr) => write!(f, "({subexpr})"),
Error => write!(f, "Error"),
}
}
Expand Down
10 changes: 6 additions & 4 deletions compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,17 +499,18 @@ fn add_method_to_struct_namespace(
struct_type: &Shared<StructType>,
func_id: FuncId,
name_ident: &Ident,
trait_id: TraitId,
) -> Result<(), DefCollectorErrorKind> {
let struct_type = struct_type.borrow();
let type_module = struct_type.id.local_module_id();
let module = &mut current_def_map.modules[type_module.0];
module.declare_function(name_ident.clone(), func_id).map_err(|(first_def, second_def)| {
DefCollectorErrorKind::Duplicate {
module.declare_trait_function(name_ident.clone(), func_id, trait_id).map_err(
|(first_def, second_def)| DefCollectorErrorKind::Duplicate {
typ: DuplicateType::TraitImplementation,
first_def,
second_def,
}
})
},
)
}

fn collect_trait_impl(
Expand Down Expand Up @@ -550,6 +551,7 @@ fn collect_trait_impl(
struct_type,
*func_id,
ast.name_ident(),
trait_id,
) {
Ok(()) => {}
Err(err) => {
Expand Down
106 changes: 88 additions & 18 deletions compiler/noirc_frontend/src/hir/def_map/item_scope.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use super::{namespace::PerNs, ModuleDefId, ModuleId};
use crate::{node_interner::FuncId, Ident};
use crate::{
node_interner::{FuncId, TraitId},
Ident,
};
use std::collections::{hash_map::Entry, HashMap};

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
Expand All @@ -9,8 +12,8 @@ pub enum Visibility {

#[derive(Default, Debug, PartialEq, Eq)]
pub struct ItemScope {
types: HashMap<Ident, (ModuleDefId, Visibility)>,
values: HashMap<Ident, (ModuleDefId, Visibility)>,
types: HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>,
values: HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>,

defs: Vec<ModuleDefId>,
}
Expand All @@ -20,8 +23,9 @@ impl ItemScope {
&mut self,
name: Ident,
mod_def: ModuleDefId,
trait_id: Option<TraitId>,
) -> Result<(), (Ident, Ident)> {
self.add_item_to_namespace(name, mod_def)?;
self.add_item_to_namespace(name, mod_def, trait_id)?;
self.defs.push(mod_def);
Ok(())
}
Expand All @@ -33,16 +37,26 @@ impl ItemScope {
&mut self,
name: Ident,
mod_def: ModuleDefId,
trait_id: Option<TraitId>,
) -> Result<(), (Ident, Ident)> {
let add_item = |map: &mut HashMap<Ident, (ModuleDefId, Visibility)>| {
if let Entry::Occupied(o) = map.entry(name.clone()) {
let old_ident = o.key();
Err((old_ident.clone(), name))
} else {
map.insert(name, (mod_def, Visibility::Public));
Ok(())
}
};
let add_item =
|map: &mut HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>| {
if let Entry::Occupied(mut o) = map.entry(name.clone()) {
let trait_hashmap = o.get_mut();
if let Entry::Occupied(_) = trait_hashmap.entry(trait_id) {
let old_ident = o.key();
Err((old_ident.clone(), name))
} else {
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public));
Ok(())
}
} else {
let mut trait_hashmap = HashMap::new();
trait_hashmap.insert(trait_id, (mod_def, Visibility::Public));
map.insert(name, trait_hashmap);
Ok(())
}
};

match mod_def {
ModuleDefId::ModuleId(_) => add_item(&mut self.types),
Expand All @@ -55,34 +69,90 @@ impl ItemScope {
}

pub fn find_module_with_name(&self, mod_name: &Ident) -> Option<&ModuleId> {
let (module_def, _) = self.types.get(mod_name)?;
let (module_def, _) = self.types.get(mod_name)?.get(&None)?;
match module_def {
ModuleDefId::ModuleId(id) => Some(id),
_ => None,
}
}

pub fn find_func_with_name(&self, func_name: &Ident) -> Option<FuncId> {
let (module_def, _) = self.values.get(func_name)?;
let trait_hashmap = self.values.get(func_name)?;
// methods introduced without trait take priority and hide methods with the same name that come from a trait
let a = trait_hashmap.get(&None);
match a {
Some((module_def, _)) => match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
},
None => {
if trait_hashmap.len() == 1 {
let (module_def, _) = trait_hashmap.get(trait_hashmap.keys().last()?)?;
match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
}
} else {
// ambiguous name (multiple traits, containing the same function name)
None
}
}
}
}

pub fn find_func_with_name_and_trait_id(
&self,
func_name: &Ident,
trait_id: &Option<TraitId>,
) -> Option<FuncId> {
let (module_def, _) = self.values.get(func_name)?.get(trait_id)?;
match module_def {
ModuleDefId::FunctionId(id) => Some(*id),
_ => None,
}
}

pub fn find_name(&self, name: &Ident) -> PerNs {
PerNs { types: self.types.get(name).cloned(), values: self.values.get(name).cloned() }
// Names, not associated with traits are searched first. If not found, we search for name, coming from a trait.
// If we find only one name from trait, we return it. If there are multiple traits, providing the same name, we return None.
let find_name_in =
|a: &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>>| {
if let Some(t) = a.get(name) {
if let Some(tt) = t.get(&None) {
Some(*tt)
} else if t.len() == 1 {
t.values().last().cloned()
} else {
None
}
} else {
None
}
};

PerNs { types: find_name_in(&self.types), values: find_name_in(&self.values) }
}

pub fn find_name_for_trait_id(&self, name: &Ident, trait_id: &Option<TraitId>) -> PerNs {
PerNs {
types: if let Some(t) = self.types.get(name) { t.get(trait_id).cloned() } else { None },
values: if let Some(v) = self.values.get(name) {
v.get(trait_id).cloned()
} else {
None
},
}
}

pub fn definitions(&self) -> Vec<ModuleDefId> {
self.defs.clone()
}

pub fn types(&self) -> &HashMap<Ident, (ModuleDefId, Visibility)> {
pub fn types(&self) -> &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>> {
&self.types
}

pub fn values(&self) -> &HashMap<Ident, (ModuleDefId, Visibility)> {
pub fn values(&self) -> &HashMap<Ident, HashMap<Option<TraitId>, (ModuleDefId, Visibility)>> {
&self.values
}

Expand Down
Loading

0 comments on commit c61df1a

Please sign in to comment.