diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7b8b5974fe7..75913910f14 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1,4 +1,5 @@ use either::Either; +use rustc_const_eval::util::{CallDesugaringKind, CallKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -26,7 +27,7 @@ use crate::{ use super::{ explain_borrow::{BorrowExplanation, LaterUseKind}, - FnSelfUseKind, IncludingDowncast, RegionName, RegionNameSource, UseSpans, + IncludingDowncast, RegionName, RegionNameSource, UseSpans, }; #[derive(Debug)] @@ -195,7 +196,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .map(|n| format!("`{}`", n)) .unwrap_or_else(|| "value".to_owned()); match kind { - FnSelfUseKind::FnOnceCall => { + CallKind::FnCall(once_did) if Some(once_did) == self.infcx.tcx.lang_items().fn_once_trait() => { err.span_label( fn_call_span, &format!( @@ -208,7 +209,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "this value implements `FnOnce`, which causes it to be moved when called", ); } - FnSelfUseKind::Operator { self_arg } => { + CallKind::Operator { self_arg, .. } => { + let self_arg = self_arg.unwrap(); err.span_label( fn_call_span, &format!( @@ -235,12 +237,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - FnSelfUseKind::Normal { - self_arg, - implicit_into_iter, - is_option_or_result, - } => { - if implicit_into_iter { + CallKind::Normal { self_arg, desugaring, is_option_or_result } => { + let self_arg = self_arg.unwrap(); + if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { err.span_label( fn_call_span, &format!( @@ -305,8 +304,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } - // Deref::deref takes &self, which cannot cause a move - FnSelfUseKind::DerefCoercion { .. } => unreachable!(), + // Other desugarings takes &self, which cannot cause a move + _ => unreachable!(), } } else { err.span_label( @@ -433,7 +432,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } if let UseSpans::FnSelfUse { - kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty }, + kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. }, .. } = use_spans { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 84acfbf941d..4400fed13b7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1,10 +1,10 @@ //! Borrow checker diagnostics. +use rustc_const_eval::util::call_kind; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItemGroup; use rustc_hir::GeneratorKind; use rustc_middle::mir::{ AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand, @@ -13,7 +13,7 @@ use rustc_middle::mir::{ use rustc_middle::ty::print::Print; use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; -use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span}; +use rustc_span::{symbol::sym, Span}; use rustc_target::abi::VariantIdx; use super::borrow_set::BorrowData; @@ -37,7 +37,7 @@ crate use mutability_errors::AccessKind; crate use outlives_suggestion::OutlivesSuggestionBuilder; crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors}; crate use region_name::{RegionName, RegionNameSource}; -use rustc_span::symbol::Ident; +crate use rustc_const_eval::util::CallKind; pub(super) struct IncludingDowncast(pub(super) bool); @@ -563,7 +563,7 @@ pub(super) enum UseSpans<'tcx> { fn_call_span: Span, /// The definition span of the method being called fn_span: Span, - kind: FnSelfUseKind<'tcx>, + kind: CallKind<'tcx>, }, /// This access is caused by a `match` or `if let` pattern. PatUse(Span), @@ -571,38 +571,15 @@ pub(super) enum UseSpans<'tcx> { OtherUse(Span), } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind<'tcx> { - /// A normal method call of the form `receiver.foo(a, b, c)` - Normal { - self_arg: Ident, - implicit_into_iter: bool, - /// Whether the self type of the method call has an `.as_ref()` method. - /// Used for better diagnostics. - is_option_or_result: bool, - }, - /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` - FnOnceCall, - /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) - Operator { self_arg: Ident }, - DerefCoercion { - /// The `Span` of the `Target` associated type - /// in the `Deref` impl we are using. - deref_target: Span, - /// The type `T::Deref` we are dereferencing to - deref_target_ty: Ty<'tcx>, - }, -} - impl UseSpans<'_> { pub(super) fn args_or_use(self) -> Span { match self { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -613,9 +590,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { path_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -626,9 +603,9 @@ impl UseSpans<'_> { UseSpans::ClosureUse { capture_kind_span: span, .. } | UseSpans::PatUse(span) | UseSpans::OtherUse(span) => span, - UseSpans::FnSelfUse { - fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. - } => fn_call_span, + UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => { + fn_call_span + } UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -904,67 +881,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return normal_ret; }; - let tcx = self.infcx.tcx; - let parent = tcx.parent(method_did); - let is_fn_once = parent == tcx.lang_items().fn_once_trait(); - let is_operator = !from_hir_call - && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); - let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); - let fn_call_span = *fn_span; - - let self_arg = tcx.fn_arg_names(method_did)[0]; - - debug!( - "terminator = {:?} from_hir_call={:?}", - self.body[location.block].terminator, from_hir_call + let kind = call_kind( + self.infcx.tcx, + self.param_env, + method_did, + method_substs, + *fn_span, + *from_hir_call, + Some(self.infcx.tcx.fn_arg_names(method_did)[0]), ); - // Check for a 'special' use of 'self' - - // an FnOnce call, an operator (e.g. `<<`), or a - // deref coercion. - let kind = if is_fn_once { - Some(FnSelfUseKind::FnOnceCall) - } else if is_operator { - Some(FnSelfUseKind::Operator { self_arg }) - } else if is_deref { - let deref_target = - tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { - Instance::resolve(tcx, self.param_env, deref_target, method_substs) - .transpose() - }); - if let Some(Ok(instance)) = deref_target { - let deref_target_ty = instance.ty(tcx, self.param_env); - Some(FnSelfUseKind::DerefCoercion { - deref_target: tcx.def_span(instance.def_id()), - deref_target_ty, - }) - } else { - None - } - } else { - None - }; - - let kind = kind.unwrap_or_else(|| { - // This isn't a 'special' use of `self` - debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); - let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn() - && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop); - let parent_self_ty = parent - .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) - .and_then(|did| match tcx.type_of(did).kind() { - ty::Adt(def, ..) => Some(def.did), - _ => None, - }); - let is_option_or_result = parent_self_ty.map_or(false, |def_id| { - matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) - }); - FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result } - }); - return FnSelfUse { var_span: stmt.source_info.span, - fn_call_span, + fn_call_span: *fn_span, fn_span: self .infcx .tcx diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 2934d921868..b33b779edda 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -1,3 +1,4 @@ +use rustc_const_eval::util::CallDesugaringKind; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; @@ -8,7 +9,7 @@ use rustc_mir_dataflow::move_paths::{ use rustc_span::{sym, Span, DUMMY_SP}; use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; -use crate::diagnostics::{FnSelfUseKind, UseSpans}; +use crate::diagnostics::{CallKind, UseSpans}; use crate::prefixes::PrefixSet; use crate::MirBorrowckCtxt; @@ -410,7 +411,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } else if let Some(UseSpans::FnSelfUse { - kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. }, + kind: + CallKind::Normal { desugaring: Some((CallDesugaringKind::ForLoopIntoIter, _)), .. }, .. }) = use_spans { diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index ec96064222b..bd16a8853a6 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -293,13 +293,13 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { } /// Emits an error if an expression cannot be evaluated in the current context. - pub fn check_op(&mut self, op: impl NonConstOp) { + pub fn check_op(&mut self, op: impl NonConstOp<'tcx>) { self.check_op_spanned(op, self.span); } /// Emits an error at the given `span` if an expression cannot be evaluated in the current /// context. - pub fn check_op_spanned(&mut self, op: O, span: Span) { + pub fn check_op_spanned>(&mut self, op: O, span: Span) { let gate = match op.status_in_item(self.ccx) { Status::Allowed => return, @@ -773,7 +773,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, .. } => { + TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => { let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id().to_def_id(); @@ -797,7 +797,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { if let Some(trait_id) = tcx.trait_of_item(callee) { trace!("attempting to call a trait method"); if !self.tcx.features().const_trait_impl { - self.check_op(ops::FnCallNonConst(callee, substs)); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } @@ -856,7 +862,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(callee, substs)); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } @@ -925,7 +937,13 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } if !nonconst_call_permission { - self.check_op(ops::FnCallNonConst(callee, substs)); + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + }); return; } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index a6da3ebba6e..c26b1e550ba 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -3,14 +3,20 @@ use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; +use rustc_middle::mir; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::{mir, ty::AssocKind}; +use rustc_middle::ty::{suggest_constraining_type_param, Adt, Param, TraitPredicate, Ty}; +use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; -use rustc_span::{symbol::Ident, Span, Symbol}; -use rustc_span::{BytePos, Pos}; +use rustc_span::{BytePos, Pos, Span, Symbol}; +use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; +use crate::util::{call_kind, CallDesugaringKind, CallKind}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Status { @@ -29,9 +35,9 @@ pub enum DiagnosticImportance { } /// An operation that is not *always* allowed in a const context. -pub trait NonConstOp: std::fmt::Debug { +pub trait NonConstOp<'tcx>: std::fmt::Debug { /// Returns an enum indicating whether this operation is allowed within the given item. - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -39,13 +45,13 @@ pub trait NonConstOp: std::fmt::Debug { DiagnosticImportance::Primary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; } #[derive(Debug)] pub struct FloatingPointOp; -impl NonConstOp for FloatingPointOp { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FloatingPointOp { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() == hir::ConstContext::ConstFn { Status::Unstable(sym::const_fn_floating_point_arithmetic) } else { @@ -53,7 +59,7 @@ impl NonConstOp for FloatingPointOp { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_floating_point_arithmetic, @@ -66,40 +72,120 @@ impl NonConstOp for FloatingPointOp { /// A function call where the callee is a pointer. #[derive(Debug)] pub struct FnCallIndirect; -impl NonConstOp for FnCallIndirect { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallIndirect { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn") } } /// A function call where the callee is not marked as `const`. -#[derive(Debug)] -pub struct FnCallNonConst<'tcx>(pub DefId, pub SubstsRef<'tcx>); -impl<'a> NonConstOp for FnCallNonConst<'a> { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - let FnCallNonConst(def_id, substs) = *self; - let mut err = struct_span_err!( - ccx.tcx.sess, - span, - E0015, - "cannot call non-const fn `{}` in {}s", - ccx.tcx.def_path_str_with_substs(def_id, substs), - ccx.const_kind() - ); - err.note(&format!( - "calls in {}s are limited to constant functions, \ - tuple structs and tuple variants", - ccx.const_kind(), - )); +#[derive(Debug, Clone, Copy)] +pub struct FnCallNonConst<'tcx> { + pub caller: DefId, + pub callee: DefId, + pub substs: SubstsRef<'tcx>, + pub span: Span, + pub from_hir_call: bool, +} - if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() { - if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind( - ccx.tcx, - Ident::with_dummy_span(sym::eq), - AssocKind::Fn, - trait_def_id, - ) { - if callee == eq_item.def_id && substs.len() == 2 { +impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { + let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self; + let ConstCx { tcx, param_env, .. } = *ccx; + + let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| { + let trait_ref = TraitRef::from_method(tcx, trait_id, substs); + + match self_ty.kind() { + Param(param_ty) => { + debug!(?param_ty); + if let Some(generics) = caller + .as_local() + .map(|id| tcx.hir().local_def_id_to_hir_id(id)) + .map(|id| tcx.hir().get(id)) + .as_ref() + .and_then(|node| node.generics()) + { + let constraint = with_no_trimmed_paths(|| { + format!("~const {}", trait_ref.print_only_trait_path()) + }); + suggest_constraining_type_param( + tcx, + generics, + &mut err, + ¶m_ty.name.as_str(), + &constraint, + None, + ); + } + } + Adt(..) => { + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + Binder::dummy(TraitPredicate { + trait_ref, + constness: BoundConstness::ConstIfConst, + polarity: ImplPolarity::Positive, + }), + ); + + let implsrc = tcx.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation) + }); + + if let Ok(Some(ImplSource::UserDefined(data))) = implsrc { + let span = + tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id)); + err.span_note(span, "impl defined here, but it is not `const`"); + } + } + _ => {} + } + + err + }; + + let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None); + + debug!(?call_kind); + + let mut err = match call_kind { + CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => { + macro_rules! error { + ($fmt:literal) => { + struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind(),) + }; + } + + let err = match kind { + CallDesugaringKind::ForLoopIntoIter => { + error!("cannot convert `{}` into an iterator in {}s") + } + CallDesugaringKind::QuestionBranch => { + error!("`?` cannot determine the branch of `{}` in {}s") + } + CallDesugaringKind::QuestionFromResidual => { + error!("`?` cannot convert from residual of `{}` in {}s") + } + CallDesugaringKind::TryBlockFromOutput => { + error!("`try` block cannot convert `{}` to the result in {}s") + } + }; + + diag_trait(err, self_ty, kind.trait_def_id(tcx)) + } + CallKind::Operator { trait_id, self_ty, .. } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot call non-const operator in {}s", + ccx.const_kind() + ); + + if Some(trait_id) == ccx.tcx.lang_items().eq_trait() { match (substs[0].unpack(), substs[1].unpack()) { (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty)) if self_ty == rhs_ty @@ -137,8 +223,43 @@ impl<'a> NonConstOp for FnCallNonConst<'a> { _ => {} } } + + diag_trait(err, self_ty, trait_id) } - } + CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => { + let mut err = struct_span_err!( + tcx.sess, + span, + E0015, + "cannot perform deref coercion on `{}` in {}s", + self_ty, + ccx.const_kind() + ); + + err.note(&format!("attempting to deref into `{}`", deref_target_ty)); + + // Check first whether the source is accessible (issue #87060) + if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + err.span_note(deref_target, "deref defined here"); + } + + diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap()) + } + _ => struct_span_err!( + ccx.tcx.sess, + span, + E0015, + "cannot call non-const fn `{}` in {}s", + ccx.tcx.def_path_str_with_substs(callee, substs), + ccx.const_kind(), + ), + }; + + err.note(&format!( + "calls in {}s are limited to constant functions, \ + tuple structs and tuple variants", + ccx.const_kind(), + )); err } @@ -150,8 +271,8 @@ impl<'a> NonConstOp for FnCallNonConst<'a> { #[derive(Debug)] pub struct FnCallUnstable(pub DefId, pub Option); -impl NonConstOp for FnCallUnstable { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let FnCallUnstable(def_id, feature) = *self; let mut err = ccx.tcx.sess.struct_span_err( @@ -176,8 +297,8 @@ impl NonConstOp for FnCallUnstable { #[derive(Debug)] pub struct FnPtrCast; -impl NonConstOp for FnPtrCast { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for FnPtrCast { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -185,7 +306,7 @@ impl NonConstOp for FnPtrCast { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -197,8 +318,8 @@ impl NonConstOp for FnPtrCast { #[derive(Debug)] pub struct Generator(pub hir::GeneratorKind); -impl NonConstOp for Generator { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for Generator { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { Status::Unstable(sym::const_async_blocks) } else { @@ -206,7 +327,7 @@ impl NonConstOp for Generator { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind()); if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 { feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg) @@ -218,8 +339,8 @@ impl NonConstOp for Generator { #[derive(Debug)] pub struct HeapAllocation; -impl NonConstOp for HeapAllocation { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for HeapAllocation { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -242,8 +363,8 @@ impl NonConstOp for HeapAllocation { #[derive(Debug)] pub struct InlineAsm; -impl NonConstOp for InlineAsm { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for InlineAsm { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -258,8 +379,8 @@ impl NonConstOp for InlineAsm { pub struct LiveDrop { pub dropped_at: Option, } -impl NonConstOp for LiveDrop { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for LiveDrop { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -278,8 +399,8 @@ impl NonConstOp for LiveDrop { /// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to /// the final value of the constant. pub struct TransientCellBorrow; -impl NonConstOp for TransientCellBorrow { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_refs_to_cell) } fn importance(&self) -> DiagnosticImportance { @@ -287,7 +408,7 @@ impl NonConstOp for TransientCellBorrow { // not additionally emit a feature gate error if activating the feature gate won't work. DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_refs_to_cell, @@ -302,8 +423,8 @@ impl NonConstOp for TransientCellBorrow { /// the final value of the constant, and thus we cannot allow this (for now). We may allow /// it in the future for static items. pub struct CellBorrow; -impl NonConstOp for CellBorrow { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for CellBorrow { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -339,8 +460,8 @@ impl NonConstOp for CellBorrow { /// static or const items. pub struct MutBorrow(pub hir::BorrowKind); -impl NonConstOp for MutBorrow { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutBorrow { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Forbidden } @@ -350,7 +471,7 @@ impl NonConstOp for MutBorrow { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -384,12 +505,12 @@ impl NonConstOp for MutBorrow { #[derive(Debug)] pub struct TransientMutBorrow(pub hir::BorrowKind); -impl NonConstOp for TransientMutBorrow { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let raw = match self.0 { hir::BorrowKind::Raw => "raw ", hir::BorrowKind::Ref => "", @@ -406,8 +527,8 @@ impl NonConstOp for TransientMutBorrow { #[derive(Debug)] pub struct MutDeref; -impl NonConstOp for MutDeref { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for MutDeref { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -416,7 +537,7 @@ impl NonConstOp for MutDeref { DiagnosticImportance::Secondary } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -429,8 +550,8 @@ impl NonConstOp for MutDeref { /// A call to a `panic()` lang item where the first argument is _not_ a `&str`. #[derive(Debug)] pub struct PanicNonStr; -impl NonConstOp for PanicNonStr { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for PanicNonStr { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { ccx.tcx.sess.struct_span_err( span, "argument to `panic!()` in a const context must have type `&str`", @@ -443,8 +564,8 @@ impl NonConstOp for PanicNonStr { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrComparison; -impl NonConstOp for RawPtrComparison { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -459,12 +580,12 @@ impl NonConstOp for RawPtrComparison { #[derive(Debug)] pub struct RawMutPtrDeref; -impl NonConstOp for RawMutPtrDeref { +impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_mut_refs) } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -479,8 +600,8 @@ impl NonConstOp for RawMutPtrDeref { /// allocation base addresses that are not known at compile-time. #[derive(Debug)] pub struct RawPtrToIntCast; -impl NonConstOp for RawPtrToIntCast { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = ccx .tcx .sess @@ -496,8 +617,8 @@ impl NonConstOp for RawPtrToIntCast { /// An access to a (non-thread-local) `static`. #[derive(Debug)] pub struct StaticAccess; -impl NonConstOp for StaticAccess { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { +impl<'tcx> NonConstOp<'tcx> for StaticAccess { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if let hir::ConstContext::Static(_) = ccx.const_kind() { Status::Allowed } else { @@ -505,7 +626,7 @@ impl NonConstOp for StaticAccess { } } - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = struct_span_err!( ccx.tcx.sess, span, @@ -530,8 +651,8 @@ impl NonConstOp for StaticAccess { /// An access to a thread-local `static`. #[derive(Debug)] pub struct ThreadLocalAccess; -impl NonConstOp for ThreadLocalAccess { - fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { +impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { struct_span_err!( ccx.tcx.sess, span, @@ -548,8 +669,8 @@ pub mod ty { #[derive(Debug)] pub struct MutRef(pub mir::LocalKind); - impl NonConstOp for MutRef { - fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for MutRef { + fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_mut_refs) } @@ -562,11 +683,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, @@ -578,7 +695,7 @@ pub mod ty { #[derive(Debug)] pub struct FnPtr(pub mir::LocalKind); - impl NonConstOp for FnPtr { + impl<'tcx> NonConstOp<'tcx> for FnPtr { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -588,7 +705,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -596,11 +713,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_fn_ptr_basics, @@ -612,16 +725,12 @@ pub mod ty { #[derive(Debug)] pub struct ImplTrait; - impl NonConstOp for ImplTrait { + impl<'tcx> NonConstOp<'tcx> for ImplTrait { fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { Status::Unstable(sym::const_impl_trait) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_impl_trait, @@ -633,7 +742,7 @@ pub mod ty { #[derive(Debug)] pub struct TraitBound(pub mir::LocalKind); - impl NonConstOp for TraitBound { + impl<'tcx> NonConstOp<'tcx> for TraitBound { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -643,7 +752,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -651,11 +760,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -676,7 +781,7 @@ pub mod ty { #[derive(Debug)] pub struct DynTrait(pub mir::LocalKind); - impl NonConstOp for DynTrait { + impl<'tcx> NonConstOp<'tcx> for DynTrait { fn importance(&self) -> DiagnosticImportance { match self.0 { mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary, @@ -686,7 +791,7 @@ pub mod ty { } } - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { + fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status { if ccx.const_kind() != hir::ConstContext::ConstFn { Status::Allowed } else { @@ -694,11 +799,7 @@ pub mod ty { } } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { let mut err = feature_err( &ccx.tcx.sess.parse_sess, sym::const_fn_trait_bound, @@ -720,16 +821,12 @@ pub mod ty { /// A trait bound with the `?const Trait` opt-out #[derive(Debug)] pub struct TraitBoundNotConst; - impl NonConstOp for TraitBoundNotConst { - fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst { + fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable(sym::const_trait_bound_opt_out) } - fn build_error<'tcx>( - &self, - ccx: &ConstCx<'_, 'tcx>, - span: Span, - ) -> DiagnosticBuilder<'tcx> { + fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { feature_err( &ccx.tcx.sess.parse_sess, sym::const_trait_bound_opt_out, diff --git a/compiler/rustc_const_eval/src/util/call_kind.rs b/compiler/rustc_const_eval/src/util/call_kind.rs new file mode 100644 index 00000000000..34925692664 --- /dev/null +++ b/compiler/rustc_const_eval/src/util/call_kind.rs @@ -0,0 +1,145 @@ +//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`, +//! as well as errors when attempting to call a non-const function in a const +//! context. + +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItemGroup; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, DesugaringKind, Span}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallDesugaringKind { + /// for _ in x {} calls x.into_iter() + ForLoopIntoIter, + /// x? calls x.branch() + QuestionBranch, + /// x? calls type_of(x)::from_residual() + QuestionFromResidual, + /// try { ..; x } calls type_of(x)::from_output(x) + TryBlockFromOutput, +} + +impl CallDesugaringKind { + pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { + match self { + Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::QuestionBranch | Self::TryBlockFromOutput => { + tcx.lang_items().try_trait().unwrap() + } + Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CallKind<'tcx> { + /// A normal method call of the form `receiver.foo(a, b, c)` + Normal { + self_arg: Option, + desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>, + /// Whether the self type of the method call has an `.as_ref()` method. + /// Used for better diagnostics. + is_option_or_result: bool, + }, + /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)` + FnCall(DefId), + /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) + Operator { self_arg: Option, trait_id: DefId, self_ty: Ty<'tcx> }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target: Span, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + }, +} + +pub fn call_kind<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + method_did: DefId, + method_substs: SubstsRef<'tcx>, + fn_call_span: Span, + from_hir_call: bool, + self_arg: Option, +) -> CallKind<'tcx> { + let parent = tcx.opt_associated_item(method_did).and_then(|assoc| match assoc.container { + AssocItemContainer::ImplContainer(impl_did) => tcx.trait_id_of_impl(impl_did), + AssocItemContainer::TraitContainer(trait_did) => Some(trait_did), + }); + + let fn_call = (!from_hir_call) + .then(|| parent) + .flatten() + .and_then(|p| tcx.lang_items().group(LangItemGroup::Fn).iter().find(|did| **did == p)); + + let operator = (!from_hir_call) + .then(|| parent) + .flatten() + .and_then(|p| tcx.lang_items().group(LangItemGroup::Op).iter().find(|did| **did == p)); + + let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. + let kind = if let Some(&trait_id) = fn_call { + Some(CallKind::FnCall(trait_id)) + } else if let Some(&trait_id) = operator { + Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) }) + } else if is_deref { + let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, param_env, deref_target, method_substs).transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, param_env); + Some(CallKind::DerefCoercion { + deref_target: tcx.def_span(instance.def_id()), + deref_target_ty, + self_ty: method_substs.type_at(0), + }) + } else { + None + } + } else { + None + }; + + kind.unwrap_or_else(|| { + // This isn't a 'special' use of `self` + debug!(?method_did, ?fn_call_span); + let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0))) + } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) { + if Some(method_did) == tcx.lang_items().branch_fn() { + Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0))) + } else if Some(method_did) == tcx.lang_items().from_residual_fn() { + Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0))) + } else { + None + } + } else if Some(method_did) == tcx.lang_items().from_output_fn() + && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock) + { + Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0))) + } else { + None + }; + let parent_self_ty = tcx + .parent(method_did) + .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl) + .and_then(|did| match tcx.type_of(did).kind() { + ty::Adt(def, ..) => Some(def.did), + _ => None, + }); + let is_option_or_result = parent_self_ty.map_or(false, |def_id| { + matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result)) + }); + CallKind::Normal { self_arg, desugaring, is_option_or_result } + }) +} diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs index 4a406f8bfd0..a1876bed83e 100644 --- a/compiler/rustc_const_eval/src/util/mod.rs +++ b/compiler/rustc_const_eval/src/util/mod.rs @@ -1,8 +1,10 @@ pub mod aggregate; mod alignment; +mod call_kind; pub mod collect_writes; mod find_self_call; pub use self::aggregate::expand_aggregate; pub use self::alignment::is_disaligned; +pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind}; pub use self::find_self_call::find_self_call; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 603971a6a91..3e0bc2e58fc 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -21,6 +21,7 @@ use std::lazy::SyncLazy; pub enum LangItemGroup { Op, + Fn, } const NUM_GROUPS: usize = 1; @@ -251,9 +252,9 @@ language_item_table! { DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; - Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + Fn(Fn), kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut(Fn), sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce(Fn), sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; @@ -264,8 +265,8 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; - PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + PartialEq(Op), sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd(Op), sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cab1d4e21c9..2e451502263 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -184,6 +184,7 @@ symbols! { Formatter, From, FromIterator, + FromResidual, Future, FxHashMap, FxHashSet, diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index eac426ad311..ba369e7f3aa 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -302,6 +302,7 @@ pub trait Try: FromResidual { enclosing_scope = "this function should return `Result` or `Option` to accept `?`" ), )] +#[rustc_diagnostic_item = "FromResidual"] #[unstable(feature = "try_trait_v2", issue = "84277")] pub trait FromResidual::Residual> { /// Constructs the type from a compatible `Residual` type.