[DEV-7087] TAMER: Type compatability check during extern resolution

This properly verifies extern types, and cleans up Asg's API a little so
that externs aren't handled much differently than other declarations.

With that said, after making src optional, I realized that we will indeed
want source information for externs themselves so we can direct the user to
what package is expecting that symbol (as the old linker does).  So this
approach will not work, and I'll have to undo some of those changes.
master
Mike Gerwitz 2020-03-25 15:37:55 -04:00
parent 8de174d6a2
commit d6762ab547
5 changed files with 305 additions and 223 deletions

View File

@ -199,7 +199,7 @@ where
&mut self,
name: &'i Symbol<'i>,
kind: IdentKind,
src: Source<'i>,
src: Option<Source<'i>>,
) -> AsgResult<ObjectRef<Ix>, Ix> {
if let Some(existing) = self.lookup(name) {
let node = self.graph.node_weight_mut(existing.0).unwrap();
@ -229,19 +229,6 @@ where
Ok(ObjectRef(node))
}
fn declare_extern(
&mut self,
name: &'i Symbol<'i>,
expected_kind: IdentKind,
) -> AsgResult<ObjectRef<Ix>, Ix> {
// TODO: resolution!
let node = self.graph.add_node(Some(O::extern_(name, expected_kind)));
self.index_identifier(name, node);
Ok(ObjectRef(node))
}
fn set_fragment(
&mut self,
identi: ObjectRef<Ix>,
@ -412,9 +399,8 @@ mod test {
#[derive(Debug, Default, PartialEq)]
struct StubIdentObject<'i> {
given_missing: Option<&'i Symbol<'i>>,
given_ident: Option<(&'i Symbol<'i>, IdentKind, Source<'i>)>,
given_extern: Option<(&'i Symbol<'i>, IdentKind)>,
given_redeclare: Option<(IdentKind, Source<'i>)>,
given_ident: Option<(&'i Symbol<'i>, IdentKind, Option<Source<'i>>)>,
given_redeclare: Option<(IdentKind, Option<Source<'i>>)>,
given_set_fragment: Option<FragmentText>,
fail_redeclare: RefCell<Option<TransitionError>>,
}
@ -423,14 +409,12 @@ mod test {
fn name(&self) -> Option<&'i Symbol<'i>> {
self.given_missing
.or(self.given_ident.as_ref().map(|args| args.0))
.or(self.given_extern.as_ref().map(|args| args.0))
}
fn kind(&self) -> Option<&IdentKind> {
self.given_ident
.as_ref()
.map(|args| &args.1)
.or(self.given_extern.as_ref().map(|args| &args.1))
.or(self.given_redeclare.as_ref().map(|args| &args.0))
}
@ -458,7 +442,7 @@ mod test {
fn ident(
name: &'i Symbol<'i>,
kind: IdentKind,
src: Source<'i>,
src: Option<Source<'i>>,
) -> Self {
Self {
given_ident: Some((name, kind, src)),
@ -466,17 +450,10 @@ mod test {
}
}
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self {
Self {
given_extern: Some((name, kind)),
..Default::default()
}
}
fn redeclare(
mut self,
kind: IdentKind,
src: Source<'i>,
src: Option<Source<'i>>,
) -> TransitionResult<StubIdentObject<'i>> {
if self.fail_redeclare.borrow().is_some() {
let err = self.fail_redeclare.replace(None).unwrap();
@ -528,19 +505,19 @@ mod test {
let nodea = sut.declare(
&syma,
IdentKind::Meta,
Source {
Some(Source {
desc: Some("a".to_string()),
..Default::default()
},
}),
)?;
let nodeb = sut.declare(
&symb,
IdentKind::Worksheet,
Source {
Some(Source {
desc: Some("b".to_string()),
..Default::default()
},
}),
)?;
assert_ne!(nodea, nodeb);
@ -549,10 +526,10 @@ mod test {
Some((
&syma,
IdentKind::Meta,
Source {
Some(Source {
desc: Some("a".to_string()),
..Default::default()
},
}),
)),
sut.get(nodea).unwrap().given_ident
);
@ -561,10 +538,10 @@ mod test {
Some((
&symb,
IdentKind::Worksheet,
Source {
Some(Source {
desc: Some("b".to_string()),
..Default::default()
},
}),
)),
sut.get(nodeb).unwrap().given_ident
);
@ -580,10 +557,10 @@ mod test {
let node = sut.declare(
&sym,
IdentKind::Meta,
Source {
Some(Source {
generated: true,
..Default::default()
},
}),
)?;
assert_eq!(Some(node), sut.lookup(&sym));
@ -591,28 +568,13 @@ mod test {
Ok(())
}
#[test]
fn declare_extern() -> AsgResult<(), u8> {
let mut sut = Sut::with_capacity(0, 0);
let sym = symbol_dummy!(1, "extern");
let node = sut.declare_extern(&sym, IdentKind::Meta)?;
assert_eq!(
Some((&sym, IdentKind::Meta)),
sut.get(node).unwrap().given_extern,
);
Ok(())
}
#[test]
fn declare_returns_existing() -> AsgResult<(), u8> {
let mut sut = Sut::with_capacity(0, 0);
let sym = symbol_dummy!(1, "symdup");
let src = Source::default();
let node = sut.declare(&sym, IdentKind::Meta, src.clone())?;
let node = sut.declare(&sym, IdentKind::Meta, Some(src.clone()))?;
// Remember that our stub does not care about compatibility.
let rekind = IdentKind::Class(Dim::from_u8(3));
@ -620,14 +582,15 @@ mod test {
desc: Some("redeclare".into()),
..Default::default()
};
let redeclare = sut.declare(&sym, rekind.clone(), resrc.clone())?;
let redeclare =
sut.declare(&sym, rekind.clone(), Some(resrc.clone()))?;
// We don't care what the objects are for this test, just that the
// same node is referenced.
assert_eq!(node, redeclare);
assert_eq!(
Some((rekind, resrc)),
Some((rekind, Some(resrc))),
sut.get(node).unwrap().given_redeclare,
);
@ -646,18 +609,19 @@ mod test {
};
// Set up an object to fail redeclaration.
let node = sut.declare(&sym, IdentKind::Meta, src.clone())?;
let node = sut.declare(&sym, IdentKind::Meta, Some(src.clone()))?;
let obj = sut.get(node).unwrap();
let terr = TransitionError::Incompatible(String::from("test fail"));
obj.fail_redeclare.replace(Some(terr.clone()));
// Should invoke StubIdentObject::redeclare on the above `obj`.
let result = sut.declare(&sym, IdentKind::Meta, Source::default());
let result =
sut.declare(&sym, IdentKind::Meta, Some(Source::default()));
if let Err(err) = result {
// The node should have been restored.
let obj = sut.get(node).unwrap();
assert_eq!(src, obj.given_ident.as_ref().unwrap().2);
assert_eq!(Some(src), obj.given_ident.as_ref().unwrap().2);
assert_eq!(AsgError::ObjectTransition(terr), err);
@ -676,7 +640,7 @@ mod test {
generated: true,
..Default::default()
};
let node = sut.declare(&sym, IdentKind::Meta, src.clone())?;
let node = sut.declare(&sym, IdentKind::Meta, Some(src.clone()))?;
let fragment = "a fragment".to_string();
let node_with_frag = sut.set_fragment(node, fragment.clone())?;
@ -690,7 +654,7 @@ mod test {
let obj = sut.get(node).unwrap();
assert_eq!(Some((&sym, IdentKind::Meta, src,)), obj.given_ident);
assert_eq!(Some((&sym, IdentKind::Meta, Some(src))), obj.given_ident);
assert_eq!(Some(fragment), obj.given_set_fragment);
Ok(())
@ -705,8 +669,10 @@ mod test {
let sym = symbol_dummy!(1, "sym");
let dep = symbol_dummy!(2, "dep");
let symnode = sut.declare(&sym, IdentKind::Meta, Source::default())?;
let depnode = sut.declare(&dep, IdentKind::Meta, Source::default())?;
let symnode =
sut.declare(&sym, IdentKind::Meta, Some(Source::default()))?;
let depnode =
sut.declare(&dep, IdentKind::Meta, Some(Source::default()))?;
sut.add_dep(symnode, depnode);
assert!(sut.has_dep(symnode, depnode));
@ -726,8 +692,8 @@ mod test {
let sym = symbol_dummy!(1, "sym");
let dep = symbol_dummy!(2, "dep");
let _ = sut.declare(&sym, IdentKind::Meta, Source::default())?;
let _ = sut.declare(&dep, IdentKind::Meta, Source::default())?;
let _ = sut.declare(&sym, IdentKind::Meta, Some(Source::default()))?;
let _ = sut.declare(&dep, IdentKind::Meta, Some(Source::default()))?;
let (symnode, depnode) = sut.add_dep_lookup(&sym, &dep);
assert!(sut.has_dep(symnode, depnode));
@ -768,14 +734,14 @@ mod test {
};
// Check with a declared value
let declared = sut.declare(&sym, IdentKind::Meta, src.clone())?;
let declared = sut.declare(&sym, IdentKind::Meta, Some(src.clone()))?;
assert_eq!(symnode, declared);
let obj = sut.get(declared).unwrap();
assert_eq!(Some(&sym), obj.given_missing);
assert_eq!(Some((IdentKind::Meta, src)), obj.given_redeclare);
assert_eq!(Some((IdentKind::Meta, Some(src))), obj.given_redeclare);
Ok(())
}
@ -801,7 +767,7 @@ mod test {
let sym = symbol_dummy!(i, stringify!($name));
$sut.declare(&sym, $kind, Source::default())?;
$sut.declare(&sym, $kind, Some(Source::default()))?;
let (_, _) = $sut.add_dep_lookup($base, &sym);
$dest.push(sym);
@ -820,7 +786,7 @@ mod test {
let base = symbol_dummy!(1, "sym1");
let base_node =
sut.declare(&base, IdentKind::Map, Source::default())?;
sut.declare(&base, IdentKind::Map, Some(Source::default()))?;
add_syms!(sut, &base, {
meta <- meta1: IdentKind::Meta,
@ -863,10 +829,10 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
@ -910,19 +876,19 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep_node = sut.declare(
&dep,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
@ -956,37 +922,37 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym2_node = sut.declare(
&sym2,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep_node = sut.declare(
&dep,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep2_node = sut.declare(
&dep2,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
@ -1024,19 +990,19 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep_node = sut.declare(
&dep,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
@ -1066,28 +1032,28 @@ mod test {
let sym1_node = sut.declare(
&sym1,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym2_node = sut.declare(
&sym2,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym3_node = sut.declare(
&sym3,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym1_node, FragmentText::from("foo"))?;
@ -1123,28 +1089,28 @@ mod test {
let sym1_node = sut.declare(
&sym1,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym2_node = sut.declare(
&sym2,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym3_node = sut.declare(
&sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym1_node, FragmentText::from("foo"))?;
@ -1179,28 +1145,28 @@ mod test {
let sym1_node = sut.declare(
&sym1,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym2_node = sut.declare(
&sym2,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym3_node = sut.declare(
&sym3,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym1_node, FragmentText::from("foo"))?;
@ -1234,19 +1200,19 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep_node = sut.declare(
&dep,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
@ -1276,28 +1242,28 @@ mod test {
let sym1_node = sut.declare(
&sym1,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym2_node = sut.declare(
&sym2,
IdentKind::Func(Dim::default(), SymDtype::Empty),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let sym3_node = sut.declare(
&sym3,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym1_node, FragmentText::from("foo"))?;
@ -1332,28 +1298,28 @@ mod test {
let sym_node = sut.declare(
&sym,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let dep_node = sut.declare(
&dep,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
let ignored_node = sut.declare(
&ignored,
IdentKind::Tpl,
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
)?;
sut.set_fragment(sym_node, FragmentText::from("foo"))?;

View File

@ -62,6 +62,21 @@ where
/// then the operation will fail;
/// otherwise,
/// the existing identifier will be returned.
///
/// If `src` is omitted,
/// then an abstract identifier (an _extern_) is declared.
/// Externs are identifiers that are expected to be defined somewhere
/// else ("externally"),
/// and are resolved at [link-time][crate::ld].
///
/// If a concrete identifier has already been declared (see
/// [`Asg::declare`]),
/// then extern declarations will be compared and,
/// if compatible,
/// the identifier will be immediately _resolved_ and the object
/// on the graph will not be altered.
/// Resolution will otherwise fail in error.
///
/// For more information on state transitions that can occur when
/// redeclaring an identifier that already exists,
/// see [`IdentObjectState::redeclare`].
@ -72,34 +87,10 @@ where
&mut self,
name: &'i Symbol<'i>,
kind: IdentKind,
src: Source<'i>,
src: Option<Source<'i>>,
) -> AsgResult<ObjectRef<Ix>, Ix>;
/// Declare an abstract identifier.
///
/// An _extern_ declaration declares an identifier the same as
/// [`Asg::declare`],
/// but omits source information.
/// Externs are identifiers that are expected to be defined somewhere
/// else ("externally"),
/// and are resolved at [link-time][crate::ld].
///
/// If a concrete identifier has already been declared (see
/// [`Asg::declare`]),
/// then the declarations will be compared and,
/// if compatible,
/// the identifier will be immediately _resolved_ and the object
/// on the graph will not be altered.
/// Resolution will otherwise fail in error.
///
/// See [`IdentObjectState::extern_`] and
/// [`IdentObjectState::redeclare`] for more information on
/// compatibility related to extern resolution.
fn declare_extern(
&mut self,
name: &'i Symbol<'i>,
expected_kind: IdentKind,
) -> AsgResult<ObjectRef<Ix>, Ix>;
/// Set the fragment associated with a concrete identifier.
///

View File

@ -133,7 +133,7 @@
//!
//! // Once declared, the missing identifier changes state and dependencies
//! // are retained.
//! asg.declare(identa_sym, IdentKind::Meta, Source::default())?;
//! asg.declare(identa_sym, IdentKind::Meta, Some(Source::default()))?;
//!
//! assert_eq!(
//! Some(&IdentObject::Ident(identa_sym, IdentKind::Meta, Source::default())),
@ -168,7 +168,7 @@
//! #
//! // Fragments can be attached to resolved identifiers.
//! let ident = asg.declare(
//! interner.intern("ident"), IdentKind::Meta, Source::default()
//! interner.intern("ident"), IdentKind::Meta, Some(Source::default())
//! )?;
//! asg.set_fragment(ident, FragmentText::from("test fragment"))?;
//!

View File

@ -189,18 +189,22 @@ where
fn missing(ident: &'i Symbol<'i>) -> T;
/// Produce an object representing a concrete identifier.
fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> T;
/// Produce an object representing an extern.
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> T;
fn ident(
name: &'i Symbol<'i>,
kind: IdentKind,
src: Option<Source<'i>>,
) -> T;
/// Attempt to redeclare an identifier with additional information.
///
/// For specific information on compatibility rules,
/// see implementers of this trait,
/// since rules may vary between implementations.
fn redeclare(self, kind: IdentKind, src: Source<'i>)
-> TransitionResult<T>;
fn redeclare(
self,
kind: IdentKind,
src: Option<Source<'i>>,
) -> TransitionResult<T>;
/// Attach a code fragment (compiled text) to an identifier.
///
@ -218,12 +222,15 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
IdentObject::Missing(ident)
}
fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> Self {
IdentObject::Ident(name, kind, src)
}
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self {
IdentObject::Extern(name, kind)
fn ident(
name: &'i Symbol<'i>,
kind: IdentKind,
src: Option<Source<'i>>,
) -> Self {
match src {
Some(s) => IdentObject::Ident(name, kind, s),
None => IdentObject::Extern(name, kind),
}
}
/// Attempt to redeclare an identifier with additional information.
@ -247,44 +254,63 @@ impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
fn redeclare(
mut self,
kind: IdentKind,
src: Source<'i>,
src: Option<Source<'i>>,
) -> TransitionResult<IdentObject<'i>> {
match self {
IdentObject::Ident(_, _, ref mut orig_src)
if orig_src.virtual_ && src.override_ =>
{
*orig_src = src;
Ok(self)
}
match src {
None => match self.kind() {
None => Ok(IdentObject::Extern(self.name().unwrap(), kind)),
Some(cur_kind) => {
if cur_kind != &kind {
let err = TransitionError::ExternResolution {
name: self.name().unwrap().to_string(),
expected: kind.clone(),
given: cur_kind.clone(),
};
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));
}
return Err((self, err));
// Resolved successfully, so keep what we already have.
Ok(self)
}
},
Some(new_src) => match self {
IdentObject::Ident(_, _, ref mut orig_src)
if orig_src.virtual_ && new_src.override_ =>
{
*orig_src = new_src;
Ok(self)
}
Ok(IdentObject::Ident(name, kind, src))
}
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(),
};
// TODO: no override-override
IdentObject::IdentFragment(name, _, orig_src, _)
if orig_src.virtual_ && src.override_ =>
{
// clears fragment, which is no longer applicable
Ok(IdentObject::Ident(name, kind, src))
}
return Err((self, err));
}
IdentObject::Missing(name) | IdentObject::Ident(name, _, _) => {
Ok(IdentObject::Ident(name, kind, src))
}
Ok(IdentObject::Ident(name, kind, new_src))
}
// TODO: incompatible (check now-dangling commits)
_ => Ok(self),
// TODO: no override-override
IdentObject::IdentFragment(name, _, orig_src, _)
if orig_src.virtual_ && new_src.override_ =>
{
// clears fragment, which is no longer applicable
Ok(IdentObject::Ident(name, kind, new_src))
}
IdentObject::Missing(name) | IdentObject::Ident(name, _, _) => {
Ok(IdentObject::Ident(name, kind, new_src))
}
// TODO
_ => Ok(self),
},
}
}
@ -387,7 +413,6 @@ impl std::fmt::Display for TransitionError {
write!(fmt, "object incompatible: {}", msg)
}
// TODO
Self::ExternResolution {
name,
expected,
@ -673,7 +698,7 @@ mod test {
assert_eq!(
IdentObject::Ident(&sym, kind.clone(), src.clone()),
IdentObject::ident(&sym, kind.clone(), src.clone()),
IdentObject::ident(&sym, kind.clone(), Some(src.clone())),
);
}
@ -687,13 +712,14 @@ mod test {
assert_eq!(
IdentObject::Extern(&sym, kind.clone()),
IdentObject::extern_(&sym, kind.clone()),
IdentObject::ident(&sym, kind.clone(), None),
);
}
// Extern first, then identifier
#[test]
fn redeclare_compatible_resolves() {
let sym = symbol_dummy!(1, "extern_re");
let sym = symbol_dummy!(1, "extern_re_pre");
let kind = IdentKind::Class(Dim::from_u8(10));
let src = Source {
desc: Some("okay".into()),
@ -701,26 +727,68 @@ mod test {
};
// Compatible kind, should resolve.
let result = IdentObject::extern_(&sym, kind.clone())
.redeclare(kind.clone(), src.clone());
let result = IdentObject::ident(&sym, kind.clone(), None)
.redeclare(kind.clone(), Some(src.clone()));
assert_eq!(Ok(IdentObject::Ident(&sym, kind, src)), result,);
}
// Identifier first, then extern
#[test]
fn redeclare_compatible_resolves_post() {
let sym = symbol_dummy!(1, "extern_re_post");
let kind = IdentKind::Class(Dim::from_u8(10));
let src = Source {
desc: Some("okay".into()),
..Default::default()
};
// Compatible kind, should resolve.
let result =
IdentObject::ident(&sym, kind.clone(), Some(src.clone()))
.redeclare(kind.clone(), None);
assert_eq!(Ok(IdentObject::Ident(&sym, kind, src)), result,);
}
#[test]
fn redeclare_incompatible_kind() {
let sym = symbol_dummy!(1, "extern_re_bad");
fn redeclare_missing() {
let sym = symbol_dummy!(1, "extern_missing");
let kind = IdentKind::Class(Dim::from_u8(7));
let result =
IdentObject::missing(&sym).redeclare(kind.clone(), None);
assert_eq!(Ok(IdentObject::Extern(&sym, kind)), result);
}
#[test]
fn redeclare_another_extern() {
let sym = symbol_dummy!(1, "extern_extern");
let kind = IdentKind::Class(Dim::from_u8(20));
let result = IdentObject::ident(&sym, kind.clone(), None)
.redeclare(kind.clone(), None);
assert_eq!(Ok(IdentObject::Extern(&sym, kind)), result);
}
// Extern first, then identifier
#[test]
fn redeclare_post_incompatible_kind() {
let sym = symbol_dummy!(1, "extern_re_bad_post");
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());
let orig = IdentObject::ident(&sym, kind.clone(), None);
// Incompatible kind
let kind_bad = IdentKind::Meta;
let result = orig.clone().redeclare(kind_bad.clone(), src);
let result =
orig.clone().redeclare(kind_bad.clone(), Some(src));
match result {
Err((given_orig, err @ _)) => {
@ -747,6 +815,52 @@ mod test {
_ => panic!("expected failure: {:?}", result),
}
}
// Identifier first, then extern
#[test]
fn redeclare_pre_incompatible_kind() {
let sym = symbol_dummy!(1, "extern_re_bad_pre");
let kind_given = IdentKind::Class(Dim::from_u8(10));
let src = Source {
desc: Some("bad kind".into()),
..Default::default()
};
let orig = IdentObject::ident(
&sym,
kind_given.clone(),
Some(src.clone()),
);
// Extern with incompatible kind.
let kind_extern = IdentKind::Meta;
let result = orig.clone().redeclare(kind_extern.clone(), None);
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_extern, e_expected);
assert_eq!(kind_given, e_given);
}
// Formatted error
let msg = format!("{}", err);
assert!(msg.contains(&format!("{}", sym)));
assert!(msg.contains(&format!("{}", kind_extern)));
assert!(msg.contains(&format!("{}", kind_given)));
}
_ => panic!("expected failure: {:?}", result),
}
}
}
// TODO: incompatible
@ -754,15 +868,18 @@ mod test {
fn redeclare_returns_existing_compatible() {
let sym = symbol_dummy!(1, "symdup");
let first =
IdentObject::ident(&sym, IdentKind::Meta, Source::default());
let first = IdentObject::ident(
&sym,
IdentKind::Meta,
Some(Source::default()),
);
// Same declaration a second time
assert_eq!(
Ok(first.clone()),
first.clone().redeclare(
first.kind().unwrap().clone(),
first.src().unwrap().clone(),
Some(first.src().unwrap().clone()),
)
);
}
@ -776,7 +893,8 @@ mod test {
};
let kind = IdentKind::Meta;
let ident = IdentObject::ident(&sym, kind.clone(), src.clone());
let ident =
IdentObject::ident(&sym, kind.clone(), Some(src.clone()));
let text = FragmentText::from("a fragment");
let ident_with_frag = ident.set_fragment(text.clone());
@ -789,8 +907,11 @@ mod test {
#[test]
fn add_fragment_to_fragment_fails() {
let sym = symbol_dummy!(1, "badsym");
let ident =
IdentObject::ident(&sym, IdentKind::Meta, Source::default());
let ident = IdentObject::ident(
&sym,
IdentKind::Meta,
Some(Source::default()),
);
let ident_with_frag = ident
.set_fragment("orig fragment".into())
@ -825,10 +946,10 @@ mod test {
let virt = IdentObject::ident(
&sym,
kind.clone(),
Source {
Some(Source {
virtual_: true,
..Default::default()
},
}),
);
let over_src = Source {
@ -837,7 +958,7 @@ mod test {
..Default::default()
};
let result = virt.redeclare(kind.clone(), over_src.clone());
let result = virt.redeclare(kind.clone(), Some(over_src.clone()));
assert_eq!(Ok(IdentObject::Ident(&sym, kind, over_src)), result);
}
@ -854,7 +975,8 @@ mod test {
..Default::default()
};
let virt = IdentObject::ident(&sym, kind.clone(), virt_src.clone());
let virt =
IdentObject::ident(&sym, kind.clone(), Some(virt_src.clone()));
let text = FragmentText::from("remove me");
let virt_frag = virt.set_fragment(text.clone());
@ -874,8 +996,9 @@ mod test {
..Default::default()
};
let result =
virt_frag.unwrap().redeclare(kind.clone(), over_src.clone());
let result = virt_frag
.unwrap()
.redeclare(kind.clone(), Some(over_src.clone()));
// The act of overriding the object should have cleared any
// existing fragment, making way for a new fragment to take its
@ -891,7 +1014,7 @@ mod test {
..Default::default()
};
let obj = IdentObject::ident(&sym, given, src.clone());
let obj = IdentObject::ident(&sym, given, Some(src.clone()));
let fragment = "a fragment".to_string();
let obj_with_frag = obj.set_fragment(fragment.clone());

View File

@ -174,10 +174,6 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
Ok(XmloEvent::SymDecl(sym, attrs)) => {
if let Some(sym_src) = attrs.src {
found.insert(sym_src);
} else if attrs.extern_ {
// TODO: externs (they're implicitly handled, without
// checks, by Missing)
// depgraph.declare_extern(sym, kind);
} else {
let owned = attrs.src.is_none();
@ -185,13 +181,19 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
format!("sym `{}` attrs error: {}", sym, err)
});
let mut src: Source = attrs.into();
let src = if attrs.extern_ {
None
} else {
let mut s: Source = attrs.into();
// Existing convention is to omit @src of local package
// (in this case, the program being linked)
if first {
src.pkg_name = None;
}
// Existing convention is to omit @src of local package
// (in this case, the program being linked)
if first {
s.pkg_name = None;
}
Some(s)
};
match kind {
Ok(kindval) => {