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

Out of storage use of local for temporary caused by label break #104736

Closed
JakobDegen opened this issue Nov 22, 2022 · 21 comments · Fixed by #119077
Closed

Out of storage use of local for temporary caused by label break #104736

JakobDegen opened this issue Nov 22, 2022 · 21 comments · Fixed by #119077
Assignees
Labels
-Zvalidate-mir Unstable option: MIR validation A-mir Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html C-bug Category: This is a bug. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. glacier ICE tracked in rust-lang/glacier. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@JakobDegen
Copy link
Contributor

JakobDegen commented Nov 22, 2022

Code

Playground. Needs release

struct A;
struct B;

impl Drop for A {
    fn drop(&mut self) {}
}
impl Drop for B {
    fn drop(&mut self) {}
}

#[inline(always)]
fn no_unwind() {}

fn weird_temporary(a: A, b: B, nothing: ((), (), ()), x: bool) -> ((), (), ()) {
    'scope: {
        (
            {
                let _z = b;
                if x {
                    break 'scope nothing;
                }
            },
            match { a } {
                _ => (),
            },
            no_unwind(),
        )
    }
}

Meta

Version: 0d5573e and all other recent versions

Explanation

This causes an ICE in the MIR validator. The reason is an out of storage use of the local that is the temporary for { a }. This local has a couple interesting properties. The most important of these is that MIR building thinks that it may need to be dropped while unwinding in two scenarios:

  1. If the drop of _z unwinds.
  2. If no_unwind() unwinds.

Neither of these are actually possible.

First, drop elaboration comes along, sees that only the second of these is possible. It reacts to this by inserting a drop flag for the local. Then, inlining comes along and inlines no_unwind(). The potential unwind edge is now gone.

The result is that the cleanup drop is actually completely unreachable, and this ends up causing an ICE.

Curiously, this does not reproduce if one replaces break 'scope nothing; with return nothing;. In the case of the return, MIR building correctly realizes that the temporary has not been created yet and hence does not need to be dropped. The best solution to this is likely to make the break do the same thing - I'm not sure how difficult that is.

@JakobDegen JakobDegen added C-bug Category: This is a bug. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 22, 2022
@rustbot rustbot added A-mir Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html F-try_blocks `#![feature(try_blocks)]` labels Nov 22, 2022
@JakobDegen
Copy link
Contributor Author

This regressed at some point in the last 90 days, so needs a bisect. Someone should also test this under -Zvalidate-mir and dump the output into the issue. I have a feeling this is due to MIR building changes that were made to support let-else and if-let (could be wrong though)

cc @jsgf who found this

@dtolnay dtolnay added the E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc label Nov 22, 2022
@Noratrieb
Copy link
Member

Noratrieb commented Nov 22, 2022

searched nightlies: from nightly-2022-01-01 to nightly-2022-11-22
regressed nightly: nightly-2022-10-23
searched commit range: 5c8bff7...6e95b6d
regressed commit: eecde58

bisected with cargo-bisect-rustc v0.6.4

Host triple: x86_64-unknown-linux-gnu
Reproduce with:

cargo bisect-rustc --access github --script ./script

So this was regressed by #103172

@JakobDegen
Copy link
Contributor Author

Interesting. cc @pcwalton but @rustbot claim

@dtolnay dtolnay removed the E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc label Nov 22, 2022
@JakobDegen
Copy link
Contributor Author

JakobDegen commented Nov 27, 2022

