From 9af5357657c1c7d5e0882ccad9bb7ff8759ad5bf Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 6 Dec 2024 22:30:45 +0000 Subject: [PATCH 01/13] first compiling proptests (in types.rs) --- hugr-core/src/types.rs | 272 ++++++++++++++++++++++++++++++ hugr-core/src/types/type_param.rs | 2 +- 2 files changed, 273 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 655531ab5..8f3bdc907 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -700,4 +700,276 @@ pub(crate) mod test { } } } + + mod proptest2 { + use std::{iter::once, sync::Arc}; + + use crate::extension::{ExtensionRegistry, ExtensionSet, TypeDef}; + use crate::types::{ + type_param::TypeParam, CustomType, FuncValueType, Type, TypeArg, TypeBound, TypeRow, + }; + use crate::Extension; + use itertools::Itertools; + use proptest::sample::Index; + use proptest::string::string_regex; + use proptest::{ + collection::vec, + prelude::{any, Just, Strategy}, + prop_oneof, + sample::select, + }; + + trait VarEnvState: Send + Sync { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone; + } + + struct MakeTypeVar(TypeParam); + + impl VarEnvState for MakeTypeVar { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + let mut opts = env + .iter() + .enumerate() + .filter(|(_, p)| self.0.contains(p)) + .map(|(i, p)| (TypeArg::new_var_use(i, p.clone()), env.clone())) + .collect_vec(); + let mut env_with_new = env.clone(); + env_with_new.push(self.0.clone()); + opts.push(( + TypeArg::new_var_use(env.len(), self.0.clone()), + env_with_new, + )); + select(opts) + } + } + + #[derive(Debug, Clone)] + struct MakeType(TypeBound); + + impl VarEnvState for MakeType { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + let reg: &ExtensionRegistry = get_reg(); + let tys = reg + .iter() + .map(Arc::as_ref) + .flat_map(Extension::types) + .map(|(_, typedef)| typedef) + .collect_vec(); + let env2 = env.clone(); + prop_oneof![ + // no Alias + any::() + .prop_flat_map(move |idx| MakeCustomType(idx.get::<&TypeDef>(&tys)) + .with_vars(env2.clone())) + .prop_map(|(ct, env)| (Type::new_extension(ct), env)), + MakeFuncType.with_vars(env.clone()), + MakeTypeVar(self.0.into()).with_vars(env.clone()).prop_map( + |(ta, env)| match ta { + TypeArg::Type { ty } => (ty, env), + _ => panic!("Passed in a TypeBound, expected a Type"), + } + ), + // Type has no row_variable; consider joining with TypeRV + MakeSumType(self.0).with_vars(env) + ] //.sboxed().prop_map(|(e,env)| (Type::from(e), env)) + } + } + + struct MakeSumType(TypeBound); + const MAX_NUM_VARIANTS: usize = 5; + impl VarEnvState for MakeSumType { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + let b = self.0; + (1..MAX_NUM_VARIANTS) + .prop_flat_map(move |nv| fold_vec(vec![MakeTypeRow(b); nv], env.clone())) + .prop_map(|(rows, env)| (Type::new_sum(rows), env)) + } + } + + #[derive(Clone, Debug)] + struct MakeTypeArg(TypeParam); + const MAX_LIST_LEN: usize = 4; + impl VarEnvState for MakeTypeArg { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + match &self.0 { + TypeParam::Type { b } => MakeType(*b) + .with_vars(env) + .prop_map(|(ty, env)| (TypeArg::Type { ty }, env)) + .sboxed(), + TypeParam::BoundedNat { bound } => { + let env2 = env.clone(); + prop_oneof![ + match bound.value() { + Some(max) => (0..max.get()).sboxed(), + None => proptest::num::u64::ANY.sboxed(), + } + .prop_map(move |n| (TypeArg::BoundedNat { n }, env.clone())), + MakeTypeVar(self.0).with_vars(env2) + ] + .sboxed() + } + TypeParam::String => { + let env2 = env.clone(); + prop_oneof![ + string_regex("[a-z]+") + .unwrap() + .prop_map(move |arg| (TypeArg::String { arg }, env.clone())), + MakeTypeVar(self.0).with_vars(env2) + ] + .sboxed() + } + TypeParam::List { param } => { + fold_n(MakeTypeArg((**param).clone()), 0..MAX_LIST_LEN, env) + .prop_map(|(elems, env)| (TypeArg::Sequence { elems }, env)) + .sboxed() + } + TypeParam::Tuple { params } => { + fold_vec(params.iter().cloned().map(MakeTypeArg).collect(), env) + .prop_map(|(elems, env)| (TypeArg::Sequence { elems }, env)) + .sboxed() + } + TypeParam::Extensions => make_extensions(env, get_reg()) + .prop_map(|(es, env)| (TypeArg::Extensions { es }, env)) + .sboxed(), + } + } + } + + struct MakeFuncType; + const MAX_TYPE_ROW_LEN: usize = 5; + impl VarEnvState for MakeFuncType { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + MakeTypeRow(TypeBound::Any) + .with_vars(env) + .prop_flat_map(|(inputs, env)| { + MakeTypeRow(TypeBound::Any).with_vars(env).prop_flat_map( + move |(outputs, env)| { + let inputs = inputs.clone(); + make_extensions(env, get_reg()).prop_map(move |(exts, env)| { + ( + Type::new_function( + FuncValueType::new(inputs.clone(), outputs.clone()) + .with_extension_delta(exts), + ), + env, + ) + }) + }, + ) + }) + } + } + + fn make_extensions( + env: Vec, + reg: &ExtensionRegistry, + ) -> impl Strategy)> { + // Some number of extensions from the registry + let es = vec( + select( + reg.iter() + .map(|e| ExtensionSet::singleton(e.name())) + .collect_vec(), + ), + 0..2, + ) + .prop_map(ExtensionSet::union_over); + let env2 = env.clone(); + prop_oneof![ + es.clone().prop_map(move |es| (es, env2.clone())), + es.prop_flat_map(move |es2| MakeTypeVar(TypeParam::Extensions) + .with_vars(env.clone()) + .prop_map(move |(ta, env)| ( + match ta { + TypeArg::Extensions { es } => es2.clone().union(es), + _ => panic!("Asked for TypeParam::Extensions"), + }, + env + ))) + ] + } + + struct MakeCustomType<'a>(&'a TypeDef); + + impl<'a> VarEnvState for MakeCustomType<'a> { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + fold_vec( + self.0.params().iter().cloned().map(MakeTypeArg).collect(), + env, + ) + .prop_map(|(v, env)| (self.0.instantiate(v).unwrap(), env)) + } + } + + fn get_reg() -> &'static ExtensionRegistry { + todo!() + } // probably pass Arc into every with_vars?? + + #[derive(Clone)] + struct MakeTypeRow(TypeBound); + + impl VarEnvState for MakeTypeRow { + fn with_vars( + self, + env: Vec, + ) -> impl Strategy)> + Clone { + (0..MAX_TYPE_ROW_LEN) + .prop_flat_map(move |sz| fold_vec(vec![MakeType(self.0); sz], env.clone())) + .prop_map(|(vec, env)| (TypeRow::from(vec), env)) + } + } + + fn fold_n< + E: Clone + std::fmt::Debug + 'static + Send + Sync, + T: VarEnvState + Clone + Send + Sync + 'static, + >( + elem_strat: T, + size_strat: impl Strategy, + params: Vec, + ) -> impl Strategy, Vec)> { + size_strat + .prop_flat_map(move |sz| fold_vec(vec![elem_strat.clone(); sz], params.clone())) + } + + fn fold_vec< + E: Clone + std::fmt::Debug + 'static + Send + Sync, + T: VarEnvState + Clone + Send + Sync + 'static, + >( + strats: Vec, + params: Vec, + ) -> impl Strategy, Vec)> + Clone { + let mut s = Just((Vec::::new(), params)).sboxed(); + for strat in strats { + s = s + .prop_flat_map(move |(v, env)| { + strat.clone().with_vars(env).prop_map(move |(elem, env)| { + (v.iter().cloned().chain(once(elem)).collect(), env) + }) + }) + .sboxed(); + } + s + } + } } diff --git a/hugr-core/src/types/type_param.rs b/hugr-core/src/types/type_param.rs index 3c40552aa..77e49a18f 100644 --- a/hugr-core/src/types/type_param.rs +++ b/hugr-core/src/types/type_param.rs @@ -108,7 +108,7 @@ impl TypeParam { } } - fn contains(&self, other: &TypeParam) -> bool { + pub(super) fn contains(&self, other: &TypeParam) -> bool { match (self, other) { (TypeParam::Type { b: b1 }, TypeParam::Type { b: b2 }) => b1.contains(*b2), (TypeParam::BoundedNat { bound: b1 }, TypeParam::BoundedNat { bound: b2 }) => { From 625b5874bdf86f14773797af7789666cf2fbbd3d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 8 Dec 2024 09:58:27 +0000 Subject: [PATCH 02/13] with_vars -> with_env, also taking Arc; rewrite CustomType --- hugr-core/src/types.rs | 215 +++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 94 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 8f3bdc907..83c39dd00 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -704,11 +704,10 @@ pub(crate) mod test { mod proptest2 { use std::{iter::once, sync::Arc}; - use crate::extension::{ExtensionRegistry, ExtensionSet, TypeDef}; + use crate::extension::{ExtensionRegistry, ExtensionSet}; use crate::types::{ - type_param::TypeParam, CustomType, FuncValueType, Type, TypeArg, TypeBound, TypeRow, + type_param::TypeParam, FuncValueType, Type, TypeArg, TypeBound, TypeRow, }; - use crate::Extension; use itertools::Itertools; use proptest::sample::Index; use proptest::string::string_regex; @@ -720,29 +719,31 @@ pub(crate) mod test { }; trait VarEnvState: Send + Sync { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone; } struct MakeTypeVar(TypeParam); impl VarEnvState for MakeTypeVar { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + _reg: Arc, ) -> impl Strategy)> + Clone { - let mut opts = env + let mut opts = vars .iter() .enumerate() .filter(|(_, p)| self.0.contains(p)) - .map(|(i, p)| (TypeArg::new_var_use(i, p.clone()), env.clone())) + .map(|(i, p)| (TypeArg::new_var_use(i, p.clone()), vars.clone())) .collect_vec(); - let mut env_with_new = env.clone(); + let mut env_with_new = vars.clone(); env_with_new.push(self.0.clone()); opts.push(( - TypeArg::new_var_use(env.len(), self.0.clone()), + TypeArg::new_var_use(vars.len(), self.0.clone()), env_with_new, )); select(opts) @@ -753,48 +754,41 @@ pub(crate) mod test { struct MakeType(TypeBound); impl VarEnvState for MakeType { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone { - let reg: &ExtensionRegistry = get_reg(); - let tys = reg - .iter() - .map(Arc::as_ref) - .flat_map(Extension::types) - .map(|(_, typedef)| typedef) - .collect_vec(); - let env2 = env.clone(); prop_oneof![ // no Alias - any::() - .prop_flat_map(move |idx| MakeCustomType(idx.get::<&TypeDef>(&tys)) - .with_vars(env2.clone())) - .prop_map(|(ct, env)| (Type::new_extension(ct), env)), - MakeFuncType.with_vars(env.clone()), - MakeTypeVar(self.0.into()).with_vars(env.clone()).prop_map( - |(ta, env)| match ta { - TypeArg::Type { ty } => (ty, env), + MakeCustomType.with_env(vars.clone(), reg.clone()), + MakeFuncType.with_env(vars.clone(), reg.clone()), + MakeTypeVar(self.0.into()) + .with_env(vars.clone(), reg.clone()) + .prop_map(|(ta, vars)| match ta { + TypeArg::Type { ty } => (ty, vars), _ => panic!("Passed in a TypeBound, expected a Type"), - } - ), + }), // Type has no row_variable; consider joining with TypeRV - MakeSumType(self.0).with_vars(env) - ] //.sboxed().prop_map(|(e,env)| (Type::from(e), env)) + MakeSumType(self.0).with_env(vars, reg) + ] } } struct MakeSumType(TypeBound); const MAX_NUM_VARIANTS: usize = 5; impl VarEnvState for MakeSumType { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone { let b = self.0; (1..MAX_NUM_VARIANTS) - .prop_flat_map(move |nv| fold_vec(vec![MakeTypeRow(b); nv], env.clone())) - .prop_map(|(rows, env)| (Type::new_sum(rows), env)) + .prop_flat_map(move |nv| { + fold_vec(vec![MakeTypeRow(b); nv], vars.clone(), reg.clone()) + }) + .prop_map(|(rows, vars)| (Type::new_sum(rows), vars)) } } @@ -802,49 +796,50 @@ pub(crate) mod test { struct MakeTypeArg(TypeParam); const MAX_LIST_LEN: usize = 4; impl VarEnvState for MakeTypeArg { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone { match &self.0 { TypeParam::Type { b } => MakeType(*b) - .with_vars(env) - .prop_map(|(ty, env)| (TypeArg::Type { ty }, env)) + .with_env(vars, reg) + .prop_map(|(ty, vars)| (TypeArg::Type { ty }, vars)) .sboxed(), TypeParam::BoundedNat { bound } => { - let env2 = env.clone(); + let vars2 = vars.clone(); prop_oneof![ match bound.value() { Some(max) => (0..max.get()).sboxed(), None => proptest::num::u64::ANY.sboxed(), } - .prop_map(move |n| (TypeArg::BoundedNat { n }, env.clone())), - MakeTypeVar(self.0).with_vars(env2) + .prop_map(move |n| (TypeArg::BoundedNat { n }, vars.clone())), + MakeTypeVar(self.0).with_env(vars2, reg) ] .sboxed() } TypeParam::String => { - let env2 = env.clone(); + let vars2 = vars.clone(); prop_oneof![ string_regex("[a-z]+") .unwrap() - .prop_map(move |arg| (TypeArg::String { arg }, env.clone())), - MakeTypeVar(self.0).with_vars(env2) + .prop_map(move |arg| (TypeArg::String { arg }, vars.clone())), + MakeTypeVar(self.0).with_env(vars2, reg) ] .sboxed() } TypeParam::List { param } => { - fold_n(MakeTypeArg((**param).clone()), 0..MAX_LIST_LEN, env) - .prop_map(|(elems, env)| (TypeArg::Sequence { elems }, env)) + fold_n(MakeTypeArg((**param).clone()), 0..MAX_LIST_LEN, vars, reg) + .prop_map(|(elems, vars)| (TypeArg::Sequence { elems }, vars)) .sboxed() } TypeParam::Tuple { params } => { - fold_vec(params.iter().cloned().map(MakeTypeArg).collect(), env) - .prop_map(|(elems, env)| (TypeArg::Sequence { elems }, env)) + fold_vec(params.iter().cloned().map(MakeTypeArg).collect(), vars, reg) + .prop_map(|(elems, vars)| (TypeArg::Sequence { elems }, vars)) .sboxed() } - TypeParam::Extensions => make_extensions(env, get_reg()) - .prop_map(|(es, env)| (TypeArg::Extensions { es }, env)) + TypeParam::Extensions => make_extensions(vars, reg) + .prop_map(|(es, vars)| (TypeArg::Extensions { es }, vars)) .sboxed(), } } @@ -853,34 +848,36 @@ pub(crate) mod test { struct MakeFuncType; const MAX_TYPE_ROW_LEN: usize = 5; impl VarEnvState for MakeFuncType { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone { MakeTypeRow(TypeBound::Any) - .with_vars(env) - .prop_flat_map(|(inputs, env)| { - MakeTypeRow(TypeBound::Any).with_vars(env).prop_flat_map( - move |(outputs, env)| { + .with_env(vars, reg.clone()) + .prop_flat_map(move |(inputs, vars)| { + let reg2 = reg.clone(); + MakeTypeRow(TypeBound::Any) + .with_env(vars, reg.clone()) + .prop_flat_map(move |(outputs, vars)| { let inputs = inputs.clone(); - make_extensions(env, get_reg()).prop_map(move |(exts, env)| { + make_extensions(vars, reg2.clone()).prop_map(move |(exts, vars)| { ( Type::new_function( FuncValueType::new(inputs.clone(), outputs.clone()) .with_extension_delta(exts), ), - env, + vars, ) }) - }, - ) + }) }) } } fn make_extensions( - env: Vec, - reg: &ExtensionRegistry, + vars: Vec, + reg: Arc, ) -> impl Strategy)> { // Some number of extensions from the registry let es = vec( @@ -892,51 +889,74 @@ pub(crate) mod test { 0..2, ) .prop_map(ExtensionSet::union_over); - let env2 = env.clone(); + let vars2 = vars.clone(); prop_oneof![ - es.clone().prop_map(move |es| (es, env2.clone())), + es.clone().prop_map(move |es| (es, vars2.clone())), es.prop_flat_map(move |es2| MakeTypeVar(TypeParam::Extensions) - .with_vars(env.clone()) - .prop_map(move |(ta, env)| ( + .with_env(vars.clone(), reg.clone()) + .prop_map(move |(ta, vars)| ( match ta { TypeArg::Extensions { es } => es2.clone().union(es), _ => panic!("Asked for TypeParam::Extensions"), }, - env + vars ))) ] } - struct MakeCustomType<'a>(&'a TypeDef); + struct MakeCustomType; - impl<'a> VarEnvState for MakeCustomType<'a> { - fn with_vars( + impl VarEnvState for MakeCustomType { + fn with_env( self, - env: Vec, - ) -> impl Strategy)> + Clone { - fold_vec( - self.0.params().iter().cloned().map(MakeTypeArg).collect(), - env, - ) - .prop_map(|(v, env)| (self.0.instantiate(v).unwrap(), env)) + vars: Vec, + reg: Arc, + ) -> impl Strategy)> + Clone { + any::().prop_flat_map(move |idx| { + let (ext, typ) = *idx.get( + ®.iter() + .flat_map(|e| e.types().map(|(name, _)| (e.name(), name))) + .collect_vec(), + ); + let typedef = reg.get(ext).unwrap().get_type(typ).unwrap(); + // Make unborrowed things that inner closure can take ownership op: + let (ext, typ, reg) = (ext.clone(), typ.clone(), reg.clone()); + fold_vec( + typedef.params().iter().cloned().map(MakeTypeArg).collect(), + vars.clone(), + reg.clone(), + ) + .prop_map(move |(v, vars)| { + ( + Type::new_extension( + reg.get(&ext) + .unwrap() + .get_type(&typ) + .unwrap() + .instantiate(v) + .unwrap(), + ), + vars, + ) + }) + }) } } - fn get_reg() -> &'static ExtensionRegistry { - todo!() - } // probably pass Arc into every with_vars?? - #[derive(Clone)] struct MakeTypeRow(TypeBound); impl VarEnvState for MakeTypeRow { - fn with_vars( + fn with_env( self, - env: Vec, + vars: Vec, + reg: Arc, ) -> impl Strategy)> + Clone { (0..MAX_TYPE_ROW_LEN) - .prop_flat_map(move |sz| fold_vec(vec![MakeType(self.0); sz], env.clone())) - .prop_map(|(vec, env)| (TypeRow::from(vec), env)) + .prop_flat_map(move |sz| { + fold_vec(vec![MakeType(self.0); sz], vars.clone(), reg.clone()) + }) + .prop_map(|(vec, vars)| (TypeRow::from(vec), vars)) } } @@ -947,9 +967,11 @@ pub(crate) mod test { elem_strat: T, size_strat: impl Strategy, params: Vec, + reg: Arc, ) -> impl Strategy, Vec)> { - size_strat - .prop_flat_map(move |sz| fold_vec(vec![elem_strat.clone(); sz], params.clone())) + size_strat.prop_flat_map(move |sz| { + fold_vec(vec![elem_strat.clone(); sz], params.clone(), reg.clone()) + }) } fn fold_vec< @@ -958,14 +980,19 @@ pub(crate) mod test { >( strats: Vec, params: Vec, + reg: Arc, ) -> impl Strategy, Vec)> + Clone { let mut s = Just((Vec::::new(), params)).sboxed(); for strat in strats { + let reg2 = reg.clone(); s = s - .prop_flat_map(move |(v, env)| { - strat.clone().with_vars(env).prop_map(move |(elem, env)| { - (v.iter().cloned().chain(once(elem)).collect(), env) - }) + .prop_flat_map(move |(v, vars)| { + strat + .clone() + .with_env(vars, reg2.clone()) + .prop_map(move |(elem, vars)| { + (v.iter().cloned().chain(once(elem)).collect(), vars) + }) }) .sboxed(); } From 6d744692a141698d8e0c751fc00355ef040cf46e Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sat, 7 Dec 2024 09:39:44 +0000 Subject: [PATCH 03/13] Add MakePair --- hugr-core/src/types.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 83c39dd00..5193bd2a4 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -998,5 +998,28 @@ pub(crate) mod test { } s } + + struct MakePair(S, T); + + impl, T: VarEnvState + Clone> VarEnvState<(A, B)> for MakePair + where + A: std::fmt::Debug + Clone, + B: std::fmt::Debug, + { + fn with_env( + self, + env: Vec, + reg: Arc, + ) -> impl Strategy)> + Clone { + self.0 + .with_env(env, reg.clone()) + .prop_flat_map(move |(v1, env)| { + self.1 + .clone() + .with_env(env, reg.clone()) + .prop_map(move |(v2, env)| ((v1.clone(), v2), env)) + }) + } + } } } From 24dc93d6667c8ffe77cf57dfdfaaba1f15877869 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 8 Dec 2024 14:04:30 +0000 Subject: [PATCH 04/13] fold_vec => MakeVec --- hugr-core/src/types.rs | 86 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 5193bd2a4..3d4c772c0 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -786,7 +786,7 @@ pub(crate) mod test { let b = self.0; (1..MAX_NUM_VARIANTS) .prop_flat_map(move |nv| { - fold_vec(vec![MakeTypeRow(b); nv], vars.clone(), reg.clone()) + MakeVec(vec![MakeTypeRow(b); nv]).with_env(vars.clone(), reg.clone()) }) .prop_map(|(rows, vars)| (Type::new_sum(rows), vars)) } @@ -834,7 +834,8 @@ pub(crate) mod test { .sboxed() } TypeParam::Tuple { params } => { - fold_vec(params.iter().cloned().map(MakeTypeArg).collect(), vars, reg) + MakeVec(params.iter().cloned().map(MakeTypeArg).collect()) + .with_env(vars, reg) .prop_map(|(elems, vars)| (TypeArg::Sequence { elems }, vars)) .sboxed() } @@ -921,24 +922,21 @@ pub(crate) mod test { let typedef = reg.get(ext).unwrap().get_type(typ).unwrap(); // Make unborrowed things that inner closure can take ownership op: let (ext, typ, reg) = (ext.clone(), typ.clone(), reg.clone()); - fold_vec( - typedef.params().iter().cloned().map(MakeTypeArg).collect(), - vars.clone(), - reg.clone(), - ) - .prop_map(move |(v, vars)| { - ( - Type::new_extension( - reg.get(&ext) - .unwrap() - .get_type(&typ) - .unwrap() - .instantiate(v) - .unwrap(), - ), - vars, - ) - }) + MakeVec(typedef.params().iter().cloned().map(MakeTypeArg).collect()) + .with_env(vars.clone(), reg.clone()) + .prop_map(move |(v, vars)| { + ( + Type::new_extension( + reg.get(&ext) + .unwrap() + .get_type(&typ) + .unwrap() + .instantiate(v) + .unwrap(), + ), + vars, + ) + }) }) } } @@ -954,7 +952,7 @@ pub(crate) mod test { ) -> impl Strategy)> + Clone { (0..MAX_TYPE_ROW_LEN) .prop_flat_map(move |sz| { - fold_vec(vec![MakeType(self.0); sz], vars.clone(), reg.clone()) + MakeVec(vec![MakeType(self.0); sz]).with_env(vars.clone(), reg.clone()) }) .prop_map(|(vec, vars)| (TypeRow::from(vec), vars)) } @@ -970,33 +968,37 @@ pub(crate) mod test { reg: Arc, ) -> impl Strategy, Vec)> { size_strat.prop_flat_map(move |sz| { - fold_vec(vec![elem_strat.clone(); sz], params.clone(), reg.clone()) + MakeVec(vec![elem_strat.clone(); sz]).with_env(params.clone(), reg.clone()) }) } - fn fold_vec< + struct MakeVec(Vec); + + impl> VarEnvState> for MakeVec + where E: Clone + std::fmt::Debug + 'static + Send + Sync, T: VarEnvState + Clone + Send + Sync + 'static, - >( - strats: Vec, - params: Vec, - reg: Arc, - ) -> impl Strategy, Vec)> + Clone { - let mut s = Just((Vec::::new(), params)).sboxed(); - for strat in strats { - let reg2 = reg.clone(); - s = s - .prop_flat_map(move |(v, vars)| { - strat - .clone() - .with_env(vars, reg2.clone()) - .prop_map(move |(elem, vars)| { - (v.iter().cloned().chain(once(elem)).collect(), vars) - }) - }) - .sboxed(); + { + fn with_env( + self, + vars: Vec, + reg: Arc, + ) -> impl Strategy, Vec)> + Clone { + let mut s = Just((Vec::::new(), vars)).sboxed(); + for strat in self.0 { + let reg2 = reg.clone(); + s = s + .prop_flat_map(move |(v, vars)| { + strat.clone().with_env(vars, reg2.clone()).prop_map( + move |(elem, vars)| { + (v.iter().cloned().chain(once(elem)).collect(), vars) + }, + ) + }) + .sboxed(); + } + s } - s } struct MakePair(S, T); From 15936dd7c46b9ef58d0422578d9cc2eed5524868 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 09:08:33 +0000 Subject: [PATCH 05/13] fix merge --- hugr-core/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 247cf3ef8..34ef3e5de 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -930,7 +930,7 @@ pub(crate) mod test { let es = vec( select( reg.iter() - .map(|e| ExtensionSet::singleton(e.name())) + .map(|e| ExtensionSet::singleton(e.name().clone())) .collect_vec(), ), 0..2, From f7cca461c052c9dc591ca344c647a14521dc1d76 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 16:30:46 +0000 Subject: [PATCH 06/13] make_type_var --- hugr-core/src/types.rs | 73 ++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 34ef3e5de..b64ef68ee 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -772,28 +772,23 @@ pub(crate) mod test { ) -> impl Strategy)> + Clone; } - struct MakeTypeVar(TypeParam); - - impl VarEnvState for MakeTypeVar { - fn with_env( - self, - vars: Vec, - _reg: Arc, - ) -> impl Strategy)> + Clone { - let mut opts = vars - .iter() - .enumerate() - .filter(|(_, p)| self.0.contains(p)) - .map(|(i, p)| (TypeArg::new_var_use(i, p.clone()), vars.clone())) - .collect_vec(); - let mut env_with_new = vars.clone(); - env_with_new.push(self.0.clone()); - opts.push(( - TypeArg::new_var_use(vars.len(), self.0.clone()), - env_with_new, - )); - select(opts) - } + fn make_type_var( + kind: TypeParam, + vars: Vec, + ) -> impl Strategy)> + Clone { + let mut opts = vars + .iter() + .enumerate() + .filter(|(_, p)| kind.contains(p)) + .map(|(i, p)| (TypeArg::new_var_use(i, p.clone()), vars.clone())) + .collect_vec(); + let mut env_with_new = vars; + env_with_new.push(kind.clone()); + opts.push(( + TypeArg::new_var_use(env_with_new.len() - 1, kind), + env_with_new, + )); + select(opts) } #[derive(Debug, Clone)] @@ -809,12 +804,10 @@ pub(crate) mod test { // no Alias MakeCustomType.with_env(vars.clone(), reg.clone()), MakeFuncType.with_env(vars.clone(), reg.clone()), - MakeTypeVar(self.0.into()) - .with_env(vars.clone(), reg.clone()) - .prop_map(|(ta, vars)| match ta { - TypeArg::Type { ty } => (ty, vars), - _ => panic!("Passed in a TypeBound, expected a Type"), - }), + make_type_var(self.0.into(), vars.clone()).prop_map(|(ta, vars)| match ta { + TypeArg::Type { ty } => (ty, vars), + _ => panic!("Passed in a TypeBound, expected a Type"), + }), // Type has no row_variable; consider joining with TypeRV MakeSumType(self.0).with_env(vars, reg) ] @@ -860,7 +853,7 @@ pub(crate) mod test { None => proptest::num::u64::ANY.sboxed(), } .prop_map(move |n| (TypeArg::BoundedNat { n }, vars.clone())), - MakeTypeVar(self.0).with_env(vars2, reg) + make_type_var(self.0, vars2) ] .sboxed() } @@ -870,7 +863,7 @@ pub(crate) mod test { string_regex("[a-z]+") .unwrap() .prop_map(move |arg| (TypeArg::String { arg }, vars.clone())), - MakeTypeVar(self.0).with_env(vars2, reg) + make_type_var(self.0, vars2) ] .sboxed() } @@ -939,15 +932,17 @@ pub(crate) mod test { let vars2 = vars.clone(); prop_oneof![ es.clone().prop_map(move |es| (es, vars2.clone())), - es.prop_flat_map(move |es2| MakeTypeVar(TypeParam::Extensions) - .with_env(vars.clone(), reg.clone()) - .prop_map(move |(ta, vars)| ( - match ta { - TypeArg::Extensions { es } => es2.clone().union(es), - _ => panic!("Asked for TypeParam::Extensions"), - }, - vars - ))) + es.prop_flat_map( + move |es2| make_type_var(TypeParam::Extensions, vars.clone()).prop_map( + move |(ta, vars)| ( + match ta { + TypeArg::Extensions { es } => es2.clone().union(es), + _ => panic!("Asked for TypeParam::Extensions"), + }, + vars + ) + ) + ) ] } From 86df78d42601b92e0f43b7c5f78a185d1045e59e Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 09:12:27 +0000 Subject: [PATCH 07/13] prop_filter for CustomType --- hugr-core/src/types.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index b64ef68ee..35c752985 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -802,7 +802,11 @@ pub(crate) mod test { ) -> impl Strategy)> + Clone { prop_oneof![ // no Alias - MakeCustomType.with_env(vars.clone(), reg.clone()), + MakeCustomType + .with_env(vars.clone(), reg.clone()) + .prop_filter("Must fit TypeBound", move |(ct, _)| self + .0 + .contains(ct.least_upper_bound())), MakeFuncType.with_env(vars.clone(), reg.clone()), make_type_var(self.0.into(), vars.clone()).prop_map(|(ta, vars)| match ta { TypeArg::Type { ty } => (ty, vars), From f7730f271b4126415fd1ce7d0fd27b72f15a8809 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 14:23:23 +0000 Subject: [PATCH 08/13] WIP First running test - fails with stack overflow --- hugr-core/src/types.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 35c752985..e70ec90e4 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -751,17 +751,17 @@ pub(crate) mod test { use std::{iter::once, sync::Arc}; use crate::extension::{ExtensionRegistry, ExtensionSet}; + use crate::std_extensions::std_reg; use crate::types::{ type_param::TypeParam, FuncValueType, Type, TypeArg, TypeBound, TypeRow, }; use itertools::Itertools; - use proptest::sample::Index; - use proptest::string::string_regex; use proptest::{ collection::vec, prelude::{any, Just, Strategy}, - prop_oneof, - sample::select, + prop_assert, prop_oneof, proptest, + sample::{select, Index}, + string::string_regex, }; trait VarEnvState: Send + Sync { @@ -1068,5 +1068,13 @@ pub(crate) mod test { }) } } + + proptest! { + #[test] + fn test_type_valid(t_with_env in MakeType(TypeBound::Any).with_env(vec![], Arc::new(std_reg()))) { + let (t,env) = t_with_env; + prop_assert!(t.validate(&env).is_ok()); + } + } } } From 073554958ae78db0192dfcd95ec444fca45328b1 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 16:52:38 +0000 Subject: [PATCH 09/13] Add RecursionDepth, limit stack to 3. Here's an example type: TypeBase(Function(FuncTypeBase { input: TypeRowBase { types: [ TypeBase(Extension(CustomType { extension: IdentList("prelude"), extension_ref: (Weak), id: "string", args: [], bound: Copyable }), Copyable)] }, output: TypeRowBase { types: [ TypeBase(Variable(0, Any), Any), TypeBase(Function(FuncTypeBase { input: TypeRowBase { types: [TypeBase(Sum(Unit { size: 1 }), Copyable), TypeBase(Sum(Unit { size: 1 }), Copyable)] }, output: TypeRowBase { types: [] }, runtime_reqs: ExtensionSet({}) }), Copyable)] }, runtime_reqs: ExtensionSet({IdentList("arithmetic.conversions")}) }), Copyable) with environment [Type { b: Any }] --- hugr-core/src/types.rs | 94 +++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index e70ec90e4..2495d3b56 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -751,6 +751,7 @@ pub(crate) mod test { use std::{iter::once, sync::Arc}; use crate::extension::{ExtensionRegistry, ExtensionSet}; + use crate::proptest::RecursionDepth; use crate::std_extensions::std_reg; use crate::types::{ type_param::TypeParam, FuncValueType, Type, TypeArg, TypeBound, TypeRow, @@ -768,6 +769,7 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone; } @@ -798,40 +800,55 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { + let non_leaf = (!depth.leaf()) as u32; + let depth = depth.descend(); prop_oneof![ // no Alias - MakeCustomType - .with_env(vars.clone(), reg.clone()) + 1 => MakeCustomType + .with_env(vars.clone(), depth, reg.clone()) .prop_filter("Must fit TypeBound", move |(ct, _)| self .0 .contains(ct.least_upper_bound())), - MakeFuncType.with_env(vars.clone(), reg.clone()), - make_type_var(self.0.into(), vars.clone()).prop_map(|(ta, vars)| match ta { + non_leaf => MakeFuncType.with_env(vars.clone(), depth, reg.clone()), + 1 => make_type_var(self.0.into(), vars.clone()).prop_map(|(ta, vars)| match ta { TypeArg::Type { ty } => (ty, vars), _ => panic!("Passed in a TypeBound, expected a Type"), }), // Type has no row_variable; consider joining with TypeRV - MakeSumType(self.0).with_env(vars, reg) + 1 => MakeSumType(self.0).with_env(vars, depth, reg) ] } } struct MakeSumType(TypeBound); - const MAX_NUM_VARIANTS: usize = 5; + const MAX_NUM_VARIANTS: u8 = 5; impl VarEnvState for MakeSumType { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { let b = self.0; - (1..MAX_NUM_VARIANTS) - .prop_flat_map(move |nv| { - MakeVec(vec![MakeTypeRow(b); nv]).with_env(vars.clone(), reg.clone()) - }) - .prop_map(|(rows, vars)| (Type::new_sum(rows), vars)) + if depth.leaf() { + (1..MAX_NUM_VARIANTS) + .prop_map(move |nv| (Type::new_unit_sum(nv), vars.clone())) + .sboxed() + } else { + (1..MAX_NUM_VARIANTS) + .prop_flat_map(move |nv| { + MakeVec(vec![MakeTypeRow(b); nv as usize]).with_env( + vars.clone(), + depth, + reg.clone(), + ) + }) + .prop_map(|(rows, vars)| (Type::new_sum(rows), vars)) + .sboxed() + } } } @@ -842,11 +859,12 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { match &self.0 { TypeParam::Type { b } => MakeType(*b) - .with_env(vars, reg) + .with_env(vars, depth, reg) .prop_map(|(ty, vars)| (TypeArg::Type { ty }, vars)) .sboxed(), TypeParam::BoundedNat { bound } => { @@ -872,13 +890,23 @@ pub(crate) mod test { .sboxed() } TypeParam::List { param } => { - fold_n(MakeTypeArg((**param).clone()), 0..MAX_LIST_LEN, vars, reg) + if depth.leaf() { + Just((TypeArg::Sequence { elems: vec![] }, vars)).sboxed() + } else { + fold_n( + MakeTypeArg((**param).clone()), + 0..MAX_LIST_LEN, + vars, + depth.descend(), + reg, + ) .prop_map(|(elems, vars)| (TypeArg::Sequence { elems }, vars)) .sboxed() + } } TypeParam::Tuple { params } => { MakeVec(params.iter().cloned().map(MakeTypeArg).collect()) - .with_env(vars, reg) + .with_env(vars, depth, reg) .prop_map(|(elems, vars)| (TypeArg::Sequence { elems }, vars)) .sboxed() } @@ -895,14 +923,15 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { MakeTypeRow(TypeBound::Any) - .with_env(vars, reg.clone()) + .with_env(vars, depth, reg.clone()) .prop_flat_map(move |(inputs, vars)| { let reg2 = reg.clone(); MakeTypeRow(TypeBound::Any) - .with_env(vars, reg.clone()) + .with_env(vars, depth, reg.clone()) .prop_flat_map(move |(outputs, vars)| { let inputs = inputs.clone(); make_extensions(vars, reg2.clone()).prop_map(move |(exts, vars)| { @@ -956,19 +985,24 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { any::().prop_flat_map(move |idx| { let (ext, typ) = *idx.get( ®.iter() - .flat_map(|e| e.types().map(|(name, _)| (e.name(), name))) + .flat_map(|e| { + e.types() + .filter(|t| !depth.leaf() || t.1.params().is_empty()) + .map(|(name, _)| (e.name(), name)) + }) .collect_vec(), ); let typedef = reg.get(ext).unwrap().get_type(typ).unwrap(); // Make unborrowed things that inner closure can take ownership op: let (ext, typ, reg) = (ext.clone(), typ.clone(), reg.clone()); MakeVec(typedef.params().iter().cloned().map(MakeTypeArg).collect()) - .with_env(vars.clone(), reg.clone()) + .with_env(vars.clone(), depth, reg.clone()) .prop_map(move |(v, vars)| { ( Type::new_extension( @@ -993,11 +1027,16 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { (0..MAX_TYPE_ROW_LEN) .prop_flat_map(move |sz| { - MakeVec(vec![MakeType(self.0); sz]).with_env(vars.clone(), reg.clone()) + MakeVec(vec![MakeType(self.0); sz]).with_env( + vars.clone(), + depth, + reg.clone(), + ) }) .prop_map(|(vec, vars)| (TypeRow::from(vec), vars)) } @@ -1010,10 +1049,11 @@ pub(crate) mod test { elem_strat: T, size_strat: impl Strategy, params: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy, Vec)> { size_strat.prop_flat_map(move |sz| { - MakeVec(vec![elem_strat.clone(); sz]).with_env(params.clone(), reg.clone()) + MakeVec(vec![elem_strat.clone(); sz]).with_env(params.clone(), depth, reg.clone()) }) } @@ -1027,6 +1067,7 @@ pub(crate) mod test { fn with_env( self, vars: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy, Vec)> + Clone { let mut s = Just((Vec::::new(), vars)).sboxed(); @@ -1034,7 +1075,7 @@ pub(crate) mod test { let reg2 = reg.clone(); s = s .prop_flat_map(move |(v, vars)| { - strat.clone().with_env(vars, reg2.clone()).prop_map( + strat.clone().with_env(vars, depth, reg2.clone()).prop_map( move |(elem, vars)| { (v.iter().cloned().chain(once(elem)).collect(), vars) }, @@ -1056,14 +1097,15 @@ pub(crate) mod test { fn with_env( self, env: Vec, + depth: RecursionDepth, reg: Arc, ) -> impl Strategy)> + Clone { self.0 - .with_env(env, reg.clone()) + .with_env(env, depth, reg.clone()) .prop_flat_map(move |(v1, env)| { self.1 .clone() - .with_env(env, reg.clone()) + .with_env(env, depth, reg.clone()) .prop_map(move |(v2, env)| ((v1.clone(), v2), env)) }) } @@ -1071,8 +1113,10 @@ pub(crate) mod test { proptest! { #[test] - fn test_type_valid(t_with_env in MakeType(TypeBound::Any).with_env(vec![], Arc::new(std_reg()))) { - let (t,env) = t_with_env; + // We override the RecursionDepth from default 4 down to 3 because otherwise we overflow the stack. + // It doesn't seem to be an infinite loop, I infer that the folding etc. in the VarEnvState methods + // just use a lot more stack than the simpler, original, proptests. + fn test_type_valid((t,env) in MakeType(TypeBound::Any).with_env(vec![], 3.into(), Arc::new(std_reg()))) { prop_assert!(t.validate(&env).is_ok()); } } From 458845e42ba5cea1b32d3013ff443c0fc17139fa Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 17:14:01 +0000 Subject: [PATCH 10/13] TEMP allow MakePair to be unused --- hugr-core/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 2495d3b56..5a59970e6 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -1087,6 +1087,7 @@ pub(crate) mod test { } } + #[allow(unused)] struct MakePair(S, T); impl, T: VarEnvState + Clone> VarEnvState<(A, B)> for MakePair From 47603bf51a93052d9f1fe5cf83cd3a7bb815bbe7 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 17:35:15 +0000 Subject: [PATCH 11/13] Test substitution of a single Type. Example original type TypeBase(Extension(CustomType { extension: IdentList("collections.list"), extension_ref: (Weak), id: "List", args: [Type { ty: TypeBase(Variable(0, Any), Any) }], bound: Any }), Any) Original environment [Type { b: Any }] Substituted variable 0: Type { ty: TypeBase(Sum(General { rows: [TypeRowBase { types: [TypeBase(Function(FuncTypeBase { input: TypeRowBase { types: [TypeBase(Extension(CustomType { extension: IdentList("prelude"), extension_ref: (Weak), id: "usize", args: [], bound: Copyable }), Copyable)] }, output: TypeRowBase { types: [TypeBase(Variable(0, Any), Any)] }, runtime_reqs: ExtensionSet({IdentList("logic")}) }), Copyable), TypeBase(Extension(CustomType { extension: IdentList("prelude"), extension_ref: (Weak), id: "qubit", args: [], bound: Any }), Any), TypeBase(Extension(CustomType { extension: IdentList("arithmetic.float.types"), extension_ref: (Weak), id: "float64", args: [], bound: Copyable }), Copyable)] }] }), Any) } Substitution result: TypeBase(Extension(CustomType { extension: IdentList("collections.list"), extension_ref: (Weak), id: "List", args: [Type { ty: TypeBase(Sum(General { rows: [TypeRowBase { types: [TypeBase(Function(FuncTypeBase { input: TypeRowBase { types: [TypeBase(Extension(CustomType { extension: IdentList("prelude"), extension_ref: (Weak), id: "usize", args: [], bound: Copyable }), Copyable)] }, output: TypeRowBase { types: [TypeBase(Variable(0, Any), Any)] }, runtime_reqs: ExtensionSet({IdentList("logic")}) }), Copyable), TypeBase(Extension(CustomType { extension: IdentList("prelude"), extension_ref: (Weak), id: "qubit", args: [], bound: Any }), Any), TypeBase(Extension(CustomType { extension: IdentList("arithmetic.float.types"), extension_ref: (Weak), id: "float64", args: [], bound: Copyable }), Copyable)] }] }), Any) }], bound: Any }), Any) --- hugr-core/src/types.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 5a59970e6..70ff44dc5 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -753,6 +753,7 @@ pub(crate) mod test { use crate::extension::{ExtensionRegistry, ExtensionSet}; use crate::proptest::RecursionDepth; use crate::std_extensions::std_reg; + use crate::types::Substitution; use crate::types::{ type_param::TypeParam, FuncValueType, Type, TypeArg, TypeBound, TypeRow, }; @@ -1112,13 +1113,42 @@ pub(crate) mod test { } } + /// Given a VarEnvState that builds a T with an environment, + /// Builds (a T, the environment for that T, and a TypeArg for each TypeParam in that environment) + /// with the environment making the TypeArgs valid + fn with_substitution( + content: impl VarEnvState, + content_depth: RecursionDepth, + subst_depth: RecursionDepth, + reg: Arc, + ) -> impl Strategy), Vec, Vec)> { + content + .with_env(vec![], content_depth, reg.clone()) + .prop_flat_map(move |(val, val_env)| { + MakeVec(val_env.iter().cloned().map(MakeTypeArg).collect()) + .with_env(vec![], subst_depth, reg.clone()) + .prop_map(move |(subst, subst_env)| { + ((val.clone(), val_env.clone()), subst, subst_env) + }) + }) + } + proptest! { #[test] // We override the RecursionDepth from default 4 down to 3 because otherwise we overflow the stack. // It doesn't seem to be an infinite loop, I infer that the folding etc. in the VarEnvState methods // just use a lot more stack than the simpler, original, proptests. - fn test_type_valid((t,env) in MakeType(TypeBound::Any).with_env(vec![], 3.into(), Arc::new(std_reg()))) { - prop_assert!(t.validate(&env).is_ok()); + fn test_type_substitution(((t,t_env), s, s_env) in with_substitution( + MakeType(TypeBound::Any), + 2.into(), + 2.into(), + Arc::new(std_reg()))) { + prop_assert!(t.validate(&t_env).is_ok()); + for s1 in s.iter() { + prop_assert!(s1.validate(&s_env).is_ok()); + } + let ts = t.substitute1(&Substitution::new(&s)); + prop_assert!(ts.validate(&s_env).is_ok()); } } } From 8ffec46679fcaab83e675a63a538bcb5eeba4126 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 17:41:04 +0000 Subject: [PATCH 12/13] RecursionDepth 3 and set RUST_MIN_STACK=10485760 --- .github/workflows/ci-rs.yml | 10 +++++----- hugr-core/src/types.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-rs.yml b/.github/workflows/ci-rs.yml index c3e6fc740..b975b4e16 100644 --- a/.github/workflows/ci-rs.yml +++ b/.github/workflows/ci-rs.yml @@ -117,7 +117,7 @@ jobs: - name: Build with no features run: cargo test --verbose --no-default-features --no-run - name: Tests with no features - run: cargo test --verbose --no-default-features + run: RUST_MIN_STACK=10485760 cargo test --verbose --no-default-features # Run tests on Rust stable tests-stable-all-features: @@ -140,7 +140,7 @@ jobs: - name: Build with all features run: cargo test --verbose --all-features --no-run - name: Tests with all features - run: cargo test --verbose --all-features + run: RUST_MIN_STACK=10485760 cargo test --verbose --all-features - name: Build HUGR binary run: cargo build -p hugr-cli - name: Upload the binary to the artifacts @@ -175,11 +175,11 @@ jobs: - name: Build with no features run: cargo test --verbose --no-default-features --no-run - name: Tests with no features - run: cargo test --verbose --no-default-features + run: RUST_MIN_STACK=10485760 cargo test --verbose --no-default-features - name: Build with all features run: cargo test --verbose --all-features --no-run - name: Tests with all features - run: cargo test --verbose --all-features + run: RUST_MIN_STACK=10485760 cargo test --verbose --all-features # Ensure that serialized extensions match rust implementation std-extensions: @@ -277,7 +277,7 @@ jobs: - name: Build run: cargo test -p hugr-llvm --verbose --features llvm${{ matrix.llvm-version[1] }} --no-run - name: Tests with no features - run: cargo test -p hugr-llvm --verbose --features llvm${{ matrix.llvm-version[1] }} + run: cargo test -p hugr-llvm --verbose --features llvm${{ matrix.llvm-version[1] }} --exclude test_type_substitution # This is a meta job to mark successful completion of the required checks, # even if they are skipped due to no changes in the relevant files. diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 70ff44dc5..cabb45502 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -1140,8 +1140,8 @@ pub(crate) mod test { // just use a lot more stack than the simpler, original, proptests. fn test_type_substitution(((t,t_env), s, s_env) in with_substitution( MakeType(TypeBound::Any), - 2.into(), - 2.into(), + 3.into(), + 3.into(), Arc::new(std_reg()))) { prop_assert!(t.validate(&t_env).is_ok()); for s1 in s.iter() { From 0a0f5ec9b7c77ddb05d3a13c456605eab9f9f7fd Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 23 Dec 2024 17:47:41 +0000 Subject: [PATCH 13/13] (TEMP) Remove MakePair --- hugr-core/src/types.rs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index cabb45502..a8b57ac22 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -1088,31 +1088,6 @@ pub(crate) mod test { } } - #[allow(unused)] - struct MakePair(S, T); - - impl, T: VarEnvState + Clone> VarEnvState<(A, B)> for MakePair - where - A: std::fmt::Debug + Clone, - B: std::fmt::Debug, - { - fn with_env( - self, - env: Vec, - depth: RecursionDepth, - reg: Arc, - ) -> impl Strategy)> + Clone { - self.0 - .with_env(env, depth, reg.clone()) - .prop_flat_map(move |(v1, env)| { - self.1 - .clone() - .with_env(env, depth, reg.clone()) - .prop_map(move |(v2, env)| ((v1.clone(), v2), env)) - }) - } - } - /// Given a VarEnvState that builds a T with an environment, /// Builds (a T, the environment for that T, and a TypeArg for each TypeParam in that environment) /// with the environment making the TypeArgs valid