diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 9bfcd232221b..e6e88eff2d5b 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -96,7 +96,7 @@ pub(crate) fn lower_delegation( let generics = self.lower_delegation_generics(span); DelegationResults { body_id, sig, ident, generics } } - Err(err) => self.generate_delegation_error(err, span), + Err(err) => self.generate_delegation_error(err, span, delegation), } } @@ -404,6 +404,7 @@ fn generate_delegation_error( &mut self, err: ErrorGuaranteed, span: Span, + delegation: &Delegation, ) -> DelegationResults<'hir> { let generics = self.lower_delegation_generics(span); @@ -418,8 +419,41 @@ fn generate_delegation_error( let header = self.generate_header_error(); let sig = hir::FnSig { decl, header, span }; - let ident = Ident::dummy(); - let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span))); + let ident = self.lower_ident(delegation.ident); + + let body_id = self.lower_body(|this| { + let body_expr = match delegation.body.as_ref() { + Some(box block) => { + // Generates a block when we failed to resolve delegation, where a target expression is its only statement, + // thus there will be no ICEs on further stages of analysis (see #144594) + + // As we generate a void function we want to convert target expression to statement to avoid additional + // errors, such as mismatched return type + let stmts = this.arena.alloc_from_iter([hir::Stmt { + hir_id: this.next_id(), + kind: rustc_hir::StmtKind::Semi( + this.arena.alloc(this.lower_target_expr(block)), + ), + span, + }]); + + let block = this.arena.alloc(hir::Block { + stmts, + expr: None, + hir_id: this.next_id(), + rules: hir::BlockCheckMode::DefaultBlock, + span, + targeted_by_break: false, + }); + + hir::ExprKind::Block(block, None) + } + None => hir::ExprKind::Err(err), + }; + + (&[], this.mk_expr(body_expr, span)) + }); + DelegationResults { ident, generics, body_id, sig } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a8927c10297..f8a6fafbe78a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3940,13 +3940,30 @@ pub(crate) fn report_illegal_reassignment( if let Some(decl) = local_decl && decl.can_be_made_mutable() { + let is_for_loop = matches!( + decl.local_info(), + LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((_, match_span)), + .. + })) if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) + ); + let message = if is_for_loop + && let Ok(binding_name) = + self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) + { + format!("(mut {}) ", binding_name) + } else { + "mut ".to_string() + }; err.span_suggestion_verbose( decl.source_info.span.shrink_to_lo(), "consider making this binding mutable", - "mut ".to_string(), + message, Applicability::MachineApplicable, ); + if !from_arg + && !is_for_loop && matches!( decl.local_info(), LocalInfo::User(BindingForm::Var(VarBindingForm { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 5f86e8646c03..43005fca2984 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1061,7 +1061,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { Rvalue::Cast(cast_kind, op, ty) => { match *cast_kind { - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, coercion_source) => { + CastKind::PointerCoercion( + PointerCoercion::ReifyFnPointer(target_safety), + coercion_source, + ) => { let is_implicit_coercion = coercion_source == CoercionSource::Implicit; let src_ty = op.ty(self.body, tcx); let mut src_sig = src_ty.fn_sig(tcx); @@ -1078,6 +1081,10 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { src_sig = safe_sig; } + if src_sig.safety().is_safe() && target_safety.is_unsafe() { + src_sig = tcx.safe_to_unsafe_sig(src_sig); + } + // HACK: This shouldn't be necessary... We can remove this when we actually // get binders with where clauses, then elaborate implied bounds into that // binder, and implement a higher-ranked subtyping algorithm that actually diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index e445f9457477..a0bee4e18214 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -689,7 +689,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: lval.write_cvalue(fx, res); } Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, to_ty, ) => { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c0c01b80372f..b131de1df8ba 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -75,7 +75,7 @@ fn write_output_file<'ll>( let result = unsafe { let pm = llvm::LLVMCreatePassManager(); llvm::LLVMAddAnalysisPasses(target, pm); - llvm::LLVMRustAddLibraryInfo(pm, m, no_builtins); + llvm::LLVMRustAddLibraryInfo(target, pm, m, no_builtins); llvm::LLVMRustWriteOutputFile( target, pm, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1b65a133d58c..8c0c0afcc1dd 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -257,6 +257,10 @@ fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) { } writeln!(out).unwrap(); } + PrintKind::BackendHasZstd => { + let has_zstd = llvm::LLVMRustLLVMHasZstdCompression(); + writeln!(out, "{has_zstd}").unwrap(); + } PrintKind::CodeModels => { writeln!(out, "Available code models:").unwrap(); for name in &["tiny", "small", "kernel", "medium", "large"] { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 53b9a2bda894..c5cbc92ae772 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2379,6 +2379,7 @@ pub(crate) fn LLVMRustCreateTargetMachine( ) -> *mut TargetMachine; pub(crate) fn LLVMRustAddLibraryInfo<'a>( + T: &TargetMachine, PM: &PassManager<'a>, M: &'a Module, DisableSimplifyLibCalls: bool, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 233f9e787398..de626d04e785 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -405,7 +405,7 @@ pub(crate) fn codegen_rvalue_operand( let lladdr = bx.ptrtoint(llptr, llcast_ty); OperandValue::Immediate(lladdr) } - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => { match *operand.layout.ty.kind() { ty::FnDef(def_id, args) => { let instance = ty::Instance::resolve_for_fn_ptr( diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index c5ecf43046c7..187e4b90656a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -36,10 +36,10 @@ fn type_checked_load( vtable_byte_offset: u64, typeid: Self::Metadata, ) -> Self::Value; - /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in + /// Trait method used to inject `va_start` on the "spoofed" `VaList` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value; - /// Trait method used to inject `va_end` on the "spoofed" `VaListImpl` before + /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. fn va_end(&mut self, val: Self::Value) -> Self::Value; } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 66a2afa0aa7d..78e4066ca910 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -627,7 +627,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { | PointerCoercion::ArrayToPointer | PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer(_), _, ), _, diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index cf5ee03bedae..3485a5c625ba 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -74,7 +74,7 @@ pub fn cast( bug!("{cast_kind:?} casts are for borrowck only, not runtime MIR"); } - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => { // All reifications must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, src.layout.ty)?; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fa5041daa69e..a7a3bbebed5f 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -40,18 +40,20 @@ pub(crate) enum MinMax { /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Minimum, - /// The IEEE-2008 `minNum` operation - see `f32::min` etc. + /// The IEEE-2008 `minNum` operation with the SNaN handling of the + /// IEEE-2019 `minimumNumber` operation - see `f32::min` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and if one argument is NaN, the other one is returned. - MinNum, + /// and if one argument is NaN (quiet or signaling), the other one is returned. + MinimumNumber, /// The IEEE-2019 `maximum` operation - see `f32::maximum` etc. /// In particular, `-0.0` is considered smaller than `+0.0` and /// if either input is NaN, the result is NaN. Maximum, - /// The IEEE-2008 `maxNum` operation - see `f32::max` etc. + /// The IEEE-2008 `maxNum` operation with the SNaN handling of the + /// IEEE-2019 `maximumNumber` operation - see `f32::max` etc. /// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic, - /// and if one argument is NaN, the other one is returned. - MaxNum, + /// and if one argument is NaN (quiet or signaling), the other one is returned. + MaximumNumber, } /// Directly returns an `Allocation` containing an absolute path representation of the given type. @@ -524,10 +526,18 @@ pub fn eval_intrinsic( self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } - sym::minnumf16 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf32 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf64 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, - sym::minnumf128 => self.float_minmax_intrinsic::(args, MinMax::MinNum, dest)?, + sym::minnumf16 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf32 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf64 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } + sym::minnumf128 => { + self.float_minmax_intrinsic::(args, MinMax::MinimumNumber, dest)? + } sym::minimumf16 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, sym::minimumf32 => { @@ -538,10 +548,18 @@ pub fn eval_intrinsic( } sym::minimumf128 => self.float_minmax_intrinsic::(args, MinMax::Minimum, dest)?, - sym::maxnumf16 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf32 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf64 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, - sym::maxnumf128 => self.float_minmax_intrinsic::(args, MinMax::MaxNum, dest)?, + sym::maxnumf16 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf32 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf64 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } + sym::maxnumf128 => { + self.float_minmax_intrinsic::(args, MinMax::MaximumNumber, dest)? + } sym::maximumf16 => self.float_minmax_intrinsic::(args, MinMax::Maximum, dest)?, sym::maximumf32 => { @@ -966,16 +984,16 @@ fn float_minmax( { let a: F = a.to_float()?; let b: F = b.to_float()?; - let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b { + let res = if matches!(op, MinMax::MinimumNumber | MinMax::MaximumNumber) && a == b { // They are definitely not NaN (those are never equal), but they could be `+0` and `-0`. // Let the machine decide which one to return. M::equal_float_min_max(self, a, b) } else { let result = match op { MinMax::Minimum => a.minimum(b), - MinMax::MinNum => a.min(b), + MinMax::MinimumNumber => a.min(b), MinMax::Maximum => a.maximum(b), - MinMax::MaxNum => a.max(b), + MinMax::MaximumNumber => a.max(b), }; self.adjust_nan(result, &[a, b]) }; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index bae423840ee1..20de47683122 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -202,8 +202,8 @@ enum Op { sym::simd_le => Op::MirOp(BinOp::Le), sym::simd_gt => Op::MirOp(BinOp::Gt), sym::simd_ge => Op::MirOp(BinOp::Ge), - sym::simd_fmax => Op::FMinMax(MinMax::MaxNum), - sym::simd_fmin => Op::FMinMax(MinMax::MinNum), + sym::simd_fmax => Op::FMinMax(MinMax::MaximumNumber), + sym::simd_fmin => Op::FMinMax(MinMax::MinimumNumber), sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add), sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub), sym::simd_arith_offset => Op::WrappingOffset, @@ -295,8 +295,8 @@ enum Op { sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor), sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr), sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd), - sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum), - sym::simd_reduce_min => Op::MinMax(MinMax::MinNum), + sym::simd_reduce_max => Op::MinMax(MinMax::MaximumNumber), + sym::simd_reduce_min => Op::MinMax(MinMax::MinimumNumber), _ => unreachable!(), }; @@ -320,8 +320,8 @@ enum Op { } else { // Just boring integers, no NaNs to worry about. let mirop = match mmop { - MinMax::MinNum | MinMax::Minimum => BinOp::Le, - MinMax::MaxNum | MinMax::Maximum => BinOp::Ge, + MinMax::MinimumNumber | MinMax::Minimum => BinOp::Le, + MinMax::MaximumNumber | MinMax::Maximum => BinOp::Ge, }; if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? { res diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9a3d7cc506cf..0853f638509f 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -799,6 +799,7 @@ fn print_crate_info( println_info!("{}", calling_conventions.join("\n")); } RelocationModels + | BackendHasZstd | CodeModels | TlsModels | TargetCPUs diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 4abbace05e7c..127965cb4b30 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -53,7 +53,7 @@ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; @@ -113,6 +113,17 @@ fn success<'tcx>( Ok(InferOk { value: (adj, target), obligations }) } +/// Whether to force a leak check to occur in `Coerce::unify_raw`. +/// Note that leak checks may still occur evn with `ForceLeakCheck::No`. +/// +/// FIXME: We may want to change type relations to always leak-check +/// after exiting a binder, at which point we will always do so and +/// no longer need to handle this explicitly +enum ForceLeakCheck { + Yes, + No, +} + impl<'f, 'tcx> Coerce<'f, 'tcx> { fn new( fcx: &'f FnCtxt<'f, 'tcx>, @@ -123,9 +134,16 @@ fn new( Coerce { fcx, cause, allow_two_phase, use_lub: false, coerce_never } } - fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { + fn unify_raw( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + leak_check: ForceLeakCheck, + ) -> InferResult<'tcx, Ty<'tcx>> { debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); - self.commit_if_ok(|_| { + self.commit_if_ok(|snapshot| { + let outer_universe = self.infcx.universe(); + let at = self.at(&self.cause, self.fcx.param_env); let res = if self.use_lub { @@ -138,7 +156,7 @@ fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { // In the new solver, lazy norm may allow us to shallowly equate // more types, but we emit possibly impossible-to-satisfy obligations. // Filter these cases out to make sure our coercion is more accurate. - match res { + let res = match res { Ok(InferOk { value, obligations }) if self.next_trait_solver() => { let ocx = ObligationCtxt::new(self); ocx.register_obligations(obligations); @@ -149,13 +167,32 @@ fn unify_raw(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { } } res => res, + }; + + // We leak check here mostly because lub operations are + // kind of scuffed around binders. Instead of computing an actual + // lub'd binder we instead: + // - Equate the binders + // - Return the lhs of the lub operation + // + // This may lead to incomplete type inference for the resulting type + // of a `match` or `if .. else`, etc. This is a backwards compat + // hazard for if/when we start handling `lub` more correctly. + // + // In order to actually ensure that equating the binders *does* + // result in equal binders, and that the lhs is actually a supertype + // of the rhs, we must perform a leak check here. + if matches!(leak_check, ForceLeakCheck::Yes) { + self.leak_check(outer_universe, Some(snapshot))?; } + + res }) } /// Unify two types (using sub or lub). - fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - self.unify_raw(a, b) + fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>, leak_check: ForceLeakCheck) -> CoerceResult<'tcx> { + self.unify_raw(a, b, leak_check) .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) } @@ -166,8 +203,9 @@ fn unify_and( b: Ty<'tcx>, adjustments: impl IntoIterator>, final_adjustment: Adjust, + leak_check: ForceLeakCheck, ) -> CoerceResult<'tcx> { - self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { + self.unify_raw(a, b, leak_check).and_then(|InferOk { value: ty, obligations }| { success( adjustments .into_iter() @@ -179,7 +217,7 @@ fn unify_and( }) } - #[instrument(skip(self))] + #[instrument(skip(self), ret)] fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { // First, remove any resolved type variables (at the top level, at least): let a = self.shallow_resolve(a); @@ -196,7 +234,7 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { ); } else { // Otherwise the only coercion we can do is unification. - return self.unify(a, b); + return self.unify(a, b, ForceLeakCheck::No); } } @@ -223,21 +261,20 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { } } - // Examine the supertype and consider type-specific coercions, such - // as auto-borrowing, coercing pointer mutability, a `dyn*` coercion, - // or pin-ergonomics. + // Examine the target type and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, or pin-ergonomics. match *b.kind() { ty::RawPtr(_, b_mutbl) => { - return self.coerce_raw_ptr(a, b, b_mutbl); + return self.coerce_to_raw_ptr(a, b, b_mutbl); } ty::Ref(r_b, _, mutbl_b) => { - return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); + return self.coerce_to_ref(a, b, r_b, mutbl_b); } ty::Adt(pin, _) if self.tcx.features().pin_ergonomics() && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { - let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b)); + let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b)); if pin_coerce.is_ok() { return pin_coerce; } @@ -257,17 +294,17 @@ fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { ty::FnPtr(a_sig_tys, a_hdr) => { // We permit coercion of fn pointers to drop the // unsafe qualifier. - self.coerce_from_fn_pointer(a_sig_tys.with(a_hdr), b) + self.coerce_from_fn_pointer(a, a_sig_tys.with(a_hdr), b) } - ty::Closure(closure_def_id_a, args_a) => { + ty::Closure(..) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(a, closure_def_id_a, args_a, b) + self.coerce_closure_to_fn(a, b) } _ => { // Otherwise, just use unification rules. - self.unify(a, b) + self.unify(a, b, ForceLeakCheck::No) } } } @@ -281,23 +318,29 @@ fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResu debug_assert!(self.shallow_resolve(b) == b); if b.is_ty_var() { - // Two unresolved type variables: create a `Coerce` predicate. - let target_ty = if self.use_lub { self.next_ty_var(self.cause.span) } else { b }; - let mut obligations = PredicateObligations::with_capacity(2); - for &source_ty in &[a, b] { - if source_ty != target_ty { - obligations.push(Obligation::new( - self.tcx(), - self.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { - a: source_ty, - b: target_ty, - })), - )); - } - } + let mut push_coerce_obligation = |a, b| { + obligations.push(Obligation::new( + self.tcx(), + self.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { a, b })), + )); + }; + + let target_ty = if self.use_lub { + // When computing the lub, we create a new target + // and coerce both `a` and `b` to it. + let target_ty = self.next_ty_var(self.cause.span); + push_coerce_obligation(a, target_ty); + push_coerce_obligation(b, target_ty); + target_ty + } else { + // When subtyping, we don't need to create a new target + // as we only coerce `a` to `b`. + push_coerce_obligation(a, b); + b + }; debug!( "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", @@ -307,163 +350,103 @@ fn coerce_from_inference_variable(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResu } else { // One unresolved type variable: just apply subtyping, we may be able // to do something useful. - self.unify(a, b) + self.unify(a, b, ForceLeakCheck::No) } } - /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. - /// To match `A` with `B`, autoderef will be performed, - /// calling `deref`/`deref_mut` where necessary. - fn coerce_borrowed_pointer( + /// Handles coercing some arbitrary type `a` to some reference (`b`). This + /// handles a few cases: + /// - Introducing reborrows to give more flexible lifetimes + /// - Deref coercions to allow `&T` to coerce to `&T::Target` + /// - Coercing mutable references to immutable references + /// These coercions can be freely intermixed, for example we are able to + /// coerce `&mut T` to `&mut T::Target`. + fn coerce_to_ref( &self, a: Ty<'tcx>, b: Ty<'tcx>, r_b: ty::Region<'tcx>, mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { - debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); + debug!("coerce_to_ref(a={:?}, b={:?})", a, b); debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); - // If we have a parameter of type `&M T_a` and the value - // provided is `expr`, we will be adding an implicit borrow, - // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, - // to type check, we will construct the type that `&M*expr` would - // yield. - let (r_a, mt_a) = match *a.kind() { ty::Ref(r_a, ty, mutbl) => { - let mt_a = ty::TypeAndMut { ty, mutbl }; - coerce_mutbls(mt_a.mutbl, mutbl_b)?; - (r_a, mt_a) + coerce_mutbls(mutbl, mutbl_b)?; + (r_a, ty::TypeAndMut { ty, mutbl }) } - _ => return self.unify(a, b), + _ => return self.unify(a, b, ForceLeakCheck::No), }; - let span = self.cause.span; - + // Look at each step in the `Deref` chain and check if + // any of the autoref'd `Target` types unify with the + // coercion target. + // + // For example when coercing from `&mut Vec` to `&M [T]` we + // have three deref steps: + // 1. `&mut Vec`, skip autoref + // 2. `Vec`, autoref'd ty: `&M Vec` + // - `&M Vec` does not unify with `&M [T]` + // 3. `[T]`, autoref'd ty: `&M [T]` + // - `&M [T]` does unify with `&M [T]` let mut first_error = None; let mut r_borrow_var = None; - let mut autoderef = self.autoderef(span, a); - let mut found = None; - - for (referent_ty, autoderefs) in autoderef.by_ref() { + let mut autoderef = self.autoderef(self.cause.span, a); + let found = autoderef.by_ref().find_map(|(deref_ty, autoderefs)| { if autoderefs == 0 { - // Don't let this pass, otherwise it would cause - // &T to autoref to &&T. - continue; + // Don't autoref the first step as otherwise we'd allow + // coercing `&T` to `&&T`. + return None; } - // At this point, we have deref'd `a` to `referent_ty`. So - // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. - // In the autoderef loop for `&'a mut Vec`, we would get - // three callbacks: + // The logic here really shouldn't exist. We don't care about free + // lifetimes during HIR typeck. Unfortunately later parts of this + // function rely on structural identity of the autoref'd deref'd ty. // - // - `&'a mut Vec` -- 0 derefs, just ignore it - // - `Vec` -- 1 deref - // - `[T]` -- 2 deref - // - // At each point after the first callback, we want to - // check to see whether this would match out target type - // (`&'b mut [T]`) if we autoref'd it. We can't just - // compare the referent types, though, because we still - // have to consider the mutability. E.g., in the case - // we've been considering, we have an `&mut` reference, so - // the `T` in `[T]` needs to be unified with equality. - // - // Therefore, we construct reference types reflecting what - // the types will be after we do the final auto-ref and - // compare those. Note that this means we use the target - // mutability [1], since it may be that we are coercing - // from `&mut T` to `&U`. - // - // One fine point concerns the region that we use. We - // choose the region such that the region of the final - // type that results from `unify` will be the region we - // want for the autoref: - // - // - if in sub mode, that means we want to use `'b` (the - // region from the target reference) for both - // pointers [2]. This is because sub mode (somewhat - // arbitrarily) returns the subtype region. In the case - // where we are coercing to a target type, we know we - // want to use that target type region (`'b`) because -- - // for the program to type-check -- it must be the - // smaller of the two. - // - One fine point. It may be surprising that we can - // use `'b` without relating `'a` and `'b`. The reason - // that this is ok is that what we produce is - // effectively a `&'b *x` expression (if you could - // annotate the region of a borrow), and regionck has - // code that adds edges from the region of a borrow - // (`'b`, here) into the regions in the borrowed - // expression (`*x`, here). (Search for "link".) - // - if in lub mode, things can get fairly complicated. The - // easiest thing is just to make a fresh - // region variable [4], which effectively means we defer - // the decision to region inference (and regionck, which will add - // some more edges to this variable). However, this can wind up - // creating a crippling number of variables in some cases -- - // e.g., #32278 -- so we optimize one particular case [3]. - // Let me try to explain with some examples: - // - The "running example" above represents the simple case, - // where we have one `&` reference at the outer level and - // ownership all the rest of the way down. In this case, - // we want `LUB('a, 'b)` as the resulting region. - // - However, if there are nested borrows, that region is - // too strong. Consider a coercion from `&'a &'x Rc` to - // `&'b T`. In this case, `'a` is actually irrelevant. - // The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)` - // we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`). - // (The errors actually show up in borrowck, typically, because - // this extra edge causes the region `'a` to be inferred to something - // too big, which then results in borrowck errors.) - // - We could track the innermost shared reference, but there is already - // code in regionck that has the job of creating links between - // the region of a borrow and the regions in the thing being - // borrowed (here, `'a` and `'x`), and it knows how to handle - // all the various cases. So instead we just make a region variable - // and let regionck figure it out. + // This means that what region we use here actually impacts whether + // we emit a reborrow coercion or not which can affect diagnostics + // and capture analysis (which in turn affects borrowck). let r = if !self.use_lub { - r_b // [2] above + r_b } else if autoderefs == 1 { - r_a // [3] above + r_a } else { if r_borrow_var.is_none() { // create var lazily, at most once - let coercion = RegionVariableOrigin::Coercion(span); + let coercion = RegionVariableOrigin::Coercion(self.cause.span); let r = self.next_region_var(coercion); - r_borrow_var = Some(r); // [4] above + r_borrow_var = Some(r); } r_borrow_var.unwrap() }; - let derefd_ty_a = Ty::new_ref( - self.tcx, - r, - referent_ty, - mutbl_b, // [1] above - ); - match self.unify_raw(derefd_ty_a, b) { - Ok(ok) => { - found = Some(ok); - break; - } + + let autorefd_deref_ty = Ty::new_ref(self.tcx, r, deref_ty, mutbl_b); + + // Note that we unify the autoref'd `Target` type with `b` rather than + // the `Target` type with the pointee of `b`. This is necessary + // to properly account for the differing variances of the pointees + // of `&` vs `&mut` references. + match self.unify_raw(autorefd_deref_ty, b, ForceLeakCheck::No) { + Ok(ok) => Some(ok), Err(err) => { if first_error.is_none() { first_error = Some(err); } + None } } - } + }); // Extract type or return an error. We return the first error // we got, which should be from relating the "base" type // (e.g., in example above, the failure from relating `Vec` // to the target type), since that should be the least // confusing. - let Some(InferOk { value: ty, mut obligations }) = found else { + let Some(InferOk { value: coerced_a, mut obligations }) = found else { if let Some(first_error) = first_error { - debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error); + debug!("coerce_to_ref: failed with err = {:?}", first_error); return Err(first_error); } else { // This may happen in the new trait solver since autoderef requires @@ -475,11 +458,15 @@ fn coerce_borrowed_pointer( } }; - if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { + if coerced_a == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { // As a special case, if we would produce `&'a *x`, that's // a total no-op. We end up with the type `&'a T` just as - // we started with. In that case, just skip it - // altogether. This is just an optimization. + // we started with. In that case, just skip it altogether. + // + // Unfortunately, this can actually effect capture analysis + // which in turn means this effects borrow checking. This can + // also effect diagnostics. + // FIXME(BoxyUwU): we should always emit reborrow coercions // // Note that for `&mut`, we DO want to reborrow -- // otherwise, this would be a move, which might be an @@ -488,7 +475,7 @@ fn coerce_borrowed_pointer( // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. assert!(mutbl_b.is_not()); // can only coerce &T -> &U - return success(vec![], ty, obligations); + return success(vec![], coerced_a, obligations); } let InferOk { value: mut adjustments, obligations: o } = @@ -496,17 +483,20 @@ fn coerce_borrowed_pointer( obligations.extend(o); obligations.extend(autoderef.into_obligations()); - // Now apply the autoref. We have to extract the region out of - // the final ref type we got. - let ty::Ref(..) = ty.kind() else { - span_bug!(span, "expected a ref type, got {:?}", ty); - }; + assert!( + matches!(coerced_a.kind(), ty::Ref(..)), + "expected a ref type, got {:?}", + coerced_a + ); + + // Now apply the autoref let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase); - adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty }); + adjustments + .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: coerced_a }); - debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); + debug!("coerce_to_ref: succeeded coerced_a={:?} adjustments={:?}", coerced_a, adjustments); - success(adjustments, ty, obligations) + success(adjustments, coerced_a, obligations) } /// Performs [unsized coercion] by emulating a fulfillment loop on a @@ -569,9 +559,8 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc | ty::Tuple(_) => return Err(TypeError::Mismatch), _ => {} } - // Additionally, we ignore `&str -> &str` coercions, which happen very - // commonly since strings are one of the most used argument types in Rust, - // we do coercions when type checking call expressions. + // `&str: CoerceUnsized<&str>` does not hold but is encountered frequently + // so we fast path bail out here if let ty::Ref(_, source_pointee, ty::Mutability::Not) = *source.kind() && source_pointee.is_str() && let ty::Ref(_, target_pointee, ty::Mutability::Not) = *target.kind() @@ -639,6 +628,7 @@ fn coerce_unsized(&self, source: Ty<'tcx>, target: Ty<'tcx>) -> CoerceResult<'tc target, reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), Adjust::Pointer(PointerCoercion::Unsize), + ForceLeakCheck::No, )?; // Create an obligation for `Source: CoerceUnsized`. @@ -810,7 +800,7 @@ fn coerce_unsized_old_solver( /// - `Pin>` as `Pin<&T>` /// - `Pin>` as `Pin<&mut T>` #[instrument(skip(self), level = "trace")] - fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + fn coerce_to_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); @@ -853,61 +843,26 @@ fn coerce_pin_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { // To complete the reborrow, we need to make sure we can unify the inner types, and if so we // add the adjustments. - self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b)) - } - - fn coerce_from_safe_fn( - &self, - fn_ty_a: ty::PolyFnSig<'tcx>, - b: Ty<'tcx>, - adjustment: Option, - ) -> CoerceResult<'tcx> { - debug_assert!(self.shallow_resolve(b) == b); - - self.commit_if_ok(|snapshot| { - let outer_universe = self.infcx.universe(); - - let result = if let ty::FnPtr(_, hdr_b) = b.kind() - && fn_ty_a.safety().is_safe() - && hdr_b.safety.is_unsafe() - { - let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); - self.unify_and( - unsafe_a, - b, - adjustment - .map(|kind| Adjustment { kind, target: Ty::new_fn_ptr(self.tcx, fn_ty_a) }), - Adjust::Pointer(PointerCoercion::UnsafeFnPointer), - ) - } else { - let a = Ty::new_fn_ptr(self.tcx, fn_ty_a); - match adjustment { - Some(adjust) => self.unify_and(a, b, [], adjust), - None => self.unify(a, b), - } - }; - - // FIXME(#73154): This is a hack. Currently LUB can generate - // unsolvable constraints. Additionally, it returns `a` - // unconditionally, even when the "LUB" is `b`. In the future, we - // want the coerced type to be the actual supertype of these two, - // but for now, we want to just error to ensure we don't lock - // ourselves into a specific behavior with NLL. - self.leak_check(outer_universe, Some(snapshot))?; - - result - }) + self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No) } fn coerce_from_fn_pointer( &self, - fn_ty_a: ty::PolyFnSig<'tcx>, + a: Ty<'tcx>, + a_sig: ty::PolyFnSig<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { - debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); + debug!(?a_sig, ?b, "coerce_from_fn_pointer"); debug_assert!(self.shallow_resolve(b) == b); - self.coerce_from_safe_fn(fn_ty_a, b, None) + match b.kind() { + ty::FnPtr(_, b_hdr) if a_sig.safety().is_safe() && b_hdr.safety.is_unsafe() => { + let a = self.tcx.safe_to_unsafe_fn_ty(a_sig); + let adjust = Adjust::Pointer(PointerCoercion::UnsafeFnPointer); + self.unify_and(a, b, [], adjust, ForceLeakCheck::Yes) + } + _ => self.unify(a, b, ForceLeakCheck::Yes), + } } fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { @@ -915,118 +870,60 @@ fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); - let InferOk { value: b, mut obligations } = - self.at(&self.cause, self.param_env).normalize(b); - match b.kind() { ty::FnPtr(_, b_hdr) => { - let mut a_sig = a.fn_sig(self.tcx); - if let ty::FnDef(def_id, _) = *a.kind() { - // Intrinsics are not coercible to function pointers - if self.tcx.intrinsic(def_id).is_some() { - return Err(TypeError::IntrinsicCast); - } + let a_sig = self.sig_for_fn_def_coercion(a, Some(b_hdr.safety))?; - let fn_attrs = self.tcx.codegen_fn_attrs(def_id); - if matches!(fn_attrs.inline, InlineAttr::Force { .. }) { - return Err(TypeError::ForceInlineCast); - } - - if b_hdr.safety.is_safe() - && self.tcx.codegen_fn_attrs(def_id).safe_target_features - { - // Allow the coercion if the current function has all the features that would be - // needed to call the coercee safely. - if let Some(safe_sig) = self.tcx.adjust_target_feature_sig( - def_id, - a_sig, - self.fcx.body_id.into(), - ) { - a_sig = safe_sig; - } else { - return Err(TypeError::TargetFeatureCast(def_id)); - } - } - } - - let InferOk { value: a_sig, obligations: o1 } = + let InferOk { value: a_sig, mut obligations } = self.at(&self.cause, self.param_env).normalize(a_sig); - obligations.extend(o1); + let a = Ty::new_fn_ptr(self.tcx, a_sig); - let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn( - a_sig, - b, - Some(Adjust::Pointer(PointerCoercion::ReifyFnPointer)), - )?; + let adjust = Adjust::Pointer(PointerCoercion::ReifyFnPointer(b_hdr.safety)); + let InferOk { value, obligations: o2 } = + self.unify_and(a, b, [], adjust, ForceLeakCheck::Yes)?; obligations.extend(o2); Ok(InferOk { value, obligations }) } - _ => self.unify(a, b), + _ => self.unify(a, b, ForceLeakCheck::No), } } - /// Attempts to coerce from the type of a non-capturing closure - /// into a function pointer. - fn coerce_closure_to_fn( - &self, - a: Ty<'tcx>, - closure_def_id_a: DefId, - args_a: GenericArgsRef<'tcx>, - b: Ty<'tcx>, - ) -> CoerceResult<'tcx> { + /// Attempts to coerce from a closure to a function pointer. Fails + /// if the closure has any upvars. + fn coerce_closure_to_fn(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); match b.kind() { - // At this point we haven't done capture analysis, which means - // that the ClosureArgs just contains an inference variable instead - // of tuple of captured types. - // - // All we care here is if any variable is being captured and not the exact paths, - // so we check `upvars_mentioned` for root variables being captured. - ty::FnPtr(_, hdr) - if self - .tcx - .upvars_mentioned(closure_def_id_a.expect_local()) - .is_none_or(|u| u.is_empty()) => - { - // We coerce the closure, which has fn type - // `extern "rust-call" fn((arg0,arg1,...)) -> _` - // to - // `fn(arg0,arg1,...) -> _` - // or - // `unsafe fn(arg0,arg1,...) -> _` - let closure_sig = args_a.as_closure().sig(); + ty::FnPtr(_, hdr) => { let safety = hdr.safety; - let pointer_ty = - Ty::new_fn_ptr(self.tcx, self.tcx.signature_unclosure(closure_sig, safety)); + let terr = TypeError::Sorts(ty::error::ExpectedFound::new(a, b)); + let closure_sig = self.sig_for_closure_coercion(a, Some(hdr.safety), terr)?; + let pointer_ty = Ty::new_fn_ptr(self.tcx, closure_sig); debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); - self.unify_and( - pointer_ty, - b, - [], - Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)), - ) + + let adjust = Adjust::Pointer(PointerCoercion::ClosureFnPointer(safety)); + self.unify_and(pointer_ty, b, [], adjust, ForceLeakCheck::No) } - _ => self.unify(a, b), + _ => self.unify(a, b, ForceLeakCheck::No), } } - fn coerce_raw_ptr( + fn coerce_to_raw_ptr( &self, a: Ty<'tcx>, b: Ty<'tcx>, mutbl_b: hir::Mutability, ) -> CoerceResult<'tcx> { - debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); + debug!("coerce_to_raw_ptr(a={:?}, b={:?})", a, b); debug_assert!(self.shallow_resolve(a) == a); debug_assert!(self.shallow_resolve(b) == b); let (is_ref, mt_a) = match *a.kind() { ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), ty::RawPtr(ty, mutbl) => (false, ty::TypeAndMut { ty, mutbl }), - _ => return self.unify(a, b), + _ => return self.unify(a, b, ForceLeakCheck::No), }; coerce_mutbls(mt_a.mutbl, mutbl_b)?; @@ -1041,11 +938,18 @@ fn coerce_raw_ptr( b, [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), + ForceLeakCheck::No, ) } else if mt_a.mutbl != mutbl_b { - self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCoercion::MutToConstPointer)) + self.unify_and( + a_raw, + b, + [], + Adjust::Pointer(PointerCoercion::MutToConstPointer), + ForceLeakCheck::No, + ) } else { - self.unify(a_raw, b) + self.unify(a_raw, b, ForceLeakCheck::No) } } } @@ -1144,7 +1048,7 @@ pub(crate) fn deref_steps_for_suggestion( // We don't ever need two-phase here since we throw out the result of the coercion. let coerce = Coerce::new(self, cause, AllowTwoPhase::No, true); coerce.autoderef(DUMMY_SP, expr_ty).find_map(|(ty, steps)| { - self.probe(|_| coerce.unify_raw(ty, target)).ok().map(|_| steps) + self.probe(|_| coerce.unify_raw(ty, target, ForceLeakCheck::No)).ok().map(|_| steps) }) } @@ -1167,6 +1071,94 @@ pub(crate) fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Opt }) } + #[instrument(level = "debug", skip(self), ret)] + fn sig_for_coerce_lub( + &self, + ty: Ty<'tcx>, + closure_upvars_terr: TypeError<'tcx>, + ) -> Result, TypeError<'tcx>> { + match ty.kind() { + ty::FnDef(..) => self.sig_for_fn_def_coercion(ty, None), + ty::Closure(..) => self.sig_for_closure_coercion(ty, None, closure_upvars_terr), + _ => unreachable!("`sig_for_fn_def_closure_coerce_lub` called with wrong ty: {:?}", ty), + } + } + + fn sig_for_fn_def_coercion( + &self, + fndef: Ty<'tcx>, + expected_safety: Option, + ) -> Result, TypeError<'tcx>> { + let tcx = self.tcx; + + let &ty::FnDef(def_id, _) = fndef.kind() else { + unreachable!("`sig_for_fn_def_coercion` called with non-fndef: {:?}", fndef); + }; + + // Intrinsics are not coercible to function pointers + if tcx.intrinsic(def_id).is_some() { + return Err(TypeError::IntrinsicCast); + } + + let fn_attrs = tcx.codegen_fn_attrs(def_id); + if matches!(fn_attrs.inline, InlineAttr::Force { .. }) { + return Err(TypeError::ForceInlineCast); + } + + let sig = fndef.fn_sig(tcx); + let sig = if fn_attrs.safe_target_features { + // Allow the coercion if the current function has all the features that would be + // needed to call the coercee safely. + match tcx.adjust_target_feature_sig(def_id, sig, self.body_id.into()) { + Some(adjusted_sig) => adjusted_sig, + None if matches!(expected_safety, Some(hir::Safety::Safe)) => { + return Err(TypeError::TargetFeatureCast(def_id)); + } + None => sig, + } + } else { + sig + }; + + if sig.safety().is_safe() && matches!(expected_safety, Some(hir::Safety::Unsafe)) { + Ok(tcx.safe_to_unsafe_sig(sig)) + } else { + Ok(sig) + } + } + + fn sig_for_closure_coercion( + &self, + closure: Ty<'tcx>, + expected_safety: Option, + closure_upvars_terr: TypeError<'tcx>, + ) -> Result, TypeError<'tcx>> { + let tcx = self.tcx; + + let ty::Closure(closure_def, closure_args) = closure.kind() else { + unreachable!("`sig_for_closure_coercion` called with non closure ty: {:?}", closure); + }; + + // At this point we haven't done capture analysis, which means + // that the ClosureArgs just contains an inference variable instead + // of tuple of captured types. + // + // All we care here is if any variable is being captured and not the exact paths, + // so we check `upvars_mentioned` for root variables being captured. + if !tcx.upvars_mentioned(closure_def.expect_local()).is_none_or(|u| u.is_empty()) { + return Err(closure_upvars_terr); + } + + // We coerce the closure, which has fn type + // `extern "rust-call" fn((arg0,arg1,...)) -> _` + // to + // `fn(arg0,arg1,...) -> _` + // or + // `unsafe fn(arg0,arg1,...) -> _` + let closure_sig = closure_args.as_closure().sig(); + Ok(tcx.signature_unclosure(closure_sig, expected_safety.unwrap_or(hir::Safety::Safe))) + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). @@ -1193,91 +1185,71 @@ fn try_find_coercion_lub( exprs.len() ); - // The following check fixes #88097, where the compiler erroneously - // attempted to coerce a closure type to itself via a function pointer. + // Fast Path: don't go through the coercion logic if we're coercing + // a type to itself. This is unfortunately quite perf relevant so + // we do it even though it may mask bugs in the coercion logic. if prev_ty == new_ty { return Ok(prev_ty); } - let is_force_inline = |ty: Ty<'tcx>| { - if let ty::FnDef(did, _) = ty.kind() { - matches!(self.tcx.codegen_fn_attrs(did).inline, InlineAttr::Force { .. }) - } else { - false - } - }; - if is_force_inline(prev_ty) || is_force_inline(new_ty) { - return Err(TypeError::ForceInlineCast); - } + let terr = TypeError::Sorts(ty::error::ExpectedFound::new(prev_ty, new_ty)); + let opt_sigs = match (prev_ty.kind(), new_ty.kind()) { + // Don't coerce pairs of fndefs or pairs of closures to fn ptrs + // if they can just be lubbed. + // + // See #88097 or `lub_closures_before_fnptr_coercion.rs` for where + // we would erroneously coerce closures to fnptrs when attempting to + // coerce a closure to itself. + (ty::FnDef(..), ty::FnDef(..)) | (ty::Closure(..), ty::Closure(..)) => { + let lubbed_ty = self.commit_if_ok(|snapshot| { + let outer_universe = self.infcx.universe(); - // Special-case that coercion alone cannot handle: - // Function items or non-capturing closures of differing IDs or GenericArgs. - let (a_sig, b_sig) = { - let is_capturing_closure = |ty: Ty<'tcx>| { - if let &ty::Closure(closure_def_id, _args) = ty.kind() { - self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() - } else { - false - } - }; - if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { - (None, None) - } else { - match (prev_ty.kind(), new_ty.kind()) { - (ty::FnDef(..), ty::FnDef(..)) => { - // Don't reify if the function types have a LUB, i.e., they - // are the same function and their parameters have a LUB. - match self.commit_if_ok(|_| { - // We need to eagerly handle nested obligations due to lazy norm. - if self.next_trait_solver() { - let ocx = ObligationCtxt::new(self); - let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; - if ocx.try_evaluate_obligations().is_empty() { - Ok(InferOk { - value, - obligations: ocx.into_pending_obligations(), - }) - } else { - Err(TypeError::Mismatch) - } - } else { - self.at(cause, self.param_env).lub(prev_ty, new_ty) - } - }) { - // We have a LUB of prev_ty and new_ty, just return it. - Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), - Err(_) => { - (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))) - } + // We need to eagerly handle nested obligations due to lazy norm. + let result = if self.next_trait_solver() { + let ocx = ObligationCtxt::new(self); + let value = ocx.lub(cause, self.param_env, prev_ty, new_ty)?; + if ocx.try_evaluate_obligations().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) } + } else { + self.at(cause, self.param_env).lub(prev_ty, new_ty) + }; + + self.leak_check(outer_universe, Some(snapshot))?; + result + }); + + match lubbed_ty { + Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), + Err(_) => { + let a_sig = self.sig_for_coerce_lub(prev_ty, terr)?; + let b_sig = self.sig_for_coerce_lub(new_ty, terr)?; + Some((a_sig, b_sig)) } - (ty::Closure(_, args), ty::FnDef(..)) => { - let b_sig = new_ty.fn_sig(self.tcx); - let a_sig = - self.tcx.signature_unclosure(args.as_closure().sig(), b_sig.safety()); - (Some(a_sig), Some(b_sig)) - } - (ty::FnDef(..), ty::Closure(_, args)) => { - let a_sig = prev_ty.fn_sig(self.tcx); - let b_sig = - self.tcx.signature_unclosure(args.as_closure().sig(), a_sig.safety()); - (Some(a_sig), Some(b_sig)) - } - (ty::Closure(_, args_a), ty::Closure(_, args_b)) => ( - Some( - self.tcx - .signature_unclosure(args_a.as_closure().sig(), hir::Safety::Safe), - ), - Some( - self.tcx - .signature_unclosure(args_b.as_closure().sig(), hir::Safety::Safe), - ), - ), - _ => (None, None), } } + + (ty::Closure(..), ty::FnDef(..)) | (ty::FnDef(..), ty::Closure(..)) => { + let a_sig = self.sig_for_coerce_lub(prev_ty, terr)?; + let b_sig = self.sig_for_coerce_lub(new_ty, terr)?; + Some((a_sig, b_sig)) + } + // ty::FnPtr x ty::FnPtr is fine to just be handled through a normal `unify` + // call using `lub` which is what will happen on the normal path. + (ty::FnPtr(..), ty::FnPtr(..)) => None, + _ => None, }; - if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + + if let Some((mut a_sig, mut b_sig)) = opt_sigs { + // Allow coercing safe sigs to unsafe sigs + if a_sig.safety().is_safe() && b_sig.safety().is_unsafe() { + a_sig = self.tcx.safe_to_unsafe_sig(a_sig); + } else if b_sig.safety().is_safe() && a_sig.safety().is_unsafe() { + b_sig = self.tcx.safe_to_unsafe_sig(b_sig); + }; + // The signature must match. let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig)); let sig = self @@ -1288,29 +1260,13 @@ fn try_find_coercion_lub( // Reify both sides and return the reified fn pointer type. let fn_ptr = Ty::new_fn_ptr(self.tcx, sig); let prev_adjustment = match prev_ty.kind() { - ty::Closure(..) => { - Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.safety())) - } - ty::FnDef(def_id, ..) => { - // Intrinsics are not coercible to function pointers - if self.tcx.intrinsic(def_id).is_some() { - return Err(TypeError::IntrinsicCast); - } - Adjust::Pointer(PointerCoercion::ReifyFnPointer) - } + ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())), + ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(cause.span, "should not try to coerce a {prev_ty} to a fn pointer"), }; let next_adjustment = match new_ty.kind() { - ty::Closure(..) => { - Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.safety())) - } - ty::FnDef(def_id, ..) => { - // Intrinsics are not coercible to function pointers - if self.tcx.intrinsic(def_id).is_some() { - return Err(TypeError::IntrinsicCast); - } - Adjust::Pointer(PointerCoercion::ReifyFnPointer) - } + ty::Closure(..) => Adjust::Pointer(PointerCoercion::ClosureFnPointer(sig.safety())), + ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; for expr in exprs.iter().map(|e| e.as_coercion_site()) { @@ -1353,30 +1309,21 @@ fn try_find_coercion_lub( } } - match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) { - Err(_) => { - // Avoid giving strange errors on failed attempts. - if let Some(e) = first_error { - Err(e) - } else { - Err(self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .unwrap_err()) - } - } - Ok(ok) => { - let (adjustments, target) = self.register_infer_ok_obligations(ok); - for expr in exprs { - let expr = expr.as_coercion_site(); - self.apply_adjustments(expr, adjustments.clone()); - } - debug!( - "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", - prev_ty, new_ty, target - ); - Ok(target) - } + let ok = self + .commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) + // Avoid giving strange errors on failed attempts. + .map_err(|e| first_error.unwrap_or(e))?; + + let (adjustments, target) = self.register_infer_ok_obligations(ok); + for expr in exprs { + let expr = expr.as_coercion_site(); + self.apply_adjustments(expr, adjustments.clone()); } + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", + prev_ty, new_ty, target + ); + Ok(target) } } diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 143cc9479089..714ba0f177a8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -6,6 +6,9 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/Lint.h" #include "llvm/Analysis/TargetLibraryInfo.h" +#if LLVM_VERSION_GE(22, 0) +#include "llvm/Analysis/RuntimeLibcallInfo.h" +#endif #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/Bitcode/BitcodeWriterPass.h" #include "llvm/CodeGen/CommandFlags.h" @@ -379,13 +382,20 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, +extern "C" void LLVMRustAddLibraryInfo(LLVMTargetMachineRef T, + LLVMPassManagerRef PMR, LLVMModuleRef M, bool DisableSimplifyLibCalls) { auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); + TargetOptions *Options = &unwrap(T)->Options; auto TLII = TargetLibraryInfoImpl(TargetTriple); if (DisableSimplifyLibCalls) TLII.disableAllFunctions(); unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); +#if LLVM_VERSION_GE(22, 0) + unwrap(PMR)->add(new RuntimeLibraryInfoWrapper( + TargetTriple, Options->ExceptionModel, Options->FloatABIType, + Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); +#endif } extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2920c9cb42ab..c806366b518a 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -9,8 +9,9 @@ #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum PointerCoercion { - /// Go from a fn-item type to a fn-pointer type. - ReifyFnPointer, + /// Go from a fn-item type to a fn pointer or an unsafe fn pointer. + /// It cannot convert an unsafe fn-item to a safe fn pointer. + ReifyFnPointer(hir::Safety), /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0cd36d5e971d..baf4d364714a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2837,7 +2837,7 @@ impl<'tcx> TyCtxt<'tcx> { ); impl<'tcx> TyCtxt<'tcx> { - /// Given a `fn` type, returns an equivalent `unsafe fn` type; + /// Given a `fn` sig, returns an equivalent `unsafe fn` type; /// that is, a `fn` type that is equivalent in every way for being /// unsafe. pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { @@ -2845,6 +2845,14 @@ pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> { Ty::new_fn_ptr(self, sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig })) } + /// Given a `fn` sig, returns an equivalent `unsafe fn` sig; + /// that is, a `fn` sig that is equivalent in every way for being + /// unsafe. + pub fn safe_to_unsafe_sig(self, sig: PolyFnSig<'tcx>) -> PolyFnSig<'tcx> { + assert!(sig.safety().is_safe()); + sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig }) + } + /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`. pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index d9c26faaf445..ebfeba5ad225 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1518,7 +1518,7 @@ fn simplify_cast( return Some(value); } - if let CastKind::PointerCoercion(ReifyFnPointer | ClosureFnPointer(_), _) = kind { + if let CastKind::PointerCoercion(ReifyFnPointer(_) | ClosureFnPointer(_), _) = kind { // Each reification of a generic fn may get a different pointer. // Do not try to merge them. return Some(self.new_opaque(to)); diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index f011d394f616..a9f2c32171c3 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -109,7 +109,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { } // And finally, function pointer reification casts. mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, _, ) => { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 91617be085c3..cf8247c12abd 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1272,7 +1272,7 @@ macro_rules! check_kinds { match kind { // FIXME: Add Checks for these CastKind::PointerWithExposedProvenance | CastKind::PointerExposeProvenance => {} - CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _) => { + CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _) => { // FIXME: check signature compatibility. check_kinds!( op_ty, diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index f33f22460467..948f965ed7ad 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -765,7 +765,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { } } mir::Rvalue::Cast( - mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer, _), + mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer(_), _), ref operand, _, ) => { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index fde4d40bea10..03f289f30f42 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -978,7 +978,7 @@ pub enum Safety { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize)] pub enum PointerCoercion { /// Go from a fn-item type to a fn-pointer type. - ReifyFnPointer, + ReifyFnPointer(Safety), /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 36fc5724d51a..ca8234280be8 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -130,7 +130,9 @@ fn stable<'cx>( ) -> Self::T { use rustc_middle::ty::adjustment::PointerCoercion; match self { - PointerCoercion::ReifyFnPointer => crate::mir::PointerCoercion::ReifyFnPointer, + PointerCoercion::ReifyFnPointer(safety) => { + crate::mir::PointerCoercion::ReifyFnPointer(safety.stable(tables, cx)) + } PointerCoercion::UnsafeFnPointer => crate::mir::PointerCoercion::UnsafeFnPointer, PointerCoercion::ClosureFnPointer(safety) => { crate::mir::PointerCoercion::ClosureFnPointer(safety.stable(tables, cx)) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2b85639376d9..3ab6c7dcc006 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1155,6 +1155,7 @@ fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], let callsite_span = span.source_callsite(); for rib in self.ribs[ValueNS].iter().rev() { for (binding_ident, _) in &rib.bindings { + // Case 1: the identifier is defined in the same scope as the macro is called if binding_ident.name == ident.name && !binding_ident.span.eq_ctxt(span) && !binding_ident.span.from_expansion() @@ -1166,6 +1167,19 @@ fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], ); return; } + + // Case 2: the identifier is defined in a macro call in the same scope + if binding_ident.name == ident.name + && binding_ident.span.from_expansion() + && binding_ident.span.source_callsite().eq_ctxt(callsite_span) + && binding_ident.span.source_callsite().lo() < callsite_span.lo() + { + err.span_help( + binding_ident.span, + "an identifier with the same name is defined here, but is not accessible due to macro hygiene", + ); + return; + } } } } diff --git a/compiler/rustc_session/src/config/print_request.rs b/compiler/rustc_session/src/config/print_request.rs index b8111fbc17f8..395f7a4fab71 100644 --- a/compiler/rustc_session/src/config/print_request.rs +++ b/compiler/rustc_session/src/config/print_request.rs @@ -22,6 +22,7 @@ pub struct PrintRequest { pub enum PrintKind { // tidy-alphabetical-start AllTargetSpecsJson, + BackendHasZstd, CallingConventions, Cfg, CheckCfg, @@ -59,6 +60,7 @@ fn name(self) -> &'static str { match self { // tidy-alphabetical-start AllTargetSpecsJson => "all-target-specs-json", + BackendHasZstd => "backend-has-zstd", CallingConventions => "calling-conventions", Cfg => "cfg", CheckCfg => "check-cfg", @@ -111,6 +113,7 @@ fn is_stable(self) -> bool { // Unstable values: AllTargetSpecsJson => false, + BackendHasZstd => false, // (perma-unstable, for use by compiletest) CheckCfg => false, CrateRootLintLevels => false, SupportedCrateTypes => false, diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4a2689e01ff1..f5ba71c28833 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -454,6 +454,10 @@ pub const fn new() -> String { /// /// [`new`]: String::new /// + /// # Panics + /// + /// Panics if the capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1079,6 +1083,10 @@ pub const fn as_mut_str(&mut self) -> &mut str { /// Appends a given string slice onto the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1101,8 +1109,9 @@ pub fn push_str(&mut self, string: &str) { /// /// # Panics /// - /// Panics if the range has `start_bound > end_bound`, or, if the range is - /// bounded on either end and does not lie on a [`char`] boundary. + /// Panics if the range has `start_bound > end_bound`, if the range is + /// bounded on either end and does not lie on a [`char`] boundary, or if the + /// new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// @@ -1158,7 +1167,7 @@ pub const fn capacity(&self) -> usize { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1208,7 +1217,7 @@ pub fn reserve(&mut self, additional: usize) { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1372,6 +1381,10 @@ pub fn shrink_to(&mut self, min_capacity: usize) { /// Appends the given [`char`] to the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 6f587df6e33c..2adce8d27039 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3340,6 +3340,10 @@ pub fn resize(&mut self, new_len: usize, value: T) { /// except that it also works with slice elements that are Clone but not Copy. /// If Rust gets specialization this function may be deprecated. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -3361,8 +3365,9 @@ pub fn extend_from_slice(&mut self, other: &[T]) { /// /// # Panics /// - /// Panics if starting index is greater than the end index - /// or if the index is greater than the length of the vector. + /// Panics if starting index is greater than the end index, if the index is + /// greater than the length of the vector, or if the new capacity exceeds + /// `isize::MAX` _bytes_. /// /// # Examples /// diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 1356ca217c9a..f1b928da7ef3 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -28,7 +28,7 @@ issue = "44930", reason = "the `c_variadic` feature has not been properly tested on all supported platforms" )] -pub use self::va_list::{VaArgSafe, VaList, VaListImpl}; +pub use self::va_list::{VaArgSafe, VaList}; #[unstable( feature = "c_variadic", diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 233a2ee3e484..4c59ea0cc532 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -4,15 +4,33 @@ #[cfg(not(target_arch = "xtensa"))] use crate::ffi::c_void; -#[allow(unused_imports)] use crate::fmt; -use crate::intrinsics::{va_arg, va_copy, va_end}; -use crate::marker::{PhantomData, PhantomInvariantLifetime}; -use crate::ops::{Deref, DerefMut}; +use crate::intrinsics::{va_arg, va_copy}; +use crate::marker::PhantomCovariantLifetime; -// The name is WIP, using `VaListImpl` for now. +// There are currently three flavors of how a C `va_list` is implemented for +// targets that Rust supports: // -// Most targets explicitly specify the layout of `va_list`, this layout is matched here. +// - `va_list` is an opaque pointer +// - `va_list` is a struct +// - `va_list` is a single-element array, containing a struct +// +// The opaque pointer approach is the simplest to implement: the pointer just +// points to an array of arguments on the caller's stack. +// +// The struct and single-element array variants are more complex, but +// potentially more efficient because the additional state makes it +// possible to pass variadic arguments via registers. +// +// The Rust `VaList` type is ABI-compatible with the C `va_list`. +// The struct and pointer cases straightforwardly map to their Rust equivalents, +// but the single-element array case is special: in C, this type is subject to +// array-to-pointer decay. +// +// The `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute is used to match +// the pointer decay behavior in Rust, while otherwise matching Rust semantics. +// This attribute ensures that the compiler uses the correct ABI for functions +// like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly. crate::cfg_select! { all( target_arch = "aarch64", @@ -20,73 +38,89 @@ not(target_os = "uefi"), not(windows), ) => { - /// AArch64 ABI implementation of a `va_list`. See the - /// [AArch64 Procedure Call Standard] for more details. + /// AArch64 ABI implementation of a `va_list`. + /// + /// See the [AArch64 Procedure Call Standard] for more details. /// /// [AArch64 Procedure Call Standard]: /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf #[repr(C)] #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, + struct VaListInner { + stack: *const c_void, + gr_top: *const c_void, + vr_top: *const c_void, gr_offs: i32, vr_offs: i32, - _marker: PhantomInvariantLifetime<'f>, } } all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => { /// PowerPC ABI implementation of a `va_list`. + /// + /// See the [LLVM source] and [GCC header] for more details. + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111 + /// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h #[repr(C)] #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gpr: u8, fpr: u8, reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } target_arch = "s390x" => { /// s390x ABI implementation of a `va_list`. + /// + /// See the [S/390x ELF Application Binary Interface Supplement] for more details. + /// + /// [S/390x ELF Application Binary Interface Supplement]: + /// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf #[repr(C)] #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gpr: i64, fpr: i64, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => { - /// x86_64 ABI implementation of a `va_list`. + /// x86_64 System V ABI implementation of a `va_list`. + /// + /// See the [System V AMD64 ABI] for more details. + /// + /// [System V AMD64 ABI]: + /// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf #[repr(C)] #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gp_offset: i32, fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } target_arch = "xtensa" => { /// Xtensa ABI implementation of a `va_list`. + /// + /// See the [LLVM source] for more details. + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215 #[repr(C)] #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - stk: *mut i32, - reg: *mut i32, + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { + stk: *const i32, + reg: *const i32, ndx: i32, - _marker: PhantomInvariantLifetime<'f>, } } @@ -94,95 +128,34 @@ pub struct VaListImpl<'f> { // // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599) // - windows + // - powerpc64 & powerpc64le // - uefi - // - any other target for which we don't specify the `VaListImpl` above + // - any other target for which we don't specify the `VaListInner` above // // In this implementation the `va_list` type is just an alias for an opaque pointer. // That pointer is probably just the next variadic argument on the caller's stack. _ => { /// Basic implementation of a `va_list`. #[repr(transparent)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - ptr: *mut c_void, - - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomInvariantLifetime<'f>, - } - - impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) - } - } - } -} - -crate::cfg_select! { - all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - not(target_arch = "xtensa"), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), - ) => { - /// A wrapper for a `va_list` - #[repr(transparent)] #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: &'a mut VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - - - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } - } - } - - _ => { - /// A wrapper for a `va_list` - #[repr(transparent)] - #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } + struct VaListInner { + ptr: *const c_void, } } } -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - - #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner - } +/// A variable argument list, equivalent to `va_list` in C. +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomCovariantLifetime<'a>, } -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner +impl fmt::Debug for VaList<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // No need to include `_marker` in debug output. + f.debug_tuple("VaList").field(&self.inner).finish() } } @@ -203,7 +176,7 @@ impl Sealed for *mut T {} impl Sealed for *const T {} } -/// Types that are valid to read using [`VaListImpl::arg`]. +/// Types that are valid to read using [`VaList::arg`]. /// /// # Safety /// @@ -238,7 +211,7 @@ unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *mut T {} unsafe impl VaArgSafe for *const T {} -impl<'f> VaListImpl<'f> { +impl<'f> VaList<'f> { /// Advance to and read the next variable argument. /// /// # Safety @@ -258,27 +231,13 @@ pub unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } } -impl<'f> Clone for VaListImpl<'f> { +impl<'f> Clone for VaList<'f> { #[inline] fn clone(&self) -> Self { let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal + // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal. unsafe { va_copy(dest.as_mut_ptr(), self); dest.assume_init() @@ -286,18 +245,11 @@ fn clone(&self) -> Self { } } -impl<'f> Drop for VaListImpl<'f> { +impl<'f> Drop for VaList<'f> { fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. + // Rust requires that not calling `va_end` on a `va_list` does not cause undefined behaviour + // (as it is safe to leak values). As `va_end` is a no-op on all current LLVM targets, this + // destructor is empty. } } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 2115c5c9a85d..7571f4a1fc12 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -54,7 +54,7 @@ )] #![allow(missing_docs)] -use crate::ffi::va_list::{VaArgSafe, VaListImpl}; +use crate::ffi::va_list::{VaArgSafe, VaList}; use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple}; use crate::{mem, ptr}; @@ -3447,7 +3447,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); +pub unsafe fn va_copy<'f>(dest: *mut VaList<'f>, src: &VaList<'f>); /// Loads an argument of type `T` from the `va_list` `ap` and increment the /// argument `ap` points to. @@ -3465,7 +3465,7 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; +pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; /// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. /// @@ -3475,4 +3475,4 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaListImpl<'_>); +pub unsafe fn va_end(ap: &mut VaList<'_>); diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 20353c65d491..24e7fb39ccb1 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -756,8 +756,15 @@ pub const fn min(self, other: f128) -> f128 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f128)] @@ -772,13 +779,6 @@ pub const fn min(self, other: f128) -> f128 { /// assert!(x.maximum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -789,8 +789,15 @@ pub const fn maximum(self, other: f128) -> f128 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f128)] @@ -805,13 +812,6 @@ pub const fn maximum(self, other: f128) -> f128 { /// assert!(x.minimum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 823bd4917b3d..34996e667923 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -747,8 +747,15 @@ pub const fn min(self, other: f16) -> f16 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f16)] @@ -762,13 +769,6 @@ pub const fn min(self, other: f16) -> f16 { /// assert!(x.maximum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -779,8 +779,15 @@ pub const fn maximum(self, other: f16) -> f16 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f16)] @@ -794,13 +801,6 @@ pub const fn maximum(self, other: f16) -> f16 { /// assert!(x.minimum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 361f273ead01..88253c61e157 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -949,8 +949,15 @@ pub const fn min(self, other: f32) -> f32 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -960,13 +967,6 @@ pub const fn min(self, other: f32) -> f32 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -976,8 +976,15 @@ pub const fn maximum(self, other: f32) -> f32 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -987,13 +994,6 @@ pub const fn maximum(self, other: f32) -> f32 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 17a908643a41..5854d306fea9 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -967,8 +967,15 @@ pub const fn min(self, other: f64) -> f64 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -978,13 +985,6 @@ pub const fn min(self, other: f64) -> f64 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `maximum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -994,8 +994,15 @@ pub const fn maximum(self, other: f64) -> f64 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -1005,13 +1012,6 @@ pub const fn maximum(self, other: f64) -> f64 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the IEEE 754-2019 semantics for `minimum`. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 8176af03d13a..f37f5370997e 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -105,38 +105,72 @@ impl<'a> Location<'a> { /// ```standalone_crate /// use std::panic::Location; /// - /// /// Returns the [`Location`] at which it is called. + /// /// ``` + /// /// |1 |11 |21 |31 |41 + /// /// +-|---------|---------|---------|---------|-------- + /// /// 15 | #[track_caller] + /// /// 16 | fn new_location() -> &'static Location<'static> { + /// /// 17 | Location::caller() + /// /// | ------------------| the value of this expression depends on the caller, + /// /// | | since the function is marked #[track_caller] + /// /// 18 | } + /// /// ``` /// #[track_caller] - /// fn get_caller_location() -> &'static Location<'static> { + /// fn new_location() -> &'static Location<'static> { /// Location::caller() /// } /// - /// /// Returns a [`Location`] from within this function's definition. - /// fn get_just_one_location() -> &'static Location<'static> { - /// get_caller_location() + /// /// ``` + /// /// |1 |5 |11 |21 |31 |41 |51 + /// /// +-|---|-----|---------|---------|---------|---------|--- + /// /// 29 | fn constant_location() -> &'static Location<'static> { + /// /// 30 | new_location() + /// /// | ^ any invocation of constant_location() points here, + /// /// | no matter the location it is called from + /// /// 31 | } + /// /// ``` + /// fn constant_location() -> &'static Location<'static> { + /// new_location() /// } /// - /// let fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), file!()); - /// assert_eq!(fixed_location.line(), 14); - /// assert_eq!(fixed_location.column(), 5); + /// fn main() { + /// // |1 |5 |11 |21 |31 |41 |51 + /// // +-|---|-----|---------|---------|---------|---------|--- + /// // 29 | fn constant_location() -> &'static Location<'static> { + /// // 30 | new_location() + /// // | ^ `let constant` points here + /// // 31 | } + /// let constant = constant_location(); + /// assert_eq!(constant.file(), file!()); + /// assert_eq!((constant.line(), constant.column()), (30, 5)); /// - /// // running the same untracked function in a different location gives us the same result - /// let second_fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), second_fixed_location.file()); - /// assert_eq!(fixed_location.line(), second_fixed_location.line()); - /// assert_eq!(fixed_location.column(), second_fixed_location.column()); + /// let constant_2 = constant_location(); + /// assert_eq!( + /// (constant.file(), constant.line(), constant.column()), + /// (constant_2.file(), constant_2.line(), constant_2.column()) + /// ); /// - /// let this_location = get_caller_location(); - /// assert_eq!(this_location.file(), file!()); - /// assert_eq!(this_location.line(), 28); - /// assert_eq!(this_location.column(), 21); + /// // |1 |11 |16 |21 |31 + /// // +-|---------|----|----|---------|------ + /// // 55 | let here = new_location(); + /// // | ^ `let here` points here, as `new_location()` is the callsite + /// // 56 | assert_eq!(here.file(), file!()); + /// let here = new_location(); + /// assert_eq!(here.file(), file!()); + /// assert_eq!((here.line(), here.column()), (55, 16)); /// - /// // running the tracked function in a different location produces a different value - /// let another_location = get_caller_location(); - /// assert_eq!(this_location.file(), another_location.file()); - /// assert_ne!(this_location.line(), another_location.line()); - /// assert_ne!(this_location.column(), another_location.column()); + /// // |1 |11 |21 ||32 |41 |51 + /// // +-|---------|---------|---------||--------|---------|------ + /// // 64 | let yet_another_location = new_location(); + /// // | ^ `let yet_another_location` points here + /// // 65 | assert_eq!(here.file(), yet_another_location.file()); + /// let yet_another_location = new_location(); + /// assert_eq!(here.file(), yet_another_location.file()); + /// assert_ne!( + /// (here.line(), here.column()), + /// (yet_another_location.line(), yet_another_location.column()) + /// ); + /// } /// ``` #[must_use] #[stable(feature = "track_caller", since = "1.46.0")] diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 2df520d35b39..4b87d426bda7 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -26,7 +26,7 @@ use Bound::{Excluded, Included, Unbounded}; #[doc(inline)] -pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; +pub use iter::{RangeFromIter, RangeInclusiveIter, RangeIter}; #[doc(inline)] pub use crate::iter::Step; @@ -89,7 +89,7 @@ impl Range { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRange { + pub fn iter(&self) -> RangeIter { self.clone().into_iter() } } @@ -340,7 +340,7 @@ impl RangeInclusive { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeInclusive { + pub fn iter(&self) -> RangeInclusiveIter { self.clone().into_iter() } } @@ -477,7 +477,7 @@ impl RangeFrom { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeFrom { + pub fn iter(&self) -> RangeFromIter { self.clone().into_iter() } } diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 9a8824baefe4..6fe5d9b34361 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -8,9 +8,9 @@ /// By-value [`Range`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRange(legacy::Range); +pub struct RangeIter(legacy::Range); -impl IterRange { +impl RangeIter { /// Returns the remainder of the range being iterated over. pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } @@ -23,11 +23,11 @@ macro_rules! unsafe_range_trusted_random_access_impl { ($($t:ty)*) => ($( #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccess for IterRange<$t> {} + unsafe impl TrustedRandomAccess for RangeIter<$t> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccessNoCoerce for IterRange<$t> { + unsafe impl TrustedRandomAccessNoCoerce for RangeIter<$t> { const MAY_HAVE_SIDE_EFFECT: bool = false; } )*) @@ -50,7 +50,7 @@ unsafe impl TrustedRandomAccessNoCoerce for IterRange<$t> { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRange { +impl Iterator for RangeIter { type Item = A; #[inline] @@ -118,7 +118,7 @@ unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRange { +impl DoubleEndedIterator for RangeIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -136,27 +136,27 @@ fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRange {} +unsafe impl TrustedLen for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRange {} +impl FusedIterator for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for Range { type Item = A; - type IntoIter = IterRange; + type IntoIter = RangeIter; fn into_iter(self) -> Self::IntoIter { - IterRange(self.into()) + RangeIter(self.into()) } } /// By-value [`RangeInclusive`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeInclusive(legacy::RangeInclusive); +pub struct RangeInclusiveIter(legacy::RangeInclusive); -impl IterRangeInclusive { +impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. @@ -170,7 +170,7 @@ pub fn remainder(self) -> Option> { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeInclusive { +impl Iterator for RangeInclusiveIter { type Item = A; #[inline] @@ -226,7 +226,7 @@ fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRangeInclusive { +impl DoubleEndedIterator for RangeInclusiveIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -244,18 +244,18 @@ fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeInclusive {} +unsafe impl TrustedLen for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeInclusive {} +impl FusedIterator for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeInclusive { type Item = A; - type IntoIter = IterRangeInclusive; + type IntoIter = RangeInclusiveIter; fn into_iter(self) -> Self::IntoIter { - IterRangeInclusive(self.into()) + RangeInclusiveIter(self.into()) } } @@ -270,14 +270,14 @@ fn into_iter(self) -> Self::IntoIter { macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRange<$t> { } + impl ExactSizeIterator for RangeIter<$t> { } )*) } macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRangeInclusive<$t> { } + impl ExactSizeIterator for RangeInclusiveIter<$t> { } )*) } @@ -294,14 +294,14 @@ impl ExactSizeIterator for IterRangeInclusive<$t> { } /// By-value [`RangeFrom`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeFrom { +pub struct RangeFromIter { start: A, /// Whether the first element of the iterator has yielded. /// Only used when overflow checks are enabled. first: bool, } -impl IterRangeFrom { +impl RangeFromIter { /// Returns the remainder of the range being iterated over. #[inline] #[rustc_inherit_overflow_checks] @@ -317,7 +317,7 @@ pub fn remainder(self) -> RangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeFrom { +impl Iterator for RangeFromIter { type Item = A; #[inline] @@ -366,17 +366,17 @@ fn nth(&mut self, n: usize) -> Option { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeFrom {} +unsafe impl TrustedLen for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeFrom {} +impl FusedIterator for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeFrom { type Item = A; - type IntoIter = IterRangeFrom; + type IntoIter = RangeFromIter; fn into_iter(self) -> Self::IntoIter { - IterRangeFrom { start: self.start, first: true } + RangeFromIter { start: self.start, first: true } } } diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index f44e12d48add..999bd5e63dc4 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -172,7 +172,7 @@ all supported platforms", issue = "44930" )] -pub use core::ffi::{VaArgSafe, VaList, VaListImpl}; +pub use core::ffi::{VaArgSafe, VaList}; #[stable(feature = "core_ffi_c", since = "1.64.0")] pub use core::ffi::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 1fc8e86f3193..462cc644d4be 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -150,7 +150,7 @@ fn check_rvalue<'tcx>( CastKind::PointerCoercion( PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) - | PointerCoercion::ReifyFnPointer, + | PointerCoercion::ReifyFnPointer(_), _, ), _, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index eb1fc55a2624..d8472691afdf 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1096,7 +1096,11 @@ fn supported_crate_types(config: &Config) -> HashSet { crate_types } -fn query_rustc_output(config: &Config, args: &[&str], envs: HashMap) -> String { +pub(crate) fn query_rustc_output( + config: &Config, + args: &[&str], + envs: HashMap, +) -> String { let query_rustc_path = config.query_rustc_path.as_deref().unwrap_or(&config.rustc_path); let mut command = Command::new(query_rustc_path); diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index fed30415de56..c154886ebcde 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -885,107 +885,6 @@ pub fn extract_llvm_version_from_binary(binary_path: &str) -> Option { None } -/// For tests using the `needs-llvm-zstd` directive: -/// - for local LLVM builds, try to find the static zstd library in the llvm-config system libs. -/// - for `download-ci-llvm`, see if `lld` was built with zstd support. -pub fn llvm_has_libzstd(config: &Config) -> bool { - // Strategy 1: works for local builds but not with `download-ci-llvm`. - // - // We check whether `llvm-config` returns the zstd library. Bootstrap's `llvm.libzstd` will only - // ask to statically link it when building LLVM, so we only check if the list of system libs - // contains a path to that static lib, and that it exists. - // - // See compiler/rustc_llvm/build.rs for more details and similar expectations. - fn is_zstd_in_config(llvm_bin_dir: &Utf8Path) -> Option<()> { - let llvm_config_path = llvm_bin_dir.join("llvm-config"); - let output = Command::new(llvm_config_path).arg("--system-libs").output().ok()?; - assert!(output.status.success(), "running llvm-config --system-libs failed"); - - let libs = String::from_utf8(output.stdout).ok()?; - for lib in libs.split_whitespace() { - if lib.ends_with("libzstd.a") && Utf8Path::new(lib).exists() { - return Some(()); - } - } - - None - } - - // Strategy 2: `download-ci-llvm`'s `llvm-config --system-libs` will not return any libs to - // use. - // - // The CI artifacts also don't contain the bootstrap config used to build them: otherwise we - // could have looked at the `llvm.libzstd` config. - // - // We infer whether `LLVM_ENABLE_ZSTD` was used to build LLVM as a byproduct of testing whether - // `lld` supports it. If not, an error will be emitted: "LLVM was not built with - // LLVM_ENABLE_ZSTD or did not find zstd at build time". - #[cfg(unix)] - fn is_lld_built_with_zstd(llvm_bin_dir: &Utf8Path) -> Option<()> { - let lld_path = llvm_bin_dir.join("lld"); - if lld_path.exists() { - // We can't call `lld` as-is, it expects to be invoked by a compiler driver using a - // different name. Prepare a temporary symlink to do that. - let lld_symlink_path = llvm_bin_dir.join("ld.lld"); - if !lld_symlink_path.exists() { - std::os::unix::fs::symlink(lld_path, &lld_symlink_path).ok()?; - } - - // Run `lld` with a zstd flag. We expect this command to always error here, we don't - // want to link actual files and don't pass any. - let output = Command::new(&lld_symlink_path) - .arg("--compress-debug-sections=zstd") - .output() - .ok()?; - assert!(!output.status.success()); - - // Look for a specific error caused by LLVM not being built with zstd support. We could - // also look for the "no input files" message, indicating the zstd flag was accepted. - let stderr = String::from_utf8(output.stderr).ok()?; - let zstd_available = !stderr.contains("LLVM was not built with LLVM_ENABLE_ZSTD"); - - // We don't particularly need to clean the link up (so the previous commands could fail - // in theory but won't in practice), but we can try. - std::fs::remove_file(lld_symlink_path).ok()?; - - if zstd_available { - return Some(()); - } - } - - None - } - - #[cfg(not(unix))] - fn is_lld_built_with_zstd(_llvm_bin_dir: &Utf8Path) -> Option<()> { - None - } - - if let Some(llvm_bin_dir) = &config.llvm_bin_dir { - // Strategy 1: for local LLVM builds. - if is_zstd_in_config(llvm_bin_dir).is_some() { - return true; - } - - // Strategy 2: for LLVM artifacts built on CI via `download-ci-llvm`. - // - // It doesn't work for cases where the artifacts don't contain the linker, but it's - // best-effort: CI has `llvm.libzstd` and `lld` enabled on the x64 linux artifacts, so it - // will at least work there. - // - // If this can be improved and expanded to less common cases in the future, it should. - if config.target == "x86_64-unknown-linux-gnu" - && config.host == config.target - && is_lld_built_with_zstd(llvm_bin_dir).is_some() - { - return true; - } - } - - // Otherwise, all hope is lost. - false -} - /// Takes a directive of the form `" [- ]"`, returns the numeric representation /// of `` and `` as tuple: `(, )`. /// diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 7b1c0a18670d..e9824edfef60 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -121,6 +121,7 @@ "ignore-thumbv8m.base-none-eabi", "ignore-thumbv8m.main-none-eabi", "ignore-tvos", + "ignore-uefi", "ignore-unix", "ignore-unknown", "ignore-uwp", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index b54400621092..208e96166021 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,5 +1,7 @@ -use crate::common::{Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; -use crate::directives::{DirectiveLine, IgnoreDecision, llvm_has_libzstd}; +use crate::common::{ + Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output, +}; +use crate::directives::{DirectiveLine, IgnoreDecision}; pub(super) fn handle_needs( cache: &CachedNeedsConditions, @@ -377,7 +379,7 @@ pub(super) fn load(config: &Config) -> Self { .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) .exists(), - llvm_zstd: llvm_has_libzstd(&config), + llvm_zstd: llvm_has_zstd(&config), dlltool: find_dlltool(&config), symlinks: has_symlinks(), } @@ -428,3 +430,18 @@ fn has_symlinks() -> bool { fn has_symlinks() -> bool { true } + +fn llvm_has_zstd(config: &Config) -> bool { + // The compiler already knows whether LLVM was built with zstd or not, + // so compiletest can just ask the compiler. + let output = query_rustc_output( + config, + &["-Zunstable-options", "--print=backend-has-zstd"], + Default::default(), + ); + match output.trim() { + "true" => true, + "false" => false, + _ => panic!("unexpected output from `--print=backend-has-zstd`: {output:?}"), + } +} diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index d6c702848494..4eeba3022892 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -31,13 +31,13 @@ jobs: os: ubuntu-24.04-arm multiarch: armhf gcc_cross: arm-linux-gnueabihf - - host_target: riscv64gc-unknown-linux-gnu - os: ubuntu-latest - multiarch: riscv64 - gcc_cross: riscv64-linux-gnu - qemu: true # Ubuntu mirrors are not reliable enough for these architectures # (see ). + # - host_target: riscv64gc-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: riscv64 + # gcc_cross: riscv64-linux-gnu + # qemu: true # - host_target: s390x-unknown-linux-gnu # os: ubuntu-latest # multiarch: s390x diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 45bf94eb4de4..b6a1415f1834 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -864339abf952f07098dd82610256338520167d4a +36b2369c91d32c2659887ed6fe3d570640f44fd2 diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 7b23518d73da..a74a66d5455a 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -48,29 +48,15 @@ macro_rules! assert_approx_eq { }; } -/// From IEEE 754 a Signaling NaN for single precision has the following representation: -/// ``` -/// s | 1111 1111 | 0x..x -/// ```` -/// Were at least one `x` is a 1. -/// -/// This sNaN has the following representation and is used for testing purposes.: -/// ``` -/// 0 | 1111111 | 01..0 -/// ``` -const SNAN_F32: f32 = f32::from_bits(0x7fa00000); - -/// From IEEE 754 a Signaling NaN for double precision has the following representation: -/// ``` -/// s | 1111 1111 111 | 0x..x -/// ```` -/// Were at least one `x` is a 1. -/// -/// This sNaN has the following representation and is used for testing purposes.: -/// ``` -/// 0 | 1111 1111 111 | 01..0 -/// ``` -const SNAN_F64: f64 = f64::from_bits(0x7ff4000000000000); +/// We turn the quiet NaN f*::NAN into a signaling one by flipping the first (most significant) +/// two bits of the mantissa. For this we have to shift by `MANTISSA_DIGITS-3` because: +/// we subtract 1 as the actual mantissa is 1 bit smaller, and 2 more as that's the width +/// if the value we are shifting. +const F16_SNAN: f16 = f16::from_bits(f16::NAN.to_bits() ^ (0b11 << (f16::MANTISSA_DIGITS - 3))); +const F32_SNAN: f32 = f32::from_bits(f32::NAN.to_bits() ^ (0b11 << (f32::MANTISSA_DIGITS - 3))); +const F64_SNAN: f64 = f64::from_bits(f64::NAN.to_bits() ^ (0b11 << (f64::MANTISSA_DIGITS - 3))); +const F128_SNAN: f128 = + f128::from_bits(f128::NAN.to_bits() ^ (0b11 << (f128::MANTISSA_DIGITS - 3))); fn main() { basic(); @@ -757,6 +743,8 @@ fn ops() { assert_eq(f16::NAN.max(-9.0), -9.0); assert_eq((9.0_f16).min(f16::NAN), 9.0); assert_eq((-9.0_f16).max(f16::NAN), -9.0); + assert_eq(F16_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f16).max(F16_SNAN), -9.0); // f32 min/max assert_eq((1.0 as f32).max(-1.0), 1.0); @@ -765,6 +753,8 @@ fn ops() { assert_eq(f32::NAN.max(-9.0), -9.0); assert_eq((9.0 as f32).min(f32::NAN), 9.0); assert_eq((-9.0 as f32).max(f32::NAN), -9.0); + assert_eq(F32_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f32).max(F32_SNAN), -9.0); // f64 min/max assert_eq((1.0 as f64).max(-1.0), 1.0); @@ -773,6 +763,8 @@ fn ops() { assert_eq(f64::NAN.max(-9.0), -9.0); assert_eq((9.0 as f64).min(f64::NAN), 9.0); assert_eq((-9.0 as f64).max(f64::NAN), -9.0); + assert_eq(F64_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f64).max(F64_SNAN), -9.0); // f128 min/max assert_eq((1.0_f128).max(-1.0), 1.0); @@ -781,6 +773,8 @@ fn ops() { assert_eq(f128::NAN.max(-9.0), -9.0); assert_eq((9.0_f128).min(f128::NAN), 9.0); assert_eq((-9.0_f128).max(f128::NAN), -9.0); + assert_eq(F128_SNAN.min(9.0), 9.0); + assert_eq((-9.0_f128).max(F128_SNAN), -9.0); // f16 copysign assert_eq(3.5_f16.copysign(0.42), 3.5_f16); @@ -1548,15 +1542,15 @@ fn test_operations_f128(a: f128, b: f128) { test_operations_f128(25., 18.); // SNaN^0 = (1 | NaN) - check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan()); - check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan()); + check_nondet(|| f32::powf(F32_SNAN, 0.0).is_nan()); + check_nondet(|| f64::powf(F64_SNAN, 0.0).is_nan()); // 1^SNaN = (1 | NaN) - check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan()); - check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan()); + check_nondet(|| f32::powf(1.0, F32_SNAN).is_nan()); + check_nondet(|| f64::powf(1.0, F64_SNAN).is_nan()); // same as powf (keep it consistent): // x^SNaN = (1 | NaN) - check_nondet(|| f32::powi(SNAN_F32, 0).is_nan()); - check_nondet(|| f64::powi(SNAN_F64, 0).is_nan()); + check_nondet(|| f32::powi(F32_SNAN, 0).is_nan()); + check_nondet(|| f64::powi(F64_SNAN, 0).is_nan()); } diff --git a/src/version b/src/version index 95784efddbc4..8db4a57b3d02 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.93.0 +1.94.0 diff --git a/tests/auxiliary/rust_test_helpers.c b/tests/auxiliary/rust_test_helpers.c index 34cc7fd5dfbe..cd10d6b98ca7 100644 --- a/tests/auxiliary/rust_test_helpers.c +++ b/tests/auxiliary/rust_test_helpers.c @@ -314,6 +314,10 @@ double rust_interesting_average(uint64_t n, ...) { return sum; } +int32_t rust_va_list_next_i32(va_list* ap) { + return va_arg(*ap, int32_t); +} + int32_t rust_int8_to_int32(int8_t x) { return (int32_t)x; } diff --git a/tests/codegen-llvm/cffi/c-variadic-copy.rs b/tests/codegen-llvm/cffi/c-variadic-copy.rs index 4c61c4fcf68d..0cbdcb4bbb85 100644 --- a/tests/codegen-llvm/cffi/c-variadic-copy.rs +++ b/tests/codegen-llvm/cffi/c-variadic-copy.rs @@ -1,4 +1,4 @@ -// Tests that `VaListImpl::clone` gets inlined into a call to `llvm.va_copy` +// Tests that `VaList::clone` gets inlined into a call to `llvm.va_copy` #![crate_type = "lib"] #![feature(c_variadic)] @@ -12,5 +12,5 @@ pub unsafe extern "C" fn clone_variadic(ap: VaList) { let mut ap2 = ap.clone(); // CHECK: call void @llvm.va_copy - foreign_c_variadic_1(ap2.as_va_list(), 42i32); + foreign_c_variadic_1(ap2, 42i32); } diff --git a/tests/codegen-llvm/cffi/c-variadic-opt.rs b/tests/codegen-llvm/cffi/c-variadic-opt.rs index 7e544ee7f37d..3cc0c3e9f9bd 100644 --- a/tests/codegen-llvm/cffi/c-variadic-opt.rs +++ b/tests/codegen-llvm/cffi/c-variadic-opt.rs @@ -10,21 +10,21 @@ } // Ensure that `va_start` and `va_end` are properly injected even -// when the "spoofed" `VaListImpl` is not used. +// when the "spoofed" `VaList` is not used. #[no_mangle] pub unsafe extern "C" fn c_variadic_no_use(fmt: *const i8, mut ap: ...) -> i32 { // CHECK: call void @llvm.va_start - vprintf(fmt, ap.as_va_list()) + vprintf(fmt, ap) // CHECK: call void @llvm.va_end } -// Check that `VaListImpl::clone` gets inlined into a direct call to `llvm.va_copy` +// Check that `VaList::clone` gets inlined into a direct call to `llvm.va_copy` #[no_mangle] pub unsafe extern "C" fn c_variadic_clone(fmt: *const i8, mut ap: ...) -> i32 { // CHECK: call void @llvm.va_start let mut ap2 = ap.clone(); // CHECK: call void @llvm.va_copy - let res = vprintf(fmt, ap2.as_va_list()); + let res = vprintf(fmt, ap2); res // CHECK: call void @llvm.va_end } diff --git a/tests/codegen-llvm/cffi/c-variadic-va_list.rs b/tests/codegen-llvm/cffi/c-variadic-va_list.rs new file mode 100644 index 000000000000..e2652491f421 --- /dev/null +++ b/tests/codegen-llvm/cffi/c-variadic-va_list.rs @@ -0,0 +1,36 @@ +//@ needs-unwind +//@ compile-flags: -Copt-level=3 +//@ min-llvm-version: 21 + +#![crate_type = "lib"] +#![feature(c_variadic)] +#![no_std] +use core::ffi::VaList; + +// Ensure that we do not remove the `va_list` passed to the foreign function when +// removing the "spoofed" `VaList` that is used by Rust defined C-variadics. + +extern "C" { + fn foreign_c_variadic_1(_: VaList, ...); +} + +// CHECK-LABEL: use_foreign_c_variadic_1_0 +pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) { + // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap) + foreign_c_variadic_1(ap); +} + +// CHECK-LABEL: use_foreign_c_variadic_1_1 +pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) { + // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 42) + foreign_c_variadic_1(ap, 42i32); +} +pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) { + // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42) + foreign_c_variadic_1(ap, 2i32, 42i32); +} + +pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) { + // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, i32 noundef 2, i32 noundef 42, i32 noundef 0) + foreign_c_variadic_1(ap, 2i32, 42i32, 0i32); +} diff --git a/tests/codegen-llvm/cffi/c-variadic.rs b/tests/codegen-llvm/cffi/c-variadic.rs index 140d2f37f469..7a2e2ba5047e 100644 --- a/tests/codegen-llvm/cffi/c-variadic.rs +++ b/tests/codegen-llvm/cffi/c-variadic.rs @@ -1,6 +1,6 @@ //@ needs-unwind //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -// +//@ min-llvm-version: 21 #![crate_type = "lib"] #![feature(c_variadic)] @@ -9,7 +9,6 @@ extern "C" { fn foreign_c_variadic_0(_: i32, ...); - fn foreign_c_variadic_1(_: VaList, ...); } pub unsafe extern "C" fn use_foreign_c_variadic_0() { @@ -24,27 +23,6 @@ foreign_c_variadic_0(0, 42i32, 1024i32, 0i32); } -// Ensure that we do not remove the `va_list` passed to the foreign function when -// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics. -pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) { - // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap) - foreign_c_variadic_1(ap); -} - -pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) { - // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42) - foreign_c_variadic_1(ap, 42i32); -} -pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) { - // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42) - foreign_c_variadic_1(ap, 2i32, 42i32); -} - -pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) { - // CHECK: call void ({{.*}}, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0) - foreign_c_variadic_1(ap, 2i32, 42i32, 0i32); -} - // Ensure that `va_start` and `va_end` are properly injected. #[no_mangle] pub unsafe extern "C" fn c_variadic(n: i32, mut ap: ...) -> i32 { diff --git a/tests/codegen-llvm/iterrangefrom-overflow-checks.rs b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs similarity index 90% rename from tests/codegen-llvm/iterrangefrom-overflow-checks.rs rename to tests/codegen-llvm/fromrangeiter-overflow-checks.rs index 88ff5a8508c8..4d27f118ddd3 100644 --- a/tests/codegen-llvm/iterrangefrom-overflow-checks.rs +++ b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs @@ -11,11 +11,11 @@ #![crate_type = "lib"] #![feature(new_range_api)] -use std::range::{IterRangeFrom, RangeFrom}; +use std::range::{RangeFrom, RangeFromIter}; // CHECK-LABEL: @iterrangefrom_remainder( #[no_mangle] -pub unsafe fn iterrangefrom_remainder(x: IterRangeFrom) -> RangeFrom { +pub unsafe fn iterrangefrom_remainder(x: RangeFromIter) -> RangeFrom { // DEBUG: i32 noundef %x // NOCHECKS: i32 noundef returned %x // DEBUG: br i1 diff --git a/tests/crashes/132765.rs b/tests/crashes/132765.rs deleted file mode 100644 index 01e8fdaacff7..000000000000 --- a/tests/crashes/132765.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #132765 - -trait LendingIterator { - type Item<'q>; - fn for_each(&self, _f: Box)>) {} -} - -fn f(_: ()) {} - -fn main() { - LendingIterator::for_each(&(), f); -} diff --git a/tests/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir index 583a5ecd2270..1f2a271b6294 100644 --- a/tests/mir-opt/build_correct_coerce.main.built.after.mir +++ b/tests/mir-opt/build_correct_coerce.main.built.after.mir @@ -9,7 +9,7 @@ fn main() -> () { bb0: { StorageLive(_1); - _1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer, AsCast)); + _1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer(Safe), AsCast)); FakeRead(ForLet(None), _1); _0 = const (); StorageDead(_1); diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff index 50a17326c2aa..7919c816b561 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff +++ b/tests/mir-opt/const_prop/reify_fn_ptr.main.GVN.diff @@ -13,7 +13,7 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); - _3 = main as fn() (PointerCoercion(ReifyFnPointer, AsCast)); + _3 = main as fn() (PointerCoercion(ReifyFnPointer(Safe), AsCast)); _2 = move _3 as usize (PointerExposeProvenance); StorageDead(_3); _1 = move _2 as *const fn() (PointerWithExposedProvenance); diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs index d56f21e586aa..92b9a923a424 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.rs +++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs @@ -3,7 +3,7 @@ fn main() { // CHECK-LABEL: fn main( - // CHECK: [[ptr:_.*]] = main as fn() (PointerCoercion(ReifyFnPointer, AsCast)); + // CHECK: [[ptr:_.*]] = main as fn() (PointerCoercion(ReifyFnPointer(Safe), AsCast)); // CHECK: [[addr:_.*]] = move [[ptr]] as usize (PointerExposeProvenance); // CHECK: [[back:_.*]] = move [[addr]] as *const fn() (PointerWithExposedProvenance); let _ = main as usize as *const fn(); diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff index f3f631956374..90920dd0be8f 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff @@ -37,7 +37,7 @@ bb0: { - StorageLive(_1); + nop; - _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_2); StorageLive(_3); _3 = copy _1; @@ -50,7 +50,7 @@ StorageDead(_2); - StorageLive(_4); + nop; - _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_5); StorageLive(_6); _6 = copy _4; diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff index 029e736a9795..0aca8e508f5c 100644 --- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff @@ -37,7 +37,7 @@ bb0: { - StorageLive(_1); + nop; - _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _1 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_2); StorageLive(_3); _3 = copy _1; @@ -50,7 +50,7 @@ StorageDead(_2); - StorageLive(_4); + nop; - _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer, AsCast)); + _4 = identity:: as fn(u8) -> u8 (PointerCoercion(ReifyFnPointer(Safe), AsCast)); StorageLive(_5); StorageLive(_6); _6 = copy _4; diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index 63d8d713d622..dd2d09470994 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -2,7 +2,7 @@ #![feature(c_variadic)] #![feature(cfg_select)] -use std::ffi::{CStr, CString, VaList, VaListImpl, c_char, c_double, c_int, c_long, c_longlong}; +use std::ffi::{CStr, CString, VaList, c_char, c_double, c_int, c_long, c_longlong}; macro_rules! continue_if { ($cond:expr) => { @@ -58,11 +58,8 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { continue_if!(ap.arg::() == 16); continue_if!(ap.arg::() == 'A' as c_int); continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!")); - ap.with_copy( - |mut ap| { - if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff } - }, - ) + let mut ap = ap.clone(); + if compare_c_str(ap.arg::<*const c_char>(), "Correct") { 0 } else { 0xff } } #[unsafe(no_mangle)] @@ -153,8 +150,8 @@ unsafe fn compare_c_str(ptr: *const c_char, val: &str) -> bool { unsafe extern "C" { fn test_variadic(_: c_int, ...) -> usize; fn test_va_list_by_value(_: VaList) -> usize; - fn test_va_list_by_pointer(_: *mut VaListImpl) -> usize; - fn test_va_list_by_pointer_pointer(_: *mut *mut VaListImpl) -> usize; + fn test_va_list_by_pointer(_: *mut VaList) -> usize; + fn test_va_list_by_pointer_pointer(_: *mut *mut VaList) -> usize; } #[unsafe(no_mangle)] @@ -165,7 +162,7 @@ extern "C" fn run_test_variadic() -> usize { #[unsafe(no_mangle)] extern "C" fn run_test_va_list_by_value() -> usize { unsafe extern "C" fn helper(mut ap: ...) -> usize { - unsafe { test_va_list_by_value(ap.as_va_list()) } + unsafe { test_va_list_by_value(ap) } } unsafe { helper(1 as c_longlong, 2 as c_int, 3 as c_longlong) } diff --git a/tests/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff index 044302a19a01..e382a2478271 100644 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ b/tests/run-make/print-request-help-stable-unstable/help-diff.diff @@ -2,6 +2,6 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models` -+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` ++ = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err index cc6c3c909b36..70764ea13aa8 100644 --- a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err +++ b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err @@ -1,5 +1,5 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index cd161c51ee3b..c41cb5e3bde8 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index 74ec083bdee7..5e13a900484d 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index dfdbff33264b..3ffa0bea0ecf 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -10,37 +10,45 @@ fn rust_interesting_average(_: u64, ...) -> f64; fn rust_valist_interesting_average(_: u64, _: VaList) -> f64; + + fn rust_va_list_next_i32(_: *mut VaList<'_>) -> i32; } -pub unsafe extern "C" fn test_valist_forward(n: u64, mut ap: ...) -> f64 { - rust_valist_interesting_average(n, ap.as_va_list()) +pub unsafe extern "C" fn test_valist_forward(n: u64, ap: ...) -> f64 { + rust_valist_interesting_average(n, ap) } -pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, mut ap: ...) -> f64 { - rust_valist_interesting_average(n, ap.as_va_list()) +pub unsafe extern "C-unwind" fn c_unwind_can_forward(n: u64, ap: ...) -> f64 { + rust_valist_interesting_average(n, ap) } pub unsafe extern "C" fn test_va_copy(_: u64, mut ap: ...) { - let mut ap2 = ap.clone(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 30); + let ap2 = ap.clone(); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 30); // Advance one pair in the copy before checking let mut ap2 = ap.clone(); let _ = ap2.arg::(); let _ = ap2.arg::(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50); // Advance one pair in the original let _ = ap.arg::(); let _ = ap.arg::(); - let mut ap2 = ap.clone(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 50); + let ap2 = ap.clone(); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 50); let mut ap2 = ap.clone(); let _ = ap2.arg::(); let _ = ap2.arg::(); - assert_eq!(rust_valist_interesting_average(2, ap2.as_va_list()) as i64, 70); + assert_eq!(rust_valist_interesting_average(2, ap2) as i64, 70); +} + +pub unsafe extern "C" fn test_ref(mut ap: ...) { + assert_eq!(rust_va_list_next_i32(&mut ap), 2); + assert_eq!(rust_va_list_next_i32(&mut ap), 4); + assert_eq!(rust_va_list_next_i32(&mut ap), 8); } pub fn main() { @@ -85,4 +93,8 @@ unsafe fn call(fp: unsafe extern "C" fn(u64, ...) -> f64) { unsafe { test_va_copy(4, 10i64, 10f64, 20i64, 20f64, 30i64, 30f64, 40i64, 40f64); } + + unsafe { + test_ref(2, 4, 8); + } } diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs new file mode 100644 index 000000000000..fc4f1e4eacb9 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs @@ -0,0 +1,10 @@ +//! regression test for +//! Ensure the diagnostic suggests `for &(mut x) ...` (parenthesized) instead of `&mut x`. + +fn main() { + let nums: &[u32] = &[1, 2, 3]; + for &num in nums { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + println!("{num}"); + } +} diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr new file mode 100644 index 000000000000..fa230134df55 --- /dev/null +++ b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr @@ -0,0 +1,16 @@ +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-for-loop-deref-pattern-assignment.rs:7:9 + | +LL | for &num in nums { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | for &(mut num) num in nums { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0384`. diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr new file mode 100644 index 000000000000..a86b28d98ecc --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -0,0 +1,80 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(32 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: $OFFSETS, + memory_index: $MEMORY_INDEX, + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(32 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:27:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/c-variadic/pass-by-value-abi.rs b/tests/ui/c-variadic/pass-by-value-abi.rs new file mode 100644 index 000000000000..1e3935067cc2 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.rs @@ -0,0 +1,47 @@ +//@ check-fail +//@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" +//@ normalize-stderr: "valid_range: 0\.\.=\d+" -> "valid_range: 0..=$$MAX" +//@ normalize-stderr: "memory_index: \[[^\]]+\]" -> "memory_index: $$MEMORY_INDEX" +//@ normalize-stderr: "offsets: \[[^\]]+\]" -> "offsets: $$OFFSETS" +//@ revisions: x86_64 aarch64 win +//@ compile-flags: -O +//@ [x86_64] only-x86_64 +//@ [x86_64] ignore-windows +//@ [x86_64] ignore-uefi +//@ [aarch64] only-aarch64 +//@ [aarch64] ignore-windows +//@ [aarch64] ignore-apple +//@ [aarch64] ignore-uefi +// Windows doesn't use `#[rustc_pass_indirectly_in_non_rustic_abis]` and is tested in CI, so is here +// for comparison. +//@ [win] only-windows +//@ [win] only-x86_64 + +#![feature(rustc_attrs, c_variadic)] +#![crate_type = "lib"] + +// Can't use `minicore` here as this is testing the implementation in `core::ffi` specifically. +use std::ffi::VaList; + +#[rustc_abi(debug)] +pub extern "C" fn take_va_list(_: VaList<'_>) {} +//~^ ERROR fn_abi_of(take_va_list) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, +//[aarch64]~^^^^ ERROR mode: Indirect { +//[aarch64]~^^^^^ ERROR on_stack: false, +//[win]~^^^^^^ ERROR mode: Direct( + +#[cfg(all(target_arch = "x86_64", not(windows)))] +#[rustc_abi(debug)] +pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {} +//[x86_64]~^ ERROR fn_abi_of(take_va_list_sysv64) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, + +#[cfg(all(target_arch = "x86_64", not(windows)))] +#[rustc_abi(debug)] +pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {} +//[x86_64]~^ ERROR: fn_abi_of(take_va_list_win64) = FnAbi { +//[x86_64]~^^ ERROR mode: Indirect { +//[x86_64]~^^^ ERROR on_stack: false, diff --git a/tests/ui/c-variadic/pass-by-value-abi.win.stderr b/tests/ui/c-variadic/pass-by-value-abi.win.stderr new file mode 100644 index 000000000000..8ff93961f667 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -0,0 +1,83 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(8 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Pointer( + AddressSpace( + 0, + ), + ), + valid_range: 0..=$MAX, + }, + ), + fields: Arbitrary { + offsets: $OFFSETS, + memory_index: $MEMORY_INDEX, + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Direct( + ArgAttributes { + regular: NoUndef, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: None, + }, + ), + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:27:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr new file mode 100644 index 000000000000..aafe89d59e33 --- /dev/null +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -0,0 +1,240 @@ +error: fn_abi_of(take_va_list) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: $OFFSETS, + memory_index: $MEMORY_INDEX, + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:27:1 + | +LL | pub extern "C" fn take_va_list(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: fn_abi_of(take_va_list_sysv64) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: $OFFSETS, + memory_index: $MEMORY_INDEX, + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: X86( + SysV64, + ), + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:37:1 + | +LL | pub extern "sysv64" fn take_va_list_sysv64(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: fn_abi_of(take_va_list_win64) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: VaList<'_>, + layout: Layout { + size: Size(24 bytes), + align: AbiAlign { + abi: Align(8 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: $OFFSETS, + memory_index: $MEMORY_INDEX, + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(8 bytes), + randomization_seed: $SEED, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef, + arg_ext: None, + pointee_size: Size(24 bytes), + pointee_align: Some( + Align(8 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: Align(1 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(1 bytes), + randomization_seed: $SEED, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: X86( + Win64, + ), + can_unwind: false, + } + --> $DIR/pass-by-value-abi.rs:44:1 + | +LL | pub extern "win64" fn take_va_list_win64(_: VaList<'_>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/c-variadic/variadic-ffi-4.rs b/tests/ui/c-variadic/variadic-ffi-4.rs index 806403794225..d9e2e617ce3a 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.rs +++ b/tests/ui/c-variadic/variadic-ffi-4.rs @@ -2,37 +2,30 @@ #![no_std] #![feature(c_variadic)] -use core::ffi::{VaList, VaListImpl}; +use core::ffi::VaList; -pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { +pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> { ap //~^ ERROR: lifetime may not live long enough - //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { +pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> { ap //~ ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape2(_: usize, ap: ...) { - let _ = ap.with_copy(|ap| ap); //~ ERROR: lifetime may not live long enough -} - -pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) { *ap0 = ap1; //~^ ERROR: lifetime may not live long enough - //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { ap0 = &mut ap1; //~^ ERROR: `ap1` does not live long enough //~| ERROR: lifetime may not live long enough //~| ERROR: lifetime may not live long enough } -pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { +pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) { *ap0 = ap1.clone(); //~^ ERROR: lifetime may not live long enough - //~| ERROR: lifetime may not live long enough } diff --git a/tests/ui/c-variadic/variadic-ffi-4.stderr b/tests/ui/c-variadic/variadic-ffi-4.stderr index fc9f8036083a..a230bb6f5861 100644 --- a/tests/ui/c-variadic/variadic-ffi-4.stderr +++ b/tests/ui/c-variadic/variadic-ffi-4.stderr @@ -1,113 +1,64 @@ error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:8:5 | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `VaListImpl<'1>` - | | - | lifetime `'f` defined here -LL | ap - | ^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'f` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:8:5 - | -LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f> { - | -- -- has type `VaListImpl<'1>` +LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaList<'f> { + | -- -- has type `VaList<'1>` | | | lifetime `'f` defined here LL | ap | ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:14:5 + --> $DIR/variadic-ffi-4.rs:13:5 | -LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaListImpl<'static> { - | -- has type `VaListImpl<'1>` +LL | pub unsafe extern "C" fn no_escape1(_: usize, ap: ...) -> VaList<'static> { + | -- has type `VaList<'1>` LL | ap | ^^ returning this value requires that `'1` must outlive `'static` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:18:31 + --> $DIR/variadic-ffi-4.rs:17:5 | -LL | let _ = ap.with_copy(|ap| ap); - | --- ^^ returning this value requires that `'1` must outlive `'2` - | | | - | | return type of closure is VaList<'2, '_> - | has type `VaList<'1, '_>` - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:22:5 - | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'1>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'2>` LL | *ap0 = ap1; | ^^^^ assignment requires that `'1` must outlive `'2` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance error: lifetime may not live long enough --> $DIR/variadic-ffi-4.rs:22:5 | -LL | pub unsafe extern "C" fn no_escape3(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` -LL | *ap0 = ap1; - | ^^^^ assignment requires that `'2` must outlive `'1` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance - -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:28:5 - | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` - | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | - = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` + = note: requirement occurs because of a mutable reference to `VaList<'_>` = note: mutable references are invariant over their type parameter = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:28:5 + --> $DIR/variadic-ffi-4.rs:22:5 | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'2>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'1>` LL | ap0 = &mut ap1; | ^^^^^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | - = note: requirement occurs because of a mutable reference to `VaListImpl<'_>` + = note: requirement occurs because of a mutable reference to `VaList<'_>` = note: mutable references are invariant over their type parameter = help: see for more information about variance error[E0597]: `ap1` does not live long enough - --> $DIR/variadic-ffi-4.rs:28:11 + --> $DIR/variadic-ffi-4.rs:22:11 | -LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | - ------- binding `ap1` declared here +LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | - ------- binding `ap1` declared here | | | let's call the lifetime of this reference `'3` LL | ap0 = &mut ap1; @@ -120,33 +71,15 @@ LL | } | - `ap1` dropped here while still borrowed error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:5 + --> $DIR/variadic-ffi-4.rs:29:5 | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` +LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaList, mut ap1: ...) { + | ------- ------- has type `VaList<'1>` | | - | has type `&mut VaListImpl<'1>` + | has type `&mut VaList<'2>` LL | *ap0 = ap1.clone(); - | ^^^^ assignment requires that `'2` must outlive `'1` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance + | ^^^^ assignment requires that `'1` must outlive `'2` -error: lifetime may not live long enough - --> $DIR/variadic-ffi-4.rs:35:12 - | -LL | pub unsafe extern "C" fn no_escape5(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) { - | ------- ------- has type `VaListImpl<'2>` - | | - | has type `&mut VaListImpl<'1>` -LL | *ap0 = ap1.clone(); - | ^^^^^^^^^^^ argument requires that `'1` must outlive `'2` - | - = note: requirement occurs because of the type `VaListImpl<'_>`, which makes the generic argument `'_` invariant - = note: the struct `VaListImpl<'f>` is invariant over the parameter `'f` - = help: see for more information about variance - -error: aborting due to 11 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs b/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs new file mode 100644 index 000000000000..71e8fdfcfa78 --- /dev/null +++ b/tests/ui/coercion/hr_alias_normalization_leaking_vars.rs @@ -0,0 +1,38 @@ +// Regression test for #132765 +// +// We have two function parameters with types: +// - `&?0` +// - `Box fn(>::Item)>` +// +// As the alias in the second parameter has a `?0` it is an ambig +// alias, and as it references bound vars it can't be normalized to +// an infer var. +// +// When checking function arguments we try to coerce both: +// - `&()` to `&?0` +// - `FnDef(f)` to `Box fn(>::Item)>` +// +// The first coercion infers `?0=()`. Previously when handling +// the second coercion we wound *re-normalize* the alias, which +// now that `?0` has been inferred allowed us to determine this +// alias is not wellformed and normalize it to some infer var `?1`. +// +// We would then see that `FnDef(f)` can't be coerced to `Box` +// and return a `TypeError` referencing this new variable `?1`. This +// then caused ICEs as diagnostics would encounter inferences variables +// from the result of normalization inside of the probe used be coercion. + + +trait LendingIterator { + type Item<'q>; + fn for_each(&self, _f: Box)>) {} +} + +fn f(_: ()) {} + +fn main() { + LendingIterator::for_each(&(), f); + //~^ ERROR: the trait bound `(): LendingIterator` is not satisfied + //~| ERROR: the trait bound `(): LendingIterator` is not satisfied + //~| ERROR: mismatched types +} diff --git a/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr new file mode 100644 index 000000000000..54da352c6503 --- /dev/null +++ b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr @@ -0,0 +1,46 @@ +error[E0277]: the trait bound `(): LendingIterator` is not satisfied + --> $DIR/hr_alias_normalization_leaking_vars.rs:34:31 + | +LL | LendingIterator::for_each(&(), f); + | ------------------------- ^^^ the trait `LendingIterator` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/hr_alias_normalization_leaking_vars.rs:26:1 + | +LL | trait LendingIterator { + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/hr_alias_normalization_leaking_vars.rs:34:36 + | +LL | LendingIterator::for_each(&(), f); + | ------------------------- ^ expected `Box`, found fn item + | | + | arguments to this function are incorrect + | + = note: expected struct `Box fn(<() as LendingIterator>::Item<'a>)>` + found fn item `fn(()) {f}` +note: method defined here + --> $DIR/hr_alias_normalization_leaking_vars.rs:28:8 + | +LL | fn for_each(&self, _f: Box)>) {} + | ^^^^^^^^ --------------------------- + +error[E0277]: the trait bound `(): LendingIterator` is not satisfied + --> $DIR/hr_alias_normalization_leaking_vars.rs:34:36 + | +LL | LendingIterator::for_each(&(), f); + | ^ the trait `LendingIterator` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/hr_alias_normalization_leaking_vars.rs:26:1 + | +LL | trait LendingIterator { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/issue-88097.rs b/tests/ui/coercion/issue-88097.rs index f636323d6236..a5804e3b789c 100644 --- a/tests/ui/coercion/issue-88097.rs +++ b/tests/ui/coercion/issue-88097.rs @@ -3,6 +3,9 @@ // behavior has been fixed. //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver fn peculiar() -> impl Fn(u8) -> u8 { return |x| x + 1 diff --git a/tests/ui/coercion/leak_check_fndef_lub.rs b/tests/ui/coercion/leak_check_fndef_lub.rs new file mode 100644 index 000000000000..2385117f5e2f --- /dev/null +++ b/tests/ui/coercion/leak_check_fndef_lub.rs @@ -0,0 +1,30 @@ +//@ check-pass + +fn foo() {} + +fn fndef_lub_leak_check() { + macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; + } + + // Unused parameters on FnDefs are considered invariant + let lhs = foo:: fn(&'static (), &'a ())>; + let rhs = foo:: fn(&'a (), &'static ())>; + + // If we leak check then we know we should coerce these + // to `fn()`, if we don't leak check we may try to keep + // them as `FnDef`s which would result in a borrowck + // error. + let lubbed = lub!(lhs, rhs); + + // assert that we coerced lhs/rhs to a fn ptr + is_fnptr(lubbed); +} + +trait FnPtr {} +impl FnPtr for fn() {} +fn is_fnptr(_: T) {} + +fn main() {} diff --git a/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs new file mode 100644 index 000000000000..0e79ca28654e --- /dev/null +++ b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.rs @@ -0,0 +1,29 @@ +//@ normalize-stderr: "32 bits" -> "64 bits" + +fn foo() {} + +fn fndef_lub_leak_check() { + macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; + } + + // Unused parameters on FnDefs are considered invariant + let lhs = foo:: fn(&'static (), &'a ())>; + let rhs = foo:: fn(&'a (), &'static ())>; + + loop {} + + // If we leak check then we know we should coerce these + // to `fn()`, if we don't leak check we may try to keep + // them as `FnDef`s which would cause this code to compile + // as borrowck won't emit errors for deadcode. + let lubbed = lub!(lhs, rhs); + + // assert that `lubbed` is a ZST/`FnDef` + unsafe { std::mem::transmute::<_, ()>(lubbed) } + //~^ ERROR: cannot transmute between types of different sizes +} + +fn main() {} diff --git a/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr new file mode 100644 index 000000000000..f7336d49e8ca --- /dev/null +++ b/tests/ui/coercion/leak_check_fndef_lub_deadcode_breakage.stderr @@ -0,0 +1,12 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> $DIR/leak_check_fndef_lub_deadcode_breakage.rs:25:14 + | +LL | unsafe { std::mem::transmute::<_, ()>(lubbed) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `fn()` (64 bits) + = note: target type: `()` (0 bits) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0512`. diff --git a/tests/ui/coercion/lub_coercion_handles_safety.rs b/tests/ui/coercion/lub_coercion_handles_safety.rs new file mode 100644 index 000000000000..e44874f46cec --- /dev/null +++ b/tests/ui/coercion/lub_coercion_handles_safety.rs @@ -0,0 +1,52 @@ +//@ check-pass + +//@ only-x86_64 +// because target features + +macro_rules! lub { + ($lhs:expr, $rhs:expr) => { + if true { $lhs } else { $rhs } + }; +} + +fn safety_lub() { + unsafe fn lhs() {} + fn rhs() {} + + // We have two different fn defs, the only valid lub here + // is to go to fnptrs. However, in order to go to fnptrs + // `rhs` must coerce from a *safe* function to an *unsafe* + // one. + let lubbed = lub!(lhs, rhs); + is_unsafe_fnptr(lubbed); +} + +#[target_feature(enable = "sse2")] +fn target_feature_aware_safety_lub() { + #[target_feature(enable = "sse2")] + fn lhs() {} + fn rhs() {} + unsafe fn rhs_unsafe() {} + + // We have two different fn defs, the only valid lub here + // is to go to fnptrs. However, in order to go to fnptrs + // `lhs` must coerce from an unsafe fn to a safe one due + // to the correct target features being enabled + let lubbed = lub!(lhs, rhs); + is_fnptr(lubbed); + + // Similar case here except we must recognise that rhs + // is an unsafe fn so lhs must be an unsafe fn even though + // it *could* be safe + let lubbed = lub!(lhs, rhs_unsafe); + is_unsafe_fnptr(lubbed); +} + +trait FnPtr {} +impl FnPtr for fn() {} +fn is_fnptr(_: T) {} +trait UnsafeFnPtr {} +impl UnsafeFnPtr for unsafe fn() {} +fn is_unsafe_fnptr(_: T) {} + +fn main() {} diff --git a/tests/ui/coercion/structural_identity_dependent_reborrows.rs b/tests/ui/coercion/structural_identity_dependent_reborrows.rs new file mode 100644 index 000000000000..60cf28aaf303 --- /dev/null +++ b/tests/ui/coercion/structural_identity_dependent_reborrows.rs @@ -0,0 +1,24 @@ +//@ edition: 2024 + +// We avoid emitting reborrow coercions if it seems like it would +// not result in a different lifetime on the borrow. This can effect +// capture analysis resulting in borrow checking errors. + +fn foo<'a>(b: &'a ()) -> impl Fn() { + || { + expected::<&()>(b); + } +} + +// No reborrow of `b` is emitted which means our closure captures +// `b` by ref resulting in an upvar of `&&'a ()` +fn bar<'a>(b: &'a ()) -> impl Fn() { + || { + //~^ ERROR: closure may outlive the current function + expected::<&'a ()>(b); + } +} + +fn expected(_: T) {} + +fn main() {} diff --git a/tests/ui/coercion/structural_identity_dependent_reborrows.stderr b/tests/ui/coercion/structural_identity_dependent_reborrows.stderr new file mode 100644 index 000000000000..d598a4fa7633 --- /dev/null +++ b/tests/ui/coercion/structural_identity_dependent_reborrows.stderr @@ -0,0 +1,25 @@ +error[E0373]: closure may outlive the current function, but it borrows `b`, which is owned by the current function + --> $DIR/structural_identity_dependent_reborrows.rs:16:5 + | +LL | || { + | ^^ may outlive borrowed value `b` +LL | +LL | expected::<&'a ()>(b); + | - `b` is borrowed here + | +note: closure is returned here + --> $DIR/structural_identity_dependent_reborrows.rs:16:5 + | +LL | / || { +LL | | +LL | | expected::<&'a ()>(b); +LL | | } + | |_____^ +help: to force the closure to take ownership of `b` (and any other referenced variables), use the `move` keyword + | +LL | move || { + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0373`. diff --git a/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs new file mode 100644 index 000000000000..6a8d291e3cc0 --- /dev/null +++ b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.rs @@ -0,0 +1,13 @@ +//! Regression test for +//! Checks const resolution stability when using inherent associated types +//! and generic const arguments. + +//@compile-flags: --crate-type=lib +#![expect(incomplete_features)] +#![feature(inherent_associated_types, min_generic_const_args)] +trait Trait {} + +struct Struct; + +type Alias = Struct<{ Struct::N }>; +//~^ ERROR: missing generics for struct `Struct` [E0107] diff --git a/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr new file mode 100644 index 000000000000..13e73dafbea6 --- /dev/null +++ b/tests/ui/const-generics/mgca/resolution-with-inherent-associated-types.stderr @@ -0,0 +1,19 @@ +error[E0107]: missing generics for struct `Struct` + --> $DIR/resolution-with-inherent-associated-types.rs:12:33 + | +LL | type Alias = Struct<{ Struct::N }>; + | ^^^^^^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `N` + --> $DIR/resolution-with-inherent-associated-types.rs:10:8 + | +LL | struct Struct; + | ^^^^^^ -------------- +help: add missing generic argument + | +LL | type Alias = Struct<{ Struct::N }>; + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.rs b/tests/ui/delegation/ice-line-bounds-issue-148732.rs index 699e7d86f258..e44c78476021 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.rs +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.rs @@ -3,6 +3,7 @@ //~| ERROR functions delegation is not yet fully implemented dbg!(b); //~^ ERROR missing lifetime specifier + //~| ERROR `fn() {b}` doesn't implement `Debug` } fn main() {} diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index c65b1560818d..1f43ec335448 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -20,7 +20,7 @@ LL | / reuse a as b { LL | | LL | | LL | | dbg!(b); -LL | | +... | LL | | } | |_^ | @@ -28,7 +28,19 @@ LL | | } = help: add `#![feature(fn_delegation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error[E0277]: `fn() {b}` doesn't implement `Debug` + --> $DIR/ice-line-bounds-issue-148732.rs:4:5 + | +LL | reuse a as b { + | - consider calling this function +... +LL | dbg!(b); + | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` + | + = help: use parentheses to call this function: `b()` + = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) -Some errors have detailed explanations: E0106, E0425, E0658. +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0106, E0277, E0425, E0658. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/delegation/unused-import-ice-144594.rs b/tests/ui/delegation/unused-import-ice-144594.rs new file mode 100644 index 000000000000..1d064a4c978f --- /dev/null +++ b/tests/ui/delegation/unused-import-ice-144594.rs @@ -0,0 +1,13 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +reuse a as b { + //~^ ERROR cannot find function `a` in this scope [E0425] + || { + use std::ops::Add; + x.add + //~^ ERROR cannot find value `x` in this scope [E0425] + } +} + +fn main() {} diff --git a/tests/ui/delegation/unused-import-ice-144594.stderr b/tests/ui/delegation/unused-import-ice-144594.stderr new file mode 100644 index 000000000000..1939380235ee --- /dev/null +++ b/tests/ui/delegation/unused-import-ice-144594.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `a` in this scope + --> $DIR/unused-import-ice-144594.rs:4:7 + | +LL | reuse a as b { + | ^ not found in this scope + +error[E0425]: cannot find value `x` in this scope + --> $DIR/unused-import-ice-144594.rs:8:9 + | +LL | x.add + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/hygiene/pattern-macro.stderr b/tests/ui/hygiene/pattern-macro.stderr index a9764cea49e5..047244ba9ede 100644 --- a/tests/ui/hygiene/pattern-macro.stderr +++ b/tests/ui/hygiene/pattern-macro.stderr @@ -3,6 +3,16 @@ error[E0425]: cannot find value `x` in this scope | LL | x + 1; | ^ not found in this scope + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/pattern-macro.rs:1:28 + | +LL | macro_rules! foo { () => ( x ) } + | ^ +... +LL | let foo!() = 2; + | ------ in this macro invocation + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index 4163d4e06022..ff9669614360 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -3,5 +3,5 @@ error: Argument to option 'print' missing --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index e8adbfd87d76..e2521ebf26a4 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,5 +1,5 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/iterators/iterrangefrom.rs b/tests/ui/iterators/fromrangeiter.rs similarity index 100% rename from tests/ui/iterators/iterrangefrom.rs rename to tests/ui/iterators/fromrangeiter.rs diff --git a/tests/ui/macros/macro-hygiene-help-issue-149604.rs b/tests/ui/macros/macro-hygiene-help-issue-149604.rs new file mode 100644 index 000000000000..5700218cc867 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-149604.rs @@ -0,0 +1,9 @@ +macro_rules! let_it { {} => { let it = (); } } +macro_rules! print_it { {} => { println!("{:?}", it); } } +//~^ ERROR cannot find value `it` in this scope + +fn main() { + let_it!(); + let () = it; //~ ERROR cannot find value `it` in this scope + print_it!(); +} diff --git a/tests/ui/macros/macro-hygiene-help-issue-149604.stderr b/tests/ui/macros/macro-hygiene-help-issue-149604.stderr new file mode 100644 index 000000000000..dc95cb7a43f0 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-149604.stderr @@ -0,0 +1,38 @@ +error[E0425]: cannot find value `it` in this scope + --> $DIR/macro-hygiene-help-issue-149604.rs:7:14 + | +LL | let () = it; + | ^^ not found in this scope + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-help-issue-149604.rs:1:35 + | +LL | macro_rules! let_it { {} => { let it = (); } } + | ^^ +... +LL | let_it!(); + | --------- in this macro invocation + = note: this error originates in the macro `let_it` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `it` in this scope + --> $DIR/macro-hygiene-help-issue-149604.rs:2:50 + | +LL | macro_rules! print_it { {} => { println!("{:?}", it); } } + | ^^ not found in this scope +... +LL | print_it!(); + | ----------- in this macro invocation + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-help-issue-149604.rs:1:35 + | +LL | macro_rules! let_it { {} => { let it = (); } } + | ^^ +... +LL | let_it!(); + | --------- in this macro invocation + = note: this error originates in the macro `print_it` which comes from the expansion of the macro `let_it` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/new-range/enabled.rs b/tests/ui/new-range/enabled.rs index 5ddbba492e76..140e3b648709 100644 --- a/tests/ui/new-range/enabled.rs +++ b/tests/ui/new-range/enabled.rs @@ -17,8 +17,8 @@ fn main() { let c: core::range::RangeInclusive = 4..=5; let d: core::range::RangeToInclusive = ..=3; - let _: core::range::IterRangeFrom = a.into_iter(); - let _: core::range::IterRange = b.into_iter(); - let _: core::range::IterRangeInclusive = c.into_iter(); + let _: core::range::RangeFromIter = a.into_iter(); + let _: core::range::RangeIter = b.into_iter(); + let _: core::range::RangeInclusiveIter = c.into_iter(); // RangeToInclusive has no Iterator implementation } diff --git a/tests/ui/parser/macro/macro-dotdotdot-may-not-begin-a-type.rs b/tests/ui/parser/macro/macro-dotdotdot-may-not-begin-a-type.rs index 8be99f22d2ee..b29f6915ae3d 100644 --- a/tests/ui/parser/macro/macro-dotdotdot-may-not-begin-a-type.rs +++ b/tests/ui/parser/macro/macro-dotdotdot-may-not-begin-a-type.rs @@ -1,4 +1,4 @@ -// A bare `...` represents `CVarArgs` (`VaListImpl<'_>`) in function argument type +// A bare `...` represents `CVarArgs` (`VaList<'_>`) in function argument type // position without being a proper type syntactically. // This test ensures that we do not regress certain MBE calls would we ever promote // `...` to a proper type syntactically. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 025c0e3ecaca..6f61425a8bd6 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -32,12 +32,12 @@ extern "C" fn f3_3(_: ..., x: isize) {} const unsafe extern "C" fn f4_1(x: isize, _: ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_2(x: isize, _: ...) {} //~^ ERROR functions cannot be both `const` and C-variadic //~| ERROR functions with a C variable argument list must be unsafe -//~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +//~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(_: ..., x: isize, _: ...) {} //~^ ERROR functions cannot be both `const` and C-variadic @@ -65,7 +65,7 @@ fn i_f4(_: ..., x: isize, _: ...) {} const fn i_f5(x: isize, _: ...) {} //~^ ERROR `...` is not supported for non-extern functions //~| ERROR functions cannot be both `const` and C-variadic - //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time + //~| ERROR destructor of `VaList<'_>` cannot be evaluated at compile-time } trait T { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 0e02d4434233..318015737fa1 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -236,7 +236,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn t_f6(_: ..., x: isize); | ^^^^^^ -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:33:43 | LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} @@ -244,7 +244,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, _: ...) {} | | | the destructor for this type cannot be evaluated in constant functions -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, _: ...) {} @@ -252,7 +252,7 @@ LL | const extern "C" fn f4_2(x: isize, _: ...) {} | | | the destructor for this type cannot be evaluated in constant functions -error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time +error[E0493]: destructor of `VaList<'_>` cannot be evaluated at compile-time --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, _: ...) {} diff --git a/tests/ui/print-request/backend-has-zstd-unstable.rs b/tests/ui/print-request/backend-has-zstd-unstable.rs new file mode 100644 index 000000000000..cfed4af0c794 --- /dev/null +++ b/tests/ui/print-request/backend-has-zstd-unstable.rs @@ -0,0 +1,8 @@ +//! Check that `--print=backend-has-zstd` is unstable. +//! +//! That print value is intended for use by compiletest, and should probably +//! never be stabilized in this form. + +//@ compile-flags: --print=backend-has-zstd + +//~? ERROR: the `-Z unstable-options` flag must also be passed diff --git a/tests/ui/print-request/backend-has-zstd-unstable.stderr b/tests/ui/print-request/backend-has-zstd-unstable.stderr new file mode 100644 index 000000000000..c7b5aa68d873 --- /dev/null +++ b/tests/ui/print-request/backend-has-zstd-unstable.stderr @@ -0,0 +1,2 @@ +error: the `-Z unstable-options` flag must also be passed to enable the `backend-has-zstd` print option + diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr index 297a3aa79e1f..d39c6326e318 100644 --- a/tests/ui/print-request/print-lints-help.stderr +++ b/tests/ui/print-request/print-lints-help.stderr @@ -1,6 +1,6 @@ error: unknown print request: `lints` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `backend-has-zstd`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: use `-Whelp` to print a list of lints = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr index 17171ad5c5cc..ed8ee4dc52cb 100644 --- a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr +++ b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr @@ -30,6 +30,16 @@ error[E0425]: cannot find value `local_def` in this scope | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/gen-macro-rules-hygiene.rs:13:1 + | +LL | gen_macro_rules!(); + | ^^^^^^^^^^^^^^^^^^ +... +LL | generated!(); + | ------------ in this macro invocation + = note: this error originates in the macro `generated` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/proc-macro/mixed-site-span.stderr b/tests/ui/proc-macro/mixed-site-span.stderr index d5cf484f6dd0..97e3f3e3dea8 100644 --- a/tests/ui/proc-macro/mixed-site-span.stderr +++ b/tests/ui/proc-macro/mixed-site-span.stderr @@ -606,6 +606,13 @@ error[E0425]: cannot find value `local_def` in this scope | LL | local_def; | ^^^^^^^^^ help: a local variable with a similar name exists: `local_use` + | +help: an identifier with the same name is defined here, but is not accessible due to macro hygiene + --> $DIR/mixed-site-span.rs:23:9 + | +LL | proc_macro_rules!(); + | ^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 52 previous errors diff --git a/tests/ui/thir-print/c-variadic.stdout b/tests/ui/thir-print/c-variadic.stdout index d64b2b9aa9d1..f1905e04f72b 100644 --- a/tests/ui/thir-print/c-variadic.stdout +++ b/tests/ui/thir-print/c-variadic.stdout @@ -16,13 +16,13 @@ params: [ ) } Param { - ty: std::ffi::VaListImpl<'{erased}> + ty: std::ffi::VaList<'{erased}> ty_span: None self_kind: None hir_id: Some(HirId(DefId(0:3 ~ c_variadic[a5de]::foo).3)) param: Some( Pat: { - ty: std::ffi::VaListImpl<'{erased}> + ty: std::ffi::VaList<'{erased}> span: $DIR/c-variadic.rs:7:34: 7:37 (#0) kind: PatKind { Missing