I've dug into this a bit. It turns out that the path this takes to an ICE is complicated and rather subtle, so I'm not going to explain it in this message (if you'd like to know, feel free to ping me on Zulip). Instead, I'll just explain how this can be fixed. Two options:

  1. Disable the liveness check in the MIR validator. That check already has a comment:
    // We check that the local is live whenever it is used. Technically, violating this
    // restriction is only UB and not actually indicative of not well-formed MIR. This means
    // that an optimization which turns MIR that already has UB into MIR that fails this
    // check is not necessarily wrong. However, we have no such optimizations at the moment,
    // and so we include this check anyway to help us catch bugs. If you happen to write an
    // optimization that might cause this to incorrectly fire, feel free to remove this
    // check.
    
    That is (almost) what's going on here. More accurately, for ReasonsTM inlining does some things that creates dead code which contains this kind of an "out of liveness" use of a local. Removing the check would fix the ICE, but I don't think we should remove that check if we can avoid it, because it catches a lot of bugs.
  2. Stop generating the MIR that is the underlying cause of this behavior. This does not actually ICE in the validator until after inlining, but the root cause is in MIR building. The rest of this message will be my explanation of the root cause.

Code:

#![feature(try_blocks)]

struct Response {
    bookmarks: String,
    continue_after: String,
}

#[inline(never)]
fn new_string() -> String {
    String::new()
}

#[inline(never)]
fn def() -> Response {
    Response {
        bookmarks: String::new(),
        continue_after: String::new(),
    }
}

fn format_response(page: Result<String, String>) -> Result<Response, String> {
    try {
        Response {
            bookmarks: new_string(),
            continue_after: page?,
            ..def()
        }
    }
}

fn format_response_no_try(page: Result<String, String>) -> Result<Response, String> {
    Ok(
        Response {
            bookmarks: new_string(),
            continue_after: page?,
            ..def()
        }
    )
}

Invocation on a stage 1 build of 80a9646:

$ rustc +master -Copt-level=3 --crate-type lib --edition 2021 -Zvalidate-mir -Zdump-mir=all -Zdump-mir-graphviz test.rs
$ dot -Tpng mir_dump/test.format_response.001-000.built.after.dot -o built.png
$ dot -Tpng mir_dump/test.format_response_no_try.001-000.built.after.dot -o built_no_try.png

Yields built.png and built_no_try.png.

Despite the images looking different, the structure is actually quite similar. Basic blocks 6 plays the same role in both versions of the function. Basic blocks 17 in built.png corresponds to basic blocks 20 in built_no_try.png. In both functions, _12 is the Response returned by def().

What we're interested in is the unwind paths. Bb 6 has the same unwind path (ie same sequence of locals dropped) in both versions of the function. However, bb 17/20 have different unwind paths. For some reason, bb17's unwind path in built.png includes a drop of _12, but there's no good reason for it to do that. def() hasn't been called yet. Either this means that the THIR we're generating is weird or there's something weird going on in MIR building. I haven't looked at what it is, but that is what needs to be fixed. (This is probably also observable in the form of a weird borrowck error).

@JakobDegen
Copy link
Contributor Author

Worth noting also that I have no idea why this started to ICE as a result of Patrick's PR (maybe an extra MIR validation call for some reason?). In any case, the MIR building behavior does not seem to be a regression. It's been this way since at least 5c8bff7 .

Unassigning myself because I have no time to dig into this

@JakobDegen JakobDegen removed their assignment Nov 27, 2022
@JakobDegen
Copy link
Contributor Author

I think I have enough context here to be able to

@rustbot label +E-mentor +E-hard

@rustbot rustbot added E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. labels Nov 27, 2022
@b-naber
Copy link
Contributor

b-naber commented Dec 4, 2022

Seems that build_exit_tree is wrong for the DropTree of a breakable scope, more specifically linking the exit drop tree to the unwind drop tree. Not sure if I can fix this on my own, I'll contact you on zulip if I'm stuck, @JakobDegen .

@rustbot claim

@b-naber
Copy link
Contributor

b-naber commented Dec 5, 2022

@JakobDegen Actually can you explain why this ICEs?

#![feature(try_blocks)]

struct Response {
    bookmarks: String,
    continue_after: String,
}

#[inline(never)]
fn def() -> Response {
    Response {
        bookmarks: String::new(),
        continue_after: String::new(),
    }
}

fn format_response(page: Result<String, String>) -> Result<Response, String> {
    try {
        Response {
            continue_after: page?,
            ..def()
        }
    }
}

In this program we also have a drop of the local _11) that contains the def call result in the unwind path of the Break branch (from bb15), even though it was never called on that branch, but this program does not ICE.

using-default-try

@JakobDegen
Copy link
Contributor Author

