Skip to content

Replacing template alias that does not use its template parameter with one that does is broken #89

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

Closed
fitzgen opened this issue Oct 14, 2016 · 6 comments

Comments

@fitzgen
Copy link
Member

fitzgen commented Oct 14, 2016

// bindgen-flags: -- --std=c++14

namespace JS {
namespace detail {

// Notice how this doesn't use T.
template <typename T>
using MaybeWrapped = int;

}

template <typename T>
class Rooted {
    detail::MaybeWrapped<T> ptr;
};

}

/// But the replacement type does use T!
///
/// <div rustbindgen replaces="MaybeWrapped" />
template <typename T>
using replaces_MaybeWrapped = T;

Oh man this took a while to track down. I'm hitting this in the SM bindings because we don't understand mozilla::Conditional and therefore don't understand that it is using T and we shouldn't filter the type parameter out for being unused, but we aren't smart enough to understand that.

I "fixed" this with this diff:

             TypeKind::Alias(_, inner) => {
                 let parent_args = ctx.resolve_item(self.parent_id())
                    .applicable_template_args(ctx);
                 let inner = ctx.resolve_type(inner);
                 // Avoid unused type parameters, sigh.
                 parent_args.iter().cloned().filter(|arg| {
-                    let arg = ctx.resolve_type(*arg);
-                    arg.is_named() && inner.signature_contains_named_type(ctx, arg)
+                    // let arg = ctx.resolve_type(*arg);
+                    // arg.is_named() && inner.signature_contains_named_type(ctx, arg)
+
+                    true
                 }).collect()
             }

however that breaks perfectly reasonable things like tests/headers/template_typedefs.h.

Clearly we need to be smarter about type parameters, but I'm not 100% sure how to do that yet, and I'm still fumbling around this code base.

@emilio
Copy link
Contributor

emilio commented Oct 16, 2016

I think it's not about being smarter about type parameters, but about being replacement-aware. That's partially why the replaces annotation sucks quite a bit (and it'd come a bit more handy to have a replaced_by annotation or something, but that's way more annoying).

Right now the amount of code that is replacement-aware is minimal. Probably we should make Context::resolve_item replacement-aware by default (it's not hard, just slightly annoying), and then have another function to bypass this in the appropriate places.

@fitzgen
Copy link
Member Author

fitzgen commented Nov 3, 2016

After digging in more, this is not what we thought this was. Code generation, which is when we get the named types we need to emit, happens after we have already processed replacements, so the issue is not that we are failing to be replacement aware.

In fact, it looks like we end up replacing the wrong items, and then end up ignoring the replacements or something.

I'm digging in further...

@fitzgen
Copy link
Member Author

fitzgen commented Nov 3, 2016

First, we parse the replaces_MaybeWrapped template alias, which has the use_instead_of (né replaces) annotation, and it ends up with an id of ItemId(13):

DEBUG:bindgen::ir::context: BindgenContext::add_item(Item { id: ItemId(13), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: Some("/// But the replacement type does use T!\n///\n/// <div rustbindgen replaces=\"MaybeWrapped\" />"), annotations: Annotations { opaque: false, hide: false, use_instead_of: Some("MaybeWrapped"), disallow_copy: false, private_fields: None, accessor_kind: None }, parent_id: ItemId(0), kind: Type(Type { name: None, layout: None, kind: TemplateAlias(ItemId(16), [ItemId(15)]), is_const: false }) }, declaration: Some(Cursor( kind: NoDeclFound, loc: builtin definitions, usr: None)), loc: Some(Cursor(replaces_MaybeWrapped kind: TypeAliasTemplateDecl, loc: tests/headers/replace_template_alias.hpp:23:1, usr: Some("c:@replaces_MaybeWrapped")))

Then, we define the replacement for MaybeWrapped (ie a call to BindgenContext::replace) as ItemId(18)(!!) and not the template alias above which is ItemId(13):

DEBUG:bindgen::ir::context: Defining replacement for MaybeWrapped as ItemId(18)

Finally, we add the item with id itemId(18), which is the named template type argument T:

DEBUG:bindgen::ir::context: BindgenContext::add_item(Item { id: ItemId(18), local_id: Cell { value: None }, next_child_local_id: Cell { value: 1 }, canonical_name_cache: RefCell { value: None }, comment: None, annotations: Annotations { opaque: false, hide: false, use_instead_of: None, disallow_copy: false, private_fields: None, accessor_kind: None }, parent_id: ItemId(13), kind: Type(Type { name: Some("T"), layout: None, kind: Named("T", None), is_const: false }) }, declaration: None, loc: None

So that seems to be the bug: we should be defining the replacement as ItemId(13) not ItemId(18).

Still figuring out why this is happening in the first place.

@emilio
Copy link
Contributor

emilio commented Nov 3, 2016

I bet we're inserting it twice in the hashmap?

@fitzgen
Copy link
Member Author

fitzgen commented Nov 3, 2016

I bet we're inserting it twice in the hashmap?

Great guess! ;)

DEBUG:bindgen::ir::context: Defining replacement for MaybeWrapped as ItemId(13)
DEBUG:bindgen::ir::context: Defining replacement for MaybeWrapped as ItemId(16)
DEBUG:bindgen::ir::context: Defining replacement for MaybeWrapped as ItemId(18)

@fitzgen
Copy link
Member Author

fitzgen commented Nov 3, 2016

I guess we can assume the first is the correct replacement and then warn for duplicate replacement definition attempts.

Fix incoming.

bors-servo pushed a commit that referenced this issue Nov 3, 2016
Do not overwrite existing replacements

It turns out that we can end up overwriting existing replacements. This commit embeds the assumption that the first replacement definition is the correct one, and warns on all attempts to overwrite the first replacement definition with a new one. Additionally, it adds some debug logging about replacements.

This actually isn't enough to fix the test case in #89, but it is a good start.

r? @emilio
bors-servo pushed a commit that referenced this issue Nov 3, 2016
Allow aliases and template aliases to be considered for replacement

Fixes #89.

I'm not exactly *happy* with the way this is implemented (making `real_canonical_name` public so that we can use it in replacement lookups) but I'm not sure of a better way without refactoring most of how naming works right now.

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

No branches or pull requests

2 participants