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

Emit simpler code from format_args #91359

Merged
merged 5 commits into from
Jan 21, 2022
Merged

Emit simpler code from format_args #91359

merged 5 commits into from
Jan 21, 2022

Conversation

dtolnay
Copy link
Member

@dtolnay dtolnay commented Nov 29, 2021

I made this PR so that cargo expand dumps a less overwhelming amount of formatting-related code.


println!("rust") Before:

{
    ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"],
                                                     &match () {
                                                          _args => [],
                                                      }));
};

After:

{ ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };

println!("{}", x) Before:

{
    ::std::io::_print(::core::fmt::Arguments::new_v1(
        &["", "\n"],
        &match (&x,) {
            _args => [::core::fmt::ArgumentV1::new(
                _args.0,
                ::core::fmt::Display::fmt,
            )],
        },
    ));
};

After:

{
    ::std::io::_print(::core::fmt::Arguments::new_v1(
        &["", "\n"],
        &[::core::fmt::ArgumentV1::new(&x, ::core::fmt::Display::fmt)],
    ));
};

@rust-highfive
Copy link
Collaborator

r? @michaelwoerister

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 29, 2021
Comment on lines -761 to -764
// Right now there is a bug such that for the expression:
// foo(bar(&1))
// the lifetime of `1` doesn't outlast the call to `bar`, so it's not
// valid for the call to `foo`. To work around this all arguments to the
Copy link
Member Author

Choose a reason for hiding this comment

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

