1
Fork 0

Merge pull request #4173 from RalfJung/rustup

Rustup
This commit is contained in:
Ralf Jung 2025-02-02 15:46:53 +00:00 committed by GitHub
commit cd13b44ede
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
102 changed files with 1842 additions and 806 deletions

View file

@ -16,8 +16,7 @@ Version 1.84.1 (2025-01-30)
Version 1.84.0 (2025-01-09)
==========================
<a id="
Language"></a>
<a id="1.84.0-Language"></a>
Language
--------

View file

@ -1657,7 +1657,7 @@ impl GenBlockKind {
}
/// Whether we're unwrapping or wrapping an unsafe binder
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum UnsafeBinderCastKind {
// e.g. `&i32` -> `unsafe<'a> &'a i32`

View file

@ -3915,7 +3915,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => kind,
| ProjectionElem::Index(_)
| ProjectionElem::UnwrapUnsafeBinder(_) => kind,
},
place_ty.projection_ty(tcx, elem),
)

View file

@ -370,6 +370,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
ProjectionElem::Subtype(..) => (),
ProjectionElem::UnwrapUnsafeBinder(_) => (),
ProjectionElem::Field(field, _ty) => {
// FIXME(project-rfc_2229#36): print capture precisely here.
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
@ -450,9 +451,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
}
ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
ProjectionElem::Subtype(ty) | ProjectionElem::OpaqueCast(ty) => {
PlaceTy::from_ty(*ty)
}
ProjectionElem::Subtype(ty)
| ProjectionElem::OpaqueCast(ty)
| ProjectionElem::UnwrapUnsafeBinder(ty) => PlaceTy::from_ty(*ty),
ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
},
};

View file

@ -167,7 +167,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(..),
| ProjectionElem::Downcast(..)
| ProjectionElem::UnwrapUnsafeBinder(_),
],
} => bug!("Unexpected immutable place."),
}

View file

@ -1398,6 +1398,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
self.consume_operand(location, (operand, span), state);
}
}
Rvalue::WrapUnsafeBinder(op, _) => {
self.consume_operand(location, (op, span), state);
}
}
}
@ -1770,7 +1774,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
// So it's safe to skip these.
ProjectionElem::OpaqueCast(_)
| ProjectionElem::Subtype(_)
| ProjectionElem::Downcast(_, _) => (),
| ProjectionElem::Downcast(_, _)
| ProjectionElem::UnwrapUnsafeBinder(_) => (),
}
place_ty = place_ty.projection_ty(tcx, elem);
@ -2004,6 +2009,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
// FIXME: is this true even if P is an adt with a dtor?
{ }
ProjectionElem::UnwrapUnsafeBinder(_) => {
check_parent_of_field(self, location, place_base, span, state);
}
// assigning to (*P) requires P to be initialized
ProjectionElem::Deref => {
self.check_if_full_path_is_moved(
@ -2384,7 +2393,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(..)
| ProjectionElem::OpaqueCast { .. }
| ProjectionElem::Downcast(..) => {
| ProjectionElem::Downcast(..)
| ProjectionElem::UnwrapUnsafeBinder(_) => {
let upvar_field_projection = self.is_upvar_field_projection(place);
if let Some(field) = upvar_field_projection {
let upvar = &self.upvars[field.index()];

View file

@ -250,7 +250,8 @@ fn place_components_conflict<'tcx>(
| (ProjectionElem::Subslice { .. }, _, _)
| (ProjectionElem::OpaqueCast { .. }, _, _)
| (ProjectionElem::Subtype(_), _, _)
| (ProjectionElem::Downcast { .. }, _, _) => {
| (ProjectionElem::Downcast { .. }, _, _)
| (ProjectionElem::UnwrapUnsafeBinder(_), _, _) => {
// Recursive case. This can still be disjoint on a
// further iteration if this a shallow access and
// there's a deref later on, e.g., a borrow
@ -519,5 +520,9 @@ fn place_projection_conflict<'tcx>(
pi1_elem,
pi2_elem
),
(ProjectionElem::UnwrapUnsafeBinder(_), _) => {
todo!()
}
}
}

View file

@ -325,6 +325,10 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
self.consume_operand(location, operand);
}
}
Rvalue::WrapUnsafeBinder(op, _) => {
self.consume_operand(location, op);
}
}
}

View file

@ -66,6 +66,10 @@ impl<'tcx> Iterator for Prefixes<'tcx> {
self.next = Some(cursor_base);
return Some(cursor);
}
ProjectionElem::UnwrapUnsafeBinder(_) => {
self.next = Some(cursor_base);
return Some(cursor);
}
ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::OpaqueCast { .. }

View file

@ -302,6 +302,25 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
)
.unwrap();
}
ProjectionElem::UnwrapUnsafeBinder(ty) => {
let ty::UnsafeBinder(binder_ty) = *base_ty.ty.kind() else {
unreachable!();
};
let found_ty = self.typeck.infcx.instantiate_binder_with_fresh_vars(
self.body().source_info(location).span,
BoundRegionConversionTime::HigherRankedType,
binder_ty.into(),
);
self.typeck
.relate_types(
ty,
context.ambient_variance(),
found_ty,
location.to_locations(),
ConstraintCategory::Boring,
)
.unwrap();
}
ProjectionElem::Subtype(_) => {
bug!("ProjectionElem::Subtype shouldn't exist in borrowck")
}
@ -2233,6 +2252,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_operand(right, location);
}
Rvalue::WrapUnsafeBinder(op, ty) => {
self.check_operand(op, location);
let operand_ty = op.ty(self.body, self.tcx());
let ty::UnsafeBinder(binder_ty) = *ty.kind() else {
unreachable!();
};
let expected_ty = self.infcx.instantiate_binder_with_fresh_vars(
self.body().source_info(location).span,
BoundRegionConversionTime::HigherRankedType,
binder_ty.into(),
);
self.sub_types(
operand_ty,
expected_ty,
location.to_locations(),
ConstraintCategory::Boring,
)
.unwrap();
}
Rvalue::RawPtr(..)
| Rvalue::ThreadLocalRef(..)
| Rvalue::Len(..)
@ -2258,7 +2298,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| Rvalue::NullaryOp(..)
| Rvalue::CopyForDeref(..)
| Rvalue::UnaryOp(..)
| Rvalue::Discriminant(..) => None,
| Rvalue::Discriminant(..)
| Rvalue::WrapUnsafeBinder(..) => None,
Rvalue::Aggregate(aggregate, _) => match **aggregate {
AggregateKind::Adt(_, _, _, user_ty, _) => user_ty,
@ -2450,7 +2491,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
| ProjectionElem::Subslice { .. }
| ProjectionElem::UnwrapUnsafeBinder(_) => {
// other field access
}
ProjectionElem::Subtype(_) => {

View file

@ -925,6 +925,10 @@ fn codegen_stmt<'tcx>(
}
crate::discriminant::codegen_set_discriminant(fx, lval, variant_index);
}
Rvalue::WrapUnsafeBinder(ref operand, _to_ty) => {
let operand = codegen_operand(fx, operand);
lval.write_cvalue_transmute(fx, operand);
}
}
}
StatementKind::StorageLive(_)
@ -993,7 +997,9 @@ pub(crate) fn codegen_place<'tcx>(
cplace = cplace.place_deref(fx);
}
PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"),
PlaceElem::Subtype(ty) => cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)),
PlaceElem::Subtype(ty) | PlaceElem::UnwrapUnsafeBinder(ty) => {
cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty));
}
PlaceElem::Field(field, _ty) => {
cplace = cplace.place_field(fx, field);
}

View file

@ -4,6 +4,8 @@ use libc::{c_char, c_uint};
use super::ffi::{BasicBlock, Metadata, Module, Type, Value};
use crate::llvm::Bool;
#[link(name = "llvm-wrapper", kind = "static")]
extern "C" {
// Enzyme
pub fn LLVMRustHasMetadata(I: &Value, KindID: c_uint) -> bool;
@ -12,10 +14,13 @@ extern "C" {
pub fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>;
pub fn LLVMRustEraseInstFromParent(V: &Value);
pub fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value;
pub fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool;
}
extern "C" {
// Enzyme
pub fn LLVMDumpModule(M: &Module);
pub fn LLVMDumpValue(V: &Value);
pub fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool;
pub fn LLVMGetFunctionCallConv(F: &Value) -> c_uint;
pub fn LLVMGetReturnType(T: &Type) -> &Type;
pub fn LLVMGetParams(Fnc: &Value, parms: *mut &Value);

View file

@ -502,6 +502,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bug!("encountered OpaqueCast({ty}) in codegen")
}
mir::ProjectionElem::Subtype(ty) => cg_base.project_type(bx, self.monomorphize(ty)),
mir::ProjectionElem::UnwrapUnsafeBinder(ty) => {
cg_base.project_type(bx, self.monomorphize(ty))
}
mir::ProjectionElem::Index(index) => {
let index = &mir::Operand::Copy(mir::Place::from(index));
let index = self.codegen_operand(bx, index);

View file

@ -823,6 +823,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandRef { val: OperandValue::Immediate(val), layout: box_layout }
}
mir::Rvalue::WrapUnsafeBinder(ref operand, binder_ty) => {
let operand = self.codegen_operand(bx, operand);
let binder_ty = self.monomorphize(binder_ty);
let layout = bx.cx().layout_of(binder_ty);
OperandRef { val: operand.val, layout }
}
}
}
@ -1123,7 +1129,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::Discriminant(..) |
mir::Rvalue::NullaryOp(..) |
mir::Rvalue::ThreadLocalRef(_) |
mir::Rvalue::Use(..) => // (*)
mir::Rvalue::Use(..) |
mir::Rvalue::WrapUnsafeBinder(..) => // (*)
true,
// Arrays are always aggregates, so it's not worth checking anything here.
// (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.)

View file

@ -728,6 +728,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
);
}
}
Rvalue::WrapUnsafeBinder(..) => {
// Unsafe binders are always trivial to create.
}
}
}

View file

@ -258,6 +258,8 @@ where
in_place::<Q, _>(cx, in_local, place.as_ref())
}
Rvalue::WrapUnsafeBinder(op, _) => in_operand::<Q, _>(cx, in_local, op),
Rvalue::Aggregate(kind, operands) => {
// Return early if we know that the struct or enum being constructed is always
// qualified.
@ -297,7 +299,8 @@ where
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Downcast(_, _)
| ProjectionElem::Index(_) => {}
| ProjectionElem::Index(_)
| ProjectionElem::UnwrapUnsafeBinder(_) => {}
}
let base_ty = place_base.ty(cx.body, cx.tcx);

View file

@ -202,7 +202,8 @@ where
| mir::Rvalue::NullaryOp(..)
| mir::Rvalue::UnaryOp(..)
| mir::Rvalue::Discriminant(..)
| mir::Rvalue::Aggregate(..) => {}
| mir::Rvalue::Aggregate(..)
| mir::Rvalue::WrapUnsafeBinder(..) => {}
}
}

View file

@ -747,7 +747,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = self.adjust_nan(a.min(b), &[a, b]);
let res = if 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 {
self.adjust_nan(a.min(b), &[a, b])
};
self.write_scalar(res, dest)?;
interp_ok(())
}
@ -762,7 +768,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let res = self.adjust_nan(a.max(b), &[a, b]);
let res = if 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 {
self.adjust_nan(a.max(b), &[a, b])
};
self.write_scalar(res, dest)?;
interp_ok(())
}

View file

@ -278,6 +278,12 @@ pub trait Machine<'tcx>: Sized {
F2::NAN
}
/// Determines the result of `min`/`max` on floats when the arguments are equal.
fn equal_float_min_max<F: Float>(_ecx: &InterpCx<'tcx, Self>, a: F, _b: F) -> F {
// By default, we pick the left argument.
a
}
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {

View file

@ -381,6 +381,7 @@ where
OpaqueCast(ty) => {
span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck")
}
UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?,
// We don't want anything happening here, this is here as a dummy.
Subtype(_) => base.transmute(base.layout(), self)?,
Field(field, _) => self.project_field(base, field.index())?,

View file

@ -277,6 +277,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let discr = self.discriminant_for_variant(op.layout.ty, variant)?;
self.write_immediate(*discr, &dest)?;
}
WrapUnsafeBinder(ref op, _ty) => {
// Constructing an unsafe binder acts like a transmute
// since the operand's layout does not change.
let op = self.eval_operand(op, None)?;
self.copy_op_allow_transmute(&op, &dest)?;
}
}
trace!("{:?}", self.dump_place(&dest));

View file

@ -1658,8 +1658,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir_ty: Option<&'tcx hir::Ty<'tcx>>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
self.dcx().span_err(inner_expr.span, "unsafe binder casts are not fully implemented");
match kind {
hir::UnsafeBinderCastKind::Wrap => {
let ascribed_ty =

View file

@ -253,6 +253,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
let mut expr_ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
for a in &adj {
match a.kind {
Adjust::NeverToAny => {
@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None,
expr.span,
overloaded_deref.method_call(self.tcx),
self.tcx.mk_args(&[a.target.into()]),
self.tcx.mk_args(&[expr_ty.into()]),
);
}
Adjust::Deref(None) => {
@ -283,6 +285,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// No effects to enforce here.
}
}
expr_ty = a.target;
}
let autoborrow_mut = adj.iter().any(|adj| {

View file

@ -1248,6 +1248,10 @@ impl<'tcx> Debug for Rvalue<'tcx> {
ShallowInitBox(ref place, ref ty) => {
with_no_trimmed_paths!(write!(fmt, "ShallowInitBox({place:?}, {ty})"))
}
WrapUnsafeBinder(ref op, ty) => {
with_no_trimmed_paths!(write!(fmt, "wrap_binder!({op:?}; {ty})"))
}
}
}
}
@ -1308,6 +1312,9 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
ProjectionElem::Index(_)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {}
ProjectionElem::UnwrapUnsafeBinder(_) => {
write!(fmt, "unwrap_binder!(")?;
}
}
}
@ -1356,6 +1363,9 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
ProjectionElem::Subslice { from, to, from_end: false } => {
write!(fmt, "[{from:?}..{to:?}]")?;
}
ProjectionElem::UnwrapUnsafeBinder(ty) => {
write!(fmt, "; {ty})")?;
}
}
}