@b-naber this might turn into quite an essay, but more time and shorter letters and all that. I think the better question isn't why that version doesn't ICE, but why the original version does. There are two important things to remember:

  1. The semantics of MIR are very clear that before drop elaboration, a Drop terminator is a conditional drop and actually means "drop if initialized." In other words, for some kinds of spurious drops, it's not wrong in the sense of being a miscompilation for MIR building to insert them, since they won't be executed as long as the place is uninitialized.
  2. The MIR validator checks that all locals are only used within their "storage liveness range." Using a local outside of its storage liveness is UB, but that is only true dynamically.

What this all means is there are two ways that we can get ICEs from code that is not a miscompilation. This pre-drop elaboration MIR will ICE in the validator:

// _5 is not initialized here, so this drop does not actually execute at runtime,
// but still it is an ICE because of a use outside of storage liveness
Drop(_5);

StorageLive(_5);
_5 = something;
other_thing = _5;
StorageDead(_5);

And similarly, this will ICE after drop elaboration:

if false {
    // This drop now does run "unconditionally" in the sense of there being no more
    // initializedness check, but of course it does not *actually* run because of `if false`
    Drop(_5);
}

StorageLive(_5);
_5 = something;
other_thing = _5;
StorageDead(_5);

Let's take a look at how this comes up in the format_response example from my code (you'll want to open the linked diagrams in different tabs). This is the MIR immediately before drop elaboration (003-002.ElaborateDrops.before): before_elab.png.

The bad drop that we're looking at here is now in bb 21. It is "reachable" via a path like 5 -> 6 -> 12 -> 21 although it should not be (that is the bug). However, this does not immediately ICE, because the validator sees that storage could be live there, via a path like 7 -> 8 -> 9 -> 10 -> 20 -> 21.

Drop elaboration now runs and after some simplification, we get this (004-003.SimplifyCfg-elaborate-drops.after): after_elab.png. The important thing to note is that _13 is a drop flag that has been inserted for _12. Every drop(_12) was replaced by a if _13 { drop(_12) }.

The bad drop we care about is still there, at bb 17. We can also still find both the paths I mentioned above. The path on which the drop is reachable even though it shouldn't be is there in the form of 5 -> 6 -> 18 -> 17. Note that the Drop will never actually be executed on this path, because _13 is always false. However, this also still does not ICE, because dataflow reports that storage for _12 might still be live via a path like 3 -> 7 -> 18 -> 17.

What actually ends up setting off the ICE is inlining (005-006.Inline.after): after_inline.png

The MIR has changed a lot, but we can do our best to still keep track of all the things we identified above. The bad drop is now at bb 14. The from_residual call in _5 has been inlined, and so the path on which the drop should not be reachable is now 5 -> 15 -> 14. Note that it is still the case that _13 is always false on this path (and so the drop does not run).

If we try and find the other path we cared about, we run into trouble. bb7 has turned into bb6 with from_output inlined. We would have expected the 7 -> 18 edge to turn into a 6 -> 15 edge, but it's not there. The reason why is obvious: There is no unwind edge from bb6 at all. Indeed, if we go back to the post drop elaboration MIR, we might note that the 7 -> 18 edge was "fake" in some sense all along - from_output can't actually unwind. Inlining is just the thing that gives us enough information to notice this.

This is what sets off the ICE. The validator now detects that _12 cannot have live storage in bb14, and assumes this is a bug. It is of course not actually a miscompilation, because _13 is always false in bb15.

So why doesn't this happen in your example? Well, we can take a look (003-002.ElaborateDrops.before): new_before_elab.png. The important change here is that a part of _11, the local we care about, is moved out of in bb7. For reasons that I won't get into right now, this causes drop elaboration to be slightly smarter. It ends up splitting the drop of _11 up into a number of parts, and it turns out that none of these parts exhibit the problematic behavior that we saw. We end up with this (004-003.SimplifyCfg-elaborate-drops.after): new_after_elab.png. There's now no path from bb4 to a drop of _11, and so the bug has accidentally disappeared.

@b-naber
Copy link
Contributor

b-naber commented Dec 6, 2022

Thank you for the great answer.

