diff options
author | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2020-03-24 16:28:22 -0400 |
---|---|---|
committer | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2020-03-26 09:08:13 -0400 |
commit | 05d03dc4bbdcffae7fb1424ff5a9d78dc68eb0c2 (patch) | |
tree | ef27c930d4a0d3b74adcafa4eb7a78d703bd1b86 /tamer/src | |
parent | b35dd4f4dd6c29ff63ed558caad5c19861913bf1 (diff) | |
download | tame-05d03dc4bbdcffae7fb1424ff5a9d78dc68eb0c2.tar.gz tame-05d03dc4bbdcffae7fb1424ff5a9d78dc68eb0c2.tar.bz2 tame-05d03dc4bbdcffae7fb1424ff5a9d78dc68eb0c2.zip |
[DEV-7087] Beginning of extern type verification and reporting
This only verifies when externs are defined _before_ they need to be
resolved. See a future commit for the rest of this.
Diffstat (limited to 'tamer/src')
-rw-r--r-- | tamer/src/ir/asg/ident.rs | 42 | ||||
-rw-r--r-- | tamer/src/ir/asg/object.rs | 112 | ||||
-rw-r--r-- | tamer/src/ir/legacyir.rs | 11 |
3 files changed, 157 insertions, 8 deletions
diff --git a/tamer/src/ir/asg/ident.rs b/tamer/src/ir/asg/ident.rs index 6a66316..b5ef44b 100644 --- a/tamer/src/ir/asg/ident.rs +++ b/tamer/src/ir/asg/ident.rs @@ -144,6 +144,42 @@ pub enum IdentKind { Worksheet, } +impl std::fmt::Display for IdentKind { + /// Format identifier type for display to the user. + /// + /// TODO: We have not yet finalized how we will represent types in the + /// new type system, + /// so for now this just uses a syntax similar to Rust. + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Cgen(dim) => { + write!(fmt, "cgen[{}; {}]", DataType::Boolean, dim) + } + Self::Class(dim) => { + write!(fmt, "class[{}; {}]", DataType::Boolean, dim) + } + Self::Const(dim, dtype) => write!(fmt, "const[{}; {}]", dtype, dim), + Self::Func(dim, dtype) => write!(fmt, "func[{}; {}]", dtype, dim), + Self::Gen(dim, dtype) => write!(fmt, "gen[{}; {}]", dtype, dim), + Self::Lparam(dim, dtype) => { + write!(fmt, "lparam[{}; {}]", dtype, dim) + } + Self::Param(dim, dtype) => write!(fmt, "param[{}; {}]", dtype, dim), + Self::Rate(dtype) => write!(fmt, "rate[{}; 0]", dtype), + Self::Tpl => write!(fmt, "tpl"), + Self::Type(dtype) => write!(fmt, "type[{}]", dtype), + Self::MapHead => write!(fmt, "map:head"), + Self::Map => write!(fmt, "map"), + Self::MapTail => write!(fmt, "map:tail"), + Self::RetMapHead => write!(fmt, "retmap:head"), + Self::RetMap => write!(fmt, "retmap"), + Self::RetMapTail => write!(fmt, "retmap:tail"), + Self::Meta => write!(fmt, "meta"), + Self::Worksheet => write!(fmt, "worksheet"), + } + } +} + impl<'i> TryFrom<SymAttrs<'i>> for IdentKind { type Error = &'static str; @@ -247,6 +283,12 @@ impl AsRef<str> for Dim { } } +impl std::fmt::Display for Dim { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + (self.0).fmt(fmt) + } +} + /// Underlying datatype of identifier. pub type DataType = SymDtype; diff --git a/tamer/src/ir/asg/object.rs b/tamer/src/ir/asg/object.rs index 2d5836b..a9d90a9 100644 --- a/tamer/src/ir/asg/object.rs +++ b/tamer/src/ir/asg/object.rs @@ -257,6 +257,20 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> { Ok(self) } + IdentObject::Extern(name, ref orig_kind) => { + if orig_kind != &kind { + let err = TransitionError::ExternResolution { + name: name.to_string(), + expected: orig_kind.clone(), + given: kind.clone(), + }; + + return Err((self, err)); + } + + Ok(IdentObject::Ident(name, kind, src)) + } + // TODO: no override-override IdentObject::IdentFragment(name, _, orig_src, _) if orig_src.virtual_ && src.override_ => @@ -349,6 +363,16 @@ pub enum TransitionError { /// See [`IdentObjectState::redeclare`]. Incompatible(String), + /// Extern resolution failure. + /// + /// An extern could not be resolved because the provided identifier had + /// a type that is incompatible with the extern definition. + ExternResolution { + name: String, + expected: IdentKind, + given: IdentKind, + }, + /// The provided identifier is not in a state that is permitted to /// receive a fragment. /// @@ -363,6 +387,17 @@ impl std::fmt::Display for TransitionError { write!(fmt, "object incompatible: {}", msg) } + // TODO + Self::ExternResolution { + name, + expected, + given, + } => write!( + fmt, + "extern `{}` of type `{}` is incompatible with type `{}`", + name, expected, given, + ), + Self::BadFragmentDest(msg) => { write!(fmt, "bad fragment destination: {}", msg) } @@ -642,15 +677,76 @@ mod test { ); } - #[test] - fn ident_object_extern() { - let sym = symbol_dummy!(1, "missing"); - let kind = IdentKind::Class(Dim::from_u8(1)); + mod extern_ { + use super::*; - assert_eq!( - IdentObject::Extern(&sym, kind.clone()), - IdentObject::extern_(&sym, kind.clone()), - ); + #[test] + fn ident_object() { + let sym = symbol_dummy!(1, "missing"); + let kind = IdentKind::Class(Dim::from_u8(1)); + + assert_eq!( + IdentObject::Extern(&sym, kind.clone()), + IdentObject::extern_(&sym, kind.clone()), + ); + } + + #[test] + fn redeclare_compatible_resolves() { + let sym = symbol_dummy!(1, "extern_re"); + let kind = IdentKind::Class(Dim::from_u8(10)); + let src = Source { + desc: Some("okay".into()), + ..Default::default() + }; + + // Compatible kind, should resolve. + let result = IdentObject::extern_(&sym, kind.clone()) + .redeclare(kind.clone(), src.clone()); + + assert_eq!(Ok(IdentObject::Ident(&sym, kind, src)), result,); + } + + #[test] + fn redeclare_incompatible_kind() { + let sym = symbol_dummy!(1, "extern_re_bad"); + let kind = IdentKind::Class(Dim::from_u8(10)); + let src = Source { + desc: Some("bad kind".into()), + ..Default::default() + }; + + let orig = IdentObject::extern_(&sym, kind.clone()); + + // Incompatible kind + let kind_bad = IdentKind::Meta; + let result = orig.clone().redeclare(kind_bad.clone(), src); + + match result { + Err((given_orig, err @ _)) => { + assert_eq!(orig, given_orig); + + if let TransitionError::ExternResolution { + name: e_name, + expected: e_expected, + given: e_given, + } = err.clone() + { + assert_eq!(sym.to_string(), e_name); + assert_eq!(kind, e_expected); + assert_eq!(kind_bad, e_given); + } + + // Formatted error + let msg = format!("{}", err); + + assert!(msg.contains(&format!("{}", sym))); + assert!(msg.contains(&format!("{}", kind))); + assert!(msg.contains(&format!("{}", kind_bad))); + } + _ => panic!("expected failure: {:?}", result), + } + } } // TODO: incompatible diff --git a/tamer/src/ir/legacyir.rs b/tamer/src/ir/legacyir.rs index fbe68f0..1d23f3f 100644 --- a/tamer/src/ir/legacyir.rs +++ b/tamer/src/ir/legacyir.rs @@ -305,6 +305,17 @@ impl TryFrom<&[u8]> for SymDtype { } } +impl std::fmt::Display for SymDtype { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Boolean => write!(fmt, "boolean"), + Self::Integer => write!(fmt, "integer"), + Self::Float => write!(fmt, "float"), + Self::Empty => write!(fmt, "(unknown)"), + } + } +} + #[cfg(test)] mod test { use super::*; |