View file

@ -62,7 +62,8 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => false,
| Self::Downcast(_, _)
| Self::UnwrapUnsafeBinder(..) => false,
}
}
@ -76,7 +77,8 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _) => true,
| Self::Downcast(_, _)
| Self::UnwrapUnsafeBinder(..) => true,
}
}
@ -102,6 +104,9 @@ impl<V, T> ProjectionElem<V, T> {
| Self::Subtype(_)
| Self::OpaqueCast(_)
| Self::Subslice { .. } => false,
// FIXME(unsafe_binders): Figure this out.
Self::UnwrapUnsafeBinder(..) => false,
}
}
}
@ -443,7 +448,8 @@ impl<'tcx> Rvalue<'tcx> {
| Rvalue::UnaryOp(_, _)
| Rvalue::Discriminant(_)
| Rvalue::Aggregate(_, _)
| Rvalue::ShallowInitBox(_, _) => true,
| Rvalue::ShallowInitBox(_, _)
| Rvalue::WrapUnsafeBinder(_, _) => true,
}
}
}

View file

@ -1276,6 +1276,10 @@ pub enum ProjectionElem<V, T> {
/// requiring an intermediate variable.
OpaqueCast(T),
/// A transmute from an unsafe binder to the type that it wraps. This is a projection
/// of a place, so it doesn't necessarily constitute a move out of the binder.
UnwrapUnsafeBinder(T),
/// A `Subtype(T)` projection is applied to any `StatementKind::Assign` where
/// type of lvalue doesn't match the type of rvalue, the primary goal is making subtyping
/// explicit during optimizations and codegen.
@ -1493,6 +1497,9 @@ pub enum Rvalue<'tcx> {
/// optimizations and codegen backends that previously had to handle deref operations anywhere
/// in a place.
CopyForDeref(Place<'tcx>),
/// Wraps a value in an unsafe binder.
WrapUnsafeBinder(Operand<'tcx>, Ty<'tcx>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]

View file

@ -146,6 +146,11 @@ impl<'tcx> PlaceTy<'tcx> {
ProjectionElem::Subtype(ty) => {
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
}
// FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.
ProjectionElem::UnwrapUnsafeBinder(ty) => {
PlaceTy::from_ty(handle_opaque_cast_and_subtype(&self, ty))
}
};
debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer);
answer
@ -241,6 +246,7 @@ impl<'tcx> Rvalue<'tcx> {
},
Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
Rvalue::WrapUnsafeBinder(_, ty) => ty,
}
}

View file

@ -1,6 +1,7 @@
//! `TypeFoldable` implementations for MIR types
use rustc_ast::InlineAsmTemplatePiece;
use rustc_hir::UnsafeBinderCastKind;
use rustc_hir::def_id::LocalDefId;
use super::*;
@ -21,6 +22,7 @@ TrivialTypeTraversalImpls! {
SwitchTargets,
CoroutineKind,
CoroutineSavedLocal,
UnsafeBinderCastKind,
}
TrivialTypeTraversalImpls! {

View file

@ -781,6 +781,11 @@ macro_rules! make_mir_visitor {
self.visit_operand(operand, location);
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
Rvalue::WrapUnsafeBinder(op, ty) => {
self.visit_operand(op, location);
self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
}
}
@ -1151,6 +1156,11 @@ macro_rules! visit_place_fns {
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::Subtype(new_ty)) } else { None }
}
PlaceElem::UnwrapUnsafeBinder(ty) => {
let mut new_ty = ty;
self.visit_ty(&mut new_ty, TyContext::Location(location));
if ty != new_ty { Some(PlaceElem::UnwrapUnsafeBinder(new_ty)) } else { None }
}
PlaceElem::Deref
| PlaceElem::ConstantIndex { .. }
| PlaceElem::Subslice { .. }
@ -1219,7 +1229,8 @@ macro_rules! visit_place_fns {
match elem {
ProjectionElem::OpaqueCast(ty)
| ProjectionElem::Subtype(ty)
| ProjectionElem::Field(_, ty) => {
| ProjectionElem::Field(_, ty)
| ProjectionElem::UnwrapUnsafeBinder(ty) => {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(local) => {

View file

@ -489,6 +489,19 @@ pub enum ExprKind<'tcx> {
user_ty: UserTy<'tcx>,
user_ty_span: Span,
},
/// An unsafe binder cast on a place, e.g. `unwrap_binder!(*ptr)`.
PlaceUnwrapUnsafeBinder {
source: ExprId,
},
/// An unsafe binder cast on a value, e.g. `unwrap_binder!(rvalue())`,
/// which makes a temporary.
ValueUnwrapUnsafeBinder {
source: ExprId,
},
/// Construct an unsafe binder, e.g. `wrap_binder(&ref)`.
WrapUnsafeBinder {
source: ExprId,
},
/// A closure definition.
Closure(Box<ClosureExpr<'tcx>>),
/// A literal.

View file

@ -136,6 +136,9 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
| ValueTypeAscription { source, user_ty: _, user_ty_span: _ } => {
visitor.visit_expr(&visitor.thir()[source])
}
PlaceUnwrapUnsafeBinder { source }
| ValueUnwrapUnsafeBinder { source }
| WrapUnsafeBinder { source } => visitor.visit_expr(&visitor.thir()[source]),
Closure(box ClosureExpr {
closure_id: _,
args: _,

View file

@ -361,6 +361,18 @@ mir_build_unreachable_pattern = unreachable pattern
.unreachable_pattern_let_binding = there is a binding of the same name; if you meant to pattern match against the value of that binding, that is a feature of constants that is not available for `let` bindings
.suggestion = remove the match arm
mir_build_unsafe_binder_cast_requires_unsafe =
unsafe binder cast is unsafe and requires unsafe block
.label = unsafe binder cast
.note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime
information that may be required to uphold safety guarantees of a type
mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
unsafe binder cast is unsafe and requires unsafe block or unsafe fn
.label = unsafe binder cast
.note = casting to or from an `unsafe<...>` binder type is unsafe since it erases lifetime
information that may be required to uphold safety guarantees of a type
mir_build_unsafe_field_requires_unsafe =
use of unsafe field is unsafe and requires unsafe block
.note = unsafe fields may carry library invariants

View file

@ -105,7 +105,8 @@ fn convert_to_hir_projections_and_truncate_for_capture(
ProjectionElem::OpaqueCast(_) | ProjectionElem::Subtype(..) => continue,
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
| ProjectionElem::Subslice { .. }
| ProjectionElem::UnwrapUnsafeBinder(_) => {
// We don't capture array-access projections.
// We can stop here as arrays are captured completely.
break;
@ -523,6 +524,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(PlaceBuilder::from(temp))
}
ExprKind::PlaceUnwrapUnsafeBinder { source } => {
let place_builder = unpack!(
block = this.expr_as_place(block, source, mutability, fake_borrow_temps,)
);
block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty)))
}
ExprKind::ValueUnwrapUnsafeBinder { source } => {
let source_expr = &this.thir[source];
let temp = unpack!(
block = this.as_temp(block, source_expr.temp_lifetime, source, mutability)
);
block.and(PlaceBuilder::from(temp).project(PlaceElem::UnwrapUnsafeBinder(expr.ty)))
}
ExprKind::Array { .. }
| ExprKind::Tuple { .. }
| ExprKind::Adt { .. }
@ -560,7 +575,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::OffsetOf { .. }
| ExprKind::Yield { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::Call { .. } => {
| ExprKind::Call { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
// these are not places, so we need to make a temporary.
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
let temp =
@ -776,7 +792,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Subtype(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => (),
| ProjectionElem::Subslice { .. }
| ProjectionElem::UnwrapUnsafeBinder(_) => (),
}
}
}

View file

@ -508,6 +508,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
}
ExprKind::WrapUnsafeBinder { source } => {
let source = unpack!(
block = this.as_operand(
block,
scope,
source,
LocalInfo::Boring,
NeedsTemporary::Maybe
)
);
block.and(Rvalue::WrapUnsafeBinder(source, expr.ty))
}
ExprKind::Yield { .. }
| ExprKind::Block { .. }
| ExprKind::Match { .. }
@ -532,7 +545,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Become { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {
| ExprKind::ValueTypeAscription { .. }
| ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. } => {
// these do not have corresponding `Rvalue` variants,
// so make an operand and then return that
debug_assert!(!matches!(

View file

@ -41,7 +41,9 @@ impl Category {
| ExprKind::UpvarRef { .. }
| ExprKind::VarRef { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => Some(Category::Place),
| ExprKind::ValueTypeAscription { .. }
| ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place),
ExprKind::LogicalOp { .. }
| ExprKind::Match { .. }
@ -68,7 +70,8 @@ impl Category {
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::OffsetOf { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
| ExprKind::OffsetOf { .. }
| ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)),
ExprKind::ConstBlock { .. }
| ExprKind::Literal { .. }

View file

@ -554,7 +554,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {
| ExprKind::ValueTypeAscription { .. }
| ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr_id));
@ -613,7 +615,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ConstParam { .. }
| ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. }
| ExprKind::OffsetOf { .. } => {
| ExprKind::OffsetOf { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
debug_assert!(match Category::of(&expr.kind).unwrap() {
// should be handled above
Category::Rvalue(RvalueFunc::Into) => false,

View file

@ -439,6 +439,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::NeverToAny { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. }
| ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. }
| ExprKind::WrapUnsafeBinder { .. }
| ExprKind::PointerCoercion { .. }
| ExprKind::Repeat { .. }
| ExprKind::StaticRef { .. }
@ -680,6 +683,11 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}
}
ExprKind::PlaceUnwrapUnsafeBinder { .. }
| ExprKind::ValueUnwrapUnsafeBinder { .. }
| ExprKind::WrapUnsafeBinder { .. } => {
self.requires_unsafe(expr.span, UnsafeBinderCast);
}
_ => {}
}
visit::walk_expr(self, expr);
@ -728,6 +736,7 @@ enum UnsafeOpKind {
/// (e.g., with `-C target-feature`).
build_enabled: Vec<Symbol>,
},
UnsafeBinderCast,
}
use UnsafeOpKind::*;
@ -891,6 +900,15 @@ impl UnsafeOpKind {
unsafe_not_inherited_note,
},
),
UnsafeBinderCast => tcx.emit_node_span_lint(
UNSAFE_OP_IN_UNSAFE_FN,
hir_id,
span,
UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe {
span,
unsafe_not_inherited_note,
},
),
}
}
@ -1099,6 +1117,15 @@ impl UnsafeOpKind {
function: tcx.def_path_str(*function),
});
}
UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => {
dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
span,
unsafe_not_inherited_note,
});
}
UnsafeBinderCast => {
dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
}
}
}
}

View file

@ -160,6 +160,18 @@ pub(crate) struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
}
#[derive(LintDiagnostic)]
#[diag(
mir_build_unsafe_binder_cast_requires_unsafe,
code = E0133,
)]
pub(crate) struct UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe {
#[label]
pub(crate) span: Span,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedLintNote>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe, code = E0133)]
#[help]
@ -494,6 +506,32 @@ pub(crate) struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
}
#[derive(Diagnostic)]
#[diag(
mir_build_unsafe_binder_cast_requires_unsafe,
code = E0133,
)]
pub(crate) struct UnsafeBinderCastRequiresUnsafe {
#[primary_span]
#[label]
pub(crate) span: Span,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
}
#[derive(Diagnostic)]
#[diag(
mir_build_unsafe_binder_cast_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
code = E0133,
)]
pub(crate) struct UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
#[primary_span]
#[label]
pub(crate) span: Span,
#[subdiagnostic]
pub(crate) unsafe_not_inherited_note: Option<UnsafeNotInheritedNote>,
}
#[derive(Subdiagnostic)]
#[label(mir_build_unsafe_not_inherited)]
pub(crate) struct UnsafeNotInheritedNote {

View file

@ -1,5 +1,6 @@
use itertools::Itertools;
use rustc_abi::{FIRST_VARIANT, FieldIdx};
use rustc_ast::UnsafeBinderCastKind;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@ -910,8 +911,19 @@ impl<'tcx> Cx<'tcx> {
}
}
hir::ExprKind::UnsafeBinderCast(_kind, _source, _ty) => {
unreachable!("unsafe binders are not yet implemented")
hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Unwrap, source, _ty) => {
// FIXME(unsafe_binders): Take into account the ascribed type, too.
let mirrored = self.mirror_expr(source);
if source.is_syntactic_place_expr() {
ExprKind::PlaceUnwrapUnsafeBinder { source: mirrored }
} else {
ExprKind::ValueUnwrapUnsafeBinder { source: mirrored }
}
}
hir::ExprKind::UnsafeBinderCast(UnsafeBinderCastKind::Wrap, source, _ty) => {
// FIXME(unsafe_binders): Take into account the ascribed type, too.
let mirrored = self.mirror_expr(source);
ExprKind::WrapUnsafeBinder { source: mirrored }
}
hir::ExprKind::DropTemps(source) => ExprKind::Use { source: self.mirror_expr(source) },

View file

@ -326,9 +326,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
| Use { source }
| PointerCoercion { source, .. }
| PlaceTypeAscription { source, .. }
| ValueTypeAscription { source, .. } => {
self.is_known_valid_scrutinee(&self.thir()[*source])
}
| ValueTypeAscription { source, .. }
| PlaceUnwrapUnsafeBinder { source }
| ValueUnwrapUnsafeBinder { source }
| WrapUnsafeBinder { source } => self.is_known_valid_scrutinee(&self.thir()[*source]),
// These diverge.
Become { .. } | Break { .. } | Continue { .. } | Return { .. } => true,

View file

