mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 20:46:07 +03:00
Merge pull request #4743 from rust-lang/rustup-2025-12-06
Automatic Rustup
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -799,6 +799,7 @@ fn print_crate_info(
|
||||
println_info!("{}", calling_conventions.join("\n"));
|
||||
}
|
||||
RelocationModels
|
||||
| BackendHasZstd
|
||||
| CodeModels
|
||||
| TlsModels
|
||||
| TargetCPUs
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
_,
|
||||
) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
_,
|
||||
) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
///
|
||||
/// ```
|
||||
|
||||
@@ -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
|
||||
///
|
||||
|
||||
@@ -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
@@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<'_>);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(_),
|
||||
_,
|
||||
),
|
||||
_,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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 @@
|
||||
864339abf952f07098dd82610256338520167d4a
|
||||
36b2369c91d32c2659887ed6fe3d570640f44fd2
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
1.93.0
|
||||
1.94.0
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
+2
-2
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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`.
|
||||
@@ -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,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`.
|
||||
@@ -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
Reference in New Issue
Block a user