[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
parent
b35dd4f4dd
commit
05d03dc4bb
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::*;
|
||||
|
|
Loading…
Reference in New Issue