@ -477,6 +477,24 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_expr(*source, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
PlaceUnwrapUnsafeBinder { source } => {
print_indented!(self, "PlaceUnwrapUnsafeBinder {", depth_lvl);
print_indented!(self, "source:", depth_lvl + 1);
self.print_expr(*source, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
ValueUnwrapUnsafeBinder { source } => {
print_indented!(self, "ValueUnwrapUnsafeBinder {", depth_lvl);
print_indented!(self, "source:", depth_lvl + 1);
self.print_expr(*source, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
WrapUnsafeBinder { source } => {
print_indented!(self, "WrapUnsafeBinder {", depth_lvl);
print_indented!(self, "source:", depth_lvl + 1);
self.print_expr(*source, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
Closure(closure_expr) => {
print_indented!(self, "Closure {", depth_lvl);
print_indented!(self, "closure_expr:", depth_lvl + 1);

View file

@ -97,7 +97,8 @@ where
| Rvalue::UnaryOp(..)
| Rvalue::Discriminant(..)
| Rvalue::Aggregate(..)
| Rvalue::CopyForDeref(..) => {}
| Rvalue::CopyForDeref(..)
| Rvalue::WrapUnsafeBinder(..) => {}
}
}

View file

@ -32,6 +32,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> {
}
ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()),
ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()),
}
}
}

View file

@ -208,7 +208,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
| ty::Infer(_)
| ty::Error(_)
| ty::Placeholder(_) => bug!(
"When Place contains ProjectionElem::Field it's type shouldn't be {place_ty:#?}"
"When Place contains ProjectionElem::Field its type shouldn't be {place_ty:#?}"
),
},
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
@ -226,6 +226,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
}
_ => bug!("Unexpected type {place_ty:#?}"),
},
ProjectionElem::UnwrapUnsafeBinder(_) => {}
// `OpaqueCast`:Only transmutes the type, so no moves there.
// `Downcast` :Only changes information about a `Place` without moving.
// `Subtype` :Only transmutes the type, so moves.
@ -399,7 +400,8 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
| Rvalue::Repeat(ref operand, _)
| Rvalue::Cast(_, ref operand, _)
| Rvalue::ShallowInitBox(ref operand, _)
| Rvalue::UnaryOp(_, ref operand) => self.gather_operand(operand),
| Rvalue::UnaryOp(_, ref operand)
| Rvalue::WrapUnsafeBinder(ref operand, _) => self.gather_operand(operand),
Rvalue::BinaryOp(ref _binop, box (ref lhs, ref rhs)) => {
self.gather_operand(lhs);
self.gather_operand(rhs);

View file

@ -504,7 +504,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
| Rvalue::Cast(..)
| Rvalue::BinaryOp(..)
| Rvalue::Aggregate(..)
| Rvalue::ShallowInitBox(..) => {
| Rvalue::ShallowInitBox(..)
| Rvalue::WrapUnsafeBinder(..) => {
// No modification is possible through these r-values.
return ValueOrPlace::TOP;
}

View file

@ -575,6 +575,9 @@ impl WriteInfo {
self.add_operand(op);
}
}
Rvalue::WrapUnsafeBinder(op, _) => {
self.add_operand(op);
}
Rvalue::ThreadLocalRef(_)
| Rvalue::NullaryOp(_, _)
| Rvalue::Ref(_, _, _)

View file

@ -476,6 +476,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
ProjectionElem::UnwrapUnsafeBinder(ty) => {
ProjectionElem::UnwrapUnsafeBinder(ty)
}
// This should have been replaced by a `ConstantIndex` earlier.
ProjectionElem::Index(_) => return None,
};
@ -713,6 +716,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
}
ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty),
ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty),
ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
};
Some(self.insert(Value::Projection(value, proj)))
@ -867,6 +871,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
self.simplify_place_projection(place, location);
return self.new_pointer(*place, AddressKind::Address(mutbl));
}
Rvalue::WrapUnsafeBinder(ref mut op, _) => {
return self.simplify_operand(op, location);
}
// Operations.
Rvalue::Len(ref mut place) => return self.simplify_len(place, location),
@ -931,6 +938,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
ProjectionElem::OpaqueCast(idx) => ProjectionElem::OpaqueCast(idx),
ProjectionElem::Subtype(idx) => ProjectionElem::Subtype(idx),
ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(ty),
})
}

View file

@ -444,7 +444,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
| Rvalue::Cast(..)
| Rvalue::ShallowInitBox(..)
| Rvalue::Discriminant(..)
| Rvalue::NullaryOp(..) => {}
| Rvalue::NullaryOp(..)
| Rvalue::WrapUnsafeBinder(..) => {}
}
// FIXME we need to revisit this for #67176
@ -546,7 +547,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let val: Value<'_> = match *rvalue {
ThreadLocalRef(_) => return None,
Use(ref operand) => self.eval_operand(operand)?.into(),
Use(ref operand) | WrapUnsafeBinder(ref operand, _) => {
self.eval_operand(operand)?.into()
}
CopyForDeref(place) => self.eval_place(place)?.into(),

View file

@ -293,7 +293,8 @@ impl<'tcx> Validator<'_, 'tcx> {
// Recurse directly.
ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Subslice { .. } => {}
| ProjectionElem::Subslice { .. }
| ProjectionElem::UnwrapUnsafeBinder(_) => {}
// Never recurse.
ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
@ -426,7 +427,9 @@ impl<'tcx> Validator<'_, 'tcx> {
fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match rvalue {
Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => {
Rvalue::Use(operand)
| Rvalue::Repeat(operand, _)
| Rvalue::WrapUnsafeBinder(operand, _) => {
self.validate_operand(operand)?;
}
Rvalue::CopyForDeref(place) => {

View file

@ -807,6 +807,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
)
}
}
ProjectionElem::UnwrapUnsafeBinder(unwrapped_ty) => {
let binder_ty = place_ref.ty(&self.body.local_decls, self.tcx);
let ty::UnsafeBinder(binder_ty) = *binder_ty.ty.kind() else {
self.fail(
location,
format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"),
);
return;
};
let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty);
if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) {
self.fail(
location,
format!(
"Cannot unwrap unsafe binder {binder_ty:?} into type {unwrapped_ty:?}"
),
);
}
}
_ => {}
}
self.super_projection_elem(place_ref, elem, context, location);
@ -1362,6 +1381,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| Rvalue::RawPtr(_, _)
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _)
| Rvalue::Discriminant(_) => {}
Rvalue::WrapUnsafeBinder(op, ty) => {
let unwrapped_ty = op.ty(self.body, self.tcx);
let ty::UnsafeBinder(binder_ty) = *ty.kind() else {
self.fail(
location,
format!("WrapUnsafeBinder does not produce a ty::UnsafeBinder"),
);
return;
};
let binder_inner_ty = self.tcx.instantiate_bound_regions_with_erased(*binder_ty);
if !self.mir_assign_valid_types(unwrapped_ty, binder_inner_ty) {
self.fail(
location,
format!("Cannot wrap {unwrapped_ty:?} into unsafe binder {binder_ty:?}"),
);
}
}
}
self.super_rvalue(rvalue, location);
}

View file

@ -1522,6 +1522,14 @@ impl<'tcx> Liveness<'_, 'tcx> {
}
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
if let Some(intrinsic) =
self.ir.tcx.intrinsic(self.ir.tcx.hir().body_owner_def_id(body.id()))
{
if intrinsic.must_be_overridden {
return;
}
}
for p in body.params {
self.check_unused_vars_in_pat(
p.pat,

View file

@ -217,6 +217,7 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> {
stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), ty.stable(tables))
}
CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)),
WrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
}
}
}
@ -395,6 +396,7 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
Downcast(_, idx) => stable_mir::mir::ProjectionElem::Downcast(idx.stable(tables)),
OpaqueCast(ty) => stable_mir::mir::ProjectionElem::OpaqueCast(ty.stable(tables)),
Subtype(ty) => stable_mir::mir::ProjectionElem::Subtype(ty.stable(tables)),
UnwrapUnsafeBinder(..) => todo!("FIXME(unsafe_binders):"),
}
}
}

View file

@ -2152,7 +2152,6 @@ symbols! {
unwrap,
unwrap_binder,
unwrap_or,
unwrap_unsafe_binder,
use_extern_macros,
use_nested_groups,
used,

View file

@ -1,7 +1,7 @@
use std::ops::Deref;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
@ -98,9 +98,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
param_env: ty::ParamEnv<'tcx>,
arg: ty::GenericArg<'tcx>,
) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
obligations.into_iter().map(|obligation| obligation.into()).collect()
})
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
.map(|obligations| {
obligations.into_iter().map(|obligation| obligation.into()).collect()
})
}
fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {

View file

@ -1,25 +1,21 @@
use std::marker::PhantomData;
use std::mem;
use std::ops::ControlFlow;
use rustc_data_structures::thinvec::ExtractIf;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
use rustc_infer::traits::{
self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine,
FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
use tracing::{instrument, trace};
use tracing::instrument;
use self::derive_errors::*;
use super::Certainty;
use super::delegate::SolverDelegate;
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
use crate::traits::{FulfillmentError, ScrubbedTraitError};
mod derive_errors;
/// A trait engine using the new trait solver.
///
@ -244,483 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'
}
}
}
fn fulfillment_error_for_no_solution<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
let code = match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
FulfillmentErrorCode::Project(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
let ct_ty = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => {
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
}
ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
ty::ConstKind::Value(cv) => cv.ty,
kind => span_bug!(
obligation.cause.span,
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
),
};
FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
ct,
ct_ty,
expected_ty,
})
}
ty::PredicateKind::NormalizesTo(..) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
ty::PredicateKind::AliasRelate(_, _, _) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
ty::PredicateKind::Subtype(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(a, b);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
ty::PredicateKind::Coerce(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(b, a);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::DynCompatible(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::Select(SelectionError::Unimplemented)
}
ty::PredicateKind::ConstEquate(..) => {
bug!("unexpected goal: {obligation:?}")
}
};
FulfillmentError { obligation, code, root_obligation }
}
fn fulfillment_error_for_stalled<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
let (code, refine_obligation) = infcx.probe(|_| {
match <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
.0
{
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
(FulfillmentErrorCode::Ambiguity { overflow: None }, true)
}
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways.
// We discard the inference constraints from overflowing goals, so
// recomputing the goal again during `find_best_leaf_obligation` may apply
// inference guidance that makes other goals go from ambig -> pass, for example.
//
// FIXME: We should probably just look into overflows here.
false,
),
Ok((_, Certainty::Yes)) => {
bug!("did not expect successful goal when collecting ambiguity errors")
}
Err(_) => {
bug!("did not expect selection error when collecting ambiguity errors")
}
}
});
FulfillmentError {
obligation: if refine_obligation {
find_best_leaf_obligation(infcx, &root_obligation, true)
} else {
root_obligation.clone()
},
code,
root_obligation,
}
}
fn fulfillment_error_for_overflow<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
FulfillmentError {
obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
root_obligation,
}
}
fn find_best_leaf_obligation<'tcx>(
infcx: &InferCtxt<'tcx>,
obligation: &PredicateObligation<'tcx>,
consider_ambiguities: bool,
) -> PredicateObligation<'tcx> {
let obligation = infcx.resolve_vars_if_possible(obligation.clone());
// FIXME: we use a probe here as the `BestObligation` visitor does not
// check whether it uses candidates which get shadowed by where-bounds.
//
// We should probably fix the visitor to not do so instead, as this also
// means the leaf obligation may be incorrect.
infcx
.fudge_inference_if_ok(|| {
infcx
.visit_proof_tree(obligation.clone().into(), &mut BestObligation {
obligation: obligation.clone(),
consider_ambiguities,
})
.break_value()
.ok_or(())
})
.unwrap_or(obligation)
}
struct BestObligation<'tcx> {
obligation: PredicateObligation<'tcx>,
consider_ambiguities: bool,
}
impl<'tcx> BestObligation<'tcx> {
fn with_derived_obligation(
&mut self,
derived_obligation: PredicateObligation<'tcx>,
and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
) -> <Self as ProofTreeVisitor<'tcx>>::Result {
let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
let res = and_then(self);
self.obligation = old_obligation;
res
}
/// Filter out the candidates that aren't interesting to visit for the
/// purposes of reporting errors. For ambiguities, we only consider
/// candidates that may hold. For errors, we only consider candidates that
/// *don't* hold and which have impl-where clauses that also don't hold.
fn non_trivial_candidates<'a>(
&self,
goal: &'a inspect::InspectGoal<'a, 'tcx>,
) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
let mut candidates = goal.candidates();
match self.consider_ambiguities {
true => {
// If we have an ambiguous obligation, we must consider *all* candidates
// that hold, or else we may guide inference causing other goals to go
// from ambig -> pass/fail.
candidates.retain(|candidate| candidate.result().is_ok());
}
false => {
// If we have >1 candidate, one may still be due to "boring" reasons, like
// an alias-relate that failed to hold when deeply evaluated. We really
// don't care about reasons like this.
if candidates.len() > 1 {
candidates.retain(|candidate| {
goal.infcx().probe(|_| {
candidate.instantiate_nested_goals(self.span()).iter().any(
|nested_goal| {
matches!(
nested_goal.source(),
GoalSource::ImplWhereBound
| GoalSource::AliasBoundConstCondition
| GoalSource::InstantiateHigherRanked
| GoalSource::AliasWellFormed
) && match self.consider_ambiguities {
true => {
matches!(
nested_goal.result(),
Ok(Certainty::Maybe(MaybeCause::Ambiguity))
)
}
false => matches!(nested_goal.result(), Err(_)),
}
},
)
})
});
}
// Prefer a non-rigid candidate if there is one.
if candidates.len() > 1 {
candidates.retain(|candidate| {
!matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
});
}
}
}
candidates
}
}
impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
type Result = ControlFlow<PredicateObligation<'tcx>>;
fn span(&self) -> rustc_span::Span {
self.obligation.cause.span
}
#[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
let candidates = self.non_trivial_candidates(goal);
trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
let [candidate] = candidates.as_slice() else {
return ControlFlow::Break(self.obligation.clone());
};
// Don't walk into impls that have `do_not_recommend`.
if let inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} = candidate.kind()
&& goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
{
return ControlFlow::Break(self.obligation.clone());
}
let tcx = goal.infcx().tcx;
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
// for normalizes-to.
let pred_kind = goal.goal().predicate.kind();
let child_mode = match pred_kind.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
ChildMode::Trait(pred_kind.rebind(pred))
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
ChildMode::Host(pred_kind.rebind(pred))
}
ty::PredicateKind::NormalizesTo(normalizes_to)
if matches!(
normalizes_to.alias.kind(tcx),
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
) =>
{
ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
trait_ref: normalizes_to.alias.trait_ref(tcx),
polarity: ty::PredicatePolarity::Positive,
}))
}
_ => ChildMode::PassThrough,
};
let nested_goals = candidate.instantiate_nested_goals(self.span());
// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
// an actual candidate, instead we should treat them as if the impl was never considered to
// have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
// instead of `impl<T: FnPtr> Trait for T`.
//
// We do this as a separate loop so that we do not choose to tell the user about some nested
// goal before we encounter a `T: FnPtr` nested goal.
for nested_goal in &nested_goals {
if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
&& let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
&& poly_trait_pred.def_id() == fn_ptr_trait
&& let Err(NoSolution) = nested_goal.result()
{
return ControlFlow::Break(self.obligation.clone());
}
}
let mut impl_where_bound_count = 0;
for nested_goal in nested_goals {
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
let make_obligation = |cause| Obligation {
cause,
param_env: nested_goal.goal().param_env,
predicate: nested_goal.goal().predicate,
recursion_depth: self.obligation.recursion_depth + 1,
};
let obligation;
match (child_mode, nested_goal.source()) {
(ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
continue;
}
(ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
obligation = make_obligation(derive_cause(
tcx,
candidate.kind(),
self.obligation.cause.clone(),
impl_where_bound_count,
parent_trait_pred,
));
impl_where_bound_count += 1;
}
(
ChildMode::Host(parent_host_pred),
GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
) => {
obligation = make_obligation(derive_host_cause(
tcx,
candidate.kind(),
self.obligation.cause.clone(),
impl_where_bound_count,
parent_host_pred,
));
impl_where_bound_count += 1;
}
// Skip over a higher-ranked predicate.
(_, GoalSource::InstantiateHigherRanked) => {
obligation = self.obligation.clone();
}
(ChildMode::PassThrough, _)
| (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
obligation = make_obligation(self.obligation.cause.clone());
}
}
// Skip nested goals that aren't the *reason* for our goal's failure.
match self.consider_ambiguities {
true if matches!(
nested_goal.result(),
Ok(Certainty::Maybe(MaybeCause::Ambiguity))
) => {}
false if matches!(nested_goal.result(), Err(_)) => {}
_ => continue,
}
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
}
// alias-relate may fail because the lhs or rhs can't be normalized,
// and therefore is treated as rigid.
if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
goal.depth() + 1,
self,
)
.break_value()
{
return ControlFlow::Break(obligation);
} else if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
goal.depth() + 1,
self,
)
.break_value()
{
return ControlFlow::Break(obligation);
}
}
ControlFlow::Break(self.obligation.clone())
}
}
#[derive(Debug, Copy, Clone)]
enum ChildMode<'tcx> {
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Trait(ty::PolyTraitPredicate<'tcx>),
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
// Skip trying to derive an `ObligationCause` from this obligation, and
// report *all* sub-obligations as if they came directly from the parent
// obligation.
PassThrough,
}
fn derive_cause<'tcx>(
tcx: TyCtxt<'tcx>,
candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
mut cause: ObligationCause<'tcx>,
idx: usize,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> ObligationCause<'tcx> {
match candidate_kind {
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} => {
if let Some((_, span)) =
tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
{
cause = cause.derived_cause(parent_trait_pred, |derived| {
ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
derived,
impl_or_alias_def_id: impl_def_id,
impl_def_predicate_index: Some(idx),
span,
}))
})
}
}
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::BuiltinImpl(..),
result: _,
} => {
cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
}
_ => {}
};
cause
}
fn derive_host_cause<'tcx>(
tcx: TyCtxt<'tcx>,
candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
mut cause: ObligationCause<'tcx>,
idx: usize,
parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
) -> ObligationCause<'tcx> {
match candidate_kind {
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} => {
if let Some((_, span)) = tcx
.predicates_of(impl_def_id)
.instantiate_identity(tcx)
.into_iter()
.chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
|(trait_ref, span)| {
(
trait_ref.to_host_effect_clause(
tcx,
parent_host_pred.skip_binder().constness,
),
span,
)
},
))
.nth(idx)
{
cause =
cause.derived_host_cause(parent_host_pred, |derived| {
ObligationCauseCode::ImplDerivedHost(Box::new(
traits::ImplDerivedHostCause { derived, impl_def_id, span },
))
})
}
}
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::BuiltinImpl(..),
result: _,
} => {
cause =
cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
}
_ => {}
};
cause
}

