[DEV-7087] TAMER: Asg: Reintroduce declare_extern

There is some duplication here with `declare` that will be cleared up in a
following commit.  Reintroducing this method is necessary so that Source can
be used to represent the source location of the extern itself; it's
currently None to indicate an extern in `declare`.
master
Mike Gerwitz 2020-03-25 23:49:37 -04:00
parent 537d9e64af
commit 7dd8717f2f
4 changed files with 145 additions and 19 deletions

View File

@ -201,6 +201,10 @@ where
kind: IdentKind,
src: Option<Source<'i>>,
) -> AsgResult<ObjectRef<Ix>, Ix> {
if src.is_none() {
panic!("TODO: remove optional src");
}
if let Some(existing) = self.lookup(name) {
let node = self.graph.node_weight_mut(existing.0).unwrap();
@ -229,6 +233,30 @@ where
Ok(ObjectRef(node))
}
fn declare_extern(
&mut self,
name: &'i Symbol<'i>,
kind: IdentKind,
src: Source<'i>,
) -> AsgResult<ObjectRef<Ix>, Ix> {
let identi = self.lookup_or_missing(name);
let node = self.graph.node_weight_mut(identi.0).unwrap();
let obj = node
.take()
.expect(&format!("internal error: missing object for {}", name));
obj.extern_(kind, src)
.and_then(|obj| {
node.replace(obj);
Ok(identi)
})
.or_else(|(orig, err)| {
node.replace(orig);
Err(err.into())
})
}
fn set_fragment(
&mut self,
identi: ObjectRef<Ix>,
@ -400,9 +428,11 @@ mod test {
struct StubIdentObject<'i> {
given_missing: Option<&'i Symbol<'i>>,
given_ident: Option<(&'i Symbol<'i>, IdentKind, Option<Source<'i>>)>,
given_extern: Option<(IdentKind, Source<'i>)>,
given_redeclare: Option<(IdentKind, Option<Source<'i>>)>,
given_set_fragment: Option<FragmentText>,
fail_redeclare: RefCell<Option<TransitionError>>,
fail_extern: RefCell<Option<TransitionError>>,
}
impl<'i> IdentObjectData<'i> for StubIdentObject<'i> {
@ -465,10 +495,16 @@ mod test {
}
fn extern_(
self,
_kind: IdentKind,
_src: Source<'i>,
mut self,
kind: IdentKind,
src: Source<'i>,
) -> TransitionResult<StubIdentObject<'i>> {
if self.fail_extern.borrow().is_some() {
let err = self.fail_extern.replace(None).unwrap();
return Err((self, err));
}
self.given_extern = Some((kind, src));
Ok(self)
}
@ -639,6 +675,68 @@ mod test {
}
}
#[test]
fn declare_extern_returns_existing() -> AsgResult<(), u8> {
let mut sut = Sut::with_capacity(0, 0);
let sym = symbol_dummy!(1, "symext");
let src = Source::default();
let node = sut.declare_extern(&sym, IdentKind::Meta, src.clone())?;
// Remember that our stub does not care about compatibility.
let rekind = IdentKind::Class(Dim::from_u8(3));
let resrc = Source {
desc: Some("redeclare".into()),
..Default::default()
};
let redeclare =
sut.declare_extern(&sym, rekind.clone(), 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)), sut.get(node).unwrap().given_extern);
Ok(())
}
// Builds upon declare_returns_existing.
#[test]
fn declare_extern_fails_if_transition_fails() -> AsgResult<(), u8> {
let mut sut = Sut::with_capacity(0, 0);
let sym = symbol_dummy!(1, "symdup");
let src = Source {
desc: Some("orig".into()),
..Default::default()
};
// Set up an object to fail redeclaration.
let node = sut.declare_extern(&sym, IdentKind::Meta, src.clone())?;
let obj = sut.get(node).unwrap();
// It doesn't matter that this isn't the error that'll actually be
// returned, as long as it's some sort of TransitionError.
let terr = TransitionError::Incompatible(String::from("test fail"));
obj.fail_extern.replace(Some(terr.clone()));
// Should invoke StubIdentObject::extern_ on the above `obj`.
let result =
sut.declare_extern(&sym, IdentKind::Meta, Source::default());
if let Err(err) = result {
// The node should have been restored.
let obj = sut.get(node).unwrap();
assert_eq!(src, obj.given_extern.as_ref().unwrap().1);
assert_eq!(AsgError::ObjectTransition(terr), err);
Ok(())
} else {
panic!("failure expected: {:?}", result);
}
}
#[test]
fn add_fragment_to_ident() -> AsgResult<(), u8> {
let mut sut = Sut::with_capacity(0, 0);

View File

@ -91,6 +91,31 @@ where
) -> 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>,
kind: IdentKind,
src: Source<'i>,
) -> AsgResult<ObjectRef<Ix>, Ix>;
/// Set the fragment associated with a concrete identifier.
///

View File

@ -77,7 +77,7 @@
//! let identb_sym = interner.intern("identb");
//!
//! let identa = asg.declare(identa_sym, IdentKind::Meta, Some(Source::default()))?;
//! let identb = asg.declare(identb_sym, IdentKind::Meta, None)?;
//! let identb = asg.declare_extern(identb_sym, IdentKind::Meta, Source::default())?;
//!
//! assert_eq!(
//! Some(&IdentObject::Extern(identb_sym, IdentKind::Meta)),

View File

@ -176,24 +176,19 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
found.insert(sym_src);
} else {
let owned = attrs.src.is_none();
let extern_ = attrs.extern_;
let kind = (&attrs).try_into().map_err(|err| {
format!("sym `{}` attrs error: {}", sym, err)
});
let src = if attrs.extern_ {
None
} else {
let mut s: Source = attrs.into();
let mut src: Source = attrs.into();
// Existing convention is to omit @src of local package
// (in this case, the program being linked)
if first {
s.pkg_name = None;
}
Some(s)
};
// Existing convention is to omit @src of local package
// (in this case, the program being linked)
if first {
src.pkg_name = None;
}
match kind {
Ok(kindval) => {
@ -203,10 +198,18 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
|| kindval == IdentKind::Map
|| kindval == IdentKind::RetMap);
let node = depgraph.declare(sym, kindval, src)?;
if extern_ {
depgraph.declare_extern(sym, kindval, src)?;
} else {
let node = depgraph.declare(
sym,
kindval,
Some(src),
)?;
if link_root {
roots.push(node);
if link_root {
roots.push(node);
}
}
}
Err(e) => return Err(e.into()),