diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 0adf4f898ab6d..e21155002b0f2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2579,6 +2579,9 @@ pub enum TyPatKind { /// A range pattern (e.g., `1...2`, `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, Spanned), + /// A `!null` pattern for raw pointers. + NotNull, + Or(ThinVec), /// Placeholder for a pattern that wasn't syntactically well formed in some way. diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 03aeb064d7f11..7e4fa840f2523 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -143,7 +143,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } // return inner to be processed in next loop PatKind::Paren(inner) => pattern = inner, - PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span), + PatKind::MacCall(_) => { + panic!("{pattern:#?} shouldn't exist here") + } PatKind::Err(guar) => break hir::PatKind::Err(*guar), } }; @@ -460,6 +462,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) }), ), + TyPatKind::NotNull => hir::TyPatKind::NotNull, TyPatKind::Or(variants) => { hir::TyPatKind::Or(self.arena.alloc_from_iter( variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 41b520b04c993..a5e2bcaa3bd06 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1232,6 +1232,7 @@ impl<'a> State<'a> { self.print_expr_anon_const(end, &[]); } } + rustc_ast::TyPatKind::NotNull => self.word("!null"), rustc_ast::TyPatKind::Or(variants) => { let mut first = true; for pat in variants { diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 8b64bdf02d3b7..87a5a440140e0 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -30,15 +30,21 @@ fn parse_pat_ty<'a>( let ty = parser.parse_ty()?; parser.expect_keyword(exp!(Is))?; - let pat = pat_to_ty_pat( - cx, - parser.parse_pat_no_top_guard( - None, - RecoverComma::No, - RecoverColon::No, - CommaRecoveryMode::EitherTupleOrPipe, - )?, - ); + let start = parser.token.span; + let pat = if parser.eat(exp!(Bang)) { + parser.expect_keyword(exp!(Null))?; + ty_pat(TyPatKind::NotNull, start.to(parser.token.span)) + } else { + pat_to_ty_pat( + cx, + parser.parse_pat_no_top_guard( + None, + RecoverComma::No, + RecoverColon::No, + CommaRecoveryMode::EitherTupleOrPipe, + )?, + ) + }; if parser.token != token::Eof { parser.unexpected()?; diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index d994f3e32ec3c..c97eb3874b02c 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -131,6 +131,11 @@ pub(crate) fn coerce_unsized_into<'tcx>( dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); }; match (&src_ty.kind(), &dst_ty.kind()) { + (ty::Pat(a, _), ty::Pat(b, _)) => { + let src = src.cast_pat_ty_to_base(fx.layout_of(*a)); + let dst = dst.place_transmute_type(fx, *b); + return coerce_unsized_into(fx, src, dst); + } (&ty::Ref(..), &ty::Ref(..)) | (&ty::Ref(..), &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(), diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index db9b80c0f6a07..9dcd4a33d44f6 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> { assert_eq!(self.layout().backend_repr, layout.backend_repr); CValue(self.0, layout) } + + pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self { + let ty::Pat(base, _) = *self.layout().ty.kind() else { + panic!("not a pattern type: {:#?}", self.layout()) + }; + assert_eq!(layout.ty, base); + CValue(self.0, layout) + } } /// A place where you can write a value to or read a value from diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ecb1750ddfd34..c35b05f798eac 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -228,6 +228,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> (Bx::Value, Bx::Value) { debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty); match (src_ty.kind(), dst_ty.kind()) { + (&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info), (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _)) | (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index b058d4b8ad446..cf5ee03bedae8 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -466,6 +466,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty); match (src.layout.ty.kind(), cast_ty.ty.kind()) { + (&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => { + let src = self.project_field(src, FieldIdx::ZERO)?; + let dest = self.project_field(dest, FieldIdx::ZERO)?; + let cast_ty = self.layout_of(cast_ty)?; + self.unsize_into(&src, cast_ty, &dest) + } (&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _)) | (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index c62a23eb0b34b..89a3303eb3902 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1261,9 +1261,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, // When you extend this match, make sure to also add tests to // tests/ui/type/pattern_types/validity.rs(( match **pat { - // Range patterns are precisely reflected into `valid_range` and thus + // Range and non-null patterns are precisely reflected into `valid_range` and thus // handled fully by `visit_scalar` (called below). ty::PatternKind::Range { .. } => {}, + ty::PatternKind::NotNull => {}, // FIXME(pattern_types): check that the value is covered by one of the variants. // For now, we rely on layout computation setting the scalar's `valid_range` to diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 394747f81581e..90bf82f15f819 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1854,6 +1854,9 @@ pub enum TyPatKind<'hir> { /// A range pattern (e.g., `1..=2` or `1..2`). Range(&'hir ConstArg<'hir>, &'hir ConstArg<'hir>), + /// A pattern that excludes null pointers + NotNull, + /// A list of patterns where only one needs to be satisfied Or(&'hir [TyPat<'hir>]), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index eb682f32111a5..dde71e499c47a 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -725,7 +725,7 @@ pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) try_visit!(visitor.visit_const_arg_unambig(upper_bound)); } TyPatKind::Or(patterns) => walk_list!(visitor, visit_pattern_type_pattern, patterns), - TyPatKind::Err(_) => (), + TyPatKind::NotNull | TyPatKind::Err(_) => (), } V::Result::output() } diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 6add9baa1520f..a313b91ef9bda 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -103,6 +103,8 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout +hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other + hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}` .label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 6dee1675b6dee..61562cc1e4f30 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -243,6 +243,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() // in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent) // even if they do not carry that attribute. match (source.kind(), target.kind()) { + (&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => { + if pat_a != pat_b { + return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind { + span, + trait_name, + pat_a: pat_a.to_string(), + pat_b: pat_b.to_string(), + })); + } + Ok(()) + } + (&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b)) if r_a == *r_b && mutbl_a == *mutbl_b => { @@ -408,6 +420,18 @@ pub(crate) fn coerce_unsized_info<'tcx>( (mt_a.ty, mt_b.ty, unsize_trait, None, span) }; let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) { + (&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => { + if pat_a != pat_b { + return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind { + span, + trait_name, + pat_a: pat_a.to_string(), + pat_b: pat_b.to_string(), + })); + } + (ty_a, ty_b, coerce_unsized_trait, None, span) + } + (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a); let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 27682d0cc95b8..69921893fce40 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -206,12 +206,8 @@ pub(crate) fn orphan_check_impl( (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther) } - ty::Pat(..) => ( - LocalImpl::Disallow { problematic_kind: "pattern type" }, - NonlocalImpl::DisallowOther, - ), - ty::Bool + | ty::Pat(..) | ty::Char | ty::Int(..) | ty::Uint(..) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 49c5106422881..d1eb328c0e766 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1258,6 +1258,16 @@ pub(crate) struct CoerceUnsizedNonStruct { pub trait_name: &'static str, } +#[derive(Diagnostic)] +#[diag(hir_analysis_coerce_same_pat_kind)] +pub(crate) struct CoerceSamePatKind { + #[primary_span] + pub span: Span, + pub trait_name: &'static str, + pub pat_a: String, + pub pat_b: String, +} + #[derive(Diagnostic)] #[diag(hir_analysis_coerce_unsized_may, code = E0377)] pub(crate) struct CoerceSameStruct { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 58bd8e3bbbf3b..00f9095757806 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2611,6 +2611,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .span_delayed_bug(ty_span, "invalid base type for range pattern")), } } + hir::TyPatKind::NotNull => Ok(ty::PatternKind::NotNull), hir::TyPatKind::Or(patterns) => { self.tcx() .mk_patterns_from_iter(patterns.iter().map(|pat| { diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index be841675821c1..ce4668736b570 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -340,6 +340,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_const(current, start, variance); self.add_constraints_from_const(current, end, variance); } + ty::PatternKind::NotNull => {} ty::PatternKind::Or(patterns) => { for pat in patterns { self.add_constraints_from_pat(current, variance, pat) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index b9d8eed54a9e3..ed5f61b3c69ab 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1888,6 +1888,10 @@ impl<'a> State<'a> { self.word("..="); self.print_const_arg(end); } + TyPatKind::NotNull => { + self.word_space("not"); + self.word("null"); + } TyPatKind::Or(patterns) => { self.popen(); let mut first = true; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 709a8b74d0b7a..5bb717a2a8281 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1953,7 +1953,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]); + self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]); } } } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 35be28f7f7b81..bf829c2d645d4 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -26,6 +26,7 @@ use tracing::{debug, instrument}; pub(crate) use self::MethodError::*; use self::probe::{IsSuggestion, ProbeScope}; use crate::FnCtxt; +use crate::method::probe::UnsatisfiedPredicates; #[derive(Clone, Copy, Debug)] pub(crate) struct MethodCallee<'tcx> { @@ -71,8 +72,7 @@ pub(crate) enum MethodError<'tcx> { #[derive(Debug)] pub(crate) struct NoMatchData<'tcx> { pub static_candidates: Vec, - pub unsatisfied_predicates: - Vec<(ty::Predicate<'tcx>, Option>, Option>)>, + pub unsatisfied_predicates: UnsatisfiedPredicates<'tcx>, pub out_of_scope_traits: Vec, pub similar_candidate: Option, pub mode: probe::Mode, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 14043be65f623..a3f6803c5dc2c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -165,13 +165,12 @@ struct PickDiagHints<'a, 'tcx> { /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used /// for error reporting - unsatisfied_predicates: &'a mut Vec<( - ty::Predicate<'tcx>, - Option>, - Option>, - )>, + unsatisfied_predicates: &'a mut UnsatisfiedPredicates<'tcx>, } +pub(crate) type UnsatisfiedPredicates<'tcx> = + Vec<(ty::Predicate<'tcx>, Option>, Option>)>; + /// Criteria to apply when searching for a given Pick. This is used during /// the search for potentially shadowed methods to ensure we don't search /// more candidates than strictly necessary. @@ -1212,11 +1211,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_core( &self, - unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option>, - Option>, - )>, + unsatisfied_predicates: &mut UnsatisfiedPredicates<'tcx>, ) -> Option> { // Pick stable methods only first, and consider unstable candidates if not found. self.pick_all_method(&mut PickDiagHints { @@ -1889,11 +1884,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self_ty: Ty<'tcx>, instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], probe: &Candidate<'tcx>, - possibly_unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option>, - Option>, - )>, + possibly_unsatisfied_predicates: &mut UnsatisfiedPredicates<'tcx>, ) -> ProbeResult { self.probe(|snapshot| { let outer_universe = self.universe(); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 7a211dbb14292..fb5ba3d33328e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -38,13 +38,14 @@ use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplem use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ - FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, supertraits, + FulfillmentError, Obligation, ObligationCauseCode, supertraits, }; use tracing::{debug, info, instrument}; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; use crate::errors::{self, CandidateTraitNote, NoAssociatedItem}; +use crate::method::probe::UnsatisfiedPredicates; use crate::{Expectation, FnCtxt}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -58,11 +59,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, span: Span, - unsatisfied_predicates: &Vec<( - ty::Predicate<'tcx>, - Option>, - Option>, - )>, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) -> bool { fn predicate_bounds_generic_param<'tcx>( predicate: ty::Predicate<'_>, @@ -374,7 +371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn suggest_missing_writer( + fn create_missing_writer_err( &self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>, @@ -401,6 +398,87 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err } + fn create_no_assoc_err( + &self, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + item_kind: &'static str, + trait_missing_method: bool, + source: SelfSource<'tcx>, + is_method: bool, + sugg_span: Span, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, + ) -> Diag<'_> { + // Don't show expanded generic arguments when the method can't be found in any + // implementation (#81576). + let mut ty = rcvr_ty; + let span = item_ident.span; + if let ty::Adt(def, generics) = rcvr_ty.kind() { + if generics.len() > 0 { + let mut autoderef = self.autoderef(span, rcvr_ty).silence_errors(); + let candidate_found = autoderef.any(|(ty, _)| { + if let ty::Adt(adt_def, _) = ty.kind() { + self.tcx + .inherent_impls(adt_def.did()) + .into_iter() + .any(|def_id| self.associated_value(*def_id, item_ident).is_some()) + } else { + false + } + }); + let has_deref = autoderef.step_count() > 0; + if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { + ty = self.tcx.at(span).type_of(def.did()).instantiate_identity(); + } + } + } + + let mut err = self.dcx().create_err(NoAssociatedItem { + span, + item_kind, + item_ident, + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty, + trait_missing_method, + }); + + if is_method { + self.suggest_use_shadowed_binding_with_method(source, item_ident, rcvr_ty, &mut err); + } + + let tcx = self.tcx; + // Check if we wrote `Self::Assoc(1)` as if it were a tuple ctor. + if let SelfSource::QPath(ty) = source + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res + && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) + && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( + self.tcx, + item_ident, + ty::AssocTag::Type, + impl_def_id, + ) + && let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def() + && adt_def.is_struct() + && adt_def.non_enum_variant().ctor_kind() == Some(CtorKind::Fn) + { + let def_path = tcx.def_path_str(adt_def.did()); + err.span_suggestion( + sugg_span, + format!("to construct a value of type `{}`, use the explicit path", def_path), + def_path, + Applicability::MachineApplicable, + ); + } + + err + } + fn suggest_use_shadowed_binding_with_method( &self, self_source: SelfSource<'tcx>, @@ -645,103 +723,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_ident.name == sym::write_fmt; let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { - self.suggest_missing_writer(rcvr_ty, rcvr_expr, ty_file) + self.create_missing_writer_err(rcvr_ty, rcvr_expr, ty_file) } else { - // Don't show expanded generic arguments when the method can't be found in any - // implementation (#81576). - let mut ty = rcvr_ty; - if let ty::Adt(def, generics) = rcvr_ty.kind() { - if generics.len() > 0 { - let mut autoderef = self.autoderef(span, rcvr_ty).silence_errors(); - let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_def, _) = ty.kind() { - self.tcx - .inherent_impls(adt_def.did()) - .into_iter() - .any(|def_id| self.associated_value(*def_id, item_ident).is_some()) - } else { - false - } - }); - let has_deref = autoderef.step_count() > 0; - if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { - ty = self.tcx.at(span).type_of(def.did()).instantiate_identity(); - } - } - } - - let mut err = self.dcx().create_err(NoAssociatedItem { - span, - item_kind, + self.create_no_assoc_err( + rcvr_ty, item_ident, - ty_prefix: if trait_missing_method { - // FIXME(mu001999) E0599 maybe not suitable here because it is for types - Cow::from("trait") - } else { - rcvr_ty.prefix_string(self.tcx) - }, - ty, + item_kind, trait_missing_method, - }); - - if is_method { - self.suggest_use_shadowed_binding_with_method( - source, item_ident, rcvr_ty, &mut err, - ); - } - - // Check if we wrote `Self::Assoc(1)` as if it were a tuple ctor. - if let SelfSource::QPath(ty) = source - && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::SelfTyAlias { alias_to: impl_def_id, .. } = path.res - && let DefKind::Impl { .. } = self.tcx.def_kind(impl_def_id) - && let Some(candidate) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( - self.tcx, - item_ident, - ty::AssocTag::Type, - impl_def_id, - ) - && let Some(adt_def) = tcx.type_of(candidate.def_id).skip_binder().ty_adt_def() - && adt_def.is_struct() - && adt_def.non_enum_variant().ctor_kind() == Some(CtorKind::Fn) - { - let def_path = tcx.def_path_str(adt_def.did()); - err.span_suggestion( - sugg_span, - format!("to construct a value of type `{}`, use the explicit path", def_path), - def_path, - Applicability::MachineApplicable, - ); - } - - err + source, + is_method, + sugg_span, + unsatisfied_predicates, + ) }; - - if tcx.sess.source_map().is_multiline(sugg_span) { - err.span_label(sugg_span.with_hi(span.lo()), ""); - } - if let Some(within_macro_span) = within_macro_span { - err.span_label(within_macro_span, "due to this macro variable"); - } - if rcvr_ty.references_error() { err.downgrade_to_delayed_bug(); } - if matches!(source, SelfSource::QPath(_)) && args.is_some() { - self.find_builder_fn(&mut err, rcvr_ty, expr_id); - } - - if tcx.ty_is_opaque_future(rcvr_ty) && item_ident.name == sym::poll { - let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); - err.help(format!( - "method `poll` found on `Pin<&mut {ty_str}>`, \ - see documentation for `std::pin::Pin`" - )); - err.help("self type must be pinned to call `Future::poll`, \ - see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" - ); - } + self.set_label_for_method_error( + &mut err, + source, + rcvr_ty, + item_ident, + expr_id, + span, + sugg_span, + within_macro_span, + args, + ); if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source @@ -755,63 +764,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected.only_has_type(self), ); } - if let Some(span) = - tcx.resolutions(()).confused_type_with_std_module.get(&span.with_parent(None)) - { - err.span_suggestion( - span.shrink_to_lo(), - "you are looking for the module in `std`, not the primitive type", - "std::", - Applicability::MachineApplicable, - ); - } - - // on pointers, check if the method would exist on a reference - if let SelfSource::MethodCall(rcvr_expr) = source - && let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind() - && let Ok(pick) = self.lookup_probe_for_diagnostic( - item_ident, - Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl), - self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), - ProbeScope::TraitsInScope, - None, - ) - && let ty::Ref(_, _, sugg_mutbl) = *pick.self_ty.kind() - && (sugg_mutbl.is_not() || ptr_mutbl.is_mut()) - { - let (method, method_anchor) = match sugg_mutbl { - Mutability::Not => { - let method_anchor = match ptr_mutbl { - Mutability::Not => "as_ref", - Mutability::Mut => "as_ref-1", - }; - ("as_ref", method_anchor) - } - Mutability::Mut => ("as_mut", "as_mut"), - }; - err.span_note( - tcx.def_span(pick.item.def_id), - format!("the method `{item_ident}` exists on the type `{ty}`", ty = pick.self_ty), - ); - let mut_str = ptr_mutbl.ptr_str(); - err.note(format!( - "you might want to use the unsafe method `<*{mut_str} T>::{method}` to get \ - an optional reference to the value behind the pointer" - )); - err.note(format!( - "read the documentation for `<*{mut_str} T>::{method}` and ensure you satisfy its \ - safety preconditions before calling it to avoid undefined behavior: \ - https://doc.rust-lang.org/std/primitive.pointer.html#method.{method_anchor}" - )); - } - let mut ty_span = match rcvr_ty.kind() { - ty::Param(param_type) => { - Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id())) - } - ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), - _ => None, - }; + self.suggest_on_pointer_type(&mut err, source, rcvr_ty, item_ident); if let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { @@ -834,8 +788,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut custom_span_label = false; - - let static_candidates = &mut no_match_data.static_candidates; + let mut static_candidates = no_match_data.static_candidates.clone(); // `static_candidates` may have same candidates appended by // inherent and extension, which may result in incorrect @@ -853,7 +806,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if static_candidates.len() == 1 { self.suggest_associated_call_syntax( &mut err, - static_candidates, + &static_candidates, rcvr_ty, source, item_ident, @@ -867,7 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, span, &mut err, - static_candidates, + &mut static_candidates, None, ); } else if static_candidates.len() > 1 { @@ -878,7 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, span, &mut err, - static_candidates, + &mut static_candidates, Some(sugg_span), ); } @@ -887,6 +840,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut restrict_type_params = false; let mut suggested_derive = false; let mut unsatisfied_bounds = false; + let mut ty_span = match rcvr_ty.kind() { + ty::Param(param_type) => { + Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id())) + } + ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), + _ => None, + }; + if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) { let msg = "consider using `len` instead"; if let SelfSource::MethodCall(_expr) = source { @@ -911,590 +872,144 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } return err.emit(); - } else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) { - // We special case the situation where we are looking for `_` in - // `::method` because otherwise the machinery will look for blanket - // implementations that have unsatisfied trait bounds to suggest, leading us to claim - // things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord` - // have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so - // that `impl Ord for T` can apply", which is not what we want. We have a type - // parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict - // `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling - // `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates` - // suggestions. } else if !unsatisfied_predicates.is_empty() { - let mut type_params = FxIndexMap::default(); - - // Pick out the list of unimplemented traits on the receiver. - // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. - let mut unimplemented_traits = FxIndexMap::default(); - let mut unimplemented_traits_only = true; - for (predicate, _parent_pred, cause) in unsatisfied_predicates { - if let (ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)), Some(cause)) = - (predicate.kind().skip_binder(), cause.as_ref()) - { - if p.trait_ref.self_ty() != rcvr_ty { - // This is necessary, not just to keep the errors clean, but also - // because our derived obligations can wind up with a trait ref that - // requires a different param_env to be correctly compared. - continue; - } - unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( - predicate.kind().rebind(p), - Obligation { - cause: cause.clone(), - param_env: self.param_env, - predicate: *predicate, - recursion_depth: 0, - }, - )); - } + if matches!(rcvr_ty.kind(), ty::Param(_)) { + // We special case the situation where we are looking for `_` in + // `::method` because otherwise the machinery will look for blanket + // implementations that have unsatisfied trait bounds to suggest, leading us to claim + // things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord` + // have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so + // that `impl Ord for T` can apply", which is not what we want. We have a type + // parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict + // `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling + // `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates` + // suggestions. + } else { + self.handle_unsatisfied_predicates( + &mut err, + rcvr_ty, + item_ident, + item_kind, + span, + unsatisfied_predicates, + &mut restrict_type_params, + &mut suggested_derive, + &mut unsatisfied_bounds, + &mut custom_span_label, + &mut bound_spans, + ); } - - // Make sure that, if any traits other than the found ones were involved, - // we don't report an unimplemented trait. - // We don't want to say that `iter::Cloned` is not an iterator, just - // because of some non-Clone item being iterated over. - for (predicate, _parent_pred, _cause) in unsatisfied_predicates { - match predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) - if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} - _ => { - unimplemented_traits_only = false; - break; + } else if let ty::Adt(def, targs) = rcvr_ty.kind() + && let SelfSource::MethodCall(rcvr_expr) = source + { + // This is useful for methods on arbitrary self types that might have a simple + // mutability difference, like calling a method on `Pin<&mut Self>` that is on + // `Pin<&Self>`. + if targs.len() == 1 { + let mut item_segment = hir::PathSegment::invalid(); + item_segment.ident = item_ident; + for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] { + let new_args = + tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() { + Some(ty) => ty::GenericArg::from(t( + tcx, + tcx.lifetimes.re_erased, + ty.peel_refs(), + )), + _ => arg, + })); + let rcvr_ty = Ty::new_adt(tcx, *def, new_args); + if let Ok(method) = self.lookup_method_for_diagnostic( + rcvr_ty, + &item_segment, + span, + tcx.parent_hir_node(rcvr_expr.hir_id).expect_expr(), + rcvr_expr, + ) { + err.span_note( + tcx.def_span(method.def_id), + format!("{item_kind} is available for `{rcvr_ty}`"), + ); } } } + } - let mut collect_type_param_suggestions = - |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { - // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateKind::Clause(ty::ClauseKind::Trait(p))) = - (self_ty.kind(), parent_pred.kind().skip_binder()) - { - let node = match p.trait_ref.self_ty().kind() { - ty::Param(_) => { - // Account for `fn` items like in `issue-35677.rs` to - // suggest restricting its type params. - Some(self.tcx.hir_node_by_def_id(self.body_id)) - } - ty::Adt(def, _) => def - .did() - .as_local() - .map(|def_id| self.tcx.hir_node_by_def_id(def_id)), - _ => None, - }; - if let Some(hir::Node::Item(hir::Item { kind, .. })) = node - && let Some(g) = kind.generics() - { - let key = ( - g.tail_span_for_predicate_suggestion(), - g.add_where_or_trailing_comma(), - ); - type_params - .entry(key) - .or_insert_with(UnordSet::default) - .insert(obligation.to_owned()); - return true; - } - } - false - }; - let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { - let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); - match self_ty.kind() { - // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => { - bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) - } - // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _) => { - for pred in preds.iter() { - match pred.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => { - bound_spans - .get_mut_or_insert_default(tcx.def_span(tr.def_id)) - .push(msg.clone()); - } - ty::ExistentialPredicate::Projection(_) - | ty::ExistentialPredicate::AutoTrait(_) => {} - } - } - } - // Point at the closure that couldn't satisfy the bound. - ty::Closure(def_id, _) => { - bound_spans - .get_mut_or_insert_default(tcx.def_span(*def_id)) - .push(format!("`{quiet}`")); - } - _ => {} - } - }; - let mut format_pred = |pred: ty::Predicate<'tcx>| { - let bound_predicate = pred.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { - let pred = bound_predicate.rebind(pred); - // `::Item = String`. - let projection_term = pred.skip_binder().projection_term; - let quiet_projection_term = projection_term - .with_replaced_self_ty(tcx, Ty::new_var(tcx, ty::TyVid::ZERO)); - - let term = pred.skip_binder().term; - - let obligation = format!("{projection_term} = {term}"); - let quiet = with_forced_trimmed_paths!(format!( - "{} = {}", - quiet_projection_term, term - )); + let mut find_candidate_for_method = false; + let should_label_not_found = match source { + // If the method name is the name of a field with a function or closure type, + // give a helping note that it has to be called as `(x.f)(...)`. + SelfSource::MethodCall(expr) => { + !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_ident, &mut err) + && similar_candidate.is_none() + } + _ => true, + }; - bound_span_label(projection_term.self_ty(), &obligation, &quiet); - Some((obligation, projection_term.self_ty())) - } - ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => { - let p = poly_trait_ref.trait_ref; - let self_ty = p.self_ty(); - let path = p.print_only_trait_path(); - let obligation = format!("{self_ty}: {path}"); - let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); - bound_span_label(self_ty, &obligation, &quiet); - Some((obligation, self_ty)) - } - _ => None, - } - }; + if should_label_not_found && !custom_span_label { + self.set_not_found_span_label( + &mut err, + rcvr_ty, + item_ident, + item_kind, + mode, + source, + span, + unsatisfied_predicates, + &mut find_candidate_for_method, + ); + } + if !find_candidate_for_method { + self.lookup_segments_chain_for_no_match_method( + &mut err, + item_ident, + item_kind, + source, + no_match_data, + ); + } - // Find all the requirements that come from a local `impl` block. - let mut skip_list: UnordSet<_> = Default::default(); - let mut spanned_predicates = FxIndexMap::default(); - for (p, parent_p, cause) in unsatisfied_predicates { - // Extract the predicate span and parent def id of the cause, - // if we have one. - let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { - Some(ObligationCauseCode::ImplDerived(data)) => { - (data.impl_or_alias_def_id, data.span) - } - Some( - ObligationCauseCode::WhereClauseInExpr(def_id, span, _, _) - | ObligationCauseCode::WhereClause(def_id, span), - ) if !span.is_dummy() => (*def_id, *span), - _ => continue, - }; - - // Don't point out the span of `WellFormed` predicates. - if !matches!( - p.kind().skip_binder(), - ty::PredicateKind::Clause( - ty::ClauseKind::Projection(..) | ty::ClauseKind::Trait(..) - ) - ) { - continue; - } - - match self.tcx.hir_get_if_local(item_def_id) { - // Unmet obligation comes from a `derive` macro, point at it once to - // avoid multiple span labels pointing at the same place. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), - .. - })) if matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) || matches!( - of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { - let span = self_ty.span.ctxt().outer_expn_data().call_site; - let entry = spanned_predicates.entry(span); - let entry = entry.or_insert_with(|| { - (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) - }); - entry.0.insert(span); - entry.1.insert(( - span, - "unsatisfied trait bound introduced in this `derive` macro", - )); - entry.2.push(p); - skip_list.insert(p); - } - - // Unmet obligation coming from an `impl`. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), - span: item_span, - .. - })) => { - let sized_pred = - unsatisfied_predicates.iter().any(|(pred, _, _)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) - && pred.polarity == ty::PredicatePolarity::Positive - } - _ => false, - } - }); - for param in generics.params { - if param.span == cause_span && sized_pred { - let (sp, sugg) = match param.colon_span { - Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), - None => (param.span.shrink_to_hi(), ": ?Sized"), - }; - err.span_suggestion_verbose( - sp, - "consider relaxing the type parameter's implicit `Sized` bound", - sugg, - Applicability::MachineApplicable, - ); - } - } - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let entry = spanned_predicates.entry(self_ty.span); - let entry = entry.or_insert_with(|| { - (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) - }); - entry.2.push(p); - if cause_span != *item_span { - entry.0.insert(cause_span); - entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); - } else { - if let Some(of_trait) = of_trait { - entry.0.insert(of_trait.trait_ref.path.span); - } - entry.0.insert(self_ty.span); - }; - if let Some(of_trait) = of_trait { - entry.1.insert((of_trait.trait_ref.path.span, "")); - } - entry.1.insert((self_ty.span, "")); - } - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, rustc_ast::ast::IsAuto::Yes, ..), - span: item_span, - .. - })) => { - self.dcx().span_delayed_bug( - *item_span, - "auto trait is invoked with no method error, but no error reported?", - ); - } - Some( - Node::Item(hir::Item { - kind: - hir::ItemKind::Trait(_, _, _, ident, ..) - | hir::ItemKind::TraitAlias(ident, ..), - .. - }) - // We may also encounter unsatisfied GAT or method bounds - | Node::TraitItem(hir::TraitItem { ident, .. }) - | Node::ImplItem(hir::ImplItem { ident, .. }) - ) => { - skip_list.insert(p); - let entry = spanned_predicates.entry(ident.span); - let entry = entry.or_insert_with(|| { - (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) - }); - entry.0.insert(cause_span); - entry.1.insert((ident.span, "")); - entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); - entry.2.push(p); - } - _ => { - // It's possible to use well-formedness clauses to get obligations - // which point arbitrary items like ADTs, so there's no use in ICEing - // here if we find that the obligation originates from some other - // node that we don't handle. - } - } - } - let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); - spanned_predicates.sort_by_key(|(span, _)| *span); - for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { - let mut preds: Vec<_> = predicates - .iter() - .filter_map(|pred| format_pred(**pred)) - .map(|(p, _)| format!("`{p}`")) - .collect(); - preds.sort(); - preds.dedup(); - let msg = if let [pred] = &preds[..] { - format!("trait bound {pred} was not satisfied") - } else { - format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) - }; - let mut span: MultiSpan = primary_spans.into_iter().collect::>().into(); - for (sp, label) in span_labels { - span.push_span_label(sp, label); - } - err.span_note(span, msg); - unsatisfied_bounds = true; - } - - let mut suggested_bounds = UnordSet::default(); - // The requirements that didn't have an `impl` span to show. - let mut bound_list = unsatisfied_predicates - .iter() - .filter_map(|(pred, parent_pred, _cause)| { - let mut suggested = false; - format_pred(*pred).map(|(p, self_ty)| { - if let Some(parent) = parent_pred - && suggested_bounds.contains(parent) - { - // We don't suggest `PartialEq` when we already suggest `Eq`. - } else if !suggested_bounds.contains(pred) - && collect_type_param_suggestions(self_ty, *pred, &p) - { - suggested = true; - suggested_bounds.insert(pred); - } - ( - match parent_pred { - None => format!("`{p}`"), - Some(parent_pred) => match format_pred(*parent_pred) { - None => format!("`{p}`"), - Some((parent_p, _)) => { - if !suggested - && !suggested_bounds.contains(pred) - && !suggested_bounds.contains(parent_pred) - && collect_type_param_suggestions( - self_ty, - *parent_pred, - &p, - ) - { - suggested_bounds.insert(pred); - } - format!("`{p}`\nwhich is required by `{parent_p}`") - } - }, - }, - *pred, - ) - }) - }) - .filter(|(_, pred)| !skip_list.contains(&pred)) - .map(|(t, _)| t) - .enumerate() - .collect::>(); - - if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) { - for ((span, add_where_or_comma), obligations) in type_params.into_iter() { - restrict_type_params = true; - // #74886: Sort here so that the output is always the same. - let obligations = obligations.into_sorted_stable_ord(); - err.span_suggestion_verbose( - span, - format!( - "consider restricting the type parameter{s} to satisfy the trait \ - bound{s}", - s = pluralize!(obligations.len()) - ), - format!("{} {}", add_where_or_comma, obligations.join(", ")), - Applicability::MaybeIncorrect, - ); - } - } - - bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. - bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 - bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. - - if !bound_list.is_empty() || !skip_list.is_empty() { - let bound_list = - bound_list.into_iter().map(|(_, path)| path).collect::>().join("\n"); - let actual_prefix = rcvr_ty.prefix_string(self.tcx); - info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let (primary_message, label, notes) = if unimplemented_traits.len() == 1 - && unimplemented_traits_only - { - unimplemented_traits - .into_iter() - .next() - .map(|(_, (trait_ref, obligation))| { - if trait_ref.self_ty().references_error() || rcvr_ty.references_error() - { - // Avoid crashing. - return (None, None, Vec::new()); - } - let OnUnimplementedNote { message, label, notes, .. } = self - .err_ctxt() - .on_unimplemented_note(trait_ref, &obligation, err.long_ty_path()); - (message, label, notes) - }) - .unwrap() - } else { - (None, None, Vec::new()) - }; - let primary_message = primary_message.unwrap_or_else(|| { - let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); - format!( - "the {item_kind} `{item_ident}` exists for {actual_prefix} `{ty_str}`, \ - but its trait bounds were not satisfied" - ) - }); - err.primary_message(primary_message); - if let Some(label) = label { - custom_span_label = true; - err.span_label(span, label); - } - if !bound_list.is_empty() { - err.note(format!( - "the following trait bounds were not satisfied:\n{bound_list}" - )); - } - for note in notes { - err.note(note); - } - - suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates); - - unsatisfied_bounds = true; - } - } else if let ty::Adt(def, targs) = rcvr_ty.kind() - && let SelfSource::MethodCall(rcvr_expr) = source - { - // This is useful for methods on arbitrary self types that might have a simple - // mutability difference, like calling a method on `Pin<&mut Self>` that is on - // `Pin<&Self>`. - if targs.len() == 1 { - let mut item_segment = hir::PathSegment::invalid(); - item_segment.ident = item_ident; - for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] { - let new_args = - tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() { - Some(ty) => ty::GenericArg::from(t( - tcx, - tcx.lifetimes.re_erased, - ty.peel_refs(), - )), - _ => arg, - })); - let rcvr_ty = Ty::new_adt(tcx, *def, new_args); - if let Ok(method) = self.lookup_method_for_diagnostic( - rcvr_ty, - &item_segment, - span, - tcx.parent_hir_node(rcvr_expr.hir_id).expect_expr(), - rcvr_expr, - ) { - err.span_note( - tcx.def_span(method.def_id), - format!("{item_kind} is available for `{rcvr_ty}`"), - ); - } - } - } + // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` + // can't be called due to `typeof(expr): Clone` not holding. + if unsatisfied_predicates.is_empty() { + self.suggest_calling_method_on_field( + &mut err, + source, + span, + rcvr_ty, + item_ident, + expected.only_has_type(self), + ); } - let mut find_candidate_for_method = false; - - let mut label_span_not_found = |err: &mut Diag<'_>| { - let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); - if unsatisfied_predicates.is_empty() { - err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); - let is_string_or_ref_str = match rcvr_ty.kind() { - ty::Ref(_, ty, _) => { - ty.is_str() - || matches!( - ty.kind(), - ty::Adt(adt, _) if self.tcx.is_lang_item(adt.did(), LangItem::String) - ) - } - ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String), - _ => false, - }; - if is_string_or_ref_str && item_ident.name == sym::iter { - err.span_suggestion_verbose( - item_ident.span, - "because of the in-memory representation of `&str`, to obtain \ - an `Iterator` over each of its codepoint use method `chars`", - "chars", - Applicability::MachineApplicable, - ); - } - if let ty::Adt(adt, _) = rcvr_ty.kind() { - let mut inherent_impls_candidate = self - .tcx - .inherent_impls(adt.did()) - .into_iter() - .copied() - .filter(|def_id| { - if let Some(assoc) = self.associated_value(*def_id, item_ident) { - // Check for both mode is the same so we avoid suggesting - // incorrect associated item. - match (mode, assoc.is_method(), source) { - (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { - // We check that the suggest type is actually - // different from the received one - // So we avoid suggestion method with Box - // for instance - self.tcx.at(span).type_of(*def_id).instantiate_identity() - != rcvr_ty - } - (Mode::Path, false, _) => true, - _ => false, - } - } else { - false - } - }) - .collect::>(); - if !inherent_impls_candidate.is_empty() { - inherent_impls_candidate.sort_by_key(|id| self.tcx.def_path_str(id)); - inherent_impls_candidate.dedup(); - - // number of types to show at most - let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; - let type_candidates = inherent_impls_candidate - .iter() - .take(limit) - .map(|impl_item| { - format!( - "- `{}`", - self.tcx.at(span).type_of(*impl_item).instantiate_identity() - ) - }) - .collect::>() - .join("\n"); - let additional_types = if inherent_impls_candidate.len() > limit { - format!("\nand {} more types", inherent_impls_candidate.len() - limit) - } else { - "".to_string() - }; - err.note(format!( - "the {item_kind} was found for\n{type_candidates}{additional_types}" - )); - find_candidate_for_method = mode == Mode::MethodCall; - } - } - } else { - let ty_str = - if ty_str.len() > 50 { String::new() } else { format!("on `{ty_str}` ") }; - err.span_label( - span, - format!("{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"), - ); - } - }; + self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_ident); - // If the method name is the name of a field with a function or closure type, - // give a helping note that it has to be called as `(x.f)(...)`. - if let SelfSource::MethodCall(expr) = source { - if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_ident, &mut err) - && similar_candidate.is_none() - && !custom_span_label - { - label_span_not_found(&mut err); - } - } else if !custom_span_label { - label_span_not_found(&mut err); + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive { + // skip suggesting traits to import + } else { + self.suggest_traits_to_import( + &mut err, + span, + rcvr_ty, + item_ident, + args.map(|args| args.len() + 1), + source, + no_match_data.out_of_scope_traits.clone(), + &static_candidates, + unsatisfied_bounds, + expected.only_has_type(self), + trait_missing_method, + ); } + self.suggest_enum_variant_for_method_call( + &mut err, + rcvr_ty, + item_ident, + span, + source, + unsatisfied_predicates, + ); let confusable_suggested = self.confusable_method_name( &mut err, rcvr_ty, @@ -1507,22 +1022,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() }), ); - - // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` - // can't be called due to `typeof(expr): Clone` not holding. - if unsatisfied_predicates.is_empty() { - self.suggest_calling_method_on_field( - &mut err, - source, - span, - rcvr_ty, - item_ident, - expected.only_has_type(self), - ); + if let Some(similar_candidate) = similar_candidate { + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() + // ...or if we already suggested that name because of `rustc_confusable` annotation + && Some(similar_candidate.name()) != confusable_suggested + // and if we aren't in an expansion. + && !span.from_expansion() + { + self.find_likely_intended_associated_item( + &mut err, + similar_candidate, + span, + args, + mode, + ); + } } - self.suggest_unwrapping_inner_self(&mut err, source, rcvr_ty, item_ident); - for (span, mut bounds) in bound_spans { if !tcx.sess.source_map().is_span_accessible(span) { continue; @@ -1558,181 +1076,684 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive { + self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); + err.emit() + } + + fn set_not_found_span_label( + &self, + err: &mut Diag<'_>, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + item_kind: &str, + mode: Mode, + source: SelfSource<'tcx>, + span: Span, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, + find_candidate_for_method: &mut bool, + ) { + let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); + if unsatisfied_predicates.is_empty() { + err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); + let is_string_or_ref_str = match rcvr_ty.kind() { + ty::Ref(_, ty, _) => { + ty.is_str() + || matches!( + ty.kind(), + ty::Adt(adt, _) if self.tcx.is_lang_item(adt.did(), LangItem::String) + ) + } + ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String), + _ => false, + }; + if is_string_or_ref_str && item_ident.name == sym::iter { + err.span_suggestion_verbose( + item_ident.span, + "because of the in-memory representation of `&str`, to obtain \ + an `Iterator` over each of its codepoint use method `chars`", + "chars", + Applicability::MachineApplicable, + ); + } + if let ty::Adt(adt, _) = rcvr_ty.kind() { + let mut inherent_impls_candidate = self + .tcx + .inherent_impls(adt.did()) + .into_iter() + .copied() + .filter(|def_id| { + if let Some(assoc) = self.associated_value(*def_id, item_ident) { + // Check for both mode is the same so we avoid suggesting + // incorrect associated item. + match (mode, assoc.is_method(), source) { + (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { + // We check that the suggest type is actually + // different from the received one + // So we avoid suggestion method with Box + // for instance + self.tcx.at(span).type_of(*def_id).instantiate_identity() + != rcvr_ty + } + (Mode::Path, false, _) => true, + _ => false, + } + } else { + false + } + }) + .collect::>(); + if !inherent_impls_candidate.is_empty() { + inherent_impls_candidate.sort_by_key(|id| self.tcx.def_path_str(id)); + inherent_impls_candidate.dedup(); + + // number of types to show at most + let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; + let type_candidates = inherent_impls_candidate + .iter() + .take(limit) + .map(|impl_item| { + format!( + "- `{}`", + self.tcx.at(span).type_of(*impl_item).instantiate_identity() + ) + }) + .collect::>() + .join("\n"); + let additional_types = if inherent_impls_candidate.len() > limit { + format!("\nand {} more types", inherent_impls_candidate.len() - limit) + } else { + "".to_string() + }; + err.note(format!( + "the {item_kind} was found for\n{type_candidates}{additional_types}" + )); + *find_candidate_for_method = mode == Mode::MethodCall; + } + } } else { - self.suggest_traits_to_import( - &mut err, + let ty_str = if ty_str.len() > 50 { String::new() } else { format!("on `{ty_str}` ") }; + err.span_label( span, - rcvr_ty, - item_ident, - args.map(|args| args.len() + 1), - source, - no_match_data.out_of_scope_traits.clone(), - static_candidates, - unsatisfied_bounds, - expected.only_has_type(self), - trait_missing_method, + format!("{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"), ); } + } + + /// Suggest similar enum variant when method call fails + fn suggest_enum_variant_for_method_call( + &self, + err: &mut Diag<'_>, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + span: Span, + source: SelfSource<'tcx>, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, + ) { + // Don't emit a suggestion if we found an actual method that had unsatisfied trait bounds + if !unsatisfied_predicates.is_empty() || !rcvr_ty.is_enum() { + return; + } + + let tcx = self.tcx; + let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(var_name) = edit_distance::find_best_match_for_name( + &adt_def.variants().iter().map(|s| s.name).collect::>(), + item_ident.name, + None, + ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) + { + let mut suggestion = vec![(span, var_name.to_string())]; + if let SelfSource::QPath(ty) = source + && let hir::Node::Expr(ref path_expr) = tcx.parent_hir_node(ty.hir_id) + && let hir::ExprKind::Path(_) = path_expr.kind + && let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(parent), .. }) + | hir::Node::Expr(parent) = tcx.parent_hir_node(path_expr.hir_id) + { + let replacement_span = + if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind { + // We want to replace the parts that need to go, like `()` and `{}`. + span.with_hi(parent.span.hi()) + } else { + span + }; + match (variant.ctor, parent.kind) { + (None, hir::ExprKind::Struct(..)) => { + // We want a struct and we have a struct. We won't suggest changing + // the fields (at least for now). + suggestion = vec![(span, var_name.to_string())]; + } + (None, _) => { + // struct + suggestion = vec![( + replacement_span, + if variant.fields.is_empty() { + format!("{var_name} {{}}") + } else { + format!( + "{var_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + (Some((hir::def::CtorKind::Const, _)), _) => { + // unit, remove the `()`. + suggestion = vec![(replacement_span, var_name.to_string())]; + } + (Some((hir::def::CtorKind::Fn, def_id)), hir::ExprKind::Call(rcvr, args)) => { + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + // FIXME: reuse the logic for "change args" suggestion to account for types + // involved and detect things like substitution. + match (inputs, args) { + (inputs, []) => { + // Add arguments. + suggestion.push(( + rcvr.span.shrink_to_hi().with_hi(parent.span.hi()), + format!( + "({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )); + } + (_, [arg]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg.span, + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + (_, [arg_start, .., arg_end]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg_start.span.to(arg_end.span), + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + // Argument count is the same, keep as is. + _ => {} + } + } + (Some((hir::def::CtorKind::Fn, def_id)), _) => { + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + replacement_span, + format!( + "{var_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + } + } + err.multipart_suggestion_verbose( + "there is a variant with a similar name", + suggestion, + Applicability::HasPlaceholders, + ); + } + } + + fn handle_unsatisfied_predicates( + &self, + err: &mut Diag<'_>, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + item_kind: &str, + span: Span, + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, + restrict_type_params: &mut bool, + suggested_derive: &mut bool, + unsatisfied_bounds: &mut bool, + custom_span_label: &mut bool, + bound_spans: &mut SortedMap>, + ) { + let tcx = self.tcx; + let mut type_params = FxIndexMap::default(); - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { - let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(var_name) = edit_distance::find_best_match_for_name( - &adt_def.variants().iter().map(|s| s.name).collect::>(), - item_ident.name, - None, - ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) + // Pick out the list of unimplemented traits on the receiver. + // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. + let mut unimplemented_traits = FxIndexMap::default(); + + let mut unimplemented_traits_only = true; + for (predicate, _parent_pred, cause) in unsatisfied_predicates { + if let (ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)), Some(cause)) = + (predicate.kind().skip_binder(), cause.as_ref()) { - let mut suggestion = vec![(span, var_name.to_string())]; - if let SelfSource::QPath(ty) = source - && let hir::Node::Expr(ref path_expr) = self.tcx.parent_hir_node(ty.hir_id) - && let hir::ExprKind::Path(_) = path_expr.kind - && let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(parent), .. }) - | hir::Node::Expr(parent) = self.tcx.parent_hir_node(path_expr.hir_id) + if p.trait_ref.self_ty() != rcvr_ty { + // This is necessary, not just to keep the errors clean, but also + // because our derived obligations can wind up with a trait ref that + // requires a different param_env to be correctly compared. + continue; + } + unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( + predicate.kind().rebind(p), + Obligation { + cause: cause.clone(), + param_env: self.param_env, + predicate: *predicate, + recursion_depth: 0, + }, + )); + } + } + + // Make sure that, if any traits other than the found ones were involved, + // we don't report an unimplemented trait. + // We don't want to say that `iter::Cloned` is not an iterator, just + // because of some non-Clone item being iterated over. + for (predicate, _parent_pred, _cause) in unsatisfied_predicates { + match predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) + if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} + _ => { + unimplemented_traits_only = false; + break; + } + } + } + + let mut collect_type_param_suggestions = + |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { + // We don't care about regions here, so it's fine to skip the binder here. + if let (ty::Param(_), ty::PredicateKind::Clause(ty::ClauseKind::Trait(p))) = + (self_ty.kind(), parent_pred.kind().skip_binder()) { - let replacement_span = - if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind { - // We want to replace the parts that need to go, like `()` and `{}`. - span.with_hi(parent.span.hi()) - } else { - span - }; - match (variant.ctor, parent.kind) { - (None, hir::ExprKind::Struct(..)) => { - // We want a struct and we have a struct. We won't suggest changing - // the fields (at least for now). - suggestion = vec![(span, var_name.to_string())]; + let node = match p.trait_ref.self_ty().kind() { + ty::Param(_) => { + // Account for `fn` items like in `issue-35677.rs` to + // suggest restricting its type params. + Some(self.tcx.hir_node_by_def_id(self.body_id)) } - (None, _) => { - // struct - suggestion = vec![( - replacement_span, - if variant.fields.is_empty() { - format!("{var_name} {{}}") - } else { - format!( - "{var_name} {{ {} }}", - variant - .fields - .iter() - .map(|f| format!("{}: /* value */", f.name)) - .collect::>() - .join(", ") - ) - }, - )]; + ty::Adt(def, _) => { + def.did().as_local().map(|def_id| self.tcx.hir_node_by_def_id(def_id)) } - (Some((hir::def::CtorKind::Const, _)), _) => { - // unit, remove the `()`. - suggestion = vec![(replacement_span, var_name.to_string())]; + _ => None, + }; + if let Some(hir::Node::Item(hir::Item { kind, .. })) = node + && let Some(g) = kind.generics() + { + let key = ( + g.tail_span_for_predicate_suggestion(), + g.add_where_or_trailing_comma(), + ); + type_params + .entry(key) + .or_insert_with(UnordSet::default) + .insert(obligation.to_owned()); + return true; + } + } + false + }; + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!("`{}`", if obligation.len() > 50 { quiet } else { obligation }); + match self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => { + bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) + } + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => { + bound_spans + .get_mut_or_insert_default(tcx.def_span(tr.def_id)) + .push(msg.clone()); + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} } - ( - Some((hir::def::CtorKind::Fn, def_id)), - hir::ExprKind::Call(rcvr, args), - ) => { - let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); - let inputs = fn_sig.inputs().skip_binder(); - // FIXME: reuse the logic for "change args" suggestion to account for types - // involved and detect things like substitution. - match (inputs, args) { - (inputs, []) => { - // Add arguments. - suggestion.push(( - rcvr.span.shrink_to_hi().with_hi(parent.span.hi()), - format!( - "({})", - inputs - .iter() - .map(|i| format!("/* {i} */")) - .collect::>() - .join(", ") - ), - )); - } - (_, [arg]) if inputs.len() != args.len() => { - // Replace arguments. - suggestion.push(( - arg.span, - inputs - .iter() - .map(|i| format!("/* {i} */")) - .collect::>() - .join(", "), - )); - } - (_, [arg_start, .., arg_end]) if inputs.len() != args.len() => { - // Replace arguments. - suggestion.push(( - arg_start.span.to(arg_end.span), - inputs - .iter() - .map(|i| format!("/* {i} */")) - .collect::>() - .join(", "), - )); + } + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => { + bound_spans + .get_mut_or_insert_default(tcx.def_span(*def_id)) + .push(format!("`{quiet}`")); + } + _ => {} + } + }; + + let mut format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => { + let pred = bound_predicate.rebind(pred); + // `::Item = String`. + let projection_term = pred.skip_binder().projection_term; + let quiet_projection_term = projection_term + .with_replaced_self_ty(tcx, Ty::new_var(tcx, ty::TyVid::ZERO)); + + let term = pred.skip_binder().term; + + let obligation = format!("{projection_term} = {term}"); + let quiet = + with_forced_trimmed_paths!(format!("{} = {}", quiet_projection_term, term)); + + bound_span_label(projection_term.self_ty(), &obligation, &quiet); + Some((obligation, projection_term.self_ty())) + } + ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{self_ty}: {path}"); + let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; + + // Find all the requirements that come from a local `impl` block. + let mut skip_list: UnordSet<_> = Default::default(); + let mut spanned_predicates = FxIndexMap::default(); + for (p, parent_p, cause) in unsatisfied_predicates { + // Extract the predicate span and parent def id of the cause, + // if we have one. + let (item_def_id, cause_span) = match cause.as_ref().map(|cause| cause.code()) { + Some(ObligationCauseCode::ImplDerived(data)) => { + (data.impl_or_alias_def_id, data.span) + } + Some( + ObligationCauseCode::WhereClauseInExpr(def_id, span, _, _) + | ObligationCauseCode::WhereClause(def_id, span), + ) if !span.is_dummy() => (*def_id, *span), + _ => continue, + }; + + // Don't point out the span of `WellFormed` predicates. + if !matches!( + p.kind().skip_binder(), + ty::PredicateKind::Clause( + ty::ClauseKind::Projection(..) | ty::ClauseKind::Trait(..) + ) + ) { + continue; + } + + match self.tcx.hir_get_if_local(item_def_id) { + // Unmet obligation comes from a `derive` macro, point at it once to + // avoid multiple span labels pointing at the same place. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) if matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) || matches!( + of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) => + { + let span = self_ty.span.ctxt().outer_expn_data().call_site; + let entry = spanned_predicates.entry(span); + let entry = entry.or_insert_with(|| { + (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) + }); + entry.0.insert(span); + entry.1.insert(( + span, + "unsatisfied trait bound introduced in this `derive` macro", + )); + entry.2.push(p); + skip_list.insert(p); + } + + // Unmet obligation coming from an `impl`. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), + span: item_span, + .. + })) => { + let sized_pred = + unsatisfied_predicates.iter().any(|(pred, _, _)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { + self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) + && pred.polarity == ty::PredicatePolarity::Positive } - // Argument count is the same, keep as is. - _ => {} + _ => false, } + }); + for param in generics.params { + if param.span == cause_span && sized_pred { + let (sp, sugg) = match param.colon_span { + Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), + None => (param.span.shrink_to_hi(), ": ?Sized"), + }; + err.span_suggestion_verbose( + sp, + "consider relaxing the type parameter's implicit `Sized` bound", + sugg, + Applicability::MachineApplicable, + ); } - (Some((hir::def::CtorKind::Fn, def_id)), _) => { - let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); - let inputs = fn_sig.inputs().skip_binder(); - suggestion = vec![( - replacement_span, - format!( - "{var_name}({})", - inputs - .iter() - .map(|i| format!("/* {i} */")) - .collect::>() - .join(", ") - ), - )]; + } + if let Some(pred) = parent_p { + // Done to add the "doesn't satisfy" `span_label`. + let _ = format_pred(*pred); + } + skip_list.insert(p); + let entry = spanned_predicates.entry(self_ty.span); + let entry = entry.or_insert_with(|| { + (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) + }); + entry.2.push(p); + if cause_span != *item_span { + entry.0.insert(cause_span); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); + } else { + if let Some(of_trait) = of_trait { + entry.0.insert(of_trait.trait_ref.path.span); } + entry.0.insert(self_ty.span); + }; + if let Some(of_trait) = of_trait { + entry.1.insert((of_trait.trait_ref.path.span, "")); } + entry.1.insert((self_ty.span, "")); + } + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, rustc_ast::ast::IsAuto::Yes, ..), + span: item_span, + .. + })) => { + self.dcx().span_delayed_bug( + *item_span, + "auto trait is invoked with no method error, but no error reported?", + ); + } + Some( + Node::Item(hir::Item { + kind: + hir::ItemKind::Trait(_, _, _, ident, ..) + | hir::ItemKind::TraitAlias(ident, ..), + .. + }) + // We may also encounter unsatisfied GAT or method bounds + | Node::TraitItem(hir::TraitItem { ident, .. }) + | Node::ImplItem(hir::ImplItem { ident, .. }) + ) => { + skip_list.insert(p); + let entry = spanned_predicates.entry(ident.span); + let entry = entry.or_insert_with(|| { + (FxIndexSet::default(), FxIndexSet::default(), Vec::new()) + }); + entry.0.insert(cause_span); + entry.1.insert((ident.span, "")); + entry.1.insert((cause_span, "unsatisfied trait bound introduced here")); + entry.2.push(p); + } + _ => { + // It's possible to use well-formedness clauses to get obligations + // which point arbitrary items like ADTs, so there's no use in ICEing + // here if we find that the obligation originates from some other + // node that we don't handle. } - err.multipart_suggestion_verbose( - "there is a variant with a similar name", - suggestion, - Applicability::HasPlaceholders, - ); } } + let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); + spanned_predicates.sort_by_key(|(span, _)| *span); + for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { + let mut preds: Vec<_> = predicates + .iter() + .filter_map(|pred| format_pred(**pred)) + .map(|(p, _)| format!("`{p}`")) + .collect(); + preds.sort(); + preds.dedup(); + let msg = if let [pred] = &preds[..] { + format!("trait bound {pred} was not satisfied") + } else { + format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) + }; + let mut span: MultiSpan = primary_spans.into_iter().collect::>().into(); + for (sp, label) in span_labels { + span.push_span_label(sp, label); + } + err.span_note(span, msg); + *unsatisfied_bounds = true; + } - if let Some(similar_candidate) = similar_candidate { - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() - // ...or if we already suggested that name because of `rustc_confusable` annotation - && Some(similar_candidate.name()) != confusable_suggested - // and if we aren't in an expansion. - && !span.from_expansion() - { - self.find_likely_intended_associated_item( - &mut err, - similar_candidate, + let mut suggested_bounds = UnordSet::default(); + // The requirements that didn't have an `impl` span to show. + let mut bound_list = unsatisfied_predicates + .iter() + .filter_map(|(pred, parent_pred, _cause)| { + let mut suggested = false; + format_pred(*pred).map(|(p, self_ty)| { + if let Some(parent) = parent_pred + && suggested_bounds.contains(parent) + { + // We don't suggest `PartialEq` when we already suggest `Eq`. + } else if !suggested_bounds.contains(pred) + && collect_type_param_suggestions(self_ty, *pred, &p) + { + suggested = true; + suggested_bounds.insert(pred); + } + ( + match parent_pred { + None => format!("`{p}`"), + Some(parent_pred) => match format_pred(*parent_pred) { + None => format!("`{p}`"), + Some((parent_p, _)) => { + if !suggested + && !suggested_bounds.contains(pred) + && !suggested_bounds.contains(parent_pred) + && collect_type_param_suggestions(self_ty, *parent_pred, &p) + { + suggested_bounds.insert(pred); + } + format!("`{p}`\nwhich is required by `{parent_p}`") + } + }, + }, + *pred, + ) + }) + }) + .filter(|(_, pred)| !skip_list.contains(&pred)) + .map(|(t, _)| t) + .enumerate() + .collect::>(); + + if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) { + for ((span, add_where_or_comma), obligations) in type_params.into_iter() { + *restrict_type_params = true; + // #74886: Sort here so that the output is always the same. + let obligations = obligations.into_sorted_stable_ord(); + err.span_suggestion_verbose( span, - args, - mode, + format!( + "consider restricting the type parameter{s} to satisfy the trait \ + bound{s}", + s = pluralize!(obligations.len()) + ), + format!("{} {}", add_where_or_comma, obligations.join(", ")), + Applicability::MaybeIncorrect, ); } } - if !find_candidate_for_method { - self.lookup_segments_chain_for_no_match_method( - &mut err, - item_ident, - item_kind, - source, - no_match_data, - ); - } + bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. + bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 + bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. + + if !bound_list.is_empty() || !skip_list.is_empty() { + let bound_list = + bound_list.into_iter().map(|(_, path)| path).collect::>().join("\n"); + let actual_prefix = rcvr_ty.prefix_string(self.tcx); + info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); + let (primary_message, label, notes) = if unimplemented_traits.len() == 1 + && unimplemented_traits_only + { + unimplemented_traits + .into_iter() + .next() + .map(|(_, (trait_ref, obligation))| { + if trait_ref.self_ty().references_error() || rcvr_ty.references_error() { + // Avoid crashing. + return (None, None, Vec::new()); + } + let OnUnimplementedNote { message, label, notes, .. } = self + .err_ctxt() + .on_unimplemented_note(trait_ref, &obligation, err.long_ty_path()); + (message, label, notes) + }) + .unwrap() + } else { + (None, None, Vec::new()) + }; + let primary_message = primary_message.unwrap_or_else(|| { + let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); + format!( + "the {item_kind} `{item_ident}` exists for {actual_prefix} `{ty_str}`, \ + but its trait bounds were not satisfied" + ) + }); + err.primary_message(primary_message); + if let Some(label) = label { + *custom_span_label = true; + err.span_label(span, label); + } + if !bound_list.is_empty() { + err.note(format!("the following trait bounds were not satisfied:\n{bound_list}")); + } + for note in notes { + err.note(note); + } - self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); - err.emit() + *suggested_derive = self.suggest_derive(err, unsatisfied_predicates); + *unsatisfied_bounds = true; + } } /// If an appropriate error source is not found, check method chain for possible candidates @@ -3082,13 +3103,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn note_predicate_source_and_get_derives( &self, err: &mut Diag<'_>, - unsatisfied_predicates: &[( - ty::Predicate<'tcx>, - Option>, - Option>, - )], + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) -> Vec<(String, Span, Symbol)> { - let mut derives = Vec::<(String, Span, Symbol)>::new(); + let mut derives = Vec::new(); let mut traits = Vec::new(); for (pred, _, _) in unsatisfied_predicates { let Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) = @@ -3164,11 +3181,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn suggest_derive( &self, err: &mut Diag<'_>, - unsatisfied_predicates: &[( - ty::Predicate<'tcx>, - Option>, - Option>, - )], + unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, ) -> bool { let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates); derives.sort(); @@ -3302,6 +3315,101 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn set_label_for_method_error( + &self, + err: &mut Diag<'_>, + source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + expr_id: hir::HirId, + span: Span, + sugg_span: Span, + within_macro_span: Option, + args: Option<&'tcx [hir::Expr<'tcx>]>, + ) { + let tcx = self.tcx; + if tcx.sess.source_map().is_multiline(sugg_span) { + err.span_label(sugg_span.with_hi(span.lo()), ""); + } + if let Some(within_macro_span) = within_macro_span { + err.span_label(within_macro_span, "due to this macro variable"); + } + + if matches!(source, SelfSource::QPath(_)) && args.is_some() { + self.find_builder_fn(err, rcvr_ty, expr_id); + } + + if tcx.ty_is_opaque_future(rcvr_ty) && item_ident.name == sym::poll { + let ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path()); + err.help(format!( + "method `poll` found on `Pin<&mut {ty_str}>`, \ + see documentation for `std::pin::Pin`" + )); + err.help("self type must be pinned to call `Future::poll`, \ + see https://rust-lang.github.io/async-book/04_pinning/01_chapter.html#pinning-in-practice" + ); + } + + if let Some(span) = + tcx.resolutions(()).confused_type_with_std_module.get(&span.with_parent(None)) + { + err.span_suggestion( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::", + Applicability::MachineApplicable, + ); + } + } + + fn suggest_on_pointer_type( + &self, + err: &mut Diag<'_>, + source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_ident: Ident, + ) { + let tcx = self.tcx; + // on pointers, check if the method would exist on a reference + if let SelfSource::MethodCall(rcvr_expr) = source + && let ty::RawPtr(ty, ptr_mutbl) = *rcvr_ty.kind() + && let Ok(pick) = self.lookup_probe_for_diagnostic( + item_ident, + Ty::new_ref(tcx, ty::Region::new_error_misc(tcx), ty, ptr_mutbl), + self.tcx.hir_expect_expr(self.tcx.parent_hir_id(rcvr_expr.hir_id)), + ProbeScope::TraitsInScope, + None, + ) + && let ty::Ref(_, _, sugg_mutbl) = *pick.self_ty.kind() + && (sugg_mutbl.is_not() || ptr_mutbl.is_mut()) + { + let (method, method_anchor) = match sugg_mutbl { + Mutability::Not => { + let method_anchor = match ptr_mutbl { + Mutability::Not => "as_ref", + Mutability::Mut => "as_ref-1", + }; + ("as_ref", method_anchor) + } + Mutability::Mut => ("as_mut", "as_mut"), + }; + err.span_note( + tcx.def_span(pick.item.def_id), + format!("the method `{item_ident}` exists on the type `{ty}`", ty = pick.self_ty), + ); + let mut_str = ptr_mutbl.ptr_str(); + err.note(format!( + "you might want to use the unsafe method `<*{mut_str} T>::{method}` to get \ + an optional reference to the value behind the pointer" + )); + err.note(format!( + "read the documentation for `<*{mut_str} T>::{method}` and ensure you satisfy its \ + safety preconditions before calling it to avoid undefined behavior: \ + https://doc.rust-lang.org/std/primitive.pointer.html#method.{method_anchor}" + )); + } + } + fn suggest_use_candidates(&self, candidates: Vec, handle_candidates: F) where F: FnOnce(Vec, Vec, Span), diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 7c66559269580..8ce74ff76effc 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -758,6 +758,7 @@ fn pat_ty_is_known_nonnull<'tcx>( // to ensure we aren't wrapping over zero. start > 0 && end >= start } + ty::PatternKind::NotNull => true, ty::PatternKind::Or(patterns) => { patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) } @@ -918,7 +919,9 @@ fn get_nullable_type_from_pat<'tcx>( pat: ty::Pattern<'tcx>, ) -> Option> { match *pat { - ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base), + ty::PatternKind::NotNull | ty::PatternKind::Range { .. } => { + get_nullable_type(tcx, typing_env, base) + } ty::PatternKind::Or(patterns) => { let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?; for &pat in &patterns[1..] { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 6c341c441dc26..616c8a17dd66d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1134,8 +1134,8 @@ struct LLVMRustThinLTOModule { // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp`, not sure what it // does. -static const GlobalValueSummary * -getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { +static const GlobalValueSummary *getFirstDefinitionForLinker( + ArrayRef> GVSummaryList) { auto StrongDefForLinker = llvm::find_if( GVSummaryList, [](const std::unique_ptr &Summary) { auto Linkage = Summary->linkage(); @@ -1213,9 +1213,13 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules, // being lifted from `lib/LTO/LTO.cpp` as well DenseMap PrevailingCopy; for (auto &I : Ret->Index) { - if (I.second.SummaryList.size() > 1) - PrevailingCopy[I.first] = - getFirstDefinitionForLinker(I.second.SummaryList); +#if LLVM_VERSION_GE(22, 0) + const auto &SummaryList = I.second.getSummaryList(); +#else + const auto &SummaryList = I.second.SummaryList; +#endif + if (SummaryList.size() > 1) + PrevailingCopy[I.first] = getFirstDefinitionForLinker(SummaryList); } auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { const auto &Prevailing = PrevailingCopy.find(GUID); @@ -1246,7 +1250,12 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, size_t num_modules, // linkage will stay as external, and internal will stay as internal. std::set ExportedGUIDs; for (auto &List : Ret->Index) { - for (auto &GVS : List.second.SummaryList) { +#if LLVM_VERSION_GE(22, 0) + const auto &SummaryList = List.second.getSummaryList(); +#else + const auto &SummaryList = List.second.SummaryList; +#endif + for (auto &GVS : SummaryList) { if (GlobalValue::isLocalLinkage(GVS->linkage())) continue; auto GUID = GVS->getOriginalName(); diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 32a74b335c117..3861efd364281 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -160,6 +160,9 @@ pub enum SelectionCandidate<'tcx> { /// types generated for a fn pointer type (e.g., `fn(int) -> int`) FnPointerCandidate, + /// Builtin impl of the `PointerLike` trait. + PointerLikeCandidate, + TraitAliasCandidate, /// Matching `dyn Trait` with a supertrait of `Trait`. The index is the diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 507a25a432594..eefb913a33fa4 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -839,11 +839,15 @@ where | ty::FnDef(..) | ty::CoroutineWitness(..) | ty::Foreign(..) - | ty::Pat(_, _) | ty::Dynamic(_, _) => { bug!("TyAndLayout::field({:?}): not applicable", this) } + ty::Pat(base, _) => { + assert_eq!(i, 0); + TyMaybeWithLayout::Ty(base) + } + ty::UnsafeBinder(bound_ty) => { let ty = tcx.instantiate_bound_regions_with_erased(bound_ty.into()); field_ty_or_layout(TyAndLayout { ty, ..this }, cx, i) diff --git a/compiler/rustc_middle/src/ty/pattern.rs b/compiler/rustc_middle/src/ty/pattern.rs index 5af9b17dd7777..335e5c0647435 100644 --- a/compiler/rustc_middle/src/ty/pattern.rs +++ b/compiler/rustc_middle/src/ty/pattern.rs @@ -30,6 +30,7 @@ impl<'tcx> Flags for Pattern<'tcx> { } flags } + ty::PatternKind::NotNull => rustc_type_ir::TypeFlags::empty(), } } @@ -45,6 +46,7 @@ impl<'tcx> Flags for Pattern<'tcx> { } idx } + ty::PatternKind::NotNull => rustc_type_ir::INNERMOST, } } } @@ -91,6 +93,7 @@ impl<'tcx> IrPrint> for TyCtxt<'tcx> { write!(f, "..={end}") } + PatternKind::NotNull => write!(f, "!null"), PatternKind::Or(patterns) => { write!(f, "(")?; let mut first = true; diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 2f96970af4788..9f2a1187a9ddc 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -35,6 +35,7 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { let end = relation.relate(end_a, end_b)?; Ok(tcx.mk_pat(ty::PatternKind::Range { start, end })) } + (ty::PatternKind::NotNull, ty::PatternKind::NotNull) => Ok(a), (&ty::PatternKind::Or(a), &ty::PatternKind::Or(b)) => { if a.len() != b.len() { return Err(TypeError::Mismatch); @@ -43,7 +44,10 @@ impl<'tcx> Relate> for ty::Pattern<'tcx> { let patterns = tcx.mk_patterns_from_iter(v)?; Ok(tcx.mk_pat(ty::PatternKind::Or(patterns))) } - (ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), _) => Err(TypeError::Mismatch), + ( + ty::PatternKind::NotNull | ty::PatternKind::Range { .. } | ty::PatternKind::Or(_), + _, + ) => Err(TypeError::Mismatch), } } } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 634c01e2df32f..e9c94104ae775 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -695,6 +695,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; check_equal(self, location, *f_ty); } + // Debug info is allowed to project into pattern types + ty::Pat(base, _) => check_equal(self, location, *base), ty::Adt(adt_def, args) => { // see if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 93f647c05e02d..f33f22460467b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1145,6 +1145,7 @@ fn find_tails_for_unsizing<'tcx>( debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic"); match (source_ty.kind(), target_ty.kind()) { + (&ty::Pat(source, _), &ty::Pat(target, _)) => find_tails_for_unsizing(tcx, source, target), ( &ty::Ref(_, source_pointee, _), &ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _), diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index bd4bb368df05f..4c4f09ab3ea8a 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -139,6 +139,7 @@ pub enum TokenType { SymNomem, SymNoreturn, SymNostack, + SymNull, SymOptions, SymOut, SymPreservesFlags, @@ -273,6 +274,7 @@ impl TokenType { SymNomem, SymNoreturn, SymNostack, + SymNull, SymOptions, SymOut, SymPreservesFlags, @@ -348,6 +350,7 @@ impl TokenType { TokenType::SymNomem => Some(sym::nomem), TokenType::SymNoreturn => Some(sym::noreturn), TokenType::SymNostack => Some(sym::nostack), + TokenType::SymNull => Some(sym::null), TokenType::SymOptions => Some(sym::options), TokenType::SymOut => Some(sym::out), TokenType::SymPreservesFlags => Some(sym::preserves_flags), @@ -562,6 +565,7 @@ macro_rules! exp { (Nomem) => { exp!(@sym, nomem, SymNomem) }; (Noreturn) => { exp!(@sym, noreturn, SymNoreturn) }; (Nostack) => { exp!(@sym, nostack, SymNostack) }; + (Null) => { exp!(@sym, null, SymNull) }; (Options) => { exp!(@sym, options, SymOptions) }; (Out) => { exp!(@sym, out, SymOut) }; (PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) }; diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 59440e5407f40..36fc5724d51a9 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -485,6 +485,7 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { end: Some(end.stable(tables, cx)), include_end: true, }, + ty::PatternKind::NotNull => todo!(), ty::PatternKind::Or(_) => todo!(), } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 8c6e9f6fb3a40..fd7ace368aa67 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -968,7 +968,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_ty_pat(pat) } } - TyPatKind::Err(_) => {} + TyPatKind::NotNull | TyPatKind::Err(_) => {} } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ef72c478951b8..38340f9bd7ec5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1554,6 +1554,7 @@ symbols! { not, notable_trait, note, + null, nvptx_target_feature, object_safe_for_dispatch, of, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index d5bc831b650aa..2c71b22c4a2b9 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -266,6 +266,9 @@ impl<'tcx> V0SymbolMangler<'tcx> { self.print_const(start)?; self.print_const(end)?; } + ty::PatternKind::NotNull => { + self.tcx.types.unit.print(self)?; + } ty::PatternKind::Or(patterns) => { self.push("O"); for pat in patterns { diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 66311de7446b0..88f512708ff04 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -115,6 +115,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } + PointerLikeCandidate => { + let data = self.confirm_pointer_like_candidate(obligation); + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); ImplSource::Builtin(BuiltinImplSource::Misc, data) @@ -631,6 +636,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(nested) } + fn confirm_pointer_like_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> PredicateObligations<'tcx> { + debug!(?obligation, "confirm_pointer_like_candidate"); + let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty()); + let ty::Pat(base, _) = *self_ty.kind() else { bug!() }; + let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); + + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + placeholder_predicate.def_id(), + vec![base], + ) + } + fn confirm_trait_alias_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 614c09d913f72..70a0896ae384c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2036,6 +2036,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | TraitUpcastingUnsizeCandidate(_) | BuiltinObjectCandidate | BuiltinUnsizeCandidate + | PointerLikeCandidate | BikeshedGuaranteedNoDropCandidate => false, // Non-global param candidates have already been handled, global // where-bounds get ignored. diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 45cbb56b1c2ef..db7bd635f32e6 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -705,6 +705,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { check(start); check(end); } + ty::PatternKind::NotNull => {} ty::PatternKind::Or(patterns) => { for pat in patterns { self.add_wf_preds_for_pat_ty(base_ty, pat) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 317d101dafe05..b3c0953b8e147 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -216,9 +216,7 @@ fn layout_of_uncached<'tcx>( let mut layout = LayoutData::clone(&layout.0); match *pat { ty::PatternKind::Range { start, end } => { - if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = - &mut layout.backend_repr - { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { scalar.valid_range_mut().start = extract_const_value(cx, ty, start)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; @@ -266,6 +264,25 @@ fn layout_of_uncached<'tcx>( bug!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") } } + ty::PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(cx), + }; + + layout.largest_niche = Some(niche); + } else { + bug!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + ty::PatternKind::Or(variants) => match *variants[0] { ty::PatternKind::Range { .. } => { if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { @@ -282,7 +299,7 @@ fn layout_of_uncached<'tcx>( .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?, )), - ty::PatternKind::Or(_) => { + ty::PatternKind::NotNull | ty::PatternKind::Or(_) => { unreachable!("mixed or patterns are not allowed") } }) @@ -347,9 +364,18 @@ fn layout_of_uncached<'tcx>( ) } } + ty::PatternKind::NotNull => bug!("or patterns can't contain `!null` patterns"), ty::PatternKind::Or(..) => bug!("patterns cannot have nested or patterns"), }, } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + memory_index: [0].into_iter().collect(), + }; tcx.mk_layout(layout) } diff --git a/compiler/rustc_type_ir/src/pattern.rs b/compiler/rustc_type_ir/src/pattern.rs index d757ac625f2db..1f8b7158cb111 100644 --- a/compiler/rustc_type_ir/src/pattern.rs +++ b/compiler/rustc_type_ir/src/pattern.rs @@ -14,6 +14,7 @@ use crate::Interner; pub enum PatternKind { Range { start: I::Const, end: I::Const }, Or(I::PatList), + NotNull, } impl Eq for PatternKind {} diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 6d51817a7bf44..e48d598a5328d 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -178,5 +178,6 @@ fn push_ty_pat(stack: &mut TypeWalkerStack, pat: I::Pat) { push_ty_pat::(stack, pat) } } + ty::PatternKind::NotNull => {} } } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index ae30cabf5af5b..d5ea5dc1e79e4 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2378,6 +2378,28 @@ impl<'a> FromIterator> for String { } } +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl FromIterator for String { + fn from_iter>(iter: T) -> Self { + let buf = iter.into_iter().map(core::ascii::Char::to_u8).collect(); + // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type + // only contains ASCII values (0x00-0x7F), which are valid UTF-8. + unsafe { String::from_utf8_unchecked(buf) } + } +} + +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl<'a> FromIterator<&'a core::ascii::Char> for String { + fn from_iter>(iter: T) -> Self { + let buf = iter.into_iter().copied().map(core::ascii::Char::to_u8).collect(); + // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type + // only contains ASCII values (0x00-0x7F), which are valid UTF-8. + unsafe { String::from_utf8_unchecked(buf) } + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Extend for String { @@ -3184,6 +3206,14 @@ impl<'a> FromIterator for Cow<'a, str> { } } +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "ascii_char", issue = "110998")] +impl<'a> FromIterator for Cow<'a, str> { + fn from_iter>(it: T) -> Self { + Cow::Owned(FromIterator::from_iter(it)) + } +} + #[stable(feature = "from_string_for_vec_u8", since = "1.14.0")] impl From for Vec { /// Converts the given [`String`] to a vector [`Vec`] that holds values of type [`u8`]. diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index aeac35e45a5d0..988c50795e299 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -252,7 +252,7 @@ use crate::cmp::Ordering; use crate::fmt::{self, Debug, Display}; -use crate::marker::{PhantomData, Unsize}; +use crate::marker::{Destruct, PhantomData, Unsize}; use crate::mem::{self, ManuallyDrop}; use crate::ops::{self, CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn}; use crate::panic::const_panic; @@ -429,7 +429,11 @@ impl Cell { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn set(&self, val: T) { + #[rustc_const_unstable(feature = "const_cell_traits", issue = "147787")] + pub const fn set(&self, val: T) + where + T: [const] Destruct, + { self.replace(val); } @@ -561,7 +565,12 @@ impl Cell { /// ``` #[inline] #[stable(feature = "cell_update", since = "1.88.0")] - pub fn update(&self, f: impl FnOnce(T) -> T) { + #[rustc_const_unstable(feature = "const_cell_traits", issue = "147787")] + pub const fn update(&self, f: impl [const] FnOnce(T) -> T) + where + // FIXME(const-hack): `Copy` should imply `const Destruct` + T: [const] Destruct, + { let old = self.get(); self.set(f(old)); } @@ -654,7 +663,11 @@ impl Cell { /// assert_eq!(c.into_inner(), 0); /// ``` #[stable(feature = "move_cell", since = "1.17.0")] - pub fn take(&self) -> T { + #[rustc_const_unstable(feature = "const_cell_traits", issue = "147787")] + pub const fn take(&self) -> T + where + T: [const] Default, + { self.replace(Default::default()) } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 54adf97f10020..bc428c37a88f9 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -116,6 +116,7 @@ #![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] +#![feature(pattern_type_macro)] #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] @@ -171,6 +172,7 @@ #![feature(never_type)] #![feature(no_core)] #![feature(optimize_attribute)] +#![feature(pattern_types)] #![feature(prelude_import)] #![feature(reborrow)] #![feature(repr_simd)] diff --git a/library/core/src/pat.rs b/library/core/src/pat.rs index a13eea3fb585c..2670c2614198c 100644 --- a/library/core/src/pat.rs +++ b/library/core/src/pat.rs @@ -1,5 +1,8 @@ //! Helper module for exporting the `pattern_type` macro +use crate::marker::{Freeze, PointeeSized, Unsize}; +use crate::ops::{CoerceUnsized, DispatchFromDyn}; + /// Creates a pattern type. /// ```ignore (cannot test this from within core yet) /// type Positive = std::pat::pattern_type!(i32 is 1..); @@ -73,3 +76,16 @@ impl const RangePattern for char { } } } + +impl CoerceUnsized for pattern_type!(*const T is !null) where + T: Unsize +{ +} + +impl, U> DispatchFromDyn for pattern_type!(T is !null) {} + +impl Unpin for pattern_type!(*const T is !null) {} + +unsafe impl Freeze for pattern_type!(*const T is !null) {} + +unsafe impl Freeze for pattern_type!(*mut T is !null) {} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c2dc3a99ab109..5c2522acb1362 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -16,6 +16,7 @@ #![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] +#![feature(const_cell_traits)] #![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] diff --git a/library/coretests/tests/manually_drop.rs b/library/coretests/tests/manually_drop.rs index bbf444471ad2a..1638c82b161b4 100644 --- a/library/coretests/tests/manually_drop.rs +++ b/library/coretests/tests/manually_drop.rs @@ -27,3 +27,41 @@ fn smoke() { drop(x); drop(y); } + +#[test] +fn const_drop_in_place() { + const COUNTER: usize = { + use core::cell::Cell; + + let counter = Cell::new(0); + + // only exists to make `Drop` indirect impl + #[allow(dead_code)] + struct Test<'a>(Dropped<'a>); + + struct Dropped<'a>(&'a Cell); + impl const Drop for Dropped<'_> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } + } + + let mut one = ManuallyDrop::new(Test(Dropped(&counter))); + let mut two = ManuallyDrop::new(Test(Dropped(&counter))); + let mut three = ManuallyDrop::new(Test(Dropped(&counter))); + assert!(counter.get() == 0); + unsafe { + ManuallyDrop::drop(&mut one); + } + assert!(counter.get() == 1); + unsafe { + ManuallyDrop::drop(&mut two); + } + assert!(counter.get() == 2); + unsafe { + ManuallyDrop::drop(&mut three); + } + counter.get() + }; + assert_eq!(COUNTER, 3); +} diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index e89f21271027a..4d5138d539b95 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use core::marker::Freeze; -use core::mem::{ManuallyDrop, MaybeUninit}; +use core::mem::MaybeUninit; use core::num::NonZero; use core::ptr; use core::ptr::*; @@ -1045,42 +1045,3 @@ fn test_ptr_default() { let default = PtrMutDefaultTest::default(); assert!(default.ptr.is_null()); } - -#[test] -fn test_const_drop_in_place() { - const COUNTER: usize = { - let mut counter = 0; - let counter_ptr = &raw mut counter; - - // only exists to make `Drop` indirect impl - #[allow(dead_code)] - struct Test(Dropped); - - struct Dropped(*mut usize); - impl const Drop for Dropped { - fn drop(&mut self) { - unsafe { - *self.0 += 1; - } - } - } - - let mut one = ManuallyDrop::new(Test(Dropped(counter_ptr))); - let mut two = ManuallyDrop::new(Test(Dropped(counter_ptr))); - let mut three = ManuallyDrop::new(Test(Dropped(counter_ptr))); - assert!(counter == 0); - unsafe { - ManuallyDrop::drop(&mut one); - } - assert!(counter == 1); - unsafe { - ManuallyDrop::drop(&mut two); - } - assert!(counter == 2); - unsafe { - ManuallyDrop::drop(&mut three); - } - counter - }; - assert_eq!(COUNTER, 3); -} diff --git a/library/std/src/os/motor/ffi.rs b/library/std/src/os/motor/ffi.rs index 509fe641bb353..10e8da392dcc5 100644 --- a/library/std/src/os/motor/ffi.rs +++ b/library/std/src/os/motor/ffi.rs @@ -3,35 +3,40 @@ use crate::ffi::{OsStr, OsString}; use crate::sealed::Sealed; +use crate::sys_common::{AsInner, IntoInner}; -/// Motor OS-specific extensions to [`OsString`]. +/// Motor OS–specific extensions to [`OsString`]. /// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. pub trait OsStringExt: Sealed { - /// Motor OS strings are utf-8, and thus just strings. - fn as_str(&self) -> &str; + /// Yields the underlying UTF-8 string of this [`OsString`]. + /// + /// OS strings on Motor OS are guaranteed to be UTF-8, so are just strings. + fn into_string(self) -> String; } impl OsStringExt for OsString { #[inline] - fn as_str(&self) -> &str { - self.to_str().unwrap() + fn into_string(self) -> String { + self.into_inner().inner } } -/// Motor OS-specific extensions to [`OsString`]. +/// Motor OS–specific extensions to [`OsString`]. /// /// This trait is sealed: it cannot be implemented outside the standard library. /// This is so that future additional methods are not breaking changes. pub trait OsStrExt: Sealed { - /// Motor OS strings are utf-8, and thus just strings. + /// Gets the underlying UTF-8 string view of the [`OsStr`] slice. + /// + /// OS strings on Motor OS are guaranteed to be UTF-8, so are just strings. fn as_str(&self) -> &str; } impl OsStrExt for OsStr { #[inline] fn as_str(&self) -> &str { - self.to_str().unwrap() + &self.as_inner().inner } } diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index f8ab4543a3a52..9373982c455fa 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -176,17 +176,17 @@ impl Buf { #[inline] pub fn as_slice(&self) -> &Slice { - // SAFETY: Slice just wraps [u8], - // and &*self.inner is &[u8], therefore - // transmuting &[u8] to &Slice is safe. + // SAFETY: Slice is just a wrapper for [u8], + // and self.inner.as_slice() returns &[u8]. + // Therefore, transmuting &[u8] to &Slice is safe. unsafe { mem::transmute(self.inner.as_slice()) } } #[inline] pub fn as_mut_slice(&mut self) -> &mut Slice { - // SAFETY: Slice just wraps [u8], - // and &mut *self.inner is &mut [u8], therefore - // transmuting &mut [u8] to &mut Slice is safe. + // SAFETY: Slice is just a wrapper for [u8], + // and self.inner.as_mut_slice() returns &mut [u8]. + // Therefore, transmuting &mut [u8] to &mut Slice is safe. unsafe { mem::transmute(self.inner.as_mut_slice()) } } @@ -233,7 +233,9 @@ impl Buf { /// /// # Safety /// - /// This encoding has no safety requirements. + /// The slice must be valid for the platform encoding (as described in + /// `OsStr::from_encoded_bytes_unchecked`). This encoding has no safety + /// requirements. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { self.inner.extend_from_slice(other); diff --git a/library/std/src/sys/os_str/mod.rs b/library/std/src/sys/os_str/mod.rs index 65c90d880495d..f7007cbf18b4c 100644 --- a/library/std/src/sys/os_str/mod.rs +++ b/library/std/src/sys/os_str/mod.rs @@ -5,6 +5,10 @@ cfg_select! { mod wtf8; pub use wtf8::{Buf, Slice}; } + any(target_os = "motor") => { + mod utf8; + pub use utf8::{Buf, Slice}; + } _ => { mod bytes; pub use bytes::{Buf, Slice}; diff --git a/library/std/src/sys/os_str/utf8.rs b/library/std/src/sys/os_str/utf8.rs new file mode 100644 index 0000000000000..5dd24f67d3039 --- /dev/null +++ b/library/std/src/sys/os_str/utf8.rs @@ -0,0 +1,330 @@ +//! An OsString/OsStr implementation that is guaranteed to be UTF-8. + +use core::clone::CloneToUninit; + +use crate::borrow::Cow; +use crate::collections::TryReserveError; +use crate::rc::Rc; +use crate::sync::Arc; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::{fmt, mem}; + +#[derive(Hash)] +#[repr(transparent)] +pub struct Buf { + pub inner: String, +} + +#[repr(transparent)] +pub struct Slice { + pub inner: str, +} + +impl IntoInner for Buf { + fn into_inner(self) -> String { + self.inner + } +} + +impl FromInner for Buf { + fn from_inner(inner: String) -> Self { + Buf { inner } + } +} + +impl AsInner for Buf { + #[inline] + fn as_inner(&self) -> &str { + &self.inner + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl fmt::Debug for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl Clone for Buf { + #[inline] + fn clone(&self) -> Self { + Buf { inner: self.inner.clone() } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.inner.clone_from(&source.inner) + } +} + +impl Buf { + #[inline] + pub fn into_encoded_bytes(self) -> Vec { + self.inner.into_bytes() + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: Vec) -> Self { + unsafe { Self { inner: String::from_utf8_unchecked(s) } } + } + + #[inline] + pub fn into_string(self) -> Result { + Ok(self.inner) + } + + #[inline] + pub const fn from_string(s: String) -> Buf { + Buf { inner: s } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { inner: String::with_capacity(capacity) } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn push_slice(&mut self, s: &Slice) { + self.inner.push_str(&s.inner) + } + + #[inline] + pub fn push_str(&mut self, s: &str) { + self.inner.push_str(s); + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + #[inline] + pub fn as_slice(&self) -> &Slice { + Slice::from_str(&self.inner) + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut Slice { + Slice::from_mut_str(&mut self.inner) + } + + #[inline] + pub fn leak<'a>(self) -> &'a mut Slice { + Slice::from_mut_str(self.inner.leak()) + } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_str()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_string() } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } + + /// Provides plumbing to `Vec::truncate` without giving full mutable access + /// to the `Vec`. + /// + /// # Safety + /// + /// The length must be at an `OsStr` boundary, according to + /// `Slice::check_public_boundary`. + #[inline] + pub unsafe fn truncate_unchecked(&mut self, len: usize) { + self.inner.truncate(len); + } + + /// Provides plumbing to `Vec::extend_from_slice` without giving full + /// mutable access to the `Vec`. + /// + /// # Safety + /// + /// The slice must be valid for the platform encoding (as described in + /// `OsStr::from_encoded_bytes_unchecked`). For this encoding, that means + /// `other` must be valid UTF-8. + #[inline] + pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { + self.inner.push_str(unsafe { str::from_utf8_unchecked(other) }); + } +} + +impl Slice { + #[inline] + pub fn as_encoded_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + #[inline] + pub unsafe fn from_encoded_bytes_unchecked(s: &[u8]) -> &Slice { + Slice::from_str(unsafe { str::from_utf8_unchecked(s) }) + } + + #[track_caller] + #[inline] + pub fn check_public_boundary(&self, index: usize) { + if !self.inner.is_char_boundary(index) { + panic!("byte index {index} is not an OsStr boundary"); + } + } + + #[inline] + pub fn from_str(s: &str) -> &Slice { + // SAFETY: Slice is just a wrapper over str. + unsafe { mem::transmute(s) } + } + + #[inline] + fn from_mut_str(s: &mut str) -> &mut Slice { + // SAFETY: Slice is just a wrapper over str. + unsafe { mem::transmute(s) } + } + + #[inline] + pub fn to_str(&self) -> Result<&str, crate::str::Utf8Error> { + Ok(&self.inner) + } + + #[inline] + pub fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(&self.inner) + } + + #[inline] + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_owned() } + } + + #[inline] + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn empty_box() -> Box { + let boxed: Box = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } +} + +#[unstable(feature = "clone_to_uninit", issue = "126799")] +unsafe impl CloneToUninit for Slice { + #[inline] + #[cfg_attr(debug_assertions, track_caller)] + unsafe fn clone_to_uninit(&self, dst: *mut u8) { + // SAFETY: we're just a transparent wrapper around [u8] + unsafe { self.inner.clone_to_uninit(dst) } + } +} diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 96da891874ef0..208755cd5b9c7 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,5 +1,6 @@ //! The underlying OsString/OsStr implementation on Windows is a //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. + use alloc::wtf8::{Wtf8, Wtf8Buf}; use core::clone::CloneToUninit; @@ -11,6 +12,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; #[derive(Hash)] +#[repr(transparent)] pub struct Buf { pub inner: Wtf8Buf, } @@ -213,11 +215,12 @@ impl Buf { /// # Safety /// /// The slice must be valid for the platform encoding (as described in - /// [`Slice::from_encoded_bytes_unchecked`]). + /// `OsStr::from_encoded_bytes_unchecked`). For this encoding, that means + /// `other` must be valid WTF-8. /// - /// This bypasses the WTF-8 surrogate joining, so either `self` must not - /// end with a leading surrogate half, or `other` must not start with a - /// trailing surrogate half. + /// Additionally, this method bypasses the WTF-8 surrogate joining, so + /// either `self` must not end with a leading surrogate half, or `other` + /// must not start with a trailing surrogate half. #[inline] pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { unsafe { diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index a6d821c99c1b5..974596a81a409 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1123,7 +1123,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_ty_pat(variant); } }, - TyPatKind::Err(_) => {}, + TyPatKind::NotNull | TyPatKind::Err(_) => {}, } } diff --git a/src/tools/miri/tests/pass/pattern-types.rs b/src/tools/miri/tests/pass/pattern-types.rs new file mode 100644 index 0000000000000..90fe0de546915 --- /dev/null +++ b/src/tools/miri/tests/pass/pattern-types.rs @@ -0,0 +1,18 @@ +#![feature(pattern_types, pattern_type_macro, sized_hierarchy)] +#![allow(dead_code)] + +use std::marker::PointeeSized; +use std::mem::transmute; + +pub struct NonNull { + pointer: std::pat::pattern_type!(*const T is !null), +} + +trait Trait {} +impl Trait for () {} + +fn main() { + unsafe { + let _: NonNull = NonNull { pointer: transmute(&mut () as *mut dyn Trait) }; + } +} diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 5dce9a0c8d0f2..242d8c23113c3 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1099,7 +1099,7 @@ impl Rewrite for ast::TyPat { } Ok(s) } - ast::TyPatKind::Err(_) => Err(RewriteError::Unknown), + ast::TyPatKind::NotNull | ast::TyPatKind::Err(_) => Err(RewriteError::Unknown), } } } diff --git a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr index 4b83bfff86333..aa96159aacf57 100644 --- a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr +++ b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr @@ -21,9 +21,11 @@ LL | .collect::(); | = help: the trait `FromIterator<()>` is not implemented for `String` = help: the following other types implement trait `FromIterator`: + `String` implements `FromIterator<&Char>` `String` implements `FromIterator<&char>` `String` implements `FromIterator<&str>` `String` implements `FromIterator>` + `String` implements `FromIterator` `String` implements `FromIterator>` `String` implements `FromIterator` `String` implements `FromIterator` diff --git a/tests/ui/type/pattern_types/bad_pat.rs b/tests/ui/type/pattern_types/bad_pat.rs index 549b0d11dd187..7e2b4cb996f27 100644 --- a/tests/ui/type/pattern_types/bad_pat.rs +++ b/tests/ui/type/pattern_types/bad_pat.rs @@ -10,4 +10,15 @@ type Positive2 = pattern_type!(i32 is 0..=); type Wild = pattern_type!(() is _); //~^ ERROR: pattern not supported in pattern types +// FIXME: confusing diagnostic because `not` can be a binding +type NonNull = pattern_type!(*const () is not null); +//~^ ERROR: expected one of `@` or `|`, found `null` +//~| ERROR: pattern not supported in pattern types + +type NonNull2 = pattern_type!(*const () is !nil); +//~^ ERROR: expected `null`, found `nil` + +// FIXME: reject with a type mismatch +type Mismatch2 = pattern_type!(() is !null); + fn main() {} diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr index d2a5a20bf89b6..e72279542280c 100644 --- a/tests/ui/type/pattern_types/bad_pat.stderr +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -30,6 +30,24 @@ error: pattern not supported in pattern types LL | type Wild = pattern_type!(() is _); | ^ -error: aborting due to 3 previous errors +error: pattern not supported in pattern types + --> $DIR/bad_pat.rs:14:43 + | +LL | type NonNull = pattern_type!(*const () is not null); + | ^^^ + +error: expected one of `@` or `|`, found `null` + --> $DIR/bad_pat.rs:14:47 + | +LL | type NonNull = pattern_type!(*const () is not null); + | ^^^^ expected one of `@` or `|` + +error: expected `null`, found `nil` + --> $DIR/bad_pat.rs:18:45 + | +LL | type NonNull2 = pattern_type!(*const () is !nil); + | ^^^ expected `null` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/type/pattern_types/non_null.rs b/tests/ui/type/pattern_types/non_null.rs new file mode 100644 index 0000000000000..7e86b8b684d17 --- /dev/null +++ b/tests/ui/type/pattern_types/non_null.rs @@ -0,0 +1,21 @@ +//! Show that pattern-types non-null is the same as libstd's + +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" +//@ only-64bit + +#![feature(pattern_type_macro, pattern_types, rustc_attrs)] + +use std::pat::pattern_type; + +#[rustc_layout(debug)] +type NonNull = pattern_type!(*const T is !null); //~ ERROR layout_of + +#[rustc_layout(debug)] +type Test = Option>; //~ ERROR layout_of + +#[rustc_layout(debug)] +type Wide = pattern_type!(*const [u8] is !null); //~ ERROR layout_of + +const _: () = assert!(size_of::>() == size_of::>>()); + +fn main() {} diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr new file mode 100644 index 0000000000000..ad61e9a591473 --- /dev/null +++ b/tests/ui/type/pattern_types/non_null.stderr @@ -0,0 +1,218 @@ +error: layout_of((*const T) is !null) = Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + } + --> $DIR/non_null.rs:11:1 + | +LL | type NonNull = pattern_type!(*const T is !null); + | ^^^^^^^^^^^^^^^ + +error: layout_of(Option<(*const ()) is !null>) = Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: (..=0) | (1..), + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Multiple { + tag: Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: (..=0) | (1..), + }, + tag_encoding: Niche { + untagged_variant: 1, + niche_variants: 0..=0, + niche_start: 0, + }, + tag_field: 0, + variants: [ + Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + ), + uninhabited: false, + variants: Single { + index: 1, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + ], + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + } + --> $DIR/non_null.rs:14:1 + | +LL | type Test = Option>; + | ^^^^^^^^^ + +error: layout_of((*const [u8]) is !null) = Layout { + size: Size(16 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: ScalarPair( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + Initialized { + value: Int( + I64, + false, + ), + valid_range: 0..=18446744073709551615, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 1..=18446744073709551615, + }, + ), + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + } + --> $DIR/non_null.rs:17:1 + | +LL | type Wide = pattern_type!(*const [u8] is !null); + | ^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/type/pattern_types/or_patterns.stderr b/tests/ui/type/pattern_types/or_patterns.stderr index a417e502e3562..adc7320829b95 100644 --- a/tests/ui/type/pattern_types/or_patterns.stderr +++ b/tests/ui/type/pattern_types/or_patterns.stderr @@ -53,7 +53,14 @@ error: layout_of((i8) is (i8::MIN..=-1 | 1..)) = Layout { valid_range: 1..=255, }, ), - fields: Primitive, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, largest_niche: Some( Niche { offset: Size(0 bytes), @@ -91,7 +98,14 @@ error: layout_of((i8) is (i8::MIN..=-2 | 0..)) = Layout { valid_range: 0..=254, }, ), - fields: Primitive, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, largest_niche: Some( Niche { offset: Size(0 bytes), diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index abd8b87fffc6f..c9a846e22f581 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -57,7 +57,14 @@ error: layout_of((u32) is 1..) = Layout { valid_range: 1..=4294967295, }, ), - fields: Primitive, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, largest_niche: Some( Niche { offset: Size(0 bytes), @@ -390,7 +397,14 @@ error: layout_of((i8) is -10..=10) = Layout { valid_range: (..=10) | (246..), }, ), - fields: Primitive, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, largest_niche: Some( Niche { offset: Size(0 bytes), @@ -428,7 +442,14 @@ error: layout_of((i8) is i8::MIN..=0) = Layout { valid_range: (..=0) | (128..), }, ), - fields: Primitive, + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, largest_niche: Some( Niche { offset: Size(0 bytes), diff --git a/tests/ui/type/pattern_types/unsize.rs b/tests/ui/type/pattern_types/unsize.rs new file mode 100644 index 0000000000000..1020dd8c21544 --- /dev/null +++ b/tests/ui/type/pattern_types/unsize.rs @@ -0,0 +1,18 @@ +//! Show that pattern-types with pointer base types can be part of unsizing coercions + +//@ check-pass + +#![feature(pattern_type_macro, pattern_types)] + +use std::pat::pattern_type; + +type NonNull = pattern_type!(*const T is !null); + +trait Trait {} +impl Trait for u32 {} +impl Trait for i32 {} + +fn main() { + let x: NonNull = unsafe { std::mem::transmute(std::ptr::dangling::()) }; + let x: NonNull = x; +}