View file

@ -0,0 +1,527 @@
use std::ops::ControlFlow;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
use rustc_infer::traits::{
self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
PredicateObligation, SelectionError,
};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
use rustc_type_ir::solve::{Goal, NoSolution};
use tracing::{instrument, trace};
use crate::solve::Certainty;
use crate::solve::delegate::SolverDelegate;
use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
pub(super) fn fulfillment_error_for_no_solution<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
let code = match obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
FulfillmentErrorCode::Project(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
let ct_ty = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => {
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
}
ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
ty::ConstKind::Value(cv) => cv.ty,
kind => span_bug!(
obligation.cause.span,
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
),
};
FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
ct,
ct_ty,
expected_ty,
})
}
ty::PredicateKind::NormalizesTo(..) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
ty::PredicateKind::AliasRelate(_, _, _) => {
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
}
ty::PredicateKind::Subtype(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(a, b);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
ty::PredicateKind::Coerce(pred) => {
let (a, b) = infcx.enter_forall_and_leak_universe(
obligation.predicate.kind().rebind((pred.a, pred.b)),
);
let expected_found = ExpectedFound::new(b, a);
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::DynCompatible(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::Select(SelectionError::Unimplemented)
}
ty::PredicateKind::ConstEquate(..) => {
bug!("unexpected goal: {obligation:?}")
}
};
FulfillmentError { obligation, code, root_obligation }
}
pub(super) fn fulfillment_error_for_stalled<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
let (code, refine_obligation) = infcx.probe(|_| {
match <&SolverDelegate<'tcx>>::from(infcx)
.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
.0
{
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
(FulfillmentErrorCode::Ambiguity { overflow: None }, true)
}
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways.
// We discard the inference constraints from overflowing goals, so
// recomputing the goal again during `find_best_leaf_obligation` may apply
// inference guidance that makes other goals go from ambig -> pass, for example.
//
// FIXME: We should probably just look into overflows here.
false,
),
Ok((_, Certainty::Yes)) => {
bug!("did not expect successful goal when collecting ambiguity errors")
}
Err(_) => {
bug!("did not expect selection error when collecting ambiguity errors")
}
}
});
FulfillmentError {
obligation: if refine_obligation {
find_best_leaf_obligation(infcx, &root_obligation, true)
} else {
root_obligation.clone()
},
code,
root_obligation,
}
}
pub(super) fn fulfillment_error_for_overflow<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
FulfillmentError {
obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
root_obligation,
}
}
fn find_best_leaf_obligation<'tcx>(
infcx: &InferCtxt<'tcx>,
obligation: &PredicateObligation<'tcx>,
consider_ambiguities: bool,
) -> PredicateObligation<'tcx> {
let obligation = infcx.resolve_vars_if_possible(obligation.clone());
// FIXME: we use a probe here as the `BestObligation` visitor does not
// check whether it uses candidates which get shadowed by where-bounds.
//
// We should probably fix the visitor to not do so instead, as this also
// means the leaf obligation may be incorrect.
infcx
.fudge_inference_if_ok(|| {
infcx
.visit_proof_tree(obligation.clone().into(), &mut BestObligation {
obligation: obligation.clone(),
consider_ambiguities,
})
.break_value()
.ok_or(())
})
.unwrap_or(obligation)
}
struct BestObligation<'tcx> {
obligation: PredicateObligation<'tcx>,
consider_ambiguities: bool,
}
impl<'tcx> BestObligation<'tcx> {
fn with_derived_obligation(
&mut self,
derived_obligation: PredicateObligation<'tcx>,
and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
) -> <Self as ProofTreeVisitor<'tcx>>::Result {
let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
let res = and_then(self);
self.obligation = old_obligation;
res
}
/// Filter out the candidates that aren't interesting to visit for the
/// purposes of reporting errors. For ambiguities, we only consider
/// candidates that may hold. For errors, we only consider candidates that
/// *don't* hold and which have impl-where clauses that also don't hold.
fn non_trivial_candidates<'a>(
&self,
goal: &'a inspect::InspectGoal<'a, 'tcx>,
) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
let mut candidates = goal.candidates();
match self.consider_ambiguities {
true => {
// If we have an ambiguous obligation, we must consider *all* candidates
// that hold, or else we may guide inference causing other goals to go
// from ambig -> pass/fail.
candidates.retain(|candidate| candidate.result().is_ok());
}
false => {
// If we have >1 candidate, one may still be due to "boring" reasons, like
// an alias-relate that failed to hold when deeply evaluated. We really
// don't care about reasons like this.
if candidates.len() > 1 {
candidates.retain(|candidate| {
goal.infcx().probe(|_| {
candidate.instantiate_nested_goals(self.span()).iter().any(
|nested_goal| {
matches!(
nested_goal.source(),
GoalSource::ImplWhereBound
| GoalSource::AliasBoundConstCondition
| GoalSource::InstantiateHigherRanked
| GoalSource::AliasWellFormed
) && match (self.consider_ambiguities, nested_goal.result()) {
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity)))
| (false, Err(_)) => true,
_ => false,
}
},
)
})
});
}
// Prefer a non-rigid candidate if there is one.
if candidates.len() > 1 {
candidates.retain(|candidate| {
!matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
});
}
}
}
candidates
}
/// HACK: We walk the nested obligations for a well-formed arg manually,
/// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
/// Ideally we'd be able to track this better.
fn visit_well_formed_goal(
&mut self,
candidate: &inspect::InspectCandidate<'_, 'tcx>,
arg: ty::GenericArg<'tcx>,
) -> ControlFlow<PredicateObligation<'tcx>> {
let infcx = candidate.goal().infcx();
let param_env = candidate.goal().goal().param_env;
let body_id = self.obligation.cause.body_id;
for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id)
.into_iter()
.flatten()
{
let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
GoalSource::Misc,
Goal::new(infcx.tcx, obligation.param_env, obligation.predicate),
self.span(),
);
// Skip nested goals that aren't the *reason* for our goal's failure.
match (self.consider_ambiguities, nested_goal.result()) {
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
_ => continue,
}
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
}
ControlFlow::Break(self.obligation.clone())
}
}
impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
type Result = ControlFlow<PredicateObligation<'tcx>>;
fn span(&self) -> rustc_span::Span {
self.obligation.cause.span
}
#[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
let candidates = self.non_trivial_candidates(goal);
trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
let [candidate] = candidates.as_slice() else {
return ControlFlow::Break(self.obligation.clone());
};
// Don't walk into impls that have `do_not_recommend`.
if let inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} = candidate.kind()
&& goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
{
return ControlFlow::Break(self.obligation.clone());
}
let tcx = goal.infcx().tcx;
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
// for normalizes-to.
let pred_kind = goal.goal().predicate.kind();
let child_mode = match pred_kind.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
ChildMode::Trait(pred_kind.rebind(pred))
}
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
ChildMode::Host(pred_kind.rebind(pred))
}
ty::PredicateKind::NormalizesTo(normalizes_to)
if matches!(
normalizes_to.alias.kind(tcx),
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
) =>
{
ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
trait_ref: normalizes_to.alias.trait_ref(tcx),
polarity: ty::PredicatePolarity::Positive,
}))
}
ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
return self.visit_well_formed_goal(candidate, arg);
}
_ => ChildMode::PassThrough,
};
let nested_goals = candidate.instantiate_nested_goals(self.span());
// If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
// an actual candidate, instead we should treat them as if the impl was never considered to
// have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
// instead of `impl<T: FnPtr> Trait for T`.
//
// We do this as a separate loop so that we do not choose to tell the user about some nested
// goal before we encounter a `T: FnPtr` nested goal.
for nested_goal in &nested_goals {
if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
&& let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
&& poly_trait_pred.def_id() == fn_ptr_trait
&& let Err(NoSolution) = nested_goal.result()
{
return ControlFlow::Break(self.obligation.clone());
}
}
let mut impl_where_bound_count = 0;
for nested_goal in nested_goals {
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
let make_obligation = |cause| Obligation {
cause,
param_env: nested_goal.goal().param_env,
predicate: nested_goal.goal().predicate,
recursion_depth: self.obligation.recursion_depth + 1,
};
let obligation;
match (child_mode, nested_goal.source()) {
(ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
continue;
}
(ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
obligation = make_obligation(derive_cause(
tcx,
candidate.kind(),
self.obligation.cause.clone(),
impl_where_bound_count,
parent_trait_pred,
));
impl_where_bound_count += 1;
}
(
ChildMode::Host(parent_host_pred),
GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
) => {
obligation = make_obligation(derive_host_cause(
tcx,
candidate.kind(),
self.obligation.cause.clone(),
impl_where_bound_count,
parent_host_pred,
));
impl_where_bound_count += 1;
}
// Skip over a higher-ranked predicate.
(_, GoalSource::InstantiateHigherRanked) => {
obligation = self.obligation.clone();
}
(ChildMode::PassThrough, _)
| (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
obligation = make_obligation(self.obligation.cause.clone());
}
}
// Skip nested goals that aren't the *reason* for our goal's failure.
match (self.consider_ambiguities, nested_goal.result()) {
(true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
_ => continue,
}
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
}
// alias-relate may fail because the lhs or rhs can't be normalized,
// and therefore is treated as rigid.
if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
goal.depth() + 1,
self,
)
.break_value()
{
return ControlFlow::Break(obligation);
} else if let Some(obligation) = goal
.infcx()
.visit_proof_tree_at_depth(
goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
goal.depth() + 1,
self,
)
.break_value()
{
return ControlFlow::Break(obligation);
}
}
ControlFlow::Break(self.obligation.clone())
}
}
#[derive(Debug, Copy, Clone)]
enum ChildMode<'tcx> {
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Trait(ty::PolyTraitPredicate<'tcx>),
// Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
// and skip all `GoalSource::Misc`, which represent useless obligations
// such as alias-eq which may not hold.
Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
// Skip trying to derive an `ObligationCause` from this obligation, and
// report *all* sub-obligations as if they came directly from the parent
// obligation.
PassThrough,
}
fn derive_cause<'tcx>(
tcx: TyCtxt<'tcx>,
candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
mut cause: ObligationCause<'tcx>,
idx: usize,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> ObligationCause<'tcx> {
match candidate_kind {
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} => {
if let Some((_, span)) =
tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
{
cause = cause.derived_cause(parent_trait_pred, |derived| {
ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
derived,
impl_or_alias_def_id: impl_def_id,
impl_def_predicate_index: Some(idx),
span,
}))
})
}
}
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::BuiltinImpl(..),
result: _,
} => {
cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
}
_ => {}
};
cause
}
fn derive_host_cause<'tcx>(
tcx: TyCtxt<'tcx>,
candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
mut cause: ObligationCause<'tcx>,
idx: usize,
parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
) -> ObligationCause<'tcx> {
match candidate_kind {
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::Impl(impl_def_id),
result: _,
} => {
if let Some((_, span)) = tcx
.predicates_of(impl_def_id)
.instantiate_identity(tcx)
.into_iter()
.chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
|(trait_ref, span)| {
(
trait_ref.to_host_effect_clause(
tcx,
parent_host_pred.skip_binder().constness,
),
span,
)
},
))
.nth(idx)
{
cause =
cause.derived_host_cause(parent_host_pred, |derived| {
ObligationCauseCode::ImplDerivedHost(Box::new(
traits::ImplDerivedHostCause { derived, impl_def_id, span },
))
})
}
}
inspect::ProbeKind::TraitCandidate {
source: CandidateSource::BuiltinImpl(..),
result: _,
} => {
cause =
cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
}
_ => {}
};
cause
}