Lol at leftover comments from the original implementation of std::fmt (the ifmt! macro at the time) >8 years ago in ffb670f, which has been obsolete/inaccurate since a year before rust 1.0 (early 2014 — #11585).

@camsteffen
Copy link
Contributor

I am planning to make Clippy less sensitive to macro changes at some point in the near future (rust-lang/rust-clippy#7843). You might want to hold this until that lands.

@camelid camelid added A-fmt Area: std::fmt T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 29, 2021
@dtolnay
Copy link
Member Author

dtolnay commented Nov 30, 2021

Labeling blocked on rust-lang/rust-clippy#7843.
@rustbot label -S-waiting-on-review +S-blocked

@rustbot rustbot added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Nov 30, 2021
@bors
Copy link
Contributor

bors commented Dec 15, 2021

☔ The latest upstream changes (presumably #91945) made this pull request unmergeable. Please resolve the merge conflicts.

@dtolnay
Copy link
Member Author

dtolnay commented Dec 23, 2021

@camsteffen
Copy link
Contributor

Clippy should be out of your way after the next sync!

@dtolnay
Copy link
Member Author

dtolnay commented Jan 4, 2022

@rustbot label -S-blocked +S-waiting-on-review

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. labels Jan 4, 2022
@michaelwoerister
Copy link
Member

Let's check if this has any implications for compile time.

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jan 5, 2022
@bors
Copy link
Contributor

bors commented Jan 5, 2022

⌛ Trying commit 20bda20b5f66e30cc64ccb64b087f0ea3cb5edae with merge 3a6b09020f099db8b66b764a71e2ef5608e6c4ea...

Copy link
Member

@michaelwoerister michaelwoerister left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, @dtolnay! Looks like a really nice simplification. I left a couple of suggestions below.

Is there anything that could go subtly wrong with this? Like hygiene or borrow checking? Should we ask someone with more experience in macro expansion to take a look too?

// we can generate simpler code.
let nicely_ordered = fmt_arg_index_and_ty
.clone()
.is_sorted_by(|(i, _), (j, _)| (i < j).then_some(Ordering::Less));
Copy link
Member

Choose a reason for hiding this comment

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

Is that different from .is_sorted_by_key(|(i, _)| i)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, we need to catch permuted as well as repeated elements. Yours is true on 0 0 which would result in an incorrect expansion.

Copy link
Member

Choose a reason for hiding this comment

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

That's very subtle. It would be great if you could add a comment. And maybe find a simpler (if more verbose) way to express this. I personally cannot decode what's going on here in a reasonable amount of time.

Copy link
Member Author

@dtolnay dtolnay Jan 15, 2022

Choose a reason for hiding this comment

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

array_windows (#75027) may be clearer for this. The sequence has no indices out of order or repeated if: for every adjacent pair of elements, the first one's index is less than the second one's index.

https://github.com/rust-lang/rust/blob/97e68feec37be1aacb93ae3c9fb0761b9d16467e/compiler/rustc_builtin_macros/src/format.rs#L772-L779

// The following Iterator<Item = (usize, &ArgumentType)> has one item
// per element of our output slice, identifying the index of which
// element of original_args it's passing, and that argument's type.
let fmt_arg_index_and_ty = self
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to use a SmallVec for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

SmallVec<(usize, &ArgumentType)> instead of Iterator<Item = (usize, &ArgumentType)>? I'm not sure why that would be better. Do you think any of the code below would get clearer? We only ever need to iterate it forward.

Copy link
Member

Choose a reason for hiding this comment

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

The sequence would be "cached" on the stack instead of evaluated twice. But I'm fine with keeping this as an iterator either.

@dtolnay
Copy link
Member Author

dtolnay commented Jan 5, 2022

Is there anything that could go subtly wrong with this? Like hygiene or borrow checking? Should we ask someone with more experience in macro expansion to take a look too?

From what I know about macros, I do not expect any difference in hygiene or borrow checking between the two expansions.

The only other thing to be mindful of is const promotion. The kind of thing in the following snippet -- even though both macros nominally appear equivalent, only one of them works when a promotion is required. However in the PR we are only going in the direction of can't promote ⟶ can promote so nothing would break, and conversely in format_args nothing relies on promotion anyway because the relevant argument of core::fmt::Arguments::new_v1 is args: &'a [ArgumentV1<'a>], not &'static [ArgumentV1<'a>], so there isn't any case of new code that would work after the PR that didn't work before the PR.

macro_rules! a {
    ($e:expr) => {
        match ($e,) {
            (e,) => &[e],
        }
    };
}

macro_rules! b {
    ($e:expr) => {
        &[$e]
    };
}

fn main() {
    let _: &'static [i32] = a!(1); // fail
    let _: &'static [i32] = b!(1); // ok
}
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:4:22
   |
4  |             (e,) => &[e],
   |                      ^^-
   |                      | |
   |                      | temporary value is freed at the end of this statement
   |                      creates a temporary which is freed while still in use
...
16 |     let _: &'static [i32] = a!(1); // fail
   |            --------------   ----- in this macro invocation
   |            |
   |            type annotation requires that borrow lasts for `'static`

@dtolnay
Copy link
Member Author

dtolnay commented Jan 5, 2022

…, so there isn't any case of new code that would work after the PR that didn't work before the PR.

To elaborate slightly: if core::fmt::ArgumentV1::new were made const fn in the future then this PR would make a difference. It would mean that the following would work whereas it wouldn't before:

let _: core::fmt::Arguments<'static> = format_args!("{}", 1);

where the formatted values are all consts.

Today ArgumentV1::new is not const so the PR doesn't make any code newly compile.

@bors
Copy link
Contributor

bors commented Jan 5, 2022

☀️ Try build successful - checks-actions
Build commit: 3a6b09020f099db8b66b764a71e2ef5608e6c4ea (3a6b09020f099db8b66b764a71e2ef5608e6c4ea)

@rust-timer
Copy link
Collaborator

Queued 3a6b09020f099db8b66b764a71e2ef5608e6c4ea with parent 181e915, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (3a6b09020f099db8b66b764a71e2ef5608e6c4ea): comparison url.

Summary: This change led to very large relevant mixed results 🤷 in compiler performance.

  • Moderate improvement in instruction counts (up to -2.0% on full builds of cranelift-codegen)
  • Very large regression in instruction counts (up to 7.4% on full builds of keccak)

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR led to changes in compiler perf.

Next Steps: If you can justify the regressions found in this try perf run, please indicate this with @rustbot label: +perf-regression-triaged along with sufficient written justification. If you cannot justify the regressions please fix the regressions and do another perf run. If the next run shows neutral or positive results, the label will be automatically removed.

@bors rollup=never
@rustbot label: +S-waiting-on-review -S-waiting-on-perf +perf-regression

@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@bors
Copy link
Contributor

bors commented Jan 20, 2022

⌛ Testing commit 7ee21e3 with merge 1513d8ff8709dc2258a37f7e184e4d4130a94db3...

@bors
Copy link
Contributor

bors commented Jan 20, 2022

💥 Test timed out

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jan 20, 2022
@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@dtolnay
Copy link
Member Author

dtolnay commented Jan 20, 2022

@bors retry

@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-review Status: Awaiting review from the assignee but also interested parties. labels Jan 20, 2022
@dtolnay
Copy link
Member Author

dtolnay commented Jan 20, 2022

The "x86_64-msvc-1" job timed out during "Testing rustc_macros stage1 (x86_64-pc-windows-msvc -> x86_64-pc-windows-msvc)". All other jobs succeeded. Gonna gamble that this is an infra glitch and not an msvc-specific issue with this PR. We'll find out after the next round.

@bors
Copy link
Contributor

bors commented Jan 20, 2022

⌛ Testing commit 7ee21e3 with merge 77a8c17aa7dd1ffeadf8a39e118b35ead50ec940...

@pietroalbini
Copy link
Member

@bors retry

Yielding priority to 1.58.1 security point release.

@rust-log-analyzer
Copy link
Collaborator

A job failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)

@bors
Copy link
Contributor

bors commented Jan 21, 2022

⌛ Testing commit 7ee21e3 with merge 0bcacb3...

@bors
Copy link
Contributor

bors commented Jan 21, 2022

☀️ Test successful - checks-actions
Approved by: Mark-Simulacrum
Pushing 0bcacb3 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Jan 21, 2022
@bors bors merged commit 0bcacb3 into rust-lang:master Jan 21, 2022
@rustbot rustbot added this to the 1.60.0 milestone Jan 21, 2022
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (0bcacb3): comparison url.

Summary: This change led to large relevant mixed results 🤷 in compiler performance.

  • Large improvement in instruction counts (up to -2.2% on full builds of cranelift-codegen check)
  • Large regression in instruction counts (up to 3.0% on full builds of html5ever opt)

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

Next Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression-triaged along with sufficient written justification. If you cannot justify the regressions please open an issue or create a new PR that fixes the regressions, add a comment linking to the newly created issue or PR, and then add the perf-regression-triaged label to this PR.

@rustbot label: +perf-regression

@pnkfelix
Copy link
Member

the latest performance report nearly exactly matches the predicted performance and @Mark-Simulacrum had a great analysis for where that came from.

@rustbot label: +perf-regression-triaged

flip1995 pushed a commit to flip1995/rust that referenced this pull request Jan 27, 2022
Emit simpler code from format_args

I made this PR so that `cargo expand` dumps a less overwhelming amount of formatting-related code.

<br>

`println!("rust")` **Before:**

```rust
{
    ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"],
                                                     &match () {
                                                          _args => [],
                                                      }));
};
```

**After:**

```rust
{ ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };
```

`println!("{}", x)` **Before:**

```rust
{
    ::std::io::_print(::core::fmt::Arguments::new_v1(
        &["", "\n"],
        &match (&x,) {
            _args => [::core::fmt::ArgumentV1::new(
                _args.0,
                ::core::fmt::Display::fmt,
            )],
        },
    ));
};
```

**After:**

```rust
{
    ::std::io::_print(::core::fmt::Arguments::new_v1(
        &["", "\n"],
        &[::core::fmt::ArgumentV1::new(&x, ::core::fmt::Display::fmt)],
    ));
};
```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jan 29, 2022
Rename _args -> args in format_args expansion

As observed in rust-lang#91359 (comment), prior to that PR this variable was sometimes never used, such as in the case of:

```rust
println!("");

// used to expand to:
::std::io::_print(
    ::core::fmt::Arguments::new_v1(
        &["\n"],
        &match () {
            _args => [],
        },
    ),
);
```

so the leading underscore in `_args` was used to suppress an unused variable lint. However after rust-lang#91359 the variable is always used when present, as the unused case would instead expand to:

```rust
::std::io::_print(::core::fmt::Arguments::new_v1(&["\n"], &[]));
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-fmt Area: std::fmt merged-by-bors This PR was explicitly merged by bors. perf-regression Performance regression. perf-regression-triaged The performance regression has been triaged. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet