-
Notifications
You must be signed in to change notification settings - Fork 7
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: binary compute_signature returning a PolyFuncType with binders #710
Changes from 4 commits
098d623
d973909
2162552
fb76697
d0d9da4
5f4e71a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -180,9 +180,6 @@ impl OpDef { | |||||
args: &[TypeArg], | ||||||
exts: &ExtensionRegistry, | ||||||
) -> Result<FunctionType, SignatureError> { | ||||||
// Hugr's are monomorphic, so check the args have no free variables | ||||||
args.iter().try_for_each(|ta| ta.validate(exts, &[]))?; | ||||||
|
||||||
let temp: PolyFuncType; // to keep alive | ||||||
let (pf, args) = match &self.signature_func { | ||||||
SignatureFunc::TypeScheme(ts) => (ts, args), | ||||||
|
@@ -369,11 +366,16 @@ impl Extension { | |||||
|
||||||
#[cfg(test)] | ||||||
mod test { | ||||||
use std::num::NonZeroU64; | ||||||
|
||||||
use smol_str::SmolStr; | ||||||
|
||||||
use crate::builder::{DFGBuilder, Dataflow, DataflowHugr}; | ||||||
use crate::extension::prelude::USIZE_T; | ||||||
use crate::extension::{ExtensionRegistry, PRELUDE}; | ||||||
use crate::extension::{ | ||||||
CustomSignatureFunc, ExtensionRegistry, SignatureError, EMPTY_REG, PRELUDE, | ||||||
PRELUDE_REGISTRY, | ||||||
}; | ||||||
use crate::ops::custom::ExternalOp; | ||||||
use crate::ops::LeafOp; | ||||||
use crate::std_extensions::collections::{EXTENSION, LIST_TYPENAME}; | ||||||
|
@@ -413,4 +415,113 @@ mod test { | |||||
|
||||||
Ok(()) | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn binary_polyfunc() -> Result<(), Box<dyn std::error::Error>> { | ||||||
struct SigFun(); | ||||||
impl CustomSignatureFunc for SigFun { | ||||||
fn compute_signature( | ||||||
&self, | ||||||
_name: &SmolStr, | ||||||
arg_values: &[TypeArg], | ||||||
_misc: &std::collections::HashMap<String, serde_yaml::Value>, | ||||||
_exts: &ExtensionRegistry, | ||||||
) -> Result<PolyFuncType, SignatureError> { | ||||||
const TP: TypeParam = TypeParam::Type(TypeBound::Any); | ||||||
let [TypeArg::BoundedNat { n }] = arg_values else { | ||||||
return Err(SignatureError::InvalidTypeArgs); | ||||||
}; | ||||||
let n = *n as usize; | ||||||
let tvs: Vec<Type> = (0..n) | ||||||
.map(|_| Type::new_var_use(0, TypeBound::Any)) | ||||||
.collect(); | ||||||
Ok(PolyFuncType::new( | ||||||
vec![TP], | ||||||
FunctionType::new(tvs.clone(), vec![Type::new_tuple(tvs)]), | ||||||
)) | ||||||
} | ||||||
} | ||||||
let mut e = Extension::new(EXT_ID); | ||||||
let def = e.add_op_custom_sig_simple( | ||||||
"MyOp".into(), | ||||||
"".to_string(), | ||||||
vec![TypeParam::max_nat()], | ||||||
SigFun(), | ||||||
)?; | ||||||
|
||||||
// Base case, no type variables: | ||||||
let args = [TypeArg::BoundedNat { n: 3 }, USIZE_T.into()]; | ||||||
assert_eq!( | ||||||
def.compute_signature(&args, &PRELUDE_REGISTRY), | ||||||
Ok(FunctionType::new( | ||||||
vec![USIZE_T; 3], | ||||||
vec![Type::new_tuple(vec![USIZE_T; 3])] | ||||||
)) | ||||||
); | ||||||
assert_eq!(def.validate_args(&args, &PRELUDE_REGISTRY, &[]), Ok(())); | ||||||
|
||||||
// Second arg may be a variable (substitutable) | ||||||
let tyvar = Type::new_var_use(0, TypeBound::Eq); | ||||||
let tyvars: Vec<Type> = (0..3).map(|_| tyvar.clone()).collect(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fyi
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thank you! Thought I'd tried that several times and not got it to work, but obviously I did something not quite right. Done :) |
||||||
let args = [TypeArg::BoundedNat { n: 3 }, tyvar.clone().into()]; | ||||||
assert_eq!( | ||||||
def.compute_signature(&args, &PRELUDE_REGISTRY), | ||||||
Ok(FunctionType::new( | ||||||
tyvars.clone(), | ||||||
vec![Type::new_tuple(tyvars)] | ||||||
)) | ||||||
); | ||||||
def.validate_args(&args, &PRELUDE_REGISTRY, &[TypeParam::Type(TypeBound::Eq)]) | ||||||
.unwrap(); | ||||||
|
||||||
// quick sanity check that we are validating the args - note changed bound: | ||||||
assert_eq!( | ||||||
def.validate_args(&args, &PRELUDE_REGISTRY, &[TypeParam::Type(TypeBound::Any)]), | ||||||
Err(SignatureError::TypeVarDoesNotMatchDeclaration { | ||||||
actual: TypeBound::Any.into(), | ||||||
cached: TypeBound::Eq.into() | ||||||
}) | ||||||
); | ||||||
|
||||||
// First arg must be concrete, not a variable | ||||||
let kind = TypeParam::bounded_nat(NonZeroU64::new(5).unwrap()); | ||||||
let args = [TypeArg::new_var_use(0, kind.clone()), USIZE_T.into()]; | ||||||
// We can't prevent this from getting into our compute_signature implementation: | ||||||
assert_eq!( | ||||||
def.compute_signature(&args, &PRELUDE_REGISTRY), | ||||||
Err(SignatureError::InvalidTypeArgs) | ||||||
); | ||||||
// But validation rules it out, even when the variable is declared: | ||||||
assert_eq!( | ||||||
def.validate_args(&args, &PRELUDE_REGISTRY, &[kind]), | ||||||
Err(SignatureError::FreeTypeVar { | ||||||
idx: 0, | ||||||
num_decls: 0 | ||||||
}) | ||||||
); | ||||||
|
||||||
Ok(()) | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn type_scheme_instantiate_var() -> Result<(), Box<dyn std::error::Error>> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure what this one is testing exactly, maybe add a comment? |
||||||
let mut e = Extension::new(EXT_ID); | ||||||
let def = e.add_op_type_scheme_simple( | ||||||
"SimpleOp".into(), | ||||||
"".into(), | ||||||
PolyFuncType::new( | ||||||
vec![TypeParam::Type(TypeBound::Any)], | ||||||
FunctionType::new_endo(vec![Type::new_var_use(0, TypeBound::Any)]), | ||||||
), | ||||||
)?; | ||||||
let tv = Type::new_var_use(1, TypeBound::Eq); | ||||||
let args = [TypeArg::Type { ty: tv.clone() }]; | ||||||
let decls = [TypeParam::Extensions, TypeBound::Eq.into()]; | ||||||
def.validate_args(&args, &EMPTY_REG, &decls).unwrap(); | ||||||
assert_eq!( | ||||||
def.compute_signature(&args, &EMPTY_REG), | ||||||
Ok(FunctionType::new_endo(vec![tv])) | ||||||
); | ||||||
Ok(()) | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, we could do this for
args.take(self.static_params.len())
sort of thingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing the test fails without removing this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(The case that tries to pass a typevar in as the second, substitutable, parameter, i.e. which should succeed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and the
type_scheme_instantiate_var