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

chore: proptest for canonicalize on infix type expressions #6269

Merged
merged 10 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions compiler/noirc_frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ strum_macros = "0.24"

[dev-dependencies]
base64.workspace = true
proptest.workspace = true
proptest-derive = "0.5.0"

[features]
experimental_parser = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc fc27f4091dfa5f938973048209b5fcf22aefa1cfaffaaa3e349f30e9b1f93f49 # shrinks to infix_and_bindings = (((0: numeric bool) % (Numeric(Shared(RefCell { value: Unbound('2, Numeric(bool)) }): bool) + Numeric(Shared(RefCell { value: Unbound('0, Numeric(bool)) }): bool))), [('0, (0: numeric bool)), ('1, (0: numeric bool)), ('2, (0: numeric bool))])
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/ast/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl UnresolvedGeneric {
UnresolvedGeneric::Variable(_) => Ok(Kind::Normal),
UnresolvedGeneric::Numeric { typ, .. } => {
let typ = self.resolve_numeric_kind_type(typ)?;
Ok(Kind::Numeric(Box::new(typ)))
Ok(Kind::numeric(typ))
}
UnresolvedGeneric::Resolved(..) => {
panic!("Don't know the kind of a resolved generic here")
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub use visitor::Visitor;
pub use expression::*;
pub use function::*;

#[cfg(test)]
use proptest_derive::Arbitrary;

use acvm::FieldElement;
pub use docs::*;
use noirc_errors::Span;
Expand All @@ -37,6 +40,7 @@ use crate::{
use acvm::acir::AcirField;
use iter_extended::vecmap;

#[cfg_attr(test, derive(Arbitrary))]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
pub enum IntegerBitSize {
One,
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ impl<'context> Elaborator<'context> {
let typ = if unresolved_typ.is_type_expression() {
self.resolve_type_inner(
unresolved_typ.clone(),
&Kind::Numeric(Box::new(Type::default_int_type())),
&Kind::numeric(Type::default_int_type()),
)
} else {
self.resolve_type(unresolved_typ.clone())
Expand All @@ -654,7 +654,7 @@ impl<'context> Elaborator<'context> {
});
self.push_err(unsupported_typ_err);
}
Kind::Numeric(Box::new(typ))
Kind::numeric(typ)
} else {
Kind::Normal
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ impl<'context> Elaborator<'context> {
let kind = self
.interner
.get_global_let_statement(id)
.map(|let_statement| Kind::Numeric(Box::new(let_statement.r#type)))
.map(|let_statement| Kind::numeric(let_statement.r#type))
.unwrap_or(Kind::u32());

// TODO(https://github.com/noir-lang/noir/issues/6238):
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ impl<'a> ModCollector<'a> {
name: Rc::new(name.to_string()),
type_var: TypeVariable::unbound(
type_variable_id,
Kind::Numeric(Box::new(typ)),
Kind::numeric(typ),
),
span: name.span(),
});
Expand Down
15 changes: 12 additions & 3 deletions compiler/noirc_frontend/src/hir_def/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use std::{
rc::Rc,
};

#[cfg(test)]
use proptest_derive::Arbitrary;

use acvm::{AcirField, FieldElement};

use crate::{
Expand Down Expand Up @@ -169,6 +172,11 @@ pub enum Kind {
}

impl Kind {
// Kind::Numeric constructor helper
pub fn numeric(typ: Type) -> Kind {
Kind::Numeric(Box::new(typ))
}

pub(crate) fn is_error(&self) -> bool {
match self.follow_bindings() {
Self::Numeric(typ) => *typ == Type::Error,
Expand Down Expand Up @@ -196,7 +204,7 @@ impl Kind {
}

pub(crate) fn u32() -> Self {
Self::Numeric(Box::new(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo)))
Self::numeric(Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo))
}

pub(crate) fn follow_bindings(&self) -> Self {
Expand All @@ -205,7 +213,7 @@ impl Kind {
Self::Normal => Self::Normal,
Self::Integer => Self::Integer,
Self::IntegerOrField => Self::IntegerOrField,
Self::Numeric(typ) => Self::Numeric(Box::new(typ.follow_bindings())),
Self::Numeric(typ) => Self::numeric(typ.follow_bindings()),
}
}

Expand Down Expand Up @@ -671,6 +679,7 @@ impl<T> Shared<T> {

/// A restricted subset of binary operators useable on
/// type level integers for use in the array length positions of types.
#[cfg_attr(test, derive(Arbitrary))]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum BinaryTypeOperator {
Addition,
Expand Down Expand Up @@ -1422,7 +1431,7 @@ impl Type {
if self_kind.unifies(&other_kind) {
self_kind
} else {
Kind::Numeric(Box::new(Type::Error))
Kind::numeric(Type::Error)
}
}

Expand Down
4 changes: 1 addition & 3 deletions compiler/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,9 +944,7 @@ impl<'interner> Monomorphizer<'interner> {
};
let location = self.interner.id_location(expr_id);

if !Kind::Numeric(numeric_typ.clone())
.unifies(&Kind::Numeric(Box::new(typ.clone())))
{
if !Kind::Numeric(numeric_typ.clone()).unifies(&Kind::numeric(typ.clone())) {
let message = "ICE: Generic's kind does not match expected type";
return Err(MonomorphizationError::InternalError { location, message });
}
Expand Down
119 changes: 1 addition & 118 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(test)]

mod aliases;
mod arithmetic_generics;
mod bound_checks;
mod imports;
mod metaprogramming;
Expand All @@ -16,7 +17,6 @@ mod visibility;
// A test harness will allow for more expressive and readable tests
use std::collections::BTreeMap;

use acvm::{AcirField, FieldElement};
use fm::FileId;

use iter_extended::vecmap;
Expand All @@ -37,7 +37,6 @@ use crate::hir::def_collector::dc_crate::DefCollector;
use crate::hir::def_map::{CrateDefMap, LocalModuleId};
use crate::hir_def::expr::HirExpression;
use crate::hir_def::stmt::HirStatement;
use crate::hir_def::types::{BinaryTypeOperator, Type};
use crate::monomorphization::ast::Program;
use crate::monomorphization::errors::MonomorphizationError;
use crate::monomorphization::monomorphize;
Expand Down Expand Up @@ -3210,122 +3209,6 @@ fn as_trait_path_syntax_no_impl() {
assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. })));
}

#[test]
fn arithmetic_generics_canonicalization_deduplication_regression() {
let source = r#"
struct ArrData<let N: u32> {
a: [Field; N],
b: [Field; N + N - 1],
}

fn main() {
let _f: ArrData<5> = ArrData {
a: [0; 5],
b: [0; 9],
};
}
"#;
let errors = get_program_errors(source);
assert_eq!(errors.len(), 0);
}

#[test]
fn arithmetic_generics_checked_cast_zeros() {
let source = r#"
struct W<let N: u1> {}

fn foo<let N: u1>(_x: W<N>) -> W<(0 * N) / (N % N)> {
W {}
}

fn bar<let N: u1>(_x: W<N>) -> u1 {
N
}

fn main() -> pub u1 {
let w_0: W<0> = W {};
let w: W<_> = foo(w_0);
bar(w)
}
"#;

let errors = get_program_errors(source);
assert_eq!(errors.len(), 0);

let monomorphization_error = get_monomorphization_error(source);
assert!(monomorphization_error.is_some());

// Expect a CheckedCast (0 % 0) failure
let monomorphization_error = monomorphization_error.unwrap();
if let MonomorphizationError::UnknownArrayLength { ref length, ref err, location: _ } =
monomorphization_error
{
match length {
Type::CheckedCast { from, to } => {
assert!(matches!(*from.clone(), Type::InfixExpr { .. }));
assert!(matches!(*to.clone(), Type::InfixExpr { .. }));
}
_ => panic!("unexpected length: {:?}", length),
}
assert!(matches!(
err,
TypeCheckError::FailingBinaryOp { op: BinaryTypeOperator::Modulo, lhs: 0, rhs: 0, .. }
));
} else {
panic!("unexpected error: {:?}", monomorphization_error);
}
}

#[test]
fn arithmetic_generics_checked_cast_indirect_zeros() {
let source = r#"
struct W<let N: Field> {}

fn foo<let N: Field>(_x: W<N>) -> W<(N - N) % (N - N)> {
W {}
}

fn bar<let N: Field>(_x: W<N>) -> Field {
N
}

fn main() {
let w_0: W<0> = W {};
let w = foo(w_0);
let _ = bar(w);
}
"#;

let errors = get_program_errors(source);
assert_eq!(errors.len(), 0);

let monomorphization_error = get_monomorphization_error(source);
assert!(monomorphization_error.is_some());

// Expect a CheckedCast (0 % 0) failure
let monomorphization_error = monomorphization_error.unwrap();
if let MonomorphizationError::UnknownArrayLength { ref length, ref err, location: _ } =
monomorphization_error
{
match length {
Type::CheckedCast { from, to } => {
assert!(matches!(*from.clone(), Type::InfixExpr { .. }));
assert!(matches!(*to.clone(), Type::InfixExpr { .. }));
}
_ => panic!("unexpected length: {:?}", length),
}
match err {
TypeCheckError::ModuloOnFields { lhs, rhs, .. } => {
assert_eq!(lhs.clone(), FieldElement::zero());
assert_eq!(rhs.clone(), FieldElement::zero());
}
_ => panic!("expected ModuloOnFields, but found: {:?}", err),
}
} else {
panic!("unexpected error: {:?}", monomorphization_error);
}
}

#[test]
fn infer_globals_to_u32_from_type_use() {
let src = r#"
Expand Down
Loading
Loading