[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
parent
537d9e64af
commit
7dd8717f2f
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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()),
|
||||
|
|
Loading…
Reference in New Issue