I believe the cause of the bug is that we assign a wrong region scope to the try block during thir construction. We create some new Node scope for the try block, but I believe we should assign the scope of the next outer breakable scope to the scope of the try block. I think it should be fairly straightforward to implement this just by maintaining a stack of breakable scopes when constructing thir.

I haven't implemented this yet, but have used a hack where in break_scope I use the scope that I believe to be correct (I'm using break_index - 1 here) just to check whether our example still ICEs and it doesn't. We get the following cfg:

ice-mir-try

Does that fix sound correct to you?

EDIT: Actually I think this might require a more nuanced solution, we only need the next outer breakable scope when we actually encounter any break statements, more specifically inside break_scope where we add the drops for build_exit_tree (the one used by in_breakable_scope of the try-block). I would assume that simply using the next outer breakable scope would lead to other problems.

@JakobDegen
Copy link
Contributor Author

The CFG looks like what we should be generating, and what you said in the edit sounds vaguely correct. What would be really helpful for debugging this is if we had a way to get a proper tree-like view of the THIR, to determine if what we're generating there is what we expect. Unfortunately -Zunpretty=thir-tree is nearly useless

@b-naber
Copy link
Contributor

b-naber commented Dec 9, 2022

That approach does not work, since if we e.g. have multiple <expr>? in a try block we drop everything after the first one.

I'm fairly certain that there actually isn't anything wrong with either thir construction or the way we create drops and the unwind paths. I guess one could argue that we could shrink the lifetime scope of an unused "struct base" in thir construction, but I believe the proper fix here is to actually never construct the place for that base in the first place if all fields have been provided. If we only create this PlaceBuilder in case the number of defined fields is larger than the number of provided fields, then we never get the drop problems in the first place. I don't see anything wrong with doing that and the test set passes with that change. Do you see anything wrong with this or do you maybe have an alternate fix?

@JakobDegen
Copy link
Contributor Author

Unless I'm misunderstanding something, that can't work because this still ICEs:

#![feature(try_blocks)]

struct Response {
    bookmarks: String,
    continue_after: String,
    f: String,
}

#[inline(never)]
fn new_string() -> String {
    String::new()
}

#[inline(never)]
fn def() -> Response {
    Response {
        bookmarks: String::new(),
        continue_after: String::new(),
        f: String::new(),
    }
}

fn format_response(page: Result<String, String>) -> Result<Response, String> {
    try {
        Response {
            bookmarks: new_string(),
            continue_after: page?,
            ..def()
        }
    }
}

@JakobDegen
Copy link
Contributor Author

JakobDegen commented Dec 10, 2022

You are right though, this is unrelated to try blocks. Still reproduces as:

struct Response {
    f: String,
}

#[inline(always)]
fn ident(a: Response) -> Response {
    a
}

fn format_response(a: Response, b: Response, c: String, d: String, x: bool) -> Response {
    'scope: {
        ident(Response {
            f: {
                let _z = d;
                if x {
                    c
                } else {
                    break 'scope b;
                }
            },
            ..{ a }
        })
    }
}

built.png. The bad drops are of _11.

@JakobDegen
Copy link
Contributor Author

@rustbot label -F-try_blocks

@rustbot rustbot removed the F-try_blocks `#![feature(try_blocks)]` label Dec 10, 2022
@JakobDegen
Copy link
Contributor Author

JakobDegen commented Dec 10, 2022

I did a bunch more investigation and have updated the main comment with a much better reproducer.

@JakobDegen JakobDegen changed the title ICE with try blocks and out-of-storage use of local Out of storage use of local for temporary caused by label break Dec 10, 2022
@b-naber
Copy link
Contributor

b-naber commented Dec 10, 2022

Thanks for investigating further, I'll try to look more into your updated problem statement (might take me a bit though since I'm not that familiar with mir optimization).

What I find really weird though is that your first example and the new one only ICE with crate-type=lib. Why is that?

@JakobDegen
Copy link
Contributor Author

