diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index c5d8d758c4733..11aa18cdb224d 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -126,6 +126,13 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display { } impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Synthetize a layout representing the variant-specific fields of an enum-like layout. + /// + /// Note that the resulting layout *does not* fully describes `self.ty` at that specific + /// variant: prefix fields (e.g. in coroutines) and tag information are lost. + /// + /// If you don't need type information about the variant's fields, prefer using + /// `self.layout.variants` directly. pub fn for_variant(self, cx: &C, variant_index: VariantIdx) -> Self where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e0e9ecaa49c63..1e0fd78b4dd75 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -2203,6 +2203,17 @@ impl LayoutData { pub fn is_uninhabited(&self) -> bool { self.uninhabited } + + /// Returns `true` if the given variant is uninhabited. + pub fn is_variant_uninhabited(&self, variant: VariantIdx) -> bool { + match self.variants { + Variants::Empty => true, + Variants::Single { index } => variant != index || self.uninhabited, + Variants::Multiple { ref variants, .. } => { + variants.get(variant).map(|v| v.uninhabited).unwrap_or(true) + } + } + } } impl fmt::Debug for LayoutData diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs index 8315c080dfa86..1a60688312473 100644 --- a/compiler/rustc_ast_ir/src/visit.rs +++ b/compiler/rustc_ast_ir/src/visit.rs @@ -99,7 +99,7 @@ macro_rules! walk_list { macro_rules! walk_visitable_list { ($visitor: expr, $list: expr $(, $($extra_args: expr),* )?) => { for elem in $list { - $crate::try_visit!(elem.visit_with($visitor $(, $($extra_args,)* )?)); + $crate::try_visit!(::rustc_type_ir::TypeVisitable::visit_with(elem, $visitor $(, $($extra_args,)* )?)); } } } diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs index 3438fc044ec55..5a49feca2a1ea 100644 --- a/compiler/rustc_attr_parsing/src/attributes/unroll.rs +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -6,7 +6,8 @@ use super::prelude::*; pub(crate) struct UnrollParser; impl SingleAttributeParser for UnrollParser { - const PATH: &[Symbol] = &[sym::unroll]; + // FIXME(#159429): temporarily renamed to mitigate `#[unroll]` nameres ambiguity. + const PATH: &[Symbol] = &[sym::rustc_unroll]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Loop), Allow(Target::ForLoop), diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 5bf692eaa7205..5bfe5ee64f050 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -2,9 +2,7 @@ use std::fmt; use rustc_data_structures::fx::FxIndexMap; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; -use rustc_middle::mir::{ - self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges, -}; +use rustc_middle::mir::{self, BasicBlock, Body, CallReturnPlaces, Location, Place}; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::impls::{ @@ -76,19 +74,15 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> { self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - term: &'mir mir::Terminator<'tcx>, + term: &mir::Terminator<'tcx>, loc: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc); self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc); self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc); - - // This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this - // analysis doesn't use. - TerminatorEdges::None } fn apply_call_return_effect( @@ -598,12 +592,12 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { self.kill_loans_out_of_scope_at_location(state, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + terminator: &mir::Terminator<'tcx>, _location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind { for op in operands { if let mir::InlineAsmOperand::Out { place: Some(place), .. } @@ -613,7 +607,6 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> { } } } - terminator.edges() } } diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index 8818e8634952e..fd4f1d8c61e55 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -14,7 +14,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( variant_index: VariantIdx, ) { let layout = place.layout(); - if layout.for_variant(fx, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { return; } match layout.variants { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index b592e4a339346..14a5f71fbceaa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -477,7 +477,7 @@ pub(super) fn codegen_tag_value<'tcx, V>( ) -> Result, UninhabitedVariantError> { // By checking uninhabited-ness first we don't need to worry about types // like `(u32, !)` which are single-variant but weird. - if layout.for_variant(cx, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { return Err(UninhabitedVariantError); } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e0388f3cc7464..3c9629c1d551e 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -626,28 +626,38 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Cast( - CastKind::PointerCoercion( + CastKind::IntToInt + | CastKind::FloatToInt + | CastKind::FloatToFloat + | CastKind::IntToFloat + | CastKind::PtrToPtr + | CastKind::FnPtrToPtr + | CastKind::Transmute + | CastKind::BoxDerefTransmute + | CastKind::PointerCoercion( PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer | PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer(_), + | PointerCoercion::ReifyFnPointer(_) + | PointerCoercion::Unsize, _, ), _, _, ) => { - // These are all okay; they only change the type, not the data. + // Operations that are fully supported by const-eval. } - + // Special checks for special casts Rvalue::Cast(CastKind::PointerExposeProvenance, _, _) => { self.check_op(ops::RawPtrToIntCast); } Rvalue::Cast(CastKind::PointerWithExposedProvenance, _, _) => { // Since no pointer can ever get exposed (rejected above), this is easy to support. } - - Rvalue::Cast(_, _, _) => {} + Rvalue::Cast(kind @ CastKind::Subtype, _, _) => { + span_bug!(self.span, "invalid CastKind for this MIR phase: {kind:?}"); + } Rvalue::UnaryOp(op, operand) => { let ty = operand.ty(self.body, self.tcx); diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index a230f797b56fd..29b6e26d950d5 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -8,7 +8,7 @@ use std::marker::PhantomData; use rustc_index::bit_set::MixedBitSet; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges, + self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, }; use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::{Analysis, JoinSemiLattice}; @@ -351,14 +351,13 @@ where self.transfer_function(state).visit_statement(statement, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { self.transfer_function(state).visit_terminator(terminator, location); - terminator.edges() } fn apply_call_return_effect( diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index a1776c6ba3d13..9d0499102c08e 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -210,7 +210,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Reading the discriminant of an uninhabited variant is UB. This is the basis for the // `uninhabited_enum_branching` MIR pass. It also ensures consistency with // `write_discriminant`. - if op.layout().for_variant(self, index).is_uninhabited() { + if op.layout().is_variant_uninhabited(index) { throw_ub!(UninhabitedEnumVariantRead(Some(index))) } interp_ok(index) @@ -252,7 +252,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Therefore, there's no way to represent those variants in the given layout. // Essentially, uninhabited variants do not have a tag that corresponds to their // discriminant, so we have to bail out here. - if layout.for_variant(self, variant_index).is_uninhabited() { + if layout.is_variant_uninhabited(variant_index) { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 045233c0c4d21..4846b48af8d5e 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -858,6 +858,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext { + self.gate_proc_macro_attr_item(span, &item); // `LegacyAttr` is only used for builtin attribute macros, which have their // safety checked by `check_builtin_meta_item`, so we don't need to check // `unsafety` here. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 72b51ad204b9d..bc6f87a2a7f17 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -217,10 +217,12 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // - https://github.com/rust-lang/rust/issues/153629 sym::rustc_splat, - // The `#[unroll]` attribute. + // The `#[rustc_unroll]` attribute. // // - https://github.com/rust-lang/rust/pull/156816 - sym::unroll, + // + // FIXME(#159429): temporarily renamed to mitigate `#[unroll]` nameres ambiguity + sym::rustc_unroll, // `#[instrument_fn = "on|off"]` to insert or inhibit instrumentation function // calls inside a function, usually around the prologue. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 530483e87329c..94241e6a31eb0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1706,7 +1706,8 @@ pub enum AttributeKind { limit: Limit, }, - /// Represents `#[unroll]` + /// Represents `#[rustc_unroll]` + // FIXME(#159429): temporarily renamed from `#[unroll]` to mitigate nameres ambiguity Unroll(UnrollAttr), /// Represents `#[unstable_feature_bound]`. diff --git a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs index c80c63b7c0188..5cf13b51a7a8c 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics/wrong_number_of_generic_args.rs @@ -489,7 +489,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { items .in_definition_order() .filter(|item| { - (item.is_type() || item.is_type_const()) + item.can_have_equality_constraint(self.tcx) && !item.is_impl_trait_in_trait() && !self .gen_args @@ -1016,8 +1016,9 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { // that would result in invalid syntax (fixes #116464) if !self.is_in_trait_impl() { let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..]; - let mut unbound_assoc_consts = - unbound_assoc_items.iter().filter(|item| item.is_type_const()); + let mut unbound_assoc_consts = unbound_assoc_items + .iter() + .filter(|item| matches!(item.kind, ty::AssocKind::Const { .. })); let mut unbound_assoc_types = unbound_assoc_items.iter().filter(|item| item.is_type()); let suggestions = unused_generics diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index add45e83f7fdd..18147afff15ce 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -554,13 +554,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let ty::AssocTag::Const = assoc_tag && !self.tcx().is_type_const(assoc_item.def_id) + && !tcx.features().generic_const_args() { if tcx.features().min_generic_const_args() { let mut err = self.dcx().struct_span_err( constraint.span, "use of trait associated const not defined as `type const`", ); - err.note("the declaration in the trait must begin with `type const` not just `const` alone"); + err.note( + "the declaration in the trait must begin with `type const` not just `const` alone", + ); return Err(err.emit()); } else { let err = self.dcx().span_delayed_bug( @@ -569,9 +572,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); return Err(err); } - } else { - bounds.push((bound.upcast(tcx), constraint.span)); } + + bounds.push((bound.upcast(tcx), constraint.span)); } // SelfTraitThatDefines is only interested in trait predicates. PredicateFilter::SelfTraitThatDefines(_) => {} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index f9ff76e293614..720dcf89523b5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -231,9 +231,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ordered_associated_items.extend( tcx.associated_items(pred.trait_ref.def_id) .in_definition_order() - // Only associated types & type consts can possibly be - // constrained in a trait object type via a binding. - .filter(|item| item.is_type() || item.is_type_const()) + .filter(|item| item.can_have_equality_constraint(tcx)) // Traits with RPITITs are simply not dyn compatible (for now). .filter(|item| !item.is_impl_trait_in_trait()) .map(|item| (item.def_id, trait_ref)), diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 97cc76d833e9e..a9d524711a141 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::mem; use std::sync::Arc; +use rustc_data_structures::unord::ExtendUnord; use rustc_hir::attrs::Deprecation; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; @@ -472,7 +473,7 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { // the former. // This is a rudimentary check that does not catch all cases, // just the easiest. - let mut fallback_map: Vec<(DefId, DefId)> = Default::default(); + let mut fallback_map: DefIdMap = Default::default(); // Issue 46112: We want the map to prefer the shortest // paths when reporting the path to an item. Therefore we @@ -533,14 +534,24 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { } } Entry::Vacant(entry) => { + if !fallback { + entry.insert(parent); + } + + // Make sure that we have not already explored this child + // through a previous fallback entry further up the BFS, + // in which case we do not want to put it back into the BFS queue, + // nor record a new fallback parent. + if fallback_map.contains_key(&def_id) { + return; + } + if fallback { // We do all of the same steps to fallback entries as to // preferred entries, except for recording them in a separate map. // It is important to not return early in the fallback cases to // ensure that we extend the BFS to the children of fallback items. - fallback_map.push((def_id, parent)); - } else { - entry.insert(parent); + fallback_map.insert(def_id, parent); } if child.res.module_like_def_id().is_some() { @@ -560,12 +571,13 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { // Fill in any missing entries with the less preferable path. // If this path re-exports the child as `_`, we still use this // path in a diagnostic that suggests importing `::*`. + // We must extend the fallback map with items from the visible parent map + // as the extend call overrides existing entries from the latter map, + // which we prefer over fallback entries. + let mut merged_visible_parent_map = fallback_map; + merged_visible_parent_map.extend_unord(visible_parent_map.into_items()); - for (child, parent) in fallback_map { - visible_parent_map.entry(child).or_insert(parent); - } - - visible_parent_map + merged_visible_parent_map }, dependency_formats: |tcx, ()| Arc::new(crate::dependency_format::calculate(tcx)), diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 4126531229b48..a119424f85af1 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -837,13 +837,13 @@ impl DynCompatibilityViolation { Self::AssocConst(name, AssocConstViolation::FeatureNotEnabled, _) => { format!("it contains associated const `{name}`").into() } - Self::AssocConst(name, AssocConstViolation::Generic, _) => { - format!("it contains generic associated const `{name}`").into() - } Self::AssocConst(name, AssocConstViolation::NonType, _) => { format!("it contains associated const `{name}` that's not defined as `type const`") .into() } + Self::AssocConst(name, AssocConstViolation::Generic, _) => { + format!("it contains generic associated const `{name}`").into() + } Self::AssocConst(name, AssocConstViolation::TypeReferencesSelf, _) => format!( "it contains associated const `{name}` whose type references the `Self` type" ) @@ -992,12 +992,12 @@ pub enum AssocConstViolation { /// Unstable feature `min_generic_const_args` wasn't enabled. FeatureNotEnabled, + /// Not defined as a type-level associated const. + NonType, + /// Has own generic parameters (GAC). Generic, - /// Isn't defined as `type const`. - NonType, - /// Its type mentions the `Self` type parameter. TypeReferencesSelf, } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 85c94d0b598e6..279a3658109bc 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -142,6 +142,18 @@ impl AssocItem { matches!(self.kind, ty::AssocKind::Const { is_type_const: true, .. }) } + /// Whether this associated item can be constrained with an equality binding. + pub fn can_have_equality_constraint(&self, tcx: TyCtxt<'_>) -> bool { + match self.kind { + ty::AssocKind::Type { .. } => true, + ty::AssocKind::Const { is_type_const: true, .. } => true, + ty::AssocKind::Const { is_type_const: false, .. } => { + tcx.features().generic_const_args() + } + ty::AssocKind::Fn { .. } => false, + } + } + pub fn is_fn(&self) -> bool { matches!(self.kind, ty::AssocKind::Fn { .. }) } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 9e7e4f2fe0c4f..470abf327679f 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -1,6 +1,5 @@ //! Implementation of [`rustc_type_ir::Interner`] for [`TyCtxt`]. -use std::ops::ControlFlow; use std::{debug_assert_matches, fmt}; use rustc_data_structures::Limit; @@ -14,7 +13,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}; use rustc_type_ir::{ BoundVar, CollectAndApply, DebruijnIndex, Interner, TypeFoldable, Unnormalized, VisitorResult, - search_graph, + search_graph, try_visit, }; use crate::dep_graph::{DepKind, DepNodeIndex}; @@ -560,10 +559,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> R { let trait_impls = self.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { - match f(impl_def_id).branch() { - ControlFlow::Break(b) => return R::from_residual(b), - ControlFlow::Continue(()) => {} - } + try_visit!(f(impl_def_id)); } R::output() diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 6c449eb62ae22..3b6c38a17a625 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -770,7 +770,7 @@ impl<'tcx> Ty<'tcx> { .map(|principal| { tcx.associated_items(principal.def_id()) .in_definition_order() - .filter(|item| item.is_type() || item.is_type_const()) + .filter(|item| item.can_have_equality_constraint(tcx)) .filter(|item| !item.is_impl_trait_in_trait()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .count() diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index da514036b20b9..5309e35b1073c 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,5 +1,4 @@ use std::iter; -use std::ops::ControlFlow; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; @@ -13,7 +12,7 @@ use tracing::debug; use crate::query::LocalCrate; use crate::traits::specialization_graph; use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use crate::ty::{self, Ident, Interner, RestrictionKind, Ty, TyCtxt, VisitorResult}; +use crate::ty::{self, Ident, Interner, RestrictionKind, Ty, TyCtxt, VisitorResult, try_visit}; /// A trait's definition with type information. #[derive(StableHash, Encodable, Decodable)] @@ -142,21 +141,12 @@ impl<'tcx> TyCtxt<'tcx> { self_ty: Ty<'tcx>, mut f: impl FnMut(DefId) -> R, ) -> R { - macro_rules! ret { - ($e: expr) => { - match $e.branch() { - ControlFlow::Break(b) => return R::from_residual(b), - ControlFlow::Continue(()) => {} - } - }; - } - let tcx = self; let trait_impls = tcx.trait_impls_of(trait_def_id); let mut consider_impls_for_simplified_type = |simp| { if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { for &impl_def_id in impls_for_type { - ret!(f(impl_def_id)) + try_visit!(f(impl_def_id)) } } @@ -191,7 +181,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::fast_reject::TreatParams::AsRigid, ) .unwrap(); - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } // HACK: For integer and float variables we have to manually look at all impls @@ -219,7 +209,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::SimplifiedType::Uint(Usize), ]; for simp in possible_integers { - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } } @@ -234,7 +224,7 @@ impl<'tcx> TyCtxt<'tcx> { ]; for simp in possible_floats { - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } } @@ -245,14 +235,14 @@ impl<'tcx> TyCtxt<'tcx> { self_ty, ty::fast_reject::TreatParams::AsRigid, ) { - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } } // This is only for diagnostics and normally ty vars should be handled by the callers. ty::Infer(ty::TyVar(_)) => { for &impl_def_id in trait_impls.non_blanket_impls().values().flatten() { - ret!(f(impl_def_id)); + try_visit!(f(impl_def_id)); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 68c8e03de8022..7b577c2b9df4c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -194,7 +194,9 @@ impl Direction for Forward { let terminator = block_data.terminator(); let location = Location { block, statement_index: block_data.statements.len() }; analysis.apply_early_terminator_effect(state, terminator, location); - let edges = analysis.apply_primary_terminator_effect(state, terminator, location); + // Edges are obtained *before* calling `apply_primary_terminator_effect`. + let edges = analysis.get_terminator_edges(state, terminator, location); + analysis.apply_primary_terminator_effect(state, terminator, location); let exit_state = state; match edges { diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 84895f7f4e1b4..a72bb3494be4c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -196,19 +196,30 @@ pub trait Analysis<'tcx> { ) { } + /// Gets the terminator edges. Used by forward analyses only. Called *before* + /// `apply_primary_terminator_effect` is applied; this might seem strange but in practice + /// `MaybeInitializedPlaces` needs that ordering and other analyses work with either ordering. + fn get_terminator_edges<'mir>( + &self, + _state: &Self::Domain, + terminator: &'mir mir::Terminator<'tcx>, + _location: Location, + ) -> TerminatorEdges<'mir, 'tcx> { + terminator.edges() + } + /// Updates the current dataflow state with the effect of evaluating a terminator. /// /// The effect of a successful return from a `Call` terminator should **not** be accounted for /// in this function. That should go in `apply_call_return_effect`. For example, in the /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, _state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, _location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { - terminator.edges() + ) { } /* Edge-specific effects */ diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 86ea3a34ae0ea..ee6330bfe1c2c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -197,15 +197,14 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { assert!(state.insert(idx)); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { let idx = self.effect(Effect::Primary.at_index(location.statement_index)); assert!(state.insert(idx)); - terminator.edges() } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 9ec68f5260c05..c5b69c563b2fe 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -41,14 +41,13 @@ impl<'tcx> Analysis<'tcx> for MaybeBorrowedLocals { Self::transfer_function(state).visit_statement(statement, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir Terminator<'tcx>, + terminator: &Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { Self::transfer_function(state).visit_terminator(terminator, location); - terminator.edges() } } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 543c833326021..1b2c58c7e514c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -391,14 +391,15 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } } - fn apply_primary_terminator_effect<'mir>( + fn get_terminator_edges<'mir>( &self, - state: &mut Self::Domain, + state: &Self::Domain, terminator: &'mir mir::Terminator<'tcx>, - location: Location, + _location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - // Note: `edges` must be computed first because `drop_flag_effects_for_location` can change - // the result of `is_unwind_dead`. + // Note: this relies on `get_terminator_edges` being called before + // `apply_primary_terminator_effect` because the result of `is_unwind_dead` is affected by + // the `drop_flag_effects_for_location` in `apply_primary_terminator_effect`. let mut edges = terminator.edges(); if self.skip_unreachable_unwind && let mir::TerminatorKind::Drop { target, unwind, place, replace: _, drop: _ } = @@ -408,10 +409,18 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { { edges = TerminatorEdges::Single(target); } + edges + } + + fn apply_primary_terminator_effect( + &self, + state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { Self::update_bits(state, path, s) }); - edges } fn apply_call_return_effect( @@ -514,15 +523,12 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { // mutable borrow occurs. Places cannot become uninitialized through a mutable reference. } - fn apply_primary_terminator_effect<'mir>( + fn get_terminator_edges<'mir>( &self, - state: &mut Self::Domain, + _state: &Self::Domain, terminator: &'mir mir::Terminator<'tcx>, location: Location, ) -> TerminatorEdges<'mir, 'tcx> { - drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { - Self::update_bits(state, path, s) - }); if self.skip_unreachable_unwind.contains(location.block) { let mir::TerminatorKind::Drop { target, unwind, .. } = terminator.kind else { bug!() }; assert_matches!(unwind, mir::UnwindAction::Cleanup(_)); @@ -532,6 +538,17 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } } + fn apply_primary_terminator_effect( + &self, + state: &mut Self::Domain, + _terminator: &mir::Terminator<'tcx>, + location: Location, + ) { + drop_flag_effects_for_location(self.body, self.move_data, location, |path, s| { + Self::update_bits(state, path, s) + }); + } + fn apply_call_return_effect( &self, state: &mut Self::Domain, @@ -633,13 +650,13 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } } - #[instrument(skip(self, state, terminator), level = "debug")] - fn apply_primary_terminator_effect<'mir>( + #[instrument(skip(self, state, _terminator), level = "debug")] + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + _terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { let move_data = self.move_data(); let init_loc_map = &move_data.init_loc_map; @@ -652,7 +669,6 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { None } })); - terminator.edges() } fn apply_call_return_effect( diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index b690e86b747d5..da2ea948366db 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -1,8 +1,6 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{ - self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, -}; +use rustc_middle::mir::{self, CallReturnPlaces, Local, Location, Place, StatementKind}; use crate::{Analysis, Backward, GenKill}; @@ -55,14 +53,13 @@ impl<'tcx> Analysis<'tcx> for MaybeLiveLocals { TransferFunction(state).visit_statement(statement, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { TransferFunction(state).visit_terminator(terminator, location); - terminator.edges() } fn apply_call_return_effect( @@ -301,14 +298,13 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { TransferFunction(state).visit_statement(statement, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'mir mir::Terminator<'tcx>, + terminator: &mir::Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { TransferFunction(state).visit_terminator(terminator, location); - terminator.edges() } fn apply_call_return_effect( diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 550f1463056f6..a3cf21bcc3578 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -295,12 +295,12 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage { } } - fn apply_primary_terminator_effect<'t>( + fn apply_primary_terminator_effect( &self, state: &mut Self::Domain, - terminator: &'t Terminator<'tcx>, + terminator: &Terminator<'tcx>, loc: Location, - ) -> TerminatorEdges<'t, 'tcx> { + ) { match terminator.kind { // For call terminators the destination requires storage for the call // and after the call returns successfully, but not after a panic. @@ -333,7 +333,6 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage { } self.check_for_move(state, loc); - terminator.edges() } fn apply_call_return_effect( diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 4ad701ddf37f3..5659157937005 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -124,19 +124,34 @@ impl<'tcx> Analysis<'tcx> for ConstAnalysis<'_, 'tcx> { } } - fn apply_primary_terminator_effect<'mir>( + fn get_terminator_edges<'mir>( &self, - state: &mut Self::Domain, + state: &Self::Domain, terminator: &'mir Terminator<'tcx>, _location: Location, ) -> TerminatorEdges<'mir, 'tcx> { if state.is_reachable() { - self.handle_terminator(terminator, state) + if let TerminatorKind::SwitchInt { discr, targets } = &terminator.kind { + self.get_switch_int_edges(discr, targets, state) + } else { + terminator.edges() + } } else { TerminatorEdges::None } } + fn apply_primary_terminator_effect( + &self, + state: &mut Self::Domain, + terminator: &Terminator<'tcx>, + _location: Location, + ) { + if state.is_reachable() { + self.handle_terminator(terminator, state) + } + } + fn apply_call_return_effect( &self, state: &mut Self::Domain, @@ -206,16 +221,10 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } - fn handle_operand( - &self, - operand: &Operand<'tcx>, - state: &mut State>, - ) -> ValueOrPlace> { + fn handle_operand(&self, operand: &Operand<'tcx>) -> ValueOrPlace> { match operand { Operand::RuntimeChecks(_) => ValueOrPlace::TOP, - Operand::Constant(constant) => { - ValueOrPlace::Value(self.handle_constant(constant, state)) - } + Operand::Constant(constant) => ValueOrPlace::Value(self.handle_constant(constant)), Operand::Copy(place) | Operand::Move(place) => { // On move, we would ideally flood the place with bottom. But with the current // framework this is not possible (similar to `InterpCx::eval_operand`). @@ -230,7 +239,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { &self, terminator: &'mir Terminator<'tcx>, state: &mut State>, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { match &terminator.kind { TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => { // Effect is applied by `handle_call_return`. @@ -242,14 +251,12 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // They would have an effect, but are not allowed in this phase. bug!("encountered disallowed terminator"); } - TerminatorKind::SwitchInt { discr, targets } => { - return self.handle_switch_int(discr, targets, state); - } TerminatorKind::TailCall { .. } => { // FIXME(explicit_tail_calls): determine if we need to do something here (probably // not) } - TerminatorKind::Goto { .. } + TerminatorKind::SwitchInt { .. } + | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return @@ -261,7 +268,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // These terminators have no effect on the analysis. } } - terminator.edges() } fn handle_call_return( @@ -378,7 +384,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { operand, _, ) => { - let pointer = self.handle_operand(operand, state); + let pointer = self.handle_operand(operand); state.assign(target.as_ref(), pointer, &self.map); if let Some(target_len) = self.map.find_len(target.as_ref()) @@ -463,7 +469,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), &self.map), - Rvalue::Use(operand, _) => return self.handle_operand(operand, state), + Rvalue::Use(operand, _) => return self.handle_operand(operand), Rvalue::CopyForDeref(_) => bug!("`CopyForDeref` in runtime MIR"), Rvalue::Ref(..) | Rvalue::Reborrow(..) | Rvalue::RawPtr(..) => { // We don't track such places. @@ -482,24 +488,20 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { ValueOrPlace::Value(val) } - fn handle_constant( - &self, - constant: &ConstOperand<'tcx>, - _state: &mut State>, - ) -> FlatSet { + fn handle_constant(&self, constant: &ConstOperand<'tcx>) -> FlatSet { constant .const_ .try_eval_scalar(self.tcx, self.typing_env) .map_or(FlatSet::Top, FlatSet::Elem) } - fn handle_switch_int<'mir>( + fn get_switch_int_edges<'mir>( &self, discr: &'mir Operand<'tcx>, targets: &'mir SwitchTargets, - state: &mut State>, + state: &State>, ) -> TerminatorEdges<'mir, 'tcx> { - let value = match self.handle_operand(discr, state) { + let value = match self.handle_operand(discr) { ValueOrPlace::Value(value) => value, ValueOrPlace::Place(place) => state.get_idx(place, &self.map), }; @@ -678,7 +680,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { op: &Operand<'tcx>, state: &mut State>, ) -> FlatSet> { - let value = match self.handle_operand(op, state) { + let value = match self.handle_operand(op) { ValueOrPlace::Value(value) => value, ValueOrPlace::Place(place) => state.get_idx(place, &self.map), }; diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index 32951ea0162a6..c895819a9f8cc 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1342,14 +1342,13 @@ impl<'tcx> Analysis<'tcx> for MaybeLivePlaces<'_, 'tcx> { self.transfer_function(trans).visit_statement(statement, location); } - fn apply_primary_terminator_effect<'mir>( + fn apply_primary_terminator_effect( &self, trans: &mut Self::Domain, - terminator: &'mir Terminator<'tcx>, + terminator: &Terminator<'tcx>, location: Location, - ) -> TerminatorEdges<'mir, 'tcx> { + ) { self.transfer_function(trans).visit_terminator(terminator, location); - terminator.edges() } fn apply_call_return_effect( diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 5c66d2be7dfdd..31c7d9af33bee 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -20,6 +20,10 @@ pub(super) struct TokenTreeDiagInfo { /// Collect empty block spans that might have been auto-inserted by editors. pub empty_block_spans: Vec, + /// Spans of `&&`/`||` tokens that directly open a brace-delimited block, + /// which usually means the user meant to continue an if-let chain. + pub if_let_chain_hint_spans: Vec, + /// Collect the spans of braces (Open, Close). Used only /// for detecting if blocks are empty and only braces. pub matching_block_spans: Vec<(Span, Span)>, @@ -124,6 +128,10 @@ pub(super) fn report_suspicious_mismatch_block( err.span_label(parent.1, "...matches this closing brace"); } } + + for span in diag_info.if_let_chain_hint_spans.iter() { + err.span_label(*span, "you might have meant to continue an if-let chain here"); + } } pub(crate) fn make_errors_for_mismatched_closing_delims<'psess>( diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 757cd755bf65f..3455947471503 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -90,6 +90,15 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.diag_info.matching_block_spans.push((pre_span, close_delimiter_span)); } + // A brace-delimited block whose first token is `&&`/`||` usually means + // the user meant to continue an if-let chain, e.g. `if let P = e { && cond {`. + if Delimiter::Brace == open_delim + && let Some(TokenTree::Token(tok, _)) = tts.iter().next() + && matches!(tok.kind, token::AndAnd | token::OrOr) + { + self.diag_info.if_let_chain_hint_spans.push(tok.span); + } + // Move past the closing delimiter. self.bump_minimal() } else { diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 6b3554331b420..6bc1647c4b05b 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -241,7 +241,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc .flat_map(|super_poly_trait_ref| { tcx.associated_items(super_poly_trait_ref.def_id()) .in_definition_order() - .filter(|item| item.is_type() || item.is_type_const()) + .filter(|item| item.can_have_equality_constraint(tcx)) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) .map(move |assoc_item| { super_poly_trait_ref.map_bound(|super_trait_ref| { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ff1d4253c4414..a346a5216128b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1872,6 +1872,8 @@ symbols! { rustc_test_marker, rustc_then_this_would_need, rustc_trivial_field_reads, + // FIXME(#159429): temporary rename to avoid `#[unroll]` nameres ambiguity + rustc_unroll, rustdoc, rustdoc_internals, rustdoc_missing_doc_code_examples, @@ -2254,7 +2256,6 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, - unroll, unsafe_attributes, unsafe_binders, unsafe_block_in_unsafe_fn, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 512a8d91338bc..3c5d473dcc045 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -370,7 +370,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( if tcx.features().min_generic_const_args() { if !tcx.generics_of(item.def_id).is_own_empty() { errors.push(AssocConstViolation::Generic); - } else if !is_type_const { + } else if !is_type_const && !tcx.features().generic_const_args() { errors.push(AssocConstViolation::NonType); } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index e5d3ccb027b70..a5896f3f863cf 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1611,7 +1611,7 @@ pub trait UpperExp: PointeeSized { /// /// let mut output = String::new(); /// fmt::write(&mut output, format_args!("Hello {}!", "world")) -/// .expect("Error occurred while trying to write in String"); +/// .expect("Writing to a `String` should not fail"); /// assert_eq!(output, "Hello world!"); /// ``` /// @@ -1622,7 +1622,7 @@ pub trait UpperExp: PointeeSized { /// /// let mut output = String::new(); /// write!(&mut output, "Hello {}!", "world") -/// .expect("Error occurred while trying to write in String"); +/// .expect("Writing to a `String` should not fail"); /// assert_eq!(output, "Hello world!"); /// ``` /// diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 1e1053583a617..fb99e86139d7e 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,11 +24,13 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - if text.len() < 2 * USIZE_BYTES { - return memchr_naive(x, text); + let result = + if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; + if let Some(index) = result { + // SAFETY: Both implementations only return an index from within `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; } - - memchr_aligned(x, text) + result } #[inline] @@ -107,8 +109,18 @@ const fn memchr_aligned(x: u8, text: &[u8]) -> Option { } /// Returns the last index matching the byte `x` in `text`. +#[inline] #[must_use] pub fn memrchr(x: u8, text: &[u8]) -> Option { + let result = memrchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memrchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result +} + +fn memrchr_aligned(x: u8, text: &[u8]) -> Option { // Scan for a single byte value by reading two `usize` words at a time. // // Split `text` in three parts: diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 22b1ba7738af9..276aab67085b3 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -1814,6 +1814,17 @@ pub mod memchr { assert_eq!(None, memchr(b'a', b"xyz")); } + #[test] + fn each_alignment() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memchr(needle, &data[start..])); + } + } + #[test] fn matches_one_reversed() { assert_eq!(Some(0), memrchr(b'a', b"a")); diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index cada78a0d55ad..45a512cb9b4c7 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -956,3 +956,37 @@ fn connect_timeout_valid() { let addr = listener.local_addr().unwrap(); TcpStream::connect_timeout(&addr, Duration::from_secs(2)).unwrap(); } + +// #115325: writing a buffer larger than `c_int::MAX` bytes used to fail on +// macOS with `EINVAL`; `write_all` should now transfer it via short sends. +#[test] +#[cfg(all(target_pointer_width = "64", unix))] +fn write_buffer_larger_than_c_int_max() { + const LEN: usize = crate::ffi::c_int::MAX as usize + 1; + + // Back the source buffer with a read-only anonymous mapping rather than a + // 2 GiB `Vec`, so the test doesn't actually consume ~2 GiB of physical + // memory while `write_all` reads through the buffer. + let data = crate::net::tests::ZeroedMmap::new(LEN); + + let listener = t!(TcpListener::bind("127.0.0.1:0")); + let addr = t!(listener.local_addr()); + let reader = thread::spawn(move || { + let (mut sock, _) = t!(listener.accept()); + let mut received = 0usize; + let mut buf = vec![0u8; 1 << 20]; + loop { + match sock.read(&mut buf) { + Ok(0) => break, + Ok(n) => received += n, + Err(e) => panic!("read error: {e}"), + } + } + received + }); + + let mut stream = t!(TcpStream::connect(addr)); + t!(stream.write_all(&data)); + drop(stream); // signal EOF so the reader loop terminates + assert_eq!(reader.join().unwrap(), LEN); +} diff --git a/library/std/src/net/tests.rs b/library/std/src/net/tests.rs index cb1c1ca36b124..213c9a5a3e3a1 100644 --- a/library/std/src/net/tests.rs +++ b/library/std/src/net/tests.rs @@ -37,6 +37,56 @@ pub fn compare_ignore_zoneid(a: &SocketAddr, b: &SocketAddr) -> bool { } } +/// A read-only anonymous mapping of `len` zero bytes. +/// +/// The tests that need a buffer larger than `c_int::MAX` use this instead of a +/// `Vec`: the pages are demand-zero and never written, so they stay mapped to +/// the shared zero page and the mapping doesn't actually consume `len` bytes of +/// physical memory. +#[cfg(all(target_pointer_width = "64", unix))] +pub struct ZeroedMmap { + ptr: *mut libc::c_void, + len: usize, +} + +#[cfg(all(target_pointer_width = "64", unix))] +impl ZeroedMmap { + pub fn new(len: usize) -> ZeroedMmap { + let ptr = unsafe { + libc::mmap( + crate::ptr::null_mut(), + len, + libc::PROT_READ, + libc::MAP_PRIVATE | libc::MAP_ANON, + -1, + 0, + ) + }; + assert_ne!(ptr, libc::MAP_FAILED, "mmap failed: {}", crate::io::Error::last_os_error()); + ZeroedMmap { ptr, len } + } +} + +#[cfg(all(target_pointer_width = "64", unix))] +impl crate::ops::Deref for ZeroedMmap { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + // SAFETY: the mapping is live for `self.len` readable bytes until `Drop`. + unsafe { crate::slice::from_raw_parts(self.ptr as *const u8, self.len) } + } +} + +#[cfg(all(target_pointer_width = "64", unix))] +impl Drop for ZeroedMmap { + fn drop(&mut self) { + // SAFETY: `ptr`/`len` come from the `mmap` call above and are unmapped once. + unsafe { + libc::munmap(self.ptr, self.len); + } + } +} + #[test] fn hostname_smoketest() { // Just a smoke test to ensure it can be called. diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index eeb6afdb072eb..38d6dab80f64e 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -374,3 +374,35 @@ fn set_nonblocking() { } }) } + +// #115325: a datagram larger than `c_int::MAX` bytes can't be sent atomically +// and must be rejected rather than truncated. +#[test] +#[cfg(all(target_pointer_width = "64", unix))] +fn send_datagram_larger_than_c_int_max() { + // A read-only anonymous mapping rather than a 2 GiB `Vec`: the datagram is + // rejected before the kernel ever reads the pages, so this costs no + // physical memory. + let data = crate::net::tests::ZeroedMmap::new(crate::ffi::c_int::MAX as usize + 1); + + let socket = t!(UdpSocket::bind("127.0.0.1:0")); + let addr = t!(socket.local_addr()); + assert!(socket.send_to(&data, addr).is_err()); + t!(socket.connect(addr)); + assert!(socket.send(&data).is_err()); +} + +// Same as above, for the platforms where the `mmap` trick isn't available and +// the buffer really has to be allocated. +#[test] +#[cfg(all(target_pointer_width = "64", not(unix)))] +#[ignore = "requires ~2 GiB of memory"] +fn send_datagram_larger_than_c_int_max() { + let data = vec![0u8; crate::ffi::c_int::MAX as usize + 1]; + + let socket = t!(UdpSocket::bind("127.0.0.1:0")); + let addr = t!(socket.local_addr()); + assert!(socket.send_to(&data, addr).is_err()); + t!(socket.connect(addr)); + assert!(socket.send(&data).is_err()); +} diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 66aa2a804db22..769dc66af8ed1 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -35,6 +35,9 @@ cfg_select! { use netc as c; +const MAX_SEND_LEN: usize = + if cfg!(target_vendor = "apple") { c_int::MAX as usize } else { ::MAX as usize }; + cfg_select! { any( target_os = "dragonfly", @@ -430,7 +433,7 @@ impl TcpStream { } pub fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let len = cmp::min(buf.len(), MAX_SEND_LEN) as wrlen_t; let ret = cvt(unsafe { c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; @@ -706,14 +709,18 @@ impl UdpSocket { self.inner.peek_from(buf) } + // `MAX_SEND_LEN` is `usize::MAX` off Apple/Windows, where the guard is a no-op. + #[allow(clippy::absurd_extreme_comparisons)] pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + if buf.len() > MAX_SEND_LEN { + return Err(io::Error::from_raw_os_error(c::EMSGSIZE)); + } let (dst, dstlen) = socket_addr_to_c(dst); let ret = cvt(unsafe { c::sendto( self.inner.as_raw(), buf.as_ptr() as *const c_void, - len, + buf.len() as wrlen_t, MSG_NOSIGNAL, dst.as_ptr(), dstlen, @@ -859,10 +866,19 @@ impl UdpSocket { self.inner.peek(buf) } + // `MAX_SEND_LEN` is `usize::MAX` off Apple/Windows, where the guard is a no-op. + #[allow(clippy::absurd_extreme_comparisons)] pub fn send(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + if buf.len() > MAX_SEND_LEN { + return Err(io::Error::from_raw_os_error(c::EMSGSIZE)); + } let ret = cvt(unsafe { - c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + c::send( + self.inner.as_raw(), + buf.as_ptr() as *const c_void, + buf.len() as wrlen_t, + MSG_NOSIGNAL, + ) })?; Ok(ret as usize) } diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index 049355afca7ac..e6f02d7a93859 100644 --- a/library/std/src/sys/net/connection/socket/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -17,3 +17,14 @@ fn no_lookup_host_duplicates() { "There should be no duplicate localhost entries" ); } + +// #115325: on Apple, `send` rejects a length > `c_int::MAX` with `EINVAL`, so +// the clamp must not regress to the unbounded `wrlen_t::MAX`. +#[test] +fn max_send_len_within_platform_limit() { + if cfg!(target_vendor = "apple") { + assert_eq!(MAX_SEND_LEN, c_int::MAX as usize); + } else { + assert_eq!(MAX_SEND_LEN, ::MAX as usize); + } +} diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 41850574c96fa..c687ed652d74a 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -279,7 +279,7 @@ impl Socket { #[cfg(not(target_os = "wasi"))] pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let len = cmp::min(buf.len(), super::MAX_SEND_LEN) as wrlen_t; let ret = cvt(unsafe { libc::send(self.as_raw_fd(), buf.as_ptr() as *const c_void, len, flags) })?; diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index aa6b6756357ac..075e77bc4457c 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -31,8 +31,8 @@ pub(super) mod netc { IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, - SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, - getsockname, getsockopt, listen, setsockopt, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, WSAEMSGSIZE as EMSGSIZE, bind, connect, + freeaddrinfo, getpeername, getsockname, getsockopt, listen, setsockopt, }; #[allow(non_camel_case_types)] diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index de3029bc0e620..652e797538223 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2350,17 +2350,13 @@ impl CommandLineStep for Assemble { let is_dylib_or_debug = is_dylib(&f.path()) || is_debug_info(&filename); // If we link statically to stdlib, do not copy the libstd dynamic library file - // FIXME: Also do this for Windows once incremental post-optimization stage0 tests - // work without std.dll (see https://github.com/rust-lang/rust/pull/131188). - let can_be_rustc_dynamic_dep = if builder - .link_std_into_rustc_driver(target_compiler.host) - && !target_compiler.host.is_windows() - { - let is_std = filename.starts_with("std-") || filename.starts_with("libstd-"); - !is_std - } else { - true - }; + let can_be_rustc_dynamic_dep = + if builder.link_std_into_rustc_driver(target_compiler.host) { + let is_std = filename.starts_with("std-") || filename.starts_with("libstd-"); + !is_std + } else { + true + }; if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro { builder.copy_link(&f.path(), &rustc_libdir.join(&filename), FileType::Regular); diff --git a/src/ci/docker/scripts/stage_2_test_set1.sh b/src/ci/docker/scripts/stage_2_test_set1.sh index e7930513c0d62..62b3c2c051a40 100755 --- a/src/ci/docker/scripts/stage_2_test_set1.sh +++ b/src/ci/docker/scripts/stage_2_test_set1.sh @@ -4,6 +4,8 @@ set -ex # Run a subset of tests. Used to run tests in parallel in multiple jobs. +# NOTE: keep in sync with `aarch64-apple*-{1,2}` jobs. + # When this job partition is run as part of PR CI, skip tidy to allow revealing more failures. The # dedicated `tidy` job failing won't block other PR CI jobs from completing, and so tidy failures # shouldn't inhibit revealing other failures in PR CI jobs. diff --git a/src/ci/docker/scripts/stage_2_test_set2.sh b/src/ci/docker/scripts/stage_2_test_set2.sh index 5963924cce529..c0cdc31011378 100755 --- a/src/ci/docker/scripts/stage_2_test_set2.sh +++ b/src/ci/docker/scripts/stage_2_test_set2.sh @@ -4,6 +4,8 @@ set -ex # Run a subset of tests. Used to run tests in parallel in multiple jobs. +# NOTE: keep in sync with `aarch64-apple*-{1,2}` jobs. + # When this job partition is run as part of PR CI, skip tidy to allow revealing more failures. The # dedicated `tidy` job failing won't block other PR CI jobs from completing, and so tidy failures # shouldn't inhibit revealing other failures in PR CI jobs. diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 5e1ef98906d00..20e52b6b52297 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -586,11 +586,41 @@ auto: CODEGEN_BACKENDS: llvm,cranelift <<: *job-macos-15 - - name: aarch64-apple + - name: aarch64-apple-1 env: - SCRIPT: > - ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin && - ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin src/tools/cargo + # NOTE: keep in sync with `src/ci/docker/scripts/stage_2_test_set1.sh` + SCRIPT: >- + ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + --skip compiler + --skip src + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set build.allocator=jemalloc + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer + # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else + # supports the hardware, so only need to test it there. + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + <<: *job-macos-15 + + - name: aarch64-apple-2 + env: + # NOTE: keep in sync with `src/ci/docker/scripts/stage_2_test_set2.sh`, + # union `src/tools/cargo` specifically. + SCRIPT: >- + ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + --skip tests + --skip library + --skip tidyselftest + && ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + src/tools/cargo RUST_CONFIGURE_ARGS: >- --enable-sanitizers --enable-profiler @@ -607,12 +637,43 @@ auto: # previous attempts have timed out multiple times. Remove/revert this job if # this hangs or times out, or if it becomes the slowest Merge CI job, and let # T-infra know. - - name: aarch64-apple-macos-26 + - name: aarch64-apple-macos-26-1 doc_url: https://github.com/rust-lang/rust/issues/157687 env: - SCRIPT: > - ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin && - ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin src/tools/cargo + # NOTE: keep in sync with `src/ci/docker/scripts/stage_2_test_set1.sh` + SCRIPT: >- + ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + --skip compiler + --skip src + RUST_CONFIGURE_ARGS: >- + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer + # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else + # supports the hardware, so only need to test it there. + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + <<: *job-macos-26 + + - name: aarch64-apple-macos-26-2 + doc_url: https://github.com/rust-lang/rust/issues/157687 + env: + # NOTE: keep in sync with `src/ci/docker/scripts/stage_2_test_set2.sh`, + # union `src/tools/cargo` specifically. + SCRIPT: >- + ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + --skip tests + --skip library + --skip tidyselftest + && ./x.py --stage 2 test + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + src/tools/cargo RUST_CONFIGURE_ARGS: >- --enable-sanitizers --enable-profiler diff --git a/src/doc/unstable-book/src/language-features/loop-hints.md b/src/doc/unstable-book/src/language-features/loop-hints.md index c02411d30c668..b82a7b367095a 100644 --- a/src/doc/unstable-book/src/language-features/loop-hints.md +++ b/src/doc/unstable-book/src/language-features/loop-hints.md @@ -6,18 +6,22 @@ The tracking issue for this feature is: [#156874] ------ + + Loop unrolling can be a powerful optimization but like inlining, it is sometimes useful to manually provide hints to optimizations. -`#[unroll]` will encourage unrolling of a loop. +`#[rustc_unroll]` will encourage unrolling of a loop. -`#[unroll(full)]` is a stronger hint and can cause optimizations to completely ignore the code +`#[rustc_unroll(full)]` is a stronger hint and can cause optimizations to completely ignore the code side growth from repeating a loop body. -`#[unroll(never)]` is a strong hint to not unroll the loop at all. Note that other loop +`#[rustc_unroll(never)]` is a strong hint to not unroll the loop at all. Note that other loop optimizations may still be applied. -`#[unroll(N)]` is a hint to unroll `N` iterations of the loop. +`#[rustc_unroll(N)]` is a hint to unroll `N` iterations of the loop. In all cases these are just hints and may be ignored. But unlike function inlining hints, loops tend to be heavily modified during compilation, which can make obeying hints challenging. diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs new file mode 100644 index 0000000000000..beeab470c08af --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -0,0 +1,38 @@ +// Ensure `memchr` communicates that a returned index is in bounds. +//@ compile-flags: -Copt-level=3 -Zinline-mir=false +//@ only-x86_64 +//@ revisions: llvm-old llvm-new +//@ [llvm-old] max-llvm-major-version: 21 +//@ [llvm-new] min-llvm-version: 22 + +#![crate_type = "lib"] +#![feature(slice_internals)] + +extern crate core; + +use core::slice::memchr::{memchr, memrchr}; + +// CHECK-LABEL: @find_char +#[no_mangle] +pub fn find_char(haystack: &str, needle: char) -> Option { + // llvm-old: call void @llvm.assume + // llvm-new-NOT: phi { i64, i64 } + // CHECK: ret { i64, i64 } + haystack.find(needle) +} + +// CHECK-LABEL: @find_byte +#[no_mangle] +pub fn find_byte(haystack: &[u8], needle: u8) -> Option { + // llvm-new-NOT: panic_bounds_check + // CHECK: ret { i1, i8 } + memchr(needle, haystack).map(|index| haystack[index]) +} + +// CHECK-LABEL: @rfind_byte +#[no_mangle] +pub fn rfind_byte(haystack: &[u8], needle: u8) -> Option { + // CHECK-NOT: panic_bounds_check + // CHECK: ret { i1, i8 } + memrchr(needle, haystack).map(|index| haystack[index]) +} diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs index 64113fbeb3247..60f9b6da6c9fe 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs @@ -15,7 +15,7 @@ unsafe extern "C" { pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] - #[unroll] + #[rustc_unroll] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -25,7 +25,7 @@ pub fn unroll_hint() { pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] - #[unroll(full)] + #[rustc_unroll(full)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -35,7 +35,7 @@ pub fn unroll_full() { pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] - #[unroll(never)] + #[rustc_unroll(never)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -45,7 +45,7 @@ pub fn unroll_never() { pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] - #[unroll(5)] + #[rustc_unroll(5)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-works.rs b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs index b2f8b58c93573..0aa8d805c4f68 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-for-works.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs @@ -11,7 +11,7 @@ unsafe extern "C" { pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK-COUNT-512: tail call void @maybe_has_side_effect() - #[unroll(full)] + #[rustc_unroll(full)] for _ in 0..512 { unsafe { maybe_has_side_effect() } } @@ -22,7 +22,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: tail call void @maybe_has_side_effect() // CHECK-NOT: tail call void @maybe_has_side_effect() - #[unroll(never)] + #[rustc_unroll(never)] for _ in 0..3 { unsafe { maybe_has_side_effect() } } @@ -32,7 +32,7 @@ pub fn unroll_never() { pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK-COUNT-5: tail call void @maybe_has_side_effect() - #[unroll(5)] + #[rustc_unroll(5)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } diff --git a/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs index 2b2b0779cf49e..7b715d1ac1e32 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs @@ -17,7 +17,7 @@ pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] let mut i = 0; - #[unroll] + #[rustc_unroll] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -35,7 +35,7 @@ pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] let mut i = 0; - let _return = (#[unroll(full)] + let _return = (#[rustc_unroll(full)] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -50,7 +50,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] let mut i = 0; - let _return = (1 + #[unroll(never)] + let _return = (1 + #[rustc_unroll(never)] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -65,7 +65,7 @@ pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] let mut i = 0; - #[unroll(5)] + #[rustc_unroll(5)] loop { unsafe { maybe_has_side_effect() } i += 1; diff --git a/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs index c40a4188334e8..1a100aae1e717 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs @@ -16,7 +16,7 @@ pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] let mut i = 0; - #[unroll] + #[rustc_unroll] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -28,7 +28,7 @@ pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] let mut i = 0; - #[unroll(full)] + #[rustc_unroll(full)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -40,7 +40,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] let mut i = 0; - #[unroll(never)] + #[rustc_unroll(never)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -52,7 +52,7 @@ pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] let mut i = 0; - #[unroll(5)] + #[rustc_unroll(5)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; diff --git a/tests/ui/attributes/unroll/invalid-unroll.rs b/tests/ui/attributes/unroll/invalid-unroll.rs index 8696cefe818f7..13a14c2713fc1 100644 --- a/tests/ui/attributes/unroll/invalid-unroll.rs +++ b/tests/ui/attributes/unroll/invalid-unroll.rs @@ -2,18 +2,18 @@ #![crate_type = "lib"] pub fn main() { - #[unroll(please)] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll(please)] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll("never")] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll("never")] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll()] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll()] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll(-1)] //~ ERROR expected a literal + #[rustc_unroll(-1)] //~ ERROR expected a literal for _ in 0..10 {} - #[unroll(1.5)] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll(1.5)] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} } diff --git a/tests/ui/attributes/unroll/invalid-unroll.stderr b/tests/ui/attributes/unroll/invalid-unroll.stderr index 9d25fa2c42d66..ced0523bf99ea 100644 --- a/tests/ui/attributes/unroll/invalid-unroll.stderr +++ b/tests/ui/attributes/unroll/invalid-unroll.stderr @@ -1,46 +1,46 @@ -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:5:7 | -LL | #[unroll(please)] - | ^^^^^^^------^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll(please)] + | ^^^^^^^^^^^^^------^ + | | + | valid arguments are `full` or `never` -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:8:7 | -LL | #[unroll("never")] - | ^^^^^^^-------^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll("never")] + | ^^^^^^^^^^^^^-------^ + | | + | valid arguments are `full` or `never` -error[E0805]: malformed `unroll` attribute input +error[E0805]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:11:7 | -LL | #[unroll()] - | ^^^^^^-- - | | - | expected an argument here +LL | #[rustc_unroll()] + | ^^^^^^^^^^^^-- + | | + | expected an argument here error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/invalid-unroll.rs:14:14 + --> $DIR/invalid-unroll.rs:14:20 | -LL | #[unroll(-1)] - | ^^ expressions are not allowed here +LL | #[rustc_unroll(-1)] + | ^^ expressions are not allowed here | help: negative numbers are not literals, try removing the `-` sign | -LL - #[unroll(-1)] -LL + #[unroll(1)] +LL - #[rustc_unroll(-1)] +LL + #[rustc_unroll(1)] | -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:17:7 | -LL | #[unroll(1.5)] - | ^^^^^^^---^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll(1.5)] + | ^^^^^^^^^^^^^---^ + | | + | valid arguments are `full` or `never` error: aborting due to 5 previous errors diff --git a/tests/ui/borrowck/alias-liveness/gat-static-unnormalized.rs b/tests/ui/borrowck/alias-liveness/gat-static-unnormalized.rs new file mode 100644 index 0000000000000..bb8dfc4553146 --- /dev/null +++ b/tests/ui/borrowck/alias-liveness/gat-static-unnormalized.rs @@ -0,0 +1,47 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for #158461. Outlives clauses from the parameter environment +// need to be normalized before alias liveness analysis can match them. + +trait Id { + type SelfType; +} + +impl Id for T { + type SelfType = T; +} + +trait Foo { + type Assoc<'a> + where + Self: 'a; + + fn assoc(&mut self) -> Self::Assoc<'_>; +} + +// The normalized `'static` bound allows this value's borrow to end immediately. +fn overlapping_mut(mut t: T) +where + T: Foo, + for<'a> as Id>::SelfType: 'static, +{ + let a = t.assoc(); + let b = t.assoc(); +} + +// This is a distinct liveness path: the owner can be moved while the projected +// value remains live. +fn live_past_borrow(mut t: T) +where + T: Foo, + for<'a> as Id>::SelfType: 'static, +{ + let x = t.assoc(); + drop(t); + drop(x); +} + +fn main() {} diff --git a/tests/ui/cfg/cfg-stmt-recovery.rs b/tests/ui/cfg/cfg-stmt-recovery.rs index f0f9a649165b5..98f79cd8cfc1c 100644 --- a/tests/ui/cfg/cfg-stmt-recovery.rs +++ b/tests/ui/cfg/cfg-stmt-recovery.rs @@ -1,7 +1,7 @@ // Verify that we do not ICE when failing to parse a statement in `cfg_eval`. #![feature(cfg_eval)] -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] #[cfg_eval] fn main() { diff --git a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs index 7c42be3ed4d6e..3f6f902cf3688 100644 --- a/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs +++ b/tests/ui/conditional-compilation/invalid-node-range-issue-129166.rs @@ -3,7 +3,7 @@ //@ check-pass #![feature(cfg_eval)] -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] fn f() -> u32 { #[cfg_eval] #[cfg(not(FALSE))] 0 diff --git a/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs index 38d593984724e..302c4e4187349 100644 --- a/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs +++ b/tests/ui/const-generics/associated-const-bindings/dyn-compat-non-type-assoc-const.rs @@ -13,7 +13,7 @@ trait Trait { fn main() { let _: dyn Trait; //~ ERROR the trait `Trait` is not dyn compatible - // Check that specifying the non-type assoc const doesn't "magically make it work". + // Check that specifying the non-type assoc const doesn't work without full GCA. let _: dyn Trait; //~^ ERROR the trait `Trait` is not dyn compatible //~| ERROR use of trait associated const not defined as `type const` diff --git a/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.rs b/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.rs new file mode 100644 index 0000000000000..ec32c7c8b1062 --- /dev/null +++ b/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.rs @@ -0,0 +1,18 @@ +// Ensure that traits with generic non-type associated consts are dyn *in*compatible, +// even when non-type associated const equality is enabled by `generic_const_args`. + +//@ dont-require-annotations: NOTE +//@ compile-flags: -Znext-solver=globally + +#![feature(generic_const_args, generic_const_items, min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const ASSOC: usize; + //~^ NOTE it contains generic associated const `ASSOC` +} + +fn main() { + let _: dyn Trait; + //~^ ERROR the trait `Trait` is not dyn compatible +} diff --git a/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.stderr b/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.stderr new file mode 100644 index 0000000000000..ff09f40f7ca98 --- /dev/null +++ b/tests/ui/const-generics/gca/dyn-compat-generic-non-type-assoc-const.stderr @@ -0,0 +1,19 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/dyn-compat-generic-non-type-assoc-const.rs:16:16 + | +LL | let _: dyn Trait; + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/dyn-compat-generic-non-type-assoc-const.rs:11:11 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +LL | const ASSOC: usize; + | ^^^^^ ...because it contains generic associated const `ASSOC` + = help: consider moving `ASSOC` to another trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/gca/dyn-non-type-assoc-const-binding.rs b/tests/ui/const-generics/gca/dyn-non-type-assoc-const-binding.rs new file mode 100644 index 0000000000000..bf764b829ab6d --- /dev/null +++ b/tests/ui/const-generics/gca/dyn-non-type-assoc-const-binding.rs @@ -0,0 +1,13 @@ +//@ check-pass +//@ compile-flags: -Znext-solver=globally + +#![feature(min_generic_const_args, generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const ASSOC: usize; +} + +fn foo(_: &dyn Trait) {} + +fn main() {} diff --git a/tests/ui/eii/errors.rs b/tests/ui/eii/errors.rs index bc6c17f463a78..3b28e268662ef 100644 --- a/tests/ui/eii/errors.rs +++ b/tests/ui/eii/errors.rs @@ -8,7 +8,7 @@ #[eii_declaration(bar)] //~ ERROR `#[eii_declaration(...)]` is only valid on macros fn hello() { #[eii_declaration(bar)] //~ ERROR `#[eii_declaration(...)]` is only valid on macros - let x = 3 + 3; + let x = 3 + 3; //~| ERROR custom attributes cannot be applied to statements } #[eii_declaration] //~ ERROR `#[eii_declaration(...)]` expects a list of one or two elements diff --git a/tests/ui/eii/errors.stderr b/tests/ui/eii/errors.stderr index 553ae622cb36f..512cd135de4c3 100644 --- a/tests/ui/eii/errors.stderr +++ b/tests/ui/eii/errors.stderr @@ -4,6 +4,16 @@ error: `#[eii_declaration(...)]` is only valid on macros LL | #[eii_declaration(bar)] | ^^^^^^^^^^^^^^^^^^^^^^^ +error[E0658]: custom attributes cannot be applied to statements + --> $DIR/errors.rs:10:5 + | +LL | #[eii_declaration(bar)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: `#[eii_declaration(...)]` is only valid on macros --> $DIR/errors.rs:10:5 | @@ -88,5 +98,6 @@ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` LL | #[foo = "default"] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.rs b/tests/ui/feature-gates/feature-gate-loop-hints.rs index 85a1f10ab0a63..480d9a95f08a9 100644 --- a/tests/ui/feature-gates/feature-gate-loop-hints.rs +++ b/tests/ui/feature-gates/feature-gate-loop-hints.rs @@ -1,4 +1,4 @@ fn main() { - #[unroll] //~ ERROR the `unroll` attribute is an experimental feature + #[rustc_unroll] //~ ERROR the `rustc_unroll` attribute is an experimental feature for _ in 0..10 {} } diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.stderr b/tests/ui/feature-gates/feature-gate-loop-hints.stderr index 98279fe144126..56c3ec6812c9c 100644 --- a/tests/ui/feature-gates/feature-gate-loop-hints.stderr +++ b/tests/ui/feature-gates/feature-gate-loop-hints.stderr @@ -1,8 +1,8 @@ -error[E0658]: the `unroll` attribute is an experimental feature +error[E0658]: the `rustc_unroll` attribute is an experimental feature --> $DIR/feature-gate-loop-hints.rs:2:7 | -LL | #[unroll] - | ^^^^^^ +LL | #[rustc_unroll] + | ^^^^^^^^^^^^ | = note: see issue #156874 for more information = help: add `#![feature(loop_hints)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr index 629d25ec4f01c..884a02c5ec25d 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr @@ -33,6 +33,12 @@ error: cannot find attribute `rustc_unknown` in this scope | LL | #[rustc_unknown] | ^^^^^^^^^^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[rustc_unknown] +LL + #[rustc_unroll] + | error[E0658]: use of an internal attribute --> $DIR/feature-gate-rustc-attrs.rs:20:3 diff --git a/tests/ui/macros/issue-111749.rs b/tests/ui/macros/issue-111749.rs index f009a69fe2535..799fee22685ab 100644 --- a/tests/ui/macros/issue-111749.rs +++ b/tests/ui/macros/issue-111749.rs @@ -9,4 +9,5 @@ fn main() { //~^ ERROR the `test` attribute may only be used on a free function //~| ERROR attribute must be of the form `#[test]` //~| WARNING this was previously accepted by the compiler but is being phased out + //~| ERROR custom attributes cannot be applied to expressions } diff --git a/tests/ui/macros/issue-111749.stderr b/tests/ui/macros/issue-111749.stderr index 267f939602b5b..f2773e7029ab5 100644 --- a/tests/ui/macros/issue-111749.stderr +++ b/tests/ui/macros/issue-111749.stderr @@ -1,3 +1,13 @@ +error[E0658]: custom attributes cannot be applied to expressions + --> $DIR/issue-111749.rs:8:17 + | +LL | cbor_map! { #[test(test)] 4i32}; + | ^^^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error: the `test` attribute may only be used on a free function --> $DIR/issue-111749.rs:8:17 | @@ -20,8 +30,9 @@ LL | cbor_map! { #[test(test)] 4i32}; = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. Future incompatibility report: Future breakage diagnostic: error: attribute must be of the form `#[test]` --> $DIR/issue-111749.rs:8:17 diff --git a/tests/ui/nll/polonius/nll-legacy-unnecessary-error.legacy.stderr b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.legacy.stderr new file mode 100644 index 0000000000000..79f32e55559cf --- /dev/null +++ b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.legacy.stderr @@ -0,0 +1,14 @@ +error[E0506]: cannot assign to `z` because it is borrowed + --> $DIR/nll-legacy-unnecessary-error.rs:20:5 + | +LL | x.0 = &z; + | -- `z` is borrowed here +LL | z += 1; + | ^^^^^^ `z` is assigned to here but it was already borrowed +... +LL | dbg!(y.0); + | --- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/nll/polonius/nll-legacy-unnecessary-error.nll.stderr b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.nll.stderr new file mode 100644 index 0000000000000..79f32e55559cf --- /dev/null +++ b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.nll.stderr @@ -0,0 +1,14 @@ +error[E0506]: cannot assign to `z` because it is borrowed + --> $DIR/nll-legacy-unnecessary-error.rs:20:5 + | +LL | x.0 = &z; + | -- `z` is borrowed here +LL | z += 1; + | ^^^^^^ `z` is assigned to here but it was already borrowed +... +LL | dbg!(y.0); + | --- borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/nll/polonius/nll-legacy-unnecessary-error.rs b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.rs new file mode 100644 index 0000000000000..06a4f9b1a5640 --- /dev/null +++ b/tests/ui/nll/polonius/nll-legacy-unnecessary-error.rs @@ -0,0 +1,25 @@ +// NLLs and legacy polonius emit an unnecessary error here, unlike the alpha. It's not clear +// *exactly* why the datalog implementation rejects this, but it looks like it propagates the loan +// from 'x to 'y very eagerly, even though x is dead before the assignment. The loan would thus be +// live and invalidated by the assignment, AKA an error. + +//@ ignore-compare-mode-polonius (explicit revisions) +//@ revisions: nll polonius legacy +//@ [nll] compile-flags: -Z polonius=off +//@ [polonius] check-pass +//@ [polonius] compile-flags: -Z polonius=next +//@ [legacy] compile-flags: -Z polonius=legacy + +fn main() { + let mut x: (&u32,) = (&1,); + let mut y: (&u32,) = (&2,); + let mut z = 3; + + y.0 = x.0; + x.0 = &z; + z += 1; + //[nll]~^ ERROR: cannot assign to `z` because it is borrowed + //[legacy]~^^ ERROR: cannot assign to `z` because it is borrowed + + dbg!(y.0); +} diff --git a/tests/ui/parser/brace-in-let-chain.stderr b/tests/ui/parser/brace-in-let-chain.stderr index 12af95c278688..15622bd3266b2 100644 --- a/tests/ui/parser/brace-in-let-chain.stderr +++ b/tests/ui/parser/brace-in-let-chain.stderr @@ -4,24 +4,46 @@ error: this file contains an unclosed delimiter LL | fn main() { | - unclosed delimiter ... +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... LL | fn quux() { | - unclosed delimiter ... +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... LL | fn foobar() { | - unclosed delimiter ... +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... LL | fn fubar() { | - unclosed delimiter ... +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... LL | fn qux() { | - unclosed delimiter ... +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... LL | fn foo() { | - another 3 unclosed delimiters begin from here +LL | { +LL | && let () = () + | -- you might have meant to continue an if-let chain here +... +LL | && let () = () + | -- you might have meant to continue an if-let chain here ... LL | { | - this delimiter might not be properly closed... LL | && let () = () + | -- you might have meant to continue an if-let chain here LL | } | - ...as it matches this but it has different indentation LL | } diff --git a/tests/ui/parser/deli-ident-issue-1.stderr b/tests/ui/parser/deli-ident-issue-1.stderr index d17913eb7ea40..7abe8b0ea5554 100644 --- a/tests/ui/parser/deli-ident-issue-1.stderr +++ b/tests/ui/parser/deli-ident-issue-1.stderr @@ -6,7 +6,9 @@ LL | impl dyn Demo { ... LL | && let Some(c) = num { | - this delimiter might not be properly closed... -... +LL | && b == c { + | -- you might have meant to continue an if-let chain here +LL | } LL | } | - ...as it matches this but it has different indentation ... diff --git a/tests/ui/parser/if-let-chain-unclosed-delim.rs b/tests/ui/parser/if-let-chain-unclosed-delim.rs new file mode 100644 index 0000000000000..11f365ce5311c --- /dev/null +++ b/tests/ui/parser/if-let-chain-unclosed-delim.rs @@ -0,0 +1,8 @@ +//! Regression test for an unclosed delimiter whose block begins with `&&`/`||` +//! should hint that the user may have meant to continue an if-let chain. +fn main() { + if let Some(x) = Some(42) { + && x == 42 + { + } +} //~ ERROR this file contains an unclosed delimiter diff --git a/tests/ui/parser/if-let-chain-unclosed-delim.stderr b/tests/ui/parser/if-let-chain-unclosed-delim.stderr new file mode 100644 index 0000000000000..ce34a89b62b5a --- /dev/null +++ b/tests/ui/parser/if-let-chain-unclosed-delim.stderr @@ -0,0 +1,17 @@ +error: this file contains an unclosed delimiter + --> $DIR/if-let-chain-unclosed-delim.rs:8:54 + | +LL | fn main() { + | - unclosed delimiter +LL | if let Some(x) = Some(42) { + | - this delimiter might not be properly closed... +LL | && x == 42 + | -- you might have meant to continue an if-let chain here +... +LL | } + | - ^ + | | + | ...as it matches this but it has different indentation + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.rs b/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.rs new file mode 100644 index 0000000000000..e0088837fac8e --- /dev/null +++ b/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.rs @@ -0,0 +1,6 @@ +//! Regression test for . + +fn main() { + drop::<[(), 0]>([]); + //~^ ERROR expected `;` or `]`, found `,` +} diff --git a/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.stderr b/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.stderr new file mode 100644 index 0000000000000..17dc812c8e6ec --- /dev/null +++ b/tests/ui/parser/recover/array-type-no-semi-turbofish-81097.stderr @@ -0,0 +1,14 @@ +error: expected `;` or `]`, found `,` + --> $DIR/array-type-no-semi-turbofish-81097.rs:4:15 + | +LL | drop::<[(), 0]>([]); + | ^ expected `;` or `]` + | +help: you might have meant to use `;` as the separator + | +LL - drop::<[(), 0]>([]); +LL + drop::<[(); 0]>([]); + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/proc-macro/cfg-eval-fail.rs b/tests/ui/proc-macro/cfg-eval-fail.rs index a94dcd2837811..2cde895f2ea44 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.rs +++ b/tests/ui/proc-macro/cfg-eval-fail.rs @@ -4,4 +4,5 @@ fn main() { let _ = #[cfg_eval] #[cfg(false)] 0; //~^ ERROR removing an expression is not supported in this position + //~| ERROR custom attributes cannot be applied to expressions } diff --git a/tests/ui/proc-macro/cfg-eval-fail.stderr b/tests/ui/proc-macro/cfg-eval-fail.stderr index 7f21e4646b1cc..61da346fa69f6 100644 --- a/tests/ui/proc-macro/cfg-eval-fail.stderr +++ b/tests/ui/proc-macro/cfg-eval-fail.stderr @@ -4,5 +4,16 @@ error: removing an expression is not supported in this position LL | let _ = #[cfg_eval] #[cfg(false)] 0; | ^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0658]: custom attributes cannot be applied to expressions + --> $DIR/cfg-eval-fail.rs:5:13 + | +LL | let _ = #[cfg_eval] #[cfg(false)] 0; + | ^^^^^^^^^^^ + | + = note: see issue #54727 for more information + = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/proc-macro/derive-macro-invalid-placement.rs b/tests/ui/proc-macro/derive-macro-invalid-placement.rs index fd24bd7284a92..463e7dc758505 100644 --- a/tests/ui/proc-macro/derive-macro-invalid-placement.rs +++ b/tests/ui/proc-macro/derive-macro-invalid-placement.rs @@ -1,6 +1,6 @@ //! regression test for -#![feature(stmt_expr_attributes)] +#![feature(stmt_expr_attributes, proc_macro_hygiene)] fn foo<#[derive(Debug)] T>() { //~ ERROR expected non-macro attribute, found attribute macro match 0 { diff --git a/tests/ui/suggestions/suggest-path-through-direct-dep-crate/auxiliary/direct-dep-with-multiple-reexports.rs b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/auxiliary/direct-dep-with-multiple-reexports.rs new file mode 100644 index 0000000000000..8ff8d3b572741 --- /dev/null +++ b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/auxiliary/direct-dep-with-multiple-reexports.rs @@ -0,0 +1,15 @@ +#![crate_type = "lib"] + +extern crate transitive_dep; + +mod private { + pub use crate::transitive_dep::Struct; +} + +#[doc(hidden)] +pub use crate::private::*; + +#[doc(hidden)] +pub mod __private { + pub use crate::private::*; +} diff --git a/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.rs b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.rs new file mode 100644 index 0000000000000..c3ec780429376 --- /dev/null +++ b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.rs @@ -0,0 +1,16 @@ +//@ aux-build: transitive-dep.rs +//@ aux-build: direct-dep-with-multiple-reexports.rs + +extern crate direct_dep_with_multiple_reexports as direct_dep; + +struct Struct; +//~^ NOTE `Struct` is defined in the current crate + +fn main() { + let _: direct_dep::Struct = Struct; + //~^ ERROR mismatched types + //~| NOTE expected `direct_dep::Struct`, found `Struct` + //~| NOTE expected due to this + //~| NOTE `Struct` and `direct_dep::Struct` have similar names, but are actually distinct types + //~| NOTE `direct_dep::Struct` is defined in crate `transitive_dep` +} diff --git a/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.stderr b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.stderr new file mode 100644 index 0000000000000..46042907b38d7 --- /dev/null +++ b/tests/ui/suggestions/suggest-path-through-direct-dep-crate/use-shortest-hidden-reexport-path.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/use-shortest-hidden-reexport-path.rs:10:33 + | +LL | let _: direct_dep::Struct = Struct; + | ------------------ ^^^^^^ expected `direct_dep::Struct`, found `Struct` + | | + | expected due to this + | + = note: `Struct` and `direct_dep::Struct` have similar names, but are actually distinct types +note: `Struct` is defined in the current crate + --> $DIR/use-shortest-hidden-reexport-path.rs:6:1 + | +LL | struct Struct; + | ^^^^^^^^^^^^^ +note: `direct_dep::Struct` is defined in crate `transitive_dep` + --> $DIR/auxiliary/transitive-dep.rs:3:1 + | +LL | pub struct Struct; + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/next-solver/generalize/eagerly-normalizing-aliases.rs b/tests/ui/traits/next-solver/generalize/eagerly-normalizing-aliases.rs new file mode 100644 index 0000000000000..2d5ae9d21010f --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/eagerly-normalizing-aliases.rs @@ -0,0 +1,31 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#262. + +trait View {} + +trait HasAssoc { + type Assoc; +} + +struct StableVec(T); + +impl View for StableVec {} + +fn assert_view(f: F) -> F { + f +} + +fn store() -> StableVec +where + T: HasAssoc, + StableVec: View, +{ + let x = todo!(); + assert_view(x) +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.rs b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.rs new file mode 100644 index 0000000000000..f93410550bdcf --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Znext-solver + +// Regression test for trait-system-refactor-initiative#267. This recursively +// changing opaque type used to overflow the stack while instantiating a +// canonical response. + +trait Distribution {} + +impl Distribution<(A, B)> for u32 +where + u32: Distribution, + u32: Distribution, +{ +} + +fn require_distribution, T>(_: *mut T) {} + +fn random_paulis() -> Option<*mut impl Sized> { + if false { + let r = random_paulis().unwrap(); + //~^ ERROR type annotations needed + require_distribution::(r); + } + + None +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr new file mode 100644 index 0000000000000..b3b173def6a01 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/recursive-hidden-type-canonicalization.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed for `*mut _` + --> $DIR/recursive-hidden-type-canonicalization.rs:20:13 + | +LL | let r = random_paulis().unwrap(); + | ^ + | +help: consider giving `r` an explicit type, where the placeholder `_` is specified + | +LL | let r: *mut _ = random_paulis().unwrap(); + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/traits/next-solver/opaques/stalled-goal-rerun.rs b/tests/ui/traits/next-solver/opaques/stalled-goal-rerun.rs new file mode 100644 index 0000000000000..8402d695749cd --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/stalled-goal-rerun.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#267. This used to hang +// because a fast-path goal was not rerun after the opaque type storage changed. + +trait Distribution {} + +impl Distribution<()> for u32 {} + +impl Distribution<(A, B)> for u32 +where + u32: Distribution, + u32: Distribution, +{ +} + +trait Trait { + type Item; +} + +impl Trait for Option +where + u32: Distribution, +{ + type Item = T; +} + +fn random_paulis() -> impl Trait { + None +} + +fn main() {} diff --git a/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.rs b/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.rs new file mode 100644 index 0000000000000..58bc4daedfcc1 --- /dev/null +++ b/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.rs @@ -0,0 +1,21 @@ +// Demonstrate that generic const arguments in GAT constraints are rejected at +// the definition site of an eager type alias. + +//@ compile-flags: -Znext-solver=globally + +#![feature(generic_const_args, min_generic_const_args)] +#![expect(incomplete_features)] + +// * dyn incompatible due to GAT +// * `'a: 'static`, `String: Copy` and `[u8]: Sized` unsatisfied, `loop {}` diverging +type Several<'a> = dyn HasGenericAssocType = [u8]>; +//~^ ERROR + +trait HasGenericAssocType { + type Type<'a: 'static, T: Copy, const N: usize>; +} + +fn main() { + let _: &Several<'_>; + //~^ ERROR the trait `HasGenericAssocType` is not dyn compatible +} diff --git a/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.stderr b/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.stderr new file mode 100644 index 0000000000000..6b06ba9cb14fe --- /dev/null +++ b/tests/ui/type-alias/lack-of-wfcheck-gat-generic-const-args.stderr @@ -0,0 +1,34 @@ +error: constant evaluation is taking a long time + --> $DIR/lack-of-wfcheck-gat-generic-const-args.rs:11:63 + | +LL | type Several<'a> = dyn HasGenericAssocType = [u8]>; + | ^^^^^^^ + | + = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. + If your compilation actually takes a long time, you can safely allow the lint +help: the constant being evaluated + --> $DIR/lack-of-wfcheck-gat-generic-const-args.rs:11:61 + | +LL | type Several<'a> = dyn HasGenericAssocType = [u8]>; + | ^^^^^^^^^^^ + = note: `#[deny(long_running_const_eval)]` on by default + +error[E0038]: the trait `HasGenericAssocType` is not dyn compatible + --> $DIR/lack-of-wfcheck-gat-generic-const-args.rs:19:12 + | +LL | let _: &Several<'_>; + | ^^^^^^^^^^^^ `HasGenericAssocType` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/lack-of-wfcheck-gat-generic-const-args.rs:15:10 + | +LL | trait HasGenericAssocType { + | ------------------- this trait is not dyn compatible... +LL | type Type<'a: 'static, T: Copy, const N: usize>; + | ^^^^ ...because it contains generic associated type `Type` + = help: consider moving `Type` to another trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.gca.stderr b/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.gca.stderr new file mode 100644 index 0000000000000..52edd50aaaaad --- /dev/null +++ b/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.gca.stderr @@ -0,0 +1,17 @@ +error[E0191]: the value of the associated constant `N` in `HasAssocConst` must be specified + --> $DIR/lack-of-wfcheck-generic-const-args.rs:19:25 + | +LL | type DynIncompat1 = dyn HasAssocConst; + | ^^^^^^^^^^^^^ +... +LL | const N: usize; + | -------------- `N` defined here + | +help: specify the associated constant + | +LL | type DynIncompat1 = dyn HasAssocConst; + | +++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0191`. diff --git a/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.rs b/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.rs new file mode 100644 index 0000000000000..afca550944ffc --- /dev/null +++ b/tests/ui/type-alias/lack-of-wfcheck-generic-const-args.rs @@ -0,0 +1,26 @@ +// Demonstrate that generic_const_args changes the behavior for dyn trait aliases +// with non-type associated consts: the associated const must be specified. + +//@ revisions: no_gca gca +//@ compile-flags: -Znext-solver=globally +//@ [no_gca] check-pass + +#![cfg_attr(gca, feature(generic_const_args, min_generic_const_args))] +#![cfg_attr(gca, expect(incomplete_features))] + +type UnsatTraitBound0 = [str]; // `str: Sized` unsatisfied +type UnsatTraitBound1> = T; // `str: Sized` unsatisfied +type UnsatOutlivesBound<'a> = &'static &'a (); // `'a: 'static` unsatisfied + +type Diverging = [(); panic!()]; // `panic!()` diverging + +type DynIncompat0 = dyn Sized; // `Sized` axiomatically dyn incompatible +// issue: +type DynIncompat1 = dyn HasAssocConst; +//[gca]~^ ERROR the value of the associated constant `N` in `HasAssocConst` must be specified + +trait HasAssocConst { + const N: usize; +} + +fn main() {}