Skip to content

Fix v0 symbol mangling bug #83767

New issue

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

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

Already on GitHub? # to your account

Merged
merged 3 commits into from
May 19, 2021
Merged

Fix v0 symbol mangling bug #83767

merged 3 commits into from
May 19, 2021

Conversation

camelid
Copy link
Member

@camelid camelid commented Apr 2, 2021

Fixes #83611.

r? @jackh726

@camelid camelid added the A-codegen Area: Code generation label Apr 2, 2021
@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 2, 2021
@rust-log-analyzer

This comment has been minimized.

@camelid
Copy link
Member Author

camelid commented Apr 2, 2021

It seems @eddyb was suggesting a slightly different approach: #83611 (comment)

@camelid
Copy link
Member Author

camelid commented Apr 2, 2021

By the way, why do so many SymbolMangler functions return Result<T, !> instead of just T?

@camelid
Copy link
Member Author

camelid commented Apr 2, 2021

How do I add a test for this? I wasn't able to find any existing name mangling tests.

@tmiasko
Copy link
Contributor

tmiasko commented Apr 2, 2021

--emit llvm-ir output contains both mangled function name and demangled one (obtained from mangled one), so a codegen test should do just fine.

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

This looks exactly like what I was expecting. Just needs a test (and comment).

}
Ok(cx)
})?;
ty::ExistentialPredicate::Projection(projection) => {
Copy link
Member

Choose a reason for hiding this comment

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

Also, based on @eddyb's comment, we should just do a simple validation here.

When we encounter an ExistentialPredicate::Trait, we should store that in a local variable last_trait_ref: Binder<'tcx, TraitRef> then when we see an ExistentialPredicate::Projection, we want to check that 1) the binders match and 2) the projection's trait is the same as the last trait

Copy link
Member Author

Choose a reason for hiding this comment

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

I added some assertions, are they what you expected? 65c04123f81

@camelid
Copy link
Member Author

camelid commented Apr 2, 2021

The assertions panic on the test case :/

thread 'rustc' panicked at 'assertion failed: `(left == right)`
  left: `std::ops::FnMut<(&u8,)>`,
 right: `std::ops::FnOnce<(&u8,)>`', compiler/rustc_symbol_mangling/src/v0.rs:507:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.53.0-dev running on x86_64-apple-darwin

note: compiler flags: -Z symbol-mangling-version=v0

query stack during panic:
#0 [symbol_name] computing the symbol for `f::<&dyn for<'r> std::ops::FnMut(&'r u8)>`
#1 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack

I think the issue is that the projection for the FnMut arguments is actually projecting out of FnOnce, since FnOnce is a supertrait of FnMut. What should I do?

@jackh726
Copy link
Member

jackh726 commented Apr 2, 2021

So, you should be able to get the trait that defines the associated type by

traits::supertraits(tcx, trait_ref).filter(|r| {
  tcx
    .associated_items(r.def_id())
    .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, r.def_id())
    .is_some()
}).next()

@camelid
Copy link
Member Author

camelid commented Apr 3, 2021

So, you should be able to get the trait that defines the associated type by

traits::supertraits(tcx, trait_ref).filter(|r| {
  tcx
    .associated_items(r.def_id())
    .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, r.def_id())
    .is_some()
}).next()

It looks like in order to do that I would need to add a dependency on rustc_infer. Is that okay to do?

@eddyb
Copy link
Member

eddyb commented Apr 3, 2021

It seems @eddyb was suggesting a slightly different approach: #83611 (comment)

Please add my example as a test - I don't see how in_binder can correctly work without being around everything it applies to.

r? @nikomatsakis

@camelid
Copy link
Member Author

camelid commented Apr 3, 2021

Please add my example as a test - I don't see how in_binder can correctly work without being around everything it applies to.

Sure, I'm happy to add your test case, but I have never written a codegen test before and am new to the symbol mangling code, so I would appreciate extra help :)

