Auto merge of #125691 - jieyouxu:rollup-0i3wrc4, r=jieyouxu

Rollup of 8 pull requests

Successful merges:

 - #124251 (Add an intrinsic for `ptr::metadata`)
 - #124320 (Add `--print=check-cfg` to get the expected configs)
 - #125226 (Make more of the test suite run on Mac Catalyst)
 - #125381 (Silence some resolve errors when there have been glob import errors)
 - #125633 (miri: avoid making a full copy of all new allocations)
 - #125638 (Rewrite `lto-smoke`, `simple-rlib` and `mixing-deps` `run-make` tests in `rmake.rs` format)
 - #125639 (Support `./x doc run-make-support --open`)
 - #125664 (Tweak relations to no longer rely on `TypeTrace`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-05-29 03:55:21 +00:00
commit 751691271d
140 changed files with 1278 additions and 465 deletions

View File

@ -616,22 +616,34 @@ fn codegen_stmt<'tcx>(
Rvalue::UnaryOp(un_op, ref operand) => {
let operand = codegen_operand(fx, operand);
let layout = operand.layout();
let val = operand.load_scalar(fx);
let res = match un_op {
UnOp::Not => match layout.ty.kind() {
ty::Bool => {
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
CValue::by_val(res, layout)
UnOp::Not => {
let val = operand.load_scalar(fx);
match layout.ty.kind() {
ty::Bool => {
let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0);
CValue::by_val(res, layout)
}
ty::Uint(_) | ty::Int(_) => {
CValue::by_val(fx.bcx.ins().bnot(val), layout)
}
_ => unreachable!("un op Not for {:?}", layout.ty),
}
ty::Uint(_) | ty::Int(_) => {
CValue::by_val(fx.bcx.ins().bnot(val), layout)
}
UnOp::Neg => {
let val = operand.load_scalar(fx);
match layout.ty.kind() {
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
_ => unreachable!("un op Neg for {:?}", layout.ty),
}
_ => unreachable!("un op Not for {:?}", layout.ty),
},
UnOp::Neg => match layout.ty.kind() {
ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout),
ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout),
_ => unreachable!("un op Neg for {:?}", layout.ty),
}
UnOp::PtrMetadata => match layout.abi {
Abi::Scalar(_) => CValue::zst(dest_layout),
Abi::ScalarPair(_, _) => {
CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout)
}
_ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"),
},
};
lval.write_cvalue(fx, res);

View File

@ -100,7 +100,7 @@ pub(crate) fn codegen_const_value<'tcx>(
assert!(layout.is_sized(), "unsized const value");
if layout.is_zst() {
return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
return CValue::zst(layout);
}
match const_val {

View File

@ -95,6 +95,14 @@ impl<'tcx> CValue<'tcx> {
CValue(CValueInner::ByValPair(value, extra), layout)
}
/// Create an instance of a ZST
///
/// The is represented by a dangling pointer of suitable alignment.
pub(crate) fn zst(layout: TyAndLayout<'tcx>) -> CValue<'tcx> {
assert!(layout.is_zst());
CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout)
}
pub(crate) fn layout(&self) -> TyAndLayout<'tcx> {
self.1
}

View File

@ -565,6 +565,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
for elem in place_ref.projection.iter() {
match elem {
mir::ProjectionElem::Field(ref f, _) => {
debug_assert!(
!o.layout.ty.is_any_ptr(),
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
but tried to access field {f:?} of pointer {o:?}",
);
o = o.extract_field(bx, f.index());
}
mir::ProjectionElem::Index(_)

View File

@ -480,6 +480,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
cg_base = match *elem {
mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()),
mir::ProjectionElem::Field(ref field, _) => {
debug_assert!(
!cg_base.layout.ty.is_any_ptr(),
"Bad PlaceRef: destructing pointers should use cast/PtrMetadata, \
but tried to access field {field:?} of pointer {cg_base:?}",
);
cg_base.project_field(bx, field.index())
}
mir::ProjectionElem::OpaqueCast(ty) => {

View File

@ -623,19 +623,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::UnaryOp(op, ref operand) => {
let operand = self.codegen_operand(bx, operand);
let lloperand = operand.immediate();
let is_float = operand.layout.ty.is_floating_point();
let llval = match op {
mir::UnOp::Not => bx.not(lloperand),
let (val, layout) = match op {
mir::UnOp::Not => {
let llval = bx.not(operand.immediate());
(OperandValue::Immediate(llval), operand.layout)
}
mir::UnOp::Neg => {
if is_float {
bx.fneg(lloperand)
let llval = if is_float {
bx.fneg(operand.immediate())
} else {
bx.neg(lloperand)
bx.neg(operand.immediate())
};
(OperandValue::Immediate(llval), operand.layout)
}
mir::UnOp::PtrMetadata => {
debug_assert!(operand.layout.ty.is_unsafe_ptr());
let (_, meta) = operand.val.pointer_parts();
assert_eq!(operand.layout.fields.count() > 1, meta.is_some());
if let Some(meta) = meta {
(OperandValue::Immediate(meta), operand.layout.field(self.cx, 1))
} else {
(OperandValue::ZeroSized, bx.cx().layout_of(bx.tcx().types.unit))
}
}
};
OperandRef { val: OperandValue::Immediate(llval), layout: operand.layout }
debug_assert!(
val.is_expected_variant_for_type(self.cx, layout),
"Made wrong variant {val:?} for type {layout:?}",
);
OperandRef { val, layout }
}
mir::Rvalue::Discriminant(ref place) => {
@ -718,8 +735,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let values = op.val.immediates_or_place().left_or_else(|p| {
bug!("Field {field_idx:?} is {p:?} making {layout:?}");
});
inputs.extend(values);
let scalars = self.value_kind(op.layout).scalars().unwrap();
debug_assert_eq!(values.len(), scalars.len());
inputs.extend(values);
input_scalars.extend(scalars);
}

View File

@ -174,7 +174,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
unimplemented!()
}
fn init_frame_extra(
fn init_frame(
_ecx: &mut InterpCx<'tcx, Self>,
_frame: interpret::Frame<'tcx, Self::Provenance>,
) -> interpret::InterpResult<'tcx, interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>>

View File

@ -643,7 +643,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> {
}
#[inline(always)]
fn init_frame_extra(
fn init_frame(
ecx: &mut InterpCx<'tcx, Self>,
frame: Frame<'tcx>,
) -> InterpResult<'tcx, Frame<'tcx>> {

View File

@ -207,7 +207,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
assert!(cast_to.ty.is_unsafe_ptr());
// Handle casting any ptr to raw ptr (might be a fat ptr).
if cast_to.size == src.layout.size {
// Thin or fat pointer that just hast the ptr kind of target type changed.
// Thin or fat pointer that just has the ptr kind of target type changed.
return Ok(ImmTy::from_immediate(**src, cast_to));
} else {
// Casting the metadata away from a fat ptr.

View File

@ -819,7 +819,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
tracing_span: SpanGuard::new(),
extra: (),
};
let frame = M::init_frame_extra(self, pre_frame)?;
let frame = M::init_frame(self, pre_frame)?;
self.stack_mut().push(frame);
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).

View File

@ -329,27 +329,41 @@ pub trait Machine<'tcx>: Sized {
ptr: Pointer<Self::Provenance>,
) -> Option<(AllocId, Size, Self::ProvenanceExtra)>;
/// Called to adjust allocations to the Provenance and AllocExtra of this machine.
/// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
///
/// If `alloc` contains pointers, then they are all pointing to globals.
///
/// The way we construct allocations is to always first construct it without extra and then add
/// the extra. This keeps uniform code paths for handling both allocations created by CTFE for
/// globals, and allocations created by Miri during evaluation.
///
/// `kind` is the kind of the allocation being adjusted; it can be `None` when
/// it's a global and `GLOBAL_KIND` is `None`.
///
/// This should avoid copying if no work has to be done! If this returns an owned
/// allocation (because a copy had to be done to adjust things), machine memory will
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
/// owned allocation to the map even when the map is shared.)
fn adjust_allocation<'b>(
fn adjust_global_allocation<'b>(
ecx: &InterpCx<'tcx, Self>,
id: AllocId,
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKind>>,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>;
alloc: &'b Allocation,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
{
// The default implementation does a copy; CTFE machines have a more efficient implementation
// based on their particular choice for `Provenance`, `AllocExtra`, and `Bytes`.
let kind = Self::GLOBAL_KIND
.expect("if GLOBAL_KIND is None, adjust_global_allocation must be overwritten");
let alloc = alloc.adjust_from_tcx(&ecx.tcx, |ptr| ecx.global_root_pointer(ptr))?;
let extra =
Self::init_alloc_extra(ecx, id, MemoryKind::Machine(kind), alloc.size(), alloc.align)?;
Ok(Cow::Owned(alloc.with_extra(extra)))
}
/// Initialize the extra state of an allocation.
///
/// This is guaranteed to be called exactly once on all allocations that are accessed by the
/// program.
fn init_alloc_extra(
ecx: &InterpCx<'tcx, Self>,
id: AllocId,
kind: MemoryKind<Self::MemoryKind>,
size: Size,
align: Align,
) -> InterpResult<'tcx, Self::AllocExtra>;
/// Return a "root" pointer for the given allocation: the one that is used for direct
/// accesses to this static/const/fn allocation, or the one returned from the heap allocator.
@ -473,7 +487,7 @@ pub trait Machine<'tcx>: Sized {
}
/// Called immediately before a new stack frame gets pushed.
fn init_frame_extra(
fn init_frame(
ecx: &mut InterpCx<'tcx, Self>,
frame: Frame<'tcx, Self::Provenance>,
) -> InterpResult<'tcx, Frame<'tcx, Self::Provenance, Self::FrameExtra>>;
@ -590,13 +604,23 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
}
#[inline(always)]
fn adjust_allocation<'b>(
fn adjust_global_allocation<'b>(
_ecx: &InterpCx<$tcx, Self>,
_id: AllocId,
alloc: Cow<'b, Allocation>,
_kind: Option<MemoryKind<Self::MemoryKind>>,
alloc: &'b Allocation,
) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> {
Ok(alloc)
// Overwrite default implementation: no need to adjust anything.
Ok(Cow::Borrowed(alloc))
}
fn init_alloc_extra(
_ecx: &InterpCx<$tcx, Self>,
_id: AllocId,
_kind: MemoryKind<Self::MemoryKind>,
_size: Size,
_align: Align,
) -> InterpResult<$tcx, Self::AllocExtra> {
Ok(())
}
fn extern_static_pointer(

View File

@ -239,7 +239,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
pub fn allocate_raw_ptr(
&mut self,
alloc: Allocation,
alloc: Allocation<M::Provenance, (), M::Bytes>,
kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let id = self.tcx.reserve_alloc_id();
@ -248,8 +248,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
M::GLOBAL_KIND.map(MemoryKind::Machine),
"dynamically allocating global memory"
);
let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?;
self.memory.alloc_map.insert(id, (kind, alloc.into_owned()));
// We have set things up so we don't need to call `adjust_from_tcx` here,
// so we avoid copying the entire allocation contents.
let extra = M::init_alloc_extra(self, id, kind, alloc.size(), alloc.align)?;
let alloc = alloc.with_extra(extra);
self.memory.alloc_map.insert(id, (kind, alloc));
M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind))
}
@ -583,11 +586,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};
M::before_access_global(self.tcx, &self.machine, id, alloc, def_id, is_write)?;
// We got tcx memory. Let the machine initialize its "extra" stuff.
M::adjust_allocation(
M::adjust_global_allocation(
self,
id, // always use the ID we got as input, not the "hidden" one.
Cow::Borrowed(alloc.inner()),
M::GLOBAL_KIND.map(MemoryKind::Machine),
alloc.inner(),
)
}

