Code change:
<!-- Thank you for finding an Internal Compiler Error! If possible, try to provide a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for how to create smaller examples. --Please clone and build it. <!-- 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. --: <!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summary<strongBacktrace</strong</summary<p</p</details
This is a new incremental compilation issue, distinct from the previous 'called Option::unwrap' issues. cc
Thanks for the great reproducer - I was able to reproduce this by first building commit , then building commit
This is caused by the way that we handle (which is an anon dep-node). The current query system is set up so that we will never try to force a query with a stale . Normally, the only way of invoking a query with a is to obtain that from another query result, or to receive that as part of the query argument. If a query 'A' obtains a by invoking another query 'B', then removing the corresponding definition from the crate will change the result of query 'B', causing it to be marked red. When we try to mark query 'A' as green, we will see the red dependency 'B' and bail out, before we ever attempt to force another query with the stale that we saved from the previous compilation session. When we actually invoke a query during the execution of some query's body, we will try to find a matching from the previous compilation session. If the input to the invoked query contains a (e.g. as part of the inside of a ), then a change to the will cause us to compute a different hash for the query input. As a result, we will never try to load the depnode from a previous session, since we will not find a node with a matching hash. However, anon dep nodes break this assumption. Specifically, when we create a , we invoke queries like using s taken from the surrounding . From the perspective of the query system, these s are essentially pulled out of thin air - the query system sees as having no input. Specifically, we call: Inside a (recursive) call to , we may call queries with s obtained from the predicate - for example, to look up constituent types for auto trait impls. As a result, trying to mark a node green can cause us to try to force a query with a stale , since our edges vec will not contain a dependency on a query used to produce the to begin with. With normal non-anon nodes, the dependency on a particular will always be explicitly carried through the graph of query invocations - either being forwarded through a query argument (thus affecting the hash), or by using the result of another query. The fact that we will never legitimately try to force a stale has helped us find bugs (e.g. ), so I think it would be unfortunate if we had to start silently ignoring the forcing of a stale . I'll need to think about how we can adjust anon dep nodes to fix this issue. Ideally, we would just remove the concept of anon dep nodes entirely - however, I'm pretty sure that it's the only way to make the global evaluation cache work with incremental compilation.
I've determined the root cause of the issue - we're failing to keep track of the created when using the provisional cache. When evaluation finishes 'normally', we store the created anon depnode into the evaluation cache: When we later read from the evaluation cache, we will generate a read of that : This ensures that any queries using a cached result will still get all of the dependency edges created during the initial uncached execution (e.g. a call to when determining constituent types). However, when we store a result in the provisional cache (due to an auto-trait cycle), we don't store the generated : When we later move entires from the provisional cache to the 'real' evaluation cache, we insert them using a different : In the provided reproducer, we first store in the provisional cache, and then move it to the 'real' evaluation cache. This means that any queries using the cached result of evaluating (e.g. a call to ) will not get dependency edges for any queries invoked during the initial 'provisional' evaluation of the predicate. In particular, we will not get the edge to , which has a changed result between the two compilation sessions. I've confirmed that storing the provisional result into the evaluation cache using its original fixes the ICE. However, I need to do some more investigation - I think we may need to combine both 's to ensure that we don't miss any edges.
Minimal reproduction script: : : produces:

Related discussion:
text: debug!(?result, "CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); <del> stack.cache().on_completion(stack.dfn, |fresh_trait_pred, provisional_result| { self.insert_evaluation_cache( param_env, fresh_trait_pred, dep_node, provisional_result.max(result), ); }); </del> <ins> stack.cache().on_completion( stack.dfn, |fresh_trait_pred, provisional_result, provisional_dep_node| { // Create a new `DepNode` that has dependencies on: // * The `DepNode` for the original evaluation that resulted in a provisional cache // entry being crated // * The `DepNode` for the *current* evaluation, which resulted in us completing // provisional caches entries and inserting them into the evaluation cache // // This ensures that when a query reads this entry from the evaluation cache, // it will end up (transitively) dependening on all of the incr-comp dependencies // created during the evaluation of this trait. For example, evaluating a trait // will usually require us to invoke `type_of(field_def_id)` to determine the // constituent types, and we want any queries reading from this evaluation // cache entry to end up with a transitive `type_of(field_def_id`)` dependency. // // By using `in_task`, we're also creating an edge from the *current* query // to the newly-created `combined_dep_node`. This is probably redundant, // but it's better to add too many dep graph edges than to add too few // dep graph edges. let ((), combined_dep_node) = self.in_task(|this| { this.tcx().dep_graph.read_index(provisional_dep_node); this.tcx().dep_graph.read_index(dep_node); }); self.insert_evaluation_cache( param_env, fresh_trait_pred, combined_dep_node, provisional_result.max(result), ); }, ); </ins> } else { debug!(?result, "PROVISIONAL"); debug!(