diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index a9697950e38b..ab2c3bdba85f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -602,8 +602,14 @@ pub(super) fn borrowed_content_source( BorrowedContentSource::DerefRawPointer } else if base_ty.is_mutable_ptr() { BorrowedContentSource::DerefMutableRef - } else { + } else if base_ty.is_ref() { BorrowedContentSource::DerefSharedRef + } else { + // Custom type implementing `Deref` (e.g. `MyBox`, `Rc`, `Arc`) + // that wasn't detected via the MIR init trace above. This can happen + // when the deref base is initialized by a regular statement rather than + // a `TerminatorKind::Call` with `CallSource::OverloadedOperator`. + BorrowedContentSource::OverloadedDeref(base_ty) } } @@ -1002,6 +1008,14 @@ struct CapturedMessageOpt { maybe_reinitialized_locations_is_empty: bool, } +/// Tracks whether [`MirBorrowckCtxt::explain_captures`] emitted a clone +/// suggestion, so callers can avoid emitting redundant suggestions downstream. +#[derive(Copy, Clone, PartialEq, Eq)] +pub(super) enum CloneSuggestion { + Emitted, + NotEmitted, +} + impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// Finds the spans associated to a move or copy of move_place at location. pub(super) fn move_spans( @@ -1226,7 +1240,7 @@ fn explain_captures( move_spans: UseSpans<'tcx>, moved_place: Place<'tcx>, msg_opt: CapturedMessageOpt, - ) { + ) -> CloneSuggestion { let CapturedMessageOpt { is_partial_move: is_partial, is_loop_message, @@ -1235,6 +1249,7 @@ fn explain_captures( has_suggest_reborrow, maybe_reinitialized_locations_is_empty, } = msg_opt; + let mut suggested_cloning = false; if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { let place_name = self .describe_place(moved_place.as_ref()) @@ -1461,10 +1476,26 @@ fn explain_captures( has_sugg = true; } if let Some(clone_trait) = tcx.lang_items().clone_trait() { - let sugg = if moved_place + // Check whether the deref is from a custom Deref impl + // (e.g. Rc, Box) or a built-in reference deref. + // For built-in derefs with Clone fully satisfied, we skip + // the UFCS suggestion here and let `suggest_cloning` + // downstream emit a simpler `.clone()` suggestion instead. + let has_overloaded_deref = + moved_place.iter_projections().any(|(place, elem)| { + matches!(elem, ProjectionElem::Deref) + && matches!( + self.borrowed_content_source(place), + BorrowedContentSource::OverloadedDeref(_) + | BorrowedContentSource::OverloadedIndex(_) + ) + }); + + let has_deref = moved_place .iter_projections() - .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)) - { + .any(|(_, elem)| matches!(elem, ProjectionElem::Deref)); + + let sugg = if has_deref { let (start, end) = if let Some(expr) = self.find_expr(move_span) && let Some(_) = self.clone_on_reference(expr) && let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind @@ -1490,43 +1521,58 @@ fn explain_captures( self.infcx.param_env, ) && !has_sugg { - let msg = match &errors[..] { - [] => "you can `clone` the value and consume it, but this \ - might not be your desired behavior" - .to_string(), - [error] => { - format!( - "you could `clone` the value and consume it, if the \ - `{}` trait bound could be satisfied", - error.obligation.predicate, - ) - } - _ => { - format!( - "you could `clone` the value and consume it, if the \ - following trait bounds could be satisfied: {}", - listify(&errors, |e: &FulfillmentError<'tcx>| format!( - "`{}`", - e.obligation.predicate - )) - .unwrap(), - ) - } - }; - err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); - for error in errors { - if let FulfillmentErrorCode::Select( - SelectionError::Unimplemented, - ) = error.code - && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( - pred, - )) = error.obligation.predicate.kind().skip_binder() - { - self.infcx.err_ctxt().suggest_derive( - &error.obligation, - err, - error.obligation.predicate.kind().rebind(pred), - ); + let skip_for_simple_clone = + has_deref && !has_overloaded_deref && errors.is_empty(); + if !skip_for_simple_clone { + let msg = match &errors[..] { + [] => "you can `clone` the value and consume it, but \ + this might not be your desired behavior" + .to_string(), + [error] => { + format!( + "you could `clone` the value and consume it, if \ + the `{}` trait bound could be satisfied", + error.obligation.predicate, + ) + } + _ => { + format!( + "you could `clone` the value and consume it, if \ + the following trait bounds could be satisfied: \ + {}", + listify( + &errors, + |e: &FulfillmentError<'tcx>| format!( + "`{}`", + e.obligation.predicate + ) + ) + .unwrap(), + ) + } + }; + err.multipart_suggestion( + msg, + sugg, + Applicability::MaybeIncorrect, + ); + + suggested_cloning = errors.is_empty(); + + for error in errors { + if let FulfillmentErrorCode::Select( + SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); + } } } } @@ -1558,6 +1604,7 @@ fn explain_captures( }) } } + if suggested_cloning { CloneSuggestion::Emitted } else { CloneSuggestion::NotEmitted } } /// Skip over locals that begin with an underscore or have no name diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 0d0147e5eb51..9aa0bc1b745d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -14,7 +14,9 @@ use tracing::debug; use crate::MirBorrowckCtxt; -use crate::diagnostics::{CapturedMessageOpt, DescribePlaceOpt, UseSpans}; +use crate::diagnostics::{ + BorrowedContentSource, CapturedMessageOpt, CloneSuggestion, DescribePlaceOpt, UseSpans, +}; use crate::prefixes::PrefixSet; #[derive(Debug)] @@ -269,14 +271,19 @@ fn report(&mut self, error: GroupedMoveError<'tcx>) { .span_delayed_bug(span, "Type may implement copy, but there is no other error."); return; } + + let mut has_clone_suggestion = CloneSuggestion::NotEmitted; let mut err = match kind { - &IllegalMoveOriginKind::BorrowedContent { target_place } => self - .report_cannot_move_from_borrowed_content( + &IllegalMoveOriginKind::BorrowedContent { target_place } => { + let (diag, clone_sugg) = self.report_cannot_move_from_borrowed_content( original_path, target_place, span, use_spans, - ), + ); + has_clone_suggestion = clone_sugg; + diag + } &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => { self.cannot_move_out_of_interior_of_drop(span, ty) } @@ -285,7 +292,7 @@ fn report(&mut self, error: GroupedMoveError<'tcx>) { } }; - self.add_move_hints(error, &mut err, span); + self.add_move_hints(error, &mut err, span, has_clone_suggestion); self.buffer_error(err); } @@ -426,7 +433,7 @@ fn report_cannot_move_from_borrowed_content( deref_target_place: Place<'tcx>, span: Span, use_spans: Option>, - ) -> Diag<'infcx> { + ) -> (Diag<'infcx>, CloneSuggestion) { let tcx = self.infcx.tcx; // Inspect the type of the content behind the // borrow to provide feedback about why this @@ -447,8 +454,8 @@ fn report_cannot_move_from_borrowed_content( let decl = &self.body.local_decls[local]; let local_name = self.local_name(local).map(|sym| format!("`{sym}`")); if decl.is_ref_for_guard() { - return self - .cannot_move_out_of( + return ( + self.cannot_move_out_of( span, &format!( "{} in pattern guard", @@ -458,9 +465,14 @@ fn report_cannot_move_from_borrowed_content( .with_note( "variables bound in patterns cannot be moved from \ until after the end of the pattern guard", - ); + ), + CloneSuggestion::NotEmitted, + ); } else if decl.is_ref_to_static() { - return self.report_cannot_move_from_static(move_place, span); + return ( + self.report_cannot_move_from_static(move_place, span), + CloneSuggestion::NotEmitted, + ); } } @@ -539,10 +551,12 @@ fn report_cannot_move_from_borrowed_content( has_suggest_reborrow: false, maybe_reinitialized_locations_is_empty: true, }; - if let Some(use_spans) = use_spans { - self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt); - } - err + let suggested_cloning = if let Some(use_spans) = use_spans { + self.explain_captures(&mut err, span, span, use_spans, move_place, msg_opt) + } else { + CloneSuggestion::NotEmitted + }; + (err, suggested_cloning) } fn report_closure_move_error( @@ -676,7 +690,49 @@ fn closure_clause_kind( } } - fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) { + /// Suggest cloning via UFCS when a move occurs through a custom `Deref` impl. + /// + /// A simple `.clone()` on a type like `MyBox>` would clone the wrapper, + /// not the inner `Vec`. Instead, we suggest ` as Clone>::clone(&val)`. + fn suggest_cloning_through_overloaded_deref( + &self, + err: &mut Diag<'_>, + ty: Ty<'tcx>, + span: Span, + ) -> CloneSuggestion { + let tcx = self.infcx.tcx; + let Some(clone_trait) = tcx.lang_items().clone_trait() else { + return CloneSuggestion::NotEmitted; + }; + let Some(errors) = + self.infcx.type_implements_trait_shallow(clone_trait, ty, self.infcx.param_env) + else { + return CloneSuggestion::NotEmitted; + }; + + if !errors.is_empty() { + return CloneSuggestion::NotEmitted; + } + let sugg = vec![ + (span.shrink_to_lo(), format!("<{ty} as Clone>::clone(&")), + (span.shrink_to_hi(), ")".to_string()), + ]; + err.multipart_suggestion( + "you can `clone` the value and consume it, but this might not be \ + your desired behavior", + sugg, + Applicability::MaybeIncorrect, + ); + CloneSuggestion::Emitted + } + + fn add_move_hints( + &self, + error: GroupedMoveError<'tcx>, + err: &mut Diag<'_>, + span: Span, + has_clone_suggestion: CloneSuggestion, + ) { match error { GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => { self.add_borrow_suggestions(err, span, !binds_to.is_empty()); @@ -719,14 +775,43 @@ fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span None => "value".to_string(), }; - if let Some(expr) = self.find_expr(use_span) { - self.suggest_cloning( - err, - original_path.as_ref(), - place_ty, - expr, - Some(use_spans), - ); + if has_clone_suggestion == CloneSuggestion::NotEmitted { + // Check if the move is directly through a custom Deref impl + // (e.g. `*my_box` where MyBox implements Deref). + // A simple `.clone()` would clone the wrapper type rather than + // the inner value, so we need a UFCS suggestion instead. + // + // However, if there are further projections after the deref + // (e.g. `(*rc).field`), the value accessed is already the inner + // type and a simple `.clone()` works correctly. + let needs_ufcs = original_path.projection.last() + == Some(&ProjectionElem::Deref) + && original_path.iter_projections().any(|(place, elem)| { + matches!(elem, ProjectionElem::Deref) + && matches!( + self.borrowed_content_source(place), + BorrowedContentSource::OverloadedDeref(_) + | BorrowedContentSource::OverloadedIndex(_) + ) + }); + + let emitted_ufcs = if needs_ufcs { + self.suggest_cloning_through_overloaded_deref(err, place_ty, use_span) + } else { + CloneSuggestion::NotEmitted + }; + + if emitted_ufcs == CloneSuggestion::NotEmitted { + if let Some(expr) = self.find_expr(use_span) { + self.suggest_cloning( + err, + original_path.as_ref(), + place_ty, + expr, + Some(use_spans), + ); + } + } } if let Some(upvar_field) = self diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index a698db543759..92d19820848b 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -2,6 +2,7 @@ use rustc_ast::tokenstream::TokenStreamIter; use rustc_ast::{NodeId, tokenstream}; use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; use rustc_feature::Features; use rustc_session::Session; use rustc_session::parse::feature_err; @@ -88,16 +89,17 @@ fn parse( continue; }; - // Push a metavariable with no fragment specifier at the given span - let mut missing_fragment_specifier = |span| { + let fallback_metavar_decl = + |span| TokenTree::MetaVarDecl { span, name: ident, kind: NonterminalKind::TT }; + // Emit a missing-fragment diagnostic and return a `TokenTree` fallback so parsing can + // continue. + let missing_fragment_specifier = |span, add_span| { sess.dcx().emit_err(errors::MissingFragmentSpecifier { span, - add_span: span.shrink_to_hi(), + add_span, valid: VALID_FRAGMENT_NAMES_MSG, }); - - // Fall back to a `TokenTree` since that will match anything if we continue expanding. - result.push(TokenTree::MetaVarDecl { span, name: ident, kind: NonterminalKind::TT }); + fallback_metavar_decl(span) }; // Not consuming the next token immediately, as it may not be a colon @@ -112,13 +114,39 @@ fn parse( // since if it's not a token then it will be an invalid declaration. let Some(tokenstream::TokenTree::Token(token, _)) = iter.next() else { // Invalid, return a nice source location as `var:` - missing_fragment_specifier(colon_span.with_lo(start_sp.lo())); + result.push(missing_fragment_specifier( + colon_span.with_lo(start_sp.lo()), + colon_span.shrink_to_hi(), + )); continue; }; let Some((fragment, _)) = token.ident() else { // No identifier for the fragment specifier; - missing_fragment_specifier(token.span); + if token.kind == token::Dollar + && iter.peek().is_some_and(|next| { + matches!( + next, + tokenstream::TokenTree::Token(next_token, _) + if next_token.ident().is_some() + ) + }) + { + let mut err = + sess.dcx().struct_span_err(token.span, "missing fragment specifier"); + err.note("fragment specifiers must be provided"); + err.help(VALID_FRAGMENT_NAMES_MSG); + err.span_suggestion_verbose( + token.span, + "fragment specifiers should not be prefixed with `$`", + "", + Applicability::MaybeIncorrect, + ); + err.emit(); + result.push(fallback_metavar_decl(token.span)); + } else { + result.push(missing_fragment_specifier(token.span, token.span.shrink_to_hi())); + } continue; }; @@ -146,7 +174,7 @@ fn parse( } else { // Whether it's none or some other tree, it doesn't belong to // the current meta variable, returning the original span. - missing_fragment_specifier(start_sp); + result.push(missing_fragment_specifier(start_sp, start_sp.shrink_to_hi())); } } result diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 63ee69322eef..e968ed640ecf 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -47,13 +47,21 @@ fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { let mut patch = Vec::new(); for (bb, bb_data) in traversal::preorder(body) { - if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind { + if let TerminatorKind::Call { ref args, ref destination, .. } = bb_data.terminator().kind { let loc = Location { block: bb, statement_index: bb_data.statements.len() }; // Position ourselves between the evaluation of `args` and the write to `destination`. live.seek_to_block_end(bb); let mut state = live.get().clone(); + // Don't turn into a move if the local is used as an index + // projection for the destination place. + LivenessTransferFunction(&mut state).visit_place( + destination, + visit::PlaceContext::MutatingUse(visit::MutatingUseContext::Call), + loc, + ); + for (index, arg) in args.iter().map(|a| &a.node).enumerate().rev() { if let Operand::Copy(place) = *arg && !place.is_indirect() diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 49b57d4791de..953c7d9e3d86 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -339,14 +339,78 @@ impl f128 { #[unstable(feature = "float_exact_integer_constants", issue = "152466")] pub const MIN_EXACT_INTEGER: i128 = -Self::MAX_EXACT_INTEGER; - /// Sign bit - pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; + /// The mask of the bit used to encode the sign of an [`f128`]. + /// + /// This bit is set when the sign is negative and unset when the sign is + /// positive. + /// If you only need to check whether a value is positive or negative, + /// [`is_sign_positive`] or [`is_sign_negative`] can be used. + /// + /// [`is_sign_positive`]: f128::is_sign_positive + /// [`is_sign_negative`]: f128::is_sign_negative + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f128)] + /// # #[cfg(target_has_reliable_f128)] { + /// let sign_mask = f128::SIGN_MASK; + /// let a = 1.6552f128; + /// let a_bits = a.to_bits(); + /// + /// assert_eq!(a_bits & sign_mask, 0x0); + /// assert_eq!(f128::from_bits(a_bits ^ sign_mask), -a); + /// assert_eq!(sign_mask, (-0.0f128).to_bits()); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; - /// Exponent mask - pub(crate) const EXP_MASK: u128 = 0x7fff_0000_0000_0000_0000_0000_0000_0000; + /// The mask of the bits used to encode the exponent of an [`f128`]. + /// + /// Note that the exponent is stored as a biased value, with a bias of 16383 for `f128`. + /// + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f128)] + /// # #[cfg(target_has_reliable_f128)] { + /// fn get_exp(a: f128) -> i128 { + /// let bias = 16383; + /// let biased = a.to_bits() & f128::EXPONENT_MASK; + /// (biased >> (f128::MANTISSA_DIGITS - 1)).cast_signed() - bias + /// } + /// + /// assert_eq!(get_exp(0.5), -1); + /// assert_eq!(get_exp(1.0), 0); + /// assert_eq!(get_exp(2.0), 1); + /// assert_eq!(get_exp(4.0), 2); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const EXPONENT_MASK: u128 = 0x7fff_0000_0000_0000_0000_0000_0000_0000; - /// Mantissa mask - pub(crate) const MAN_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; + /// The mask of the bits used to encode the mantissa of an [`f128`]. + /// + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f128)] + /// # #[cfg(target_has_reliable_f128)] { + /// let mantissa_mask = f128::MANTISSA_MASK; + /// + /// assert_eq!(0f128.to_bits() & mantissa_mask, 0x0); + /// assert_eq!(1f128.to_bits() & mantissa_mask, 0x0); + /// + /// // multiplying a finite value by a power of 2 doesn't change its mantissa + /// // unless the result or initial value is not normal. + /// let a = 1.6552f128; + /// let b = 4.0 * a; + /// assert_eq!(a.to_bits() & mantissa_mask, b.to_bits() & mantissa_mask); + /// + /// // The maximum and minimum values have a saturated significand + /// assert_eq!(f128::MAX.to_bits() & f128::MANTISSA_MASK, f128::MANTISSA_MASK); + /// assert_eq!(f128::MIN.to_bits() & f128::MANTISSA_MASK, f128::MANTISSA_MASK); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const MANTISSA_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u128 = 0x1; @@ -511,9 +575,9 @@ pub const fn is_normal(self) -> bool { #[must_use] pub const fn classify(self) -> FpCategory { let bits = self.to_bits(); - match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) { - (0, Self::EXP_MASK) => FpCategory::Infinite, - (_, Self::EXP_MASK) => FpCategory::Nan, + match (bits & Self::MANTISSA_MASK, bits & Self::EXPONENT_MASK) { + (0, Self::EXPONENT_MASK) => FpCategory::Infinite, + (_, Self::EXPONENT_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e3f546926f48..e4f0d08feb8e 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -333,14 +333,80 @@ impl f16 { #[unstable(feature = "float_exact_integer_constants", issue = "152466")] pub const MIN_EXACT_INTEGER: i16 = -Self::MAX_EXACT_INTEGER; - /// Sign bit - pub(crate) const SIGN_MASK: u16 = 0x8000; + /// The mask of the bit used to encode the sign of an [`f16`]. + /// + /// This bit is set when the sign is negative and unset when the sign is + /// positive. + /// If you only need to check whether a value is positive or negative, + /// [`is_sign_positive`] or [`is_sign_negative`] can be used. + /// + /// [`is_sign_positive`]: f16::is_sign_positive + /// [`is_sign_negative`]: f16::is_sign_negative + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f16)] + /// # #[cfg(target_has_reliable_f16)] { + /// let sign_mask = f16::SIGN_MASK; + /// let a = 1.6552f16; + /// let a_bits = a.to_bits(); + /// + /// assert_eq!(a_bits & sign_mask, 0x0); + /// assert_eq!(f16::from_bits(a_bits ^ sign_mask), -a); + /// assert_eq!(sign_mask, (-0.0f16).to_bits()); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const SIGN_MASK: u16 = 0x8000; - /// Exponent mask - pub(crate) const EXP_MASK: u16 = 0x7c00; + /// The mask of the bits used to encode the exponent of an [`f16`]. + /// + /// Note that the exponent is stored as a biased value, with a bias of 15 for `f16`. + /// + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f16)] + /// # #[cfg(target_has_reliable_f16)] { + /// let exponent_mask = f16::EXPONENT_MASK; + /// + /// fn get_exp(a: f16) -> i16 { + /// let bias = 15; + /// let biased = a.to_bits() & f16::EXPONENT_MASK; + /// (biased >> (f16::MANTISSA_DIGITS - 1)).cast_signed() - bias + /// } + /// + /// assert_eq!(get_exp(0.5), -1); + /// assert_eq!(get_exp(1.0), 0); + /// assert_eq!(get_exp(2.0), 1); + /// assert_eq!(get_exp(4.0), 2); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const EXPONENT_MASK: u16 = 0x7c00; - /// Mantissa mask - pub(crate) const MAN_MASK: u16 = 0x03ff; + /// The mask of the bits used to encode the mantissa of an [`f16`]. + /// + /// ```rust + /// #![feature(float_masks)] + /// #![feature(f16)] + /// # #[cfg(target_has_reliable_f16)] { + /// let mantissa_mask = f16::MANTISSA_MASK; + /// + /// assert_eq!(0f16.to_bits() & mantissa_mask, 0x0); + /// assert_eq!(1f16.to_bits() & mantissa_mask, 0x0); + /// + /// // multiplying a finite value by a power of 2 doesn't change its mantissa + /// // unless the result or initial value is not normal. + /// let a = 1.6552f16; + /// let b = 4.0 * a; + /// assert_eq!(a.to_bits() & mantissa_mask, b.to_bits() & mantissa_mask); + /// + /// // The maximum and minimum values have a saturated significand + /// assert_eq!(f16::MAX.to_bits() & f16::MANTISSA_MASK, f16::MANTISSA_MASK); + /// assert_eq!(f16::MIN.to_bits() & f16::MANTISSA_MASK, f16::MANTISSA_MASK); + /// # } + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const MANTISSA_MASK: u16 = 0x03ff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u16 = 0x1; @@ -503,9 +569,9 @@ pub const fn is_normal(self) -> bool { #[must_use] pub const fn classify(self) -> FpCategory { let b = self.to_bits(); - match (b & Self::MAN_MASK, b & Self::EXP_MASK) { - (0, Self::EXP_MASK) => FpCategory::Infinite, - (_, Self::EXP_MASK) => FpCategory::Nan, + match (b & Self::MANTISSA_MASK, b & Self::EXPONENT_MASK) { + (0, Self::EXPONENT_MASK) => FpCategory::Infinite, + (_, Self::EXPONENT_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index ac4eedaefbdc..f9cb7cc650f4 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -572,14 +572,69 @@ impl f32 { #[unstable(feature = "float_exact_integer_constants", issue = "152466")] pub const MIN_EXACT_INTEGER: i32 = -Self::MAX_EXACT_INTEGER; - /// Sign bit - pub(crate) const SIGN_MASK: u32 = 0x8000_0000; + /// The mask of the bit used to encode the sign of an [`f32`]. + /// + /// This bit is set when the sign is negative and unset when the sign is + /// positive. + /// If you only need to check whether a value is positive or negative, + /// [`is_sign_positive`] or [`is_sign_negative`] can be used. + /// + /// [`is_sign_positive`]: f32::is_sign_positive + /// [`is_sign_negative`]: f32::is_sign_negative + /// ```rust + /// #![feature(float_masks)] + /// let sign_mask = f32::SIGN_MASK; + /// let a = 1.6552f32; + /// let a_bits = a.to_bits(); + /// + /// assert_eq!(a_bits & sign_mask, 0x0); + /// assert_eq!(f32::from_bits(a_bits ^ sign_mask), -a); + /// assert_eq!(sign_mask, (-0.0f32).to_bits()); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const SIGN_MASK: u32 = 0x8000_0000; - /// Exponent mask - pub(crate) const EXP_MASK: u32 = 0x7f80_0000; + /// The mask of the bits used to encode the exponent of an [`f32`]. + /// + /// Note that the exponent is stored as a biased value, with a bias of 127 for `f32`. + /// + /// ```rust + /// #![feature(float_masks)] + /// fn get_exp(a: f32) -> i32 { + /// let bias = 127; + /// let biased = a.to_bits() & f32::EXPONENT_MASK; + /// (biased >> (f32::MANTISSA_DIGITS - 1)).cast_signed() - bias + /// } + /// + /// assert_eq!(get_exp(0.5), -1); + /// assert_eq!(get_exp(1.0), 0); + /// assert_eq!(get_exp(2.0), 1); + /// assert_eq!(get_exp(4.0), 2); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const EXPONENT_MASK: u32 = 0x7f80_0000; - /// Mantissa mask - pub(crate) const MAN_MASK: u32 = 0x007f_ffff; + /// The mask of the bits used to encode the mantissa of an [`f32`]. + /// + /// ```rust + /// #![feature(float_masks)] + /// let mantissa_mask = f32::MANTISSA_MASK; + /// + /// assert_eq!(0f32.to_bits() & mantissa_mask, 0x0); + /// assert_eq!(1f32.to_bits() & mantissa_mask, 0x0); + /// + /// // multiplying a finite value by a power of 2 doesn't change its mantissa + /// // unless the result or initial value is not normal. + /// let a = 1.6552f32; + /// let b = 4.0 * a; + /// assert_eq!(a.to_bits() & mantissa_mask, b.to_bits() & mantissa_mask); + /// + /// // The maximum and minimum values have a saturated significand + /// assert_eq!(f32::MAX.to_bits() & f32::MANTISSA_MASK, f32::MANTISSA_MASK); + /// assert_eq!(f32::MIN.to_bits() & f32::MANTISSA_MASK, f32::MANTISSA_MASK); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const MANTISSA_MASK: u32 = 0x007f_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u32 = 0x1; @@ -731,9 +786,9 @@ pub const fn classify(self) -> FpCategory { // of our tests is able to find any difference between the complicated and the naive // version, so now we are back to the naive version. let b = self.to_bits(); - match (b & Self::MAN_MASK, b & Self::EXP_MASK) { - (0, Self::EXP_MASK) => FpCategory::Infinite, - (_, Self::EXP_MASK) => FpCategory::Nan, + match (b & Self::MANTISSA_MASK, b & Self::EXPONENT_MASK) { + (0, Self::EXPONENT_MASK) => FpCategory::Infinite, + (_, Self::EXPONENT_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 8682774aa3ae..f24213f909b5 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -571,14 +571,69 @@ impl f64 { #[unstable(feature = "float_exact_integer_constants", issue = "152466")] pub const MIN_EXACT_INTEGER: i64 = -Self::MAX_EXACT_INTEGER; - /// Sign bit - pub(crate) const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + /// The mask of the bit used to encode the sign of an [`f64`]. + /// + /// This bit is set when the sign is negative and unset when the sign is + /// positive. + /// If you only need to check whether a value is positive or negative, + /// [`is_sign_positive`] or [`is_sign_negative`] can be used. + /// + /// [`is_sign_positive`]: f64::is_sign_positive + /// [`is_sign_negative`]: f64::is_sign_negative + /// ```rust + /// #![feature(float_masks)] + /// let sign_mask = f64::SIGN_MASK; + /// let a = 1.6552f64; + /// let a_bits = a.to_bits(); + /// + /// assert_eq!(a_bits & sign_mask, 0x0); + /// assert_eq!(f64::from_bits(a_bits ^ sign_mask), -a); + /// assert_eq!(sign_mask, (-0.0f64).to_bits()); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const SIGN_MASK: u64 = 0x8000_0000_0000_0000; - /// Exponent mask - pub(crate) const EXP_MASK: u64 = 0x7ff0_0000_0000_0000; + /// The mask of the bits used to encode the exponent of an [`f64`]. + /// + /// Note that the exponent is stored as a biased value, with a bias of 1024 for `f64`. + /// + /// ```rust + /// #![feature(float_masks)] + /// fn get_exp(a: f64) -> i64 { + /// let bias = 1023; + /// let biased = a.to_bits() & f64::EXPONENT_MASK; + /// (biased >> (f64::MANTISSA_DIGITS - 1)).cast_signed() - bias + /// } + /// + /// assert_eq!(get_exp(0.5), -1); + /// assert_eq!(get_exp(1.0), 0); + /// assert_eq!(get_exp(2.0), 1); + /// assert_eq!(get_exp(4.0), 2); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const EXPONENT_MASK: u64 = 0x7ff0_0000_0000_0000; - /// Mantissa mask - pub(crate) const MAN_MASK: u64 = 0x000f_ffff_ffff_ffff; + /// The mask of the bits used to encode the mantissa of an [`f64`]. + /// + /// ```rust + /// #![feature(float_masks)] + /// let mantissa_mask = f64::MANTISSA_MASK; + /// + /// assert_eq!(0f64.to_bits() & mantissa_mask, 0x0); + /// assert_eq!(1f64.to_bits() & mantissa_mask, 0x0); + /// + /// // multiplying a finite value by a power of 2 doesn't change its mantissa + /// // unless the result or initial value is not normal. + /// let a = 1.6552f64; + /// let b = 4.0 * a; + /// assert_eq!(a.to_bits() & mantissa_mask, b.to_bits() & mantissa_mask); + /// + /// // The maximum and minimum values have a saturated significand + /// assert_eq!(f64::MAX.to_bits() & f64::MANTISSA_MASK, f64::MANTISSA_MASK); + /// assert_eq!(f64::MIN.to_bits() & f64::MANTISSA_MASK, f64::MANTISSA_MASK); + /// ``` + #[unstable(feature = "float_masks", issue = "154064")] + pub const MANTISSA_MASK: u64 = 0x000f_ffff_ffff_ffff; /// Minimum representable positive value (min subnormal) const TINY_BITS: u64 = 0x1; @@ -730,9 +785,9 @@ pub const fn classify(self) -> FpCategory { // of our tests is able to find any difference between the complicated and the naive // version, so now we are back to the naive version. let b = self.to_bits(); - match (b & Self::MAN_MASK, b & Self::EXP_MASK) { - (0, Self::EXP_MASK) => FpCategory::Infinite, - (_, Self::EXP_MASK) => FpCategory::Nan, + match (b & Self::MANTISSA_MASK, b & Self::EXPONENT_MASK) { + (0, Self::EXPONENT_MASK) => FpCategory::Infinite, + (_, Self::EXPONENT_MASK) => FpCategory::Nan, (0, 0) => FpCategory::Zero, (_, 0) => FpCategory::Subnormal, _ => FpCategory::Normal, diff --git a/library/core/src/num/imp/traits.rs b/library/core/src/num/imp/traits.rs index 7b84f7a4a5aa..3a7ea23f73f3 100644 --- a/library/core/src/num/imp/traits.rs +++ b/library/core/src/num/imp/traits.rs @@ -204,8 +204,8 @@ impl Float for f16 { const BITS: u32 = 16; const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; - const EXP_MASK: Self::Int = Self::EXP_MASK; - const SIG_MASK: Self::Int = Self::MAN_MASK; + const EXP_MASK: Self::Int = Self::EXPONENT_MASK; + const SIG_MASK: Self::Int = Self::MANTISSA_MASK; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22; const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; @@ -238,8 +238,8 @@ impl Float for f32 { const BITS: u32 = 32; const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; - const EXP_MASK: Self::Int = Self::EXP_MASK; - const SIG_MASK: Self::Int = Self::MAN_MASK; + const EXP_MASK: Self::Int = Self::EXPONENT_MASK; + const SIG_MASK: Self::Int = Self::MANTISSA_MASK; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -17; const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; @@ -271,8 +271,8 @@ impl Float for f64 { const BITS: u32 = 64; const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS; - const EXP_MASK: Self::Int = Self::EXP_MASK; - const SIG_MASK: Self::Int = Self::MAN_MASK; + const EXP_MASK: Self::Int = Self::EXPONENT_MASK; + const SIG_MASK: Self::Int = Self::MANTISSA_MASK; const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -4; const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23; diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index babcafb024e0..b933e668c26a 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2448,17 +2448,7 @@ pub(crate) fn update_submodule<'a>( return; } - // Submodule updating actually happens during in the dry run mode. We need to make sure that - // all the git commands below are actually executed, because some follow-up code - // in bootstrap might depend on the submodules being checked out. Furthermore, not all - // the command executions below work with an empty output (produced during dry run). - // Therefore, all commands below are marked with `run_in_dry_run()`, so that they also run in - // dry run mode. - let submodule_git = || { - let mut cmd = helpers::git(Some(&absolute_path)); - cmd.run_in_dry_run(); - cmd - }; + let submodule_git = || helpers::git(Some(&absolute_path)); // Determine commit checked out in submodule. let checked_out_hash = @@ -2466,7 +2456,7 @@ pub(crate) fn update_submodule<'a>( let checked_out_hash = checked_out_hash.trim_end(); // Determine commit that the submodule *should* have. let recorded = helpers::git(Some(dwn_ctx.src)) - .run_in_dry_run() + .run_in_dry_run() // otherwise parsing `actual_hash` fails .args(["ls-tree", "HEAD"]) .arg(relative_path) .run_capture_stdout(dwn_ctx.exec_ctx) @@ -2482,11 +2472,12 @@ pub(crate) fn update_submodule<'a>( return; } - println!("Updating submodule {relative_path}"); + if !dwn_ctx.exec_ctx.dry_run() { + println!("Updating submodule {relative_path}"); + }; helpers::git(Some(dwn_ctx.src)) .allow_failure() - .run_in_dry_run() .args(["submodule", "-q", "sync"]) .arg(relative_path) .run(dwn_ctx.exec_ctx); @@ -2497,12 +2488,10 @@ pub(crate) fn update_submodule<'a>( // even though that has no relation to the upstream for the submodule. let current_branch = helpers::git(Some(dwn_ctx.src)) .allow_failure() - .run_in_dry_run() .args(["symbolic-ref", "--short", "HEAD"]) .run_capture(dwn_ctx.exec_ctx); let mut git = helpers::git(Some(dwn_ctx.src)).allow_failure(); - git.run_in_dry_run(); if current_branch.is_success() { // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name. // This syntax isn't accepted by `branch.{branch}`. Strip it. diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 6af11d9ae0a8..fd3d129c231d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -676,6 +676,10 @@ pub fn require_submodule(&self, submodule: &str, err_hint: Option<&str>) { return; } + if self.config.dry_run() { + return; + } + // When testing bootstrap itself, it is much faster to ignore // submodules. Almost all Steps work fine without their submodules. if cfg!(test) && !self.config.submodules() { diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index ef3f94ee8353..65f20210323e 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -108,8 +108,6 @@ class DefaultSyntheticProvider: return self.valobj.GetNumChildren() def get_child_index(self, name: str) -> int: - if self.is_ptr and name == "$$dereference$$": - return self.valobj.Dereference().GetSyntheticValue() return self.valobj.GetIndexOfChildWithName(name) def get_child_at_index(self, index: int) -> Optional[SBValue]: @@ -133,13 +131,17 @@ class IndirectionSyntheticProvider: return 1 def get_child_index(self, name: str) -> int: - if self.is_ptr and name == "$$dereference$$": + if name == "$$dereference$$": return 0 return -1 def get_child_at_index(self, index: int) -> Optional[SBValue]: if index == 0: - return self.valobj.Dereference().GetSyntheticValue() + value = self.valobj.Dereference() + if (synth := value.GetSyntheticValue()).IsValid(): + return synth + else: + return value return None def update(self): @@ -266,7 +268,7 @@ def aggregate_field_summary(valobj: SBValue, _dict) -> Generator[str, None, None if summary is None: summary = child.value if summary is None: - if is_tuple_fields(child): + if is_tuple_fields(child.GetType().fields): summary = TupleSummaryProvider(child, _dict) else: summary = StructSummaryProvider(child, _dict) @@ -569,7 +571,9 @@ class ClangEncodedEnumProvider: self.variant = all_variants.GetChildAtIndex(index) self.value = self.variant.GetChildMemberWithName( ClangEncodedEnumProvider.VALUE_MEMBER_NAME - ).GetSyntheticValue() + ) + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth def _getCurrentVariantIndex(self, all_variants: SBValue) -> int: default_index = 0 @@ -651,9 +655,10 @@ class MSVCEnumSyntheticProvider: ).GetValueAsUnsigned() if tag == discr: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth + return else: # if invalid, DISCR must be a range begin: int = ( @@ -671,16 +676,18 @@ class MSVCEnumSyntheticProvider: if begin < end: if begin <= tag <= end: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth + return else: if tag >= begin or tag <= end: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth + return else: # if invalid, tag is a 128 bit value tag_lo: int = self.valobj.GetChildMemberWithName( @@ -714,9 +721,9 @@ class MSVCEnumSyntheticProvider: discr: int = (exact_hi << 64) | exact_lo if tag == discr: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth return else: # if invalid, DISCR must be a range begin_lo: int = ( @@ -748,16 +755,16 @@ class MSVCEnumSyntheticProvider: if begin < end: if begin <= tag <= end: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth return else: if tag >= begin or tag <= end: self.variant = child - self.value = child.GetChildMemberWithName( - "value" - ).GetSyntheticValue() + self.value = child.GetChildMemberWithName("value") + if (synth := self.value.GetSyntheticValue()).IsValid(): + self.value = synth return def num_children(self) -> int: diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff new file mode 100644 index 000000000000..012dd7d88a5f --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-abort.diff @@ -0,0 +1,15 @@ +- // MIR for `move_index` before DeadStoreElimination-final ++ // MIR for `move_index` after DeadStoreElimination-final + + fn move_index(_1: [usize; 10], _2: usize) -> () { + let mut _0: (); + + bb0: { + _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff new file mode 100644 index 000000000000..fcd0ae43e289 --- /dev/null +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.move_index.DeadStoreElimination-final.panic-unwind.diff @@ -0,0 +1,15 @@ +- // MIR for `move_index` before DeadStoreElimination-final ++ // MIR for `move_index` after DeadStoreElimination-final + + fn move_index(_1: [usize; 10], _2: usize) -> () { + let mut _0: (); + + bb0: { + _1[_2] = passthrough_usize(copy _2) -> [return: bb1, unwind continue]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index 27b5ccdb936d..00a9a49c2abb 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -40,7 +40,31 @@ fn move_packed(packed: Packed) { } } +#[inline(never)] +fn passthrough_usize(a: usize) -> usize { + a +} + +// EMIT_MIR call_arg_copy.move_index.DeadStoreElimination-final.diff +#[custom_mir(dialect = "analysis")] +fn move_index(a: [usize; 10], b: usize) { + // CHECK-LABEL: fn move_index( + // CHECK: = passthrough_usize(copy _2) + mir! { + { + // The index is used again after the operand is evaluated to + // evaluate the destionation place, so the argument cannot be turned + // into a move. + Call(a[b] = passthrough_usize(b), ReturnTo(ret), UnwindContinue()) + } + ret = { + Return() + } + } +} + fn main() { move_simple(1); move_packed(Packed { x: 0, y: 1 }); + move_index([0; _], 1); } diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed index a19db7e5cd32..8d5ebbc77440 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.fixed @@ -2,6 +2,6 @@ use std::rc::Rc; pub fn main() { - let _x = as Clone>::clone(&Rc::new(vec![1, 2]).clone()).into_iter(); + let _x = as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); //~^ ERROR [E0507] } diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index 577c2de38be0..076f0ce3440a 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -12,10 +12,6 @@ help: you can `clone` the value and consume it, but this might not be your desir | LL | let _x = as Clone>::clone(&Rc::new(vec![1, 2])).into_iter(); | ++++++++++++++++++++++++++++ + -help: consider cloning the value if the performance cost is acceptable - | -LL | let _x = Rc::new(vec![1, 2]).clone().into_iter(); - | ++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/clone-span-on-try-operator.fixed b/tests/ui/borrowck/clone-span-on-try-operator.fixed index 59a162e72c17..86ce083da65e 100644 --- a/tests/ui/borrowck/clone-span-on-try-operator.fixed +++ b/tests/ui/borrowck/clone-span-on-try-operator.fixed @@ -7,5 +7,5 @@ impl Foo { } fn main() { let foo = &Foo; - ::clone(&foo.clone()).foo(); //~ ERROR cannot move out + foo.clone().foo(); //~ ERROR cannot move out } diff --git a/tests/ui/borrowck/clone-span-on-try-operator.stderr b/tests/ui/borrowck/clone-span-on-try-operator.stderr index c2c63f949436..4cf1de52a4a4 100644 --- a/tests/ui/borrowck/clone-span-on-try-operator.stderr +++ b/tests/ui/borrowck/clone-span-on-try-operator.stderr @@ -11,10 +11,6 @@ note: `Foo::foo` takes ownership of the receiver `self`, which moves `*foo` | LL | fn foo(self) {} | ^^^^ -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | ::clone(&(*foo)).foo(); - | +++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL - (*foo).foo(); diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed index 3b4f7c8465cf..c7ddef0f7bc7 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.fixed @@ -9,7 +9,7 @@ fn call(f: F) where F : Fn() { fn main() { let y = vec![format!("World")]; call(|| { - as Clone>::clone(&y.clone()).into_iter(); + y.clone().into_iter(); //~^ ERROR cannot move out of `y`, a captured variable in an `Fn` closure }); } diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 69c366749163..08cf52883983 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -17,10 +17,6 @@ LL | fn call(f: F) where F : Fn() { | ^^^^ note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | as Clone>::clone(&y).into_iter(); - | +++++++++++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | y.clone().into_iter(); diff --git a/tests/ui/macros/macro-missing-fragment.rs b/tests/ui/macros/macro-missing-fragment.rs index 7ed9074020e4..827c7fc31927 100644 --- a/tests/ui/macros/macro-missing-fragment.rs +++ b/tests/ui/macros/macro-missing-fragment.rs @@ -13,6 +13,10 @@ macro_rules! unused_macro { ( $name ) => {}; //~ ERROR missing fragment } +macro_rules! accidental_dollar_prefix { + ( $test:$tt ) => {}; //~ ERROR missing fragment +} + fn main() { used_arm!(); used_macro_unused_arm!(); diff --git a/tests/ui/macros/macro-missing-fragment.stderr b/tests/ui/macros/macro-missing-fragment.stderr index 886292378d1a..b1b9bf3d8aa9 100644 --- a/tests/ui/macros/macro-missing-fragment.stderr +++ b/tests/ui/macros/macro-missing-fragment.stderr @@ -37,5 +37,19 @@ help: try adding a specifier here LL | ( $name:spec ) => {}; | +++++ -error: aborting due to 3 previous errors +error: missing fragment specifier + --> $DIR/macro-missing-fragment.rs:17:13 + | +LL | ( $test:$tt ) => {}; + | ^ + | + = note: fragment specifiers must be provided + = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility +help: fragment specifiers should not be prefixed with `$` + | +LL - ( $test:$tt ) => {}; +LL + ( $test:tt ) => {}; + | + +error: aborting due to 4 previous errors diff --git a/tests/ui/moves/suggest-clone.fixed b/tests/ui/moves/suggest-clone.fixed index 59a162e72c17..86ce083da65e 100644 --- a/tests/ui/moves/suggest-clone.fixed +++ b/tests/ui/moves/suggest-clone.fixed @@ -7,5 +7,5 @@ impl Foo { } fn main() { let foo = &Foo; - ::clone(&foo.clone()).foo(); //~ ERROR cannot move out + foo.clone().foo(); //~ ERROR cannot move out } diff --git a/tests/ui/moves/suggest-clone.stderr b/tests/ui/moves/suggest-clone.stderr index f8e0ccdfceff..ec18d2abdec0 100644 --- a/tests/ui/moves/suggest-clone.stderr +++ b/tests/ui/moves/suggest-clone.stderr @@ -11,10 +11,6 @@ note: `Foo::foo` takes ownership of the receiver `self`, which moves `*foo` | LL | fn foo(self) {} | ^^^^ -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | ::clone(&foo).foo(); - | +++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | foo.clone().foo(); diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed index 0720146280f9..f36c36d7d645 100644 --- a/tests/ui/suggestions/option-content-move.fixed +++ b/tests/ui/suggestions/option-content-move.fixed @@ -7,7 +7,7 @@ impl LipogramCorpora { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_some() { - if as Clone>::clone(&selection.1.clone()).as_mut().as_ref().unwrap().contains(selection.0) { + if selection.1.clone().as_mut().as_ref().unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } @@ -25,7 +25,7 @@ impl LipogramCorpora2 { pub fn validate_all(&mut self) -> Result<(), char> { for selection in &self.selections { if selection.1.is_ok() { - if as Clone>::clone(&selection.1.clone()).as_mut().as_ref().unwrap().contains(selection.0) { + if selection.1.clone().as_mut().as_ref().unwrap().contains(selection.0) { //~^ ERROR cannot move out of `selection.1` return Err(selection.0); } diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index 09c6f39fd803..b514622699e7 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -16,10 +16,6 @@ help: consider calling `.as_mut()` to mutably borrow the value's contents | LL | if selection.1.as_mut().unwrap().contains(selection.0) { | +++++++++ -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { - | ++++++++++++++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | if selection.1.clone().unwrap().contains(selection.0) { @@ -43,10 +39,6 @@ help: consider calling `.as_mut()` to mutably borrow the value's contents | LL | if selection.1.as_mut().unwrap().contains(selection.0) { | +++++++++ -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | if as Clone>::clone(&selection.1).unwrap().contains(selection.0) { - | ++++++++++++++++++++++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | if selection.1.clone().unwrap().contains(selection.0) { diff --git a/tests/ui/suggestions/ufcs-for-deref-inner-clone.fixed b/tests/ui/suggestions/ufcs-for-deref-inner-clone.fixed new file mode 100644 index 000000000000..de85216e8880 --- /dev/null +++ b/tests/ui/suggestions/ufcs-for-deref-inner-clone.fixed @@ -0,0 +1,16 @@ +//@ run-rustfix +use std::ops::Deref; + +struct MyBox(T); + +impl Deref for MyBox { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub fn main() { + let _x = as Clone>::clone(&MyBox(vec![1, 2])).into_iter(); + //~^ ERROR cannot move out of dereference of `MyBox>` +} diff --git a/tests/ui/suggestions/ufcs-for-deref-inner-clone.rs b/tests/ui/suggestions/ufcs-for-deref-inner-clone.rs new file mode 100644 index 000000000000..dc0dd02f97a9 --- /dev/null +++ b/tests/ui/suggestions/ufcs-for-deref-inner-clone.rs @@ -0,0 +1,16 @@ +//@ run-rustfix +use std::ops::Deref; + +struct MyBox(T); + +impl Deref for MyBox { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub fn main() { + let _x = MyBox(vec![1, 2]).into_iter(); + //~^ ERROR cannot move out of dereference of `MyBox>` +} diff --git a/tests/ui/suggestions/ufcs-for-deref-inner-clone.stderr b/tests/ui/suggestions/ufcs-for-deref-inner-clone.stderr new file mode 100644 index 000000000000..7fde14f1a61b --- /dev/null +++ b/tests/ui/suggestions/ufcs-for-deref-inner-clone.stderr @@ -0,0 +1,14 @@ +error[E0507]: cannot move out of dereference of `MyBox>` + --> $DIR/ufcs-for-deref-inner-clone.rs:14:14 + | +LL | let _x = MyBox(vec![1, 2]).into_iter(); + | ^^^^^^^^^^^^^^^^^ move occurs because value has type `Vec`, which does not implement the `Copy` trait + | +help: you can `clone` the value and consume it, but this might not be your desired behavior + | +LL | let _x = as Clone>::clone(&MyBox(vec![1, 2])).into_iter(); + | ++++++++++++++++++++++++++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0507`.