@tmiasko
Copy link
Contributor

tmiasko commented Apr 3, 2021

Codegen tests are located in src/test/codegen. They use LLVM FileCheck https://www.llvm.org/docs/CommandGuide/FileCheck.html to verify output. In this case I would check that LLVM IR contains correctly demangled names in the comments:

// compile-flags: -Zsymbol-mangling-version=v0 -Cno-prepopulate-passes 

pub fn test<T>() {}

fn main() {
    // CHECK: ; call a::test::<&dyn for<'a> core::ops::function::FnMut<(&'a u8,), Output = ()>>
    test::<&dyn FnMut(&u8)>();
    // CHECK: ; call a::test::<for<'a> fn(&'a dyn for<'b> core::ops::function::FnOnce<(&'b u8,), Output = &'b u8> + 'a) -> &'a u8>
    test::<for<'a> fn(&'a dyn for<'b> FnOnce(&'b u8) -> &'b u8) -> &'a u8>();
}

You can cross verify output with a toolchain before refactoring, e.g., d23e08448332425a84ae23124bea4dbd685536ce.

@camelid
Copy link
Member Author

camelid commented Apr 3, 2021

So, you should be able to get the trait that defines the associated type by

traits::supertraits(tcx, trait_ref).filter(|r| {
  tcx
    .associated_items(r.def_id())
    .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, r.def_id())
    .is_some()
}).next()

That's giving errors:

error[E0308]: mismatched types
   --> compiler/rustc_symbol_mangling/src/v0.rs:511:55
    |
