diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/dropck.rs (renamed from compiler/rustc_typeck/src/check/dropck.rs) | 14 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/intrinsic.rs (renamed from compiler/rustc_typeck/src/check/intrinsic.rs) | 60 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/region.rs (renamed from compiler/rustc_typeck/src/check/region.rs) | 51 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/wfcheck.rs (renamed from compiler/rustc_typeck/src/check/wfcheck.rs) | 367 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/_match.rs (renamed from compiler/rustc_typeck/src/check/_match.rs) | 131 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/autoderef.rs (renamed from compiler/rustc_typeck/src/check/autoderef.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/cast.rs (renamed from compiler/rustc_typeck/src/check/cast.rs) | 139 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/coercion.rs (renamed from compiler/rustc_typeck/src/check/coercion.rs) | 172 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/demand.rs (renamed from compiler/rustc_typeck/src/check/demand.rs) | 218 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/diverges.rs (renamed from compiler/rustc_typeck/src/check/diverges.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/expectation.rs (renamed from compiler/rustc_typeck/src/check/expectation.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fallback.rs (renamed from compiler/rustc_typeck/src/check/fallback.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs) | 198 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs) | 25 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/gather_locals.rs (renamed from compiler/rustc_typeck/src/check/gather_locals.rs) | 3 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs) | 7 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs) | 19 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/confirm.rs (renamed from compiler/rustc_typeck/src/check/method/confirm.rs) | 20 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/prelude2021.rs (renamed from compiler/rustc_typeck/src/check/method/prelude2021.rs) | 122 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/probe.rs (renamed from compiler/rustc_typeck/src/check/method/probe.rs) | 180 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs (renamed from compiler/rustc_typeck/src/check/method/suggest.rs) | 663 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/pat.rs (renamed from compiler/rustc_typeck/src/check/pat.rs) | 77 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/place_op.rs (renamed from compiler/rustc_typeck/src/check/place_op.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/rvalue_scopes.rs (renamed from compiler/rustc_typeck/src/check/rvalue_scopes.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/upvar.rs (renamed from compiler/rustc_typeck/src/check/upvar.rs) | 34 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/writeback.rs (renamed from compiler/rustc_typeck/src/check/writeback.rs) | 118 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/callee.rs | 675 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/check.rs | 1712 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/closure.rs | 805 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/compare_method.rs | 1547 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/expr.rs | 2824 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/checks.rs | 1900 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/mod.rs | 296 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs | 912 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/generator_interior.rs | 632 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs | 309 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/inherited.rs | 183 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/intrinsicck.rs | 530 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/method/mod.rs | 658 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/mod.rs | 970 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/op.rs | 1076 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check/regionck.rs | 47 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/check_unused.rs | 196 |
45 files changed, 1676 insertions, 16222 deletions
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 321064ec0..a74016e22 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,4 +1,4 @@ -// FIXME(@lcnr): Move this module out of `rustc_typeck`. +// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. // // We don't do any drop checking during hir typeck. use crate::hir::def_id::{DefId, LocalDefId}; @@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs); let assumptions_in_impl_context = assumptions_in_impl_context.predicates; + debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates); + let self_param_env = tcx.param_env(self_type_did); // An earlier version of this code attempted to do this checking @@ -182,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - // Since struct predicates cannot have ~const, project the impl predicate - // onto one that ignores the constness. This is equivalent to saying that - // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` - // in the impl. - let non_const_a = - ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; - relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() + relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() @@ -196,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), + ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3f2a0da8d..609095c9c 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -7,10 +7,10 @@ use crate::errors::{ }; use crate::require_same_types; -use rustc_errors::struct_span_err; +use hir::def_id::DefId; +use rustc_errors::{struct_span_err, DiagnosticMessage}; use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>( ) { let (own_counts, span) = match &it.kind { hir::ForeignItemKind::Fn(.., generics) => { - let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts(); + let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts(); (own_counts, generics.span) } _ => { @@ -57,18 +57,25 @@ fn equate_intrinsic_type<'tcx>( { let fty = tcx.mk_fn_ptr(sig); let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType); - require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty); + require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty); } } /// Returns the unsafety of the given intrinsic. -pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { - match intrinsic { +pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety { + let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) { + true => hir::Unsafety::Normal, + false => hir::Unsafety::Unsafe, + }; + let is_in_list = match tcx.item_name(intrinsic_id) { // When adding a new intrinsic to this list, // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. sym::abort + | sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_uninit_valid | sym::size_of | sym::min_align_of | sym::needs_drop @@ -92,8 +99,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_id | sym::likely | sym::unlikely - | sym::ptr_guaranteed_eq - | sym::ptr_guaranteed_ne + | sym::ptr_guaranteed_cmp | sym::minnumf32 | sym::minnumf64 | sym::maxnumf32 @@ -102,16 +108,29 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::type_name | sym::forget | sym::black_box - | sym::variant_count => hir::Unsafety::Normal, + | sym::variant_count + | sym::ptr_mask => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, + }; + + if has_safe_attr != is_in_list { + tcx.sess.struct_span_err( + tcx.def_span(intrinsic_id), + DiagnosticMessage::Str(format!( + "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`", + tcx.item_name(intrinsic_id) + ))).emit(); } + + is_in_list } /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n))); - let intrinsic_name = tcx.item_name(it.def_id.to_def_id()); + let intrinsic_id = it.owner_id.to_def_id(); + let intrinsic_name = tcx.item_name(intrinsic_id); let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds( @@ -158,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { }; (n_tps, 0, inputs, output, hir::Unsafety::Unsafe) } else { - let unsafety = intrinsic_operation_unsafety(intrinsic_name); + let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, inputs, output) = match intrinsic_name { sym::abort => (0, Vec::new(), tcx.types.never), sym::unreachable => (0, Vec::new(), tcx.types.never), @@ -200,6 +219,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { ], tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), ), + sym::ptr_mask => ( + 1, + vec![ + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + tcx.types.usize, + ], + tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), + ), + sym::copy | sym::copy_nonoverlapping => ( 1, vec![ @@ -289,8 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { (1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool])) } - sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool) + sym::ptr_guaranteed_cmp => { + (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8) } sym::const_allocate => { @@ -465,7 +493,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()), sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)), sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)), - sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)), + sym::simd_cast + | sym::simd_as + | sym::simd_cast_ptr + | sym::simd_expose_addr + | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)), sym::simd_bitmask => (2, vec![param(0)], param(1)), sym::simd_select | sym::simd_select_bitmask => { (2, vec![param(0), param(1), param(1)], param(1)) diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 0081e9049..ff32329e4 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { + hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => { + // Let-else has a special lexical structure for variables. + // First we take a checkpoint of the current scope context here. + let mut prev_cx = visitor.cx; + + visitor.enter_scope(Scope { + id: blk.hir_id.local_id, + data: ScopeData::Remainder(FirstStatementIndex::new(i)), + }); + visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement); + // We need to back out temporarily to the last enclosing scope + // for the `else` block, so that even the temporaries receiving + // extended lifetime will be dropped inside this block. + // We are visiting the `else` block in this order so that + // the sequence of visits agree with the order in the default + // `hir::intravisit` visitor. + mem::swap(&mut prev_cx, &mut visitor.cx); + visitor.terminating_scopes.insert(els.hir_id.local_id); + visitor.visit_block(els); + // From now on, we continue normally. + visitor.cx = prev_cx; + } hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => { // Each declaration introduces a subscope for bindings // introduced by the declaration; this subscope covers a @@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h data: ScopeData::Remainder(FirstStatementIndex::new(i)), }); visitor.cx.var_parent = visitor.cx.parent; + visitor.visit_stmt(statement) } - hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), } - visitor.visit_stmt(statement) } walk_list!(visitor, visit_expr, &blk.expr); } @@ -229,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h ) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. - terminating(r.hir_id.local_id); - } + // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries + // should live beyond the immediate expression + if !matches!(r.kind, hir::ExprKind::Let(_)) { + terminating(r.hir_id.local_id); + } + } hir::ExprKind::If(_, ref then, Some(ref otherwise)) => { terminating(then.hir_id.local_id); terminating(otherwise.hir_id.local_id); @@ -460,7 +487,6 @@ fn resolve_local<'tcx>( visitor: &mut RegionResolutionVisitor<'tcx>, pat: Option<&'tcx hir::Pat<'tcx>>, init: Option<&'tcx hir::Expr<'tcx>>, - els: Option<&'tcx hir::Block<'tcx>>, ) { debug!("resolve_local(pat={:?}, init={:?})", pat, init); @@ -547,9 +573,6 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { visitor.visit_pat(pat); } - if let Some(els) = els { - visitor.visit_block(els); - } /// Returns `true` if `pat` match the `P&` non-terminal. /// @@ -587,8 +610,7 @@ fn resolve_local<'tcx>( // & expression, and its lifetime would be extended to the end of the block (due // to a different rule, not the below code). match pat.kind { - PatKind::Binding(hir::BindingAnnotation::Ref, ..) - | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true, + PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true, PatKind::Struct(_, ref field_pats, _) => { field_pats.iter().any(|fp| is_binding_pat(&fp.pat)) @@ -607,10 +629,7 @@ fn resolve_local<'tcx>( PatKind::Box(ref subpat) => is_binding_pat(&subpat), PatKind::Ref(_, _) - | PatKind::Binding( - hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, - .., - ) + | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Path(_) | PatKind::Lit(_) @@ -770,7 +789,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { // (i.e., `'static`), which means that after `g` returns, it drops, // and all the associated destruction scope rules apply. self.cx.var_parent = None; - resolve_local(self, None, Some(&body.value), None); + resolve_local(self, None, Some(&body.value)); } if body.generator_kind.is_some() { @@ -797,7 +816,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { resolve_expr(self, ex); } fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init, l.els) + resolve_local(self, Some(&l.pat), l.init) } } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index d0334cd0d..a23575004 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1,5 +1,5 @@ -use crate::check::regionck::OutlivesEnvironmentExt; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; +use hir::def::DefKind; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; @@ -10,22 +10,21 @@ use rustc_hir::ItemKind; use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::Normalized; +use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{GenericArgKind, InternalSubsts}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::query::normalize::AtExt; -use rustc_trait_selection::traits::query::NoSolution; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; @@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) { let cause = traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)); + // for a type to be WF, we do not need to check if const trait predicates satisfy. + let param_env = self.param_env.without_const(); self.ocx.register_obligation(traits::Obligation::new( cause, - self.param_env, + param_env, ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), )); } @@ -86,31 +87,35 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( body_def_id: LocalDefId, f: F, ) where - F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>, + F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>), { let param_env = tcx.param_env(body_def_id); let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id); - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); - let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); - if !tcx.features().trivial_bounds { - wfcx.check_false_global_bounds() - } - let wf_tys = f(&mut wfcx); - let errors = wfcx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; - } + let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, body_id); - infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); - }) + let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + + if !tcx.features().trivial_bounds { + wfcx.check_false_global_bounds() + } + f(&mut wfcx); + let errors = wfcx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return; + } + + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); } -fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { let node = tcx.hir().expect_owner(def_id); match node { hir::OwnerNode::Crate(_) => {} @@ -142,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// the types first. #[instrument(skip(tcx), level = "debug")] fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); @@ -169,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // for `T` hir::ItemKind::Impl(ref impl_) => { let is_auto = tcx - .impl_trait_ref(item.def_id) + .impl_trait_ref(def_id) .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id)); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); @@ -205,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } } hir::ItemKind::Fn(ref sig, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl); + check_item_fn(tcx, def_id, item.ident, item.span, sig.decl); } hir::ItemKind::Static(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + check_item_type(tcx, def_id, ty.span, false); } hir::ItemKind::Const(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + check_item_type(tcx, def_id, ty.span, false); } hir::ItemKind::Struct(ref struct_def, ref ast_generics) => { check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); @@ -241,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); match item.kind { hir::ForeignItemKind::Fn(decl, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, decl) + check_item_fn(tcx, def_id, item.ident, item.span, decl) } - hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true), + hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true), hir::ForeignItemKind::Type => (), } } fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { - let def_id = trait_item.def_id; + let def_id = trait_item.owner_id.def_id; let (method_sig, span) = match trait_item.kind { hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span), @@ -266,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { _ => (None, trait_item.span), }; check_object_unsafe_self_trait_by_name(tcx, trait_item); - check_associated_item(tcx, trait_item.def_id, span, method_sig); + check_associated_item(tcx, def_id, span, method_sig); let encl_trait_def_id = tcx.local_parent(def_id); let encl_trait = tcx.hir().expect_item(encl_trait_def_id); - let encl_trait_def_id = encl_trait.def_id.to_def_id(); + let encl_trait_def_id = encl_trait.owner_id.to_def_id(); let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { Some("fn") } else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() { @@ -343,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe loop { let mut should_continue = false; for gat_item in associated_items { - let gat_def_id = gat_item.id.def_id; + let gat_def_id = gat_item.id.owner_id; let gat_item = tcx.associated_item(gat_def_id); // If this item is not an assoc ty, or has no substs, then it's not a GAT if gat_item.kind != ty::AssocKind::Type { @@ -360,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe // constrains the GAT with individually. let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None; for item in associated_items { - let item_def_id = item.id.def_id; + let item_def_id = item.id.owner_id; // Skip our own GAT, since it does not constrain itself at all. if item_def_id == gat_def_id { continue; @@ -383,11 +388,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe tcx, param_env, item_hir_id, - sig.output(), + sig.inputs_and_output, // We also assume that all of the function signature's parameter types // are well formed. &sig.inputs().iter().copied().collect(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -410,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe .copied() .collect::<Vec<_>>(), &FxHashSet::default(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -450,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe } for (gat_def_id, required_bounds) in required_bounds_by_item { - let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); + let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id); debug!(?required_bounds); let param_env = tcx.param_env(gat_def_id); let gat_hir = gat_item_hir.hir_id(); @@ -658,7 +663,7 @@ fn ty_known_to_outlive<'tcx>( resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| { let origin = infer::RelateParamBound(DUMMY_SP, ty, None); let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env); - outlives.type_must_outlive(origin, ty, region); + outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation); }) } @@ -676,7 +681,12 @@ fn region_known_to_outlive<'tcx>( use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; let origin = infer::RelateRegionParamBound(DUMMY_SP); // `region_a: region_b` -> `region_b <= region_a` - infcx.push_sub_region_constraint(origin, region_b, region_a); + infcx.push_sub_region_constraint( + origin, + region_b, + region_a, + ConstraintCategory::BoringNoLocation, + ); }) } @@ -688,26 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>( id: hir::HirId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxHashSet<Ty<'tcx>>, - add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>), + add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each // call individually. - tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id); - let region_bound_pairs = outlives_environment.region_bound_pairs(); + let infcx = tcx.infer_ctxt().build(); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + ); + let region_bound_pairs = outlives_environment.region_bound_pairs(); - add_constraints(&infcx, region_bound_pairs); + add_constraints(&infcx, region_bound_pairs); - let errors = infcx.resolve_regions(&outlives_environment); + infcx.process_registered_region_obligations( + outlives_environment.region_bound_pairs(), + param_env, + ); + let errors = infcx.resolve_regions(&outlives_environment); - debug!(?errors, "errors"); + debug!(?errors, "errors"); - // If we were able to prove that the type outlives the region without - // an error, it must be because of the implied or explicit bounds... - errors.is_empty() - }) + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() } /// TypeVisitor that looks for uses of GATs like @@ -761,7 +777,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { - [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()), + [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, _ => false, @@ -772,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { /// When this is done, suggest using `Self` instead. fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) { let (trait_name, trait_def_id) = - match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) { + match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.def_id), + hir::ItemKind::Trait(..) => (item.ident, item.owner_id), _ => return, }, _ => return, @@ -782,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem let mut trait_should_be_self = vec![]; match &item.kind { hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty)) - if could_be_self(trait_def_id, ty) => + if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span) } hir::TraitItemKind::Fn(sig, _) => { for ty in sig.decl.inputs { - if could_be_self(trait_def_id, ty) { + if could_be_self(trait_def_id.def_id, ty) { trait_should_be_self.push(ty.span); } } match sig.decl.output { - hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => { + hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span); } _ => {} @@ -822,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { - let def_id = impl_item.def_id; - let (method_sig, span) = match impl_item.kind { hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span), // Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`. - hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span), + hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span), _ => (None, impl_item.span), }; - check_associated_item(tcx, def_id, span, method_sig); + check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig); } fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { @@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } } -#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))] +#[instrument(level = "debug", skip(tcx, span, sig_if_method))] fn check_associated_item( tcx: TyCtxt<'_>, item_id: LocalDefId, @@ -976,15 +990,9 @@ fn check_associated_item( enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { let item = tcx.associated_item(item_id); - let (mut implied_bounds, self_ty) = match item.container { - ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param), - ty::ImplContainer => { - let def_id = item.container_id(tcx); - ( - impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span), - tcx.type_of(def_id), - ) - } + let self_ty = match item.container { + ty::TraitContainer => tcx.types.self_param, + ty::ImplContainer => tcx.type_of(item.container_id(tcx)), }; match item.kind { @@ -1002,7 +1010,6 @@ fn check_associated_item( sig, hir_sig.decl, item.def_id.expect_local(), - &mut implied_bounds, ); check_method_receiver(wfcx, hir_sig, item, self_ty); } @@ -1017,8 +1024,6 @@ fn check_associated_item( } } } - - implied_bounds }) } @@ -1040,9 +1045,11 @@ fn check_type_defn<'tcx, F>( ) where F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + let _ = tcx.representability(item.owner_id.def_id); + + enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { let variants = lookup_fields(wfcx); - let packed = tcx.adt_def(item.def_id).repr().packed(); + let packed = tcx.adt_def(item.owner_id).repr().packed(); for variant in &variants { // All field types must be well-formed. @@ -1066,7 +1073,7 @@ fn check_type_defn<'tcx, F>( // Just treat unresolved type expression as if it needs drop. true } else { - ty.needs_drop(tcx, tcx.param_env(item.def_id)) + ty.needs_drop(tcx, tcx.param_env(item.owner_id)) } } }; @@ -1098,8 +1105,6 @@ fn check_type_defn<'tcx, F>( // Explicit `enum` discriminant values must const-evaluate successfully. if let Some(discr_def_id) = variant.explicit_discr { - let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id()); - let cause = traits::ObligationCause::new( tcx.def_span(discr_def_id), wfcx.body_id, @@ -1108,31 +1113,28 @@ fn check_type_defn<'tcx, F>( wfcx.register_obligation(traits::Obligation::new( cause, wfcx.param_env, - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new( - ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), - discr_substs, - ))) + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable( + ty::Const::from_anon_const(tcx, discr_def_id), + )) .to_predicate(tcx), )); } } - check_where_clauses(wfcx, item.span, item.def_id); - - // No implied bounds in a struct definition. - FxHashSet::default() + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } #[instrument(skip(tcx, item))] fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - debug!(?item.def_id); + debug!(?item.owner_id); - let trait_def = tcx.trait_def(item.def_id); + let def_id = item.owner_id.def_id; + let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) { - for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) { + for associated_def_id in &*tcx.associated_item_def_ids(def_id) { struct_span_err!( tcx.sess, tcx.def_span(*associated_def_id), @@ -1143,10 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } } - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - check_where_clauses(wfcx, item.span, item.def_id); - - FxHashSet::default() + enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + check_where_clauses(wfcx, item.span, def_id) }); // Only check traits, don't check trait aliases @@ -1186,9 +1186,7 @@ fn check_item_fn( ) { enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { let sig = tcx.fn_sig(def_id); - let mut implied_bounds = FxHashSet::default(); - check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds); - implied_bounds + check_fn_or_method(wfcx, ident.span, sig, decl, def_id); }) } @@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo tcx.require_lang_item(LangItem::Sync, Some(ty_span)), ); } - - // No implied bounds in a const, etc. - FxHashSet::default() }); } -#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] +#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] fn check_impl<'tcx>( tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>, @@ -1245,13 +1240,13 @@ fn check_impl<'tcx>( ast_trait_ref: &Option<hir::TraitRef<'_>>, constness: hir::Constness, ) { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { match *ast_trait_ref { Some(ref ast_trait_ref) => { // `#[rustc_reservation_impl]` impls are not real impls and // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). - let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap(); + let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap(); let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); let trait_pred = ty::TraitPredicate { trait_ref, @@ -1273,19 +1268,21 @@ fn check_impl<'tcx>( wfcx.register_obligations(obligations); } None => { - let self_ty = tcx.type_of(item.def_id); - let self_ty = wfcx.normalize(item.span, None, self_ty); + let self_ty = tcx.type_of(item.owner_id); + let self_ty = wfcx.normalize( + item.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), + self_ty, + ); wfcx.register_wf_obligation( ast_self_ty.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty.into(), ); } } - check_where_clauses(wfcx, item.span, item.def_id); - - impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span) + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } @@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id // parameter includes another (e.g., `<T, U = T>`). In those cases, we can't // be sure if it will error or not as user might always specify the other. if !ty.needs_subst() { - wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into()); + wfcx.register_wf_obligation( + tcx.def_span(param.def_id), + Some(WellFormedLoc::Ty(param.def_id.expect_local())), + ty.into(), + ); } } } @@ -1426,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id let substituted_pred = predicates.rebind(pred).subst(tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. - if substituted_pred.has_param_types_or_consts() - || param_count.params.len() > 1 - || has_region + if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region { None } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) { @@ -1465,21 +1464,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id assert_eq!(predicates.predicates.len(), predicates.spans.len()); let wf_obligations = iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| { - traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp) + traits::wf::predicate_obligations( + infcx, + wfcx.param_env.without_const(), + wfcx.body_id, + p, + sp, + ) }); let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); wfcx.register_obligations(obligations); } -#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))] +#[instrument(level = "debug", skip(wfcx, span, hir_decl))] fn check_fn_or_method<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, sig: ty::PolyFnSig<'tcx>, hir_decl: &hir::FnDecl<'_>, def_id: LocalDefId, - implied_bounds: &mut FxHashSet<Ty<'tcx>>, ) { let tcx = wfcx.tcx(); let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); @@ -1521,23 +1525,66 @@ fn check_fn_or_method<'tcx>( ); } - implied_bounds.extend(sig.inputs()); - - wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into()); + wfcx.register_wf_obligation( + hir_decl.output.span(), + Some(WellFormedLoc::Param { + function: def_id, + param_idx: sig.inputs().len().try_into().unwrap(), + }), + sig.output().into(), + ); - // FIXME(#27579) return types should not be implied bounds - implied_bounds.insert(sig.output()); + check_where_clauses(wfcx, span, def_id); - debug!(?implied_bounds); + check_return_position_impl_trait_in_trait_bounds( + tcx, + wfcx, + def_id, + sig.output(), + hir_decl.output.span(), + ); +} - check_where_clauses(wfcx, span, def_id); +/// Basically `check_associated_type_bounds`, but separated for now and should be +/// deduplicated when RPITITs get lowered into real associated items. +fn check_return_position_impl_trait_in_trait_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + wfcx: &WfCheckingCtxt<'_, 'tcx>, + fn_def_id: LocalDefId, + fn_output: Ty<'tcx>, + span: Span, +) { + if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) + && assoc_item.container == ty::AssocItemContainer::TraitContainer + { + for arg in fn_output.walk() { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(proj) = ty.kind() + && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id() + { + let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id); + let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let normalized_bound = wfcx.normalize(span, None, bound); + traits::wf::predicate_obligations( + wfcx.infcx, + wfcx.param_env, + wfcx.body_id, + normalized_bound, + bound_span, + ) + }); + wfcx.register_obligations(wf_obligations); + } + } + } } const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \ `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \ of the previous types except `Self`)"; -#[tracing::instrument(level = "debug", skip(wfcx))] +#[instrument(level = "debug", skip(wfcx))] fn check_method_receiver<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, fn_sig: &hir::FnSig<'_>, @@ -1629,7 +1676,7 @@ fn receiver_is_valid<'tcx>( // `self: Self` is always valid. if can_eq_self(receiver_ty) { if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) { - infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); + infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); } return true; } @@ -1661,7 +1708,10 @@ fn receiver_is_valid<'tcx>( if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty) { - infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit(); + infcx + .err_ctxt() + .report_mismatched_types(&cause, self_ty, potential_self_ty, err) + .emit(); } break; @@ -1728,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>( item: &hir::Item<'tcx>, hir_generics: &hir::Generics<'_>, ) { - let ty = tcx.type_of(item.def_id); + let ty = tcx.type_of(item.owner_id); if tcx.has_error_field(ty) { return; } - let ty_predicates = tcx.predicates_of(item.def_id); + let ty_predicates = tcx.predicates_of(item.owner_id); assert_eq!(ty_predicates.parent, None); - let variances = tcx.variances_of(item.def_id); + let variances = tcx.variances_of(item.owner_id); let mut constrained_parameters: FxHashSet<_> = variances .iter() @@ -1748,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>( // Lazily calculated because it is only needed in case of an error. let explicitly_bounded_params = LazyCell::new(|| { - let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id()); + let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id()); hir_generics .predicates .iter() @@ -1817,6 +1867,7 @@ fn report_bivariance( impl<'tcx> WfCheckingCtxt<'_, 'tcx> { /// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that /// aren't true. + #[instrument(level = "debug", skip(self))] fn check_false_global_bounds(&mut self) { let tcx = self.ocx.infcx.tcx; let mut span = self.span; @@ -1868,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { let items = tcx.hir_module_items(module); - items.par_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)); } /////////////////////////////////////////////////////////////////////////// @@ -1924,40 +1975,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { } } -pub fn impl_implied_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - impl_def_id: LocalDefId, - span: Span, -) -> FxHashSet<Ty<'tcx>> { - // We completely ignore any obligations caused by normalizing the types - // we assume to be well formed. Considering that the user of the implied - // bounds will also normalize them, we leave it to them to emit errors - // which should result in better causes and spans. - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id)); - match tcx.impl_trait_ref(impl_def_id) { - Some(trait_ref) => { - // Trait impl: take implied bounds from all types that - // appear in the trait reference. - match infcx.at(&cause, param_env).normalize(trait_ref) { - Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(), - Err(NoSolution) => FxHashSet::default(), - } - } - - None => { - // Inherent impl: take implied bounds from the `self` type. - let self_ty = tcx.type_of(impl_def_id); - match infcx.at(&cause, param_env).normalize(self_ty) { - Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]), - Err(NoSolution) => FxHashSet::default(), - } - } - } - }) -} - fn error_392( tcx: TyCtxt<'_>, span: Span, diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 1b13c98e4..2b15d4dcd 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,10 +1,10 @@ -use crate::check::coercion::{AsCoercionSite, CoerceMany}; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; +use crate::coercion::{AsCoercionSite, CoerceMany}; +use crate::{Diverges, Expectation, FnCtxt, Needs}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; +use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -12,7 +12,7 @@ use rustc_trait_selection::traits::{ }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] pub fn check_match( &self, expr: &'tcx hir::Expr<'tcx>, @@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let arm_ty = self.check_expr_with_expectation(&arm.body, expected); all_arms_diverge &= self.diverges.get(); - let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected); + let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| { + self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected) + }); let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { (Some(blk.hir_id), self.find_block_span(blk)) @@ -135,9 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(&arm.body), arm_ty, Some(&mut |err| { - let Some(ret) = self.ret_type_span else { - return; - }; + let Some(ret) = self + .tcx + .hir() + .find_by_def_id(self.body_id.owner.def_id) + .and_then(|owner| owner.fn_decl()) + .map(|decl| decl.output.span()) + else { return; }; let Expectation::IsLast(stmt) = orig_expected else { return }; @@ -210,9 +216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We won't diverge unless the scrutinee or all arms diverge. self.diverges.set(scrut_diverges | all_arms_diverge); - let match_ty = coercion.complete(self); - debug!(?match_ty); - match_ty + coercion.complete(self) } /// When the previously checked expression (the scrutinee) diverges, @@ -255,7 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("consider adding an `else` block that evaluates to the expected type"); error = true; }, - ret_reason.is_none(), + false, ); error } @@ -468,53 +472,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - // we check if the different arms would work with boxed trait objects instead and - // provide a structured suggestion in that case. + /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` + /// we check if the different arms would work with boxed trait objects instead and + /// provide a structured suggestion in that case. pub(crate) fn opt_suggest_box_span( &self, - outer_ty: Ty<'tcx>, + first_ty: Ty<'tcx>, + second_ty: Ty<'tcx>, orig_expected: Expectation<'tcx>, ) -> Option<Span> { + // FIXME(compiler-errors): This really shouldn't need to be done during the + // "good" path of typeck, but here we are. match orig_expected { - Expectation::ExpectHasType(expected) - if self.in_tail_expr - && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types() - && self.can_coerce(outer_ty, expected) => - { - let obligations = self.fulfillment_cx.borrow().pending_obligations(); - let mut suggest_box = !obligations.is_empty(); - for o in obligations { - match o.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(t) => { - let pred = - ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: t.def_id(), - substs: self.tcx.mk_substs_trait(outer_ty, &[]), - }, - constness: t.constness, - polarity: t.polarity, - })); - let obl = Obligation::new( - o.cause.clone(), - self.param_env, - pred.to_predicate(self.tcx), - ); - suggest_box &= self.predicate_must_hold_modulo_regions(&obl); - if !suggest_box { - // We've encountered some obligation that didn't hold, so the - // return expression can't just be boxed. We don't need to - // evaluate the rest of the obligations. - break; + Expectation::ExpectHasType(expected) => { + let TypeVariableOrigin { + span, + kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id), + .. + } = self.type_var_origin(expected)? else { return None; }; + + let sig = *self + .typeck_results + .borrow() + .liberated_fn_sigs() + .get(hir::HirId::make_owner(self.body_id.owner.def_id))?; + + let substs = sig.output().walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Opaque(def_id, substs) = *ty.kind() + && def_id == rpit_def_id + { + Some(substs) + } else { + None + } + })?; + let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs); + + if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { + return None; + } + + for ty in [first_ty, second_ty] { + for (pred, _) in self + .tcx + .bound_explicit_item_bounds(rpit_def_id) + .subst_iter_copied(self.tcx, substs) + { + let pred = match pred.kind().skip_binder() { + ty::PredicateKind::Trait(mut trait_pred) => { + assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); + trait_pred.trait_ref.substs = + self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]); + pred.kind().rebind(trait_pred).to_predicate(self.tcx) } + ty::PredicateKind::Projection(mut proj_pred) => { + assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty); + proj_pred.projection_ty.substs = self + .tcx + .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]); + pred.kind().rebind(proj_pred).to_predicate(self.tcx) + } + _ => continue, + }; + if !self.predicate_must_hold_modulo_regions(&Obligation::new( + ObligationCause::misc(span, self.body_id), + self.param_env, + pred, + )) { + return None; } - _ => {} } } - // If all the obligations hold (or there are no obligations) the tail expression - // we can suggest to return a boxed trait object instead of an opaque type. - if suggest_box { self.ret_type_span } else { None } + + Some(span) } _ => None, } diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 59c366ad7..59c366ad7 100644 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7aaddc2bd..d1dab0540 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -30,35 +30,38 @@ use super::FnCtxt; -use crate::hir::def_id::DefId; use crate::type_error_struct; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; use rustc_session::lint; use rustc_session::Session; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits; use rustc_trait_selection::traits::error_reporting::report_object_safety_error; /// Reifies a cast check to be checked once we have full type information for /// a function context. #[derive(Debug)] pub struct CastCheck<'tcx> { + /// The expression whose value is being casted expr: &'tcx hir::Expr<'tcx>, + /// The source type for the cast expression expr_ty: Ty<'tcx>, expr_span: Span, + /// The target type. That is, the type we are casting to. cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + /// whether the cast is made in a const context or not. + pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -96,13 +99,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Err(reported); } - if self.type_is_known_to_be_sized_modulo_regions(t, span) { + if self.type_is_sized_modulo_regions(self.param_env, t, span) { return Ok(Some(PointerKind::Thin)); } Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, ..) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { None => Some(PointerKind::Thin), Some(f) => { @@ -139,6 +142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Generator(..) | ty::Adt(..) | ty::Never + | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { let reported = self .tcx @@ -173,6 +177,7 @@ pub enum CastError { /// or "a length". If this argument is None, then the metadata is unknown, for example, /// when we're typechecking a type parameter with a ?Sized bound. IntToFatCast(Option<&'static str>), + ForeignNonExhaustiveAdt, } impl From<ErrorGuaranteed> for CastError { @@ -207,15 +212,16 @@ impl<'a, 'tcx> CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + constness: hir::Constness, ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(..) | ty::Slice(..) => { + ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { let reported = check.report_cast_to_unsized_type(fcx); Err(reported) } @@ -523,7 +529,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic}; + use rustc_hir_analysis::structured_errors::{ + SizedUnsizedCast, StructuredDiagnostic, + }; SizedUnsizedCast { sess: &fcx.tcx.sess, @@ -591,6 +599,17 @@ impl<'a, 'tcx> CastCheck<'tcx> { } err.emit(); } + CastError::ForeignNonExhaustiveAdt => { + make_invalid_casting_error( + fcx.tcx.sess, + self.span, + self.expr_ty, + self.cast_ty, + fcx, + ) + .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") + .emit(); + } } } @@ -670,19 +689,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { ("", lint::builtin::TRIVIAL_CASTS) }; - fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { - err.build(&format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - )) - .help(&format!( - "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" - )) - .emit(); - }); + fcx.tcx.struct_span_lint_hir( + lint, + self.expr.hir_id, + self.span, + DelayDm(|| { + format!( + "trivial {}cast: `{}` as `{}`", + adjective, + fcx.ty_to_string(t_expr), + fcx.ty_to_string(t_cast) + ) + }), + |lint| { + lint.help(format!( + "cast can be replaced by coercion; this might \ + require {type_asc_or}a temporary variable" + )) + }, + ); } #[instrument(skip(fcx), level = "debug")] @@ -692,7 +717,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) + if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span) && !self.cast_ty.has_infer_types() { self.report_cast_to_unsized_type(fcx); @@ -789,6 +814,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { _ => return Err(CastError::NonScalar), }; + if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { + if adt_def.did().krate != LOCAL_CRATE { + if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { + return Err(CastError::ForeignNonExhaustiveAdt); + } + } + } + match (t_from, t_cast) { // These types have invariants! can't cast into them. (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), @@ -835,6 +868,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), + + (_, DynStar) | (DynStar, _) => { + if fcx.tcx.features().dyn_star { + bug!("should be handled by `try_coerce`") + } else { + Err(CastError::IllegalCast) + } + } } } @@ -976,12 +1017,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::CENUM_IMPL_DROP_CAST, self.expr.hir_id, self.span, - |err| { - err.build(&format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )) - .emit(); + DelayDm(|| format!( + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", + self.expr_ty, self.cast_ty + )), + |lint| { + lint }, ); } @@ -992,12 +1033,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( + DelayDm(|| format!( "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", self.expr_ty, self.cast_ty - )); - + )), + |lint| { let msg = "use `.addr()` to obtain the address of a pointer"; let expr_prec = self.expr.precedence().order(); @@ -1016,9 +1056,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { (cast_span, format!(").addr(){scalar_cast}")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); } else { - err.span_suggestion( + lint.span_suggestion( cast_span, msg, format!(".addr(){scalar_cast}"), @@ -1026,12 +1066,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } - err.help( + lint.help( "if you can't comply with strict provenance and need to expose the pointer \ provenance you can use `.expose_addr()` instead" ); - err.emit(); + lint }, ); } @@ -1041,32 +1081,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::FUZZY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( - "strict provenance disallows casting integer `{}` to pointer `{}`", - self.expr_ty, self.cast_ty - )); + DelayDm(|| format!( + "strict provenance disallows casting integer `{}` to pointer `{}`", + self.expr_ty, self.cast_ty + )), + |lint| { let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; let suggestions = vec![ (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")), (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - err.help( + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.help( "if you can't comply with strict provenance and don't have a pointer with \ the correct provenance you can use `std::ptr::from_exposed_addr()` instead" ); - err.emit(); + lint }, ); } } - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) - } -} diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 2ed5f569b..86597a703 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -35,13 +35,15 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use crate::astconv::AstConv; -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::Expr; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; @@ -59,7 +61,7 @@ use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -87,6 +89,19 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>; +struct CollectRetsVisitor<'tcx> { + ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Ret(_) = expr.kind { + self.ret_exprs.push(expr); + } + intravisit::walk_expr(self, expr); + } +} + /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -201,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); } + ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => { + return self.coerce_dyn_star(a, b, predicates, region); + } _ => {} } @@ -687,7 +705,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Object safety violations or miscellaneous. Err(err) => { - self.report_selection_error(obligation.clone(), &obligation, &err, false); + self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + false, + ); // Treat this like an obligation and follow through // with the unsizing - the lack of a coercion should // be silent, as it causes a type mismatch later. @@ -725,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(coercion) } + fn coerce_dyn_star( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + b_region: ty::Region<'tcx>, + ) -> CoerceResult<'tcx> { + if !self.tcx.features().dyn_star { + return Err(TypeError::Mismatch); + } + + if let ty::Dynamic(a_data, _, _) = a.kind() + && let ty::Dynamic(b_data, _, _) = b.kind() + { + if a_data.principal_def_id() == b_data.principal_def_id() { + return self.unify_and(a, b, |_| vec![]); + } else if !self.tcx().features().trait_upcasting { + let mut err = feature_err( + &self.tcx.sess.parse_sess, + sym::trait_upcasting, + self.cause.span, + &format!( + "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental" + ), + ); + err.emit(); + } + } + + // Check the obligations of the cast -- for example, when casting + // `usize` to `dyn* Clone + 'static`: + let obligations = predicates + .iter() + .map(|predicate| { + // For each existential predicate (e.g., `?Self: Clone`) substitute + // the type of the expression (e.g., `usize` in our example above) + // and then require that the resulting predicate (e.g., `usize: Clone`) + // holds (it does). + let predicate = predicate.with_self_ty(self.tcx, a); + Obligation::new(self.cause.clone(), self.param_env, predicate) + }) + // Enforce the region bound (e.g., `usize: 'static`, in our example). + .chain([Obligation::new( + self.cause.clone(), + self.param_env, + self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(a, b_region), + ))), + )]) + .collect(); + + Ok(InferOk { + value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), + obligations, + }) + } + fn coerce_from_safe_fn<F, G>( &self, a: Ty<'tcx>, @@ -1464,23 +1544,29 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } Err(coercion_error) => { + // Mark that we've failed to coerce the types here to suppress + // any superfluous errors we might encounter while trying to + // emit or provide suggestions on how to fix the initial error. + fcx.set_tainted_by_errors(); let (expected, found) = if label_expression_as_expected { // In the case where this is a "forced unit", like // `break`, we want to call the `()` "expected" // since it is implied by the syntax. // (Note: not all force-units work this way.)" - (expression_ty, self.final_ty.unwrap_or(self.expected_ty)) + (expression_ty, self.merged_ty()) } else { // Otherwise, the "expected" type for error // reporting is the current unification type, // which is basically the LUB of the expressions // we've seen so far (combined with the expected // type) - (self.final_ty.unwrap_or(self.expected_ty), expression_ty) + (self.merged_ty(), expression_ty) }; + let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); let mut err; let mut unsized_return = false; + let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; match *cause.code() { ObligationCauseCode::ReturnNoExpression => { err = struct_span_err!( @@ -1506,6 +1592,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if !fcx.tcx.features().unsized_locals { unsized_return = self.is_return_ty_unsized(fcx, blk_id); } + if let Some(expression) = expression + && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { + intravisit::walk_block(& mut visitor, loop_blk); + } } ObligationCauseCode::ReturnValue(id) => { err = self.report_return_mismatched_types( @@ -1524,7 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } _ => { - err = fcx.report_mismatched_types( + err = fcx.err_ctxt().report_mismatched_types( cause, expected, found, @@ -1551,12 +1641,47 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { ); } + if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { + self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); + } err.emit_unless(unsized_return); self.final_ty = Some(fcx.tcx.ty_error()); } } } + fn note_unreachable_loop_return( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'tcx>, + ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, + ) { + let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;}; + let mut span: MultiSpan = vec![loop_span].into(); + span.push_span_label(loop_span, "this might have zero elements to iterate on"); + const MAXITER: usize = 3; + let iter = ret_exprs.iter().take(MAXITER); + for ret_expr in iter { + span.push_span_label( + ret_expr.span, + "if the loop doesn't execute, this value would never get returned", + ); + } + err.span_note( + span, + "the function expects a value to always be returned, but loops might run zero times", + ); + if MAXITER < ret_exprs.len() { + err.note(&format!( + "if the loop doesn't execute, {} other values would never get returned", + ret_exprs.len() - MAXITER + )); + } + err.help( + "return a value for the case when the loop has zero elements to iterate on, or \ + consider changing the return type to account for that possibility", + ); + } fn report_return_mismatched_types<'a>( &self, @@ -1569,7 +1694,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expression: Option<&'tcx hir::Expr<'tcx>>, blk_id: Option<hir::HirId>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); + let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); let mut pointing_at_return_type = false; let mut fn_output = None; @@ -1623,7 +1748,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, can_suggest, - fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)), + fcx.tcx.hir().get_parent_item(id).into(), ); } if !pointing_at_return_type { @@ -1632,7 +1757,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); + let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id); if let (Some(expr), Some(_), Some((fn_decl, _, _))) = (expression, blk_id, fcx.get_node_fn_decl(parent_item)) @@ -1644,13 +1769,34 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, id, - fcx.tcx.hir().local_def_id_to_hir_id(parent_id), + parent_id.into(), + ); + } + + let ret_coercion_span = fcx.ret_coercion_span.get(); + + if let Some(sp) = ret_coercion_span + // If the closure has an explicit return type annotation, or if + // the closure's return type has been inferred from outside + // requirements (such as an Fn* trait bound), then a type error + // may occur at the first return expression we see in the closure + // (if it conflicts with the declared return type). Skip adding a + // note in this case, since it would be incorrect. + && !fcx.return_type_pre_known + { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + expected + ), ); } - if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.get(), fn_output) { + if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); } + err } diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 4de48dc5b..16febfc46 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,21 +1,20 @@ -use crate::check::FnCtxt; -use rustc_infer::infer::InferOk; -use rustc_middle::middle::stability::EvalResult; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::ObligationCause; - +use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; +use rustc_infer::infer::InferOk; use rustc_middle::lint::in_external_macro; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -32,20 +31,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option<TypeError<'tcx>>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); - self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty); - if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { - return; - } - self.suggest_no_capture_closure(err, expected, expr_ty); - self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); - self.suggest_missing_parentheses(err, expr); - self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); + + // Use `||` to give these suggestions a precedence + let _ = self.suggest_missing_parentheses(err, expr) + || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) + || self.suggest_compatible_variants(err, expr, expected, expr_ty) + || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty) + || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_no_capture_closure(err, expected, expr_ty) + || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) + || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) + || self.suggest_into(err, expr, expr_ty, expected); + self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); - self.report_closure_inferred_return_type(err, expected); } // Requires that the two types unify, and prints an error message if @@ -77,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, expected, actual, e)), } } @@ -107,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)), } } @@ -131,7 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` /// will be permitted if the diverges flag is currently "always". - #[tracing::instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] + #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, expr: &hir::Expr<'tcx>, @@ -151,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); + let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone()); let is_insufficiently_polymorphic = matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..)); @@ -286,7 +287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { if let ty::Adt(expected_adt, substs) = expected.kind() { if let hir::ExprKind::Field(base, ident) = expr.kind { let base_ty = self.typeck_results.borrow().expr_ty(base); @@ -299,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "", Applicability::MaybeIncorrect, ); - return + return true; } } @@ -338,7 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) { vec!["None", "Some(())"] } else { - return; + return false; }; if let Some(indent) = self.tcx.sess.source_map().indentation_before(span.shrink_to_lo()) @@ -358,7 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - return; + return true; } } } @@ -375,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_is_local = sole_field.did.is_local(); let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx) + sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) // Skip suggestions for unstable public fields (for example `Pin::pointer`) && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); @@ -417,6 +418,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::def::CtorKind::Const => unreachable!(), }; + // Suggest constructor as deep into the block tree as possible. + // This fixes https://github.com/rust-lang/rust/issues/101065, + // and also just helps make the most minimal suggestions. + let mut expr = expr; + while let hir::ExprKind::Block(block, _) = &expr.kind + && let Some(expr_) = &block.expr + { + expr = expr_ + } + vec![ (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), (expr.span.shrink_to_hi(), close.to_owned()), @@ -435,6 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestions_for(&**variant, *ctor_kind, *field_name), Applicability::MaybeIncorrect, ); + return true; } _ => { // More than one matching variant. @@ -450,9 +462,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), Applicability::MaybeIncorrect, ); + return true; } } } + + false } fn suggest_non_zero_new_unwrap( @@ -461,19 +476,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { let tcx = self.tcx; let (adt, unwrap) = match expected.kind() { // In case Option<NonZero*> is wanted, but * is provided, suggest calling new ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => { // Unwrap option - let ty::Adt(adt, _) = substs.type_at(0).kind() else { return }; + let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; }; (adt, "") } // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types ty::Adt(adt, _) => (adt, ".unwrap()"), - _ => return, + _ => return false, }; let map = [ @@ -492,7 +507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some((s, _)) = map .iter() .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)) - else { return }; + else { return false; }; let path = self.tcx.def_path_str(adt.non_enum_variant().def_id); @@ -504,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Applicability::MaybeIncorrect, ); + + true } pub fn get_conversion_methods( @@ -513,24 +530,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, hir_id: hir::HirId, ) -> Vec<AssocItem> { - let mut methods = - self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); - methods.retain(|m| { - self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) - }); + let methods = self.probe_for_return_type( + span, + probe::Mode::MethodCall, + expected, + checked_ty, + hir_id, + |m| { + self.has_only_self_parameter(m) + && self + .tcx + // This special internal attribute is used to permit + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + .has_attr(m.def_id, sym::rustc_conversion_suggestion) + }, + ); methods } @@ -590,7 +612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let closure_params_len = closure_fn_decl.inputs.len(); let ( Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(method_path, method_expr, _), + kind: hir::ExprKind::MethodCall(method_path, receiver, ..), .. })), 1, @@ -598,14 +620,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]); - let self_ty = format!("{:?}", self_ty); + let self_ty = self.typeck_results.borrow().expr_ty(receiver); let name = method_path.ident.name; - let is_as_ref_able = (self_ty.starts_with("&std::option::Option") - || self_ty.starts_with("&std::result::Result") - || self_ty.starts_with("std::option::Option") - || self_ty.starts_with("std::result::Result")) - && (name == sym::map || name == sym::and_then); + let is_as_ref_able = match self_ty.peel_refs().kind() { + ty::Adt(def, _) => { + (self.tcx.is_diagnostic_item(sym::Option, def.did()) + || self.tcx.is_diagnostic_item(sym::Result, def.did())) + && (name == sym::map || name == sym::and_then) + } + _ => false, + }; match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { (true, Ok(src)) => { let suggestion = format!("as_ref().{}", src); @@ -637,11 +661,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }?; match hir.find(hir.get_parent_node(expr.hir_id))? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { - for field in *fields { - if field.ident.name == local.name && field.is_shorthand { - return Some(local.name); - } + Node::ExprField(field) => { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } _ => {} @@ -697,7 +719,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { + ) -> Option<( + Span, + String, + String, + Applicability, + bool, /* verbose */ + bool, /* suggest `&` or `&mut` type annotation */ + )> { let sess = self.sess(); let sp = expr.span; @@ -729,6 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + false, )); } } @@ -743,6 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "b".to_string(), Applicability::MachineApplicable, true, + false, )); } } @@ -767,22 +798,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if self.can_coerce(ref_ty, expected) { let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind { + if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind { let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span)); - if let ([arg], Some(true), sym::clone) = ( - &args[..], - self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( + if args.is_empty() + && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( |did| { let ai = self.tcx.associated_item(did); ai.trait_container(self.tcx) == Some(clone_trait) }, - ), - segment.ident.name, - ) { + ) == Some(true) + && segment.ident.name == sym::clone + { // If this expression had a clone call when suggesting borrowing // we want to suggest removing it because it'd now be unnecessary. - sugg_sp = arg.span; + sugg_sp = receiver.span; } } if let Ok(src) = sm.span_to_snippet(sugg_sp) { @@ -793,7 +823,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ if is_range_literal(expr) => true, _ => false, }; - let sugg_expr = if needs_parens { format!("({src})") } else { src }; if let Some(sugg) = self.can_use_as_ref(expr) { return Some(( @@ -802,6 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg.2, Applicability::MachineApplicable, false, + false, )); } @@ -821,6 +851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + let sugg_expr = if needs_parens { format!("({src})") } else { src }; return Some(match mutability { hir::Mutability::Mut => ( sp, @@ -828,6 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&mut {sugg_expr}"), Applicability::MachineApplicable, false, + false, ), hir::Mutability::Not => ( sp, @@ -835,6 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&{sugg_expr}"), Applicability::MachineApplicable, false, + false, ), }); } @@ -864,6 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true )); } return None; @@ -877,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true, )); } } @@ -943,6 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { src, applicability, true, + false, )); } } @@ -983,6 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable }, true, + false, )); } @@ -1034,6 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestion, Applicability::MachineApplicable, true, + false, )); } } @@ -1072,21 +1110,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Struct(_, fields, _), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + if let Some(hir::Node::ExprField(field)) = + self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) { // `expr` is a literal field for a struct, only suggest if appropriate - match (*fields) - .iter() - .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand) - { + if field.is_shorthand { // This is a field literal - Some(field) => { - sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); - } + sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); + } else { // Likely a field was meant, but this field wasn't found. Do not suggest anything. - None => return false, + return false; } }; @@ -1418,25 +1451,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => false, } } - - // Report the type inferred by the return statement. - fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) { - if let Some(sp) = self.ret_coercion_span.get() - // If the closure has an explicit return type annotation, or if - // the closure's return type has been inferred from outside - // requirements (such as an Fn* trait bound), then a type error - // may occur at the first return expression we see in the closure - // (if it conflicts with the declared return type). Skip adding a - // note in this case, since it would be incorrect. - && !self.return_type_pre_known - { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - self.resolve_vars_if_possible(expected) - ), - ); - } - } } diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs index 963a93a95..963a93a95 100644 --- a/compiler/rustc_typeck/src/check/diverges.rs +++ b/compiler/rustc_hir_typeck/src/diverges.rs diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index e9e810344..e9e810344 100644 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 4059b3403..747ecb036 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,4 +1,4 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_data_structures::{ fx::{FxHashMap, FxHashSet}, graph::WithSuccessors, @@ -72,7 +72,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // // - Unconstrained ints are replaced with `i32`. // - // - Unconstrained floats are replaced with with `f64`. + // - Unconstrained floats are replaced with `f64`. // // - Non-numerics may get replaced with `()` or `!`, depending on // how they were categorized by `calculate_diverging_fallback` diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 3a8093345..6a1cffe3e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,12 +1,7 @@ -use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, -}; -use crate::check::callee::{self, DeferredCallResolution}; -use crate::check::method::{self, MethodCallee, SelfSource}; -use crate::check::rvalue_scopes; -use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; - +use crate::callee::{self, DeferredCallResolution}; +use crate::method::{self, MethodCallee, SelfSource}; +use crate::rvalue_scopes; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -15,26 +10,28 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::astconv::{ + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, IsMethodCall, PathSeg, +}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{ - self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, -}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, ToPredicate, Ty, UserType, }; +use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; @@ -60,17 +57,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( + let msg = format!("unreachable {}", kind); + self.tcx().struct_span_lint_hir( + lint::builtin::UNREACHABLE_CODE, + id, + span, + &msg, + |lint| { + lint.span_label(span, &msg).span_label( orig_span, custom_note .unwrap_or("any code following this expression is unreachable"), ) - .emit(); - }) + }, + ) } } } @@ -83,21 +83,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) } - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug")] + #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( &self, mut ty: Ty<'tcx>, mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) -> Ty<'tcx> { // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!("no inference var, nothing needs doing"); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(ty); - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!(?ty); return ty; } @@ -107,10 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // indirect dependencies that don't seem worth tracking // precisely. self.select_obligations_where_possible(false, mutate_fulfillment_errors); - ty = self.resolve_vars_if_possible(ty); - - debug!(?ty); - ty + self.resolve_vars_if_possible(ty) } pub(in super::super) fn record_deferred_call_resolution( @@ -412,7 +409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_span: opt_input_expr.map(|expr| expr.span), is_lit: opt_input_expr .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_pred: None, + output_ty: None, }, ), self.param_env, @@ -492,21 +489,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), - hir::ArrayLen::Body(anon_const) => self.to_const(anon_const), + hir::ArrayLen::Body(anon_const) => { + let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let span = self.tcx.hir().span(anon_const.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); + self.normalize_associated_types_in(span, c) + } } } - pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::WellFormed(None), - ); - c - } - pub fn const_arg_to_const( &self, ast_c: &hir::AnonConst, @@ -565,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub fn register_wf_obligation( &self, - arg: subst::GenericArg<'tcx>, + arg: ty::GenericArg<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -610,18 +602,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { self.select_obligations_where_possible(false, |_| {}); - crate::check::generator_interior::resolve_interior( - self, def_id, body_id, interior, kind, - ); + crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } #[instrument(skip(self), level = "debug")] pub(in super::super) fn select_all_obligations_or_error(&self) { - let errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); + let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); if !errors.is_empty() { - self.report_fulfillment_errors(&errors, self.inh.body_id, false); + self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); } } @@ -634,7 +625,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); if !result.is_empty() { mutate_fulfillment_errors(&mut result); - self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); + self.adjust_fulfillment_errors_for_expr_obligation(&mut result); + self.err_ctxt().report_fulfillment_errors( + &result, + self.inh.body_id, + fallback_has_occurred, + ); } } @@ -831,23 +827,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = item_ty.subst(self.tcx, substs); self.write_resolution(hir_id, Ok((def_kind, def_id))); - self.add_required_obligations_with_code( - span, - def_id, - &substs, - match lang_item { - hir::LangItem::IntoFutureIntoFuture => { - ObligationCauseCode::AwaitableExpr(expr_hir_id) - } - hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { - ObligationCauseCode::ForLoopIterator - } - hir::LangItem::TryTraitFromOutput - | hir::LangItem::TryTraitFromResidual - | hir::LangItem::TryTraitBranch => ObligationCauseCode::QuestionMark, - _ => traits::ItemObligation(def_id), - }, - ); + + let code = match lang_item { + hir::LangItem::IntoFutureIntoFuture => { + Some(ObligationCauseCode::AwaitableExpr(expr_hir_id)) + } + hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { + Some(ObligationCauseCode::ForLoopIterator) + } + hir::LangItem::TryTraitFromOutput + | hir::LangItem::TryTraitFromResidual + | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark), + _ => None, + }; + if let Some(code) = code { + self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone()); + } else { + self.add_required_obligations_for_hir(span, def_id, substs, hir_id); + } + (Res::Def(def_kind, def_id), ty) } @@ -986,7 +984,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind { + if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { if self .typeck_results .borrow() @@ -1273,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut self, param: &ty::GenericParamDef, arg: &GenericArg<'_>, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into() @@ -1297,10 +1295,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn inferred_kind( &mut self, - substs: Option<&[subst::GenericArg<'tcx>]>, + substs: Option<&[ty::GenericArg<'tcx>]>, param: &ty::GenericParamDef, infer_args: bool, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); match param.kind { GenericParamDefKind::Lifetime => { @@ -1359,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // First, store the "user substs" for later. self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - self.add_required_obligations(span, def_id, &substs); + self.add_required_obligations_for_hir(span, def_id, &substs, hir_id); // Substitute the values for the type parameters into the type of // the referenced item. @@ -1396,35 +1394,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Add all the obligations that are required, substituting and normalized appropriately. - pub(crate) fn add_required_obligations( + pub(crate) fn add_required_obligations_for_hir( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, + substs: SubstsRef<'tcx>, + hir_id: hir::HirId, ) { - self.add_required_obligations_with_code( - span, - def_id, - substs, - traits::ItemObligation(def_id), - ) + self.add_required_obligations_with_code(span, def_id, substs, |idx, span| { + if span.is_dummy() { + ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx) + } else { + ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx) + } + }) } - #[tracing::instrument(level = "debug", skip(self, span, def_id, substs))] + #[instrument(level = "debug", skip(self, code, span, substs))] fn add_required_obligations_with_code( &self, span: Span, def_id: DefId, - substs: &SubstsRef<'tcx>, - code: ObligationCauseCode<'tcx>, + substs: SubstsRef<'tcx>, + code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>, ) { + let param_env = self.param_env; + + let remap = match self.tcx.def_kind(def_id) { + // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when + // `Self: Trait` is satisfied because it does not matter whether the impl is `const`. + // Therefore we have to remap the param env here to be non-const. + hir::def::DefKind::AssocConst => true, + hir::def::DefKind::AssocFn + if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait => + { + // N.B.: All callsites to this function involve checking a path expression. + // + // When instantiating a trait method as a function item, it does not actually matter whether + // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as + // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would + // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a + // `const fn` pointer. + // + // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy + // `~const FnOnce` or can be coerced to `const fn` pointer. + true + } + _ => false, + }; let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); - for obligation in traits::predicates_for_generics( - traits::ObligationCause::new(span, self.body_id, code), - self.param_env, + for mut obligation in traits::predicates_for_generics( + |idx, predicate_span| { + traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) + }, + param_env, bounds, ) { + if remap { + obligation = obligation.without_const(self.tcx); + } self.register_predicate(obligation); } } @@ -1438,7 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } else { if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + self.err_ctxt() + .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) .emit(); } let err = self.tcx.ty_error(); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs index 7602f2550..fc83994ca 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs @@ -130,14 +130,17 @@ impl<'tcx> ArgMatrix<'tcx> { let ai = &self.expected_indices; let ii = &self.provided_indices; + // Issue: 100478, when we end the iteration, + // `next_unmatched_idx` will point to the index of the first unmatched + let mut next_unmatched_idx = 0; for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over inputs are considered missing + // If we eliminate the last row, any left-over arguments are considered missing if i >= mat.len() { - return Some(Issue::Missing(i)); + return Some(Issue::Missing(next_unmatched_idx)); } - // If we eliminate the last column, any left-over arguments are extra + // If we eliminate the last column, any left-over inputs are extra if mat[i].len() == 0 { - return Some(Issue::Extra(i)); + return Some(Issue::Extra(next_unmatched_idx)); } // Make sure we don't pass the bounds of our matrix @@ -145,6 +148,7 @@ impl<'tcx> ArgMatrix<'tcx> { let is_input = i < ii.len(); if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { // This is a satisfied input, so move along + next_unmatched_idx += 1; continue; } @@ -163,7 +167,7 @@ impl<'tcx> ArgMatrix<'tcx> { if is_input { for j in 0..ai.len() { // If we find at least one argument that could satisfy this input - // this argument isn't useless + // this input isn't useless if matches!(mat[i][j], Compatibility::Compatible) { useless = false; break; @@ -232,8 +236,8 @@ impl<'tcx> ArgMatrix<'tcx> { if matches!(c, Compatibility::Compatible) { Some(i) } else { None } }) .collect(); - if compat.len() != 1 { - // this could go into multiple slots, don't bother exploring both + if compat.len() < 1 { + // try to find a cycle even when this could go into multiple slots, see #101097 is_cycle = false; break; } @@ -309,7 +313,8 @@ impl<'tcx> ArgMatrix<'tcx> { } while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - match self.find_issue() { + let res = self.find_issue(); + match res { Some(Issue::Invalid(idx)) => { let compatibility = self.compatibility_matrix[idx][idx].clone(); let input_idx = self.provided_indices[idx]; @@ -364,7 +369,9 @@ impl<'tcx> ArgMatrix<'tcx> { None => { // We didn't find any issues, so we need to push the algorithm forward // First, eliminate any arguments that currently satisfy their inputs - for (inp, arg) in self.eliminate_satisfied() { + let eliminated = self.eliminate_satisfied(); + assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); + for (inp, arg) in eliminated { matched_inputs[arg] = Some(inp); } } diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 8f34a970f..9a096f24f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -1,9 +1,10 @@ -use crate::check::{FnCtxt, LocalTy, UserType}; +use crate::{FnCtxt, LocalTy}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; +use rustc_middle::ty::UserType; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index a2c23db16..122ad7009 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { } /// For an expression with an uninhabited return type (e.g. a function that returns !), - /// this adds a self edge to to the CFG to model the fact that the function does not + /// this adds a self edge to the CFG to model the fact that the function does not /// return. fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { let ty = self.typeck_results.expr_ty(expr); @@ -256,6 +256,8 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::TypeBinding(..) | hir::Node::TraitRef(..) | hir::Node::Pat(..) + | hir::Node::PatField(..) + | hir::Node::ExprField(..) | hir::Node::Arm(..) | hir::Node::Local(..) | hir::Node::Ctor(..) @@ -432,7 +434,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { self.handle_uninhabited_return(expr); } - ExprKind::MethodCall(_, exprs, _) => { + ExprKind::MethodCall(_, receiver, exprs, _) => { + self.visit_expr(receiver); for expr in exprs { self.visit_expr(expr); } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs index 139d17d2e..139d17d2e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs index c0a0bfe8e..c0a0bfe8e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index ded0888c3..bfe95852a 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -1,13 +1,16 @@ use super::TrackedValue; use crate::{ - check::FnCtxt, expr_use_visitor::{self, ExprUseVisitor}, + FnCtxt, }; use hir::{def_id::DefId, Body, HirId, HirIdMap}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_middle::{ + hir::place::{PlaceBase, Projection, ProjectionKind}, + ty::TypeVisitable, +}; pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, @@ -159,8 +162,8 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { bk: rustc_middle::ty::BorrowKind, ) { debug!( - "borrow: place_with_id = {place_with_id:?}, diag_expr_id={diag_expr_id:?}, \ - borrow_kind={bk:?}" + "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \ + borrow_kind={bk:#?}" ); self.borrow_place(place_with_id); @@ -198,7 +201,13 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { // If the type being assigned needs dropped, then the mutation counts as a borrow // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. - if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) { + let ty = self.tcx.erase_regions(assignee_place.place.base_ty); + if ty.needs_infer() { + self.tcx.sess.delay_span_bug( + self.tcx.hir().span(assignee_place.hir_id), + &format!("inference variables in {ty}"), + ); + } else if ty.needs_drop(self.tcx, self.param_env) { self.places .borrowed .insert(TrackedValue::from_place_with_projections_allowed(assignee_place)); diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 2c89b63ae..be4ea9986 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,16 +1,16 @@ use super::{probe, MethodCallee}; -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use crate::check::{callee, FnCtxt}; +use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; 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, Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; @@ -491,7 +491,19 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // 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( - traits::ObligationCause::new(self.span, self.body_id, traits::ItemObligation(def_id)), + |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, ) { diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 7c68d9304..3c98a2aa3 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -1,3 +1,7 @@ +use crate::{ + method::probe::{self, Pick}, + FnCtxt, +}; use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; @@ -12,11 +16,6 @@ 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, @@ -82,14 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |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 { @@ -133,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } else { @@ -143,6 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( @@ -151,16 +147,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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() - .skip(1) .map(|arg| { let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); format!( @@ -203,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } @@ -258,15 +248,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 { + self.tcx.struct_span_lint_hir( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + ), + |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(); @@ -283,44 +281,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - 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(", ") - ); + 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(); - }); + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index efe15fec7..28aa2302f 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,14 +3,12 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::check::FnCtxt; use crate::errors::MethodCallOnUnknownType; -use crate::hir::def::DefKind; -use crate::hir::def_id::DefId; - +use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -19,10 +17,11 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; +use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, @@ -253,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self, candidate_filter))] pub fn probe_for_return_type( &self, span: Span, @@ -261,11 +260,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Ty<'tcx>, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec<ty::AssocItem> { - debug!( - "probe(self_ty={:?}, return_type={}, scope_expr_id={})", - self_ty, return_type, scope_expr_id - ); let method_names = self .probe_op( span, @@ -276,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, scope_expr_id, ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), + |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) .unwrap_or_default(); method_names @@ -299,7 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - #[instrument(level = "debug", skip(self, scope_expr_id))] + #[instrument(level = "debug", skip(self))] pub fn probe_for_name( &self, span: Span, @@ -310,10 +306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { scope_expr_id: hir::HirId, scope: ProbeScope, ) -> PickResult<'tcx> { - debug!( - "probe(self_ty={:?}, item_name={}, scope_expr_id={})", - self_ty, item_name, scope_expr_id - ); self.probe_op( span, mode, @@ -417,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.build("type annotations needed").emit(); - }, + "type annotations needed", + |lint| lint, ); } } else { @@ -481,69 +472,65 @@ fn method_autoderef_steps<'tcx>( ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { - let ParamEnvAnd { param_env, value: self_ty } = goal; - - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars.clone(), - ty, - ), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - - let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), - }), - ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - - steps.push(CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - infcx.tcx.mk_slice(*elem_ty), - ), - autoderefs: dereferences, - // this could be from an unsafe deref if we had - // a *mut/const [T; N] - from_unsafe_deref: reached_raw_pointer, - unsize: true, - }); - - None + let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let ParamEnvAnd { param_env, value: self_ty } = goal; + + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .include_raw_pointers() + .silence_errors(); + let mut reached_raw_pointer = false; + let mut steps: Vec<_> = autoderef + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + }; + if let ty::RawPtr(_) = ty.kind() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; } - _ => None, - }; - - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + step + }) + .collect(); + + let final_ty = autoderef.final_ty(true); + let opt_bad_ty = match final_ty.kind() { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Array(elem_ty, _) => { + let dereferences = steps.len() - 1; + + steps.push(CandidateStep { + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + infcx.tcx.mk_slice(*elem_ty), + ), + autoderefs: dereferences, + // this could be from an unsafe deref if we had + // a *mut/const [T; N] + from_unsafe_deref: reached_raw_pointer, + unsize: true, + }); - MethodAutoderefStepsResult { - steps: tcx.arena.alloc_from_iter(steps), - opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + None } - }) + _ => None, + }; + + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + + MethodAutoderefStepsResult { + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), + reached_recursion_limit: autoderef.reached_recursion_limit(), + } } impl<'a, 'tcx> ProbeContext<'a, 'tcx> { @@ -980,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn candidate_method_names(&self) -> Vec<Ident> { + fn candidate_method_names( + &self, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, + ) -> Vec<Ident> { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates .iter() .chain(&self.extension_candidates) + .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { self.matches_return_type(&candidate.item, None, return_ty) @@ -1366,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { stable_pick: &Pick<'_>, unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { + let def_kind = stable_pick.item.kind.as_def_kind(); self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, self.scope_expr_id, self.span, + format!( + "{} {} with this name may be added to the standard library in the future", + def_kind.article(), + def_kind.descr(stable_pick.item.def_id), + ), |lint| { - let def_kind = stable_pick.item.kind.as_def_kind(); - let mut diag = lint.build(&format!( - "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(stable_pick.item.def_id), - )); match (stable_pick.item.kind, stable_pick.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing // the code from `report_method_error()`. - diag.help(&format!( + lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", self.tcx.def_path_str(stable_pick.item.def_id), @@ -1391,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { let def_id = stable_pick.item.container_id(self.tcx); - diag.span_suggestion( + lint.span_suggestion( self.span, "use the fully qualified path to the associated const", format!( @@ -1407,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } if self.tcx.sess.is_nightly_build() { for (candidate, feature) in unstable_candidates { - diag.help(&format!( + lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, self.tcx.def_path_str(candidate.item.def_id), @@ -1415,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - diag.emit(); + lint }, ); } @@ -1514,8 +1505,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); // Convert the bounds into obligations. - let impl_obligations = - traits::predicates_for_generics(cause, self.param_env, impl_bounds); + let impl_obligations = traits::predicates_for_generics( + move |_, _| cause.clone(), + self.param_env, + impl_bounds, + ); let candidate_obligations = impl_obligations .chain(norm_obligations.into_iter()) @@ -1700,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - let method_names = pcx.candidate_method_names(); + let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; let applicable_close_candidates: Vec<ty::AssocItem> = method_names .iter() diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c92b93cbc..6c21ed902 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,7 +1,9 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. -use crate::check::FnCtxt; +use crate::errors; +use crate::FnCtxt; +use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -16,12 +18,12 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, @@ -30,8 +32,8 @@ use rustc_trait_selection::traits::{ use std::cmp::Ordering; use std::iter; -use super::probe::{Mode, ProbeScope}; -use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; +use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; +use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -95,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -104,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, - mut sources: Vec<CandidateSource>, + sources: &mut Vec<CandidateSource>, sugg_span: Span| { sources.sort(); sources.dedup(); @@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: static_sources, + static_candidates: mut static_sources, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -270,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_constraining_numerical_ty( + if self.suggest_wrapping_range_with_parens( + tcx, actual, source, span, item_name, &ty_str, + ) || self.suggest_constraining_numerical_ty( tcx, actual, source, span, item_kind, item_name, &ty_str, ) { return None; @@ -363,44 +367,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if self.is_fn_ty(rcvr_ty, span) { - if let SelfSource::MethodCall(expr) = source { - let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { - if let Some(local_id) = def_id.as_local() { - let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); - let node = tcx.hir().get(hir_id); - let fields = node.tuple_fields(); - if let Some(fields) = fields - && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { - Some((fields.len(), of)) - } else { - None - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) { - let parent_def_id = tcx.parent(*def_id); - Some((tcx.associated_item_def_ids(parent_def_id).len(), of)) - } else { - None - } - } - } else { - None - }; - - // If the function is a tuple constructor, we recommend that they call it - if let Some((fields, kind)) = suggest { - suggest_call_constructor(expr.span, kind, fields, &mut err); - } else { - // General case - err.span_label( - expr.span, - "this is a function, perhaps you wish to call it", - ); - } - } + if let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { + let call_expr = self + .tcx + .hir() + .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); + let probe = self.lookup_probe( + span, + item_name, + output_ty, + call_expr, + ProbeScope::AllTraits, + ); + probe.is_ok() + }); } let mut custom_span_label = false; @@ -441,9 +422,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help(&format!("try with `{}::{}`", ty_str, item_name,)); } - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } let mut bound_spans = vec![]; @@ -560,7 +541,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.push((self.tcx.def_span(def.did()), msg)) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _) => { + ty::Dynamic(preds, _, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => bound_spans @@ -877,8 +858,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Avoid crashing. return (None, None); } - let OnUnimplementedNote { message, label, .. } = - self.on_unimplemented_note(trait_ref, &obligation); + let OnUnimplementedNote { message, label, .. } = self + .err_ctxt() + .on_unimplemented_note(trait_ref, &obligation); (message, label) }) .unwrap_or((None, None)) @@ -904,7 +886,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { + let label_span_not_found = |err: &mut Diagnostic| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); let is_string_or_ref_str = match actual.kind() { @@ -1000,9 +982,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { label_span_not_found(&mut err); } - self.check_for_field_method(&mut err, source, span, actual, item_name); + // Don't suggest (for example) `expr.field.method()` if `expr.method()` + // doesn't exist due to unsatisfied predicates. + if unsatisfied_predicates.is_empty() { + self.check_for_field_method(&mut err, source, span, actual, item_name); + } - self.check_for_unwrap_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, span, actual, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -1017,10 +1003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|args| args.len()), + args.map(|(_, args)| args.len() + 1), source, out_of_scope_traits, &unsatisfied_predicates, + &static_sources, unsatisfied_bounds, ); } @@ -1062,23 +1049,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() { let def_kind = lev_candidate.kind.as_def_kind(); - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); + // Methods are defined within the context of a struct and their first parameter is always self, + // which represents the instance of the struct the method is being called on + // Associated functions don’t take self as a parameter and + // they are not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { + err.span_suggestion( + span, + &format!("there is a method with a similar name",), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } } } + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + return Some(err); } - MethodError::Ambiguity(sources) => { + MethodError::Ambiguity(mut sources) => { let mut err = struct_span_err!( self.sess(), item_name.span, @@ -1087,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, sources, sugg_span); + report_candidates(span, &mut err, &mut sources, sugg_span); err.emit(); } @@ -1150,7 +1152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, expr: &hir::Expr<'_>, item_name: Ident, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, ) -> bool { let tcx = self.tcx; let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { @@ -1165,7 +1167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }); if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module(self.body_id).to_def_id(); + let scope = tcx.parent_module(self.body_id); let is_accessible = field.vis.is_accessible_from(scope, tcx); if is_accessible { @@ -1204,6 +1206,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// Suggest possible range with adding parentheses, for example: + /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. + fn suggest_wrapping_range_with_parens( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'tcx>, + span: Span, + item_name: Ident, + ty_str: &str, + ) -> bool { + if let SelfSource::MethodCall(expr) = source { + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, + }; + + if !span_included { + continue; + } + + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + + let pick = self.probe_for_name( + span, + Mode::MethodCall, + item_name, + IsSuggestion(true), + range_ty, + expr.hir_id, + ProbeScope::AllTraits, + ); + if pick.is_ok() { + let range_span = parent_expr.span.with_hi(expr.span.hi()); + tcx.sess.emit_err(errors::MissingParentheseInRange { + span, + ty_str: ty_str.to_string(), + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), + }); + return true; + } + } + } + } + false + } + fn suggest_constraining_numerical_ty( &self, tcx: TyCtxt<'tcx>, @@ -1266,7 +1351,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a floating point literal that ends with '.', // get rid of it to stop this from becoming a member access. let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( lit.span, &format!( @@ -1282,7 +1366,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // local binding if let hir::def::Res::Local(hir_id) = path.res { let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = @@ -1292,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { concrete_type, ); - match (filename, parent_node, snippet) { + match (filename, parent_node) { ( FileName::Real(_), Node::Local(hir::Local { @@ -1300,14 +1383,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty, .. }), - Ok(ref snippet), ) => { + let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi()); err.span_suggestion( // account for `let x: _ = 42;` - // ^^^^ - span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + // ^^^ + type_span, &msg, - format!("{}: {}", snippet, concrete_type), + format!(": {concrete_type}"), Applicability::MaybeIncorrect, ); } @@ -1327,55 +1410,82 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_for_field_method( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, item_name: Ident, ) { if let SelfSource::MethodCall(expr) = source - && let Some((fields, substs)) = self.get_field_candidates(span, actual) + && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() + && let Some((fields, substs)) = + self.get_field_candidates_considering_privacy(span, actual, mod_id) { let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - for candidate_field in fields.iter() { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::AllTraits, - ) - .is_ok() - }, - candidate_field, - substs, - vec![], - self.tcx.parent_module(expr.hir_id).to_def_id(), - ) { - let field_path_str = field_path + + let lang_items = self.tcx.lang_items(); + let never_mention_traits = [ + lang_items.clone_trait(), + lang_items.deref_trait(), + lang_items.deref_mut_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + self.tcx.get_diagnostic_item(sym::AsMut), + self.tcx.get_diagnostic_item(sym::Borrow), + self.tcx.get_diagnostic_item(sym::BorrowMut), + ]; + let candidate_fields: Vec<_> = fields + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .map_or(false, |pick| { + !never_mention_traits + .iter() + .flatten() + .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) + }) + }, + candidate_field, + substs, + vec![], + mod_id, + ) + }) + .map(|field_path| { + field_path .iter() .map(|id| id.name.to_ident_string()) .collect::<Vec<String>>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); - - err.span_suggestion_verbose( - item_name.span.shrink_to_lo(), - "one of the expressions' fields has a method of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } + .join(".") + }) + .collect(); + + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + item_name.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a method of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); } } } - fn check_for_unwrap_self( + fn check_for_inner_self( &self, - err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + err: &mut Diagnostic, source: SelfSource<'tcx>, span: Span, actual: Ty<'tcx>, @@ -1386,81 +1496,168 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; - if !kind.is_enum() { - return; - } + match kind.adt_kind() { + ty::AdtKind::Enum => { + let matching_variants: Vec<_> = kind + .variants() + .iter() + .flat_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if self.resolve_vars_if_possible(field_ty).is_ty_var() { + return None; + } - let matching_variants: Vec<_> = kind - .variants() - .iter() - .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; - let field_ty = field.ty(tcx, substs); + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .ok() + .map(|pick| (variant, field, pick)) + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; - // Skip `_`, since that'll just lead to ambiguity. - if self.resolve_vars_if_possible(field_ty).is_ty_var() { - return None; + match &matching_variants[..] { + [(_, field, pick)] => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + let (article, kind, variant, question) = + if tcx.is_diagnostic_item(sym::Result, kind.did()) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if tcx.is_diagnostic_item(sym::Option, kind.did()) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), + "?", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), + ".expect(\"REASON\")", + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} } - - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - .ok() - .map(|pick| (variant, field, pick)) - }) - .collect(); - - let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false } - }; + // Target wrapper types - types that wrap or pretend to wrap another type, + // perhaps this inner type is meant to be called? + ty::AdtKind::Struct | ty::AdtKind::Union => { + let [first] = ***substs else { return; }; + let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; + let Ok(pick) = self.lookup_probe( + span, + item_name, + ty, + call_expr, + ProbeScope::TraitsInScope, + ) else { return; }; - match &matching_variants[..] { - [(_, field, pick)] => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - let (article, kind, variant, question) = - if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { - ("a", "Result", "Err", ret_ty_matches(sym::Result)) - } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { - ("an", "Option", "None", ret_ty_matches(sym::Option)) - } else { - return; + let name = self.ty_to_value_string(actual); + let inner_id = kind.did(); + let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) = + pick.autoref_or_ptr_adjustment + { + Some(mutbl) + } else { + None + }; + + if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { + err.help("use `with` or `try_with` to access thread local storage"); + } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { + err.help(format!( + "if this `{name}` has been initialized, \ + use one of the `assume_init` methods to access the inner value" + )); + } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) { + let (suggestion, borrow_kind, panic_if) = match mutable { + Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"), + Some(Mutability::Mut) => { + (".borrow_mut()", "mutably borrow", "any borrows exist") + } + None => return, }; - if question { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "use the `?` operator to extract the `{self_ty}` value, propagating \ - {article} `{kind}::{variant}` value to the caller" + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + panicking if {panic_if}" ), - "?", - Applicability::MachineApplicable, + suggestion, + Applicability::MaybeIncorrect, ); - } else { + } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ - panicking if the value is {article} `{kind}::{variant}`" + "use `.lock().unwrap()` to borrow the `{ty}`, \ + blocking the current thread until it can be acquired" ), - ".expect(\"REASON\")", - Applicability::HasPlaceholders, + ".lock().unwrap()", + Applicability::MaybeIncorrect, ); - } + } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) { + let (suggestion, borrow_kind) = match mutable { + Some(Mutability::Not) => (".read().unwrap()", "borrow"), + Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"), + None => return, + }; + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + blocking the current thread until it can be acquired" + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + return; + }; + + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{ty}`"), + ); } - // FIXME(compiler-errors): Support suggestions for other matching enum variants - _ => {} } } @@ -1631,6 +1828,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_for_deref_method( + &self, + err: &mut Diagnostic, + self_source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + ) { + let SelfSource::QPath(ty) = self_source else { return; }; + for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { + if let Ok(pick) = self.probe_for_name( + ty.span, + Mode::Path, + item_name, + IsSuggestion(true), + deref_ty, + ty.hir_id, + ProbeScope::TraitsInScope, + ) { + if deref_ty.is_suggestable(self.tcx, true) + // If this method receives `&self`, then the provided + // argument _should_ coerce, so it's valid to suggest + // just changing the path. + && pick.item.fn_has_self_parameter + && let Some(self_ty) = + self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) + && self_ty.is_ref() + { + let suggested_path = match deref_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Str + | ty::Projection(_) + | ty::Param(_) => format!("{deref_ty}"), + _ => format!("<{deref_ty}>"), + }; + err.span_suggestion_verbose( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + suggested_path, + Applicability::MaybeIncorrect, + ); + } else { + err.span_note( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + ); + } + return; + } + } + } + /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { @@ -1763,6 +2016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>, )], + static_candidates: &[CandidateSource], unsatisfied_bounds: bool, ) { let mut alt_rcvr_sugg = false; @@ -1877,6 +2131,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => true, }) .filter(|info| { + // Static candidates are already implemented, and known not to work + // Do not suggest them again + static_candidates.iter().all(|sc| match *sc { + CandidateSource::Trait(def_id) => def_id != info.def_id, + CandidateSource::Impl(def_id) => { + self.tcx.trait_id_of_impl(def_id) != Some(info.def_id) + } + }) + }) + .filter(|info| { // We approximate the coherence rules to only suggest // traits that are legal to implement by requiring that // either the type or trait is local. Multi-dispatch means @@ -1999,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner).unwrap(); + let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); let (sp, mut introducer) = if let Some(span) = ast_generics.bounds_span_for_suggestions(def_id) { @@ -2158,6 +2422,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` + /// FIXME: currently not working for suggesting `map_or_else`, see #102408 + pub(crate) fn suggest_else_fn_with_closure( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + + if !self.can_coerce(output, expected) { + return false; + } + + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && + let hir::ExprKind::MethodCall( + hir::PathSegment { ident: method_name, .. }, + self_expr, + args, + .., + ) = call_expr.kind && + let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) { + let new_name = Ident { + name: Symbol::intern(&format!("{}_else", method_name.as_str())), + span: method_name.span, + }; + let probe = self.lookup_probe( + expr.span, + new_name, + self_ty, + self_expr, + ProbeScope::TraitsInScope, + ); + + // check the method arguments number + if let Ok(pick) = probe && + let fn_sig = self.tcx.fn_sig(pick.item.def_id) && + let fn_args = fn_sig.skip_binder().inputs() && + fn_args.len() == args.len() + 1 { + err.span_suggestion_verbose( + method_name.span.shrink_to_hi(), + &format!("try calling `{}` instead", new_name.name.as_str()), + "_else", + Applicability::MaybeIncorrect, + ); + return true; + } + } + false + } + /// Checks whether there is a local type somewhere in the chain of /// autoderefs of `rcvr_ty`. fn type_derefs_to_local( @@ -2232,7 +2550,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { fn print_disambiguation_help<'tcx>( item_name: Ident, - args: Option<&'tcx [hir::Expr<'tcx>]>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, err: &mut Diagnostic, trait_name: String, rcvr_ty: Ty<'_>, @@ -2244,7 +2562,7 @@ fn print_disambiguation_help<'tcx>( fn_has_self_parameter: bool, ) { let mut applicability = Applicability::MachineApplicable; - let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) { + let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { let args = format!( "({}{})", if rcvr_ty.is_region_ptr() { @@ -2252,7 +2570,8 @@ fn print_disambiguation_help<'tcx>( } else { "" }, - args.iter() + std::iter::once(receiver) + .chain(args.iter()) .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { applicability = Applicability::HasPlaceholders; "_".to_owned() diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 837c32355..ea90da4a6 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,6 +1,5 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_ast as ast; - use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -569,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { // Determine the binding mode... let bm = match ba { - hir::BindingAnnotation::Unannotated => def_bm, + hir::BindingAnnotation::NONE => def_bm, _ => BindingMode::convert(ba), }; // ...and store it in a side table: @@ -600,7 +599,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If there are multiple arms, make sure they all agree on // what the type of the binding `x` ought to be. if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(pat.span, var_id, local_ty, ti); + self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); } if let Some(p) = sub { @@ -610,7 +609,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { local_ty } - fn check_binding_alt_eq_ty(&self, span: Span, var_id: HirId, ty: Ty<'tcx>, ti: TopInfo<'tcx>) { + fn check_binding_alt_eq_ty( + &self, + ba: hir::BindingAnnotation, + span: Span, + var_id: HirId, + ty: Ty<'tcx>, + ti: TopInfo<'tcx>, + ) { let var_ty = self.local_ty(span, var_id).decl_ty; if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { let hir = self.tcx.hir(); @@ -628,12 +634,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let pre = if in_match { "in the same arm, " } else { "" }; err.note(&format!("{}a binding must have the same type in all alternatives", pre)); - // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing - // `ref` or `&` to the pattern. + self.suggest_adding_missing_ref_or_removing_ref( + &mut err, + span, + var_ty, + self.resolve_vars_with_obligations(ty), + ba, + ); err.emit(); } } + fn suggest_adding_missing_ref_or_removing_ref( + &self, + err: &mut Diagnostic, + span: Span, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + ba: hir::BindingAnnotation, + ) { + match (expected.kind(), actual.kind(), ba) { + (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) + if self.can_eq(self.param_env, *inner_ty, actual).is_ok() => + { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider adding `ref`", + "ref ", + Applicability::MaybeIncorrect, + ); + } + (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) + if self.can_eq(self.param_env, expected, *inner_ty).is_ok() => + { + err.span_suggestion_verbose( + span.with_hi(span.lo() + BytePos(4)), + "consider removing `ref`", + "", + Applicability::MaybeIncorrect, + ); + } + _ => (), + } + } + // Precondition: pat is a Ref(_) pattern fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) { let tcx = self.tcx; @@ -882,7 +926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { - hir::Node::Pat(Pat { kind: hir::PatKind::Struct(..), .. }) => { + hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), "bind the struct field to a different name instead", @@ -936,7 +980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, subpats: &'tcx [Pat<'tcx>], - ddpos: Option<usize>, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, @@ -1021,7 +1065,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check subpatterns. if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.is_some() + || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some() { let ty::Adt(_, substs) = pat_ty.kind() else { bug!("unexpected pattern type {:?}", pat_ty); @@ -1209,14 +1253,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, span: Span, elements: &'tcx [Pat<'tcx>], - ddpos: Option<usize>, + ddpos: hir::DotDotPos, expected: Ty<'tcx>, def_bm: BindingMode, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; let mut expected_len = elements.len(); - if ddpos.is_some() { + if ddpos.as_opt_usize().is_some() { // Require known type only when `..` is present. if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() { expected_len = tys.len(); @@ -1352,7 +1396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .copied() .filter(|(field, _)| { - field.vis.is_accessible_from(tcx.parent_module(pat.hir_id).to_def_id(), tcx) + field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx) && !matches!( tcx.eval_stability(field.did, None, DUMMY_SP, None), EvalResult::Deny { .. } @@ -1745,10 +1789,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(), ); - self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| { - let mut lint = build.build("some fields are not explicitly listed"); + self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( "ensure that all fields are mentioned explicitly by adding the suggested fields", ); @@ -1756,7 +1798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", ty, )); - lint.emit(); + + lint }); } diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 2e0f37eba..ba8cf6926 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,5 +1,5 @@ -use crate::check::method::MethodCallee; -use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp}; +use crate::method::MethodCallee; +use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 22c9e7961..22c9e7961 100644 --- a/compiler/rustc_typeck/src/check/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index dd8f943b9..4dea40829 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -352,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and that the path can be captured with required capture kind (depending on use in closure, /// move closure etc.) /// - /// Returns the set of of adjusted information along with the inferred closure kind and span + /// Returns the set of adjusted information along with the inferred closure kind and span /// associated with the closure kind inference. /// /// Note that we *always* infer a minimal kind, even if @@ -749,10 +749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, closure_head_span, + reasons.migration_message(), |lint| { - let mut diagnostics_builder = lint.build( - &reasons.migration_message(), - ); for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { // Labels all the usage of the captured variable and why they are responsible // for migration being needed @@ -760,13 +758,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { let cause_span = self.tcx.hir().span(*capture_expr_id); - diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", + lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span } => { - diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", self.tcx.hir().name(*var_hir_id), )); } @@ -781,13 +779,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", v = self.tcx.hir().name(*var_hir_id), )); } @@ -800,7 +798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { let var_name = self.tcx.hir().name(*var_hir_id); - diagnostics_builder.span_label(closure_head_span, format!("\ + lint.span_label(closure_head_span, format!("\ in Rust 2018, this closure implements {missing_trait} \ as `{var_name}` implements {missing_trait}, but in Rust 2021, \ this closure will no longer implement {missing_trait} \ @@ -814,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); + lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); let diagnostic_msg = format!( "add a dummy let to cause {} to be fully captured", @@ -857,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We take the indentation from the next non-empty line. let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), &diagnostic_msg, format!("\n{indent}{migration_string};"), @@ -868,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // braces, but with more than just the opening // brace on the first line. We put the `let` // directly after the `{`. - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), &diagnostic_msg, format!(" {migration_string};"), @@ -877,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // This is a closure without braces around the body. // We add braces to add the `let` before the body. - diagnostics_builder.multipart_suggestion( + lint.multipart_suggestion( &diagnostic_msg, vec![ (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), @@ -887,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } else { - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_span, &diagnostic_msg, migration_string, @@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - diagnostics_builder.emit(); + lint }, ); } @@ -1217,7 +1215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Combine all the reasons of why the root variable should be captured as a result of // auto trait implementation issues - auto_trait_migration_reasons.extend(capture_trait_reasons.clone()); + auto_trait_migration_reasons.extend(capture_trait_reasons.iter().copied()); diagnostics_info.push(MigrationLintNote { captures_info, @@ -2024,6 +2022,10 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, closure_id: hir::HirId, ) -> bool { + if tcx.sess.rust_2021() { + return false; + } + let (level, _) = tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index f549807c3..1e26daa9c 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -2,8 +2,7 @@ // unresolved type variables and replaces "ty_var" types with their // substitutions. -use crate::check::FnCtxt; - +use crate::FnCtxt; use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; @@ -16,6 +15,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -192,6 +192,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + // (ouz-a 1005988): Normally `[T] : std::ops::Index<usize>` should be normalized + // into [T] but currently `Where` clause stops the normalization process for it, + // here we compare types of expr and base in a code without `Where` clause they would be equal + // if they are not we don't modify the expr, hence we bypass the ICE + fn is_builtin_index( + &mut self, + typeck_results: &TypeckResults<'tcx>, + e: &hir::Expr<'_>, + base_ty: Ty<'tcx>, + index_ty: Ty<'tcx>, + ) -> bool { + if let Some(elem_ty) = base_ty.builtin_index() { + let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; + let resolved_exp_ty = self.resolve(exp_ty, &e.span); + + elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize + } else { + false + } + } + // Similar to operators, indexing is always assumed to be overloaded // Here, correct cases where an indexing expression can be simplified // to use builtin indexing because the index type is known to be @@ -222,8 +243,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { ) }); let index_ty = self.fcx.resolve_vars_if_possible(index_ty); + let resolved_base_ty = self.resolve(*base_ty, &base.span); - if base_ty.builtin_index().is_some() && index_ty == self.fcx.tcx.types.usize { + if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { // Remove the method call record typeck_results.type_dependent_defs_mut().remove(e.hir_id); typeck_results.node_substs_mut().remove(e.hir_id); @@ -292,6 +314,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_expr(self, e); } + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + match &p.kind { + hir::GenericParamKind::Lifetime { .. } => { + // Nothing to write back here + } + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); + } + } + } + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); @@ -468,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { if !errors_buffer.is_empty() { errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer.drain(..) { + for mut diag in errors_buffer { self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); } } @@ -503,33 +536,37 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let opaque_types = self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { - let hidden_type = match decl.origin { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) + let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); + let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); + + struct RecursionChecker { + def_id: LocalDefId, + } + impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::Opaque(def_id, _) = *t.kind() { + if def_id == self.def_id.to_def_id() { + return ControlFlow::Break(()); } } - if ty - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() - { - return; - } - Some(ty) + t.super_visit_with(self) } - hir::OpaqueTyOrigin::TyAlias => None, - }; + } + if hidden_type + .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) + .is_break() + { + continue; + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.fcx.infcx.tcx, + true, + decl.origin, + ); + self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } } @@ -667,7 +704,7 @@ impl Locatable for hir::HirId { /// unresolved types and so forth. struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -684,27 +721,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } } - fn report_type_error(&self, t: Ty<'tcx>) { + fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) { if !self.tcx.sess.has_errors().is_some() { self.infcx + .err_ctxt() .emit_inference_failure_err( Some(self.body.id()), self.span.to_span(self.tcx), - t.into(), - E0282, - false, - ) - .emit(); - } - } - - fn report_const_error(&self, c: ty::Const<'tcx>) { - if self.tcx.sess.has_errors().is_none() { - self.infcx - .emit_inference_failure_err( - Some(self.body.id()), - self.span.to_span(self.tcx), - c.into(), + p.into(), E0282, false, ) @@ -749,7 +773,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_type_error(t); + self.report_error(t); self.replaced_with_error = true; self.tcx().ty_error() } @@ -766,7 +790,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - self.report_const_error(ct); + self.report_error(ct); self.replaced_with_error = true; self.tcx().const_error(ct.ty()) } diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs deleted file mode 100644 index 75f5aced8..000000000 --- a/compiler/rustc_typeck/src/check/callee.rs +++ /dev/null @@ -1,675 +0,0 @@ -use super::method::MethodCallee; -use super::{Expectation, FnCtxt, TupleArgumentsFlag}; -use crate::type_error_struct; - -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; -use rustc_hir as hir; -use rustc_hir::def::{self, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_infer::{ - infer, - traits::{self, Obligation}, -}; -use rustc_infer::{ - infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, - traits::ObligationCause, -}; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; -use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_target::spec::abi; -use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; - -use std::iter; - -/// Checks that it is legal to call methods of the trait corresponding -/// to `trait_id` (this only cares about the trait, not the specific -/// method that is called). -pub fn check_legal_trait_for_method_call( - tcx: TyCtxt<'_>, - span: Span, - receiver: Option<Span>, - expr_span: Span, - trait_id: DefId, -) { - if tcx.lang_items().drop_trait() == Some(trait_id) { - let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); - err.span_label(span, "explicit destructor calls not allowed"); - - let (sp, suggestion) = receiver - .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) - .filter(|snippet| !snippet.is_empty()) - .map(|snippet| (expr_span, format!("drop({snippet})"))) - .unwrap_or_else(|| (span, "drop".to_string())); - - err.span_suggestion( - sp, - "consider using `drop` function", - suggestion, - Applicability::MaybeIncorrect, - ); - - err.emit(); - } -} - -enum CallStep<'tcx> { - Builtin(Ty<'tcx>), - DeferredClosure(LocalDefId, ty::FnSig<'tcx>), - /// E.g., enum variant constructors. - Overloaded(MethodCallee<'tcx>), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let original_callee_ty = match &callee_expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self - .check_expr_with_expectation_and_args( - callee_expr, - Expectation::NoExpectation, - arg_exprs, - ), - _ => self.check_expr(callee_expr), - }; - - let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); - - let mut autoderef = self.autoderef(callee_expr.span, expr_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); - } - self.register_predicates(autoderef.into_obligations()); - - let output = match result { - None => { - // this will report an error since original_callee_ty is not a fn - self.confirm_builtin_call( - call_expr, - callee_expr, - original_callee_ty, - arg_exprs, - expected, - ) - } - - Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) - } - - Some(CallStep::DeferredClosure(def_id, fn_sig)) => { - self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig) - } - - Some(CallStep::Overloaded(method_callee)) => { - self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) - } - }; - - // we must check that return type of called functions is WF: - self.register_wf_obligation(output.into(), call_expr.span, traits::WellFormed(None)); - - output - } - - fn try_overloaded_call_step( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - autoderef: &Autoderef<'a, 'tcx>, - ) -> Option<CallStep<'tcx>> { - let adjusted_ty = - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", - call_expr, adjusted_ty - ); - - // If the callee is a bare function or a closure, then we're all set. - match *adjusted_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let adjustments = self.adjust_steps(autoderef); - self.apply_adjustments(callee_expr, adjustments); - return Some(CallStep::Builtin(adjusted_ty)); - } - - ty::Closure(def_id, substs) => { - let def_id = def_id.expect_local(); - - // Check whether this is a call to a closure where we - // haven't yet decided on whether the closure is fn vs - // fnmut vs fnonce. If so, we have to defer further processing. - if self.closure_kind(substs).is_none() { - let closure_sig = substs.as_closure().sig(); - let closure_sig = self.replace_bound_vars_with_fresh_vars( - call_expr.span, - infer::FnCall, - closure_sig, - ); - let adjustments = self.adjust_steps(autoderef); - self.record_deferred_call_resolution( - def_id, - DeferredCallResolution { - call_expr, - callee_expr, - adjusted_ty, - adjustments, - fn_sig: closure_sig, - closure_substs: substs, - }, - ); - return Some(CallStep::DeferredClosure(def_id, closure_sig)); - } - } - - // Hack: we know that there are traits implementing Fn for &F - // where F:Fn and so forth. In the particular case of types - // like `x: &mut FnMut()`, if there is a call `x()`, we would - // normally translate to `FnMut::call_mut(&mut x, ())`, but - // that winds up requiring `mut x: &mut FnMut()`. A little - // over the top. The simplest fix by far is to just ignore - // this case and deref again, so we wind up with - // `FnMut::call_mut(&mut *x, ())`. - ty::Ref(..) if autoderef.step_count() == 0 => { - return None; - } - - _ => {} - } - - // Now, we look for the implementation of a Fn trait on the object's type. - // We first do it with the explicit instruction to look for an impl of - // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding - // to the number of call parameters. - // If that fails (or_else branch), we try again without specifying the - // shape of the tuple (hence the None). This allows to detect an Fn trait - // is implemented, and use this information for diagnostic. - self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) - .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) - .map(|(autoref, method)| { - let mut adjustments = self.adjust_steps(autoderef); - adjustments.extend(autoref); - self.apply_adjustments(callee_expr, adjustments); - CallStep::Overloaded(method) - }) - } - - fn try_overloaded_call_traits( - &self, - call_expr: &hir::Expr<'_>, - adjusted_ty: Ty<'tcx>, - opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, - ) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> { - // Try the options that are least restrictive on the caller first. - for (opt_trait_def_id, method_name, borrow) in [ - (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), - (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), - (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), - ] { - let Some(trait_def_id) = opt_trait_def_id else { continue }; - - let opt_input_types = opt_arg_exprs.map(|arg_exprs| { - [self.tcx.mk_tup(arg_exprs.iter().map(|e| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: e.span, - }) - }))] - }); - let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); - - if let Some(ok) = self.lookup_method_in_trait( - call_expr.span, - method_name, - trait_def_id, - adjusted_ty, - opt_input_types, - ) { - let method = self.register_infer_ok_obligations(ok); - let mut autoref = None; - if borrow { - // Check for &self vs &mut self in the method signature. Since this is either - // the Fn or FnMut trait, it should be one of those. - let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() else { - // The `fn`/`fn_mut` lang item is ill-formed, which should have - // caused an error elsewhere. - self.tcx - .sess - .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?"); - return None; - }; - - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[0], - }); - } - return Some((autoref, method)); - } - } - - None - } - - /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the - /// likely intention is to call the closure, suggest `(||{})()`. (#55851) - fn identify_bad_closure_def_and_call( - &self, - err: &mut Diagnostic, - hir_id: hir::HirId, - callee_node: &hir::ExprKind<'_>, - callee_span: Span, - ) { - let hir = self.tcx.hir(); - let parent_hir_id = hir.get_parent_node(hir_id); - let parent_node = hir.get(parent_hir_id); - if let ( - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }), - .. - }), - hir::ExprKind::Block(..), - ) = (parent_node, callee_node) - { - let fn_decl_span = if hir.body(body).generator_kind - == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) - { - // Actually need to unwrap a few more layers of HIR to get to - // the _real_ closure... - let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), - .. - }) = hir.get(async_closure) - { - fn_decl_span - } else { - return; - } - } else { - fn_decl_span - }; - - let start = fn_decl_span.shrink_to_lo(); - let end = callee_span.shrink_to_hi(); - err.multipart_suggestion( - "if you meant to create this closure and immediately call it, surround the \ - closure with parentheses", - vec![(start, "(".to_string()), (end, ")".to_string())], - Applicability::MaybeIncorrect, - ); - } - } - - /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the - /// likely intention is to create an array containing tuples. - fn maybe_suggest_bad_array_definition( - &self, - err: &mut Diagnostic, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - ) -> bool { - let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id); - let parent_node = self.tcx.hir().get(hir_id); - if let ( - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), - hir::ExprKind::Tup(exp), - hir::ExprKind::Call(_, args), - ) = (parent_node, &callee_expr.kind, &call_expr.kind) - && args.len() == exp.len() - { - let start = callee_expr.span.shrink_to_hi(); - err.span_suggestion( - start, - "consider separating array elements with a comma", - ",", - Applicability::MaybeIncorrect, - ); - return true; - } - false - } - - fn confirm_builtin_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - callee_ty: Ty<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { - ty::FnDef(def_id, subst) => { - let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst); - - // Unit testing: function items annotated with - // `#[rustc_evaluate_where_clauses]` trigger special output - // to let us test the trait evaluation system. - if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { - let predicates = self.tcx.predicates_of(def_id); - let predicates = predicates.instantiate(self.tcx, subst); - for (predicate, predicate_span) in - predicates.predicates.iter().zip(&predicates.spans) - { - let obligation = Obligation::new( - ObligationCause::dummy_with_span(callee_expr.span), - self.param_env, - *predicate, - ); - let result = self.evaluate_obligation(&obligation); - self.tcx - .sess - .struct_span_err( - callee_expr.span, - &format!("evaluate({:?}) = {:?}", predicate, result), - ) - .span_label(*predicate_span, "predicate") - .emit(); - } - } - (fn_sig, Some(def_id)) - } - ty::FnPtr(sig) => (sig, None), - _ => { - let mut unit_variant = None; - if let hir::ExprKind::Path(qpath) = &callee_expr.kind - && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) - = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - // Only suggest removing parens if there are no arguments - && arg_exprs.is_empty() - { - let descr = match kind { - def::CtorOf::Struct => "struct", - def::CtorOf::Variant => "enum variant", - }; - let removal_span = - callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); - unit_variant = - Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); - } - - let callee_ty = self.resolve_vars_if_possible(callee_ty); - let mut err = type_error_struct!( - self.tcx.sess, - callee_expr.span, - callee_ty, - E0618, - "expected function, found {}", - match &unit_variant { - Some((_, kind, path)) => format!("{kind} `{path}`"), - None => format!("`{callee_ty}`"), - } - ); - - self.identify_bad_closure_def_and_call( - &mut err, - call_expr.hir_id, - &callee_expr.kind, - callee_expr.span, - ); - - if let Some((removal_span, kind, path)) = &unit_variant { - err.span_suggestion_verbose( - *removal_span, - &format!( - "`{path}` is a unit {kind}, and does not take parentheses to be constructed", - ), - "", - Applicability::MachineApplicable, - ); - } - - let mut inner_callee_path = None; - let def = match callee_expr.kind { - hir::ExprKind::Path(ref qpath) => { - self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - } - hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = - self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee_expr.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MaybeIncorrect, - ); - } - if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { - inner_callee_path = Some(inner_qpath); - self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) - } else { - Res::Err - } - } - _ => Res::Err, - }; - - if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { - err.span_label(call_expr.span, "call expression requires function"); - } - - if let Some(span) = self.tcx.hir().res_span(def) { - let callee_ty = callee_ty.to_string(); - let label = match (unit_variant, inner_callee_path) { - (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), - (_, Some(hir::QPath::Resolved(_, path))) => self - .tcx - .sess - .source_map() - .span_to_snippet(path.span) - .ok() - .map(|p| format!("`{p}` defined here returns `{callee_ty}`")), - _ => { - match def { - // Emit a different diagnostic for local variables, as they are not - // type definitions themselves, but rather variables *of* that type. - Res::Local(hir_id) => Some(format!( - "`{}` has type `{}`", - self.tcx.hir().name(hir_id), - callee_ty - )), - Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { - Some(format!( - "`{}` defined here", - self.tcx.def_path_str(def_id), - )) - } - _ => Some(format!("`{callee_ty}` defined here")), - } - } - }; - if let Some(label) = label { - err.span_label(span, label); - } - } - err.emit(); - - // This is the "default" function signature, used in case of error. - // In that case, we check each argument against "error" in order to - // set up all the node type bindings. - ( - ty::Binder::dummy(self.tcx.mk_fn_sig( - self.err_args(arg_exprs.len()).into_iter(), - self.tcx.ty_error(), - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )), - None, - ) - } - }; - - // Replace any late-bound regions that appear in the function - // signature with region variables. We also have to - // renormalize the associated types at this point, since they - // previously appeared within a `Binder<>` and hence would not - // have been normalized before. - let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); - let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); - - // Call the generic checker. - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - expected_arg_tys, - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::DontTupleArguments, - def_id, - ); - - fn_sig.output() - } - - fn confirm_deferred_closure_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - closure_def_id: LocalDefId, - fn_sig: ty::FnSig<'tcx>, - ) -> Ty<'tcx> { - // `fn_sig` is the *signature* of the closure being called. We - // don't know the full details yet (`Fn` vs `FnMut` etc), but we - // do know the types expected for each argument and the return - // type. - - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - expected_arg_tys, - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::TupleArguments, - Some(closure_def_id.to_def_id()), - ); - - fn_sig.output() - } - - fn confirm_overloaded_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - method_callee: MethodCallee<'tcx>, - ) -> Ty<'tcx> { - let output_type = self.check_method_argument_types( - call_expr.span, - call_expr, - Ok(method_callee), - arg_exprs, - TupleArgumentsFlag::TupleArguments, - expected, - ); - - self.write_method_call(call_expr.hir_id, method_callee); - output_type - } -} - -#[derive(Debug)] -pub struct DeferredCallResolution<'tcx> { - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - adjusted_ty: Ty<'tcx>, - adjustments: Vec<Adjustment<'tcx>>, - fn_sig: ty::FnSig<'tcx>, - closure_substs: SubstsRef<'tcx>, -} - -impl<'a, 'tcx> DeferredCallResolution<'tcx> { - pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) { - debug!("DeferredCallResolution::resolve() {:?}", self); - - // we should not be invoked until the closure kind has been - // determined by upvar inference - assert!(fcx.closure_kind(self.closure_substs).is_some()); - - // We may now know enough to figure out fn vs fnmut etc. - match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { - Some((autoref, method_callee)) => { - // One problem is that when we get here, we are going - // to have a newly instantiated function signature - // from the call trait. This has to be reconciled with - // the older function signature we had before. In - // principle we *should* be able to fn_sigs(), but we - // can't because of the annoying need for a TypeTrace. - // (This always bites me, should find a way to - // refactor it.) - let method_sig = method_callee.sig; - - debug!("attempt_resolution: method_callee={:?}", method_callee); - - for (method_arg_ty, self_arg_ty) in - iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs()) - { - fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty); - } - - fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); - - let mut adjustments = self.adjustments; - adjustments.extend(autoref); - fcx.apply_adjustments(self.callee_expr, adjustments); - - fcx.write_method_call(self.call_expr.hir_id, method_callee); - } - None => { - // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once` - // lang items are not defined (issue #86238). - let mut err = fcx.inh.tcx.sess.struct_span_err( - self.call_expr.span, - "failed to find an overloaded call trait for closure call", - ); - err.help( - "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \ - and have associated `call`/`call_mut`/`call_once` functions", - ); - err.emit(); - } - } - } -} diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs deleted file mode 100644 index 9c1fd9b30..000000000 --- a/compiler/rustc_typeck/src/check/check.rs +++ /dev/null @@ -1,1712 +0,0 @@ -use crate::check::intrinsicck::InlineAsmCtxt; - -use super::coercion::CoerceMany; -use super::compare_method::check_type_bounds; -use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; -use super::*; -use rustc_attr as attr; -use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ItemKind, Node, PathSegment}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; -use rustc_infer::traits::Obligation; -use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::util::{Discr, IntTypeExt}; -use rustc_middle::ty::{ - self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, -}; -use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; -use rustc_span::symbol::sym; -use rustc_span::{self, Span}; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCtxt}; -use rustc_ty_utils::representability::{self, Representability}; - -use std::iter; -use std::ops::ControlFlow; - -pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { - match tcx.sess.target.is_abi_supported(abi) { - Some(true) => (), - Some(false) => { - struct_span_err!( - tcx.sess, - span, - E0570, - "`{abi}` is not a supported ABI for the current target", - ) - .emit(); - } - None => { - tcx.struct_span_lint_hir(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.build("use of calling convention not supported on this target").emit(); - }); - } - } - - // This ABI is only allowed on function pointers - if abi == Abi::CCmseNonSecureCall { - struct_span_err!( - tcx.sess, - span, - E0781, - "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers" - ) - .emit(); - } -} - -/// Helper used for fns and closures. Does the grungy work of checking a function -/// body and returns the function context used for that purpose, since in the case of a fn item -/// there is still a bit more to do. -/// -/// * ... -/// * inherited: other fields inherited from the enclosing fn (if any) -#[instrument(skip(inherited, body), level = "debug")] -pub(super) fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - can_be_generator: Option<hir::Movability>, - return_type_pre_known: bool, -) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); - fcx.return_type_pre_known = return_type_pre_known; - - let tcx = fcx.tcx; - let hir = tcx.hir(); - - let declared_ret_ty = fn_sig.output(); - - let ret_ty = - fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars( - declared_ret_ty, - body.value.hir_id, - decl.output.span(), - param_env, - )); - // If we replaced declared_ret_ty with infer vars, then we must be infering - // an opaque type, so set a flag so we can improve diagnostics. - fcx.return_type_has_opaque = ret_ty != declared_ret_ty; - - fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); - fcx.ret_type_span = Some(decl.output.span()); - - let span = body.value.span; - - fn_maybe_err(tcx, span, fn_sig.abi); - - if fn_sig.abi == Abi::RustCall { - let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 }; - - let err = || { - let item = match tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(header, ..), .. - }) => Some(header), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(header, ..), - .. - }) => Some(header), - // Closures are RustCall, but they tuple their arguments, so shouldn't be checked - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None, - node => bug!("Item being checked wasn't a function/closure: {:?}", node), - }; - - if let Some(header) = item { - tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple"); - } - }; - - if fn_sig.inputs().len() != expected_args { - err() - } else { - // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on - // This will probably require wide-scale changes to support a TupleKind obligation - // We can't resolve this without knowing the type of the param - if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) { - err() - } - } - } - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - // Resume type defaults to `()` if the generator has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - - GatherLocalsVisitor::new(&fcx).visit_body(body); - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { - let span = body.params.last().unwrap().span; - let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); - let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - - Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; - - // Add formal parameters. - let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); - let inputs_fn = fn_sig.inputs().iter().copied(); - for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { - // Check the pattern. - let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); - - // Check that argument is Sized. - // The check for a non-trivial pattern is a hack to avoid duplicate warnings - // for simple cases like `fn foo(x: Trait)`, - // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); - } - - fcx.write_ty(param.hir_id, param_ty); - } - - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - - fcx.in_tail_expr = true; - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value, false); - } - fcx.in_tail_expr = false; - - // We insert the deferred_generator_interiors entry after visiting the body. - // This ensures that all nested generators appear before the entry of this generator. - // resolve_generator_interiors relies on this property. - let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(GeneratorTypes { - resume_ty, - yield_ty, - interior, - movability: can_be_generator.unwrap(), - }) - } else { - None - }; - - // Finalize the return check by taking the LUB of the return types - // we saw and assigning it to the expected return type. This isn't - // really expected to fail, since the coercions would have failed - // earlier when trying to find a LUB. - let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(&fcx); - debug!("actual_return_ty = {:?}", actual_return_ty); - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // We have special-cased the case where the function is declared - // `-> dyn Foo` and we don't actually relate it to the - // `fcx.ret_coercion`, so just substitute a type variable. - actual_return_ty = - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); - debug!("actual_return_ty replaced with {:?}", actual_return_ty); - } - - // HACK(oli-obk, compiler-errors): We should be comparing this against - // `declared_ret_ty`, but then anything uninferred would be inferred to - // the opaque type itself. That again would cause writeback to assume - // we have a recursive call site and do the sadly stabilized fallback to `()`. - fcx.demand_suptype(span, ret_ty, actual_return_ty); - - // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() - && panic_impl_did == hir.local_def_id(fn_id).to_def_id() - { - check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() - && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() - { - check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - (fcx, gen_ty) -} - -fn check_panic_info_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(panic_info_did) = tcx.lang_items().panic_info() else { - tcx.sess.err("language item required, but not found: `panic_info`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_panic_info = match *inputs[0].kind() { - ty::Ref(region, ty, mutbl) => match *ty.kind() { - ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static() - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no const parameters"); - } -} - -fn check_alloc_error_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else { - tcx.sess.err("language item required, but not found: `alloc_layout`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_alloc_layout = match inputs[0].kind() { - ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess - .span_err(span, "`#[alloc_error_handler]` function should have no const parameters"); - } -} - -fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let def = tcx.adt_def(def_id); - let span = tcx.def_span(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - - if def.repr().simd() { - check_simd(tcx, span, def_id); - } - - check_transparent(tcx, span, def); - check_packed(tcx, span, def); -} - -fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { - let def = tcx.adt_def(def_id); - let span = tcx.def_span(def_id); - def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); - check_transparent(tcx, span, def); - check_union_fields(tcx, span, def_id); - check_packed(tcx, span, def); -} - -/// Check that the fields of the `union` do not need dropping. -fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { - let item_type = tcx.type_of(item_def_id); - if let ty::Adt(def, substs) = item_type.kind() { - assert!(def.is_union()); - - fn allowed_union_field<'tcx>( - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - span: Span, - ) -> bool { - // We don't just accept all !needs_drop fields, due to semver concerns. - match ty.kind() { - ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) - ty::Tuple(tys) => { - // allow tuples of allowed types - tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span)) - } - ty::Array(elem, _len) => { - // Like `Copy`, we do *not* special-case length 0. - allowed_union_field(*elem, tcx, param_env, span) - } - _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx.at(span), param_env) - } - } - } - - let param_env = tcx.param_env(item_def_id); - for field in &def.non_enum_variant().fields { - let field_ty = field.ty(tcx, substs); - - if !allowed_union_field(field_ty, tcx, param_env, span) { - let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { - // We are currently checking the type this field came from, so it must be local. - Some(Node::Field(field)) => (field.span, field.ty.span), - _ => unreachable!("mir field has to correspond to hir field"), - }; - struct_span_err!( - tcx.sess, - field_span, - E0740, - "unions cannot contain fields that may need dropping" - ) - .note( - "a type is guaranteed not to need dropping \ - when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type", - ) - .multipart_suggestion_verbose( - "when the type does not implement `Copy`, \ - wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped", - vec![ - (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()), - (ty_span.shrink_to_hi(), ">".into()), - ], - Applicability::MaybeIncorrect, - ) - .emit(); - return false; - } else if field_ty.needs_drop(tcx, param_env) { - // This should never happen. But we can get here e.g. in case of name resolution errors. - tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields"); - } - } - } else { - span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind()); - } - true -} - -/// Check that a `static` is inhabited. -fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { - // Make sure statics are inhabited. - // Other parts of the compiler assume that there are no uninhabited places. In principle it - // would be enough to check this for `extern` statics, as statics with an initializer will - // have UB during initialization if they are uninhabited, but there also seems to be no good - // reason to allow any statics to be uninhabited. - let ty = tcx.type_of(def_id); - let span = tcx.def_span(def_id); - let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) { - Ok(l) => l, - // Foreign statics that overflow their allowed size should emit an error - Err(LayoutError::SizeOverflow(_)) - if { - let node = tcx.hir().get_by_def_id(def_id); - matches!( - node, - hir::Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Static(..), - .. - }) - ) - } => - { - tcx.sess - .struct_span_err(span, "extern static is too large for the current architecture") - .emit(); - return; - } - // Generic statics are rejected, but we still reach this case. - Err(e) => { - tcx.sess.delay_span_bug(span, &e.to_string()); - return; - } - }; - if layout.abi.is_uninhabited() { - tcx.struct_span_lint_hir( - UNINHABITED_STATIC, - tcx.hir().local_def_id_to_hir_id(def_id), - span, - |lint| { - lint.build("static of uninhabited type") - .note("uninhabited statics cannot be initialized, and any access would be an immediate error") - .emit(); - }, - ); - } -} - -/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` -/// projections that would result in "inheriting lifetimes". -pub(super) fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - origin: &hir::OpaqueTyOrigin, -) { - let span = tcx.def_span(def_id); - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - if tcx.type_of(def_id).references_error() { - return; - } - if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { - return; - } - check_opaque_meets_bounds(tcx, def_id, substs, span, origin); -} - -/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result -/// in "inheriting lifetimes". -#[instrument(level = "debug", skip(tcx, span))] -pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - span: Span, -) { - let item = tcx.hir().expect_item(def_id); - debug!(?item, ?span); - - struct FoundParentLifetime; - struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics); - impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> { - type BreakTy = FoundParentLifetime; - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - debug!("FindParentLifetimeVisitor: r={:?}", r); - if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r { - if index < self.0.parent_count as u32 { - return ControlFlow::Break(FoundParentLifetime); - } else { - return ControlFlow::CONTINUE; - } - } - - r.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ConstKind::Unevaluated(..) = c.kind() { - // FIXME(#72219) We currently don't detect lifetimes within substs - // which would violate this check. Even though the particular substitution is not used - // within the const, this should still be fixed. - return ControlFlow::CONTINUE; - } - c.super_visit_with(self) - } - } - - struct ProhibitOpaqueVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - opaque_identity_ty: Ty<'tcx>, - generics: &'tcx ty::Generics, - selftys: Vec<(Span, Option<String>)>, - } - - impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - type BreakTy = Ty<'tcx>; - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t); - if t == self.opaque_identity_ty { - ControlFlow::CONTINUE - } else { - t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics)) - .map_break(|FoundParentLifetime| t) - } - } - } - - impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { - match arg.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { - [ - PathSegment { - res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }), - .. - }, - ] => { - let impl_ty_name = - impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id)); - self.selftys.push((path.span, impl_ty_name)); - } - _ => {} - }, - _ => {} - } - hir::intravisit::walk_ty(self, arg); - } - } - - if let ItemKind::OpaqueTy(hir::OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), - .. - }) = item.kind - { - let mut visitor = ProhibitOpaqueVisitor { - opaque_identity_ty: tcx.mk_opaque( - def_id.to_def_id(), - InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), - ), - generics: tcx.generics_of(def_id), - tcx, - selftys: vec![], - }; - let prohibit_opaque = tcx - .explicit_item_bounds(def_id) - .iter() - .try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor)); - debug!( - "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}", - prohibit_opaque, visitor.opaque_identity_ty, visitor.generics - ); - - if let Some(ty) = prohibit_opaque.break_value() { - visitor.visit_item(&item); - let is_async = match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { - matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..)) - } - _ => unreachable!(), - }; - - let mut err = struct_span_err!( - tcx.sess, - span, - E0760, - "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", - if is_async { "async fn" } else { "impl Trait" }, - ); - - for (span, name) in visitor.selftys { - err.span_suggestion( - span, - "consider spelling out the type instead", - name.unwrap_or_else(|| format!("{:?}", ty)), - Applicability::MaybeIncorrect, - ); - } - err.emit(); - } - } -} - -/// Checks that an opaque type does not contain cycles. -pub(super) fn check_opaque_for_cycles<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) -> Result<(), ErrorGuaranteed> { - if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() { - let reported = match origin { - hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span), - _ => opaque_type_cycle_error(tcx, def_id, span), - }; - Err(reported) - } else { - Ok(()) - } -} - -/// Check that the concrete type behind `impl Trait` actually implements `Trait`. -/// -/// This is mostly checked at the places that specify the opaque type, but we -/// check those cases in the `param_env` of that function, which may have -/// bounds not on this opaque type: -/// -/// type X<T> = impl Clone -/// fn f<T: Clone>(t: T) -> X<T> { -/// t -/// } -/// -/// Without this check the above code is incorrectly accepted: we would ICE if -/// some tried, for example, to clone an `Option<X<&mut ()>>`. -#[instrument(level = "debug", skip(tcx))] -fn check_opaque_meets_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - span: Span, - origin: &hir::OpaqueTyOrigin, -) { - let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let defining_use_anchor = match *origin { - hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, - hir::OpaqueTyOrigin::TyAlias => def_id, - }; - let param_env = tcx.param_env(defining_use_anchor); - - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter( - move |infcx| { - let ocx = ObligationCtxt::new(&infcx); - let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); - - let misc_cause = traits::ObligationCause::misc(span, hir_id); - - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { - Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), - Err(ty_err) => { - tcx.sess.delay_span_bug( - span, - &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), - ); - } - } - - // Additionally require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())) - .to_predicate(tcx); - ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => { - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - defining_use_anchor, - &outlives_environment, - ); - } - } - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - }, - ); -} - -fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { - debug!( - "check_item_type(it.def_id={:?}, it.name={})", - id.def_id, - tcx.def_path_str(id.def_id.to_def_id()) - ); - let _indenter = indenter(); - match tcx.def_kind(id.def_id) { - DefKind::Static(..) => { - tcx.ensure().typeck(id.def_id); - maybe_check_static_with_link_section(tcx, id.def_id); - check_static_inhabited(tcx, id.def_id); - } - DefKind::Const => { - tcx.ensure().typeck(id.def_id); - } - DefKind::Enum => { - let item = tcx.hir().item(id); - let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { - return; - }; - check_enum(tcx, &enum_definition.variants, item.def_id); - } - DefKind::Fn => {} // entirely within check_item_body - DefKind::Impl => { - let it = tcx.hir().item(id); - let hir::ItemKind::Impl(ref impl_) = it.kind else { - return; - }; - debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id); - if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) { - check_impl_items_against_trait( - tcx, - it.span, - it.def_id, - impl_trait_ref, - &impl_.items, - ); - check_on_unimplemented(tcx, it); - } - } - DefKind::Trait => { - let it = tcx.hir().item(id); - let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else { - return; - }; - check_on_unimplemented(tcx, it); - - for item in items.iter() { - let item = tcx.hir().trait_item(item.id); - match item.kind { - hir::TraitItemKind::Fn(ref sig, _) => { - let abi = sig.header.abi; - fn_maybe_err(tcx, item.ident.span, abi); - } - hir::TraitItemKind::Type(.., Some(default)) => { - let assoc_item = tcx.associated_item(item.def_id); - let trait_substs = - InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id()); - let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( - tcx, - assoc_item, - assoc_item, - default.span, - ty::TraitRef { def_id: it.def_id.to_def_id(), substs: trait_substs }, - ); - } - _ => {} - } - } - } - DefKind::Struct => { - check_struct(tcx, id.def_id); - } - DefKind::Union => { - check_union(tcx, id.def_id); - } - DefKind::OpaqueTy => { - let item = tcx.hir().item(id); - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - return; - }; - // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting - // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! - // See https://github.com/rust-lang/rust/issues/75100 - if !tcx.sess.opts.actually_rustdoc { - let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); - check_opaque(tcx, item.def_id, substs, &origin); - } - } - DefKind::TyAlias => { - let pty_ty = tcx.type_of(id.def_id); - let generics = tcx.generics_of(id.def_id); - check_type_params_are_used(tcx, &generics, pty_ty); - } - DefKind::ForeignMod => { - let it = tcx.hir().item(id); - let hir::ItemKind::ForeignMod { abi, items } = it.kind else { - return; - }; - check_abi(tcx, it.hir_id(), it.span, abi); - - if abi == Abi::RustIntrinsic { - for item in items { - let item = tcx.hir().foreign_item(item.id); - intrinsic::check_intrinsic_type(tcx, item); - } - } else if abi == Abi::PlatformIntrinsic { - for item in items { - let item = tcx.hir().foreign_item(item.id); - intrinsic::check_platform_intrinsic_type(tcx, item); - } - } else { - for item in items { - let def_id = item.id.def_id; - let generics = tcx.generics_of(def_id); - let own_counts = generics.own_counts(); - if generics.params.len() - own_counts.lifetimes != 0 { - let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { - (_, 0) => ("type", "types", Some("u32")), - // We don't specify an example value, because we can't generate - // a valid value for any type. - (0, _) => ("const", "consts", None), - _ => ("type or const", "types or consts", None), - }; - struct_span_err!( - tcx.sess, - item.span, - E0044, - "foreign items may not have {kinds} parameters", - ) - .span_label(item.span, &format!("can't have {kinds} parameters")) - .help( - // FIXME: once we start storing spans for type arguments, turn this - // into a suggestion. - &format!( - "replace the {} parameters with concrete {}{}", - kinds, - kinds_pl, - egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), - ), - ) - .emit(); - } - - let item = tcx.hir().foreign_item(item.id); - match item.kind { - hir::ForeignItemKind::Fn(ref fn_decl, _, _) => { - require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span); - } - hir::ForeignItemKind::Static(..) => { - check_static_inhabited(tcx, def_id); - } - _ => {} - } - } - } - } - DefKind::GlobalAsm => { - let it = tcx.hir().item(id); - let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) }; - InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id()); - } - _ => {} - } -} - -pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, item.def_id.to_def_id()); -} - -pub(super) fn check_specialization_validity<'tcx>( - tcx: TyCtxt<'tcx>, - trait_def: &ty::TraitDef, - trait_item: &ty::AssocItem, - impl_id: DefId, - impl_item: &hir::ImplItemRef, -) { - let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) else { return }; - let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| { - if parent.is_from_trait() { - None - } else { - Some((parent, parent.item(tcx, trait_item.def_id))) - } - }); - - let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { - match parent_item { - // Parent impl exists, and contains the parent item we're trying to specialize, but - // doesn't mark it `default`. - Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { - Some(Err(parent_impl.def_id())) - } - - // Parent impl contains item and makes it specializable. - Some(_) => Some(Ok(())), - - // Parent impl doesn't mention the item. This means it's inherited from the - // grandparent. In that case, if parent is a `default impl`, inherited items use the - // "defaultness" from the grandparent, else they are final. - None => { - if tcx.impl_defaultness(parent_impl.def_id()).is_default() { - None - } else { - Some(Err(parent_impl.def_id())) - } - } - } - }); - - // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the - // item. This is allowed, the item isn't actually getting specialized here. - let result = opt_result.unwrap_or(Ok(())); - - if let Err(parent_impl) = result { - report_forbidden_specialization(tcx, impl_item, parent_impl); - } -} - -fn check_impl_items_against_trait<'tcx>( - tcx: TyCtxt<'tcx>, - full_impl_span: Span, - impl_id: LocalDefId, - impl_trait_ref: ty::TraitRef<'tcx>, - impl_item_refs: &[hir::ImplItemRef], -) { - // If the trait reference itself is erroneous (so the compilation is going - // to fail), skip checking the items here -- the `impl_item` table in `tcx` - // isn't populated for such impls. - if impl_trait_ref.references_error() { - return; - } - - // Negative impls are not expected to have any items - match tcx.impl_polarity(impl_id) { - ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} - ty::ImplPolarity::Negative => { - if let [first_item_ref, ..] = impl_item_refs { - let first_item_span = tcx.hir().impl_item(first_item_ref.id).span; - struct_span_err!( - tcx.sess, - first_item_span, - E0749, - "negative impls cannot have any items" - ) - .emit(); - } - return; - } - } - - let trait_def = tcx.trait_def(impl_trait_ref.def_id); - - for impl_item in impl_item_refs { - let ty_impl_item = tcx.associated_item(impl_item.id.def_id); - let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { - tcx.associated_item(trait_item_id) - } else { - // Checked in `associated_item`. - tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait"); - continue; - }; - let impl_item_full = tcx.hir().impl_item(impl_item.id); - match impl_item_full.kind { - hir::ImplItemKind::Const(..) => { - // Find associated const definition. - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } - hir::ImplItemKind::Fn(..) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_impl_method( - tcx, - &ty_impl_item, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } - hir::ImplItemKind::TyAlias(impl_ty) => { - let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); - compare_ty_impl( - tcx, - &ty_impl_item, - impl_ty.span, - &ty_trait_item, - impl_trait_ref, - opt_trait_span, - ); - } - } - - check_specialization_validity( - tcx, - trait_def, - &ty_trait_item, - impl_id.to_def_id(), - impl_item, - ); - } - - if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { - // Check for missing items from trait - let mut missing_items = Vec::new(); - - let mut must_implement_one_of: Option<&[Ident]> = - trait_def.must_implement_one_of.as_deref(); - - for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) { - let is_implemented = ancestors - .leaf_def(tcx, trait_item_id) - .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value()); - - if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { - missing_items.push(tcx.associated_item(trait_item_id)); - } - - if let Some(required_items) = &must_implement_one_of { - // true if this item is specifically implemented in this impl - let is_implemented_here = ancestors - .leaf_def(tcx, trait_item_id) - .map_or(false, |node_item| !node_item.defining_node.is_from_trait()); - - if is_implemented_here { - let trait_item = tcx.associated_item(trait_item_id); - if required_items.contains(&trait_item.ident(tcx)) { - must_implement_one_of = None; - } - } - } - } - - if !missing_items.is_empty() { - missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span); - } - - if let Some(missing_items) = must_implement_one_of { - let attr_span = tcx - .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of) - .map(|attr| attr.span); - - missing_items_must_implement_one_of_err( - tcx, - tcx.def_span(impl_id), - missing_items, - attr_span, - ); - } - } -} - -/// Checks whether a type can be represented in memory. In particular, it -/// identifies types that contain themselves without indirection through a -/// pointer, which would mean their size is unbounded. -pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { - let rty = tcx.type_of(item_def_id); - - // Check that it is possible to represent this type. This call identifies - // (1) types that contain themselves and (2) types that contain a different - // recursive type. It is only necessary to throw an error on those that - // contain themselves. For case 2, there must be an inner type that will be - // caught by case 1. - match representability::ty_is_representable(tcx, rty, sp, None) { - Representability::SelfRecursive(spans) => { - recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); - return false; - } - Representability::Representable | Representability::ContainsRecursive => (), - } - true -} - -pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { - let t = tcx.type_of(def_id); - if let ty::Adt(def, substs) = t.kind() - && def.is_struct() - { - let fields = &def.non_enum_variant().fields; - if fields.is_empty() { - struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); - return; - } - let e = fields[0].ty(tcx, substs); - if !fields.iter().all(|f| f.ty(tcx, substs) == e) { - struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous") - .span_label(sp, "SIMD elements must have the same type") - .emit(); - return; - } - - let len = if let ty::Array(_ty, c) = e.kind() { - c.try_eval_usize(tcx, tcx.param_env(def.did())) - } else { - Some(fields.len() as u64) - }; - if let Some(len) = len { - if len == 0 { - struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit(); - return; - } else if len > MAX_SIMD_LANES { - struct_span_err!( - tcx.sess, - sp, - E0075, - "SIMD vector cannot have more than {MAX_SIMD_LANES} elements", - ) - .emit(); - return; - } - } - - // Check that we use types valid for use in the lanes of a SIMD "vector register" - // These are scalar types which directly match a "machine" type - // Yes: Integers, floats, "thin" pointers - // No: char, "fat" pointers, compound types - match e.kind() { - ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok - ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors - ty::Array(t, _clen) - if matches!( - t.kind(), - ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) - ) => - { /* struct([f32; 4]) is ok */ } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0077, - "SIMD vector element type should be a \ - primitive scalar (integer/float/pointer) type" - ) - .emit(); - return; - } - } - } -} - -pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { - let repr = def.repr(); - if repr.packed() { - for attr in tcx.get_attrs(def.did(), sym::repr) { - for r in attr::parse_repr_attr(&tcx.sess, attr) { - if let attr::ReprPacked(pack) = r - && let Some(repr_pack) = repr.pack - && pack as u64 != repr_pack.bytes() - { - struct_span_err!( - tcx.sess, - sp, - E0634, - "type has conflicting packed representation hints" - ) - .emit(); - } - } - } - if repr.align.is_some() { - struct_span_err!( - tcx.sess, - sp, - E0587, - "type has conflicting packed and align representation hints" - ) - .emit(); - } else { - if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) { - let mut err = struct_span_err!( - tcx.sess, - sp, - E0588, - "packed type cannot transitively contain a `#[repr(align)]` type" - ); - - err.span_note( - tcx.def_span(def_spans[0].0), - &format!( - "`{}` has a `#[repr(align)]` attribute", - tcx.item_name(def_spans[0].0) - ), - ); - - if def_spans.len() > 2 { - let mut first = true; - for (adt_def, span) in def_spans.iter().skip(1).rev() { - let ident = tcx.item_name(*adt_def); - err.span_note( - *span, - &if first { - format!( - "`{}` contains a field of type `{}`", - tcx.type_of(def.did()), - ident - ) - } else { - format!("...which contains a field of type `{ident}`") - }, - ); - first = false; - } - } - - err.emit(); - } - } - } -} - -pub(super) fn check_packed_inner( - tcx: TyCtxt<'_>, - def_id: DefId, - stack: &mut Vec<DefId>, -) -> Option<Vec<(DefId, Span)>> { - if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() { - if def.is_struct() || def.is_union() { - if def.repr().align.is_some() { - return Some(vec![(def.did(), DUMMY_SP)]); - } - - stack.push(def_id); - for field in &def.non_enum_variant().fields { - if let ty::Adt(def, _) = field.ty(tcx, substs).kind() - && !stack.contains(&def.did()) - && let Some(mut defs) = check_packed_inner(tcx, def.did(), stack) - { - defs.push((def.did(), field.ident(tcx).span)); - return Some(defs); - } - } - stack.pop(); - } - } - - None -} - -pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) { - if !adt.repr().transparent() { - return; - } - - if adt.is_union() && !tcx.features().transparent_unions { - feature_err( - &tcx.sess.parse_sess, - sym::transparent_unions, - sp, - "transparent unions are unstable", - ) - .emit(); - } - - if adt.variants().len() != 1 { - bad_variant_count(tcx, adt, sp, adt.did()); - if adt.variants().is_empty() { - // Don't bother checking the fields. No variants (and thus no fields) exist. - return; - } - } - - // For each field, figure out if it's known to be a ZST and align(1), with "known" - // respecting #[non_exhaustive] attributes. - let field_infos = adt.all_fields().map(|field| { - let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did)); - let param_env = tcx.param_env(field.did); - let layout = tcx.layout_of(param_env.and(ty)); - // We are currently checking the type this field came from, so it must be local - let span = tcx.hir().span_if_local(field.did).unwrap(); - let zst = layout.map_or(false, |layout| layout.is_zst()); - let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1); - if !zst { - return (span, zst, align1, None); - } - - fn check_non_exhaustive<'tcx>( - tcx: TyCtxt<'tcx>, - t: Ty<'tcx>, - ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> { - match t.kind() { - ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)), - ty::Array(ty, _) => check_non_exhaustive(tcx, *ty), - ty::Adt(def, subst) => { - if !def.did().is_local() { - let non_exhaustive = def.is_variant_list_non_exhaustive() - || def - .variants() - .iter() - .any(ty::VariantDef::is_field_list_non_exhaustive); - let has_priv = def.all_fields().any(|f| !f.vis.is_public()); - if non_exhaustive || has_priv { - return ControlFlow::Break(( - def.descr(), - def.did(), - subst, - non_exhaustive, - )); - } - } - def.all_fields() - .map(|field| field.ty(tcx, subst)) - .try_for_each(|t| check_non_exhaustive(tcx, t)) - } - _ => ControlFlow::Continue(()), - } - } - - (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) - }); - - let non_zst_fields = field_infos - .clone() - .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); - let non_zst_count = non_zst_fields.clone().count(); - if non_zst_count >= 2 { - bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp); - } - let incompatible_zst_fields = - field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); - let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; - for (span, zst, align1, non_exhaustive) in field_infos { - if zst && !align1 { - struct_span_err!( - tcx.sess, - span, - E0691, - "zero-sized field in transparent {} has alignment larger than 1", - adt.descr(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); - } - if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive { - tcx.struct_span_lint_hir( - REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, - tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), - span, - |lint| { - let note = if non_exhaustive { - "is marked with `#[non_exhaustive]`" - } else { - "contains private fields" - }; - let field_ty = tcx.def_path_str_with_substs(def_id, substs); - lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types") - .note(format!("this {descr} contains `{field_ty}`, which {note}, \ - and makes it not a breaking change to become non-zero-sized in the future.")) - .emit(); - }, - ) - } - } -} - -#[allow(trivial_numeric_casts)] -fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) { - let def = tcx.adt_def(def_id); - let sp = tcx.def_span(def_id); - def.destructor(tcx); // force the destructor to be evaluated - - if vs.is_empty() { - if let Some(attr) = tcx.get_attr(def_id.to_def_id(), sym::repr) { - struct_span_err!( - tcx.sess, - attr.span, - E0084, - "unsupported representation for zero-variant enum" - ) - .span_label(sp, "zero-variant enum") - .emit(); - } - } - - let repr_type_ty = def.repr().discr_type().to_ty(tcx); - if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 { - if !tcx.features().repr128 { - feature_err( - &tcx.sess.parse_sess, - sym::repr128, - sp, - "repr with 128-bit type is unstable", - ) - .emit(); - } - } - - for v in vs { - if let Some(ref e) = v.disr_expr { - tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id)); - } - } - - if tcx.adt_def(def_id).repr().int.is_none() && tcx.features().arbitrary_enum_discriminant { - let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); - - let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); - let has_non_units = vs.iter().any(|var| !is_unit(var)); - let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); - let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); - - if disr_non_unit || (disr_units && has_non_units) { - let mut err = - struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified"); - err.emit(); - } - } - - let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len()); - // This tracks the previous variant span (in the loop) incase we need it for diagnostics - let mut prev_variant_span: Span = DUMMY_SP; - for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) { - // Check for duplicate discriminant values - if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) { - let variant_did = def.variant(VariantIdx::new(i)).def_id; - let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local()); - let variant_i = tcx.hir().expect_variant(variant_i_hir_id); - let i_span = match variant_i.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => tcx.def_span(variant_did), - }; - let span = match v.disr_expr { - Some(ref expr) => tcx.hir().span(expr.hir_id), - None => v.span, - }; - let display_discr = format_discriminant_overflow(tcx, v, discr); - let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]); - let no_disr = v.disr_expr.is_none(); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0081, - "discriminant value `{}` assigned more than once", - discr, - ); - - err.span_label(i_span, format!("first assignment of {display_discr_i}")); - err.span_label(span, format!("second assignment of {display_discr}")); - - if no_disr { - err.span_label( - prev_variant_span, - format!( - "assigned discriminant for `{}` was incremented from this discriminant", - v.ident - ), - ); - } - err.emit(); - } - - disr_vals.push(discr); - prev_variant_span = v.span; - } - - check_representable(tcx, sp, def_id); - check_transparent(tcx, sp, def); -} - -/// In the case that a discriminant is both a duplicate and an overflowing literal, -/// we insert both the assigned discriminant and the literal it overflowed from into the formatted -/// output. Otherwise we format the discriminant normally. -fn format_discriminant_overflow<'tcx>( - tcx: TyCtxt<'tcx>, - variant: &hir::Variant<'_>, - dis: Discr<'tcx>, -) -> String { - if let Some(expr) = &variant.disr_expr { - let body = &tcx.hir().body(expr.body).value; - if let hir::ExprKind::Lit(lit) = &body.kind - && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node - && dis.val != *lit_value - { - return format!("`{dis}` (overflowed from `{lit_value}`)"); - } - } - - format!("`{dis}`") -} - -pub(super) fn check_type_params_are_used<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &ty::Generics, - ty: Ty<'tcx>, -) { - debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty); - - assert_eq!(generics.parent, None); - - if generics.own_counts().types == 0 { - return; - } - - let mut params_used = BitSet::new_empty(generics.params.len()); - - if ty.references_error() { - // If there is already another error, do not emit - // an error for not using a type parameter. - assert!(tcx.sess.has_errors().is_some()); - return; - } - - for leaf in ty.walk() { - if let GenericArgKind::Type(leaf_ty) = leaf.unpack() - && let ty::Param(param) = leaf_ty.kind() - { - debug!("found use of ty param {:?}", param); - params_used.insert(param.index); - } - } - - for param in &generics.params { - if !params_used.contains(param.index) - && let ty::GenericParamDefKind::Type { .. } = param.kind - { - let span = tcx.def_span(param.def_id); - struct_span_err!( - tcx.sess, - span, - E0091, - "type parameter `{}` is unused", - param.name, - ) - .span_label(span, "unused type parameter") - .emit(); - } - } -} - -pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - let module = tcx.hir_module_items(module_def_id); - for id in module.items() { - check_item_type(tcx, id); - } -} - -fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed { - struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing") - .span_label(span, "recursive `async fn`") - .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`") - .note( - "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion", - ) - .emit() -} - -/// Emit an error for recursive opaque types. -/// -/// If this is a return `impl Trait`, find the item's return expressions and point at them. For -/// direct recursion this is enough, but for indirect recursion also point at the last intermediary -/// `impl Trait`. -/// -/// If all the return expressions evaluate to `!`, then we explain that the error will go away -/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. -fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed { - let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type"); - - let mut label = false; - if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) { - let typeck_results = tcx.typeck(def_id); - if visitor - .returns - .iter() - .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) - .all(|ty| matches!(ty.kind(), ty::Never)) - { - let spans = visitor - .returns - .iter() - .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) - .map(|expr| expr.span) - .collect::<Vec<Span>>(); - let span_len = spans.len(); - if span_len == 1 { - err.span_label(spans[0], "this returned value is of `!` type"); - } else { - let mut multispan: MultiSpan = spans.clone().into(); - for span in spans { - multispan.push_span_label(span, "this returned value is of `!` type"); - } - err.span_note(multispan, "these returned values have a concrete \"never\" type"); - } - err.help("this error will resolve once the item's body returns a concrete type"); - } else { - let mut seen = FxHashSet::default(); - seen.insert(span); - err.span_label(span, "recursive opaque type"); - label = true; - for (sp, ty) in visitor - .returns - .iter() - .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) - .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) - { - struct OpaqueTypeCollector(Vec<DefId>); - impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector { - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - match *t.kind() { - ty::Opaque(def, _) => { - self.0.push(def); - ControlFlow::CONTINUE - } - _ => t.super_visit_with(self), - } - } - } - let mut visitor = OpaqueTypeCollector(vec![]); - ty.visit_with(&mut visitor); - for def_id in visitor.0 { - let ty_span = tcx.def_span(def_id); - if !seen.contains(&ty_span) { - err.span_label(ty_span, &format!("returning this opaque type `{ty}`")); - seen.insert(ty_span); - } - err.span_label(sp, &format!("returning here with type `{ty}`")); - } - } - } - } - if !label { - err.span_label(span, "cannot resolve opaque type"); - } - err.emit() -} diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs deleted file mode 100644 index fee872155..000000000 --- a/compiler/rustc_typeck/src/check/closure.rs +++ /dev/null @@ -1,805 +0,0 @@ -//! Code for type-checking closure expressions. - -use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; - -use crate::astconv::AstConv; -use crate::rustc_middle::ty::subst::Subst; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::LateBoundRegionConversionTime; -use rustc_infer::infer::{InferOk, InferResult}; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty}; -use rustc_span::source_map::Span; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::ArgKind; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use std::cmp; -use std::iter; - -/// What signature do we *expect* the closure to have from context? -#[derive(Debug)] -struct ExpectedSig<'tcx> { - /// Span that gave us this expectation, if we know that. - cause_span: Option<Span>, - sig: ty::PolyFnSig<'tcx>, -} - -struct ClosureSignatures<'tcx> { - bound_sig: ty::PolyFnSig<'tcx>, - liberated_sig: ty::FnSig<'tcx>, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")] - pub fn check_expr_closure( - &self, - expr: &hir::Expr<'_>, - _capture: hir::CaptureBy, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - gen: Option<hir::Movability>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - trace!("expr = {:#?}", expr); - - // It's always helpful for inference if we know the kind of - // closure sooner rather than later, so first examine the expected - // type, and see if can glean a closure kind from there. - let (expected_sig, expected_kind) = match expected.to_option(self) { - Some(ty) => self.deduce_expectations_from_expected_type(ty), - None => (None, None), - }; - let body = self.tcx.hir().body(body_id); - self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) - } - - #[instrument(skip(self, expr, body, decl), level = "debug")] - fn check_closure( - &self, - expr: &hir::Expr<'_>, - opt_kind: Option<ty::ClosureKind>, - decl: &'tcx hir::FnDecl<'tcx>, - body: &'tcx hir::Body<'tcx>, - gen: Option<hir::Movability>, - expected_sig: Option<ExpectedSig<'tcx>>, - ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); - debug!(?expr_def_id); - - let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig); - - debug!(?bound_sig, ?liberated_sig); - - let return_type_pre_known = !liberated_sig.output().is_ty_infer(); - - let generator_types = check_fn( - self, - self.param_env.without_const(), - liberated_sig, - decl, - expr.hir_id, - body, - gen, - return_type_pre_known, - ) - .1; - - let parent_substs = InternalSubsts::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), - ); - - let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(expr.hir_id), - }); - - if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types - { - let generator_substs = ty::GeneratorSubsts::new( - self.tcx, - ty::GeneratorSubstsParts { - parent_substs, - resume_ty, - yield_ty, - return_ty: liberated_sig.output(), - witness: interior, - tupled_upvars_ty, - }, - ); - - return self.tcx.mk_generator( - expr_def_id.to_def_id(), - generator_substs.substs, - movability, - ); - } - - // Tuple up the arguments and insert the resulting function type into - // the `closures` table. - let sig = bound_sig.map_bound(|sig| { - self.tcx.mk_fn_sig( - iter::once(self.tcx.intern_tup(sig.inputs())), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) - }); - - debug!(?sig, ?opt_kind); - - let closure_kind_ty = match opt_kind { - Some(kind) => kind.to_ty(self.tcx), - - // Create a type variable (for now) to represent the closure kind. - // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, - }), - }; - - let closure_substs = ty::ClosureSubsts::new( - self.tcx, - ty::ClosureSubstsParts { - parent_substs, - closure_kind_ty, - closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig), - tupled_upvars_ty, - }, - ); - - let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs); - - debug!(?expr.hir_id, ?closure_type); - - closure_type - } - - /// Given the expected type, figures out what it can about this closure we - /// are about to type check: - #[instrument(skip(self), level = "debug")] - fn deduce_expectations_from_expected_type( - &self, - expected_ty: Ty<'tcx>, - ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { - match *expected_ty.kind() { - ty::Opaque(def_id, substs) => { - let bounds = self.tcx.bound_explicit_item_bounds(def_id); - let sig = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(pred, span)| match pred.0.kind().skip_binder() { - ty::PredicateKind::Projection(proj_predicate) => self - .deduce_sig_from_projection( - Some(span.0), - pred.0 - .kind() - .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)), - ), - _ => None, - }); - - let kind = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .filter_map(|(pred, _)| match pred.0.kind().skip_binder() { - ty::PredicateKind::Trait(tp) => { - self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) - } - _ => None, - }) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - trace!(?sig, ?kind); - (sig, kind) - } - ty::Dynamic(ref object_type, ..) => { - let sig = object_type.projection_bounds().find_map(|pb| { - let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); - self.deduce_sig_from_projection(None, pb) - }); - let kind = object_type - .principal_def_id() - .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); - (sig, kind) - } - ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), - ty::FnPtr(sig) => { - let expected_sig = ExpectedSig { cause_span: None, sig }; - (Some(expected_sig), Some(ty::ClosureKind::Fn)) - } - _ => (None, None), - } - } - - fn deduce_expectations_from_obligations( - &self, - expected_vid: ty::TyVid, - ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { - let expected_sig = - self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| { - debug!(?obligation.predicate); - - let bound_predicate = obligation.predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = - obligation.predicate.kind().skip_binder() - { - // Given a Projection predicate, we can potentially infer - // the complete signature. - self.deduce_sig_from_projection( - Some(obligation.cause.span), - bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }); - - // Even if we can't infer the full signature, we may be able to - // infer the kind. This can occur when we elaborate a predicate - // like `F : Fn<A>`. Note that due to subtyping we could encounter - // many viable options, so pick the most restrictive. - let expected_kind = self - .obligations_for_self_ty(expected_vid) - .filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id())) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - - (expected_sig, expected_kind) - } - - /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce - /// everything we need to know about a closure or generator. - /// - /// The `cause_span` should be the span that caused us to - /// have this expected signature, or `None` if we can't readily - /// know that. - #[instrument(level = "debug", skip(self, cause_span))] - fn deduce_sig_from_projection( - &self, - cause_span: Option<Span>, - projection: ty::PolyProjectionPredicate<'tcx>, - ) -> Option<ExpectedSig<'tcx>> { - let tcx = self.tcx; - - let trait_def_id = projection.trait_def_id(tcx); - - let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some(); - let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); - let is_gen = gen_trait == trait_def_id; - if !is_fn && !is_gen { - debug!("not fn or generator"); - return None; - } - - if is_gen { - // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` - // associated item and not yield. - let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; - if return_assoc_item != projection.projection_def_id() { - debug!("not return assoc item of generator"); - return None; - } - } - - let input_tys = if is_fn { - let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1); - let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); - debug!(?arg_param_ty); - - match arg_param_ty.kind() { - &ty::Tuple(tys) => tys, - _ => return None, - } - } else { - // Generators with a `()` resume type may be defined with 0 or 1 explicit arguments, - // else they must have exactly 1 argument. For now though, just give up in this case. - return None; - }; - - // Since this is a return parameter type it is safe to unwrap. - let ret_param_ty = projection.skip_binder().term.ty().unwrap(); - let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); - debug!(?ret_param_ty); - - let sig = projection.rebind(self.tcx.mk_fn_sig( - input_tys.iter(), - ret_param_ty, - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - debug!(?sig); - - Some(ExpectedSig { cause_span, sig }) - } - - fn sig_of_closure( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: Option<ExpectedSig<'tcx>>, - ) -> ClosureSignatures<'tcx> { - if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e) - } else { - self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body) - } - } - - /// If there is no expected signature, then we will convert the - /// types that the user gave into a signature. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] - fn sig_of_closure_no_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - ) -> ClosureSignatures<'tcx> { - let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); - - self.closure_sigs(expr_def_id, body, bound_sig) - } - - /// Invoked to compute the signature of a closure expression. This - /// combines any user-provided type annotations (e.g., `|x: u32| - /// -> u32 { .. }`) with the expected signature. - /// - /// The approach is as follows: - /// - /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. - /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. - /// - If we have no expectation `E`, then the signature of the closure is `S`. - /// - Otherwise, the signature of the closure is E. Moreover: - /// - Skolemize the late-bound regions in `E`, yielding `E'`. - /// - Instantiate all the late-bound regions bound in the closure within `S` - /// with fresh (existential) variables, yielding `S'` - /// - Require that `E' = S'` - /// - We could use some kind of subtyping relationship here, - /// I imagine, but equality is easier and works fine for - /// our purposes. - /// - /// The key intuition here is that the user's types must be valid - /// from "the inside" of the closure, but the expectation - /// ultimately drives the overall signature. - /// - /// # Examples - /// - /// ```ignore (illustrative) - /// fn with_closure<F>(_: F) - /// where F: Fn(&u32) -> &u32 { .. } - /// - /// with_closure(|x: &u32| { ... }) - /// ``` - /// - /// Here: - /// - E would be `fn(&u32) -> &u32`. - /// - S would be `fn(&u32) -> - /// - E' is `&'!0 u32 -> &'!0 u32` - /// - S' is `&'?0 u32 -> ?T` - /// - /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. - /// - /// # Arguments - /// - /// - `expr_def_id`: the `DefId` of the closure expression - /// - `decl`: the HIR declaration of the closure - /// - `body`: the body of the closure - /// - `expected_sig`: the expected signature (if any). Note that - /// this is missing a binder: that is, there may be late-bound - /// regions with depth 1, which are bound then by the closure. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] - fn sig_of_closure_with_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: ExpectedSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - // Watch out for some surprises and just ignore the - // expectation if things don't see to match up with what we - // expect. - if expected_sig.sig.c_variadic() != decl.c_variadic { - return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body); - } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { - return self.sig_of_closure_with_mismatched_number_of_arguments( - expr_def_id, - decl, - body, - expected_sig, - ); - } - - // Create a `PolyFnSig`. Note the oddity that late bound - // regions appearing free in `expected_sig` are now bound up - // in this binder we are creating. - assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); - let bound_sig = expected_sig.sig.map_bound(|sig| { - self.tcx.mk_fn_sig( - sig.inputs().iter().cloned(), - sig.output(), - sig.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - ) - }); - - // `deduce_expectations_from_expected_type` introduces - // late-bound lifetimes defined elsewhere, which we now - // anonymize away, so as not to confuse the user. - let bound_sig = self.tcx.anonymize_late_bound_regions(bound_sig); - - let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig); - - // Up till this point, we have ignored the annotations that the user - // gave. This function will check that they unify successfully. - // Along the way, it also writes out entries for types that the user - // wrote into our typeck results, which are then later used by the privacy - // check. - match self.check_supplied_sig_against_expectation( - hir_id, - expr_def_id, - decl, - body, - &closure_sigs, - ) { - Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), - } - - closure_sigs - } - - fn sig_of_closure_with_mismatched_number_of_arguments( - &self, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: ExpectedSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - let hir = self.tcx.hir(); - let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); - let expected_args: Vec<_> = expected_sig - .sig - .skip_binder() - .inputs() - .iter() - .map(|ty| ArgKind::from_expected_ty(*ty, None)) - .collect(); - let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { - Some((sp, args)) => (Some(sp), args), - None => (None, Vec::new()), - }; - let expected_span = - expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); - self.report_arg_count_mismatch( - expected_span, - closure_span, - expected_args, - found_args, - true, - ) - .emit(); - - let error_sig = self.error_sig_of_closure(decl); - - self.closure_sigs(expr_def_id, body, error_sig) - } - - /// Enforce the user's types against the expectation. See - /// `sig_of_closure_with_expectation` for details on the overall - /// strategy. - fn check_supplied_sig_against_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sigs: &ClosureSignatures<'tcx>, - ) -> InferResult<'tcx, ()> { - // Get the signature S that the user gave. - // - // (See comment on `sig_of_closure_with_expectation` for the - // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); - - debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig); - - // FIXME(#45727): As discussed in [this comment][c1], naively - // forcing equality here actually results in suboptimal error - // messages in some cases. For now, if there would have been - // an obvious error, we fallback to declaring the type of the - // closure to be the one the user gave, which allows other - // error message code to trigger. - // - // However, I think [there is potential to do even better - // here][c2], since in *this* code we have the precise span of - // the type parameter in question in hand when we report the - // error. - // - // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 - // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 - self.commit_if_ok(|_| { - let mut all_obligations = vec![]; - - // The liberated version of this signature should be a subtype - // of the liberated form of the expectation. - for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( - iter::zip( - decl.inputs, - supplied_sig.inputs().skip_binder(), // binder moved to (*) below - ), - expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. - ) { - // Instantiate (this part of..) S to S', i.e., with fresh variables. - let supplied_ty = self.replace_bound_vars_with_fresh_vars( - hir_ty.span, - LateBoundRegionConversionTime::FnCall, - supplied_sig.inputs().rebind(supplied_ty), - ); // recreated from (*) above - - // Check that E' = S'. - let cause = self.misc(hir_ty.span); - let InferOk { value: (), obligations } = - self.at(&cause, self.param_env).eq(*expected_ty, supplied_ty)?; - all_obligations.extend(obligations); - } - - let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( - decl.output.span(), - LateBoundRegionConversionTime::FnCall, - supplied_sig.output(), - ); - let cause = &self.misc(decl.output.span()); - let InferOk { value: (), obligations } = self - .at(cause, self.param_env) - .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; - all_obligations.extend(obligations); - - Ok(InferOk { value: (), obligations: all_obligations }) - }) - } - - /// If there is no expected signature, then we will convert the - /// types that the user gave into a signature. - /// - /// Also, record this closure signature for later. - #[instrument(skip(self, decl, body), level = "debug")] - fn supplied_sig_of_closure( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - ) -> ty::PolyFnSig<'tcx> { - let astconv: &dyn AstConv<'_> = self; - - trace!("decl = {:#?}", decl); - debug!(?body.generator_kind); - - let bound_vars = self.tcx.late_bound_vars(hir_id); - - // First, convert the types that the user supplied (if any). - let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); - let supplied_return = match decl.output { - hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(&output), - hir::FnRetTy::DefaultReturn(_) => match body.generator_kind { - // In the case of the async block that we create for a function body, - // we expect the return type of the block to match that of the enclosing - // function. - Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => { - debug!("closure is async fn body"); - self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id) - .unwrap_or_else(|| { - // AFAIK, deducing the future output - // always succeeds *except* in error cases - // like #65159. I'd like to return Error - // here, but I can't because I can't - // easily (and locally) prove that we - // *have* reported an - // error. --nikomatsakis - astconv.ty_infer(None, decl.output.span()) - }) - } - - _ => astconv.ty_infer(None, decl.output.span()), - }, - }; - - let result = ty::Binder::bind_with_vars( - self.tcx.mk_fn_sig( - supplied_arguments, - supplied_return, - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - ), - bound_vars, - ); - - debug!(?result); - - let c_result = self.inh.infcx.canonicalize_response(result); - self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); - - result - } - - /// Invoked when we are translating the generator that results - /// from desugaring an `async fn`. Returns the "sugared" return - /// type of the `async fn` -- that is, the return type that the - /// user specified. The "desugared" return type is an `impl - /// Future<Output = T>`, so we do this by searching through the - /// obligations to extract the `T`. - #[instrument(skip(self), level = "debug")] - fn deduce_future_output_from_obligations( - &self, - expr_def_id: DefId, - body_id: hir::HirId, - ) -> Option<Ty<'tcx>> { - let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { - span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn") - }); - - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - - let get_future_output = |predicate: ty::Predicate<'tcx>, span| { - // Search for a pending obligation like - // - // `<R as Future>::Output = T` - // - // where R is the return type we are expecting. This type `T` - // will be our output. - let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { - self.deduce_future_output_from_projection( - span, - bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }; - - let output_ty = match *ret_ty.kind() { - ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - get_future_output(obligation.predicate, obligation.cause.span) - })? - } - ty::Opaque(def_id, substs) => self - .tcx - .bound_explicit_item_bounds(def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(p, s)| get_future_output(p.subst(self.tcx, substs), s.0))?, - ty::Error(_) => return None, - _ => span_bug!( - self.tcx.def_span(expr_def_id), - "async fn generator return type not an inference variable" - ), - }; - - // async fn that have opaque types in their return type need to redo the conversion to inference variables - // as they fetch the still opaque version from the signature. - let InferOk { value: output_ty, obligations } = self - .replace_opaque_types_with_inference_vars( - output_ty, - body_id, - self.tcx.def_span(expr_def_id), - self.param_env, - ); - self.register_predicates(obligations); - - debug!("deduce_future_output_from_obligations: output_ty={:?}", output_ty); - Some(output_ty) - } - - /// Given a projection like - /// - /// `<X as Future>::Output = T` - /// - /// where `X` is some type that has no late-bound regions, returns - /// `Some(T)`. If the projection is for some other trait, returns - /// `None`. - fn deduce_future_output_from_projection( - &self, - cause_span: Span, - predicate: ty::PolyProjectionPredicate<'tcx>, - ) -> Option<Ty<'tcx>> { - debug!("deduce_future_output_from_projection(predicate={:?})", predicate); - - // We do not expect any bound regions in our predicate, so - // skip past the bound vars. - let Some(predicate) = predicate.no_bound_vars() else { - debug!("deduce_future_output_from_projection: has late-bound regions"); - return None; - }; - - // Check that this is a projection from the `Future` trait. - let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx); - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); - if trait_def_id != future_trait { - debug!("deduce_future_output_from_projection: not a future"); - return None; - } - - // The `Future` trait has only one associated item, `Output`, - // so check that this is what we see. - let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; - if output_assoc_item != predicate.projection_ty.item_def_id { - span_bug!( - cause_span, - "projecting associated item `{:?}` from future, which is not Output `{:?}`", - predicate.projection_ty.item_def_id, - output_assoc_item, - ); - } - - // Extract the type from the projection. Note that there can - // be no bound variables in this type because the "self type" - // does not have any regions in it. - let output_ty = self.resolve_vars_if_possible(predicate.term); - debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); - // This is a projection on a Fn trait so will always be a type. - Some(output_ty.ty().unwrap()) - } - - /// Converts the types that the user supplied, in case that doing - /// so should yield an error, but returns back a signature where - /// all parameters are of type `TyErr`. - fn error_sig_of_closure(&self, decl: &hir::FnDecl<'_>) -> ty::PolyFnSig<'tcx> { - let astconv: &dyn AstConv<'_> = self; - - let supplied_arguments = decl.inputs.iter().map(|a| { - // Convert the types that the user supplied (if any), but ignore them. - astconv.ast_ty_to_ty(a); - self.tcx.ty_error() - }); - - if let hir::FnRetTy::Return(ref output) = decl.output { - astconv.ast_ty_to_ty(&output); - } - - let result = ty::Binder::dummy(self.tcx.mk_fn_sig( - supplied_arguments, - self.tcx.ty_error(), - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - )); - - debug!("supplied_sig_of_closure: result={:?}", result); - - result - } - - fn closure_sigs( - &self, - expr_def_id: DefId, - body: &hir::Body<'_>, - bound_sig: ty::PolyFnSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig); - let liberated_sig = self.inh.normalize_associated_types_in( - body.value.span, - body.value.hir_id, - self.param_env, - liberated_sig, - ); - ClosureSignatures { bound_sig, liberated_sig } - } -} diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs deleted file mode 100644 index 666498403..000000000 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ /dev/null @@ -1,1547 +0,0 @@ -use super::potentially_plural_count; -use crate::check::regionck::OutlivesEnvironmentExt; -use crate::check::wfcheck; -use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit; -use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::{self, TyCtxtInferExt}; -use rustc_infer::traits::util; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::util::ExplicitSelf; -use rustc_middle::ty::{self, DefIdTree}; -use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; -use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, -}; -use std::iter; - -/// Checks that a method from an impl conforms to the signature of -/// the same method as declared in the trait. -/// -/// # Parameters -/// -/// - `impl_m`: type of the method we are checking -/// - `impl_m_span`: span to use for reporting errors -/// - `trait_m`: the method in the trait -/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation -pub(crate) fn compare_impl_method<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, - trait_item_span: Option<Span>, -) { - debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref); - - let impl_m_span = tcx.def_span(impl_m.def_id); - - if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) { - return; - } - - if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) { - return; - } - - if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) { - return; - } - - if let Err(_) = - compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span) - { - return; - } - - if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) { - return; - } - - if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) - { - return; - } -} - -fn compare_predicate_entailment<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - impl_m_span: Span, - trait_m: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let trait_to_impl_substs = impl_trait_ref.substs; - - // This node-id should be used for the `body_id` field on each - // `ObligationCause` (and the `FnCtxt`). - // - // FIXME(@lcnr): remove that after removing `cause.body_id` from - // obligations. - let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local()); - // We sometimes modify the span further down. - let mut cause = ObligationCause::new( - impl_m_span, - impl_m_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_m.def_id.expect_local(), - trait_item_def_id: trait_m.def_id, - kind: impl_m.kind, - }, - ); - - // This code is best explained by example. Consider a trait: - // - // trait Trait<'t, T> { - // fn method<'a, M>(t: &'t T, m: &'a M) -> Self; - // } - // - // And an impl: - // - // impl<'i, 'j, U> Trait<'j, &'i U> for Foo { - // fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo; - // } - // - // We wish to decide if those two method types are compatible. - // - // We start out with trait_to_impl_substs, that maps the trait - // type parameters to impl type parameters. This is taken from the - // impl trait reference: - // - // trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo} - // - // We create a mapping `dummy_substs` that maps from the impl type - // parameters to fresh types and regions. For type parameters, - // this is the identity transform, but we could as well use any - // placeholder types. For regions, we convert from bound to free - // regions (Note: but only early-bound regions, i.e., those - // declared on the impl or used in type parameter bounds). - // - // impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 } - // - // Now we can apply placeholder_substs to the type of the impl method - // to yield a new function type in terms of our fresh, placeholder - // types: - // - // <'b> fn(t: &'i0 U0, m: &'b) -> Foo - // - // We now want to extract and substitute the type of the *trait* - // method and compare it. To do so, we must create a compound - // substitution by combining trait_to_impl_substs and - // impl_to_placeholder_substs, and also adding a mapping for the method - // type parameters. We extend the mapping to also include - // the method parameters. - // - // trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 } - // - // Applying this to the trait method type yields: - // - // <'a> fn(t: &'i0 U0, m: &'a) -> Foo - // - // This type is also the same but the name of the bound region ('a - // vs 'b). However, the normal subtyping rules on fn types handle - // this kind of equivalency just fine. - // - // We now use these substitutions to ensure that all declared bounds are - // satisfied by the implementation's method. - // - // We do this by creating a parameter environment which contains a - // substitution corresponding to impl_to_placeholder_substs. We then build - // trait_to_placeholder_substs and use it to convert the predicates contained - // in the trait_m.generics to the placeholder form. - // - // Finally we register each of these predicates as an obligation in - // a fresh FulfillmentCtxt, and invoke select_all_or_error. - - // Create mapping from impl to placeholder. - let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id); - - // Create mapping from trait to placeholder. - let trait_to_placeholder_substs = - impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs); - debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs); - - let impl_m_generics = tcx.generics_of(impl_m.def_id); - let trait_m_generics = tcx.generics_of(trait_m.def_id); - let impl_m_predicates = tcx.predicates_of(impl_m.def_id); - let trait_m_predicates = tcx.predicates_of(trait_m.def_id); - - // Check region bounds. - check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?; - - // Create obligations for each predicate declared by the impl - // definition in the context of the trait's parameter - // environment. We can't just use `impl_env.caller_bounds`, - // however, because we want to replace all late-bound regions with - // region variables. - let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); - - debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds); - - // This is the only tricky bit of the new way we check implementation methods - // We need to build a set of predicates where only the method-level bounds - // are from the trait and we assume all other bounds from the implementation - // to be previously satisfied. - // - // We then register the obligations from the impl_m and check to see - // if all constraints hold. - hybrid_preds - .predicates - .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates); - - // Construct trait parameter environment and then shift it into the placeholder viewpoint. - // The key step here is to update the caller_bounds's predicates to be - // the new hybrid bounds we computed. - let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id); - let param_env = ty::ParamEnv::new( - tcx.intern_predicates(&hybrid_preds.predicates), - Reveal::UserFacing, - hir::Constness::NotConst, - ); - let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); - - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); - - debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); - - let mut selcx = traits::SelectionContext::new(&infcx); - let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); - for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { - let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause, predicate); - - ocx.register_obligations(obligations); - let cause = ObligationCause::new( - span, - impl_m_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_m.def_id.expect_local(), - trait_item_def_id: trait_m.def_id, - kind: impl_m.kind, - }, - ); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); - } - - // We now need to check that the signature of the impl method is - // compatible with that of the trait method. We do this by - // checking that `impl_fty <: trait_fty`. - // - // FIXME. Unfortunately, this doesn't quite work right now because - // associated type normalization is not integrated into subtype - // checks. For the comparison to be valid, we need to - // normalize the associated types in the impl/trait methods - // first. However, because function types bind regions, just - // calling `normalize_associated_types_in` would have no effect on - // any associated types appearing in the fn arguments or return - // type. - - // Compute placeholder form of impl and trait method tys. - let tcx = infcx.tcx; - - let mut wf_tys = FxHashSet::default(); - - let impl_sig = infcx.replace_bound_vars_with_fresh_vars( - impl_m_span, - infer::HigherRankedType, - tcx.fn_sig(impl_m.def_id), - ); - - let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); - debug!("compare_impl_method: impl_fty={:?}", impl_fty); - - let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); - let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); - let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); - // Add the resulting inputs and output as well-formed. - wf_tys.extend(trait_sig.inputs_and_output.iter()); - let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); - - debug!("compare_impl_method: trait_fty={:?}", trait_fty); - - // FIXME: We'd want to keep more accurate spans than "the method signature" when - // processing the comparison between the trait and impl fn, but we sadly lose them - // and point at the whole signature when a trait bound or specific input or output - // type would be more appropriate. In other places we have a `Vec<Span>` - // corresponding to their `Vec<Predicate>`, but we don't have that here. - // Fixing this would improve the output of test `issue-83765.rs`. - let sub_result = infcx - .at(&cause, param_env) - .sup(trait_fty, impl_fty) - .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); - - if let Err(terr) = sub_result { - debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - - let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, &terr, &cause, impl_m, trait_m); - - cause.span = impl_err_span; - - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible type for trait", - trait_m.name - ); - match &terr { - TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => - { - let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) - { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => { - "&mut self".to_owned() - } - _ => format!("self: {ty}"), - }; - - // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the - // span points only at the type `Box<Self`>, but we want to cover the whole - // argument pattern and type. - let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, body) => tcx - .hir() - .body_param_names(body) - .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) - .next() - .unwrap_or(impl_err_span), - _ => bug!("{:?} is not a method", impl_m), - }; - - diag.span_suggestion( - span, - "change the self-receiver type to match the trait", - sugg, - Applicability::MachineApplicable, - ); - } - TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { - if trait_sig.inputs().len() == *i { - // Suggestion to change output type. We do not suggest in `async` functions - // to avoid complex logic or incorrect output. - match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) - if sig.header.asyncness == hir::IsAsync::NotAsync => - { - let msg = "change the output type to match the trait"; - let ap = Applicability::MachineApplicable; - match sig.decl.output { - hir::FnRetTy::DefaultReturn(sp) => { - let sugg = format!("-> {} ", trait_sig.output()); - diag.span_suggestion_verbose(sp, msg, sugg, ap); - } - hir::FnRetTy::Return(hir_ty) => { - let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); - } - }; - } - _ => {} - }; - } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( - impl_err_span, - "change the parameter type to match the trait", - trait_ty, - Applicability::MachineApplicable, - ); - } - } - _ => {} - } - - infcx.note_type_err( - &mut diag, - &cause, - trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), - &terr, - false, - false, - ); - - return Err(diag.emit()); - } - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } - - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(infcx, wf_tys, impl_m_hir_id); - infcx.check_region_obligations_and_report_errors( - impl_m.def_id.expect_local(), - &outlives_environment, - ); - - Ok(()) - }) -} - -fn check_region_bounds_on_impl_item<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, - trait_generics: &ty::Generics, - impl_generics: &ty::Generics, -) -> Result<(), ErrorGuaranteed> { - let trait_params = trait_generics.own_counts().lifetimes; - let impl_params = impl_generics.own_counts().lifetimes; - - debug!( - "check_region_bounds_on_impl_item: \ - trait_generics={:?} \ - impl_generics={:?}", - trait_generics, impl_generics - ); - - // Must have same number of early-bound lifetime parameters. - // Unfortunately, if the user screws up the bounds, then this - // will change classification between early and late. E.g., - // if in trait we have `<'a,'b:'a>`, and in impl we just have - // `<'a,'b>`, then we have 2 early-bound lifetime parameters - // in trait but 0 in the impl. But if we report "expected 2 - // but found 0" it's confusing, because it looks like there - // are zero. Since I don't quite know how to phrase things at - // the moment, give a kind of vague error message. - if trait_params != impl_params { - let span = tcx - .hir() - .get_generics(impl_m.def_id.expect_local()) - .expect("expected impl item to have generics or else we can't compare them") - .span; - let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() { - Some( - tcx.hir() - .get_generics(local_def_id) - .expect("expected trait item to have generics or else we can't compare them") - .span, - ) - } else { - None - }; - - let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait { - span, - item_kind: assoc_item_kind_str(impl_m), - ident: impl_m.ident(tcx), - generics_span, - }); - return Err(reported); - } - - Ok(()) -} - -#[instrument(level = "debug", skip(infcx))] -fn extract_spans_for_error_reporting<'a, 'tcx>( - infcx: &infer::InferCtxt<'a, 'tcx>, - terr: &TypeError<'_>, - cause: &ObligationCause<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, -) -> (Span, Option<Span>) { - let tcx = infcx.tcx; - let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) => { - sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) - } - _ => bug!("{:?} is not a method", impl_m), - }; - let trait_args = - trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind { - TraitItemKind::Fn(ref sig, _) => { - sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span())) - } - _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m), - }); - - match *terr { - TypeError::ArgumentMutability(i) => { - (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) - } - TypeError::ArgumentSorts(ExpectedFound { .. }, i) => { - (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i))) - } - _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)), - } -} - -fn compare_self_type<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - impl_m_span: Span, - trait_m: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - // Try to give more informative error messages about self typing - // mismatches. Note that any mismatch will also be detected - // below, where we construct a canonical function type that - // includes the self parameter as a normal parameter. It's just - // that the error messages you get out of this code are a bit more - // inscrutable, particularly for cases where one method has no - // self. - - let self_string = |method: &ty::AssocItem| { - let untransformed_self_ty = match method.container { - ty::ImplContainer => impl_trait_ref.self_ty(), - ty::TraitContainer => tcx.types.self_param, - }; - let self_arg_ty = tcx.fn_sig(method.def_id).input(0); - let param_env = ty::ParamEnv::reveal_all(); - - tcx.infer_ctxt().enter(|infcx| { - let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); - let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); - match ExplicitSelf::determine(self_arg_ty, can_eq_self) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {self_arg_ty}"), - } - }) - }; - - match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) { - (false, false) | (true, true) => {} - - (false, true) => { - let self_descr = self_string(impl_m); - let mut err = struct_span_err!( - tcx.sess, - impl_m_span, - E0185, - "method `{}` has a `{}` declaration in the impl, but not in the trait", - trait_m.name, - self_descr - ); - err.span_label(impl_m_span, format!("`{self_descr}` used in impl")); - if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { - err.span_label(span, format!("trait method declared without `{self_descr}`")); - } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); - } - let reported = err.emit(); - return Err(reported); - } - - (true, false) => { - let self_descr = self_string(trait_m); - let mut err = struct_span_err!( - tcx.sess, - impl_m_span, - E0186, - "method `{}` has a `{}` declaration in the trait, but not in the impl", - trait_m.name, - self_descr - ); - err.span_label(impl_m_span, format!("expected `{self_descr}` in impl")); - if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { - err.span_label(span, format!("`{self_descr}` used in trait")); - } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); - } - let reported = err.emit(); - return Err(reported); - } - } - - Ok(()) -} - -/// Checks that the number of generics on a given assoc item in a trait impl is the same -/// as the number of generics on the respective assoc item in the trait definition. -/// -/// For example this code emits the errors in the following code: -/// ``` -/// trait Trait { -/// fn foo(); -/// type Assoc<T>; -/// } -/// -/// impl Trait for () { -/// fn foo<T>() {} -/// //~^ error -/// type Assoc = u32; -/// //~^ error -/// } -/// ``` -/// -/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or -/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in -/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters -fn compare_number_of_generics<'tcx>( - tcx: TyCtxt<'tcx>, - impl_: &ty::AssocItem, - _impl_span: Span, - trait_: &ty::AssocItem, - trait_span: Option<Span>, -) -> Result<(), ErrorGuaranteed> { - let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts(); - let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts(); - - // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented - // in `compare_generic_param_kinds` which will give a nicer error message than something like: - // "expected 1 type parameter, found 0 type parameters" - if (trait_own_counts.types + trait_own_counts.consts) - == (impl_own_counts.types + impl_own_counts.consts) - { - return Ok(()); - } - - let matchings = [ - ("type", trait_own_counts.types, impl_own_counts.types), - ("const", trait_own_counts.consts, impl_own_counts.consts), - ]; - - let item_kind = assoc_item_kind_str(impl_); - - let mut err_occurred = None; - for (kind, trait_count, impl_count) in matchings { - if impl_count != trait_count { - let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| { - let mut spans = generics - .params - .iter() - .filter(|p| match p.kind { - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Elided, - } => { - // A fn can have an arbitrary number of extra elided lifetimes for the - // same signature. - !matches!(kind, ty::AssocKind::Fn) - } - _ => true, - }) - .map(|p| p.span) - .collect::<Vec<Span>>(); - if spans.is_empty() { - spans = vec![generics.span] - } - spans - }; - let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() { - let trait_item = tcx.hir().expect_trait_item(def_id); - let arg_spans: Vec<Span> = arg_spans(trait_.kind, trait_item.generics); - let impl_trait_spans: Vec<Span> = trait_item - .generics - .params - .iter() - .filter_map(|p| match p.kind { - GenericParamKind::Type { synthetic: true, .. } => Some(p.span), - _ => None, - }) - .collect(); - (Some(arg_spans), impl_trait_spans) - } else { - (trait_span.map(|s| vec![s]), vec![]) - }; - - let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local()); - let impl_item_impl_trait_spans: Vec<Span> = impl_item - .generics - .params - .iter() - .filter_map(|p| match p.kind { - GenericParamKind::Type { synthetic: true, .. } => Some(p.span), - _ => None, - }) - .collect(); - let spans = arg_spans(impl_.kind, impl_item.generics); - let span = spans.first().copied(); - - let mut err = tcx.sess.struct_span_err_with_code( - spans, - &format!( - "{} `{}` has {} {kind} parameter{} but its trait \ - declaration has {} {kind} parameter{}", - item_kind, - trait_.name, - impl_count, - pluralize!(impl_count), - trait_count, - pluralize!(trait_count), - kind = kind, - ), - DiagnosticId::Error("E0049".into()), - ); - - let mut suffix = None; - - if let Some(spans) = trait_spans { - let mut spans = spans.iter(); - if let Some(span) = spans.next() { - err.span_label( - *span, - format!( - "expected {} {} parameter{}", - trait_count, - kind, - pluralize!(trait_count), - ), - ); - } - for span in spans { - err.span_label(*span, ""); - } - } else { - suffix = Some(format!(", expected {trait_count}")); - } - - if let Some(span) = span { - err.span_label( - span, - format!( - "found {} {} parameter{}{}", - impl_count, - kind, - pluralize!(impl_count), - suffix.unwrap_or_else(String::new), - ), - ); - } - - for span in impl_trait_spans.iter().chain(impl_item_impl_trait_spans.iter()) { - err.span_label(*span, "`impl Trait` introduces an implicit type parameter"); - } - - let reported = err.emit(); - err_occurred = Some(reported); - } - } - - if let Some(reported) = err_occurred { Err(reported) } else { Ok(()) } -} - -fn compare_number_of_method_arguments<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - impl_m_span: Span, - trait_m: &ty::AssocItem, - trait_item_span: Option<Span>, -) -> Result<(), ErrorGuaranteed> { - let impl_m_fty = tcx.fn_sig(impl_m.def_id); - let trait_m_fty = tcx.fn_sig(trait_m.def_id); - let trait_number_args = trait_m_fty.inputs().skip_binder().len(); - let impl_number_args = impl_m_fty.inputs().skip_binder().len(); - if trait_number_args != impl_number_args { - let trait_span = if let Some(def_id) = trait_m.def_id.as_local() { - match tcx.hir().expect_trait_item(def_id).kind { - TraitItemKind::Fn(ref trait_m_sig, _) => { - let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 }; - if let Some(arg) = trait_m_sig.decl.inputs.get(pos) { - Some(if pos == 0 { - arg.span - } else { - arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo()) - }) - } else { - trait_item_span - } - } - _ => bug!("{:?} is not a method", impl_m), - } - } else { - trait_item_span - }; - let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref impl_m_sig, _) => { - let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 }; - if let Some(arg) = impl_m_sig.decl.inputs.get(pos) { - if pos == 0 { - arg.span - } else { - arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo()) - } - } else { - impl_m_span - } - } - _ => bug!("{:?} is not a method", impl_m), - }; - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0050, - "method `{}` has {} but the declaration in trait `{}` has {}", - trait_m.name, - potentially_plural_count(impl_number_args, "parameter"), - tcx.def_path_str(trait_m.def_id), - trait_number_args - ); - if let Some(trait_span) = trait_span { - err.span_label( - trait_span, - format!( - "trait requires {}", - potentially_plural_count(trait_number_args, "parameter") - ), - ); - } else { - err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); - } - err.span_label( - impl_span, - format!( - "expected {}, found {}", - potentially_plural_count(trait_number_args, "parameter"), - impl_number_args - ), - ); - let reported = err.emit(); - return Err(reported); - } - - Ok(()) -} - -fn compare_synthetic_generics<'tcx>( - tcx: TyCtxt<'tcx>, - impl_m: &ty::AssocItem, - trait_m: &ty::AssocItem, -) -> Result<(), ErrorGuaranteed> { - // FIXME(chrisvittal) Clean up this function, list of FIXME items: - // 1. Better messages for the span labels - // 2. Explanation as to what is going on - // If we get here, we already have the same number of generics, so the zip will - // be okay. - let mut error_found = None; - let impl_m_generics = tcx.generics_of(impl_m.def_id); - let trait_m_generics = tcx.generics_of(trait_m.def_id); - let impl_m_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind { - GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), - GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, - }); - let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind { - GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)), - GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None, - }); - for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in - iter::zip(impl_m_type_params, trait_m_type_params) - { - if impl_synthetic != trait_synthetic { - let impl_def_id = impl_def_id.expect_local(); - let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id); - let impl_span = tcx.hir().span(impl_hir_id); - let trait_span = tcx.def_span(trait_def_id); - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0643, - "method `{}` has incompatible signature for trait", - trait_m.name - ); - err.span_label(trait_span, "declaration in trait here"); - match (impl_synthetic, trait_synthetic) { - // The case where the impl method uses `impl Trait` but the trait method uses - // explicit generics - (true, false) => { - err.span_label(impl_span, "expected generic parameter, found `impl Trait`"); - (|| { - // try taking the name from the trait impl - // FIXME: this is obviously suboptimal since the name can already be used - // as another generic argument - let new_name = tcx.sess.source_map().span_to_snippet(trait_span).ok()?; - let trait_m = trait_m.def_id.as_local()?; - let trait_m = tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }); - - let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m }); - - // in case there are no generics, take the spot between the function name - // and the opening paren of the argument list - let new_generics_span = - tcx.sess.source_map().generate_fn_name_span(impl_span)?.shrink_to_hi(); - // in case there are generics, just replace them - let generics_span = - impl_m.generics.span.substitute_dummy(new_generics_span); - // replace with the generics from the trait - let new_generics = - tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?; - - err.multipart_suggestion( - "try changing the `impl Trait` argument to a generic parameter", - vec![ - // replace `impl Trait` with `T` - (impl_span, new_name), - // replace impl method generics with trait method generics - // This isn't quite right, as users might have changed the names - // of the generics, but it works for the common case - (generics_span, new_generics), - ], - Applicability::MaybeIncorrect, - ); - Some(()) - })(); - } - // The case where the trait method uses `impl Trait`, but the impl method uses - // explicit generics. - (false, true) => { - err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); - (|| { - let impl_m = impl_m.def_id.as_local()?; - let impl_m = tcx.hir().impl_item(hir::ImplItemId { def_id: impl_m }); - let input_tys = match impl_m.kind { - hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, - _ => unreachable!(), - }; - struct Visitor(Option<Span>, hir::def_id::LocalDefId); - impl<'v> intravisit::Visitor<'v> for Visitor { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - intravisit::walk_ty(self, ty); - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = - ty.kind - && let Res::Def(DefKind::TyParam, def_id) = path.res - && def_id == self.1.to_def_id() - { - self.0 = Some(ty.span); - } - } - } - let mut visitor = Visitor(None, impl_def_id); - for ty in input_tys { - intravisit::Visitor::visit_ty(&mut visitor, ty); - } - let span = visitor.0?; - - let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; - let bounds = bounds.first()?.span().to(bounds.last()?.span()); - let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?; - - err.multipart_suggestion( - "try removing the generic parameter and using `impl Trait` instead", - vec![ - // delete generic parameters - (impl_m.generics.span, String::new()), - // replace param usage with `impl Trait` - (span, format!("impl {bounds}")), - ], - Applicability::MaybeIncorrect, - ); - Some(()) - })(); - } - _ => unreachable!(), - } - let reported = err.emit(); - error_found = Some(reported); - } - } - if let Some(reported) = error_found { Err(reported) } else { Ok(()) } -} - -/// Checks that all parameters in the generics of a given assoc item in a trait impl have -/// the same kind as the respective generic parameter in the trait def. -/// -/// For example all 4 errors in the following code are emitted here: -/// ``` -/// trait Foo { -/// fn foo<const N: u8>(); -/// type bar<const N: u8>; -/// fn baz<const N: u32>(); -/// type blah<T>; -/// } -/// -/// impl Foo for () { -/// fn foo<const N: u64>() {} -/// //~^ error -/// type bar<const N: u64> {} -/// //~^ error -/// fn baz<T>() {} -/// //~^ error -/// type blah<const N: i64> = u32; -/// //~^ error -/// } -/// ``` -/// -/// This function does not handle lifetime parameters -fn compare_generic_param_kinds<'tcx>( - tcx: TyCtxt<'tcx>, - impl_item: &ty::AssocItem, - trait_item: &ty::AssocItem, -) -> Result<(), ErrorGuaranteed> { - assert_eq!(impl_item.kind, trait_item.kind); - - let ty_const_params_of = |def_id| { - tcx.generics_of(def_id).params.iter().filter(|param| { - matches!( - param.kind, - GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. } - ) - }) - }; - - for (param_impl, param_trait) in - iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id)) - { - use GenericParamDefKind::*; - if match (¶m_impl.kind, ¶m_trait.kind) { - (Const { .. }, Const { .. }) - if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) => - { - true - } - (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true, - // this is exhaustive so that anyone adding new generic param kinds knows - // to make sure this error is reported for them. - (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false, - (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(), - } { - let param_impl_span = tcx.def_span(param_impl.def_id); - let param_trait_span = tcx.def_span(param_trait.def_id); - - let mut err = struct_span_err!( - tcx.sess, - param_impl_span, - E0053, - "{} `{}` has an incompatible generic parameter for trait `{}`", - assoc_item_kind_str(&impl_item), - trait_item.name, - &tcx.def_path_str(tcx.parent(trait_item.def_id)) - ); - - let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind { - Const { .. } => { - format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id)) - } - Type { .. } => format!("{} type parameter", prefix), - Lifetime { .. } => unreachable!(), - }; - - let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap(); - err.span_label(trait_header_span, ""); - err.span_label(param_trait_span, make_param_message("expected", param_trait)); - - let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id)); - err.span_label(impl_header_span, ""); - err.span_label(param_impl_span, make_param_message("found", param_impl)); - - let reported = err.emit(); - return Err(reported); - } - } - - Ok(()) -} - -pub(crate) fn compare_const_impl<'tcx>( - tcx: TyCtxt<'tcx>, - impl_c: &ty::AssocItem, - impl_c_span: Span, - trait_c: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) { - debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - - tcx.infer_ctxt().enter(|infcx| { - let param_env = tcx.param_env(impl_c.def_id); - let ocx = ObligationCtxt::new(&infcx); - - // The below is for the most part highly similar to the procedure - // for methods above. It is simpler in many respects, especially - // because we shouldn't really have to deal with lifetimes or - // predicates. In fact some of this should probably be put into - // shared functions because of DRY violations... - let trait_to_impl_substs = impl_trait_ref.substs; - - // Create a parameter environment that represents the implementation's - // method. - let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_c.def_id.expect_local()); - - // Compute placeholder form of impl and trait const tys. - let impl_ty = tcx.type_of(impl_c.def_id); - let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); - let mut cause = ObligationCause::new( - impl_c_span, - impl_c_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_c.def_id.expect_local(), - trait_item_def_id: trait_c.def_id, - kind: impl_c.kind, - }, - ); - - // There is no "body" here, so just pass dummy id. - let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); - - debug!("compare_const_impl: impl_ty={:?}", impl_ty); - - let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty); - - debug!("compare_const_impl: trait_ty={:?}", trait_ty); - - let err = infcx - .at(&cause, param_env) - .sup(trait_ty, impl_ty) - .map(|ok| ocx.register_infer_ok_obligations(ok)); - - if let Err(terr) = err { - debug!( - "checking associated const for compatibility: impl ty {:?}, trait ty {:?}", - impl_ty, trait_ty - ); - - // Locate the Span containing just the type of the offending impl - match tcx.hir().expect_impl_item(impl_c.def_id.expect_local()).kind { - ImplItemKind::Const(ref ty, _) => cause.span = ty.span, - _ => bug!("{:?} is not a impl const", impl_c), - } - - let mut diag = struct_span_err!( - tcx.sess, - cause.span, - E0326, - "implemented const `{}` has an incompatible type for trait", - trait_c.name - ); - - let trait_c_span = trait_c.def_id.as_local().map(|trait_c_def_id| { - // Add a label to the Span containing just the type of the const - match tcx.hir().expect_trait_item(trait_c_def_id).kind { - TraitItemKind::Const(ref ty, _) => ty.span, - _ => bug!("{:?} is not a trait const", trait_c), - } - }); - - infcx.note_type_err( - &mut diag, - &cause, - trait_c_span.map(|span| (span, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_ty.into(), - found: impl_ty.into(), - })), - &terr, - false, - false, - ); - diag.emit(); - } - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; - } - - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - impl_c.def_id.expect_local(), - &outlives_environment, - ); - }); -} - -pub(crate) fn compare_ty_impl<'tcx>( - tcx: TyCtxt<'tcx>, - impl_ty: &ty::AssocItem, - impl_ty_span: Span, - trait_ty: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, - trait_item_span: Option<Span>, -) { - debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref); - - let _: Result<(), ErrorGuaranteed> = (|| { - compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?; - - compare_generic_param_kinds(tcx, impl_ty, trait_ty)?; - - let sp = tcx.def_span(impl_ty.def_id); - compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?; - - check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) - })(); -} - -/// The equivalent of [compare_predicate_entailment], but for associated types -/// instead of associated functions. -fn compare_type_predicate_entailment<'tcx>( - tcx: TyCtxt<'tcx>, - impl_ty: &ty::AssocItem, - impl_ty_span: Span, - trait_ty: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); - let trait_to_impl_substs = - impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs); - - let impl_ty_generics = tcx.generics_of(impl_ty.def_id); - let trait_ty_generics = tcx.generics_of(trait_ty.def_id); - let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id); - let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id); - - check_region_bounds_on_impl_item( - tcx, - impl_ty, - trait_ty, - &trait_ty_generics, - &impl_ty_generics, - )?; - - let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs); - - if impl_ty_own_bounds.is_empty() { - // Nothing to check. - return Ok(()); - } - - // This `HirId` should be used for the `body_id` field on each - // `ObligationCause` (and the `FnCtxt`). This is what - // `regionck_item` expects. - let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); - debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs); - - // The predicates declared by the impl definition, the trait and the - // associated type in the trait are assumed. - let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap()); - let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); - hybrid_preds - .predicates - .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates); - - debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); - - let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id); - let param_env = ty::ParamEnv::new( - tcx.intern_predicates(&hybrid_preds.predicates), - Reveal::UserFacing, - hir::Constness::NotConst, - ); - let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); - tcx.infer_ctxt().enter(|infcx| { - let ocx = ObligationCtxt::new(&infcx); - - debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); - - let mut selcx = traits::SelectionContext::new(&infcx); - - assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); - for (span, predicate) in - std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) - { - let cause = ObligationCause::misc(span, impl_ty_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, cause, predicate); - - let cause = ObligationCause::new( - span, - impl_ty_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - kind: impl_ty.kind, - }, - ); - ocx.register_obligations(obligations); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); - } - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } - - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - impl_ty.def_id.expect_local(), - &outlives_environment, - ); - - Ok(()) - }) -} - -/// Validate that `ProjectionCandidate`s created for this associated type will -/// be valid. -/// -/// Usually given -/// -/// trait X { type Y: Copy } impl X for T { type Y = S; } -/// -/// We are able to normalize `<T as X>::U` to `S`, and so when we check the -/// impl is well-formed we have to prove `S: Copy`. -/// -/// For default associated types the normalization is not possible (the value -/// from the impl could be overridden). We also can't normalize generic -/// associated types (yet) because they contain bound parameters. -#[tracing::instrument(level = "debug", skip(tcx))] -pub fn check_type_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ty: &ty::AssocItem, - impl_ty: &ty::AssocItem, - impl_ty_span: Span, - impl_trait_ref: ty::TraitRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - // Given - // - // impl<A, B> Foo<u32> for (A, B) { - // type Bar<C> =... - // } - // - // - `impl_trait_ref` would be `<(A, B) as Foo<u32>> - // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0) - // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from - // the *trait* with the generic associated type parameters (as bound vars). - // - // A note regarding the use of bound vars here: - // Imagine as an example - // ``` - // trait Family { - // type Member<C: Eq>; - // } - // - // impl Family for VecFamily { - // type Member<C: Eq> = i32; - // } - // ``` - // Here, we would generate - // ```notrust - // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) } - // ``` - // when we really would like to generate - // ```notrust - // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) } - // ``` - // But, this is probably fine, because although the first clause can be used with types C that - // do not implement Eq, for it to cause some kind of problem, there would have to be a - // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type - // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing - // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in - // the trait (notably, that X: Eq and T: Family). - let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id); - let mut substs = smallvec::SmallVec::with_capacity(defs.count()); - if let Some(def_id) = defs.parent { - let parent_defs = tcx.generics_of(def_id); - InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| { - tcx.mk_param_from_def(param) - }); - } - let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = - smallvec::SmallVec::with_capacity(defs.count()); - InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind { - GenericParamDefKind::Type { .. } => { - let kind = ty::BoundTyKind::Param(param.name); - let bound_var = ty::BoundVariableKind::Ty(kind); - bound_vars.push(bound_var); - tcx.mk_ty(ty::Bound( - ty::INNERMOST, - ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, - )) - .into() - } - GenericParamDefKind::Lifetime => { - let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name); - let bound_var = ty::BoundVariableKind::Region(kind); - bound_vars.push(bound_var); - tcx.mk_region(ty::ReLateBound( - ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind }, - )) - .into() - } - GenericParamDefKind::Const { .. } => { - let bound_var = ty::BoundVariableKind::Const; - bound_vars.push(bound_var); - tcx.mk_const(ty::ConstS { - ty: tcx.type_of(param.def_id), - kind: ty::ConstKind::Bound( - ty::INNERMOST, - ty::BoundVar::from_usize(bound_vars.len() - 1), - ), - }) - .into() - } - }); - let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter()); - let impl_ty_substs = tcx.intern_substs(&substs); - let container_id = impl_ty.container_id(tcx); - - let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); - let impl_ty_value = tcx.type_of(impl_ty.def_id); - - let param_env = tcx.param_env(impl_ty.def_id); - - // When checking something like - // - // trait X { type Y: PartialEq<<Self as X>::Y> } - // impl X for T { default type Y = S; } - // - // We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case - // we want <T as X>::Y to normalize to S. This is valid because we are - // checking the default value specifically here. Add this equality to the - // ParamEnv for normalization specifically. - let normalize_param_env = { - let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>(); - match impl_ty_value.kind() { - ty::Projection(proj) - if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs => - { - // Don't include this predicate if the projected type is - // exactly the same as the projection. This can occur in - // (somewhat dubious) code like this: - // - // impl<T> X for T where T: X { type Y = <T as X>::Y; } - } - _ => predicates.push( - ty::Binder::bind_with_vars( - ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - item_def_id: trait_ty.def_id, - substs: rebased_substs, - }, - term: impl_ty_value.into(), - }, - bound_vars, - ) - .to_predicate(tcx), - ), - }; - ty::ParamEnv::new( - tcx.intern_predicates(&predicates), - Reveal::UserFacing, - param_env.constness(), - ) - }; - debug!(?normalize_param_env); - - let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); - let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); - - tcx.infer_ctxt().enter(move |infcx| { - let ocx = ObligationCtxt::new(&infcx); - - let mut selcx = traits::SelectionContext::new(&infcx); - let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()); - let normalize_cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::CheckAssociatedTypeBounds { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - }, - ); - let mk_cause = |span: Span| { - let code = if span.is_dummy() { - traits::MiscObligation - } else { - traits::BindingObligation(trait_ty.def_id, span) - }; - ObligationCause::new(impl_ty_span, impl_ty_hir_id, code) - }; - - let obligations = tcx - .bound_explicit_item_bounds(trait_ty.def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .map(|(bound, span)| { - debug!(?bound); - // this is where opaque type is found - let concrete_ty_bound = bound.subst(tcx, rebased_substs); - debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - - traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound) - }) - .collect(); - debug!("check_type_bounds: item_bounds={:?}", obligations); - - for mut obligation in util::elaborate_obligations(tcx, obligations) { - let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( - &mut selcx, - normalize_param_env, - normalize_cause.clone(), - obligation.predicate, - ); - debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); - obligation.predicate = normalized_predicate; - - ocx.register_obligations(obligations); - ocx.register_obligation(obligation); - } - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } - - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let implied_bounds = match impl_ty.container { - ty::TraitContainer => FxHashSet::default(), - ty::ImplContainer => wfcheck::impl_implied_bounds( - tcx, - param_env, - container_id.expect_local(), - impl_ty_span, - ), - }; - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, implied_bounds, impl_ty_hir_id); - infcx.check_region_obligations_and_report_errors( - impl_ty.def_id.expect_local(), - &outlives_environment, - ); - - let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - for (key, value) in constraints { - infcx - .report_mismatched_types( - &ObligationCause::misc( - value.hidden_type.span, - tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), - ), - tcx.mk_opaque(key.def_id.to_def_id(), key.substs), - value.hidden_type.ty, - TypeError::Mismatch, - ) - .emit(); - } - - Ok(()) - }) -} - -fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str { - match impl_item.kind { - ty::AssocKind::Const => "const", - ty::AssocKind::Fn => "method", - ty::AssocKind::Type => "type", - } -} diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs deleted file mode 100644 index 6e97b0bf2..000000000 --- a/compiler/rustc_typeck/src/check/expr.rs +++ /dev/null @@ -1,2824 +0,0 @@ -//! Type checking expressions. -//! -//! See `mod.rs` for more context on type checking in general. - -use crate::astconv::AstConv as _; -use crate::check::cast; -use crate::check::coercion::CoerceMany; -use crate::check::fatally_break_rust; -use crate::check::method::SelfSource; -use crate::check::report_unexpected_variant_res; -use crate::check::BreakableCtxt; -use crate::check::Diverges; -use crate::check::DynamicCoerceMany; -use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::FnCtxt; -use crate::check::Needs; -use crate::check::TupleArgumentsFlag::DontTupleArguments; -use crate::errors::{ - FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, - YieldExprOutsideOfGenerator, -}; -use crate::type_error_struct; - -use super::suggest_call_constructor; -use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; -use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - EmissionGuarantee, ErrorGuaranteed, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{Closure, ExprKind, HirId, QPath}; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; -use rustc_infer::traits::ObligationCause; -use rustc_middle::middle::stability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; -use rustc_session::parse::feature_err; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Pos}; -use rustc_target::spec::abi::Abi::RustIntrinsic; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCauseCode}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_eqtype(expr.span, expected, ty); - } - - pub fn check_expr_has_type_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - extend_err: impl FnMut(&mut Diagnostic), - ) -> Ty<'tcx> { - self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) - } - - fn check_expr_meets_expectation_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - mut extend_err: impl FnMut(&mut Diagnostic), - ) -> Ty<'tcx> { - let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); - let mut ty = self.check_expr_with_expectation(expr, expected); - - // While we don't allow *arbitrary* coercions here, we *do* allow - // coercions from ! to `expected`. - if ty.is_never() { - if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - self.tcx().sess.delay_span_bug( - expr.span, - "expression with never type wound up being adjusted", - ); - return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { - target.to_owned() - } else { - self.tcx().ty_error() - }; - } - - let adj_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AdjustmentType, - span: expr.span, - }); - self.apply_adjustments( - expr, - vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], - ); - ty = adj_ty; - } - - if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); - extend_err(&mut err); - err.emit(); - } - ty - } - - pub(super) fn check_expr_coercable_to_type( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); - // checks don't need two phase - self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) - } - - pub(super) fn check_expr_with_hint( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - ) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, ExpectHasType(expected)) - } - - fn check_expr_with_expectation_and_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_expectation(expr, expected); - - // If the expression is used in a place whether mutable place is required - // e.g. LHS of assignment, perform the conversion. - if let Needs::MutPlace = needs { - self.convert_place_derefs_to_mutable(expr); - } - - ty - } - - pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, NoExpectation) - } - - pub(super) fn check_expr_with_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) - } - - /// Invariant: - /// If an expression has any sub-expressions that result in a type error, - /// inspecting that expression's type with `ty.references_error()` will return - /// true. Likewise, if an expression is known to diverge, inspecting its - /// type with `ty::type_is_bot` will return true (n.b.: since Rust is - /// strict, _|_ can appear in the type of an expression that does not, - /// itself, diverge: for example, fn() -> _|_.) - /// Note that inspecting a type's structure *directly* may expose the fact - /// that there are actually multiple representations for `Error`, so avoid - /// that when err needs to be handled differently. - #[instrument(skip(self, expr), level = "debug")] - pub(super) fn check_expr_with_expectation( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_args(expr, expected, &[]) - } - - /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a - /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`. - pub(super) fn check_expr_with_expectation_and_args( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) -> Ty<'tcx> { - if self.tcx().sess.verbose() { - // make this code only run with -Zverbose because it is probably slow - if let Ok(lint_str) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - if !lint_str.contains('\n') { - debug!("expr text: {lint_str}"); - } else { - let mut lines = lint_str.lines(); - if let Some(line0) = lines.next() { - let remaining_lines = lines.count(); - debug!("expr text: {line0}"); - debug!("expr text: ...(and {remaining_lines} more lines)"); - } - } - } - } - - // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block - // without the final expr (e.g. `try { return; }`). We don't want to generate an - // unreachable_code lint for it since warnings for autogenerated code are confusing. - let is_try_block_generated_unit_expr = match expr.kind { - ExprKind::Call(_, args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => { - args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock) - } - - _ => false, - }; - - // Warn for expressions after diverging siblings. - if !is_try_block_generated_unit_expr { - self.warn_if_unreachable(expr.hir_id, expr.span, "expression"); - } - - // Hide the outer diverging and has_errors flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - let ty = ensure_sufficient_stack(|| match &expr.kind { - hir::ExprKind::Path( - qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..), - ) => self.check_expr_path(qpath, expr, args), - _ => self.check_expr_kind(expr, expected), - }); - - // Warn for non-block expressions with diverging children. - match expr.kind { - ExprKind::Block(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) => {} - // If `expr` is a result of desugaring the try block and is an ok-wrapped - // diverging expression (e.g. it arose from desugaring of `try { return }`), - // we skip issuing a warning because it is autogenerated code. - ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} - ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), - ExprKind::MethodCall(segment, ..) => { - self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") - } - _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), - } - - // Any expression that produces a value of type `!` must have diverged - if ty.is_never() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); - } - - // Record the type, which applies it effects. - // We need to do this after the warning above, so that - // we don't warn for the diverging expression itself. - self.write_ty(expr.hir_id, ty); - - // Combine the diverging and has_error flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - - debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); - debug!("... {:?}, expected is {:?}", ty, expected); - - ty - } - - #[instrument(skip(self, expr), level = "debug")] - fn check_expr_kind( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - trace!("expr={:#?}", expr); - - let tcx = self.tcx; - match expr.kind { - ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected), - ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), - ExprKind::Assign(lhs, rhs, span) => { - self.check_expr_assign(expr, expected, lhs, rhs, span) - } - ExprKind::AssignOp(op, lhs, rhs) => { - self.check_binop_assign(expr, op, lhs, rhs, expected) - } - ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), - ExprKind::AddrOf(kind, mutbl, oprnd) => { - self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) - } - ExprKind::Path(QPath::LangItem(lang_item, _, hir_id)) => { - self.check_lang_item_path(lang_item, expr, hir_id) - } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]), - ExprKind::InlineAsm(asm) => { - // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). - self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); - self.check_expr_asm(asm) - } - ExprKind::Break(destination, ref expr_opt) => { - self.check_expr_break(destination, expr_opt.as_deref(), expr) - } - ExprKind::Continue(destination) => { - if destination.target_id.is_ok() { - tcx.types.never - } else { - // There was an error; make type-check fail. - tcx.ty_error() - } - } - ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), - ExprKind::Let(let_expr) => self.check_expr_let(let_expr), - ExprKind::Loop(body, _, source, _) => { - self.check_expr_loop(body, source, expected, expr) - } - ExprKind::Match(discrim, arms, match_src) => { - self.check_match(expr, &discrim, arms, expected, match_src) - } - ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { - self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) - } - ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), - ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(segment, args, _) => { - self.check_method_call(expr, segment, args, expected) - } - ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), - ExprKind::Type(e, t) => { - let ty = self.to_ty_saving_user_provided_ty(&t); - self.check_expr_eq_type(&e, ty); - ty - } - ExprKind::If(cond, then_expr, opt_else_expr) => { - self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) - } - ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), - ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => { - self.check_expr_const_block(anon_const, expected, expr) - } - ExprKind::Repeat(element, ref count) => { - self.check_expr_repeat(element, count, expected, expr) - } - ExprKind::Tup(elts) => self.check_expr_tuple(elts, expected, expr), - ExprKind::Struct(qpath, fields, ref base_expr) => { - self.check_expr_struct(expr, expected, qpath, fields, base_expr) - } - ExprKind::Field(base, field) => self.check_field(expr, &base, field), - ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), - ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.ty_error(), - } - } - - fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { - ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), - _ => NoExpectation, - }); - let referent_ty = self.check_expr_with_expectation(expr, expected_inner); - self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType); - self.tcx.mk_box(referent_ty) - } - - fn check_expr_unary( - &self, - unop: hir::UnOp, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected_inner = match unop { - hir::UnOp::Not | hir::UnOp::Neg => expected, - hir::UnOp::Deref => NoExpectation, - }; - let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); - - if !oprnd_t.references_error() { - oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); - match unop { - hir::UnOp::Deref => { - if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { - oprnd_t = ty; - } else { - let mut err = type_error_struct!( - tcx.sess, - expr.span, - oprnd_t, - E0614, - "type `{oprnd_t}` cannot be dereferenced", - ); - let sp = tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = - tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) - { - tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); - } - err.emit(); - oprnd_t = tcx.ty_error(); - } - } - hir::UnOp::Not => { - let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); - // If it's builtin, we can reuse the type, this helps inference. - if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { - oprnd_t = result; - } - } - hir::UnOp::Neg => { - let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); - // If it's builtin, we can reuse the type, this helps inference. - if !oprnd_t.is_numeric() { - oprnd_t = result; - } - } - } - } - oprnd_t - } - - fn check_expr_addr_of( - &self, - kind: hir::BorrowKind, - mutbl: hir::Mutability, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind() { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - if oprnd.is_syntactic_place_expr() { - // Places may legitimately have unsized types. - // For example, dereferences of a fat pointer and - // the last field of a struct can be unsized. - ExpectHasType(*ty) - } else { - Expectation::rvalue_hint(self, *ty) - } - } - _ => NoExpectation, - } - }); - let ty = - self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); - - let tm = ty::TypeAndMut { ty, mutbl }; - match kind { - _ if tm.ty.references_error() => self.tcx.ty_error(), - hir::BorrowKind::Raw => { - self.check_named_place_expr(oprnd); - self.tcx.mk_ptr(tm) - } - hir::BorrowKind::Ref => { - // Note: at this point, we cannot say what the best lifetime - // is to use for resulting pointer. We want to use the - // shortest lifetime possible so as to avoid spurious borrowck - // errors. Moreover, the longest lifetime will depend on the - // precise details of the value whose address is being taken - // (and how long it is valid), which we don't know yet until - // type inference is complete. - // - // Therefore, here we simply generate a region variable. The - // region inferencer will then select a suitable value. - // Finally, borrowck will infer the value of the region again, - // this time with enough precision to check that the value - // whose address was taken can actually be made to live as long - // as it needs to live. - let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - self.tcx.mk_ref(region, tm) - } - } - } - - /// Does this expression refer to a place that either: - /// * Is based on a local or static. - /// * Contains a dereference - /// Note that the adjustments for the children of `expr` should already - /// have been resolved. - fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) { - let is_named = oprnd.is_place_expr(|base| { - // Allow raw borrows if there are any deref adjustments. - // - // const VAL: (i32,) = (0,); - // const REF: &(i32,) = &(0,); - // - // &raw const VAL.0; // ERROR - // &raw const REF.0; // OK, same as &raw const (*REF).0; - // - // This is maybe too permissive, since it allows - // `let u = &raw const Box::new((1,)).0`, which creates an - // immediately dangling raw pointer. - self.typeck_results - .borrow() - .adjustments() - .get(base.hir_id) - .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }); - if !is_named { - self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }); - } - } - - fn check_lang_item_path( - &self, - lang_item: hir::LangItem, - expr: &'tcx hir::Expr<'tcx>, - hir_id: Option<hir::HirId>, - ) -> Ty<'tcx> { - self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1 - } - - pub(crate) fn check_expr_path( - &self, - qpath: &'tcx hir::QPath<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) -> Ty<'tcx> { - let tcx = self.tcx; - let (res, opt_ty, segs) = - self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); - let ty = match res { - Res::Err => { - self.set_tainted_by_errors(); - tcx.ty_error() - } - Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, qpath, expr.span); - tcx.ty_error() - } - _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, - }; - - if let ty::FnDef(did, ..) = *ty.kind() { - let fn_sig = ty.fn_sig(tcx); - if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute { - let from = fn_sig.inputs().skip_binder()[0]; - let to = fn_sig.output().skip_binder(); - // We defer the transmute to the end of typeck, once all inference vars have - // been resolved or we errored. This is important as we can only check transmute - // on concrete types, but the output type may not be known yet (it would only - // be known if explicitly specified via turbofish). - self.deferred_transmute_checks.borrow_mut().push((from, to, expr.span)); - } - if !tcx.features().unsized_fn_params { - // We want to remove some Sized bounds from std functions, - // but don't want to expose the removal to stable Rust. - // i.e., we don't want to allow - // - // ```rust - // drop as fn(str); - // ``` - // - // to work in stable even if the Sized bound on `drop` is relaxed. - for i in 0..fn_sig.inputs().skip_binder().len() { - // We just want to check sizedness, so instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let span = args.get(i).map(|a| a.span).unwrap_or(expr.span); - let input = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::FnCall, - fn_sig.input(i), - ); - self.require_type_is_sized_deferred( - input, - span, - traits::SizedArgumentType(None), - ); - } - } - // Here we want to prevent struct constructors from returning unsized types. - // There were two cases this happened: fn pointer coercion in stable - // and usual function call in presence of unsized_locals. - // Also, as we just want to check sizedness, instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let output = self.replace_bound_vars_with_fresh_vars( - expr.span, - infer::LateBoundRegionConversionTime::FnCall, - fn_sig.output(), - ); - self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); - } - - // We always require that the type provided as the value for - // a type parameter outlives the moment of instantiation. - let substs = self.typeck_results.borrow().node_substs(expr.hir_id); - self.add_wf_bounds(substs, expr); - - ty - } - - fn check_expr_break( - &self, - destination: hir::Destination, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - if let Ok(target_id) = destination.target_id { - let (e_ty, cause); - if let Some(e) = expr_opt { - // If this is a break with a value, we need to type-check - // the expression. Get an expected type from the loop context. - let opt_coerce_to = { - // We should release `enclosing_breakables` before the `check_expr_with_hint` - // below, so can't move this block of code to the enclosing scope and share - // `ctxt` with the second `enclosing_breakables` borrow below. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - match enclosing_breakables.opt_find_breakable(target_id) { - Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), - None => { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - } - } - }; - - // If the loop context is not a `loop { }`, then break with - // a value is illegal, and `opt_coerce_to` will be `None`. - // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); - - // Recurse without `enclosing_breakables` borrowed. - e_ty = self.check_expr_with_hint(e, coerce_to); - cause = self.misc(e.span); - } else { - // Otherwise, this is a break *without* a value. That's - // always legal, and is equivalent to `break ()`. - e_ty = tcx.mk_unit(); - cause = self.misc(expr.span); - } - - // Now that we have type-checked `expr_opt`, borrow - // the `enclosing_loops` field and let's coerce the - // type of `expr_opt` into what is expected. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - }; - - if let Some(ref mut coerce) = ctxt.coerce { - if let Some(ref e) = expr_opt { - coerce.coerce(self, &cause, e, e_ty); - } else { - assert!(e_ty.is_unit()); - let ty = coerce.expected_ty(); - coerce.coerce_forced_unit( - self, - &cause, - &mut |mut err| { - self.suggest_mismatched_types_on_tail( - &mut err, expr, ty, e_ty, target_id, - ); - if let Some(val) = ty_kind_suggestion(ty) { - let label = destination - .label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - err.span_suggestion( - expr.span, - "give it a value of the expected type", - format!("break{label} {val}"), - Applicability::HasPlaceholders, - ); - } - }, - false, - ); - } - } else { - // If `ctxt.coerce` is `None`, we can just ignore - // the type of the expression. This is because - // either this was a break *without* a value, in - // which case it is always a legal type (`()`), or - // else an error would have been flagged by the - // `loops` pass for using break with an expression - // where you are not supposed to. - assert!(expr_opt.is_none() || self.tcx.sess.has_errors().is_some()); - } - - // If we encountered a `break`, then (no surprise) it may be possible to break from the - // loop... unless the value being returned from the loop diverges itself, e.g. - // `break return 5` or `break loop {}`. - ctxt.may_break |= !self.diverges.get().is_always(); - - // the type of a `break` is always `!`, since it diverges - tcx.types.never - } else { - // Otherwise, we failed to find the enclosing loop; - // this can only happen if the `break` was not - // inside a loop at all, which is caught by the - // loop-checking pass. - let err = self.tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - - // We still need to assign a type to the inner expression to - // prevent the ICE in #43162. - if let Some(e) = expr_opt { - self.check_expr_with_hint(e, err); - - // ... except when we try to 'break rust;'. - // ICE this expression in particular (see #43162). - if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { - fatally_break_rust(self.tcx.sess); - } - } - } - - // There was an error; make type-check fail. - err - } - } - - fn check_expr_return( - &self, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - if self.ret_coercion.is_none() { - let mut err = ReturnStmtOutsideOfFnBody { - span: expr.span, - encl_body_span: None, - encl_fn_span: None, - }; - - let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id); - - if let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(..), - span: encl_fn_span, - .. - })) - | Some(hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), - span: encl_fn_span, - .. - })) - | Some(hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - span: encl_fn_span, - .. - })) = self.tcx.hir().find_by_def_id(encl_item_id) - { - // We are inside a function body, so reporting "return statement - // outside of function body" needs an explanation. - - let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id); - - // If this didn't hold, we would not have to report an error in - // the first place. - assert_ne!(encl_item_id, encl_body_owner_id); - - let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); - let encl_body = self.tcx.hir().body(encl_body_id); - - err.encl_body_span = Some(encl_body.value.span); - err.encl_fn_span = Some(*encl_fn_span); - } - - self.tcx.sess.emit_err(err); - - if let Some(e) = expr_opt { - // We still have to type-check `e` (issue #86188), but calling - // `check_return_expr` only works inside fn bodies. - self.check_expr(e); - } - } else if let Some(e) = expr_opt { - if self.ret_coercion_span.get().is_none() { - self.ret_coercion_span.set(Some(e.span)); - } - self.check_return_expr(e, true); - } else { - let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); - if self.ret_coercion_span.get().is_none() { - self.ret_coercion_span.set(Some(expr.span)); - } - let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { - coercion.coerce_forced_unit( - self, - &cause, - &mut |db| { - let span = fn_decl.output.span(); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - db.span_label( - span, - format!("expected `{snippet}` because of this return type"), - ); - } - }, - true, - ); - } else { - coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); - } - } - self.tcx.types.never - } - - /// `explicit_return` is `true` if we're checking an explicit `return expr`, - /// and `false` if we're checking a trailing expression. - pub(super) fn check_return_expr( - &self, - return_expr: &'tcx hir::Expr<'tcx>, - explicit_return: bool, - ) { - let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { - span_bug!(return_expr.span, "check_return_expr called outside fn body") - }); - - let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); - let mut span = return_expr.span; - // Use the span of the trailing expression for our cause, - // not the span of the entire function - if !explicit_return { - if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr { - span = last_expr.span; - } - } - ret_coercion.borrow_mut().coerce( - self, - &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), - return_expr, - return_expr_ty, - ); - - if self.return_type_has_opaque { - // Point any obligations that were registered due to opaque type - // inference at the return expression. - self.select_obligations_where_possible(false, |errors| { - self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); - }); - } - } - - fn point_at_return_for_opaque_ty_error( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - span: Span, - return_expr_ty: Ty<'tcx>, - ) { - // Don't point at the whole block if it's empty - if span == self.tcx.hir().span(self.body_id) { - return; - } - for err in errors { - let cause = &mut err.obligation.cause; - if let ObligationCauseCode::OpaqueReturnType(None) = cause.code() { - let new_cause = ObligationCause::new( - cause.span, - cause.body_id, - ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))), - ); - *cause = new_cause; - } - } - } - - pub(crate) fn check_lhs_assignable( - &self, - lhs: &'tcx hir::Expr<'tcx>, - err_code: &'static str, - op_span: Span, - adjust_err: impl FnOnce(&mut DiagnosticBuilder<'tcx, ErrorGuaranteed>), - ) { - if lhs.is_syntactic_place_expr() { - return; - } - - // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. - let mut err = self.tcx.sess.struct_span_err_with_code( - op_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); - err.span_label(lhs.span, "cannot assign to this expression"); - - self.comes_from_while_condition(lhs.hir_id, |expr| { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern destructuring", - "let ", - Applicability::MachineApplicable, - ); - }); - - adjust_err(&mut err); - - err.emit(); - } - - // Check if an expression `original_expr_id` comes from the condition of a while loop, - // as opposed from the body of a while loop, which we can naively check by iterating - // parents until we find a loop... - pub(super) fn comes_from_while_condition( - &self, - original_expr_id: HirId, - then: impl FnOnce(&hir::Expr<'_>), - ) { - let mut parent = self.tcx.hir().get_parent_node(original_expr_id); - while let Some(node) = self.tcx.hir().find(parent) { - match node { - hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::Loop( - hir::Block { - expr: - Some(hir::Expr { - kind: - hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..), - .. - }), - .. - }, - _, - hir::LoopSource::While, - _, - ), - .. - }) => { - // Check if our original expression is a child of the condition of a while loop - let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| { - self.tcx.hir().find_parent_node(*id) - }) - .take_while(|id| *id != parent) - .any(|id| id == expr.hir_id); - // if it is, then we have a situation like `while Some(0) = value.get(0) {`, - // where `while let` was more likely intended. - if expr_is_ancestor { - then(expr); - } - break; - } - hir::Node::Item(_) - | hir::Node::ImplItem(_) - | hir::Node::TraitItem(_) - | hir::Node::Crate(_) => break, - _ => { - parent = self.tcx.hir().get_parent_node(parent); - } - } - } - } - - // A generic function for checking the 'then' and 'else' clauses in an 'if' - // or 'if-else' expression. - fn check_then_else( - &self, - cond_expr: &'tcx hir::Expr<'tcx>, - then_expr: &'tcx hir::Expr<'tcx>, - opt_else_expr: Option<&'tcx hir::Expr<'tcx>>, - sp: Span, - orig_expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); - - self.warn_if_unreachable( - cond_expr.hir_id, - then_expr.span, - "block in `if` or `while` expression", - ); - - let cond_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - let expected = orig_expected.adjust_for_branches(self); - let then_ty = self.check_expr_with_expectation(then_expr, expected); - let then_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - // We've already taken the expected type's preferences - // into account when typing the `then` branch. To figure - // out the initial shot at a LUB, we thus only consider - // `expected` if it represents a *hard* constraint - // (`only_has_type`); otherwise, we just go with a - // fresh type variable. - let coerce_to_ty = expected.coercion_target_type(self, sp); - let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); - - coerce.coerce(self, &self.misc(sp), then_expr, then_ty); - - if let Some(else_expr) = opt_else_expr { - let else_ty = self.check_expr_with_expectation(else_expr, expected); - let else_diverges = self.diverges.get(); - - let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected); - let if_cause = self.if_cause( - sp, - cond_expr.span, - then_expr, - else_expr, - then_ty, - else_ty, - opt_suggest_box_span, - ); - - coerce.coerce(self, &if_cause, else_expr, else_ty); - - // We won't diverge unless both branches do (or the condition does). - self.diverges.set(cond_diverges | then_diverges & else_diverges); - } else { - self.if_fallback_coercion(sp, then_expr, &mut coerce); - - // If the condition is false we can't diverge. - self.diverges.set(cond_diverges); - } - - let result_ty = coerce.complete(self); - if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty } - } - - /// Type check assignment expression `expr` of form `lhs = rhs`. - /// The expected type is `()` and is passed to the function for the purposes of diagnostics. - fn check_expr_assign( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - lhs: &'tcx hir::Expr<'tcx>, - rhs: &'tcx hir::Expr<'tcx>, - span: Span, - ) -> Ty<'tcx> { - let expected_ty = expected.coercion_target_type(self, expr.span); - if expected_ty == self.tcx.types.bool { - // The expected type is `bool` but this will result in `()` so we can reasonably - // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. - // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = self.tcx.mk_unit(); - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); - let lhs_ty = self.check_expr(&lhs); - let rhs_ty = self.check_expr(&rhs); - let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) { - (Applicability::MachineApplicable, true) - } else { - (Applicability::MaybeIncorrect, false) - }; - if !lhs.is_syntactic_place_expr() - && lhs.is_approximately_pattern() - && !matches!(lhs.kind, hir::ExprKind::Lit(_)) - { - // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - let hir = self.tcx.hir(); - if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id))) - { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - applicability, - ); - }; - } - if eq { - err.span_suggestion_verbose( - span, - "you might have meant to compare for equality", - "==", - applicability, - ); - } - - // If the assignment expression itself is ill-formed, don't - // bother emitting another error - if lhs_ty.references_error() || rhs_ty.references_error() { - err.delay_as_bug() - } else { - err.emit(); - } - return self.tcx.ty_error(); - } - - let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - - let suggest_deref_binop = |err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, - rhs_ty: Ty<'tcx>| { - if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - // Can only assign if the type is sized, so if `DerefMut` yields a type that is - // unsized, do not suggest dereferencing it. - let lhs_deref_ty_is_sized = self - .infcx - .type_implements_trait( - self.tcx.lang_items().sized_trait().unwrap(), - lhs_deref_ty, - ty::List::empty(), - self.param_env, - ) - .may_apply(); - if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) { - err.span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "consider dereferencing here to assign to the mutably borrowed value", - "*", - Applicability::MachineApplicable, - ); - } - } - }; - - self.check_lhs_assignable(lhs, "E0070", span, |err| { - let rhs_ty = self.check_expr(&rhs); - suggest_deref_binop(err, rhs_ty); - }); - - // This is (basically) inlined `check_expr_coercable_to_type`, but we want - // to suggest an additional fixup here in `suggest_deref_binop`. - let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); - if let (_, Some(mut diag)) = - self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No) - { - suggest_deref_binop(&mut diag, rhs_ty); - diag.emit(); - } - - self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - - if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.ty_error() - } else { - self.tcx.mk_unit() - } - } - - pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> { - // for let statements, this is done in check_stmt - let init = let_expr.init; - self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); - // otherwise check exactly as a let statement - self.check_decl(let_expr.into()); - // but return a bool, for this is a boolean expression - self.tcx.types.bool - } - - fn check_expr_loop( - &self, - body: &'tcx hir::Block<'tcx>, - source: hir::LoopSource, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let coerce = match source { - // you can only use break with a value from a normal `loop { }` - hir::LoopSource::Loop => { - let coerce_to = expected.coercion_target_type(self, body.span); - Some(CoerceMany::new(coerce_to)) - } - - hir::LoopSource::While | hir::LoopSource::ForLoop => None, - }; - - let ctxt = BreakableCtxt { - coerce, - may_break: false, // Will get updated if/when we find a `break`. - }; - - let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || { - self.check_block_no_value(&body); - }); - - if ctxt.may_break { - // No way to know whether it's diverging because - // of a `break` or an outer `break` or `return`. - self.diverges.set(Diverges::Maybe); - } - - // If we permit break with a value, then result type is - // the LUB of the breaks (possibly ! if none); else, it - // is nil. This makes sense because infinite loops - // (which would have type !) are only possible iff we - // permit break with a value [1]. - if ctxt.coerce.is_none() && !ctxt.may_break { - // [1] - self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break"); - } - ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit()) - } - - /// Checks a method call. - fn check_method_call( - &self, - expr: &'tcx hir::Expr<'tcx>, - segment: &hir::PathSegment<'_>, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let rcvr = &args[0]; - let rcvr_t = self.check_expr(&rcvr); - // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); - let span = segment.ident.span; - - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { - Ok(method) => { - // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to - // trigger this codepath causing `structurally_resolved_type` to emit an error. - - self.write_method_call(expr.hir_id, method); - Ok(method) - } - Err(error) => { - if segment.ident.name != kw::Empty { - if let Some(mut err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(&args[0]), - error, - Some(args), - ) { - err.emit(); - } - } - Err(()) - } - }; - - // Call the generic checker. - self.check_method_argument_types( - span, - expr, - method, - &args[1..], - DontTupleArguments, - expected, - ) - } - - fn check_expr_cast( - &self, - e: &'tcx hir::Expr<'tcx>, - t: &'tcx hir::Ty<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // Find the type of `e`. Supply hints based on the type we are casting to, - // if appropriate. - let t_cast = self.to_ty_saving_user_provided_ty(t); - let t_cast = self.resolve_vars_if_possible(t_cast); - let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); - let t_expr = self.resolve_vars_if_possible(t_expr); - - // Eagerly check for some obvious errors. - if t_expr.references_error() || t_cast.references_error() { - self.tcx.ty_error() - } else { - // Defer other checks until we're done type checking. - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { - Ok(cast_check) => { - debug!( - "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", - t_cast, t_expr, cast_check, - ); - deferred_cast_checks.push(cast_check); - t_cast - } - Err(_) => self.tcx.ty_error(), - } - } - } - - fn check_expr_array( - &self, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let element_ty = if !args.is_empty() { - let coerce_to = expected - .to_option(self) - .and_then(|uty| match *uty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }) - .unwrap_or_else(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }); - let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); - assert_eq!(self.diverges.get(), Diverges::Maybe); - for e in args { - let e_ty = self.check_expr_with_hint(e, coerce_to); - let cause = self.misc(e.span); - coerce.coerce(self, &cause, e, e_ty); - } - coerce.complete(self) - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }; - self.tcx.mk_array(element_ty, args.len() as u64) - } - - fn check_expr_const_block( - &self, - anon_const: &'tcx hir::AnonConst, - expected: Expectation<'tcx>, - _expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let body = self.tcx.hir().body(anon_const.body); - - // Create a new function context. - let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); - crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); - - let ty = fcx.check_expr_with_expectation(&body.value, expected); - fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); - fcx.write_ty(anon_const.hir_id, ty); - ty - } - - fn check_expr_repeat( - &self, - element: &'tcx hir::Expr<'tcx>, - count: &'tcx hir::ArrayLen, - expected: Expectation<'tcx>, - _expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let count = self.array_length_to_const(count); - - let uty = match expected { - ExpectHasType(uty) => match *uty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }, - _ => None, - }; - - let (element_ty, t) = match uty { - Some(uty) => { - self.check_expr_coercable_to_type(&element, uty, None); - (uty, uty) - } - None => { - let ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: element.span, - }); - let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); - (element_ty, ty) - } - }; - - if element_ty.references_error() { - return tcx.ty_error(); - } - - self.check_repeat_element_needs_copy_bound(element, count, element_ty); - - tcx.mk_ty(ty::Array(t, count)) - } - - fn check_repeat_element_needs_copy_bound( - &self, - element: &hir::Expr<'_>, - count: ty::Const<'tcx>, - element_ty: Ty<'tcx>, - ) { - let tcx = self.tcx; - // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy. - match &element.kind { - hir::ExprKind::ConstBlock(..) => return, - hir::ExprKind::Path(qpath) => { - let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); - if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res - { - return; - } - } - _ => {} - } - // If someone calls a const fn, they can extract that call out into a separate constant (or a const - // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck. - let is_const_fn = match element.kind { - hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) => tcx.is_const_fn(def_id), - _ => false, - }, - _ => false, - }; - - // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we - // don't copy that one element, we move it. Only check for Copy if the length is larger. - if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { - let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); - let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn }; - self.require_type_meets(element_ty, element.span, code, lang_item); - } - } - - fn check_expr_tuple( - &self, - elts: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let flds = expected.only_has_type(self).and_then(|ty| { - let ty = self.resolve_vars_with_obligations(ty); - match ty.kind() { - ty::Tuple(flds) => Some(&flds[..]), - _ => None, - } - }); - - let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { - Some(fs) if i < fs.len() => { - let ety = fs[i]; - self.check_expr_coercable_to_type(&e, ety, None); - ety - } - _ => self.check_expr_with_expectation(&e, NoExpectation), - }); - let tuple = self.tcx.mk_tup(elt_ts_iter); - if tuple.references_error() { - self.tcx.ty_error() - } else { - self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); - tuple - } - } - - fn check_expr_struct( - &self, - expr: &hir::Expr<'_>, - expected: Expectation<'tcx>, - qpath: &QPath<'_>, - fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - // Find the relevant variant - let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else { - self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.ty_error(); - }; - - // Prohibit struct expressions when non-exhaustive flag is set. - let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); - if !adt.did().is_local() && variant.is_field_list_non_exhaustive() { - self.tcx - .sess - .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); - } - - self.check_expr_struct_fields( - adt_ty, - expected, - expr.hir_id, - qpath.span(), - variant, - fields, - base_expr, - expr.span, - ); - - self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); - adt_ty - } - - fn check_expr_struct_fields( - &self, - adt_ty: Ty<'tcx>, - expected: Expectation<'tcx>, - expr_id: hir::HirId, - span: Span, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - expr_span: Span, - ) { - let tcx = self.tcx; - - let expected_inputs = - self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]); - let adt_ty_hint = if let Some(expected_inputs) = expected_inputs { - expected_inputs.get(0).cloned().unwrap_or(adt_ty) - } else { - adt_ty - }; - // re-link the regions that EIfEO can erase. - self.demand_eqtype(span, adt_ty_hint, adt_ty); - - let ty::Adt(adt, substs) = adt_ty.kind() else { - span_bug!(span, "non-ADT passed to check_expr_struct_fields"); - }; - let adt_kind = adt.adt_kind(); - - let mut remaining_fields = variant - .fields - .iter() - .enumerate() - .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) - .collect::<FxHashMap<_, _>>(); - - let mut seen_fields = FxHashMap::default(); - - let mut error_happened = false; - - // Type-check each field. - for field in ast_fields { - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { - seen_fields.insert(ident, field.span); - self.write_field_index(field.hir_id, i); - - // We don't look at stability attributes on - // struct-like enums (yet...), but it's definitely not - // a bug to have constructed one. - if adt_kind != AdtKind::Enum { - tcx.check_stability(v_field.did, Some(expr_id), field.span, None); - } - - self.field_ty(field.span, v_field, substs) - } else { - error_happened = true; - if let Some(prev_span) = seen_fields.get(&ident) { - tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { - span: field.ident.span, - prev_span: *prev_span, - ident, - }); - } else { - self.report_unknown_field( - adt_ty, - variant, - field, - ast_fields, - adt.variant_descr(), - expr_span, - ); - } - - tcx.ty_error() - }; - - // Make sure to give a type to the field even if there's - // an error, so we can continue type-checking. - self.check_expr_coercable_to_type(&field.expr, field_type, None); - } - - // Make sure the programmer specified correct number of fields. - if adt_kind == AdtKind::Union { - if ast_fields.len() != 1 { - struct_span_err!( - tcx.sess, - span, - E0784, - "union expressions should have exactly one field", - ) - .emit(); - } - } - - // If check_expr_struct_fields hit an error, do not attempt to populate - // the fields with the base_expr. This could cause us to hit errors later - // when certain fields are assumed to exist that in fact do not. - if error_happened { - return; - } - - if let Some(base_expr) = base_expr { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update { - if adt.is_struct() { - // Make some fresh substitutions for our ADT type. - let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did()); - // We do subtyping on the FRU fields first, so we can - // learn exactly what types we expect the base expr - // needs constrained to be compatible with the struct - // type we expect from the expectation value. - let fru_tys = variant - .fields - .iter() - .map(|f| { - let fru_ty = self.normalize_associated_types_in( - expr_span, - self.field_ty(base_expr.span, f, fresh_substs), - ); - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, substs); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - Err(_) => { - // This should never happen, since we're just subtyping the - // remaining_fields, but it's fine to emit this, I guess. - self.report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); - } - } - } - self.resolve_vars_if_possible(fru_ty) - }) - .collect(); - // The use of fresh substs that we have subtyped against - // our base ADT type's fields allows us to guide inference - // along so that, e.g. - // ``` - // MyStruct<'a, F1, F2, const C: usize> { - // f: F1, - // // Other fields that reference `'a`, `F2`, and `C` - // } - // - // let x = MyStruct { - // f: 1usize, - // ..other_struct - // }; - // ``` - // will have the `other_struct` expression constrained to - // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... - // This is important to allow coercions to happen in - // `other_struct` itself. See `coerce-in-base-expr.rs`. - let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs); - self.check_expr_has_type_or_error( - base_expr, - self.resolve_vars_if_possible(fresh_base_ty), - |_| {}, - ); - fru_tys - } else { - // Check the base_expr, regardless of a bad expected adt_ty, so we can get - // type errors on that expression, too. - self.check_expr(base_expr); - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; - } - } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { - let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); - let same_adt = match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, - _ => false, - }; - if self.tcx.sess.is_nightly_build() && same_adt { - feature_err( - &self.tcx.sess.parse_sess, - sym::type_changing_struct_update, - base_expr.span, - "type changing struct updating is experimental", - ) - .emit(); - } - }); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => variant - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) - }) - .collect(), - _ => { - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; - } - } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); - } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { - debug!(?remaining_fields); - let private_fields: Vec<&ty::FieldDef> = variant - .fields - .iter() - .filter(|field| { - !field.vis.is_accessible_from(tcx.parent_module(expr_id).to_def_id(), tcx) - }) - .collect(); - - if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, private_fields, ast_fields); - } else { - self.report_missing_fields( - adt_ty, - span, - remaining_fields, - variant, - ast_fields, - substs, - ); - } - } - } - - fn check_struct_fields_on_error( - &self, - fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) { - for field in fields { - self.check_expr(&field.expr); - } - if let Some(base) = *base_expr { - self.check_expr(&base); - } - } - - /// Report an error for a struct field expression when there are fields which aren't provided. - /// - /// ```text - /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` - /// --> src/main.rs:8:5 - /// | - /// 8 | foo::Foo {}; - /// | ^^^^^^^^ missing `you_can_use_this_field` - /// - /// error: aborting due to previous error - /// ``` - fn report_missing_fields( - &self, - adt_ty: Ty<'tcx>, - span: Span, - remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], - substs: SubstsRef<'tcx>, - ) { - let len = remaining_fields.len(); - - let mut displayable_field_names: Vec<&str> = - remaining_fields.keys().map(|ident| ident.as_str()).collect(); - // sorting &str primitives here, sort_unstable is ok - displayable_field_names.sort_unstable(); - - let mut truncated_fields_error = String::new(); - let remaining_fields_names = match &displayable_field_names[..] { - [field1] => format!("`{}`", field1), - [field1, field2] => format!("`{field1}` and `{field2}`"), - [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"), - _ => { - truncated_fields_error = - format!(" and {} other field{}", len - 3, pluralize!(len - 3)); - displayable_field_names - .iter() - .take(3) - .map(|n| format!("`{n}`")) - .collect::<Vec<_>>() - .join(", ") - } - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0063, - "missing field{} {}{} in initializer of `{}`", - pluralize!(len), - remaining_fields_names, - truncated_fields_error, - adt_ty - ); - err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - - // If the last field is a range literal, but it isn't supposed to be, then they probably - // meant to use functional update syntax. - // - // I don't use 'is_range_literal' because only double-sided, half-open ranges count. - if let Some(( - last, - ExprKind::Struct( - QPath::LangItem(LangItem::Range, ..), - &[ref range_start, ref range_end], - _, - ), - )) = ast_fields.last().map(|last| (last, &last.expr.kind)) && - let variant_field = - variant.fields.iter().find(|field| field.ident(self.tcx) == last.ident) && - let range_def_id = self.tcx.lang_items().range_struct() && - variant_field - .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) - .map(|adt| adt.did()) - != range_def_id - { - let instead = self - .tcx - .sess - .source_map() - .span_to_snippet(range_end.expr.span) - .map(|s| format!(" from `{s}`")) - .unwrap_or_default(); - err.span_suggestion( - range_start.span.shrink_to_hi(), - &format!("to set the remaining fields{instead}, separate the last named field with a comma"), - ",", - Applicability::MaybeIncorrect, - ); - } - - err.emit(); - } - - /// Report an error for a struct field expression when there are invisible fields. - /// - /// ```text - /// error: cannot construct `Foo` with struct literal syntax due to private fields - /// --> src/main.rs:8:5 - /// | - /// 8 | foo::Foo {}; - /// | ^^^^^^^^ - /// - /// error: aborting due to previous error - /// ``` - fn report_private_fields( - &self, - adt_ty: Ty<'tcx>, - span: Span, - private_fields: Vec<&ty::FieldDef>, - used_fields: &'tcx [hir::ExprField<'tcx>], - ) { - let mut err = self.tcx.sess.struct_span_err( - span, - &format!( - "cannot construct `{adt_ty}` with struct literal syntax due to private fields", - ), - ); - let (used_private_fields, remaining_private_fields): ( - Vec<(Symbol, Span, bool)>, - Vec<(Symbol, Span, bool)>, - ) = private_fields - .iter() - .map(|field| { - match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { - Some(used_field) => (field.name, used_field.span, true), - None => (field.name, self.tcx.def_span(field.did), false), - } - }) - .partition(|field| field.2); - err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); - if !remaining_private_fields.is_empty() { - let remaining_private_fields_len = remaining_private_fields.len(); - let names = match &remaining_private_fields - .iter() - .map(|(name, _, _)| name) - .collect::<Vec<_>>()[..] - { - _ if remaining_private_fields_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); - format!("{} and `{last}` ", names.join(", ")) - } - [] => unreachable!(), - }; - err.note(format!( - "... and other private field{s} {names}that {were} not provided", - s = pluralize!(remaining_private_fields_len), - were = pluralize!("was", remaining_private_fields_len), - )); - } - err.emit(); - } - - fn report_unknown_field( - &self, - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - field: &hir::ExprField<'_>, - skip_fields: &[hir::ExprField<'_>], - kind_name: &str, - expr_span: Span, - ) { - if variant.is_recovered() { - self.set_tainted_by_errors(); - return; - } - let mut err = self.type_error_struct_with_diag( - field.ident.span, - |actual| match ty.kind() { - ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0559, - "{} `{}::{}` has no field named `{}`", - kind_name, - actual, - variant.name, - field.ident - ), - _ => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0560, - "{} `{}` has no field named `{}`", - kind_name, - actual, - field.ident - ), - }, - ty, - ); - - let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind { - CtorKind::Fn => match ty.kind() { - ty::Adt(adt, ..) if adt.is_enum() => { - err.span_label( - variant_ident_span, - format!( - "`{adt}::{variant}` defined here", - adt = ty, - variant = variant.name, - ), - ); - err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion_verbose( - expr_span, - &format!( - "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", - adt = ty, - variant = variant.name, - ), - format!( - "{adt}::{variant}(/* fields */)", - adt = ty, - variant = variant.name, - ), - Applicability::HasPlaceholders, - ); - } - _ => { - err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); - err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion_verbose( - expr_span, - &format!( - "`{adt}` is a tuple {kind_name}, use the appropriate syntax", - adt = ty, - kind_name = kind_name, - ), - format!("{adt}(/* fields */)", adt = ty), - Applicability::HasPlaceholders, - ); - } - }, - _ => { - // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = self.suggest_field_name( - variant, - field.ident.name, - skip_fields.collect(), - expr_span, - ) { - err.span_suggestion( - field.ident.span, - "a field with a similar name exists", - field_name, - Applicability::MaybeIncorrect, - ); - } else { - match ty.kind() { - ty::Adt(adt, ..) => { - if adt.is_enum() { - err.span_label( - field.ident.span, - format!("`{}::{}` does not have this field", ty, variant.name), - ); - } else { - err.span_label( - field.ident.span, - format!("`{ty}` does not have this field"), - ); - } - let available_field_names = - self.available_field_names(variant, expr_span); - if !available_field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(available_field_names) - )); - } - } - _ => bug!("non-ADT passed to report_unknown_field"), - } - }; - } - } - err.emit(); - } - - // Return a hint about the closest match in field names - fn suggest_field_name( - &self, - variant: &'tcx ty::VariantDef, - field: Symbol, - skip: Vec<Symbol>, - // The span where stability will be checked - span: Span, - ) -> Option<Symbol> { - let names = variant - .fields - .iter() - .filter_map(|field| { - // ignore already set fields and private fields from non-local crates - // and unstable fields. - if skip.iter().any(|&x| x == field.name) - || (!variant.def_id.is_local() && !field.vis.is_public()) - || matches!( - self.tcx.eval_stability(field.did, None, span, None), - stability::EvalResult::Deny { .. } - ) - { - None - } else { - Some(field.name) - } - }) - .collect::<Vec<Symbol>>(); - - find_best_match_for_name(&names, field, None) - } - - fn available_field_names( - &self, - variant: &'tcx ty::VariantDef, - access_span: Span, - ) -> Vec<Symbol> { - variant - .fields - .iter() - .filter(|field| { - let def_scope = self - .tcx - .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) - .1; - field.vis.is_accessible_from(def_scope, self.tcx) - && !matches!( - self.tcx.eval_stability(field.did, None, access_span, None), - stability::EvalResult::Deny { .. } - ) - }) - .filter(|field| !self.tcx.is_doc_hidden(field.did)) - .map(|field| field.name) - .collect() - } - - fn name_series_display(&self, names: Vec<Symbol>) -> String { - // dynamic limit, to never omit just one field - let limit = if names.len() == 6 { 6 } else { 5 }; - let mut display = - names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); - if names.len() > limit { - display = format!("{} ... and {} others", display, names.len() - limit); - } - display - } - - // Check field access expressions - fn check_field( - &self, - expr: &'tcx hir::Expr<'tcx>, - base: &'tcx hir::Expr<'tcx>, - field: Ident, - ) -> Ty<'tcx> { - debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); - let expr_t = self.check_expr(base); - let expr_t = self.structurally_resolved_type(base.span, expr_t); - let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, expr_t); - while let Some((base_t, _)) = autoderef.next() { - debug!("base_t: {:?}", base_t); - match base_t.kind() { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", base_t); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); - let fields = &base_def.non_enum_variant().fields; - if let Some(index) = fields - .iter() - .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) - { - let field = &fields[index]; - let field_ty = self.field_ty(expr.span, field, substs); - // Save the index of all fields regardless of their visibility in case - // of error recovery. - self.write_field_index(expr.hir_id, index); - let adjustments = self.adjust_steps(&autoderef); - if field.vis.is_accessible_from(def_scope, self.tcx) { - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); - return field_ty; - } - private_candidate = Some((adjustments, base_def.did(), field_ty)); - } - } - ty::Tuple(tys) => { - let fstr = field.as_str(); - if let Ok(index) = fstr.parse::<usize>() { - if fstr == index.to_string() { - if let Some(&field_ty) = tys.get(index) { - let adjustments = self.adjust_steps(&autoderef); - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.write_field_index(expr.hir_id, index); - return field_ty; - } - } - } - } - _ => {} - } - } - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - - if let Some((adjustments, did, field_ty)) = private_candidate { - // (#90483) apply adjustments to avoid ExprUseVisitor from - // creating erroneous projection. - self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, expr_t, field, did); - return field_ty; - } - - if field.name == kw::Empty { - } else if self.method_exists(field, expr_t, expr.hir_id, true) { - self.ban_take_value_of_method(expr, expr_t, field); - } else if !expr_t.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, expr_t); - } else { - let field_name = field.to_string(); - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0610, - "`{expr_t}` is a primitive type and therefore doesn't have fields", - ); - let is_valid_suffix = |field: String| { - if field == "f32" || field == "f64" { - return true; - } - let mut chars = field.chars().peekable(); - match chars.peek() { - Some('e') | Some('E') => { - chars.next(); - if let Some(c) = chars.peek() - && !c.is_numeric() && *c != '-' && *c != '+' - { - return false; - } - while let Some(c) = chars.peek() { - if !c.is_numeric() { - break; - } - chars.next(); - } - } - _ => (), - } - let suffix = chars.collect::<String>(); - suffix.is_empty() || suffix == "f32" || suffix == "f64" - }; - if let ty::Infer(ty::IntVar(_)) = expr_t.kind() - && let ExprKind::Lit(Spanned { - node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), - .. - }) = base.kind - && !base.span.from_expansion() - && is_valid_suffix(field_name) - { - err.span_suggestion_verbose( - field.span.shrink_to_lo(), - "If the number is meant to be a floating point number, consider adding a `0` after the period", - '0', - Applicability::MaybeIncorrect, - ); - } - err.emit(); - } - - self.tcx().ty_error() - } - - fn check_call_constructor<G: EmissionGuarantee>( - &self, - err: &mut DiagnosticBuilder<'_, G>, - base: &'tcx hir::Expr<'tcx>, - def_id: DefId, - ) { - if let Some(local_id) = def_id.as_local() { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); - let node = self.tcx.hir().get(hir_id); - - if let Some(fields) = node.tuple_fields() { - let kind = match self.tcx.opt_def_kind(local_id) { - Some(DefKind::Ctor(of, _)) => of, - _ => return, - }; - - suggest_call_constructor(base.span, kind, fields.len(), err); - } - } else { - // The logic here isn't smart but `associated_item_def_ids` - // doesn't work nicely on local. - if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) { - let parent_def_id = self.tcx.parent(def_id); - let fields = self.tcx.associated_item_def_ids(parent_def_id); - suggest_call_constructor(base.span, of, fields.len(), err); - } - } - } - - fn suggest_await_on_field_access( - &self, - err: &mut Diagnostic, - field_ident: Ident, - base: &'tcx hir::Expr<'tcx>, - ty: Ty<'tcx>, - ) { - let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty), - _ => return, - }; - let mut add_label = true; - if let ty::Adt(def, _) = output_ty.skip_binder().kind() { - // no field access on enum type - if !def.is_enum() { - if def - .non_enum_variant() - .fields - .iter() - .any(|field| field.ident(self.tcx) == field_ident) - { - add_label = false; - err.span_label( - field_ident.span, - "field not available in `impl Future`, but it is available in its `Output`", - ); - err.span_suggestion_verbose( - base.span.shrink_to_hi(), - "consider `await`ing on the `Future` and access the field of its `Output`", - ".await", - Applicability::MaybeIncorrect, - ); - } - } - } - if add_label { - err.span_label(field_ident.span, &format!("field not found in `{ty}`")); - } - } - - fn ban_nonexisting_field( - &self, - field: Ident, - base: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_t: Ty<'tcx>, - ) { - debug!( - "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}", - field, base, expr, expr_t - ); - let mut err = self.no_such_field_err(field, expr_t, base.hir_id); - - match *expr_t.peel_refs().kind() { - ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, field, len); - } - ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, field); - } - ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, field, expr.span); - } - ty::Param(param_ty) => { - self.point_at_param_definition(&mut err, param_ty); - } - ty::Opaque(_, _) => { - self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs()); - } - ty::FnDef(def_id, _) => { - self.check_call_constructor(&mut err, base, def_id); - } - _ => {} - } - - if field.name == kw::Await { - // We know by construction that `<expr>.await` is either on Rust 2015 - // or results in `ExprKind::Await`. Suggest switching the edition to 2018. - err.note("to `.await` a `Future`, switch to Rust 2018 or later"); - err.help_use_latest_edition(); - } - - err.emit(); - } - - fn ban_private_field_access( - &self, - expr: &hir::Expr<'_>, - expr_t: Ty<'tcx>, - field: Ident, - base_did: DefId, - ) { - let struct_path = self.tcx().def_path_str(base_did); - let kind_name = self.tcx().def_kind(base_did).descr(base_did); - let mut err = struct_span_err!( - self.tcx().sess, - field.span, - E0616, - "field `{field}` of {kind_name} `{struct_path}` is private", - ); - err.span_label(field.span, "private field"); - // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) - { - self.suggest_method_call( - &mut err, - &format!("a method `{field}` also exists, call it with parentheses"), - field, - expr_t, - expr, - None, - ); - } - err.emit(); - } - - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0615, - "attempted to take value of method `{field}` on type `{expr_t}`", - ); - err.span_label(field.span, "method, not a field"); - let expr_is_call = - if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id)) - { - expr.hir_id == callee.hir_id - } else { - false - }; - let expr_snippet = - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default(); - let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')'); - let after_open = expr.span.lo() + rustc_span::BytePos(1); - let before_close = expr.span.hi() - rustc_span::BytePos(1); - - if expr_is_call && is_wrapped { - err.multipart_suggestion( - "remove wrapping parentheses to call the method", - vec![ - (expr.span.with_hi(after_open), String::new()), - (expr.span.with_lo(before_close), String::new()), - ], - Applicability::MachineApplicable, - ); - } else if !self.expr_in_place(expr.hir_id) { - // Suggest call parentheses inside the wrapping parentheses - let span = if is_wrapped { - expr.span.with_lo(after_open).with_hi(before_close) - } else { - expr.span - }; - self.suggest_method_call( - &mut err, - "use parentheses to call the method", - field, - expr_t, - expr, - Some(span), - ); - } else { - let mut found = false; - - if let ty::RawPtr(ty_and_mut) = expr_t.kind() - && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() - { - if adt_def.variants().len() == 1 - && adt_def - .variants() - .iter() - .next() - .unwrap() - .fields - .iter() - .any(|f| f.ident(self.tcx) == field) - { - if let Some(dot_loc) = expr_snippet.rfind('.') { - found = true; - err.span_suggestion( - expr.span.with_hi(expr.span.lo() + BytePos::from_usize(dot_loc)), - "to access the field, dereference first", - format!("(*{})", &expr_snippet[0..dot_loc]), - Applicability::MaybeIncorrect, - ); - } - } - } - - if !found { - err.help("methods are immutable and cannot be assigned to"); - } - } - - err.emit(); - } - - fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let generic_param = generics.type_param(¶m, self.tcx); - if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { - return; - } - let param_def_id = generic_param.def_id; - let param_hir_id = match param_def_id.as_local() { - Some(x) => self.tcx.hir().local_def_id_to_hir_id(x), - None => return, - }; - let param_span = self.tcx.hir().span(param_hir_id); - let param_name = self.tcx.hir().ty_param_name(param_def_id.expect_local()); - - err.span_label(param_span, &format!("type parameter '{param_name}' declared here")); - } - - fn suggest_fields_on_recordish( - &self, - err: &mut Diagnostic, - def: ty::AdtDef<'tcx>, - field: Ident, - access_span: Span, - ) { - if let Some(suggested_field_name) = - self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) - { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def, access_span); - if !field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(field_names), - )); - } - } - } - - fn maybe_suggest_array_indexing( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - len: ty::Const<'tcx>, - ) { - if let (Some(len), Ok(user_index)) = - (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) - && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) - { - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{base}[{field}]"); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion(expr.span, help, suggestion, applicability); - } - } - - fn suggest_first_deref_field( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - ) { - if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { - let msg = format!("`{base}` is a raw pointer; try dereferencing it"); - let suggestion = format!("(*{base}).{field}"); - err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); - } - } - - fn no_such_field_err( - &self, - field: Ident, - expr_t: Ty<'tcx>, - id: HirId, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let span = field.span; - debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); - - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0609, - "no field `{field}` on type `{expr_t}`", - ); - - // try to add a suggestion in case the field is a nested field of a field of the Adt - if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) { - for candidate_field in fields.iter() { - if let Some(mut field_path) = self.check_for_nested_field_satisfying( - span, - &|candidate_field, _| candidate_field.ident(self.tcx()) == field, - candidate_field, - substs, - vec![], - self.tcx.parent_module(id).to_def_id(), - ) { - // field_path includes `field` that we're looking for, so pop it. - field_path.pop(); - - let field_path_str = field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join("."); - debug!("field_path_str: {:?}", field_path_str); - - err.span_suggestion_verbose( - field.span.shrink_to_lo(), - "one of the expressions' fields has a field of the same name", - format!("{field_path_str}."), - Applicability::MaybeIncorrect, - ); - } - } - } - err - } - - pub(crate) fn get_field_candidates( - &self, - span: Span, - base_t: Ty<'tcx>, - ) -> Option<(&[ty::FieldDef], SubstsRef<'tcx>)> { - debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t); - - for (base_t, _) in self.autoderef(span, base_t) { - match base_t.kind() { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - let fields = &base_def.non_enum_variant().fields; - // For compile-time reasons put a limit on number of fields we search - if fields.len() > 100 { - return None; - } - return Some((fields, substs)); - } - _ => {} - } - } - None - } - - /// This method is called after we have encountered a missing field error to recursively - /// search for the field - pub(crate) fn check_for_nested_field_satisfying( - &self, - span: Span, - matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, - candidate_field: &ty::FieldDef, - subst: SubstsRef<'tcx>, - mut field_path: Vec<Ident>, - id: DefId, - ) -> Option<Vec<Ident>> { - debug!( - "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", - span, candidate_field, field_path - ); - - if field_path.len() > 3 { - // For compile-time reasons and to avoid infinite recursion we only check for fields - // up to a depth of three - None - } else { - // recursively search fields of `candidate_field` if it's a ty::Adt - field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0()); - let field_ty = candidate_field.ty(self.tcx, subst); - if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) { - for field in nested_fields.iter() { - if field.vis.is_accessible_from(id, self.tcx) { - if matches(candidate_field, field_ty) { - return Some(field_path); - } else if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - matches, - field, - subst, - field_path.clone(), - id, - ) { - return Some(field_path); - } - } - } - } - None - } - } - - fn check_expr_index( - &self, - base: &'tcx hir::Expr<'tcx>, - idx: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let base_t = self.check_expr(&base); - let idx_t = self.check_expr(&idx); - - if base_t.references_error() { - base_t - } else if idx_t.references_error() { - idx_t - } else { - let base_t = self.structurally_resolved_type(base.span, base_t); - match self.lookup_indexing(expr, base, base_t, idx, idx_t) { - Some((index_ty, element_ty)) => { - // two-phase not needed because index_ty is never mutable - self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); - self.select_obligations_where_possible(false, |errors| { - self.point_at_index_if_possible(errors, idx.span) - }); - element_ty - } - None => { - let mut err = type_error_struct!( - self.tcx.sess, - expr.span, - base_t, - E0608, - "cannot index into a value of type `{base_t}`", - ); - // Try to give some advice about indexing tuples. - if let ty::Tuple(..) = base_t.kind() { - let mut needs_note = true; - // If the index is an integer, we can show the actual - // fixed expression: - if let ExprKind::Lit(ref lit) = idx.kind { - if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{snip}.{i}"), - Applicability::MachineApplicable, - ); - needs_note = false; - } - } - } - if needs_note { - err.help( - "to access tuple elements, use tuple indexing \ - syntax (e.g., `tuple.0`)", - ); - } - } - err.emit(); - self.tcx.ty_error() - } - } - } - } - - fn point_at_index_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - span: Span, - ) { - for error in errors { - match error.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(predicate) - if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { - } - _ => continue, - } - error.obligation.cause.span = span; - } - } - - fn check_expr_yield( - &self, - value: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - src: &'tcx hir::YieldSource, - ) -> Ty<'tcx> { - match self.resume_yield_tys { - Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty, None); - - resume_ty - } - // Given that this `yield` expression was generated as a result of lowering a `.await`, - // we know that the yield type must be `()`; however, the context won't contain this - // information. Hence, we check the source of the yield expression here and check its - // value's type against `()` (this check should always hold). - None if src.is_await() => { - self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); - self.tcx.mk_unit() - } - _ => { - self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); - // Avoid expressions without types during writeback (#78653). - self.check_expr(value); - self.tcx.mk_unit() - } - } - } - - fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { - let needs = if is_input { Needs::None } else { Needs::MutPlace }; - let ty = self.check_expr_with_needs(expr, needs); - self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); - - if !is_input && !expr.is_syntactic_place_expr() { - let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); - err.span_label(expr.span, "cannot assign to this expression"); - err.emit(); - } - - // If this is an input value, we require its type to be fully resolved - // at this point. This allows us to provide helpful coercions which help - // pass the type candidate list in a later pass. - // - // We don't require output types to be resolved at this point, which - // allows them to be inferred based on how they are used later in the - // function. - if is_input { - let ty = self.structurally_resolved_type(expr.span, ty); - match *ty.kind() { - ty::FnDef(..) => { - let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); - self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); - } - ty::Ref(_, base_ty, mutbl) => { - let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); - self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); - } - _ => {} - } - } - } - - fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } => { - self.check_expr_asm_operand(expr, true); - } - hir::InlineAsmOperand::Out { expr: Some(expr), .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - self.check_expr_asm_operand(expr, false); - } - hir::InlineAsmOperand::Out { expr: None, .. } => {} - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.check_expr_asm_operand(in_expr, true); - if let Some(out_expr) = out_expr { - self.check_expr_asm_operand(out_expr, false); - } - } - // `AnonConst`s have their own body and is type-checked separately. - // As they don't flow into the type system we don't need them to - // be well-formed. - hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} - hir::InlineAsmOperand::SymStatic { .. } => {} - } - } - if asm.options.contains(ast::InlineAsmOptions::NORETURN) { - self.tcx.types.never - } else { - self.tcx.mk_unit() - } - } -} - -pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { - Some(match ty.kind() { - ty::Bool => "true", - ty::Char => "'a'", - ty::Int(_) | ty::Uint(_) => "42", - ty::Float(_) => "3.14159", - ty::Error(_) | ty::Never => return None, - _ => "value", - }) -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs deleted file mode 100644 index 660e7e4e3..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ /dev/null @@ -1,1900 +0,0 @@ -use crate::astconv::AstConv; -use crate::check::coercion::CoerceMany; -use crate::check::fn_ctxt::arg_matrix::{ - ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx, -}; -use crate::check::gather_locals::Declaration; -use crate::check::intrinsicck::InlineAsmCtxt; -use crate::check::method::MethodCallee; -use crate::check::Expectation::*; -use crate::check::TupleArgumentsFlag::*; -use crate::check::{ - potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, - LocalTy, Needs, TupleArgumentsFlag, -}; -use crate::structured_errors::StructuredDiagnostic; - -use rustc_ast as ast; -use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{ExprKind, Node, QPath}; -use rustc_index::vec::IndexVec; -use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; -use rustc_infer::infer::TypeTrace; -use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; -use rustc_session::Session; -use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; - -use std::iter; -use std::slice; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(in super::super) fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); - for cast in deferred_cast_checks.drain(..) { - cast.check(self); - } - } - - pub(in super::super) fn check_transmutes(&self) { - let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); - debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); - for (from, to, span) in deferred_transmute_checks.drain(..) { - self.check_transmute(span, from, to); - } - } - - pub(in super::super) fn check_asms(&self) { - let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut(); - debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); - for (asm, hir_id) in deferred_asm_checks.drain(..) { - let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); - InlineAsmCtxt::new_in_fn(self) - .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); - } - } - - pub(in super::super) fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result<MethodCallee<'tcx>, ()>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.substs.references_error() || method.sig.references_error(), - Err(_) => true, - }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs, - None, - args_no_rcvr, - false, - tuple_arguments, - method.ok().map(|method| method.def_id), - ); - return self.tcx.ty_error(); - } - - let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_input_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - expected_input_tys, - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - Some(method.def_id), - ); - method.sig.output() - } - - /// Generic function that factors out common logic from function calls, - /// method calls and overloaded operators. - pub(in super::super) fn check_argument_types( - &self, - // Span enclosing the call site - call_span: Span, - // Expression of the call site - call_expr: &'tcx hir::Expr<'tcx>, - // Types (as defined in the *signature* of the target function) - formal_input_tys: &[Ty<'tcx>], - // More specific expected types, after unifying with caller output types - expected_input_tys: Option<Vec<Ty<'tcx>>>, - // The expressions for each provided argument - provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) - tuple_arguments: TupleArgumentsFlag, - // The DefId for the function being called, for better error messages - fn_def_id: Option<DefId>, - ) { - let tcx = self.tcx; - - // Conceptually, we've got some number of expected inputs, and some number of provided aguments - // and we can form a grid of whether each argument could satisfy a given input: - // in1 | in2 | in3 | ... - // arg1 ? | | | - // arg2 | ? | | - // arg3 | | ? | - // ... - // Initially, we just check the diagonal, because in the case of correct code - // these are the only checks that matter - // However, in the unhappy path, we'll fill in this whole grid to attempt to provide - // better error messages about invalid method calls. - - // All the input types from the fn signature must outlive the call - // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) { - self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); - } - - let mut err_code = "E0061"; - - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - ty::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - err_code = "E0057"; - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages - struct_span_err!( - tcx.sess, - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - (self.err_args(provided_args.len()), None) - } - } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; - - // If there are no external expectations at the call site, just use the types from the function defn - let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { - assert_eq!(expected_input_tys.len(), formal_input_tys.len()); - expected_input_tys - } else { - formal_input_tys.clone() - }; - - let minimum_input_count = expected_input_tys.len(); - let provided_arg_count = provided_args.len(); - - // We introduce a helper function to demand that a given argument satisfy a given input - // This is more complicated than just checking type equality, as arguments could be coerced - // This version writes those types back so further type checking uses the narrowed types - let demand_compatible = |idx| { - let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; - let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; - let provided_arg = &provided_args[idx]; - - debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); - - // We're on the happy path here, so we'll do a more involved check and write back types - // To check compatibility, we'll do 3 things: - // 1. Unify the provided argument with the expected type - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - - let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - - // Cause selection errors caused by resolving a single argument to point at the - // argument and not the call. This lets us customize the span pointed to in the - // fulfillment error to be more accurate. - let coerced_ty = - self.resolve_vars_with_obligations_and_mutate_fulfillment(coerced_ty, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - call_expr, - call_span, - provided_args, - &expected_input_tys, - ); - }); - - let coerce_error = self - .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None) - .err(); - - if coerce_error.is_some() { - return Compatibility::Incompatible(coerce_error); - } - - // 3. Check if the formal type is a supertype of the checked one - // and register any such obligations for future type checks - let supertype_error = self - .at(&self.misc(provided_arg.span), self.param_env) - .sup(formal_input_ty, coerced_ty); - let subtyping_error = match supertype_error { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(err) => Some(err), - }; - - // If neither check failed, the types are compatible - match subtyping_error { - None => Compatibility::Compatible, - Some(_) => Compatibility::Incompatible(subtyping_error), - } - }; - - // To start, we only care "along the diagonal", where we expect every - // provided arg to be in the right spot - let mut compatibility_diagonal = - vec![Compatibility::Incompatible(None); provided_args.len()]; - - // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path - // if the wrong number of arguments were supplied, we CAN'T be satisfied, - // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function - // otherwise, they need to be identical, because rust doesn't currently support variadic functions - let mut call_appears_satisfied = if c_variadic { - provided_arg_count >= minimum_input_count - } else { - provided_arg_count == minimum_input_count - }; - - // Check the arguments. - // We do this in a pretty awful way: first we type-check any arguments - // that are not closures, then we type-check the closures. This is so - // that we have more information about the types of arguments when we - // type-check the functions. This isn't really the right way to do this. - for check_closures in [false, true] { - // More awful hacks: before we check argument types, try to do - // an "opportunistic" trait resolution of any trait bounds on - // the call. This helps coercions. - if check_closures { - self.select_obligations_where_possible(false, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, call_expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - call_expr, - call_span, - &provided_args, - &expected_input_tys, - ); - }) - } - - // Check each argument, to satisfy the input it was provided for - // Visually, we're traveling down the diagonal of the compatibility matrix - for (idx, arg) in provided_args.iter().enumerate() { - // Warn only for the first loop (the "no closures" one). - // Closure arguments themselves can't be diverging, but - // a previous argument can, e.g., `foo(panic!(), || {})`. - if !check_closures { - self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); - } - - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. However, we *can* check - // for unreachable expressions (see above). - // FIXME: unreachable warning current isn't emitted - if idx >= minimum_input_count { - continue; - } - - let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); - if is_closure != check_closures { - continue; - } - - let compatible = demand_compatible(idx); - let is_compatible = matches!(compatible, Compatibility::Compatible); - compatibility_diagonal[idx] = compatible; - - if !is_compatible { - call_appears_satisfied = false; - } - } - } - - if c_variadic && provided_arg_count < minimum_input_count { - err_code = "E0060"; - } - - for arg in provided_args.iter().skip(minimum_input_count) { - // Make sure we've checked this expr at least once. - let arg_ty = self.check_expr(&arg); - - // If the function is c-style variadic, we skipped a bunch of arguments - // so we need to check those, and write out the types - // Ideally this would be folded into the above, for uniform style - // but c-variadic is already a corner case - if c_variadic { - fn variadic_error<'tcx>( - sess: &'tcx Session, - span: Span, - ty: Ty<'tcx>, - cast_ty: &str, - ) { - use crate::structured_errors::MissingCastForVariadicArg; - - MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); - } - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind() { - ty::Float(ty::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); - } - _ => {} - } - } - } - - if !call_appears_satisfied { - let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); - let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { - minimum_input_count - } else { - provided_arg_count - })); - debug_assert_eq!( - formal_input_tys.len(), - expected_input_tys.len(), - "expected formal_input_tys to be the same size as expected_input_tys" - ); - let formal_and_expected_inputs = IndexVec::from_iter( - formal_input_tys - .iter() - .copied() - .zip(expected_input_tys.iter().copied()) - .map(|vars| self.resolve_vars_if_possible(vars)), - ); - - self.report_arg_errors( - compatibility_diagonal, - formal_and_expected_inputs, - provided_args, - c_variadic, - err_code, - fn_def_id, - call_span, - call_expr, - ); - } - } - - fn report_arg_errors( - &self, - compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, - formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, - provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, - c_variadic: bool, - err_code: &str, - fn_def_id: Option<DefId>, - call_span: Span, - call_expr: &hir::Expr<'tcx>, - ) { - // Next, let's construct the error - let (error_span, full_call_span, ctor_of) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, - _, - ) => { - if let Res::Def(DefKind::Ctor(of, _), _) = - self.typeck_results.borrow().qpath_res(qpath, *hir_id) - { - (call_span, *span, Some(of)) - } else { - (call_span, *span, None) - } - } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None), - hir::ExprKind::MethodCall(path_segment, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - ( - *span, ident_span, None, // methods are never ctors - ) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - let call_name = match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }; - - // Don't print if it has error types or is just plain `_` - fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { - tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) - } - - self.set_tainted_by_errors(); - let tcx = self.tcx; - - // Get the argument span in the context of the call span so that - // suggestions and labels are (more) correct when an arg is a - // macro invocation. - let normalize_span = |span: Span| -> Span { - let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span); - // Sometimes macros mess up the spans, so do not normalize the - // arg span to equal the error span, because that's less useful - // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(error_span) { span } else { normalized_span } - }; - - // Precompute the provided types and spans, since that's all we typically need for below - let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args - .iter() - .map(|expr| { - let ty = self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| tcx.ty_error()); - (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) - }) - .collect(); - let callee_expr = match &call_expr.peel_blocks().kind { - hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, callee, _) => { - if let Some((DefKind::AssocFn, def_id)) = - self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) - && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.fn_has_self_parameter - { - Some(&callee[0]) - } else { - None - } - } - _ => None, - }; - let callee_ty = callee_expr - .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); - - // A "softer" version of the `demand_compatible`, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { - if provided_idx.as_usize() == expected_idx.as_usize() { - return compatibility_diagonal[provided_idx].clone(); - } - - let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if (formal_input_ty, expected_input_ty).references_error() { - return Compatibility::Incompatible(None); - } - - let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; - - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.can_coerce(arg_ty, coerced_ty); - if !can_coerce { - return Compatibility::Incompatible(None); - } - - // Using probe here, since we don't want this subtyping to affect inference. - let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err() - }); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let references_error = (coerced_ty, arg_ty).references_error(); - match (references_error, subtyping_error) { - (false, None) => Compatibility::Compatible, - (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), - } - }; - - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid - - // First, let's find the errors - let (mut errors, matched_inputs) = - ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) - .find_errors(); - - // First, check if we just need to wrap some arguments in a tuple. - if let Some((mismatch_idx, terr)) = - compatibility_diagonal.iter().enumerate().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { Some((i, terr)) } else { None } - }) - { - // Is the first bad expected argument a tuple? - // Do we have as many extra provided arguments as the tuple's length? - // If so, we might have just forgotten to wrap some args in a tuple. - if let Some(ty::Tuple(tys)) = - formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind()) - // If the tuple is unit, we're not actually wrapping any arguments. - && !tys.is_empty() - && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() - { - // Wrap up the N provided arguments starting at this position in a tuple. - let provided_as_tuple = tcx.mk_tup( - provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()), - ); - - let mut satisfied = true; - // Check if the newly wrapped tuple + rest of the arguments are compatible. - for ((_, expected_ty), provided_ty) in std::iter::zip( - formal_and_expected_inputs.iter().skip(mismatch_idx), - [provided_as_tuple].into_iter().chain( - provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()), - ), - ) { - if !self.can_coerce(provided_ty, *expected_ty) { - satisfied = false; - break; - } - } - - // If they're compatible, suggest wrapping in an arg, and we're done! - // Take some care with spans, so we don't suggest wrapping a macro's - // innards in parenthesis, for example. - if satisfied - && let Some((_, lo)) = - provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx)) - && let Some((_, hi)) = - provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1)) - { - let mut err; - if tys.len() == 1 { - // A tuple wrap suggestion actually occurs within, - // so don't do anything special here. - err = self.report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, - provided_arg_tys[mismatch_idx.into()].0, - ), - terr, - ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - } else { - err = tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count( - formal_and_expected_inputs.len(), - "argument" - ), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - DiagnosticId::Error(err_code.to_owned()), - ); - err.multipart_suggestion_verbose( - "wrap these arguments in parentheses to construct a tuple", - vec![ - (lo.shrink_to_lo(), "(".to_string()), - (hi.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - }; - self.label_fn_like(&mut err, fn_def_id, callee_ty); - err.emit(); - return; - } - } - } - - // Okay, so here's where it gets complicated in regards to what errors - // we emit and how. - // There are 3 different "types" of errors we might encounter. - // 1) Missing/extra/swapped arguments - // 2) Valid but incorrect arguments - // 3) Invalid arguments - // - Currently I think this only comes up with `CyclicTy` - // - // We first need to go through, remove those from (3) and emit those - // as their own error, particularly since they're error code and - // message is special. From what I can tell, we *must* emit these - // here (vs somewhere prior to this function) since the arguments - // become invalid *because* of how they get used in the function. - // It is what it is. - - if errors.is_empty() { - if cfg!(debug_assertions) { - span_bug!(error_span, "expected errors from argument matrix"); - } else { - tcx.sess - .struct_span_err( - error_span, - "argument type mismatch was detected, \ - but rustc had trouble determining where", - ) - .note( - "we would appreciate a bug report: \ - https://github.com/rust-lang/rust/issues/new", - ) - .emit(); - } - return; - } - - errors.drain_filter(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false }; - let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if let Some(e) = error { - if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, e).emit(); - return true; - } - } - false - }); - - // We're done if we found errors, but we already emitted them. - if errors.is_empty() { - return; - } - - // Okay, now that we've emitted the special errors separately, we - // are only left missing/extra/swapped and mismatched arguments, both - // can be collated pretty easily if needed. - - // Next special case: if there is only one "Incompatible" error, just emit that - if let [ - Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), - ] = &errors[..] - { - let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - let mut err = self.report_and_explain_type_error(trace, err); - self.emit_coerce_suggestions( - &mut err, - &provided_args[*provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); - err.emit(); - return; - } - - let mut err = if formal_and_expected_inputs.len() == provided_args.len() { - struct_span_err!( - tcx.sess, - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - DiagnosticId::Error(err_code.to_owned()), - ) - }; - - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, - } - let mut suggestion_text = SuggestionText::None; - - let mut errors = errors.into_iter().peekable(); - while let Some(error) = errors.next() { - match error { - Error::Invalid(provided_idx, expected_idx, compatibility) => { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = &compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if let Some(e) = error { - self.note_type_err( - &mut err, - &trace.cause, - None, - Some(trace.values), - e, - false, - true, - ); - } - } - - self.emit_coerce_suggestions( - &mut err, - &provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - } - Error::Extra(arg_idx) => { - let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - // FIXME: not suggestable, use something else - format!(" of type `{}`", provided_ty) - } else { - "".to_string() - }; - labels - .push((provided_span, format!("argument{} unexpected", provided_ty_name))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - } - Error::Missing(expected_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. - - let mut missing_idxs = vec![expected_idx]; - while let Some(e) = errors.next_if(|e| { - matches!(e, Error::Missing(next_expected_idx) - if *next_expected_idx == *missing_idxs.last().unwrap() + 1) - }) { - match e { - Error::Missing(expected_idx) => missing_idxs.push(expected_idx), - _ => unreachable!(), - } - } - - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[expected_idx] => { - let (_, input_ty) = formal_and_expected_inputs[expected_idx]; - let span = if let Some((_, arg_span)) = - provided_arg_tys.get(expected_idx.to_provided_idx()) - { - *arg_span - } else { - args_span - }; - let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{}`", input_ty) - } else { - "".to_string() - }; - labels.push((span, format!("an argument{} is missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let span = if let (Some((_, first_span)), Some((_, second_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(second_idx.to_provided_idx()), - ) { - first_span.to(*second_span) - } else { - args_span - }; - let rendered = - if !has_error_or_infer([first_expected_ty, second_expected_ty]) { - format!( - " of type `{}` and `{}`", - first_expected_ty, second_expected_ty - ) - } else { - "".to_string() - }; - labels.push((span, format!("two arguments{} are missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; - let span = if let (Some((_, first_span)), Some((_, third_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(third_idx.to_provided_idx()), - ) { - first_span.to(*third_span) - } else { - args_span - }; - let rendered = if !has_error_or_infer([ - first_expected_ty, - second_expected_ty, - third_expected_ty, - ]) { - format!( - " of type `{}`, `{}`, and `{}`", - first_expected_ty, second_expected_ty, third_expected_ty - ) - } else { - "".to_string() - }; - labels.push((span, format!("three arguments{} are missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if let (Some((_, first_span)), Some((_, last_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(last_idx.to_provided_idx()), - ) { - first_span.to(*last_span) - } else { - args_span - }; - labels.push((span, format!("multiple arguments are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - } - } - Error::Swap( - first_provided_idx, - second_provided_idx, - first_expected_idx, - second_expected_idx, - ) => { - let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; - let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; - let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { - format!(", found `{}`", first_provided_ty) - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{}`{}", first_expected_ty, first_provided_ty_name), - )); - - let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; - let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { - format!(", found `{}`", second_provided_ty) - } else { - String::new() - }; - labels.push(( - second_span, - format!("expected `{}`{}", second_expected_ty, second_provided_ty_name), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; - let (provided_ty, provided_span) = provided_arg_tys[dest_input]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - format!(", found `{}`", provided_ty) - } else { - String::new() - }; - labels.push(( - provided_span, - format!("expected `{}`{}", expected_ty, provided_ty_name), - )); - } - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; - } - } - } - - // If we have less than 5 things to say, it would be useful to call out exactly what's wrong - if labels.len() <= 5 { - for (span, label) in labels { - err.span_label(span, label); - } - } - - // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty); - - // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text { - let source_map = self.sess().source_map(); - let mut suggestion = format!( - "{}(", - source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| fn_def_id - .map_or("".to_string(), |fn_def_id| tcx.item_name(fn_def_id).to_string())) - ); - let mut needs_comma = false; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; - } - let suggestion_text = if let Some(provided_idx) = provided_idx - && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = - source_map.span_to_snippet(provided_span) - { - arg_text - } else { - // Propose a placeholder of the correct type - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - if expected_ty.is_unit() { - "()".to_string() - } else if expected_ty.is_suggestable(tcx, false) { - format!("/* {} */", expected_ty) - } else { - "/* value */".to_string() - } - }; - suggestion += &suggestion_text; - } - suggestion += ")"; - err.span_suggestion_verbose( - error_span, - &suggestion_text, - suggestion, - Applicability::HasPlaceholders, - ); - } - - err.emit(); - } - - // AST fragment checking - pub(in super::super) fn check_lit( - &self, - lit: &hir::Lit, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) - } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Int(_) | ty::Uint(_) => Some(ty), - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => { - tcx.mk_mach_float(ty::float_ty(t)) - } - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.ty_error(), - } - } - - pub fn check_struct_path( - &self, - qpath: &QPath<'_>, - hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = qpath.span(); - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - self.set_tainted_by_errors(); - return None; - } - Res::Def(DefKind::Variant, _) => match ty.kind() { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)), - _ => bug!("unexpected type: {:?}", ty), - }, - Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy { .. } => match ty.kind() { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did(), substs)) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, substs)) = variant { - debug!("check_struct_path: did={:?} substs={:?}", did, substs); - self.write_user_type_annotation_from_substs(hir_id, did, substs, None); - - // Check bounds on type arguments used in the path. - self.add_required_obligations(path_span, did, substs); - - Some((variant, ty)) - } else { - match ty.kind() { - ty::Error(_) => { - // E0071 might be caused by a spelling error, which will have - // already caused an error message and probably a suggestion - // elsewhere. Refrain from emitting more unhelpful errors here - // (issue #88844). - } - _ => { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - } - } - None - } - } - - pub fn check_decl_initializer( - &self, - hir_id: hir::HirId, - pat: &'tcx hir::Pat<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, hir_id).revealed_ty; - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); - init_ty - } else { - self.check_expr_coercable_to_type(init, local_ty, None) - } - } - - pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) { - // Determine and write the type which we'll check the pattern against. - let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty; - self.write_ty(decl.hir_id, decl_ty); - - // Type check the initializer. - if let Some(ref init) = decl.init { - let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) - } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); - let pat_ty = self.node_ty(decl.pat.hir_id); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); - - if let Some(blk) = decl.els { - let previous_diverges = self.diverges.get(); - let else_ty = self.check_block_with_expected(blk, NoExpectation); - let cause = self.cause(blk.span, ObligationCauseCode::LetElse); - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - } - self.diverges.set(previous_diverges); - } - } - - /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { - self.check_decl(local.into()); - } - - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging and `has_errors` flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - match stmt.kind { - hir::StmtKind::Local(l) => { - self.check_decl_local(l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - if expr.can_have_side_effects() { - self.suggest_semicolon_at_end(expr.span, err); - } - }); - } - hir::StmtKind::Semi(ref expr) => { - // All of this is equivalent to calling `check_expr`, but it is inlined out here - // in order to capture the fact that this `match` is the last statement in its - // function. This is done for better suggestions to remove the `;`. - let expectation = match expr.kind { - hir::ExprKind::Match(..) if is_last => IsLast(stmt.span), - _ => NoExpectation, - }; - self.check_expr_with_expectation(expr, expectation); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - } - - pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - pub(in super::super) fn check_block_with_expected( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let prev = self.ps.replace(self.ps.get().recurse(blk)); - - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let tail_expr = blk.expr.as_ref(); - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for (pos, s) in blk.stmts.iter().enumerate() { - self.check_stmt(s, blk.stmts.len() - 1 == pos); - } - - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - let ty_for_diagnostic = coerce.merged_ty(); - // We use coerce_inner here because we want to augment the error - // suggesting to wrap the block in square brackets if it might've - // been mistaken array syntax - coerce.coerce_inner( - self, - &cause, - Some(tail_expr), - tail_expr_ty, - Some(&mut |diag: &mut Diagnostic| { - self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic); - }), - false, - ); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } - } - } - coerce.coerce_forced_unit( - self, - &self.misc(sp), - &mut |err| { - if let Some(expected_ty) = expected.only_has_type(self) { - if !self.consider_removing_semicolon(blk, expected_ty, err) { - self.consider_returning_binding(blk, expected_ty, err); - } - if expected_ty == self.tcx.types.bool { - // If this is caused by a missing `let` in a `while let`, - // silence this redundant error, as we already emit E0070. - - // Our block must be a `assign desugar local; assignment` - if let Some(hir::Node::Block(hir::Block { - stmts: - [ - hir::Stmt { - kind: - hir::StmtKind::Local(hir::Local { - source: - hir::LocalSource::AssignDesugar(_), - .. - }), - .. - }, - hir::Stmt { - kind: - hir::StmtKind::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - }), - .. - }, - ], - .. - })) = self.tcx.hir().find(blk.hir_id) - { - self.comes_from_while_condition(blk.hir_id, |_| { - err.downgrade_to_delayed_bug(); - }) - } - } - } - if let Some(fn_span) = fn_span { - err.span_label( - fn_span, - "implicitly returns `()` as its body has no tail or `return` \ - expression", - ); - } - }, - false, - ); - } - } - }); - - if ctxt.may_break { - // If we can break from the block, then the block's exit is always reachable - // (... as long as the entry is reachable) - regardless of the tail of the block. - self.diverges.set(prev_diverges); - } - - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } - - self.write_ty(blk.hir_id, ty); - - self.ps.set(prev); - ty - } - - fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { - let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); - match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { - let body = self.tcx.hir().body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); - } - } - _ => {} - } - None - } - - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id)); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) - } - - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - let check_in_progress = |elem: &hir::Expr<'_>| { - self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( - |_| match elem.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), - _ => elem.span, - }, - ) - }; - - if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { - if let Some(rslt) = check_in_progress(el) { - return rslt; - } - } - - if let hir::ExprKind::Match(_, arms, _) = expr.kind { - let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); - if let Some(span) = iter.next() { - if iter.next().is_none() { - return span; - } - } - } - - expr.span - } - - fn overwrite_local_ty_if_err( - &self, - hir_id: hir::HirId, - pat: &'tcx hir::Pat<'tcx>, - decl_ty: Ty<'tcx>, - ty: Ty<'tcx>, - ) { - if ty.references_error() { - // Override the types everywhere with `err()` to avoid knock on errors. - self.write_ty(hir_id, ty); - self.write_ty(pat.hir_id, ty); - let local_ty = LocalTy { decl_ty, revealed_ty: ty }; - self.locals.borrow_mut().insert(hir_id, local_ty); - self.locals.borrow_mut().insert(pat.hir_id, local_ty); - } - } - - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'_>, - path_span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true); - (path.res, ty) - } - QPath::TypeRelative(ref qself, ref segment) => { - let ty = self.to_ty(qself); - - let result = <dyn AstConv<'_>>::associated_path_to_ty( - self, hir_id, path_span, ty, qself, segment, true, - ); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) - } - QPath::LangItem(lang_item, span, id) => { - self.resolve_lang_item_path(lang_item, span, hir_id, id) - } - } - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk - /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s - /// reference a type argument. The reason to walk also the checked type is that the coerced type - /// can be not easily comparable with predicate type (because of coercion). If the types match - /// for either checked or coerced type, and there's only *one* argument that does, we point at - /// the corresponding argument's expression span instead of the `fn` call path span. - fn point_at_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - call_sp: Span, - args: &'tcx [hir::Expr<'tcx>], - expected_tys: &[Ty<'tcx>], - ) { - // We *do not* do this for desugared call spans to keep good diagnostics when involving - // the `?` operator. - if call_sp.desugaring_kind().is_some() { - return; - } - - 'outer: for error in errors { - // Only if the cause is somewhere inside the expression we want try to point at arg. - // Otherwise, it means that the cause is somewhere else and we should not change - // anything because we can break the correct span. - if !call_sp.contains(error.obligation.cause.span) { - continue; - } - - // Peel derived obligation, because it's the type that originally - // started this inference chain that matters, not the one we wound - // up with at the end. - fn unpeel_to_top<'a, 'tcx>( - mut code: &'a ObligationCauseCode<'tcx>, - ) -> &'a ObligationCauseCode<'tcx> { - let mut result_code = code; - loop { - let parent = match code { - ObligationCauseCode::ImplDerivedObligation(c) => &c.derived.parent_code, - ObligationCauseCode::BuiltinDerivedObligation(c) - | ObligationCauseCode::DerivedObligation(c) => &c.parent_code, - _ => break result_code, - }; - (result_code, code) = (code, parent); - } - } - let self_: ty::subst::GenericArg<'_> = - match unpeel_to_top(error.obligation.cause.code()) { - ObligationCauseCode::BuiltinDerivedObligation(code) - | ObligationCauseCode::DerivedObligation(code) => { - code.parent_trait_pred.self_ty().skip_binder().into() - } - ObligationCauseCode::ImplDerivedObligation(code) => { - code.derived.parent_trait_pred.self_ty().skip_binder().into() - } - _ if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() => - { - predicate.self_ty().into() - } - _ => continue, - }; - let self_ = self.resolve_vars_if_possible(self_); - let ty_matches_self = |ty: Ty<'tcx>| ty.walk().any(|arg| arg == self_); - - let typeck_results = self.typeck_results.borrow(); - - for (idx, arg) in args.iter().enumerate() { - // Don't adjust the span if we already have a more precise span - // within one of the args. - if arg.span.contains(error.obligation.cause.span) { - let references_arg = - typeck_results.expr_ty_opt(arg).map_or(false, &ty_matches_self) - || expected_tys.get(idx).copied().map_or(false, &ty_matches_self); - if references_arg && !arg.span.from_expansion() { - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: args[idx].hir_id, - call_hir_id: expr.hir_id, - parent_code, - } - }) - } - continue 'outer; - } - } - - // Collect the argument position for all arguments that could have caused this - // `FulfillmentError`. - let mut referenced_in: Vec<_> = std::iter::zip(expected_tys, args) - .enumerate() - .flat_map(|(idx, (expected_ty, arg))| { - if let Some(arg_ty) = typeck_results.expr_ty_opt(arg) { - vec![(idx, arg_ty), (idx, *expected_ty)] - } else { - vec![] - } - }) - .filter_map(|(i, ty)| { - let ty = self.resolve_vars_if_possible(ty); - // We walk the argument type because the argument's type could have - // been `Option<T>`, but the `FulfillmentError` references `T`. - if ty_matches_self(ty) { Some(i) } else { None } - }) - .collect(); - - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. - - // We sort primitive type usize here and can use unstable sort - referenced_in.sort_unstable(); - referenced_in.dedup(); - - if let &[idx] = &referenced_in[..] { - // Do not point at the inside of a macro. - // That would often result in poor error messages. - if args[idx].span.from_expansion() { - continue; - } - // We make sure that only *one* argument matches the obligation failure - // and we assign the obligation's span to its expression's. - error.obligation.cause.span = args[idx].span; - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: args[idx].hir_id, - call_hir_id: expr.hir_id, - parent_code, - } - }); - } else if error.obligation.cause.span == call_sp { - // Make function calls point at the callee, not the whole thing. - if let hir::ExprKind::Call(callee, _) = expr.kind { - error.obligation.cause.span = callee.span; - } - } - } - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the - /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s - /// were caused by them. If they were, we point at the corresponding type argument's span - /// instead of the `fn` call path span. - fn point_at_type_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - call_expr: &'tcx hir::Expr<'tcx>, - ) { - if let hir::ExprKind::Call(path, _) = &call_expr.kind { - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &path.kind { - for error in errors { - if let ty::PredicateKind::Trait(predicate) = - error.obligation.predicate.kind().skip_binder() - { - // If any of the type arguments in this path segment caused the - // `FulfillmentError`, point at its span (#61860). - for arg in path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .flat_map(|a| a.args.iter()) - { - if let hir::GenericArg::Type(hir_ty) = &arg - && let Some(ty) = - self.typeck_results.borrow().node_type_opt(hir_ty.hir_id) - && self.resolve_vars_if_possible(ty) == predicate.self_ty() - { - error.obligation.cause.span = hir_ty.span; - break; - } - } - } - } - } - } - } - - fn label_fn_like( - &self, - err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>, - callable_def_id: Option<DefId>, - callee_ty: Option<Ty<'tcx>>, - ) { - let Some(mut def_id) = callable_def_id else { - return; - }; - - if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) - // Possibly points at either impl or trait item, so try to get it - // to point to trait item, then get the parent. - // This parent might be an impl in the case of an inherent function, - // but the next check will fail. - && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) - && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) - // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) - && let Some(callee_ty) = callee_ty - { - let callee_ty = callee_ty.peel_refs(); - match *callee_ty.kind() { - ty::Param(param) => { - let param = - self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); - if param.kind.is_synthetic() { - // if it's `impl Fn() -> ..` then just fall down to the def-id based logic - def_id = param.def_id; - } else { - // Otherwise, find the predicate that makes this generic callable, - // and point at that. - let instantiated = self - .tcx - .explicit_predicates_of(self.body_id.owner) - .instantiate_identity(self.tcx); - // FIXME(compiler-errors): This could be problematic if something has two - // fn-like predicates with different args, but callable types really never - // do that, so it's OK. - for (predicate, span) in - std::iter::zip(instantiated.predicates, instantiated.spans) - { - if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() - && pred.self_ty().peel_refs() == callee_ty - && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() - { - err.span_note(span, "callable defined here"); - return; - } - } - } - } - ty::Opaque(new_def_id, _) - | ty::Closure(new_def_id, _) - | ty::FnDef(new_def_id, _) => { - def_id = new_def_id; - } - _ => { - // Look for a user-provided impl of a `Fn` trait, and point to it. - let new_def_id = self.probe(|_| { - let trait_ref = ty::TraitRef::new( - call_kind.to_def_id(self.tcx), - self.tcx.mk_substs([ - ty::GenericArg::from(callee_ty), - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rustc_span::DUMMY_SP, - }) - .into(), - ].into_iter()), - ); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), - self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), - ); - match SelectionContext::new(&self).select(&obligation) { - Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { - Some(impl_source.impl_def_id) - } - _ => None - } - }); - if let Some(new_def_id) = new_def_id { - def_id = new_def_id; - } else { - return; - } - } - } - } - - if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() { - let mut spans: MultiSpan = def_span.into(); - - let params = self - .tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params); - - for param in params { - spans.push_span_label(param.span, ""); - } - - let def_kind = self.tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } else { - let def_kind = self.tcx.def_kind(def_id); - err.span_note( - self.tcx.def_span(def_id), - &format!("{} defined here", def_kind.descr(def_id)), - ); - } - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs deleted file mode 100644 index 05bcc710e..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ /dev/null @@ -1,296 +0,0 @@ -mod _impl; -mod arg_matrix; -mod checks; -mod suggestions; - -pub use _impl::*; -pub use suggestions::*; - -use crate::astconv::AstConv; -use crate::check::coercion::DynamicCoerceMany; -use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; - -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Const, Ty, TyCtxt}; -use rustc_session::Session; -use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; - -use std::cell::{Cell, RefCell}; -use std::ops::Deref; - -pub struct FnCtxt<'a, 'tcx> { - pub(super) body_id: hir::HirId, - - /// The parameter environment used for proving trait obligations - /// in this function. This can change when we descend into - /// closures (as they bring new things into scope), hence it is - /// not part of `Inherited` (as of the time of this writing, - /// closures do not yet change the environment, but they will - /// eventually). - pub(super) param_env: ty::ParamEnv<'tcx>, - - /// Number of errors that had been reported when we started - /// checking this function. On exit, if we find that *more* errors - /// have been reported, we will skip regionck and other work that - /// expects the types within the function to be consistent. - // FIXME(matthewjasper) This should not exist, and it's not correct - // if type checking is run in parallel. - err_count_on_creation: usize, - - /// If `Some`, this stores coercion information for returned - /// expressions. If `None`, this is in a context where return is - /// inappropriate, such as a const expression. - /// - /// This is a `RefCell<DynamicCoerceMany>`, which means that we - /// can track all the return expressions and then use them to - /// compute a useful coercion from the set, similar to a match - /// expression or other branching context. You can use methods - /// like `expected_ty` to access the declared return type (if - /// any). - pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, - - pub(super) ret_type_span: Option<Span>, - - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - - /// First span of a return site that we find. Used in error messages. - pub(super) ret_coercion_span: Cell<Option<Span>>, - - pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - - pub(super) ps: Cell<UnsafetyState>, - - /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to `Always`). In general, when entering - /// an expression or other node in the tree, the initial value - /// indicates whether prior parts of the containing expression may - /// have diverged. It is then typically set to `Maybe` (and the - /// old value remembered) for processing the subparts of the - /// current expression. As each subpart is processed, they may set - /// the flag to `Always`, etc. Finally, at the end, we take the - /// result and "union" it with the original value, so that when we - /// return the flag indicates if any subpart of the parent - /// expression (up to and including this part) has diverged. So, - /// if you read it after evaluating a subexpression `X`, the value - /// you get indicates whether any subexpression that was - /// evaluating up to and including `X` diverged. - /// - /// We currently use this flag only for diagnostic purposes: - /// - /// - To warn about unreachable code: if, after processing a - /// sub-expression but before we have applied the effects of the - /// current node, we see that the flag is set to `Always`, we - /// can issue a warning. This corresponds to something like - /// `foo(return)`; we warn on the `foo()` expression. (We then - /// update the flag to `WarnedAlways` to suppress duplicate - /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from an `Always` setting, we will issue a - /// warning. This corresponds to something like `{return; - /// foo();}` or `{return; 22}`, where we would warn on the - /// `foo()` or `22`. - /// - /// An expression represents dead code if, after checking it, - /// the diverges flag is set to something other than `Maybe`. - pub(super) diverges: Cell<Diverges>, - - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell<bool>, - - pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, - - pub(super) inh: &'a Inherited<'a, 'tcx>, - - /// True if the function or closure's return type is known before - /// entering the function/closure, i.e. if the return type is - /// either given explicitly or inferred from, say, an `Fn*` trait - /// bound. Used for diagnostic purposes only. - pub(super) return_type_pre_known: bool, - - /// True if the return type has an Opaque type - pub(super) return_type_has_opaque: bool, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn new( - inh: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ) -> FnCtxt<'a, 'tcx> { - FnCtxt { - body_id, - param_env, - err_count_on_creation: inh.tcx.sess.err_count(), - ret_coercion: None, - ret_type_span: None, - in_tail_expr: false, - ret_coercion_span: Cell::new(None), - resume_yield_tys: None, - ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), - diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), - enclosing_breakables: RefCell::new(EnclosingBreakables { - stack: Vec::new(), - by_id: Default::default(), - }), - inh, - return_type_pre_known: true, - return_type_has_opaque: false, - } - } - - pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { - ObligationCause::new(span, self.body_id, code) - } - - pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { - self.cause(span, ObligationCauseCode::MiscObligation) - } - - pub fn sess(&self) -> &Session { - &self.tcx.sess - } - - pub fn errors_reported_since_creation(&self) -> bool { - self.tcx.sess.err_count() > self.err_count_on_creation - } -} - -impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.inh - } -} - -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option<DefId> { - None - } - - fn get_type_parameter_bounds( - &self, - _: Span, - def_id: DefId, - _: Ident, - ) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); - let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => { - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - Some((predicate, span)) - } - _ => None, - } - }), - ), - } - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span), - }; - Some(self.next_region_var(v)) - } - - fn allow_ty_infer(&self) -> bool { - true - } - - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }) - } - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) - } - } - - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { - let trait_ref = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), - poly_trait_ref, - ); - - let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - self.tcx().mk_projection(item_def_id, item_substs) - } - - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize_associated_types_in(span, ty) - } - } - - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() - } - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { - self.write_ty(hir_id, ty) - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs deleted file mode 100644 index 57771e096..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ /dev/null @@ -1,912 +0,0 @@ -use super::FnCtxt; -use crate::astconv::AstConv; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; - -use rustc_ast::util::parser::ExprPrecedence; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind}; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ - Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, -}; -use rustc_infer::infer::{self, TyCtxtInferExt}; -use rustc_infer::traits::{self, StatementAsExpression}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; -use rustc_span::symbol::sym; -use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { - err.span_suggestion_short( - span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MachineApplicable, - ); - } - - /// On implicit return expressions with mismatched types, provides the following suggestions: - /// - /// - Points out the method's return type as the reason for the expected type. - /// - Possible missing semicolon. - /// - Possible missing return type if the return type is the default, and not `fn main()`. - pub fn suggest_mismatched_types_on_tail( - &self, - err: &mut Diagnostic, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - blk_id: hir::HirId, - ) -> bool { - let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, false); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); - pointing_at_return_type = self.suggest_missing_return_type( - err, - &fn_decl, - expected, - found, - can_suggest, - fn_id, - ); - self.suggest_missing_break_or_return_expr( - err, expr, &fn_decl, expected, found, blk_id, fn_id, - ); - } - pointing_at_return_type - } - - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ```compile_fail,E0308 - /// fn foo(x: usize) -> usize { x } - /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` - /// ``` - fn suggest_fn_call( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - let (def_id, output, inputs) = match *found.kind() { - ty::FnDef(def_id, _) => { - let fn_sig = found.fn_sig(self.tcx); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len()) - } - ty::Closure(def_id, substs) => { - let fn_sig = substs.as_closure().sig(); - (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1) - } - ty::Opaque(def_id, substs) => { - let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() - // args tuple will always be substs[1] - && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() - { - Some(( - pred.kind().rebind(proj.term.ty().unwrap()), - args.len(), - )) - } else { - None - } - }); - if let Some((output, inputs)) = sig { - (def_id, output, inputs) - } else { - return false; - } - } - _ => return false, - }; - - let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); - let output = self.normalize_associated_types_in(expr.span, output); - if !output.is_ty_var() && self.can_coerce(output, expected) { - let (sugg_call, mut applicability) = match inputs { - 0 => ("".to_string(), Applicability::MachineApplicable), - 1..=4 => ( - (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "), - Applicability::MachineApplicable, - ), - _ => ("...".to_string(), Applicability::HasPlaceholders), - }; - - let msg = match self.tcx.def_kind(def_id) { - DefKind::Fn => "call this function", - DefKind::Closure | DefKind::OpaqueTy => "call this closure", - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct", - DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant", - _ => "call this function", - }; - - let sugg = match expr.kind { - hir::ExprKind::Call(..) - | hir::ExprKind::Path(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Lit(..) => { - vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] - } - hir::ExprKind::Closure { .. } => { - // Might be `{ expr } || { bool }` - applicability = Applicability::MaybeIncorrect; - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] - } - _ => { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] - } - }; - - err.multipart_suggestion_verbose( - format!("use parentheses to {msg}"), - sugg, - applicability, - ); - - return true; - } - false - } - - pub fn suggest_deref_ref_or_into( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { - let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose)) = - self.check_ref(expr, found, expected) - { - if verbose { - err.span_suggestion_verbose(sp, &msg, suggestion, applicability); - } else { - err.span_suggestion(sp, &msg, suggestion, applicability); - } - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) - { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - err.span_label(sp, format!("{found} defined here")); - } - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if !methods.is_empty() { - let mut suggestions = methods.iter() - .filter_map(|conversion_method| { - let receiver_method_ident = expr.method_ident(); - if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name - { - return None // do not suggest code that is already there (#53348) - } - - let method_call_list = [sym::to_vec, sym::to_string]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) - // If receiver is `.clone()` and found type has one of those methods, - // we guess that the user wants to convert from a slice type (`&[]` or `&str`) - // to an owned type (`Vec` or `String`). These conversions clone internally, - // so we remove the user's `clone` call. - { - vec![( - receiver_method.ident.span, - conversion_method.name.to_string() - )] - } else if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), - ] - } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] - }; - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } else if let ty::Adt(found_adt, found_substs) = found.kind() - && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) - && let ty::Adt(expected_adt, expected_substs) = expected.kind() - && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) - && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() - && inner_ty.is_str() - { - let ty = found_substs.type_at(0); - let mut peeled = ty; - let mut ref_cnt = 0; - while let ty::Ref(_, inner, _) = peeled.kind() { - peeled = *inner; - ref_cnt += 1; - } - if let ty::Adt(adt, _) = peeled.kind() - && self.tcx.is_diagnostic_item(sym::String, adt.did()) - { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - "try converting the passed type into a `&str`", - format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), - Applicability::MaybeIncorrect, - ); - } - } - } - } - - /// When encountering the expected boxed value allocated in the stack, suggest allocating it - /// in the heap by calling `Box::new()`. - pub(in super::super) fn suggest_boxing_when_appropriate( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return; - } - if !expected.is_box() || found.is_box() { - return; - } - let boxed_found = self.tcx.mk_box(found); - if self.can_coerce(boxed_found, expected) { - err.multipart_suggestion( - "store this in the heap by calling `Box::new`", - vec![ - (expr.span.shrink_to_lo(), "Box::new(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); - } - } - - /// When encountering a closure that captures variables, where a FnPtr is expected, - /// suggest a non-capturing closure - pub(in super::super) fn suggest_no_capture_closure( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { - if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { - // Report upto four upvars being captured to reduce the amount error messages - // reported back to the user. - let spans_and_labels = upvars - .iter() - .take(4) - .map(|(var_hir_id, upvar)| { - let var_name = self.tcx.hir().name(*var_hir_id).to_string(); - let msg = format!("`{}` captured here", var_name); - (upvar.span, msg) - }) - .collect::<Vec<_>>(); - - let mut multi_span: MultiSpan = - spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); - for (sp, label) in spans_and_labels { - multi_span.push_span_label(sp, label); - } - err.span_note( - multi_span, - "closures can only be coerced to `fn` types if they do not capture any variables" - ); - } - } - } - - /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. - #[instrument(skip(self, err))] - pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - // Handle #68197. - - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - let pin_did = self.tcx.lang_items().pin_type(); - // This guards the `unwrap` and `mk_box` below. - if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { - return false; - } - let box_found = self.tcx.mk_box(found); - let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap(); - let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap(); - match expected.kind() { - ty::Adt(def, _) if Some(def.did()) == pin_did => { - if self.can_coerce(pin_box_found, expected) { - debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.multipart_suggestion( - "you need to pin and box this expression", - vec![ - (expr.span.shrink_to_lo(), "Box::pin(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } - true - } else if self.can_coerce(pin_found, expected) { - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - true - } - _ => false, - } - } else { - false - } - } - ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { - // Check if the parent expression is a call to Pin::new. If it - // is and we were expecting a Box, ergo Pin<Box<expected>>, we - // can suggest Box::pin. - let parent = self.tcx.hir().get_parent_node(expr.hir_id); - let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { - return false; - }; - match fn_name.kind { - ExprKind::Path(QPath::TypeRelative( - hir::Ty { - kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), - .. - }, - method, - )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => { - err.span_suggestion( - fn_name.span, - "use `Box::pin` to pin and box this expression", - "Box::pin", - Applicability::MachineApplicable, - ); - true - } - _ => false, - } - } - _ => false, - } - } - - /// A common error is to forget to add a semicolon at the end of a block, e.g., - /// - /// ```compile_fail,E0308 - /// # fn bar_that_returns_u32() -> u32 { 4 } - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as default or has been specified as `()`. If so, - /// it suggests adding a semicolon. - /// - /// If the expression is the expression of a closure without block (`|| expr`), a - /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. - pub fn suggest_missing_semicolon( - &self, - err: &mut Diagnostic, - expression: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - needs_block: bool, - ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::If(..) - | ExprKind::Match(..) - | ExprKind::Block(..) - if expression.can_have_side_effects() - // If the expression is from an external macro, then do not suggest - // adding a semicolon, because there's nowhere to put it. - // See issue #81943. - && !in_external_macro(self.tcx.sess, expression.span) => - { - if needs_block { - err.multipart_suggestion( - "consider using a semicolon here", - vec![ - (expression.span.shrink_to_lo(), "{ ".to_owned()), - (expression.span.shrink_to_hi(), "; }".to_owned()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - expression.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MachineApplicable, - ); - } - } - _ => (), - } - } - } - - /// A possible error is to forget to add a return type that is needed: - /// - /// ```compile_fail,E0308 - /// # fn bar_that_returns_u32() -> u32 { 4 } - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return type is left as default, the method is not part of an - /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return - /// type. - pub(in super::super) fn suggest_missing_return_type( - &self, - err: &mut Diagnostic, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - can_suggest: bool, - fn_id: hir::HirId, - ) -> bool { - let found = - self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); - // Only suggest changing the return type for methods that - // haven't set a return type at all (and aren't `fn main()` or an impl). - match ( - &fn_decl.output, - found.is_suggestable(self.tcx, false), - can_suggest, - expected.is_unit(), - ) { - (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found }); - true - } - (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { - // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest - // that. - err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); - true - } - (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { - // `fn main()` must return `()`, do not suggest changing return type - err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); - true - } - // expectation was caused by something else, not the default return - (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, - (&hir::FnRetTy::Return(ref ty), _, _, _) => { - // Only point to return type if the expected type is the return type, as if they - // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let span = ty.span; - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = Binder::bind_with_vars(ty, bound_vars); - let ty = self.normalize_associated_types_in(span, ty); - let ty = self.tcx.erase_late_bound_regions(ty); - if self.can_coerce(expected, ty) { - err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); - self.try_suggest_return_impl_trait(err, expected, ty, fn_id); - return true; - } - false - } - } - } - - /// check whether the return type is a generic type with a trait bound - /// only suggest this if the generic param is not present in the arguments - /// if this is true, hint them towards changing the return type to `impl Trait` - /// ```compile_fail,E0308 - /// fn cant_name_it<T: Fn() -> u32>() -> T { - /// || 3 - /// } - /// ``` - fn try_suggest_return_impl_trait( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - fn_id: hir::HirId, - ) { - // Only apply the suggestion if: - // - the return type is a generic parameter - // - the generic param is not used as a fn param - // - the generic param has at least one bound - // - the generic param doesn't appear in any other bounds where it's not the Self type - // Suggest: - // - Changing the return type to be `impl <all bounds>` - - debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); - - let ty::Param(expected_ty_as_param) = expected.kind() else { return }; - - let fn_node = self.tcx.hir().find(fn_id); - - let Some(hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Fn( - hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, - hir::Generics { params, predicates, .. }, - _body_id, - ), - .. - })) = fn_node else { return }; - - if params.get(expected_ty_as_param.index as usize).is_none() { - return; - }; - - // get all where BoundPredicates here, because they are used in to cases below - let where_predicates = predicates - .iter() - .filter_map(|p| match p { - WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bounds, - bounded_ty, - .. - }) => { - // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty); - Some((ty, bounds)) - } - _ => None, - }) - .map(|(ty, bounds)| match ty.kind() { - ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), - // check whether there is any predicate that contains our `T`, like `Option<T>: Send` - _ => match ty.contains(expected) { - true => Err(()), - false => Ok(None), - }, - }) - .collect::<Result<Vec<_>, _>>(); - - let Ok(where_predicates) = where_predicates else { return }; - - // now get all predicates in the same types as the where bounds, so we can chain them - let predicates_from_where = - where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); - - // extract all bounds from the source code using their spans - let all_matching_bounds_strs = predicates_from_where - .filter_map(|bound| match bound { - GenericBound::Trait(_, _) => { - self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() - } - _ => None, - }) - .collect::<Vec<String>>(); - - if all_matching_bounds_strs.len() == 0 { - return; - } - - let all_bounds_str = all_matching_bounds_strs.join(" + "); - - let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param); - matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) - }); - - if ty_param_used_in_fn_params { - return; - } - - err.span_suggestion( - fn_return.span(), - "consider using an impl return type", - format!("impl {}", all_bounds_str), - Applicability::MaybeIncorrect, - ); - } - - pub(in super::super) fn suggest_missing_break_or_return_expr( - &self, - err: &mut Diagnostic, - expr: &'tcx hir::Expr<'tcx>, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - id: hir::HirId, - fn_id: hir::HirId, - ) { - if !expected.is_unit() { - return; - } - let found = self.resolve_vars_with_obligations(found); - - let in_loop = self.is_loop(id) - || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id)); - - let in_local_statement = self.is_local_statement(id) - || self - .tcx - .hir() - .parent_iter(id) - .any(|(parent_id, _)| self.is_local_statement(parent_id)); - - if in_loop && in_local_statement { - err.multipart_suggestion( - "you might have meant to break the loop with this value", - vec![ - (expr.span.shrink_to_lo(), "break ".to_string()), - (expr.span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); - return; - } - - if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); - let ty = self.normalize_associated_types_in(expr.span, ty); - let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self - .tcx - .infer_ctxt() - .enter(|infcx| { - infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }) - }) - .skip_binder(), - hir::IsAsync::NotAsync => ty, - }; - if self.can_coerce(found, ty) { - err.multipart_suggestion( - "you might have meant to return this value", - vec![ - (expr.span.shrink_to_lo(), "return ".to_string()), - (expr.span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } - } - - pub(in super::super) fn suggest_missing_parentheses( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - ) { - let sp = self.tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { - // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp); - } - } - - /// Given an expression type mismatch, peel any `&` expressions until we get to - /// a block expression, and then suggest replacing the braces with square braces - /// if it was possibly mistaken array syntax. - pub(crate) fn suggest_block_to_brackets_peeling_refs( - &self, - diag: &mut Diagnostic, - mut expr: &hir::Expr<'_>, - mut expr_ty: Ty<'tcx>, - mut expected_ty: Ty<'tcx>, - ) { - loop { - match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { - ( - hir::ExprKind::AddrOf(_, _, inner_expr), - ty::Ref(_, inner_expr_ty, _), - ty::Ref(_, inner_expected_ty, _), - ) => { - expr = *inner_expr; - expr_ty = *inner_expr_ty; - expected_ty = *inner_expected_ty; - } - (hir::ExprKind::Block(blk, _), _, _) => { - self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); - break; - } - _ => break, - } - } - } - - /// Suggest wrapping the block in square brackets instead of curly braces - /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. - pub(crate) fn suggest_block_to_brackets( - &self, - diag: &mut Diagnostic, - blk: &hir::Block<'_>, - blk_ty: Ty<'tcx>, - expected_ty: Ty<'tcx>, - ) { - if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { - if self.can_coerce(blk_ty, *elem_ty) - && blk.stmts.is_empty() - && blk.rules == hir::BlockCheckMode::DefaultBlock - { - let source_map = self.tcx.sess.source_map(); - if let Ok(snippet) = source_map.span_to_snippet(blk.span) { - if snippet.starts_with('{') && snippet.ends_with('}') { - diag.multipart_suggestion_verbose( - "to create an array, use square brackets instead of curly braces", - vec![ - ( - blk.span - .shrink_to_lo() - .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), - "[".to_string(), - ), - ( - blk.span - .shrink_to_hi() - .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), - "]".to_string(), - ), - ], - Applicability::MachineApplicable, - ); - } - } - } - } - } - - fn is_loop(&self, id: hir::HirId) -> bool { - let node = self.tcx.hir().get(id); - matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) - } - - fn is_local_statement(&self, id: hir::HirId) -> bool { - let node = self.tcx.hir().get(id); - matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. })) - } - - /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`, - /// which is a side-effect of autoref. - pub(crate) fn note_type_is_not_clone( - &self, - diag: &mut Diagnostic, - expected_ty: Ty<'tcx>, - found_ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - ) { - let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; }; - let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; - let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; - let results = self.typeck_results.borrow(); - // First, look for a `Clone::clone` call - if segment.ident.name == sym::clone - && results.type_dependent_def_id(expr.hir_id).map_or( - false, - |did| { - let assoc_item = self.tcx.associated_item(did); - assoc_item.container == ty::AssocItemContainer::TraitContainer - && assoc_item.container_id(self.tcx) == clone_trait_did - }, - ) - // If that clone call hasn't already dereferenced the self type (i.e. don't give this - // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). - && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) - // Check that we're in fact trying to clone into the expected type - && self.can_coerce(*pointee_ty, expected_ty) - // And the expected type doesn't implement `Clone` - && !self.predicate_must_hold_considering_regions(&traits::Obligation { - cause: traits::ObligationCause::dummy(), - param_env: self.param_env, - recursion_depth: 0, - predicate: ty::Binder::dummy(ty::TraitRef { - def_id: clone_trait_did, - substs: self.tcx.mk_substs([expected_ty.into()].iter()), - }) - .without_const() - .to_predicate(self.tcx), - }) - { - diag.span_note( - callee_expr.span, - &format!( - "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" - ), - ); - } - } - - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - pub(crate) fn consider_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diagnostic, - ) -> bool { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - err.span_suggestion_short( - span_semi, - "remove this semicolon", - "", - Applicability::MachineApplicable, - ); - } - true - } else { - false - } - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs deleted file mode 100644 index d4f800149..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ /dev/null @@ -1,632 +0,0 @@ -//! This calculates the types which has storage which lives across a suspension point in a -//! generator from the perspective of typeck. The actual types used at runtime -//! is calculated in `rustc_mir_transform::generator` and may be a subset of the -//! types computed here. - -use self::drop_ranges::DropRanges; -use super::FnCtxt; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::pluralize; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::HirIdSet; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; -use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; -use rustc_span::symbol::sym; -use rustc_span::Span; -use tracing::debug; - -mod drop_ranges; - -struct InteriorVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - region_scope_tree: &'a region::ScopeTree, - types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, - rvalue_scopes: &'a RvalueScopes, - expr_count: usize, - kind: hir::GeneratorKind, - prev_unresolved_span: Option<Span>, - linted_values: HirIdSet, - drop_ranges: DropRanges, -} - -impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { - fn record( - &mut self, - ty: Ty<'tcx>, - hir_id: HirId, - scope: Option<region::Scope>, - expr: Option<&'tcx Expr<'tcx>>, - source_span: Span, - ) { - use rustc_span::DUMMY_SP; - - let ty = self.fcx.resolve_vars_if_possible(ty); - - debug!( - "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}", - ty, hir_id, scope, expr, source_span, self.expr_count, - ); - - let live_across_yield = scope - .map(|s| { - self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| { - // If we are recording an expression that is the last yield - // in the scope, or that has a postorder CFG index larger - // than the one of all of the yields, then its value can't - // be storage-live (and therefore live) at any of the yields. - // - // See the mega-comment at `yield_in_scope` for a proof. - - yield_data - .iter() - .find(|yield_data| { - debug!( - "comparing counts yield: {} self: {}, source_span = {:?}", - yield_data.expr_and_pat_count, self.expr_count, source_span - ); - - if self.fcx.sess().opts.unstable_opts.drop_tracking - && self - .drop_ranges - .is_dropped_at(hir_id, yield_data.expr_and_pat_count) - { - debug!("value is dropped at yield point; not recording"); - return false; - } - - // If it is a borrowing happening in the guard, - // it needs to be recorded regardless because they - // do live across this yield point. - yield_data.expr_and_pat_count >= self.expr_count - }) - .cloned() - }) - }) - .unwrap_or_else(|| { - Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() }) - }); - - if let Some(yield_data) = live_across_yield { - debug!( - "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", - expr, scope, ty, self.expr_count, yield_data.span - ); - - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) - { - // If unresolved type isn't a ty_var then unresolved_type_span is None - let span = self - .prev_unresolved_span - .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); - - // If we encounter an int/float variable, then inference fallback didn't - // finish due to some other error. Don't emit spurious additional errors. - if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = - unresolved_type.kind() - { - self.fcx - .tcx - .sess - .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); - } else { - let note = format!( - "the type is part of the {} because of this {}", - self.kind, yield_data.source - ); - - self.fcx - .need_type_info_err_in_generator(self.kind, span, unresolved_type) - .span_note(yield_data.span, &*note) - .emit(); - } - } else { - // Insert the type into the ordered set. - let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); - - if !self.linted_values.contains(&hir_id) { - check_must_not_suspend_ty( - self.fcx, - ty, - hir_id, - SuspendCheckData { - expr, - source_span, - yield_span: yield_data.span, - plural_len: 1, - ..Default::default() - }, - ); - self.linted_values.insert(hir_id); - } - - self.types.insert(ty::GeneratorInteriorTypeCause { - span: source_span, - ty, - scope_span, - yield_span: yield_data.span, - expr: expr.map(|e| e.hir_id), - }); - } - } else { - debug!( - "no type in expr = {:?}, count = {:?}, span = {:?}", - expr, - self.expr_count, - expr.map(|e| e.span) - ); - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) - { - debug!( - "remained unresolved_type = {:?}, unresolved_type_span: {:?}", - unresolved_type, unresolved_type_span - ); - self.prev_unresolved_span = unresolved_type_span; - } - } - } -} - -pub fn resolve_interior<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - def_id: DefId, - body_id: hir::BodyId, - interior: Ty<'tcx>, - kind: hir::GeneratorKind, -) { - let body = fcx.tcx.hir().body(body_id); - let typeck_results = fcx.inh.typeck_results.borrow(); - let mut visitor = InteriorVisitor { - fcx, - types: FxIndexSet::default(), - region_scope_tree: fcx.tcx.region_scope_tree(def_id), - rvalue_scopes: &typeck_results.rvalue_scopes, - expr_count: 0, - kind, - prev_unresolved_span: None, - linted_values: <_>::default(), - drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), - }; - intravisit::walk_body(&mut visitor, body); - - // Check that we visited the same amount of expressions as the RegionResolutionVisitor - let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap(); - assert_eq!(region_expr_count, visitor.expr_count); - - // The types are already kept in insertion order. - let types = visitor.types; - - // The types in the generator interior contain lifetimes local to the generator itself, - // which should not be exposed outside of the generator. Therefore, we replace these - // lifetimes with existentially-bound lifetimes, which reflect the exact value of the - // lifetimes not being known by users. - // - // These lifetimes are used in auto trait impl checking (for example, - // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), - // so knowledge of the exact relationships between them isn't particularly important. - - debug!("types in generator {:?}, span = {:?}", types, body.value.span); - - let mut counter = 0; - let mut captured_tys = FxHashSet::default(); - let type_causes: Vec<_> = types - .into_iter() - .filter_map(|mut cause| { - // Erase regions and canonicalize late-bound regions to deduplicate as many types as we - // can. - let erased = fcx.tcx.erase_regions(cause.ty); - if captured_tys.insert(erased) { - // Replace all regions inside the generator interior with late bound regions. - // Note that each region slot in the types gets a new fresh late bound region, - // which means that none of the regions inside relate to any other, even if - // typeck had previously found constraints that would cause them to be related. - let folded = fcx.tcx.fold_regions(erased, |_, current_depth| { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(counter), - }; - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); - counter += 1; - r - }); - - cause.ty = folded; - Some(cause) - } else { - None - } - }) - .collect(); - - // Extract type components to build the witness type. - let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let bound_vars = fcx.tcx.mk_bound_variable_kinds( - (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), - ); - let witness = - fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); - - drop(typeck_results); - // Store the generator types and spans into the typeck results for this generator. - fcx.inh.typeck_results.borrow_mut().generator_interior_types = - ty::Binder::bind_with_vars(type_causes, bound_vars); - - debug!( - "types in generator after region replacement {:?}, span = {:?}", - witness, body.value.span - ); - - // Unify the type variable inside the generator with the new witness - match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { - Ok(ok) => fcx.register_infer_ok_obligations(ok), - _ => bug!(), - } -} - -// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in -// librustc_middle/middle/region.rs since `expr_count` is compared against the results -// there. -impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { - fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { - let Arm { guard, pat, body, .. } = arm; - self.visit_pat(pat); - if let Some(ref g) = guard { - { - // If there is a guard, we need to count all variables bound in the pattern as - // borrowed for the entire guard body, regardless of whether they are accessed. - // We do this by walking the pattern bindings and recording `&T` for any `x: T` - // that is bound. - - struct ArmPatCollector<'a, 'b, 'tcx> { - interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>, - scope: Scope, - } - - impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> { - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - intravisit::walk_pat(self, pat); - if let PatKind::Binding(_, id, ident, ..) = pat.kind { - let ty = - self.interior_visitor.fcx.typeck_results.borrow().node_type(id); - let tcx = self.interior_visitor.fcx.tcx; - let ty = tcx.mk_ref( - // Use `ReErased` as `resolve_interior` is going to replace all the - // regions anyway. - tcx.mk_region(ty::ReErased), - ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, - ); - self.interior_visitor.record( - ty, - id, - Some(self.scope), - None, - ident.span, - ); - } - } - } - - ArmPatCollector { - interior_visitor: self, - scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node }, - } - .visit_pat(pat); - } - - match g { - Guard::If(ref e) => { - self.visit_expr(e); - } - Guard::IfLet(ref l) => { - self.visit_let_expr(l); - } - } - } - self.visit_expr(body); - } - - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - intravisit::walk_pat(self, pat); - - self.expr_count += 1; - - if let PatKind::Binding(..) = pat.kind { - let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); - let ty = self.fcx.typeck_results.borrow().pat_ty(pat); - self.record(ty, pat.hir_id, Some(scope), None, pat.span); - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match &expr.kind { - ExprKind::Call(callee, args) => match &callee.kind { - ExprKind::Path(qpath) => { - let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id); - match res { - // Direct calls never need to keep the callee `ty::FnDef` - // ZST in a temporary, so skip its type, just in case it - // can significantly complicate the generator type. - Res::Def( - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), - _, - ) => { - // NOTE(eddyb) this assumes a path expression has - // no nested expressions to keep track of. - self.expr_count += 1; - - // Record the rest of the call expression normally. - for arg in *args { - self.visit_expr(arg); - } - } - _ => intravisit::walk_expr(self, expr), - } - } - _ => intravisit::walk_expr(self, expr), - }, - _ => intravisit::walk_expr(self, expr), - } - - self.expr_count += 1; - - debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); - - let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); - let may_need_drop = |ty: Ty<'tcx>| { - // Avoid ICEs in needs_drop. - let ty = self.fcx.resolve_vars_if_possible(ty); - let ty = self.fcx.tcx.erase_regions(ty); - if ty.needs_infer() { - return true; - } - ty.needs_drop(self.fcx.tcx, self.fcx.param_env) - }; - - // Typically, the value produced by an expression is consumed by its parent in some way, - // so we only have to check if the parent contains a yield (note that the parent may, for - // example, store the value into a local variable, but then we already consider local - // variables to be live across their scope). - // - // However, in the case of temporary values, we are going to store the value into a - // temporary on the stack that is live for the current temporary scope and then return a - // reference to it. That value may be live across the entire temporary scope. - // - // There's another subtlety: if the type has an observable drop, it must be dropped after - // the yield, even if it's not borrowed or referenced after the yield. Ideally this would - // *only* happen for types with observable drop, not all types which wrap them, but that - // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in - // src/test/ui/generator/drop-tracking-parent-expression.rs. - let scope = if self.drop_ranges.is_borrowed_temporary(expr) - || ty.map_or(true, |ty| { - let needs_drop = may_need_drop(ty); - debug!(?needs_drop, ?ty); - needs_drop - }) { - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) - } else { - debug!("parent_node: {:?}", self.fcx.tcx.hir().find_parent_node(expr.hir_id)); - match self.fcx.tcx.hir().find_parent_node(expr.hir_id) { - Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), - None => { - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) - } - } - }; - - // If there are adjustments, then record the final type -- - // this is the actual value that is being produced. - if let Some(adjusted_ty) = ty { - self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); - } - - // Also record the unadjusted type (which is the only type if - // there are no adjustments). The reason for this is that the - // unadjusted value is sometimes a "temporary" that would wind - // up in a MIR temporary. - // - // As an example, consider an expression like `vec![].push(x)`. - // Here, the `vec![]` would wind up MIR stored into a - // temporary variable `t` which we can borrow to invoke - // `<Vec<_>>::push(&mut t, x)`. - // - // Note that an expression can have many adjustments, and we - // are just ignoring those intermediate types. This is because - // those intermediate values are always linearly "consumed" by - // the other adjustments, and hence would never be directly - // captured in the MIR. - // - // (Note that this partly relies on the fact that the `Deref` - // traits always return references, which means their content - // can be reborrowed without needing to spill to a temporary. - // If this were not the case, then we could conceivably have - // to create intermediate temporaries.) - // - // The type table might not have information for this expression - // if it is in a malformed scope. (#66387) - if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { - self.record(ty, expr.hir_id, scope, Some(expr), expr.span); - } else { - self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); - } - } -} - -#[derive(Default)] -pub struct SuspendCheckData<'a, 'tcx> { - expr: Option<&'tcx Expr<'tcx>>, - source_span: Span, - yield_span: Span, - descr_pre: &'a str, - descr_post: &'a str, - plural_len: usize, -} - -// Returns whether it emitted a diagnostic or not -// Note that this fn and the proceeding one are based on the code -// for creating must_use diagnostics -// -// Note that this technique was chosen over things like a `Suspend` marker trait -// as it is simpler and has precedent in the compiler -pub fn check_must_not_suspend_ty<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - ty: Ty<'tcx>, - hir_id: HirId, - data: SuspendCheckData<'_, 'tcx>, -) -> bool { - if ty.is_unit() - // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage - // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in - // `must_use` - // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env) - { - return false; - } - - let plural_suffix = pluralize!(data.plural_len); - - match *ty.kind() { - ty::Adt(..) if ty.is_box() => { - let boxed_ty = ty.boxed_ty(); - let descr_pre = &format!("{}boxed ", data.descr_pre); - check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data }) - } - ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data), - // FIXME: support adding the attribute to TAITs - ty::Opaque(def, _) => { - let mut has_emitted = false; - for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateKind::Trait(ref poly_trait_predicate) = - predicate.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix); - if check_must_not_suspend_def( - fcx.tcx, - def_id, - hir_id, - SuspendCheckData { descr_pre, ..data }, - ) { - has_emitted = true; - break; - } - } - } - has_emitted - } - ty::Dynamic(binder, _) => { - let mut has_emitted = false; - for predicate in binder.iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - let def_id = trait_ref.def_id; - let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post); - if check_must_not_suspend_def( - fcx.tcx, - def_id, - hir_id, - SuspendCheckData { descr_post, ..data }, - ) { - has_emitted = true; - break; - } - } - } - has_emitted - } - ty::Tuple(fields) => { - let mut has_emitted = false; - let comps = match data.expr.map(|e| &e.kind) { - Some(hir::ExprKind::Tup(comps)) => { - debug_assert_eq!(comps.len(), fields.len()); - Some(comps) - } - _ => None, - }; - for (i, ty) in fields.iter().enumerate() { - let descr_post = &format!(" in tuple element {i}"); - let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span); - if check_must_not_suspend_ty( - fcx, - ty, - hir_id, - SuspendCheckData { - descr_post, - expr: comps.and_then(|comps| comps.get(i)), - source_span: span, - ..data - }, - ) { - has_emitted = true; - } - } - has_emitted - } - ty::Array(ty, len) => { - let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); - check_must_not_suspend_ty( - fcx, - ty, - hir_id, - SuspendCheckData { - descr_pre, - plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize - + 1, - ..data - }, - ) - } - _ => false, - } -} - -fn check_must_not_suspend_def( - tcx: TyCtxt<'_>, - def_id: DefId, - hir_id: HirId, - data: SuspendCheckData<'_, '_>, -) -> bool { - if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::MUST_NOT_SUSPEND, - hir_id, - data.source_span, - |lint| { - let msg = format!( - "{}`{}`{} held across a suspend point, but should not be", - data.descr_pre, - tcx.def_path_str(def_id), - data.descr_post, - ); - let mut err = lint.build(&msg); - - // add span pointing to the offending yield/await - err.span_label(data.yield_span, "the value is held across this suspend point"); - - // Add optional reason note - if let Some(note) = attr.value_str() { - // FIXME(guswynn): consider formatting this better - err.span_note(data.source_span, note.as_str()); - } - - // Add some quick suggestions on what to do - // FIXME: can `drop` work as a suggestion here as well? - err.span_help( - data.source_span, - "consider using a block (`{ ... }`) \ - to shrink the value's scope, ending before the suspend point", - ); - - err.emit(); - }, - ); - - true - } else { - false - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs deleted file mode 100644 index 518cd7342..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped -//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the -//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used. -//! -//! There are three phases to this analysis: -//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed. -//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized, -//! and also build a control flow graph. -//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through -//! the CFG and find the exact points where we know a value is definitely dropped. -//! -//! The end result is a data structure that maps the post-order index of each node in the HIR tree -//! to a set of values that are known to be dropped at that location. - -use self::cfg_build::build_control_flow_graph; -use self::record_consumed_borrow::find_consumed_and_borrowed; -use crate::check::FnCtxt; -use hir::def_id::DefId; -use hir::{Body, HirId, HirIdMap, Node}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_middle::hir::map::Map; -use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; -use rustc_middle::ty; -use std::collections::BTreeMap; -use std::fmt::Debug; - -mod cfg_build; -mod cfg_propagate; -mod cfg_visualize; -mod record_consumed_borrow; - -pub fn compute_drop_ranges<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - def_id: DefId, - body: &'tcx Body<'tcx>, -) -> DropRanges { - if fcx.sess().opts.unstable_opts.drop_tracking { - let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); - - let typeck_results = &fcx.typeck_results.borrow(); - let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); - let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( - fcx.tcx.hir(), - fcx.tcx, - typeck_results, - consumed_borrowed_places, - body, - num_exprs, - ); - - drop_ranges.propagate_to_fixpoint(); - - debug!("borrowed_temporaries = {borrowed_temporaries:?}"); - DropRanges { - tracked_value_map: drop_ranges.tracked_value_map, - nodes: drop_ranges.nodes, - borrowed_temporaries: Some(borrowed_temporaries), - } - } else { - // If drop range tracking is not enabled, skip all the analysis and produce an - // empty set of DropRanges. - DropRanges { - tracked_value_map: FxHashMap::default(), - nodes: IndexVec::new(), - borrowed_temporaries: None, - } - } -} - -/// Applies `f` to consumable node in the HIR subtree pointed to by `place`. -/// -/// This includes the place itself, and if the place is a reference to a local -/// variable then `f` is also called on the HIR node for that variable as well. -/// -/// For example, if `place` points to `foo()`, then `f` is called once for the -/// result of `foo`. On the other hand, if `place` points to `x` then `f` will -/// be called both on the `ExprKind::Path` node that represents the expression -/// as well as the HirId of the local `x` itself. -fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) { - f(place); - let node = hir.find(place.hir_id()); - if let Some(Node::Expr(expr)) = node { - match expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) => { - f(TrackedValue::Variable(*hir_id)); - } - _ => (), - } - } -} - -rustc_index::newtype_index! { - pub struct PostOrderId { - DEBUG_FORMAT = "id({})", - } -} - -rustc_index::newtype_index! { - pub struct TrackedValueIndex { - DEBUG_FORMAT = "hidx({})", - } -} - -/// Identifies a value whose drop state we need to track. -#[derive(PartialEq, Eq, Hash, Clone, Copy)] -enum TrackedValue { - /// Represents a named variable, such as a let binding, parameter, or upvar. - /// - /// The HirId points to the variable's definition site. - Variable(HirId), - /// A value produced as a result of an expression. - /// - /// The HirId points to the expression that returns this value. - Temporary(HirId), -} - -impl Debug for TrackedValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ty::tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - write!(f, "{}", tcx.hir().node_to_string(self.hir_id())) - } else { - match self { - Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id), - Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id), - } - } - }) - } -} - -impl TrackedValue { - fn hir_id(&self) -> HirId { - match self { - TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id, - } - } - - fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self { - match place_with_id.place.base { - PlaceBase::Rvalue | PlaceBase::StaticItem => { - TrackedValue::Temporary(place_with_id.hir_id) - } - PlaceBase::Local(hir_id) - | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => { - TrackedValue::Variable(hir_id) - } - } - } -} - -/// Represents a reason why we might not be able to convert a HirId or Place -/// into a tracked value. -#[derive(Debug)] -enum TrackedValueConversionError { - /// Place projects are not currently supported. - /// - /// The reasoning around these is kind of subtle, so we choose to be more - /// conservative around these for now. There is no reason in theory we - /// cannot support these, we just have not implemented it yet. - PlaceProjectionsNotSupported, -} - -impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue { - type Error = TrackedValueConversionError; - - fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> { - if !place_with_id.place.projections.is_empty() { - debug!( - "TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.", - place_with_id - ); - return Err(TrackedValueConversionError::PlaceProjectionsNotSupported); - } - - Ok(TrackedValue::from_place_with_projections_allowed(place_with_id)) - } -} - -pub struct DropRanges { - tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, - nodes: IndexVec<PostOrderId, NodeInfo>, - borrowed_temporaries: Option<FxHashSet<HirId>>, -} - -impl DropRanges { - pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool { - self.tracked_value_map - .get(&TrackedValue::Temporary(hir_id)) - .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) - .cloned() - .map_or(false, |tracked_value_id| { - self.expect_node(location.into()).drop_state.contains(tracked_value_id) - }) - } - - pub fn is_borrowed_temporary(&self, expr: &hir::Expr<'_>) -> bool { - if let Some(b) = &self.borrowed_temporaries { b.contains(&expr.hir_id) } else { true } - } - - /// Returns a reference to the NodeInfo for a node, panicking if it does not exist - fn expect_node(&self, id: PostOrderId) -> &NodeInfo { - &self.nodes[id] - } -} - -/// Tracks information needed to compute drop ranges. -struct DropRangesBuilder { - /// The core of DropRangesBuilder is a set of nodes, which each represent - /// one expression. We primarily refer to them by their index in a - /// post-order traversal of the HIR tree, since this is what - /// generator_interior uses to talk about yield positions. - /// - /// This IndexVec keeps the relevant details for each node. See the - /// NodeInfo struct for more details, but this information includes things - /// such as the set of control-flow successors, which variables are dropped - /// or reinitialized, and whether each variable has been inferred to be - /// known-dropped or potentially reinitialized at each point. - nodes: IndexVec<PostOrderId, NodeInfo>, - /// We refer to values whose drop state we are tracking by the HirId of - /// where they are defined. Within a NodeInfo, however, we store the - /// drop-state in a bit vector indexed by a HirIdIndex - /// (see NodeInfo::drop_state). The hir_id_map field stores the mapping - /// from HirIds to the HirIdIndex that is used to represent that value in - /// bitvector. - tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, - - /// When building the control flow graph, we don't always know the - /// post-order index of the target node at the point we encounter it. - /// For example, this happens with break and continue. In those cases, - /// we store a pair of the PostOrderId of the source and the HirId - /// of the target. Once we have gathered all of these edges, we make a - /// pass over the set of deferred edges (see process_deferred_edges in - /// cfg_build.rs), look up the PostOrderId for the target (since now the - /// post-order index for all nodes is known), and add missing control flow - /// edges. - deferred_edges: Vec<(PostOrderId, HirId)>, - /// This maps HirIds of expressions to their post-order index. It is - /// used in process_deferred_edges to correctly add back-edges. - post_order_map: HirIdMap<PostOrderId>, -} - -impl Debug for DropRangesBuilder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DropRanges") - .field("hir_id_map", &self.tracked_value_map) - .field("post_order_maps", &self.post_order_map) - .field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>()) - .finish() - } -} - -/// DropRanges keeps track of what values are definitely dropped at each point in the code. -/// -/// Values of interest are defined by the hir_id of their place. Locations in code are identified -/// by their index in the post-order traversal. At its core, DropRanges maps -/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely -/// dropped at the point of the node identified by post_order_id. -impl DropRangesBuilder { - /// Returns the number of values (hir_ids) that are tracked - fn num_values(&self) -> usize { - self.tracked_value_map.len() - } - - fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo { - let size = self.num_values(); - self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)); - &mut self.nodes[id] - } - - fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) { - trace!("adding control edge from {:?} to {:?}", from, to); - self.node_mut(from).successors.push(to); - } -} - -#[derive(Debug)] -struct NodeInfo { - /// IDs of nodes that can follow this one in the control flow - /// - /// If the vec is empty, then control proceeds to the next node. - successors: Vec<PostOrderId>, - - /// List of hir_ids that are dropped by this node. - drops: Vec<TrackedValueIndex>, - - /// List of hir_ids that are reinitialized by this node. - reinits: Vec<TrackedValueIndex>, - - /// Set of values that are definitely dropped at this point. - drop_state: BitSet<TrackedValueIndex>, -} - -impl NodeInfo { - fn new(num_values: usize) -> Self { - Self { - successors: vec![], - drops: vec![], - reinits: vec![], - drop_state: BitSet::new_filled(num_values), - } - } -} diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs deleted file mode 100644 index cd152eb97..000000000 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ /dev/null @@ -1,183 +0,0 @@ -use super::callee::DeferredCallResolution; - -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::HirIdMap; -use rustc_infer::infer; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::LocalDefIdMap; -use rustc_span::{self, Span}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt}; - -use std::cell::RefCell; -use std::ops::Deref; - -/// Closures defined within the function. For example: -/// ```ignore (illustrative) -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// ``` -/// Here, the function `foo()` and the closure passed to -/// `bar()` will each have their own `FnCtxt`, but they will -/// share the inherited fields. -pub struct Inherited<'a, 'tcx> { - pub(super) infcx: InferCtxt<'a, 'tcx>, - - pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>, - - pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, - - pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>, - - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - pub(super) deferred_sized_obligations: - RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>, - - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. - pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>, - - pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, - - pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, Span)>>, - - pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>, - - pub(super) deferred_generator_interiors: - RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, - - pub(super) body_id: Option<hir::BodyId>, - - /// Whenever we introduce an adjustment from `!` into a type variable, - /// we record that type variable here. This is later used to inform - /// fallback. See the `fallback` module for details. - pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>, -} - -impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { - type Target = InferCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.infcx - } -} - -/// A temporary returned by `Inherited::build(...)`. This is necessary -/// for multiple `InferCtxt` to share the same `in_progress_typeck_results` -/// without using `Rc` or something similar. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, -} - -impl<'tcx> Inherited<'_, 'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { - let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - - InheritedBuilder { - infcx: tcx - .infer_ctxt() - .ignoring_regions() - .with_fresh_in_progress_typeck_results(hir_owner), - def_id, - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R - where - F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, - { - let def_id = self.def_id; - self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) - } -} - -impl<'a, 'tcx> Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { - let tcx = infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(def_id); - let typeck_results = - infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results"); - - Inherited { - typeck_results, - infcx, - fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)), - locals: RefCell::new(Default::default()), - deferred_sized_obligations: RefCell::new(Vec::new()), - deferred_call_resolutions: RefCell::new(Default::default()), - deferred_cast_checks: RefCell::new(Vec::new()), - deferred_transmute_checks: RefCell::new(Vec::new()), - deferred_asm_checks: RefCell::new(Vec::new()), - deferred_generator_interiors: RefCell::new(Vec::new()), - diverging_type_vars: RefCell::new(Default::default()), - body_id, - } - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { - if obligation.has_escaping_bound_vars() { - span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); - } - self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); - } - - pub(super) fn register_predicates<I>(&self, obligations: I) - where - I: IntoIterator<Item = traits::PredicateObligation<'tcx>>, - { - for obligation in obligations { - self.register_predicate(obligation); - } - } - - pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { - self.register_predicates(infer_ok.obligations); - infer_ok.value - } - - pub(super) fn normalize_associated_types_in<T>( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - self.normalize_associated_types_in_with_cause( - ObligationCause::misc(span, body_id), - param_env, - value, - ) - } - - pub(super) fn normalize_associated_types_in_with_cause<T>( - &self, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(cause, param_env, value); - debug!(?ok); - self.register_infer_ok_obligations(ok) - } -} diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs deleted file mode 100644 index df94abbaf..000000000 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ /dev/null @@ -1,530 +0,0 @@ -use rustc_ast::InlineAsmTemplatePiece; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_index::vec::Idx; -use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; -use rustc_session::lint; -use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{Pointer, VariantIdx}; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; -use rustc_trait_selection::infer::InferCtxtExt; - -use super::FnCtxt; - -/// If the type is `Option<T>`, it will return `T`, otherwise -/// the type itself. Works on most `Option`-like types. -fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Adt(def, substs) = *ty.kind() else { return ty }; - - if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() { - let data_idx; - - let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); - - if def.variant(zero).fields.is_empty() { - data_idx = one; - } else if def.variant(one).fields.is_empty() { - data_idx = zero; - } else { - return ty; - } - - if def.variant(data_idx).fields.len() == 1 { - return def.variant(data_idx).fields[0].ty(tcx, substs); - } - } - - ty -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { - let convert = |ty: Ty<'tcx>| { - let ty = self.resolve_vars_if_possible(ty); - let ty = self.tcx.normalize_erasing_regions(self.param_env, ty); - (SizeSkeleton::compute(ty, self.tcx, self.param_env), ty) - }; - let (sk_from, from) = convert(from); - let (sk_to, to) = convert(to); - - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } - - // Special-case transmuting from `typeof(function)` and - // `Option<typeof(function)>` to present a clearer error. - let from = unpack_option_like(self.tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&self.tcx) { - struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type") - .note(&format!("source type: {from}")) - .note(&format!("target type: {to}")) - .help("cast with `as` to a pointer instead") - .emit(); - return; - } - } - - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk| match sk { - Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), - Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), - Err(LayoutError::Unknown(bad)) => { - if bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {bad}") - } - } - Err(err) => err.to_string(), - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types" - ); - if from == to { - err.note(&format!("`{from}` does not have a fixed size")); - } else { - err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - } - err.emit(); - } - - // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` - fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { - // Type still may have region variables, but `Sized` does not depend - // on those, so just erase them before querying. - if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) { - return true; - } - if let ty::Foreign(..) = ty.kind() { - return true; - } - false - } -} - -pub struct InlineAsmCtxt<'a, 'tcx> { - tcx: TyCtxt<'tcx>, - fcx: Option<&'a FnCtxt<'a, 'tcx>>, -} - -impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { - pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { - InlineAsmCtxt { tcx, fcx: None } - } - - pub fn new_in_fn(fcx: &'a FnCtxt<'a, 'tcx>) -> Self { - InlineAsmCtxt { tcx: fcx.tcx, fcx: Some(fcx) } - } - - fn check_asm_operand_type( - &self, - idx: usize, - reg: InlineAsmRegOrRegClass, - expr: &hir::Expr<'tcx>, - template: &[InlineAsmTemplatePiece], - is_input: bool, - tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>, - target_features: &FxHashSet<Symbol>, - ) -> Option<InlineAsmType> { - let fcx = self.fcx.unwrap_or_else(|| span_bug!(expr.span, "asm operand for global asm")); - // Check the type against the allowed types for inline asm. - let ty = fcx.typeck_results.borrow().expr_ty_adjusted(expr); - let ty = fcx.resolve_vars_if_possible(ty); - let asm_ty_isize = match self.tcx.sess.target.pointer_width { - 16 => InlineAsmType::I16, - 32 => InlineAsmType::I32, - 64 => InlineAsmType::I64, - _ => unreachable!(), - }; - - // Expect types to be fully resolved, no const or type variables. - if ty.has_infer_types_or_consts() { - assert!(fcx.is_tainted_by_errors()); - return None; - } - - let asm_ty = match *ty.kind() { - // `!` is allowed for input but not for output (issue #87802) - ty::Never if is_input => return None, - ty::Error(_) => return None, - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), - ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), - ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), - ty::FnPtr(_) => Some(asm_ty_isize), - ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if fcx.is_thin_ptr_ty(ty) => { - Some(asm_ty_isize) - } - ty::Adt(adt, substs) if adt.repr().simd() => { - let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[0].ty(self.tcx, substs); - match elem_ty.kind() { - ty::Never | ty::Error(_) => return None, - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => { - Some(InlineAsmType::VecI8(fields.len() as u64)) - } - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { - Some(InlineAsmType::VecI16(fields.len() as u64)) - } - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { - Some(InlineAsmType::VecI32(fields.len() as u64)) - } - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { - Some(InlineAsmType::VecI64(fields.len() as u64)) - } - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { - Some(InlineAsmType::VecI128(fields.len() as u64)) - } - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Some(match self.tcx.sess.target.pointer_width { - 16 => InlineAsmType::VecI16(fields.len() as u64), - 32 => InlineAsmType::VecI32(fields.len() as u64), - 64 => InlineAsmType::VecI64(fields.len() as u64), - _ => unreachable!(), - }) - } - ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)), - ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)), - _ => None, - } - } - ty::Infer(_) => unreachable!(), - _ => None, - }; - let Some(asm_ty) = asm_ty else { - let msg = &format!("cannot use value of type `{ty}` for inline assembly"); - let mut err = self.tcx.sess.struct_span_err(expr.span, msg); - err.note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ); - err.emit(); - return None; - }; - - // Check that the type implements Copy. The only case where this can - // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, ty, DUMMY_SP) { - let msg = "arguments for inline assembly must be copyable"; - let mut err = self.tcx.sess.struct_span_err(expr.span, msg); - err.note(&format!("`{ty}` does not implement the Copy trait")); - err.emit(); - } - - // Ideally we wouldn't need to do this, but LLVM's register allocator - // really doesn't like it when tied operands have different types. - // - // This is purely an LLVM limitation, but we have to live with it since - // there is no way to hide this with implicit conversions. - // - // For the purposes of this check we only look at the `InlineAsmType`, - // which means that pointers and integers are treated as identical (modulo - // size). - if let Some((in_expr, Some(in_asm_ty))) = tied_input { - if in_asm_ty != asm_ty { - let msg = "incompatible types for asm inout argument"; - let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg); - - let in_expr_ty = fcx.typeck_results.borrow().expr_ty_adjusted(in_expr); - let in_expr_ty = fcx.resolve_vars_if_possible(in_expr_ty); - err.span_label(in_expr.span, &format!("type `{in_expr_ty}`")); - err.span_label(expr.span, &format!("type `{ty}`")); - err.note( - "asm inout arguments must have the same type, \ - unless they are both pointers or integers of the same size", - ); - err.emit(); - } - - // All of the later checks have already been done on the input, so - // let's not emit errors and warnings twice. - return Some(asm_ty); - } - - // Check the type against the list of types supported by the selected - // register class. - let asm_arch = self.tcx.sess.asm_arch.unwrap(); - let reg_class = reg.reg_class(); - let supported_tys = reg_class.supported_types(asm_arch); - let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else { - let msg = &format!("type `{ty}` cannot be used with this register class"); - let mut err = self.tcx.sess.struct_span_err(expr.span, msg); - let supported_tys: Vec<_> = - supported_tys.iter().map(|(t, _)| t.to_string()).collect(); - err.note(&format!( - "register class `{}` supports these types: {}", - reg_class.name(), - supported_tys.join(", "), - )); - if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) { - err.help(&format!( - "consider using the `{}` register class instead", - suggest.name() - )); - } - err.emit(); - return Some(asm_ty); - }; - - // Check whether the selected type requires a target feature. Note that - // this is different from the feature check we did earlier. While the - // previous check checked that this register class is usable at all - // with the currently enabled features, some types may only be usable - // with a register class when a certain feature is enabled. We check - // this here since it depends on the results of typeck. - // - // Also note that this check isn't run when the operand type is never - // (!). In that case we still need the earlier check to verify that the - // register class is usable at all. - if let Some(feature) = feature { - if !target_features.contains(&feature) { - let msg = &format!("`{}` target feature is not enabled", feature); - let mut err = self.tcx.sess.struct_span_err(expr.span, msg); - err.note(&format!( - "this is required to use type `{}` with register class `{}`", - ty, - reg_class.name(), - )); - err.emit(); - return Some(asm_ty); - } - } - - // Check whether a modifier is suggested for using this type. - if let Some((suggested_modifier, suggested_result)) = - reg_class.suggest_modifier(asm_arch, asm_ty) - { - // Search for any use of this operand without a modifier and emit - // the suggestion for them. - let mut spans = vec![]; - for piece in template { - if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece - { - if operand_idx == idx && modifier.is_none() { - spans.push(span); - } - } - } - if !spans.is_empty() { - let (default_modifier, default_result) = - reg_class.default_modifier(asm_arch).unwrap(); - self.tcx.struct_span_lint_hir( - lint::builtin::ASM_SUB_REGISTER, - expr.hir_id, - spans, - |lint| { - let msg = "formatting may not be suitable for sub-register argument"; - let mut err = lint.build(msg); - err.span_label(expr.span, "for this argument"); - err.help(&format!( - "use the `{suggested_modifier}` modifier to have the register formatted as `{suggested_result}`", - )); - err.help(&format!( - "or use the `{default_modifier}` modifier to keep the default formatting of `{default_result}`", - )); - err.emit(); - }, - ); - } - } - - Some(asm_ty) - } - - pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) { - let hir = self.tcx.hir(); - let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id(); - let target_features = self.tcx.asm_target_features(enclosing_def_id); - let Some(asm_arch) = self.tcx.sess.asm_arch else { - self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm"); - return; - }; - for (idx, (op, op_sp)) in asm.operands.iter().enumerate() { - // Validate register classes against currently enabled target - // features. We check that at least one type is available for - // the enabled features. - // - // We ignore target feature requirements for clobbers: if the - // feature is disabled then the compiler doesn't care what we - // do with the registers. - // - // Note that this is only possible for explicit register - // operands, which cannot be used in the asm string. - if let Some(reg) = op.reg() { - // Some explicit registers cannot be used depending on the - // target. Reject those here. - if let InlineAsmRegOrRegClass::Reg(reg) = reg { - if let InlineAsmReg::Err = reg { - // `validate` will panic on `Err`, as an error must - // already have been reported. - continue; - } - if let Err(msg) = reg.validate( - asm_arch, - self.tcx.sess.relocation_model(), - &target_features, - &self.tcx.sess.target, - op.is_clobber(), - ) { - let msg = format!("cannot use register `{}`: {}", reg.name(), msg); - self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); - continue; - } - } - - if !op.is_clobber() { - let mut missing_required_features = vec![]; - let reg_class = reg.reg_class(); - if let InlineAsmRegClass::Err = reg_class { - continue; - } - for &(_, feature) in reg_class.supported_types(asm_arch) { - match feature { - Some(feature) => { - if target_features.contains(&feature) { - missing_required_features.clear(); - break; - } else { - missing_required_features.push(feature); - } - } - None => { - missing_required_features.clear(); - break; - } - } - } - - // We are sorting primitive strs here and can use unstable sort here - missing_required_features.sort_unstable(); - missing_required_features.dedup(); - match &missing_required_features[..] { - [] => {} - [feature] => { - let msg = format!( - "register class `{}` requires the `{}` target feature", - reg_class.name(), - feature - ); - self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); - // register isn't enabled, don't do more checks - continue; - } - features => { - let msg = format!( - "register class `{}` requires at least one of the following target features: {}", - reg_class.name(), - features - .iter() - .map(|f| f.as_str()) - .intersperse(", ") - .collect::<String>(), - ); - self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); - // register isn't enabled, don't do more checks - continue; - } - } - } - } - - match *op { - hir::InlineAsmOperand::In { reg, ref expr } => { - self.check_asm_operand_type( - idx, - reg, - expr, - asm.template, - true, - None, - &target_features, - ); - } - hir::InlineAsmOperand::Out { reg, late: _, ref expr } => { - if let Some(expr) = expr { - self.check_asm_operand_type( - idx, - reg, - expr, - asm.template, - false, - None, - &target_features, - ); - } - } - hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => { - self.check_asm_operand_type( - idx, - reg, - expr, - asm.template, - false, - None, - &target_features, - ); - } - hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => { - let in_ty = self.check_asm_operand_type( - idx, - reg, - in_expr, - asm.template, - true, - None, - &target_features, - ); - if let Some(out_expr) = out_expr { - self.check_asm_operand_type( - idx, - reg, - out_expr, - asm.template, - false, - Some((in_expr, in_ty)), - &target_features, - ); - } - } - // No special checking is needed for these: - // - Typeck has checked that Const operands are integers. - // - AST lowering guarantees that SymStatic points to a static. - hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {} - // Check that sym actually points to a function. Later passes - // depend on this. - hir::InlineAsmOperand::SymFn { anon_const } => { - let ty = self.tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); - match ty.kind() { - ty::Never | ty::Error(_) => {} - ty::FnDef(..) => {} - _ => { - let mut err = - self.tcx.sess.struct_span_err(*op_sp, "invalid `sym` operand"); - err.span_label( - self.tcx.hir().span(anon_const.body.hir_id), - &format!("is {} `{}`", ty.kind().article(), ty), - ); - err.help("`sym` operands must refer to either a function or a static"); - err.emit(); - } - }; - } - } - } - } -} diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs deleted file mode 100644 index 0e678c41f..000000000 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ /dev/null @@ -1,658 +0,0 @@ -//! 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::Subst; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{ - self, AssocKind, DefIdTree, GenericParamDefKind, ProjectionPredicate, ProjectionTy, Term, - 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, call_expr, self_expr))] - 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>> { - debug!( - "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", - segment.ident, self_ty, call_expr, self_expr - ); - - 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 opt_output_ty = - expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); - let opt_output_assoc_item = self.tcx.associated_items(trait_def_id).find_by_name_and_kind( - self.tcx, - Ident::from_str("Output"), - AssocKind::Type, - trait_def_id, - ); - let output_pred = - opt_output_ty.zip(opt_output_assoc_item).map(|(output_ty, output_assoc_item)| { - ty::Binder::dummy(ty::PredicateKind::Projection(ProjectionPredicate { - projection_ty: ProjectionTy { substs, item_def_id: output_assoc_item.def_id }, - term: Term::Ty(output_ty), - })) - .to_predicate(self.tcx) - }); - - ( - 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_pred, - }, - ), - 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, opt_input_types))] - 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>>> { - debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", - self_ty, m_name, trait_def_id, opt_input_types - ); - - 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_pred: None, - }, - ) - } else { - traits::ObligationCause::misc(span, self.body_id) - }; - obligations.extend(traits::predicates_for_generics(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))] - 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>> { - debug!( - "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}", - method_name, self_ty, expr_id, - ); - - 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!("resolve_fully_qualified_call: pick={:?}", 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!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id); - used_trait_imports.insert(import_id); - } - } - - let def_kind = pick.item.kind.as_def_kind(); - debug!( - "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}", - def_kind, pick.item.def_id - ); - 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() - } -} diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs deleted file mode 100644 index 17c2e4868..000000000 --- a/compiler/rustc_typeck/src/check/mod.rs +++ /dev/null @@ -1,970 +0,0 @@ -/*! - -# typeck: check phase - -Within the check phase of type check, we check each item one at a time -(bodies of function expressions are checked as part of the containing -function). Inference is used to supply types wherever they are unknown. - -By far the most complex case is checking the body of a function. This -can be broken down into several distinct phases: - -- gather: creates type variables to represent the type of each local - variable and pattern binding. - -- main: the main pass does the lion's share of the work: it - determines the types of all expressions, resolves - methods, checks for most invalid conditions, and so forth. In - some cases, where a type is unknown, it may create a type or region - variable and use that as the type of an expression. - - In the process of checking, various constraints will be placed on - these type variables through the subtyping relationships requested - through the `demand` module. The `infer` module is in charge - of resolving those constraints. - -- regionck: after main is complete, the regionck pass goes over all - types looking for regions and making sure that they did not escape - into places where they are not in scope. This may also influence the - final assignments of the various region variables if there is some - flexibility. - -- writeback: writes the final types within a function body, replacing - type variables with their final inferred types. These final types - are written into the `tcx.node_types` table, which should *never* contain - any reference to a type variable. - -## Intermediate types - -While type checking a function, the intermediate types for the -expressions, blocks, and so forth contained within the function are -stored in `fcx.node_types` and `fcx.node_substs`. These types -may contain unresolved type variables. After type checking is -complete, the functions in the writeback module are used to take the -types from this table, resolve them, and then write them into their -permanent home in the type context `tcx`. - -This means that during inferencing you should use `fcx.write_ty()` -and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of -nodes within the function. - -The types of top-level items, which never contain unbound type -variables, are stored directly into the `tcx` typeck_results. - -N.B., a type variable is not the same thing as a type parameter. A -type variable is an instance of a type parameter. That is, -given a generic function `fn foo<T>(t: T)`, while checking the -function `foo`, the type `ty_param(0)` refers to the type `T`, which -is treated in abstract. However, when `foo()` is called, `T` will be -substituted for a fresh type variable `N`. This variable will -eventually be resolved to some concrete type (which might itself be -a type parameter). - -*/ - -pub mod _match; -mod autoderef; -mod callee; -pub mod cast; -mod check; -mod closure; -pub mod coercion; -mod compare_method; -pub mod demand; -mod diverges; -pub mod dropck; -mod expectation; -mod expr; -mod fallback; -mod fn_ctxt; -mod gather_locals; -mod generator_interior; -mod inherited; -pub mod intrinsic; -mod intrinsicck; -pub mod method; -mod op; -mod pat; -mod place_op; -mod region; -pub mod regionck; -pub mod rvalue_scopes; -mod upvar; -pub mod wfcheck; -pub mod writeback; - -use check::{check_abi, check_fn, check_mod_item_types}; -pub use diverges::Diverges; -pub use expectation::Expectation; -pub use fn_ctxt::*; -use hir::def::CtorOf; -pub use inherited::{Inherited, InheritedBuilder}; - -use crate::astconv::AstConv; -use crate::check::gather_locals::GatherLocalsVisitor; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::Idx; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, UserType}; -use rustc_session::config; -use rustc_session::parse::feature_err; -use rustc_session::Session; -use rustc_span::source_map::DUMMY_SP; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, BytePos, Span}; -use rustc_target::abi::VariantIdx; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; -use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; -use std::cell::RefCell; - -use crate::require_c_abi_if_c_variadic; -use crate::util::common::indenter; - -use self::coercion::DynamicCoerceMany; -use self::region::region_scope_tree; -pub use self::Expectation::*; - -#[macro_export] -macro_rules! type_error_struct { - ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ - let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*); - - if $typ.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - }) -} - -/// The type of a local binding, including the revealed type for anon types. -#[derive(Copy, Clone, Debug)] -pub struct LocalTy<'tcx> { - decl_ty: Ty<'tcx>, - revealed_ty: Ty<'tcx>, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Needs { - MutPlace, - None, -} - -impl Needs { - fn maybe_mut_place(m: hir::Mutability) -> Self { - match m { - hir::Mutability::Mut => Needs::MutPlace, - hir::Mutability::Not => Needs::None, - } - } -} - -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, from_fn: true } - } - - pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => self, - - unsafety => { - let (unsafety, def) = match blk.rules { - BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id), - BlockCheckMode::DefaultBlock => (unsafety, self.def), - }; - UnsafetyState { def, unsafety, from_fn: false } - } - } - } -} - -#[derive(Debug, Copy, Clone)] -pub enum PlaceOp { - Deref, - Index, -} - -pub struct BreakableCtxt<'tcx> { - may_break: bool, - - // this is `null` for loops where break with a value is illegal, - // such as `while`, `for`, and `while let` - coerce: Option<DynamicCoerceMany<'tcx>>, -} - -pub struct EnclosingBreakables<'tcx> { - stack: Vec<BreakableCtxt<'tcx>>, - by_id: HirIdMap<usize>, -} - -impl<'tcx> EnclosingBreakables<'tcx> { - fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { - self.opt_find_breakable(target_id).unwrap_or_else(|| { - bug!("could not find enclosing breakable with id {}", target_id); - }) - } - - fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { - match self.by_id.get(&target_id) { - Some(ix) => Some(&mut self.stack[*ix]), - None => None, - } - } -} - -pub fn provide(providers: &mut Providers) { - method::provide(providers); - wfcheck::provide(providers); - *providers = Providers { - typeck_item_bodies, - typeck_const_arg, - typeck, - diagnostic_only_typeck, - has_typeck_results, - adt_destructor, - used_trait_imports, - check_mod_item_types, - region_scope_tree, - ..*providers - }; -} - -fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> { - tcx.calculate_dtor(def_id, dropck::check_drop_impl) -} - -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { - Some((body, Some(ty), None)) - } - hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(sig))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), - hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.has_typeck_results(typeck_root_def_id); - } - - if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() - } else { - false - } -} - -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> { - &*tcx.typeck(def_id).used_trait_imports -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); - typeck_with_fallback(tcx, did, fallback) -} - -fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); - typeck_with_fallback(tcx, def_id, fallback) - } -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); - tcx.ty_error_with_message(span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback) -} - -#[instrument(skip(tcx, fallback))] -fn typeck_with_fallback<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, -) -> &'tcx ty::TypeckResults<'tcx> { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); - if typeck_root_def_id != def_id { - return tcx.typeck(typeck_root_def_id); - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let span = tcx.hir().span(id); - - // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { - span_bug!(span, "can't type-check body of {:?}", def_id); - }); - let body = tcx.hir().body(body_id); - - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id) - }; - - check_abi(tcx, id, span, fn_sig.abi()); - - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 - } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = asm - .operands - .iter() - .filter_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }) - .next(); - operand_ty.unwrap_or_else(fallback) - } - _ => fallback(), - }, - _ => fallback(), - }); - - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - - fcx.write_ty(id, expected_type); - - fcx - }; - - let fallback_has_occurred = fcx.type_inference_fallback(); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - fcx.closure_analyze(body); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); - fcx.resolve_generator_interiors(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); - fcx.require_type_is_sized(ty, span, code); - } - - fcx.select_all_obligations_or_error(); - - if !fcx.infcx.is_tainted_by_errors() { - fcx.check_transmutes(); - } - - fcx.check_asms(); - - fcx.infcx.skip_region_resolution(); - - fcx.resolve_type_vars_in_body(body) - }); - - // Consistency check our TypeckResults instance can hold all ItemLocalIds - // it will need to hold. - assert_eq!(typeck_results.hir_owner, id.owner); - - typeck_results -} - -/// When `check_fn` is invoked on a generator (i.e., a body that -/// includes yield), it returns back some information about the yield -/// points. -struct GeneratorTypes<'tcx> { - /// Type of generator argument / values returned by `yield`. - resume_ty: Ty<'tcx>, - - /// Type of value that is yielded. - yield_ty: Ty<'tcx>, - - /// Types that are captured (see `GeneratorInterior` for more). - interior: Ty<'tcx>, - - /// Indicates if the generator is movable or static (immovable). - movability: hir::Movability, -} - -/// Given a `DefId` for an opaque type in return position, find its parent item's return -/// expressions. -fn get_owner_return_paths<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, -) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_id = tcx.hir().get_parent_item(hir_id); - tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| { - let body = tcx.hir().body(body_id); - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(body); - (parent_id, visitor) - }) -} - -// Forbid defining intrinsics in Rust code, -// as they must always be defined by the compiler. -fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { - tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); - } -} - -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { - // Only restricted on wasm target for now - if !tcx.sess.target.is_like_wasm { - return; - } - - // If `#[link_section]` is missing, then nothing to verify - let attrs = tcx.codegen_fn_attrs(id); - if attrs.link_section.is_none() { - return; - } - - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with pointers to anything else or relocations. If any relocation - // show up, reject them here. - // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is - // the consumer's responsibility to ensure all bytes that have been read - // have defined values. - if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) - && alloc.inner().relocations().len() != 0 - { - let msg = "statics with a custom `#[link_section]` must be a \ - simple list of bytes on the wasm target with no \ - extra levels of indirection such as references"; - tcx.sess.span_err(tcx.def_span(id), msg); - } -} - -fn report_forbidden_specialization( - tcx: TyCtxt<'_>, - impl_item: &hir::ImplItemRef, - parent_impl: DefId, -) { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0520, - "`{}` specializes an item from a parent `impl`, but \ - that item is not marked `default`", - impl_item.ident - ); - err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident)); - - match tcx.span_of_impl(parent_impl) { - Ok(span) => { - err.span_label(span, "parent `impl` is here"); - err.note(&format!( - "to specialize, `{}` in the parent `impl` must be marked `default`", - impl_item.ident - )); - } - Err(cname) => { - err.note(&format!("parent implementation is in crate `{cname}`")); - } - } - - err.emit(); -} - -fn missing_items_err( - tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[&ty::AssocItem], - full_impl_span: Span, -) { - let missing_items_msg = missing_items - .iter() - .map(|trait_item| trait_item.name.to_string()) - .collect::<Vec<_>>() - .join("`, `"); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing: `{missing_items_msg}`", - ); - err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation")); - - // `Span` before impl block closing brace. - let hi = full_impl_span.hi() - BytePos(1); - // Point at the place right before the closing brace of the relevant `impl` to suggest - // adding the associated item at the end of its body. - let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); - // Obtain the level of indentation ending in `sugg_sp`. - let padding = - tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); - - for trait_item in missing_items { - let snippet = suggestion_signature(trait_item, tcx); - let code = format!("{}{}\n{}", padding, snippet, padding); - let msg = format!("implement the missing item: `{snippet}`"); - let appl = Applicability::HasPlaceholders; - if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { - err.span_label(span, format!("`{}` from trait", trait_item.name)); - err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); - } else { - err.span_suggestion_hidden(sugg_sp, &msg, code, appl); - } - } - err.emit(); -} - -fn missing_items_must_implement_one_of_err( - tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[Ident], - annotation_span: Option<Span>, -) { - let missing_items_msg = - missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `"); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing one of: `{missing_items_msg}`", - ); - err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation")); - - if let Some(annotation_span) = annotation_span { - err.span_note(annotation_span, "required because of this annotation"); - } - - err.emit(); -} - -/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. -fn bounds_from_generic_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: ty::GenericPredicates<'tcx>, -) -> (String, String) { - let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default(); - let mut projections = vec![]; - for (predicate, _) in predicates.predicates { - debug!("predicate {:?}", predicate); - let bound_predicate = predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { - let entry = types.entry(trait_predicate.self_ty()).or_default(); - let def_id = trait_predicate.def_id(); - if Some(def_id) != tcx.lang_items().sized_trait() { - // Type params are `Sized` by default, do not add that restriction to the list - // if it is a positive requirement. - entry.push(trait_predicate.def_id()); - } - } - ty::PredicateKind::Projection(projection_pred) => { - projections.push(bound_predicate.rebind(projection_pred)); - } - _ => {} - } - } - let generics = if types.is_empty() { - "".to_string() - } else { - format!( - "<{}>", - types - .keys() - .filter_map(|t| match t.kind() { - ty::Param(_) => Some(t.to_string()), - // Avoid suggesting the following: - // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {} - _ => None, - }) - .collect::<Vec<_>>() - .join(", ") - ) - }; - let mut where_clauses = vec![]; - for (ty, bounds) in types { - where_clauses - .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound)))); - } - for projection in &projections { - let p = projection.skip_binder(); - // FIXME: this is not currently supported syntax, we should be looking at the `types` and - // insert the associated types where they correspond, but for now let's be "lazy" and - // propose this instead of the following valid resugaring: - // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>` - where_clauses.push(format!( - "{} = {}", - tcx.def_path_str(p.projection_ty.item_def_id), - p.term, - )); - } - let where_clauses = if where_clauses.is_empty() { - String::new() - } else { - format!(" where {}", where_clauses.join(", ")) - }; - (generics, where_clauses) -} - -/// Return placeholder code for the given function. -fn fn_sig_suggestion<'tcx>( - tcx: TyCtxt<'tcx>, - sig: ty::FnSig<'tcx>, - ident: Ident, - predicates: ty::GenericPredicates<'tcx>, - assoc: &ty::AssocItem, -) -> String { - let args = sig - .inputs() - .iter() - .enumerate() - .map(|(i, ty)| { - Some(match ty.kind() { - ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), - ty::Ref(reg, ref_ty, mutability) if i == 0 => { - let reg = format!("{reg} "); - let reg = match ®[..] { - "'_ " | " " => "", - reg => reg, - }; - if assoc.fn_has_self_parameter { - match ref_ty.kind() { - ty::Param(param) if param.name == kw::SelfUpper => { - format!("&{}{}self", reg, mutability.prefix_str()) - } - - _ => format!("self: {ty}"), - } - } else { - format!("_: {ty}") - } - } - _ => { - if assoc.fn_has_self_parameter && i == 0 { - format!("self: {ty}") - } else { - format!("_: {ty}") - } - } - }) - }) - .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) - .flatten() - .collect::<Vec<String>>() - .join(", "); - let output = sig.output(); - let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() }; - - let unsafety = sig.unsafety.prefix_str(); - let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); - - // FIXME: this is not entirely correct, as the lifetimes from borrowed params will - // not be present in the `fn` definition, not will we account for renamed - // lifetimes between the `impl` and the `trait`, but this should be good enough to - // fill in a significant portion of the missing code, and other subsequent - // suggestions can help the user fix the code. - format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}") -} - -/// Return placeholder code for the given associated item. -/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a -/// structured suggestion. -fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { - match assoc.kind { - ty::AssocKind::Fn => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - fn_sig_suggestion( - tcx, - tcx.fn_sig(assoc.def_id).skip_binder(), - assoc.ident(tcx), - tcx.predicates_of(assoc.def_id), - assoc, - ) - } - ty::AssocKind::Type => format!("type {} = Type;", assoc.name), - ty::AssocKind::Const => { - let ty = tcx.type_of(assoc.def_id); - let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); - format!("const {}: {} = {};", assoc.name, ty, val) - } - } -} - -/// Emit an error when encountering two or more variants in a transparent enum. -fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) { - let variant_spans: Vec<_> = adt - .variants() - .iter() - .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) - .collect(); - let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),); - let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}"); - err.span_label(sp, &msg); - if let [start @ .., end] = &*variant_spans { - for variant_span in start { - err.span_label(*variant_span, ""); - } - err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did))); - } - err.emit(); -} - -/// Emit an error when encountering two or more non-zero-sized fields in a transparent -/// enum. -fn bad_non_zero_sized_fields<'tcx>( - tcx: TyCtxt<'tcx>, - adt: ty::AdtDef<'tcx>, - field_count: usize, - field_spans: impl Iterator<Item = Span>, - sp: Span, -) { - let msg = format!("needs at most one non-zero-sized field, but has {field_count}"); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0690, - "{}transparent {} {}", - if adt.is_enum() { "the variant of a " } else { "" }, - adt.descr(), - msg, - ); - err.span_label(sp, &msg); - for sp in field_spans { - err.span_label(sp, "this field is non-zero-sized"); - } - err.emit(); -} - -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0533, - "expected unit struct, unit variant or constant, found {} `{}`", - res.descr(), - rustc_hir_pretty::qpath_to_string(qpath), - ) - .emit(); -} - -/// Controls whether the arguments are tupled. This is used for the call -/// operator. -/// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: -/// ``` -/// fn f(x: (isize, isize)) {} -/// ``` -/// Can be called as: -/// ```ignore UNSOLVED (can this be done in user code?) -/// # fn f(x: (isize, isize)) {} -/// f(1, 2); -/// ``` -/// Instead of: -/// ``` -/// # fn f(x: (isize, isize)) {} -/// f((1, 2)); -/// ``` -#[derive(Clone, Eq, PartialEq)] -enum TupleArgumentsFlag { - DontTupleArguments, - TupleArguments, -} - -fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { - tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); -} - -fn fatally_break_rust(sess: &Session) { - let handler = sess.diagnostic(); - handler.span_bug_no_panic( - MultiSpan::new(), - "It looks like you're trying to break rust; would you like some ICE?", - ); - handler.note_without_error("the compiler expectedly panicked. this is a feature."); - handler.note_without_error( - "we would appreciate a joke overview: \ - https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", - ); - handler.note_without_error(&format!( - "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), - config::host_triple(), - )); -} - -fn potentially_plural_count(count: usize, word: &str) -> String { - format!("{} {}{}", count, word, pluralize!(count)) -} - -fn has_expected_num_generic_args<'tcx>( - tcx: TyCtxt<'tcx>, - trait_did: Option<DefId>, - expected: usize, -) -> bool { - trait_did.map_or(true, |trait_did| { - let generics = tcx.generics_of(trait_did); - generics.count() == expected + if generics.has_self { 1 } else { 0 } - }) -} - -/// Suggests calling the constructor of a tuple struct or enum variant -/// -/// * `snippet` - The snippet of code that references the constructor -/// * `span` - The span of the snippet -/// * `params` - The number of parameters the constructor accepts -/// * `err` - A mutable diagnostic builder to add the suggestion to -fn suggest_call_constructor<G: EmissionGuarantee>( - span: Span, - kind: CtorOf, - params: usize, - err: &mut DiagnosticBuilder<'_, G>, -) { - // Note: tuple-structs don't have named fields, so just use placeholders - let args = vec!["_"; params].join(", "); - let applicable = if params > 0 { - Applicability::HasPlaceholders - } else { - // When n = 0, it's an empty-tuple struct/enum variant - // so we trivially know how to construct it - Applicability::MachineApplicable - }; - let kind = match kind { - CtorOf::Struct => "a struct", - CtorOf::Variant => "an enum variant", - }; - err.span_label(span, &format!("this is the constructor of {kind}")); - err.multipart_suggestion( - "call the constructor", - vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))], - applicable, - ); -} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs deleted file mode 100644 index 920b3e688..000000000 --- a/compiler/rustc_typeck/src/check/op.rs +++ /dev/null @@ -1,1076 +0,0 @@ -//! Code related to processing overloaded binary and unary operators. - -use super::method::MethodCallee; -use super::{has_expected_num_generic_args, FnCtxt}; -use crate::check::Expectation; -use rustc_ast as ast; -use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; -use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _; -use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; -use rustc_type_ir::sty::TyKind::*; - -use std::ops::ControlFlow; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Checks a `a <op>= b` - pub fn check_binop_assign( - &self, - expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - lhs: &'tcx hir::Expr<'tcx>, - rhs: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); - - let ty = - if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { - self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); - self.tcx.mk_unit() - } else { - return_ty - }; - - self.check_lhs_assignable(lhs, "E0067", op.span, |err| { - if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - if self - .lookup_op_method( - lhs_deref_ty, - Some(rhs_ty), - Some(rhs), - Op::Binary(op, IsAssign::Yes), - expected, - ) - .is_ok() - { - // Suppress this error, since we already emitted - // a deref suggestion in check_overloaded_binop - err.delay_as_bug(); - } - } - }); - - ty - } - - /// Checks a potentially overloaded binary operator. - pub fn check_binop( - &self, - expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - debug!( - "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})", - expr.hir_id, expr, op, lhs_expr, rhs_expr - ); - - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => { - // && and || are a simple case. - self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); - let lhs_diverges = self.diverges.get(); - self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); - - // Depending on the LHS' value, the RHS can never execute. - self.diverges.set(lhs_diverges); - - tcx.types.bool - } - _ => { - // Otherwise, we always treat operators as if they are - // overloaded. This is the way to be most flexible w/r/t - // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( - expr, - lhs_expr, - rhs_expr, - op, - IsAssign::No, - expected, - ); - - // Supply type inference hints if relevant. Probably these - // hints should be enforced during select as part of the - // `consider_unification_despite_ambiguity` routine, but this - // more convenient for now. - // - // The basic idea is to help type inference by taking - // advantage of things we know about how the impls for - // scalar types are arranged. This is important in a - // scenario like `1_u32 << 2`, because it lets us quickly - // deduce that the result type should be `u32`, even - // though we don't know yet what type 2 has and hence - // can't pin this down to a specific impl. - if !lhs_ty.is_ty_var() - && !rhs_ty.is_ty_var() - && is_builtin_binop(lhs_ty, rhs_ty, op) - { - let builtin_return_ty = self.enforce_builtin_binop_types( - lhs_expr.span, - lhs_ty, - rhs_expr.span, - rhs_ty, - op, - ); - self.demand_suptype(expr.span, builtin_return_ty, return_ty); - } - - return_ty - } - } - } - - fn enforce_builtin_binop_types( - &self, - lhs_span: Span, - lhs_ty: Ty<'tcx>, - rhs_span: Span, - rhs_ty: Ty<'tcx>, - op: hir::BinOp, - ) -> Ty<'tcx> { - debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); - - // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. - // (See https://github.com/rust-lang/rust/issues/57447.) - let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); - - let tcx = self.tcx; - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => { - self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); - self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); - tcx.types.bool - } - - BinOpCategory::Shift => { - // result type is same as LHS always - lhs_ty - } - - BinOpCategory::Math | BinOpCategory::Bitwise => { - // both LHS and RHS and result will have the same type - self.demand_suptype(rhs_span, lhs_ty, rhs_ty); - lhs_ty - } - - BinOpCategory::Comparison => { - // both LHS and RHS and result will have the same type - self.demand_suptype(rhs_span, lhs_ty, rhs_ty); - tcx.types.bool - } - } - } - - fn check_overloaded_binop( - &self, - expr: &'tcx hir::Expr<'tcx>, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, - expected: Expectation<'tcx>, - ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!( - "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", - expr.hir_id, op, is_assign - ); - - let lhs_ty = match is_assign { - IsAssign::No => { - // Find a suitable supertype of the LHS expression's type, by coercing to - // a type variable, to pass as the `Self` to the trait, avoiding invariant - // trait matching creating lifetime constraints that are too strict. - // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result - // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. - let lhs_ty = self.check_expr(lhs_expr); - let fresh_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: lhs_expr.span, - }); - self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) - } - IsAssign::Yes => { - // rust-lang/rust#52126: We have to use strict - // equivalence on the LHS of an assign-op like `+=`; - // overwritten or mutably-borrowed places cannot be - // coerced to a supertype. - self.check_expr(lhs_expr) - } - }; - let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); - - // N.B., as we have not yet type-checked the RHS, we don't have the - // type at hand. Make a variable to represent it. The whole reason - // for this indirection is so that, below, we can check the expr - // using this variable as the expected type, which sometimes lets - // us do better coercions than we would be able to do otherwise, - // particularly for things like `String + &String`. - let rhs_ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rhs_expr.span, - }); - - let result = self.lookup_op_method( - lhs_ty, - Some(rhs_ty_var), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ); - - // see `NB` above - let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); - let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); - - let return_ty = match result { - Ok(method) => { - let by_ref_binop = !op.node.is_by_value(); - if is_assign == IsAssign::Yes || by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[0], - }; - self.apply_adjustments(lhs_expr, vec![autoref]); - } - } - if by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[1], - }; - // HACK(eddyb) Bypass checks due to reborrows being in - // some cases applied on the RHS, on top of which we need - // to autoref, which is not allowed by apply_adjustments. - // self.apply_adjustments(rhs_expr, vec![autoref]); - self.typeck_results - .borrow_mut() - .adjustments_mut() - .entry(rhs_expr.hir_id) - .or_default() - .push(autoref); - } - } - self.write_method_call(expr.hir_id, method); - - method.sig.output() - } - // error types are considered "builtin" - Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), - Err(errors) => { - let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, use_output) = match is_assign { - IsAssign::Yes => { - let mut err = struct_span_err!( - self.tcx.sess, - expr.span, - E0368, - "binary assignment operation `{}=` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty, - ); - err.span_label( - lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), - ); - let missing_trait = match op.node { - hir::BinOpKind::Add => Some("std::ops::AddAssign"), - hir::BinOpKind::Sub => Some("std::ops::SubAssign"), - hir::BinOpKind::Mul => Some("std::ops::MulAssign"), - hir::BinOpKind::Div => Some("std::ops::DivAssign"), - hir::BinOpKind::Rem => Some("std::ops::RemAssign"), - hir::BinOpKind::BitAnd => Some("std::ops::BitAndAssign"), - hir::BinOpKind::BitXor => Some("std::ops::BitXorAssign"), - hir::BinOpKind::BitOr => Some("std::ops::BitOrAssign"), - hir::BinOpKind::Shl => Some("std::ops::ShlAssign"), - hir::BinOpKind::Shr => Some("std::ops::ShrAssign"), - _ => None, - }; - self.note_unmet_impls_on_type(&mut err, errors); - (err, missing_trait, false) - } - IsAssign::No => { - let (message, missing_trait, use_output) = match op.node { - hir::BinOpKind::Add => ( - format!("cannot add `{rhs_ty}` to `{lhs_ty}`"), - Some("std::ops::Add"), - true, - ), - hir::BinOpKind::Sub => ( - format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`"), - Some("std::ops::Sub"), - true, - ), - hir::BinOpKind::Mul => ( - format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Mul"), - true, - ), - hir::BinOpKind::Div => ( - format!("cannot divide `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Div"), - true, - ), - hir::BinOpKind::Rem => ( - format!("cannot mod `{lhs_ty}` by `{rhs_ty}`"), - Some("std::ops::Rem"), - true, - ), - hir::BinOpKind::BitAnd => ( - format!("no implementation for `{lhs_ty} & {rhs_ty}`"), - Some("std::ops::BitAnd"), - true, - ), - hir::BinOpKind::BitXor => ( - format!("no implementation for `{lhs_ty} ^ {rhs_ty}`"), - Some("std::ops::BitXor"), - true, - ), - hir::BinOpKind::BitOr => ( - format!("no implementation for `{lhs_ty} | {rhs_ty}`"), - Some("std::ops::BitOr"), - true, - ), - hir::BinOpKind::Shl => ( - format!("no implementation for `{lhs_ty} << {rhs_ty}`"), - Some("std::ops::Shl"), - true, - ), - hir::BinOpKind::Shr => ( - format!("no implementation for `{lhs_ty} >> {rhs_ty}`"), - Some("std::ops::Shr"), - true, - ), - hir::BinOpKind::Eq | hir::BinOpKind::Ne => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - Some("std::cmp::PartialEq"), - false, - ), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Gt - | hir::BinOpKind::Ge => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - Some("std::cmp::PartialOrd"), - false, - ), - _ => ( - format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - None, - false, - ), - }; - let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); - if !lhs_expr.span.eq(&rhs_expr.span) { - self.add_type_neq_err_label( - &mut err, - lhs_expr.span, - lhs_ty, - rhs_ty, - rhs_expr, - op, - is_assign, - expected, - ); - self.add_type_neq_err_label( - &mut err, - rhs_expr.span, - rhs_ty, - lhs_ty, - lhs_expr, - op, - is_assign, - expected, - ); - } - self.note_unmet_impls_on_type(&mut err, errors); - (err, missing_trait, use_output) - } - }; - - let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { - if self - .lookup_op_method( - lhs_deref_ty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) { - let msg = &format!( - "`{}{}` can be used on `{}`, you can dereference `{}`", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, - lhs_deref_ty.peel_refs(), - lstring, - ); - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_lo(), - msg, - "*", - rustc_errors::Applicability::MachineApplicable, - ); - } - } - }; - - // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest - // `a += b` => `*a += b` if a is a mut ref. - if is_assign == IsAssign::Yes - && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - suggest_deref_binop(lhs_deref_ty); - } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() { - if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) { - suggest_deref_binop(*lhs_deref_ty); - } - } - if let Some(missing_trait) = missing_trait { - let mut visitor = TypeParamVisitor(vec![]); - visitor.visit_ty(lhs_ty); - - if op.node == hir::BinOpKind::Add - && self.check_str_addition( - lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, - ) - { - // This has nothing here because it means we did string - // concatenation (e.g., "Hello " + "World!"). This means - // we don't want the note in the else clause to be emitted - } else if let [ty] = &visitor.0[..] { - // Look for a TraitPredicate in the Fulfillment errors, - // and use it to generate a suggestion. - // - // Note that lookup_op_method must be called again but - // with a specific rhs_ty instead of a placeholder so - // the resulting predicate generates a more specific - // suggestion for the user. - let errors = self - .lookup_op_method( - lhs_ty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ) - .unwrap_err(); - if !errors.is_empty() { - for error in errors { - if let Some(trait_pred) = - error.obligation.predicate.to_opt_poly_trait_pred() - { - let proj_pred = match error.obligation.cause.code() { - ObligationCauseCode::BinOp { - output_pred: Some(output_pred), - .. - } if use_output => { - output_pred.to_opt_poly_projection_pred() - } - _ => None, - }; - - self.suggest_restricting_param_bound( - &mut err, - trait_pred, - proj_pred, - self.body_id, - ); - } - } - } else if *ty != lhs_ty { - // When we know that a missing bound is responsible, we don't show - // this note as it is redundant. - err.note(&format!( - "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" - )); - } - } - } - err.emit(); - self.tcx.ty_error() - } - }; - - (lhs_ty, rhs_ty, return_ty) - } - - /// If one of the types is an uncalled function and calling it would yield the other type, - /// suggest calling the function. Returns `true` if suggestion would apply (even if not given). - fn add_type_neq_err_label( - &self, - err: &mut Diagnostic, - span: Span, - ty: Ty<'tcx>, - other_ty: Ty<'tcx>, - other_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, - expected: Expectation<'tcx>, - ) -> bool /* did we suggest to call a function because of missing parentheses? */ { - err.span_label(span, ty.to_string()); - if let FnDef(def_id, _) = *ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // FIXME: Instead of exiting early when encountering bound vars in - // the function signature, consider keeping the binder here and - // propagating it downwards. - let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else { - return false; - }; - - let other_ty = if let FnDef(def_id, _) = *other_ty.kind() { - if !self.tcx.has_typeck_results(def_id) { - return false; - } - // We're emitting a suggestion, so we can just ignore regions - self.tcx.fn_sig(def_id).skip_binder().output() - } else { - other_ty - }; - - if self - .lookup_op_method( - fn_sig.output(), - Some(other_ty), - Some(other_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() { - ("( /* arguments */ )", Applicability::HasPlaceholders) - } else { - ("()", Applicability::MaybeIncorrect) - }; - - err.span_suggestion_verbose( - span.shrink_to_hi(), - "you might have forgotten to call this function", - variable_snippet, - applicability, - ); - return true; - } - } - false - } - - /// Provide actionable suggestions when trying to add two strings with incorrect types, - /// like `&str + &str`, `String + String` and `&str + &String`. - /// - /// If this function returns `true` it means a note was printed, so we don't need - /// to print the normal "implementation of `std::ops::Add` might be missing" note - fn check_str_addition( - &self, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - lhs_ty: Ty<'tcx>, - rhs_ty: Ty<'tcx>, - err: &mut Diagnostic, - is_assign: IsAssign, - op: hir::BinOp, - ) -> bool { - let str_concat_note = "string concatenation requires an owned `String` on the left"; - let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; - let to_owned_msg = "create an owned `String` from a string reference"; - - let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def() - .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) - }; - - match (lhs_ty.kind(), rhs_ty.kind()) { - (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (*l_ty.kind() == Str || is_std_string(l_ty)) - && (*r_ty.kind() == Str - || is_std_string(r_ty) - || matches!( - r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str - )) => - { - if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` - err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); - err.note(str_concat_note); - if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { - err.span_suggestion_verbose( - lhs_expr.span.until(lhs_inner_expr.span), - rm_borrow_msg, - "", - Applicability::MachineApplicable - ); - } else { - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_hi(), - to_owned_msg, - ".to_owned()", - Applicability::MachineApplicable - ); - } - } - true - } - (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` - if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => - { - err.span_label( - op.span, - "`+` cannot be used to concatenate a `&str` with a `String`", - ); - match is_assign { - IsAssign::No => { - let sugg_msg; - let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { - sugg_msg = "remove the borrow on the left and add one on the right"; - (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned()) - } else { - sugg_msg = "create an owned `String` on the left and add a borrow on the right"; - (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned()) - }; - let suggestions = vec![ - lhs_sugg, - (rhs_expr.span.shrink_to_lo(), "&".to_owned()), - ]; - err.multipart_suggestion_verbose( - sugg_msg, - suggestions, - Applicability::MachineApplicable, - ); - } - IsAssign::Yes => { - err.note(str_concat_note); - } - } - true - } - _ => false, - } - } - - pub fn check_user_unop( - &self, - ex: &'tcx hir::Expr<'tcx>, - operand_ty: Ty<'tcx>, - op: hir::UnOp, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { - Ok(method) => { - self.write_method_call(ex.hir_id, method); - method.sig.output() - } - Err(errors) => { - let actual = self.resolve_vars_if_possible(operand_ty); - if !actual.references_error() { - let mut err = struct_span_err!( - self.tcx.sess, - ex.span, - E0600, - "cannot apply unary operator `{}` to type `{}`", - op.as_str(), - actual - ); - err.span_label( - ex.span, - format!("cannot apply unary operator `{}`", op.as_str()), - ); - - let mut visitor = TypeParamVisitor(vec![]); - visitor.visit_ty(operand_ty); - if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() { - let predicates = errors - .iter() - .filter_map(|error| { - error.obligation.predicate.to_opt_poly_trait_pred() - }); - for pred in predicates { - self.suggest_restricting_param_bound( - &mut err, - pred, - None, - self.body_id, - ); - } - } - - let sp = self.tcx.sess.source_map().start_point(ex.span); - if let Some(sp) = - self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) - { - // If the previous expression was a block expression, suggest parentheses - // (turning this into a binary subtraction operation instead.) - // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) - self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); - } else { - match actual.kind() { - Uint(_) if op == hir::UnOp::Neg => { - err.note("unsigned values cannot be negated"); - - if let hir::ExprKind::Unary( - _, - hir::Expr { - kind: - hir::ExprKind::Lit(Spanned { - node: ast::LitKind::Int(1, _), - .. - }), - .. - }, - ) = ex.kind - { - err.span_suggestion( - ex.span, - &format!( - "you may have meant the maximum value of `{actual}`", - ), - format!("{actual}::MAX"), - Applicability::MaybeIncorrect, - ); - } - } - Str | Never | Char | Tuple(_) | Array(_, _) => {} - Ref(_, lty, _) if *lty.kind() == Str => {} - _ => { - self.note_unmet_impls_on_type(&mut err, errors); - } - } - } - err.emit(); - } - self.tcx.ty_error() - } - } - } - - fn lookup_op_method( - &self, - lhs_ty: Ty<'tcx>, - other_ty: Option<Ty<'tcx>>, - other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - op: Op, - expected: Expectation<'tcx>, - ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { - let lang = self.tcx.lang_items(); - - let span = match op { - Op::Binary(op, _) => op.span, - Op::Unary(_, span) => span, - }; - let (opname, trait_did) = if let Op::Binary(op, IsAssign::Yes) = op { - match op.node { - hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), - hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), - hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), - hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), - hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), - hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), - hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), - hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), - hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), - hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::And - | hir::BinOpKind::Or => { - span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) - } - } - } else if let Op::Binary(op, IsAssign::No) = op { - match op.node { - hir::BinOpKind::Add => (sym::add, lang.add_trait()), - hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), - hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), - hir::BinOpKind::Div => (sym::div, lang.div_trait()), - hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), - hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), - hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), - hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), - hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), - hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), - hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), - hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), - hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), - hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), - hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), - hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), - hir::BinOpKind::And | hir::BinOpKind::Or => { - span_bug!(span, "&& and || are not overloadable") - } - } - } else if let Op::Unary(hir::UnOp::Not, _) = op { - (sym::not, lang.not_trait()) - } else if let Op::Unary(hir::UnOp::Neg, _) = op { - (sym::neg, lang.neg_trait()) - } else { - bug!("lookup_op_method: op not supported: {:?}", op) - }; - - debug!( - "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", - lhs_ty, op, opname, trait_did - ); - - // Catches cases like #83893, where a lang item is declared with the - // wrong number of generic arguments. Should have yielded an error - // elsewhere by now, but we have to catch it here so that we do not - // index `other_tys` out of bounds (if the lang item has too many - // generic arguments, `other_tys` is too short). - if !has_expected_num_generic_args( - self.tcx, - trait_did, - match op { - // Binary ops have a generic right-hand side, unary ops don't - Op::Binary(..) => 1, - Op::Unary(..) => 0, - }, - ) { - return Err(vec![]); - } - - let opname = Ident::with_dummy_span(opname); - let method = trait_did.and_then(|trait_did| { - self.lookup_op_method_in_trait( - span, - opname, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ) - }); - - match (method, trait_did) { - (Some(ok), _) => { - let method = self.register_infer_ok_obligations(ok); - self.select_obligations_where_possible(false, |_| {}); - Ok(method) - } - (None, None) => Err(vec![]), - (None, Some(trait_did)) => { - let (obligation, _) = self.obligation_for_op_method( - span, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ); - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - fulfill.register_predicate_obligation(self, obligation); - Err(fulfill.select_where_possible(&self.infcx)) - } - } - } -} - -// Binary operator categories. These categories summarize the behavior -// with respect to the builtin operations supported. -enum BinOpCategory { - /// &&, || -- cannot be overridden - Shortcircuit, - - /// <<, >> -- when shifting a single integer, rhs can be any - /// integer type. For simd, types must match. - Shift, - - /// +, -, etc -- takes equal types, produces same type as input, - /// applicable to ints/floats/simd - Math, - - /// &, |, ^ -- takes equal types, produces same type as input, - /// applicable to ints/floats/simd/bool - Bitwise, - - /// ==, !=, etc -- takes equal types, produces bools, except for simd, - /// which produce the input type - Comparison, -} - -impl BinOpCategory { - fn from(op: hir::BinOp) -> BinOpCategory { - match op.node { - hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, - - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul - | hir::BinOpKind::Div - | hir::BinOpKind::Rem => BinOpCategory::Math, - - hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { - BinOpCategory::Bitwise - } - - hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => BinOpCategory::Comparison, - - hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, - } - } -} - -/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) -#[derive(Clone, Copy, Debug, PartialEq)] -enum IsAssign { - No, - Yes, -} - -#[derive(Clone, Copy, Debug)] -enum Op { - Binary(hir::BinOp, IsAssign), - Unary(hir::UnOp, Span), -} - -/// Dereferences a single level of immutable referencing. -fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Ref(_, ty, hir::Mutability::Not) => *ty, - _ => ty, - } -} - -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be -/// overloaded to be legal. There are two reasons that we distinguish -/// builtin operations from overloaded ones (vs trying to drive -/// everything uniformly through the trait system and intrinsics or -/// something like that): -/// -/// 1. Builtin operations can trivially be evaluated in constants. -/// 2. For comparison operators applied to SIMD types the result is -/// not of type `bool`. For example, `i16x4 == i16x4` yields a -/// type like `i16x4`. This means that the overloaded trait -/// `PartialEq` is not applicable. -/// -/// Reason #2 is the killer. I tried for a while to always use -/// overloaded logic and just check the types in constants/codegen after -/// the fact, and it worked fine, except for SIMD types. -nmatsakis -fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { - // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. - // (See https://github.com/rust-lang/rust/issues/57447.) - let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => true, - - BinOpCategory::Shift => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - } - - BinOpCategory::Math => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - || lhs.is_floating_point() && rhs.is_floating_point() - } - - BinOpCategory::Bitwise => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - || lhs.is_floating_point() && rhs.is_floating_point() - || lhs.is_bool() && rhs.is_bool() - } - - BinOpCategory::Comparison => { - lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() - } - } -} - -struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>); - -impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Param(_) = ty.kind() { - self.0.push(ty); - } - ty.super_visit_with(self) - } -} - -struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); - -impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.0.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.1, - }), - _ => ty.super_fold_with(self), - } - } -} diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs deleted file mode 100644 index d49a6138f..000000000 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::outlives::outlives_bounds::InferCtxtExt as _; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::Ty; - -pub(crate) trait OutlivesEnvironmentExt<'tcx> { - fn add_implied_bounds( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - fn_sig_tys: FxHashSet<Ty<'tcx>>, - body_id: hir::HirId, - ); -} - -impl<'tcx> OutlivesEnvironmentExt<'tcx> for OutlivesEnvironment<'tcx> { - /// This method adds "implied bounds" into the outlives environment. - /// Implied bounds are outlives relationships that we can deduce - /// on the basis that certain types must be well-formed -- these are - /// either the types that appear in the function signature or else - /// the input types to an impl. For example, if you have a function - /// like - /// - /// ``` - /// fn foo<'a, 'b, T>(x: &'a &'b [T]) { } - /// ``` - /// - /// we can assume in the caller's body that `'b: 'a` and that `T: - /// 'b` (and hence, transitively, that `T: 'a`). This method would - /// add those assumptions into the outlives-environment. - /// - /// Tests: `src/test/ui/regions/regions-free-region-ordering-*.rs` - #[instrument(level = "debug", skip(self, infcx))] - fn add_implied_bounds<'a>( - &mut self, - infcx: &InferCtxt<'a, 'tcx>, - fn_sig_tys: FxHashSet<Ty<'tcx>>, - body_id: hir::HirId, - ) { - for ty in fn_sig_tys { - let ty = infcx.resolve_vars_if_possible(ty); - let implied_bounds = infcx.implied_outlives_bounds(self.param_env, body_id, ty); - self.add_outlives_bounds(Some(infcx), implied_bounds) - } - } -} diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs deleted file mode 100644 index 4a3cfa1ca..000000000 --- a/compiler/rustc_typeck/src/check_unused.rs +++ /dev/null @@ -1,196 +0,0 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::TyCtxt; -use rustc_session::lint; -use rustc_span::{Span, Symbol}; - -pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports: FxHashSet<LocalDefId> = FxHashSet::default(); - - for item_def_id in tcx.hir().body_owners() { - let imports = tcx.used_trait_imports(item_def_id); - debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); - used_trait_imports.extend(imports.iter()); - } - - for &id in tcx.maybe_unused_trait_imports(()) { - debug_assert_eq!(tcx.def_kind(id), DefKind::Use); - if tcx.visibility(id).is_public() { - continue; - } - if used_trait_imports.contains(&id) { - continue; - } - let item = tcx.hir().expect_item(id); - if item.span.is_dummy() { - continue; - } - let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() }; - tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { - let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_owned() - }; - lint.build(&msg).emit(); - }); - } - - unused_crates_lint(tcx); -} - -fn unused_crates_lint(tcx: TyCtxt<'_>) { - let lint = lint::builtin::UNUSED_EXTERN_CRATES; - - // Collect first the crates that are completely unused. These we - // can always suggest removing (no matter which edition we are - // in). - let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx - .maybe_unused_extern_crates(()) - .iter() - .filter(|&&(def_id, _)| { - // The `def_id` here actually was calculated during resolution (at least - // at the time of this writing) and is being shipped to us via a side - // channel of the tcx. There may have been extra expansion phases, - // however, which ended up removing the `def_id` *after* expansion. - // - // As a result we need to verify that `def_id` is indeed still valid for - // our AST and actually present in the HIR map. If it's not there then - // there's safely nothing to warn about, and otherwise we carry on with - // our execution. - // - // Note that if we carry through to the `extern_mod_stmt_cnum` query - // below it'll cause a panic because `def_id` is actually bogus at this - // point in time otherwise. - if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() { - return false; - } - true - }) - .filter(|&&(def_id, _)| { - tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { - !tcx.is_compiler_builtins(cnum) - && !tcx.is_panic_runtime(cnum) - && !tcx.has_global_allocator(cnum) - && !tcx.has_panic_handler(cnum) - }) - }) - .cloned() - .collect(); - - // Collect all the extern crates (in a reliable order). - let mut crates_to_lint = vec![]; - - for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) { - let item = tcx.hir().item(id); - if let hir::ItemKind::ExternCrate(orig_name) = item.kind { - crates_to_lint.push(ExternCrateToLint { - def_id: item.def_id.to_def_id(), - span: item.span, - orig_name, - warn_if_unused: !item.ident.as_str().starts_with('_'), - }); - } - } - } - - let extern_prelude = &tcx.resolutions(()).extern_prelude; - - for extern_crate in &crates_to_lint { - let def_id = extern_crate.def_id.expect_local(); - let item = tcx.hir().expect_item(def_id); - - // If the crate is fully unused, we suggest removing it altogether. - // We do this in any edition. - if extern_crate.warn_if_unused { - if let Some(&span) = unused_extern_crates.get(&def_id) { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - tcx.struct_span_lint_hir(lint, id, span, |lint| { - // Removal suggestion span needs to include attributes (Issue #54400) - let span_with_attrs = tcx - .hir() - .attrs(id) - .iter() - .map(|attr| attr.span) - .fold(span, |acc, attr_span| acc.to(attr_span)); - - lint.build("unused extern crate") - .span_suggestion_short( - span_with_attrs, - "remove it", - "", - Applicability::MachineApplicable, - ) - .emit(); - }); - continue; - } - } - - // If we are not in Rust 2018 edition, then we don't make any further - // suggestions. - if !tcx.sess.rust_2018() { - continue; - } - - // If the extern crate isn't in the extern prelude, - // there is no way it can be written as a `use`. - let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); - if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { - continue; - } - - // If the extern crate is renamed, then we cannot suggest replacing it with a use as this - // would not insert the new name into the prelude, where other imports in the crate may be - // expecting it. - if extern_crate.orig_name.is_some() { - continue; - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - // If the extern crate has any attributes, they may have funky - // semantics we can't faithfully represent using `use` (most - // notably `#[macro_use]`). Ignore it. - if !tcx.hir().attrs(id).is_empty() { - continue; - } - tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { - // Otherwise, we can convert it into a `use` of some kind. - let base_replacement = match extern_crate.orig_name { - Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), - None => format!("use {};", item.ident.name), - }; - let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default(); - let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; - lint.build("`extern crate` is not idiomatic in the new edition") - .span_suggestion_short( - extern_crate.span, - &format!("convert it to a `{}`", add_vis("use".to_string())), - add_vis(base_replacement), - Applicability::MachineApplicable, - ) - .emit(); - }) - } -} - -struct ExternCrateToLint { - /// `DefId` of the extern crate - def_id: DefId, - - /// span from the item - span: Span, - - /// if `Some`, then this is renamed (`extern crate orig_name as - /// crate_name`), and -- perhaps surprisingly -- this stores the - /// *original* name (`item.name` will contain the new name) - orig_name: Option<Symbol>, - - /// if `false`, the original name started with `_`, so we shouldn't lint - /// about it going unused (but we should still emit idiom lints). - warn_if_unused: bool, -} |