diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5668d8992c8dc..7bd2507d1ade6 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -833,6 +833,28 @@ pub enum Integer { } impl Integer { + pub fn int_ty_str(self) -> &'static str { + use Integer::*; + match self { + I8 => "i8", + I16 => "i16", + I32 => "i32", + I64 => "i64", + I128 => "i128", + } + } + + pub fn uint_ty_str(self) -> &'static str { + use Integer::*; + match self { + I8 => "u8", + I16 => "u16", + I32 => "u32", + I64 => "u64", + I128 => "u128", + } + } + #[inline] pub fn size(self) -> Size { use Integer::*; diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 4af4b39cc5b9e..8839829e2f5cd 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -785,8 +785,10 @@ fn codegen_stmt<'tcx>( } Rvalue::Repeat(ref operand, times) => { let operand = codegen_operand(fx, operand); - let times = - fx.monomorphize(times).eval_target_usize(fx.tcx, ParamEnv::reveal_all()); + let times = fx + .monomorphize(times) + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen"); if operand.layout().size.bytes() == 0 { // Do nothing for ZST's } else if fx.clif_type(operand.layout().ty) == Some(types::I8) { @@ -944,7 +946,10 @@ fn codegen_stmt<'tcx>( fn codegen_array_len<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>) -> Value { match *place.layout().ty.kind() { ty::Array(_elem_ty, len) => { - let len = fx.monomorphize(len).eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64; + let len = fx + .monomorphize(len) + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") as i64; fx.bcx.ins().iconst(fx.pointer_type, len) } ty::Slice(_elem_ty) => place.to_ptr_unsized().1, diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 7baf0a3868d2c..8a55a23128db8 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -44,7 +44,7 @@ impl DebugContext { type_dbg, ty, *elem_ty, - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + len.try_to_target_usize(tcx).expect("expected monomorphic const in codegen"), ), // ty::Slice(_) | ty::Str // ty::Dynamic diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index a5621aec2444f..b96abb14b2e9e 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -131,9 +131,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let idx = generic_args[2] .expect_const() - .eval(fx.tcx, ty::ParamEnv::reveal_all(), span) - .unwrap() - .1 + .try_to_valtree() + .expect("expected monomorphic const in codegen") .unwrap_branch(); assert_eq!(x.layout(), y.layout()); diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index e09cd16e89a64..8cfe93b4d9c91 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -24,10 +24,10 @@ pub(crate) fn unsized_info<'tcx>( let (source, target) = fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all()); match (&source.kind(), &target.kind()) { - (&ty::Array(_, len), &ty::Slice(_)) => fx - .bcx - .ins() - .iconst(fx.pointer_type, len.eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64), + (&ty::Array(_, len), &ty::Slice(_)) => fx.bcx.ins().iconst( + fx.pointer_type, + len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64, + ), (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) if src_dyn_kind == target_dyn_kind => { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 6c84a40defb8a..57e396415ccef 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -125,7 +125,9 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( let (size, align) = cx.size_and_align_of(array_type); - let upper_bound = len.eval_target_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong; + let upper_bound = len + .try_to_target_usize(cx.tcx) + .expect("expected monomorphic const in codegen") as c_longlong; let subrange = unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f6b45eb4466fb..3d1007a46737b 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -115,9 +115,9 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let (source, target) = cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env()); match (source.kind(), target.kind()) { - (&ty::Array(_, len), &ty::Slice(_)) => { - cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all())) - } + (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize( + len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"), + ), (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) if src_dyn_kind == target_dyn_kind => { diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 55a71db9baebd..369ab387bea58 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -188,7 +188,8 @@ fn push_debuginfo_type_name<'tcx>( _ => write!( output, ",{}>", - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) + len.try_to_target_usize(tcx) + .expect("expected monomorphic const in codegen") ) .unwrap(), } @@ -200,7 +201,8 @@ fn push_debuginfo_type_name<'tcx>( _ => write!( output, "; {}]", - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) + len.try_to_target_usize(tcx) + .expect("expected monomorphic const in codegen") ) .unwrap(), } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 37474d85b11b6..6bf75293fce45 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -114,7 +114,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let count = self .monomorphize(count) - .eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); + .try_to_target_usize(bx.tcx()) + .expect("expected monomorphic const in codegen"); bx.write_operand_repeatedly(cg_elem, count, dest); } @@ -803,7 +804,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(index) = place.as_local() { if let LocalRef::Operand(op) = self.locals[index] { if let ty::Array(_, n) = op.layout.ty.kind() { - let n = n.eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); + let n = n + .try_to_target_usize(bx.tcx()) + .expect("expected monomorphic const in codegen"); return bx.cx().const_usize(n); } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 45a6efc7a6a32..7dd6deb4fe6fd 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,7 +11,6 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind}; use rustc_infer::infer; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -2413,17 +2412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option>, Ty<'tcx>) { - let len = match len.eval(self.tcx, self.param_env, span) { - Ok((_, val)) => val - .try_to_scalar() - .and_then(|scalar| scalar.try_to_scalar_int().ok()) - .map(|int| int.to_target_usize(self.tcx)), - Err(ErrorHandled::Reported(..)) => { - let guar = self.error_scrutinee_unfixed_length(span); - return (Some(Ty::new_error(self.tcx, guar)), arr_ty); - } - Err(ErrorHandled::TooGeneric(..)) => None, - }; + let len = len.try_eval_target_usize(self.tcx, self.param_env); let guar = if let Some(len) = len { // Now we know the length... diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index c2555d2bb47dc..2fbbc3200e12a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -803,7 +803,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { // We must deeply normalize in the new solver, since later lints // expect that types that show up in the typeck are fully // normalized. - let value = if self.should_normalize { + let mut value = if self.should_normalize { let body_id = tcx.hir().body_owner_def_id(self.body.id()); let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let at = self.fcx.at(&cause, self.fcx.param_env); @@ -818,12 +818,27 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { value }; + // Bail if there are any non-region infer. if value.has_non_region_infer() { let guar = self.report_error(value); - new_err(tcx, guar) - } else { - tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased) + value = new_err(tcx, guar); + } + + // Erase the regions from the ty, since it's not really meaningful what + // these region values are; there's not a trivial correspondence between + // regions in the HIR and MIR, so when we turn the body into MIR, there's + // no reason to keep regions around. They will be repopulated during MIR + // borrowck, and specifically region constraints will be populated during + // MIR typeck which is run on the new body. + value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased); + + // Normalize consts in writeback, because GCE doesn't normalize eagerly. + if tcx.features().generic_const_exprs { + value = + value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env }); } + + value } } @@ -858,3 +873,17 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { predicate } } + +struct EagerlyNormalizeConsts<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +} +impl<'tcx> TypeFolder> for EagerlyNormalizeConsts<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + self.tcx.try_normalize_erasing_regions(self.param_env, ct).unwrap_or(ct) + } +} diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 0e3d34355a1f2..e71c5676ce4f5 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -395,6 +395,8 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent lint_improper_ctypes_pat_help = consider using the base type instead lint_improper_ctypes_pat_reason = pattern types have no C equivalent + +lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 900c496e0330d..f9d0cd49708ae 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -3,9 +3,9 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; +use rustc_hir::{Expr, ExprKind}; use rustc_middle::bug; -use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; +use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; @@ -13,22 +13,23 @@ use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; use rustc_span::{source_map, Span, Symbol}; -use rustc_target::abi::{Abi, Integer, Size, TagEncoding, Variants, WrappingRange}; +use rustc_target::abi::{Abi, TagEncoding, Variants, WrappingRange}; use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, - OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, - OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, - UseInclusiveRange, VariantSizeDifferencesDiag, + InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, }; use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext}; +mod literal; + +use literal::{int_ty_range, lint_literal, uint_ty_range}; + declare_lint! { /// The `unused_comparisons` lint detects comparisons made useless by /// limits of the types involved. @@ -185,403 +186,6 @@ impl TypeLimits { } } -/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). -/// Returns `true` iff the lint was emitted. -fn lint_overflowing_range_endpoint<'tcx>( - cx: &LateContext<'tcx>, - lit: &hir::Lit, - lit_val: u128, - max: u128, - expr: &'tcx hir::Expr<'tcx>, - ty: &str, -) -> bool { - // Look past casts to support cases like `0..256 as u8` - let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::Cast(_, _) = par_expr.kind - { - (par_expr, expr.span) - } else { - (expr, expr.span) - }; - - // We only want to handle exclusive (`..`) ranges, - // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; - let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; - if !is_range_literal(struct_expr) { - return false; - }; - let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; - - // We can suggest using an inclusive range - // (`..=`) instead only if it is the `end` that is - // overflowing and only by 1. - if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { - return false; - }; - - use rustc_ast::{LitIntType, LitKind}; - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsuffixed) => "", - _ => bug!(), - }; - - let sub_sugg = if expr.span.lo() == lit_span.lo() { - let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; - UseInclusiveRange::WithoutParen { - sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), - start, - literal: lit_val - 1, - suffix, - } - } else { - UseInclusiveRange::WithParen { - eq_sugg: expr.span.shrink_to_lo(), - lit_sugg: lit_span, - literal: lit_val - 1, - suffix, - } - }; - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - struct_expr.span, - RangeEndpointOutOfRange { ty, sub: sub_sugg }, - ); - - // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, - // return `true` so the callers don't also emit a lint - true -} - -// For `isize` & `usize`, be conservative with the warnings, so that the -// warnings are consistent between 32- and 64-bit platforms. -fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { - match int_ty { - ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), - ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), - ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), - ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), - ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), - ty::IntTy::I128 => (i128::MIN, i128::MAX), - } -} - -fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { - let max = match uint_ty { - ty::UintTy::Usize => u64::MAX.into(), - ty::UintTy::U8 => u8::MAX.into(), - ty::UintTy::U16 => u16::MAX.into(), - ty::UintTy::U32 => u32::MAX.into(), - ty::UintTy::U64 => u64::MAX.into(), - ty::UintTy::U128 => u128::MAX, - }; - (0, max) -} - -fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option { - let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; - let firstch = src.chars().next()?; - - if firstch == '0' { - match src.chars().nth(1) { - Some('x' | 'b') => return Some(src), - _ => return None, - } - } - - None -} - -fn report_bin_hex_error( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - ty: attr::IntType, - size: Size, - repr_str: String, - val: u128, - negative: bool, -) { - let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { - let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; - (t.name_str(), actually.to_string()) - } - attr::IntType::UnsignedInt(t) => { - let actually = size.truncate(val); - (t.name_str(), actually.to_string()) - } - }; - let sign = - if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; - let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( - |suggestion_ty| { - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - let (sans_suffix, _) = repr_str.split_at(pos); - OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } - } else { - OverflowingBinHexSub::Help { suggestion_ty } - } - }, - ); - let sign_bit_sub = (!negative) - .then(|| { - let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { - return None; - }; - - let Some(bit_width) = int_ty.bit_width() else { - return None; // isize case - }; - - // Skip if sign bit is not set - if (val & (1 << (bit_width - 1))) == 0 { - return None; - } - - let lit_no_suffix = - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - repr_str.split_at(pos).0 - } else { - &repr_str - }; - - Some(OverflowingBinHexSignBitSub { - span: expr.span, - lit_no_suffix, - negative_val: actually.clone(), - int_ty: int_ty.name_str(), - uint_ty: int_ty.to_unsigned().name_str(), - }) - }) - .flatten(); - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - expr.span, - OverflowingBinHex { - ty: t, - lit: repr_str.clone(), - dec: val, - actually, - sign, - sub, - sign_bit_sub, - }, - ) -} - -// This function finds the next fitting type and generates a suggestion string. -// It searches for fitting types in the following way (`X < Y`): -// - `iX`: if literal fits in `uX` => `uX`, else => `iY` -// - `-iX` => `iY` -// - `uX` => `uY` -// -// No suggestion for: `isize`, `usize`. -fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { - use ty::IntTy::*; - use ty::UintTy::*; - macro_rules! find_fit { - ($ty:expr, $val:expr, $negative:expr, - $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { - { - let _neg = if negative { 1 } else { 0 }; - match $ty { - $($type => { - $(if !negative && val <= uint_ty_range($utypes).1 { - return Some($utypes.name_str()) - })* - $(if val <= int_ty_range($itypes).1 as u128 + _neg { - return Some($itypes.name_str()) - })* - None - },)+ - _ => None - } - } - } - } - match t.kind() { - ty::Int(i) => find_fit!(i, val, negative, - I8 => [U8] => [I16, I32, I64, I128], - I16 => [U16] => [I32, I64, I128], - I32 => [U32] => [I64, I128], - I64 => [U64] => [I128], - I128 => [U128] => []), - ty::Uint(u) => find_fit!(u, val, negative, - U8 => [U8, U16, U32, U64, U128] => [], - U16 => [U16, U32, U64, U128] => [], - U32 => [U32, U64, U128] => [], - U64 => [U64, U128] => [], - U128 => [U128] => []), - _ => None, - } -} - -fn lint_int_literal<'tcx>( - cx: &LateContext<'tcx>, - type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, - t: ty::IntTy, - v: u128, -) { - let int_type = t.normalize(cx.sess().target.pointer_width); - let (min, max) = int_ty_range(int_type); - let max = max as u128; - let negative = type_limits.negated_expr_id == Some(e.hir_id); - - // Detect literal value out of range [min, max] inclusive - // avoiding use of -min to prevent overflow/panic - if (negative && v > max + 1) || (!negative && v > max) { - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::SignedInt(ty::ast_int_ty(t)), - Integer::from_int_ty(cx, t).size(), - repr_str, - v, - negative, - ); - return; - } - - if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { - // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. - return; - } - - let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; - let lit = cx - .sess() - .source_map() - .span_to_snippet(span) - .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); - let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) - .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - span, - OverflowingInt { ty: t.name_str(), lit, min, max, help }, - ); - } -} - -fn lint_uint_literal<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, - t: ty::UintTy, -) { - let uint_type = t.normalize(cx.sess().target.pointer_width); - let (min, max) = uint_ty_range(uint_type); - let lit_val: u128 = match lit.node { - // _v is u8, within range by definition - ast::LitKind::Byte(_v) => return, - ast::LitKind::Int(v, _) => v.get(), - _ => bug!(), - }; - - if lit_val < min || lit_val > max { - if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { - match par_e.kind { - hir::ExprKind::Cast(..) => { - if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - par_e.span, - OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, - ); - return; - } - } - _ => {} - } - } - if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { - // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. - return; - } - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), - Integer::from_uint_ty(cx, t).size(), - repr_str, - lit_val, - false, - ); - return; - } - cx.emit_span_lint( - OVERFLOWING_LITERALS, - e.span, - OverflowingUInt { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| lit_val.to_string()), - min, - max, - }, - ); - } -} - -fn lint_literal<'tcx>( - cx: &LateContext<'tcx>, - type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, -) { - match *cx.typeck_results().node_type(e.hir_id).kind() { - ty::Int(t) => { - match lit.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { - lint_int_literal(cx, type_limits, e, lit, t, v.get()) - } - _ => bug!(), - }; - } - ty::Uint(t) => lint_uint_literal(cx, e, lit, t), - ty::Float(t) => { - let (is_infinite, sym) = match lit.node { - ast::LitKind::Float(v, _) => match t { - // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI - // issues resolved). - ty::FloatTy::F16 => (Ok(false), v), - ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), - ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), - ty::FloatTy::F128 => (Ok(false), v), - }, - _ => bug!(), - }; - if is_infinite == Ok(true) { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - e.span, - OverflowingLiteral { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| sym.to_string()), - }, - ); - } - } - _ => {} - } -} - fn lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, @@ -991,6 +595,8 @@ struct CTypesVisitorState<'tcx> { /// The original type being checked, before we recursed /// to any other types it contains. base_ty: Ty<'tcx>, + /// Number of times we recursed while checking the type + recursion_depth: usize, } enum FfiResult<'tcx> { @@ -1296,12 +902,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. if !acc.cache.insert(ty) { return FfiSafe; } + // Additional recursion check for more complex types like + // `struct A { v: *const A>, ... }` for which the + // cache check above won't be enough (fixes #130310) + if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) { + return FfiUnsafe { + ty: acc.base_ty, + reason: fluent::lint_improper_ctypes_recursion_limit_reached, + help: None, + }; + } + + acc.recursion_depth += 1; + match *ty.kind() { ty::Adt(def, args) => { if let Some(boxed) = ty.boxed_ty() @@ -1644,7 +1261,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return; } - let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; + let mut acc = + CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 }; match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs new file mode 100644 index 0000000000000..67404be24b52e --- /dev/null +++ b/compiler/rustc_lint/src/types/literal.rs @@ -0,0 +1,386 @@ +use hir::{is_range_literal, ExprKind, Node}; +use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::Ty; +use rustc_middle::{bug, ty}; +use rustc_target::abi::{Integer, Size}; +use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; + +use crate::context::LintContext; +use crate::lints::{ + OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, + OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, + RangeEndpointOutOfRange, UseInclusiveRange, +}; +use crate::types::{TypeLimits, OVERFLOWING_LITERALS}; +use crate::LateContext; + +/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). +/// Returns `true` iff the lint was emitted. +fn lint_overflowing_range_endpoint<'tcx>( + cx: &LateContext<'tcx>, + lit: &hir::Lit, + lit_val: u128, + max: u128, + expr: &'tcx hir::Expr<'tcx>, + ty: &str, +) -> bool { + // Look past casts to support cases like `0..256 as u8` + let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Cast(_, _) = par_expr.kind + { + (par_expr, expr.span) + } else { + (expr, expr.span) + }; + + // We only want to handle exclusive (`..`) ranges, + // which are represented as `ExprKind::Struct`. + let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; + let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; + if !is_range_literal(struct_expr) { + return false; + }; + let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; + + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { + return false; + }; + + use rustc_ast::{LitIntType, LitKind}; + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", + _ => bug!(), + }; + + let sub_sugg = if expr.span.lo() == lit_span.lo() { + let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; + UseInclusiveRange::WithoutParen { + sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), + start, + literal: lit_val - 1, + suffix, + } + } else { + UseInclusiveRange::WithParen { + eq_sugg: expr.span.shrink_to_lo(), + lit_sugg: lit_span, + literal: lit_val - 1, + suffix, + } + }; + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + struct_expr.span, + RangeEndpointOutOfRange { ty, sub: sub_sugg }, + ); + + // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, + // return `true` so the callers don't also emit a lint + true +} + +// For `isize` & `usize`, be conservative with the warnings, so that the +// warnings are consistent between 32- and 64-bit platforms. +pub(crate) fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { + match int_ty { + ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), + ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), + ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), + ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I128 => (i128::MIN, i128::MAX), + } +} + +pub(crate) fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { + let max = match uint_ty { + ty::UintTy::Usize => u64::MAX.into(), + ty::UintTy::U8 => u8::MAX.into(), + ty::UintTy::U16 => u16::MAX.into(), + ty::UintTy::U32 => u32::MAX.into(), + ty::UintTy::U64 => u64::MAX.into(), + ty::UintTy::U128 => u128::MAX, + }; + (0, max) +} + +fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option { + let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; + let firstch = src.chars().next()?; + + if firstch == '0' { + match src.chars().nth(1) { + Some('x' | 'b') => return Some(src), + _ => return None, + } + } + + None +} + +fn report_bin_hex_error( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + ty: attr::IntType, + size: Size, + repr_str: String, + val: u128, + negative: bool, +) { + let (t, actually) = match ty { + attr::IntType::SignedInt(t) => { + let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; + (t.name_str(), actually.to_string()) + } + attr::IntType::UnsignedInt(t) => { + let actually = size.truncate(val); + (t.name_str(), actually.to_string()) + } + }; + let sign = + if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; + let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( + |suggestion_ty| { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } + } else { + OverflowingBinHexSub::Help { suggestion_ty } + } + }, + ); + let sign_bit_sub = (!negative) + .then(|| { + let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { + return None; + }; + + let Some(bit_width) = int_ty.bit_width() else { + return None; // isize case + }; + + // Skip if sign bit is not set + if (val & (1 << (bit_width - 1))) == 0 { + return None; + } + + let lit_no_suffix = + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + repr_str.split_at(pos).0 + } else { + &repr_str + }; + + Some(OverflowingBinHexSignBitSub { + span: expr.span, + lit_no_suffix, + negative_val: actually.clone(), + int_ty: int_ty.name_str(), + uint_ty: int_ty.to_unsigned().name_str(), + }) + }) + .flatten(); + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + expr.span, + OverflowingBinHex { + ty: t, + lit: repr_str.clone(), + dec: val, + actually, + sign, + sub, + sign_bit_sub, + }, + ) +} + +// Find the "next" fitting integer and return a suggestion string +// +// No suggestion is offered for `{i,u}size`. Otherwise, we try to suggest an equal-sized type. +fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { + match t.kind() { + ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, + ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), + ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()), + ty::Int(int) => { + let signed = Integer::fit_signed(val as i128); + let unsigned = Integer::fit_unsigned(val); + Some(if Some(unsigned.size().bits()) == int.bit_width() { + unsigned.uint_ty_str() + } else { + signed.int_ty_str() + }) + } + _ => None, + } +} + +fn lint_int_literal<'tcx>( + cx: &LateContext<'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, + t: ty::IntTy, + v: u128, +) { + let int_type = t.normalize(cx.sess().target.pointer_width); + let (min, max) = int_ty_range(int_type); + let max = max as u128; + let negative = type_limits.negated_expr_id == Some(e.hir_id); + + // Detect literal value out of range [min, max] inclusive + // avoiding use of -min to prevent overflow/panic + if (negative && v > max + 1) || (!negative && v > max) { + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error( + cx, + e, + attr::IntType::SignedInt(ty::ast_int_ty(t)), + Integer::from_int_ty(cx, t).size(), + repr_str, + v, + negative, + ); + return; + } + + if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. + return; + } + + let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; + let lit = cx + .sess() + .source_map() + .span_to_snippet(span) + .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); + let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) + .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingInt { ty: t.name_str(), lit, min, max, help }, + ); + } +} + +fn lint_uint_literal<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, + t: ty::UintTy, +) { + let uint_type = t.normalize(cx.sess().target.pointer_width); + let (min, max) = uint_ty_range(uint_type); + let lit_val: u128 = match lit.node { + // _v is u8, within range by definition + ast::LitKind::Byte(_v) => return, + ast::LitKind::Int(v, _) => v.get(), + _ => bug!(), + }; + + if lit_val < min || lit_val > max { + if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { + match par_e.kind { + hir::ExprKind::Cast(..) => { + if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, + ); + return; + } + } + _ => {} + } + } + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. + return; + } + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error( + cx, + e, + attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + Integer::from_uint_ty(cx, t).size(), + repr_str, + lit_val, + false, + ); + return; + } + cx.emit_span_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingUInt { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| lit_val.to_string()), + min, + max, + }, + ); + } +} + +pub(crate) fn lint_literal<'tcx>( + cx: &LateContext<'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, +) { + match *cx.typeck_results().node_type(e.hir_id).kind() { + ty::Int(t) => { + match lit.node { + ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { + lint_int_literal(cx, type_limits, e, lit, t, v.get()) + } + _ => bug!(), + }; + } + ty::Uint(t) => lint_uint_literal(cx, e, lit, t), + ty::Float(t) => { + let (is_infinite, sym) = match lit.node { + ast::LitKind::Float(v, _) => match t { + // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI + // issues resolved). + ty::FloatTy::F16 => (Ok(false), v), + ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), + ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), + ty::FloatTy::F128 => (Ok(false), v), + }, + _ => bug!(), + }; + if is_infinite == Ok(true) { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingLiteral { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| sym.to_string()), + }, + ); + } + } + _ => {} + } +} diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 6708ae6056284..1a584cf289004 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -521,6 +521,10 @@ impl<'tcx> Const<'tcx> { self.try_to_valtree()?.try_to_scalar() } + pub fn try_to_bool(self) -> Option { + self.try_to_scalar()?.to_bool().ok() + } + #[inline] pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option { self.try_to_valtree()?.try_to_target_usize(tcx) diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index a0a0dd058ffc8..9c2f869f35768 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -145,7 +145,7 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } ty::Bool => { - let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let val = c.try_to_bool().expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } _ => { @@ -411,7 +411,7 @@ pub fn encode_ty<'tcx>( ty::Array(ty0, len) => { // A - let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); + let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi"); let mut s = String::from("A"); let _ = write!(s, "{len}"); s.push_str(&encode_ty(tcx, *ty0, dict, options)); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 34763adde4f0a..e4231d755067f 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -146,7 +146,10 @@ impl<'tcx> TypeFolder> for TransformTy<'tcx> { !is_zst }); if let Some(field) = field { - let ty0 = self.tcx.erase_regions(field.ty(self.tcx, args)); + let ty0 = self.tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + field.ty(self.tcx, args), + ); // Generalize any repr(transparent) user-defined type that is either a // pointer or reference, and either references itself or any other type that // contains or references itself, to avoid a reference cycle. diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 99689511854e0..55f3b628ce8e9 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1991,6 +1991,11 @@ impl AsInner for DirEntry { /// * The file doesn't exist. /// * The user lacks permissions to remove the file. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2448,6 +2453,11 @@ pub fn create_dir_all>(path: P) -> io::Result<()> { /// * The user lacks permissions to remove the directory at the provided `path`. /// * The directory isn't empty. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a7e9352bb1c4c..2047345d78a74 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1733,6 +1733,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); + if mode == "run-make" { + let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); + cmd.arg("--cargo-path").arg(cargo); + } + // Avoid depending on rustdoc when we don't need it. if mode == "rustdoc" || mode == "run-make" diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5c18179b6fe2f..414f9f3a7f13a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -183,6 +183,9 @@ pub struct Config { /// The rustc executable. pub rustc_path: PathBuf, + /// The cargo executable. + pub cargo_path: Option, + /// The rustdoc executable. pub rustdoc_path: Option, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 250b5084d1362..3339116d542bc 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -47,6 +47,7 @@ pub fn parse_config(args: Vec) -> Config { opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH") + .optopt("", "cargo-path", "path to cargo to use for compiling", "PATH") .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH") .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") @@ -260,6 +261,7 @@ pub fn parse_config(args: Vec) -> Config { compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), + cargo_path: matches.opt_str("cargo-path").map(PathBuf::from), rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), @@ -364,6 +366,7 @@ pub fn log_config(config: &Config) { logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path)); logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); + logv(c, format!("cargo_path: {:?}", config.cargo_path)); logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path)); logv(c, format!("src_base: {:?}", config.src_base.display())); logv(c, format!("build_base: {:?}", config.build_base.display())); diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 852568ae92536..75fe6a6baafa1 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -61,6 +61,10 @@ impl TestCx<'_> { .env_remove("MFLAGS") .env_remove("CARGO_MAKEFLAGS"); + if let Some(ref cargo) = self.config.cargo_path { + cmd.env("CARGO", cwd.join(cargo)); + } + if let Some(ref rustdoc) = self.config.rustdoc_path { cmd.env("RUSTDOC", cwd.join(rustdoc)); } @@ -409,6 +413,10 @@ impl TestCx<'_> { // through a specific CI runner). .env("LLVM_COMPONENTS", &self.config.llvm_components); + if let Some(ref cargo) = self.config.cargo_path { + cmd.env("CARGO", source_root.join(cargo)); + } + if let Some(ref rustdoc) = self.config.rustdoc_path { cmd.env("RUSTDOC", source_root.join(rustdoc)); } diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c78..0000000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs index 42ed07d9dafae..3b05fe2055c34 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -33,8 +33,8 @@ fn main() { let path = env_var("PATH"); let rustc = env_var("RUSTC"); - let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); - let mut cmd = cmd(bootstrap_cargo); + let cargo = env_var("CARGO"); + let mut cmd = cmd(cargo); cmd.args(&[ "build", "--manifest-path", diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs index 0ddb91d378fb7..9112646290f20 100644 --- a/tests/run-make/thumb-none-cortex-m/rmake.rs +++ b/tests/run-make/thumb-none-cortex-m/rmake.rs @@ -36,10 +36,10 @@ fn main() { let path = env_var("PATH"); let rustc = env_var("RUSTC"); - let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); - // FIXME: extract bootstrap cargo invocations to a proper command + let cargo = env_var("CARGO"); + // FIXME: extract cargo invocations to a proper command // https://github.com/rust-lang/rust/issues/128734 - let mut cmd = cmd(bootstrap_cargo); + let mut cmd = cmd(cargo); cmd.args(&[ "build", "--manifest-path", diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs index c6dd981cced00..5303b24717353 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs @@ -3,8 +3,7 @@ impl EntriesBuffer { fn a(&self) -> impl Iterator { - self.0.iter_mut() //~ ERROR: cannot borrow `*self.0` as mutable, as it is behind a `&` reference - //~| ERROR captures lifetime that does not appear in bounds + self.0.iter_mut() } } diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr index 24f3ed7cdf15e..fcbd690459972 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `HashesEntryLEN` in this scope - --> $DIR/issue-109141.rs:11:32 + --> $DIR/issue-109141.rs:10:32 | LL | struct EntriesBuffer(Box<[[u8; HashesEntryLEN]; 5]>); | ^^^^^^^^^^^^^^ not found in this scope @@ -9,33 +9,6 @@ help: you might be missing a const parameter LL | struct EntriesBuffer(Box<[[u8; HashesEntryLEN]; 5]>); | ++++++++++++++++++++++++++++++++++ -error[E0596]: cannot borrow `*self.0` as mutable, as it is behind a `&` reference - --> $DIR/issue-109141.rs:6:9 - | -LL | self.0.iter_mut() - | ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable - | -help: consider changing this to be a mutable reference - | -LL | fn a(&mut self) -> impl Iterator { - | ~~~~~~~~~ - -error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds - --> $DIR/issue-109141.rs:6:9 - | -LL | fn a(&self) -> impl Iterator { - | ----- ------------- opaque type defined here - | | - | hidden type `std::slice::IterMut<'_, [u8; {const error}]>` captures the anonymous lifetime defined here -LL | self.0.iter_mut() - | ^^^^^^^^^^^^^^^^^ - | -help: add a `use<...>` bound to explicitly capture `'_` - | -LL | fn a(&self) -> impl Iterator + use<'_> { - | +++++++++ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0425, E0596, E0700. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs index 7209290a36e0a..56b8acbf88cde 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs @@ -2,7 +2,6 @@ #![allow(incomplete_features)] type Foo = impl Sized; -//~^ ERROR: unconstrained opaque type fn with_bound() -> Foo where diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr index c7a266205b4b5..e9fb8c0f403ae 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/opaque_type.rs:11:17 + --> $DIR/opaque_type.rs:10:17 | LL | type Foo = impl Sized; | ---------- the found opaque type @@ -11,20 +11,12 @@ LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; found opaque type `Foo` error[E0605]: non-primitive cast: `usize` as `Foo` - --> $DIR/opaque_type.rs:11:17 + --> $DIR/opaque_type.rs:10:17 | LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; | ^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error: unconstrained opaque type - --> $DIR/opaque_type.rs:4:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - | - = note: `Foo` must be used in combination with a concrete type within the same module - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0308, E0605. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/issue-116186.rs b/tests/ui/consts/issue-116186.rs index a77c38c64dc57..8bfb47629e7f4 100644 --- a/tests/ui/consts/issue-116186.rs +++ b/tests/ui/consts/issue-116186.rs @@ -4,7 +4,7 @@ fn something(path: [usize; N]) -> impl Clone { //~^ ERROR cannot find value `N` in this scope match path { - [] => 0, //~ ERROR cannot pattern-match on an array without a fixed length + [] => 0, _ => 1, }; } diff --git a/tests/ui/consts/issue-116186.stderr b/tests/ui/consts/issue-116186.stderr index e6eae2d9f55ef..46931f79dd0cb 100644 --- a/tests/ui/consts/issue-116186.stderr +++ b/tests/ui/consts/issue-116186.stderr @@ -9,13 +9,6 @@ help: you might be missing a const parameter LL | fn something(path: [usize; N]) -> impl Clone { | +++++++++++++++++++++ -error[E0730]: cannot pattern-match on an array without a fixed length - --> $DIR/issue-116186.rs:7:9 - | -LL | [] => 0, - | ^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0425, E0730. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.rs b/tests/ui/lint/improper-types-stack-overflow-130310.rs new file mode 100644 index 0000000000000..60eb873981756 --- /dev/null +++ b/tests/ui/lint/improper-types-stack-overflow-130310.rs @@ -0,0 +1,20 @@ +// Regression test for #130310 +// Tests that we do not fall into infinite +// recursion while checking FFI safety of +// recursive types like `A` below + +//@ build-pass +use std::marker::PhantomData; + +#[repr(C)] +struct A { + a: *const A>, // Recursive because of this field + p: PhantomData, +} + +extern "C" { + fn f(a: *const A<()>); + //~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe +} + +fn main() {} diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.stderr b/tests/ui/lint/improper-types-stack-overflow-130310.stderr new file mode 100644 index 0000000000000..6981bb2575542 --- /dev/null +++ b/tests/ui/lint/improper-types-stack-overflow-130310.stderr @@ -0,0 +1,11 @@ +warning: `extern` block uses type `*const A<()>`, which is not FFI-safe + --> $DIR/improper-types-stack-overflow-130310.rs:16:13 + | +LL | fn f(a: *const A<()>); + | ^^^^^^^^^^^^ not FFI-safe + | + = note: type is infinitely recursive + = note: `#[warn(improper_ctypes)]` on by default + +warning: 1 warning emitted +