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

[Refactor] struct CustomSerialized contains Yaml (+ CustomType) #331

Merged
merged 6 commits into from
Aug 2, 2023
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
2 changes: 1 addition & 1 deletion src/extensions/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl CustomConst for Constant {
if &self_typ.custom_type() == typ {
Ok(())
} else {
Err(CustomCheckFail::new(
Err(CustomCheckFail::Message(
"Rotation constant type mismatch.".into(),
))
}
Expand Down
78 changes: 69 additions & 9 deletions src/ops/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ pub enum ConstValue {
Container(ContainerValue<ConstValue>),
/// Double precision float
F64(f64),
/// An opaque constant value, with cached type. TODO put this into ContainerValue.
/// An opaque constant value, that can check it is of a given [CustomType].
/// This may include values that are [hashable]
///
/// [hashable]: crate::types::simple::TypeTag::Hashable
// Note: the extra level of tupling is to avoid https://github.com/rust-lang/rust/issues/78808
Opaque((Box<dyn CustomConst>,)),
}
Expand Down Expand Up @@ -176,8 +179,6 @@ impl ValueOfType for ConstValue {
// A "hashable" value might be an instance of a non-hashable type:
// e.g. an empty list is hashable, yet can be checked against a classic element type!
if let HashableValue::Container(ctr) = hv {
// Note if ctr is a ContainerValue<HashableValue>::Opaque, this means we can check that
// against a Container<ClassicType>::Opaque, which is perhaps unnecessary, but harmless.
return ctr.map_vals(&ConstValue::Hashable).check_container(cty);
}
}
Expand Down Expand Up @@ -305,20 +306,45 @@ pub trait CustomConst:
impl_downcast!(CustomConst);
impl_box_clone!(CustomConst, CustomConstBoxClone);

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct CustomSerialized {
typ: CustomType,
value: serde_yaml::Value,
}

#[typetag::serde]
impl CustomConst for CustomSerialized {
fn name(&self) -> SmolStr {
format!("yaml:{:?}", self.value).into()
}

fn check_custom_type(&self, typ: &CustomType) -> Result<(), CustomCheckFail> {
if &self.typ == typ {
Ok(())
} else {
Err(CustomCheckFail::TypeMismatch(typ.clone(), self.typ.clone()))
}
}
}

#[cfg(test)]
mod test {
use cool_asserts::assert_matches;
use serde_yaml::Value;

use super::{typecheck::ConstIntError, Const, ConstValue};
use super::{typecheck::ConstIntError, Const, ConstValue, CustomSerialized};
use crate::{
builder::{BuildError, Container, DFGBuilder, Dataflow, DataflowHugr},
builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr},
classic_row, type_row,
types::{ClassicType, SimpleRow, SimpleType},
values::{ConstTypeError, HashableValue, ValueOfType},
types::simple::Container,
types::type_param::TypeArg,
types::{ClassicType, CustomType, HashableType, SimpleRow, SimpleType, TypeTag},
values::{ConstTypeError, CustomCheckFail, HashableValue, ValueOfType},
};

#[test]
fn test_predicate() -> Result<(), BuildError> {
use crate::builder::Container;
let pred_rows = vec![
classic_row![ClassicType::i64(), ClassicType::F64],
type_row![],
Expand Down Expand Up @@ -378,11 +404,45 @@ mod test {
tuple_val2.check_type(&tuple_ty),
Err(ConstTypeError::ValueCheckFail(ty, tv2)) => ty == tuple_ty && tv2 == tuple_val2
);
let tuple_val3 =
ConstValue::sequence(&vec![V_INT, ConstValue::F64(3.3), ConstValue::F64(2.0)]);
let tuple_val3 = ConstValue::sequence(&[V_INT, ConstValue::F64(3.3), ConstValue::F64(2.0)]);
assert_eq!(
tuple_val3.check_type(&tuple_ty),
Err(ConstTypeError::TupleWrongLength)
);
}

#[test]
fn test_yaml_const() {
let typ_int = CustomType::new(
"mytype",
vec![TypeArg::ClassicType(ClassicType::Hashable(
HashableType::Int(8),
))],
"myrsrc",
TypeTag::Hashable,
);
let val = ConstValue::Opaque((Box::new(CustomSerialized {
typ: typ_int.clone(),
value: Value::Number(6.into()),
}),));
let SimpleType::Classic(classic_t) = typ_int.clone().into()
else {panic!("Hashable CustomType returned as non-Classic");};
assert_matches!(classic_t, ClassicType::Hashable(_));
val.check_type(&classic_t).unwrap();

// This misrepresents the CustomType, so doesn't really "have to work".
// But just as documentation of current behaviour:
val.check_type(&ClassicType::Container(Container::Opaque(typ_int.clone())))
.unwrap();

let typ_float = CustomType::new(
"mytype",
vec![TypeArg::ClassicType(ClassicType::F64)],
"myrsrc",
TypeTag::Hashable,
);
let t: SimpleType = typ_float.clone().into();
assert_matches!(val.check_type(&t.try_into().unwrap()),
Err(ConstTypeError::CustomCheckFail(CustomCheckFail::TypeMismatch(a, b))) => a == typ_int && b == typ_float);
}
}
31 changes: 12 additions & 19 deletions src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

use thiserror::Error;

use crate::types::{ClassicType, Container, HashableType, PrimType};
use crate::types::{ClassicType, Container, CustomType, HashableType, PrimType};
use crate::{
ops::constant::{
typecheck::{check_int_fits_in_width, ConstIntError},
Expand Down Expand Up @@ -94,7 +94,9 @@ impl ValueOfType for HashableValue {
/// A value that is a container of other values, e.g. a tuple or sum;
/// thus, corresponding to [Container]. Note there is no member
/// corresponding to [Container::Alias]; such types must have been
/// resolved to concrete types in order to create instances (values).
/// resolved to concrete types in order to create instances (values),
/// nor to [Container::Opaque], which is left to classes for broader
/// sets of values (see e.g. [ConstValue::Opaque])
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ContainerValue<T> {
/// A [Container::Array] or [Container::Tuple] or [Container::List]
Expand All @@ -104,11 +106,6 @@ pub enum ContainerValue<T> {
/// A [Container::Sum] - for any Sum type where this value meets
/// the type of the variant indicated by the tag
Sum(usize, Box<T>), // Tag and value
/// A value of a custom type defined by an extension/[Resource].
///
/// [Resource]: crate::resource::Resource
// TODO replace this with CustomConst
Opaque(serde_yaml::Value),
}

impl<Elem: ValueOfType> ContainerValue<Elem> {
Expand All @@ -120,7 +117,6 @@ impl<Elem: ValueOfType> ContainerValue<Elem> {
}
ContainerValue::Map(_) => "a map".to_string(),
ContainerValue::Sum(tag, val) => format!("const:sum:{{tag:{tag}, val:{}}}", val.name()),
ContainerValue::Opaque(c) => format!("const:yaml:{:?}", c),
}
}
pub(crate) fn check_container(&self, ty: &Container<Elem::T>) -> Result<(), ConstTypeError> {
Expand Down Expand Up @@ -160,7 +156,6 @@ impl<Elem: ValueOfType> ContainerValue<Elem> {
(ContainerValue::Sum(tag, value), Container::Sum(variants)) => {
value.check_type(variants.get(*tag).ok_or(ConstTypeError::InvalidSumTag)?)
}
(ContainerValue::Opaque(_), Container::Opaque(_)) => Ok(()), // TODO
(_, Container::Alias(s)) => Err(ConstTypeError::NoAliases(s.to_string())),
(_, _) => Err(ValueOfType::container_error(ty.clone(), self.clone())),
}
Expand All @@ -175,7 +170,6 @@ impl<Elem: ValueOfType> ContainerValue<Elem> {
ContainerValue::Sum(tag, value) => {
ContainerValue::Sum(*tag, Box::new(f((**value).clone())))
}
ContainerValue::Opaque(v) => ContainerValue::Opaque(v.clone()),
}
}
}
Expand Down Expand Up @@ -213,14 +207,13 @@ pub(crate) fn map_container_type<T: PrimType, T2: PrimType>(

/// Struct for custom type check fails.
#[derive(Clone, Debug, PartialEq, Error)]
#[error("Error when checking custom type.")]
pub struct CustomCheckFail(String);

impl CustomCheckFail {
/// Creates a new [`CustomCheckFail`].
pub fn new(message: String) -> Self {
Self(message)
}
pub enum CustomCheckFail {
/// The value had a specific type that was not what was expected
#[error("Expected type: {0} but value was of type: {1}")]
TypeMismatch(CustomType, CustomType),
/// Any other message
#[error("{0}")]
Message(String),
}

/// Errors that arise from typechecking constants
Expand All @@ -246,6 +239,6 @@ pub enum ConstTypeError {
#[error("Value {1:?} does not match expected type {0}")]
ValueCheckFail(ClassicType, ConstValue),
/// Error when checking a custom value.
#[error("Custom value type check error: {0:?}")]
#[error("Error when checking custom type: {0:?}")]
CustomCheckFail(#[from] CustomCheckFail),
}