Skip to content

Discover which template type parameters are actually used #515

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 2 commits into from
Feb 15, 2017

Conversation

fitzgen
Copy link
Member

@fitzgen fitzgen commented Feb 14, 2017

This is just landing the initial analysis, and does not start leveraging this analysis's results instead of using the Item::signature_contains_named_type method yet.

r? @emilio

C++ allows ignoring template parameters, while Rust does not. Usually we can
blindly stick a `PhantomData<T>` inside a generic Rust struct to make up for
this. That doesn't work for templated type aliases, however:

```C++
template <typename T>
using Fml = int;
```

If we generate the naive Rust code for this alias, we get:

```ignore
pub type Fml<T> = ::std::os::raw::int;
```

And this is rejected by `rustc` due to the unused type parameter.

(Aside: in these simple cases, `libclang` will often just give us the
aliased type directly, and we will never even know we were dealing with
aliases, let alone templated aliases. It's the more convoluted scenarios
where we get to have some fun...)

For such problematic template aliases, we could generate a tuple whose
second member is a `PhantomData<T>`. Or, if we wanted to go the extra mile,
we could even generate some smarter wrapper that implements `Deref`,
`DerefMut`, `From`, `Into`, `AsRef`, and `AsMut` to the actually aliased
type. However, this is still lackluster:

1. Even with a billion conversion-trait implementations, using the generated
   bindings is rather un-ergonomic.
2. With either of these solutions, we need to keep track of which aliases
   we've transformed like this in order to generate correct uses of the
   wrapped type.

Given that we have to properly track which template parameters ended up used
for (2), we might as well leverage that information to make ergonomic
bindings that don't contain any unused type parameters at all, and
completely avoid the pain of (1).

Determining which template parameters are actually used is a trickier
problem than it might seem at a glance. On the one hand, trivial uses are
easy to detect:

```C++
template <typename T>
class Foo {
    T trivial_use_of_t;
};
```

It gets harder when determining if one template parameter is used depends on
determining if another template parameter is used. In this example, whether
`U` is used depends on whether `T` is used.

```C++
template <typename T>
class DoesntUseT {
    int x;
};

template <typename U>
class Fml {
    DoesntUseT<U> lololol;
};
```

We can express the set of used template parameters as a constraint solving
problem (where the set of template parameters used by a given IR item is the
union of its sub-item's used template parameters) and iterate to a
fixed-point.

We use the "monotone framework" for this fix-point analysis where our
lattice is the powerset of the template parameters that appear in the input
C++ header, our join function is set union, and we use the
`ir::traversal::Trace` trait to implement the work-list optimization so we
don't have to revisit every node in the graph when for every iteration
towards the fix-point.

For a deeper introduction to the general form of this kind of analysis, see
[Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa].

[spa]: https://cs.au.dk/~amoeller/spa/spa.pdf
@fitzgen
Copy link
Member Author

fitzgen commented Feb 15, 2017

I just realized that while this will successfully compute whether a template parameter is ever used, it doesn't determine if a given item uses a given template parameter, which we need for cases like this:

template <typename T>
class Foo {
    T use_of_t;

    // T is a template parameter for Foo<T>::Bar, and T is used, but
    // Bar does NOT use T, and so our bindings for Bar should not
    // have a generic parameter T.
    using Bar = int;
};

It is actually pretty easy to make this analysis track this information. I'll do another PR as a follow up.

@emilio
Copy link
Contributor

emilio commented Feb 15, 2017

This is fine. I didn't expect bindgen to end up having a control flow analysis framework when I wrote it, but as long as it's well documented and maintainable, wfm.

@bors-servo r+

@bors-servo
Copy link

📌 Commit 12b2452 has been approved by emilio

@bors-servo
Copy link

⌛ Testing commit 12b2452 with merge 8c4d1ae...

bors-servo pushed a commit that referenced this pull request Feb 15, 2017
Discover which template type parameters are actually used

This is just landing the initial analysis, and does not start leveraging this analysis's results instead of using the `Item::signature_contains_named_type` method yet.

r? @emilio
@bors-servo
Copy link

☀️ Test successful - status-travis
Approved by: emilio
Pushing 8c4d1ae to master...

@bors-servo bors-servo merged commit 12b2452 into rust-lang:master Feb 15, 2017
@fitzgen fitzgen deleted the monotone-framework branch February 15, 2017 16:29
@fitzgen
Copy link
Member Author

fitzgen commented Feb 15, 2017

I didn't expect bindgen to end up having a control flow analysis framework when I wrote it

I know right?? Isn't implementing a crappy C++ compiler fun?

(To be pedantic, its not really a control flow analysis, it just uses the same techniques... I'll show myself out now :-P )

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.

4 participants