diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/codegen/mod.rs')
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/codegen/mod.rs | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/codegen/mod.rs b/compiler/rustc_trait_selection/src/traits/codegen/mod.rs new file mode 100644 index 00000000000..dd7ea55cc10 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/codegen/mod.rs @@ -0,0 +1,126 @@ +// This file contains various trait resolution methods used by codegen. +// They all assume regions can be erased and monomorphic types. It +// seems likely that they should eventually be merged into more +// general routines. + +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::traits::{ + FulfillmentContext, ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, + Unimplemented, +}; +use rustc_errors::ErrorReported; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, TyCtxt}; + +/// Attempts to resolve an obligation to a `ImplSource`. The result is +/// a shallow `ImplSource` resolution, meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn codegen_fulfill_obligation<'tcx>( + ty: TyCtxt<'tcx>, + (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), +) -> Result<ImplSource<'tcx, ()>, ErrorReported> { + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!( + "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), + trait_ref.def_id() + ); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = + Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!( + "encountered ambiguity selecting `{:?}` during codegen, presuming due to \ + overflow or prior type error", + trait_ref + ), + ); + return Err(ErrorReported); + } + Err(Unimplemented) => { + // This can trigger when we probe for the source of a `'static` lifetime requirement + // on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound. + infcx.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!( + "Encountered error `Unimplemented` selecting `{:?}` during codegen", + trait_ref + ), + ); + return Err(ErrorReported); + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let impl_source = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, &impl_source); + + info!("Cache miss: {:?} => {:?}", trait_ref, impl_source); + Ok(impl_source) + }) +} + +// # Global Cache + +/// Finishes processes any obligations that remain in the +/// fulfillment context, and then returns the result with all type +/// variables removed and regions erased. Because this is intended +/// for use after type-check has completed, if any errors occur, +/// it will panic. It is used during normalization and other cases +/// where processing the obligations in `fulfill_cx` may cause +/// type inference variables that appear in `result` to be +/// unified, and hence we need to process those obligations to get +/// the complete picture of the type. +fn drain_fulfillment_cx_or_panic<T>( + infcx: &InferCtxt<'_, 'tcx>, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + if let Err(errors) = fulfill_cx.select_all_or_error(infcx) { + bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors); + } + + let result = infcx.resolve_vars_if_possible(result); + infcx.tcx.erase_regions(&result) +} |