View File

@ -9,7 +9,7 @@ use rustc_middle::{bug, span_bug};
use rustc_span::symbol::sym;
use tracing::trace;
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine};
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
@ -415,11 +415,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
use rustc_middle::mir::UnOp::*;
let layout = val.layout;
let val = val.to_scalar();
trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
match layout.ty.kind() {
ty::Bool => {
let val = val.to_scalar();
let val = val.to_bool()?;
let res = match un_op {
Not => !val,
@ -428,6 +428,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
Ok(ImmTy::from_bool(res, *self.tcx))
}
ty::Float(fty) => {
let val = val.to_scalar();
// No NaN adjustment here, `-` is a bitwise operation!
let res = match (un_op, fty) {
(Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
@ -436,8 +437,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};
Ok(ImmTy::from_scalar(res, layout))
}
_ => {
assert!(layout.ty.is_integral());
_ if layout.ty.is_integral() => {
let val = val.to_scalar();
let val = val.to_bits(layout.size)?;
let res = match un_op {
Not => self.truncate(!val, layout), // bitwise negation, then truncate
@ -450,9 +451,28 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Truncate to target type.
self.truncate(res, layout)
}
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
};
Ok(ImmTy::from_uint(res, layout))
}
ty::RawPtr(..) => {
assert_eq!(un_op, PtrMetadata);
let (_, meta) = val.to_scalar_and_meta();
Ok(match meta {
MemPlaceMeta::Meta(scalar) => {
let ty = un_op.ty(*self.tcx, val.layout.ty);
let layout = self.layout_of(ty)?;
ImmTy::from_scalar(scalar, layout)
}
MemPlaceMeta::None => {
let unit_layout = self.layout_of(self.tcx.types.unit)?;
ImmTy::uninit(unit_layout)
}
})
}
_ => {
bug!("Unexpected unary op argument {val:?}")
}
}
}
}

View File

@ -804,6 +804,39 @@ fn print_crate_info(
println_info!("{cfg}");
}
}
CheckCfg => {
let mut check_cfgs: Vec<String> = Vec::with_capacity(410);
// INSTABILITY: We are sorting the output below.
#[allow(rustc::potential_query_instability)]
for (name, expected_values) in &sess.psess.check_config.expecteds {
use crate::config::ExpectedValues;
match expected_values {
ExpectedValues::Any => check_cfgs.push(format!("{name}=any()")),
ExpectedValues::Some(values) => {
check_cfgs.extend(values.iter().map(|value| {
if let Some(value) = value {
format!("{name}=\"{value}\"")
} else {
name.to_string()
}
}))
}
}
}
check_cfgs.sort_unstable();
if !sess.psess.check_config.exhaustive_names {
if !sess.psess.check_config.exhaustive_values {
println_info!("any()=any()");
} else {
println_info!("any()");
}
}
for check_cfg in check_cfgs {
println_info!("{check_cfg}");
}
}
CallingConventions => {
let mut calling_conventions = rustc_target::spec::abi::all_names();
calling_conventions.sort_unstable();

View File

@ -130,6 +130,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
| sym::is_val_statically_known
| sym::ptr_mask
| sym::aggregate_raw_ptr
| sym::ptr_metadata
| sym::ub_checks
| sym::fadd_algebraic
| sym::fsub_algebraic
@ -576,6 +577,7 @@ pub fn check_intrinsic_type(
// This type check is not particularly useful, but the `where` bounds
// on the definition in `core` do the heavy lifting for checking it.
sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)),
sym::ptr_metadata => (2, 1, vec![Ty::new_imm_ptr(tcx, param(0))], param(1)),
sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool),

View File

@ -1158,7 +1158,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (a_sig, b_sig) = self.normalize(new.span, (a_sig, b_sig));
let sig = self
.at(cause, self.param_env)
.trace(prev_ty, new_ty)
.lub(DefineOpaqueTypes::Yes, a_sig, b_sig)
.map(|ok| self.register_infer_ok_obligations(ok))?;

View File

