Skip to content

Add assert for dangling references without an associated ItemId #312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 15, 2016
Merged

Add assert for dangling references without an associated ItemId #312

merged 1 commit into from
Dec 15, 2016

Conversation

impowski
Copy link
Contributor

@impowski impowski commented Dec 2, 2016

So I think this is it? Fix issue #209
r? @fitzgen

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to its own function, that is called from right here? Additionally, we want this function to be a no-op in release builds.

Also, we can remove the NOTE comment. This doesn't need testing itself, this is more of extra testing code for the rest of bindgen.

@@ -431,6 +431,18 @@ impl<'ctx> BindgenContext<'ctx> {
// because we remove it before the end of this function.
self.gen_ctx = Some(unsafe { mem::transmute(&ctx) });

// NOTE (imp): I guess this should work but how do I test it?
for (id, item) in self.items() {
let mut sub_items = ItemSet::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On top of that, can we call this related_items instead? Seems more accurate given we insert the parent.

@impowski
Copy link
Contributor Author

impowski commented Dec 4, 2016

I was trying to understand how we will use BFS for this and quite confused. Because there is no possible way of traversing it without any parent_id, which is basically involving having an Item inside of BTreeMap<ItemId, Item>, and the only option then to traverse BTreeMap<ItemId, Item> and stdout a path of BTreeMap. Maybe I'm wrong in something?

@fitzgen
Copy link
Member

fitzgen commented Dec 8, 2016

@impowski: I haven't forgotten about this PR, just a bit busy this week at an all-hands -- will get to this end of this week / early next week. Thanks for your patience :)

@impowski
Copy link
Contributor Author

impowski commented Dec 8, 2016

@fitzgen I was little bit busy for last 2 days, I'll will try to do something right now, just need to check your example in IRC.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Will merge once my final nitpick is taken care of :)

@@ -441,6 +443,22 @@ impl<'ctx> BindgenContext<'ctx> {
ret
}

/// This function trying to find any dangling references inside of `items`
fn related_items(&self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this assert_no_dangling_references? Thanks!

@fitzgen
Copy link
Member

fitzgen commented Dec 12, 2016

Regarding the BFS stuff: let's land this now and then do that in a follow up. We talked a bit on irc, but if things are still unclear, just ping me again or comment on the issue.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start!

I've detailed how to reconstruct the shortest path to an ItemId below with a few code snippets and requested changes.

Let me know if you have questions!


let roots = self.items().map(|(&id, _)| id);

let seen: ItemSet = roots.collect();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is marking every item as seen, the BFS is going to go nowhere: it won't traverse any sub-item.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to keep the seen set empty, and also put the self.items() iterator as a member into RelatedItemsIter.

where 'gen: 'ctx,
{
ctx: &'ctx BindgenContext<'gen>,
seen: ItemSet,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a BTreeMap<ItemId, ItemId> mapping from a seen item, to the item from which we first discovered it (or mapping to itself if we found it as a root).

That way, if/when we find a dangling ItemId reference, we can walk backwards through the path with this map, something like this:

if is_dangling(id) {
    let mut path = vec![];
    let mut current = id;
    loop {
        let predecessor = self.seen.get(current)
            .expect("We know we found this item id, so it must have a predecessor");
        if predecessor == current {
            break;
        }
        path.push(predecessor);
        current = predecessor;
    }
    path.reverse();
    panic!("Found reference to dangling id = {:?}\n
            via path = {:?}",
           id,
           path);
}

id.collect_types(self.ctx, &mut sub_types, &());

for id in sub_types {
if self.seen.insert(id) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Map from a seen id to the id of its predecessor:

for sub_id in sub_types {
    if self.seen.insert(sub_id, id) {
        ...
    }
}


fn next(&mut self) -> Option<Self::Item> {
let id = match self.to_iterate.pop_front() {
None => return None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of eagerly returning None, we should see if there is a next root:

let id = match self.to_iterate.pop_front() {
    None => {
        // We've traversed everything reachable from the previous root(s), see if
        // we have any more roots.
        //
        // (Note: The `self.all_items` here would be the iterator returned by `ctx.items()`.)
        match self.all_items.filter(|&(id, _)| !self.seen.contains_key(id)).next() {
            None => return None,
            Some(id) => {
                 // This is a new root.
                 self.seen.insert(id, id);
                 id
            }
    },
    Some(id) => id,
};

};

debug_assert!(self.seen.contains(&id));
debug_assert!(self.ctx.items.contains_key(&id));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the if is_dangling(id) { ... } snippet that finds the BFS path would go.

@@ -1066,3 +1102,38 @@ impl<'ctx, 'gen> Iterator for WhitelistedItemsIter<'ctx, 'gen>
Some(id)
}
}

pub struct RelatedItemsIter<'ctx, 'gen>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this iterator is going to be specific to finding BFS paths of dangling items, lets call it AssertNoDanglingItemIter.

@@ -441,6 +443,40 @@ impl<'ctx> BindgenContext<'ctx> {
ret
}

/// This function trying to find any dangling references inside of `items`
fn assert_no_dangling_references(&self) {
if cfg!(debug_assertions) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the changes requested below, the contents of this if branch can now simply be:

if cfg!(debug_assertions) {
    for _ in self.assert_no_dangling_item_traversal() {
        // The iterator's next method does the asserting for us.
    }
}

}
}

fn related_items_traversal<'me>(&'me self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename this method to assert_no_dangling_item_traversal()

@emilio
Copy link
Contributor

emilio commented Dec 13, 2016 via email

@fitzgen
Copy link
Member

fitzgen commented Dec 13, 2016

We could add a feature for this, that we only enable in local + CI tests.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for sticking with this all the way through :)

Can you rebase + squash this branch, and then force push it here? After that, I'll merge :)

Thanks again!

@fitzgen
Copy link
Member

fitzgen commented Dec 15, 2016

@bors-servo r+

@bors-servo
Copy link

📌 Commit 989a516 has been approved by fitzgen

@bors-servo
Copy link

⌛ Testing commit 989a516 with merge 59a5256...

bors-servo pushed a commit that referenced this pull request Dec 15, 2016
Add assert for dangling references without an associated ItemId

So I think this is it? Fix issue #209
r? @fitzgen
@bors-servo
Copy link

☀️ Test successful - status-travis

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants