Auto merge of #130649 - matthiaskrgr:rollup-57zc7lz, r=matthiaskrgr

Rollup of 6 pull requests

Successful merges:

 - #129718 (add guarantee about remove_dir and remove_file error kinds)
 - #130598 (Add recursion limit to FFI safety lint)
 - #130642 (Pass the current cargo to `run-make` tests)
 - #130644 (Only expect valtree consts in codegen)
 - #130645 (Normalize consts in writeback when GCE is enabled)
 - #130646 (compiler: factor out `OVERFLOWING_LITERALS` impl)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-09-21 07:00:52 +00:00
commit 74fd001cda
33 changed files with 585 additions and 520 deletions

View file

@ -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::*;

View file

@ -785,8 +785,10 @@ fn is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
}
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 is_fat_ptr<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
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,

View file

@ -44,7 +44,7 @@ pub(crate) fn debug_type<'tcx>(
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

View file

@ -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());

View file

@ -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 =>
{

View file

@ -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)) };

View file

@ -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 =>
{

View file

@ -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(),
}

View file

@ -114,7 +114,8 @@ pub(crate) fn codegen_rvalue(
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 @@ fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Va
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);
}
}

View file

@ -11,7 +11,6 @@
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 @@ fn check_array_pat_len(
len: ty::Const<'tcx>,
min_len: u64,
) -> (Option<Ty<'tcx>>, 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...

View file

@ -803,7 +803,7 @@ fn handle_term<T>(
// 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 @@ fn handle_term<T>(
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 @@ fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'t
predicate
}
}
struct EagerlyNormalizeConsts<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> 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)
}
}

View file

@ -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

View file

@ -3,9 +3,9 @@
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_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 @@ pub(crate) fn new() -> 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<String> {
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 @@ fn check_type_for_ffi(
// 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<T> { v: *const A<A<T>>, ... }` 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 @@ fn check_type_for_ffi_and_report_errors(
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) => {

View file

@ -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<String> {
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()),
},
);
}
}
_ => {}
}
}

View file

@ -521,6 +521,10 @@ pub fn try_to_scalar(self) -> Option<Scalar> {
self.try_to_valtree()?.try_to_scalar()
}
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_scalar()?.to_bool().ok()
}
#[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_valtree()?.try_to_target_usize(tcx)

View file

@ -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<array-length><element-type>
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));

View file

@ -146,7 +146,10 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'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.

View file

@ -1991,6 +1991,11 @@ fn as_inner(&self) -> &fs_imp::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<P: AsRef<Path>>(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

View file

@ -1733,6 +1733,11 @@ fn run(self, builder: &Builder<'_>) {
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"

View file

@ -183,6 +183,9 @@ pub struct Config {
/// The rustc executable.
pub rustc_path: PathBuf,
/// The cargo executable.
pub cargo_path: Option<PathBuf>,
/// The rustdoc executable.
pub rustdoc_path: Option<PathBuf>,

View file

@ -47,6 +47,7 @@ pub fn parse_config(args: Vec<String>) -> 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 @@ fn make_absolute(path: PathBuf) -> PathBuf {
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()));

View file

@ -61,6 +61,10 @@ fn run_rmake_legacy_test(&self) {
.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 @@ fn run_rmake_v2_test(&self) {
// 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));
}

View file

@ -1,15 +0,0 @@
//@ known-bug: rust-lang/rust#130310
use std::marker::PhantomData;
#[repr(C)]
struct A<T> {
a: *const A<A<T>>,
p: PhantomData<T>,
}
extern "C" {
fn f(a: *const A<()>);
}
fn main() {}

View file

@ -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",

View file

@ -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",

View file

@ -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()
}
}

View file

@ -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<const HashesEntryLEN: /* Type */>(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: aborting due to 1 previous error
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
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`.

View file

@ -2,7 +2,6 @@
#![allow(incomplete_features)]
type Foo = impl Sized;
//~^ ERROR: unconstrained opaque type
fn with_bound<const N: usize>() -> Foo
where

View file

@ -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`.

View file

@ -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,
};
}

View file

@ -9,13 +9,6 @@ help: you might be missing a const parameter
LL | fn something<const N: /* Type */>(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 1 previous error
error: aborting due to 2 previous errors
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`.

View file

@ -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<T>` below
//@ build-pass
use std::marker::PhantomData;
#[repr(C)]
struct A<T> {
a: *const A<A<T>>, // Recursive because of this field
p: PhantomData<T>,
}
extern "C" {
fn f(a: *const A<()>);
//~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe
}
fn main() {}

View file

@ -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