@ -48,11 +48,6 @@ pub struct At<'a, 'tcx> {
pub param_env: ty::ParamEnv<'tcx>,
}
pub struct Trace<'a, 'tcx> {
at: At<'a, 'tcx>,
trace: TypeTrace<'tcx>,
}
impl<'tcx> InferCtxt<'tcx> {
#[inline]
pub fn at<'a>(
@ -109,9 +104,6 @@ impl<'a, 'tcx> At<'a, 'tcx> {
/// call like `foo(x)`, where `foo: fn(i32)`, you might have
/// `sup(i32, x)`, since the "expected" type is the type that
/// appears in the signature.
///
/// See [`At::trace`] and [`Trace::sub`] for a version of
/// this method that only requires `T: Relate<'tcx>`
pub fn sup<T>(
self,
define_opaque_types: DefineOpaqueTypes,
@ -121,13 +113,19 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
self.trace(expected, actual).sup(define_opaque_types, expected, actual)
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
define_opaque_types,
);
fields
.sup()
.relate(expected, actual)
.map(|_| InferOk { value: (), obligations: fields.obligations })
}
/// Makes `expected <: actual`.
///
/// See [`At::trace`] and [`Trace::sub`] for a version of
/// this method that only requires `T: Relate<'tcx>`
pub fn sub<T>(
self,
define_opaque_types: DefineOpaqueTypes,
@ -137,13 +135,19 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
self.trace(expected, actual).sub(define_opaque_types, expected, actual)
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
define_opaque_types,
);
fields
.sub()
.relate(expected, actual)
.map(|_| InferOk { value: (), obligations: fields.obligations })
}
/// Makes `expected <: actual`.
///
/// See [`At::trace`] and [`Trace::eq`] for a version of
/// this method that only requires `T: Relate<'tcx>`
/// Makes `expected == actual`.
pub fn eq<T>(
self,
define_opaque_types: DefineOpaqueTypes,
@ -153,7 +157,40 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
self.trace(expected, actual).eq(define_opaque_types, expected, actual)
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
define_opaque_types,
);
fields
.equate(StructurallyRelateAliases::No)
.relate(expected, actual)
.map(|_| InferOk { value: (), obligations: fields.obligations })
}
/// Equates `expected` and `found` while structurally relating aliases.
/// This should only be used inside of the next generation trait solver
/// when relating rigid aliases.
pub fn eq_structurally_relating_aliases<T>(
self,
expected: T,
actual: T,
) -> InferResult<'tcx, ()>
where
T: ToTrace<'tcx>,
{
assert!(self.infcx.next_trait_solver());
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
DefineOpaqueTypes::Yes,
);
fields
.equate(StructurallyRelateAliases::Yes)
.relate(expected, actual)
.map(|_| InferOk { value: (), obligations: fields.obligations })
}
pub fn relate<T>(
@ -185,9 +222,6 @@ impl<'a, 'tcx> At<'a, 'tcx> {
/// this can result in an error (e.g., if asked to compute LUB of
/// u32 and i32), it is meaningful to call one of them the
/// "expected type".
///
/// See [`At::trace`] and [`Trace::lub`] for a version of
/// this method that only requires `T: Relate<'tcx>`
pub fn lub<T>(
self,
define_opaque_types: DefineOpaqueTypes,
@ -197,15 +231,21 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
self.trace(expected, actual).lub(define_opaque_types, expected, actual)
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
define_opaque_types,
);
fields
.lub()
.relate(expected, actual)
.map(|value| InferOk { value, obligations: fields.obligations })
}
/// Computes the greatest-lower-bound, or mutual subtype, of two
/// values. As with `lub` order doesn't matter, except for error
/// cases.
///
/// See [`At::trace`] and [`Trace::glb`] for a version of
/// this method that only requires `T: Relate<'tcx>`
pub fn glb<T>(
self,
define_opaque_types: DefineOpaqueTypes,
@ -215,105 +255,16 @@ impl<'a, 'tcx> At<'a, 'tcx> {
where
T: ToTrace<'tcx>,
{
self.trace(expected, actual).glb(define_opaque_types, expected, actual)
}
/// Sets the "trace" values that will be used for
/// error-reporting, but doesn't actually perform any operation
/// yet (this is useful when you want to set the trace using
/// distinct values from those you wish to operate upon).
pub fn trace<T>(self, expected: T, actual: T) -> Trace<'a, 'tcx>
where
T: ToTrace<'tcx>,
{
let trace = ToTrace::to_trace(self.cause, true, expected, actual);
Trace { at: self, trace }
}
}
impl<'a, 'tcx> Trace<'a, 'tcx> {
/// Makes `a <: b`.
#[instrument(skip(self), level = "debug")]
pub fn sub<T>(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
fields
.sub()
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}
/// Makes `a :> b`.
#[instrument(skip(self), level = "debug")]
pub fn sup<T>(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
fields
.sup()
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}
/// Makes `a == b`.
#[instrument(skip(self), level = "debug")]
pub fn eq<T>(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
fields
.equate(StructurallyRelateAliases::No)
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}
/// Equates `a` and `b` while structurally relating aliases. This should only
/// be used inside of the next generation trait solver when relating rigid aliases.
#[instrument(skip(self), level = "debug")]
pub fn eq_structurally_relating_aliases<T>(self, a: T, b: T) -> InferResult<'tcx, ()>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
debug_assert!(at.infcx.next_trait_solver());
let mut fields = at.infcx.combine_fields(trace, at.param_env, DefineOpaqueTypes::Yes);
fields
.equate(StructurallyRelateAliases::Yes)
.relate(a, b)
.map(move |_| InferOk { value: (), obligations: fields.obligations })
}
#[instrument(skip(self), level = "debug")]
pub fn lub<T>(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, T>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
fields
.lub()
.relate(a, b)
.map(move |t| InferOk { value: t, obligations: fields.obligations })
}
#[instrument(skip(self), level = "debug")]
pub fn glb<T>(self, define_opaque_types: DefineOpaqueTypes, a: T, b: T) -> InferResult<'tcx, T>
where
T: Relate<'tcx>,
{
let Trace { at, trace } = self;
let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
let mut fields = CombineFields::new(
self.infcx,
ToTrace::to_trace(self.cause, true, expected, actual),
self.param_env,
define_opaque_types,
);
fields
.glb()
.relate(a, b)
.map(move |t| InferOk { value: t, obligations: fields.obligations })
.relate(expected, actual)
.map(|value| InferOk { value, obligations: fields.obligations })
}
}

View File

@ -836,21 +836,6 @@ impl<'tcx> InferCtxt<'tcx> {
.collect()
}
fn combine_fields<'a>(
&'a self,
trace: TypeTrace<'tcx>,
param_env: ty::ParamEnv<'tcx>,
define_opaque_types: DefineOpaqueTypes,
) -> CombineFields<'a, 'tcx> {
CombineFields {
infcx: self,
trace,
param_env,
obligations: PredicateObligations::new(),
define_opaque_types,
}
}
pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool
where
T: at::ToTrace<'tcx>,

View File

@ -42,6 +42,17 @@ pub struct CombineFields<'infcx, 'tcx> {
pub define_opaque_types: DefineOpaqueTypes,
}
impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
pub fn new(
infcx: &'infcx InferCtxt<'tcx>,
trace: TypeTrace<'tcx>,
param_env: ty::ParamEnv<'tcx>,
define_opaque_types: DefineOpaqueTypes,
) -> Self {
Self { infcx, trace, param_env, define_opaque_types, obligations: vec![] }
}
}
impl<'tcx> InferCtxt<'tcx> {
pub fn super_combine_tys<R>(
&self,

View File

@ -266,19 +266,6 @@ impl AllocRange {
// The constructors are all without extra; the extra gets added by a machine hook later.
impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
/// Creates an allocation from an existing `Bytes` value - this is needed for miri FFI support
pub fn from_raw_bytes(bytes: Bytes, align: Align, mutability: Mutability) -> Self {
let size = Size::from_bytes(bytes.len());
Self {
bytes,
provenance: ProvenanceMap::new(),
init_mask: InitMask::new(size, true),
align,
mutability,
extra: (),
}
}
/// Creates an allocation initialized by the given bytes
pub fn from_bytes<'a>(
slice: impl Into<Cow<'a, [u8]>>,
@ -342,18 +329,30 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
Err(x) => x,
}
}
/// Add the extra.
pub fn with_extra<Extra>(self, extra: Extra) -> Allocation<Prov, Extra, Bytes> {
Allocation {
bytes: self.bytes,
provenance: self.provenance,
init_mask: self.init_mask,
align: self.align,
mutability: self.mutability,
extra,
}
}
}
impl Allocation {
/// Adjust allocation from the ones in `tcx` to a custom Machine instance
/// with a different `Provenance`, `Extra` and `Byte` type.
pub fn adjust_from_tcx<Prov: Provenance, Extra, Bytes: AllocBytes, Err>(
self,
/// with a different `Provenance` and `Byte` type.
pub fn adjust_from_tcx<Prov: Provenance, Bytes: AllocBytes, Err>(
&self,
cx: &impl HasDataLayout,
extra: Extra,
mut adjust_ptr: impl FnMut(Pointer<CtfeProvenance>) -> Result<Pointer<Prov>, Err>,
) -> Result<Allocation<Prov, Extra, Bytes>, Err> {
let mut bytes = self.bytes;
) -> Result<Allocation<Prov, (), Bytes>, Err> {
// Copy the data.
let mut bytes = Bytes::from_bytes(Cow::Borrowed(&*self.bytes), self.align);
// Adjust provenance of pointers stored in this allocation.
let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len());
let ptr_size = cx.data_layout().pointer_size.bytes_usize();
@ -369,12 +368,12 @@ impl Allocation {
}
// Create allocation.
Ok(Allocation {
bytes: AllocBytes::from_bytes(Cow::Owned(Vec::from(bytes)), self.align),
bytes,
provenance: ProvenanceMap::from_presorted_ptrs(new_provenance),
init_mask: self.init_mask,
init_mask: self.init_mask.clone(),
align: self.align,
mutability: self.mutability,
extra,
extra: self.extra,
})
}
}

View File

@ -1434,6 +1434,13 @@ pub enum UnOp {
Not,
/// The `-` operator for negation
Neg,
/// Get the metadata `M` from a `*const/mut impl Pointee<Metadata = M>`.
///
/// For example, this will give a `()` from `*const i32`, a `usize` from
/// `*mut [u8]`, or a pointer to a vtable from a `*const dyn Foo`.
///
/// Allowed only in [`MirPhase::Runtime`]; earlier it's an intrinsic.
PtrMetadata,
}
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]

View File

@ -180,7 +180,10 @@ impl<'tcx> Rvalue<'tcx> {
let rhs_ty = rhs.ty(local_decls, tcx);
op.ty(tcx, lhs_ty, rhs_ty)
}
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
Rvalue::UnaryOp(op, ref operand) => {
let arg_ty = operand.ty(local_decls, tcx);
op.ty(tcx, arg_ty)
}
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
tcx.types.usize
@ -282,6 +285,27 @@ impl<'tcx> BinOp {
}
}
impl<'tcx> UnOp {
pub fn ty(&self, tcx: TyCtxt<'tcx>, arg_ty: Ty<'tcx>) -> Ty<'tcx> {
match self {
UnOp::Not | UnOp::Neg => arg_ty,
UnOp::PtrMetadata => {
let pointee_ty = arg_ty
.builtin_deref(true)
.unwrap_or_else(|| bug!("PtrMetadata of non-dereferenceable ty {arg_ty:?}"));
if pointee_ty.is_trivially_sized(tcx) {
tcx.types.unit
} else {
let Some(metadata_def_id) = tcx.lang_items().metadata_type() else {
bug!("No metadata_type lang item while looking at {arg_ty:?}")
};
Ty::new_projection(tcx, metadata_def_id, [pointee_ty])
}
}
}
}
}
impl BorrowKind {
pub fn to_mutbl_lossy(self) -> hir::Mutability {
match self {

View File

@ -1579,8 +1579,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
let formatted_op = match op {
UnOp::Not => "!",
UnOp::Neg => "-",
UnOp::PtrMetadata => "PtrMetadata",
};
let parenthesized = match ct.kind() {
_ if op == UnOp::PtrMetadata => true,
ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op,
ty::ConstKind::Expr(_) => true,
_ => false,

View File

@ -212,6 +212,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset))))
},
@call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
@call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)),
@call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)),
ExprKind::Borrow { borrow_kind, arg } => Ok(
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)

View File

@ -316,6 +316,23 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
terminator.kind = TerminatorKind::Goto { target };
}
sym::ptr_metadata => {
let Ok([ptr]) = <[_; 1]>::try_from(std::mem::take(args)) else {
span_bug!(
terminator.source_info.span,
"Wrong number of arguments for ptr_metadata intrinsic",
);
};
let target = target.unwrap();
block.statements.push(Statement {
source_info: terminator.source_info,
kind: StatementKind::Assign(Box::new((
*destination,
Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
_ => {}
}
}

View File

@ -464,7 +464,7 @@ impl<'tcx> Validator<'_, 'tcx> {
Rvalue::UnaryOp(op, operand) => {
match op {
// These operations can never fail.
UnOp::Neg | UnOp::Not => {}
UnOp::Neg | UnOp::Not | UnOp::PtrMetadata => {}
}
self.validate_operand(operand)?;

View File

@ -1109,6 +1109,16 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ty::Int(..) | ty::Uint(..) | ty::Bool
);
}
UnOp::PtrMetadata => {
if !matches!(self.mir_phase, MirPhase::Runtime(_)) {
// It would probably be fine to support this in earlier phases,
// but at the time of writing it's only ever introduced from intrinsic lowering,
// so earlier things can just `bug!` on it.
self.fail(location, "PtrMetadata should be in runtime MIR only");
}
check_kinds!(a, "Cannot PtrMetadata non-pointer type {:?}", ty::RawPtr(..));
}
}
}
Rvalue::ShallowInitBox(operand, _) => {

View File

@ -537,6 +537,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let determined_imports = mem::take(&mut self.determined_imports);
let indeterminate_imports = mem::take(&mut self.indeterminate_imports);
let mut glob_error = false;
for (is_indeterminate, import) in determined_imports
.iter()
.map(|i| (false, i))
@ -548,6 +549,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
self.import_dummy_binding(*import, is_indeterminate);
if let Some(err) = unresolved_import_error {
glob_error |= import.is_glob();
if let ImportKind::Single { source, ref source_bindings, .. } = import.kind {
if source.name == kw::SelfLower {
// Silence `unresolved import` error if E0429 is already emitted
@ -563,7 +566,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
{
// In the case of a new import line, throw a diagnostic message
// for the previous line.
self.throw_unresolved_import_error(errors);
self.throw_unresolved_import_error(errors, glob_error);
errors = vec![];
}
if seen_spans.insert(err.span) {
@ -574,7 +577,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
if !errors.is_empty() {
self.throw_unresolved_import_error(errors);
self.throw_unresolved_import_error(errors, glob_error);
return;
}
@ -600,9 +603,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
if !errors.is_empty() {
self.throw_unresolved_import_error(errors);
}
self.throw_unresolved_import_error(errors, glob_error);
}
pub(crate) fn check_hidden_glob_reexports(
@ -672,7 +673,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
fn throw_unresolved_import_error(&mut self, errors: Vec<(Import<'_>, UnresolvedImportError)>) {
fn throw_unresolved_import_error(
&mut self,
errors: Vec<(Import<'_>, UnresolvedImportError)>,
glob_error: bool,
) {
if errors.is_empty() {
return;
}
@ -751,7 +756,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
diag.emit();
let guar = diag.emit();
if glob_error {
self.glob_error = Some(guar);
}
}
/// Attempts to resolve the given import, returning:

View File

@ -4033,9 +4033,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
}
#[inline]
/// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items.
/// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items or
// an invalid `use foo::*;` was found, which can cause unbounded ammounts of "item not found"
// errors. We silence them all.
fn should_report_errs(&self) -> bool {
!(self.r.tcx.sess.opts.actually_rustdoc && self.in_func_body)
&& !self.r.glob_error.is_some()
}
// Resolve in alternative namespaces if resolution in the primary namespace fails.

View File

@ -32,7 +32,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{FreezeReadGuard, Lrc};
use rustc_errors::{Applicability, Diag, ErrCode};
use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed};
use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
@ -1048,6 +1048,7 @@ pub struct Resolver<'a, 'tcx> {
/// Maps glob imports to the names of items actually imported.
glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
glob_error: Option<ErrorGuaranteed>,
visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
used_imports: FxHashSet<NodeId>,
maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
@ -1417,6 +1418,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ast_transform_scopes: FxHashMap::default(),
glob_map: Default::default(),
glob_error: None,
visibilities_for_hashing: Default::default(),
used_imports: FxHashSet::default(),
maybe_unused_trait_imports: Default::default(),

View File

@ -773,6 +773,7 @@ pub enum PrintKind {
TargetLibdir,
CrateName,
Cfg,
CheckCfg,
CallingConventions,
TargetList,
TargetCPUs,
@ -1443,7 +1444,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
"",
"print",
"Compiler information to print on stdout",
"[crate-name|file-names|sysroot|target-libdir|cfg|calling-conventions|\
"[crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|\
target-list|target-cpus|target-features|relocation-models|code-models|\
tls-models|target-spec-json|all-target-specs-json|native-static-libs|\
stack-protector-strategies|link-args|deployment-target]",
@ -1859,6 +1860,7 @@ fn collect_print_requests(
("all-target-specs-json", PrintKind::AllTargetSpecs),
("calling-conventions", PrintKind::CallingConventions),
("cfg", PrintKind::Cfg),
("check-cfg", PrintKind::CheckCfg),
("code-models", PrintKind::CodeModels),
("crate-name", PrintKind::CrateName),
("deployment-target", PrintKind::DeploymentTarget),
@ -1908,6 +1910,16 @@ fn collect_print_requests(
);
}
}
Some((_, PrintKind::CheckCfg)) => {
if unstable_opts.unstable_options {
PrintKind::CheckCfg
} else {
early_dcx.early_fatal(
"the `-Z unstable-options` flag must also be passed to \
enable the check-cfg print option",
);
}
}
Some(&(_, print_kind)) => print_kind,
None => {
let prints =

View File

@ -10,7 +10,7 @@ use rustc_span::Symbol;
use stable_mir::abi::Layout;
use stable_mir::mir::alloc::AllocId;
use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety};
use stable_mir::mir::{BinOp, Mutability, Place, ProjectionElem, Safety, UnOp};
use stable_mir::ty::{
Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, Const,
DynKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig,
@ -582,6 +582,18 @@ impl RustcInternal for BinOp {
}
}
impl RustcInternal for UnOp {
type T<'tcx> = rustc_middle::mir::UnOp;
fn internal<'tcx>(&self, _tables: &mut Tables<'_>, _tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
match self {
UnOp::Not => rustc_middle::mir::UnOp::Not,
UnOp::Neg => rustc_middle::mir::UnOp::Neg,
UnOp::PtrMetadata => rustc_middle::mir::UnOp::PtrMetadata,
}
}
}
impl<T> RustcInternal for &T
where
T: RustcInternal,

View File

@ -19,7 +19,7 @@ use stable_mir::abi::{FnAbi, Layout, LayoutShape};
use stable_mir::compiler_interface::Context;
use stable_mir::mir::alloc::GlobalAlloc;
use stable_mir::mir::mono::{InstanceDef, StaticDef};
use stable_mir::mir::{BinOp, Body, Place};
use stable_mir::mir::{BinOp, Body, Place, UnOp};
use stable_mir::target::{MachineInfo, MachineSize};
use stable_mir::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@ -700,6 +700,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let ty = bin_op.internal(&mut *tables, tcx).ty(tcx, rhs_internal, lhs_internal);
ty.stable(&mut *tables)
}
fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty {
let mut tables = self.0.borrow_mut();
let tcx = tables.tcx;
let arg_internal = arg.internal(&mut *tables, tcx);
let ty = un_op.internal(&mut *tables, tcx).ty(tcx, arg_internal);
ty.stable(&mut *tables)
}
}
pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);

View File

@ -526,6 +526,7 @@ impl<'tcx> Stable<'tcx> for mir::UnOp {
match self {
UnOp::Not => stable_mir::mir::UnOp::Not,
UnOp::Neg => stable_mir::mir::UnOp::Neg,
UnOp::PtrMetadata => stable_mir::mir::UnOp::PtrMetadata,
}
}
}

View File

@ -1179,6 +1179,7 @@ symbols! {
mir_make_place,
mir_move,
mir_offset,
mir_ptr_metadata,
mir_retag,
mir_return,
mir_return_to,
@ -1433,6 +1434,7 @@ symbols! {
ptr_guaranteed_cmp,
ptr_is_null,
ptr_mask,
ptr_metadata,
ptr_null,
ptr_null_mut,
ptr_offset_from,

View File

@ -363,7 +363,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
for (&orig, response) in iter::zip(original_values, var_values.var_values) {
let InferOk { value: (), obligations } = infcx
.at(&cause, param_env)
.trace(orig, response)
.eq_structurally_relating_aliases(orig, response)
.unwrap();
assert!(obligations.is_empty());

View File

@ -776,7 +776,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
let InferOk { value: (), obligations } = self
.infcx
.at(&ObligationCause::dummy(), param_env)
.trace(term, ctor_term)
.eq_structurally_relating_aliases(term, ctor_term)?;
debug_assert!(obligations.is_empty());
self.relate(param_env, alias, variance, rigid_ctor)
@ -796,11 +795,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
rhs: T,
) -> Result<(), NoSolution> {
let cause = ObligationCause::dummy();
let InferOk { value: (), obligations } = self
.infcx
.at(&cause, param_env)
.trace(lhs, rhs)
.eq_structurally_relating_aliases(lhs, rhs)?;
let InferOk { value: (), obligations } =
self.infcx.at(&cause, param_env).eq_structurally_relating_aliases(lhs, rhs)?;
assert!(obligations.is_empty());
Ok(())
}

View File

@ -566,10 +566,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
{
if let Ok(new_obligations) = infcx
.at(&obligation.cause, obligation.param_env)
.trace(c1, c2)
// Can define opaque types as this is only reachable with
// `generic_const_exprs`
.eq(DefineOpaqueTypes::Yes, a.args, b.args)
.eq(
DefineOpaqueTypes::Yes,
ty::AliasTerm::from(a),
ty::AliasTerm::from(b),
)
{
return ProcessResult::Changed(mk_pending(
new_obligations.into_obligations(),

View File

@ -910,10 +910,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if let Ok(InferOk { obligations, value: () }) = self
.infcx
.at(&obligation.cause, obligation.param_env)
.trace(c1, c2)
// Can define opaque types as this is only reachable with
// `generic_const_exprs`
.eq(DefineOpaqueTypes::Yes, a.args, b.args)
.eq(
DefineOpaqueTypes::Yes,
ty::AliasTerm::from(a),
ty::AliasTerm::from(b),
)
{
return self.evaluate_predicates_recursively(
previous_stack,

View File

@ -94,7 +94,7 @@ fn check_binop(op: mir::BinOp) -> bool {
fn check_unop(op: mir::UnOp) -> bool {
use mir::UnOp::*;
match op {
Not | Neg => true,
Not | Neg | PtrMetadata => true,
}
}

View File

@ -8,7 +8,7 @@ use std::cell::Cell;
use crate::abi::{FnAbi, Layout, LayoutShape};
use crate::mir::alloc::{AllocId, GlobalAlloc};
use crate::mir::mono::{Instance, InstanceDef, StaticDef};
use crate::mir::{BinOp, Body, Place};
use crate::mir::{BinOp, Body, Place, UnOp};
use crate::target::MachineInfo;
use crate::ty::{
AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef,
@ -226,6 +226,9 @@ pub trait Context {
/// Get the resulting type of binary operation.
fn binop_ty(&self, bin_op: BinOp, rhs: Ty, lhs: Ty) -> Ty;
/// Get the resulting type of unary operation.
fn unop_ty(&self, un_op: UnOp, arg: Ty) -> Ty;
}
// A thread local variable that stores a pointer to the tables mapping between TyCtxt

View File

@ -346,6 +346,15 @@ impl BinOp {
pub enum UnOp {
Not,
Neg,
PtrMetadata,
}
impl UnOp {
/// Return the type of this operation for the given input Ty.
/// This function does not perform type checking, and it currently doesn't handle SIMD.
pub fn ty(&self, arg_ty: Ty) -> Ty {
with(|ctx| ctx.unop_ty(*self, arg_ty))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
@ -580,7 +589,10 @@ impl Rvalue {
let ty = op.ty(lhs_ty, rhs_ty);
Ok(Ty::new_tuple(&[ty, Ty::bool_ty()]))
}
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, operand) => operand.ty(locals),
Rvalue::UnaryOp(op, operand) => {
let arg_ty = operand.ty(locals)?;
Ok(op.ty(arg_ty))
}
Rvalue::Discriminant(place) => {
let place_ty = place.ty(locals)?;
place_ty

View File

@ -2821,6 +2821,21 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
type Metadata = <P as ptr::Pointee>::Metadata;
}
/// Lowers in MIR to `Rvalue::UnaryOp` with `UnOp::PtrMetadata`.
///
/// This is used to implement functions like `ptr::metadata`.
#[rustc_nounwind]
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[rustc_intrinsic]
#[rustc_intrinsic_must_be_overridden]
#[cfg(not(bootstrap))]
pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *const P) -> M {
// To implement a fallback we'd have to assume the layout of the pointer,
// but the whole point of this intrinsic is that we shouldn't do that.
unreachable!()
}
// Some functions are defined here because they accidentally got made
// available in this module on stable. See <https://github.com/rust-lang/rust/issues/15702>.
// (`transmute` also falls into this category, but it cannot be wrapped due to the

View File

@ -360,6 +360,10 @@ define!("mir_assume", fn Assume(operand: bool));
define!("mir_deinit", fn Deinit<T>(place: T));
define!("mir_checked", fn Checked<T>(binop: T) -> (T, bool));
define!("mir_len", fn Len<T>(place: T) -> usize);
define!(
"mir_ptr_metadata",
fn PtrMetadata<P: ?Sized>(place: *const P) -> <P as ::core::ptr::Pointee>::Metadata
);
define!("mir_copy_for_deref", fn CopyForDeref<T>(place: T) -> T);
define!("mir_retag", fn Retag<T>(place: T));
define!("mir_move", fn Move<T>(place: T) -> T);

View File

@ -3,6 +3,8 @@
use crate::fmt;
use crate::hash::{Hash, Hasher};
use crate::intrinsics::aggregate_raw_ptr;
#[cfg(not(bootstrap))]
use crate::intrinsics::ptr_metadata;
use crate::marker::Freeze;
/// Provides the pointer metadata type of any pointed-to type.
@ -94,10 +96,17 @@ pub trait Thin = Pointee<Metadata = ()>;
#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")]
#[inline]
pub const fn metadata<T: ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
#[cfg(bootstrap)]
{
// SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T
// and PtrComponents<T> have the same memory layouts. Only std can make this
// guarantee.
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
}
#[cfg(not(bootstrap))]
{
ptr_metadata(ptr)
}
}
/// Forms a (possibly-wide) raw pointer from a data pointer and metadata.
@ -132,6 +141,7 @@ pub const fn from_raw_parts_mut<T: ?Sized>(
}
#[repr(C)]
#[cfg(bootstrap)]
union PtrRepr<T: ?Sized> {
const_ptr: *const T,
mut_ptr: *mut T,
@ -139,15 +149,18 @@ union PtrRepr<T: ?Sized> {
}
#[repr(C)]
#[cfg(bootstrap)]
struct PtrComponents<T: ?Sized> {
data_pointer: *const (),
metadata: <T as Pointee>::Metadata,
}
// Manual impl needed to avoid `T: Copy` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Copy for PtrComponents<T> {}
// Manual impl needed to avoid `T: Clone` bound.
#[cfg(bootstrap)]
impl<T: ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self {
*self

View File

@ -1171,3 +1171,15 @@ fn test_ptr_from_raw_parts_in_const() {
assert_eq!(EMPTY_SLICE_PTR.addr(), 123);
assert_eq!(EMPTY_SLICE_PTR.len(), 456);
}
#[test]
fn test_ptr_metadata_in_const() {
use std::fmt::Debug;
const ARRAY_META: () = std::ptr::metadata::<[u16; 3]>(&[1, 2, 3]);
const SLICE_META: usize = std::ptr::metadata::<[u16]>(&[1, 2, 3]);
const DYN_META: DynMetadata<dyn Debug> = std::ptr::metadata::<dyn Debug>(&[0_u8; 42]);
assert_eq!(ARRAY_META, ());
assert_eq!(SLICE_META, 3);
assert_eq!(DYN_META.size_of(), 42);
}

View File

@ -1431,7 +1431,7 @@ fn metadata_access_times() {
assert_eq!(check!(a.modified()), check!(a.modified()));
assert_eq!(check!(b.accessed()), check!(b.modified()));
if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
if cfg!(target_vendor = "apple") || cfg!(target_os = "windows") {
check!(a.created());
check!(b.created());
}

View File

@ -491,6 +491,14 @@ mod imp {
}
}
// This is intentionally not enabled on iOS/tvOS/watchOS/visionOS, as it uses
// several symbols that might lead to rejections from the App Store, namely
// `sigaction`, `sigaltstack`, `sysctlbyname`, `mmap`, `munmap` and `mprotect`.
//
// This might be overly cautious, though it is also what Swift does (and they
// usually have fewer qualms about forwards compatibility, since the runtime
// is shipped with the OS):
// <https://github.com/apple/swift/blob/swift-5.10-RELEASE/stdlib/public/runtime/CrashHandlerMacOS.cpp>
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",

View File

@ -1028,6 +1028,14 @@ tool_doc!(
is_library = true,
crates = ["bootstrap"]
);
tool_doc!(
RunMakeSupport,
"run_make_support",
"src/tools/run-make-support",
rustc_tool = false,
is_library = true,
crates = ["run_make_support"]
);
#[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)]
pub struct ErrorIndex {

View File

@ -888,6 +888,7 @@ impl<'a> Builder<'a> {
doc::Tidy,
doc::Bootstrap,
doc::Releases,
doc::RunMakeSupport,
),
Kind::Dist => describe!(
dist::Docs,

View File

@ -5,7 +5,7 @@
pub fn dylib_path_var() -> &'static str {
if cfg!(target_os = "windows") {
"PATH"
} else if cfg!(target_os = "macos") {
} else if cfg!(target_vendor = "apple") {
"DYLD_LIBRARY_PATH"
} else if cfg!(target_os = "haiku") {
"LIBRARY_PATH"

View File

@ -0,0 +1,25 @@
# `print=check-cfg`
The tracking issue for this feature is: [#XXXXXX](https://github.com/rust-lang/rust/issues/XXXXXX).
------------------------
This option of the `--print` flag print the list of expected cfgs.
This is related to the `--check-cfg` flag which allows specifying arbitrary expected
names and values.
This print option works similarly to `--print=cfg` (modulo check-cfg specifics):
- *check_cfg syntax*: *output of --print=check-cfg*
- `cfg(windows)`: `windows`
- `cfg(feature, values("foo", "bar"))`: `feature="foo"` and `feature="bar"`
- `cfg(feature, values(none(), ""))`: `feature` and `feature=""`
- `cfg(feature, values(any()))`: `feature=any()`
- `cfg(any())`: `any()`
- *nothing*: `any()=any()`
To be used like this:
```bash
rustc --print=check-cfg -Zunstable-options lib.rs
```

View File

@ -747,6 +747,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-aarch64",
"ignore-aarch64-unknown-linux-gnu",
"ignore-android",
"ignore-apple",
"ignore-arm",
"ignore-avr",
"ignore-beta",
@ -829,7 +830,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-x32",
"ignore-x86",
"ignore-x86_64",
"ignore-x86_64-apple-darwin",
"ignore-x86_64-unknown-linux-gnu",
"incremental",
"known-bug",
@ -876,6 +876,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"only-32bit",
"only-64bit",
"only-aarch64",
"only-apple",
"only-arm",
"only-avr",
"only-beta",

View File

@ -159,6 +159,12 @@ pub(super) fn parse_cfg_name_directive<'a>(
message: "when the architecture is part of the Thumb family"
}
condition! {
name: "apple",
condition: config.target.contains("apple"),
message: "when the target vendor is Apple"
}
// Technically the locally built compiler uses the "dev" channel rather than the "nightly"
// channel, even though most people don't know or won't care about it. To avoid confusion, we
// treat the "dev" channel as the "nightly" channel when processing the directive.

View File

@ -98,7 +98,7 @@ fn get_lib_name(lib: &str, aux_type: AuxType) -> Option<String> {
AuxType::Lib => Some(format!("lib{}.rlib", lib)),
AuxType::Dylib => Some(if cfg!(windows) {
format!("{}.dll", lib)
} else if cfg!(target_os = "macos") {
} else if cfg!(target_vendor = "apple") {
format!("lib{}.dylib", lib)
} else {
format!("lib{}.so", lib)

View File

@ -57,7 +57,7 @@ impl PathBufExt for PathBuf {
pub fn dylib_env_var() -> &'static str {
if cfg!(windows) {
"PATH"
} else if cfg!(target_os = "macos") {
} else if cfg!(target_vendor = "apple") {
"DYLD_LIBRARY_PATH"
} else if cfg!(target_os = "haiku") {
"LIBRARY_PATH"

View File

@ -862,14 +862,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if tcx.is_foreign_item(def_id) {
throw_unsup_format!("foreign thread-local statics are not supported");
}
let allocation = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
let mut allocation = allocation.inner().clone();
let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
// We make a full copy of this allocation.
let mut alloc = alloc.inner().adjust_from_tcx(&this.tcx, |ptr| this.global_root_pointer(ptr))?;
// This allocation will be deallocated when the thread dies, so it is not in read-only memory.
allocation.mutability = Mutability::Mut;
alloc.mutability = Mutability::Mut;
// Create a fresh allocation with this content.
let new_alloc = this.allocate_raw_ptr(allocation, MiriMemoryKind::Tls.into())?;
this.machine.threads.set_thread_local_alloc(def_id, new_alloc);
Ok(new_alloc)
let ptr = this.allocate_raw_ptr(alloc, MiriMemoryKind::Tls.into())?;
this.machine.threads.set_thread_local_alloc(def_id, ptr);
Ok(ptr)
}
}

View File

@ -1,7 +1,6 @@
//! Global machine state as well as implementation of the interpreter engine
//! `Machine` trait.
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::fmt;
@ -1086,40 +1085,33 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
}
fn adjust_allocation<'b>(
fn init_alloc_extra(
ecx: &MiriInterpCx<'tcx>,
id: AllocId,
alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind>,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
{
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
kind: MemoryKind,
size: Size,
align: Align,
) -> InterpResult<'tcx, Self::AllocExtra> {
if ecx.machine.tracked_alloc_ids.contains(&id) {
ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
id,
alloc.size(),
alloc.align,
kind,
));
ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id, size, align, kind));
}
let alloc = alloc.into_owned();
let borrow_tracker = ecx
.machine
.borrow_tracker
.as_ref()
.map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine));
.map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
let data_race = ecx.machine.data_race.as_ref().map(|data_race| {
data_race::AllocState::new_allocation(
data_race,
&ecx.machine.threads,
alloc.size(),
size,
kind,
ecx.machine.current_span(),
)
});
let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
let weak_memory = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
// If an allocation is leaked, we want to report a backtrace to indicate where it was
// allocated. We don't need to record a backtrace for allocations which are allowed to
@ -1130,17 +1122,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
Some(ecx.generate_stacktrace())
};
let alloc: Allocation<Provenance, Self::AllocExtra, Self::Bytes> = alloc.adjust_from_tcx(
&ecx.tcx,
AllocExtra {
borrow_tracker,
data_race: race_alloc,
weak_memory: buffer_alloc,
backtrace,
},
|ptr| ecx.global_root_pointer(ptr),
)?;
if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
ecx.machine
.allocation_spans
@ -1148,7 +1129,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
.insert(id, (ecx.machine.current_span(), None));
}
Ok(Cow::Owned(alloc))
Ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace })
}
fn adjust_alloc_root_pointer(
@ -1357,7 +1338,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
}
#[inline(always)]
fn init_frame_extra(
fn init_frame(
ecx: &mut InterpCx<'tcx, Self>,
frame: Frame<'tcx, Provenance>,
) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {

View File

@ -0,0 +1,22 @@
//@compile-flags: -Zmiri-disable-validation
#![feature(core_intrinsics, custom_mir)]
use std::intrinsics::mir::*;
// This disables validation and uses custom MIR hit exactly the UB in the intrinsic,
// rather than getting UB from the typed load or parameter passing.
#[custom_mir(dialect = "runtime")]
pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
mir!({
RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
Return()
})
}
fn main() {
let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit();
unsafe {
(*p.as_mut_ptr().cast::<[usize; 2]>())[1] = 4;
let _meta = deref_meta(p.as_ptr().cast());
}
}

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC
|
LL | RET = PtrMetadata(*p);
| ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC
note: inside `main`
--> $DIR/ptr_metadata_uninit_slice_data.rs:LL:CC
|
LL | let _meta = deref_meta(p.as_ptr().cast());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -0,0 +1,22 @@
//@compile-flags: -Zmiri-disable-validation
#![feature(core_intrinsics, custom_mir)]
use std::intrinsics::mir::*;
// This disables validation and uses custom MIR hit exactly the UB in the intrinsic,
// rather than getting UB from the typed load or parameter passing.
#[custom_mir(dialect = "runtime")]
pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
mir!({
RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
Return()
})
}
fn main() {
let mut p = std::mem::MaybeUninit::<*const [i32]>::uninit();
unsafe {
(*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32;
let _meta = deref_meta(p.as_ptr().cast());
}
}

View File

@ -0,0 +1,35 @@
warning: integer-to-pointer cast
--> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC
|
LL | (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`,
= help: which means that Miri might miss pointer bugs in this program.
= help: See https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation.
= help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
= help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `with_exposed_provenance` semantics.
= help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
= note: BACKTRACE:
= note: inside `main` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC
|
LL | RET = PtrMetadata(*p);
| ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `deref_meta` at $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC
note: inside `main`
--> $DIR/ptr_metadata_uninit_slice_len.rs:LL:CC
|
LL | let _meta = deref_meta(p.as_ptr().cast());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error; 1 warning emitted

View File

@ -0,0 +1,23 @@
//@compile-flags: -Zmiri-disable-validation
#![feature(core_intrinsics, custom_mir)]
use std::intrinsics::mir::*;
// This disables validation and uses custom MIR hit exactly the UB in the intrinsic,
// rather than getting UB from the typed load or parameter passing.
#[custom_mir(dialect = "runtime")]
pub unsafe fn deref_meta(p: *const *const i32) -> () {
mir!({
RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
Return()
})
}
fn main() {
// Even though the meta is the trivially-valid `()`, this is still UB
let p = std::mem::MaybeUninit::<*const i32>::uninit();
unsafe {
let _meta = deref_meta(p.as_ptr());
}
}

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/ptr_metadata_uninit_thin.rs:LL:CC
|
LL | RET = PtrMetadata(*p);
| ^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `deref_meta` at $DIR/ptr_metadata_uninit_thin.rs:LL:CC
note: inside `main`
--> $DIR/ptr_metadata_uninit_thin.rs:LL:CC
|
LL | let _meta = deref_meta(p.as_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-permissive-provenance
#![feature(core_intrinsics, layout_for_ptr)]
#![feature(core_intrinsics, layout_for_ptr, ptr_metadata)]
//! Tests for various intrinsics that do not fit anywhere else.
use std::intrinsics;
@ -57,4 +57,10 @@ fn main() {
// Make sure that even if the discriminant is stored together with data, the intrinsic returns
// only the discriminant, nothing about the data.
assert_eq!(discriminant(&Some(false)), discriminant(&Some(true)));
let () = intrinsics::ptr_metadata(&[1, 2, 3]);
let len = intrinsics::ptr_metadata(&[1, 2, 3][..]);
assert_eq!(len, 3);
let dyn_meta = intrinsics::ptr_metadata(&[1, 2, 3] as &dyn std::fmt::Debug);
assert_eq!(dyn_meta.size_of(), 12);
}

View File

@ -362,7 +362,7 @@ macro_rules! impl_common_helpers {
self
}
/// Inspect what the underlying [`Command`][::std::process::Command] is up to the
/// Inspect what the underlying [`Command`] is up to the
/// current construction.
pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
where

View File

@ -203,7 +203,7 @@ impl Rustc {
self
}
/// Get the [`Output`][::std::process::Output] of the finished process.
/// Get the [`Output`] of the finished process.
#[track_caller]
pub fn command_output(&mut self) -> ::std::process::Output {
// let's make sure we piped all the input and outputs

View File

@ -94,7 +94,7 @@ impl Rustdoc {
self
}
/// Get the [`Output`][::std::process::Output] of the finished process.
/// Get the [`Output`] of the finished process.
#[track_caller]
pub fn command_output(&mut self) -> ::std::process::Output {
// let's make sure we piped all the input and outputs

View File

@ -144,7 +144,6 @@ run-make/lto-linkage-used-attr/Makefile
run-make/lto-no-link-whole-rlib/Makefile
run-make/lto-readonly-lib/Makefile
run-make/lto-smoke-c/Makefile
run-make/lto-smoke/Makefile
run-make/macos-deployment-target/Makefile
run-make/macos-fat-archive/Makefile
run-make/manual-crate-name/Makefile
@ -156,7 +155,6 @@ run-make/min-global-align/Makefile
run-make/mingw-export-call-convention/Makefile
run-make/mismatching-target-triples/Makefile
run-make/missing-crate-dependency/Makefile
run-make/mixing-deps/Makefile
run-make/mixing-formats/Makefile
run-make/mixing-libs/Makefile
run-make/msvc-opt-minsize/Makefile
@ -238,7 +236,6 @@ run-make/short-ice/Makefile
run-make/silly-file-names/Makefile
run-make/simd-ffi/Makefile
run-make/simple-dylib/Makefile
run-make/simple-rlib/Makefile
run-make/split-debuginfo/Makefile
run-make/stable-symbol-names/Makefile
run-make/static-dylib-by-default/Makefile

View File

@ -2122,7 +2122,6 @@ ui/issues/issue-33687.rs
ui/issues/issue-33770.rs
ui/issues/issue-3389.rs
ui/issues/issue-33941.rs
ui/issues/issue-33992.rs
ui/issues/issue-34047.rs
ui/issues/issue-34074.rs
ui/issues/issue-34209.rs

View File

@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: u32 = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: u32 = 1676;
const ISSUES_ENTRY_LIMIT: u32 = 1674;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files

View File

@ -1,6 +1,6 @@
//@ revisions: all strong basic none missing
//@ assembly-output: emit-asm
//@ ignore-macos slightly different policy on stack protection of arrays
//@ ignore-apple slightly different policy on stack protection of arrays
//@ ignore-msvc stack check code uses different function names
//@ ignore-nvptx64 stack protector is not supported
//@ ignore-wasm32-bare
@ -17,12 +17,9 @@
// See comments on https://github.com/rust-lang/rust/issues/114903.
#![crate_type = "lib"]
#![allow(incomplete_features)]
#![feature(unsized_locals, unsized_fn_params)]
// CHECK-LABEL: emptyfn:
#[no_mangle]
pub fn emptyfn() {

View File

@ -2,7 +2,7 @@
//@ compile-flags: --crate-type=lib -O -C llvm-args=-x86-asm-syntax=intel
//@ only-x86_64
//@ ignore-sgx
//@ ignore-macos (manipulates rsp too)
//@ ignore-apple (manipulates rsp too)
// Depending on various codegen choices, this might end up copying
// a `<2 x i8>`, an `i16`, or two `i8`s.

View File

@ -9,7 +9,7 @@
//@ [keep-thunk-extern] compile-flags: -Zfunction-return=keep -Zfunction-return=thunk-extern
//@ [thunk-extern-keep] compile-flags: -Zfunction-return=thunk-extern -Zfunction-return=keep
//@ only-x86_64
//@ ignore-x86_64-apple-darwin Symbol is called `___x86_return_thunk` (Darwin's extra underscore)
//@ ignore-apple Symbol is called `___x86_return_thunk` (Darwin's extra underscore)
//@ ignore-sgx Tests incompatible with LVI mitigations
#![crate_type = "lib"]

View File

@ -1,6 +1,6 @@
//
//@ ignore-windows
//@ ignore-macos
//@ ignore-apple
//@ ignore-wasm
//@ ignore-emscripten

View File

@ -11,7 +11,7 @@
//@ [LINUX] filecheck-flags: -DINSTR_PROF_COVFUN=__llvm_covfun
//@ [LINUX] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat'
//@ [DARWIN] only-macos
//@ [DARWIN] only-apple
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_DATA=__DATA,__llvm_prf_data,regular,live_support
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_NAME=__DATA,__llvm_prf_names
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_CNTS=__DATA,__llvm_prf_cnts
@ -49,7 +49,7 @@ where
pub fn wrap_with<F, T>(inner: T, should_wrap: bool, wrapper: F)
where
F: FnOnce(&T)
F: FnOnce(&T),
{
if should_wrap {
wrapper(&inner)

View File

@ -0,0 +1,36 @@
//@ compile-flags: -O -C no-prepopulate-passes -Z inline-mir
//@ only-64bit (so I don't need to worry about usize)
#![crate_type = "lib"]
#![feature(core_intrinsics)]
use std::intrinsics::ptr_metadata;
// CHECK-LABEL: @thin_metadata(
#[no_mangle]
pub fn thin_metadata(p: *const ()) {
// CHECK: start
// CHECK-NEXT: ret void
ptr_metadata(p)
}
// CHECK-LABEL: @slice_metadata(
#[no_mangle]
pub fn slice_metadata(p: *const [u8]) -> usize {
// CHECK: start
// CHECK-NEXT: ret i64 %p.1
ptr_metadata(p)
}
// CHECK-LABEL: @dyn_byte_offset(
#[no_mangle]
pub unsafe fn dyn_byte_offset(
p: *const dyn std::fmt::Debug,
n: usize,
) -> *const dyn std::fmt::Debug {
// CHECK: %[[Q:.+]] = getelementptr inbounds i8, ptr %p.0, i64 %n
// CHECK: %[[TEMP1:.+]] = insertvalue { ptr, ptr } poison, ptr %[[Q]], 0
// CHECK: %[[TEMP2:.+]] = insertvalue { ptr, ptr } %[[TEMP1]], ptr %p.1, 1
// CHECK: ret { ptr, ptr } %[[TEMP2]]
p.byte_add(n)
}

View File

@ -1,5 +1,5 @@
//
//@ only-macos
//@ only-apple
//@ compile-flags: -O
#![crate_type = "rlib"]

View File

@ -2,7 +2,7 @@
// before 4.0, formerly backported to the Rust LLVM fork.
//@ ignore-windows
//@ ignore-macos
//@ ignore-apple
//@ ignore-wasi
//@ compile-flags: -g -C no-prepopulate-passes
@ -10,5 +10,4 @@
// CHECK-LABEL: @main
// CHECK: {{.*}}DISubprogram{{.*}}name: "main",{{.*}}DI{{(SP)?}}FlagMainSubprogram{{.*}}
pub fn main() {
}
pub fn main() {}

View File

@ -1,5 +1,5 @@
//@ ignore-windows
//@ ignore-macos
//@ ignore-apple
//@ ignore-wasi wasi codegens the main symbol differently
//@ compile-flags: -g -C no-prepopulate-passes

View File

@ -1,6 +1,6 @@
// Test that __llvm_profile_counter_bias does not get internalized by lto.
//@ ignore-macos -runtime-counter-relocation not honored on Mach-O
//@ ignore-apple -runtime-counter-relocation not honored on Mach-O
//@ compile-flags: -Cprofile-generate -Cllvm-args=-runtime-counter-relocation -Clto=fat
//@ needs-profiler-support
//@ no-prefer-dynamic

View File

@ -0,0 +1,13 @@
// MIR for `g` after runtime
fn g(_1: *const i32, _2: *const [i32]) -> () {
let mut _0: ();
let mut _3: ();
let mut _4: usize;
bb0: {
_3 = PtrMetadata(_1);
_4 = PtrMetadata(_2);
return;
}
}

View File

@ -30,3 +30,13 @@ pub fn f(a: i32, b: bool) -> i32 {
Return()
})
}
// EMIT_MIR operators.g.runtime.after.mir
#[custom_mir(dialect = "runtime")]
pub fn g(p: *const i32, q: *const [i32]) {
mir!({
let a = PtrMetadata(p);
let b = PtrMetadata(q);
Return()
})
}

View File

@ -0,0 +1,63 @@
- // MIR for `get_metadata` before LowerIntrinsics
+ // MIR for `get_metadata` after LowerIntrinsics
fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () {
debug a => _1;
debug b => _2;
debug c => _3;
let mut _0: ();
let _4: ();
let mut _5: *const i32;
let mut _7: *const [u8];
let mut _9: *const dyn std::fmt::Debug;
scope 1 {
debug _unit => _4;
let _6: usize;
scope 2 {
debug _usize => _6;
let _8: std::ptr::DynMetadata<dyn std::fmt::Debug>;
scope 3 {
debug _vtable => _8;
}
}
}
bb0: {
StorageLive(_4);
StorageLive(_5);
_5 = _1;
- _4 = ptr_metadata::<i32, ()>(move _5) -> [return: bb1, unwind unreachable];
+ _4 = PtrMetadata(move _5);
+ goto -> bb1;
}
bb1: {
StorageDead(_5);
StorageLive(_6);
StorageLive(_7);
_7 = _2;
- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable];
+ _6 = PtrMetadata(move _7);
+ goto -> bb2;
}
bb2: {
StorageDead(_7);
StorageLive(_8);
StorageLive(_9);
_9 = _3;
- _8 = ptr_metadata::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
+ _8 = PtrMetadata(move _9);
+ goto -> bb3;
}
bb3: {
StorageDead(_9);
_0 = const ();
StorageDead(_8);
StorageDead(_6);
StorageDead(_4);
return;
}
}

View File

@ -0,0 +1,63 @@
- // MIR for `get_metadata` before LowerIntrinsics
+ // MIR for `get_metadata` after LowerIntrinsics
fn get_metadata(_1: *const i32, _2: *const [u8], _3: *const dyn Debug) -> () {
debug a => _1;
debug b => _2;
debug c => _3;
let mut _0: ();
let _4: ();
let mut _5: *const i32;
let mut _7: *const [u8];
let mut _9: *const dyn std::fmt::Debug;
scope 1 {
debug _unit => _4;
let _6: usize;
scope 2 {
debug _usize => _6;
let _8: std::ptr::DynMetadata<dyn std::fmt::Debug>;
scope 3 {
debug _vtable => _8;
}
}
}
bb0: {
StorageLive(_4);
StorageLive(_5);
_5 = _1;
- _4 = ptr_metadata::<i32, ()>(move _5) -> [return: bb1, unwind unreachable];
+ _4 = PtrMetadata(move _5);
+ goto -> bb1;
}
bb1: {
StorageDead(_5);
StorageLive(_6);
StorageLive(_7);
_7 = _2;
- _6 = ptr_metadata::<[u8], usize>(move _7) -> [return: bb2, unwind unreachable];
+ _6 = PtrMetadata(move _7);
+ goto -> bb2;
}
bb2: {
StorageDead(_7);
StorageLive(_8);
StorageLive(_9);
_9 = _3;
- _8 = ptr_metadata::<dyn Debug, DynMetadata<dyn Debug>>(move _9) -> [return: bb3, unwind unreachable];
+ _8 = PtrMetadata(move _9);
+ goto -> bb3;
}
bb3: {
StorageDead(_9);
_0 = const ();
StorageDead(_8);
StorageDead(_6);
StorageDead(_4);
return;
}
}

View File

@ -258,3 +258,12 @@ pub fn make_pointers(a: *const u8, b: *mut (), n: usize) {
let _slice_const: *const [u16] = aggregate_raw_ptr(a, n);
let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n);
}
// EMIT_MIR lower_intrinsics.get_metadata.LowerIntrinsics.diff
pub fn get_metadata(a: *const i32, b: *const [u8], c: *const dyn std::fmt::Debug) {
use std::intrinsics::ptr_metadata;
let _unit = ptr_metadata(a);
let _usize = ptr_metadata(b);
let _vtable = ptr_metadata(c);
}

View File

@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
}
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
let mut _5: *const ();
let mut _7: usize;
let mut _6: usize;
scope 5 (inlined std::ptr::metadata::<[u32]>) {
let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
}
scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
}
@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
StorageDead(_3);
StorageLive(_5);
_5 = _4 as *const () (PtrToPtr);
StorageLive(_7);
StorageLive(_6);
_6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 };
_7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize);
_6 = PtrMetadata(_1);
_0 = *const [u32] from (_5, _6);
StorageDead(_6);
_0 = *const [u32] from (_5, _7);
StorageDead(_7);
StorageDead(_5);
StorageDead(_4);
return;

View File

@ -13,9 +13,8 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
}
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
let mut _5: *const ();
let mut _7: usize;
let mut _6: usize;
scope 5 (inlined std::ptr::metadata::<[u32]>) {
let mut _6: std::ptr::metadata::PtrRepr<[u32]>;
}
scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) {
}
@ -30,13 +29,10 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
StorageDead(_3);
StorageLive(_5);
_5 = _4 as *const () (PtrToPtr);
StorageLive(_7);
StorageLive(_6);
_6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 };
_7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize);
_6 = PtrMetadata(_1);
_0 = *const [u32] from (_5, _6);
StorageDead(_6);
_0 = *const [u32] from (_5, _7);
StorageDead(_7);
StorageDead(_5);
StorageDead(_4);
return;

View File

@ -4,7 +4,7 @@
# ignore-cross-compile
include ../tools.mk
# ignore-macos
# ignore-apple
#
# This hits an assertion in the linker on older versions of osx apparently

View File

@ -4,7 +4,7 @@
# ignore-cross-compile
include ../tools.mk
# ignore-macos
# ignore-apple
#
# This hits an assertion in the linker on older versions of osx apparently

View File

@ -1,10 +1,10 @@
include ../tools.mk
# ignore-windows
# ignore-macos
# ignore-apple
#
# This feature only works when the output object format is ELF so we ignore
# macOS and Windows
# Apple and Windows
# check that the .stack_sizes section is generated
all:

View File

@ -2,7 +2,7 @@
include ../tools.mk
# ignore-windows
# ignore-macos
# ignore-apple
# Test for #39529.
# `-z text` causes ld to error if there are any non-PIC sections

View File

@ -1,4 +1,4 @@
# only-macos
# only-apple
#
# Check that linking to a framework actually makes it to the linker.

View File

@ -1,31 +0,0 @@
# ignore-cross-compile
include ../tools.mk
all: noparam bool_true bool_false thin fat
noparam:
$(RUSTC) lib.rs
$(RUSTC) main.rs -C lto
$(call RUN,main)
bool_true:
$(RUSTC) lib.rs
$(RUSTC) main.rs -C lto=yes
$(call RUN,main)
bool_false:
$(RUSTC) lib.rs
$(RUSTC) main.rs -C lto=off
$(call RUN,main)
thin:
$(RUSTC) lib.rs
$(RUSTC) main.rs -C lto=thin
$(call RUN,main)
fat:
$(RUSTC) lib.rs
$(RUSTC) main.rs -C lto=fat
$(call RUN,main)

View File

@ -0,0 +1,16 @@
// A simple smoke test to check that link time optimization
// (LTO) is accepted by the compiler, and that
// passing its various flags still results in successful compilation.
// See https://github.com/rust-lang/rust/issues/10741
//@ ignore-cross-compile
use run_make_support::rustc;
fn main() {
let lto_flags = ["-Clto", "-Clto=yes", "-Clto=off", "-Clto=thin", "-Clto=fat"];
for flag in lto_flags {
rustc().input("lib.rs").run();
rustc().input("main.rs").arg(flag).run();
}
}

View File

@ -1,4 +1,4 @@
# only-macos
# only-apple
include ../tools.mk

View File

@ -1,8 +0,0 @@
# ignore-cross-compile
include ../tools.mk
all:
$(RUSTC) both.rs -C prefer-dynamic
$(RUSTC) dylib.rs -C prefer-dynamic
$(RUSTC) prog.rs
$(call RUN,prog)

View File

@ -0,0 +1,13 @@
// This test invokes the main function in prog.rs, which has dependencies
// in both an rlib and a dylib. This test checks that these different library
// types can be successfully mixed.
//@ ignore-cross-compile
use run_make_support::{run, rustc};
fn main() {
rustc().input("both.rs").arg("-Cprefer-dynamic").run();
rustc().input("dylib.rs").arg("-Cprefer-dynamic").run();
rustc().input("prog.rs").run();
run("prog");
}

View File

@ -1,5 +1,5 @@
# ignore-cross-compile
# ignore-macos
# ignore-apple
include ../tools.mk

View File

@ -0,0 +1 @@
// empty crate

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