Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
path: root/tamer
diff options
context:
space:
mode:
authorMike Gerwitz <mike.gerwitz@ryansg.com>2020-03-24 16:28:22 -0400
committerMike Gerwitz <mike.gerwitz@ryansg.com>2020-03-26 09:08:13 -0400
commit05d03dc4bbdcffae7fb1424ff5a9d78dc68eb0c2 (patch)
treeef27c930d4a0d3b74adcafa4eb7a78d703bd1b86 /tamer
parentb35dd4f4dd6c29ff63ed558caad5c19861913bf1 (diff)
downloadtame-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')
-rw-r--r--tamer/src/ir/asg/ident.rs42
-rw-r--r--tamer/src/ir/asg/object.rs112
-rw-r--r--tamer/src/ir/legacyir.rs11
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::*;