511 |                         traits::supertraits(self.tcx, projection.trait_ref(self.tcx)).find(|r| {
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Binder`, found struct `ExistentialTraitRef`
    |
    = note: expected struct `Binder<'_, rustc_middle::ty::TraitRef<'_>>`
               found struct `ExistentialTraitRef<'_>`

error[E0308]: mismatched types
   --> /Users/user/rust4/library/core/src/macros/mod.rs:39:35
    |
35  | / macro_rules! assert_eq {
36  | |     ($left:expr, $right:expr $(,)?) => ({
37  | |         match (&$left, &$right) {
38  | |             (left_val, right_val) => {
39  | |                 if !(*left_val == *right_val) {
    | |                                   ^^^^^^^^^^ expected struct `ExistentialTraitRef`, found enum `Option`
...   |
61  | |     });
62  | | }
    | |_- in this expansion of `assert_eq!`
    | 
   ::: compiler/rustc_symbol_mangling/src/v0.rs:522:21
    |
522 |                       assert_eq!(last_trait_ref.skip_binder(), assoc_item_parent_trait);
    |                       ------------------------------------------------------------------ in this macro invocation
    |
    = note: expected struct `ExistentialTraitRef<'_>`
                 found enum `Option<Binder<'_, rustc_middle::ty::TraitRef<'_>>>`

error: aborting due to 2 previous errors

@jackh726
Copy link
Member

jackh726 commented Apr 4, 2021

You'll have to convert the ExistentialTraitRef to a normal one via with_self_ty.

@camelid camelid added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Apr 4, 2021
@eddyb
Copy link
Member

eddyb commented Apr 4, 2021

@camelid @tmiasko We don't use codegen tests for testing symbol names.
Check out src/test/ui/symbol-names instead, e.g:

// Test type mangling, by putting them in an `impl` header.
impl Bar for [&'_ (dyn Foo<Assoc = extern "C" fn(&u8, ...)> + AutoTrait); 3] {
#[rustc_symbol_name]
//[legacy]~^ ERROR symbol-name(_ZN209_$LT$$u5b$$RF$dyn$u20$impl1..Foo$u2b$Assoc$u20$$u3d$$u20$extern$u20$$u22$C$u22$$u20$fn$LP$$RF$u8$C$$u20$...$RP$$u2b$impl1..AutoTrait$u3b$$u20$3$u5d$$u20$as$u20$impl1..main..$u7b$$u7b$closure$u7d$$u7d$..Bar$GT$6method
//[legacy]~| ERROR demangling(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method
//[legacy]~| ERROR demangling-alt(<[&dyn impl1::Foo+Assoc = extern "C" fn(&u8, ::.)+impl1::AutoTrait; 3] as impl1::main::{{closure}}::Bar>::method)
//[v0]~^^^^ ERROR symbol-name(_RNvXNCNvCs21hi0yVfW1J_5impl14mains_0ARDNtB6_3Foop5AssocFG_KCRL0_hvEuNtB6_9AutoTraitEL_j3_NtB2_3Bar6method)
//[v0]~| ERROR demangling(<[&dyn impl1[17891616a171812d]::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1[17891616a171812d]::AutoTrait; 3: usize] as impl1[17891616a171812d]::main::{closure#1}::Bar>::method)
//[v0]~| ERROR demangling-alt(<[&dyn impl1::Foo<Assoc = for<'a> extern "C" fn(&'a u8, ...)> + impl1::AutoTrait; 3] as impl1::main::{closure#1}::Bar>::method)
#[rustc_def_path]
//[legacy]~^ ERROR def-path(<[&dyn Foo<Assoc = for<'r> extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{closure#1}::Bar>::method)
//[v0]~^^ ERROR def-path(<[&dyn Foo<Assoc = for<'r> extern "C" fn(&'r u8, ...)> + AutoTrait; 3] as main::{closure#1}::Bar>::method)
fn method(&self) {}

These tests include demangling output, which is important to confirm correct roundtrip.

@michaelwoerister
Copy link
Member

What's the state of this PR? Is a test case the only thing that remains to do?

@jackh726
Copy link
Member

I think tests and a review from @nikomatsakis

@nikomatsakis
Copy link
Contributor

@jackh726 I don't really understand what I'm meant to review, I think. =) Maybe we can touch on this tomorrow morning?

@jackh726
Copy link
Member

@nikomatsakis yeah we can do that

@camelid
Copy link
Member Author

camelid commented Apr 22, 2021

Hmm, I can't quite get the code working. Here's my current version:

                ty::ExistentialPredicate::Projection(projection) => {
                    let assoc_item = self.tcx.associated_item(projection.item_def_id);

                    let last_trait_ref = last_trait_ref
                        .expect("trait predicate must come before projection predicate");
                    assert_eq!(last_trait_ref.bound_vars(), predicate.bound_vars());
                    // Use a type that can't appear in defaults of type parameters.
                    let dummy_self = self.tcx.mk_ty_infer(ty::FreshTy(0));
                    let assoc_item_parent_trait =
                        traits::supertraits(self.tcx, projection.trait_ref(self.tcx).with_self_ty(self.tcx, dummy_self)).find(|r| {
                            self.tcx
                                .associated_items(r.def_id())
                                .find_by_name_and_kind(
                                    self.tcx,
                                    assoc_item.ident,
                                    assoc_item.kind,
                                    r.def_id(),
                                )
                                .is_some()
                        }).unwrap();
                    assert_eq!(last_trait_ref.skip_binder(), ty::ExistentialTraitRef::erase_self_ty(self.tcx, assoc_item_parent_trait.skip_binder()));

                    let name = assoc_item.ident;
                    self.push("p");
                    self.push_ident(&name.as_str());
                    self = projection.ty.print(self)?;
                }

This code fails to compile:

error[E0308]: mismatched types
   --> compiler/rustc_symbol_mangling/src/v0.rs:513:55
    |
513 |                         traits::supertraits(self.tcx, projection.trait_ref(self.tcx).with_self_ty(self.tcx, dummy_self)).find(|r| {
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Binder`, found struct `rustc_middle::ty::TraitRef`
    |
    = note: expected struct `Binder<'_, rustc_middle::ty::TraitRef<'_>, >`
               found struct `rustc_middle::ty::TraitRef<'_>`

Then I tried using Binder::dummy, but that causes the !has_escaping_bound_vars assertions to fail. So then I tried using Binder::bind, but that fails with

error: internal compiler error: compiler/rustc_middle/src/ty/fold.rs:824:17: Trying to collect bound vars with a bound region: DebruijnIndex(0) BoundRegion { var: 0, kind: BrAnon(0) }

thread 'rustc' panicked at 'Box<Any>', /Users/user/rust4/library/std/src/panic.rs:59:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

note: rustc 1.53.0-dev running on x86_64-apple-darwin

note: compiler flags: -Z threads=1 -Z ui-testing -Z deduplicate-diagnostics=no -Z emit-future-incompat-report -Z unstable-options -Z symbol-mangling-version=v0 -C prefer-dynamic -C rpath -C debuginfo=0

query stack during panic:
#0 [symbol_name] computing the symbol for `test::<&dyn for<'r> std::ops::FnMut(&'r u8)>`
#1 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack
error: aborting due to previous error

What should I do?

@jackh726
Copy link
Member

Should be Binder::bind_with_vars(trait_ref, last_trait_ref.bound_vars())

Copy link
Member

@jackh726 jackh726 left a comment

Choose a reason for hiding this comment

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

Might have a cleaner way to approach this. @nikomatsakis and I discussed, and would also definitely like to see the example in my comment as a test case too.

@jackh726
Copy link
Member

@nikomatsakis this is ready :)

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@jackh726
Copy link
Member

Ugh don't remember how to make this reproducible locally

@rust-log-analyzer

This comment has been minimized.

Comment on lines 13 to 14
error: demangling-alt(<&dyn core::ops::function::FnMut<(&u8,)>+Output = () as trait_objects::Bar>::method)
--> $DIR/trait-objects.rs:15:5
error: demangling-alt(<&dyn core::ops::function::FnMut<(&u8,)>+Output = () as trait_objects::Bar>::metSYMBOL_HASH)
--> $DIR/trait-objects.rs:17:5
Copy link
Member Author

@camelid camelid May 16, 2021

Choose a reason for hiding this comment

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

Is it expected that this changed from ::method to ::metSYMBOL_HASH?

EDIT: This is an outdated diff, but the same change seems to be present in the current diff as well.

Copy link
Member

Choose a reason for hiding this comment

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

Oh this file shouldn't be here currently...(mostly because I didn't think hard enough about what regex to use here to make it reproducible and we don't care about legacy here anyways)

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Collaborator

bors commented May 17, 2021

📌 Commit 8345908 has been approved by nikomatsakis

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 17, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request May 18, 2021
bors added a commit to rust-lang-ci/rust that referenced this pull request May 19, 2021
Rollup of 8 pull requests

Successful merges:

 - rust-lang#83366 (Stabilize extended_key_value_attributes)
 - rust-lang#83767 (Fix v0 symbol mangling bug)
 - rust-lang#84883 (compiletest: "fix" FileCheck with --allow-unused-prefixes)
 - rust-lang#85274 (Only pass --[no-]gc-sections if linker is GNU ld.)
 - rust-lang#85297 (bootstrap: build cargo only if requested in tools)
 - rust-lang#85396 (rustdoc: restore header sizes)
 - rust-lang#85425 (Fix must_use on `Option::is_none`)
 - rust-lang#85438 (Fix escape handling)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 809e975 into rust-lang:master May 19, 2021
@rustbot rustbot added this to the 1.54.0 milestone May 19, 2021
@camelid camelid deleted the mangle-v0-fix branch May 20, 2021 19:43
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-codegen Area: Code generation S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. WG-traits Working group: Traits, https://internals.rust-lang.org/t/announcing-traits-working-group/6804
Projects
None yet
Development

Successfully merging this pull request may close these issues.

v0 mangled symbol doesn't match specification nor can it be demangled with rustfilt or c++filt
10 participants