diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 2910ba7c46851..797e929c304ce 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1287,11 +1287,22 @@ impl<'a, T: Idx> Iterator for MixedBitIter<'a, T> { /// /// All operations that involve an element will panic if the element is equal /// to or greater than the domain size. -#[derive(Clone, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct GrowableBitSet { bit_set: DenseBitSet, } +// Manually implemented to forward `clone_from`, and to avoid the `T: Clone` bound. +impl Clone for GrowableBitSet { + fn clone(&self) -> Self { + Self { bit_set: self.bit_set.clone() } + } + + fn clone_from(&mut self, source: &Self) { + self.bit_set.clone_from(&source.bit_set); + } +} + impl Default for GrowableBitSet { fn default() -> Self { GrowableBitSet::new_empty() diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index ec2c30756ddc0..0162aa838cb6b 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -3,7 +3,7 @@ use std::iter; use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{HasTargetSpec, RustcAbi, Target}; +use crate::spec::{Arch, HasTargetSpec, RustcAbi, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -166,10 +166,26 @@ where classify_ret(cx, &mut fn_abi.ret, kind); } - for arg in fn_abi.args.iter_mut() { + // On Arm64EC the variadic portion of a c-variadic call follows the MS x64 ABI: + // "Any argument that doesn't fit in 8 bytes, or is not 1, 2, 4, or 8 bytes, must + // be passed by reference". + let c_variadic = fn_abi.c_variadic; + let fixed_count = fn_abi.fixed_count as usize; + let is_arm64ec = cx.target_spec().arch == Arch::Arm64EC; + + for (idx, arg) in fn_abi.args.iter_mut().enumerate() { if arg.is_ignore() { continue; } + + if is_arm64ec && c_variadic && idx >= fixed_count { + let size = arg.layout.size.bytes(); + if size > 8 || !size.is_power_of_two() { + arg.make_indirect(); + continue; + } + } + classify_arg(cx, arg, kind); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index ff823594ce1c0..2a1f0e7935ea0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4487,6 +4487,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { tcx.disabled_nightly_features(err, [(String::new(), sym::trivial_bounds)]); } ObligationCauseCode::OpaqueReturnType(expr_info) => { + // Point at the method call in the returned expression's chain where an + // associated type diverged from what the signature's opaque type expects, + // regardless of how the failed predicate was derived from that expectation. + if let Some(typeck_results) = self.typeck_results.as_deref() { + let chain_expr = match expr_info { + Some((_, hir_id)) => Some(tcx.hir_expect_expr(hir_id)), + None => tcx.hir_node_by_def_id(body_def_id).body_id().and_then(|body_id| { + match tcx.hir_body(body_id).value.kind { + hir::ExprKind::Block(block, _) => block.expr, + _ => None, + } + }), + }; + if let Some(chain_expr) = chain_expr { + self.point_at_chain_in_return_position( + body_def_id, + chain_expr, + typeck_results, + param_env, + err, + ); + } + } let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info { let expr = tcx.hir_expect_expr(hir_id); (expr_ty, expr) @@ -5438,6 +5461,109 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { assocs_in_this_method } + /// When a `-> impl Trait` return type obligation fails, walk the method call + /// chain in the returned expression to point at where the associated type diverged from + /// what the signature expects. + /// + /// ```text + /// note: the method call chain might not have had the expected associated types + /// --> $DIR/invalid-iterator-chain-in-return-position.rs:16:18 + /// | + /// LL | x.iter_mut().map(foo) + /// | - ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + /// | | | + /// | | `Iterator::Item` is `&mut Vec` here + /// | this expression has type `Vec>` + /// ``` + fn point_at_chain_in_return_position( + &self, + body_def_id: LocalDefId, + expr: &hir::Expr<'_>, + typeck_results: &TypeckResults<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diag<'_, G>, + ) { + let tcx = self.tcx; + if !matches!(tcx.def_kind(body_def_id), DefKind::Fn | DefKind::AssocFn) { + return; + } + let output = + tcx.fn_sig(body_def_id).instantiate_identity().skip_norm_wip().output().skip_binder(); + let &ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id: opaque_def_id }, args, .. }) = + output.kind() + else { + return; + }; + + // The predicate that reaches here has been rewritten through the impls it was + // derived from (e.g. `Iterator for Map` turns `Iterator::Item` requirements + // into requirements on `F`'s return type), so the associated types the user wrote + // in the signature are recovered from the opaque's bounds instead. + let mut probe_diffs = vec![]; + for clause in tcx.item_bounds(opaque_def_id).instantiate(tcx, args).skip_norm_wip() { + let Some(proj) = clause.as_projection_clause() else { continue }; + let proj = self.instantiate_binder_with_fresh_vars( + expr.span, + BoundRegionConversionTime::FnCall, + proj, + ); + let Some(expected_term) = proj.term.as_type() else { continue }; + // Only the projection (for its `DefId`) is used when probing the chain; the + // bound's own term is carried in `found` for the divergence check below and + // is replaced with the probed type afterwards. + probe_diffs.push(TypeError::Sorts(ty::error::ExpectedFound { + expected: proj.projection_term.expect_ty().to_ty(tcx, ty::IsRigid::No), + found: expected_term, + })); + } + if probe_diffs.is_empty() { + return; + } + + // If the returned expression is a binding, walk the chain that created it instead. + let expr = if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let hir::Node::Pat(binding) = tcx.hir_node(*hir_id) + && let hir::Node::LetStmt(local) = tcx.parent_hir_node(binding.hir_id) + && let Some(binding_expr) = local.init + { + binding_expr + } else { + expr + }; + + // Resolve what each bound associated type actually is for the returned expression, + // and keep only the ones that diverged from the signature. + let expr_ty = self.resolve_vars_if_possible( + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), + ); + let assocs = self.probe_assoc_types_at_expr( + &probe_diffs, + expr.span, + expr_ty, + expr.hir_id, + param_env, + ); + let mut type_diffs = vec![]; + for (probe_diff, assoc) in iter::zip(probe_diffs, assocs) { + let TypeError::Sorts(ty::error::ExpectedFound { expected, found: expected_term }) = + probe_diff + else { + continue; + }; + let Some((_, (_, actual_ty))) = assoc else { continue }; + if !self.can_eq(param_env, expected_term, actual_ty) { + type_diffs.push(TypeError::Sorts(ty::error::ExpectedFound { + expected, + found: actual_ty, + })); + } + } + if !type_diffs.is_empty() { + self.point_at_chain(expr, typeck_results, type_diffs, param_env, err); + } + } + /// If the type that failed selection is an array or a reference to an array, /// but the trait is implemented for slices, suggest that the user converts /// the array into a slice. diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 19dd13967fdd6..ceec98c8659fb 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1,5 +1,6 @@ #![allow(unused_imports)] +use crate::cell::Cell; use crate::fmt::{self, Debug, Formatter}; struct PadAdapter<'buf, 'state> { @@ -50,6 +51,29 @@ impl fmt::Write for PadAdapter<'_, '_> { } } +/// Wraps an `FnOnce` formatting closure in a type that implements [`fmt::Debug`] by calling the +/// closure, allowing the `*_with` builder methods to forward to their `&dyn fmt::Debug` +/// counterparts. +/// +/// By doing this, the builder logic is monomorphized only once and not for every closure type +/// (see #149745). +/// +/// Formatting a `DebugOnce` consumes the closure, so attempting to format it more than once +/// panics. This never happens because the debug builders format each value exactly once. +struct DebugOnce(Cell>); + +impl fmt::Debug for DebugOnce +where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.take() { + Some(value_fmt) => value_fmt(f), + None => panic!("formatting closure called more than once"), + } + } +} + /// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted struct as a part of your @@ -130,18 +154,6 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut Self { - self.field_with(name, |f| value.fmt(f)) - } - - /// Adds a new field to the generated struct output. - /// - /// This method is equivalent to [`DebugStruct::field`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { if self.is_pretty() { if !self.has_fields { @@ -152,14 +164,14 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); writer.write_str(name)?; writer.write_str(": ")?; - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n") } else { let prefix = if self.has_fields { ", " } else { " { " }; self.fmt.write_str(prefix)?; self.fmt.write_str(name)?; self.fmt.write_str(": ")?; - value_fmt(self.fmt) + value.fmt(self.fmt) } }); @@ -167,6 +179,18 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { self } + /// Adds a new field to the generated struct output. + /// + /// This method is equivalent to [`DebugStruct::field`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn field_with(&mut self, name: &str, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.field(name, &DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Marks the struct as non-exhaustive, indicating to the reader that there are some other /// fields that are not shown in the debug representation. /// @@ -327,18 +351,6 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn field(&mut self, value: &dyn fmt::Debug) -> &mut Self { - self.field_with(|f| value.fmt(f)) - } - - /// Adds a new field to the generated tuple struct output. - /// - /// This method is equivalent to [`DebugTuple::field`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn field_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { if self.is_pretty() { if self.fields == 0 { @@ -347,12 +359,12 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { let mut slot = None; let mut state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n") } else { let prefix = if self.fields == 0 { "(" } else { ", " }; self.fmt.write_str(prefix)?; - value_fmt(self.fmt) + value.fmt(self.fmt) } }); @@ -360,6 +372,18 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> { self } + /// Adds a new field to the generated tuple struct output. + /// + /// This method is equivalent to [`DebugTuple::field`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn field_with(&mut self, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.field(&DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some /// other fields that are not shown in the debug representation. /// @@ -453,10 +477,7 @@ struct DebugInner<'a, 'b: 'a> { } impl<'a, 'b: 'a> DebugInner<'a, 'b> { - fn entry_with(&mut self, entry_fmt: F) - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { + fn entry(&mut self, entry: &dyn fmt::Debug) { self.result = self.result.and_then(|_| { if self.is_pretty() { if !self.has_fields { @@ -465,19 +486,26 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { let mut slot = None; let mut state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); - entry_fmt(&mut writer)?; + entry.fmt(&mut writer)?; writer.write_str(",\n") } else { if self.has_fields { self.fmt.write_str(", ")? } - entry_fmt(self.fmt) + entry.fmt(self.fmt) } }); self.has_fields = true; } + fn entry_with(&mut self, entry_fmt: F) + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.entry(&DebugOnce(Cell::new(Some(entry_fmt)))); + } + fn is_pretty(&self) -> bool { self.fmt.alternate() } @@ -546,7 +574,7 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self { - self.inner.entry_with(|f| entry.fmt(f)); + self.inner.entry(entry); self } @@ -738,7 +766,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { /// ``` #[stable(feature = "debug_builders", since = "1.2.0")] pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self { - self.inner.entry_with(|f| entry.fmt(f)); + self.inner.entry(entry); self } @@ -969,18 +997,6 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] pub fn key(&mut self, key: &dyn fmt::Debug) -> &mut Self { - self.key_with(|f| key.fmt(f)) - } - - /// Adds the key part of a new entry to the map output. - /// - /// This method is equivalent to [`DebugMap::key`], but formats the - /// key using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn key_with(&mut self, key_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { assert!( !self.has_key, @@ -995,13 +1011,13 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { let mut slot = None; self.state = Default::default(); let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); - key_fmt(&mut writer)?; + key.fmt(&mut writer)?; writer.write_str(": ")?; } else { if self.has_fields { self.fmt.write_str(", ")? } - key_fmt(self.fmt)?; + key.fmt(self.fmt)?; self.fmt.write_str(": ")?; } @@ -1012,6 +1028,18 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { self } + /// Adds the key part of a new entry to the map output. + /// + /// This method is equivalent to [`DebugMap::key`], but formats the + /// key using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn key_with(&mut self, key_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.key(&DebugOnce(Cell::new(Some(key_fmt)))) + } + /// Adds the value part of a new entry to the map output. /// /// This method, together with `key`, is an alternative to `entry` that @@ -1045,28 +1073,16 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// ``` #[stable(feature = "debug_map_key_value", since = "1.42.0")] pub fn value(&mut self, value: &dyn fmt::Debug) -> &mut Self { - self.value_with(|f| value.fmt(f)) - } - - /// Adds the value part of a new entry to the map output. - /// - /// This method is equivalent to [`DebugMap::value`], but formats the - /// value using a provided closure rather than by calling [`Debug::fmt`]. - #[unstable(feature = "debug_closure_helpers", issue = "117729")] - pub fn value_with(&mut self, value_fmt: F) -> &mut Self - where - F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, - { self.result = self.result.and_then(|_| { assert!(self.has_key, "attempted to format a map value before its key"); if self.is_pretty() { let mut slot = None; let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); - value_fmt(&mut writer)?; + value.fmt(&mut writer)?; writer.write_str(",\n")?; } else { - value_fmt(self.fmt)?; + value.fmt(self.fmt)?; } self.has_key = false; @@ -1077,6 +1093,18 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { self } + /// Adds the value part of a new entry to the map output. + /// + /// This method is equivalent to [`DebugMap::value`], but formats the + /// value using a provided closure rather than by calling [`Debug::fmt`]. + #[unstable(feature = "debug_closure_helpers", issue = "117729")] + pub fn value_with(&mut self, value_fmt: F) -> &mut Self + where + F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, + { + self.value(&DebugOnce(Cell::new(Some(value_fmt)))) + } + /// Adds the contents of an iterator of entries to the map output. /// /// # Examples diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 58c68408e6a80..c5da5055e16d9 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -1432,7 +1432,7 @@ pub fn log2f128(x: f128) -> f128 { libm::maybe_available::log2f128(x) } -/// Returns `a * b + c` for `f16` values. +/// Returns `a * b + c` without rounding the intermediate result for `f16` values. /// /// The stabilized version of this intrinsic is /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) @@ -1440,7 +1440,7 @@ pub fn log2f128(x: f128) -> f128 { #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; -/// Returns `a * b + c` for `f32` values. +/// Returns `a * b + c` without rounding the intermediate result for `f32` values. /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) @@ -1448,7 +1448,7 @@ pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; -/// Returns `a * b + c` for `f64` values. +/// Returns `a * b + c` without rounding the intermediate result for `f64` values. /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) @@ -1456,7 +1456,7 @@ pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64; -/// Returns `a * b + c` for `f128` values. +/// Returns `a * b + c` without rounding the intermediate result for `f128` values. /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) @@ -1475,9 +1475,12 @@ pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; +pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16 { + a * b + c +} /// Returns `a * b + c` for `f32` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1488,9 +1491,12 @@ pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; +pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32 { + a * b + c +} /// Returns `a * b + c` for `f64` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1501,9 +1507,12 @@ pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; +pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64 { + a * b + c +} /// Returns `a * b + c` for `f128` values, non-deterministically executing /// either a fused multiply-add or two operations with rounding of the /// intermediate result. @@ -1514,9 +1523,12 @@ pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64; /// and add instructions. It is unspecified whether or not a fused operation /// is selected, and that may depend on optimization level and context, for /// example. +#[inline] #[rustc_intrinsic] #[rustc_nounwind] -pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128; +pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128 { + a * b + c +} /// Returns the largest integer less than or equal to an `f16`. /// diff --git a/library/core/src/intrinsics/simd/mod.rs b/library/core/src/intrinsics/simd/mod.rs index 903fd77661b2d..e9488a6bdb263 100644 --- a/library/core/src/intrinsics/simd/mod.rs +++ b/library/core/src/intrinsics/simd/mod.rs @@ -109,13 +109,13 @@ pub const unsafe fn simd_rem(lhs: T, rhs: T) -> T; /// Shifts vector left elementwise, with UB on overflow. /// -/// Shifts `lhs` left by `rhs`, shifting in sign bits for signed types. +/// Shifts `lhs` left by `rhs`, shifting in zeros. /// /// `T` must be a vector of integers. /// /// # Safety /// -/// Each element of `rhs` must be less than `::BITS`. +/// Each element of `rhs` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_shl(lhs: T, rhs: T) -> T; @@ -128,7 +128,7 @@ pub const unsafe fn simd_shl(lhs: T, rhs: T) -> T; /// /// # Safety /// -/// Each element of `rhs` must be less than `::BITS`. +/// Each element of `rhs` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_shr(lhs: T, rhs: T) -> T; @@ -145,7 +145,7 @@ pub const unsafe fn simd_shr(lhs: T, rhs: T) -> T; /// /// # Safety /// -/// Each element of `shift` must be less than `::BITS`. +/// Each element of `shift` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_funnel_shl(a: T, b: T, shift: T) -> T; @@ -162,7 +162,7 @@ pub const unsafe fn simd_funnel_shl(a: T, b: T, shift: T) -> T; /// /// # Safety /// -/// Each element of `shift` must be less than `::BITS`. +/// Each element of `shift` must be in `0..::BITS`. #[rustc_intrinsic] #[rustc_nounwind] pub const unsafe fn simd_funnel_shr(a: T, b: T, shift: T) -> T; @@ -433,6 +433,9 @@ pub enum SimdAlign { /// # Safety /// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details. /// +/// Each pointer offset from `ptr` whose corresponding value in `mask` is `!0` must be readable as if +/// by [`ptr::read`][crate::ptr::read]. +/// /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] @@ -455,6 +458,9 @@ pub const unsafe fn simd_masked_load(mask: V, p /// # Safety /// `ptr` must be aligned according to the `ALIGN` parameter, see [`SimdAlign`] for details. /// +/// Each pointer offset from `ptr` whose corresponding value in `mask` is `!0` must be writable as if +/// by [`ptr::write`][crate::ptr::write]. +/// /// `mask` must only contain `0` or `!0` values. #[rustc_intrinsic] #[rustc_nounwind] diff --git a/src/doc/book b/src/doc/book index dd7ab4f4f4541..917544888a55e 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit dd7ab4f4f4541adf4aa2a872cdac06c206b73288 +Subproject commit 917544888a55e4da7109bdba8c88c893c0da70f4 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index 53686db907c45..5a14f7276ebc0 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit 53686db907c45268d1b323afd9a3545a37abbced +Subproject commit 5a14f7276ebc0bd100974f02b918f30472c782fc diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index a8c27c5615007..27e3c49c36ef9 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -336,9 +336,6 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - // old nightly-only choices that are going away soon - "toolchain-shared-resources" => Ok(Self::HtmlStaticFiles), - "invocation-specific" => Ok(Self::HtmlNonStaticFiles), // modern choices "html-static-files" => Ok(Self::HtmlStaticFiles), "html-non-static-files" => Ok(Self::HtmlNonStaticFiles), diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index ad3c41bdd2180..56059c6619954 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -11,7 +11,7 @@ //! contents, so they do not include a hash in their filename and are not safe to //! cache with `Cache-Control: immutable`. They include the contents of the //! --resource-suffix flag and are emitted when --emit-type is empty (default) -//! or contains "invocation-specific". +//! or contains "html-non-static-files". use std::cell::RefCell; use std::ffi::{OsStr, OsString}; diff --git a/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs b/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs index 40d16886c6d7d..d3698fda5fe85 100644 --- a/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs +++ b/tests/assembly-llvm/simd-intrinsic-mask-reduce.rs @@ -1,12 +1,16 @@ // verify that simd mask reductions do not introduce additional bit shift operations //@ add-minicore -//@ revisions: x86 aarch64 +//@ revisions: x86 aarch64-llvm22 aarch64 //@ [x86] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel // Set the base cpu explicitly, in case the default has been changed. //@ [x86] compile-flags: -C target-cpu=x86-64 //@ [x86] needs-llvm-components: x86 +//@ [aarch64-llvm22] compile-flags: --target=aarch64-unknown-linux-gnu +//@ [aarch64-llvm22] needs-llvm-components: aarch64 +//@ [aarch64-llvm22] max-llvm-major-version: 22 //@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 +//@ [aarch64] min-llvm-version: 23 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -Copt-level=3 -C panic=abort @@ -33,12 +37,19 @@ pub unsafe extern "C" fn mask_reduce_all(m: mask8x16) -> bool { // x86-NEXT: {{cmp ax, -1|cmp eax, 65535|xor eax, 65535}} // x86-NEXT: sete al // + // aarch64-llvm22-NOT: shl + // aarch64-llvm22: cmge v0.16b, v0.16b, #0 + // aarch64-llvm22-DAG: mov [[REG1:[a-z0-9]+]], #1 + // aarch64-llvm22-DAG: umaxv b0, v0.16b + // aarch64-llvm22-NEXT: fmov [[REG2:[a-z0-9]+]], s0 + // aarch64-llvm22-NEXT: bic w0, [[REG1]], [[REG2]] + // // aarch64-NOT: shl // aarch64: cmge v0.16b, v0.16b, #0 - // aarch64-DAG: mov [[REG1:[a-z0-9]+]], #1 - // aarch64-DAG: umaxv b0, v0.16b - // aarch64-NEXT: fmov [[REG2:[a-z0-9]+]], s0 - // aarch64-NEXT: bic w0, [[REG1]], [[REG2]] + // aarch64-NEXT: addp [[REG1:d[0-9]+]], v0.2d + // aarch64-NEXT: fmov [[REG2:x[0-9]+]], [[REG1]] + // aarch64-NEXT: cmp [[REG2]], #0 + // aarch64-NEXT: cset w0, eq simd_reduce_all(m) } @@ -50,10 +61,17 @@ pub unsafe extern "C" fn mask_reduce_any(m: mask8x16) -> bool { // x86-NEXT: test eax, eax // x86-NEXT: setne al // + // aarch64-llvm22-NOT: shl + // aarch64-llvm22: cmlt v0.16b, v0.16b, #0 + // aarch64-llvm22-NEXT: umaxv b0, v0.16b + // aarch64-llvm22-NEXT: fmov [[REG:[a-z0-9]+]], s0 + // aarch64-llvm22-NEXT: and w0, [[REG]], #0x1 + // // aarch64-NOT: shl // aarch64: cmlt v0.16b, v0.16b, #0 - // aarch64-NEXT: umaxv b0, v0.16b - // aarch64-NEXT: fmov [[REG:[a-z0-9]+]], s0 - // aarch64-NEXT: and w0, [[REG]], #0x1 + // aarch64-NEXT: addp [[REG1:d[0-9]+]], v0.2d + // aarch64-NEXT: fmov [[REG2:x[0-9]+]], [[REG1]] + // aarch64-NEXT: cmp [[REG2]], #0 + // aarch64-NEXT: cset w0, ne simd_reduce_any(m) } diff --git a/tests/assembly-llvm/slice-is_ascii.rs b/tests/assembly-llvm/slice-is_ascii.rs index e53cd5160cf56..6d660e78147c2 100644 --- a/tests/assembly-llvm/slice-is_ascii.rs +++ b/tests/assembly-llvm/slice-is_ascii.rs @@ -6,6 +6,11 @@ //@ only-x86_64 //@ ignore-sgx +// No longer optimizes as desired, tracked at: +// https://github.com/rust-lang/rust/issues/154141 +// https://github.com/llvm/llvm-project/issues/209216 +//@ max-llvm-major-version: 22 + #![feature(str_internals)] // CHECK-LABEL: is_ascii_simple_demo: diff --git a/tests/codegen-llvm/arm64ec-c-variadic-i128.rs b/tests/codegen-llvm/arm64ec-c-variadic-i128.rs new file mode 100644 index 0000000000000..b98463469eee3 --- /dev/null +++ b/tests/codegen-llvm/arm64ec-c-variadic-i128.rs @@ -0,0 +1,39 @@ +//! Verify the arm64ec calling convention for `i128` passed through the variadic +//! portion of a C-variadic call. +//! +//! On arm64ec the variadic tail follows the MS x64 ABI: any argument that does not +//! fit in 8 bytes, or is not 1/2/4/8 bytes, is passed by reference. So a variadic +//! `i128`/`u128` is passed indirectly (as a pointer), which must stay in sync with +//! how `va_arg` reads it back. A *fixed* `i128` argument is still passed by value. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 --target arm64ec-pc-windows-msvc +//@ needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + +extern "C" { + fn variadic(fixed: u32, ...); + fn fixed(arg: i128); +} + +// A variadic `i128` argument is passed by reference (as a pointer). +#[no_mangle] +pub unsafe extern "C" fn pass_variadic_i128(x: i128) { + // CHECK-LABEL: @pass_variadic_i128( + // CHECK: call void (i32, ...) @variadic(i32 {{.*}}, ptr {{.*}}) + variadic(0, x); +} + +// A fixed `i128` argument is still passed by value. +#[no_mangle] +pub unsafe extern "C" fn pass_fixed_i128(x: i128) { + // CHECK-LABEL: @pass_fixed_i128( + // CHECK: call void @fixed(i128 + fixed(x); +} diff --git a/tests/codegen-llvm/asm/global_asm.rs b/tests/codegen-llvm/asm/global_asm.rs index 32075daa3cf23..8c1c4a3b5881f 100644 --- a/tests/codegen-llvm/asm/global_asm.rs +++ b/tests/codegen-llvm/asm/global_asm.rs @@ -7,10 +7,10 @@ use std::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm +// CHECK-LABEL: foo // this regex will capture the correct unconditional branch inst. -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK: "{{[[:space:]]+}}jmp baz" global_asm!( r#" .global foo diff --git a/tests/codegen-llvm/asm/global_asm_include.rs b/tests/codegen-llvm/asm/global_asm_include.rs index 98be9c3e33322..cad9901336bf3 100644 --- a/tests/codegen-llvm/asm/global_asm_include.rs +++ b/tests/codegen-llvm/asm/global_asm_include.rs @@ -7,9 +7,9 @@ use std::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK-LABEL: foo +// CHECK: "{{[[:space:]]+}}jmp baz" global_asm!(include_str!("foo.s")); extern "C" { diff --git a/tests/codegen-llvm/asm/global_asm_x2.rs b/tests/codegen-llvm/asm/global_asm_x2.rs index 9e3a00f068053..8c6f38289de33 100644 --- a/tests/codegen-llvm/asm/global_asm_x2.rs +++ b/tests/codegen-llvm/asm/global_asm_x2.rs @@ -8,12 +8,12 @@ use core::arch::global_asm; -// CHECK-LABEL: foo // CHECK: module asm -// CHECK: module asm "{{[[:space:]]+}}jmp baz" +// CHECK-LABEL: foo +// CHECK: "{{[[:space:]]+}}jmp baz" // any other global_asm will be appended to this first block, so: // CHECK-LABEL: bar -// CHECK: module asm "{{[[:space:]]+}}jmp quux" +// CHECK: "{{[[:space:]]+}}jmp quux" global_asm!( r#" .global foo diff --git a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs index 5834255f3d313..c3090a20e4d14 100644 --- a/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs +++ b/tests/codegen-llvm/issues/issue-107681-unwrap_unchecked.rs @@ -14,7 +14,6 @@ pub unsafe fn foo(x: &mut Copied>) -> u32 { // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: [[RET:%.*]] = load i32, ptr - // CHECK-NEXT: assume - // CHECK-NEXT: ret i32 [[RET]] + // CHECK: ret i32 [[RET]] x.next().unwrap_unchecked() } diff --git a/tests/debuginfo/function-call.rs b/tests/debuginfo/function-call.rs index 37eda165d99ca..104b43012a83e 100644 --- a/tests/debuginfo/function-call.rs +++ b/tests/debuginfo/function-call.rs @@ -1,5 +1,9 @@ // This test does not passed with gdb < 8.0. See #53497. -//@ min-gdb-version: 10.1 +// +// This test seems to have become very flaky with "Couldn't write extended state status: Bad +// address." since around June 2026, where CI typically uses GDB 12.1 on Ubuntu 22.04. I tried +// running this locally with GDB 15.1 and could not reproduce the flakiness. See #159073. +//@ min-gdb-version: 15.1 //@ compile-flags:-g //@ ignore-backends: gcc diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs index 9841dce27fa2f..326aaa54bbc03 100644 --- a/tests/run-make/emit-shared-files/rmake.rs +++ b/tests/run-make/emit-shared-files/rmake.rs @@ -12,7 +12,7 @@ use run_make_support::{has_extension, has_prefix, path, rustdoc, shallow_find_fi fn main() { rustdoc() .arg("-Zunstable-options") - .arg("--emit=invocation-specific") + .arg("--emit=html-non-static-files") .out_dir("invocation-only") .arg("--resource-suffix=-xxx") .args(&["--theme", "y.css"]) diff --git a/tests/run-make/pgo-branch-weights/filecheck-patterns.txt b/tests/run-make/pgo-branch-weights/filecheck-patterns.txt index 70d5a645c1454..ac9de0b16e49d 100644 --- a/tests/run-make/pgo-branch-weights/filecheck-patterns.txt +++ b/tests/run-make/pgo-branch-weights/filecheck-patterns.txt @@ -2,16 +2,16 @@ # First, establish that certain !prof labels are attached to the expected # functions and branching instructions. -CHECK: define void @function_called_twice(i32 {{.*}} !prof [[function_called_twice_id:![0-9]+]] { +CHECK: define void @function_called_twice(i32 {{.*}} !prof [[function_called_twice_id:![0-9]+]] CHECK: br i1 {{.*}}, label {{.*}}, label {{.*}}, !prof [[branch_weights0:![0-9]+]] -CHECK: define void @function_called_42_times(i32{{.*}} %c) {{.*}} !prof [[function_called_42_times_id:![0-9]+]] { +CHECK: define void @function_called_42_times(i32{{.*}} %c) {{.*}} !prof [[function_called_42_times_id:![0-9]+]] CHECK: switch i32 %c, label {{.*}} [ CHECK-NEXT: i32 97, label {{.*}} CHECK-NEXT: i32 98, label {{.*}} CHECK-NEXT: ], !prof [[branch_weights1:![0-9]+]] -CHECK: define void @function_called_never(i32 {{.*}} !prof [[function_called_never_id:![0-9]+]] { +CHECK: define void @function_called_never(i32 {{.*}} !prof [[function_called_never_id:![0-9]+]] diff --git a/tests/run-make/pgo-embed-bc-lto/interesting.rs b/tests/run-make/pgo-embed-bc-lto/interesting.rs index 13105c17e126d..94c7621457917 100644 --- a/tests/run-make/pgo-embed-bc-lto/interesting.rs +++ b/tests/run-make/pgo-embed-bc-lto/interesting.rs @@ -10,7 +10,7 @@ pub fn function_called_once() { } // CHECK-LABEL: @function_called_once -// CHECK-SAME: !prof [[function_called_once_id:![0-9]+]] { +// CHECK-SAME: !prof [[function_called_once_id:![0-9]+]] // CHECK: "CG Profile" // CHECK-NOT: "CG Profile" // CHECK-DAG: [[function_called_once_id]] = !{!"function_entry_count", i64 1} diff --git a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs index 5a612fd130052..13c62906f1687 100644 --- a/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs @@ -9,7 +9,7 @@ fn main() { scrape::scrape( &["--scrape-tests", "--emit=dep-info"], - &["--emit=dep-info,invocation-specific"], + &["--emit=dep-info,html-non-static-files"], ); let content = rfs::read_to_string("rustdoc/foobar.d").replace(r"\", "/"); diff --git a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr index 8b172cd5ca133..f685b01cd8cc3 100644 --- a/tests/ui/associated-type-bounds/duplicate-bound-err.stderr +++ b/tests/ui/associated-type-bounds/duplicate-bound-err.stderr @@ -107,6 +107,14 @@ LL | fn foo() -> impl Iterator { ... LL | [2u32].into_iter() | ------------------ return type was inferred to be `std::array::IntoIter` here + | +note: the method call chain might not have had the expected associated types + --> $DIR/duplicate-bound-err.rs:110:16 + | +LL | [2u32].into_iter() + | ------ ^^^^^^^^^^^ `Iterator::Item` is `u32` here + | | + | this expression has type `[u32; 1]` error[E0271]: expected `impl Iterator` to be an iterator that yields `i32`, but it yields `u32` --> $DIR/duplicate-bound-err.rs:107:17 diff --git a/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs b/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs new file mode 100644 index 0000000000000..c87438818e17d --- /dev/null +++ b/tests/ui/iterators/invalid-iterator-chain-in-return-position.rs @@ -0,0 +1,39 @@ +//! Check that a `-> impl Iterator` return type mismatch points at the +//! method call in the returned expression's chain where `Iterator::Item` diverged +//! from the signature's expectation, instead of only pointing at the signature. +//! Regression test for . + +fn foo(items: &mut Vec) { + items.sort(); +} + +fn bar() -> impl Iterator { + //~^ ERROR expected `foo` to return `i32`, but it returns `()` + let mut x: Vec> = vec![ + vec![0, 2, 1], + vec![5, 4, 3], + ]; + x.iter_mut().map(foo) +} + +fn baz() -> impl Iterator { + //~^ ERROR expected `foo` to return `i32`, but it returns `()` + let mut x: Vec> = vec![ + vec![0, 2, 1], + vec![5, 4, 3], + ]; + let it = x.iter_mut().map(foo); + it +} + +fn chained() -> impl Iterator { + //~^ ERROR expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + let x = vec![0u32, 1, 2]; + x.into_iter().filter(|x| *x > 0).map(|x| x.checked_add(1)).flatten() +} + +fn main() { + bar(); + baz(); + chained(); +} diff --git a/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr b/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr new file mode 100644 index 0000000000000..a21acc3dd84b3 --- /dev/null +++ b/tests/ui/iterators/invalid-iterator-chain-in-return-position.stderr @@ -0,0 +1,69 @@ +error[E0271]: expected `foo` to return `i32`, but it returns `()` + --> $DIR/invalid-iterator-chain-in-return-position.rs:10:13 + | +LL | fn bar() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` +... +LL | x.iter_mut().map(foo) + | --------------------- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here + | + = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:16:18 + | +LL | let mut x: Vec> = vec![ + | _______________________________- +LL | | vec![0, 2, 1], +LL | | vec![5, 4, 3], +LL | | ]; + | |_____- this expression has type `Vec>` +LL | x.iter_mut().map(foo) + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here + +error[E0271]: expected `foo` to return `i32`, but it returns `()` + --> $DIR/invalid-iterator-chain-in-return-position.rs:19:13 + | +LL | fn baz() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` +... +LL | it + | -- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here + | + = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:25:27 + | +LL | let mut x: Vec> = vec![ + | _______________________________- +LL | | vec![0, 2, 1], +LL | | vec![5, 4, 3], +LL | | ]; + | |_____- this expression has type `Vec>` +LL | let it = x.iter_mut().map(foo); + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here + +error[E0271]: expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` + --> $DIR/invalid-iterator-chain-in-return-position.rs:29:17 + | +LL | fn chained() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32` + | +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain-in-return-position.rs:32:7 + | +LL | let x = vec![0u32, 1, 2]; + | ---------------- this expression has type `Vec` +LL | x.into_iter().filter(|x| *x > 0).map(|x| x.checked_add(1)).flatten() + | ^^^^^^^^^^^ ------------------ ------------------------- ^^^^^^^^^ `Iterator::Item` changed to `u32` here + | | | | + | | | `Iterator::Item` changed to `Option` here + | | `Iterator::Item` remains `u32` here + | `Iterator::Item` is `u32` here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/lint/issue-106991.stderr b/tests/ui/lint/issue-106991.stderr index 15e0ba5337f14..5c31c240dfff5 100644 --- a/tests/ui/lint/issue-106991.stderr +++ b/tests/ui/lint/issue-106991.stderr @@ -8,6 +8,15 @@ LL | x.iter_mut().map(foo) | --------------------- return type was inferred to be `Map>, for<'a> fn(&'a mut Vec) {foo}>` here | = note: required for `Map>, for<'a> fn(&'a mut Vec) {foo}>` to implement `Iterator` +note: the method call chain might not have had the expected associated types + --> $DIR/issue-106991.rs:8:18 + | +LL | let mut x: Vec> = vec![vec![0, 2, 1], vec![5, 4, 3]]; + | ---------------------------------- this expression has type `Vec>` +LL | x.iter_mut().map(foo) + | ---------- ^^^^^^^^ `Iterator::Item` changed to `()` here + | | + | `Iterator::Item` is `&mut Vec` here error: aborting due to 1 previous error