Skip to content
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

Fix v0 symbol mangling bug #83767

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).

compiler/rustc_symbol_mangling/src/v0.rs Outdated Show resolved Hide resolved
}
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.

compiler/rustc_symbol_mangling/src/v0.rs Outdated Show resolved Hide resolved
compiler/rustc_symbol_mangling/src/v0.rs Outdated Show resolved Hide resolved
@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
Contributor

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in 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