diff options
165 files changed, 2880 insertions, 1121 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7be9978c0b..6d9e249ee44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,7 +410,7 @@ jobs: - name: dist-x86_64-msvc env: RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler" - SCRIPT: python x.py dist + SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 os: windows-latest-xl - name: dist-i686-msvc diff --git a/compiler/rustc/build.rs b/compiler/rustc/build.rs index 24c06c0ddbf..39cf3e094c8 100644 --- a/compiler/rustc/build.rs +++ b/compiler/rustc/build.rs @@ -5,6 +5,9 @@ fn main() { let target_env = env::var("CARGO_CFG_TARGET_ENV"); if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() { set_windows_exe_options(); + } else { + // Avoid rerunning the build script every time. + println!("cargo:rerun-if-changed=build.rs"); } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 2dcbd0782ef..fdf60e60914 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1159,6 +1159,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode: ParamMode, itctx: ImplTraitContext, ) -> hir::Ty<'hir> { + // Check whether we should interpret this as a bare trait object. + // This check mirrors the one in late resolution. We only introduce this special case in + // the rare occurence we need to lower `Fresh` anonymous lifetimes. + // The other cases when a qpath should be opportunistically made a trait object are handled + // by `ty_path`. + if qself.is_none() + && let Some(partial_res) = self.resolver.get_partial_res(t.id) + && partial_res.unresolved_segments() == 0 + && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res() + { + let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| { + let bound = this.lower_poly_trait_ref( + &PolyTraitRef { + bound_generic_params: vec![], + trait_ref: TraitRef { path: path.clone(), ref_id: t.id }, + span: t.span + }, + itctx, + ); + let bounds = this.arena.alloc_from_iter([bound]); + let lifetime_bound = this.elided_dyn_bound(t.span); + (bounds, lifetime_bound) + }); + let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None); + return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() }; + } + let id = self.lower_node_id(t.id); let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx); self.ty_path(id, t.span, qpath) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b9cfc3732dc..687ff0fb505 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -309,7 +309,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { )); // Check first whether the source is accessible (issue #87060) - if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) { err.span_note(deref_target, "deref defined here"); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index d296a1a0ac6..9c7671eee38 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -975,14 +975,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if self.fn_self_span_reported.insert(fn_span) { err.span_note( // Check whether the source is accessible - if self - .infcx - .tcx - .sess - .source_map() - .span_to_snippet(self_arg.span) - .is_ok() - { + if self.infcx.tcx.sess.source_map().is_span_accessible(self_arg.span) { self_arg.span } else { fn_call_span diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 92f0a7498e3..2e356f67bf3 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -9,6 +9,7 @@ mod memory; mod operand; mod operator; mod place; +mod projection; mod step; mod terminator; mod traits; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index d11ae7b4925..1465b986293 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -1,7 +1,6 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. -use std::convert::TryFrom; use std::fmt::Write; use rustc_hir::def::Namespace; @@ -15,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, - InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, }; @@ -254,6 +253,11 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { } #[inline] + pub fn uninit(layout: TyAndLayout<'tcx>) -> Self { + ImmTy { imm: Immediate::Uninit, layout } + } + + #[inline] pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> { Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) } @@ -280,6 +284,41 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> { } } +impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { + pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { + if self.layout.is_unsized() { + // There are no unsized immediates. + self.assert_mem_place().len(cx) + } else { + match self.layout.fields { + abi::FieldsShape::Array { count, .. } => Ok(count), + _ => bug!("len not supported on sized type {:?}", self.layout.ty), + } + } + } + + pub fn offset( + &self, + offset: Size, + meta: MemPlaceMeta<Tag>, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + match self.try_as_mplace() { + Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()), + Err(imm) => { + assert!( + matches!(*imm, Immediate::Uninit), + "Scalar/ScalarPair cannot be offset into" + ); + assert!(!meta.has_meta()); // no place to store metadata here + // Every part of an uninit is uninit. + Ok(ImmTy::uninit(layout).into()) + } + } + } +} + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. @@ -296,11 +335,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } let Some(alloc) = self.get_place_alloc(mplace)? else { - return Ok(Some(ImmTy { - // zero-sized type can be left uninit - imm: Immediate::Uninit, - layout: mplace.layout, - })); + // zero-sized type can be left uninit + return Ok(Some(ImmTy::uninit(mplace.layout))); }; // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point. @@ -367,6 +403,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// This flag exists only for validity checking. /// /// This is an internal function that should not usually be used; call `read_immediate` instead. + /// ConstProp needs it, though. pub fn read_immediate_raw( &self, src: &OpTy<'tcx, M::PointerTag>, @@ -421,123 +458,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(str) } - /// Projection functions - pub fn operand_field( - &self, - op: &OpTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - let base = match op.try_as_mplace() { - Ok(ref mplace) => { - // We can reuse the mplace field computation logic for indirect operands. - let field = self.mplace_field(mplace, field)?; - return Ok(field.into()); - } - Err(value) => value, - }; - - let field_layout = base.layout.field(self, field); - let offset = base.layout.fields.offset(field); - // This makes several assumptions about what layouts we will encounter; we match what - // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). - let field_val: Immediate<_> = match (*base, base.layout.abi) { - // the field contains no information, can be left uninit - _ if field_layout.is_zst() => Immediate::Uninit, - // the field covers the entire type - _ if field_layout.size == base.layout.size => { - assert!(match (base.layout.abi, field_layout.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => true, - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, - _ => false, - }); - assert!(offset.bytes() == 0); - *base - } - // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert!(matches!(field_layout.abi, Abi::Scalar(..))); - Immediate::from(if offset.bytes() == 0 { - debug_assert_eq!(field_layout.size, a.size(self)); - a_val - } else { - debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); - debug_assert_eq!(field_layout.size, b.size(self)); - b_val - }) - } - _ => span_bug!( - self.cur_span(), - "invalid field access on immediate {}, layout {:#?}", - base, - base.layout - ), - }; - - Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None }) - } - - pub fn operand_index( - &self, - op: &OpTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - if let Ok(index) = usize::try_from(index) { - // We can just treat this as a field. - self.operand_field(op, index) - } else { - // Indexing into a big array. This must be an mplace. - let mplace = op.assert_mem_place(); - Ok(self.mplace_index(&mplace, index)?.into()) - } - } - - pub fn operand_downcast( - &self, - op: &OpTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - Ok(match op.try_as_mplace() { - Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(), - Err(..) => { - // Downcasts only change the layout. - // (In particular, no check about whether this is even the active variant -- that's by design, - // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - let layout = op.layout.for_variant(self, variant); - OpTy { layout, ..*op } - } - }) - } - - #[instrument(skip(self), level = "debug")] - pub fn operand_projection( - &self, - base: &OpTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.operand_field(base, field.index())?, - Downcast(_, variant) => self.operand_downcast(base, variant)?, - Deref => self.deref_operand(base)?.into(), - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - // The rest should only occur as mplace, we do not use Immediates for types - // allowing such operations. This matches place_projection forcing an allocation. - let mplace = base.assert_mem_place(); - self.mplace_projection(&mplace, proj_elem)?.into() - } - }) - } - /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements. /// Also returns the number of elements. + /// + /// Can (but does not always) trigger UB if `op` is uninitialized. pub fn operand_to_simd( &self, - base: &OpTy<'tcx, M::PointerTag>, + op: &OpTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // This only works in memory, but repr(simd) types should never be immediates anyway. - assert!(base.layout.ty.is_simd()); - self.mplace_to_simd(&base.assert_mem_place()) + assert!(op.layout.ty.is_simd()); + match op.try_as_mplace() { + Ok(mplace) => self.mplace_to_simd(&mplace), + Err(imm) => match *imm { + Immediate::Uninit => { + throw_ub!(InvalidUninitBytes(None)) + } + Immediate::Scalar(..) | Immediate::ScalarPair(..) => { + bug!("arrays/slices can never have Scalar/ScalarPair layout") + } + }, + } } /// Read from a local. Will not actually access the local if reading from a ZST. @@ -582,30 +524,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// avoid allocations. pub fn eval_place_to_op( &self, - place: mir::Place<'tcx>, + mir_place: mir::Place<'tcx>, layout: Option<TyAndLayout<'tcx>>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { // Do not use the layout passed in as argument if the base we are looking at // here is not the entire place. - let layout = if place.projection.is_empty() { layout } else { None }; + let layout = if mir_place.projection.is_empty() { layout } else { None }; - let base_op = self.local_to_op(self.frame(), place.local, layout)?; - - let op = place - .projection - .iter() - .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?; + let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; + // Using `try_fold` turned out to be bad for performance, hence the loop. + for elem in mir_place.projection.iter() { + op = self.operand_projection(&op, elem)? + } trace!("eval_place_to_op: got {:?}", *op); // Sanity-check the type we ended up with. - debug_assert!(mir_assign_valid_types( - *self.tcx, - self.param_env, - self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty - )?)?, - op.layout, - )); + debug_assert!( + mir_assign_valid_types( + *self.tcx, + self.param_env, + self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty + )?)?, + op.layout, + ), + "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}", + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty, + op.layout.ty, + ); Ok(op) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 57ecad07b42..2001359d199 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -2,17 +2,14 @@ //! into a place. //! All high-level functions to write to memory work on places as destinations. -use std::convert::TryFrom; use std::hash::Hash; use rustc_ast::Mutability; use rustc_macros::HashStable; use rustc_middle::mir; +use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; -use rustc_middle::ty::{self, Ty}; -use rustc_target::abi::{ - Abi, Align, FieldsShape, HasDataLayout, Size, TagEncoding, VariantIdx, Variants, -}; +use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx}; use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, @@ -46,7 +43,7 @@ impl<Tag: Provenance> MemPlaceMeta<Tag> { } } } - fn has_meta(self) -> bool { + pub fn has_meta(self) -> bool { match self { Self::Meta(_) => true, Self::None | Self::Poison => false, @@ -188,6 +185,7 @@ impl<Tag: Provenance> Place<Tag> { /// Asserts that this points to some local variable. /// Returns the frame idx and the variable idx. #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn assert_local(&self) -> (usize, mir::Local) { match self { Place::Local { frame, local } => (*frame, *local), @@ -250,7 +248,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { // Go through the layout. There are lots of types that support a length, // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) match self.layout.fields { - FieldsShape::Array { count, .. } => Ok(count), + abi::FieldsShape::Array { count, .. } => Ok(count), _ => bug!("len not supported on sized type {:?}", self.layout.ty), } } @@ -281,6 +279,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { } #[inline(always)] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { @@ -298,16 +297,16 @@ impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> { } } - #[inline] - pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> { + #[inline(always)] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) + pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { self.try_as_mplace().unwrap() } } -// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385 +// FIXME: Working around https://github.com/rust-lang/rust/issues/54385 impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> where - // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 Tag: Provenance + Eq + Hash + 'static, M: Machine<'mir, 'tcx, PointerTag = Tag>, { @@ -392,276 +391,29 @@ where Ok(()) } - /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is - /// always possible without allocating, so it can take `&self`. Also return the field's layout. - /// This supports both struct and array fields. - /// - /// This also works for arrays, but then the `usize` index type is restricting. - /// For indexing into arrays, use `mplace_index`. - #[inline(always)] - pub fn mplace_field( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let offset = base.layout.fields.offset(field); - let field_layout = base.layout.field(self, field); - - // Offset may need adjustment for unsized fields. - let (meta, offset) = if field_layout.is_unsized() { - // Re-use parent metadata to determine dynamic field layout. - // With custom DSTS, this *will* execute user-defined code, but the same - // happens at run-time so that's okay. - match self.size_and_align_of(&base.meta, &field_layout)? { - Some((_, align)) => (base.meta, offset.align_to(align)), - None => { - // For unsized types with an extern type tail we perform no adjustments. - // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. - assert!(matches!(base.meta, MemPlaceMeta::None)); - (base.meta, offset) - } - } - } else { - // base.meta could be present; we might be accessing a sized field of an unsized - // struct. - (MemPlaceMeta::None, offset) - }; - - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that - base.offset(offset, meta, field_layout, self) - } - - /// Index into an array. - #[inline(always)] - pub fn mplace_index( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Not using the layout method because we want to compute on u64 - match base.layout.fields { - FieldsShape::Array { stride, .. } => { - let len = base.len(self)?; - if index >= len { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len, index }); - } - let offset = stride * index; // `Size` multiplication - // All fields have the same layout. - let field_layout = base.layout.field(self, 0); - - assert!(!field_layout.is_unsized()); - base.offset(offset, MemPlaceMeta::None, field_layout, self) - } - _ => span_bug!( - self.cur_span(), - "`mplace_index` called on non-array type {:?}", - base.layout.ty - ), - } - } - - // Iterates over all fields of an array. Much more efficient than doing the - // same by repeatedly calling `mplace_array`. - pub(super) fn mplace_array_fields<'a>( - &self, - base: &'a MPlaceTy<'tcx, Tag>, - ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a> - { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout"); - }; - let layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) - } - - fn mplace_subslice( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let actual_to = if from_end { - if from.checked_add(to).map_or(true, |to| to > len) { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); - } - len.checked_sub(to).unwrap() - } else { - to - }; - - // Not using layout method because that works with usize, and does not work with slices - // (that have count 0 in their layout). - let from_offset = match base.layout.fields { - FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked - _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) - } - }; - - // Compute meta and new layout - let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind() { - // It is not nice to match on the type, but that seems to be the only way to - // implement this. - ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)), - ty::Slice(..) => { - let len = Scalar::from_machine_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base.layout.ty) - } - _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) - } - }; - let layout = self.layout_of(ty)?; - base.offset(from_offset, meta, layout, self) - } - - pub(crate) fn mplace_downcast( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - // Downcasts only change the layout. - // (In particular, no check about whether this is even the active variant -- that's by design, - // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - assert!(!base.meta.has_meta()); - Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..*base }) - } - - /// Project into an mplace - #[instrument(skip(self), level = "debug")] - pub(super) fn mplace_projection( - &self, - base: &MPlaceTy<'tcx, M::PointerTag>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.mplace_field(base, field.index())?, - Downcast(_, variant) => self.mplace_downcast(base, variant)?, - Deref => self.deref_operand(&base.into())?, - - Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_scalar(&n)?; - let n = n.to_machine_usize(self)?; - self.mplace_index(base, n)? - } - - ConstantIndex { offset, min_length, from_end } => { - let n = base.len(self)?; - if n < min_length { - // This can only be reached in ConstProp and non-rustc-MIR. - throw_ub!(BoundsCheckFailed { len: min_length, index: n }); - } - - let index = if from_end { - assert!(0 < offset && offset <= min_length); - n.checked_sub(offset).unwrap() - } else { - assert!(offset < min_length); - offset - }; - - self.mplace_index(base, index)? - } - - Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?, - }) - } - /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. /// Also returns the number of elements. pub fn mplace_to_simd( &self, - base: &MPlaceTy<'tcx, M::PointerTag>, + mplace: &MPlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { // Basically we just transmute this place into an array following simd_size_and_type. // (Transmuting is okay since this is an in-memory place. We also double-check the size // stays the same.) - let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx); + let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx); let array = self.tcx.mk_array(e_ty, len); let layout = self.layout_of(array)?; - assert_eq!(layout.size, base.layout.size); - Ok((MPlaceTy { layout, ..*base }, len)) - } - - /// Gets the place of a field inside the place, and also the field's type. - /// Just a convenience function, but used quite a bit. - /// This is the only projection that might have a side-effect: We cannot project - /// into the field of a local `ScalarPair`, we have to first allocate it. - #[instrument(skip(self), level = "debug")] - pub fn place_field( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // FIXME: We could try to be smarter and avoid allocation for fields that span the - // entire place. - let mplace = self.force_allocation(base)?; - Ok(self.mplace_field(&mplace, field)?.into()) - } - - pub fn place_index( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mplace = self.force_allocation(base)?; - Ok(self.mplace_index(&mplace, index)?.into()) - } - - pub fn place_downcast( - &self, - base: &PlaceTy<'tcx, M::PointerTag>, - variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - // Downcast just changes the layout - Ok(match base.try_as_mplace() { - Ok(mplace) => self.mplace_downcast(&mplace, variant)?.into(), - Err(..) => { - let layout = base.layout.for_variant(self, variant); - PlaceTy { layout, ..*base } - } - }) - } - - /// Projects into a place. - pub fn place_projection( - &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, - &proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - Field(field, _) => self.place_field(base, field.index())?, - Downcast(_, variant) => self.place_downcast(base, variant)?, - Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), - // For the other variants, we have to force an allocation. - // This matches `operand_projection`. - Subslice { .. } | ConstantIndex { .. } | Index(_) => { - let mplace = self.force_allocation(base)?; - self.mplace_projection(&mplace, proj_elem)?.into() - } - }) + assert_eq!(layout.size, mplace.layout.size); + Ok((MPlaceTy { layout, ..*mplace }, len)) } /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements. /// Also returns the number of elements. pub fn place_to_simd( &mut self, - base: &PlaceTy<'tcx, M::PointerTag>, + place: &PlaceTy<'tcx, M::PointerTag>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> { - let mplace = self.force_allocation(base)?; + let mplace = self.force_allocation(place)?; self.mplace_to_simd(&mplace) } @@ -680,30 +432,30 @@ where #[instrument(skip(self), level = "debug")] pub fn eval_place( &mut self, - place: mir::Place<'tcx>, + mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { - let mut place_ty = self.local_to_place(self.frame_idx(), place.local)?; - - for elem in place.projection.iter() { - place_ty = self.place_projection(&place_ty, &elem)? + let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; + // Using `try_fold` turned out to be bad for performance, hence the loop. + for elem in mir_place.projection.iter() { + place = self.place_projection(&place, elem)? } - trace!("{:?}", self.dump_place(place_ty.place)); + trace!("{:?}", self.dump_place(place.place)); // Sanity-check the type we ended up with. debug_assert!( mir_assign_valid_types( *self.tcx, self.param_env, self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( - place.ty(&self.frame().body.local_decls, *self.tcx).ty + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty )?)?, - place_ty.layout, + place.layout, ), - "eval_place of a MIR place with type {:?} produced an interpret place with type {:?}", - place.ty(&self.frame().body.local_decls, *self.tcx).ty, - place_ty.layout.ty, + "eval_place of a MIR place with type {:?} produced an interpreter place with type {:?}", + mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty, + place.layout.ty, ); - Ok(place_ty) + Ok(place) } /// Write an immediate to a place @@ -1058,10 +810,10 @@ where } match dest.layout.variants { - Variants::Single { index } => { + abi::Variants::Single { index } => { assert_eq!(index, variant_index); } - Variants::Multiple { + abi::Variants::Multiple { tag_encoding: TagEncoding::Direct, tag: tag_layout, tag_field, @@ -1082,7 +834,7 @@ where let tag_dest = self.place_field(dest, tag_field)?; self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; } - Variants::Multiple { + abi::Variants::Multiple { tag_encoding: TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start }, tag: tag_layout, diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs new file mode 100644 index 00000000000..31fb6a8944d --- /dev/null +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -0,0 +1,393 @@ +//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy. +//! +//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not". +//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway. +//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields), +//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial, +//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually +//! implement the logic on OpTy, and MPlaceTy calls that. + +use std::hash::Hash; + +use rustc_middle::mir; +use rustc_middle::ty; +use rustc_middle::ty::layout::LayoutOf; +use rustc_target::abi::{self, Abi, VariantIdx}; + +use super::{ + ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, + Provenance, Scalar, +}; + +// FIXME: Working around https://github.com/rust-lang/rust/issues/54385 +impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> +where + Tag: Provenance + Eq + Hash + 'static, + M: Machine<'mir, 'tcx, PointerTag = Tag>, +{ + //# Field access + + /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is + /// always possible without allocating, so it can take `&self`. Also return the field's layout. + /// This supports both struct and array fields. + /// + /// This also works for arrays, but then the `usize` index type is restricting. + /// For indexing into arrays, use `mplace_index`. + pub fn mplace_field( + &self, + base: &MPlaceTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + let offset = base.layout.fields.offset(field); + let field_layout = base.layout.field(self, field); + + // Offset may need adjustment for unsized fields. + let (meta, offset) = if field_layout.is_unsized() { + // Re-use parent metadata to determine dynamic field layout. + // With custom DSTS, this *will* execute user-defined code, but the same + // happens at run-time so that's okay. + match self.size_and_align_of(&base.meta, &field_layout)? { + Some((_, align)) => (base.meta, offset.align_to(align)), + None => { + // For unsized types with an extern type tail we perform no adjustments. + // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. + assert!(matches!(base.meta, MemPlaceMeta::None)); + (base.meta, offset) + } + } + } else { + // base.meta could be present; we might be accessing a sized field of an unsized + // struct. + (MemPlaceMeta::None, offset) + }; + + // We do not look at `base.layout.align` nor `field_layout.align`, unlike + // codegen -- mostly to see if we can get away with that + base.offset(offset, meta, field_layout, self) + } + + /// Gets the place of a field inside the place, and also the field's type. + /// Just a convenience function, but used quite a bit. + /// This is the only projection that might have a side-effect: We cannot project + /// into the field of a local `ScalarPair`, we have to first allocate it. + pub fn place_field( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + // FIXME: We could try to be smarter and avoid allocation for fields that span the + // entire place. + let base = self.force_allocation(base)?; + Ok(self.mplace_field(&base, field)?.into()) + } + + pub fn operand_field( + &self, + base: &OpTy<'tcx, M::PointerTag>, + field: usize, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let base = match base.try_as_mplace() { + Ok(ref mplace) => { + // We can reuse the mplace field computation logic for indirect operands. + let field = self.mplace_field(mplace, field)?; + return Ok(field.into()); + } + Err(value) => value, + }; + + let field_layout = base.layout.field(self, field); + let offset = base.layout.fields.offset(field); + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let field_val: Immediate<_> = match (*base, base.layout.abi) { + // the field contains no information, can be left uninit + _ if field_layout.is_zst() => Immediate::Uninit, + // the field covers the entire type + _ if field_layout.size == base.layout.size => { + assert!(match (base.layout.abi, field_layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }); + assert!(offset.bytes() == 0); + *base + } + // extract fields from types with `ScalarPair` ABI + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(field_layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(field_layout.size, a.size(self)); + a_val + } else { + debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); + debug_assert_eq!(field_layout.size, b.size(self)); + b_val + }) + } + _ => span_bug!( + self.cur_span(), + "invalid field access on immediate {}, layout {:#?}", + base, + base.layout + ), + }; + + Ok(ImmTy::from_immediate(field_val, field_layout).into()) + } + + //# Downcasting + + pub fn mplace_downcast( + &self, + base: &MPlaceTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + // Downcasts only change the layout. + // (In particular, no check about whether this is even the active variant -- that's by design, + // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) + assert!(!base.meta.has_meta()); + let mut base = *base; + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + pub fn place_downcast( + &self, + base: &PlaceTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + // Downcast just changes the layout + let mut base = *base; + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + pub fn operand_downcast( + &self, + base: &OpTy<'tcx, M::PointerTag>, + variant: VariantIdx, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // Downcast just changes the layout + let mut base = *base; + base.layout = base.layout.for_variant(self, variant); + Ok(base) + } + + //# Slice indexing + + #[inline(always)] + pub fn operand_index( + &self, + base: &OpTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // Not using the layout method because we want to compute on u64 + match base.layout.fields { + abi::FieldsShape::Array { stride, count: _ } => { + // `count` is nonsense for slices, use the dynamic length instead. + let len = base.len(self)?; + if index >= len { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len, index }); + } + let offset = stride * index; // `Size` multiplication + // All fields have the same layout. + let field_layout = base.layout.field(self, 0); + assert!(!field_layout.is_unsized()); + + base.offset(offset, MemPlaceMeta::None, field_layout, self) + } + _ => span_bug!( + self.cur_span(), + "`mplace_index` called on non-array type {:?}", + base.layout.ty + ), + } + } + + // Iterates over all fields of an array. Much more efficient than doing the + // same by repeatedly calling `operand_index`. + pub fn operand_array_fields<'a>( + &self, + base: &'a OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Tag>>> + 'a> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { + span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); + }; + let layout = base.layout.field(self, 0); + let dl = &self.tcx.data_layout; + // `Size` multiplication + Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) + } + + /// Index into an array. + pub fn mplace_index( + &self, + base: &MPlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + Ok(self.operand_index(&base.into(), index)?.assert_mem_place()) + } + + pub fn place_index( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + index: u64, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + // There's not a lot we can do here, since we cannot have a place to a part of a local. If + // we are accessing the only element of a 1-element array, it's still the entire local... + // that doesn't seem worth it. + let base = self.force_allocation(base)?; + Ok(self.mplace_index(&base, index)?.into()) + } + + //# ConstantIndex support + + fn operand_constant_index( + &self, + base: &OpTy<'tcx, M::PointerTag>, + offset: u64, + min_length: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let n = base.len(self)?; + if n < min_length { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: min_length, index: n }); + } + + let index = if from_end { + assert!(0 < offset && offset <= min_length); + n.checked_sub(offset).unwrap() + } else { + assert!(offset < min_length); + offset + }; + + self.operand_index(base, index) + } + + fn place_constant_index( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + offset: u64, + min_length: u64, + from_end: bool, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let base = self.force_allocation(base)?; + Ok(self + .operand_constant_index(&base.into(), offset, min_length, from_end)? + .assert_mem_place() + .into()) + } + + //# Subslicing + + fn operand_subslice( + &self, + base: &OpTy<'tcx, M::PointerTag>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + let len = base.len(self)?; // also asserts that we have a type where this makes sense + let actual_to = if from_end { + if from.checked_add(to).map_or(true, |to| to > len) { + // This can only be reached in ConstProp and non-rustc-MIR. + throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) }); + } + len.checked_sub(to).unwrap() + } else { + to + }; + + // Not using layout method because that works with usize, and does not work with slices + // (that have count 0 in their layout). + let from_offset = match base.layout.fields { + abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked + _ => { + span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + } + }; + + // Compute meta and new layout + let inner_len = actual_to.checked_sub(from).unwrap(); + let (meta, ty) = match base.layout.ty.kind() { + // It is not nice to match on the type, but that seems to be the only way to + // implement this. + ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)), + ty::Slice(..) => { + let len = Scalar::from_machine_usize(inner_len, self); + (MemPlaceMeta::Meta(len), base.layout.ty) + } + _ => { + span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + } + }; + let layout = self.layout_of(ty)?; + base.offset(from_offset, meta, layout, self) + } + + pub fn place_subslice( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + from: u64, + to: u64, + from_end: bool, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + let base = self.force_allocation(base)?; + Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into()) + } + + //# Applying a general projection + + /// Projects into a place. + #[instrument(skip(self), level = "trace")] + pub fn place_projection( + &mut self, + base: &PlaceTy<'tcx, M::PointerTag>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.place_field(base, field.index())?, + Downcast(_, variant) => self.place_downcast(base, variant)?, + Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), + Index(local) => { + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.read_scalar(&n)?.to_machine_usize(self)?; + self.place_index(base, n)? + } + ConstantIndex { offset, min_length, from_end } => { + self.place_constant_index(base, offset, min_length, from_end)? + } + Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?, + }) + } + + #[instrument(skip(self), level = "trace")] + pub fn operand_projection( + &self, + base: &OpTy<'tcx, M::PointerTag>, + proj_elem: mir::PlaceElem<'tcx>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + use rustc_middle::mir::ProjectionElem::*; + Ok(match proj_elem { + Field(field, _) => self.operand_field(base, field.index())?, + Downcast(_, variant) => self.operand_downcast(base, variant)?, + Deref => self.deref_operand(base)?.into(), + Index(local) => { + let layout = self.layout_of(self.tcx.types.usize)?; + let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.read_scalar(&n)?.to_machine_usize(self)?; + self.operand_index(base, n)? + } + ConstantIndex { offset, min_length, from_end } => { + self.operand_constant_index(base, offset, min_length, from_end)? + } + Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?, + }) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 515cc222dc6..9e74b99ecd7 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -529,7 +529,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let receiver_place = loop { match receiver.layout.ty.kind() { ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, - ty::Dynamic(..) => break receiver.assert_mem_place(), + ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values _ => { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 08102585a7b..5114ce5d452 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -847,6 +847,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } Abi::Scalar(scalar_layout) => { + // We use a 'forced' read because we always need a `Immediate` here + // and treating "partially uninit" as "fully uninit" is fine for us. let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit(); self.visit_scalar(scalar, scalar_layout)?; } @@ -856,6 +858,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // is subtle due to enums having ScalarPair layout, where one field // is the discriminant. if cfg!(debug_assertions) { + // We use a 'forced' read because we always need a `Immediate` here + // and treating "partially uninit" as "fully uninit" is fine for us. let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair(); self.visit_scalar(a, a_layout)?; self.visit_scalar(b, b_layout)?; @@ -880,7 +884,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ) -> InterpResult<'tcx> { match op.layout.ty.kind() { ty::Str => { - let mplace = op.assert_mem_place(); // strings are never immediate + let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate let len = mplace.len(self.ecx)?; try_validation!( self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)), @@ -900,14 +904,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> { // Optimized handling for arrays of integer/float type. - // Arrays cannot be immediate, slices are never immediate. - let mplace = op.assert_mem_place(); // This is the length of the array/slice. - let len = mplace.len(self.ecx)?; + let len = op.len(self.ecx)?; // This is the element type size. let layout = self.ecx.layout_of(*tys)?; // This is the size in bytes of the whole array. (This checks for overflow.) let size = layout.size * len; + // If the size is 0, there is nothing to check. + // (`size` can only be 0 of `len` is 0, and empty arrays are always valid.) + if size == Size::ZERO { + return Ok(()); + } + // Now that we definitely have a non-ZST array, we know it lives in memory. + let mplace = match op.try_as_mplace() { + Ok(mplace) => mplace, + Err(imm) => match *imm { + Immediate::Uninit => + throw_validation_failure!(self.path, { "uninitialized bytes" }), + Immediate::Scalar(..) | Immediate::ScalarPair(..) => + bug!("arrays/slices can never have Scalar/ScalarPair layout"), + } + }; // Optimization: we just check the entire range at once. // NOTE: Keep this in sync with the handling of integer and float @@ -919,10 +936,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // to reject those pointers, we just do not have the machinery to // talk about parts of a pointer. // We also accept uninit, for consistency with the slow path. - let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)? else { - // Size 0, nothing more to check. - return Ok(()); - }; + let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0"); match alloc.check_bytes( alloc_range(Size::ZERO, size), diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index ded4c6a557a..c262bca9bb4 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -21,8 +21,10 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; - /// Creates this from an `MPlaceTy`. - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self; + /// Creates this from an `OpTy`. + /// + /// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`. + fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self; /// Projects to the given enum variant. fn project_downcast( @@ -56,8 +58,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace.into() + fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { + op } #[inline(always)] @@ -96,8 +98,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } #[inline(always)] - fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self { - mplace + fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { + // assert is justified because our `to_op` only ever produces `Indirect` operands. + op.assert_mem_place() } #[inline(always)] @@ -218,13 +221,13 @@ macro_rules! make_value_visitor { match *v.layout().ty.kind() { // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { - // immediate trait objects are not a thing + // unsized values are never immediate, so we can assert_mem_place let op = v.to_op(self.ecx())?; let dest = op.assert_mem_place(); let inner = self.ecx().unpack_dyn_trait(&dest)?.1; trace!("walk_value: dyn object layout: {:#?}", inner.layout); // recurse with the inner type - return self.visit_field(&v, 0, &Value::from_mem_place(inner)); + return self.visit_field(&v, 0, &Value::from_op(inner.into())); }, // Slices do not need special handling here: they have `Array` field // placement with length 0, so we enter the `Array` case below which @@ -292,13 +295,12 @@ macro_rules! make_value_visitor { FieldsShape::Array { .. } => { // Let's get an mplace first. let op = v.to_op(self.ecx())?; - let mplace = op.assert_mem_place(); // Now we can go over all the fields. // This uses the *run-time length*, i.e., if we are a slice, // the dynamic info from the metadata is used. - let iter = self.ecx().mplace_array_fields(&mplace)? + let iter = self.ecx().operand_array_fields(&op)? .map(|f| f.and_then(|f| { - Ok(Value::from_mem_place(f)) + Ok(Value::from_op(f)) })); self.visit_aggregate(v, iter)?; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 17376e59e09..1d083b0bf82 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -299,7 +299,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { err.note(&format!("attempting to deref into `{}`", deref_target_ty)); // Check first whether the source is accessible (issue #87060) - if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() { + if tcx.sess.source_map().is_span_accessible(deref_target) { err.span_note(deref_target, "deref defined here"); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 6e5a0c813ac..0aa7b117b89 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -226,7 +226,7 @@ impl Qualif for CustomEq { // because that component may be part of an enum variant (e.g., // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be // structural-match (`Option::None`). - traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some() + traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some() } fn in_adt_inherently<'tcx>( diff --git a/compiler/rustc_error_messages/locales/en-US/expand.ftl b/compiler/rustc_error_messages/locales/en-US/expand.ftl new file mode 100644 index 00000000000..8d506a3ea8b --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/expand.ftl @@ -0,0 +1,5 @@ +expand-explain-doc-comment-outer = + outer doc comments expand to `#[doc = "..."]`, which is what this macro attempted to match + +expand-explain-doc-comment-inner = + inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 5a482bc5b2c..d16171cb162 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -33,11 +33,12 @@ pub use unic_langid::{langid, LanguageIdentifier}; fluent_messages! { borrowck => "../locales/en-US/borrowck.ftl", builtin_macros => "../locales/en-US/builtin_macros.ftl", + const_eval => "../locales/en-US/const_eval.ftl", + expand => "../locales/en-US/expand.ftl", lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", - const_eval => "../locales/en-US/const_eval.ftl", } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9429ad1a897..da321c45875 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -614,6 +614,14 @@ impl Diagnostic { self } + /// Clear any existing suggestions. + pub fn clear_suggestions(&mut self) -> &mut Self { + if let Ok(suggestions) = &mut self.suggestions { + suggestions.clear(); + } + self + } + /// Helper for pushing to `self.suggestions`, if available (not disable). fn push_suggestion(&mut self, suggestion: CodeSuggestion) { if let Ok(suggestions) = &mut self.suggestions { diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index c3341fd68f4..99ac6a3546e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -461,6 +461,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!(pub fn set_is_lint(&mut self,) -> &mut Self); forward!(pub fn disable_suggestions(&mut self,) -> &mut Self); + forward!(pub fn clear_suggestions(&mut self,) -> &mut Self); forward!(pub fn multipart_suggestion( &mut self, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ffe4ecebb2e..e59a74e380a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1558,7 +1558,7 @@ pub fn add_elided_lifetime_in_path_suggestion( insertion_span: Span, ) { diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n))); - if source_map.span_to_snippet(insertion_span).is_err() { + if !source_map.is_span_accessible(insertion_span) { // Do not try to suggest anything if generated by a proc-macro. return; } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index f40c365cbcc..3e9ddd6aec0 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -14,7 +14,7 @@ use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_feature::Features; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, @@ -25,6 +25,7 @@ use rustc_session::parse::ParseSess; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; +use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent}; use rustc_span::Span; @@ -345,7 +346,7 @@ fn expand_macro<'cx>( if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) { err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro"); } - + annotate_doc_comment(&mut err, sess.source_map(), span); // Check whether there's a missing comma in this macro call, like `println!("{}" a);` if let Some((arg, comma_span)) = arg.add_comma() { for lhs in lhses { @@ -453,7 +454,10 @@ pub fn compile_declarative_macro( Failure(token, msg) => { let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); - sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); + let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s); + err.span_label(sp, msg); + annotate_doc_comment(&mut err, sess.source_map(), sp); + err.emit(); return dummy_syn_ext(); } Error(sp, msg) => { @@ -590,6 +594,34 @@ pub fn compile_declarative_macro( (mk_syn_ext(expander), rule_spans) } +#[derive(SessionSubdiagnostic)] +enum ExplainDocComment { + #[label(expand::explain_doc_comment_inner)] + Inner { + #[primary_span] + span: Span, + }, + #[label(expand::explain_doc_comment_outer)] + Outer { + #[primary_span] + span: Span, + }, +} + +fn annotate_doc_comment( + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + sm: &SourceMap, + span: Span, +) { + if let Ok(src) = sm.span_to_snippet(span) { + if src.starts_with("///") || src.starts_with("/**") { + err.subdiagnostic(ExplainDocComment::Outer { span }); + } else if src.starts_with("//!") || src.starts_with("/*!") { + err.subdiagnostic(ExplainDocComment::Inner { span }); + } + } +} + fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { // lhs is going to be like TokenTree::Delimited(...), where the // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d1477f9e2ae..0ecb83996a8 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1111,11 +1111,9 @@ macro_rules! visit_place_fns { context: PlaceContext, location: Location, ) { - // FIXME: Use PlaceRef::iter_projections, once that exists. - let mut cursor = place_ref.projection; - while let &[ref proj_base @ .., elem] = cursor { - cursor = proj_base; - self.visit_projection_elem(place_ref.local, cursor, elem, context, location); + for (base, elem) in place_ref.iter_projections().rev() { + let base_proj = base.projection; + self.visit_projection_elem(place_ref.local, base_proj, elem, context, location); } } diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 84547dca453..add2df25884 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -85,10 +85,10 @@ impl GenericParamDef { ) -> Option<EarlyBinder<ty::GenericArg<'tcx>>> { match self.kind { GenericParamDefKind::Type { has_default, .. } if has_default => { - Some(EarlyBinder(tcx.type_of(self.def_id).into())) + Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into())) } GenericParamDefKind::Const { has_default } if has_default => { - Some(EarlyBinder(tcx.const_param_default(self.def_id).into())) + Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into())) } _ => None, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 4b51daadabf..d663f1a3ec6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -932,6 +932,10 @@ impl<T> EarlyBinder<T> { let value = f(self.0)?; Ok(EarlyBinder(value)) } + + pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> { + EarlyBinder(value) + } } impl<T> EarlyBinder<Option<T>> { diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 52da6c3a8c0..4d2f69b23fa 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -676,6 +676,10 @@ impl<'tcx> TyCtxt<'tcx> { ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> { ty::EarlyBinder(self.item_bounds(def_id)) } + + pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> { + ty::EarlyBinder(self.const_param_default(def_id)) + } } struct OpaqueTypeExpander<'tcx> { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 61ac3d14e50..75fd156ebfd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -432,7 +432,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ an `enum` with only one variant", ); - if self.tcx.sess.source_map().span_to_snippet(span).is_ok() { + if self.tcx.sess.source_map().is_span_accessible(span) { let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); let start_span = span.shrink_to_lo(); let end_span = semi_span.shrink_to_lo(); diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index f22f3f61a01..e32e0b11ba4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -120,32 +120,37 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind { - traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), - traits::NonStructuralMatchTyKind::Dynamic => { - "trait objects cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Opaque => { - "opaque types cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Closure => { - "closures cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Generator => { - "generators cannot be used in patterns".to_string() - } - traits::NonStructuralMatchTyKind::Param => { - bug!("use of a constant whose type is a parameter inside a pattern") - } - traits::NonStructuralMatchTyKind::Projection => { - bug!("use of a constant whose type is a projection inside a pattern") - } - traits::NonStructuralMatchTyKind::Foreign => { - bug!("use of a value of a foreign type inside a pattern") - } - }) - }) + traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map( + |non_sm_ty| { + with_no_trimmed_paths!(match non_sm_ty.kind { + traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt), + traits::NonStructuralMatchTyKind::Dynamic => { + "trait objects cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::Opaque => { + "opaque types cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::Closure => { + "closures cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::Generator => { + "generators cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::Float => { + "floating-point numbers cannot be used in patterns".to_string() + } + traits::NonStructuralMatchTyKind::Param => { + bug!("use of a constant whose type is a parameter inside a pattern") + } + traits::NonStructuralMatchTyKind::Projection => { + bug!("use of a constant whose type is a projection inside a pattern") + } + traits::NonStructuralMatchTyKind::Foreign => { + bug!("use of a value of a foreign type inside a pattern") + } + }) + }, + ) } fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 3be1783ae33..f3153a64820 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -537,13 +537,12 @@ fn build_call_shim<'tcx>( }; let def_id = instance.def_id(); - let sig = tcx.fn_sig(def_id); - let mut sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.bound_fn_sig(def_id); + let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig)); assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body()); - if let Some(sig_substs) = sig_substs { - sig = EarlyBinder(sig).subst(tcx, sig_substs); - } + let mut sig = + if let Some(sig_substs) = sig_substs { sig.subst(tcx, sig_substs) } else { sig.0 }; if let CallKind::Indirect(fnty) = call_kind { // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2c43563b104..f9387e29262 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1393,7 +1393,9 @@ impl<'a> Parser<'a> { self.parse_yield_expr(attrs) } else if self.is_do_yeet() { self.parse_yeet_expr(attrs) - } else if self.eat_keyword(kw::Let) { + } else if self.check_keyword(kw::Let) { + self.manage_let_chains_context(); + self.bump(); self.parse_let_expr(attrs) } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) @@ -2355,16 +2357,30 @@ impl<'a> Parser<'a> { Ok(cond) } + // Checks if `let` is in an invalid position like `let x = let y = 1;` or + // if the current `let` is in a let_chains context but nested in another + // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`. + // + // This method expects that the current token is `let`. + fn manage_let_chains_context(&mut self) { + debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _))); + let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed + && !matches!( + self.prev_token.kind, + TokenKind::AndAnd + | TokenKind::CloseDelim(Delimiter::Brace) + | TokenKind::Ident(kw::If, _) + | TokenKind::Ident(kw::While, _) + ); + if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr { + self.struct_span_err(self.token.span, "expected expression, found `let` statement") + .emit(); + } + } + /// Parses a `let $pat = $expr` pseudo-expression. /// The `let` token has already been eaten. fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { - if !self.let_expr_allowed { - self.struct_span_err( - self.prev_token.span, - "expected expression, found `let` statement", - ) - .emit(); - } let lo = self.prev_token.span; let pat = self.parse_pat_allow_top_alt( None, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index b4a39982b74..2851b08cd93 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1647,7 +1647,7 @@ impl<'a> Resolver<'a> { fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String { let res = b.res(); - if b.span.is_dummy() || self.session.source_map().span_to_snippet(b.span).is_err() { + if b.span.is_dummy() || !self.session.source_map().is_span_accessible(b.span) { // These already contain the "built-in" prefix or look bad with it. let add_built_in = !matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f37acca3d9f..098b5a0c92e 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -611,6 +611,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { TyKind::Path(ref qself, ref path) => { self.diagnostic_metadata.current_type_path = Some(ty); self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type); + + // Check whether we should interpret this as a bare trait object. + if qself.is_none() + && let Some(partial_res) = self.r.partial_res_map.get(&ty.id) + && partial_res.unresolved_segments() == 0 + && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res() + { + // This path is actually a bare trait object. In case of a bare `Fn`-trait + // object with anonymous lifetimes, we need this rib to correctly place the + // synthetic lifetimes. + let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo()); + self.with_generic_param_rib( + &[], + NormalRibKind, + LifetimeRibKind::Generics { + binder: ty.id, + kind: LifetimeBinderKind::PolyTrait, + span, + }, + |this| this.visit_path(&path, ty.id), + ); + self.diagnostic_metadata.current_type_path = prev_ty; + return; + } } TyKind::ImplicitSelf => { let self_ty = Ident::with_dummy_span(kw::SelfUpper); diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 227127aed50..afbb88e9233 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -597,6 +597,13 @@ impl SourceMap { local_begin.sf.src.is_some() && local_end.sf.src.is_some() } + pub fn is_span_accessible(&self, sp: Span) -> bool { + self.span_to_source(sp, |src, start_index, end_index| { + Ok(src.get(start_index..end_index).is_some()) + }) + .map_or(false, |is_accessible| is_accessible) + } + /// Returns the source snippet as `String` corresponding to the given `Span`. pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> { self.span_to_source(sp, |src, start_index, end_index| { diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index 2c7834c225b..d3fd7051a12 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+strict-align,+neon,+fp-armv8".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index 1b6525a7c69..6316abe1ba9 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+strict-align,-neon,-fp-armv8".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 713dc9a1f0e..9bfae46ef32 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -25,7 +25,6 @@ pub fn opts(os: &'static str) -> TargetOptions { function_sections: false, dynamic_linking: true, linker_is_gnu: false, - executables: true, families: cvs!["unix"], is_like_osx: true, default_dwarf_version: 2, diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index ecb6cbd9f8a..0328ea98c48 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -54,7 +54,6 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions { abi: target_abi(arch).into(), cpu: target_cpu(arch).into(), dynamic_linking: false, - executables: true, link_env_remove: link_env_remove(arch), has_thread_local: false, ..super::apple_base::opts(os) diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 0cb18f17310..511693abe98 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { abi: "eabi".into(), endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index a5b7c12cc7b..5df4a0a1583 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { abi: "eabihf".into(), endian: Endian::Big, linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs index 8c2a9bcfde6..1bba3939397 100644 --- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs @@ -23,7 +23,6 @@ pub fn target() -> Target { abi: "eabihf".into(), linker_flavor: LinkerFlavor::Gcc, cpu: "mpcore".into(), - executables: true, families: cvs!["unix"], linker: Some("arm-none-eabi-gcc".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs index ff649434312..cb5cbe15836 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -22,7 +22,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index c0321d0bef4..fb5dd2e7574 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".into(), - executables: true, relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 2c3f79cc58b..5f1da09b317 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { options: TargetOptions { abi: "eabi".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 5c82e768483..0038ed0df8b 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -13,7 +13,6 @@ pub fn target() -> Target { options: TargetOptions { abi: "eabihf".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 4fd6c06394d..1d441e558dd 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -16,7 +16,6 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target { exe_suffix: ".elf".into(), linker: Some("avr-gcc".into()), - executables: true, eh_frame_header: false, pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]), late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]), diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs index 4fa6e12f5ba..3c4da6f883d 100644 --- a/compiler/rustc_target/src/spec/bpf_base.rs +++ b/compiler/rustc_target/src/spec/bpf_base.rs @@ -7,7 +7,6 @@ pub fn opts(endian: Endian) -> TargetOptions { endian, linker_flavor: LinkerFlavor::BpfLinker, atomic_cas: false, - executables: true, dynamic_linking: true, no_builtins: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index c1e469746cb..de2be781796 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "dragonfly".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index 36312d26e37..8c141aaaec3 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "freebsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index b02b70f76ee..df1e3275f73 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -23,7 +23,6 @@ pub fn opts() -> TargetOptions { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), dynamic_linking: true, - executables: true, families: cvs!["unix"], pre_link_args, pre_link_objects: crt_objects::new(&[ diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs index 61c05a2bdb6..8ab874410aa 100644 --- a/compiler/rustc_target/src/spec/haiku_base.rs +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "haiku".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], relro_level: RelroLevel::Full, ..Default::default() diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index e43153177f0..562ccef7eba 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -10,7 +10,6 @@ pub fn opts() -> TargetOptions { os: "hermit".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), - executables: true, has_thread_local: true, pre_link_args, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs index 80cf09517cc..cc2c78c69fe 100644 --- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -11,7 +11,6 @@ pub fn target() -> Target { base.has_rpath = true; base.linker_is_gnu = false; base.dynamic_linking = true; - base.executables = true; base.c_enum_min_bits = 8; diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs index b0e1b109be1..77e000474b8 100644 --- a/compiler/rustc_target/src/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -27,7 +27,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "illumos".into(), dynamic_linking: true, - executables: true, has_rpath: true, families: cvs!["unix"], is_like_solaris: true, diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index 7a051532f82..a08756861e5 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions { os: "l4re".into(), env: "uclibc".into(), linker_flavor: LinkerFlavor::L4Bender, - executables: true, panic_strategy: PanicStrategy::Abort, linker: Some("l4-bender".into()), linker_is_gnu: false, diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 0f79ada0d93..f4fce3b4050 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "linux".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs index e3522de6de0..cfc8ec21c2a 100644 --- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -18,7 +18,6 @@ pub fn target() -> Target { vendor: "sony".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), cpu: "mips2".into(), - executables: true, linker: Some("rust-lld".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs index 736af15cf44..fe2aa2de871 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -17,7 +17,6 @@ pub fn target() -> Target { cpu: "mips32r2".into(), features: "+mips32r2,+soft-float,+noabicalls".into(), max_atomic_width: Some(32), - executables: true, linker: Some("rust-lld".into()), panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 48ccb10f214..ef49fc8e968 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1212,8 +1212,7 @@ pub struct TargetOptions { pub dynamic_linking: bool, /// If dynamic linking is available, whether only cdylibs are supported. pub only_cdylib: bool, - /// Whether executables are available on this target. iOS, for example, only allows static - /// libraries. Defaults to false. + /// Whether executables are available on this target. Defaults to true. pub executables: bool, /// Relocation model to use in object file. Corresponds to `llc /// -relocation-model=$relocation_model`. Defaults to `Pic`. @@ -1520,7 +1519,7 @@ impl Default for TargetOptions { features: "".into(), dynamic_linking: false, only_cdylib: false, - executables: false, + executables: true, relocation_model: RelocModel::Pic, code_model: None, tls_model: TlsModel::GeneralDynamic, diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs index cedacb60f31..6b09386ae3e 100644 --- a/compiler/rustc_target/src/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -9,7 +9,6 @@ pub fn target() -> Target { options: TargetOptions { c_int_width: "16".into(), - executables: true, // The LLVM backend currently can't generate object files. To // workaround this LLVM generates assembly files which then we feed diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index c4df4b546e3..edb30b72bf6 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions { TargetOptions { linker_flavor: LinkerFlavor::Msvc, - executables: true, is_like_windows: true, is_like_msvc: true, lld_flavor: LldFlavor::Link, diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index 40ef04ba043..be94ea23465 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "netbsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], no_default_libraries: false, has_rpath: true, diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 9d94ed8aa48..1c5b68001b9 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -26,7 +26,6 @@ pub fn target() -> Target { // Needed to use `dylib` and `bin` crate types and the linker. dynamic_linking: true, - executables: true, // Avoid using dylib because it contain metadata not supported // by LLVM NVPTX backend. diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index 51cecdd47ea..e7db14e05a4 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "openbsd".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, abi_return_struct_as_int: true, diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs index 1878cc3fc11..468fe478549 100644 --- a/compiler/rustc_target/src/spec/redox_base.rs +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions { os: "redox".into(), env: "relibc".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 7124e2df9b3..232139db6ca 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(0), atomic_cas: false, - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs index 508982eed68..3e5d2887f43 100644 --- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { max_atomic_width: Some(0), atomic_cas: false, features: "+m".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs index f2bd6249f0a..99317b9f118 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs index b46ca159370..a5de645c984 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { cpu: "generic-rv32".into(), max_atomic_width: Some(32), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, ..Default::default() diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs index 0200862c7e0..03baef65c0d 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs @@ -26,7 +26,6 @@ pub fn target() -> Target { atomic_cas: true, features: "+m,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index 4216968cb77..bf510d204a7 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { max_atomic_width: Some(0), atomic_cas: false, features: "+m,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, emit_debug_gdb_scripts: false, diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 2a93459ef4f..03b3cfd1eb1 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -15,7 +15,6 @@ pub fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+f,+d,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index 6a8d8a97de6..2a94c9dd233 100644 --- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -14,7 +14,6 @@ pub fn target() -> Target { cpu: "generic-rv64".into(), max_atomic_width: Some(64), features: "+m,+a,+c".into(), - executables: true, panic_strategy: PanicStrategy::Abort, relocation_model: RelocModel::Static, code_model: Some(CodeModel::Medium), diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs index d61e1b2ec10..b7e8e8cf7f5 100644 --- a/compiler/rustc_target/src/spec/solaris_base.rs +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions { TargetOptions { os: "solaris".into(), dynamic_linking: true, - executables: true, has_rpath: true, families: cvs!["unix"], is_like_solaris: true, diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs index c5602a4513a..c585a6cd58e 100644 --- a/compiler/rustc_target/src/spec/solid_base.rs +++ b/compiler/rustc_target/src/spec/solid_base.rs @@ -5,6 +5,7 @@ pub fn opts(kernel: &str) -> TargetOptions { TargetOptions { os: format!("solid_{}", kernel).into(), vendor: "kmc".into(), + executables: false, frame_pointer: FramePointer::NonLeaf, has_thread_local: true, ..Default::default() diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index ef6038e6120..049142b89f1 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults TargetOptions { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, // In most cases, LLD is good enough linker: Some("rust-lld".into()), // Because these devices have very little resources having an unwinder is too onerous so we diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs index 2beb279e398..aa4784b63e7 100644 --- a/compiler/rustc_target/src/spec/vxworks_base.rs +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions { linker: Some("wr-c++".into()), exe_suffix: ".vxe".into(), dynamic_linking: true, - executables: true, families: cvs!["unix"], has_rpath: true, has_thread_local: true, diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 5736402ae14..9216d3e7b65 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -62,9 +62,6 @@ pub fn options() -> TargetOptions { dynamic_linking: true, only_cdylib: true, - // This means we'll just embed a `#[start]` function in the wasm module - executables: true, - // relatively self-explanatory! exe_suffix: ".wasm".into(), dll_prefix: "".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index a0480f386f7..90e0af3e38a 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -67,7 +67,6 @@ pub fn opts() -> TargetOptions { function_sections: false, linker: Some("gcc".into()), dynamic_linking: true, - executables: true, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index 30f995007a9..bae007dc9f3 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions { abi: "llvm".into(), linker: Some("clang".into()), dynamic_linking: true, - executables: true, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs index 334dec43ef7..fa69b919cec 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -24,7 +24,6 @@ pub fn opts() -> TargetOptions { TargetOptions { abi: "uwp".into(), vendor: "uwp".into(), - executables: false, limit_rdylib_exports: false, late_link_args, late_link_args_dynamic, diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs index 4348d924579..9d597ea2e62 100644 --- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -62,7 +62,6 @@ pub fn target() -> Target { vendor: "fortanix".into(), abi: "fortanix".into(), linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), - executables: true, linker: Some("rust-lld".into()), max_atomic_width: Some(64), cpu: "x86-64".into(), diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs index 0c510dfaa12..809fd642d41 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs @@ -24,7 +24,6 @@ pub fn target() -> Target { features: "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" .into(), - executables: true, disable_redzone: true, panic_strategy: PanicStrategy::Abort, code_model: Some(CodeModel::Kernel), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index aa1c9136289..8d7c6b26ba1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -673,6 +673,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if !self.report_similar_impl_candidates( impl_candidates, trait_ref, + obligation.cause.body_id, &mut err, ) { // This is *almost* equivalent to @@ -707,6 +708,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.report_similar_impl_candidates( impl_candidates, trait_ref, + obligation.cause.body_id, &mut err, ); } @@ -1353,6 +1355,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { &self, impl_candidates: Vec<ImplCandidate<'tcx>>, trait_ref: ty::PolyTraitRef<'tcx>, + body_id: hir::HirId, err: &mut Diagnostic, ) -> bool; @@ -1735,6 +1738,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { &self, impl_candidates: Vec<ImplCandidate<'tcx>>, trait_ref: ty::PolyTraitRef<'tcx>, + body_id: hir::HirId, err: &mut Diagnostic, ) -> bool { let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { @@ -1805,8 +1809,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { || self.tcx.is_builtin_derive(def_id) }) .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) - // Avoid mentioning type parameters. - .filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_))) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx + .visibility(def.did()) + .is_accessible_from(body_id.owner.to_def_id(), self.tcx) + } else { + true + } + }) .collect(); return report(normalized_impl_candidates, err); } @@ -2074,6 +2094,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // | // = note: cannot satisfy `_: Tt` + // Clear any more general suggestions in favor of our specific one + err.clear_suggestions(); + err.span_suggestion_verbose( span.shrink_to_hi(), &format!( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index e1131140c39..4862631980e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -12,7 +12,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_middle::ty::{ToPolyTraitRef, ToPredicate}; use rustc_span::def_id::DefId; @@ -555,7 +555,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter()); let bound = - EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs); + bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs); tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars)) }; let normalized_bound = normalize_with_depth_to( diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 94ca138b9d2..6c0b83fbd03 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -26,6 +26,7 @@ pub enum NonStructuralMatchTyKind<'tcx> { Closure, Generator, Projection, + Float, } /// This method traverses the structure of `ty`, trying to find an @@ -53,12 +54,16 @@ pub enum NonStructuralMatchTyKind<'tcx> { /// For more background on why Rust has this requirement, and issues /// that arose when the requirement was not enforced completely, see /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. +/// +/// The floats_allowed flag is used to deny constants in floating point pub fn search_for_structural_match_violation<'tcx>( span: Span, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, + floats_allowed: bool, ) -> Option<NonStructuralMatchTy<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed }) + .break_value() } /// This method returns true if and only if `adt_ty` itself has been marked as @@ -119,6 +124,8 @@ struct Search<'tcx> { /// Tracks ADTs previously encountered during search, so that /// we will not recur on them again. seen: FxHashSet<hir::def_id::DefId>, + + floats_allowed: bool, } impl<'tcx> Search<'tcx> { @@ -192,13 +199,24 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { // for empty array. return ControlFlow::CONTINUE; } - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => { // These primitive types are always structural match. // // `Never` is kind of special here, but as it is not inhabitable, this should be fine. return ControlFlow::CONTINUE; } + ty::Float(_) => { + if self.floats_allowed { + return ControlFlow::CONTINUE; + } else { + return ControlFlow::Break(NonStructuralMatchTy { + ty, + kind: NonStructuralMatchTyKind::Float, + }); + } + } + ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { // First check all contained types and then tell the caller to continue searching. return ty.super_visit_with(self); diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index d111008e82c..c873cf27e42 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -164,10 +164,62 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { suggested_name, Applicability::MaybeIncorrect, ); - } else { - err.span_label(span, format!("associated type `{}` not found", assoc_name)); + return err.emit(); } + // If we didn't find a good item in the supertraits (or couldn't get + // the supertraits), like in ItemCtxt, then look more generally from + // all visible traits. If there's one clear winner, just suggest that. + + let visible_traits: Vec<_> = self + .tcx() + .all_traits() + .filter(|trait_def_id| { + let viz = self.tcx().visibility(*trait_def_id); + if let Some(def_id) = self.item_def_id() { + viz.is_accessible_from(def_id, self.tcx()) + } else { + viz.is_visible_locally() + } + }) + .collect(); + + let wider_candidate_names: Vec<_> = visible_traits + .iter() + .flat_map(|trait_def_id| { + self.tcx().associated_items(*trait_def_id).in_definition_order() + }) + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None }, + ) + .collect(); + + if let (Some(suggested_name), true) = ( + find_best_match_for_name(&wider_candidate_names, assoc_name.name, None), + assoc_name.span != DUMMY_SP, + ) { + if let [best_trait] = visible_traits + .iter() + .filter(|trait_def_id| { + self.tcx() + .associated_items(*trait_def_id) + .filter_by_name_unhygienic(suggested_name) + .any(|item| item.kind == ty::AssocKind::Type) + }) + .collect::<Vec<_>>()[..] + { + err.span_label( + assoc_name.span, + format!( + "there is a similarly named associated type `{suggested_name}` in the trait `{}`", + self.tcx().def_path_str(*best_trait) + ), + ); + return err.emit(); + } + } + + err.span_label(span, format!("associated type `{}` not found", assoc_name)); err.emit() } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 0a2b54eec47..1d4e64b6bfc 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -550,7 +550,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { GenericParamDefKind::Const { has_default } => { let ty = tcx.at(self.span).type_of(param.def_id); if !infer_args && has_default { - EarlyBinder(tcx.const_param_default(param.def_id)) + tcx.bound_const_param_default(param.def_id) .subst(tcx, substs.unwrap()) .into() } else { diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index eb7e52c5ed3..dc553d1441e 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -760,7 +760,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(call_span) = iter::successors(Some(expr.span), |s| s.parent_callsite()) .find(|&s| sp.contains(s)) - && sm.span_to_snippet(call_span).is_ok() + && sm.is_span_accessible(call_span) { return Some(( sp.with_hi(call_span.lo()), @@ -773,7 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } if sp.contains(expr.span) - && sm.span_to_snippet(expr.span).is_ok() + && sm.is_span_accessible(expr.span) { return Some(( sp.with_hi(expr.span.lo()), diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 58c01a34cad..1d3608048f2 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -48,7 +48,7 @@ use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable}; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -2162,14 +2162,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if !expr_t.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, expr_t); } else { - type_error_struct!( + let field_name = field.to_string(); + let mut err = type_error_struct!( self.tcx().sess, field.span, expr_t, E0610, "`{expr_t}` is a primitive type and therefore doesn't have fields", - ) - .emit(); + ); + let is_valid_suffix = |field: String| { + if field == "f32" || field == "f64" { + return true; + } + let mut chars = field.chars().peekable(); + match chars.peek() { + Some('e') | Some('E') => { + chars.next(); + if let Some(c) = chars.peek() + && !c.is_numeric() && *c != '-' && *c != '+' + { + return false; + } + while let Some(c) = chars.peek() { + if !c.is_numeric() { + break; + } + chars.next(); + } + } + _ => (), + } + let suffix = chars.collect::<String>(); + suffix.is_empty() || suffix == "f32" || suffix == "f64" + }; + if let ty::Infer(ty::IntVar(_)) = expr_t.kind() + && let ExprKind::Lit(Spanned { + node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), + .. + }) = base.kind + && !base.span.from_expansion() + && is_valid_suffix(field_name) + { + err.span_suggestion_verbose( + field.span.shrink_to_lo(), + "If the number is meant to be a floating point number, consider adding a `0` after the period", + '0', + Applicability::MaybeIncorrect, + ); + } + err.emit(); } self.tcx().ty_error() diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index cf7de1dc016..d15d40bc247 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1426,7 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } GenericParamDefKind::Const { has_default } => { if !infer_args && has_default { - EarlyBinder(tcx.const_param_default(param.def_id)) + tcx.bound_const_param_default(param.def_id) .subst(tcx, substs.unwrap()) .into() } else { diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs index cc91f2431e0..a5add1e9a8a 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_typeck/src/check/intrinsicck.rs @@ -4,7 +4,7 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Article, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; +use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; use rustc_session::lint; use rustc_span::{Span, Symbol, DUMMY_SP}; use rustc_target::abi::{Pointer, VariantIdx}; @@ -99,8 +99,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } + // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { - if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { + // Type still may have region variables, but `Sized` does not depend + // on those, so just erase them before querying. + if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) { return true; } if let ty::Foreign(..) = ty.kind() { @@ -128,30 +131,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 64 => InlineAsmType::I64, _ => unreachable!(), }; + + // Expect types to be fully resolved, no const or type variables. + if ty.has_infer_types_or_consts() { + assert!(self.is_tainted_by_errors()); + return None; + } + let asm_ty = match *ty.kind() { // `!` is allowed for input but not for output (issue #87802) ty::Never if is_input => return None, ty::Error(_) => return None, ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), - // Somewhat of a hack: fallback in the presence of errors does not actually - // fall back to i32, but to ty::Error. For integer inference variables this - // means that they don't get any fallback and stay as `{integer}`. - // Since compilation can't succeed anyway, it's fine to use this to avoid printing - // "cannot use value of type `{integer}`", even though that would absolutely - // work due due i32 fallback if the current function had no other errors. - ty::Infer(InferTy::IntVar(_)) => { - assert!(self.is_tainted_by_errors()); - Some(InlineAsmType::I32) - } ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), - ty::Infer(InferTy::FloatVar(_)) => { - assert!(self.is_tainted_by_errors()); - Some(InlineAsmType::F32) - } ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), ty::FnPtr(_) => Some(asm_ty_isize), @@ -191,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, } } + ty::Infer(_) => unreachable!(), _ => None, }; let Some(asm_ty) = asm_ty else { @@ -204,11 +201,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - if ty.has_infer_types_or_consts() { - assert!(self.is_tainted_by_errors()); - return None; - } - // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) { diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index d8cdc9275f4..b14f3d6de4e 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -81,11 +81,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick); let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs); - debug!("all_substs={:?}", all_substs); + debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}"); // Create the final signature for the method, replacing late-bound regions. let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs); + // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that + // something which derefs to `Self` actually implements the trait and the caller + // wanted to make a static dispatch on it but forgot to import the trait. + // See test `src/test/ui/issue-35976.rs`. + // + // In that case, we'll error anyway, but we'll also re-run the search with all traits + // in scope, and if we find another method which can be used, we'll output an + // appropriate hint suggesting to import the trait. + let filler_substs = rcvr_substs + .extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def)); + let illegal_sized_bound = self.predicates_require_illegal_sized_bound( + &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs), + ); + // Unify the (adjusted) self type with what the method expects. // // SUBTLE: if we want good error messages, because of "guessing" while matching @@ -106,16 +120,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Make sure nobody calls `drop()` explicitly. self.enforce_illegal_method_limitations(&pick); - // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that - // something which derefs to `Self` actually implements the trait and the caller - // wanted to make a static dispatch on it but forgot to import the trait. - // See test `src/test/ui/issue-35976.rs`. - // - // In that case, we'll error anyway, but we'll also re-run the search with all traits - // in scope, and if we find another method which can be used, we'll output an - // appropriate hint suggesting to import the trait. - let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates); - // Add any trait/regions obligations specified on the method's type parameters. // We won't add these if we encountered an illegal sized bound, so that we can use // a custom error in that case. diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index e29f0275bf4..c0b3a23fde4 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -20,8 +20,8 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; +use rustc_middle::ty::{DefIdTree, GenericParamDefKind}; use rustc_span::symbol::Ident; use rustc_span::Span; use rustc_trait_selection::traits; @@ -221,7 +221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We probe again, taking all traits into account (not only those in scope). - let candidates = match self.lookup_probe( + let mut candidates = match self.lookup_probe( span, segment.ident, self_ty, @@ -243,6 +243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect(), _ => Vec::new(), }; + candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id)); return Err(IllegalSizedBound(candidates, needs_mut, span)); } diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index e9b91414a07..2de22530356 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -21,9 +21,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{ - self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable, -}; +use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ @@ -713,7 +711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs); + let impl_ty = impl_ty.subst(self.tcx, impl_substs); debug!("impl_ty: {:?}", impl_ty); @@ -1811,9 +1809,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.erase_late_bound_regions(xform_fn_sig) } - /// Gets the type of an impl and generate substitutions with placeholders. - fn impl_ty_and_substs(&self, impl_def_id: DefId) -> (Ty<'tcx>, SubstsRef<'tcx>) { - (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) + /// Gets the type of an impl and generate substitutions with inference vars. + fn impl_ty_and_substs( + &self, + impl_def_id: DefId, + ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) { + (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) } fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index f93f567fb20..5621cf2e1a4 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -824,50 +824,62 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { } if let Some(non_structural_match_ty) = - traits::search_for_structural_match_violation(param.span, tcx, ty) + traits::search_for_structural_match_violation(param.span, tcx, ty, false) { // We use the same error code in both branches, because this is really the same // issue: we just special-case the message for type parameters to make it // clearer. - if let ty::Param(_) = ty.peel_refs().kind() { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ty, - ) - .span_label( - hir_ty.span, - format!("`{}` may not derive both `PartialEq` and `Eq`", ty), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } else { - let mut diag = struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - non_structural_match_ty.ty, - ); - - if ty == non_structural_match_ty.ty { - diag.span_label( + match ty.peel_refs().kind() { + ty::Param(_) => { + // Const parameters may not have type parameters as their types, + // because we cannot be sure that the type parameter derives `PartialEq` + // and `Eq` (just implementing them is not enough for `structural_match`). + struct_span_err!( + tcx.sess, hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); + E0741, + "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ + used as the type of a const parameter", + ) + .span_label( + hir_ty.span, + format!("`{ty}` may not derive both `PartialEq` and `Eq`"), + ) + .note( + "it is not currently possible to use a type parameter as the type of a \ + const parameter", + ) + .emit(); + } + ty::Float(_) => { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") + .emit(); } + _ => { + let mut diag = struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ + the type of a const parameter", + non_structural_match_ty.ty, + ); - diag.emit(); + if ty == non_structural_match_ty.ty { + diag.span_label( + hir_ty.span, + format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), + ); + } + + diag.emit(); + } } } } else { diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs index bbf31de527e..7534482cce9 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_typeck/src/outlives/explicit.rs @@ -6,7 +6,7 @@ use super::utils::*; #[derive(Debug)] pub struct ExplicitPredicatesMap<'tcx> { - map: FxHashMap<DefId, RequiredPredicates<'tcx>>, + map: FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, } impl<'tcx> ExplicitPredicatesMap<'tcx> { @@ -14,11 +14,11 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { ExplicitPredicatesMap { map: FxHashMap::default() } } - pub fn explicit_predicates_of( + pub(crate) fn explicit_predicates_of( &mut self, tcx: TyCtxt<'tcx>, def_id: DefId, - ) -> &RequiredPredicates<'tcx> { + ) -> &ty::EarlyBinder<RequiredPredicates<'tcx>> { self.map.entry(def_id).or_insert_with(|| { let predicates = if def_id.is_local() { tcx.explicit_predicates_of(def_id) @@ -63,7 +63,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { } } - required_predicates + ty::EarlyBinder(required_predicates) }) } } diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs index 52f9e386441..257a9520eeb 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs @@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; -use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -13,20 +13,19 @@ use super::utils::*; /// `global_inferred_outlives`: this is initially the empty map that /// was generated by walking the items in the crate. This will /// now be filled with inferred predicates. -pub fn infer_predicates<'tcx>( +pub(super) fn infer_predicates<'tcx>( tcx: TyCtxt<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, -) -> FxHashMap<DefId, RequiredPredicates<'tcx>> { +) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> { debug!("infer_predicates"); - let mut predicates_added = true; + let mut explicit_map = ExplicitPredicatesMap::new(); let mut global_inferred_outlives = FxHashMap::default(); // If new predicates were added then we need to re-calculate // all crates since there could be new implied predicates. - while predicates_added { - predicates_added = false; + 'outer: loop { + let mut predicates_added = false; // Visit all the crates and infer predicates for id in tcx.hir().items() { @@ -53,9 +52,9 @@ pub fn infer_predicates<'tcx>( tcx, field_ty, field_span, - &mut global_inferred_outlives, + &global_inferred_outlives, &mut item_required_predicates, - explicit_map, + &mut explicit_map, ); } } @@ -70,12 +69,17 @@ pub fn infer_predicates<'tcx>( // we walk the crates again and re-calculate predicates for all // items. let item_predicates_len: usize = - global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.len()); + global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len()); if item_required_predicates.len() > item_predicates_len { predicates_added = true; - global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates); + global_inferred_outlives + .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates)); } } + + if !predicates_added { + break 'outer; + } } global_inferred_outlives @@ -85,7 +89,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( tcx: TyCtxt<'tcx>, field_ty: Ty<'tcx>, field_span: Span, - global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>, + global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, ) { @@ -133,11 +137,13 @@ fn insert_required_predicates_to_be_wf<'tcx>( // 'a` holds for `Foo`. debug!("Adt"); if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) { - for (unsubstituted_predicate, &span) in unsubstituted_predicates { + for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 { // `unsubstituted_predicate` is `U: 'b` in the // example above. So apply the substitution to // get `T: 'a` (or `predicate`): - let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs); + let predicate = unsubstituted_predicates + .rebind(*unsubstituted_predicate) + .subst(tcx, substs); insert_outlives_predicate( tcx, predicate.0, @@ -224,7 +230,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// will give us `U: 'static` and `U: Foo`. The latter we /// can ignore, but we will want to process `U: 'static`, /// applying the substitution as above. -pub fn check_explicit_predicates<'tcx>( +fn check_explicit_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, substs: &[GenericArg<'tcx>], @@ -242,7 +248,7 @@ pub fn check_explicit_predicates<'tcx>( ); let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id); - for (outlives_predicate, &span) in explicit_predicates { + for (outlives_predicate, &span) in &explicit_predicates.0 { debug!("outlives_predicate = {:?}", &outlives_predicate); // Careful: If we are inferring the effects of a `dyn Trait<..>` @@ -287,7 +293,7 @@ pub fn check_explicit_predicates<'tcx>( continue; } - let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs); + let predicate = explicit_predicates.rebind(*outlives_predicate).subst(tcx, substs); debug!("predicate = {:?}", &predicate); insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); } diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index dccfee19960..8fa65d51e3b 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -88,9 +88,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { // for the type. // Compute the inferred predicates - let mut exp_map = explicit::ExplicitPredicatesMap::new(); - - let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map); + let global_inferred_outlives = implicit_infer::infer_predicates(tcx); // Convert the inferred predicates into the "collected" form the // global data structure expects. @@ -100,7 +98,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { let predicates = global_inferred_outlives .iter() .map(|(&def_id, set)| { - let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map( + let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map( |(ty::OutlivesPredicate(kind1, region2), &span)| { match kind1.unpack() { GenericArgKind::Type(ty1) => Some(( diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs index 14e3048cadc..b718ca94213 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_typeck/src/outlives/utils.rs @@ -7,12 +7,12 @@ use std::collections::BTreeMap; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. -pub type RequiredPredicates<'tcx> = +pub(crate) type RequiredPredicates<'tcx> = BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` -pub fn insert_outlives_predicate<'tcx>( +pub(crate) fn insert_outlives_predicate<'tcx>( tcx: TyCtxt<'tcx>, kind: GenericArg<'tcx>, outlived_region: Region<'tcx>, diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 265a57c3929..469f7d1172a 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -812,7 +812,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { /// Builds the `type defined here` message. fn show_definition(&self, err: &mut Diagnostic) { let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) { - if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() { + if self.tcx.sess.source_map().is_span_accessible(def_span) { def_span.into() } else { return; diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index a9493a94cac..96bab59d3f8 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -129,6 +129,7 @@ pub trait OsStrExt: Sealed { #[stable(feature = "rust1", since = "1.0.0")] impl OsStrExt for OsStr { + #[inline] fn encode_wide(&self) -> EncodeWide<'_> { self.as_inner().inner.encode_wide() } diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index 206bc38efb3..85afc1f5f6c 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - The options `infodir`, `localstatedir`, and `gpg-password-file` are no longer allowed in config.toml. Previously, they were ignored without warning. Note that `infodir` and `localstatedir` are still accepted by `./configure`, with a warning. [#82451](https://github.com/rust-lang/rust/pull/82451) - Change the names for `dist` commands to match the component they generate. [#90684](https://github.com/rust-lang/rust/pull/90684) - The `build.fast-submodules` option has been removed. Fast submodule checkouts are enabled unconditionally. Automatic submodule handling can still be disabled with `build.submodules = false`. +- Several unsupported `./configure` options have been removed: `optimize`, `parallel-compiler`. These can still be enabled with `--set`, although it isn't recommended. ### Non-breaking changes diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 4985b054678..9196b78c513 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -20,7 +20,15 @@ fn args(builder: &Builder<'_>) -> Vec<String> { arr.iter().copied().map(String::from) } - if let Subcommand::Clippy { fix, .. } = builder.config.cmd { + if let Subcommand::Clippy { + fix, + clippy_lint_allow, + clippy_lint_deny, + clippy_lint_warn, + clippy_lint_forbid, + .. + } = &builder.config.cmd + { // disable the most spammy clippy lints let ignored_lints = vec![ "many_single_char_names", // there are a lot in stdarch @@ -32,7 +40,7 @@ fn args(builder: &Builder<'_>) -> Vec<String> { "wrong_self_convention", ]; let mut args = vec![]; - if fix { + if *fix { #[rustfmt::skip] args.extend(strings(&[ "--fix", "-Zunstable-options", @@ -44,6 +52,12 @@ fn args(builder: &Builder<'_>) -> Vec<String> { } args.extend(strings(&["--", "--cap-lints", "warn"])); args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint))); + let mut clippy_lint_levels: Vec<String> = Vec::new(); + clippy_lint_allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v))); + clippy_lint_deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v))); + clippy_lint_warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v))); + clippy_lint_forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v))); + args.extend(clippy_lint_levels); args } else { vec![] diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index c099fedc3a7..3d678b2290d 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -25,6 +25,7 @@ use crate::config::{LlvmLibunwind, TargetSelection}; use crate::dist; use crate::native; use crate::tool::SourceType; +use crate::util::get_clang_cl_resource_dir; use crate::util::{exe, is_debug_info, is_dylib, output, symlink_dir, t, up_to_date}; use crate::LLVM_TOOLS; use crate::{CLang, Compiler, DependencyType, GitRepo, Mode}; @@ -772,10 +773,38 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { cargo.env("CFG_LLVM_ROOT", s); } - // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm. + + // Some LLVM linker flags (-L and -l) may be needed to link `rustc_llvm`. Its build script + // expects these to be passed via the `LLVM_LINKER_FLAGS` env variable, separated by + // whitespace. + // + // For example: + // - on windows, when `clang-cl` is used with instrumentation, we need to manually add + // clang's runtime library resource directory so that the profiler runtime library can be + // found. This is to avoid the linker errors about undefined references to + // `__llvm_profile_instrument_memop` when linking `rustc_driver`. + let mut llvm_linker_flags = String::new(); + if builder.config.llvm_profile_generate && target.contains("msvc") { + if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl { + // Add clang's runtime library directory to the search path + let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path); + llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display())); + } + } + + // The config can also specify its own llvm linker flags. if let Some(ref s) = builder.config.llvm_ldflags { - cargo.env("LLVM_LINKER_FLAGS", s); + if !llvm_linker_flags.is_empty() { + llvm_linker_flags.push_str(" "); + } + llvm_linker_flags.push_str(s); + } + + // Set the linker flags via the env var that `rustc_llvm`'s build script will read. + if !llvm_linker_flags.is_empty() { + cargo.env("LLVM_LINKER_FLAGS", llvm_linker_flags); } + // Building with a static libstdc++ is only supported on linux right now, // not for MSVC or macOS if builder.config.llvm_static_stdcpp diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 2fc036082cb..6b139decb55 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -31,11 +31,10 @@ def v(*args): options.append(Option(*args, value=True)) -o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code (use `--disable-optimize` for that)") +o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code") o("docs", "build.docs", "build standard library documentation") o("compiler-docs", "build.compiler-docs", "build compiler documentation") o("optimize-tests", "rust.optimize-tests", "build tests with optimizations") -o("parallel-compiler", "rust.parallel-compiler", "build a multi-threaded rustc") o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests") o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds") o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds") @@ -70,7 +69,6 @@ v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind") # Optimization and debugging options. These may be overridden by the release # channel, etc. -o("optimize", "rust.optimize", "build optimized rust code") o("optimize-llvm", "llvm.optimize", "build optimized LLVM") o("llvm-assertions", "llvm.assertions", "build LLVM with assertions") o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface") @@ -161,7 +159,7 @@ v("default-linker", "rust.default-linker", "the default linker") # Many of these are saved below during the "writing configuration" step # (others are conditionally saved). o("manage-submodules", "build.submodules", "let the build manage the git submodules") -o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two") +o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)") o("extended", "build.extended", "build an extended rust tool set") v("tools", None, "List of extended tools will be installed") @@ -493,4 +491,3 @@ with bootstrap.output('Makefile') as f: p("") p("run `python {}/x.py --help`".format(rust_dir)) -p("") diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index eec19ab4fc9..1822c2936b7 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -91,6 +91,10 @@ pub enum Subcommand { Clippy { fix: bool, paths: Vec<PathBuf>, + clippy_lint_allow: Vec<String>, + clippy_lint_deny: Vec<String>, + clippy_lint_warn: Vec<String>, + clippy_lint_forbid: Vec<String>, }, Fix { paths: Vec<PathBuf>, @@ -246,6 +250,10 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`", opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "PROFILE"); opts.optflag("", "llvm-profile-generate", "generate PGO profile with llvm built for rustc"); opts.optopt("", "llvm-profile-use", "use PGO profile for llvm build", "PROFILE"); + opts.optmulti("A", "", "allow certain clippy lints", "OPT"); + opts.optmulti("D", "", "deny certain clippy lints", "OPT"); + opts.optmulti("W", "", "warn about certain clippy lints", "OPT"); + opts.optmulti("F", "", "forbid certain clippy lints", "OPT"); // We can't use getopt to parse the options until we have completed specifying which // options are valid, but under the current implementation, some options are conditional on @@ -544,7 +552,14 @@ Arguments: } Subcommand::Check { paths } } - Kind::Clippy => Subcommand::Clippy { paths, fix: matches.opt_present("fix") }, + Kind::Clippy => Subcommand::Clippy { + paths, + fix: matches.opt_present("fix"), + clippy_lint_allow: matches.opt_strs("A"), + clippy_lint_warn: matches.opt_strs("W"), + clippy_lint_deny: matches.opt_strs("D"), + clippy_lint_forbid: matches.opt_strs("F"), + }, Kind::Fix => Subcommand::Fix { paths }, Kind::Test => Subcommand::Test { paths, diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 01ba0169b20..01e70b3fb2d 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -18,6 +18,7 @@ use std::process::Command; use crate::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::config::TargetSelection; +use crate::util::get_clang_cl_resource_dir; use crate::util::{self, exe, output, program_out_of_date, t, up_to_date}; use crate::{CLang, GitRepo}; @@ -776,7 +777,22 @@ impl Step for Lld { t!(fs::create_dir_all(&out_dir)); let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld")); - configure_cmake(builder, target, &mut cfg, true, LdFlags::default()); + let mut ldflags = LdFlags::default(); + + // When building LLD as part of a build with instrumentation on windows, for example + // when doing PGO on CI, cmake or clang-cl don't automatically link clang's + // profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid + // linking errors, much like LLVM's cmake setup does in that situation. + if builder.config.llvm_profile_generate && target.contains("msvc") { + if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() { + // Find clang's runtime library directory and push that as a search path to the + // cmake linker flags. + let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path); + ldflags.push_all(&format!("/libpath:{}", clang_rt_dir.display())); + } + } + + configure_cmake(builder, target, &mut cfg, true, ldflags); // This is an awful, awful hack. Discovered when we migrated to using // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index b627e503789..c5d62a8810a 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -576,3 +576,27 @@ fn absolute_windows(path: &std::path::Path) -> std::io::Result<std::path::PathBu } } } + +/// Adapted from https://github.com/llvm/llvm-project/blob/782e91224601e461c019e0a4573bbccc6094fbcd/llvm/cmake/modules/HandleLLVMOptions.cmake#L1058-L1079 +/// +/// When `clang-cl` is used with instrumentation, we need to add clang's runtime library resource +/// directory to the linker flags, otherwise there will be linker errors about the profiler runtime +/// missing. This function returns the path to that directory. +pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf { + // Similar to how LLVM does it, to find clang's library runtime directory: + // - we ask `clang-cl` to locate the `clang_rt.builtins` lib. + let mut builtins_locator = Command::new(clang_cl_path); + builtins_locator.args(&["/clang:-print-libgcc-file-name", "/clang:--rtlib=compiler-rt"]); + + let clang_rt_builtins = output(&mut builtins_locator); + let clang_rt_builtins = Path::new(clang_rt_builtins.trim()); + assert!( + clang_rt_builtins.exists(), + "`clang-cl` must correctly locate the library runtime directory" + ); + + // - the profiler runtime will be located in the same directory as the builtins lib, like + // `$LLVM_DISTRO_ROOT/lib/clang/$LLVM_VERSION/lib/windows`. + let clang_rt_dir = clang_rt_builtins.parent().expect("The clang lib folder should exist"); + clang_rt_dir.to_path_buf() +} diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh index 495c539d069..1025f5bce80 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh @@ -4,7 +4,7 @@ set -ex source shared.sh -LLVM=llvmorg-14.0.2 +LLVM=llvmorg-14.0.5 mkdir llvm-project cd llvm-project diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index 7651947c525..13d440423b2 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -35,7 +35,6 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-debug \ --enable-lld \ - --enable-optimize \ --set llvm.use-linker=lld \ --set target.x86_64-unknown-linux-gnu.linker=clang \ --set target.x86_64-unknown-linux-gnu.cc=clang \ diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 65e15950559..3e5dc4af04a 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -33,7 +33,7 @@ if [ ! -d $MUSL ]; then fi cd $MUSL -./configure --enable-optimize --enable-debug --disable-shared --prefix=/musl-$TAG "$@" +./configure --enable-debug --disable-shared --prefix=/musl-$TAG "$@" if [ "$TAG" = "i586" -o "$TAG" = "i686" ]; then hide_output make -j$(nproc) AR=ar RANLIB=ranlib else diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 57832ac2b95..f92e46b0a97 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -625,7 +625,7 @@ jobs: --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler - SCRIPT: python x.py dist + SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-xl diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh index 9de970c9c2a..28bed1fa035 100755 --- a/src/ci/pgo.sh +++ b/src/ci/pgo.sh @@ -3,44 +3,82 @@ set -euxo pipefail +ci_dir=`cd $(dirname $0) && pwd` +source "$ci_dir/shared.sh" + +# The root checkout, where the source is located +CHECKOUT=/checkout + +DOWNLOADED_LLVM=/rustroot + +# The main directory where the build occurs, which can be different between linux and windows +BUILD_ROOT=$CHECKOUT/obj + +if isWindows; then + CHECKOUT=$(pwd) + DOWNLOADED_LLVM=$CHECKOUT/citools/clang-rust + BUILD_ROOT=$CHECKOUT +fi + +# The various build artifacts used in other commands: to launch rustc builds, build the perf +# collector, and run benchmarks to gather profiling data +BUILD_ARTIFACTS=$BUILD_ROOT/build/$PGO_HOST +RUSTC_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/rustc +CARGO_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/cargo +RUSTC_STAGE_2=$BUILD_ARTIFACTS/stage2/bin/rustc + +# Windows needs these to have the .exe extension +if isWindows; then + RUSTC_STAGE_0="${RUSTC_STAGE_0}.exe" + CARGO_STAGE_0="${CARGO_STAGE_0}.exe" + RUSTC_STAGE_2="${RUSTC_STAGE_2}.exe" +fi + +# Make sure we have a temporary PGO work folder +PGO_TMP=/tmp/tmp-pgo +mkdir -p $PGO_TMP +rm -rf $PGO_TMP/* + +RUSTC_PERF=$PGO_TMP/rustc-perf + # Compile several crates to gather execution PGO profiles. # Arg0 => profiles (Debug, Opt) # Arg1 => scenarios (Full, IncrFull, All) # Arg2 => crates (syn, cargo, ...) gather_profiles () { - cd /checkout/obj + cd $BUILD_ROOT # Compile libcore, both in opt-level=0 and opt-level=3 - RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ - --edition=2021 --crate-type=lib ../library/core/src/lib.rs - RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \ - --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs + RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ + --edition=2021 --crate-type=lib $CHECKOUT/library/core/src/lib.rs \ + --out-dir $PGO_TMP + RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \ + --edition=2021 --crate-type=lib -Copt-level=3 $CHECKOUT/library/core/src/lib.rs \ + --out-dir $PGO_TMP - cd rustc-perf + cd $RUSTC_PERF # Run rustc-perf benchmarks # Benchmark using profile_local with eprintln, which essentially just means # don't actually benchmark -- just make sure we run rustc a bunch of times. RUST_LOG=collector=debug \ - RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \ + RUSTC=$RUSTC_STAGE_0 \ RUSTC_BOOTSTRAP=1 \ - /checkout/obj/build/$PGO_HOST/stage0/bin/cargo run -p collector --bin collector -- \ - profile_local \ - eprintln \ - /checkout/obj/build/$PGO_HOST/stage2/bin/rustc \ - --id Test \ - --profiles $1 \ - --cargo /checkout/obj/build/$PGO_HOST/stage0/bin/cargo \ - --scenarios $2 \ - --include $3 - - cd /checkout/obj + $CARGO_STAGE_0 run -p collector --bin collector -- \ + profile_local \ + eprintln \ + $RUSTC_STAGE_2 \ + --id Test \ + --profiles $1 \ + --cargo $CARGO_STAGE_0 \ + --scenarios $2 \ + --include $3 + + cd $BUILD_ROOT } -rm -rf /tmp/rustc-pgo - # This path has to be absolute -LLVM_PROFILE_DIRECTORY_ROOT=/tmp/llvm-pgo +LLVM_PROFILE_DIRECTORY_ROOT=$PGO_TMP/llvm-pgo # We collect LLVM profiling information and rustc profiling information in # separate phases. This increases build time -- though not by a huge amount -- @@ -49,34 +87,47 @@ LLVM_PROFILE_DIRECTORY_ROOT=/tmp/llvm-pgo # LLVM IR PGO does not respect LLVM_PROFILE_FILE, so we have to set the profiling file # path through our custom environment variable. We include the PID in the directory path # to avoid updates to profile files being lost because of race conditions. -LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 ../x.py build \ +LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 $CHECKOUT/x.py build \ --target=$PGO_HOST \ --host=$PGO_HOST \ --stage 2 library/std \ --llvm-profile-generate -# Compile rustc perf -cp -r /tmp/rustc-perf ./ -chown -R $(whoami): ./rustc-perf -cd rustc-perf - -# Build the collector ahead of time, which is needed to make sure the rustc-fake -# binary used by the collector is present. -RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \ +# Compile rustc-perf: +# - get the expected commit source code: on linux, the Dockerfile downloads a source archive before +# running this script. On Windows, we do that here. +if isLinux; then + cp -r /tmp/rustc-perf $RUSTC_PERF + chown -R $(whoami): $RUSTC_PERF +else + # rustc-perf version from 2022-05-18 + PERF_COMMIT=f66cc8f3e04392b0e2fd811f21fd1ece6ebaded3 + retry curl -LS -o $PGO_TMP/perf.zip \ + https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \ + cd $PGO_TMP && unzip -q perf.zip && \ + mv rustc-perf-$PERF_COMMIT $RUSTC_PERF && \ + rm perf.zip +fi + +# - build rustc-perf's collector ahead of time, which is needed to make sure the rustc-fake binary +# used by the collector is present. +cd $RUSTC_PERF + +RUSTC=$RUSTC_STAGE_0 \ RUSTC_BOOTSTRAP=1 \ -/checkout/obj/build/$PGO_HOST/stage0/bin/cargo build -p collector +$CARGO_STAGE_0 build -p collector # Here we're profiling LLVM, so we only care about `Debug` and `Opt`, because we want to stress # codegen. We also profile some of the most prolific crates. gather_profiles "Debug,Opt" "Full" \ -"syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" + "syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18" -LLVM_PROFILE_MERGED_FILE=/tmp/llvm-pgo.profdata +LLVM_PROFILE_MERGED_FILE=$PGO_TMP/llvm-pgo.profdata # Merge the profile data we gathered for LLVM # Note that this uses the profdata from the clang we used to build LLVM, # which likely has a different version than our in-tree clang. -/rustroot/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT} +$DOWNLOADED_LLVM/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT} echo "LLVM PGO statistics" du -sh ${LLVM_PROFILE_MERGED_FILE} @@ -84,34 +135,45 @@ du -sh ${LLVM_PROFILE_DIRECTORY_ROOT} echo "Profile file count" find ${LLVM_PROFILE_DIRECTORY_ROOT} -type f | wc -l +# We don't need the individual .profraw files now that they have been merged into a final .profdata +rm -r $LLVM_PROFILE_DIRECTORY_ROOT + # Rustbuild currently doesn't support rebuilding LLVM when PGO options # change (or any other llvm-related options); so just clear out the relevant # directories ourselves. -rm -r ./build/$PGO_HOST/llvm ./build/$PGO_HOST/lld +rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld # Okay, LLVM profiling is done, switch to rustc PGO. # The path has to be absolute -RUSTC_PROFILE_DIRECTORY_ROOT=/tmp/rustc-pgo +RUSTC_PROFILE_DIRECTORY_ROOT=$PGO_TMP/rustc-pgo -python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \ +python3 $CHECKOUT/x.py build --target=$PGO_HOST --host=$PGO_HOST \ --stage 2 library/std \ --rust-profile-generate=${RUSTC_PROFILE_DIRECTORY_ROOT} # Here we're profiling the `rustc` frontend, so we also include `Check`. # The benchmark set includes various stress tests that put the frontend under pressure. -# The profile data is written into a single filepath that is being repeatedly merged when each -# rustc invocation ends. Empirically, this can result in some profiling data being lost. -# That's why we override the profile path to include the PID. This will produce many more profiling -# files, but the resulting profile will produce a slightly faster rustc binary. -LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \ - "Check,Debug,Opt" "All" \ - "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" - -RUSTC_PROFILE_MERGED_FILE=/tmp/rustc-pgo.profdata +if isLinux; then + # The profile data is written into a single filepath that is being repeatedly merged when each + # rustc invocation ends. Empirically, this can result in some profiling data being lost. That's + # why we override the profile path to include the PID. This will produce many more profiling + # files, but the resulting profile will produce a slightly faster rustc binary. + LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \ + "Check,Debug,Opt" "All" \ + "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" +else + # On windows, we don't do that yet (because it generates a lot of data, hitting disk space + # limits on the builder), and use the default profraw merging behavior. + gather_profiles \ + "Check,Debug,Opt" "All" \ + "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0" +fi + +RUSTC_PROFILE_MERGED_FILE=$PGO_TMP/rustc-pgo.profdata # Merge the profile data we gathered -./build/$PGO_HOST/llvm/bin/llvm-profdata \ +$BUILD_ARTIFACTS/llvm/bin/llvm-profdata \ merge -o ${RUSTC_PROFILE_MERGED_FILE} ${RUSTC_PROFILE_DIRECTORY_ROOT} echo "Rustc PGO statistics" @@ -120,10 +182,13 @@ du -sh ${RUSTC_PROFILE_DIRECTORY_ROOT} echo "Profile file count" find ${RUSTC_PROFILE_DIRECTORY_ROOT} -type f | wc -l +# We don't need the individual .profraw files now that they have been merged into a final .profdata +rm -r $RUSTC_PROFILE_DIRECTORY_ROOT + # Rustbuild currently doesn't support rebuilding LLVM when PGO options # change (or any other llvm-related options); so just clear out the relevant # directories ourselves. -rm -r ./build/$PGO_HOST/llvm ./build/$PGO_HOST/lld +rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld # This produces the actual final set of artifacts, using both the LLVM and rustc # collected profiling data. diff --git a/src/ci/run.sh b/src/ci/run.sh index b0314047c07..16d8bdb8153 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -132,7 +132,7 @@ trap datecheck EXIT SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then - $SRC/configure --enable-parallel-compiler + $SRC/configure --set rust.parallel-compiler CARGO_INCREMENTAL=0 $PYTHON ../x.py check rm -f config.toml rm -rf build diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index 48eb88c9f92..0bc8a0389a8 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -1,4 +1,5 @@ #!/bin/bash +# ignore-tidy-linelength # This script installs clang on the local machine. Note that we don't install # clang on Linux since its compiler story is just so different. Each container # has its own toolchain configured appropriately already. @@ -9,7 +10,7 @@ IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" # Update both macOS's and Windows's tarballs when bumping the version here. -LLVM_VERSION="14.0.2" +LLVM_VERSION="14.0.5" if isMacOS; then # If the job selects a specific Xcode version, use that instead of diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 272188f8299..1f30c7006f5 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -329,6 +329,17 @@ impl Options { return Err(0); } + let z_flags = matches.opt_strs("Z"); + if z_flags.iter().any(|x| *x == "help") { + print_flag_list("-Z", config::DB_OPTIONS); + return Err(0); + } + let c_flags = matches.opt_strs("C"); + if c_flags.iter().any(|x| *x == "help") { + print_flag_list("-C", config::CG_OPTIONS); + return Err(0); + } + let color = config::parse_color(matches); let config::JsonConfig { json_rendered, json_unused_externs, .. } = config::parse_json(matches); @@ -343,17 +354,6 @@ impl Options { // check for deprecated options check_deprecated_options(matches, &diag); - let z_flags = matches.opt_strs("Z"); - if z_flags.iter().any(|x| *x == "help") { - print_flag_list("-Z", config::DB_OPTIONS); - return Err(0); - } - let c_flags = matches.opt_strs("C"); - if c_flags.iter().any(|x| *x == "help") { - print_flag_list("-C", config::CG_OPTIONS); - return Err(0); - } - if matches.opt_strs("passes") == ["list"] { println!("Available passes for running rustdoc:"); for pass in passes::PASSES { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 509c4253f0f..39ec6a60856 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -726,31 +726,58 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { // Empty content so nothing to check in here... return true; } - rustc_span::create_session_if_not_set_then(edition, |_| { - let filename = FileName::anon_source_code(source); - let sess = ParseSess::with_silent_emitter(None); - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) - { - Ok(p) => p, - Err(_) => { - debug!("Cannot build a parser to check mod attr so skipping..."); - return true; + rustc_driver::catch_fatal_errors(|| { + rustc_span::create_session_if_not_set_then(edition, |_| { + use rustc_errors::emitter::EmitterWriter; + use rustc_errors::Handler; + use rustc_span::source_map::FilePathMapping; + + let filename = FileName::anon_source_code(source); + // Any errors in parsing should also appear when the doctest is compiled for real, so just + // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + + let emitter = EmitterWriter::new( + box io::sink(), + None, + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); + + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + let mut parser = + match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { + Ok(p) => p, + Err(_) => { + debug!("Cannot build a parser to check mod attr so skipping..."); + return true; + } + }; + // If a parsing error happened, it's very likely that the attribute is incomplete. + if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) { + e.cancel(); + return false; } - }; - // If a parsing error happened, it's very likely that the attribute is incomplete. - if parser.parse_attribute(InnerAttrPolicy::Permitted).is_err() { - return false; - } - // We now check if there is an unclosed delimiter for the attribute. To do so, we look at - // the `unclosed_delims` and see if the opening square bracket was closed. - parser - .unclosed_delims() - .get(0) - .map(|unclosed| { - unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2) - }) - .unwrap_or(true) + // We now check if there is an unclosed delimiter for the attribute. To do so, we look at + // the `unclosed_delims` and see if the opening square bracket was closed. + parser + .unclosed_delims() + .get(0) + .map(|unclosed| { + unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2) + }) + .unwrap_or(true) + }) }) + .unwrap_or(false) } fn partition_source(s: &str, edition: Edition) -> (String, String, String) { diff --git a/src/test/run-make-fulldeps/target-specs/Makefile b/src/test/run-make-fulldeps/target-specs/Makefile index dff7a966c96..fb95ee5539a 100644 --- a/src/test/run-make-fulldeps/target-specs/Makefile +++ b/src/test/run-make-fulldeps/target-specs/Makefile @@ -8,4 +8,4 @@ all: RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-x86_64-unknown-linux-gnu-platform --crate-type=lib --emit=asm $(RUSTC) -Z unstable-options --target=my-awesome-platform.json --print target-spec-json > $(TMPDIR)/test-platform.json && $(RUSTC) -Z unstable-options --target=$(TMPDIR)/test-platform.json --print target-spec-json | diff -q $(TMPDIR)/test-platform.json - $(RUSTC) foo.rs --target=definitely-not-builtin-target 2>&1 | $(CGREP) 'may not set is_builtin' - $(RUSTC) foo.rs --target=mismatching-data-layout + $(RUSTC) foo.rs --target=mismatching-data-layout --crate-type=lib diff --git a/src/test/rustdoc-ui/c-help.rs b/src/test/rustdoc-ui/c-help.rs new file mode 100644 index 00000000000..e166edf8b61 --- /dev/null +++ b/src/test/rustdoc-ui/c-help.rs @@ -0,0 +1,6 @@ +// check-pass +// compile-flags: -Chelp +// check-stdout +// regex-error-pattern: -C\s+incremental + +pub struct Foo; diff --git a/src/test/rustdoc-ui/c-help.stdout b/src/test/rustdoc-ui/c-help.stdout new file mode 100644 index 00000000000..75b2e2a2a43 --- /dev/null +++ b/src/test/rustdoc-ui/c-help.stdout @@ -0,0 +1,51 @@ + -C ar=val -- this option is deprecated and does nothing + -C code-model=val -- choose the code model to use (`rustc --print code-models` for details) + -C codegen-units=val -- divide crate into N units to optimize in parallel + -C control-flow-guard=val -- use Windows Control Flow Guard (default: no) + -C debug-assertions=val -- explicitly enable the `cfg(debug_assertions)` directive + -C debuginfo=val -- debug info emission level (0 = no debug info, 1 = line tables only, 2 = full debug info with variable and type information; default: 0) + -C default-linker-libraries=val -- allow the linker to link its default libraries (default: no) + -C embed-bitcode=val -- emit bitcode in rlibs (default: yes) + -C extra-filename=val -- extra data to put in each output filename + -C force-frame-pointers=val -- force use of the frame pointers + -C force-unwind-tables=val -- force use of unwind tables + -C incremental=val -- enable incremental compilation + -C inline-threshold=val -- set the threshold for inlining a function + -C instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default) + -C link-arg=val -- a single extra argument to append to the linker invocation (can be used several times) + -C link-args=val -- extra arguments to append to the linker invocation (space separated) + -C link-dead-code=val -- keep dead code at link time (useful for code coverage) (default: no) + -C link-self-contained=val -- control whether to link Rust provided C objects/libraries or rely + on C toolchain installed in the system + -C linker=val -- system linker to link outputs with + -C linker-flavor=val -- linker flavor + -C linker-plugin-lto=val -- generate build artifacts that are compatible with linker-based LTO + -C llvm-args=val -- a list of arguments to pass to LLVM (space separated) + -C lto=val -- perform LLVM link-time optimizations + -C metadata=val -- metadata to mangle symbol names with + -C no-prepopulate-passes=val -- give an empty list of passes to the pass manager + -C no-redzone=val -- disable the use of the redzone + -C no-stack-check=val -- this option is deprecated and does nothing + -C no-vectorize-loops=val -- disable loop vectorization optimization passes + -C no-vectorize-slp=val -- disable LLVM's SLP vectorization pass + -C opt-level=val -- optimization level (0-3, s, or z; default: 0) + -C overflow-checks=val -- use overflow checks for integer arithmetic + -C panic=val -- panic strategy to compile crate with + -C passes=val -- a list of extra LLVM passes to run (space separated) + -C prefer-dynamic=val -- prefer dynamic linking to static linking (default: no) + -C profile-generate=val -- compile the program with profiling instrumentation + -C profile-use=val -- use the given `.profdata` file for profile-guided optimization + -C relocation-model=val -- control generation of position-independent code (PIC) (`rustc --print relocation-models` for details) + -C remark=val -- print remarks for these optimization passes (space separated, or "all") + -C rpath=val -- set rpath values in libs/exes (default: no) + -C save-temps=val -- save all temporary output files during compilation (default: no) + -C soft-float=val -- use soft float ABI (*eabihf targets only) (default: no) + -C split-debuginfo=val -- how to handle split-debuginfo, a platform-specific option + -C strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -C symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') + -C target-cpu=val -- select target processor (`rustc --print target-cpus` for details) + -C target-feature=val -- target specific attributes. (`rustc --print target-features` for details). This feature is unsafe. diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs new file mode 100644 index 00000000000..a30472ac56b --- /dev/null +++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs @@ -0,0 +1,10 @@ +// compile-flags:--test --test-args=--test-threads=1 +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +// check-pass + +/// ``` +/// #![deprecated(since = "5.2", note = "foo was rarely used. \ +/// Users should instead use bar")] +/// ``` +pub fn f() {} diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout new file mode 100644 index 00000000000..07a4f657dea --- /dev/null +++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-multiline-crate-attribute.rs - f (line 6) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/test/rustdoc-ui/z-help.rs b/src/test/rustdoc-ui/z-help.rs new file mode 100644 index 00000000000..c7cf841b937 --- /dev/null +++ b/src/test/rustdoc-ui/z-help.rs @@ -0,0 +1,6 @@ +// check-pass +// compile-flags: -Zhelp +// check-stdout +// regex-error-pattern: -Z\s+self-profile + +pub struct Foo; diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout new file mode 100644 index 00000000000..7296b35788a --- /dev/null +++ b/src/test/rustdoc-ui/z-help.stdout @@ -0,0 +1,195 @@ + -Z allow-features=val -- only allow the listed language features to be enabled in code (space separated) + -Z always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no) + -Z assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no) + -Z asm-comments=val -- generate comments into the assembly (may change behavior) (default: no) + -Z assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`. + -Z binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no) + -Z branch-protection=val -- set options for branch target identification and pointer authentication on AArch64 + -Z cf-protection=val -- instrument control-flow architecture protection + -Z cgu-partitioning-strategy=val -- the codegen unit partitioning strategy to use + -Z chalk=val -- enable the experimental Chalk-based trait solving engine + -Z codegen-backend=val -- the backend to use + -Z combine-cgu=val -- combine CGUs into a single one + -Z crate-attr=val -- inject the given attribute in the crate + -Z debug-info-for-profiling=val -- emit discriminators and other data necessary for AutoFDO + -Z debug-macros=val -- emit line numbers debug info inside macros (default: no) + -Z deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes) + -Z dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no) + -Z dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no) + -Z dlltool=val -- import library generation tool (windows-gnu only) + -Z dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no) + -Z drop-tracking=val -- enables drop tracking in generators (default: no) + -Z dual-proc-macros=val -- load proc macros for both target and host, but only link to the target (default: no) + -Z dump-dep-graph=val -- dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) (default: no) + -Z dump-drop-tracking-cfg=val -- dump drop-tracking control-flow graph as a `.dot` file (default: no) + -Z dump-mir=val -- dump MIR state to file. + `val` is used to select which passes and functions to dump. For example: + `all` matches all passes and functions, + `foo` matches all passes for functions whose name contains 'foo', + `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo', + `foo | bar` all passes for function names containing 'foo' or 'bar'. + -Z dump-mir-dataflow=val -- in addition to `.mir` files, create graphviz `.dot` files with dataflow results (default: no) + -Z dump-mir-dir=val -- the directory the MIR is dumped into (default: `mir_dump`) + -Z dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no) + -Z dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no) + -Z dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans. + -Z dwarf-version=val -- version of DWARF debug information to emit (default: 2 or 4, depending on platform) + -Z emit-stack-sizes=val -- emit a section containing stack size metadata (default: no) + -Z fewer-names=val -- reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) (default: no) + -Z force-unstable-if-unmarked=val -- force all crates to be `rustc_private` unstable (default: no) + -Z fuel=val -- set the optimization fuel quota for a crate + -Z function-sections=val -- whether each function should go in its own section + -Z future-incompat-test=val -- forces all lints to be future incompatible, used for internal testing (default: no) + -Z gcc-ld=val -- implementation of ld used by cc + -Z graphviz-dark-mode=val -- use dark-themed colors in graphviz output (default: no) + -Z graphviz-font=val -- use the given `fontname` in graphviz output; can be overridden by setting environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`) + -Z hir-stats=val -- print some statistics about AST and HIR (default: no) + -Z human-readable-cgu-names=val -- generate human-readable, predictable names for codegen units (default: no) + -Z identify-regions=val -- display unnamed regions as `'<id>`, using a non-ident unique id (default: no) + -Z incremental-ignore-spans=val -- ignore spans during ICH computation -- used for testing (default: no) + -Z incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no) + -Z incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no) + -Z incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no) + -Z inline-mir=val -- enable MIR inlining (default: no) + -Z inline-mir-threshold=val -- a default MIR inlining threshold (default: 50) + -Z inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100) + -Z inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs + -Z input-stats=val -- gather statistics about the input (default: no) + -Z instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are: + `=all` (implicit value) + `=except-unused-generics` + `=except-unused-functions` + `=off` (default) + -Z instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no) + -Z keep-hygiene-data=val -- keep hygiene data after analysis (default: no) + -Z link-native-libraries=val -- link native libraries in the linker invocation (default: yes) + -Z link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no) + -Z llvm-plugins=val -- a list LLVM plugins to enable (space separated) + -Z llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no) + -Z location-detail=val -- comma separated list of location details to be tracked when using caller_location valid options are `file`, `line`, and `column` (default: all) + -Z ls=val -- list the symbols defined by a library crate (default: no) + -Z macro-backtrace=val -- show macro backtraces (default: no) + -Z merge-functions=val -- control the operation of the MergeFunctions LLVM pass, taking the same values as the target option of the same name + -Z meta-stats=val -- gather metadata statistics (default: no) + -Z mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no) + -Z mir-enable-passes=val -- use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be enabled, overriding all other checks. Passes that are not specified are enabled or disabled by other flags as usual. + -Z mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds) + -Z move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted + -Z mutable-noalias=val -- emit noalias metadata for mutable references (default: yes) + -Z new-llvm-pass-manager=val -- use new LLVM pass manager (default: no) + -Z nll-facts=val -- dump facts from NLL analysis into side files (default: no) + -Z nll-facts-dir=val -- the directory the NLL facts are dumped into (default: `nll-facts`) + -Z no-analysis=val -- parse and expand the source, but run no analysis + -Z no-codegen=val -- run all passes except codegen; no output + -Z no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups + -Z no-interleave-lints=val -- execute lints separately; allows benchmarking individual lints + -Z no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests + -Z no-link=val -- compile without linking + -Z no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO) + -Z no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used + -Z no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate + -Z normalize-docs=val -- normalize associated items in rustdoc when generating documentation + -Z oom=val -- panic strategy for out-of-memory handling + -Z osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no) + -Z diagnostic-width=val -- set the current output width for diagnostic truncation + -Z panic-abort-tests=val -- support compiling tests with panic=abort (default: no) + -Z panic-in-drop=val -- panic strategy for panics in drops + -Z parse-only=val -- parse only; do not compile, assemble, or link (default: no) + -Z perf-stats=val -- print some performance-related statistics (default: no) + -Z pick-stable-methods-before-any-unstable=val -- try to pick stable methods first before picking any unstable methods (default: yes) + -Z plt=val -- whether to use the PLT when calling into shared libraries; + only has effect for PIC code on systems with ELF binaries + (default: PLT is disabled if full relro is enabled) + -Z polonius=val -- enable polonius-based borrow-checker (default: no) + -Z polymorphize=val -- perform polymorphization analysis + -Z pre-link-arg=val -- a single extra argument to prepend the linker invocation (can be used several times) + -Z pre-link-args=val -- extra arguments to prepend to the linker invocation (space separated) + -Z precise-enum-drop-elaboration=val -- use a more precise version of drop elaboration for matches on enums (default: yes). This results in better codegen, but has caused miscompilations on some tier 2 platforms. See #77382 and #74551. + -Z print-fuel=val -- make rustc print the total optimization fuel used by a crate + -Z print-llvm-passes=val -- print the LLVM optimization passes being run (default: no) + -Z print-mono-items=val -- print the result of the monomorphization collection pass + -Z print-type-sizes=val -- print layout information for each type encountered (default: no) + -Z proc-macro-backtrace=val -- show backtraces for panics during proc-macro execution (default: no) + -Z profile=val -- insert profiling code (default: no) + -Z profile-closures=val -- profile size of closures + -Z profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path) + -Z profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`) + -Z profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO) + -Z query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no) + -Z randomize-layout=val -- randomize the layout of types (default: no) + -Z layout-seed=val -- seed layout randomization + -Z relax-elf-relocations=val -- whether ELF relocations can be relaxed + -Z relro-level=val -- choose which RELRO level to use + -Z remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix + -Z simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes + -Z report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no) + -Z sanitizer=val -- use a sanitizer + -Z sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer + -Z sanitizer-recover=val -- enable recovery for selected sanitizers + -Z saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes) + -Z save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no) + -Z self-profile=val -- run the self profiler and output the raw event data + -Z self-profile-events=val -- specify the events recorded by the self profiler; + for example: `-Z self-profile-events=default,query-keys` + all options: none, all, default, generic-activity, query-provider, query-cache-hit + query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes + -Z self-profile-counter=val -- counter used by the self profiler (default: `wall-time`), one of: + `wall-time` (monotonic clock, i.e. `std::time::Instant`) + `instructions:u` (retired instructions, userspace-only) + `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy) + -Z share-generics=val -- make the current crate share its generic instantiations + -Z show-span=val -- show spans for compiler debugging (expr|pat|ty) + -Z span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span` + -Z span-free-formats=val -- exclude spans when debug-printing compiler state (default: no) + -Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`) + -Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details) + -Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB + -Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`) + -Z split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform) + (default: `split`) + + `split`: sections which do not require relocation are written into a DWARF object (`.dwo`) + file which is ignored by the linker + `single`: sections which do not require relocation are written into object file but ignored + by the linker + -Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF + -Z symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0') + -Z teach=val -- show extended diagnostic help (default: no) + -Z temps-dir=val -- the directory the intermediate files are written to + -Z translate-lang=val -- language identifier for diagnostic output + -Z translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation) + -Z translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics + -Z tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details) + -Z thinlto=val -- enable ThinLTO when possible + -Z thir-unsafeck=val -- use the THIR unsafety checker (default: no) + -Z threads=val -- use a thread pool with N threads + -Z time=val -- measure time of rustc processes (default: no) + -Z time-llvm-passes=val -- measure time of each LLVM pass (default: no) + -Z time-passes=val -- measure time of each rustc pass (default: no) + -Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details) + -Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no) + -Z translate-remapped-path-to-local-path=val -- translate remapped paths into local paths when possible (default: yes) + -Z trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes) + -Z treat-err-as-bug=val -- treat error number `val` that occurs as bug + -Z trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items + -Z ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no) + -Z uninit-const-chunk-threshold=val -- allow generating const initializers with mixed init/uninit chunks, and set the maximum number of chunks for which this is allowed (default: 16) + -Z unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no) + -Z unpretty=val -- present the input source, unstable (and less-pretty) variants; + `normal`, `identified`, + `expanded`, `expanded,identified`, + `expanded,hygiene` (with internal representations), + `ast-tree` (raw AST before expansion), + `ast-tree,expanded` (raw AST after expansion), + `hir` (the HIR), `hir,identified`, + `hir,typed` (HIR with types for each node), + `hir-tree` (dump the raw HIR), + `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR) + -Z unsound-mir-opts=val -- enable unsound and buggy MIR optimizations (default: no) + -Z unstable-options=val -- adds unstable command line options to rustc interface (default: no) + -Z use-ctors-section=val -- use legacy .ctors section for initializers rather than .init_array + -Z validate-mir=val -- validate MIR after each transformation + -Z verbose=val -- in general, enable more debug printouts (default: no) + -Z verify-llvm-ir=val -- verify LLVM IR (default: no) + -Z virtual-function-elimination=val -- enables dead virtual function elimination optimization. Requires `-Clto[=[fat,yes]]` + -Z wasi-exec-model=val -- whether to build a wasi command or reactor diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 98c22af387e..e282884289d 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -411,7 +411,7 @@ LL | #[derive(SessionDiagnostic)] | = help: normalized in stderr note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:538:19 + --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:539:19 | LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` diff --git a/src/test/ui/asm/issue-99122-2.rs b/src/test/ui/asm/issue-99122-2.rs new file mode 100644 index 00000000000..cfb9fd90a55 --- /dev/null +++ b/src/test/ui/asm/issue-99122-2.rs @@ -0,0 +1,21 @@ +// check-pass +// needs-asm-support +// only-x86_64 + +// This demonstrates why we need to erase regions before sized check in intrinsicck + +struct NoCopy; + +struct Wrap<'a, T, Tail: ?Sized>(&'a T, Tail); + +pub unsafe fn test() { + let i = NoCopy; + let j = Wrap(&i, ()); + let pointer = &j as *const _; + core::arch::asm!( + "nop", + in("eax") pointer, + ); +} + +fn main() {} diff --git a/src/test/ui/asm/issue-99122.rs b/src/test/ui/asm/issue-99122.rs new file mode 100644 index 00000000000..744a563d3d1 --- /dev/null +++ b/src/test/ui/asm/issue-99122.rs @@ -0,0 +1,13 @@ +// needs-asm-support +// only-x86_64 + +pub unsafe fn test() { + let pointer = 1u32 as *const _; + //~^ ERROR cannot cast to a pointer of an unknown kind + core::arch::asm!( + "nop", + in("eax") pointer, + ); +} + +fn main() {} diff --git a/src/test/ui/asm/issue-99122.stderr b/src/test/ui/asm/issue-99122.stderr new file mode 100644 index 00000000000..2758a4ac437 --- /dev/null +++ b/src/test/ui/asm/issue-99122.stderr @@ -0,0 +1,11 @@ +error[E0641]: cannot cast to a pointer of an unknown kind + --> $DIR/issue-99122.rs:5:27 + | +LL | let pointer = 1u32 as *const _; + | ^^^^^^^^ needs more type information + | + = note: the type information given here is insufficient to check whether the pointer cast is valid + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0641`. diff --git a/src/test/ui/async-await/async-await.rs b/src/test/ui/async-await/async-await.rs index 3d22025bf28..9cabf16f8bb 100644 --- a/src/test/ui/async-await/async-await.rs +++ b/src/test/ui/async-await/async-await.rs @@ -6,7 +6,7 @@ #![allow(unused)] -// edition:2018 +// edition: 2018 // aux-build:arc_wake.rs extern crate arc_wake; diff --git a/src/test/ui/const-generics/float-generic.adt_const_params.stderr b/src/test/ui/const-generics/float-generic.adt_const_params.stderr new file mode 100644 index 00000000000..fef5ef0d1fa --- /dev/null +++ b/src/test/ui/const-generics/float-generic.adt_const_params.stderr @@ -0,0 +1,11 @@ +error[E0741]: `f32` is forbidden as the type of a const generic parameter + --> $DIR/float-generic.rs:5:17 + | +LL | fn foo<const F: f32>() {} + | ^^^ + | + = note: floats do not derive `Eq` or `Ord`, which are required for const parameters + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/float-generic.rs b/src/test/ui/const-generics/float-generic.rs new file mode 100644 index 00000000000..b72059b5b1c --- /dev/null +++ b/src/test/ui/const-generics/float-generic.rs @@ -0,0 +1,12 @@ +// revisions: simple adt_const_params +#![cfg_attr(adt_const_params, feature(adt_const_params))] +#![cfg_attr(adt_const_params, allow(incomplete_features))] + +fn foo<const F: f32>() {} +//~^ ERROR `f32` is forbidden as the type of a const generic parameter + +const C: f32 = 1.0; + +fn main() { + foo::<C>(); +} diff --git a/src/test/ui/const-generics/float-generic.simple.stderr b/src/test/ui/const-generics/float-generic.simple.stderr new file mode 100644 index 00000000000..89ca36b0f63 --- /dev/null +++ b/src/test/ui/const-generics/float-generic.simple.stderr @@ -0,0 +1,11 @@ +error: `f32` is forbidden as the type of a const generic parameter + --> $DIR/float-generic.rs:5:17 + | +LL | fn foo<const F: f32>() {} + | ^^^ + | + = note: the only supported types are integers, `bool` and `char` + = help: more complex types are supported with `#![feature(adt_const_params)]` + +error: aborting due to previous error + diff --git a/src/test/ui/inference/erase-type-params-in-label.stderr b/src/test/ui/inference/erase-type-params-in-label.stderr index 7bb281802d2..5c52e7bcfab 100644 --- a/src/test/ui/inference/erase-type-params-in-label.stderr +++ b/src/test/ui/inference/erase-type-params-in-label.stderr @@ -10,10 +10,6 @@ note: required by a bound in `foo` | LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> { | ^^^^^^^ required by this bound in `foo` -help: consider giving `foo` an explicit type, where the type for type parameter `W` is specified - | -LL | let foo: Foo<i32, &str, W, Z> = foo(1, ""); - | ++++++++++++++++++++++ help: consider specifying the type arguments in the function call | LL | let foo = foo::<T, K, W, Z>(1, ""); @@ -31,10 +27,6 @@ note: required by a bound in `bar` | LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> { | ^^^^^^^ required by this bound in `bar` -help: consider giving `bar` an explicit type, where the type for type parameter `Z` is specified - | -LL | let bar: Bar<i32, &str, Z> = bar(1, ""); - | +++++++++++++++++++ help: consider specifying the type arguments in the function call | LL | let bar = bar::<T, K, Z>(1, ""); diff --git a/src/test/ui/inference/issue-71732.stderr b/src/test/ui/inference/issue-71732.stderr index db153d38aaa..04673a375cf 100644 --- a/src/test/ui/inference/issue-71732.stderr +++ b/src/test/ui/inference/issue-71732.stderr @@ -13,10 +13,6 @@ note: required by a bound in `HashMap::<K, V, S>::get` | LL | K: Borrow<Q>, | ^^^^^^^^^ required by this bound in `HashMap::<K, V, S>::get` -help: consider specifying the generic argument - | -LL | .get::<Q>(&"key".into()) - | +++++ help: consider specifying the type argument in the function call | LL | .get::<Q>(&"key".into()) diff --git a/src/test/ui/issues/issue-35976.stderr b/src/test/ui/issues/issue-35976.stderr index f9b9b7dbd34..fe16f97b9d0 100644 --- a/src/test/ui/issues/issue-35976.stderr +++ b/src/test/ui/issues/issue-35976.stderr @@ -6,11 +6,6 @@ LL | fn wait(&self) where Self: Sized; ... LL | arg.wait(); | ^^^^ - | -help: another candidate was found in the following trait, perhaps add a `use` for it: - | -LL | use private::Future; - | error: aborting due to previous error diff --git a/src/test/ui/lifetimes/bare-trait-object-borrowck.rs b/src/test/ui/lifetimes/bare-trait-object-borrowck.rs new file mode 100644 index 00000000000..45f5e4ae129 --- /dev/null +++ b/src/test/ui/lifetimes/bare-trait-object-borrowck.rs @@ -0,0 +1,24 @@ +#![allow(bare_trait_objects)] +// check-pass +pub struct FormatWith<'a, I, F> { + sep: &'a str, + /// FormatWith uses interior mutability because Display::fmt takes &self. + inner: RefCell<Option<(I, F)>>, +} + +use std::cell::RefCell; +use std::fmt; + +struct Layout; + +pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F> +where + I: Iterator, + F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result, +{ + FormatWith { sep: separator, inner: RefCell::new(Some((iter, f))) } +} + +fn main() { + let _ = new_format(0..32, " | ", |i, f| f(&format_args!("0x{:x}", i))); +} diff --git a/src/test/ui/lifetimes/bare-trait-object.rs b/src/test/ui/lifetimes/bare-trait-object.rs new file mode 100644 index 00000000000..9eff618c734 --- /dev/null +++ b/src/test/ui/lifetimes/bare-trait-object.rs @@ -0,0 +1,25 @@ +// Verify that lifetime resolution correctly accounts for `Fn` bare trait objects. +// check-pass +#![allow(bare_trait_objects)] + +// This should work as: fn next_u32(fill_buf: &mut dyn FnMut(&mut [u8])) +fn next_u32(fill_buf: &mut FnMut(&mut [u8])) { + let mut buf: [u8; 4] = [0; 4]; + fill_buf(&mut buf); +} + +fn explicit(fill_buf: &mut dyn FnMut(&mut [u8])) { + let mut buf: [u8; 4] = [0; 4]; + fill_buf(&mut buf); +} + +fn main() { + let _: fn(&mut FnMut(&mut [u8])) = next_u32; + let _: &dyn Fn(&mut FnMut(&mut [u8])) = &next_u32; + let _: fn(&mut FnMut(&mut [u8])) = explicit; + let _: &dyn Fn(&mut FnMut(&mut [u8])) = &explicit; + let _: fn(&mut dyn FnMut(&mut [u8])) = next_u32; + let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &next_u32; + let _: fn(&mut dyn FnMut(&mut [u8])) = explicit; + let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &explicit; +} diff --git a/src/test/ui/methods/issues/issue-61525.rs b/src/test/ui/methods/issues/issue-61525.rs new file mode 100644 index 00000000000..c5ca0326e43 --- /dev/null +++ b/src/test/ui/methods/issues/issue-61525.rs @@ -0,0 +1,20 @@ +pub trait Example { + fn query<Q>(self, q: Q); +} + +impl Example for i32 { + fn query<Q>(self, _: Q) { + unimplemented!() + } +} + +mod nested { + use super::Example; + fn example() { + 1.query::<dyn ToString>("") + //~^ ERROR the size for values of type `dyn ToString` cannot be known at compilation time + //~| ERROR mismatched types + } +} + +fn main() {} diff --git a/src/test/ui/methods/issues/issue-61525.stderr b/src/test/ui/methods/issues/issue-61525.stderr new file mode 100644 index 00000000000..aec968d7c44 --- /dev/null +++ b/src/test/ui/methods/issues/issue-61525.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `dyn ToString` cannot be known at compilation time + --> $DIR/issue-61525.rs:14:33 + | +LL | 1.query::<dyn ToString>("") + | ----- ^^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `dyn ToString` +note: required by a bound in `Example::query` + --> $DIR/issue-61525.rs:2:14 + | +LL | fn query<Q>(self, q: Q); + | ^ required by this bound in `Example::query` +help: consider relaxing the implicit `Sized` restriction + | +LL | fn query<Q: ?Sized>(self, q: Q); + | ++++++++ + +error[E0308]: mismatched types + --> $DIR/issue-61525.rs:14:33 + | +LL | 1.query::<dyn ToString>("") + | --------------------- ^^ expected trait object `dyn ToString`, found `&str` + | | + | arguments to this function are incorrect + | + = note: expected trait object `dyn ToString` + found reference `&'static str` +note: associated function defined here + --> $DIR/issue-61525.rs:2:8 + | +LL | fn query<Q>(self, q: Q); + | ^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/parser/macro/macro-doc-comments-1.stderr b/src/test/ui/parser/macro/macro-doc-comments-1.stderr index 96bdb9808ee..0ebf3d52b63 100644 --- a/src/test/ui/parser/macro/macro-doc-comments-1.stderr +++ b/src/test/ui/parser/macro/macro-doc-comments-1.stderr @@ -5,7 +5,10 @@ LL | macro_rules! outer { | ------------------ when calling this macro ... LL | //! Inner - | ^^^^^^^^^ no rules expected this token in macro call + | ^^^^^^^^^ + | | + | no rules expected this token in macro call + | inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match error: aborting due to previous error diff --git a/src/test/ui/parser/macro/macro-doc-comments-2.stderr b/src/test/ui/parser/macro/macro-doc-comments-2.stderr index 023d1a3e039..346d865868d 100644 --- a/src/test/ui/parser/macro/macro-doc-comments-2.stderr +++ b/src/test/ui/parser/macro/macro-doc-comments-2.stderr @@ -5,7 +5,10 @@ LL | macro_rules! inner { | ------------------ when calling this macro ... LL | /// Outer - | ^^^^^^^^^ no rules expected this token in macro call + | ^^^^^^^^^ + | | + | no rules expected this token in macro call + | outer doc comments expand to `#[doc = "..."]`, which is what this macro attempted to match error: aborting due to previous error diff --git a/src/test/ui/resolve/issue-55673.rs b/src/test/ui/resolve/issue-55673.rs new file mode 100644 index 00000000000..0436bd39742 --- /dev/null +++ b/src/test/ui/resolve/issue-55673.rs @@ -0,0 +1,12 @@ +trait Foo { + type Bar; +} + +fn foo<T: Foo>() +where + T::Baa: std::fmt::Debug, + //~^ ERROR associated type `Baa` not found for `T` +{ +} + +fn main() {} diff --git a/src/test/ui/resolve/issue-55673.stderr b/src/test/ui/resolve/issue-55673.stderr new file mode 100644 index 00000000000..39318f95905 --- /dev/null +++ b/src/test/ui/resolve/issue-55673.stderr @@ -0,0 +1,9 @@ +error[E0220]: associated type `Baa` not found for `T` + --> $DIR/issue-55673.rs:7:8 + | +LL | T::Baa: std::fmt::Debug, + | ^^^ there is a similarly named associated type `Bar` in the trait `Foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs index bb1aff70d89..d8febd273c9 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -9,9 +9,11 @@ fn _if_let_guard() { () if (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement () if (((let 0 = 1))) => {} //~^ ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement () if true && let 0 = 1 => {} //~^ ERROR `if let` guards are experimental @@ -23,13 +25,17 @@ fn _if_let_guard() { () if (let 0 = 1) && true => {} //~^ ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement () if true && (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement () if (let 0 = 1) && (let 0 = 1) => {} //~^ ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} //~^ ERROR `if let` guards are experimental @@ -38,6 +44,7 @@ fn _if_let_guard() { //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable //~| ERROR `let` expressions in this position are unstable + //~| ERROR expected expression, found `let` statement () if let Range { start: _, end: _ } = (true..true) && false => {} //~^ ERROR `if let` guards are experimental diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr index 370a57318fd..52b5bca628a 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -1,17 +1,59 @@ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:59:16 + --> $DIR/feature-gate.rs:10:16 + | +LL | () if (let 0 = 1) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:14:18 + | +LL | () if (((let 0 = 1))) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:26:16 + | +LL | () if (let 0 = 1) && true => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:30:24 + | +LL | () if true && (let 0 = 1) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:34:16 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:34:31 + | +LL | () if (let 0 = 1) && (let 0 = 1) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:40:42 + | +LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/feature-gate.rs:66:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:62:16 + --> $DIR/feature-gate.rs:69:16 | LL | use_expr!((let 0 = 1)); | ^^^ error: no rules expected the token `let` - --> $DIR/feature-gate.rs:71:15 + --> $DIR/feature-gate.rs:78:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -30,7 +72,7 @@ LL | () if let 0 = 1 => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:16:12 + --> $DIR/feature-gate.rs:18:12 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +82,7 @@ LL | () if true && let 0 = 1 => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:20:12 + --> $DIR/feature-gate.rs:22:12 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +92,7 @@ LL | () if let 0 = 1 && true => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:34:12 + --> $DIR/feature-gate.rs:40:12 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +102,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:42:12 + --> $DIR/feature-gate.rs:49:12 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -70,7 +112,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>` error[E0658]: `if let` guards are experimental - --> $DIR/feature-gate.rs:67:12 + --> $DIR/feature-gate.rs:74:12 | LL | () if let 0 = 1 => {} | ^^^^^^^^^^^^ @@ -89,7 +131,7 @@ LL | () if (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:13:18 + --> $DIR/feature-gate.rs:14:18 | LL | () if (((let 0 = 1))) => {} | ^^^^^^^^^ @@ -98,7 +140,7 @@ LL | () if (((let 0 = 1))) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:16:23 + --> $DIR/feature-gate.rs:18:23 | LL | () if true && let 0 = 1 => {} | ^^^^^^^^^ @@ -107,7 +149,7 @@ LL | () if true && let 0 = 1 => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:20:15 + --> $DIR/feature-gate.rs:22:15 | LL | () if let 0 = 1 && true => {} | ^^^^^^^^^ @@ -116,7 +158,7 @@ LL | () if let 0 = 1 && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:24:16 + --> $DIR/feature-gate.rs:26:16 | LL | () if (let 0 = 1) && true => {} | ^^^^^^^^^ @@ -125,7 +167,7 @@ LL | () if (let 0 = 1) && true => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:27:24 + --> $DIR/feature-gate.rs:30:24 | LL | () if true && (let 0 = 1) => {} | ^^^^^^^^^ @@ -134,7 +176,7 @@ LL | () if true && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:30:16 + --> $DIR/feature-gate.rs:34:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -143,7 +185,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:30:31 + --> $DIR/feature-gate.rs:34:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} | ^^^^^^^^^ @@ -152,7 +194,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:15 + --> $DIR/feature-gate.rs:40:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -161,7 +203,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:28 + --> $DIR/feature-gate.rs:40:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -170,7 +212,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:42 + --> $DIR/feature-gate.rs:40:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -179,7 +221,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:55 + --> $DIR/feature-gate.rs:40:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -188,7 +230,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:34:68 + --> $DIR/feature-gate.rs:40:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} | ^^^^^^^^^ @@ -197,7 +239,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:42:15 + --> $DIR/feature-gate.rs:49:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -206,7 +248,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:59:16 + --> $DIR/feature-gate.rs:66:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ @@ -215,7 +257,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:62:16 + --> $DIR/feature-gate.rs:69:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -223,6 +265,6 @@ LL | use_expr!((let 0 = 1)); = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable -error: aborting due to 25 previous errors +error: aborting due to 32 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index 36b730505c2..069d2dc414d 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -28,47 +28,61 @@ fn main() {} fn _if() { if (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement if (((let 0 = 1))) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement if (let 0 = 1) && true {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement if true && (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement if (let 0 = 1) && (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } fn _while() { while (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement while (((let 0 = 1))) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement while (let 0 = 1) && true {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement while true && (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement while (let 0 = 1) && (let 0 = 1) {} //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } fn _macros() { @@ -89,39 +103,64 @@ fn _macros() { } fn nested_within_if_expr() { - if &let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + if &let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement - if !let 0 = 0 {} //~ ERROR `let` expressions are not supported here - if *let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR type `bool` cannot be dereferenced - if -let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR cannot apply unary operator `-` to type `bool` + if !let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + if *let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR type `bool` cannot be dereferenced + //~| ERROR expected expression, found `let` statement + if -let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR cannot apply unary operator `-` to type `bool` + //~| ERROR expected expression, found `let` statement fn _check_try_binds_tighter() -> Result<(), ()> { if let 0 = 0? {} //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } - if (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here - //~^ ERROR the `?` operator can only be applied to values that implement `Try` + if (let 0 = 0)? {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR the `?` operator can only be applied to values that implement `Try` //~| ERROR the `?` operator can only be used in a function that returns `Result` + //~| ERROR expected expression, found `let` statement - if true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here - if (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here - if true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here - if true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here + if true || let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + if (true || let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + if true && (true || let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + if true || (true && let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here let mut x = true; - if x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + if x = let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement - if true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types - if ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types - if (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + if true..(let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement + if ..(let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement + if (let 0 = 0).. {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement // Binds as `(let ... = true)..true &&/|| false`. if let Range { start: _, end: _ } = true..true && false {} @@ -151,42 +190,68 @@ fn nested_within_if_expr() { if let true = let true = true {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } fn nested_within_while_expr() { - while &let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + while &let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement - while !let 0 = 0 {} //~ ERROR `let` expressions are not supported here - while *let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR type `bool` cannot be dereferenced - while -let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR cannot apply unary operator `-` to type `bool` + while !let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + while *let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR type `bool` cannot be dereferenced + //~| ERROR expected expression, found `let` statement + while -let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR cannot apply unary operator `-` to type `bool` + //~| ERROR expected expression, found `let` statement fn _check_try_binds_tighter() -> Result<(), ()> { while let 0 = 0? {} //~^ ERROR the `?` operator can only be applied to values that implement `Try` Ok(()) } - while (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here - //~^ ERROR the `?` operator can only be applied to values that implement `Try` + while (let 0 = 0)? {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR the `?` operator can only be applied to values that implement `Try` //~| ERROR the `?` operator can only be used in a function that returns `Result` + //~| ERROR expected expression, found `let` statement - while true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here - while (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here - while true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here - while true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here + while true || let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + while (true || let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + while true && (true || let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + while true || (true && let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here let mut x = true; - while x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + while x = let 0 = 0 {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement - while true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types - while ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types - while (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here - //~^ ERROR mismatched types + while true..(let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement + while ..(let 0 = 0) {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement + while (let 0 = 0).. {} + //~^ ERROR `let` expressions are not supported here + //~| ERROR mismatched types + //~| ERROR expected expression, found `let` statement // Binds as `(let ... = true)..true &&/|| false`. while let Range { start: _, end: _ } = true..true && false {} @@ -216,6 +281,7 @@ fn nested_within_while_expr() { while let true = let true = true {} //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } fn not_error_because_clarified_intent() { @@ -316,15 +382,18 @@ fn inside_const_generic_arguments() { impl<const B: bool> A<{B}> { const O: u32 = 5; } if let A::<{ - true && let 1 = 1 //~ ERROR `let` expressions are not supported here + true && let 1 = 1 + //~^ ERROR `let` expressions are not supported here }>::O = 5 {} while let A::<{ - true && let 1 = 1 //~ ERROR `let` expressions are not supported here + true && let 1 = 1 + //~^ ERROR `let` expressions are not supported here }>::O = 5 {} if A::<{ - true && let 1 = 1 //~ ERROR `let` expressions are not supported here + true && let 1 = 1 + //~^ ERROR `let` expressions are not supported here }>::O == 5 {} // In the cases above we have `ExprKind::Block` to help us out. @@ -345,14 +414,18 @@ fn with_parenthesis() { if (let Some(a) = opt && true) { //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } if (let Some(a) = opt) && true { //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } if (let Some(a) = opt) && (let Some(b) = a) { //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement } if let Some(a) = opt && (true && true) { } @@ -360,13 +433,18 @@ fn with_parenthesis() { if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement } if (let Some(a) = opt && (let Some(b) = a)) && true { //~^ ERROR `let` expressions are not supported here //~| ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement } if (let Some(a) = opt && (true)) && true { //~^ ERROR `let` expressions are not supported here + //~| ERROR expected expression, found `let` statement } if (true && (true)) && let Some(a) = opt { diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 93a1f691c8e..cca5310ee0f 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1,113 +1,353 @@ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:232:6 + --> $DIR/disallowed-positions.rs:29:9 + | +LL | if (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:33:11 + | +LL | if (((let 0 = 1))) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:37:9 + | +LL | if (let 0 = 1) && true {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:41:17 + | +LL | if true && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:45:9 + | +LL | if (let 0 = 1) && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:45:24 + | +LL | if (let 0 = 1) && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:51:35 + | +LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:59:12 + | +LL | while (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:63:14 + | +LL | while (((let 0 = 1))) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:67:12 + | +LL | while (let 0 = 1) && true {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:71:20 + | +LL | while true && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:75:12 + | +LL | while (let 0 = 1) && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:75:27 + | +LL | while (let 0 = 1) && (let 0 = 1) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:81:38 + | +LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:106:9 + | +LL | if &let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:111:9 + | +LL | if !let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:114:9 + | +LL | if *let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:118:9 + | +LL | if -let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:128:9 + | +LL | if (let 0 = 0)? {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:134:16 + | +LL | if true || let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:137:17 + | +LL | if (true || let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:140:25 + | +LL | if true && (true || let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:147:12 + | +LL | if x = let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:152:15 + | +LL | if true..(let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:156:11 + | +LL | if ..(let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:160:9 + | +LL | if (let 0 = 0).. {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:191:19 + | +LL | if let true = let true = true {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:197:12 + | +LL | while &let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:202:12 + | +LL | while !let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:205:12 + | +LL | while *let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:209:12 + | +LL | while -let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:219:12 + | +LL | while (let 0 = 0)? {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:225:19 + | +LL | while true || let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:228:20 + | +LL | while (true || let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:231:28 + | +LL | while true && (true || let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:238:15 + | +LL | while x = let 0 = 0 {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:243:18 + | +LL | while true..(let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:247:14 + | +LL | while ..(let 0 = 0) {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:251:12 + | +LL | while (let 0 = 0).. {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:282:22 + | +LL | while let true = let true = true {} + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:298:6 | LL | &let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:236:6 + --> $DIR/disallowed-positions.rs:302:6 | LL | !let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:6 + --> $DIR/disallowed-positions.rs:305:6 | LL | *let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:243:6 + --> $DIR/disallowed-positions.rs:309:6 | LL | -let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:253:6 + --> $DIR/disallowed-positions.rs:319:6 | LL | (let 0 = 0)?; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:259:13 + --> $DIR/disallowed-positions.rs:325:13 | LL | true || let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:262:14 + --> $DIR/disallowed-positions.rs:328:14 | LL | (true || let 0 = 0); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:265:22 + --> $DIR/disallowed-positions.rs:331:22 | LL | true && (true || let 0 = 0); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:270:9 + --> $DIR/disallowed-positions.rs:336:9 | LL | x = let 0 = 0; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:274:12 + --> $DIR/disallowed-positions.rs:340:12 | LL | true..(let 0 = 0); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:277:8 + --> $DIR/disallowed-positions.rs:343:8 | LL | ..(let 0 = 0); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:280:6 + --> $DIR/disallowed-positions.rs:346:6 | LL | (let 0 = 0)..; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:284:6 + --> $DIR/disallowed-positions.rs:350:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:289:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | (let true = let true = true); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:289:17 + --> $DIR/disallowed-positions.rs:355:17 | LL | (let true = let true = true); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:296:25 + --> $DIR/disallowed-positions.rs:362:25 | LL | let x = true && let y = 1; | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:302:19 + --> $DIR/disallowed-positions.rs:368:19 | LL | [1, 2, 3][let _ = ()] | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:307:6 + --> $DIR/disallowed-positions.rs:373:6 | LL | &let 0 = 0 | ^^^ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:337:9 + --> $DIR/disallowed-positions.rs:406:9 | LL | true && let 1 = 1 | ^^^^^^^^^^^^^^^^^ @@ -118,25 +358,79 @@ LL | { true && let 1 = 1 } | + + error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:384:22 + --> $DIR/disallowed-positions.rs:415:9 + | +LL | if (let Some(a) = opt && true) { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:420:9 + | +LL | if (let Some(a) = opt) && true { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:424:9 + | +LL | if (let Some(a) = opt) && (let Some(b) = a) { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:424:32 + | +LL | if (let Some(a) = opt) && (let Some(b) = a) { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:433:9 + | +LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:433:31 + | +LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:439:9 + | +LL | if (let Some(a) = opt && (let Some(b) = a)) && true { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:439:31 + | +LL | if (let Some(a) = opt && (let Some(b) = a)) && true { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:445:9 + | +LL | if (let Some(a) = opt && (true)) && true { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/disallowed-positions.rs:462:22 | LL | let x = (true && let y = 1); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:389:20 + --> $DIR/disallowed-positions.rs:467:20 | LL | ([1, 2, 3][let _ = ()]) | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:81:16 + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^ error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:85:16 + --> $DIR/disallowed-positions.rs:99:16 | LL | use_expr!((let 0 = 1)); | ^^^ @@ -155,280 +449,280 @@ LL | if (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:32:11 + --> $DIR/disallowed-positions.rs:33:11 | LL | if (((let 0 = 1))) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:32:11 + --> $DIR/disallowed-positions.rs:33:11 | LL | if (((let 0 = 1))) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:35:9 + --> $DIR/disallowed-positions.rs:37:9 | LL | if (let 0 = 1) && true {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:35:9 + --> $DIR/disallowed-positions.rs:37:9 | LL | if (let 0 = 1) && true {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:38:17 + --> $DIR/disallowed-positions.rs:41:17 | LL | if true && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:38:17 + --> $DIR/disallowed-positions.rs:41:17 | LL | if true && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:41:9 + --> $DIR/disallowed-positions.rs:45:9 | LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:41:9 + --> $DIR/disallowed-positions.rs:45:9 | LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:41:24 + --> $DIR/disallowed-positions.rs:45:24 | LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:41:24 + --> $DIR/disallowed-positions.rs:45:24 | LL | if (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:45:35 + --> $DIR/disallowed-positions.rs:51:35 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:45:35 + --> $DIR/disallowed-positions.rs:51:35 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:45:48 + --> $DIR/disallowed-positions.rs:51:48 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:45:35 + --> $DIR/disallowed-positions.rs:51:35 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:45:61 + --> $DIR/disallowed-positions.rs:51:61 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:45:35 + --> $DIR/disallowed-positions.rs:51:35 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:52:12 + --> $DIR/disallowed-positions.rs:59:12 | LL | while (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:52:12 + --> $DIR/disallowed-positions.rs:59:12 | LL | while (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:55:14 + --> $DIR/disallowed-positions.rs:63:14 | LL | while (((let 0 = 1))) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:55:14 + --> $DIR/disallowed-positions.rs:63:14 | LL | while (((let 0 = 1))) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:58:12 + --> $DIR/disallowed-positions.rs:67:12 | LL | while (let 0 = 1) && true {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:58:12 + --> $DIR/disallowed-positions.rs:67:12 | LL | while (let 0 = 1) && true {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:61:20 + --> $DIR/disallowed-positions.rs:71:20 | LL | while true && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:61:20 + --> $DIR/disallowed-positions.rs:71:20 | LL | while true && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:64:12 + --> $DIR/disallowed-positions.rs:75:12 | LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:64:12 + --> $DIR/disallowed-positions.rs:75:12 | LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:64:27 + --> $DIR/disallowed-positions.rs:75:27 | LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:64:27 + --> $DIR/disallowed-positions.rs:75:27 | LL | while (let 0 = 1) && (let 0 = 1) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:68:38 + --> $DIR/disallowed-positions.rs:81:38 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:38 + --> $DIR/disallowed-positions.rs:81:38 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:68:51 + --> $DIR/disallowed-positions.rs:81:51 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:38 + --> $DIR/disallowed-positions.rs:81:38 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:68:64 + --> $DIR/disallowed-positions.rs:81:64 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:38 + --> $DIR/disallowed-positions.rs:81:38 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:81:16 + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:81:16 + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:81:16 + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:81:16 + --> $DIR/disallowed-positions.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); | ^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:85:16 + --> $DIR/disallowed-positions.rs:99:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:85:16 + --> $DIR/disallowed-positions.rs:99:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:85:16 + --> $DIR/disallowed-positions.rs:99:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:85:16 + --> $DIR/disallowed-positions.rs:99:16 | LL | use_expr!((let 0 = 1)); | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:92:9 + --> $DIR/disallowed-positions.rs:106:9 | LL | if &let 0 = 0 {} | ^^^^^^^^^ @@ -436,7 +730,7 @@ LL | if &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:95:9 + --> $DIR/disallowed-positions.rs:111:9 | LL | if !let 0 = 0 {} | ^^^^^^^^^ @@ -444,7 +738,7 @@ LL | if !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:96:9 + --> $DIR/disallowed-positions.rs:114:9 | LL | if *let 0 = 0 {} | ^^^^^^^^^ @@ -452,7 +746,7 @@ LL | if *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:98:9 + --> $DIR/disallowed-positions.rs:118:9 | LL | if -let 0 = 0 {} | ^^^^^^^^^ @@ -460,72 +754,72 @@ LL | if -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:106:9 + --> $DIR/disallowed-positions.rs:128:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:106:9 + --> $DIR/disallowed-positions.rs:128:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:110:16 + --> $DIR/disallowed-positions.rs:134:16 | LL | if true || let 0 = 0 {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:110:13 + --> $DIR/disallowed-positions.rs:134:13 | LL | if true || let 0 = 0 {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:111:17 + --> $DIR/disallowed-positions.rs:137:17 | LL | if (true || let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:111:14 + --> $DIR/disallowed-positions.rs:137:14 | LL | if (true || let 0 = 0) {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:112:25 + --> $DIR/disallowed-positions.rs:140:25 | LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:112:22 + --> $DIR/disallowed-positions.rs:140:22 | LL | if true && (true || let 0 = 0) {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:113:25 + --> $DIR/disallowed-positions.rs:143:25 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:113:17 + --> $DIR/disallowed-positions.rs:143:17 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:116:12 + --> $DIR/disallowed-positions.rs:147:12 | LL | if x = let 0 = 0 {} | ^^^^^^^^^ @@ -533,46 +827,46 @@ LL | if x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:119:15 + --> $DIR/disallowed-positions.rs:152:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:119:15 + --> $DIR/disallowed-positions.rs:152:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:121:11 + --> $DIR/disallowed-positions.rs:156:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:121:11 + --> $DIR/disallowed-positions.rs:156:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:123:9 + --> $DIR/disallowed-positions.rs:160:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:123:9 + --> $DIR/disallowed-positions.rs:160:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:127:8 + --> $DIR/disallowed-positions.rs:166:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +874,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:131:8 + --> $DIR/disallowed-positions.rs:170:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -588,7 +882,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:138:8 + --> $DIR/disallowed-positions.rs:177:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -596,7 +890,7 @@ LL | if let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:146:8 + --> $DIR/disallowed-positions.rs:185:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +898,7 @@ LL | if let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:152:19 + --> $DIR/disallowed-positions.rs:191:19 | LL | if let true = let true = true {} | ^^^^^^^^^^^^^^^ @@ -612,7 +906,7 @@ LL | if let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:157:12 + --> $DIR/disallowed-positions.rs:197:12 | LL | while &let 0 = 0 {} | ^^^^^^^^^ @@ -620,7 +914,7 @@ LL | while &let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:160:12 + --> $DIR/disallowed-positions.rs:202:12 | LL | while !let 0 = 0 {} | ^^^^^^^^^ @@ -628,7 +922,7 @@ LL | while !let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:161:12 + --> $DIR/disallowed-positions.rs:205:12 | LL | while *let 0 = 0 {} | ^^^^^^^^^ @@ -636,7 +930,7 @@ LL | while *let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:163:12 + --> $DIR/disallowed-positions.rs:209:12 | LL | while -let 0 = 0 {} | ^^^^^^^^^ @@ -644,72 +938,72 @@ LL | while -let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:171:12 + --> $DIR/disallowed-positions.rs:219:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:171:12 + --> $DIR/disallowed-positions.rs:219:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:175:19 + --> $DIR/disallowed-positions.rs:225:19 | LL | while true || let 0 = 0 {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:175:16 + --> $DIR/disallowed-positions.rs:225:16 | LL | while true || let 0 = 0 {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:176:20 + --> $DIR/disallowed-positions.rs:228:20 | LL | while (true || let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:176:17 + --> $DIR/disallowed-positions.rs:228:17 | LL | while (true || let 0 = 0) {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:177:28 + --> $DIR/disallowed-positions.rs:231:28 | LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:177:25 + --> $DIR/disallowed-positions.rs:231:25 | LL | while true && (true || let 0 = 0) {} | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:178:28 + --> $DIR/disallowed-positions.rs:234:28 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:178:20 + --> $DIR/disallowed-positions.rs:234:20 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:181:15 + --> $DIR/disallowed-positions.rs:238:15 | LL | while x = let 0 = 0 {} | ^^^^^^^^^ @@ -717,46 +1011,46 @@ LL | while x = let 0 = 0 {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:184:18 + --> $DIR/disallowed-positions.rs:243:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:184:18 + --> $DIR/disallowed-positions.rs:243:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:186:14 + --> $DIR/disallowed-positions.rs:247:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:186:14 + --> $DIR/disallowed-positions.rs:247:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:188:12 + --> $DIR/disallowed-positions.rs:251:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:188:12 + --> $DIR/disallowed-positions.rs:251:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:192:11 + --> $DIR/disallowed-positions.rs:257:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -764,7 +1058,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:196:11 + --> $DIR/disallowed-positions.rs:261:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -772,7 +1066,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:203:11 + --> $DIR/disallowed-positions.rs:268:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -780,7 +1074,7 @@ LL | while let Range { start: F, end } = F..|| true {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:211:11 + --> $DIR/disallowed-positions.rs:276:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -788,7 +1082,7 @@ LL | while let Range { start: true, end } = t..&&false {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:217:22 + --> $DIR/disallowed-positions.rs:282:22 | LL | while let true = let true = true {} | ^^^^^^^^^^^^^^^ @@ -796,7 +1090,7 @@ LL | while let true = let true = true {} = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:232:6 + --> $DIR/disallowed-positions.rs:298:6 | LL | &let 0 = 0; | ^^^^^^^^^ @@ -804,7 +1098,7 @@ LL | &let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:236:6 + --> $DIR/disallowed-positions.rs:302:6 | LL | !let 0 = 0; | ^^^^^^^^^ @@ -812,7 +1106,7 @@ LL | !let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:239:6 + --> $DIR/disallowed-positions.rs:305:6 | LL | *let 0 = 0; | ^^^^^^^^^ @@ -820,7 +1114,7 @@ LL | *let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:243:6 + --> $DIR/disallowed-positions.rs:309:6 | LL | -let 0 = 0; | ^^^^^^^^^ @@ -828,59 +1122,59 @@ LL | -let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:253:6 + --> $DIR/disallowed-positions.rs:319:6 | LL | (let 0 = 0)?; | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:253:6 + --> $DIR/disallowed-positions.rs:319:6 | LL | (let 0 = 0)?; | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:259:13 + --> $DIR/disallowed-positions.rs:325:13 | LL | true || let 0 = 0; | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:259:10 + --> $DIR/disallowed-positions.rs:325:10 | LL | true || let 0 = 0; | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:262:14 + --> $DIR/disallowed-positions.rs:328:14 | LL | (true || let 0 = 0); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:262:11 + --> $DIR/disallowed-positions.rs:328:11 | LL | (true || let 0 = 0); | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:265:22 + --> $DIR/disallowed-positions.rs:331:22 | LL | true && (true || let 0 = 0); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:265:19 + --> $DIR/disallowed-positions.rs:331:19 | LL | true && (true || let 0 = 0); | ^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:270:9 + --> $DIR/disallowed-positions.rs:336:9 | LL | x = let 0 = 0; | ^^^^^^^^^ @@ -888,46 +1182,46 @@ LL | x = let 0 = 0; = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:274:12 + --> $DIR/disallowed-positions.rs:340:12 | LL | true..(let 0 = 0); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:274:12 + --> $DIR/disallowed-positions.rs:340:12 | LL | true..(let 0 = 0); | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:277:8 + --> $DIR/disallowed-positions.rs:343:8 | LL | ..(let 0 = 0); | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:277:8 + --> $DIR/disallowed-positions.rs:343:8 | LL | ..(let 0 = 0); | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:280:6 + --> $DIR/disallowed-positions.rs:346:6 | LL | (let 0 = 0)..; | ^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:280:6 + --> $DIR/disallowed-positions.rs:346:6 | LL | (let 0 = 0)..; | ^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:284:6 + --> $DIR/disallowed-positions.rs:350:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -935,20 +1229,20 @@ LL | (let Range { start: _, end: _ } = true..true || false); = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:289:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | (let true = let true = true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:289:6 + --> $DIR/disallowed-positions.rs:355:6 | LL | (let true = let true = true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:307:6 + --> $DIR/disallowed-positions.rs:373:6 | LL | &let 0 = 0 | ^^^^^^^^^ @@ -956,7 +1250,7 @@ LL | &let 0 = 0 = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:319:17 + --> $DIR/disallowed-positions.rs:385:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -964,7 +1258,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:323:17 + --> $DIR/disallowed-positions.rs:390:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -972,7 +1266,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:327:17 + --> $DIR/disallowed-positions.rs:395:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -980,7 +1274,7 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:337:17 + --> $DIR/disallowed-positions.rs:406:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -988,124 +1282,124 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if` and `while` expressions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:346:9 + --> $DIR/disallowed-positions.rs:415:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:346:9 + --> $DIR/disallowed-positions.rs:415:9 | LL | if (let Some(a) = opt && true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:350:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:350:9 + --> $DIR/disallowed-positions.rs:420:9 | LL | if (let Some(a) = opt) && true { | ^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:353:9 + --> $DIR/disallowed-positions.rs:424:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:353:9 + --> $DIR/disallowed-positions.rs:424:9 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:353:32 + --> $DIR/disallowed-positions.rs:424:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:353:32 + --> $DIR/disallowed-positions.rs:424:32 | LL | if (let Some(a) = opt) && (let Some(b) = a) { | ^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:360:9 + --> $DIR/disallowed-positions.rs:433:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:360:9 + --> $DIR/disallowed-positions.rs:433:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:360:31 + --> $DIR/disallowed-positions.rs:433:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:360:31 + --> $DIR/disallowed-positions.rs:433:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { | ^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:364:9 + --> $DIR/disallowed-positions.rs:439:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:364:9 + --> $DIR/disallowed-positions.rs:439:9 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:364:31 + --> $DIR/disallowed-positions.rs:439:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:364:31 + --> $DIR/disallowed-positions.rs:439:31 | LL | if (let Some(a) = opt && (let Some(b) = a)) && true { | ^^^^^^^^^^^^^^^ error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:368:9 + --> $DIR/disallowed-positions.rs:445:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^ | = note: only supported directly in conditions of `if` and `while` expressions note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:368:9 + --> $DIR/disallowed-positions.rs:445:9 | LL | if (let Some(a) = opt && (true)) && true { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:92:8 + --> $DIR/disallowed-positions.rs:106:8 | LL | if &let 0 = 0 {} | ^^^^^^^^^^ expected `bool`, found `&bool` @@ -1117,19 +1411,19 @@ LL + if let 0 = 0 {} | error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:96:8 + --> $DIR/disallowed-positions.rs:114:8 | LL | if *let 0 = 0 {} | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:98:8 + --> $DIR/disallowed-positions.rs:118:8 | LL | if -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:106:8 + --> $DIR/disallowed-positions.rs:128:8 | LL | if (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -1137,7 +1431,7 @@ LL | if (let 0 = 0)? {} = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:106:19 + --> $DIR/disallowed-positions.rs:128:19 | LL | / fn nested_within_if_expr() { LL | | if &let 0 = 0 {} @@ -1154,7 +1448,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:116:8 + --> $DIR/disallowed-positions.rs:147:8 | LL | if x = let 0 = 0 {} | ^^^^^^^^^^^^^ expected `bool`, found `()` @@ -1165,7 +1459,7 @@ LL | if x == let 0 = 0 {} | ~~ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:119:8 + --> $DIR/disallowed-positions.rs:152:8 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1174,7 +1468,7 @@ LL | if true..(let 0 = 0) {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:121:8 + --> $DIR/disallowed-positions.rs:156:8 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` @@ -1183,7 +1477,7 @@ LL | if ..(let 0 = 0) {} found struct `RangeTo<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:123:8 + --> $DIR/disallowed-positions.rs:160:8 | LL | if (let 0 = 0).. {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` @@ -1192,7 +1486,7 @@ LL | if (let 0 = 0).. {} found struct `RangeFrom<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:127:12 + --> $DIR/disallowed-positions.rs:166:12 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1203,7 +1497,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:127:8 + --> $DIR/disallowed-positions.rs:166:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1212,7 +1506,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:131:12 + --> $DIR/disallowed-positions.rs:170:12 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1223,7 +1517,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:131:8 + --> $DIR/disallowed-positions.rs:170:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1232,7 +1526,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:138:12 + --> $DIR/disallowed-positions.rs:177:12 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1243,20 +1537,20 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:138:41 + --> $DIR/disallowed-positions.rs:177:41 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^ expected `bool`, found closure | = note: expected type `bool` - found closure `[closure@$DIR/disallowed-positions.rs:138:41: 138:43]` + found closure `[closure@$DIR/disallowed-positions.rs:177:41: 177:43]` help: use parentheses to call this closure | LL | if let Range { start: F, end } = F..(|| true)() {} | + +++ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:138:8 + --> $DIR/disallowed-positions.rs:177:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1265,7 +1559,7 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:146:12 + --> $DIR/disallowed-positions.rs:185:12 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1276,7 +1570,7 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:146:44 + --> $DIR/disallowed-positions.rs:185:44 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` @@ -1288,7 +1582,7 @@ LL + if let Range { start: true, end } = t..false {} | error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:146:8 + --> $DIR/disallowed-positions.rs:185:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1297,7 +1591,7 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<bool>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:102:20 + --> $DIR/disallowed-positions.rs:124:20 | LL | if let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1305,7 +1599,7 @@ LL | if let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:157:11 + --> $DIR/disallowed-positions.rs:197:11 | LL | while &let 0 = 0 {} | ^^^^^^^^^^ expected `bool`, found `&bool` @@ -1317,19 +1611,19 @@ LL + while let 0 = 0 {} | error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:161:11 + --> $DIR/disallowed-positions.rs:205:11 | LL | while *let 0 = 0 {} | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:163:11 + --> $DIR/disallowed-positions.rs:209:11 | LL | while -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:171:11 + --> $DIR/disallowed-positions.rs:219:11 | LL | while (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -1337,7 +1631,7 @@ LL | while (let 0 = 0)? {} = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:171:22 + --> $DIR/disallowed-positions.rs:219:22 | LL | / fn nested_within_while_expr() { LL | | while &let 0 = 0 {} @@ -1354,7 +1648,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:181:11 + --> $DIR/disallowed-positions.rs:238:11 | LL | while x = let 0 = 0 {} | ^^^^^^^^^^^^^ expected `bool`, found `()` @@ -1365,7 +1659,7 @@ LL | while x == let 0 = 0 {} | ~~ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:184:11 + --> $DIR/disallowed-positions.rs:243:11 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1374,7 +1668,7 @@ LL | while true..(let 0 = 0) {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:186:11 + --> $DIR/disallowed-positions.rs:247:11 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` @@ -1383,7 +1677,7 @@ LL | while ..(let 0 = 0) {} found struct `RangeTo<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:188:11 + --> $DIR/disallowed-positions.rs:251:11 | LL | while (let 0 = 0).. {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` @@ -1392,7 +1686,7 @@ LL | while (let 0 = 0).. {} found struct `RangeFrom<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:192:15 + --> $DIR/disallowed-positions.rs:257:15 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1403,7 +1697,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:192:11 + --> $DIR/disallowed-positions.rs:257:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1412,7 +1706,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:196:15 + --> $DIR/disallowed-positions.rs:261:15 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1423,7 +1717,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:196:11 + --> $DIR/disallowed-positions.rs:261:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1432,7 +1726,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:203:15 + --> $DIR/disallowed-positions.rs:268:15 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -1443,20 +1737,20 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:203:44 + --> $DIR/disallowed-positions.rs:268:44 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^ expected `bool`, found closure | = note: expected type `bool` - found closure `[closure@$DIR/disallowed-positions.rs:203:44: 203:46]` + found closure `[closure@$DIR/disallowed-positions.rs:268:44: 268:46]` help: use parentheses to call this closure | LL | while let Range { start: F, end } = F..(|| true)() {} | + +++ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:203:11 + --> $DIR/disallowed-positions.rs:268:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1465,7 +1759,7 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<bool>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:211:15 + --> $DIR/disallowed-positions.rs:276:15 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -1476,7 +1770,7 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:211:47 + --> $DIR/disallowed-positions.rs:276:47 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` @@ -1488,7 +1782,7 @@ LL + while let Range { start: true, end } = t..false {} | error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:211:11 + --> $DIR/disallowed-positions.rs:276:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -1497,7 +1791,7 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<bool>` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:167:23 + --> $DIR/disallowed-positions.rs:215:23 | LL | while let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -1505,19 +1799,19 @@ LL | while let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:239:5 + --> $DIR/disallowed-positions.rs:305:5 | LL | *let 0 = 0; | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:243:5 + --> $DIR/disallowed-positions.rs:309:5 | LL | -let 0 = 0; | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:253:5 + --> $DIR/disallowed-positions.rs:319:5 | LL | (let 0 = 0)?; | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -1525,7 +1819,7 @@ LL | (let 0 = 0)?; = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:253:16 + --> $DIR/disallowed-positions.rs:319:16 | LL | / fn outside_if_and_while_expr() { LL | | &let 0 = 0; @@ -1542,7 +1836,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:284:10 + --> $DIR/disallowed-positions.rs:350:10 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -1553,7 +1847,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:307:5 + --> $DIR/disallowed-positions.rs:373:5 | LL | fn outside_if_and_while_expr() { | - help: try adding a return type: `-> &bool` @@ -1562,14 +1856,14 @@ LL | &let 0 = 0 | ^^^^^^^^^^ expected `()`, found `&bool` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:249:17 + --> $DIR/disallowed-positions.rs:315:17 | LL | let 0 = 0?; | ^^ the `?` operator cannot be applied to type `{integer}` | = help: the trait `Try` is not implemented for `{integer}` -error: aborting due to 156 previous errors +error: aborting due to 205 previous errors Some errors have detailed explanations: E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs index 87718211308..2b407ef510c 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs @@ -19,6 +19,11 @@ fn _if() { if let Range { start: _, end: _ } = (true..true) && false {} //~^ ERROR `let` expressions in this position are unstable [E0658] + + if let 1 = 1 && let true = { true } && false { + //~^ ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + } } fn _while() { diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr index bcea8bbaa73..feea1c254d8 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr @@ -1,17 +1,17 @@ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:50:20 + --> $DIR/feature-gate.rs:55:20 | LL | #[cfg(FALSE)] (let 0 = 1); | ^^^ error: expected expression, found `let` statement - --> $DIR/feature-gate.rs:40:17 + --> $DIR/feature-gate.rs:45:17 | LL | noop_expr!((let 0 = 1)); | ^^^ error: no rules expected the token `let` - --> $DIR/feature-gate.rs:53:15 + --> $DIR/feature-gate.rs:58:15 | LL | macro_rules! use_expr { | --------------------- when calling this macro @@ -47,7 +47,25 @@ LL | if let Range { start: _, end: _ } = (true..true) && false {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:27:19 + --> $DIR/feature-gate.rs:23:8 + | +LL | if let 1 = 1 && let true = { true } && false { + | ^^^^^^^^^ + | + = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/feature-gate.rs:23:21 + | +LL | if let 1 = 1 && let true = { true } && false { + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information + = help: add `#![feature(let_chains)]` to the crate attributes to enable + +error[E0658]: `let` expressions in this position are unstable + --> $DIR/feature-gate.rs:32:19 | LL | while true && let 0 = 1 {} | ^^^^^^^^^ @@ -56,7 +74,7 @@ LL | while true && let 0 = 1 {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:30:11 + --> $DIR/feature-gate.rs:35:11 | LL | while let 0 = 1 && true {} | ^^^^^^^^^ @@ -65,7 +83,7 @@ LL | while let 0 = 1 && true {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:33:11 + --> $DIR/feature-gate.rs:38:11 | LL | while let Range { start: _, end: _ } = (true..true) && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +92,7 @@ LL | while let Range { start: _, end: _ } = (true..true) && false {} = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:50:20 + --> $DIR/feature-gate.rs:55:20 | LL | #[cfg(FALSE)] (let 0 = 1); | ^^^^^^^^^ @@ -83,7 +101,7 @@ LL | #[cfg(FALSE)] (let 0 = 1); = help: add `#![feature(let_chains)]` to the crate attributes to enable error[E0658]: `let` expressions in this position are unstable - --> $DIR/feature-gate.rs:40:17 + --> $DIR/feature-gate.rs:45:17 | LL | noop_expr!((let 0 = 1)); | ^^^^^^^^^ @@ -91,6 +109,6 @@ LL | noop_expr!((let 0 = 1)); = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs index 6cc53a1935b..a942d1f4caf 100644 --- a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs +++ b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs @@ -1,6 +1,3 @@ -// check-pass -// known-bug - #![feature(let_chains)] fn main() { @@ -8,10 +5,41 @@ fn main() { #[cfg(FALSE)] { + let _ = &&let Some(x) = Some(42); + //~^ ERROR expected expression, found `let` statement + } + #[cfg(FALSE)] + { + if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 { + //~^ ERROR expected expression, found `let` statement + //~| ERROR expected expression, found `let` statement + true + } + } + + #[cfg(FALSE)] + { if let Some(elem) = _opt && { [1, 2, 3][let _ = ()]; + //~^ ERROR expected expression, found `let` statement true } { } } + + #[cfg(FALSE)] + { + if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 { + //~^ ERROR expected expression, found `let` statement + true + } + } + #[cfg(FALSE)] + { + if let a = 1 && { + let x = let y = 1; + //~^ ERROR expected expression, found `let` statement + } { + } + } } diff --git a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr new file mode 100644 index 00000000000..d1ce83c7233 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr @@ -0,0 +1,38 @@ +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:8:19 + | +LL | let _ = &&let Some(x) = Some(42); + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:13:47 + | +LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:13:57 + | +LL | if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:23:23 + | +LL | [1, 2, 3][let _ = ()]; + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:32:47 + | +LL | if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 { + | ^^^ + +error: expected expression, found `let` statement + --> $DIR/invalid-let-in-a-valid-let-context.rs:40:21 + | +LL | let x = let y = 1; + | ^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/suggestions/auxiliary/meow.rs b/src/test/ui/suggestions/auxiliary/meow.rs new file mode 100644 index 00000000000..115df70a690 --- /dev/null +++ b/src/test/ui/suggestions/auxiliary/meow.rs @@ -0,0 +1,11 @@ +pub trait Meow { + fn meow(&self) {} +} + +pub struct GlobalMeow; + +impl Meow for GlobalMeow {} + +pub(crate) struct PrivateMeow; + +impl Meow for PrivateMeow {} diff --git a/src/test/ui/suggestions/issue-99080.rs b/src/test/ui/suggestions/issue-99080.rs new file mode 100644 index 00000000000..91f574f35b8 --- /dev/null +++ b/src/test/ui/suggestions/issue-99080.rs @@ -0,0 +1,16 @@ +// aux-build:meow.rs + +extern crate meow; + +use meow::Meow; + +fn needs_meow<T: Meow>(t: T) {} + +fn main() { + needs_meow(1usize); + //~^ ERROR the trait bound `usize: Meow` is not satisfied +} + +struct LocalMeow; + +impl Meow for LocalMeow {} diff --git a/src/test/ui/suggestions/issue-99080.stderr b/src/test/ui/suggestions/issue-99080.stderr new file mode 100644 index 00000000000..d1908dd9d0d --- /dev/null +++ b/src/test/ui/suggestions/issue-99080.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `usize: Meow` is not satisfied + --> $DIR/issue-99080.rs:10:16 + | +LL | needs_meow(1usize); + | ---------- ^^^^^^ the trait `Meow` is not implemented for `usize` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Meow`: + GlobalMeow + LocalMeow +note: required by a bound in `needs_meow` + --> $DIR/issue-99080.rs:7:18 + | +LL | fn needs_meow<T: Meow>(t: T) {} + | ^^^^ required by this bound in `needs_meow` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr index 115539a6dc2..6ce57b6263e 100644 --- a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr +++ b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr @@ -15,7 +15,6 @@ LL | s.strip_suffix(b'\n').unwrap_or(s) &'c &'b str [char; N] char - pattern::MultiCharEqPattern<C> = note: required because of the requirements on the impl of `Pattern<'_>` for `u8` error: aborting due to previous error diff --git a/src/test/ui/traits/issue-59029-1.stderr b/src/test/ui/traits/issue-59029-1.stderr index 53cdb8b1baf..203a8928530 100644 --- a/src/test/ui/traits/issue-59029-1.stderr +++ b/src/test/ui/traits/issue-59029-1.stderr @@ -2,13 +2,13 @@ error[E0220]: associated type `Res` not found for `Self` --> $DIR/issue-59029-1.rs:5:52 | LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>; - | ^^^ associated type `Res` not found + | ^^^ there is a similarly named associated type `Res` in the trait `Svc` error[E0220]: associated type `Res` not found for `Self` --> $DIR/issue-59029-1.rs:5:52 | LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>; - | ^^^ associated type `Res` not found + | ^^^ there is a similarly named associated type `Res` in the trait `Svc` error: aborting due to 2 previous errors diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr index a2d23c4e9df..2b832e27c52 100644 --- a/src/test/ui/traits/issue-77982.stderr +++ b/src/test/ui/traits/issue-77982.stderr @@ -13,10 +13,6 @@ note: required by a bound in `HashMap::<K, V, S>::get` | LL | K: Borrow<Q>, | ^^^^^^^^^ required by this bound in `HashMap::<K, V, S>::get` -help: consider specifying the generic argument - | -LL | opts.get::<Q>(opt.as_ref()); - | +++++ help: consider specifying the type argument in the function call | LL | opts.get::<Q>(opt.as_ref()); diff --git a/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr b/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr index 25f8d538377..cbec3593421 100644 --- a/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr +++ b/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr @@ -30,10 +30,6 @@ LL | fn test<T,U>(_: T, _: U) | ---- required by a bound in this LL | where T : Convert<U> | ^^^^^^^^^^ required by this bound in `test` -help: consider specifying the generic arguments - | -LL | test::<i32, U>(22, std::default::Default::default()); - | ++++++++++ help: consider specifying the type arguments in the function call | LL | test::<T, U>(22, std::default::Default::default()); diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr b/src/test/ui/type-alias-impl-trait/not_well_formed.stderr index 91c1d031e4e..c36b95f47e8 100644 --- a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr +++ b/src/test/ui/type-alias-impl-trait/not_well_formed.stderr @@ -2,7 +2,7 @@ error[E0220]: associated type `Assoc` not found for `V` --> $DIR/not_well_formed.rs:9:29 | LL | type Foo<V> = impl Trait<V::Assoc>; - | ^^^^^ associated type `Assoc` not found + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc` error: aborting due to previous error diff --git a/src/test/ui/type/type-annotation-needed.stderr b/src/test/ui/type/type-annotation-needed.stderr index 7d7890c8b80..4af4c22f751 100644 --- a/src/test/ui/type/type-annotation-needed.stderr +++ b/src/test/ui/type/type-annotation-needed.stderr @@ -10,10 +10,6 @@ note: required by a bound in `foo` | LL | fn foo<T: Into<String>>(x: i32) {} | ^^^^^^^^^^^^ required by this bound in `foo` -help: consider specifying the generic argument - | -LL | foo::<T>(42); - | +++++ help: consider specifying the type argument in the function call | LL | foo::<T>(42); diff --git a/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs new file mode 100644 index 00000000000..501f4b6ef9e --- /dev/null +++ b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs @@ -0,0 +1,21 @@ +macro_rules! num { () => { 1 } } + +fn main() { + let x = 1i32; + x.e10; //~ERROR `i32` is a primitive type and therefore doesn't have fields + + let y = 1; + y.e10; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + + 2u32.e10; //~ERROR `u32` is a primitive type and therefore doesn't have fields + + num!().e10; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + + 2.e10foo; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + + 42._; + //~^ERROR expected identifier, found reserved identifier `_` + //~|ERROR `{integer}` is a primitive type and therefore doesn't have fields + + 42.a; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields +} diff --git a/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr new file mode 100644 index 00000000000..1ef1d4c28e4 --- /dev/null +++ b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr @@ -0,0 +1,51 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:16:8 + | +LL | 42._; + | ^ expected identifier, found reserved identifier + +error[E0610]: `i32` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:5:7 + | +LL | x.e10; + | ^^^ + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:8:7 + | +LL | y.e10; + | ^^^ + +error[E0610]: `u32` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:10:10 + | +LL | 2u32.e10; + | ^^^ + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:12:12 + | +LL | num!().e10; + | ^^^ + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:14:7 + | +LL | 2.e10foo; + | ^^^^^^ + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:16:8 + | +LL | 42._; + | ^ + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:20:8 + | +LL | 42.a; + | ^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0610`. diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed new file mode 100644 index 00000000000..ba83e79005b --- /dev/null +++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +fn main() { + 2.0e1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0E1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0f64; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0e+12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0e-12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.0e1f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields +} diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs new file mode 100644 index 00000000000..c102447f602 --- /dev/null +++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs @@ -0,0 +1,11 @@ +// run-rustfix + +fn main() { + 2.e1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.E1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.f64; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.e+12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.e-12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields + 2.e1f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields +} diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr new file mode 100644 index 00000000000..e8e069708a8 --- /dev/null +++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr @@ -0,0 +1,80 @@ +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:4:7 + | +LL | 2.e1; + | ^^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0e1; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:5:7 + | +LL | 2.E1; + | ^^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0E1; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:6:7 + | +LL | 2.f32; + | ^^^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0f32; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:7:7 + | +LL | 2.f64; + | ^^^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0f64; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:8:7 + | +LL | 2.e+12; + | ^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0e+12; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:9:7 + | +LL | 2.e-12; + | ^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0e-12; + | + + +error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields + --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:10:7 + | +LL | 2.e1f32; + | ^^^^^ + | +help: If the number is meant to be a floating point number, consider adding a `0` after the period + | +LL | 2.0e1f32; + | + + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0610`. diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 7cf4a88c470..17f2b77dab0 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -60,6 +60,8 @@ impl EarlyProps { pub struct TestProps { // Lines that should be expected, in order, on standard out pub error_patterns: Vec<String>, + // Regexes that should be expected, in order, on standard out + pub regex_error_patterns: Vec<String>, // Extra flags to pass to the compiler pub compile_flags: Vec<String>, // Extra flags to pass when the compiled code is run (such as --bench) @@ -163,6 +165,7 @@ pub struct TestProps { mod directives { pub const ERROR_PATTERN: &'static str = "error-pattern"; + pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern"; pub const COMPILE_FLAGS: &'static str = "compile-flags"; pub const RUN_FLAGS: &'static str = "run-flags"; pub const SHOULD_ICE: &'static str = "should-ice"; @@ -200,6 +203,7 @@ impl TestProps { pub fn new() -> Self { TestProps { error_patterns: vec![], + regex_error_patterns: vec![], compile_flags: vec![], run_flags: None, pp_exact: None, @@ -285,6 +289,12 @@ impl TestProps { &mut self.error_patterns, |r| r, ); + config.push_name_value_directive( + ln, + REGEX_ERROR_PATTERN, + &mut self.regex_error_patterns, + |r| r, + ); if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) { self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned())); @@ -294,7 +304,7 @@ impl TestProps { } if let Some(edition) = config.parse_edition(ln) { - self.compile_flags.push(format!("--edition={}", edition)); + self.compile_flags.push(format!("--edition={}", edition.trim())); has_edition = true; } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index dd9e2a6687e..5517b5a12c3 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -323,12 +323,13 @@ impl<'test> TestCx<'test> { let output_to_check = self.get_output(&proc_res); let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); if !expected_errors.is_empty() { - if !self.props.error_patterns.is_empty() { + if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() + { self.fatal("both error pattern and expected errors specified"); } self.check_expected_errors(expected_errors, &proc_res); } else { - self.check_error_patterns(&output_to_check, &proc_res, pm); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); } if self.props.should_ice { match proc_res.status.code() { @@ -363,7 +364,7 @@ impl<'test> TestCx<'test> { let output_to_check = self.get_output(&proc_res); self.check_correct_failure_status(&proc_res); - self.check_error_patterns(&output_to_check, &proc_res, pm); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); } fn get_output(&self, proc_res: &ProcRes) -> String { @@ -1222,14 +1223,13 @@ impl<'test> TestCx<'test> { } } - fn check_error_patterns( + fn check_all_error_patterns( &self, output_to_check: &str, proc_res: &ProcRes, pm: Option<PassMode>, ) { - debug!("check_error_patterns"); - if self.props.error_patterns.is_empty() { + if self.props.error_patterns.is_empty() && self.props.regex_error_patterns.is_empty() { if pm.is_some() { // FIXME(#65865) return; @@ -1243,13 +1243,8 @@ impl<'test> TestCx<'test> { let mut missing_patterns: Vec<String> = Vec::new(); - for pattern in &self.props.error_patterns { - if output_to_check.contains(pattern.trim()) { - debug!("found error pattern {}", pattern); - } else { - missing_patterns.push(pattern.to_string()); - } - } + self.check_error_patterns(output_to_check, &mut missing_patterns); + self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns); if missing_patterns.is_empty() { return; @@ -1268,6 +1263,44 @@ impl<'test> TestCx<'test> { } } + fn check_error_patterns(&self, output_to_check: &str, missing_patterns: &mut Vec<String>) { + debug!("check_error_patterns"); + for pattern in &self.props.error_patterns { + if output_to_check.contains(pattern.trim()) { + debug!("found error pattern {}", pattern); + } else { + missing_patterns.push(pattern.to_string()); + } + } + } + + fn check_regex_error_patterns( + &self, + output_to_check: &str, + proc_res: &ProcRes, + missing_patterns: &mut Vec<String>, + ) { + debug!("check_regex_error_patterns"); + + for pattern in &self.props.regex_error_patterns { + let pattern = pattern.trim(); + let re = match Regex::new(pattern) { + Ok(re) => re, + Err(err) => { + self.fatal_proc_rec( + &format!("invalid regex error pattern '{}': {:?}", pattern, err), + proc_res, + ); + } + }; + if re.is_match(output_to_check) { + debug!("found regex error pattern {}", pattern); + } else { + missing_patterns.push(pattern.to_string()); + } + } + } + fn check_no_compiler_crash(&self, proc_res: &ProcRes, should_ice: bool) { match proc_res.status.code() { Some(101) if !should_ice => { @@ -1892,7 +1925,9 @@ impl<'test> TestCx<'test> { // If we are extracting and matching errors in the new // fashion, then you want JSON mode. Old-skool error // patterns still match the raw compiler output. - if self.props.error_patterns.is_empty() { + if self.props.error_patterns.is_empty() + && self.props.regex_error_patterns.is_empty() + { rustc.args(&["--error-format", "json"]); rustc.args(&["--json", "future-incompat"]); } @@ -3268,10 +3303,11 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec("test run succeeded!", &proc_res); } - if !self.props.error_patterns.is_empty() { + if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() + { // "// error-pattern" comments let output_to_check = self.get_output(&proc_res); - self.check_error_patterns(&output_to_check, &proc_res, pm); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); } } @@ -3285,15 +3321,16 @@ impl<'test> TestCx<'test> { self.props.error_patterns ); if !explicit && self.config.compare_mode.is_none() { - let check_patterns = - should_run == WillExecute::No && !self.props.error_patterns.is_empty(); + let check_patterns = should_run == WillExecute::No + && (!self.props.error_patterns.is_empty() + || !self.props.regex_error_patterns.is_empty()); let check_annotations = !check_patterns || !expected_errors.is_empty(); if check_patterns { // "// error-pattern" comments let output_to_check = self.get_output(&proc_res); - self.check_error_patterns(&output_to_check, &proc_res, pm); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); } if check_annotations { |
