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_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c22b517b3ddf9..d1368d8f9f633 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1531,7 +1531,10 @@ impl Visitor<'_> for AstValidator<'_> { if &Safety::Default == safety { if item.span.at_least_rust_2024() { - self.dcx().emit_err(diagnostics::MissingUnsafeOnExtern { span: item.span }); + self.dcx().emit_err(diagnostics::MissingUnsafeOnExtern { + span: item.span, + unsafe_span: item.span.shrink_to_lo(), + }); } else { self.lint_buffer.buffer_lint( MISSING_UNSAFE_ON_EXTERN, diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index db006e50aaa31..0814c79d339bd 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -737,6 +737,13 @@ pub(crate) struct UnsafeItem { pub(crate) struct MissingUnsafeOnExtern { #[primary_span] pub span: Span, + + #[suggestion( + "needs `unsafe` before the extern keyword", + code = "unsafe ", + applicability = "machine-applicable" + )] + pub unsafe_span: Span, } #[derive(Diagnostic)] 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_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index e9c1c1d57b936..3cbbc931d11e4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -18,7 +18,7 @@ use rustc_middle::mir::{ LocalKind, Location, Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfoContents, find_self_call, }; -use rustc_middle::ty::print::Print; +use rustc_middle::ty::print::{Print, with_no_trimmed_paths}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex}; @@ -1374,12 +1374,21 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { &move_spans, ); - let func = tcx.def_path_str(method_did); - err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { - func, - place_name: place_name.clone(), - span: self_arg.span, - }); + let func = with_no_trimmed_paths!(tcx.def_path_str(method_did)); + if let Some((kind, _)) = desugaring { + err.subdiagnostic(CaptureReasonNote::DesugaringFuncTakeSelf { + func, + desugar_name: kind.name(), + place_name: place_name.clone(), + span: self_arg.span, + }); + } else { + err.subdiagnostic(CaptureReasonNote::FuncTakeSelf { + func, + place_name: place_name.clone(), + span: self_arg.span, + }); + } } let parent_did = tcx.parent(method_did); let parent_self_ty = @@ -1400,17 +1409,20 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { var_span: var_span.shrink_to_hi(), }); } - if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring { + if let Some(( + kind @ (CallDesugaringKind::ForLoopIntoIter + | CallDesugaringKind::ForLoopIntoAsyncIter), + _, + )) = desugaring + { let ty = moved_place.ty(self.body, tcx).ty; - let suggest = match tcx.get_diagnostic_item(sym::IntoIterator) { - Some(def_id) => type_known_to_meet_bound_modulo_regions( - self.infcx, - self.infcx.param_env, - Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty), - def_id, - ), - _ => false, - }; + let def_id = kind.trait_def_id(tcx); + let suggest = type_known_to_meet_bound_modulo_regions( + self.infcx, + self.infcx.param_env, + Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty), + def_id, + ); if suggest { err.subdiagnostic(CaptureReasonSuggest::IterateSlice { ty, @@ -1418,12 +1430,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { }); } - err.subdiagnostic(CaptureReasonLabel::ImplicitCall { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }); + match kind { + CallDesugaringKind::ForLoopIntoIter => { + err.subdiagnostic(CaptureReasonLabel::ImplicitCall { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); + } + CallDesugaringKind::ForLoopIntoAsyncIter => { + err.subdiagnostic(CaptureReasonLabel::ImplicitAsyncCall { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); + } + _ => {} + } // If the moved place was a `&mut` ref, then we can // suggest to reborrow it where it was moved, so it // will still be valid by the time we get to the usage. @@ -1451,20 +1476,31 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } } else { - if let Some((CallDesugaringKind::Await, _)) = desugaring { - err.subdiagnostic(CaptureReasonLabel::Await { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }); - } else { - err.subdiagnostic(CaptureReasonLabel::MethodCall { - fn_call_span, - place_name: &place_name, - is_partial, - is_loop_message, - }); + match desugaring { + Some((CallDesugaringKind::Await, _)) => { + err.subdiagnostic(CaptureReasonLabel::Await { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); + } + Some((CallDesugaringKind::QuestionBranch, _)) => { + err.subdiagnostic(CaptureReasonLabel::QuestionMark { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); + } + _ => { + err.subdiagnostic(CaptureReasonLabel::MethodCall { + fn_call_span, + place_name: &place_name, + is_partial, + is_loop_message, + }); + } } // Erase and shadow everything that could be passed to the new infcx. let ty = moved_place.ty(self.body, tcx).ty; diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index cd4d1f16b21af..a0f347679b05a 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -389,6 +389,22 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to the question mark {$is_loop_message -> + [true] operator, in previous iteration of loop + *[false] operator + }" + )] + QuestionMark { + #[primary_span] + fn_call_span: Span, + place_name: &'a str, + is_partial: bool, + is_loop_message: bool, + }, #[label( "{$place_name} {$is_partial -> [true] partially moved @@ -405,6 +421,22 @@ pub(crate) enum CaptureReasonLabel<'a> { is_partial: bool, is_loop_message: bool, }, + #[label( + "{$place_name} {$is_partial -> + [true] partially moved + *[false] moved + } due to this implicit call to {$is_loop_message -> + [true] `.into_async_iter()`, in previous iteration of loop + *[false] `.into_async_iter()` + }" + )] + ImplicitAsyncCall { + #[primary_span] + fn_call_span: Span, + place_name: &'a str, + is_partial: bool, + is_loop_message: bool, + }, #[label( "{$place_name} {$is_partial -> [true] partially moved @@ -498,6 +530,17 @@ pub(crate) enum CaptureReasonNote { #[primary_span] span: Span, }, + #[note( + "the {$desugar_name} is desugared into a call to `{$func}`, which takes ownership of the \ + receiver `self`, which moves {$place_name}" + )] + DesugaringFuncTakeSelf { + desugar_name: &'static str, + func: String, + place_name: String, + #[primary_span] + span: Span, + }, } #[derive(Subdiagnostic)] 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/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 15e800666129a..76c0c5f0d3bdf 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -247,7 +247,9 @@ fn build_error_for_const_call<'tcx>( // Don't point at the trait if this is a desugaring... // FIXME(const_trait_impl): we could perhaps do this for `Iterator`. match kind { - CallDesugaringKind::ForLoopIntoIter | CallDesugaringKind::ForLoopNext => { + CallDesugaringKind::ForLoopIntoIter + | CallDesugaringKind::ForLoopIntoAsyncIter + | CallDesugaringKind::ForLoopNext => { error!(NonConstForLoopIntoIter) } CallDesugaringKind::QuestionBranch => { 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_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..8d6661ca1194b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -238,6 +238,7 @@ symbols! { Input, Int, Into, + IntoAsyncIterator, IntoFuture, IntoIterator, IntoIteratorItem, @@ -1872,6 +1873,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 +2257,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/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 60ae0cd644460..bdd22f89923f9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -17,6 +17,8 @@ use crate::traits::specialization_graph; pub enum CallDesugaringKind { /// for _ in x {} calls x.into_iter() ForLoopIntoIter, + /// for await _ in x {} calls x.into_async_iter() + ForLoopIntoAsyncIter, /// for _ in x {} calls iter.next() ForLoopNext, /// x? calls x.branch() @@ -30,9 +32,22 @@ pub enum CallDesugaringKind { } impl CallDesugaringKind { + pub fn name(&self) -> &'static str { + match self { + CallDesugaringKind::ForLoopIntoIter => "`for` loop", + CallDesugaringKind::ForLoopIntoAsyncIter => "`for await` loop", + CallDesugaringKind::ForLoopNext => "`for` loop", + CallDesugaringKind::QuestionBranch => "question mark operator", + CallDesugaringKind::QuestionFromResidual => "question mark operator", + CallDesugaringKind::TryBlockFromOutput => "try block", + CallDesugaringKind::Await => "`await`", + } + } + pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId { match self { Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(), + Self::ForLoopIntoAsyncIter => tcx.get_diagnostic_item(sym::IntoAsyncIterator).unwrap(), Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, DUMMY_SP), Self::QuestionBranch | Self::TryBlockFromOutput => { tcx.require_lang_item(LangItem::Try, DUMMY_SP) @@ -136,6 +151,10 @@ pub fn call_kind<'tcx>( && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) { Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0))) + } else if tcx.is_lang_item(method_did, LangItem::IntoAsyncIterIntoIter) + && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) + { + Some((CallDesugaringKind::ForLoopIntoAsyncIter, method_args.type_at(0))) } else if tcx.is_lang_item(method_did, LangItem::IteratorNext) && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop) { 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/async_iter/async_iter.rs b/library/core/src/async_iter/async_iter.rs index c21c08320bef6..b9e1065e48807 100644 --- a/library/core/src/async_iter/async_iter.rs +++ b/library/core/src/async_iter/async_iter.rs @@ -141,6 +141,7 @@ impl Poll> { /// Converts something into an async iterator #[unstable(feature = "async_iterator", issue = "79024")] +#[rustc_diagnostic_item = "IntoAsyncIterator"] pub trait IntoAsyncIterator { /// The type of the item yielded by the iterator type Item; 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/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/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/async-await/async-closures/move-consuming-capture.stderr b/tests/ui/async-await/async-closures/move-consuming-capture.stderr index e28716ca213b3..a6526981c87c1 100644 --- a/tests/ui/async-await/async-closures/move-consuming-capture.stderr +++ b/tests/ui/async-await/async-closures/move-consuming-capture.stderr @@ -9,7 +9,7 @@ LL | x().await; LL | x().await; | ^ value used here after move | -note: `async_call_once` takes ownership of the receiver `self`, which moves `x` +note: `std::ops::AsyncFnOnce::async_call_once` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied | diff --git a/tests/ui/async-await/clone-suggestion.stderr b/tests/ui/async-await/clone-suggestion.stderr index 3374068ed3f60..7220fb7dd8f05 100644 --- a/tests/ui/async-await/clone-suggestion.stderr +++ b/tests/ui/async-await/clone-suggestion.stderr @@ -8,7 +8,7 @@ LL | f.await; LL | f.await; | ^ value used here after move | -note: `into_future` takes ownership of the receiver `self`, which moves `f` +note: the `await` is desugared into a call to `std::future::IntoFuture::into_future`, which takes ownership of the receiver `self`, which moves `f` --> $SRC_DIR/core/src/future/into_future.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | diff --git a/tests/ui/async-await/for-await-consumes-iter.stderr b/tests/ui/async-await/for-await-consumes-iter.stderr index a3e5bbcabf5d0..c71ebbcc11c97 100644 --- a/tests/ui/async-await/for-await-consumes-iter.stderr +++ b/tests/ui/async-await/for-await-consumes-iter.stderr @@ -5,17 +5,13 @@ LL | let iter = core::async_iter::from_iter(0..3); | ---- move occurs because `iter` has type `FromIter>`, which does not implement the `Copy` trait LL | let mut count = 0; LL | for await i in iter { - | ---- `iter` moved due to this method call + | ---- `iter` moved due to this implicit call to `.into_async_iter()` ... LL | for await i in iter { | ^^^^ value used here after move | -note: `into_async_iter` takes ownership of the receiver `self`, which moves `iter` +note: the `for await` loop is desugared into a call to `std::async_iter::IntoAsyncIterator::into_async_iter`, which takes ownership of the receiver `self`, which moves `iter` --> $SRC_DIR/core/src/async_iter/async_iter.rs:LL:COL -help: you can `clone` the value and consume it, but this might not be your desired behavior - | -LL | for await i in iter.clone() { - | ++++++++ error: aborting due to 1 previous error 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/borrowck/borrow-of-moved-value-in-for-loop-61108.stderr b/tests/ui/borrowck/borrow-of-moved-value-in-for-loop-61108.stderr index 4c3fa5e56dc9b..48a23380d9ba6 100644 --- a/tests/ui/borrowck/borrow-of-moved-value-in-for-loop-61108.stderr +++ b/tests/ui/borrowck/borrow-of-moved-value-in-for-loop-61108.stderr @@ -9,7 +9,7 @@ LL | for l in bad_letters { LL | bad_letters.push('s'); | ^^^^^^^^^^^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `bad_letters` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `bad_letters` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | diff --git a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr index 076f0ce3440a0..bec5427a1d428 100644 --- a/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr +++ b/tests/ui/borrowck/borrowck-move-out-of-overloaded-auto-deref.stderr @@ -6,7 +6,7 @@ LL | let _x = Rc::new(vec![1, 2]).into_iter(); | | | move occurs because value has type `Vec`, which does not implement the `Copy` trait | -note: `into_iter` takes ownership of the receiver `self`, which moves value +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | diff --git a/tests/ui/borrowck/fn-closure-move-capture-no-reborrow-sugg.stderr b/tests/ui/borrowck/fn-closure-move-capture-no-reborrow-sugg.stderr index e31bae3a4ddf6..f92951313f2a4 100644 --- a/tests/ui/borrowck/fn-closure-move-capture-no-reborrow-sugg.stderr +++ b/tests/ui/borrowck/fn-closure-move-capture-no-reborrow-sugg.stderr @@ -18,7 +18,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume | LL | pub fn in_fn Result<(), ()>>(f: F) -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^ -note: `into_iter` takes ownership of the receiver `self`, which moves `foos` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `foos` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL error[E0507]: cannot move out of `foos`, a captured variable in an `FnMut` closure @@ -41,7 +41,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume | LL | pub fn in_fn_mut Result<(), ()>>(mut f: F) -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: `into_iter` takes ownership of the receiver `self`, which moves `foos` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `foos` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider creating a fresh reborrow of `foos` here | diff --git a/tests/ui/borrowck/issue-83760.stderr b/tests/ui/borrowck/issue-83760.stderr index d120adbc03bb3..a45146a565022 100644 --- a/tests/ui/borrowck/issue-83760.stderr +++ b/tests/ui/borrowck/issue-83760.stderr @@ -27,7 +27,7 @@ LL | foo = Some(Struct); LL | let _y = foo; | ^^^ value used here after move | -note: `Option::::unwrap` takes ownership of the receiver `self`, which moves `foo` +note: `std::option::Option::::unwrap` takes ownership of the receiver `self`, which moves `foo` --> $SRC_DIR/core/src/option.rs:LL:COL help: you could `clone` the value and consume it, if the `Struct: Clone` trait bound could be satisfied | @@ -61,7 +61,7 @@ LL | foo = Some(Struct2); LL | } else if true { LL | foo = Some(Struct2); | ^^^^^^^^^^^^^^^^^^^ -note: `Option::::unwrap` takes ownership of the receiver `self`, which moves `foo` +note: `std::option::Option::::unwrap` takes ownership of the receiver `self`, which moves `foo` --> $SRC_DIR/core/src/option.rs:LL:COL help: you could `clone` the value and consume it, if the `Struct2: Clone` trait bound could be satisfied | diff --git a/tests/ui/borrowck/issue-83924.stderr b/tests/ui/borrowck/issue-83924.stderr index c37de178f2499..8844766397ef7 100644 --- a/tests/ui/borrowck/issue-83924.stderr +++ b/tests/ui/borrowck/issue-83924.stderr @@ -10,7 +10,7 @@ LL | for n in v { LL | for n in v { | ^ value used here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `v` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `v` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider creating a fresh reborrow of `v` here | diff --git a/tests/ui/borrowck/moved-into-question-mark.rs b/tests/ui/borrowck/moved-into-question-mark.rs new file mode 100644 index 0000000000000..d09b7b8f6f42d --- /dev/null +++ b/tests/ui/borrowck/moved-into-question-mark.rs @@ -0,0 +1,17 @@ +// https://github.com/rust-lang/rust/issues/89567 +use std::fs; +use std::io; + +fn main() -> io::Result<()> { + for entry in fs::read_dir(".")? { + //~^ NOTE move occurs because `entry` has type `Result + let file_type = entry?.file_type()?; + //~^ NOTE `entry` moved due to the question mark operator + if file_type.is_dir() { + dbg!(entry?.file_name()); //~ ERROR use of moved value + //~^ NOTE value used here after move + //~| NOTE the question mark operator is desugared into a call to `std::ops::Try::branch` + } + } + Ok(()) +} diff --git a/tests/ui/borrowck/moved-into-question-mark.stderr b/tests/ui/borrowck/moved-into-question-mark.stderr new file mode 100644 index 0000000000000..5febf1b73b473 --- /dev/null +++ b/tests/ui/borrowck/moved-into-question-mark.stderr @@ -0,0 +1,22 @@ +error[E0382]: use of moved value: `entry` + --> $DIR/moved-into-question-mark.rs:11:18 + | +LL | for entry in fs::read_dir(".")? { + | ----- move occurs because `entry` has type `Result`, which does not implement the `Copy` trait +LL | +LL | let file_type = entry?.file_type()?; + | ------ `entry` moved due to the question mark operator +... +LL | dbg!(entry?.file_name()); + | ^^^^^ value used here after move + | +note: the question mark operator is desugared into a call to `std::ops::Try::branch`, which takes ownership of the receiver `self`, which moves `entry` + --> $SRC_DIR/core/src/ops/try_trait.rs:LL:COL +help: you could `clone` the value and consume it, if the following trait bounds could be satisfied: `DirEntry: Clone` and `std::io::Error: Clone` + | +LL | let file_type = entry.clone()?.file_type()?; + | ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/borrowck/moved-value-in-closure-suggestion-64559.stderr b/tests/ui/borrowck/moved-value-in-closure-suggestion-64559.stderr index 09d4d04295c7c..557912d0e12dc 100644 --- a/tests/ui/borrowck/moved-value-in-closure-suggestion-64559.stderr +++ b/tests/ui/borrowck/moved-value-in-closure-suggestion-64559.stderr @@ -10,7 +10,7 @@ LL | let _closure = || orig; | | | value used here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `orig` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `orig` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | diff --git a/tests/ui/borrowck/reborrow-sugg-move-then-borrow.stderr b/tests/ui/borrowck/reborrow-sugg-move-then-borrow.stderr index 8590dd9ca3d07..4cbf323a14662 100644 --- a/tests/ui/borrowck/reborrow-sugg-move-then-borrow.stderr +++ b/tests/ui/borrowck/reborrow-sugg-move-then-borrow.stderr @@ -9,7 +9,7 @@ LL | LL | fill_segment(state); | ^^^^^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `state` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `state` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider creating a fresh reborrow of `state` here | diff --git a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr index e2199fa90f263..9435abb7579fe 100644 --- a/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr +++ b/tests/ui/borrowck/suggest-as-ref-on-mut-closure.stderr @@ -6,7 +6,7 @@ LL | cb.map(|cb| cb()); | | | move occurs because `*cb` has type `Option<&mut dyn FnMut()>`, which does not implement the `Copy` trait | -note: `Option::::map` takes ownership of the receiver `self`, which moves `*cb` +note: `std::option::Option::::map` takes ownership of the receiver `self`, which moves `*cb` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider calling `.as_ref()` to borrow the value's contents | diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 08cf528839834..b44ae3b444ec4 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -15,7 +15,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume | LL | fn call(f: F) where F : Fn() { | ^^^^ -note: `into_iter` takes ownership of the receiver `self`, which moves `y` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider cloning the value if the performance cost is acceptable | diff --git a/tests/ui/codemap_tests/tab_3.stderr b/tests/ui/codemap_tests/tab_3.stderr index 2a0a9e2d48f31..757c92ba9298b 100644 --- a/tests/ui/codemap_tests/tab_3.stderr +++ b/tests/ui/codemap_tests/tab_3.stderr @@ -9,7 +9,7 @@ LL | { LL | println!("{:?}", some_vec); | ^^^^^^^^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `some_vec` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `some_vec` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | 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/errors/remap-path-prefix-sysroot.with-remap.stderr b/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr index 8540f492a662a..558a3d8e34b8b 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr +++ b/tests/ui/errors/remap-path-prefix-sysroot.with-remap.stderr @@ -6,7 +6,7 @@ LL | self.thread.join().unwrap(); | | | move occurs because `self.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait | -note: `JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` +note: `std::thread::JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` --> remapped/library/std/src/thread/join_handle.rs:LL:COL | LL | pub fn join(self) -> Result { diff --git a/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr b/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr index 91a3d5b5d6c8a..28c409a50ca7c 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr +++ b/tests/ui/errors/remap-path-prefix-sysroot.without-remap.stderr @@ -6,7 +6,7 @@ LL | self.thread.join().unwrap(); | | | move occurs because `self.thread` has type `JoinHandle<()>`, which does not implement the `Copy` trait | -note: `JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` +note: `std::thread::JoinHandle::::join` takes ownership of the receiver `self`, which moves `self.thread` --> $SRC_DIR_REAL/std/src/thread/join_handle.rs:LL:COL | LL | pub fn join(self) -> Result { 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/loops/issue-82916.stderr b/tests/ui/loops/issue-82916.stderr index 5a5e9c4f0bbeb..0a635836c800c 100644 --- a/tests/ui/loops/issue-82916.stderr +++ b/tests/ui/loops/issue-82916.stderr @@ -9,7 +9,7 @@ LL | for y in x { LL | let z = x; | ^ value used here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `x` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | diff --git a/tests/ui/moves/move-fn-self-receiver.stderr b/tests/ui/moves/move-fn-self-receiver.stderr index 40a82523c840c..1146828d9c421 100644 --- a/tests/ui/moves/move-fn-self-receiver.stderr +++ b/tests/ui/moves/move-fn-self-receiver.stderr @@ -6,7 +6,7 @@ LL | val.0.into_iter().next(); LL | val.0; | ^^^^^ value used here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `val.0` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `val.0` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL = note: move occurs because `val.0` has type `Vec`, which does not implement the `Copy` trait help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/moves/moves-based-on-type-access-to-field.stderr b/tests/ui/moves/moves-based-on-type-access-to-field.stderr index 1e656e686fd87..ff2634114e9f0 100644 --- a/tests/ui/moves/moves-based-on-type-access-to-field.stderr +++ b/tests/ui/moves/moves-based-on-type-access-to-field.stderr @@ -8,7 +8,7 @@ LL | consume(x.into_iter().next().unwrap()); LL | touch(&x[0]); | ^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `x` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | diff --git a/tests/ui/moves/moves-based-on-type-exprs.stderr b/tests/ui/moves/moves-based-on-type-exprs.stderr index 45f7d4063a593..bdb81d65a4595 100644 --- a/tests/ui/moves/moves-based-on-type-exprs.stderr +++ b/tests/ui/moves/moves-based-on-type-exprs.stderr @@ -160,7 +160,7 @@ LL | let _y = x.into_iter().next().unwrap(); LL | touch(&x); | ^^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `x` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | @@ -177,7 +177,7 @@ LL | let _y = [x.into_iter().next().unwrap(); 1]; LL | touch(&x); | ^^ value borrowed here after move | -note: `into_iter` takes ownership of the receiver `self`, which moves `x` +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | diff --git a/tests/ui/moves/needs-clone-through-deref.stderr b/tests/ui/moves/needs-clone-through-deref.stderr index 9890ad480a6f0..bcd4b807f91ac 100644 --- a/tests/ui/moves/needs-clone-through-deref.stderr +++ b/tests/ui/moves/needs-clone-through-deref.stderr @@ -6,7 +6,7 @@ LL | for _ in self.clone().into_iter() {} | | | move occurs because value has type `Vec`, which does not implement the `Copy` trait | -note: `into_iter` takes ownership of the receiver `self`, which moves value +note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | diff --git a/tests/ui/moves/pin-mut-reborrow-infer-var-issue-107419.stderr b/tests/ui/moves/pin-mut-reborrow-infer-var-issue-107419.stderr index bb179e1497b4e..4296dfd2b246e 100644 --- a/tests/ui/moves/pin-mut-reborrow-infer-var-issue-107419.stderr +++ b/tests/ui/moves/pin-mut-reborrow-infer-var-issue-107419.stderr @@ -8,7 +8,7 @@ LL | foo(r.get_mut()); LL | foo(r.get_mut()); | ^ value used here after move | -note: `Pin::<&'a mut T>::get_mut` takes ownership of the receiver `self`, which moves `r` +note: `std::pin::Pin::<&'a mut T>::get_mut` takes ownership of the receiver `self`, which moves `r` --> $SRC_DIR/core/src/pin.rs:LL:COL help: consider reborrowing the `Pin` instead of moving it | diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr index af0f67b7c1c07..c5cb7bf403d5b 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr @@ -6,7 +6,7 @@ LL | let mut copy: Vec = map.clone().into_values().collect(); | | | move occurs because value has type `HashMap`, which does not implement the `Copy` trait | -note: `HashMap::::into_values` takes ownership of the receiver `self`, which moves value +note: `std::collections::HashMap::::into_values` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL note: if `Hash128_1` implemented `Clone`, you could clone the value --> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:8:1 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/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr index 17b49d8ed5c36..16b3af0feadeb 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr +++ b/tests/ui/rust-2024/unsafe-extern-blocks/extern-items.edition2024.stderr @@ -1,7 +1,11 @@ error: extern blocks must be unsafe --> $DIR/extern-items.rs:6:1 | -LL | / extern "C" { +LL | extern "C" { + | ^ + | | + | _help: needs `unsafe` before the extern keyword: `unsafe` + | | LL | | LL | | static TEST1: i32; LL | | fn test1(i: i32); diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr index 874b32346af17..91f9a8611e508 100644 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr +++ b/tests/ui/rust-2024/unsafe-extern-blocks/safe-unsafe-on-unadorned-extern-block.edition2024.stderr @@ -1,7 +1,11 @@ error: extern blocks must be unsafe --> $DIR/safe-unsafe-on-unadorned-extern-block.rs:5:1 | -LL | / extern "C" { +LL | extern "C" { + | ^ + | | + | _help: needs `unsafe` before the extern keyword: `unsafe` + | | LL | | LL | | safe static TEST1: i32; ... | diff --git a/tests/ui/suggestions/as-ref-2.stderr b/tests/ui/suggestions/as-ref-2.stderr index b183c8b2faee4..54f06ff2db486 100644 --- a/tests/ui/suggestions/as-ref-2.stderr +++ b/tests/ui/suggestions/as-ref-2.stderr @@ -8,7 +8,7 @@ LL | let _x: Option = foo.map(|s| bar(&s)); LL | let _y = foo; | ^^^ value used here after move | -note: `Option::::map` takes ownership of the receiver `self`, which moves `foo` +note: `std::option::Option::::map` takes ownership of the receiver `self`, which moves `foo` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider calling `.as_ref()` to borrow the value's contents | diff --git a/tests/ui/suggestions/borrow-for-loop-head.stderr b/tests/ui/suggestions/borrow-for-loop-head.stderr index 55fcb44168c49..de425abe85ae5 100644 --- a/tests/ui/suggestions/borrow-for-loop-head.stderr +++ b/tests/ui/suggestions/borrow-for-loop-head.stderr @@ -23,7 +23,7 @@ LL | for i in &a { LL | for j in a { | ^ `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop | -note: `into_iter` takes ownership of the receiver `self`, which moves `a` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `a` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | diff --git a/tests/ui/suggestions/for-i-in-vec.stderr b/tests/ui/suggestions/for-i-in-vec.stderr index 64eb4f8bd23ba..ebb463c002679 100644 --- a/tests/ui/suggestions/for-i-in-vec.stderr +++ b/tests/ui/suggestions/for-i-in-vec.stderr @@ -7,7 +7,7 @@ LL | for _ in self.v { | `self.v` moved due to this implicit call to `.into_iter()` | move occurs because `self.v` has type `Vec`, which does not implement the `Copy` trait | -note: `into_iter` takes ownership of the receiver `self`, which moves `self.v` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `self.v` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | @@ -45,7 +45,7 @@ LL | for loader in *LOADERS { | value moved due to this implicit call to `.into_iter()` | move occurs because value has type `Vec<&u8>`, which does not implement the `Copy` trait | -note: `into_iter` takes ownership of the receiver `self`, which moves value +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves value --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: consider iterating over a slice of the `Vec<&u8>`'s content to avoid moving into the `for` loop | diff --git a/tests/ui/suggestions/issue-102972.stderr b/tests/ui/suggestions/issue-102972.stderr index 438f28ad03264..501a5c9efd35e 100644 --- a/tests/ui/suggestions/issue-102972.stderr +++ b/tests/ui/suggestions/issue-102972.stderr @@ -28,7 +28,7 @@ LL | iter.next(); | ^^^^ value borrowed here after move | = note: a for loop advances the iterator for you, the result is stored in `_i` -note: `into_iter` takes ownership of the receiver `self`, which moves `iter` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `iter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: if you want to call `next` on a iterator within the loop, consider using `while let` | @@ -66,7 +66,7 @@ LL | iter.next(); | ^^^^ value borrowed here after move | = note: a for loop advances the iterator for you, the result is stored in its pattern -note: `into_iter` takes ownership of the receiver `self`, which moves `iter` +note: the `for` loop is desugared into a call to `std::iter::IntoIterator::into_iter`, which takes ownership of the receiver `self`, which moves `iter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: if you want to call `next` on a iterator within the loop, consider using `while let` | diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr index b514622699e73..163c408d219c5 100644 --- a/tests/ui/suggestions/option-content-move.stderr +++ b/tests/ui/suggestions/option-content-move.stderr @@ -6,7 +6,7 @@ LL | if selection.1.unwrap().contains(selection.0) { | | | move occurs because `selection.1` has type `Option`, which does not implement the `Copy` trait | -note: `Option::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` +note: `std::option::Option::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider calling `.as_ref()` to borrow the value's contents | @@ -29,7 +29,7 @@ LL | if selection.1.unwrap().contains(selection.0) { | | | move occurs because `selection.1` has type `Result`, which does not implement the `Copy` trait | -note: `Result::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` +note: `std::result::Result::::unwrap` takes ownership of the receiver `self`, which moves `selection.1` --> $SRC_DIR/core/src/result.rs:LL:COL help: consider calling `.as_ref()` to borrow the value's contents | 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() {}