(might take me a bit though since I'm not that familiar with mir optimization)

I don't think it's an optimization issue, it's still cleanly within MIR building.

What I find really weird though is that your first example and the new one only ICE with crate-type=lib. Why is that?

If it's in a binary rustc realizes that all the functions are dead and doesn't bother optimizing them (hence no ICE). If you actually call the function and force it to get codegened the ICE is back: playground

@b-naber
Copy link
Contributor

b-naber commented Dec 11, 2022

So I think the cause of this is the way in which we're caching the unwind drops, which is done here. If we ever encounter a scope corresponding to the scope of a label in this iteration: self.scopes.scopes[uncached_scope..=target], then we set the cached_unwind_block for that scope to the last cached drop at the time we encounter that scope in that iteration (in our example this is the drop corresponding to _10).

Later when building the exit tree for the break scope we call diverge_cleanup_target and find that cached drop idx, causing us to start our unwind path from that drop idx.

The problem is that those drops have a lifetime scope greater than the scope of the break scope (because they appear in the expression of the label block). I can't think of a fix that would fit into the current design, we would need some way to track which drops follow a certain break statement I would imagine and based on that decide whether to cache a drop for the break scope. I haven't had time to properly think this through though.

@Alexendoo
Copy link
Member

Following #107270 the example now needs -Zvalidate-mir to ICE

@matthiaskrgr
Copy link
Member

Updated stacktrace for findability #104736 (comment)

--edition=2021 -Zvalidate-mir -Zmir-opt-level=3 --crate-type lib -Zprint-mono-items=eager

error: internal compiler error: no errors encountered even though `delay_span_bug` issued

error: internal compiler error: broken MIR in Item(DefId(0:12 ~ a[98ad]::drop)) (after pass Inline) at bb11[0]:
                                use of local _10, which has no storage here
  --> a.rs:29:1
   |
29 | }
   | ^
   |
note: delayed at compiler/rustc_const_eval/src/transform/validate.rs:274:22
         0: <rustc_errors::HandlerInner>::emit_diagnostic
         1: <rustc_errors::Handler>::delay_span_bug::<rustc_span::span_encoding::Span, alloc::string::String>
         2: <rustc_const_eval::transform::validate::CfgChecker>::fail::<alloc::string::String>
         3: <rustc_const_eval::transform::validate::CfgChecker as rustc_middle::mir::visit::Visitor>::visit_terminator
         4: <rustc_const_eval::transform::validate::Validator as rustc_middle::mir::MirPass>::run_pass
         5: rustc_mir_transform::pass_manager::validate_body
         6: rustc_mir_transform::pass_manager::run_passes
         7: rustc_mir_transform::optimized_mir
         8: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::optimized_mir::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 8]>>
         9: <rustc_query_impl::query_impl::optimized_mir::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)>>::call_once
        10: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, rustc_middle::query::erase::Erased<[u8; 8]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        11: rustc_query_impl::query_impl::optimized_mir::get_query_non_incr::__rust_end_short_backtrace
        12: <rustc_middle::ty::context::TyCtxt>::instance_mir
        13: rustc_monomorphize::collector::collect_used_items
        14: rustc_monomorphize::collector::collect_items_rec
        15: rustc_data_structures::sync::par_for_each_in::<alloc::vec::Vec<rustc_middle::mir::mono::MonoItem>, rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}::{closure#0}>
        16: <rustc_session::session::Session>::time::<(), rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}>
        17: rustc_monomorphize::collector::collect_crate_mono_items
        18: rustc_monomorphize::partitioning::collect_and_partition_mono_items
        19: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::collect_and_partition_mono_items::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 24]>>
        20: <rustc_query_impl::query_impl::collect_and_partition_mono_items::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, ())>>::call_once
        21: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::SingleCache<rustc_middle::query::erase::Erased<[u8; 24]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        22: rustc_query_impl::query_impl::collect_and_partition_mono_items::get_query_non_incr::__rust_end_short_backtrace
        23: rustc_codegen_ssa::back::symbol_export::exported_symbols_provider_local
        24: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::exported_symbols::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 16]>>
        25: <rustc_query_impl::query_impl::exported_symbols::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, rustc_span::def_id::CrateNum)>>::call_once
        26: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_span::def_id::CrateNum, rustc_middle::query::erase::Erased<[u8; 16]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        27: rustc_query_impl::query_impl::exported_symbols::get_query_non_incr::__rust_end_short_backtrace
        28: <rustc_metadata::rmeta::encoder::EncodeContext>::encode_crate_root
        29: rustc_metadata::rmeta::encoder::encode_metadata_impl
        30: rustc_metadata::rmeta::encoder::encode_metadata
        31: rustc_metadata::fs::encode_and_write_metadata
        32: rustc_interface::passes::start_codegen
        33: <rustc_middle::ty::context::GlobalCtxt>::enter::<<rustc_interface::queries::Queries>::ongoing_codegen::{closure#0}, core::result::Result<alloc::boxed::Box<dyn core::any::Any>, rustc_span::ErrorGuaranteed>>
        34: <rustc_interface::interface::Compiler>::enter::<rustc_driver_impl::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_span::ErrorGuaranteed>>
        35: std::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>
        36: <<std::thread::Builder>::spawn_unchecked_<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#1} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
        37: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/alloc/src/boxed.rs:2007:9
        38: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/alloc/src/boxed.rs:2007:9
        39: std::sys::unix::thread::Thread::new::thread_start
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/std/src/sys/unix/thread.rs:108:17
        40: <unknown>
        41: <unknown>
  --> a.rs:29:1
   |
29 | }
   | ^

