Merge pull request #4743 from rust-lang/rustup-2025-12-06

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

Some files were not shown because too many files have changed in this diff Show More