View file

@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
let goals = instantiated_goals
.into_iter()
.map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.unpack() {
ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
// expected term. This means that candidates which only fail due to nested goals
// and which normalize to a different term then the final result could ICE: when
// building their proof tree, the expected term was unconstrained, but when
// instantiating the candidate it is already constrained to the result of another
// candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
InspectGoal::new(
infcx,
self.goal.depth + 1,
proof_tree.unwrap(),
Some(NormalizesToTermHack { term, unconstrained_term }),
source,
)
}
_ => {
// We're using a probe here as evaluating a goal could constrain
// inference variables by choosing one candidate. If we then recurse
// into another candidate who ends up with different inference
// constraints, we get an ICE if we already applied the constraints
// from the chosen candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
.unwrap();
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
}
})
.map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
.collect();
(goals, opt_impl_args)
}
pub fn instantiate_proof_tree_for_nested_goal(
&self,
source: GoalSource,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
span: Span,
) -> InspectGoal<'a, 'tcx> {
let infcx = self.goal.infcx;
match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.unpack() {
ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
// expected term. This means that candidates which only fail due to nested goals
// and which normalize to a different term then the final result could ICE: when
// building their proof tree, the expected term was unconstrained, but when
// instantiating the candidate it is already constrained to the result of another
// candidate.
let proof_tree =
infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
InspectGoal::new(
infcx,
self.goal.depth + 1,
proof_tree.unwrap(),
Some(NormalizesToTermHack { term, unconstrained_term }),
source,
)
}
_ => {
// We're using a probe here as evaluating a goal could constrain
// inference variables by choosing one candidate. If we then recurse
// into another candidate who ends up with different inference
// constraints, we get an ICE if we already applied the constraints
// from the chosen candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
.unwrap();
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
}
}
}
/// Visit all nested goals of this candidate, rolling back
/// all inference constraints.
pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {

View file

@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::regions::InferCtxtRegionExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
#[derive(Debug)]
pub struct FulfillmentError<'tcx> {
pub obligation: PredicateObligation<'tcx>,
pub code: FulfillmentErrorCode<'tcx>,
@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> {
}
}
impl<'tcx> Debug for FulfillmentError<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
}
}
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only

View file

@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
use rustc_middle::infer::canonical::CanonicalQueryResponse;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
use rustc_span::Span;
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{DUMMY_SP, Span};
use rustc_type_ir::outlives::{Component, push_outlives_components};
use smallvec::{SmallVec, smallvec};
use tracing::debug;
@ -92,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// From the full set of obligations, just filter down to the region relationships.
for obligation in
wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
.into_iter()
.flatten()
{
assert!(!obligation.has_escaping_bound_vars());
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {

View file

@ -8,8 +8,8 @@ use rustc_middle::ty::{
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_span::{DUMMY_SP, Span};
use rustc_span::Span;
use rustc_span::def_id::{DefId, LocalDefId};
use tracing::{debug, instrument, trace};
use crate::infer::InferCtxt;
@ -89,6 +89,8 @@ pub fn unnormalized_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
span: Span,
body_id: LocalDefId,
) -> Option<PredicateObligations<'tcx>> {
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
@ -106,8 +108,8 @@ pub fn unnormalized_obligations<'tcx>(
let mut wf = WfPredicates {
infcx,
param_env,
body_id: CRATE_DEF_ID,
span: DUMMY_SP,
body_id,
span,
out: PredicateObligations::new(),
recursion_depth: 0,
item: None,
@ -828,8 +830,25 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
// Let the visitor iterate into the argument/return
// types appearing in the fn signature.
}
ty::UnsafeBinder(_) => {
// FIXME(unsafe_binders): We should also recurse into the binder here.
ty::UnsafeBinder(ty) => {
// FIXME(unsafe_binders): For now, we have no way to express
// that a type must be `ManuallyDrop` OR `Copy` (or a pointer).
if !ty.has_escaping_bound_vars() {
self.out.push(traits::Obligation::new(
self.tcx(),
self.cause(ObligationCauseCode::Misc),
self.param_env,
ty.map_bound(|ty| {
ty::TraitRef::new(
self.tcx(),
self.tcx().require_lang_item(LangItem::Copy, Some(self.span)),
[ty],
)
}),
));
}
// We recurse into the binder below.
}
ty::Dynamic(data, r, _) => {

View file

@ -116,6 +116,11 @@ fn recurse_build<'tcx>(
| &ExprKind::ValueTypeAscription { source, .. } => {
recurse_build(tcx, body, source, root_span)?
}
&ExprKind::PlaceUnwrapUnsafeBinder { .. }
| &ExprKind::ValueUnwrapUnsafeBinder { .. }
| &ExprKind::WrapUnsafeBinder { .. } => {
todo!("FIXME(unsafe_binders)")
}
&ExprKind::Literal { lit, neg } => {
let sp = node.span;
tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg })
@ -347,6 +352,9 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::Adt(_)
| thir::ExprKind::PlaceTypeAscription { .. }
| thir::ExprKind::ValueTypeAscription { .. }
| thir::ExprKind::PlaceUnwrapUnsafeBinder { .. }
| thir::ExprKind::ValueUnwrapUnsafeBinder { .. }
| thir::ExprKind::WrapUnsafeBinder { .. }
| thir::ExprKind::Closure(_)
| thir::ExprKind::Literal { .. }
| thir::ExprKind::NonHirLiteral { .. }

View file

@ -973,6 +973,24 @@ pub trait Ord: Eq + PartialOrd<Self> {
/// assert_eq!(1.max(2), 2);
/// assert_eq!(2.max(2), 2);
/// ```
/// ```
/// use std::cmp::Ordering;
///
/// #[derive(Eq)]
/// struct Equal(&'static str);
///
/// impl PartialEq for Equal {
/// fn eq(&self, other: &Self) -> bool { true }
/// }
/// impl PartialOrd for Equal {
/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
/// }
/// impl Ord for Equal {
/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
/// }
///
/// assert_eq!(Equal("self").max(Equal("other")).0, "other");
/// ```
#[stable(feature = "ord_max_min", since = "1.21.0")]
#[inline]
#[must_use]
@ -981,7 +999,7 @@ pub trait Ord: Eq + PartialOrd<Self> {
where
Self: Sized,
{
max_by(self, other, Ord::cmp)
if other < self { self } else { other }
}
/// Compares and returns the minimum of two values.
@ -994,6 +1012,24 @@ pub trait Ord: Eq + PartialOrd<Self> {
/// assert_eq!(1.min(2), 1);
/// assert_eq!(2.min(2), 2);
/// ```
/// ```
/// use std::cmp::Ordering;
///
/// #[derive(Eq)]
/// struct Equal(&'static str);
///
/// impl PartialEq for Equal {
/// fn eq(&self, other: &Self) -> bool { true }
/// }
/// impl PartialOrd for Equal {
/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
/// }
/// impl Ord for Equal {
/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
/// }
///
/// assert_eq!(Equal("self").min(Equal("other")).0, "self");
/// ```
#[stable(feature = "ord_max_min", since = "1.21.0")]
#[inline]
#[must_use]
@ -1002,7 +1038,7 @@ pub trait Ord: Eq + PartialOrd<Self> {
where
Self: Sized,
{
min_by(self, other, Ord::cmp)
if other < self { other } else { self }
}
/// Restrict a value to a certain interval.
@ -1414,6 +1450,24 @@ pub macro PartialOrd($item:item) {
/// assert_eq!(cmp::min(1, 2), 1);
/// assert_eq!(cmp::min(2, 2), 2);
/// ```
/// ```
/// use std::cmp::{self, Ordering};
///
/// #[derive(Eq)]
/// struct Equal(&'static str);
///
/// impl PartialEq for Equal {
/// fn eq(&self, other: &Self) -> bool { true }
/// }
/// impl PartialOrd for Equal {
/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
/// }
/// impl Ord for Equal {
/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
/// }
///
/// assert_eq!(cmp::min(Equal("v1"), Equal("v2")).0, "v1");
/// ```
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
@ -1431,20 +1485,22 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T {
/// ```
/// use std::cmp;
///
/// let result = cmp::min_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
/// assert_eq!(result, 1);
/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs());
///
/// let result = cmp::min_by(-2, 3, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
/// assert_eq!(result, -2);
/// let result = cmp::min_by(2, -1, abs_cmp);
/// assert_eq!(result, -1);
///
/// let result = cmp::min_by(2, -3, abs_cmp);
/// assert_eq!(result, 2);
///
/// let result = cmp::min_by(1, -1, abs_cmp);
/// assert_eq!(result, 1);
/// ```
#[inline]
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
match compare(&v1, &v2) {
Ordering::Less | Ordering::Equal => v1,
Ordering::Greater => v2,
}
if compare(&v2, &v1).is_lt() { v2 } else { v1 }
}
/// Returns the element that gives the minimum value from the specified function.
@ -1456,17 +1512,20 @@ pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
/// ```
/// use std::cmp;
///
/// let result = cmp::min_by_key(-2, 1, |x: &i32| x.abs());
/// assert_eq!(result, 1);
/// let result = cmp::min_by_key(2, -1, |x: &i32| x.abs());
/// assert_eq!(result, -1);
///
/// let result = cmp::min_by_key(-2, 2, |x: &i32| x.abs());
/// assert_eq!(result, -2);
/// let result = cmp::min_by_key(2, -3, |x: &i32| x.abs());
/// assert_eq!(result, 2);
///
/// let result = cmp::min_by_key(1, -1, |x: &i32| x.abs());
/// assert_eq!(result, 1);
/// ```
#[inline]
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
if f(&v2) < f(&v1) { v2 } else { v1 }
}
/// Compares and returns the maximum of two values.
@ -1483,6 +1542,24 @@ pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
/// assert_eq!(cmp::max(1, 2), 2);
/// assert_eq!(cmp::max(2, 2), 2);
/// ```
/// ```
/// use std::cmp::{self, Ordering};
///
/// #[derive(Eq)]
/// struct Equal(&'static str);
///
/// impl PartialEq for Equal {
/// fn eq(&self, other: &Self) -> bool { true }
/// }
/// impl PartialOrd for Equal {
/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
/// }
/// impl Ord for Equal {
/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
/// }
///
/// assert_eq!(cmp::max(Equal("v1"), Equal("v2")).0, "v2");
/// ```
#[inline]
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
@ -1500,20 +1577,22 @@ pub fn max<T: Ord>(v1: T, v2: T) -> T {
/// ```
/// use std::cmp;
///
/// let result = cmp::max_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs());
///
/// let result = cmp::max_by(3, -2, abs_cmp) ;
/// assert_eq!(result, 3);
///
/// let result = cmp::max_by(1, -2, abs_cmp);
/// assert_eq!(result, -2);
///
/// let result = cmp::max_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())) ;
/// assert_eq!(result, 2);
/// let result = cmp::max_by(1, -1, abs_cmp);
/// assert_eq!(result, -1);
/// ```
#[inline]
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
match compare(&v1, &v2) {
Ordering::Less | Ordering::Equal => v2,
Ordering::Greater => v1,
}
if compare(&v2, &v1).is_lt() { v1 } else { v2 }
}
/// Returns the element that gives the maximum value from the specified function.
@ -1525,17 +1604,20 @@ pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T {
/// ```
/// use std::cmp;
///
/// let result = cmp::max_by_key(-2, 1, |x: &i32| x.abs());
/// let result = cmp::max_by_key(3, -2, |x: &i32| x.abs());
/// assert_eq!(result, 3);
///
/// let result = cmp::max_by_key(1, -2, |x: &i32| x.abs());
/// assert_eq!(result, -2);
///
/// let result = cmp::max_by_key(-2, 2, |x: &i32| x.abs());
/// assert_eq!(result, 2);
/// let result = cmp::max_by_key(1, -1, |x: &i32| x.abs());
/// assert_eq!(result, -1);
/// ```
#[inline]
#[must_use]
#[stable(feature = "cmp_min_max_by", since = "1.53.0")]
pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
if f(&v2) < f(&v1) { v1 } else { v2 }
}
/// Compares and sorts two values, returning minimum and maximum.
@ -1549,13 +1631,32 @@ pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
/// use std::cmp;
///
/// assert_eq!(cmp::minmax(1, 2), [1, 2]);
/// assert_eq!(cmp::minmax(2, 2), [2, 2]);
/// assert_eq!(cmp::minmax(2, 1), [1, 2]);
///
/// // You can destructure the result using array patterns
/// let [min, max] = cmp::minmax(42, 17);
/// assert_eq!(min, 17);
/// assert_eq!(max, 42);
/// ```
/// ```
/// #![feature(cmp_minmax)]
/// use std::cmp::{self, Ordering};
///
/// #[derive(Eq)]
/// struct Equal(&'static str);
///
/// impl PartialEq for Equal {
/// fn eq(&self, other: &Self) -> bool { true }
/// }
/// impl PartialOrd for Equal {
/// fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(Ordering::Equal) }
/// }
/// impl Ord for Equal {
/// fn cmp(&self, other: &Self) -> Ordering { Ordering::Equal }
/// }
///
/// assert_eq!(cmp::minmax(Equal("v1"), Equal("v2")).map(|v| v.0), ["v1", "v2"]);
/// ```
#[inline]
#[must_use]
#[unstable(feature = "cmp_minmax", issue = "115939")]
@ -1563,7 +1664,7 @@ pub fn minmax<T>(v1: T, v2: T) -> [T; 2]
where
T: Ord,
{
if v1 <= v2 { [v1, v2] } else { [v2, v1] }
if v2 < v1 { [v2, v1] } else { [v1, v2] }
}
/// Returns minimum and maximum values with respect to the specified comparison function.
@ -1576,11 +1677,14 @@ where
/// #![feature(cmp_minmax)]
/// use std::cmp;
///
/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]);
/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]);
/// let abs_cmp = |x: &i32, y: &i32| x.abs().cmp(&y.abs());
///
/// assert_eq!(cmp::minmax_by(-2, 1, abs_cmp), [1, -2]);
/// assert_eq!(cmp::minmax_by(-1, 2, abs_cmp), [-1, 2]);
/// assert_eq!(cmp::minmax_by(-2, 2, abs_cmp), [-2, 2]);
///
/// // You can destructure the result using array patterns
/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
/// let [min, max] = cmp::minmax_by(-42, 17, abs_cmp);
/// assert_eq!(min, 17);
/// assert_eq!(max, -42);
/// ```
@ -1591,7 +1695,7 @@ pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2]
where
F: FnOnce(&T, &T) -> Ordering,
{
if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] }
if compare(&v2, &v1).is_lt() { [v2, v1] } else { [v1, v2] }
}
/// Returns minimum and maximum values with respect to the specified key function.
@ -1620,7 +1724,7 @@ where
F: FnMut(&T) -> K,
K: Ord,
{
minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] }
}
// Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types

