]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/codegen.rs
Rollup merge of #93721 - jyn514:less-macro-special-casing, r=petrochenkov
[rust.git] / compiler / rustc_trait_selection / src / traits / codegen.rs
1 // This file contains various trait resolution methods used by codegen.
2 // They all assume regions can be erased and monomorphic types.  It
3 // seems likely that they should eventually be merged into more
4 // general routines.
5
6 use crate::infer::{InferCtxt, TyCtxtInferExt};
7 use crate::traits::{
8     FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine,
9     Unimplemented,
10 };
11 use rustc_errors::ErrorReported;
12 use rustc_middle::ty::fold::TypeFoldable;
13 use rustc_middle::ty::{self, TyCtxt};
14
15 /// Attempts to resolve an obligation to an `ImplSource`. The result is
16 /// a shallow `ImplSource` resolution, meaning that we do not
17 /// (necessarily) resolve all nested obligations on the impl. Note
18 /// that type check should guarantee to us that all nested
19 /// obligations *could be* resolved if we wanted to.
20 ///
21 /// This also expects that `trait_ref` is fully normalized.
22 pub fn codegen_fulfill_obligation<'tcx>(
23     tcx: TyCtxt<'tcx>,
24     (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
25 ) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
26     // Remove any references to regions; this helps improve caching.
27     let trait_ref = tcx.erase_regions(trait_ref);
28     // We expect the input to be fully normalized.
29     debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref));
30     debug!(
31         "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})",
32         (param_env, trait_ref),
33         trait_ref.def_id()
34     );
35
36     // Do the initial selection for the obligation. This yields the
37     // shallow result we are looking for -- that is, what specific impl.
38     tcx.infer_ctxt().enter(|infcx| {
39         let mut selcx = SelectionContext::new(&infcx);
40
41         let obligation_cause = ObligationCause::dummy();
42         let obligation =
43             Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
44
45         let selection = match selcx.select(&obligation) {
46             Ok(Some(selection)) => selection,
47             Ok(None) => {
48                 // Ambiguity can happen when monomorphizing during trans
49                 // expands to some humongo type that never occurred
50                 // statically -- this humongo type can then overflow,
51                 // leading to an ambiguous result. So report this as an
52                 // overflow bug, since I believe this is the only case
53                 // where ambiguity can result.
54                 infcx.tcx.sess.delay_span_bug(
55                     rustc_span::DUMMY_SP,
56                     &format!(
57                         "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
58                          overflow or prior type error",
59                         trait_ref
60                     ),
61                 );
62                 return Err(ErrorReported);
63             }
64             Err(Unimplemented) => {
65                 // This can trigger when we probe for the source of a `'static` lifetime requirement
66                 // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
67                 infcx.tcx.sess.delay_span_bug(
68                     rustc_span::DUMMY_SP,
69                     &format!(
70                         "Encountered error `Unimplemented` selecting `{:?}` during codegen",
71                         trait_ref
72                     ),
73                 );
74                 return Err(ErrorReported);
75             }
76             Err(e) => {
77                 bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
78             }
79         };
80
81         debug!("fulfill_obligation: selection={:?}", selection);
82
83         // Currently, we use a fulfillment context to completely resolve
84         // all nested obligations. This is because they can inform the
85         // inference of the impl's type parameters.
86         let mut fulfill_cx = FulfillmentContext::new();
87         let impl_source = selection.map(|predicate| {
88             debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate);
89             fulfill_cx.register_predicate_obligation(&infcx, predicate);
90         });
91         let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source);
92
93         // We may constrain the hidden types of opaque types in this query, but this is
94         // not information our callers need, as all that information is handled by borrowck
95         // and typeck.
96         drop(infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types());
97
98         debug!("Cache miss: {:?} => {:?}", trait_ref, impl_source);
99         Ok(impl_source)
100     })
101 }
102
103 // # Global Cache
104
105 /// Finishes processes any obligations that remain in the
106 /// fulfillment context, and then returns the result with all type
107 /// variables removed and regions erased. Because this is intended
108 /// for use outside of type inference, if any errors occur,
109 /// it will panic. It is used during normalization and other cases
110 /// where processing the obligations in `fulfill_cx` may cause
111 /// type inference variables that appear in `result` to be
112 /// unified, and hence we need to process those obligations to get
113 /// the complete picture of the type.
114 fn drain_fulfillment_cx_or_panic<'tcx, T>(
115     infcx: &InferCtxt<'_, 'tcx>,
116     fulfill_cx: &mut FulfillmentContext<'tcx>,
117     result: T,
118 ) -> T
119 where
120     T: TypeFoldable<'tcx>,
121 {
122     debug!("drain_fulfillment_cx_or_panic()");
123
124     // In principle, we only need to do this so long as `result`
125     // contains unbound type parameters. It could be a slight
126     // optimization to stop iterating early.
127     let errors = fulfill_cx.select_all_or_error(infcx);
128     if !errors.is_empty() {
129         infcx.tcx.sess.delay_span_bug(
130             rustc_span::DUMMY_SP,
131             &format!(
132                 "Encountered errors `{:?}` resolving bounds outside of type inference",
133                 errors
134             ),
135         );
136     }
137
138     let result = infcx.resolve_vars_if_possible(result);
139     infcx.tcx.erase_regions(result)
140 }