diff options
author | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2020-03-25 23:49:37 -0400 |
---|---|---|
committer | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2020-03-26 09:15:59 -0400 |
commit | 7dd8717f2f5c3bfe5d6186da843af8bfe3192901 (patch) | |
tree | d66781c1cbdc6b5f91673ef52a6f12ac64a7b9d5 /tamer/src | |
parent | 537d9e64afdeb6fe3624e23838ecde7309486b77 (diff) | |
download | tame-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/src')
-rw-r--r-- | tamer/src/ir/asg/base.rs | 104 | ||||
-rw-r--r-- | tamer/src/ir/asg/graph.rs | 25 | ||||
-rw-r--r-- | tamer/src/ir/asg/mod.rs | 2 | ||||
-rw-r--r-- | tamer/src/ld/poc.rs | 35 |
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()), |