View file

@ -1091,7 +1091,195 @@ pub trait FnPtr: Copy + Clone {
fn addr(self) -> *const ();
}
/// Derive macro generating impls of traits related to smart pointers.
/// Derive macro that makes a smart pointer usable with trait objects.
///
/// # What this macro does
///
/// This macro is intended to be used with user-defined pointer types, and makes it possible to
/// perform coercions on the pointee of the user-defined pointer. There are two aspects to this:
///
/// ## Unsizing coercions of the pointee
///
/// By using the macro, the following example will compile:
/// ```
/// #![feature(derive_coerce_pointee)]
/// use std::marker::CoercePointee;
/// use std::ops::Deref;
///
/// #[derive(CoercePointee)]
/// #[repr(transparent)]
/// struct MySmartPointer<T: ?Sized>(Box<T>);
///
/// impl<T: ?Sized> Deref for MySmartPointer<T> {
/// type Target = T;
/// fn deref(&self) -> &T {
/// &self.0
/// }
/// }
///
/// trait MyTrait {}
///
/// impl MyTrait for i32 {}
///
/// fn main() {
/// let ptr: MySmartPointer<i32> = MySmartPointer(Box::new(4));
///
/// // This coercion would be an error without the derive.
/// let ptr: MySmartPointer<dyn MyTrait> = ptr;
/// }
/// ```
/// Without the `#[derive(CoercePointee)]` macro, this example would fail with the following error:
/// ```text
/// error[E0308]: mismatched types
/// --> src/main.rs:11:44
/// |
/// 11 | let ptr: MySmartPointer<dyn MyTrait> = ptr;
/// | --------------------------- ^^^ expected `MySmartPointer<dyn MyTrait>`, found `MySmartPointer<i32>`
/// | |
/// | expected due to this
/// |
/// = note: expected struct `MySmartPointer<dyn MyTrait>`
/// found struct `MySmartPointer<i32>`
/// = help: `i32` implements `MyTrait` so you could box the found value and coerce it to the trait object `Box<dyn MyTrait>`, you will have to change the expected type as well
/// ```
///
/// ## Dyn compatibility
///
/// This macro allows you to dispatch on the user-defined pointer type. That is, traits using the
/// type as a receiver are dyn-compatible. For example, this compiles:
///
/// ```
/// #![feature(arbitrary_self_types, derive_coerce_pointee)]
/// use std::marker::CoercePointee;
/// use std::ops::Deref;
///
/// #[derive(CoercePointee)]
/// #[repr(transparent)]
/// struct MySmartPointer<T: ?Sized>(Box<T>);
///
/// impl<T: ?Sized> Deref for MySmartPointer<T> {
/// type Target = T;
/// fn deref(&self) -> &T {
/// &self.0
/// }
/// }
///
/// // You can always define this trait. (as long as you have #![feature(arbitrary_self_types)])
/// trait MyTrait {
/// fn func(self: MySmartPointer<Self>);
/// }
///
/// // But using `dyn MyTrait` requires #[derive(CoercePointee)].
/// fn call_func(value: MySmartPointer<dyn MyTrait>) {
/// value.func();
/// }
/// ```
/// If you remove the `#[derive(CoercePointee)]` annotation from the struct, then the above example
/// will fail with this error message:
/// ```text
/// error[E0038]: the trait `MyTrait` is not dyn compatible
/// --> src/lib.rs:21:36
/// |
/// 17 | fn func(self: MySmartPointer<Self>);
/// | -------------------- help: consider changing method `func`'s `self` parameter to be `&self`: `&Self`
/// ...
/// 21 | fn call_func(value: MySmartPointer<dyn MyTrait>) {
/// | ^^^^^^^^^^^ `MyTrait` is not dyn compatible
/// |
/// note: for a trait to be dyn compatible it needs to allow building a vtable
/// for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
/// --> src/lib.rs:17:19
/// |
/// 16 | trait MyTrait {
/// | ------- this trait is not dyn compatible...
/// 17 | fn func(self: MySmartPointer<Self>);
/// | ^^^^^^^^^^^^^^^^^^^^ ...because method `func`'s `self` parameter cannot be dispatched on
/// ```
///
/// # Requirements for using the macro
///
/// This macro can only be used if:
/// * The type is a `#[repr(transparent)]` struct.
/// * The type of its non-zero-sized field must either be a standard library pointer type
/// (reference, raw pointer, `NonNull`, `Box`, `Rc`, `Arc`, etc.) or another user-defined type
/// also using the `#[derive(CoercePointee)]` macro.
/// * Zero-sized fields must not mention any generic parameters unless the zero-sized field has
/// type [`PhantomData`].
///
/// ## Multiple type parameters
///
/// If the type has multiple type parameters, then you must explicitly specify which one should be
/// used for dynamic dispatch. For example:
/// ```
/// # #![feature(derive_coerce_pointee)]
/// # use std::marker::{CoercePointee, PhantomData};
/// #[derive(CoercePointee)]
/// #[repr(transparent)]
/// struct MySmartPointer<#[pointee] T: ?Sized, U> {
/// ptr: Box<T>,
/// _phantom: PhantomData<U>,
/// }
/// ```
/// Specifying `#[pointee]` when the struct has only one type parameter is allowed, but not required.
///
/// # Examples
///
/// A custom implementation of the `Rc` type:
/// ```
/// #![feature(derive_coerce_pointee)]
/// use std::marker::CoercePointee;
/// use std::ops::Deref;
/// use std::ptr::NonNull;
///
/// #[derive(CoercePointee)]
/// #[repr(transparent)]
/// pub struct Rc<T: ?Sized> {
/// inner: NonNull<RcInner<T>>,
/// }
///
/// struct RcInner<T: ?Sized> {
/// refcount: usize,
/// value: T,
/// }
///
/// impl<T: ?Sized> Deref for Rc<T> {
/// type Target = T;
/// fn deref(&self) -> &T {
/// let ptr = self.inner.as_ptr();
/// unsafe { &(*ptr).value }
/// }
/// }
///
/// impl<T> Rc<T> {
/// pub fn new(value: T) -> Self {
/// let inner = Box::new(RcInner {
/// refcount: 1,
/// value,
/// });
/// Self {
/// inner: NonNull::from(Box::leak(inner)),
/// }
/// }
/// }
///
/// impl<T: ?Sized> Clone for Rc<T> {
/// fn clone(&self) -> Self {
/// // A real implementation would handle overflow here.
/// unsafe { (*self.inner.as_ptr()).refcount += 1 };
/// Self { inner: self.inner }
/// }
/// }
///
/// impl<T: ?Sized> Drop for Rc<T> {
/// fn drop(&mut self) {
/// let ptr = self.inner.as_ptr();
/// unsafe { (*ptr).refcount -= 1 };
/// if unsafe { (*ptr).refcount } == 0 {
/// drop(unsafe { Box::from_raw(ptr) });
/// }
/// }
/// }
/// ```
#[rustc_builtin_macro(CoercePointee, attributes(pointee))]
#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
#[unstable(feature = "derive_coerce_pointee", issue = "123430")]

View file

@ -1681,7 +1681,7 @@ impl<T, const N: usize> *const [T; N] {
}
}
// Equality for pointers
/// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> PartialEq for *const T {
#[inline]
@ -1691,10 +1691,11 @@ impl<T: ?Sized> PartialEq for *const T {
}
}
/// Pointer equality is an equivalence relation.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Eq for *const T {}
// Comparison for pointers
/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Ord for *const T {
#[inline]
@ -1710,6 +1711,7 @@ impl<T: ?Sized> Ord for *const T {
}
}
/// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> PartialOrd for *const T {
#[inline]

View file

@ -2097,7 +2097,7 @@ impl<T, const N: usize> *mut [T; N] {
}
}
// Equality for pointers
/// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> PartialEq for *mut T {
#[inline(always)]
@ -2107,9 +2107,11 @@ impl<T: ?Sized> PartialEq for *mut T {
}
}
/// Pointer equality is an equivalence relation.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Eq for *mut T {}
/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Ord for *mut T {
#[inline]
@ -2125,6 +2127,7 @@ impl<T: ?Sized> Ord for *mut T {
}
}
/// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> PartialOrd for *mut T {
#[inline(always)]

View file

@ -10,8 +10,8 @@ use crate::{cmp, intrinsics};
/// Sorts `v` based on comparison function `is_less`. If `eager_sort` is true,
/// it will only do small-sorts and physical merges, ensuring O(N * log(N))
/// worst-case complexity. `scratch.len()` must be at least `max(v.len() / 2,
/// MIN_SMALL_SORT_SCRATCH_LEN)` otherwise the implementation may abort.
/// worst-case complexity. `scratch.len()` must be at least
/// `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)` otherwise the implementation may abort.
/// Fully ascending and descending inputs will be sorted with exactly N - 1
/// comparisons.
///

View file

