Repository: rust
Issue: Quadratic code size due to combination of ? and Drop for linear code
Labels: T-compiler,C-bug,I-heavy,C-optimization

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->

Hi,

first: Sorry if this is a duplicate. I tried, but did not find anything.

There is a bug report for x11rb about code size. Some macro caused 111 KiB of code to be emited: https://github.com/psychon/x11rb/issues/912

<details>
<summary>
I tried to produce a self-contained example of what is going on and here is what I came up with the following
</summary>

 [Playground link](https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=b2d37ff014e7c0685b1d9b0193bbb52e)
```rust
macro_rules! the_macro {
    {
        $cookie_name:ident { $($field_name:ident,)* }
    } => {
        struct $cookie_name {
            $($field_name: support::Cookie,)*
        }
        
        #[inline(never)]
        fn send_requests() -> Result<$cookie_name, support::Error> {
            Ok($cookie_name {
                $($field_name: support::send_request()?,)*
            })
        }
    }
}

the_macro!{Cookies { a, b, c, d, e, } }

fn main() {
    let _ = send_requests();
}

mod support {
    #[inline(never)]
    pub fn send_request() -> Result<Cookie, Error> {
        // Some code to trick the optimizer into not optimising everything away
        if std::env::args().next().is_some() {
            Ok(Cookie(42))
        } else {
            Err(Error)
        }
    }
    
    pub struct Error;
    
    pub struct Cookie(u16);
    impl Drop for Cookie {
        #[inline(never)]
        fn drop(&mut self) {
            println!("{}", self.0);
        }
    }
}
```
(`#[inline(never)]` just exists so that I can read the resulting assembly; the original code does not use inline hints, but instead the functions are big enough not to be inlined. I'm not sure whether `drop()` of the `Cookie` perhaps ends up being inlined and being part of the huge code size.)
</details>

This code uses a macro, so just to make it clear what the problem is, here is the relevant function after macro expansion:
```rust
#[inline(never)]
fn send_requests() -> Result<Cookies, support::Error> {
    Ok(Cookies {
            a: support::send_request()?,
            b: support::send_request()?,
            c: support::send_request()?,
            d: support::send_request()?,
            e: support::send_request()?,
        })
}
```
I expected to see this happen: Linear code size (= small code)

Instead, this happened: This results in quadratic code size due to the `drop` calls.

Looking at the generated ASM in release mode and trying to translate this back into something rust-y, the code looks like this:
```rust
fn send_requests() -> Result<Cookies, support::Error> {
    let a = match support_send_request() {
       Err(e) => return Err(e);
       Ok(a) => a;
    };
    let b = match support_send_request() {
       Err(e) => { drop(a); return Err(e) };
       Ok(b) => b;
    };
    let c = match support_send_request() {
       Err(e) => { drop(a); drop(b); return Err(e) };
       Ok(c) => c;
    };
    let d = match support_send_request() {
       Err(e) => { drop(a); drop(b); drop(c); return Err(e) };
       Ok(d) => d;
    };
    let e = match support_send_request() {
       Err(e) => { drop(a); drop(b); drop(c); drop(d); return Err(e) };
       Ok(e) => e;
    };
    Ok(Cookies { a, b, c, d, e })
}
```

The actual code where this was observed has not just 5 cases (`a, b, c, d, e`) but 59 ones. Here, the quadratic behaviour starts to hurt a lot. I counted 1709 calls to functions with `drop_in_place` in their name.

### Meta
<!--
If you're using the stable version of the compiler, you should also check if the
bug also exists in the beta or nightly versions.
-->

`rustc --version --verbose`: Uhm... I tried this on the playground. But I can also reproduce this with my ancient version locally:
```
rustc 1.70.0
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.70.0
LLVM version: 16.0.6
```