rustc_typeck to rustc_hir_analysis
This commit is contained in:
594
compiler/rustc_hir_analysis/src/check/method/confirm.rs
Normal file
594
compiler/rustc_hir_analysis/src/check/method/confirm.rs
Normal file
@@ -0,0 +1,594 @@
|
||||
use super::{probe, MethodCallee};
|
||||
|
||||
use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
|
||||
use crate::check::{callee, FnCtxt};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::GenericArg;
|
||||
use rustc_infer::infer::{self, InferOk};
|
||||
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::{self, SubstsRef};
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
|
||||
struct ConfirmContext<'a, 'tcx> {
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
self_expr: &'tcx hir::Expr<'tcx>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> {
|
||||
type Target = FnCtxt<'a, 'tcx>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.fcx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConfirmResult<'tcx> {
|
||||
pub callee: MethodCallee<'tcx>,
|
||||
pub illegal_sized_bound: Option<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn confirm_method(
|
||||
&self,
|
||||
span: Span,
|
||||
self_expr: &'tcx hir::Expr<'tcx>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
unadjusted_self_ty: Ty<'tcx>,
|
||||
pick: probe::Pick<'tcx>,
|
||||
segment: &hir::PathSegment<'_>,
|
||||
) -> ConfirmResult<'tcx> {
|
||||
debug!(
|
||||
"confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
|
||||
unadjusted_self_ty, pick, segment.args,
|
||||
);
|
||||
|
||||
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
|
||||
confirm_cx.confirm(unadjusted_self_ty, pick, segment)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
fn new(
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
self_expr: &'tcx hir::Expr<'tcx>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> ConfirmContext<'a, 'tcx> {
|
||||
ConfirmContext { fcx, span, self_expr, call_expr }
|
||||
}
|
||||
|
||||
fn confirm(
|
||||
&mut self,
|
||||
unadjusted_self_ty: Ty<'tcx>,
|
||||
pick: probe::Pick<'tcx>,
|
||||
segment: &hir::PathSegment<'_>,
|
||||
) -> ConfirmResult<'tcx> {
|
||||
// Adjust the self expression the user provided and obtain the adjusted type.
|
||||
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
|
||||
|
||||
// Create substitutions for the method's type parameters.
|
||||
let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
|
||||
let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs);
|
||||
|
||||
debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}");
|
||||
|
||||
// Create the final signature for the method, replacing late-bound regions.
|
||||
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
|
||||
|
||||
// If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
|
||||
// something which derefs to `Self` actually implements the trait and the caller
|
||||
// wanted to make a static dispatch on it but forgot to import the trait.
|
||||
// See test `src/test/ui/issue-35976.rs`.
|
||||
//
|
||||
// In that case, we'll error anyway, but we'll also re-run the search with all traits
|
||||
// in scope, and if we find another method which can be used, we'll output an
|
||||
// appropriate hint suggesting to import the trait.
|
||||
let filler_substs = rcvr_substs
|
||||
.extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
|
||||
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
|
||||
&self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
|
||||
);
|
||||
|
||||
// Unify the (adjusted) self type with what the method expects.
|
||||
//
|
||||
// SUBTLE: if we want good error messages, because of "guessing" while matching
|
||||
// traits, no trait system method can be called before this point because they
|
||||
// could alter our Self-type, except for normalizing the receiver from the
|
||||
// signature (which is also done during probing).
|
||||
let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]);
|
||||
debug!(
|
||||
"confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}",
|
||||
self_ty, method_sig_rcvr, method_sig, method_predicates
|
||||
);
|
||||
self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs);
|
||||
|
||||
let (method_sig, method_predicates) =
|
||||
self.normalize_associated_types_in(self.span, (method_sig, method_predicates));
|
||||
let method_sig = ty::Binder::dummy(method_sig);
|
||||
|
||||
// Make sure nobody calls `drop()` explicitly.
|
||||
self.enforce_illegal_method_limitations(&pick);
|
||||
|
||||
// Add any trait/regions obligations specified on the method's type parameters.
|
||||
// We won't add these if we encountered an illegal sized bound, so that we can use
|
||||
// a custom error in that case.
|
||||
if illegal_sized_bound.is_none() {
|
||||
self.add_obligations(
|
||||
self.tcx.mk_fn_ptr(method_sig),
|
||||
all_substs,
|
||||
method_predicates,
|
||||
pick.item.def_id,
|
||||
);
|
||||
}
|
||||
|
||||
// Create the final `MethodCallee`.
|
||||
let callee = MethodCallee {
|
||||
def_id: pick.item.def_id,
|
||||
substs: all_substs,
|
||||
sig: method_sig.skip_binder(),
|
||||
};
|
||||
ConfirmResult { callee, illegal_sized_bound }
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ADJUSTMENTS
|
||||
|
||||
fn adjust_self_ty(
|
||||
&mut self,
|
||||
unadjusted_self_ty: Ty<'tcx>,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
// Commit the autoderefs by calling `autoderef` again, but this
|
||||
// time writing the results into the various typeck results.
|
||||
let mut autoderef =
|
||||
self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
|
||||
let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
|
||||
return self.tcx.ty_error_with_message(
|
||||
rustc_span::DUMMY_SP,
|
||||
&format!("failed autoderef {}", pick.autoderefs),
|
||||
);
|
||||
};
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
|
||||
let mut adjustments = self.adjust_steps(&autoderef);
|
||||
let mut target = self.structurally_resolved_type(autoderef.span(), ty);
|
||||
|
||||
match pick.autoref_or_ptr_adjustment {
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
|
||||
let region = self.next_region_var(infer::Autoref(self.span));
|
||||
// Type we're wrapping in a reference, used later for unsizing
|
||||
let base_ty = target;
|
||||
|
||||
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// Method call receivers are the primary use case
|
||||
// for two-phase borrows.
|
||||
allow_two_phase_borrow: AllowTwoPhase::Yes,
|
||||
},
|
||||
};
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target,
|
||||
});
|
||||
|
||||
if unsize {
|
||||
let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
|
||||
self.tcx.mk_slice(*elem_ty)
|
||||
} else {
|
||||
bug!(
|
||||
"AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
|
||||
base_ty
|
||||
)
|
||||
};
|
||||
target = self
|
||||
.tcx
|
||||
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
|
||||
adjustments
|
||||
.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
|
||||
}
|
||||
}
|
||||
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
|
||||
target = match target.kind() {
|
||||
&ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
|
||||
assert_eq!(mutbl, hir::Mutability::Mut);
|
||||
self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
|
||||
}
|
||||
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
|
||||
};
|
||||
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::MutToConstPointer),
|
||||
target,
|
||||
});
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
self.register_predicates(autoderef.into_obligations());
|
||||
|
||||
// Write out the final adjustments.
|
||||
self.apply_adjustments(self.self_expr, adjustments);
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
/// Returns a set of substitutions for the method *receiver* where all type and region
|
||||
/// parameters are instantiated with fresh variables. This substitution does not include any
|
||||
/// parameters declared on the method itself.
|
||||
///
|
||||
/// Note that this substitution may include late-bound regions from the impl level. If so,
|
||||
/// these are instantiated later in the `instantiate_method_sig` routine.
|
||||
fn fresh_receiver_substs(
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
) -> SubstsRef<'tcx> {
|
||||
match pick.kind {
|
||||
probe::InherentImplPick => {
|
||||
let impl_def_id = pick.item.container_id(self.tcx);
|
||||
assert!(
|
||||
self.tcx.impl_trait_ref(impl_def_id).is_none(),
|
||||
"impl {:?} is not an inherent impl",
|
||||
impl_def_id
|
||||
);
|
||||
self.fresh_substs_for_item(self.span, impl_def_id)
|
||||
}
|
||||
|
||||
probe::ObjectPick => {
|
||||
let trait_def_id = pick.item.container_id(self.tcx);
|
||||
self.extract_existential_trait_ref(self_ty, |this, object_ty, principal| {
|
||||
// The object data has no entry for the Self
|
||||
// Type. For the purposes of this method call, we
|
||||
// substitute the object type itself. This
|
||||
// wouldn't be a sound substitution in all cases,
|
||||
// since each instance of the object type is a
|
||||
// different existential and hence could match
|
||||
// distinct types (e.g., if `Self` appeared as an
|
||||
// argument type), but those cases have already
|
||||
// been ruled out when we deemed the trait to be
|
||||
// "object safe".
|
||||
let original_poly_trait_ref = principal.with_self_ty(this.tcx, object_ty);
|
||||
let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id);
|
||||
let upcast_trait_ref =
|
||||
this.replace_bound_vars_with_fresh_vars(upcast_poly_trait_ref);
|
||||
debug!(
|
||||
"original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}",
|
||||
original_poly_trait_ref, upcast_trait_ref, trait_def_id
|
||||
);
|
||||
upcast_trait_ref.substs
|
||||
})
|
||||
}
|
||||
|
||||
probe::TraitPick => {
|
||||
let trait_def_id = pick.item.container_id(self.tcx);
|
||||
|
||||
// Make a trait reference `$0 : Trait<$1...$n>`
|
||||
// consisting entirely of type variables. Later on in
|
||||
// the process we will unify the transformed-self-type
|
||||
// of the method with the actual type in order to
|
||||
// unify some of these variables.
|
||||
self.fresh_substs_for_item(self.span, trait_def_id)
|
||||
}
|
||||
|
||||
probe::WhereClausePick(poly_trait_ref) => {
|
||||
// Where clauses can have bound regions in them. We need to instantiate
|
||||
// those to convert from a poly-trait-ref to a trait-ref.
|
||||
self.replace_bound_vars_with_fresh_vars(poly_trait_ref).substs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_existential_trait_ref<R, F>(&mut self, self_ty: Ty<'tcx>, mut closure: F) -> R
|
||||
where
|
||||
F: FnMut(&mut ConfirmContext<'a, 'tcx>, Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>) -> R,
|
||||
{
|
||||
// If we specified that this is an object method, then the
|
||||
// self-type ought to be something that can be dereferenced to
|
||||
// yield an object-type (e.g., `&Object` or `Box<Object>`
|
||||
// etc).
|
||||
|
||||
// FIXME: this feels, like, super dubious
|
||||
self.fcx
|
||||
.autoderef(self.span, self_ty)
|
||||
.include_raw_pointers()
|
||||
.find_map(|(ty, _)| match ty.kind() {
|
||||
ty::Dynamic(data, ..) => Some(closure(
|
||||
self,
|
||||
ty,
|
||||
data.principal().unwrap_or_else(|| {
|
||||
span_bug!(self.span, "calling trait method on empty object?")
|
||||
}),
|
||||
)),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"self-type `{}` for ObjectPick never dereferenced to an object",
|
||||
self_ty
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn instantiate_method_substs(
|
||||
&mut self,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
seg: &hir::PathSegment<'_>,
|
||||
parent_substs: SubstsRef<'tcx>,
|
||||
) -> SubstsRef<'tcx> {
|
||||
// Determine the values for the generic parameters of the method.
|
||||
// If they were not explicitly supplied, just construct fresh
|
||||
// variables.
|
||||
let generics = self.tcx.generics_of(pick.item.def_id);
|
||||
|
||||
let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call(
|
||||
self.tcx,
|
||||
self.span,
|
||||
pick.item.def_id,
|
||||
generics,
|
||||
seg,
|
||||
IsMethodCall::Yes,
|
||||
);
|
||||
|
||||
// Create subst for early-bound lifetime parameters, combining
|
||||
// parameters from the type and those from the method.
|
||||
assert_eq!(generics.parent_count, parent_substs.len());
|
||||
|
||||
struct MethodSubstsCtxt<'a, 'tcx> {
|
||||
cfcx: &'a ConfirmContext<'a, 'tcx>,
|
||||
pick: &'a probe::Pick<'tcx>,
|
||||
seg: &'a hir::PathSegment<'a>,
|
||||
}
|
||||
impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> {
|
||||
fn args_for_def_id(
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
|
||||
if def_id == self.pick.item.def_id {
|
||||
if let Some(data) = self.seg.args {
|
||||
return (Some(data), false);
|
||||
}
|
||||
}
|
||||
(None, false)
|
||||
}
|
||||
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param: &ty::GenericParamDef,
|
||||
arg: &GenericArg<'_>,
|
||||
) -> subst::GenericArg<'tcx> {
|
||||
match (¶m.kind, arg) {
|
||||
(GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
|
||||
<dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param))
|
||||
.into()
|
||||
}
|
||||
(GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
|
||||
self.cfcx.to_ty(ty).into()
|
||||
}
|
||||
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
|
||||
self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
|
||||
}
|
||||
(GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
|
||||
self.cfcx.ty_infer(Some(param), inf.span).into()
|
||||
}
|
||||
(GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
|
||||
let tcx = self.cfcx.tcx();
|
||||
self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
_substs: Option<&[subst::GenericArg<'tcx>]>,
|
||||
param: &ty::GenericParamDef,
|
||||
_infer_args: bool,
|
||||
) -> subst::GenericArg<'tcx> {
|
||||
self.cfcx.var_for_def(self.cfcx.span, param)
|
||||
}
|
||||
}
|
||||
<dyn AstConv<'_>>::create_substs_for_generic_args(
|
||||
self.tcx,
|
||||
pick.item.def_id,
|
||||
parent_substs,
|
||||
false,
|
||||
None,
|
||||
&arg_count_correct,
|
||||
&mut MethodSubstsCtxt { cfcx: self, pick, seg },
|
||||
)
|
||||
}
|
||||
|
||||
fn unify_receivers(
|
||||
&mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
method_self_ty: Ty<'tcx>,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"unify_receivers: self_ty={:?} method_self_ty={:?} span={:?} pick={:?}",
|
||||
self_ty, method_self_ty, self.span, pick
|
||||
);
|
||||
let cause = self.cause(
|
||||
self.span,
|
||||
ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext {
|
||||
assoc_item: pick.item,
|
||||
param_env: self.param_env,
|
||||
substs,
|
||||
})),
|
||||
);
|
||||
match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) {
|
||||
Ok(InferOk { obligations, value: () }) => {
|
||||
self.register_predicates(obligations);
|
||||
}
|
||||
Err(_) => {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"{} was a subtype of {} but now is not?",
|
||||
self_ty,
|
||||
method_self_ty
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this returns the *unnormalized* predicates and method sig. Because of
|
||||
// inference guessing, the predicates and method signature can't be normalized
|
||||
// until we unify the `Self` type.
|
||||
fn instantiate_method_sig(
|
||||
&mut self,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
all_substs: SubstsRef<'tcx>,
|
||||
) -> (ty::FnSig<'tcx>, ty::InstantiatedPredicates<'tcx>) {
|
||||
debug!("instantiate_method_sig(pick={:?}, all_substs={:?})", pick, all_substs);
|
||||
|
||||
// Instantiate the bounds on the method with the
|
||||
// type/early-bound-regions substitutions performed. There can
|
||||
// be no late-bound regions appearing here.
|
||||
let def_id = pick.item.def_id;
|
||||
let method_predicates = self.tcx.predicates_of(def_id).instantiate(self.tcx, all_substs);
|
||||
|
||||
debug!("method_predicates after subst = {:?}", method_predicates);
|
||||
|
||||
let sig = self.tcx.bound_fn_sig(def_id);
|
||||
|
||||
let sig = sig.subst(self.tcx, all_substs);
|
||||
debug!("type scheme substituted, sig={:?}", sig);
|
||||
|
||||
let sig = self.replace_bound_vars_with_fresh_vars(sig);
|
||||
debug!("late-bound lifetimes from method instantiated, sig={:?}", sig);
|
||||
|
||||
(sig, method_predicates)
|
||||
}
|
||||
|
||||
fn add_obligations(
|
||||
&mut self,
|
||||
fty: Ty<'tcx>,
|
||||
all_substs: SubstsRef<'tcx>,
|
||||
method_predicates: ty::InstantiatedPredicates<'tcx>,
|
||||
def_id: DefId,
|
||||
) {
|
||||
debug!(
|
||||
"add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}",
|
||||
fty, all_substs, method_predicates, def_id
|
||||
);
|
||||
|
||||
// FIXME: could replace with the following, but we already calculated `method_predicates`,
|
||||
// so we just call `predicates_for_generics` directly to avoid redoing work.
|
||||
// `self.add_required_obligations(self.span, def_id, &all_substs);`
|
||||
for obligation in traits::predicates_for_generics(
|
||||
|idx, span| {
|
||||
let code = if span.is_dummy() {
|
||||
ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx)
|
||||
} else {
|
||||
ObligationCauseCode::ExprBindingObligation(
|
||||
def_id,
|
||||
span,
|
||||
self.call_expr.hir_id,
|
||||
idx,
|
||||
)
|
||||
};
|
||||
traits::ObligationCause::new(self.span, self.body_id, code)
|
||||
},
|
||||
self.param_env,
|
||||
method_predicates,
|
||||
) {
|
||||
self.register_predicate(obligation);
|
||||
}
|
||||
|
||||
// this is a projection from a trait reference, so we have to
|
||||
// make sure that the trait reference inputs are well-formed.
|
||||
self.add_wf_bounds(all_substs, self.call_expr);
|
||||
|
||||
// the function type must also be well-formed (this is not
|
||||
// implied by the substs being well-formed because of inherent
|
||||
// impls and late-bound regions - see issue #28609).
|
||||
self.register_wf_obligation(fty.into(), self.span, traits::WellFormed(None));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MISCELLANY
|
||||
|
||||
fn predicates_require_illegal_sized_bound(
|
||||
&self,
|
||||
predicates: &ty::InstantiatedPredicates<'tcx>,
|
||||
) -> Option<Span> {
|
||||
let sized_def_id = self.tcx.lang_items().sized_trait()?;
|
||||
|
||||
traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied())
|
||||
// We don't care about regions here.
|
||||
.filter_map(|obligation| match obligation.predicate.kind().skip_binder() {
|
||||
ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => {
|
||||
let span = iter::zip(&predicates.predicates, &predicates.spans)
|
||||
.find_map(
|
||||
|(p, span)| {
|
||||
if *p == obligation.predicate { Some(*span) } else { None }
|
||||
},
|
||||
)
|
||||
.unwrap_or(rustc_span::DUMMY_SP);
|
||||
Some((trait_pred, span))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() {
|
||||
ty::Dynamic(..) => Some(span),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
|
||||
// Disallow calls to the method `drop` defined in the `Drop` trait.
|
||||
if let Some(trait_def_id) = pick.item.trait_container(self.tcx) {
|
||||
callee::check_legal_trait_for_method_call(
|
||||
self.tcx,
|
||||
self.span,
|
||||
Some(self.self_expr.span),
|
||||
self.call_expr.span,
|
||||
trait_def_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn upcast(
|
||||
&mut self,
|
||||
source_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
target_trait_def_id: DefId,
|
||||
) -> ty::PolyTraitRef<'tcx> {
|
||||
let upcast_trait_refs =
|
||||
traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id);
|
||||
|
||||
// must be exactly one trait ref or we'd get an ambig error etc
|
||||
if upcast_trait_refs.len() != 1 {
|
||||
span_bug!(
|
||||
self.span,
|
||||
"cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`",
|
||||
source_trait_ref,
|
||||
target_trait_def_id,
|
||||
upcast_trait_refs
|
||||
);
|
||||
}
|
||||
|
||||
upcast_trait_refs.into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
fn replace_bound_vars_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T
|
||||
where
|
||||
T: TypeFoldable<'tcx> + Copy,
|
||||
{
|
||||
self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value)
|
||||
}
|
||||
}
|
||||
625
compiler/rustc_hir_analysis/src/check/method/mod.rs
Normal file
625
compiler/rustc_hir_analysis/src/check/method/mod.rs
Normal file
@@ -0,0 +1,625 @@
|
||||
//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information.
|
||||
//!
|
||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
|
||||
|
||||
mod confirm;
|
||||
mod prelude2021;
|
||||
pub mod probe;
|
||||
mod suggest;
|
||||
|
||||
pub use self::suggest::SelfSource;
|
||||
pub use self::MethodError::*;
|
||||
|
||||
use crate::check::{Expectation, FnCtxt};
|
||||
use crate::ObligationCause;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Namespace};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::{self, InferOk};
|
||||
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
||||
use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
||||
use self::probe::{IsSuggestion, ProbeScope};
|
||||
|
||||
pub fn provide(providers: &mut ty::query::Providers) {
|
||||
probe::provide(providers);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MethodCallee<'tcx> {
|
||||
/// Impl method ID, for inherent methods, or trait method ID, otherwise.
|
||||
pub def_id: DefId,
|
||||
pub substs: SubstsRef<'tcx>,
|
||||
|
||||
/// Instantiated method signature, i.e., it has been
|
||||
/// substituted, normalized, and has had late-bound
|
||||
/// lifetimes replaced with inference variables.
|
||||
pub sig: ty::FnSig<'tcx>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MethodError<'tcx> {
|
||||
// Did not find an applicable method, but we did find various near-misses that may work.
|
||||
NoMatch(NoMatchData<'tcx>),
|
||||
|
||||
// Multiple methods might apply.
|
||||
Ambiguity(Vec<CandidateSource>),
|
||||
|
||||
// Found an applicable method, but it is not visible. The third argument contains a list of
|
||||
// not-in-scope traits which may work.
|
||||
PrivateMatch(DefKind, DefId, Vec<DefId>),
|
||||
|
||||
// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
|
||||
// forgotten to import a trait.
|
||||
IllegalSizedBound(Vec<DefId>, bool, Span),
|
||||
|
||||
// Found a match, but the return type is wrong
|
||||
BadReturnType,
|
||||
}
|
||||
|
||||
// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
|
||||
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
|
||||
#[derive(Debug)]
|
||||
pub struct NoMatchData<'tcx> {
|
||||
pub static_candidates: Vec<CandidateSource>,
|
||||
pub unsatisfied_predicates:
|
||||
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
|
||||
pub out_of_scope_traits: Vec<DefId>,
|
||||
pub lev_candidate: Option<ty::AssocItem>,
|
||||
pub mode: probe::Mode,
|
||||
}
|
||||
|
||||
// A pared down enum describing just the places from which a method
|
||||
// candidate can arise. Used for error reporting only.
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum CandidateSource {
|
||||
Impl(DefId),
|
||||
Trait(DefId /* trait id */),
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Determines whether the type `self_ty` supports a method name `method_name` or not.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn method_exists(
|
||||
&self,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr_id: hir::HirId,
|
||||
allow_private: bool,
|
||||
) -> bool {
|
||||
let mode = probe::Mode::MethodCall;
|
||||
match self.probe_for_name(
|
||||
method_name.span,
|
||||
mode,
|
||||
method_name,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
call_expr_id,
|
||||
ProbeScope::TraitsInScope,
|
||||
) {
|
||||
Ok(..) => true,
|
||||
Err(NoMatch(..)) => false,
|
||||
Err(Ambiguity(..)) => true,
|
||||
Err(PrivateMatch(..)) => allow_private,
|
||||
Err(IllegalSizedBound(..)) => true,
|
||||
Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a suggestion to call the given method to the provided diagnostic.
|
||||
#[instrument(level = "debug", skip(self, err, call_expr))]
|
||||
pub(crate) fn suggest_method_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
msg: &str,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr: &hir::Expr<'_>,
|
||||
span: Option<Span>,
|
||||
) {
|
||||
let params = self
|
||||
.probe_for_name(
|
||||
method_name.span,
|
||||
probe::Mode::MethodCall,
|
||||
method_name,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
call_expr.hir_id,
|
||||
ProbeScope::TraitsInScope,
|
||||
)
|
||||
.map(|pick| {
|
||||
let sig = self.tcx.fn_sig(pick.item.def_id);
|
||||
sig.inputs().skip_binder().len().saturating_sub(1)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
// Account for `foo.bar<T>`;
|
||||
let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
|
||||
let (suggestion, applicability) = (
|
||||
format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
|
||||
if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
|
||||
);
|
||||
|
||||
err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
|
||||
}
|
||||
|
||||
/// Performs method lookup. If lookup is successful, it will return the callee
|
||||
/// and store an appropriate adjustment for the self-expr. In some cases it may
|
||||
/// report an error (e.g., invoking the `drop` method).
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`:
|
||||
///
|
||||
/// * `self`: the surrounding `FnCtxt` (!)
|
||||
/// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
|
||||
/// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`)
|
||||
/// * `span`: the span for the method call
|
||||
/// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`)
|
||||
/// * `self_expr`: the self expression (`foo`)
|
||||
/// * `args`: the expressions of the arguments (`a, b + 1, ...`)
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub fn lookup_method(
|
||||
&self,
|
||||
self_ty: Ty<'tcx>,
|
||||
segment: &hir::PathSegment<'_>,
|
||||
span: Span,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
self_expr: &'tcx hir::Expr<'tcx>,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
|
||||
let pick =
|
||||
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
|
||||
|
||||
self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
|
||||
|
||||
for import_id in &pick.import_ids {
|
||||
debug!("used_trait_import: {:?}", import_id);
|
||||
Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
|
||||
.unwrap()
|
||||
.insert(*import_id);
|
||||
}
|
||||
|
||||
self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
|
||||
|
||||
let result =
|
||||
self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
|
||||
debug!("result = {:?}", result);
|
||||
|
||||
if let Some(span) = result.illegal_sized_bound {
|
||||
let mut needs_mut = false;
|
||||
if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
|
||||
let trait_type = self
|
||||
.tcx
|
||||
.mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
|
||||
// We probe again to see if there might be a borrow mutability discrepancy.
|
||||
match self.lookup_probe(
|
||||
span,
|
||||
segment.ident,
|
||||
trait_type,
|
||||
call_expr,
|
||||
ProbeScope::TraitsInScope,
|
||||
) {
|
||||
Ok(ref new_pick) if *new_pick != pick => {
|
||||
needs_mut = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// We probe again, taking all traits into account (not only those in scope).
|
||||
let mut candidates = match self.lookup_probe(
|
||||
span,
|
||||
segment.ident,
|
||||
self_ty,
|
||||
call_expr,
|
||||
ProbeScope::AllTraits,
|
||||
) {
|
||||
// If we find a different result the caller probably forgot to import a trait.
|
||||
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)],
|
||||
Err(Ambiguity(ref sources)) => sources
|
||||
.iter()
|
||||
.filter_map(|source| {
|
||||
match *source {
|
||||
// Note: this cannot come from an inherent impl,
|
||||
// because the first probing succeeded.
|
||||
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
|
||||
CandidateSource::Trait(_) => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
|
||||
|
||||
return Err(IllegalSizedBound(candidates, needs_mut, span));
|
||||
}
|
||||
|
||||
Ok(result.callee)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, call_expr))]
|
||||
pub fn lookup_probe(
|
||||
&self,
|
||||
span: Span,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
scope: ProbeScope,
|
||||
) -> probe::PickResult<'tcx> {
|
||||
let mode = probe::Mode::MethodCall;
|
||||
let self_ty = self.resolve_vars_if_possible(self_ty);
|
||||
self.probe_for_name(
|
||||
span,
|
||||
mode,
|
||||
method_name,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
call_expr.hir_id,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn obligation_for_method(
|
||||
&self,
|
||||
span: Span,
|
||||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_input_types: Option<&[Ty<'tcx>]>,
|
||||
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
|
||||
{
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
|
||||
GenericParamDefKind::Type { .. } => {
|
||||
if param.index == 0 {
|
||||
return self_ty.into();
|
||||
} else if let Some(input_types) = opt_input_types {
|
||||
return input_types[param.index as usize - 1].into();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.var_for_def(span, param)
|
||||
});
|
||||
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, substs);
|
||||
|
||||
// Construct an obligation
|
||||
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
||||
(
|
||||
traits::Obligation::misc(
|
||||
span,
|
||||
self.body_id,
|
||||
self.param_env,
|
||||
poly_trait_ref.without_const().to_predicate(self.tcx),
|
||||
),
|
||||
substs,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn obligation_for_op_method(
|
||||
&self,
|
||||
span: Span,
|
||||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_input_type: Option<Ty<'tcx>>,
|
||||
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
|
||||
{
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
|
||||
GenericParamDefKind::Type { .. } => {
|
||||
if param.index == 0 {
|
||||
return self_ty.into();
|
||||
} else if let Some(input_type) = opt_input_type {
|
||||
return input_type.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.var_for_def(span, param)
|
||||
});
|
||||
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, substs);
|
||||
|
||||
// Construct an obligation
|
||||
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
||||
let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
|
||||
|
||||
(
|
||||
traits::Obligation::new(
|
||||
traits::ObligationCause::new(
|
||||
span,
|
||||
self.body_id,
|
||||
traits::BinOp {
|
||||
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||
is_lit: opt_input_expr
|
||||
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||
output_ty,
|
||||
},
|
||||
),
|
||||
self.param_env,
|
||||
poly_trait_ref.without_const().to_predicate(self.tcx),
|
||||
),
|
||||
substs,
|
||||
)
|
||||
}
|
||||
|
||||
/// `lookup_method_in_trait` is used for overloaded operators.
|
||||
/// It does a very narrow slice of what the normal probe/confirm path does.
|
||||
/// In particular, it doesn't really do any probing: it simply constructs
|
||||
/// an obligation for a particular trait with the given self type and checks
|
||||
/// whether that trait is implemented.
|
||||
#[instrument(level = "debug", skip(self, span))]
|
||||
pub(super) fn lookup_method_in_trait(
|
||||
&self,
|
||||
span: Span,
|
||||
m_name: Ident,
|
||||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_input_types: Option<&[Ty<'tcx>]>,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
let (obligation, substs) =
|
||||
self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
|
||||
self.construct_obligation_for_trait(
|
||||
span,
|
||||
m_name,
|
||||
trait_def_id,
|
||||
obligation,
|
||||
substs,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn lookup_op_method_in_trait(
|
||||
&self,
|
||||
span: Span,
|
||||
m_name: Ident,
|
||||
trait_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
opt_input_type: Option<Ty<'tcx>>,
|
||||
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
expected: Expectation<'tcx>,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
let (obligation, substs) = self.obligation_for_op_method(
|
||||
span,
|
||||
trait_def_id,
|
||||
self_ty,
|
||||
opt_input_type,
|
||||
opt_input_expr,
|
||||
expected,
|
||||
);
|
||||
self.construct_obligation_for_trait(
|
||||
span,
|
||||
m_name,
|
||||
trait_def_id,
|
||||
obligation,
|
||||
substs,
|
||||
opt_input_expr,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
// FIXME(#18741): it seems likely that we can consolidate some of this
|
||||
// code with the other method-lookup code. In particular, the second half
|
||||
// of this method is basically the same as confirmation.
|
||||
fn construct_obligation_for_trait(
|
||||
&self,
|
||||
span: Span,
|
||||
m_name: Ident,
|
||||
trait_def_id: DefId,
|
||||
obligation: traits::PredicateObligation<'tcx>,
|
||||
substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
|
||||
opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
is_op: bool,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!(?obligation);
|
||||
|
||||
// Now we want to know if this can be matched
|
||||
if !self.predicate_may_hold(&obligation) {
|
||||
debug!("--> Cannot match obligation");
|
||||
// Cannot be matched, no such method resolution is possible.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Trait must have a method named `m_name` and it should not have
|
||||
// type parameters or early-bound regions.
|
||||
let tcx = self.tcx;
|
||||
let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
|
||||
tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"operator trait does not have corresponding operator method",
|
||||
);
|
||||
return None;
|
||||
};
|
||||
let def_id = method_item.def_id;
|
||||
let generics = tcx.generics_of(def_id);
|
||||
assert_eq!(generics.params.len(), 0);
|
||||
|
||||
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
|
||||
let mut obligations = vec![];
|
||||
|
||||
// Instantiate late-bound regions and substitute the trait
|
||||
// parameters into the method type to get the actual method type.
|
||||
//
|
||||
// N.B., instantiate late-bound regions first so that
|
||||
// `instantiate_type_scheme` can normalize associated types that
|
||||
// may reference those regions.
|
||||
let fn_sig = tcx.bound_fn_sig(def_id);
|
||||
let fn_sig = fn_sig.subst(self.tcx, substs);
|
||||
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
|
||||
|
||||
let InferOk { value, obligations: o } = if is_op {
|
||||
self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
|
||||
} else {
|
||||
self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
|
||||
};
|
||||
let fn_sig = {
|
||||
obligations.extend(o);
|
||||
value
|
||||
};
|
||||
|
||||
// Register obligations for the parameters. This will include the
|
||||
// `Self` parameter, which in turn has a bound of the main trait,
|
||||
// so this also effectively registers `obligation` as well. (We
|
||||
// used to register `obligation` explicitly, but that resulted in
|
||||
// double error messages being reported.)
|
||||
//
|
||||
// Note that as the method comes from a trait, it should not have
|
||||
// any late-bound regions appearing in its bounds.
|
||||
let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
|
||||
|
||||
let InferOk { value, obligations: o } = if is_op {
|
||||
self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
|
||||
} else {
|
||||
self.normalize_associated_types_in_as_infer_ok(span, bounds)
|
||||
};
|
||||
let bounds = {
|
||||
obligations.extend(o);
|
||||
value
|
||||
};
|
||||
|
||||
assert!(!bounds.has_escaping_bound_vars());
|
||||
|
||||
let cause = if is_op {
|
||||
ObligationCause::new(
|
||||
span,
|
||||
self.body_id,
|
||||
traits::BinOp {
|
||||
rhs_span: opt_input_expr.map(|expr| expr.span),
|
||||
is_lit: opt_input_expr
|
||||
.map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
|
||||
output_ty: None,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
traits::ObligationCause::misc(span, self.body_id)
|
||||
};
|
||||
let predicates_cause = cause.clone();
|
||||
obligations.extend(traits::predicates_for_generics(
|
||||
move |_, _| predicates_cause.clone(),
|
||||
self.param_env,
|
||||
bounds,
|
||||
));
|
||||
|
||||
// Also add an obligation for the method type being well-formed.
|
||||
let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
|
||||
debug!(
|
||||
"lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
|
||||
method_ty, obligation
|
||||
);
|
||||
obligations.push(traits::Obligation::new(
|
||||
cause,
|
||||
self.param_env,
|
||||
ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
|
||||
));
|
||||
|
||||
let callee = MethodCallee { def_id, substs, sig: fn_sig };
|
||||
|
||||
debug!("callee = {:?}", callee);
|
||||
|
||||
Some(InferOk { obligations, value: callee })
|
||||
}
|
||||
|
||||
/// Performs a [full-qualified function call] (formerly "universal function call") lookup. If
|
||||
/// lookup is successful, it will return the type of definition and the [`DefId`] of the found
|
||||
/// function definition.
|
||||
///
|
||||
/// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// Given a function call like `Foo::bar::<T1,...Tn>(...)`:
|
||||
///
|
||||
/// * `self`: the surrounding `FnCtxt` (!)
|
||||
/// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`)
|
||||
/// * `method_name`: the identifier of the function within the container type (`bar`)
|
||||
/// * `self_ty`: the type to search within (`Foo`)
|
||||
/// * `self_ty_span` the span for the type being searched within (span of `Foo`)
|
||||
/// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn resolve_fully_qualified_call(
|
||||
&self,
|
||||
span: Span,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
self_ty_span: Span,
|
||||
expr_id: hir::HirId,
|
||||
) -> Result<(DefKind, DefId), MethodError<'tcx>> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
// Check if we have an enum variant.
|
||||
if let ty::Adt(adt_def, _) = self_ty.kind() {
|
||||
if adt_def.is_enum() {
|
||||
let variant_def = adt_def
|
||||
.variants()
|
||||
.iter()
|
||||
.find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
|
||||
if let Some(variant_def) = variant_def {
|
||||
// Braced variants generate unusable names in value namespace (reserved for
|
||||
// possible future use), so variants resolved as associated items may refer to
|
||||
// them as well. It's ok to use the variant's id as a ctor id since an
|
||||
// error will be reported on any use of such resolution anyway.
|
||||
let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id);
|
||||
tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span));
|
||||
return Ok((
|
||||
DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind),
|
||||
ctor_def_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pick = self.probe_for_name(
|
||||
span,
|
||||
probe::Mode::Path,
|
||||
method_name,
|
||||
IsSuggestion(false),
|
||||
self_ty,
|
||||
expr_id,
|
||||
ProbeScope::TraitsInScope,
|
||||
)?;
|
||||
|
||||
self.lint_fully_qualified_call_from_2018(
|
||||
span,
|
||||
method_name,
|
||||
self_ty,
|
||||
self_ty_span,
|
||||
expr_id,
|
||||
&pick,
|
||||
);
|
||||
|
||||
debug!(?pick);
|
||||
{
|
||||
let mut typeck_results = self.typeck_results.borrow_mut();
|
||||
let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
|
||||
for import_id in pick.import_ids {
|
||||
debug!(used_trait_import=?import_id);
|
||||
used_trait_imports.insert(import_id);
|
||||
}
|
||||
}
|
||||
|
||||
let def_kind = pick.item.kind.as_def_kind();
|
||||
tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
|
||||
Ok((def_kind, pick.item.def_id))
|
||||
}
|
||||
|
||||
/// Finds item with name `item_name` defined in impl/trait `def_id`
|
||||
/// and return it, or `None`, if no such item was defined there.
|
||||
pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> {
|
||||
self.tcx
|
||||
.associated_items(def_id)
|
||||
.find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id)
|
||||
.copied()
|
||||
}
|
||||
}
|
||||
418
compiler/rustc_hir_analysis/src/check/method/prelude2021.rs
Normal file
418
compiler/rustc_hir_analysis/src/check/method/prelude2021.rs
Normal file
@@ -0,0 +1,418 @@
|
||||
use hir::def_id::DefId;
|
||||
use hir::HirId;
|
||||
use hir::ItemKind;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::{Adt, Array, Ref, Ty};
|
||||
use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
|
||||
use rustc_span::symbol::kw::{Empty, Underscore};
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
|
||||
use crate::check::{
|
||||
method::probe::{self, Pick},
|
||||
FnCtxt,
|
||||
};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub(super) fn lint_dot_call_from_2018(
|
||||
&self,
|
||||
self_ty: Ty<'tcx>,
|
||||
segment: &hir::PathSegment<'_>,
|
||||
span: Span,
|
||||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
self_expr: &'tcx hir::Expr<'tcx>,
|
||||
pick: &Pick<'tcx>,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
) {
|
||||
debug!(
|
||||
"lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
|
||||
segment.ident, self_ty, call_expr, self_expr
|
||||
);
|
||||
|
||||
// Rust 2021 and later is already using the new prelude
|
||||
if span.rust_2021() {
|
||||
return;
|
||||
}
|
||||
|
||||
let prelude_or_array_lint = match segment.ident.name {
|
||||
// `try_into` was added to the prelude in Rust 2021.
|
||||
sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
|
||||
// `into_iter` wasn't added to the prelude,
|
||||
// but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
|
||||
// before Rust 2021, which results in the same problem.
|
||||
// It is only a problem for arrays.
|
||||
sym::into_iter if let Array(..) = self_ty.kind() => {
|
||||
// In this case, it wasn't really a prelude addition that was the problem.
|
||||
// Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
|
||||
rustc_lint::ARRAY_INTO_ITER
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// No need to lint if method came from std/core, as that will now be in the prelude
|
||||
if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
|
||||
return;
|
||||
}
|
||||
|
||||
if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
|
||||
// avoid repeatedly adding unneeded `&*`s
|
||||
if pick.autoderefs == 1
|
||||
&& matches!(
|
||||
pick.autoref_or_ptr_adjustment,
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
|
||||
)
|
||||
&& matches!(self_ty.kind(), Ref(..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if it's an inherent `self` method (not `&self` or `&mut self`), it will take
|
||||
// precedence over the `TryInto` impl, and thus won't break in 2021 edition
|
||||
if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inherent impls only require not relying on autoref and autoderef in order to
|
||||
// ensure that the trait implementation won't be used
|
||||
self.tcx.struct_span_lint_hir(
|
||||
prelude_or_array_lint,
|
||||
self_expr.hir_id,
|
||||
self_expr.span,
|
||||
|lint| {
|
||||
let sp = self_expr.span;
|
||||
|
||||
let mut lint = lint.build(&format!(
|
||||
"trait method `{}` will become ambiguous in Rust 2021",
|
||||
segment.ident.name
|
||||
));
|
||||
|
||||
let derefs = "*".repeat(pick.autoderefs);
|
||||
|
||||
let autoref = match pick.autoref_or_ptr_adjustment {
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref {
|
||||
mutbl: Mutability::Mut,
|
||||
..
|
||||
}) => "&mut ",
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref {
|
||||
mutbl: Mutability::Not,
|
||||
..
|
||||
}) => "&",
|
||||
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
||||
};
|
||||
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
|
||||
{
|
||||
let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
||||
pick.autoref_or_ptr_adjustment
|
||||
{
|
||||
format!("{}{} as *const _", derefs, self_expr)
|
||||
} else {
|
||||
format!("{}{}{}", autoref, derefs, self_expr)
|
||||
};
|
||||
|
||||
lint.span_suggestion(
|
||||
sp,
|
||||
"disambiguate the method call",
|
||||
format!("({})", self_adjusted),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
||||
pick.autoref_or_ptr_adjustment
|
||||
{
|
||||
format!("{}(...) as *const _", derefs)
|
||||
} else {
|
||||
format!("{}{}...", autoref, derefs)
|
||||
};
|
||||
lint.span_help(
|
||||
sp,
|
||||
&format!("disambiguate the method call with `({})`", self_adjusted,),
|
||||
);
|
||||
}
|
||||
|
||||
lint.emit();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// trait implementations require full disambiguation to not clash with the new prelude
|
||||
// additions (i.e. convert from dot-call to fully-qualified call)
|
||||
self.tcx.struct_span_lint_hir(
|
||||
prelude_or_array_lint,
|
||||
call_expr.hir_id,
|
||||
call_expr.span,
|
||||
|lint| {
|
||||
let sp = call_expr.span;
|
||||
let trait_name = self.trait_path_or_bare_name(
|
||||
span,
|
||||
call_expr.hir_id,
|
||||
pick.item.container_id(self.tcx),
|
||||
);
|
||||
|
||||
let mut lint = lint.build(&format!(
|
||||
"trait method `{}` will become ambiguous in Rust 2021",
|
||||
segment.ident.name
|
||||
));
|
||||
|
||||
let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
|
||||
if precise {
|
||||
let args = args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
|
||||
format!(
|
||||
", {}",
|
||||
self.sess().source_map().span_to_snippet(span).unwrap()
|
||||
)
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
lint.span_suggestion(
|
||||
sp,
|
||||
"disambiguate the associated function",
|
||||
format!(
|
||||
"{}::{}{}({}{})",
|
||||
trait_name,
|
||||
segment.ident.name,
|
||||
if let Some(args) = segment.args.as_ref().and_then(|args| self
|
||||
.sess()
|
||||
.source_map()
|
||||
.span_to_snippet(args.span_ext)
|
||||
.ok())
|
||||
{
|
||||
// Keep turbofish.
|
||||
format!("::{}", args)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self_adjusted,
|
||||
args,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
lint.span_help(
|
||||
sp,
|
||||
&format!(
|
||||
"disambiguate the associated function with `{}::{}(...)`",
|
||||
trait_name, segment.ident,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
lint.emit();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lint_fully_qualified_call_from_2018(
|
||||
&self,
|
||||
span: Span,
|
||||
method_name: Ident,
|
||||
self_ty: Ty<'tcx>,
|
||||
self_ty_span: Span,
|
||||
expr_id: hir::HirId,
|
||||
pick: &Pick<'tcx>,
|
||||
) {
|
||||
// Rust 2021 and later is already using the new prelude
|
||||
if span.rust_2021() {
|
||||
return;
|
||||
}
|
||||
|
||||
// These are the fully qualified methods added to prelude in Rust 2021
|
||||
if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No need to lint if method came from std/core, as that will now be in the prelude
|
||||
if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For from_iter, check if the type actually implements FromIterator.
|
||||
// If we know it does not, we don't need to warn.
|
||||
if method_name.name == sym::from_iter {
|
||||
if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
|
||||
if !self
|
||||
.infcx
|
||||
.type_implements_trait(
|
||||
trait_def_id,
|
||||
self_ty,
|
||||
InternalSubsts::empty(),
|
||||
self.param_env,
|
||||
)
|
||||
.may_apply()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
|
||||
// since such methods take precedence over trait methods.
|
||||
if matches!(pick.kind, probe::PickKind::InherentImplPick) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| {
|
||||
// "type" refers to either a type or, more likely, a trait from which
|
||||
// the associated function or method is from.
|
||||
let container_id = pick.item.container_id(self.tcx);
|
||||
let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
|
||||
let trait_generics = self.tcx.generics_of(container_id);
|
||||
|
||||
let trait_name =
|
||||
if trait_generics.params.len() <= trait_generics.has_self as usize {
|
||||
trait_path
|
||||
} else {
|
||||
let counts = trait_generics.own_counts();
|
||||
format!(
|
||||
"{}<{}>",
|
||||
trait_path,
|
||||
std::iter::repeat("'_")
|
||||
.take(counts.lifetimes)
|
||||
.chain(std::iter::repeat("_").take(
|
||||
counts.types + counts.consts - trait_generics.has_self as usize
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)
|
||||
};
|
||||
|
||||
let mut lint = lint.build(&format!(
|
||||
"trait-associated function `{}` will become ambiguous in Rust 2021",
|
||||
method_name.name
|
||||
));
|
||||
|
||||
let mut self_ty_name = self_ty_span
|
||||
.find_ancestor_inside(span)
|
||||
.and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
|
||||
.unwrap_or_else(|| self_ty.to_string());
|
||||
|
||||
// Get the number of generics the self type has (if an Adt) unless we can determine that
|
||||
// the user has written the self type with generics already which we (naively) do by looking
|
||||
// for a "<" in `self_ty_name`.
|
||||
if !self_ty_name.contains('<') {
|
||||
if let Adt(def, _) = self_ty.kind() {
|
||||
let generics = self.tcx.generics_of(def.did());
|
||||
if !generics.params.is_empty() {
|
||||
let counts = generics.own_counts();
|
||||
self_ty_name += &format!(
|
||||
"<{}>",
|
||||
std::iter::repeat("'_")
|
||||
.take(counts.lifetimes)
|
||||
.chain(std::iter::repeat("_").take(counts.types + counts.consts))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
lint.span_suggestion(
|
||||
span,
|
||||
"disambiguate the associated function",
|
||||
format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
lint.emit();
|
||||
});
|
||||
}
|
||||
|
||||
fn trait_path_or_bare_name(
|
||||
&self,
|
||||
span: Span,
|
||||
expr_hir_id: HirId,
|
||||
trait_def_id: DefId,
|
||||
) -> String {
|
||||
self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
|
||||
let key = self.tcx.def_key(trait_def_id);
|
||||
format!("{}", key.disambiguated_data.data)
|
||||
})
|
||||
}
|
||||
|
||||
fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
|
||||
let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
|
||||
let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
|
||||
if applicable_trait.import_ids.is_empty() {
|
||||
// The trait was declared within the module, we only need to use its name.
|
||||
return None;
|
||||
}
|
||||
|
||||
let import_items: Vec<_> = applicable_trait
|
||||
.import_ids
|
||||
.iter()
|
||||
.map(|&import_id| self.tcx.hir().expect_item(import_id))
|
||||
.collect();
|
||||
|
||||
// Find an identifier with which this trait was imported (note that `_` doesn't count).
|
||||
let any_id = import_items
|
||||
.iter()
|
||||
.filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
|
||||
.next();
|
||||
if let Some(any_id) = any_id {
|
||||
if any_id.name == Empty {
|
||||
// Glob import, so just use its name.
|
||||
return None;
|
||||
} else {
|
||||
return Some(format!("{}", any_id));
|
||||
}
|
||||
}
|
||||
|
||||
// All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
|
||||
// so just take the first one.
|
||||
match import_items[0].kind {
|
||||
ItemKind::Use(path, _) => Some(
|
||||
path.segments
|
||||
.iter()
|
||||
.map(|segment| segment.ident.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("::"),
|
||||
),
|
||||
_ => {
|
||||
span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a string version of the `expr` that includes explicit adjustments.
|
||||
/// Returns the string and also a bool indicating whether this is a *precise*
|
||||
/// suggestion.
|
||||
fn adjust_expr(
|
||||
&self,
|
||||
pick: &Pick<'tcx>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
outer: Span,
|
||||
) -> (String, bool) {
|
||||
let derefs = "*".repeat(pick.autoderefs);
|
||||
|
||||
let autoref = match pick.autoref_or_ptr_adjustment {
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
|
||||
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
|
||||
};
|
||||
|
||||
let (expr_text, precise) = if let Some(expr_text) = expr
|
||||
.span
|
||||
.find_ancestor_inside(outer)
|
||||
.and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
|
||||
{
|
||||
(expr_text, true)
|
||||
} else {
|
||||
("(..)".to_string(), false)
|
||||
};
|
||||
|
||||
let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
|
||||
pick.autoref_or_ptr_adjustment
|
||||
{
|
||||
format!("{}{} as *const _", derefs, expr_text)
|
||||
} else {
|
||||
format!("{}{}{}", autoref, derefs, expr_text)
|
||||
};
|
||||
|
||||
(adjusted_text, precise)
|
||||
}
|
||||
}
|
||||
1927
compiler/rustc_hir_analysis/src/check/method/probe.rs
Normal file
1927
compiler/rustc_hir_analysis/src/check/method/probe.rs
Normal file
File diff suppressed because it is too large
Load Diff
2365
compiler/rustc_hir_analysis/src/check/method/suggest.rs
Normal file
2365
compiler/rustc_hir_analysis/src/check/method/suggest.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user