[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.
master
Mike Gerwitz 2020-03-24 16:28:22 -04:00
parent b35dd4f4dd
commit 05d03dc4bb
3 changed files with 157 additions and 8 deletions

View File

@ -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;

View File

@ -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

View File

@ -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::*;