Skip to content

Const-eval errors in dead functions are optimization-dependent #107503

Closed

Description

@CAD97

I tried this code: [playground]

#![feature(inline_const)]

pub fn cause_late_error<T>() {
    const {
        panic!()
    };
}

fn main() {
    let a = 1;
    let b = 2;
    if a + b == 5 {
        cause_late_error::<()>();
    }
}

I expected to see this happen:

error[E0080]: evaluation of `cause_late_error::<()>::{constant#0}` failed
 --> src/main.rs:7:9
  |
7 |         panic!()
  |         ^^^^^^^^ the evaluated program panicked at 'explicit panic', src/main.rs:7:9
  |
  = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)

note: the above error was encountered while instantiating `fn cause_late_error::<()>`
  --> src/main.rs:13:9
   |
13 |         cause_late_error::<()>();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^

Instead, this happened: it compiled without any warnings.

Meta

Playground nightly, 1.69.0-nightly (2023-01-30 001a77f)

Related

Potential fix:

Activity

  1. CAD97 commented on Jan 31, 2023

    @CAD97
    ContributorAuthor

    @rustbot modify labels +T-lang

  2. joshtriplett commented on Jan 31, 2023

    @joshtriplett
    Member

    let _: c_void = screaming.into();

    Ahem. Thank you for this most illuminating and horrifying example.

  3. oskgo commented on Jan 31, 2023

    @oskgo
    Contributor

    On a related note, there's also this following code that compiles in release mode but fails to compile in debug mode: [playground]

    #![feature(inline_const)]
    
    #[inline(always)]
    fn falser() -> bool {
        false
    }
    
    fn has_late_error<T>() {
        const {
            panic!()
        };
    }
    
    fn main() {
        if falser() {
            has_late_error::<()>();
        }
    }

    Maybe it makes more sense to track inline_const's interactions with optimizations as a whole?

  4. changed the title [-]Whether panicking in inline_const causes an error a depends on constant propagation[/-] [+]Whether panicking in inline_const causes an error a depends on optimization[/+] on Jan 31, 2023
  5. changed the title [-]Whether panicking in inline_const causes an error a depends on optimization[/-] [+]Whether panicking in inline_const causes an error depends on optimization[/+] on Jan 31, 2023
  6. RalfJung commented on Jan 31, 2023

    @RalfJung
    Member

    To be clear, this has nothing to do with inline_const, right? Associated consts show the same behavior?

  7. CAD97 commented on Jan 31, 2023

    @CAD97
    ContributorAuthor

    I believe so, yes; any post-mono error should work. It's just significantly easier to show with panicking inline const than any other post-mono error, and much more accessible.

    I believe this just results from cause_late_error::<()> being determined dead by MIR analysis and thus not monomorphized. Monomorphizing all mentioned names (enough to flush middleend post-mono errors) should fundamentally resolve the issue of constant evaluation errors disappearing because of MIR-level dead code elimination.

  8. joshtriplett commented on Jan 31, 2023

    @joshtriplett
    Member

    @RalfJung Is it possible to produce a similarly horrifying example as #107503 (comment) that compiles on stable today, and is optimization-dependent and DCA dependent?

  9. RalfJung commented on Jan 31, 2023

    @RalfJung
    Member

    @joshtriplett yes. It's not even that hard.

    pub fn cause_late_error<T>() {
        struct S;
        impl S {
            const C: () = panic!();
        }
    
        S::C;
    }
    
    fn main() {
        let a = 1;
        let b = 2;
        if a + b == 5 { // changing 5 to 3 makes compilation fail
            cause_late_error::<()>();
        }
    }
  10. RalfJung commented on Jan 31, 2023

    @RalfJung
    Member

    And of course that also works for @oskgo's example:

    #[inline(always)]
    fn falser() -> bool {
        false
    }
    
    pub fn has_late_error<T>() {
        struct S;
        impl S {
            const C: () = panic!();
        }
    
        S::C;
    }
    
    fn main() {
        if falser() {
            has_late_error::<()>();
        }
    }

    Compiles in release mode but not in debug mode.

  11. zirconium-n commented on Feb 1, 2023

    @zirconium-n
    Contributor

    Sorry if this sounds rude, but could we just issue a crater run and push a breaking change at this point? Panic in consteval is not something stabilized for too long. We can recognize this1 as a design mistake. Whether the code depends on something other than #[cfg(debug_assertions)] is a bit too far for me...

    Footnotes

    1. Unused associated const doesn't not evaluate. ↩

  12. added
    F-inline_constInline constants (aka: const blocks, const expressions, anonymous constants)
    on Mar 5, 2023
  13. RalfJung commented on May 17, 2023

    @RalfJung
    Member

    This is the same underlying issue as #106617, so we probably should close one of these in favor of the other (or open a new issue to track the collected problems). We don't monomorphize dead code, which means

    • since optimizations affect which code is considered dead, we do opt-dependent monomorphization
    • since some errors are only raised during monomorphization, we have missing errors in dead code and opt-dependent errors

    All of this affects stable Rust with associated consts.

  14. 54 remaining items

  15. RalfJung commented on Mar 20, 2024

    @RalfJung
    Member

    Regarding whether this should block the stabilization of inline const, we tentatively felt that it should not, but we wanted to discuss that with more of the team before resolving the #104087 (comment) on #104087.

    Ah, reading that concern now gives me flashbacks. ;)

    That concern makes a difference between "Optimization-dependent errors" and "Errors in unused code". It turns out those cannot be disentangled, as basically all notions of "used" that exist in rustc depend on optimization levels. (This was not the case back when the comment was written, but is true ever since #[inline] is automatically inferred and that inference depends on optimization levels.)

  16. saethlin commented on Mar 20, 2024

    @saethlin
    Member

    The only way I can imagine making cross_crate_inlinable not optimization-dependent is by analyzing unoptimized MIR and doing the analysis at all optimization levels. If we do that, I am confident that we will regress both our compile time and our code generation quality (we could of course delete it altogether to avoid regressing compile time). But such a change does nothing to fix the behavior of #[inline], which is the root (heh) cause here. I do not think that library authors would not be any more troubled by not getting errors due to cross_crate_inlinable than they would be by realizing that their inline-const assert that should have prevented shipping a bug did not do its job because they followed a clippy lint to put #[inline] on all their public items.

    On the other hand, the fact that this bug has been shipping on nightly for quite some time and nobody has complained perhaps indicates that the impact is not too widespread.

    I am very happy to see anyone trying to untangle the reachability/MIR collection code.

  17. RalfJung commented on Mar 20, 2024

    @RalfJung
    Member

    I do not think that library authors would not be any more troubled by not getting errors

    That was three "not" in a row, you lost me here. ;)

    I am very happy to see anyone trying to untangle the reachability/MIR collection code.

    I gave up the hope of trying to simplify it, but maybe I can at least document it a bit better. ;)

  18. saethlin commented on Mar 21, 2024

    @saethlin
    Member

    That was three "not" in a row, you lost me here. ;)

    I think library authors will be equally confused by cross_crate_inlinable implications as they will be by plain old #[inline] possibly hiding errors.

    (yeah wow that sentence is a mess)

  19. RalfJung commented on Mar 21, 2024

    @RalfJung
    Member

    I have opened #122814 for the collection root issue @tmiasko discovered.

  20. workingjubilee commented on Mar 21, 2024

    @workingjubilee
    Member

    All uses in libraries I have seen are one of:

    • top-level items
    • only meant to stop you from monomorphizing code with the "wrong" type

    and I have certainly seen a few particularly... dank... usages of these kinds of "const assertions".

  21. RalfJung commented on Mar 21, 2024

    @RalfJung
    Member

    To be clear, the only blessed pattern libs may use is that if a const is directly used inside some function, then whenever that function executes with a given choice of its generic parameters, the const succeeded evaluating. So we do not guarantee that the code in the OP will fail to build!

    The point of this issue is that we don't want optimization-level-dependent behavior. But which functions we walk and evaluate consts for is still not a stable guarantee -- except that any function that actually gets executed has been walked and had its consts evaluated.

  22. workingjubilee commented on Mar 21, 2024

    @workingjubilee
    Member

    yeah, that seems fine? weird but fine.

  23. workingjubilee commented on Mar 21, 2024

    @workingjubilee
    Member

    the OP's code fails to build in both debug and release now. huh. weird.

  24. RalfJung commented on Mar 21, 2024

    @RalfJung
    Member
  25. RalfJung commented on Mar 21, 2024

    @RalfJung
    Member

    Hm, I wonder if inlining can also cause trouble here... if a call like f::<i32>() gets inlined, previously generic consts inside that body become monomorphic, and so if they ever get evaluated by later MIR passes then maybe that is evaluation that would not have happened otherwise (if this is all in a function that never gets collected)?

    I'm beginning to wonder if MIR passes need a way to eval consts such that even if it fails, no error is emitted.

  26. added a commit that references this issue on Mar 21, 2024
  27. RalfJung commented on Mar 21, 2024

    @RalfJung
    Member

    (After some experimentation with inlining) I think we aren't even computing optimized_mir for functions that do not participate in collection... in that case we're good. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-bugCategory: This is a bug.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions