Skip to content

Commit

Permalink
[Refactor] struct CustomSerialized contains Yaml (+ CustomType) (#331)
Browse files Browse the repository at this point in the history
* CustomCheckFail is now an enum with Message or TypeMismatch. (Remove new.)

* Add struct CustomSerialized with impl CustomConst

* Remove ContainerValue::Opaque

* Comment ConstValue::Opaque: it stays there, so not shared with TypeArg

* Fix clippy that suddenly starts showing up?!

* Test
  • Loading branch information
acl-cqc committed Aug 2, 2023
1 parent 7b927eb commit 5d2f09e
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 29 deletions.
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),
}

0 comments on commit 5d2f09e

Please sign in to comment.