error: internal compiler error: broken MIR in Item(DefId(0:12 ~ a[98ad]::drop)) (before pass RemoveStorageMarkers) at bb11[0]:
                                use of local _10, which has no storage here
  --> a.rs:29:1
   |
29 | }
   | ^
   |
note: delayed at compiler/rustc_const_eval/src/transform/validate.rs:274:22
         0: <rustc_errors::HandlerInner>::emit_diagnostic
         1: <rustc_errors::Handler>::delay_span_bug::<rustc_span::span_encoding::Span, alloc::string::String>
         2: <rustc_const_eval::transform::validate::CfgChecker>::fail::<alloc::string::String>
         3: <rustc_const_eval::transform::validate::CfgChecker as rustc_middle::mir::visit::Visitor>::visit_terminator
         4: <rustc_const_eval::transform::validate::Validator as rustc_middle::mir::MirPass>::run_pass
         5: rustc_mir_transform::pass_manager::validate_body
         6: rustc_mir_transform::pass_manager::run_passes
         7: rustc_mir_transform::optimized_mir
         8: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::optimized_mir::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 8]>>
         9: <rustc_query_impl::query_impl::optimized_mir::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, rustc_span::def_id::DefId)>>::call_once
        10: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::DefaultCache<rustc_span::def_id::DefId, rustc_middle::query::erase::Erased<[u8; 8]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        11: rustc_query_impl::query_impl::optimized_mir::get_query_non_incr::__rust_end_short_backtrace
        12: <rustc_middle::ty::context::TyCtxt>::instance_mir
        13: rustc_monomorphize::collector::collect_used_items
        14: rustc_monomorphize::collector::collect_items_rec
        15: rustc_data_structures::sync::par_for_each_in::<alloc::vec::Vec<rustc_middle::mir::mono::MonoItem>, rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}::{closure#0}>
        16: <rustc_session::session::Session>::time::<(), rustc_monomorphize::collector::collect_crate_mono_items::{closure#1}>
        17: rustc_monomorphize::collector::collect_crate_mono_items
        18: rustc_monomorphize::partitioning::collect_and_partition_mono_items
        19: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::collect_and_partition_mono_items::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 24]>>
        20: <rustc_query_impl::query_impl::collect_and_partition_mono_items::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, ())>>::call_once
        21: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::SingleCache<rustc_middle::query::erase::Erased<[u8; 24]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        22: rustc_query_impl::query_impl::collect_and_partition_mono_items::get_query_non_incr::__rust_end_short_backtrace
        23: rustc_codegen_ssa::back::symbol_export::exported_symbols_provider_local
        24: rustc_query_impl::plumbing::__rust_begin_short_backtrace::<rustc_query_impl::query_impl::exported_symbols::dynamic_query::{closure#2}::{closure#0}, rustc_middle::query::erase::Erased<[u8; 16]>>
        25: <rustc_query_impl::query_impl::exported_symbols::dynamic_query::{closure#2} as core::ops::function::FnOnce<(rustc_middle::ty::context::TyCtxt, rustc_span::def_id::CrateNum)>>::call_once
        26: rustc_query_system::query::plumbing::try_execute_query::<rustc_query_impl::DynamicConfig<rustc_query_system::query::caches::VecCache<rustc_span::def_id::CrateNum, rustc_middle::query::erase::Erased<[u8; 16]>>, false, false, false>, rustc_query_impl::plumbing::QueryCtxt, false>
        27: rustc_query_impl::query_impl::exported_symbols::get_query_non_incr::__rust_end_short_backtrace
        28: <rustc_metadata::rmeta::encoder::EncodeContext>::encode_crate_root
        29: rustc_metadata::rmeta::encoder::encode_metadata_impl
        30: rustc_metadata::rmeta::encoder::encode_metadata
        31: rustc_metadata::fs::encode_and_write_metadata
        32: rustc_interface::passes::start_codegen
        33: <rustc_middle::ty::context::GlobalCtxt>::enter::<<rustc_interface::queries::Queries>::ongoing_codegen::{closure#0}, core::result::Result<alloc::boxed::Box<dyn core::any::Any>, rustc_span::ErrorGuaranteed>>
        34: <rustc_interface::interface::Compiler>::enter::<rustc_driver_impl::run_compiler::{closure#1}::{closure#2}, core::result::Result<core::option::Option<rustc_interface::queries::Linker>, rustc_span::ErrorGuaranteed>>
        35: std::sys_common::backtrace::__rust_begin_short_backtrace::<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>
        36: <<std::thread::Builder>::spawn_unchecked_<rustc_interface::util::run_in_thread_pool_with_globals<rustc_interface::interface::run_compiler<core::result::Result<(), rustc_span::ErrorGuaranteed>, rustc_driver_impl::run_compiler::{closure#1}>::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#0}::{closure#0}, core::result::Result<(), rustc_span::ErrorGuaranteed>>::{closure#1} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
        37: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/alloc/src/boxed.rs:2007:9
        38: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/alloc/src/boxed.rs:2007:9
        39: std::sys::unix::thread::Thread::new::thread_start
                   at /rustc/474709a9a2a74a8bcf0055fadb335d0ca0d2d939/library/std/src/sys/unix/thread.rs:108:17
        40: <unknown>
        41: <unknown>
  --> a.rs:29:1
   |
29 | }
   | ^

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: please attach the file at `/tmp/rustc-ice-2023-08-04T14:54:48.192634436Z-93440.txt` to your bug report

note: compiler flags: -Z validate-mir -Z mir-opt-level=3 --crate-type lib -Z print-mono-items=eager

query stack during panic:
end of query stack
rustc 1.73.0-nightly (474709a9a 2023-08-03)
binary: rustc
commit-hash: 474709a9a2a74a8bcf0055fadb335d0ca0d2d939
commit-date: 2023-08-03
host: x86_64-unknown-linux-gnu
release: 1.73.0-nightly
LLVM version: 16.0.5

@tmiasko tmiasko added the -Zvalidate-mir Unstable option: MIR validation label Dec 17, 2023
@bors bors closed this as completed in 7dd0955 Dec 23, 2023
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Dec 23, 2023
Rollup merge of rust-lang#119077 - tmiasko:lint, r=cjgillot

Separate MIR lints from validation

Add a MIR lint pass, enabled with -Zlint-mir, which identifies undefined or
likely erroneous behaviour.

The initial implementation mostly migrates existing checks of this nature from
MIR validator, where they did not belong (those checks have false positives and
there is nothing inherently invalid about MIR with undefined behaviour).

Fixes rust-lang#104736
Fixes rust-lang#104843
Fixes rust-lang#116079
Fixes rust-lang#116736
Fixes rust-lang#118990
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
-Zvalidate-mir Unstable option: MIR validation A-mir Area: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html C-bug Category: This is a bug. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. glacier ICE tracked in rust-lang/glacier. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️ T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
9 participants