diff options
| author | bors <bors@rust-lang.org> | 2023-12-05 06:19:41 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-12-05 06:19:41 +0000 |
| commit | 88d905cb3a09054665b9cee9b7a3ee0c139f8df7 (patch) | |
| tree | f05f1c5f46ff2e217f60efad55acdae725e4bff2 | |
| parent | e27da142efd0f03795e2bb9a2925ba9611b2cd83 (diff) | |
| parent | 0d5fdcca6c935f41bc9895bfe40917e8888a1be1 (diff) | |
| download | rust-88d905cb3a09054665b9cee9b7a3ee0c139f8df7.tar.gz rust-88d905cb3a09054665b9cee9b7a3ee0c139f8df7.zip | |
Auto merge of #3210 - rust-lang:rustup-2023-12-05, r=RalfJung
Automatic Rustup
108 files changed, 2301 insertions, 769 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index bd35364509a..756af7269f2 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -197,23 +197,24 @@ impl<T> TypedArena<T> { start_ptr } + /// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`. + /// + /// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before + /// storing the elements in the arena. #[inline] pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] { - // This implementation is entirely separate to - // `DroplessIterator::alloc_from_iter`, even though conceptually they - // are the same. + // Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason + // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a + // reference to `self` and adding elements to the arena during iteration. // - // `DroplessIterator` (in the fast case) writes elements from the - // iterator one at a time into the allocated memory. That's easy - // because the elements don't implement `Drop`. But for `TypedArena` - // they do implement `Drop`, which means that if the iterator panics we - // could end up with some allocated-but-uninitialized elements, which - // will then cause UB in `TypedArena::drop`. + // For this reason, if we pre-allocated any space for the elements of this iterator, we'd + // have to track that some uninitialized elements are followed by some initialized elements, + // else we might accidentally drop uninitialized memory if something panics or if the + // iterator doesn't fill all the length we expected. // - // Instead we use an approach where any iterator panic will occur - // before the memory is allocated. This function is much less hot than - // `DroplessArena::alloc_from_iter`, so it doesn't need to be - // hyper-optimized. + // So we collect all the elements beforehand, which takes care of reentrancy and panic + // safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it + // doesn't need to be hyper-optimized. assert!(mem::size_of::<T>() != 0); let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect(); @@ -485,8 +486,9 @@ impl DroplessArena { /// # Safety /// - /// The caller must ensure that `mem` is valid for writes up to - /// `size_of::<T>() * len`. + /// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that + /// that memory stays allocated and not shared for the lifetime of `self`. This must hold even + /// if `iter.next()` allocates onto `self`. #[inline] unsafe fn write_from_iter<T, I: Iterator<Item = T>>( &self, @@ -516,6 +518,8 @@ impl DroplessArena { #[inline] pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] { + // Warning: this function is reentrant: `iter` could hold a reference to `&self` and + // allocate additional elements while we're iterating. let iter = iter.into_iter(); assert!(mem::size_of::<T>() != 0); assert!(!mem::needs_drop::<T>()); @@ -524,7 +528,7 @@ impl DroplessArena { match size_hint { (min, Some(max)) if min == max => { - // We know the exact number of elements the iterator will produce here + // We know the exact number of elements the iterator expects to produce here. let len = min; if len == 0 { @@ -532,10 +536,15 @@ impl DroplessArena { } let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T; + // SAFETY: `write_from_iter` doesn't touch `self`. It only touches the slice we just + // reserved. If the iterator panics or doesn't output `len` elements, this will + // leave some unallocated slots in the arena, which is fine because we do not call + // `drop`. unsafe { self.write_from_iter(iter, len, mem) } } (_, _) => { outline(move || -> &mut [T] { + // Takes care of reentrancy. let mut vec: SmallVec<[_; 8]> = iter.collect(); if vec.is_empty() { return &mut []; diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index b75a686b819..7111daa2ea9 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -14,8 +14,8 @@ use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::definitions::DefPathData; use rustc_session::parse::feature_err; +use rustc_span::symbol::kw; use rustc_span::{sym, Span}; use rustc_target::asm; use std::collections::hash_map::Entry; @@ -227,7 +227,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.create_def( parent_def_id.def_id, node_id, - DefPathData::AnonConst, + kw::Empty, DefKind::AnonConst, *op_sp, ); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5b07299d411..eb1a1d15027 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -13,10 +13,9 @@ use rustc_ast::*; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::definitions::DefPathData; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{respan, Spanned}; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_span::{DesugaringKind, Span}; use thin_vec::{thin_vec, ThinVec}; @@ -376,7 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.create_def( parent_def_id.def_id, node_id, - DefPathData::AnonConst, + kw::Empty, DefKind::AnonConst, f.span, ); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index fbbb9d7a52a..f0f3e2c3c74 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -3,7 +3,6 @@ use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; use super::{FnDeclKind, LoweringContext, ParamMode}; -use hir::definitions::DefPathData; use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; @@ -1367,7 +1366,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let def_id = self.create_def( self.local_def_id(parent_node_id), param_node_id, - DefPathData::TypeNs(sym::host), + sym::host, DefKind::ConstParam, span, ); @@ -1427,13 +1426,8 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some((span, hir_id, def_id)) = host_param_parts { let const_node_id = self.next_node_id(); - let anon_const = self.create_def( - def_id, - const_node_id, - DefPathData::AnonConst, - DefKind::AnonConst, - span, - ); + let anon_const = + self.create_def(def_id, const_node_id, kw::Empty, DefKind::AnonConst, span); let const_id = self.next_id(); let const_expr_id = self.next_id(); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index eb6d11a72e6..aa8ad978451 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -58,7 +58,6 @@ use rustc_errors::{DiagnosticArgFromDisplay, StashKey}; use rustc_hir as hir; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathData; use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate}; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::{ @@ -499,20 +498,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, parent: LocalDefId, node_id: ast::NodeId, - data: DefPathData, + name: Symbol, def_kind: DefKind, span: Span, ) -> LocalDefId { debug_assert_ne!(node_id, ast::DUMMY_NODE_ID); assert!( self.opt_local_def_id(node_id).is_none(), - "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", + "adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}", node_id, - data, + def_kind, self.tcx.hir().def_key(self.local_def_id(node_id)), ); - let def_id = self.tcx.at(span).create_def(parent, data, def_kind).def_id(); + let def_id = self.tcx.at(span).create_def(parent, name, def_kind).def_id(); debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); self.resolver.node_id_to_def_id.insert(node_id, def_id); @@ -809,7 +808,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let _def_id = self.create_def( self.current_hir_id_owner.def_id, param, - DefPathData::LifetimeNs(kw::UnderscoreLifetime), + kw::UnderscoreLifetime, DefKind::LifetimeParam, ident.span, ); @@ -1227,7 +1226,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let def_id = self.create_def( parent_def_id.def_id, node_id, - DefPathData::AnonConst, + kw::Empty, DefKind::AnonConst, span, ); @@ -1465,7 +1464,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.create_def( self.current_hir_id_owner.def_id, *def_node_id, - DefPathData::TypeNs(ident.name), + ident.name, DefKind::TyParam, span, ); @@ -1619,7 +1618,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let opaque_ty_def_id = self.create_def( self.current_hir_id_owner.def_id, opaque_ty_node_id, - DefPathData::ImplTrait, + kw::Empty, DefKind::OpaqueTy, opaque_ty_span, ); @@ -1674,7 +1673,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let duplicated_lifetime_def_id = self.create_def( opaque_ty_def_id, duplicated_lifetime_node_id, - DefPathData::LifetimeNs(lifetime.ident.name), + lifetime.ident.name, DefKind::LifetimeParam, lifetime.ident.span, ); @@ -2549,7 +2548,7 @@ impl<'hir> GenericArgsCtor<'hir> { let def_id = lcx.create_def( lcx.current_hir_id_owner.def_id, id, - DefPathData::AnonConst, + kw::Empty, DefKind::AnonConst, span, ); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7b259055d40..acd85dd9a2d 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -489,6 +489,15 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { #[instrument(level = "trace", skip(self))] fn load_operand(&mut self, place: PlaceRef<'tcx, &'ll Value>) -> OperandRef<'tcx, &'ll Value> { + if place.layout.is_unsized() { + let tail = self.tcx.struct_tail_with_normalize(place.layout.ty, |ty| ty, || {}); + if matches!(tail.kind(), ty::Foreign(..)) { + // Unsized locals and, at least conceptually, even unsized arguments must be copied + // around, which requires dynamically determining their size. Therefore, we cannot + // allow `extern` types here. Consult t-opsem before removing this check. + panic!("unsized locals must not be `extern` types"); + } + } assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 839cc4dabe3..8630e5623e1 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -594,7 +594,7 @@ fn push_unqualified_item_name( DefPathData::CrateRoot => { output.push_str(tcx.crate_name(def_id.krate).as_str()); } - DefPathData::ClosureExpr => { + DefPathData::Closure => { let label = coroutine_kind_label(tcx.coroutine_kind(def_id)); push_disambiguated_special_name( diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 0ab2b7ecd9c..6661f1f81e6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -414,6 +414,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { // value is through `undef`/`poison`, and the store itself is useless. } OperandValue::Ref(r, None, source_align) => { + assert!(dest.layout.is_sized(), "cannot directly store unsized values"); if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. let ty = bx.backend_type(dest.layout); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 45795a7f735..83425dee1a8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -143,7 +143,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { // Simple cases, which don't need DST adjustment: // * no metadata available - just log the case // * known alignment - sized types, `[T]`, `str` or a foreign type - // * packed struct - there is no alignment padding + // Note that looking at `field.align` is incorrect since that is not necessarily equal + // to the dynamic alignment of the type. match field.ty.kind() { _ if self.llextra.is_none() => { debug!( @@ -154,14 +155,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } _ if field.is_sized() => return simple(), ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(), - ty::Adt(def, _) => { - if def.repr().packed() { - // FIXME(eddyb) generalize the adjustment when we - // start supporting packing to larger alignments. - assert_eq!(self.layout.align.abi.bytes(), 1); - return simple(); - } - } _ => {} } @@ -186,7 +179,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { let unaligned_offset = bx.cx().const_usize(offset.bytes()); // Get the alignment of the field - let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + + // For packed types, we need to cap alignment. + if let ty::Adt(def, _) = self.layout.ty.kind() + && let Some(packed) = def.repr().pack + { + let packed = bx.const_usize(packed.bytes()); + let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed); + unsized_align = bx.select(cmp, unsized_align, packed) + } // Bump the unaligned offset up to the appropriate alignment let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align); diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index c0a20e51482..04e5b550d6d 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -282,9 +282,7 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { impl<'tcx> fmt::Display for FrameInfo<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - if tcx.def_key(self.instance.def_id()).disambiguated_data.data - == DefPathData::ClosureExpr - { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { write!(f, "inside closure") } else { // Note: this triggers a `good_path_delayed_bug` state, which means that if we ever @@ -299,7 +297,7 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> { impl<'tcx> FrameInfo<'tcx> { pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote { let span = self.span; - if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr { + if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure { errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 } } else { let instance = format!("{}", self.instance); diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 80e14f5a884..c29f23b913f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -3,17 +3,22 @@ //! and miri. use rustc_hir::def_id::DefId; -use rustc_middle::mir::{ - self, - interpret::{Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar}, - BinOp, ConstValue, NonDivergingIntrinsic, -}; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::{ + mir::{ + self, + interpret::{ + Allocation, ConstAllocation, GlobalId, InterpResult, PointerArithmetic, Scalar, + }, + BinOp, ConstValue, NonDivergingIntrinsic, + }, + ty::layout::TyAndLayout, +}; use rustc_span::symbol::{sym, Symbol}; -use rustc_target::abi::{Abi, Primitive, Size}; +use rustc_target::abi::Size; use super::{ util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy, @@ -22,23 +27,6 @@ use super::{ use crate::fluent_generated as fluent; -fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> { - let size = match kind { - Primitive::Int(integer, _) => integer.size(), - _ => bug!("invalid `{}` argument: {:?}", name, bits), - }; - let extra = 128 - u128::from(size.bits()); - let bits_out = match name { - sym::ctpop => u128::from(bits.count_ones()), - sym::ctlz => u128::from(bits.leading_zeros()) - extra, - sym::cttz => u128::from((bits << extra).trailing_zeros()) - extra, - sym::bswap => (bits << extra).swap_bytes(), - sym::bitreverse => (bits << extra).reverse_bits(), - _ => bug!("not a numeric intrinsic: {}", name), - }; - Scalar::from_uint(bits_out, size) -} - /// Directly returns an `Allocation` containing an absolute path representation of the given type. pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAllocation<'tcx> { let path = crate::util::type_name(tcx, ty); @@ -179,30 +167,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::bswap | sym::bitreverse => { let ty = instance_args.type_at(0); - let layout_of = self.layout_of(ty)?; + let layout = self.layout_of(ty)?; let val = self.read_scalar(&args[0])?; - let bits = val.to_bits(layout_of.size)?; - let kind = match layout_of.abi { - Abi::Scalar(scalar) => scalar.primitive(), - _ => span_bug!( - self.cur_span(), - "{} called on invalid type {:?}", - intrinsic_name, - ty - ), - }; - let (nonzero, actual_intrinsic_name) = match intrinsic_name { - sym::cttz_nonzero => (true, sym::cttz), - sym::ctlz_nonzero => (true, sym::ctlz), - other => (false, other), - }; - if nonzero && bits == 0 { - throw_ub_custom!( - fluent::const_eval_call_nonzero_intrinsic, - name = intrinsic_name, - ); - } - let out_val = numeric_intrinsic(actual_intrinsic_name, bits, kind); + let out_val = self.numeric_intrinsic(intrinsic_name, val, layout)?; self.write_scalar(out_val, dest)?; } sym::saturating_add | sym::saturating_sub => { @@ -493,6 +460,29 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + pub fn numeric_intrinsic( + &self, + name: Symbol, + val: Scalar<M::Provenance>, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { + assert!(layout.ty.is_integral(), "invalid type for numeric intrinsic: {}", layout.ty); + let bits = val.to_bits(layout.size)?; + let extra = 128 - u128::from(layout.size.bits()); + let bits_out = match name { + sym::ctpop => u128::from(bits.count_ones()), + sym::ctlz_nonzero | sym::cttz_nonzero if bits == 0 => { + throw_ub_custom!(fluent::const_eval_call_nonzero_intrinsic, name = name,); + } + sym::ctlz | sym::ctlz_nonzero => u128::from(bits.leading_zeros()) - extra, + sym::cttz | sym::cttz_nonzero => u128::from((bits << extra).trailing_zeros()) - extra, + sym::bswap => (bits << extra).swap_bytes(), + sym::bitreverse => (bits << extra).reverse_bits(), + _ => bug!("not a numeric intrinsic: {}", name), + }; + Ok(Scalar::from_uint(bits_out, layout.size)) + } + pub fn exact_div( &mut self, a: &ImmTy<'tcx, M::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index c8977aac0fc..4d9e296d544 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -163,7 +163,17 @@ where // 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)), + Some((_, align)) => { + // For packed types, we need to cap alignment. + let align = if let ty::Adt(def, _) = base.layout().ty.kind() + && let Some(packed) = def.repr().pack + { + align.min(packed) + } else { + 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. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index fedd380cada..258d6710bc5 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -1,3 +1,4 @@ +use crate::definitions::DefPathData; use crate::hir; use rustc_ast as ast; @@ -45,6 +46,7 @@ pub enum NonMacroAttrKind { } /// What kind of definition something is; e.g., `mod` vs `struct`. +/// `enum DefPathData` may need to be updated if a new variant is added here. #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum DefKind { // Type namespace @@ -221,6 +223,41 @@ impl DefKind { } } + pub fn def_path_data(self, name: Symbol) -> DefPathData { + match self { + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::ExternCrate => DefPathData::TypeNs(name), + DefKind::Fn + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static(..) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Field => DefPathData::ValueNs(name), + DefKind::Macro(..) => DefPathData::MacroNs(name), + DefKind::LifetimeParam => DefPathData::LifetimeNs(name), + DefKind::Ctor(..) => DefPathData::Ctor, + DefKind::Use => DefPathData::Use, + DefKind::ForeignMod => DefPathData::ForeignMod, + DefKind::AnonConst => DefPathData::AnonConst, + DefKind::InlineConst => DefPathData::AnonConst, + DefKind::OpaqueTy => DefPathData::OpaqueTy, + DefKind::GlobalAsm => DefPathData::GlobalAsm, + DefKind::Impl { .. } => DefPathData::Impl, + DefKind::Closure => DefPathData::Closure, + } + } + #[inline] pub fn is_fn_like(self) -> bool { matches!(self, DefKind::Fn | DefKind::AssocFn | DefKind::Closure) diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 168b336e374..d222325475d 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -246,6 +246,7 @@ impl DefPath { } } +/// New variants should only be added in synchronization with `enum DefKind`. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DefPathData { // Root: these should only be used for the root nodes, because @@ -271,7 +272,7 @@ pub enum DefPathData { /// Something in the lifetime namespace. LifetimeNs(Symbol), /// A closure expression. - ClosureExpr, + Closure, // Subportions of items: /// Implicit constructor for a unit or tuple-like struct or enum variant. @@ -280,9 +281,7 @@ pub enum DefPathData { AnonConst, /// An existential `impl Trait` type node. /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name. - ImplTrait, - /// `impl Trait` generated associated type node. - ImplTraitAssocTy, + OpaqueTy, } impl Definitions { @@ -403,16 +402,17 @@ impl DefPathData { pub fn get_opt_name(&self) -> Option<Symbol> { use self::DefPathData::*; match *self { + TypeNs(name) if name == kw::Empty => None, TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name), - - Impl | ForeignMod | CrateRoot | Use | GlobalAsm | ClosureExpr | Ctor | AnonConst - | ImplTrait | ImplTraitAssocTy => None, + Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst + | OpaqueTy => None, } } pub fn name(&self) -> DefPathDataName { use self::DefPathData::*; match *self { + TypeNs(name) if name == kw::Empty => DefPathDataName::Anon { namespace: sym::opaque }, TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => { DefPathDataName::Named(name) } @@ -422,10 +422,10 @@ impl DefPathData { ForeignMod => DefPathDataName::Anon { namespace: kw::Extern }, Use => DefPathDataName::Anon { namespace: kw::Use }, GlobalAsm => DefPathDataName::Anon { namespace: sym::global_asm }, - ClosureExpr => DefPathDataName::Anon { namespace: sym::closure }, + Closure => DefPathDataName::Anon { namespace: sym::closure }, Ctor => DefPathDataName::Anon { namespace: sym::constructor }, AnonConst => DefPathDataName::Anon { namespace: sym::constant }, - ImplTrait | ImplTraitAssocTy => DefPathDataName::Anon { namespace: sym::opaque }, + OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque }, } } } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index c3d07415bb8..759ebaa1d1e 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -326,7 +326,9 @@ impl<'tcx> InferCtxt<'tcx> { ) -> RelateResult<'tcx, ty::Const<'tcx>> { let span = self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span; - let Generalization { value, needs_wf: _ } = generalize::generalize( + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize( self, &mut CombineDelegate { infcx: self, span, param_env }, ct, @@ -445,7 +447,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { // `'?2` and `?3` are fresh region/type inference // variables. (Down below, we will relate `a_ty <: b_ty`, // adding constraints like `'x: '?2` and `?1 <: ?3`.) - let Generalization { value: b_ty, needs_wf } = generalize::generalize( + let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize( self.infcx, &mut CombineDelegate { infcx: self.infcx, @@ -457,7 +459,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { ambient_variance, )?; - debug!(?b_ty); self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); if needs_wf { @@ -477,19 +478,52 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { // relations wind up attributed to the same spans. We need // to associate causes/spans with each of the relations in // the stack to get this right. - match ambient_variance { - ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty), - ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance( - ty::Contravariant, - ty::VarianceDiagInfo::default(), - a_ty, - b_ty, - ), - ty::Variance::Bivariant => { - unreachable!("no code should be generalizing bivariantly (currently)") + if b_ty.is_ty_var() { + // This happens for cases like `<?0 as Trait>::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + + // FIXME(-Ztrait-solver=next): replace this with `AliasRelate` + let &ty::Alias(kind, data) = a_ty.kind() else { + bug!("generalization should only result in infer vars for aliases"); + }; + if !self.infcx.next_trait_solver() { + // The old solver only accepts projection predicates for associated types. + match kind { + ty::AliasKind::Projection => {} + ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => { + return Err(TypeError::CyclicTy(a_ty)); + } + } } - }?; + + // FIXME: This does not handle subtyping correctly, we should switch to + // alias-relate in the new solver and could instead create a new inference + // variable for `a_ty`, emitting `Projection(a_ty, a_infer)` and + // `a_infer <: b_ty`. + self.obligations.push(Obligation::new( + self.tcx(), + self.trace.cause.clone(), + self.param_env, + ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() }, + )) + } else { + match ambient_variance { + ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty), + ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty), + ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance( + ty::Contravariant, + ty::VarianceDiagInfo::default(), + a_ty, + b_ty, + ), + ty::Variance::Bivariant => { + unreachable!("no code should be generalizing bivariantly (currently)") + } + }?; + } Ok(()) } diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs index 9e24b020510..4b4017cec57 100644 --- a/compiler/rustc_infer/src/infer/generalize.rs +++ b/compiler/rustc_infer/src/infer/generalize.rs @@ -1,13 +1,16 @@ +use std::mem; + use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::visit::MaxUniverse; +use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt}; use rustc_span::Span; use crate::infer::nll_relate::TypeRelatingDelegate; -use crate::infer::type_variable::TypeVariableValue; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue}; use crate::infer::{InferCtxt, RegionVariableOrigin}; /// Attempts to generalize `term` for the type variable `for_vid`. @@ -38,27 +41,30 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> root_vid, for_universe, root_term: term.into(), + in_alias: false, needs_wf: false, cache: Default::default(), }; assert!(!term.has_escaping_bound_vars()); - let value = generalizer.relate(term, term)?; + let value_may_be_infer = generalizer.relate(term, term)?; let needs_wf = generalizer.needs_wf; - Ok(Generalization { value, needs_wf }) + Ok(Generalization { value_may_be_infer, needs_wf }) } /// Abstracts the handling of region vars between HIR and MIR/NLL typechecking /// in the generalizer code. -pub trait GeneralizerDelegate<'tcx> { +pub(super) trait GeneralizerDelegate<'tcx> { fn param_env(&self) -> ty::ParamEnv<'tcx>; fn forbid_inference_vars() -> bool; + fn span(&self) -> Span; + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; } -pub struct CombineDelegate<'cx, 'tcx> { +pub(super) struct CombineDelegate<'cx, 'tcx> { pub infcx: &'cx InferCtxt<'tcx>, pub param_env: ty::ParamEnv<'tcx>, pub span: Span, @@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> { false } + fn span(&self) -> Span { + self.span + } + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { // FIXME: This is non-ideal because we don't give a // very descriptive origin for this region variable. @@ -93,6 +103,10 @@ where <Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars() } + fn span(&self) -> Span { + <Self as TypeRelatingDelegate<'tcx>>::span(&self) + } + fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe) } @@ -139,6 +153,13 @@ struct Generalizer<'me, 'tcx, D> { cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>, + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can + /// hold by either normalizing the outer or the inner associated type. + in_alias: bool, + /// See the field `needs_wf` in `Generalization`. needs_wf: bool, } @@ -193,7 +214,7 @@ where opt_variances, a_subst, b_subst, - true, + false, ) } } @@ -309,6 +330,44 @@ where } } + ty::Alias(kind, data) => { + // An occurs check failure inside of an alias does not mean + // that the types definitely don't unify. We may be able + // to normalize the alias after all. + // + // We handle this by lazily equating the alias and generalizing + // it to an inference variable. + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(data, data) { + Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + t.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !t.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {t:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.infcx.next_ty_var_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.delegate.span(), + }, + self.for_universe, + )) + } + } + }; + self.in_alias = is_nested_alias; + result + } + _ => relate::structurally_relate_tys(self, t, t), }?; @@ -456,8 +515,16 @@ where /// not only the generalized type, but also a bool flag /// indicating whether further WF checks are needed. #[derive(Debug)] -pub struct Generalization<T> { - pub value: T, +pub(super) struct Generalization<T> { + /// When generalizing `<?0 as Trait>::Assoc` or + /// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc` + /// for `?0` generalization returns an inference + /// variable. + /// + /// This has to be handled wotj care as it can + /// otherwise very easily result in infinite + /// recursion. + pub(super) value_may_be_infer: T, /// If true, then the generalized type may not be well-formed, /// even if the source type is well-formed, so we should add an @@ -484,5 +551,5 @@ pub struct Generalization<T> { /// will force the calling code to check that `WF(Foo<?C, ?D>)` /// holds, which in turn implies that `?C::Item == ?D`. So once /// `?C` is constrained, that should suffice to restrict `?D`. - pub needs_wf: bool, + pub(super) needs_wf: bool, } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 77c1d6a7313..d707c30206d 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -214,13 +214,17 @@ where } fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> { - let Generalization { value: ty, needs_wf: _ } = generalize::generalize( + let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize( self.infcx, &mut self.delegate, ty, for_vid, self.ambient_variance, )?; + + if ty.is_ty_var() { + span_bug!(self.delegate.span(), "occurs check failure in MIR typeck"); + } Ok(ty) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ff690db7bee..8e71327f82e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -977,7 +977,7 @@ impl<'tcx> TyCtxtAt<'tcx> { pub fn create_def( self, parent: LocalDefId, - data: hir::definitions::DefPathData, + name: Symbol, def_kind: DefKind, ) -> TyCtxtFeed<'tcx, LocalDefId> { // This function modifies `self.definitions` using a side-effect. @@ -1000,6 +1000,7 @@ impl<'tcx> TyCtxtAt<'tcx> { // This is fine because: // - those queries are `eval_always` so we won't miss their result changing; // - this write will have happened before these queries are called. + let data = def_kind.def_path_data(name); let key = self.untracked.definitions.write().create_def(parent, data); let feed = TyCtxtFeed { tcx: self.tcx, key }; diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index cebefbccc11..1c7a7545e2b 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -215,7 +215,7 @@ impl<'tcx> InstanceDef<'tcx> { }; matches!( tcx.def_key(def_id).disambiguated_data.data, - DefPathData::Ctor | DefPathData::ClosureExpr + DefPathData::Ctor | DefPathData::Closure ) } diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 64ae11df5b5..5e09154789a 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -131,7 +131,7 @@ pub trait Printer<'tcx>: Sized { match key.disambiguated_data.data { // Closures' own generics are only captures, don't print them. - DefPathData::ClosureExpr => {} + DefPathData::Closure => {} // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`. // Anon consts doesn't have their own generics, and inline consts' own // generics are their inferred types, so don't print them. diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 68dd8dee87c..25423348638 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1804,13 +1804,13 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> { // (but also some things just print a `DefId` generally so maybe we need this?) fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace { match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::ImplTrait => { + DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => { Namespace::TypeNS } DefPathData::ValueNs(..) | DefPathData::AnonConst - | DefPathData::ClosureExpr + | DefPathData::Closure | DefPathData::Ctor => Namespace::ValueNS, DefPathData::MacroNs(..) => Namespace::MacroNS, diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 8f017833531..a2f829f93e3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -599,9 +599,9 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { // an or-pattern. Panics if `self` is empty. fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> { self.head().flatten_or_pat().into_iter().map(move |pat| { - let mut new_pats = smallvec![pat]; - new_pats.extend_from_slice(&self.pats[1..]); - PatStack { pats: new_pats } + let mut new = self.clone(); + new.pats[0] = pat; + new }) } @@ -732,18 +732,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// Build a new matrix from an iterator of `MatchArm`s. - fn new<'a>( - cx: &MatchCheckCtxt<'p, 'tcx>, - iter: impl Iterator<Item = &'a MatchArm<'p, 'tcx>>, - scrut_ty: Ty<'tcx>, - ) -> Self - where - 'p: 'a, - { + fn new(cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>) -> Self { let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP)); let wildcard_row = PatStack::from_pattern(wild_pattern); - let mut matrix = Matrix { rows: vec![], wildcard_row }; - for (row_id, arm) in iter.enumerate() { + let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), wildcard_row }; + for (row_id, arm) in arms.iter().enumerate() { let v = MatrixRow { pats: PatStack::from_pattern(arm.pat), parent_row: row_id, // dummy, we won't read it @@ -806,7 +799,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { ctor: &Constructor<'tcx>, ) -> Matrix<'p, 'tcx> { let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor); - let mut matrix = Matrix { rows: vec![], wildcard_row }; + let mut matrix = Matrix { rows: Vec::new(), wildcard_row }; for (i, row) in self.rows().enumerate() { if ctor.is_covered_by(pcx, row.head().ctor()) { let new_row = row.pop_head_constructor(pcx, ctor, i); @@ -1386,7 +1379,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> UsefulnessReport<'p, 'tcx> { - let mut matrix = Matrix::new(cx, arms.iter(), scrut_ty); + let mut matrix = Matrix::new(cx, arms, scrut_ty); let non_exhaustiveness_witnesses = compute_exhaustiveness_and_reachability(cx, &mut matrix, true); diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index c9bce80853d..815f2045942 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,60 +1,14 @@ //! Random access inspection of the results of a dataflow analysis. -use crate::{framework::BitSetExt, CloneAnalysis}; +use crate::framework::BitSetExt; -use std::borrow::{Borrow, BorrowMut}; use std::cmp::Ordering; #[cfg(debug_assertions)] use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned}; - -// `AnalysisResults` is needed as an impl such as the following has an unconstrained type -// parameter: -// ``` -// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R> -// where -// A: Analysis<'tcx>, -// E: Borrow<EntrySets<'tcx, A>>, -// R: Results<'tcx, A, E>, -// {} -// ``` - -/// A type representing the analysis results consumed by a `ResultsCursor`. -pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>> -where - A: Analysis<'tcx>, -{ - /// The type containing the entry sets for this `Results` type. - /// - /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`. - type EntrySets: Borrow<EntrySets<'tcx, A>>; -} -impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E> -where - A: Analysis<'tcx>, - E: Borrow<EntrySets<'tcx, A>>, -{ - type EntrySets = E; -} -impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E> -where - A: Analysis<'tcx>, - E: Borrow<EntrySets<'tcx, A>>, -{ - type EntrySets = E; -} - -/// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'res, 'mir, 'tcx, A> = - ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>; - -/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This -/// allows multiple cursors over the same `Results`. -pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> = - ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>; +use super::{Analysis, Direction, Effect, EffectIndex, Results}; /// Allows random access inspection of the results of a dataflow analysis. /// @@ -62,15 +16,12 @@ pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> = /// the same order as the `DIRECTION` of the analysis. In the worst case—when statements are /// visited in *reverse* order—performance will be quadratic in the number of statements in the /// block. The order in which basic blocks are inspected has no impact on performance. -/// -/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The -/// type of ownership is determined by `R` (see `ResultsRefCursor` above). -pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>> +pub struct ResultsCursor<'mir, 'tcx, A> where A: Analysis<'tcx>, { body: &'mir mir::Body<'tcx>, - results: R, + results: Results<'tcx, A>, state: A::Domain, pos: CursorPosition, @@ -84,7 +35,7 @@ where reachable_blocks: BitSet<BasicBlock>, } -impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -99,30 +50,13 @@ where } /// Unwraps this cursor, returning the underlying `Results`. - pub fn into_results(self) -> R { + pub fn into_results(self) -> Results<'tcx, A> { self.results } -} - -impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>> -where - A: Analysis<'tcx> + CloneAnalysis, -{ - /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not* - /// copied. - pub fn new_cursor(&self) -> Self { - Self::new(self.body, self.results.reclone_analysis()) - } -} -impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> -where - A: Analysis<'tcx>, - R: AnalysisResults<'tcx, A>, -{ /// Returns a new cursor that can inspect `results`. - pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { - let bottom_value = results.borrow().analysis.bottom_value(body); + pub fn new(body: &'mir mir::Body<'tcx>, results: Results<'tcx, A>) -> Self { + let bottom_value = results.analysis.bottom_value(body); ResultsCursor { body, results, @@ -147,23 +81,23 @@ where } /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A, R::EntrySets> { - self.results.borrow() + pub fn results(&self) -> &Results<'tcx, A> { + &self.results } /// Returns the underlying `Results`. - pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> { - self.results.borrow_mut() + pub fn mut_results(&mut self) -> &mut Results<'tcx, A> { + &mut self.results } /// Returns the `Analysis` used to generate the underlying `Results`. pub fn analysis(&self) -> &A { - &self.results.borrow().analysis + &self.results.analysis } /// Returns the `Analysis` used to generate the underlying `Results`. pub fn mut_analysis(&mut self) -> &mut A { - &mut self.results.borrow_mut().analysis + &mut self.results.analysis } /// Resets the cursor to hold the entry set for the given basic block. @@ -175,7 +109,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(self.results.entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -264,11 +198,10 @@ where ) }; - let analysis = &mut self.results.borrow_mut().analysis; let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( - analysis, + &mut self.results.analysis, &mut self.state, target.block, block_data, @@ -284,12 +217,12 @@ where /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) { - f(&mut self.results.borrow_mut().analysis, &mut self.state); + f(&mut self.results.analysis, &mut self.state); self.state_needs_reset = true; } } -impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +impl<'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A> where A: crate::GenKillAnalysis<'tcx>, A::Domain: BitSetExt<A::Idx>, diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index ed82b1e8cdc..784872c7ed7 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -5,9 +5,7 @@ use crate::errors::{ }; use crate::framework::BitSetExt; -use std::borrow::Borrow; use std::ffi::OsString; -use std::marker::PhantomData; use std::path::PathBuf; use rustc_ast as ast; @@ -24,42 +22,37 @@ use rustc_span::symbol::{sym, Symbol}; use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis, - GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, - ResultsVisitor, + visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, GenKillSet, + JoinSemiLattice, ResultsCursor, ResultsVisitor, }; pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>; /// A dataflow analysis that has converged to fixpoint. -pub struct Results<'tcx, A, E = EntrySets<'tcx, A>> +#[derive(Clone)] +pub struct Results<'tcx, A> where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: E, - pub(super) _marker: PhantomData<&'tcx ()>, + pub(super) entry_sets: EntrySets<'tcx, A>, } -/// `Results` type with a cloned `Analysis` and borrowed entry sets. -pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>; - -impl<'tcx, A, E> Results<'tcx, A, E> +impl<'tcx, A> Results<'tcx, A> where A: Analysis<'tcx>, - E: Borrow<EntrySets<'tcx, A>>, { /// Creates a `ResultsCursor` that can inspect these `Results`. pub fn into_results_cursor<'mir>( self, body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A, Self> { + ) -> ResultsCursor<'mir, 'tcx, A> { ResultsCursor::new(body, self) } /// Gets the dataflow state for the given block. pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_sets.borrow()[block] + &self.entry_sets[block] } pub fn visit_with<'mir>( @@ -80,52 +73,6 @@ where visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } -impl<'tcx, A> Results<'tcx, A> -where - A: Analysis<'tcx>, -{ - /// Creates a `ResultsCursor` that can inspect these `Results`. - pub fn as_results_cursor<'a, 'mir>( - &'a mut self, - body: &'mir mir::Body<'tcx>, - ) -> ResultsRefCursor<'a, 'mir, 'tcx, A> { - ResultsCursor::new(body, self) - } -} -impl<'tcx, A> Results<'tcx, A> -where - A: Analysis<'tcx> + CloneAnalysis, -{ - /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. - pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> { - Results { - analysis: self.analysis.clone_analysis(), - entry_sets: &self.entry_sets, - _marker: PhantomData, - } - } - - /// Creates a `ResultsCursor` that can inspect these `Results`. - pub fn cloned_results_cursor<'mir>( - &self, - body: &'mir mir::Body<'tcx>, - ) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> { - self.clone_analysis().into_results_cursor(body) - } -} -impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>> -where - A: Analysis<'tcx> + CloneAnalysis, -{ - /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. - pub fn reclone_analysis(&self) -> Self { - Results { - analysis: self.analysis.clone_analysis(), - entry_sets: self.entry_sets, - _marker: PhantomData, - } - } -} /// A solver for dataflow problems. pub struct Engine<'mir, 'tcx, A> @@ -291,29 +238,31 @@ where ); } - let mut results = Results { analysis, entry_sets, _marker: PhantomData }; + let results = Results { analysis, entry_sets }; if tcx.sess.opts.unstable_opts.dump_mir_dataflow { - let res = write_graphviz_results(tcx, body, &mut results, pass_name); + let (res, results) = write_graphviz_results(tcx, body, results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } + results + } else { + results } - - results } } // Graphviz /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via -/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. +/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are +/// the same. fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, - results: &mut Results<'tcx, A>, + results: Results<'tcx, A>, pass_name: Option<&'static str>, -) -> std::io::Result<()> +) -> (std::io::Result<()>, Results<'tcx, A>) where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -324,23 +273,30 @@ where let def_id = body.source.def_id(); let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); + return (Ok(()), results); }; - let mut file = match attrs.output_path(A::NAME) { - Some(path) => { - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; + let file = try { + match attrs.output_path(A::NAME) { + Some(path) => { + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + let f = fs::File::create(&path)?; + io::BufWriter::new(f) } - io::BufWriter::new(fs::File::create(&path)?) - } - None if dump_enabled(tcx, A::NAME, def_id) => { - create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? - } + None if dump_enabled(tcx, A::NAME, def_id) => { + create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)? + } - _ => return Ok(()), + _ => return (Ok(()), results), + } + }; + let mut file = match file { + Ok(f) => f, + Err(e) => return (Err(e), results), }; let style = match attrs.formatter { @@ -356,11 +312,14 @@ where if tcx.sess.opts.unstable_opts.graphviz_dark_mode { render_opts.push(dot::RenderOption::DarkTheme); } - with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)?); + let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts)); - file.write_all(&buf)?; + let lhs = try { + r?; + file.write_all(&buf)?; + }; - Ok(()) + (lhs, graphviz.into_results()) } #[derive(Default)] diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 832d1cba9a7..fa16cac3168 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::graphviz_safe_def_name; use rustc_middle::mir::{self, BasicBlock, Body, Location}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; -use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsRefCursor, ResultsVisitor}; +use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum OutputStyle { @@ -29,27 +29,31 @@ impl OutputStyle { } } -pub(crate) struct Formatter<'res, 'mir, 'tcx, A> +pub(crate) struct Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { body: &'mir Body<'tcx>, - results: RefCell<&'res mut Results<'tcx, A>>, + results: RefCell<Option<Results<'tcx, A>>>, style: OutputStyle, reachable: BitSet<BasicBlock>, } -impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A> +impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { pub(crate) fn new( body: &'mir Body<'tcx>, - results: &'res mut Results<'tcx, A>, + results: Results<'tcx, A>, style: OutputStyle, ) -> Self { let reachable = mir::traversal::reachable_as_bitset(body); - Formatter { body, results: results.into(), style, reachable } + Formatter { body, results: Some(results).into(), style, reachable } + } + + pub(crate) fn into_results(self) -> Results<'tcx, A> { + self.results.into_inner().unwrap() } } @@ -69,7 +73,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> { .collect() } -impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A> +impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, @@ -88,14 +92,19 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); - let mut results = self.results.borrow_mut(); - let mut fmt = BlockFormatter { - results: results.as_results_cursor(self.body), - style: self.style, - bg: Background::Light, - }; + self.results.replace_with(|results| { + // `Formatter::result` is a `RefCell<Option<_>>` so we can replace + // the value with `None`, move it into the results cursor, move it + // back out, and return it to the refcell wrapped in `Some`. + let mut fmt = BlockFormatter { + results: results.take().unwrap().into_results_cursor(self.body), + style: self.style, + bg: Background::Light, + }; - fmt.write_node_label(&mut label, *block).unwrap(); + fmt.write_node_label(&mut label, *block).unwrap(); + Some(fmt.results.into_results()) + }); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -109,7 +118,7 @@ where } } -impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A> +impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -143,16 +152,16 @@ where } } -struct BlockFormatter<'res, 'mir, 'tcx, A> +struct BlockFormatter<'mir, 'tcx, A> where A: Analysis<'tcx>, { - results: ResultsRefCursor<'res, 'mir, 'tcx, A>, + results: ResultsCursor<'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A> +impl<'mir, 'tcx, A> BlockFormatter<'mir, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext<A>, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index a5ae1edf221..b7dfbe0710d 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -45,9 +45,9 @@ pub mod graphviz; pub mod lattice; mod visitor; -pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; +pub use self::cursor::ResultsCursor; pub use self::direction::{Backward, Direction, Forward}; -pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; +pub use self::engine::{Engine, Results}; pub use self::lattice::{JoinSemiLattice, MaybeReachable}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; @@ -246,21 +246,6 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { } } -/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or -/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new -/// `ResultsCursor` or `ResultsVisitor` -pub trait CloneAnalysis { - fn clone_analysis(&self) -> Self; -} -impl<'tcx, A> CloneAnalysis for A -where - A: Analysis<'tcx> + Copy, -{ - fn clone_analysis(&self) -> Self { - *self - } -} - /// A gen/kill dataflow problem. /// /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 9cce5b26cd3..1da5057ff40 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -267,8 +267,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData } - .into_results_cursor(body); + Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); cursor.allow_unreachable(); diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 3cfa7cc1c02..e3648bb4076 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -1,8 +1,6 @@ -use std::borrow::Borrow; - use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, EntrySets, Results}; +use super::{Analysis, Direction, Results}; /// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the /// dataflow state at that location. @@ -143,10 +141,9 @@ pub trait ResultsVisitable<'tcx> { ); } -impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E> +impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> where A: Analysis<'tcx>, - E: Borrow<EntrySets<'tcx, A>>, { type FlowState = A::Domain; diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 5a58e3af8be..26fc903973f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::*; use std::borrow::Cow; use super::MaybeBorrowedLocals; -use crate::{GenKill, ResultsClonedCursor}; +use crate::{GenKill, ResultsCursor}; #[derive(Clone)] pub struct MaybeStorageLive<'a> { @@ -18,12 +18,6 @@ impl<'a> MaybeStorageLive<'a> { } } -impl crate::CloneAnalysis for MaybeStorageLive<'_> { - fn clone_analysis(&self) -> Self { - self.clone() - } -} - impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { type Domain = BitSet<Local>; @@ -158,28 +152,21 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } } -type BorrowedLocalsResults<'res, 'mir, 'tcx> = - ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>; +type BorrowedLocalsResults<'mir, 'tcx> = ResultsCursor<'mir, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a /// given location; i.e. whether its storage can go away without being observed. -pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> { - borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>, +pub struct MaybeRequiresStorage<'mir, 'tcx> { + borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>, } -impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> { - pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self { +impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { + pub fn new(borrowed_locals: BorrowedLocalsResults<'mir, 'tcx>) -> Self { MaybeRequiresStorage { borrowed_locals } } } -impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> { - fn clone_analysis(&self) -> Self { - Self { borrowed_locals: self.borrowed_locals.new_cursor() } - } -} - -impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { +impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, 'tcx> { type Domain = BitSet<Local>; const NAME: &'static str = "requires_storage"; @@ -198,7 +185,7 @@ impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { } } -impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { +impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { type Idx = Local; fn domain_size(&self, body: &Body<'tcx>) -> usize { @@ -355,7 +342,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { } } -impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> { +impl<'tcx> MaybeRequiresStorage<'_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) { let body = self.borrowed_locals.body(); @@ -364,12 +351,12 @@ impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> { } } -struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> { - borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>, +struct MoveVisitor<'a, 'mir, 'tcx, T> { + borrowed_locals: &'a mut BorrowedLocalsResults<'mir, 'tcx>, trans: &'a mut T, } -impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T> +impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, 'tcx, T> where T: GenKill<Local>, { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index d218f033d62..f0b21fd4184 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -4,6 +4,7 @@ #![feature(let_chains)] #![feature(min_specialization)] #![feature(stmt_expr_attributes)] +#![feature(try_blocks)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -23,7 +24,7 @@ pub use self::framework::{ fmt, lattice, visit_results, Analysis, AnalysisDomain, Direction, GenKill, GenKillAnalysis, JoinSemiLattice, MaybeReachable, Results, ResultsCursor, ResultsVisitable, ResultsVisitor, }; -use self::framework::{Backward, CloneAnalysis, ResultsClonedCursor, SwitchIntEdgeEffects}; +use self::framework::{Backward, SwitchIntEdgeEffects}; use self::move_paths::MoveData; pub mod debuginfo; diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 1373596fd2b..79a1509531d 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -679,15 +679,15 @@ fn locals_live_across_suspend_points<'tcx>( let borrowed_locals_results = MaybeBorrowedLocals.into_engine(tcx, body).pass_name("coroutine").iterate_to_fixpoint(); - let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body); + let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body); // Calculate the MIR locals that we actually need to keep storage around // for. - let mut requires_storage_results = - MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body)) + let mut requires_storage_cursor = + MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body)) .into_engine(tcx, body) - .iterate_to_fixpoint(); - let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body); + .iterate_to_fixpoint() + .into_results_cursor(body); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = MaybeLiveLocals @@ -763,7 +763,7 @@ fn locals_live_across_suspend_points<'tcx>( body, &saved_locals, always_live_locals.clone(), - requires_storage_results, + requires_storage_cursor.into_results(), ); LivenessInfo { @@ -828,7 +828,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &CoroutineSavedLocals, always_live_locals: BitSet<Local>, - mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>, + mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, ) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 224f3f36a3f..647c92785e1 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -4,9 +4,8 @@ use rustc_ast::*; use rustc_expand::expand::AstFragment; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::definitions::*; use rustc_span::hygiene::LocalExpnId; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; pub(crate) fn collect_definitions( @@ -30,16 +29,19 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { fn create_def( &mut self, node_id: NodeId, - data: DefPathData, + name: Symbol, def_kind: DefKind, span: Span, ) -> LocalDefId { let parent_def = self.parent_def; - debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def); + debug!( + "create_def(node_id={:?}, def_kind={:?}, parent_def={:?})", + node_id, def_kind, parent_def + ); self.resolver.create_def( parent_def, node_id, - data, + name, def_kind, self.expansion.to_expn_id(), span.with_parent(None), @@ -76,8 +78,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { self.visit_macro_invoc(field.id); } else { let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); - let def = - self.create_def(field.id, DefPathData::ValueNs(name), DefKind::Field, field.span); + let def = self.create_def(field.id, name, DefKind::Field, field.span); self.with_parent(def, |this| visit::walk_field_def(this, field)); } } @@ -97,40 +98,36 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { // Pick the def data. This need not be unique, but the more // information we encapsulate into, the better let mut opt_macro_data = None; - let ty_data = DefPathData::TypeNs(i.ident.name); - let value_data = DefPathData::ValueNs(i.ident.name); - let (def_data, def_kind) = match &i.kind { - ItemKind::Impl(i) => { - (DefPathData::Impl, DefKind::Impl { of_trait: i.of_trait.is_some() }) - } - ItemKind::ForeignMod(..) => (DefPathData::ForeignMod, DefKind::ForeignMod), - ItemKind::Mod(..) => (ty_data, DefKind::Mod), - ItemKind::Trait(..) => (ty_data, DefKind::Trait), - ItemKind::TraitAlias(..) => (ty_data, DefKind::TraitAlias), - ItemKind::Enum(..) => (ty_data, DefKind::Enum), - ItemKind::Struct(..) => (ty_data, DefKind::Struct), - ItemKind::Union(..) => (ty_data, DefKind::Union), - ItemKind::ExternCrate(..) => (ty_data, DefKind::ExternCrate), - ItemKind::TyAlias(..) => (ty_data, DefKind::TyAlias), - ItemKind::Static(s) => (value_data, DefKind::Static(s.mutability)), - ItemKind::Const(..) => (value_data, DefKind::Const), - ItemKind::Fn(..) => (value_data, DefKind::Fn), + let def_kind = match &i.kind { + ItemKind::Impl(i) => DefKind::Impl { of_trait: i.of_trait.is_some() }, + ItemKind::ForeignMod(..) => DefKind::ForeignMod, + ItemKind::Mod(..) => DefKind::Mod, + ItemKind::Trait(..) => DefKind::Trait, + ItemKind::TraitAlias(..) => DefKind::TraitAlias, + ItemKind::Enum(..) => DefKind::Enum, + ItemKind::Struct(..) => DefKind::Struct, + ItemKind::Union(..) => DefKind::Union, + ItemKind::ExternCrate(..) => DefKind::ExternCrate, + ItemKind::TyAlias(..) => DefKind::TyAlias, + ItemKind::Static(s) => DefKind::Static(s.mutability), + ItemKind::Const(..) => DefKind::Const, + ItemKind::Fn(..) => DefKind::Fn, ItemKind::MacroDef(..) => { let macro_data = self.resolver.compile_macro(i, self.resolver.tcx.sess.edition()); let macro_kind = macro_data.ext.macro_kind(); opt_macro_data = Some(macro_data); - (DefPathData::MacroNs(i.ident.name), DefKind::Macro(macro_kind)) + DefKind::Macro(macro_kind) } ItemKind::MacCall(..) => { visit::walk_item(self, i); return self.visit_macro_invoc(i.id); } - ItemKind::GlobalAsm(..) => (DefPathData::GlobalAsm, DefKind::GlobalAsm), + ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(..) => { return visit::walk_item(self, i); } }; - let def_id = self.create_def(i.id, def_data, def_kind, i.span); + let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span); if let Some(macro_data) = opt_macro_data { self.resolver.macro_map.insert(def_id.to_def_id(), macro_data); @@ -144,7 +141,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) { this.create_def( ctor_node_id, - DefPathData::Ctor, + kw::Empty, DefKind::Ctor(CtorOf::Struct, ctor_kind), i.span, ); @@ -174,12 +171,8 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { // then the closure_def will never be used, and we should avoid generating a // def-id for it. if let Some(body) = body { - let closure_def = self.create_def( - closure_id, - DefPathData::ClosureExpr, - DefKind::Closure, - span, - ); + let closure_def = + self.create_def(closure_id, kw::Empty, DefKind::Closure, span); self.with_parent(closure_def, |this| this.visit_block(body)); } return; @@ -190,21 +183,19 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { - self.create_def(id, DefPathData::Use, DefKind::Use, use_tree.span); + self.create_def(id, kw::Empty, DefKind::Use, use_tree.span); visit::walk_use_tree(self, use_tree, id); } fn visit_foreign_item(&mut self, fi: &'a ForeignItem) { - let (def_data, def_kind) = match fi.kind { - ForeignItemKind::Static(_, mt, _) => { - (DefPathData::ValueNs(fi.ident.name), DefKind::Static(mt)) - } - ForeignItemKind::Fn(_) => (DefPathData::ValueNs(fi.ident.name), DefKind::Fn), - ForeignItemKind::TyAlias(_) => (DefPathData::TypeNs(fi.ident.name), DefKind::ForeignTy), + let def_kind = match fi.kind { + ForeignItemKind::Static(_, mt, _) => DefKind::Static(mt), + ForeignItemKind::Fn(_) => DefKind::Fn, + ForeignItemKind::TyAlias(_) => DefKind::ForeignTy, ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id), }; - let def = self.create_def(fi.id, def_data, def_kind, fi.span); + let def = self.create_def(fi.id, fi.ident.name, def_kind, fi.span); self.with_parent(def, |this| visit::walk_foreign_item(this, fi)); } @@ -213,13 +204,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { if v.is_placeholder { return self.visit_macro_invoc(v.id); } - let def = - self.create_def(v.id, DefPathData::TypeNs(v.ident.name), DefKind::Variant, v.span); + let def = self.create_def(v.id, v.ident.name, DefKind::Variant, v.span); self.with_parent(def, |this| { if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&v.data) { this.create_def( ctor_node_id, - DefPathData::Ctor, + kw::Empty, DefKind::Ctor(CtorOf::Variant, ctor_kind), v.span, ); @@ -242,15 +232,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { self.visit_macro_invoc(param.id); return; } - let name = param.ident.name; - let (def_path_data, def_kind) = match param.kind { - GenericParamKind::Lifetime { .. } => { - (DefPathData::LifetimeNs(name), DefKind::LifetimeParam) - } - GenericParamKind::Type { .. } => (DefPathData::TypeNs(name), DefKind::TyParam), - GenericParamKind::Const { .. } => (DefPathData::ValueNs(name), DefKind::ConstParam), + let def_kind = match param.kind { + GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam, + GenericParamKind::Type { .. } => DefKind::TyParam, + GenericParamKind::Const { .. } => DefKind::ConstParam, }; - self.create_def(param.id, def_path_data, def_kind, param.ident.span); + self.create_def(param.id, param.ident.name, def_kind, param.ident.span); // impl-Trait can happen inside generic parameters, like // ``` @@ -264,14 +251,14 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) { - let (def_data, def_kind) = match &i.kind { - AssocItemKind::Fn(..) => (DefPathData::ValueNs(i.ident.name), DefKind::AssocFn), - AssocItemKind::Const(..) => (DefPathData::ValueNs(i.ident.name), DefKind::AssocConst), - AssocItemKind::Type(..) => (DefPathData::TypeNs(i.ident.name), DefKind::AssocTy), + let def_kind = match &i.kind { + AssocItemKind::Fn(..) => DefKind::AssocFn, + AssocItemKind::Const(..) => DefKind::AssocConst, + AssocItemKind::Type(..) => DefKind::AssocTy, AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id), }; - let def = self.create_def(i.id, def_data, def_kind, i.span); + let def = self.create_def(i.id, i.ident.name, def_kind, i.span); self.with_parent(def, |this| visit::walk_assoc_item(this, i, ctxt)); } @@ -283,12 +270,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { - let def = self.create_def( - constant.id, - DefPathData::AnonConst, - DefKind::AnonConst, - constant.value.span, - ); + let def = self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span); self.with_parent(def, |this| visit::walk_anon_const(this, constant)); } @@ -298,25 +280,21 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { ExprKind::Closure(ref closure) => { // Async closures desugar to closures inside of closures, so // we must create two defs. - let closure_def = - self.create_def(expr.id, DefPathData::ClosureExpr, DefKind::Closure, expr.span); + let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span); match closure.asyncness { - Async::Yes { closure_id, .. } => self.create_def( - closure_id, - DefPathData::ClosureExpr, - DefKind::Closure, - expr.span, - ), + Async::Yes { closure_id, .. } => { + self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span) + } Async::No => closure_def, } } ExprKind::Gen(_, _, _) => { - self.create_def(expr.id, DefPathData::ClosureExpr, DefKind::Closure, expr.span) + self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span) } ExprKind::ConstBlock(ref constant) => { let def = self.create_def( constant.id, - DefPathData::AnonConst, + kw::Empty, DefKind::InlineConst, constant.value.span, ); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 208391cc019..70e0eb12c01 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -45,7 +45,6 @@ use rustc_hir::def::NonMacroAttrKind; use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LocalDefIdSet}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::definitions::DefPathData; use rustc_hir::{PrimTy, TraitCandidate}; use rustc_index::IndexVec; use rustc_metadata::creader::{CStore, CrateLoader}; @@ -1212,11 +1211,12 @@ impl<'tcx> Resolver<'_, 'tcx> { &mut self, parent: LocalDefId, node_id: ast::NodeId, - data: DefPathData, + name: Symbol, def_kind: DefKind, expn_id: ExpnId, span: Span, ) -> LocalDefId { + let data = def_kind.def_path_data(name); assert!( !self.node_id_to_def_id.contains_key(&node_id), "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 1fd8542b2a6..18ca347de97 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -379,14 +379,13 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> hir::definitions::DefPathData::TypeNs(..) => "t", hir::definitions::DefPathData::ValueNs(..) => "v", - hir::definitions::DefPathData::ClosureExpr => "C", + hir::definitions::DefPathData::Closure => "C", hir::definitions::DefPathData::Ctor => "c", hir::definitions::DefPathData::AnonConst => "k", - hir::definitions::DefPathData::ImplTrait => "i", + hir::definitions::DefPathData::OpaqueTy => "i", hir::definitions::DefPathData::CrateRoot | hir::definitions::DefPathData::Use | hir::definitions::DefPathData::GlobalAsm - | hir::definitions::DefPathData::ImplTraitAssocTy | hir::definitions::DefPathData::MacroNs(..) | hir::definitions::DefPathData::LifetimeNs(..) => { bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 88989806997..e002e345ae6 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -770,17 +770,16 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // Uppercase categories are more stable than lowercase ones. DefPathData::TypeNs(_) => 't', DefPathData::ValueNs(_) => 'v', - DefPathData::ClosureExpr => 'C', + DefPathData::Closure => 'C', DefPathData::Ctor => 'c', DefPathData::AnonConst => 'k', - DefPathData::ImplTrait => 'i', + DefPathData::OpaqueTy => 'i', // These should never show up as `path_append` arguments. DefPathData::CrateRoot | DefPathData::Use | DefPathData::GlobalAsm | DefPathData::Impl - | DefPathData::ImplTraitAssocTy | DefPathData::MacroNs(_) | DefPathData::LifetimeNs(_) => { bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data) diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index d753aa8618e..41db8059cbe 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -55,3 +55,6 @@ trait_selection_trait_has_no_impls = this trait has no implementations, consider trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} + +trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}` + .help = expect either a generic argument name or {"`{Self}`"} as format argument diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index ba019c4ff6f..fbe6e2bd5b8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -321,7 +321,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } #[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString(Symbol, Span); +pub struct OnUnimplementedFormatString { + symbol: Symbol, + span: Span, + is_diagnostic_namespace_variant: bool, +} #[derive(Debug)] pub struct OnUnimplementedDirective { @@ -401,6 +405,14 @@ impl IgnoredDiagnosticOption { } } +#[derive(LintDiagnostic)] +#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] +#[help] +pub struct UnknownFormatParameterForOnUnimplementedAttr { + argument_name: Symbol, + trait_name: Symbol, +} + impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -414,8 +426,14 @@ impl<'tcx> OnUnimplementedDirective { let mut item_iter = items.iter(); let parse_value = |value_str, value_span| { - OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span) - .map(Some) + OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value_str, + value_span, + is_diagnostic_namespace_variant, + ) + .map(Some) }; let condition = if is_root { @@ -552,15 +570,15 @@ impl<'tcx> OnUnimplementedDirective { IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.message.as_ref().map(|f| f.1), - aggr.message.as_ref().map(|f| f.1), + directive.message.as_ref().map(|f| f.span), + aggr.message.as_ref().map(|f| f.span), "message", ); IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.label.as_ref().map(|f| f.1), - aggr.label.as_ref().map(|f| f.1), + directive.label.as_ref().map(|f| f.span), + aggr.label.as_ref().map(|f| f.span), "label", ); IgnoredDiagnosticOption::maybe_emit_warning( @@ -573,8 +591,8 @@ impl<'tcx> OnUnimplementedDirective { IgnoredDiagnosticOption::maybe_emit_warning( tcx, item_def_id, - directive.parent_label.as_ref().map(|f| f.1), - aggr.parent_label.as_ref().map(|f| f.1), + directive.parent_label.as_ref().map(|f| f.span), + aggr.parent_label.as_ref().map(|f| f.span), "parent_label", ); IgnoredDiagnosticOption::maybe_emit_warning( @@ -634,7 +652,7 @@ impl<'tcx> OnUnimplementedDirective { item_def_id, value, attr.span, - attr.span, + is_diagnostic_namespace_variant, )?), notes: Vec::new(), parent_label: None, @@ -713,7 +731,12 @@ impl<'tcx> OnUnimplementedDirective { // `with_no_visible_paths` is also used when generating the options, // so we need to match it here. ty::print::with_no_visible_paths!( - OnUnimplementedFormatString(v, cfg.span).format( + OnUnimplementedFormatString { + symbol: v, + span: cfg.span, + is_diagnostic_namespace_variant: false + } + .format( tcx, trait_ref, &options_map @@ -761,20 +784,19 @@ impl<'tcx> OnUnimplementedFormatString { tcx: TyCtxt<'tcx>, item_def_id: DefId, from: Symbol, - err_sp: Span, value_span: Span, + is_diagnostic_namespace_variant: bool, ) -> Result<Self, ErrorGuaranteed> { - let result = OnUnimplementedFormatString(from, value_span); - result.verify(tcx, item_def_id, err_sp)?; + let result = OnUnimplementedFormatString { + symbol: from, + span: value_span, + is_diagnostic_namespace_variant, + }; + result.verify(tcx, item_def_id)?; Ok(result) } - fn verify( - &self, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - span: Span, - ) -> Result<(), ErrorGuaranteed> { + fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> { let trait_def_id = if tcx.is_trait(item_def_id) { item_def_id } else { @@ -783,7 +805,7 @@ impl<'tcx> OnUnimplementedFormatString { }; let trait_name = tcx.item_name(trait_def_id); let generics = tcx.generics_of(item_def_id); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in parser { @@ -793,24 +815,40 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name => (), - s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (), + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } // So is `{A}` if A is a type parameter s if generics.params.iter().any(|param| param.name == s) => (), s => { - result = Err(struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{trait_name}`") - } else { - "impl".to_string() - } - ) - .emit()); + if self.is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_err!( + tcx.sess, + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } } } } @@ -818,7 +856,7 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { let reported = struct_span_err!( tcx.sess, - span, + self.span, E0231, "only named substitution parameters are allowed" ) @@ -857,37 +895,42 @@ impl<'tcx> OnUnimplementedFormatString { .collect::<FxHashMap<Symbol, String>>(); let empty_string = String::new(); - let s = self.0.as_str(); + let s = self.symbol.as_str(); let parser = Parser::new(s, None, None, false, ParseMode::Format); let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); parser .map(|p| match p { - Piece::String(s) => s, + Piece::String(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - let s = Symbol::intern(s); + Position::ArgumentNamed(arg) => { + let s = Symbol::intern(arg); match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, + Some(val) => val.to_string(), + None if self.is_diagnostic_namespace_variant => { + format!("{{{arg}}}") + } + None if s == name => trait_str.clone(), None => { if let Some(val) = options.get(&s) { - val + val.clone() } else if s == sym::from_desugaring { // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - item_context + String::new() + } else if s == sym::ItemContext + && !self.is_diagnostic_namespace_variant + { + item_context.clone() } else if s == sym::integral { - "{integral}" + String::from("{integral}") } else if s == sym::integer_ { - "{integer}" + String::from("{integer}") } else if s == sym::float { - "{float}" + String::from("{float}") } else { bug!( "broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", - self.0, + self.symbol, trait_ref, s ) @@ -895,7 +938,7 @@ impl<'tcx> OnUnimplementedFormatString { } } } - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), }, }) .collect() diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index c6ff7e2a9ef..b28e3d5c412 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -424,11 +424,23 @@ fn fn_abi_sanity_check<'tcx>( } PassMode::Indirect { meta_attrs: None, .. } => { // No metadata, must be sized. + // Conceptually, unsized arguments must be copied around, which requires dynamically + // determining their size, which we cannot do without metadata. Consult + // t-opsem before removing this check. assert!(arg.layout.is_sized()); } PassMode::Indirect { meta_attrs: Some(_), on_stack, .. } => { // With metadata. Must be unsized and not on the stack. assert!(arg.layout.is_unsized() && !on_stack); + // Also, must not be `extern` type. + let tail = cx.tcx.struct_tail_with_normalize(arg.layout.ty, |ty| ty, || {}); + if matches!(tail.kind(), ty::Foreign(..)) { + // These types do not have metadata, so having `meta_attrs` is bogus. + // Conceptually, unsized arguments must be copied around, which requires dynamically + // determining their size. Therefore, we cannot allow `extern` types here. Consult + // t-opsem before removing this check. + panic!("unsized arguments must not be `extern` types"); + } } } } diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 82cd0cc50d2..7a81570e55b 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -2,7 +2,6 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; -use rustc_hir::definitions::DefPathData; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, GenericArgs, ImplTraitInTraitData, Ty, TyCtxt}; @@ -254,8 +253,7 @@ fn associated_type_for_impl_trait_in_trait( assert_eq!(tcx.def_kind(trait_def_id), DefKind::Trait); let span = tcx.def_span(opaque_ty_def_id); - let trait_assoc_ty = - tcx.at(span).create_def(trait_def_id, DefPathData::ImplTraitAssocTy, DefKind::AssocTy); + let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy); let local_def_id = trait_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); @@ -356,8 +354,7 @@ fn associated_type_for_impl_trait_in_impl( hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id), hir::FnRetTy::Return(ty) => ty.span, }; - let impl_assoc_ty = - tcx.at(span).create_def(impl_local_def_id, DefPathData::ImplTraitAssocTy, DefKind::AssocTy); + let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, kw::Empty, DefKind::AssocTy); let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 27afd3b8017..373b4aee47a 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -233,7 +233,10 @@ unsafe impl<T> SliceIndex<[T]> for usize { // cannot be longer than `isize::MAX`. They also guarantee that // `self` is in bounds of `slice` so `self` cannot overflow an `isize`, // so the call to `add` is safe. - unsafe { slice.as_ptr().add(self) } + unsafe { + crate::intrinsics::assume(self < slice.len()); + slice.as_ptr().add(self) + } } #[inline] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 5957f9fd443..dec9f194863 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1045,11 +1045,11 @@ impl<T> [T] { /// # Examples /// /// ``` - /// let slice = ['r', 'u', 's', 't']; - /// let mut iter = slice.windows(2); - /// assert_eq!(iter.next().unwrap(), &['r', 'u']); - /// assert_eq!(iter.next().unwrap(), &['u', 's']); - /// assert_eq!(iter.next().unwrap(), &['s', 't']); + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.windows(3); + /// assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']); + /// assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']); /// assert!(iter.next().is_none()); /// ``` /// diff --git a/library/portable-simd/crates/core_simd/tests/pointers.rs b/library/portable-simd/crates/core_simd/tests/pointers.rs index a90ff928ced..b9f32d16e01 100644 --- a/library/portable-simd/crates/core_simd/tests/pointers.rs +++ b/library/portable-simd/crates/core_simd/tests/pointers.rs @@ -1,4 +1,4 @@ -#![feature(portable_simd, strict_provenance)] +#![feature(portable_simd, strict_provenance, exposed_provenance)] use core_simd::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs index 4a19d0a44c3..ea4b573aeb9 100644 --- a/src/librustdoc/html/escape.rs +++ b/src/librustdoc/html/escape.rs @@ -38,3 +38,39 @@ impl<'a> fmt::Display for Escape<'a> { Ok(()) } } + +/// Wrapper struct which will emit the HTML-escaped version of the contained +/// string when passed to a format string. +/// +/// This is only safe to use for text nodes. If you need your output to be +/// safely contained in an attribute, use [`Escape`]. If you don't know the +/// difference, use [`Escape`]. +pub(crate) struct EscapeBodyText<'a>(pub &'a str); + +impl<'a> fmt::Display for EscapeBodyText<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + // Because the internet is always right, turns out there's not that many + // characters to escape: http://stackoverflow.com/questions/7381974 + let EscapeBodyText(s) = *self; + let pile_o_bits = s; + let mut last = 0; + for (i, ch) in s.char_indices() { + let s = match ch { + '>' => ">", + '<' => "<", + '&' => "&", + _ => continue, + }; + fmt.write_str(&pile_o_bits[last..i])?; + fmt.write_str(s)?; + // NOTE: we only expect single byte characters here - which is fine as long as we + // only match single byte characters + last = i + 1; + } + + if last < s.len() { + fmt.write_str(&pile_o_bits[last..])?; + } + Ok(()) + } +} diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index b762c8a1ce6..1cdc792a819 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -6,7 +6,7 @@ //! Use the `render_with_highlighting` to highlight some rust code. use crate::clean::PrimitiveType; -use crate::html::escape::Escape; +use crate::html::escape::EscapeBodyText; use crate::html::render::{Context, LinkFromSrc}; use std::collections::VecDeque; @@ -189,7 +189,7 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> { && can_merge(current_class, Some(*parent_class), "") { for (text, class) in self.pending_elems.iter() { - string(self.out, Escape(text), *class, &self.href_context, false); + string(self.out, EscapeBodyText(text), *class, &self.href_context, false); } } else { // We only want to "open" the tag ourselves if we have more than one pending and if the @@ -202,7 +202,13 @@ impl<'a, 'tcx, F: Write> TokenHandler<'a, 'tcx, F> { None }; for (text, class) in self.pending_elems.iter() { - string(self.out, Escape(text), *class, &self.href_context, close_tag.is_none()); + string( + self.out, + EscapeBodyText(text), + *class, + &self.href_context, + close_tag.is_none(), + ); } if let Some(close_tag) = close_tag { exit_span(self.out, close_tag); diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html index 30b50ca7c66..b98e6712590 100644 --- a/src/librustdoc/html/highlight/fixtures/dos_line.html +++ b/src/librustdoc/html/highlight/fixtures/dos_line.html @@ -1,3 +1,3 @@ <span class="kw">pub fn </span>foo() { -<span class="macro">println!</span>(<span class="string">"foo"</span>); +<span class="macro">println!</span>(<span class="string">"foo"</span>); } diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index fced2eacd9e..aa735e81597 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -8,12 +8,12 @@ .lifetime { color: #B76514; } .question-mark { color: #ff9011; } </style> -<pre><code><span class="attr">#![crate_type = <span class="string">"lib"</span>] +<pre><code><span class="attr">#![crate_type = <span class="string">"lib"</span>] </span><span class="kw">use </span>std::path::{Path, PathBuf}; -<span class="attr">#[cfg(target_os = <span class="string">"linux"</span>)] -#[cfg(target_os = <span class="string">"windows"</span>)] +<span class="attr">#[cfg(target_os = <span class="string">"linux"</span>)] +#[cfg(target_os = <span class="string">"windows"</span>)] </span><span class="kw">fn </span>main() -> () { <span class="kw">let </span>foo = <span class="bool-val">true </span>&& <span class="bool-val">false </span>|| <span class="bool-val">true</span>; <span class="kw">let _</span>: <span class="kw-2">*const </span>() = <span class="number">0</span>; @@ -22,7 +22,7 @@ <span class="kw">let _ </span>= <span class="kw-2">*</span>foo; <span class="macro">mac!</span>(foo, <span class="kw-2">&mut </span>bar); <span class="macro">assert!</span>(<span class="self">self</span>.length < N && index <= <span class="self">self</span>.length); - ::std::env::var(<span class="string">"gateau"</span>).is_ok(); + ::std::env::var(<span class="string">"gateau"</span>).is_ok(); <span class="attr">#[rustfmt::skip] </span><span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new(); <span class="kw">let </span><span class="kw-2">mut </span>s = String::new(); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 131b1d608e6..ff7ce01e807 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1737,7 +1737,14 @@ fn item_variants( w.write_str("</h3></section>"); let heading_and_fields = match &variant_data.kind { - clean::VariantKind::Struct(s) => Some(("Fields", &s.fields)), + clean::VariantKind::Struct(s) => { + // If there is no field to display, no need to add the heading. + if s.fields.iter().any(|f| !f.is_doc_hidden()) { + Some(("Fields", &s.fields)) + } else { + None + } + } clean::VariantKind::Tuple(fields) => { // Documentation on tuple variant fields is rare, so to reduce noise we only emit // the section if at least one field is documented. diff --git a/src/tools/cargotest/main.rs b/src/tools/cargotest/main.rs index 7044cb89286..a5d2ffb1572 100644 --- a/src/tools/cargotest/main.rs +++ b/src/tools/cargotest/main.rs @@ -140,7 +140,7 @@ fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf { let status = Command::new("git") .arg("fetch") .arg(test.repo) - .arg("master") + .arg(test.sha) .arg(&format!("--depth={}", depth)) .current_dir(&out_dir) .status() diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f3492c3eb04..c60249f35e1 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c9808f87028e16d134438787cab3d4cc16d05fe2 +317d14a56cb8c748bf0e2f2afff89c2249ab4423 diff --git a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.rs b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.rs index fd51fa6468a..be4dac9957d 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.rs @@ -1,11 +1,10 @@ //@compile-flags: -Zmiri-strict-provenance -//@error-in-other-file: /retag .* tag does not exist in the borrow stack/ fn main() { unsafe { let a = [1, 2, 3]; let s = &a[0..0]; assert_eq!(s.len(), 0); - assert_eq!(*s.get_unchecked(1), 2); + assert_eq!(*s.as_ptr().add(1), 2); //~ ERROR: /retag .* tag does not exist in the borrow stack/ } } diff --git a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr index 5568051905c..acae479ced2 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/zst_slice.stderr @@ -1,26 +1,22 @@ error: Undefined Behavior: trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location - --> RUSTLIB/core/src/slice/mod.rs:LL:CC + --> $DIR/zst_slice.rs:LL:CC | -LL | unsafe { &*index.get_unchecked(self) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location - | this error occurs as part of retag at ALLOC[0x4..0x8] +LL | assert_eq!(*s.as_ptr().add(1), 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | trying to retag from <TAG> for SharedReadOnly permission at ALLOC[0x4], but that tag does not exist in the borrow stack for this location + | this error occurs as part of retag at ALLOC[0x4..0x8] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information help: <TAG> would have been created here, but this is a zero-size retag ([0x0..0x0]) so the tag in question does not exist anywhere --> $DIR/zst_slice.rs:LL:CC | -LL | assert_eq!(*s.get_unchecked(1), 2); - | ^^^^^^^^^^^^^^^^^^ +LL | assert_eq!(*s.as_ptr().add(1), 2); + | ^^^^^^^^^^ = note: BACKTRACE (of the first span): - = note: inside `core::slice::<impl [i32]>::get_unchecked::<usize>` at RUSTLIB/core/src/slice/mod.rs:LL:CC -note: inside `main` - --> $DIR/zst_slice.rs:LL:CC - | -LL | assert_eq!(*s.get_unchecked(1), 2); - | ^^^^^^^^^^^^^^^^^^ + = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/uninit/uninit_byte_read.rs b/src/tools/miri/tests/fail/uninit/uninit_byte_read.rs index f1dace0cff9..9bc2b4338b0 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_byte_read.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_byte_read.rs @@ -1,7 +1,7 @@ //@compile-flags: -Zmiri-disable-stacked-borrows fn main() { let v: Vec<u8> = Vec::with_capacity(10); - let undef = unsafe { *v.get_unchecked(5) }; //~ ERROR: uninitialized + let undef = unsafe { *v.as_ptr().add(5) }; //~ ERROR: uninitialized let x = undef + 1; panic!("this should never print: {}", x); } diff --git a/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr index b70f0ad9950..3917d868289 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr +++ b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> $DIR/uninit_byte_read.rs:LL:CC | -LL | let undef = unsafe { *v.get_unchecked(5) }; - | ^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +LL | let undef = unsafe { *v.as_ptr().add(5) }; + | ^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index 9b0a40c41b9..6ea034e2cda 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -20,8 +20,8 @@ use NaNKind::*; #[track_caller] fn check_all_outcomes<T: Eq + Hash + fmt::Display>(expected: HashSet<T>, generate: impl Fn() -> T) { let mut seen = HashSet::new(); - // Let's give it 8x as many tries as we are expecting values. - let tries = expected.len() * 8; + // Let's give it sixteen times as many tries as we are expecting values. + let tries = expected.len() * 16; for _ in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs new file mode 100644 index 00000000000..b396f3fa835 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs @@ -0,0 +1,37 @@ +#![feature(layout_for_ptr)] +use std::mem; + +#[repr(packed, C)] +struct PackedSized { + f: u8, + d: [u32; 4], +} + +#[repr(packed, C)] +struct PackedUnsized { + f: u8, + d: [u32], +} + +impl PackedSized { + fn unsize(&self) -> &PackedUnsized { + // We can't unsize via a generic type since then we get the error + // that packed structs with unsized tail don't work if the tail + // might need dropping. + let len = 4usize; + unsafe { mem::transmute((self, len)) } + } +} + +fn main() { + unsafe { + let p = PackedSized { f: 0, d: [1, 2, 3, 4] }; + let p = p.unsize() as *const PackedUnsized; + // Make sure the size computation does *not* think there is + // any padding in front of the `d` field. + assert_eq!(mem::size_of_val_raw(p), 1 + 4 * 4); + // And likewise for the offset computation. + let d = std::ptr::addr_of!((*p).d); + assert_eq!(d.cast::<u32>().read_unaligned(), 1); + } +} diff --git a/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs new file mode 100644 index 00000000000..a5cf337da02 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs @@ -0,0 +1,40 @@ +#![feature(layout_for_ptr)] +use std::mem; + +#[repr(packed(4))] +struct Slice([u32]); + +#[repr(packed(2), C)] +struct PackedSized { + f: u8, + d: [u32; 4], +} + +#[repr(packed(2), C)] +struct PackedUnsized { + f: u8, + d: Slice, +} + +impl PackedSized { + fn unsize(&self) -> &PackedUnsized { + // We can't unsize via a generic type since then we get the error + // that packed structs with unsized tail don't work if the tail + // might need dropping. + let len = 4usize; + unsafe { mem::transmute((self, len)) } + } +} + +fn main() { + unsafe { + let p = PackedSized { f: 0, d: [1, 2, 3, 4] }; + let p = p.unsize() as *const PackedUnsized; + // Make sure the size computation correctly adds exact 1 byte of padding + // in front of the `d` field. + assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4 * 4); + // And likewise for the offset computation. + let d = std::ptr::addr_of!((*p).d); + assert_eq!(d.cast::<u32>().read_unaligned(), 1); + } +} diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5a8d971c3d4..876fd93aab7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2000,9 +2000,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" +checksum = "d0c5a71827ac326072b6405552093e2ad2accd25a32fd78d4edc82d98c7f2409" [[package]] name = "tt" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 73bb9c84d2c..272f456bf9f 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -116,7 +116,7 @@ text-size = "1.1.1" rayon = "1.8.0" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" -triomphe = { version = "0.1.8", default-features = false, features = ["std"] } +triomphe = { version = "0.1.10", default-features = false, features = ["std"] } # can't upgrade due to dashmap depending on 0.12.3 currently hashbrown = { version = "0.12.3", features = [ "inline-more", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 70b96b25739..473ae298c77 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -38,7 +38,6 @@ mod tests; use std::{ fmt::{self, Debug}, hash::{Hash, Hasher}, - marker::PhantomData, ops::Index, }; @@ -340,34 +339,37 @@ pub trait ItemTreeNode: Clone { fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem; } -pub struct FileItemTreeId<N: ItemTreeNode> { - index: Idx<N>, - _p: PhantomData<N>, +pub struct FileItemTreeId<N: ItemTreeNode>(Idx<N>); + +impl<N: ItemTreeNode> FileItemTreeId<N> { + pub fn index(&self) -> Idx<N> { + self.0 + } } impl<N: ItemTreeNode> Clone for FileItemTreeId<N> { fn clone(&self) -> Self { - Self { index: self.index, _p: PhantomData } + Self(self.0) } } impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {} impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> { fn eq(&self, other: &FileItemTreeId<N>) -> bool { - self.index == other.index + self.0 == other.0 } } impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {} impl<N: ItemTreeNode> Hash for FileItemTreeId<N> { fn hash<H: Hasher>(&self, state: &mut H) { - self.index.hash(state) + self.0.hash(state) } } impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.index.fmt(f) + self.0.fmt(f) } } @@ -548,7 +550,7 @@ impl Index<RawVisibilityId> for ItemTree { impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { type Output = N; fn index(&self, id: FileItemTreeId<N>) -> &N { - N::lookup(self, id.index) + N::lookup(self, id.index()) } } @@ -925,23 +927,23 @@ impl ModItem { pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> { match self { - ModItem::Use(it) => tree[it.index].ast_id().upcast(), - ModItem::ExternCrate(it) => tree[it.index].ast_id().upcast(), - ModItem::ExternBlock(it) => tree[it.index].ast_id().upcast(), - ModItem::Function(it) => tree[it.index].ast_id().upcast(), - ModItem::Struct(it) => tree[it.index].ast_id().upcast(), - ModItem::Union(it) => tree[it.index].ast_id().upcast(), - ModItem::Enum(it) => tree[it.index].ast_id().upcast(), - ModItem::Const(it) => tree[it.index].ast_id().upcast(), - ModItem::Static(it) => tree[it.index].ast_id().upcast(), - ModItem::Trait(it) => tree[it.index].ast_id().upcast(), - ModItem::TraitAlias(it) => tree[it.index].ast_id().upcast(), - ModItem::Impl(it) => tree[it.index].ast_id().upcast(), - ModItem::TypeAlias(it) => tree[it.index].ast_id().upcast(), - ModItem::Mod(it) => tree[it.index].ast_id().upcast(), - ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(), - ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(), - ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(), + ModItem::Use(it) => tree[it.index()].ast_id().upcast(), + ModItem::ExternCrate(it) => tree[it.index()].ast_id().upcast(), + ModItem::ExternBlock(it) => tree[it.index()].ast_id().upcast(), + ModItem::Function(it) => tree[it.index()].ast_id().upcast(), + ModItem::Struct(it) => tree[it.index()].ast_id().upcast(), + ModItem::Union(it) => tree[it.index()].ast_id().upcast(), + ModItem::Enum(it) => tree[it.index()].ast_id().upcast(), + ModItem::Const(it) => tree[it.index()].ast_id().upcast(), + ModItem::Static(it) => tree[it.index()].ast_id().upcast(), + ModItem::Trait(it) => tree[it.index()].ast_id().upcast(), + ModItem::TraitAlias(it) => tree[it.index()].ast_id().upcast(), + ModItem::Impl(it) => tree[it.index()].ast_id().upcast(), + ModItem::TypeAlias(it) => tree[it.index()].ast_id().upcast(), + ModItem::Mod(it) => tree[it.index()].ast_id().upcast(), + ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(), + ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(), + ModItem::MacroDef(it) => tree[it.index()].ast_id().upcast(), } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index c898eb5f921..6807326be5a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -13,7 +13,7 @@ use crate::{ use super::*; fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> { - FileItemTreeId { index, _p: PhantomData } + FileItemTreeId(index) } pub(super) struct Ctx<'a> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 3d5ed1f93c0..8262edec22c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1152,20 +1152,15 @@ impl<'a> InferenceContext<'a> { (ty, variant) } TypeNs::TypeAliasId(it) => { - let container = it.lookup(self.db.upcast()).container; - let parent_subst = match container { - ItemContainerId::TraitId(id) => { - let subst = TyBuilder::subst_for_def(self.db, id, None) - .fill_with_inference_vars(&mut self.table) - .build(); - Some(subst) - } - // Type aliases do not exist in impls. - _ => None, + let resolved_seg = match unresolved { + None => path.segments().last().unwrap(), + Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(), }; - let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst) - .fill_with_inference_vars(&mut self.table) - .build(); + let substs = + ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None); + let ty = self.db.ty(it.into()); + let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + self.resolve_variant_on_alias(ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 04005311b67..9f5b59b239a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -768,7 +768,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn substs_from_path_segment( + pub(super) fn substs_from_path_segment( &self, segment: PathSegment<'_>, def: Option<GenericDefId>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 747ca54858c..2e6fe59d3bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -269,6 +269,10 @@ impl ProjectionStore { impl ProjectionId { pub const EMPTY: ProjectionId = ProjectionId(0); + pub fn is_empty(self) -> bool { + self == ProjectionId::EMPTY + } + pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { store.id_to_proj.get(&self).unwrap() } @@ -1069,6 +1073,10 @@ pub struct MirBody { } impl MirBody { + pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> { + self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() + } + fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { fn for_operand( op: &mut Operand, @@ -1188,3 +1196,9 @@ pub enum MirSpan { } impl_from!(ExprId, PatId for MirSpan); + +impl From<&ExprId> for MirSpan { + fn from(value: &ExprId) -> Self { + (*value).into() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9905d522146..922aee011cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -105,9 +105,14 @@ pub enum MirLowerError { /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. struct DropScopeToken; impl DropScopeToken { - fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { + fn pop_and_drop( + self, + ctx: &mut MirLowerCtx<'_>, + current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { std::mem::forget(self); - ctx.pop_drop_scope_internal(current) + ctx.pop_drop_scope_internal(current, span) } /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop @@ -582,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { - current = scope.pop_and_drop(this, current); + current = scope.pop_and_drop(this, current, body.into()); this.set_goto(current, begin, expr_id.into()); } else { scope.pop_assume_dropped(this); @@ -720,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> { .ok_or(MirLowerError::ContinueWithoutLoop)?, }; let begin = loop_data.begin; - current = self.drop_until_scope(loop_data.drop_scope_index, current); + current = + self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into()); self.set_goto(current, begin, expr_id.into()); Ok(None) } @@ -759,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.current_loop_blocks.as_ref().unwrap().drop_scope_index, ), }; - current = self.drop_until_scope(drop_scope, current); + current = self.drop_until_scope(drop_scope, current, expr_id.into()); self.set_goto(current, end, expr_id.into()); Ok(None) } @@ -773,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); } } - current = self.drop_until_scope(0, current); + current = self.drop_until_scope(0, current, expr_id.into()); self.set_terminator(current, TerminatorKind::Return, expr_id.into()); Ok(None) } @@ -1782,7 +1788,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); }; self.push_fake_read(c, p, expr.into()); - current = scope2.pop_and_drop(self, c); + current = scope2.pop_and_drop(self, c, expr.into()); } } } @@ -1793,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; current = c; } - current = scope.pop_and_drop(self, current); + current = scope.pop_and_drop(self, current, span); Ok(Some(current)) } @@ -1873,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { + fn drop_until_scope( + &mut self, + scope_index: usize, + mut current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { - self.emit_drop_and_storage_dead_for_scope(scope, &mut current); + self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span); } current } @@ -1891,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> { } /// Don't call directly - fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { + fn pop_drop_scope_internal( + &mut self, + mut current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { let scope = self.drop_scopes.pop().unwrap(); - self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); + self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span); current } fn pop_drop_scope_assert_finished( &mut self, mut current: BasicBlockId, + span: MirSpan, ) -> Result<BasicBlockId> { - current = self.pop_drop_scope_internal(current); + current = self.pop_drop_scope_internal(current, span); if !self.drop_scopes.is_empty() { implementation_error!("Mismatched count between drop scope push and pops"); } @@ -1912,6 +1928,7 @@ impl<'ctx> MirLowerCtx<'ctx> { &mut self, scope: &DropScope, current: &mut Idx<BasicBlock>, + span: MirSpan, ) { for &l in scope.locals.iter().rev() { if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { @@ -1919,13 +1936,10 @@ impl<'ctx> MirLowerCtx<'ctx> { self.set_terminator( prev, TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, - MirSpan::Unknown, + span, ); } - self.push_statement( - *current, - StatementKind::StorageDead(l).with_span(MirSpan::Unknown), - ); + self.push_statement(*current, StatementKind::StorageDead(l).with_span(span)); } } } @@ -2002,7 +2016,7 @@ pub fn mir_body_for_closure_query( |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { - let current = ctx.pop_drop_scope_assert_finished(current)?; + let current = ctx.pop_drop_scope_assert_finished(current, root.into())?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default(); @@ -2146,7 +2160,7 @@ pub fn lower_to_mir( ctx.lower_params_and_bindings([].into_iter(), binding_picker)? }; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { - let current = ctx.pop_drop_scope_assert_finished(current)?; + let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?; ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); } Ok(ctx.result) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 6e42bee97f7..a91f90bc249 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> { let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { body: &body, - local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(), + local_to_binding: body.local_to_binding_map(), result, indent, ..*self @@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> { } fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { - let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(); + let local_to_binding = body.local_to_binding_map(); MirPrettyCtx { body, db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 0f5a3e1752c..5d7bab09c26 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -1129,3 +1129,27 @@ fn foo() { "#, ); } + +#[test] +fn generic_alias() { + check_types( + r#" +type Wrap<T> = T; + +enum X { + A { cool: u32, stuff: u32 }, + B, +} + +fn main() { + let wrapped = Wrap::<X>::A { + cool: 100, + stuff: 100, + }; + + if let Wrap::<X>::A { cool, ..} = &wrapped {} + //^^^^ &u32 +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1bfbf7212bf..908027a2026 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -67,7 +67,7 @@ use hir_ty::{ known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, - mir::{self, interpret_mir}, + mir::interpret_mir, primitive::UintTy, traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, @@ -129,9 +129,10 @@ pub use { hir_ty::{ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, - mir::MirEvalError, PointerCast, Safety, }, + // FIXME: Properly encapsulate mir + hir_ty::{mir, Interner as ChalkTyInterner}, }; // These are negative re-exports: pub using these names is forbidden, they @@ -1914,17 +1915,20 @@ impl DefWithBody { if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { - if let Some(scrut_expr) = match_expr.expr() { - acc.push( - MissingMatchArms { - scrutinee_expr: InFile::new( - source_ptr.file_id, - AstPtr::new(&scrut_expr), - ), - uncovered_patterns, - } - .into(), - ); + match match_expr.expr() { + Some(scrut_expr) if match_expr.match_arm_list().is_some() => { + acc.push( + MissingMatchArms { + scrutinee_expr: InFile::new( + source_ptr.file_id, + AstPtr::new(&scrut_expr), + ), + uncovered_patterns, + } + .into(), + ); + } + _ => {} } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index ffc32f80499..0281b29cd42 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -1,4 +1,4 @@ -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, SyntaxKind, T}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -39,7 +39,19 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> AssistId("remove_parentheses", AssistKind::Refactor), "Remove redundant parentheses", target, - |builder| builder.replace_ast(parens.into(), expr), + |builder| { + let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token()); + let need_to_add_ws = match prev_token { + Some(it) => { + let tokens = vec![T![&], T![!], T!['('], T!['['], T!['{']]; + it.kind() != SyntaxKind::WHITESPACE && !tokens.contains(&it.kind()) + } + None => false, + }; + let expr = if need_to_add_ws { format!(" {}", expr) } else { expr.to_string() }; + + builder.replace(parens.syntax().text_range(), expr) + }, ) } @@ -50,6 +62,15 @@ mod tests { use super::*; #[test] + fn remove_parens_space() { + check_assist( + remove_parentheses, + r#"fn f() { match$0(true) {} }"#, + r#"fn f() { match true {} }"#, + ); + } + + #[test] fn remove_parens_simple() { check_assist(remove_parentheses, r#"fn f() { $0(2) + 2; }"#, r#"fn f() { 2 + 2; }"#); check_assist(remove_parentheses, r#"fn f() { ($02) + 2; }"#, r#"fn f() { 2 + 2; }"#); @@ -94,8 +115,8 @@ mod tests { check_assist(remove_parentheses, r#"fn f() { f(($02 + 2)); }"#, r#"fn f() { f(2 + 2); }"#); check_assist( remove_parentheses, - r#"fn f() { (1<2)&&$0(3>4); }"#, - r#"fn f() { (1<2)&&3>4; }"#, + r#"fn f() { (1<2) &&$0(3>4); }"#, + r#"fn f() { (1<2) && 3>4; }"#, ); } @@ -164,8 +185,8 @@ mod tests { fn remove_parens_weird_places() { check_assist( remove_parentheses, - r#"fn f() { match () { _=>$0(()) } }"#, - r#"fn f() { match () { _=>() } }"#, + r#"fn f() { match () { _ =>$0(()) } }"#, + r#"fn f() { match () { _ => () } }"#, ); check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 5bcc867fe18..57e06461099 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -26,17 +26,17 @@ pub(crate) fn complete_dot( item.add_to(acc, ctx.db); } - if let DotAccessKind::Method { .. } = dot_access.kind { - cov_mark::hit!(test_no_struct_field_completion_for_method_call); - } else { - complete_fields( - acc, - ctx, - receiver_ty, - |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty), - |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), - ); - } + let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. }); + + complete_fields( + acc, + ctx, + receiver_ty, + |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty), + |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), + is_field_access, + ); + complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None)); } @@ -82,6 +82,7 @@ pub(crate) fn complete_undotted_self( ) }, |acc, field, ty| acc.add_tuple_field(ctx, Some(hir::known::SELF_PARAM), field, &ty), + true, ); complete_methods(ctx, &ty, |func| { acc.add_method( @@ -104,18 +105,23 @@ fn complete_fields( receiver: &hir::Type, mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type), + is_field_access: bool, ) { let mut seen_names = FxHashSet::default(); for receiver in receiver.autoderef(ctx.db) { for (field, ty) in receiver.fields(ctx.db) { - if seen_names.insert(field.name(ctx.db)) { + if seen_names.insert(field.name(ctx.db)) + && (is_field_access || ty.is_fn() || ty.is_closure()) + { named_field(acc, field, ty); } } for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { // Tuples are always the last type in a deref chain, so just check if the name is // already seen without inserting into the hashset. - if !seen_names.contains(&hir::Name::new_tuple_field(i)) { + if !seen_names.contains(&hir::Name::new_tuple_field(i)) + && (is_field_access || ty.is_fn() || ty.is_closure()) + { // Tuple fields are always public (tuple struct fields are handled above). tuple_index(acc, i, ty); } @@ -250,7 +256,6 @@ impl A { #[test] fn test_no_struct_field_completion_for_method_call() { - cov_mark::check!(test_no_struct_field_completion_for_method_call); check( r#" struct A { the_field: u32 } @@ -1172,4 +1177,63 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> { "#]], ); } + + #[test] + fn test_struct_function_field_completion() { + check( + r#" +struct S { va_field: u32, fn_field: fn() } +fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() } +"#, + expect![[r#" + fd fn_field fn() + "#]], + ); + + check_edit( + "fn_field", + r#" +struct S { va_field: u32, fn_field: fn() } +fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() } +"#, + r#" +struct S { va_field: u32, fn_field: fn() } +fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() } +"#, + ); + } + + #[test] + fn test_tuple_function_field_completion() { + check( + r#" +struct B(u32, fn()) +fn foo() { + let b = B(0, || {}); + b.$0() +} +"#, + expect![[r#" + fd 1 fn() + "#]], + ); + + check_edit( + "1", + r#" +struct B(u32, fn()) +fn foo() { + let b = B(0, || {}); + b.$0() +} +"#, + r#" +struct B(u32, fn()) +fn foo() { + let b = B(0, || {}); + (b.1)() +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 00a9081985b..048730c078d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -18,9 +18,10 @@ use ide_db::{ RootDatabase, SnippetCap, SymbolKind, }; use syntax::{AstNode, SmolStr, SyntaxKind, TextRange}; +use text_edit::TextEdit; use crate::{ - context::{DotAccess, PathCompletionCtx, PathKind, PatternContext}, + context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext}, item::{Builder, CompletionRelevanceTypeMatch}, render::{ function::render_fn, @@ -147,7 +148,42 @@ pub(crate) fn render_field( .set_documentation(field.docs(db)) .set_deprecated(is_deprecated) .lookup_by(name); - item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name)); + + let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. }); + if !is_field_access || ty.is_fn() || ty.is_closure() { + let mut builder = TextEdit::builder(); + // Using TextEdit, insert '(' before the struct name and ')' before the + // dot access, then comes the field name and optionally insert function + // call parens. + + builder.replace( + ctx.source_range(), + field_with_receiver(db, receiver.as_ref(), &escaped_name).into(), + ); + + let expected_fn_type = + ctx.completion.expected_type.as_ref().is_some_and(|ty| ty.is_fn() || ty.is_closure()); + + if !expected_fn_type { + if let Some(receiver) = &dot_access.receiver { + if let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) { + builder.insert(receiver.syntax().text_range().start(), "(".to_string()); + builder.insert(ctx.source_range().end(), ")".to_string()); + } + } + + let is_parens_needed = + !matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); + + if is_parens_needed { + builder.insert(ctx.source_range().end(), "()".to_string()); + } + } + + item.text_edit(builder.finish()); + } else { + item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name)); + } if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { @@ -1600,7 +1636,7 @@ fn main() { fn struct_field_method_ref() { check_kinds( r#" -struct Foo { bar: u32 } +struct Foo { bar: u32, qux: fn() } impl Foo { fn baz(&self) -> u32 { 0 } } fn foo(f: Foo) { let _: &u32 = f.b$0 } @@ -1610,24 +1646,44 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } [ CompletionItem { label: "baz()", - source_range: 98..99, - delete: 98..99, + source_range: 109..110, + delete: 109..110, insert: "baz()$0", kind: Method, lookup: "baz", detail: "fn(&self) -> u32", - ref_match: "&@96", + ref_match: "&@107", }, CompletionItem { label: "bar", - source_range: 98..99, - delete: 98..99, + source_range: 109..110, + delete: 109..110, insert: "bar", kind: SymbolKind( Field, ), detail: "u32", - ref_match: "&@96", + ref_match: "&@107", + }, + CompletionItem { + label: "qux", + source_range: 109..110, + text_edit: TextEdit { + indels: [ + Indel { + insert: "(", + delete: 107..107, + }, + Indel { + insert: "qux)()", + delete: 109..110, + }, + ], + }, + kind: SymbolKind( + Field, + ), + detail: "fn()", }, ] "#]], @@ -1635,6 +1691,48 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } } #[test] + fn expected_fn_type_ref() { + check_kinds( + r#" +struct S { field: fn() } + +fn foo() { + let foo: fn() = S { fields: || {}}.fi$0; +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Field)], + expect![[r#" + [ + CompletionItem { + label: "field", + source_range: 76..78, + delete: 76..78, + insert: "field", + kind: SymbolKind( + Field, + ), + detail: "fn()", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: Some( + Exact, + ), + is_local: false, + is_item_from_trait: false, + is_name_already_imported: false, + requires_import: false, + is_op_method: false, + is_private_editable: false, + postfix_match: None, + is_definite: false, + }, + }, + ] + "#]], + ) + } + + #[test] fn qualified_path_ref() { check_kinds( r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 8af6cce98f6..b2e8274a84d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -355,6 +355,35 @@ fn outer(Foo { bar$0 }: Foo) {} } #[test] +fn completes_in_record_field_pat_with_generic_type_alias() { + check_empty( + r#" +type Wrap<T> = T; + +enum X { + A { cool: u32, stuff: u32 }, + B, +} + +fn main() { + let wrapped = Wrap::<X>::A { + cool: 100, + stuff: 100, + }; + + if let Wrap::<X>::A { $0 } = &wrapped {}; +} +"#, + expect![[r#" + fd cool u32 + fd stuff u32 + kw mut + kw ref + "#]], + ) +} + +#[test] fn completes_in_fn_param() { check_empty( r#" diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 84267d3d906..ef6a273ed8e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -17,7 +17,10 @@ pub(crate) fn missing_match_arms( #[cfg(test)] mod tests { - use crate::tests::check_diagnostics; + use crate::{ + tests::{check_diagnostics, check_diagnostics_with_config}, + DiagnosticsConfig, + }; #[track_caller] fn check_diagnostics_no_bails(ra_fixture: &str) { @@ -26,6 +29,20 @@ mod tests { } #[test] + fn empty_body() { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_string()); + check_diagnostics_with_config( + config, + r#" +fn main() { + match 0; +} +"#, + ); + } + + #[test] fn empty_tuple() { check_diagnostics_no_bails( r#" diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 24f44ca06ff..7ea9d4f1038 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -31,6 +31,7 @@ mod discriminant; mod fn_lifetime_fn; mod implicit_static; mod param_name; +mod implicit_drop; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { @@ -45,6 +46,7 @@ pub struct InlayHintsConfig { pub closure_return_type_hints: ClosureReturnTypeHints, pub closure_capture_hints: bool, pub binding_mode_hints: bool, + pub implicit_drop_hints: bool, pub lifetime_elision_hints: LifetimeElisionHints, pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, @@ -124,6 +126,7 @@ pub enum InlayKind { Lifetime, Parameter, Type, + Drop, } #[derive(Debug)] @@ -503,7 +506,10 @@ fn hints( ast::Item(it) => match it { // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints ast::Item::Impl(_) => None, - ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it), + ast::Item::Fn(it) => { + implicit_drop::hints(hints, sema, config, &it); + fn_lifetime_fn::hints(hints, config, it) + }, // static type elisions ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)), ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), @@ -591,6 +597,7 @@ mod tests { max_length: None, closing_brace_hints_min_lines: None, fields_to_resolve: InlayFieldsToResolve::empty(), + implicit_drop_hints: false, }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs new file mode 100644 index 00000000000..60f1f3496f6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -0,0 +1,204 @@ +//! Implementation of "implicit drop" inlay hints: +//! ```no_run +//! fn main() { +//! let x = vec![2]; +//! if some_condition() { +//! /* drop(x) */return; +//! } +//! } +//! ``` +use hir::{ + db::{DefDatabase as _, HirDatabase as _}, + mir::{MirSpan, TerminatorKind}, + ChalkTyInterner, DefWithBody, Semantics, +}; +use ide_db::{base_db::FileRange, RootDatabase}; + +use syntax::{ + ast::{self, AstNode}, + match_ast, +}; + +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; + +pub(super) fn hints( + acc: &mut Vec<InlayHint>, + sema: &Semantics<'_, RootDatabase>, + config: &InlayHintsConfig, + def: &ast::Fn, +) -> Option<()> { + if !config.implicit_drop_hints { + return None; + } + + let def = sema.to_def(def)?; + let def: DefWithBody = def.into(); + + let source_map = sema.db.body_with_source_map(def.into()).1; + + let hir = sema.db.body(def.into()); + let mir = sema.db.mir_body(def.into()).ok()?; + + let local_to_binding = mir.local_to_binding_map(); + + for (_, bb) in mir.basic_blocks.iter() { + let terminator = bb.terminator.as_ref()?; + if let TerminatorKind::Drop { place, .. } = terminator.kind { + if !place.projection.is_empty() { + continue; // Ignore complex cases for now + } + if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { + continue; // Arguably only ADTs have significant drop impls + } + let Some(binding) = local_to_binding.get(place.local) else { + continue; // Ignore temporary values + }; + let range = match terminator.span { + MirSpan::ExprId(e) => match source_map.expr_syntax(e) { + Ok(s) => { + let root = &s.file_syntax(sema.db); + let expr = s.value.to_node(root); + let expr = expr.syntax(); + match_ast! { + match expr { + ast::BlockExpr(x) => x.stmt_list().and_then(|x| x.r_curly_token()).map(|x| x.text_range()).unwrap_or_else(|| expr.text_range()), + _ => expr.text_range(), + } + } + } + Err(_) => continue, + }, + MirSpan::PatId(p) => match source_map.pat_syntax(p) { + Ok(s) => s.value.text_range(), + Err(_) => continue, + }, + MirSpan::Unknown => continue, + }; + let binding = &hir.bindings[*binding]; + let binding_source = binding + .definitions + .first() + .and_then(|d| source_map.pat_syntax(*d).ok()) + .and_then(|d| { + Some(FileRange { file_id: d.file_id.file_id()?, range: d.value.text_range() }) + }); + let name = binding.name.to_smol_str(); + if name.starts_with("<ra@") { + continue; // Ignore desugared variables + } + let mut label = InlayHintLabel::simple( + name, + Some(crate::InlayTooltip::String("moz".into())), + binding_source, + ); + label.prepend_str("drop("); + label.append_str(")"); + acc.push(InlayHint { + range, + position: InlayHintPosition::Before, + pad_left: true, + pad_right: true, + kind: InlayKind::Drop, + needs_resolve: label.needs_resolve(), + label, + text_edit: None, + }) + } + } + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::{ + inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, + InlayHintsConfig, + }; + + const ONLY_DROP_CONFIG: InlayHintsConfig = + InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG }; + + #[test] + fn basic() { + check_with_config( + ONLY_DROP_CONFIG, + r#" + struct X; + fn f() { + let x = X; + if 2 == 5 { + return; + //^^^^^^ drop(x) + } + } + //^ drop(x) +"#, + ); + } + + #[test] + fn no_hint_for_copy_types_and_mutable_references() { + // `T: Copy` and `T = &mut U` types do nothing on drop, so we should hide drop inlay hint for them. + check_with_config( + ONLY_DROP_CONFIG, + r#" +//- minicore: copy, derive + + struct X(i32, i32); + #[derive(Clone, Copy)] + struct Y(i32, i32); + fn f() { + let a = 2; + let b = a + 4; + let mut x = X(a, b); + let mut y = Y(a, b); + let mx = &mut x; + let my = &mut y; + let c = a + b; + } + //^ drop(x) +"#, + ); + } + + #[test] + fn try_operator() { + // We currently show drop inlay hint for every `?` operator that may potentialy drop something. We probably need to + // make it configurable as it doesn't seem very useful. + check_with_config( + ONLY_DROP_CONFIG, + r#" +//- minicore: copy, try, option + + struct X; + fn f() -> Option<()> { + let x = X; + let t_opt = Some(2); + let t = t_opt?; + //^^^^^^ drop(x) + Some(()) + } + //^ drop(x) +"#, + ); + } + + #[test] + fn if_let() { + check_with_config( + ONLY_DROP_CONFIG, + r#" + struct X; + fn f() { + let x = X; + if let X = x { + let y = X; + } + //^ drop(y) + } + //^ drop(x) +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index aabd26da289..b54874d59f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -118,6 +118,7 @@ impl StaticIndex<'_> { adjustment_hints: crate::AdjustmentHints::Never, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, + implicit_drop_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, closure_style: hir::ClosureStyle::ImplFn, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 6a2a9adce15..19da297b58c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -376,6 +376,16 @@ fn error_block(p: &mut Parser<'_>, message: &str) { m.complete(p, ERROR); } +// test_err top_level_let +// let ref foo: fn() = 1 + 3; +fn error_let_stmt(p: &mut Parser<'_>, message: &str) { + assert!(p.at(T![let])); + let m = p.start(); + p.error(message); + expressions::let_stmt(p, expressions::Semicolon::Optional); + m.complete(p, ERROR); +} + /// The `parser` passed this is required to at least consume one token if it returns `true`. /// If the `parser` returns false, parsing will stop. fn delimited( diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 1cbd1663230..e346ece2f94 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -59,7 +59,8 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { attributes::outer_attrs(p); if p.at(T![let]) { - let_stmt(p, m, semicolon); + let_stmt(p, semicolon); + m.complete(p, LET_STMT); return; } @@ -109,54 +110,53 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) { m.complete(p, EXPR_STMT); } } +} - // test let_stmt - // fn f() { let x: i32 = 92; } - fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) { - p.bump(T![let]); - patterns::pattern(p); - if p.at(T![:]) { - // test let_stmt_ascription - // fn f() { let x: i32; } - types::ascription(p); - } +// test let_stmt +// fn f() { let x: i32 = 92; } +pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) { + p.bump(T![let]); + patterns::pattern(p); + if p.at(T![:]) { + // test let_stmt_ascription + // fn f() { let x: i32; } + types::ascription(p); + } - let mut expr_after_eq: Option<CompletedMarker> = None; - if p.eat(T![=]) { - // test let_stmt_init - // fn f() { let x = 92; } - expr_after_eq = expressions::expr(p); - } + let mut expr_after_eq: Option<CompletedMarker> = None; + if p.eat(T![=]) { + // test let_stmt_init + // fn f() { let x = 92; } + expr_after_eq = expressions::expr(p); + } - if p.at(T![else]) { - // test_err let_else_right_curly_brace - // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} - if let Some(expr) = expr_after_eq { - if BlockLike::is_blocklike(expr.kind()) { - p.error( - "right curly brace `}` before `else` in a `let...else` statement not allowed", - ) - } + if p.at(T![else]) { + // test_err let_else_right_curly_brace + // fn func() { let Some(_) = {Some(1)} else { panic!("h") };} + if let Some(expr) = expr_after_eq { + if BlockLike::is_blocklike(expr.kind()) { + p.error( + "right curly brace `}` before `else` in a `let...else` statement not allowed", + ) } - - // test let_else - // fn f() { let Some(x) = opt else { return }; } - let m = p.start(); - p.bump(T![else]); - block_expr(p); - m.complete(p, LET_ELSE); } - match with_semi { - Semicolon::Forbidden => (), - Semicolon::Optional => { - p.eat(T![;]); - } - Semicolon::Required => { - p.expect(T![;]); - } + // test let_else + // fn f() { let Some(x) = opt else { return }; } + let m = p.start(); + p.bump(T![else]); + block_expr(p); + m.complete(p, LET_ELSE); + } + + match with_semi { + Semicolon::Forbidden => (), + Semicolon::Optional => { + p.eat(T![;]); + } + Semicolon::Required => { + p.expect(T![;]); } - m.complete(p, LET_STMT); } } @@ -693,6 +693,17 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // We permit `.. }` on the left-hand side of a destructuring assignment. if !p.at(T!['}']) { expr(p); + + if p.at(T![,]) { + // test_err comma_after_functional_update_syntax + // fn foo() { + // S { ..x, }; + // S { ..x, a: 0 } + // } + + // Do not bump, so we can support additional fields after this comma. + p.error("cannot use a comma after the base struct"); + } } } T!['{'] => { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index 4e850b1f74d..34fd3420f1c 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -79,6 +79,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) { e.complete(p, ERROR); } EOF | T!['}'] => p.error("expected an item"), + T![let] => error_let_stmt(p, "expected an item"), _ => p.err_and_bump("expected an item"), } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rast new file mode 100644 index 00000000000..0e2fe5988d6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rast @@ -0,0 +1,66 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 22: cannot use a comma after the base struct +error 38: cannot use a comma after the base struct diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rs new file mode 100644 index 00000000000..14cf96719b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_comma_after_functional_update_syntax.rs @@ -0,0 +1,4 @@ +fn foo() { + S { ..x, }; + S { ..x, a: 0 } +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast new file mode 100644 index 00000000000..5ddef5f3f03 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast @@ -0,0 +1,30 @@ +SOURCE_FILE + ERROR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + REF_KW "ref" + WHITESPACE " " + NAME + IDENT "foo" + COLON ":" + WHITESPACE " " + FN_PTR_TYPE + FN_KW "fn" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + LITERAL + INT_NUMBER "1" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + LITERAL + INT_NUMBER "3" + SEMICOLON ";" + WHITESPACE "\n" +error 0: expected an item diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs new file mode 100644 index 00000000000..3d3e7dd56c7 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs @@ -0,0 +1 @@ +let ref foo: fn() = 1 + 3; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 0f6539f224d..b4debba38c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -783,6 +783,7 @@ impl flags::AnalysisStats { closure_return_type_hints: ide::ClosureReturnTypeHints::Always, closure_capture_hints: true, binding_mode_hints: true, + implicit_drop_hints: true, lifetime_elision_hints: ide::LifetimeElisionHints::Always, param_names_for_lifetime_elision_hints: true, hide_named_constructor_hints: false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f28f6ffb874..90d1d6b0555 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -381,6 +381,8 @@ config_data! { inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false", /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"", + /// Whether to show implicit drop hints. + inlayHints_implicitDrops_enable: bool = "false", /// Whether to show inlay type hints for elided lifetimes in function signatures. inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. @@ -1391,6 +1393,7 @@ impl Config { type_hints: self.data.inlayHints_typeHints_enable, parameter_hints: self.data.inlayHints_parameterHints_enable, chaining_hints: self.data.inlayHints_chainingHints_enable, + implicit_drop_hints: self.data.inlayHints_implicitDrops_enable, discriminant_hints: match self.data.inlayHints_discriminantHints_enable { DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, DiscriminantHintsDef::Never => ide::DiscriminantHints::Never, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 8dba83ed5ed..abe2191f400 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -22,6 +22,7 @@ use ide_db::{ base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, ProcMacros}, FxHashMap, }; +use itertools::Itertools; use load_cargo::{load_proc_macro, ProjectFolders}; use proc_macro_api::ProcMacroServer; use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; @@ -227,16 +228,12 @@ impl GlobalState { let mut i = 0; while i < workspaces.len() { if let Ok(w) = &workspaces[i] { - let dupes: Vec<_> = workspaces + let dupes: Vec<_> = workspaces[i + 1..] .iter() - .enumerate() - .skip(i + 1) - .filter_map(|(i, it)| { - it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i) - }) + .positions(|it| it.as_ref().is_ok_and(|ws| ws.eq_ignore_build_data(w))) .collect(); dupes.into_iter().rev().for_each(|d| { - _ = workspaces.remove(d); + _ = workspaces.remove(d + i + 1); }); } i += 1; diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index f2ca9d82ed0..ba5c86db0e2 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1054,6 +1054,10 @@ pub mod option { Some(T), } + // region:copy + impl<T: Copy> Copy for Option<T> {} + // endregion:copy + impl<T> Option<T> { pub const fn unwrap(self) -> T { match self { diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index 7091ea1ce9a..8a2d0808443 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -564,6 +564,11 @@ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. -- Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). -- +[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`):: ++ +-- +Whether to show implicit drop hints. +-- [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index c43f2b964fd..cfaf4213277 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1264,6 +1264,11 @@ "Show prefix or postfix depending on which uses less parenthesis, preferring postfix." ] }, + "rust-analyzer.inlayHints.implicitDrops.enable": { + "markdownDescription": "Whether to show implicit drop hints.", + "default": false, + "type": "boolean" + }, "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", "default": "never", diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index e817d680eae..06034e16480 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -3,7 +3,7 @@ import * as vscode from "vscode"; import * as path from "path"; import type * as ra from "./lsp_ext"; -import { Cargo, getRustcId, getSysroot } from "./toolchain"; +import { Cargo, type ExecutableInfo, getRustcId, getSysroot } from "./toolchain"; import type { Ctx } from "./ctx"; import { prepareEnv } from "./run"; import { unwrapUndefinable } from "./undefinable"; @@ -12,6 +12,7 @@ const debugOutput = vscode.window.createOutputChannel("Debug"); type DebugConfigProvider = ( config: ra.Runnable, executable: string, + cargoWorkspace: string, env: Record<string, string>, sourceFileMap?: Record<string, string>, ) => vscode.DebugConfiguration; @@ -130,7 +131,7 @@ async function getDebugConfiguration( } const env = prepareEnv(runnable, ctx.config.runnablesExtraEnv); - const executable = await getDebugExecutable(runnable, env); + const { executable, workspace: cargoWorkspace } = await getDebugExecutableInfo(runnable, env); let sourceFileMap = debugOptions.sourceFileMap; if (sourceFileMap === "auto") { // let's try to use the default toolchain @@ -142,7 +143,13 @@ async function getDebugConfiguration( } const provider = unwrapUndefinable(knownEngines[debugEngine.id]); - const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap); + const debugConfig = provider( + runnable, + simplifyPath(executable), + cargoWorkspace, + env, + sourceFileMap, + ); if (debugConfig.type in debugOptions.engineSettings) { const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; for (var key in settingsMap) { @@ -164,20 +171,21 @@ async function getDebugConfiguration( return debugConfig; } -async function getDebugExecutable( +async function getDebugExecutableInfo( runnable: ra.Runnable, env: Record<string, string>, -): Promise<string> { +): Promise<ExecutableInfo> { const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env); - const executable = await cargo.executableFromArgs(runnable.args.cargoArgs); + const executableInfo = await cargo.executableInfoFromArgs(runnable.args.cargoArgs); // if we are here, there were no compilation errors. - return executable; + return executableInfo; } function getLldbDebugConfig( runnable: ra.Runnable, executable: string, + cargoWorkspace: string, env: Record<string, string>, sourceFileMap?: Record<string, string>, ): vscode.DebugConfiguration { @@ -187,7 +195,7 @@ function getLldbDebugConfig( name: runnable.label, program: executable, args: runnable.args.executableArgs, - cwd: runnable.args.workspaceRoot, + cwd: cargoWorkspace || runnable.args.workspaceRoot, sourceMap: sourceFileMap, sourceLanguages: ["rust"], env, @@ -197,6 +205,7 @@ function getLldbDebugConfig( function getCppvsDebugConfig( runnable: ra.Runnable, executable: string, + cargoWorkspace: string, env: Record<string, string>, sourceFileMap?: Record<string, string>, ): vscode.DebugConfiguration { @@ -206,7 +215,7 @@ function getCppvsDebugConfig( name: runnable.label, program: executable, args: runnable.args.executableArgs, - cwd: runnable.args.workspaceRoot, + cwd: cargoWorkspace || runnable.args.workspaceRoot, sourceFileMap, env, }; diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index 58e5fc747a1..1037e513aa1 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -9,11 +9,17 @@ import { unwrapUndefinable } from "./undefinable"; interface CompilationArtifact { fileName: string; + workspace: string; name: string; kind: string; isTest: boolean; } +export interface ExecutableInfo { + executable: string; + workspace: string; +} + export interface ArtifactSpec { cargoArgs: string[]; filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; @@ -68,6 +74,7 @@ export class Cargo { artifacts.push({ fileName: message.executable, name: message.target.name, + workspace: message.manifest_path.replace(/\/Cargo\.toml$/, ""), kind: message.target.kind[0], isTest: message.profile.test, }); @@ -86,7 +93,7 @@ export class Cargo { return spec.filter?.(artifacts) ?? artifacts; } - async executableFromArgs(args: readonly string[]): Promise<string> { + async executableInfoFromArgs(args: readonly string[]): Promise<ExecutableInfo> { const artifacts = await this.getArtifacts(Cargo.artifactSpec(args)); if (artifacts.length === 0) { @@ -96,7 +103,10 @@ export class Cargo { } const artifact = unwrapUndefinable(artifacts[0]); - return artifact.fileName; + return { + executable: artifact.fileName, + workspace: artifact.workspace, + }; } private async runCargo( diff --git a/tests/codegen/issues/issue-116878.rs b/tests/codegen/issues/issue-116878.rs new file mode 100644 index 00000000000..d5f679459f7 --- /dev/null +++ b/tests/codegen/issues/issue-116878.rs @@ -0,0 +1,13 @@ +// no-system-llvm +// compile-flags: -O +// ignore-debug: the debug assertions get in the way +#![crate_type = "lib"] + +/// Make sure no bounds checks are emitted after a `get_unchecked`. +// CHECK-LABEL: @unchecked_slice_no_bounds_check +#[no_mangle] +pub unsafe fn unchecked_slice_no_bounds_check(s: &[u8]) -> u8 { + let a = *s.get_unchecked(1); + // CHECK-NOT: panic_bounds_check + a + s[0] +} diff --git a/tests/rustdoc/enum-variant-fields-heading.rs b/tests/rustdoc/enum-variant-fields-heading.rs new file mode 100644 index 00000000000..8a7c99a8735 --- /dev/null +++ b/tests/rustdoc/enum-variant-fields-heading.rs @@ -0,0 +1,18 @@ +// This is a regression test for <https://github.com/rust-lang/rust/issues/118195>. +// It ensures that the "Fields" heading is not generated if no field is displayed. + +#![crate_name = "foo"] + +// @has 'foo/enum.Foo.html' +// @has - '//*[@id="variant.A"]' 'A' +// @count - '//*[@id="variant.A.fields"]' 0 +// @has - '//*[@id="variant.B"]' 'B' +// @count - '//*[@id="variant.B.fields"]' 0 +// @snapshot variants - '//*[@id="main-content"]/*[@class="variants"]' + +pub enum Foo { + /// A variant with no fields + A {}, + /// A variant with hidden fields + B { #[doc(hidden)] a: u8 }, +} diff --git a/tests/rustdoc/enum-variant-fields-heading.variants.html b/tests/rustdoc/enum-variant-fields-heading.variants.html new file mode 100644 index 00000000000..bcb36f7cf86 --- /dev/null +++ b/tests/rustdoc/enum-variant-fields-heading.variants.html @@ -0,0 +1,3 @@ +<div class="variants"><section id="variant.A" class="variant"><a href="#variant.A" class="anchor">§</a><h3 class="code-header">A</h3></section><div class="docblock"><p>A variant with no fields</p> +</div><section id="variant.B" class="variant"><a href="#variant.B" class="anchor">§</a><h3 class="code-header">B</h3></section><div class="docblock"><p>A variant with hidden fields</p> +</div></div> \ No newline at end of file diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr new file mode 100644 index 00000000000..69f541cba05 --- /dev/null +++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr @@ -0,0 +1,25 @@ +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` + --> $DIR/associated-type.rs:31:1 + | +LL | impl<T> Overlap<T> for T { + | ------------------------ first implementation here +... +LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T +LL | | +LL | | where +LL | | for<'a> *const T: ToUnit<'a>, + | |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), ())` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr new file mode 100644 index 00000000000..c2c951af0db --- /dev/null +++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr @@ -0,0 +1,25 @@ +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }) +error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` + --> $DIR/associated-type.rs:31:1 + | +LL | impl<T> Overlap<T> for T { + | ------------------------ first implementation here +... +LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T +LL | | +LL | | where +LL | | for<'a> *const T: ToUnit<'a>, + | |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), _)` + | + = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/occurs-check/associated-type.rs b/tests/ui/coherence/occurs-check/associated-type.rs new file mode 100644 index 00000000000..909551f65be --- /dev/null +++ b/tests/ui/coherence/occurs-check/associated-type.rs @@ -0,0 +1,45 @@ +// revisions: old next +//[next] compile-flags: -Ztrait-solver=next + +// A regression test for #105787 + +// Using the higher ranked projection hack to prevent us from replacing the projection +// with an inference variable. +trait ToUnit<'a> { + type Unit; +} + +struct LocalTy; +impl<'a> ToUnit<'a> for *const LocalTy { + type Unit = (); +} + +impl<'a, T: Copy + ?Sized> ToUnit<'a> for *const T { + type Unit = (); +} + +trait Overlap<T> { + type Assoc; +} + +type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit; + +impl<T> Overlap<T> for T { + type Assoc = usize; +} + +impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T +//~^ ERROR conflicting implementations of trait +where + for<'a> *const T: ToUnit<'a>, +{ + type Assoc = Box<usize>; +} + +fn foo<T: Overlap<U>, U>(x: T::Assoc) -> T::Assoc { + x +} + +fn main() { + foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize); +} diff --git a/tests/ui/coherence/occurs-check/opaques.next.stderr b/tests/ui/coherence/occurs-check/opaques.next.stderr new file mode 100644 index 00000000000..428ee902ea5 --- /dev/null +++ b/tests/ui/coherence/occurs-check/opaques.next.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Trait<Alias<_>>` for type `Alias<_>` + --> $DIR/opaques.rs:29:1 + | +LL | impl<T> Trait<T> for T { + | ---------------------- first implementation here +... +LL | impl<T> Trait<T> for defining_scope::Alias<T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Alias<_>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/occurs-check/opaques.rs b/tests/ui/coherence/occurs-check/opaques.rs new file mode 100644 index 00000000000..9d31a3dc82d --- /dev/null +++ b/tests/ui/coherence/occurs-check/opaques.rs @@ -0,0 +1,37 @@ +//revisions: old next +//[next] compile-flags: -Ztrait-solver=next + +// A regression test for #105787 + +//[old] known-bug: #105787 +//[old] check-pass +#![feature(type_alias_impl_trait)] +mod defining_scope { + use super::*; + pub type Alias<T> = impl Sized; + + pub fn cast<T>(x: Container<Alias<T>, T>) -> Container<T, T> { + x + } +} + +struct Container<T: Trait<U>, U> { + x: <T as Trait<U>>::Assoc, +} + +trait Trait<T> { + type Assoc; +} + +impl<T> Trait<T> for T { + type Assoc = Box<u32>; +} +impl<T> Trait<T> for defining_scope::Alias<T> { + //[next]~^ ERROR conflicting implementations of trait + type Assoc = usize; +} + +fn main() { + let x: Box<u32> = defining_scope::cast::<()>(Container { x: 0 }).x; + println!("{}", *x); +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs new file mode 100644 index 00000000000..eb985c062f3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -0,0 +1,67 @@ +#![feature(diagnostic_namespace)] + +#[diagnostic::on_unimplemented( + on(_Self = "&str"), + //~^WARN malformed `on_unimplemented` attribute + //~|WARN malformed `on_unimplemented` attribute + message = "trait has `{Self}` and `{T}` as params", + label = "trait has `{Self}` and `{T}` as params", + note = "trait has `{Self}` and `{T}` as params", + parent_label = "in this scope", + //~^WARN malformed `on_unimplemented` attribute + //~|WARN malformed `on_unimplemented` attribute + append_const_msg + //~^WARN malformed `on_unimplemented` attribute + //~|WARN malformed `on_unimplemented` attribute +)] +trait Foo<T> {} + +#[diagnostic::on_unimplemented = "Message"] +//~^WARN malformed `on_unimplemented` attribute +//~|WARN malformed `on_unimplemented` attribute +trait Bar {} + +#[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] +//~^WARN #[diagnostic::on_unimplemented]` can only be applied to trait definitions +impl Bar for i32 {} + +// cannot use special rustc_on_unimplement symbols +// in the format string +#[diagnostic::on_unimplemented( + message = "{from_desugaring}{direct}{cause}{integral}{integer}", + //~^WARN there is no parameter `from_desugaring` on trait `Baz` + //~|WARN there is no parameter `from_desugaring` on trait `Baz` + //~|WARN there is no parameter `direct` on trait `Baz` + //~|WARN there is no parameter `direct` on trait `Baz` + //~|WARN there is no parameter `cause` on trait `Baz` + //~|WARN there is no parameter `cause` on trait `Baz` + //~|WARN there is no parameter `integral` on trait `Baz` + //~|WARN there is no parameter `integral` on trait `Baz` + //~|WARN there is no parameter `integer` on trait `Baz` + //~|WARN there is no parameter `integer` on trait `Baz` + label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + //~^WARN there is no parameter `float` on trait `Baz` + //~|WARN there is no parameter `float` on trait `Baz` + //~|WARN there is no parameter `_Self` on trait `Baz` + //~|WARN there is no parameter `_Self` on trait `Baz` + //~|WARN there is no parameter `crate_local` on trait `Baz` + //~|WARN there is no parameter `crate_local` on trait `Baz` + //~|WARN there is no parameter `Trait` on trait `Baz` + //~|WARN there is no parameter `Trait` on trait `Baz` + //~|WARN there is no parameter `ItemContext` on trait `Baz` + //~|WARN there is no parameter `ItemContext` on trait `Baz` +)] +trait Baz {} + +fn takes_foo(_: impl Foo<i32>) {} +fn takes_bar(_: impl Bar) {} +fn takes_baz(_: impl Baz) {} + +fn main() { + takes_foo(()); + //~^ERROR trait has `()` and `i32` as params + takes_bar(()); + //~^ERROR the trait bound `(): Bar` is not satisfied + takes_baz(()); + //~^ERROR {from_desugaring}{direct}{cause}{integral}{integer} +} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr new file mode 100644 index 00000000000..75a701f0b5f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -0,0 +1,305 @@ +warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1 + | +LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5 + | +LL | on(_Self = "&str"), + | ^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5 + | +LL | parent_label = "in this scope", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5 + | +LL | append_const_msg + | ^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32 + | +LL | #[diagnostic::on_unimplemented = "Message"] + | ^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: there is no parameter `from_desugaring` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `direct` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `cause` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `integral` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `integer` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `float` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `_Self` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `crate_local` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `Trait` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `ItemContext` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5 + | +LL | on(_Self = "&str"), + | ^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5 + | +LL | parent_label = "in this scope", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5 + | +LL | append_const_msg + | ^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: trait has `()` and `i32` as params + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:15 + | +LL | takes_foo(()); + | --------- ^^ trait has `()` and `i32` as params + | | + | required by a bound introduced by this call + | + = help: the trait `Foo<i32>` is not implemented for `()` + = note: trait has `()` and `i32` as params +help: this trait has no implementations, consider adding one + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:17:1 + | +LL | trait Foo<T> {} + | ^^^^^^^^^^^^ +note: required by a bound in `takes_foo` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:22 + | +LL | fn takes_foo(_: impl Foo<i32>) {} + | ^^^^^^^^ required by this bound in `takes_foo` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32 + | +LL | #[diagnostic::on_unimplemented = "Message"] + | ^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `(): Bar` is not satisfied + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:63:15 + | +LL | takes_bar(()); + | --------- ^^ the trait `Bar` is not implemented for `()` + | | + | required by a bound introduced by this call + | + = help: the trait `Bar` is implemented for `i32` +note: required by a bound in `takes_bar` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:22 + | +LL | fn takes_bar(_: impl Bar) {} + | ^^^ required by this bound in `takes_bar` + +warning: there is no parameter `from_desugaring` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `direct` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `cause` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `integral` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `integer` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `float` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `_Self` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `crate_local` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `Trait` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: there is no parameter `ItemContext` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5 + | +LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer} + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15 + | +LL | takes_baz(()); + | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext} + | | + | required by a bound introduced by this call + | + = help: the trait `Baz` is not implemented for `()` +help: this trait has no implementations, consider adding one + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:1 + | +LL | trait Baz {} + | ^^^^^^^^^ +note: required by a bound in `takes_baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:22 + | +LL | fn takes_baz(_: impl Baz) {} + | ^^^ required by this bound in `takes_baz` + +error: aborting due to 3 previous errors; 29 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs index 346d8373f73..12fe988170a 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -28,10 +28,16 @@ trait Doom {} //~|WARN missing options for `on_unimplemented` attribute trait Whatever {} +#[diagnostic::on_unimplemented(message = "{DoesNotExist}")] +//~^WARN there is no parameter `DoesNotExist` on trait `Test` +//~|WARN there is no parameter `DoesNotExist` on trait `Test` +trait Test {} + fn take_foo(_: impl Foo) {} fn take_baz(_: impl Baz) {} fn take_boom(_: impl Boom) {} fn take_whatever(_: impl Whatever) {} +fn take_test(_: impl Test) {} fn main() { take_foo(1_i32); @@ -42,4 +48,6 @@ fn main() { //~^ERROR Boom take_whatever(1_i32); //~^ERROR the trait bound `i32: Whatever` is not satisfied + take_test(()); + //~^ERROR {DoesNotExist} } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index 162ddd79fbb..11263580b15 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -46,6 +46,14 @@ LL | #[diagnostic::on_unimplemented] | = help: at least one of the `message`, `note` and `label` options are expected +warning: there is no parameter `DoesNotExist` on trait `Test` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32 + | +LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 | @@ -56,7 +64,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:14 | LL | take_foo(1_i32); | -------- ^^^^^ the trait `Foo` is not implemented for `i32` @@ -69,7 +77,7 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `take_foo` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21 | LL | fn take_foo(_: impl Foo) {} | ^^^ required by this bound in `take_foo` @@ -84,7 +92,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:45:14 | LL | take_baz(1_i32); | -------- ^^^^^ the trait `Baz` is not implemented for `i32` @@ -97,7 +105,7 @@ help: this trait has no implementations, consider adding one LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `take_baz` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:32:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:21 | LL | fn take_baz(_: impl Baz) {} | ^^^ required by this bound in `take_baz` @@ -112,7 +120,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:15 | LL | take_boom(1_i32); | --------- ^^^^^ the trait `Boom` is not implemented for `i32` @@ -125,7 +133,7 @@ help: this trait has no implementations, consider adding one LL | trait Boom {} | ^^^^^^^^^^ note: required by a bound in `take_boom` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:22 | LL | fn take_boom(_: impl Boom) {} | ^^^^ required by this bound in `take_boom` @@ -140,7 +148,7 @@ LL | #[diagnostic::on_unimplemented] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `i32: Whatever` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:19 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:19 | LL | take_whatever(1_i32); | ------------- ^^^^^ the trait `Whatever` is not implemented for `i32` @@ -153,11 +161,39 @@ help: this trait has no implementations, consider adding one LL | trait Whatever {} | ^^^^^^^^^^^^^^ note: required by a bound in `take_whatever` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:26 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:26 | LL | fn take_whatever(_: impl Whatever) {} | ^^^^^^^^ required by this bound in `take_whatever` -error: aborting due to 4 previous errors; 10 warnings emitted +warning: there is no parameter `DoesNotExist` on trait `Test` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32 + | +LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: {DoesNotExist} + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15 + | +LL | take_test(()); + | --------- ^^ the trait `Test` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:1 + | +LL | trait Test {} + | ^^^^^^^^^^ +note: required by a bound in `take_test` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:22 + | +LL | fn take_test(_: impl Test) {} + | ^^^^ required by this bound in `take_test` + +error: aborting due to 5 previous errors; 12 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/packed/issue-118537-field-offset-ice.rs b/tests/ui/packed/issue-118537-field-offset-ice.rs new file mode 100644 index 00000000000..657aec64003 --- /dev/null +++ b/tests/ui/packed/issue-118537-field-offset-ice.rs @@ -0,0 +1,39 @@ +// run-pass +#![feature(layout_for_ptr)] +use std::mem; + +#[repr(packed(4))] +struct Slice([u32]); + +#[repr(packed(2), C)] +struct PackedSized { + f: u8, + d: [u32; 4], +} + +#[repr(packed(2), C)] +struct PackedUnsized { + f: u8, + d: Slice, +} + +impl PackedSized { + fn unsize(&self) -> &PackedUnsized { + // We can't unsize via a generic type since then we get the error + // that packed structs with unsized tail don't work if the tail + // might need dropping. + let len = 4usize; + unsafe { mem::transmute((self, len)) } + } +} + +fn main() { unsafe { + let p = PackedSized { f: 0, d: [1, 2, 3, 4] }; + let p = p.unsize() as *const PackedUnsized; + // Make sure the size computation correctly adds exact 1 byte of padding + // in front of the `d` field. + assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4); + // And likewise for the offset computation. + let d = std::ptr::addr_of!((*p).d); + assert_eq!(d.cast::<u32>().read_unaligned(), 1); +} } diff --git a/tests/ui/packed/issue-118537-field-offset.rs b/tests/ui/packed/issue-118537-field-offset.rs new file mode 100644 index 00000000000..cd17f767947 --- /dev/null +++ b/tests/ui/packed/issue-118537-field-offset.rs @@ -0,0 +1,36 @@ +// run-pass +#![feature(layout_for_ptr)] +use std::mem; + +#[repr(packed, C)] +struct PackedSized { + f: u8, + d: [u32; 4], +} + +#[repr(packed, C)] +struct PackedUnsized { + f: u8, + d: [u32], +} + +impl PackedSized { + fn unsize(&self) -> &PackedUnsized { + // We can't unsize via a generic type since then we get the error + // that packed structs with unsized tail don't work if the tail + // might need dropping. + let len = 4usize; + unsafe { mem::transmute((self, len)) } + } +} + +fn main() { unsafe { + let p = PackedSized { f: 0, d: [1, 2, 3, 4] }; + let p = p.unsize() as *const PackedUnsized; + // Make sure the size computation does *not* think there is + // any padding in front of the `d` field. + assert_eq!(mem::size_of_val_raw(p), 1 + 4*4); + // And likewise for the offset computation. + let d = std::ptr::addr_of!((*p).d); + assert_eq!(d.cast::<u32>().read_unaligned(), 1); +} } diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs index 2668da1b745..845597e9ce1 100644 --- a/tests/ui/traits/new-solver/equating-projection-cyclically.rs +++ b/tests/ui/traits/new-solver/equating-projection-cyclically.rs @@ -1,3 +1,4 @@ +// check-pass // compile-flags: -Ztrait-solver=next trait Test { @@ -22,7 +23,9 @@ fn main() { let mut x: Inv<_> = Inv(None); // This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>` // which fails the occurs check when generalizing `?x`. + // + // We end up emitting a delayed obligation, causing this to still + // succeed. x = transform(x); - //~^ ERROR mismatched types x = Inv::<i32>(None); } diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr deleted file mode 100644 index 91dd3ebc31b..00000000000 --- a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/equating-projection-cyclically.rs:25:9 - | -LL | x = transform(x); - | ^^^^^^^^^^^^ cyclic type of infinite size - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr new file mode 100644 index 00000000000..34c2f0438c7 --- /dev/null +++ b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `<<T as Id<_>>::Id as Unnormalizable>::Assoc == _` + --> $DIR/occurs-check-nested-alias.rs:35:9 + | +LL | x = y; + | ^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs new file mode 100644 index 00000000000..a2113b2a8b3 --- /dev/null +++ b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs @@ -0,0 +1,37 @@ +// revisions: old next +//[old] check-pass + +// Need to emit an alias-relate instead of a `Projection` goal here. +//[next] compile-flags: -Ztrait-solver=next +//[next] known-bug: trait-system-refactor-initiative#8 +#![crate_type = "lib"] +#![allow(unused)] +trait Unnormalizable { + type Assoc; +} + +trait Id<T> { + type Id; +} +impl<T, U> Id<T> for U { + type Id = U; +} + +struct Inv<T>(*mut T); + +fn unconstrained<T>() -> T { + todo!() +} + +fn create<T, U: Unnormalizable>( + x: &U, +) -> (Inv<T>, Inv<<<U as Id<T>>::Id as Unnormalizable>::Assoc>) { + todo!() +} + +fn foo<T: Unnormalizable>() { + let q = unconstrained(); + let (mut x, y) = create::<_, _>(&q); + x = y; + drop::<T>(q); +} |
