]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_traits/src/normalize_erasing_regions.rs
Auto merge of #91255 - b-naber:normalization-ice, r=jackh276
[rust.git] / compiler / rustc_traits / src / normalize_erasing_regions.rs
1 use rustc_infer::infer::TyCtxtInferExt;
2 use rustc_middle::traits::query::NoSolution;
3 use rustc_middle::ty::query::Providers;
4 use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt, TypeFoldable};
5 use rustc_trait_selection::traits::query::normalize::AtExt;
6 use rustc_trait_selection::traits::{Normalized, ObligationCause};
7 use std::sync::atomic::Ordering;
8
9 crate fn provide(p: &mut Providers) {
10     *p = Providers {
11         normalize_generic_arg_after_erasing_regions: |tcx, goal| {
12             debug!("normalize_generic_arg_after_erasing_regions(goal={:#?})", goal);
13
14             tcx.sess
15                 .perf_stats
16                 .normalize_generic_arg_after_erasing_regions
17                 .fetch_add(1, Ordering::Relaxed);
18             normalize_after_erasing_regions(tcx, goal)
19         },
20         normalize_mir_const_after_erasing_regions: |tcx, goal| {
21             normalize_after_erasing_regions(tcx, goal)
22         },
23         try_normalize_generic_arg_after_erasing_regions: |tcx, goal| {
24             debug!("try_normalize_generic_arg_after_erasing_regions(goal={:#?}", goal);
25
26             try_normalize_after_erasing_regions(tcx, goal)
27         },
28         try_normalize_mir_const_after_erasing_regions: |tcx, goal| {
29             try_normalize_after_erasing_regions(tcx, goal)
30         },
31         ..*p
32     };
33 }
34
35 #[instrument(level = "debug", skip(tcx))]
36 fn normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Copy>(
37     tcx: TyCtxt<'tcx>,
38     goal: ParamEnvAnd<'tcx, T>,
39 ) -> T {
40     let ParamEnvAnd { param_env, value } = goal;
41     tcx.infer_ctxt().enter(|infcx| {
42         let cause = ObligationCause::dummy();
43         match infcx.at(&cause, param_env).normalize(value) {
44             Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
45                 // We don't care about the `obligations`; they are
46                 // always only region relations, and we are about to
47                 // erase those anyway:
48                 debug_assert_eq!(
49                     normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)),
50                     None,
51                 );
52
53                 let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
54                 // It's unclear when `resolve_vars` would have an effect in a
55                 // fresh `InferCtxt`. If this assert does trigger, it will give
56                 // us a test case.
57                 debug_assert_eq!(normalized_value, resolved_value);
58                 let erased = infcx.tcx.erase_regions(resolved_value);
59                 debug_assert!(!erased.needs_infer(), "{:?}", erased);
60                 erased
61             }
62             Err(NoSolution) => bug!("could not fully normalize `{:?}`", value),
63         }
64     })
65 }
66
67 #[instrument(level = "debug", skip(tcx))]
68 fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Copy>(
69     tcx: TyCtxt<'tcx>,
70     goal: ParamEnvAnd<'tcx, T>,
71 ) -> Result<T, NoSolution> {
72     let ParamEnvAnd { param_env, value } = goal;
73     tcx.infer_ctxt().enter(|infcx| {
74         let cause = ObligationCause::dummy();
75         match infcx.at(&cause, param_env).normalize(value) {
76             Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
77                 // We don't care about the `obligations`; they are
78                 // always only region relations, and we are about to
79                 // erase those anyway:
80                 debug_assert_eq!(
81                     normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)),
82                     None,
83                 );
84
85                 let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
86                 // It's unclear when `resolve_vars` would have an effect in a
87                 // fresh `InferCtxt`. If this assert does trigger, it will give
88                 // us a test case.
89                 debug_assert_eq!(normalized_value, resolved_value);
90                 let erased = infcx.tcx.erase_regions(resolved_value);
91                 debug_assert!(!erased.needs_infer(), "{:?}", erased);
92                 Ok(erased)
93             }
94             Err(NoSolution) => Err(NoSolution),
95         }
96     })
97 }
98
99 fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool {
100     match p.kind().skip_binder() {
101         ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false,
102         ty::PredicateKind::Trait(..)
103         | ty::PredicateKind::Projection(..)
104         | ty::PredicateKind::WellFormed(..)
105         | ty::PredicateKind::ObjectSafe(..)
106         | ty::PredicateKind::ClosureKind(..)
107         | ty::PredicateKind::Subtype(..)
108         | ty::PredicateKind::Coerce(..)
109         | ty::PredicateKind::ConstEvaluatable(..)
110         | ty::PredicateKind::ConstEquate(..)
111         | ty::PredicateKind::TypeWellFormedFromEnv(..) => true,
112     }
113 }