@ -41,6 +41,8 @@ pub fn sort<T, F: FnMut(&T, &T) -> bool, BufT: BufGuard<T>>(v: &mut [T], is_less
cfg_if! {
if #[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] {
// Unlike driftsort, mergesort only requires len / 2,
// not len - len / 2.
let alloc_len = len / 2;
cfg_if! {
@ -91,16 +93,26 @@ fn driftsort_main<T, F: FnMut(&T, &T) -> bool, BufT: BufGuard<T>>(v: &mut [T], i
// By allocating n elements of memory we can ensure the entire input can
// be sorted using stable quicksort, which allows better performance on
// random and low-cardinality distributions. However, we still want to
// reduce our memory usage to n / 2 for large inputs. We do this by scaling
// our allocation as max(n / 2, min(n, 8MB)), ensuring we scale like n for
// small inputs and n / 2 for large inputs, without a sudden drop off. We
// also need to ensure our alloc >= MIN_SMALL_SORT_SCRATCH_LEN, as the
// reduce our memory usage to n - n / 2 for large inputs. We do this by scaling
// our allocation as max(n - n / 2, min(n, 8MB)), ensuring we scale like n for
// small inputs and n - n / 2 for large inputs, without a sudden drop off. We
// also need to ensure our alloc >= SMALL_SORT_GENERAL_SCRATCH_LEN, as the
// small-sort always needs this much memory.
//
// driftsort will produce unsorted runs of up to min_good_run_len, which
// is at most len - len / 2.
// Unsorted runs need to be processed by quicksort, which requires as much
// scratch space as the run length, therefore the scratch space must be at
// least len - len / 2.
// If min_good_run_len is ever modified, this code must be updated to allocate
// the correct scratch size for it.
const MAX_FULL_ALLOC_BYTES: usize = 8_000_000; // 8MB
let max_full_alloc = MAX_FULL_ALLOC_BYTES / mem::size_of::<T>();
let len = v.len();
let alloc_len =
cmp::max(cmp::max(len / 2, cmp::min(len, max_full_alloc)), SMALL_SORT_GENERAL_SCRATCH_LEN);
let alloc_len = cmp::max(
cmp::max(len - len / 2, cmp::min(len, max_full_alloc)),
SMALL_SORT_GENERAL_SCRATCH_LEN,
);
// For small inputs 4KiB of stack storage suffices, which allows us to avoid
// calling the (de-)allocator. Benchmarks showed this was quite beneficial.

View file

@ -7,6 +7,8 @@ use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl;
use crate::{intrinsics, ptr};
/// Sorts `v` recursively using quicksort.
/// `scratch.len()` must be at least `max(v.len() - v.len() / 2, SMALL_SORT_GENERAL_SCRATCH_LEN)`
/// otherwise the implementation may abort.
///
/// `limit` when initialized with `c*log(v.len())` for some c ensures we do not
/// overflow the stack or go quadratic.

View file

@ -174,8 +174,6 @@ impl<T> OnceLock<T> {
///
/// Waiting for a computation on another thread to finish:
/// ```rust
/// #![feature(once_wait)]
///
/// use std::thread;
/// use std::sync::OnceLock;
///
@ -189,7 +187,7 @@ impl<T> OnceLock<T> {
/// })
/// ```
#[inline]
#[unstable(feature = "once_wait", issue = "127527")]
#[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")]
pub fn wait(&self) -> &T {
self.once.wait_force();

View file

@ -181,10 +181,29 @@ pub struct Mutex<T: ?Sized> {
data: UnsafeCell<T>,
}
// these are the only places where `T: Send` matters; all other
// functionality works fine on a single thread.
/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire
/// the owned `T` from the `Mutex` via [`into_inner`].
///
/// [`into_inner`]: Mutex::into_inner
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
/// `T` must be `Send` for [`Mutex`] to be `Sync`.
/// This ensures that the protected data can be accessed safely from multiple threads
/// without causing data races or other unsafe behavior.
///
/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential
/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in
/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer,
/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap
/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would
/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable
/// to potential data races.
///
/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available
/// to one thread at a time if `T` is not `Sync`.
///
/// [`Rc`]: crate::rc::Rc
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
@ -211,8 +230,17 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> {
poison: poison::Guard,
}
/// A [`MutexGuard`] is not `Send` to maximize platform portablity.
///
/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to
/// release mutex locks on the same thread they were acquired.
/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from
/// another thread.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync`
/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`).
#[stable(feature = "mutexguard", since = "1.19.0")]
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}

View file

@ -269,8 +269,6 @@ impl Once {
/// # Example
///
/// ```rust
/// #![feature(once_wait)]
///
/// use std::sync::Once;
/// use std::thread;
///
@ -289,7 +287,7 @@ impl Once {
/// If this [`Once`] has been poisoned because an initialization closure has
/// panicked, this method will also panic. Use [`wait_force`](Self::wait_force)
/// if this behavior is not desired.
#[unstable(feature = "once_wait", issue = "127527")]
#[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")]
pub fn wait(&self) {
if !self.inner.is_completed() {
self.inner.wait(false);
@ -298,7 +296,7 @@ impl Once {
/// Blocks the current thread until initialization has completed, ignoring
/// poisoning.
#[unstable(feature = "once_wait", issue = "127527")]
#[stable(feature = "once_wait", since = "CURRENT_RUSTC_VERSION")]
pub fn wait_force(&self) {
if !self.inner.is_completed() {
self.inner.wait(true);

View file

@ -5,7 +5,7 @@ runners:
env: { }
- &job-linux-4c
os: ubuntu-22.04
os: ubuntu-24.04
# Free some disk space to avoid running out of space during the build.
free_disk: true
<<: *base-job
@ -50,7 +50,7 @@ runners:
- &job-aarch64-linux
# Free some disk space to avoid running out of space during the build.
free_disk: true
os: ubuntu-22.04-arm
os: ubuntu-24.04-arm
- &job-aarch64-linux-8c
os: ubuntu-22.04-arm64-8core-32gb

View file

@ -1,8 +1,19 @@
#!/bin/bash
set -euo pipefail
# Free disk space on Linux GitHub action runners
# Script inspired by https://github.com/jlumbroso/free-disk-space
isX86() {
local arch
arch=$(uname -m)
if [ "$arch" = "x86_64" ]; then
return 0
else
return 1
fi
}
# print a line of the specified character
printSeparationLine() {
for ((i = 0; i < 80; i++)); do
@ -14,11 +25,15 @@ printSeparationLine() {
# compute available space
# REF: https://unix.stackexchange.com/a/42049/60849
# REF: https://stackoverflow.com/a/450821/408734
getAvailableSpace() { echo $(df -a | awk 'NR > 1 {avail+=$4} END {print avail}'); }
getAvailableSpace() {
df -a | awk 'NR > 1 {avail+=$4} END {print avail}'
}
# make Kb human readable (assume the input is Kb)
# REF: https://unix.stackexchange.com/a/44087/60849
formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); }
formatByteCount() {
numfmt --to=iec-i --suffix=B --padding=7 "$1"'000'
}
# macro to output saved space
printSavedSpace() {
@ -58,11 +73,27 @@ removeDir() {
dir=${1}
local before
before=$(getAvailableSpace)
if [ ! -d "$dir" ]; then
echo "::warning::Directory $dir does not exist, skipping."
else
before=$(getAvailableSpace)
sudo rm -rf "$dir"
printSavedSpace "$before" "Removed $dir"
fi
}
sudo rm -rf "$dir" || true
removeUnusedDirectories() {
local dirs_to_remove=(
"/usr/local/lib/android"
"/usr/share/dotnet"
printSavedSpace "$before" "$dir"
# Haskell runtime
"/usr/local/.ghcup"
)
for dir in "${dirs_to_remove[@]}"; do
removeDir "$dir"
done
}
execAndMeasureSpaceChange() {
@ -79,21 +110,29 @@ execAndMeasureSpaceChange() {
# Remove large packages
# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh
cleanPackages() {
sudo apt-get -qq remove -y --fix-missing \
'^aspnetcore-.*' \
'^dotnet-.*' \
'^llvm-.*' \
'php.*' \
'^mongodb-.*' \
'^mysql-.*' \
'azure-cli' \
'google-chrome-stable' \
'firefox' \
'powershell' \
'mono-devel' \
'libgl1-mesa-dri' \
'google-cloud-sdk' \
'google-cloud-cli'
local packages=(
'^aspnetcore-.*'
'^dotnet-.*'
'^llvm-.*'
'^mongodb-.*'
'^mysql-.*'
'azure-cli'
'firefox'
'libgl1-mesa-dri'
'mono-devel'
'php.*'
)
if isX86; then
packages+=(
'google-chrome-stable'
'google-cloud-cli'
'google-cloud-sdk'
'powershell'
)
fi
sudo apt-get -qq remove -y --fix-missing "${packages[@]}"
sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed"
sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed"
@ -101,9 +140,9 @@ cleanPackages() {
# Remove Docker images
cleanDocker() {
echo "Removing the following docker images:"
echo "=> Removing the following docker images:"
sudo docker image ls
echo "Removing docker images..."
echo "=> Removing docker images..."
sudo docker image prune --all --force || true
}
@ -121,17 +160,12 @@ AVAILABLE_INITIAL=$(getAvailableSpace)
printDF "BEFORE CLEAN-UP:"
echo ""
removeDir /usr/local/lib/android
removeDir /usr/share/dotnet
# Haskell runtime
removeDir /opt/ghc
removeDir /usr/local/.ghcup
execAndMeasureSpaceChange cleanPackages "Large misc. packages"
execAndMeasureSpaceChange cleanPackages "Unused packages"
execAndMeasureSpaceChange cleanDocker "Docker images"
execAndMeasureSpaceChange cleanSwap "Swap storage"
removeUnusedDirectories
# Output saved space statistic
echo ""
printDF "AFTER CLEAN-UP:"

View file

@ -116,6 +116,7 @@ fn check_rvalue<'tcx>(
Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv),
Rvalue::Repeat(operand, _)
| Rvalue::Use(operand)
| Rvalue::WrapUnsafeBinder(operand, _)
| Rvalue::Cast(
CastKind::PointerWithExposedProvenance
| CastKind::IntToInt
@ -289,7 +290,8 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => {},
| ProjectionElem::Index(_)
| ProjectionElem::UnwrapUnsafeBinder(_) => {},
}
}

View file

@ -1 +1 @@
9709af79014a150df7807671e6250ed940c488eb
6dd75f0d6802f56564f5f9c947a85ded286d3986

View file

@ -11,6 +11,7 @@ use std::{fmt, process};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use rustc_abi::{Align, ExternAbi, Size};
use rustc_apfloat::{Float, FloatConvert};
use rustc_attr_parsing::InlineAttr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
#[allow(unused)]
@ -1132,20 +1133,24 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
#[inline(always)]
fn generate_nan<
F1: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F2>,
F2: rustc_apfloat::Float,
>(
fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
ecx: &InterpCx<'tcx, Self>,
inputs: &[F1],
) -> F2 {
ecx.generate_nan(inputs)
}
#[inline(always)]
fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
ecx.equal_float_min_max(a, b)
}
#[inline(always)]
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.ub_checks())
}
#[inline(always)]
fn thread_local_static_pointer(
ecx: &mut MiriInterpCx<'tcx>,
def_id: DefId,

View file

@ -115,4 +115,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
nan
}
}
fn equal_float_min_max<F: Float>(&self, a: F, b: F) -> F {
let this = self.eval_context_ref();
// Return one side non-deterministically.
let mut rand = this.machine.rng.borrow_mut();
if rand.random() { a } else { b }
}
}

View file

@ -31,6 +31,7 @@ fn main() {
test_fast();
test_algebraic();
test_fmuladd();
test_min_max_nondet();
}
trait Float: Copy + PartialEq + Debug {
@ -1211,3 +1212,30 @@ fn test_fmuladd() {
test_operations_f32(0.1, 0.2, 0.3);
test_operations_f64(1.1, 1.2, 1.3);
}
/// `min` and `max` on equal arguments are non-deterministic.
fn test_min_max_nondet() {
/// Ensure that if we call the closure often enough, we see both `true` and `false.`
#[track_caller]
fn ensure_both(f: impl Fn() -> bool) {
let rounds = 16;
let first = f();
for _ in 1..rounds {
if f() != first {
// We saw two different values!
return;
}
}
// We saw the same thing N times.
panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
}
ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
}

View file

@ -1,8 +0,0 @@
//@ known-bug: #135210
#![feature(const_trait_impl)]
const _: fn(&String) = |s| {
&*s as &str;
};
fn main() {}

View file

@ -0,0 +1,10 @@
//@ run-pass
// Ensures that driftsort doesn't crash under specific slice
// length and memory size.
// Based on the example given in https://github.com/rust-lang/rust/issues/136103.
fn main() {
let n = 127;
let mut objs: Vec<_> =
(0..n).map(|i| [(i % 2) as u8; 125001]).collect();
objs.sort();
}

View file

@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::<Vec<[u32]>> {}
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u32]`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error: aborting due to 1 previous error

View file

@ -6,35 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
|
= help: remove one of these features
error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}`
--> $DIR/issue-88119.rs:19:49
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
--> $DIR/issue-88119.rs:21:5
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^ cannot normalize `<&T as ConstName>::{constant#0}`
|
note: required for `&T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:19:35
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^^^^^^^^ ^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
note: required by a bound in `<&T as ConstName>`
--> $DIR/issue-88119.rs:21:10
|
LL | [(); name_len::<T>()]:,
| ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>`
error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
--> $DIR/issue-88119.rs:26:49
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
--> $DIR/issue-88119.rs:28:5
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
|
note: required for `&mut T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:26:35
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^^^^ ^^^^^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
note: required by a bound in `<&mut T as ConstName>`
--> $DIR/issue-88119.rs:28:10
|
LL | [(); name_len::<T>()]:,
| ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>`
error: aborting due to 3 previous errors

View file

@ -16,23 +16,13 @@ LL | where
LL | T: AsExpression<Self::SqlType>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`
error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
--> $DIR/as_expression.rs:55:15
|
LL | SelectInt.check("bar");
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
|
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
but trait `AsExpression<Text>` is implemented for it
= help: for that trait implementation, expected `Text`, found `Integer`
error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
--> $DIR/as_expression.rs:55:5
|
LL | SelectInt.check("bar");
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`
error: aborting due to 3 previous errors
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.

View file

@ -53,7 +53,7 @@ impl<T> Foo for T where T: Expression {}
fn main() {
SelectInt.check("bar");
//~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
//[next]~| the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
//[current]~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
//[next]~^^ the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
//[next]~| type mismatch
}

View file

@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `A`
--> $DIR/alias-bounds-when-not-wf.rs:8:11
|
LL | type A<T: Foo> = T;
| ^^^ required by this bound in `A`
error[E0277]: the trait bound `usize: Foo` is not satisfied
--> $DIR/alias-bounds-when-not-wf.rs:16:10

View file

@ -2,6 +2,7 @@
#![deny(unused_variables)]
#![deny(unused_assignments)]
#![allow(dead_code, non_camel_case_types, trivial_numeric_casts, dropping_copy_types)]
#![feature(intrinsics)]
use std::ops::AddAssign;
@ -137,5 +138,10 @@ fn f7() {
drop(a);
}
// unused params warnings are not needed for intrinsic functions without bodies
#[rustc_intrinsic]
unsafe fn simd_shuffle<T, I, U>(a: T, b: T, i: I) -> U;
fn main() {
}

View file

@ -1,5 +1,5 @@
warning: unreachable statement
--> $DIR/liveness-unused.rs:92:9
--> $DIR/liveness-unused.rs:93:9
|
LL | continue;
| -------- any code following this expression is unreachable
@ -14,7 +14,7 @@ LL | #![warn(unused)]
= note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:8:7
--> $DIR/liveness-unused.rs:9:7
|
LL | fn f1(x: isize) {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
@ -26,25 +26,25 @@ LL | #![deny(unused_variables)]
| ^^^^^^^^^^^^^^^^
error: unused variable: `x`
--> $DIR/liveness-unused.rs:12:8
--> $DIR/liveness-unused.rs:13:8
|
LL | fn f1b(x: &mut isize) {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:20:9
--> $DIR/liveness-unused.rs:21:9
|
LL | let x: isize;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:25:9
--> $DIR/liveness-unused.rs:26:9
|
LL | let x = 3;
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: variable `x` is assigned to, but never used
--> $DIR/liveness-unused.rs:30:13
--> $DIR/liveness-unused.rs:31:13
|
LL | let mut x = 3;
| ^
@ -52,7 +52,7 @@ LL | let mut x = 3;
= note: consider using `_x` instead
error: value assigned to `x` is never read
--> $DIR/liveness-unused.rs:32:5
--> $DIR/liveness-unused.rs:33:5
|
LL | x += 4;
| ^
@ -65,7 +65,7 @@ LL | #![deny(unused_assignments)]
| ^^^^^^^^^^^^^^^^^^
error: variable `z` is assigned to, but never used
--> $DIR/liveness-unused.rs:37:13
--> $DIR/liveness-unused.rs:38:13
|
LL | let mut z = 3;
| ^
@ -73,31 +73,31 @@ LL | let mut z = 3;
= note: consider using `_z` instead
error: unused variable: `i`
--> $DIR/liveness-unused.rs:59:12
--> $DIR/liveness-unused.rs:60:12
|
LL | Some(i) => {
| ^ help: if this is intentional, prefix it with an underscore: `_i`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:79:9
--> $DIR/liveness-unused.rs:80:9
|
LL | for x in 1..10 { }
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:84:10
--> $DIR/liveness-unused.rs:85:10
|
LL | for (x, _) in [1, 2, 3].iter().enumerate() { }
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: unused variable: `x`
--> $DIR/liveness-unused.rs:89:13
--> $DIR/liveness-unused.rs:90:13
|
LL | for (_, x) in [1, 2, 3].iter().enumerate() {
| ^ help: if this is intentional, prefix it with an underscore: `_x`
error: variable `x` is assigned to, but never used
--> $DIR/liveness-unused.rs:112:9
--> $DIR/liveness-unused.rs:113:9
|
LL | let x;
| ^
@ -105,7 +105,7 @@ LL | let x;
= note: consider using `_x` instead
error: value assigned to `x` is never read
--> $DIR/liveness-unused.rs:116:9
--> $DIR/liveness-unused.rs:117:9
|
LL | x = 0;
| ^

View file

@ -0,0 +1,28 @@
//@ check-pass
#![feature(const_deref)]
#![feature(const_trait_impl)]
use std::ops::Deref;
struct Wrap<T>(T);
struct Foo;
impl Foo {
const fn call(&self) {}
}
impl<T> const Deref for Wrap<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
const fn foo() {
let x = Wrap(Foo);
x.call();
}
fn main() {}

View file

@ -1,8 +1,8 @@
error[E0284]: type annotations needed: cannot normalize `X::{constant#0}`
error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated`
--> $DIR/const-region-infer-to-static-in-binder.rs:4:10
|
LL | struct X<const FN: fn() = { || {} }>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`
error: using function pointers as const generic parameters is forbidden
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20

View file

@ -10,11 +10,11 @@ LL | #![feature(specialization)]
error: cannot normalize `<T as Default>::Id: '_`
error[E0284]: type annotations needed: cannot normalize `<T as Default>::Id`
error[E0282]: type annotations needed
--> $DIR/specialization-transmute.rs:15:23
|
LL | fn intu(&self) -> &Self::Id {
| ^^^^^^^^^ cannot normalize `<T as Default>::Id`
| ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id`
error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
--> $DIR/specialization-transmute.rs:17:9
@ -36,4 +36,5 @@ LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {
error: aborting due to 4 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0284`.
Some errors have detailed explanations: E0282, E0284.
For more information about an error, try `rustc --explain E0282`.

View file

@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize`
|
LL | fn func<const N: u32>() -> [(); N];
| ^^^^^^^ expected `usize`, found `u32`
|
= note: the length of array `[(); N]` must be type `usize`
error: aborting due to 2 previous errors

View file

@ -7,6 +7,8 @@ LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
|
note: required by a bound in `AssertParamIsEq`
--> $SRC_DIR/core/src/cmp.rs:LL:COL
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
|

View file

@ -1,3 +1,5 @@
//@ check-pass
#![feature(unsafe_binders)]
//~^ WARN the feature `unsafe_binders` is incomplete
@ -7,8 +9,6 @@ fn main() {
unsafe {
let x = 1;
let binder: unsafe<'a> &'a i32 = wrap_binder!(&x);
//~^ ERROR unsafe binder casts are not fully implemented
let rx = *unwrap_binder!(binder);
//~^ ERROR unsafe binder casts are not fully implemented
}
}

View file

@ -1,5 +1,5 @@
warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/expr.rs:1:12
--> $DIR/expr.rs:3:12
|
LL | #![feature(unsafe_binders)]
| ^^^^^^^^^^^^^^
@ -7,17 +7,5 @@ LL | #![feature(unsafe_binders)]
= note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information
= note: `#[warn(incomplete_features)]` on by default
error: unsafe binder casts are not fully implemented
--> $DIR/expr.rs:9:55
|
LL | let binder: unsafe<'a> &'a i32 = wrap_binder!(&x);
| ^^
error: unsafe binder casts are not fully implemented
--> $DIR/expr.rs:11:34
|
LL | let rx = *unwrap_binder!(binder);
| ^^^^^^
error: aborting due to 2 previous errors; 1 warning emitted
warning: 1 warning emitted

View file

@ -5,38 +5,31 @@ use std::unsafe_binder::{wrap_binder, unwrap_binder};
fn a() {
let _: unsafe<'a> &'a i32 = wrap_binder!(&());
//~^ ERROR unsafe binder casts are not fully implemented
//~| ERROR mismatched types
//~^ ERROR mismatched types
}
fn b() {
let _: i32 = wrap_binder!(&());
//~^ ERROR unsafe binder casts are not fully implemented
//~| ERROR `wrap_binder!()` can only wrap into unsafe binder
//~^ ERROR `wrap_binder!()` can only wrap into unsafe binder
}
fn c() {
let y = 1;
unwrap_binder!(y);
//~^ ERROR unsafe binder casts are not fully implemented
//~| ERROR expected unsafe binder, found integer as input
//~^ ERROR expected unsafe binder, found integer as input
}
fn d() {
let unknown = Default::default();
//~^ ERROR type annotations needed
unwrap_binder!(unknown);
//~^ ERROR unsafe binder casts are not fully implemented
// FIXME(unsafe_binders): This should report ambiguity once we've removed
// the error above which taints the infcx.
}
fn e() {
let x = wrap_binder!(&42);
//~^ ERROR unsafe binder casts are not fully implemented
//~^ ERROR type annotations needed
// Currently, type inference doesn't flow backwards for unsafe binders.
// It could, perhaps, but that may cause even more surprising corners.
// FIXME(unsafe_binders): This should report ambiguity once we've removed
// the error above which taints the infcx.
let _: unsafe<'a> &'a i32 = x;
}

View file

@ -7,12 +7,6 @@ LL | #![feature(unsafe_binders)]
= note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information
= note: `#[warn(incomplete_features)]` on by default
error: unsafe binder casts are not fully implemented
--> $DIR/mismatch.rs:7:46
|
LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&());
| ^^^
error[E0308]: mismatched types
--> $DIR/mismatch.rs:7:46
|
@ -22,14 +16,8 @@ LL | let _: unsafe<'a> &'a i32 = wrap_binder!(&());
= note: expected reference `&i32`
found reference `&()`
error: unsafe binder casts are not fully implemented
--> $DIR/mismatch.rs:13:31
|
LL | let _: i32 = wrap_binder!(&());
| ^^^
error: `wrap_binder!()` can only wrap into unsafe binder, not `i32`
--> $DIR/mismatch.rs:13:18
--> $DIR/mismatch.rs:12:18
|
LL | let _: i32 = wrap_binder!(&());
| ^^^^^^^^^^^^^^^^^
@ -37,32 +25,35 @@ LL | let _: i32 = wrap_binder!(&());
= note: unsafe binders are the only valid output of wrap
= note: this error originates in the macro `wrap_binder` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe binder casts are not fully implemented
--> $DIR/mismatch.rs:20:20
|
LL | unwrap_binder!(y);
| ^
error: expected unsafe binder, found integer as input of `unwrap_binder!()`
--> $DIR/mismatch.rs:20:20
--> $DIR/mismatch.rs:18:20
|
LL | unwrap_binder!(y);
| ^
|
= note: only an unsafe binder type can be unwrapped
error: unsafe binder casts are not fully implemented
--> $DIR/mismatch.rs:27:20
error[E0282]: type annotations needed
--> $DIR/mismatch.rs:23:9
|
LL | let unknown = Default::default();
| ^^^^^^^
LL |
LL | unwrap_binder!(unknown);
| ^^^^^^^
| ------- type must be known at this point
|
help: consider giving `unknown` an explicit type
|
LL | let unknown: /* Type */ = Default::default();
| ++++++++++++
error: unsafe binder casts are not fully implemented
--> $DIR/mismatch.rs:34:26
error[E0282]: type annotations needed
--> $DIR/mismatch.rs:29:26
|
LL | let x = wrap_binder!(&42);
| ^^^
| ^^^ cannot infer type
error: aborting due to 8 previous errors; 1 warning emitted
error: aborting due to 5 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0308`.
Some errors have detailed explanations: E0282, E0308.
For more information about an error, try `rustc --explain E0282`.

View file

@ -0,0 +1,41 @@
//@ known-bug: unknown
#![feature(unsafe_binders)]
// FIXME(unsafe_binders) ~^ WARN the feature `unsafe_binders` is incomplete
use std::unsafe_binder::{wrap_binder, unwrap_binder};
use std::mem::{drop, ManuallyDrop};
struct NotCopyInner;
type NotCopy = ManuallyDrop<NotCopyInner>;
fn use_after_wrap() {
unsafe {
let base = NotCopy;
let binder: unsafe<> NotCopy = wrap_binder!(base);
drop(base);
// FIXME(unsafe_binders) ~^ ERROR use of moved value: `base`
}
}
fn move_out_of_wrap() {
unsafe {
let binder: unsafe<> NotCopy = wrap_binder!(NotCopy);
drop(unwrap_binder!(binder));
drop(unwrap_binder!(binder));
// FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder`
}
}
fn not_conflicting() {
unsafe {
let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy));
drop(unwrap_binder!(binder).0);
drop(unwrap_binder!(binder).1);
// ^ NOT a problem.
drop(unwrap_binder!(binder).0);
// FIXME(unsafe_binders) ~^ ERROR use of moved value: `binder.0`
}
}
fn main() {}

View file

@ -0,0 +1,85 @@
error[E0423]: expected value, found type alias `NotCopy`
--> $DIR/moves.rs:14:20
|
LL | let base = NotCopy;
| ^^^^^^^
|
= note: can't use a type alias as a constructor
error[E0423]: expected value, found type alias `NotCopy`
--> $DIR/moves.rs:23:53
|
LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy);
| ^^^^^^^
|
= note: can't use a type alias as a constructor
error[E0423]: expected value, found type alias `NotCopy`
--> $DIR/moves.rs:32:65
|
LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy));
| ^^^^^^^
|
= note: can't use a type alias as a constructor
error[E0423]: expected value, found type alias `NotCopy`
--> $DIR/moves.rs:32:74
|
LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy));
| ^^^^^^^
|
= note: can't use a type alias as a constructor
warning: the feature `unsafe_binders` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/moves.rs:3:12
|
LL | #![feature(unsafe_binders)]
| ^^^^^^^^^^^^^^
|
= note: see issue #130516 <https://github.com/rust-lang/rust/issues/130516> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied
--> $DIR/moves.rs:15:21
|
LL | let binder: unsafe<> NotCopy = wrap_binder!(base);
| ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner`
|
= note: required for `ManuallyDrop<NotCopyInner>` to implement `Copy`
help: consider annotating `NotCopyInner` with `#[derive(Copy)]`
|
LL + #[derive(Copy)]
LL | struct NotCopyInner;
|
error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied
--> $DIR/moves.rs:23:21
|
LL | let binder: unsafe<> NotCopy = wrap_binder!(NotCopy);
| ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner`
|
= note: required for `ManuallyDrop<NotCopyInner>` to implement `Copy`
help: consider annotating `NotCopyInner` with `#[derive(Copy)]`
|
LL + #[derive(Copy)]
LL | struct NotCopyInner;
|
error[E0277]: the trait bound `NotCopyInner: Copy` is not satisfied
--> $DIR/moves.rs:32:21
|
LL | let binder: unsafe<> (NotCopy, NotCopy) = wrap_binder!((NotCopy, NotCopy));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `NotCopyInner`
|
= note: required for `ManuallyDrop<NotCopyInner>` to implement `Copy`
= note: required because it appears within the type `(ManuallyDrop<NotCopyInner>, ManuallyDrop<NotCopyInner>)`
help: consider annotating `NotCopyInner` with `#[derive(Copy)]`
|
LL + #[derive(Copy)]
LL | struct NotCopyInner;
|
error: aborting due to 7 previous errors; 1 warning emitted
Some errors have detailed explanations: E0277, E0423.
For more information about an error, try `rustc --explain E0277`.

View file

@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
= note: slice and array elements must have `Sized` type
error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:19:11
@ -13,6 +14,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
= note: slice and array elements must have `Sized` type
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: the size for values of type `str` cannot be known at compilation time
@ -22,6 +24,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:22:11
@ -30,6 +34,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 4 previous errors

View file

@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | fn bar(&self, x: &Bar<Self>);
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-arg.rs:11:15
|
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;

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