diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs | 205 |
1 files changed, 172 insertions, 33 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e91057356..1f5bbc178 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -5,12 +5,14 @@ //! candidates. See the [rustc dev guide] for more details. //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly + +use hir::def_id::DefId; use hir::LangItem; use rustc_hir as hir; use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; -use rustc_target::spec::abi::Abi; use crate::traits; use crate::traits::query::evaluate_obligation::InferCtxtExt; @@ -95,7 +97,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.tuple_trait() == Some(def_id) { self.assemble_candidate_for_tuple(obligation, &mut candidates); } else if lang_items.pointer_like() == Some(def_id) { - self.assemble_candidate_for_ptr_sized(obligation, &mut candidates); + self.assemble_candidate_for_pointer_like(obligation, &mut candidates); + } else if lang_items.fn_ptr_trait() == Some(def_id) { + self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -290,6 +294,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } + // Keep this function in sync with extract_tupled_inputs_and_output_from_callable + // until the old solver (and thus this function) is removed. + // Okay to skip binder because what we are inspecting doesn't involve bound regions. let self_ty = obligation.self_ty().skip_binder(); match *self_ty.kind() { @@ -298,31 +305,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.ambiguous = true; // Could wind up being a fn() type. } // Provide an impl, but only for suitable `fn` pointers. - ty::FnPtr(_) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() - { + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { candidates.vec.push(FnPointerCandidate { is_const: false }); } } // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). ty::FnDef(def_id, _) => { - if let ty::FnSig { - unsafety: hir::Unsafety::Normal, - abi: Abi::Rust, - c_variadic: false, - .. - } = self_ty.fn_sig(self.tcx()).skip_binder() + if self.tcx().fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { - if self.tcx().codegen_fn_attrs(def_id).target_features.is_empty() { - candidates - .vec - .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); - } + candidates + .vec + .push(FnPointerCandidate { is_const: self.tcx().is_const_fn(def_id) }); } } _ => {} @@ -330,13 +325,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } /// Searches for impls that might apply to `obligation`. + #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_impls( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - debug!(?obligation, "assemble_candidates_from_impls"); - // Essentially any user-written impl will match with an error type, // so creating `ImplCandidates` isn't useful. However, we might // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`) @@ -350,6 +344,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + let obligation_substs = obligation.predicate.skip_binder().trait_ref.substs; self.tcx().for_each_relevant_impl( obligation.predicate.def_id(), obligation.predicate.skip_binder().trait_ref.self_ty(), @@ -358,7 +354,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // consider a "quick reject". This avoids creating more types // and so forth that we need to. let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) { + if !drcx.substs_refs_may_unify(obligation_substs, impl_trait_ref.0.substs) { + return; + } + if self.reject_fn_ptr_impls( + impl_def_id, + obligation, + impl_trait_ref.skip_binder().self_ty(), + ) { return; } @@ -371,6 +374,99 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } + /// The various `impl<T: FnPtr> Trait for T` in libcore are more like builtin impls for all function items + /// and function pointers and less like blanket impls. Rejecting them when they can't possibly apply (because + /// the obligation's self-type does not implement `FnPtr`) avoids reporting that the self type does not implement + /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`. + #[instrument(level = "trace", skip(self), ret)] + fn reject_fn_ptr_impls( + &self, + impl_def_id: DefId, + obligation: &TraitObligation<'tcx>, + impl_self_ty: Ty<'tcx>, + ) -> bool { + // Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path. + if !matches!(impl_self_ty.kind(), ty::Param(..)) { + return false; + } + let Some(fn_ptr_trait) = self.tcx().lang_items().fn_ptr_trait() else { + return false; + }; + + for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates { + let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + = predicate.kind().skip_binder() else { continue }; + if fn_ptr_trait != pred.trait_ref.def_id { + continue; + } + trace!(?pred); + // Not the bound we're looking for + if pred.self_ty() != impl_self_ty { + continue; + } + + match obligation.self_ty().skip_binder().kind() { + // Fast path to avoid evaluating an obligation that trivially holds. + // There may be more bounds, but these are checked by the regular path. + ty::FnPtr(..) => return false, + // These may potentially implement `FnPtr` + ty::Placeholder(..) + | ty::Dynamic(_, _, _) + | ty::Alias(_, _) + | ty::Infer(_) + | ty::Param(..) => {} + + ty::Bound(_, _) => span_bug!( + obligation.cause.span(), + "cannot have escaping bound var in self type of {obligation:#?}" + ), + // These can't possibly implement `FnPtr` as they are concrete types + // and not `FnPtr` + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(_, _) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => return true, + // FIXME: Function definitions could actually implement `FnPtr` by + // casting the ZST function def to a function pointer. + ty::FnDef(_, _) => return true, + } + + // Generic params can implement `FnPtr` if the predicate + // holds within its own environment. + let obligation = Obligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| { + pred.trait_ref = + self.tcx().mk_trait_ref(fn_ptr_trait, [pred.trait_ref.self_ty()]); + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + })), + ); + if let Ok(r) = self.infcx.evaluate_obligation(&obligation) { + if !r.may_apply() { + return true; + } + } + } + false + } + fn assemble_candidates_from_auto_impls( &mut self, obligation: &TraitObligation<'tcx>, @@ -783,6 +879,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let relevant_impl = self.tcx().find_map_relevant_impl( self.tcx().require_lang_item(LangItem::Drop, None), obligation.predicate.skip_binder().trait_ref.self_ty(), + TreatProjections::ForLookup, Some, ); @@ -845,15 +942,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn assemble_candidate_for_ptr_sized( + fn assemble_candidate_for_pointer_like( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // The regions of a type don't affect the size of the type - let self_ty = self - .tcx() - .erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate.self_ty())); + let tcx = self.tcx(); + let self_ty = + tcx.erase_regions(tcx.erase_late_bound_regions(obligation.predicate.self_ty())); // But if there are inference variables, we have to wait until it's resolved. if self_ty.has_non_region_infer() { @@ -861,13 +958,55 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - let usize_layout = - self.tcx().layout_of(ty::ParamEnv::empty().and(self.tcx().types.usize)).unwrap().layout; - if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty)) - && layout.layout.size() == usize_layout.size() - && layout.layout.align().abi == usize_layout.align().abi + if let Ok(layout) = tcx.layout_of(obligation.param_env.and(self_ty)) + && layout.layout.is_pointer_like(&tcx.data_layout) { candidates.vec.push(BuiltinCandidate { has_nested: false }); } } + + fn assemble_candidates_for_fn_ptr_trait( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { + ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(..) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Infer( + ty::InferTy::IntVar(_) + | ty::InferTy::FloatVar(_) + | ty::InferTy::FreshIntTy(_) + | ty::InferTy::FreshFloatTy(_), + ) => {} + ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) => { + candidates.ambiguous = true; + } + } + } } |