Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
path: root/tamer
diff options
context:
space:
mode:
authorMike Gerwitz <mike.gerwitz@ryansg.com>2020-03-25 23:49:37 -0400
committerMike Gerwitz <mike.gerwitz@ryansg.com>2020-03-26 09:15:59 -0400
commit7dd8717f2f5c3bfe5d6186da843af8bfe3192901 (patch)
treed66781c1cbdc6b5f91673ef52a6f12ac64a7b9d5 /tamer
parent537d9e64afdeb6fe3624e23838ecde7309486b77 (diff)
downloadtame-7dd8717f2f5c3bfe5d6186da843af8bfe3192901.tar.gz
tame-7dd8717f2f5c3bfe5d6186da843af8bfe3192901.tar.bz2
tame-7dd8717f2f5c3bfe5d6186da843af8bfe3192901.zip
[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`.
Diffstat (limited to 'tamer')
-rw-r--r--tamer/src/ir/asg/base.rs104
-rw-r--r--tamer/src/ir/asg/graph.rs25
-rw-r--r--tamer/src/ir/asg/mod.rs2
-rw-r--r--tamer/src/ld/poc.rs35
4 files changed, 146 insertions, 20 deletions
diff --git a/tamer/src/ir/asg/base.rs b/tamer/src/ir/asg/base.rs
index e156a77..c748b90 100644
--- a/tamer/src/ir/asg/base.rs
+++ b/tamer/src/ir/asg/base.rs
@@ -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)
}
@@ -640,6 +676,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);
diff --git a/tamer/src/ir/asg/graph.rs b/tamer/src/ir/asg/graph.rs
index 3d16491..451387f 100644
--- a/tamer/src/ir/asg/graph.rs
+++ b/tamer/src/ir/asg/graph.rs
@@ -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.
///
diff --git a/tamer/src/ir/asg/mod.rs b/tamer/src/ir/asg/mod.rs
index 17986ec..d1744ba 100644
--- a/tamer/src/ir/asg/mod.rs
+++ b/tamer/src/ir/asg/mod.rs
@@ -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)),
diff --git a/tamer/src/ld/poc.rs b/tamer/src/ld/poc.rs
index d1677da..8bf680f 100644
--- a/tamer/src/ld/poc.rs
+++ b/tamer/src/ld/poc.rs
@@ -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 link_root {
- roots.push(node);
+ if extern_ {
+ depgraph.declare_extern(sym, kindval, src)?;
+ } else {
+ let node = depgraph.declare(
+ sym,
+ kindval,
+ Some(src),
+ )?;
+
+ if link_root {
+ roots.push(node);
+ }
}
}
Err(e) => return Err(e.into()),