Auto merge of #123645 - matthiaskrgr:rollup-yd8d7f1, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #122781 (Fix argument ABI for overaligned structs on ppc64le)
 - #123367 (Safe Transmute: Compute transmutability from `rustc_target::abi::Layout`)
 - #123518 (Fix `ByMove` coroutine-closure shim (for 2021 precise closure capturing behavior))
 - #123547 (bootstrap: remove unused pub fns)
 - #123564 (Don't emit divide-by-zero panic paths in `StepBy::len`)
 - #123578 (Restore `pred_known_to_hold_modulo_regions`)
 - #123591 (Remove unnecessary cast from `LLVMRustGetInstrProfIncrementIntrinsic`)
 - #123632 (parser: reduce visibility of unnecessary public `UnmatchedDelim`)
 - #123635 (CFI: Fix ICE in KCFI non-associated function pointers)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-04-08 20:31:08 +00:00
commit ab5bda1aa7
66 changed files with 1904 additions and 941 deletions

View File

@ -150,7 +150,10 @@ impl LlvmType for CastTarget {
// Simplify to a single unit or an array if there's no prefix.
// This produces the same layout, but using a simpler type.
if self.prefix.iter().all(|x| x.is_none()) {
if rest_count == 1 {
// We can't do this if is_consecutive is set and the unit would get
// split on the target. Currently, this is only relevant for i128
// registers.
if rest_count == 1 && (!self.rest.is_consecutive || self.rest.unit != Reg::i128()) {
return rest_ll_unit;
}

View File

@ -1524,8 +1524,8 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVM
}
extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) {
return wrap(llvm::Intrinsic::getDeclaration(unwrap(M),
(llvm::Intrinsic::ID)llvm::Intrinsic::instrprof_increment));
return wrap(llvm::Intrinsic::getDeclaration(
unwrap(M), llvm::Intrinsic::instrprof_increment));
}
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,

View File

@ -520,7 +520,10 @@ impl<'tcx> Instance<'tcx> {
// Reify `Trait::method` implementations if KCFI is enabled
// FIXME(maurer) only reify it if it is a vtable-safe function
_ if tcx.sess.is_sanitizer_kcfi_enabled()
&& tcx.associated_item(def_id).trait_item_def_id.is_some() =>
&& tcx
.opt_associated_item(def_id)
.and_then(|assoc| assoc.trait_item_def_id)
.is_some() =>
{
// If this function could also go in a vtable, we need to `ReifyShim` it with
// KCFI because it can only attach one type per function.

View File

@ -58,16 +58,24 @@
//! borrowing from the outer closure, and we simply peel off a `deref` projection
//! from them. This second body is stored alongside the first body, and optimized
//! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`,
//! we use this "by move" body instead.
//! we use this "by-move" body instead.
//!
//! ## How does this work?
//!
//! This pass essentially remaps the body of the (child) closure of the coroutine-closure
//! to take the set of upvars of the parent closure by value. This at least requires
//! changing a by-ref upvar to be by-value in the case that the outer coroutine-closure
//! captures something by value; however, it may also require renumbering field indices
//! in case precise captures (edition 2021 closure capture rules) caused the inner coroutine
//! to split one field capture into two.
use itertools::Itertools;
use rustc_data_structures::unord::UnordSet;
use rustc_data_structures::unord::UnordMap;
use rustc_hir as hir;
use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind};
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::{self, dump_mir, MirPass};
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, TypeVisitableExt};
use rustc_target::abi::FieldIdx;
use rustc_target::abi::{FieldIdx, VariantIdx};
pub struct ByMoveBody;
@ -116,32 +124,116 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
.tuple_fields()
.len();
let mut by_ref_fields = UnordSet::default();
for (idx, (coroutine_capture, parent_capture)) in tcx
let mut field_remapping = UnordMap::default();
// One parent capture may correspond to several child captures if we end up
// refining the set of captures via edition-2021 precise captures. We want to
// match up any number of child captures with one parent capture, so we keep
// peeking off this `Peekable` until the child doesn't match anymore.
let mut parent_captures =
tcx.closure_captures(parent_def_id).iter().copied().enumerate().peekable();
// Make sure we use every field at least once, b/c why are we capturing something
// if it's not used in the inner coroutine.
let mut field_used_at_least_once = false;
for (child_field_idx, child_capture) in tcx
.closure_captures(coroutine_def_id)
.iter()
.copied()
// By construction we capture all the args first.
.skip(num_args)
.zip_eq(tcx.closure_captures(parent_def_id))
.enumerate()
{
// This upvar is captured by-move from the parent closure, but by-ref
// from the inner async block. That means that it's being borrowed from
// the outer closure body -- we need to change the coroutine to take the
// upvar by value.
if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() {
assert_ne!(
coroutine_kind,
ty::ClosureKind::FnOnce,
"`FnOnce` coroutine-closures return coroutines that capture from \
their body; it will always result in a borrowck error!"
);
by_ref_fields.insert(FieldIdx::from_usize(num_args + idx));
}
loop {
let Some(&(parent_field_idx, parent_capture)) = parent_captures.peek() else {
bug!("we ran out of parent captures!")
};
// Make sure we're actually talking about the same capture.
// FIXME(async_closures): We could look at the `hir::Upvar` instead?
assert_eq!(coroutine_capture.place.ty(), parent_capture.place.ty());
let PlaceBase::Upvar(parent_base) = parent_capture.place.base else {
bug!("expected capture to be an upvar");
};
let PlaceBase::Upvar(child_base) = child_capture.place.base else {
bug!("expected capture to be an upvar");
};
assert!(
child_capture.place.projections.len() >= parent_capture.place.projections.len()
);
// A parent matches a child they share the same prefix of projections.
// The child may have more, if it is capturing sub-fields out of
// something that is captured by-move in the parent closure.
if parent_base.var_path.hir_id != child_base.var_path.hir_id
|| !std::iter::zip(
&child_capture.place.projections,
&parent_capture.place.projections,
)
.all(|(child, parent)| child.kind == parent.kind)
{
// Make sure the field was used at least once.
assert!(
field_used_at_least_once,
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
);
field_used_at_least_once = false;
// Skip this field.
let _ = parent_captures.next().unwrap();
continue;
}
// Store this set of additional projections (fields and derefs).
// We need to re-apply them later.
let child_precise_captures =
&child_capture.place.projections[parent_capture.place.projections.len()..];
// If the parent captures by-move, and the child captures by-ref, then we
// need to peel an additional `deref` off of the body of the child.
let needs_deref = child_capture.is_by_ref() && !parent_capture.is_by_ref();
if needs_deref {
assert_ne!(
coroutine_kind,
ty::ClosureKind::FnOnce,
"`FnOnce` coroutine-closures return coroutines that capture from \
their body; it will always result in a borrowck error!"
);
}
// Finally, store the type of the parent's captured place. We need
// this when building the field projection in the MIR body later on.
let mut parent_capture_ty = parent_capture.place.ty();
parent_capture_ty = match parent_capture.info.capture_kind {
ty::UpvarCapture::ByValue => parent_capture_ty,
ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
tcx,
tcx.lifetimes.re_erased,
parent_capture_ty,
kind.to_mutbl_lossy(),
),
};
field_remapping.insert(
FieldIdx::from_usize(child_field_idx + num_args),
(
FieldIdx::from_usize(parent_field_idx + num_args),
parent_capture_ty,
needs_deref,
child_precise_captures,
),
);
field_used_at_least_once = true;
break;
}
}
// Pop the last parent capture
if field_used_at_least_once {
let _ = parent_captures.next().unwrap();
}
assert_eq!(parent_captures.next(), None, "leftover parent captures?");
if coroutine_kind == ty::ClosureKind::FnOnce {
assert_eq!(field_remapping.len(), tcx.closure_captures(parent_def_id).len());
return;
}
let by_move_coroutine_ty = tcx
@ -157,7 +249,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
);
let mut by_move_body = body.clone();
MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body);
MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body);
dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(()));
by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim {
coroutine_def_id: coroutine_def_id.to_def_id(),
@ -168,7 +260,7 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody {
struct MakeByMoveBody<'tcx> {
tcx: TyCtxt<'tcx>,
by_ref_fields: UnordSet<FieldIdx>,
field_remapping: UnordMap<FieldIdx, (FieldIdx, Ty<'tcx>, bool, &'tcx [Projection<'tcx>])>,
by_move_coroutine_ty: Ty<'tcx>,
}
@ -183,24 +275,59 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
context: mir::visit::PlaceContext,
location: mir::Location,
) {
// Initializing an upvar local always starts with `CAPTURE_STRUCT_LOCAL` and a
// field projection. If this is in `field_remapping`, then it must not be an
// arg from calling the closure, but instead an upvar.
if place.local == ty::CAPTURE_STRUCT_LOCAL
&& let Some((&mir::ProjectionElem::Field(idx, ty), projection)) =
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
place.projection.split_first()
&& self.by_ref_fields.contains(&idx)
&& let Some(&(remapped_idx, remapped_ty, needs_deref, additional_projections)) =
self.field_remapping.get(&idx)
{
let (begin, end) = projection.split_first().unwrap();
// FIXME(async_closures): I'm actually a bit surprised to see that we always
// initially deref the by-ref upvars. If this is not actually true, then we
// will at least get an ICE that explains why this isn't true :^)
assert_eq!(*begin, mir::ProjectionElem::Deref);
// Peel one ref off of the ty.
let peeled_ty = ty.builtin_deref(true).unwrap().ty;
// As noted before, if the parent closure captures a field by value, and
// the child captures a field by ref, then for the by-move body we're
// generating, we also are taking that field by value. Peel off a deref,
// since a layer of reffing has now become redundant.
let final_deref = if needs_deref {
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
else {
bug!(
"There should be at least a single deref for an upvar local initialization, found {projection:#?}"
);
};
// There may be more derefs, since we may also implicitly reborrow
// a captured mut pointer.
projection
} else {
projection
};
// The only thing that should be left is a deref, if the parent captured
// an upvar by-ref.
std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]);
// For all of the additional projections that come out of precise capturing,
// re-apply these projections.
let additional_projections =
additional_projections.iter().map(|elem| match elem.kind {
ProjectionKind::Deref => mir::ProjectionElem::Deref,
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
mir::ProjectionElem::Field(idx, elem.ty)
}
_ => unreachable!("precise captures only through fields and derefs"),
});
// We start out with an adjusted field index (and ty), representing the
// upvar that we get from our parent closure. We apply any of the additional
// projections to make sure that to the rest of the body of the closure, the
// place looks the same, and then apply that final deref if necessary.
*place = mir::Place {
local: place.local,
projection: self.tcx.mk_place_elems_from_iter(
[mir::ProjectionElem::Field(idx, peeled_ty)]
[mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
.into_iter()
.chain(end.iter().copied()),
.chain(additional_projections)
.chain(final_deref.iter().copied()),
),
};
}

View File

@ -34,8 +34,7 @@ use unescape_error_reporting::{emit_unescape_error, escaped_char};
rustc_data_structures::static_assert_size!(rustc_lexer::Token, 12);
#[derive(Clone, Debug)]
pub struct UnmatchedDelim {
pub expected_delim: Delimiter,
pub(crate) struct UnmatchedDelim {
pub found_delim: Option<Delimiter>,
pub found_span: Span,
pub unclosed_span: Option<Span>,

View File

@ -77,7 +77,6 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
for &(_, sp) in &self.diag_info.open_braces {
err.span_label(sp, "unclosed delimiter");
self.diag_info.unmatched_delims.push(UnmatchedDelim {
expected_delim: Delimiter::Brace,
found_delim: None,
found_span: self.token.span,
unclosed_span: Some(sp),
@ -163,9 +162,8 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
candidate = Some(*brace_span);
}
}
let (tok, _) = self.diag_info.open_braces.pop().unwrap();
let (_, _) = self.diag_info.open_braces.pop().unwrap();
self.diag_info.unmatched_delims.push(UnmatchedDelim {
expected_delim: tok,
found_delim: Some(close_delim),
found_span: self.token.span,
unclosed_span: unclosed_delimiter,

View File

@ -31,7 +31,7 @@ where
RegKind::Vector => size.bits() == 64 || size.bits() == 128,
};
valid_unit.then_some(Uniform { unit, total: size })
valid_unit.then_some(Uniform::consecutive(unit, size))
})
}
@ -60,7 +60,7 @@ where
let size = ret.layout.size;
let bits = size.bits();
if bits <= 128 {
ret.cast_to(Uniform { unit: Reg::i64(), total: size });
ret.cast_to(Uniform::new(Reg::i64(), size));
return;
}
ret.make_indirect();
@ -100,9 +100,9 @@ where
};
if size.bits() <= 128 {
if align.bits() == 128 {
arg.cast_to(Uniform { unit: Reg::i128(), total: size });
arg.cast_to(Uniform::new(Reg::i128(), size));
} else {
arg.cast_to(Uniform { unit: Reg::i64(), total: size });
arg.cast_to(Uniform::new(Reg::i64(), size));
}
return;
}

View File

@ -21,7 +21,7 @@ where
RegKind::Vector => size.bits() == 64 || size.bits() == 128,
};
valid_unit.then_some(Uniform { unit, total: size })
valid_unit.then_some(Uniform::consecutive(unit, size))
})
}
@ -49,7 +49,7 @@ where
let size = ret.layout.size;
let bits = size.bits();
if bits <= 32 {
ret.cast_to(Uniform { unit: Reg::i32(), total: size });
ret.cast_to(Uniform::new(Reg::i32(), size));
return;
}
ret.make_indirect();
@ -78,7 +78,7 @@ where
let align = arg.layout.align.abi.bytes();
let total = arg.layout.size;
arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total });
arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total));
}
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

View File

@ -18,7 +18,7 @@ fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
if total.bits() > 64 {
arg.make_indirect();
} else if total.bits() > 32 {
arg.cast_to(Uniform { unit: Reg::i32(), total });
arg.cast_to(Uniform::new(Reg::i32(), total));
} else {
arg.cast_to(Reg::i32());
}
@ -38,7 +38,7 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
if arg.layout.is_aggregate() {
let total = arg.layout.size;
if total.bits() > 32 {
arg.cast_to(Uniform { unit: Reg::i32(), total });
arg.cast_to(Uniform::new(Reg::i32(), total));
} else {
arg.cast_to(Reg::i32());
}

View File

@ -195,7 +195,7 @@ where
if total.bits() <= xlen {
arg.cast_to(xlen_reg);
} else {
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2)));
}
return false;
}
@ -278,10 +278,10 @@ fn classify_arg<'a, Ty, C>(
if total.bits() > xlen {
let align_regs = align > xlen;
if is_loongarch_aggregate(arg) {
arg.cast_to(Uniform {
unit: if align_regs { double_xlen_reg } else { xlen_reg },
total: Size::from_bits(xlen * 2),
});
arg.cast_to(Uniform::new(
if align_regs { double_xlen_reg } else { xlen_reg },
Size::from_bits(xlen * 2),
));
}
if align_regs && is_vararg {
*avail_gprs -= *avail_gprs % 2;

View File

@ -27,7 +27,7 @@ where
if arg.layout.is_aggregate() {
let pad_i32 = !offset.is_aligned(align);
arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32);
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
} else {
arg.extend_integer_width_to(32);
}

View File

@ -68,7 +68,7 @@ where
}
// Cast to a uniform int structure
ret.cast_to(Uniform { unit: Reg::i64(), total: size });
ret.cast_to(Uniform::new(Reg::i64(), size));
} else {
ret.make_indirect();
}
@ -139,7 +139,7 @@ where
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
arg.cast_to(CastTarget {
prefix,
rest: Uniform { unit: Reg::i64(), total: rest_size },
rest: Uniform::new(Reg::i64(), rest_size),
attrs: ArgAttributes {
regular: ArgAttribute::default(),
arg_ext: ArgExtension::None,

View File

@ -255,11 +255,16 @@ pub struct Uniform {
/// for 64-bit integers with a total size of 20 bytes. When the argument is actually passed,
/// this size will be rounded up to the nearest multiple of `unit.size`.
pub total: Size,
/// Indicate that the argument is consecutive, in the sense that either all values need to be
/// passed in register, or all on the stack. If they are passed on the stack, there should be
/// no additional padding between elements.
pub is_consecutive: bool,
}
impl From<Reg> for Uniform {
fn from(unit: Reg) -> Uniform {
Uniform { unit, total: unit.size }
Uniform { unit, total: unit.size, is_consecutive: false }
}
}
@ -267,6 +272,18 @@ impl Uniform {
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
self.unit.align(cx)
}
/// Pass using one or more values of the given type, without requiring them to be consecutive.
/// That is, some values may be passed in register and some on the stack.
pub fn new(unit: Reg, total: Size) -> Self {
Uniform { unit, total, is_consecutive: false }
}
/// Pass using one or more consecutive values of the given type. Either all values will be
/// passed in registers, or all on the stack.
pub fn consecutive(unit: Reg, total: Size) -> Self {
Uniform { unit, total, is_consecutive: true }
}
}
/// Describes the type used for `PassMode::Cast`.

View File

@ -35,7 +35,7 @@ where
16 => Reg::i128(),
_ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
};
arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
arg.cast_to(Uniform::new(unit, Size::from_bytes(2 * align_bytes)));
} else {
// FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271.
arg.make_direct_deprecated();

View File

@ -2,7 +2,7 @@
// Alignment of 128 bit types is not currently handled, this will
// need to be fixed when PowerPC vector support is added.
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
use crate::abi::call::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform};
use crate::abi::{Endian, HasDataLayout, TyAbiInterface};
use crate::spec::HasTargetSpec;
@ -37,7 +37,7 @@ where
RegKind::Vector => arg.layout.size.bits() == 128,
};
valid_unit.then_some(Uniform { unit, total: arg.layout.size })
valid_unit.then_some(Uniform::consecutive(unit, arg.layout.size))
})
}
@ -81,7 +81,7 @@ where
Reg::i64()
};
ret.cast_to(Uniform { unit, total: size });
ret.cast_to(Uniform::new(unit, size));
return;
}
@ -108,18 +108,20 @@ where
}
let size = arg.layout.size;
let (unit, total) = if size.bits() <= 64 {
if size.bits() <= 64 {
// Aggregates smaller than a doubleword should appear in
// the least-significant bits of the parameter doubleword.
(Reg { kind: RegKind::Integer, size }, size)
arg.cast_to(Reg { kind: RegKind::Integer, size })
} else {
// Aggregates larger than a doubleword should be padded
// at the tail to fill out a whole number of doublewords.
let reg_i64 = Reg::i64();
(reg_i64, size.align_to(reg_i64.align(cx)))
// Aggregates larger than i64 should be padded at the tail to fill out a whole number
// of i64s or i128s, depending on the aggregate alignment. Always use an array for
// this, even if there is only a single element.
let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() };
arg.cast_to(Uniform::consecutive(
reg,
size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()),
))
};
arg.cast_to(Uniform { unit, total });
}
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

View File

@ -201,7 +201,7 @@ where
if total.bits() <= xlen {
arg.cast_to(xlen_reg);
} else {
arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) });
arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2)));
}
return false;
}
@ -284,10 +284,10 @@ fn classify_arg<'a, Ty, C>(
if total.bits() > xlen {
let align_regs = align > xlen;
if is_riscv_aggregate(arg) {
arg.cast_to(Uniform {
unit: if align_regs { double_xlen_reg } else { xlen_reg },
total: Size::from_bits(xlen * 2),
});
arg.cast_to(Uniform::new(
if align_regs { double_xlen_reg } else { xlen_reg },
Size::from_bits(xlen * 2),
));
}
if align_regs && is_vararg {
*avail_gprs -= *avail_gprs % 2;

View File

@ -27,7 +27,7 @@ where
if arg.layout.is_aggregate() {
let pad_i32 = !offset.is_aligned(align);
arg.cast_to_and_pad_i32(Uniform { unit: Reg::i32(), total: size }, pad_i32);
arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32);
} else {
arg.extend_integer_width_to(32);
}

View File

@ -192,7 +192,7 @@ where
arg.cast_to(CastTarget {
prefix: data.prefix,
rest: Uniform { unit: Reg::i64(), total: rest_size },
rest: Uniform::new(Reg::i64(), rest_size),
attrs: ArgAttributes {
regular: data.arg_attribute,
arg_ext: ArgExtension::None,
@ -205,7 +205,7 @@ where
}
}
arg.cast_to(Uniform { unit: Reg::i64(), total });
arg.cast_to(Uniform::new(Reg::i64(), total));
}
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)

View File

@ -1,4 +1,4 @@
use crate::abi::call::{ArgAbi, FnAbi, Uniform};
use crate::abi::call::{ArgAbi, FnAbi};
use crate::abi::{HasDataLayout, TyAbiInterface};
fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool
@ -10,7 +10,7 @@ where
if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) {
let size = val.layout.size;
if unit.size == size {
val.cast_to(Uniform { unit, total: size });
val.cast_to(unit);
return true;
}
}

View File

@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> {
enum GetSafeTransmuteErrorAndReason {
Silent,
Error { err_msg: String, safe_transmute_explanation: String },
Error { err_msg: String, safe_transmute_explanation: Option<String> },
}
struct UnsatisfiedConst(pub bool);

View File

@ -558,7 +558,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation,
} => (err_msg, Some(safe_transmute_explanation)),
} => (err_msg, safe_transmute_explanation),
}
} else {
(err_msg, None)
@ -3068,28 +3068,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
return GetSafeTransmuteErrorAndReason::Silent;
};
let dst = trait_ref.args.type_at(0);
let src = trait_ref.args.type_at(1);
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
obligation.cause,
src_and_dst,
assume,
) {
Answer::No(reason) => {
let dst = trait_ref.args.type_at(0);
let src = trait_ref.args.type_at(1);
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
let safe_transmute_explanation = match reason {
rustc_transmute::Reason::SrcIsNotYetSupported => {
format!("analyzing the transmutability of `{src}` is not yet supported.")
format!("analyzing the transmutability of `{src}` is not yet supported")
}
rustc_transmute::Reason::DstIsNotYetSupported => {
format!("analyzing the transmutability of `{dst}` is not yet supported.")
format!("analyzing the transmutability of `{dst}` is not yet supported")
}
rustc_transmute::Reason::DstIsBitIncompatible => {
format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
}
rustc_transmute::Reason::DstUninhabited => {
format!("`{dst}` is uninhabited")
}
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
format!("`{dst}` may carry safety invariants")
}
@ -3135,14 +3140,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!("`{dst}` has an unknown layout")
}
};
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation: Some(safe_transmute_explanation),
}
}
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
Answer::Yes => span_bug!(
span,
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
),
other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
// Reached when a different obligation (namely `Freeze`) causes the
// transmutability analysis to fail. In this case, silence the
// transmutability error message in favor of that more specific
// error.
Answer::If(_) => {
GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
}
}
}

View File

@ -119,7 +119,9 @@ pub fn predicates_for_generics<'tcx>(
/// Determines whether the type `ty` is known to meet `bound` and
/// returns true if so. Returns false if `ty` either does not meet
/// `bound` or is not known to meet bound.
/// `bound` or is not known to meet bound (note that this is
/// conservative towards *no impl*, which is the opposite of the
/// `evaluate` methods).
pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
@ -127,8 +129,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
def_id: DefId,
) -> bool {
let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref);
infcx.predicate_must_hold_modulo_regions(&obligation)
pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
}
/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
///
/// Ping me on zulip if you want to use this method and need help with finding
/// an appropriate replacement.
#[instrument(level = "debug", skip(infcx, param_env, pred), ret)]
fn pred_known_to_hold_modulo_regions<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
pred: impl ToPredicate<'tcx>,
) -> bool {
let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
let result = infcx.evaluate_obligation_no_overflow(&obligation);
debug!(?result);
if result.must_apply_modulo_regions() {
true
} else if result.may_apply() {
// Sometimes obligations are ambiguous because the recursive evaluator
// is not smart enough, so we fall back to fulfillment when we're not certain
// that an obligation holds or not. Even still, we must make sure that
// the we do no inference in the process of checking this obligation.
let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
infcx.probe(|_| {
let ocx = ObligationCtxt::new(infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
match errors.as_slice() {
// Only known to hold if we did no inference.
[] => infcx.shallow_resolve(goal) == goal,
errors => {
debug!(?errors);
false
}
}
})
} else {
false
}
}
#[instrument(level = "debug", skip(tcx, elaborated_env))]

View File

@ -314,12 +314,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
.collect(),
Condition::IfTransmutable { src, dst } => {
let trait_def_id = obligation.predicate.def_id();
let transmute_trait = obligation.predicate.def_id();
let assume_const = predicate.trait_ref.args.const_at(2);
let make_obl = |from_ty, to_ty| {
let trait_ref1 = ty::TraitRef::new(
let make_transmute_obl = |from_ty, to_ty| {
let trait_ref = ty::TraitRef::new(
tcx,
trait_def_id,
transmute_trait,
[
ty::GenericArg::from(to_ty),
ty::GenericArg::from(from_ty),
@ -331,17 +331,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref1,
trait_ref,
)
};
let make_freeze_obl = |ty| {
let trait_ref = ty::TraitRef::new(
tcx,
tcx.lang_items().freeze_trait().unwrap(),
[ty::GenericArg::from(ty)],
);
Obligation::with_depth(
tcx,
obligation.cause.clone(),
obligation.recursion_depth + 1,
obligation.param_env,
trait_ref,
)
};
let mut obls = vec![];
// If the source is a shared reference, it must be `Freeze`;
// otherwise, transmuting could lead to data races.
if src.mutability == Mutability::Not {
obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
}
// If Dst is mutable, check bidirectionally.
// For example, transmuting bool -> u8 is OK as long as you can't update that u8
// to be > 1, because you could later transmute the u8 back to a bool and get UB.
match dst.mutability {
Mutability::Not => vec![make_obl(src.ty, dst.ty)],
Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
Mutability::Mut => obls.extend([
make_transmute_obl(src.ty, dst.ty),
make_transmute_obl(dst.ty, src.ty),
]),
}
obls
}
}
}

View File

@ -35,6 +35,7 @@ impl<R> Transitions<R>
where
R: Ref,
{
#[allow(dead_code)]
fn insert(&mut self, transition: Transition<R>, state: State) {
match transition {
Transition::Byte(b) => {
@ -82,6 +83,7 @@ impl<R> Dfa<R>
where
R: Ref,
{
#[allow(dead_code)]
pub(crate) fn unit() -> Self {
let transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();

View File

@ -160,6 +160,7 @@ where
Self { transitions, start, accepting }
}
#[allow(dead_code)]
pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
self.transitions.get(&start)
}

View File

@ -173,16 +173,20 @@ pub(crate) mod rustc {
use super::Tree;
use crate::layout::rustc::{Def, Ref};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::layout::LayoutCx;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::AdtDef;
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::AdtKind;
use rustc_middle::ty::List;
use rustc_middle::ty::ScalarInt;
use rustc_middle::ty::VariantDef;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::ErrorGuaranteed;
use rustc_target::abi::Align;
use std::alloc;
use rustc_target::abi::FieldsShape;
use rustc_target::abi::Size;
use rustc_target::abi::TyAndLayout;
use rustc_target::abi::Variants;
#[derive(Debug, Copy, Clone)]
pub(crate) enum Err {
@ -206,176 +210,64 @@ pub(crate) mod rustc {
}
}
trait LayoutExt {
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self;
}
impl LayoutExt for alloc::Layout {
fn clamp_align(&self, min_align: Align, max_align: Align) -> Self {
let min_align = min_align.bytes().try_into().unwrap();
let max_align = max_align.bytes().try_into().unwrap();
Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap()
}
}
struct LayoutSummary {
total_align: Align,
total_size: usize,
discriminant_size: usize,
discriminant_align: Align,
}
impl LayoutSummary {
fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::{TyAndLayout, Variants};
let param_env = ParamEnv::reveal_all();
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
let total_size: usize = layout.size().bytes_usize();
let total_align: Align = layout.align().abi;
let discriminant_align: Align;
let discriminant_size: usize;
if let Variants::Multiple { tag, .. } = layout.variants() {
discriminant_align = tag.align(&ctx).abi;
discriminant_size = tag.size(&ctx).bytes_usize();
} else {
discriminant_align = Align::ONE;
discriminant_size = 0;
};
Ok(Self { total_align, total_size, discriminant_align, discriminant_size })
}
fn into(&self) -> alloc::Layout {
alloc::Layout::from_size_align(
self.total_size,
self.total_align.bytes().try_into().unwrap(),
)
.unwrap()
}
}
impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
use rustc_middle::ty::FloatTy::*;
use rustc_middle::ty::IntTy::*;
use rustc_middle::ty::UintTy::*;
pub fn from_ty(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
use rustc_target::abi::HasDataLayout;
if let Err(e) = ty.error_reported() {
if let Err(e) = ty_and_layout.ty.error_reported() {
return Err(Err::TypeError(e));
}
let target = tcx.data_layout();
let target = cx.tcx.data_layout();
let pointer_size = target.pointer_size;
match ty.kind() {
match ty_and_layout.ty.kind() {
ty::Bool => Ok(Self::bool()),
ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()),
ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)),
ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)),
ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)),
ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)),
ty::Int(Isize) | ty::Uint(Usize) => {
Ok(Self::number(target.pointer_size.bytes_usize()))
ty::Float(nty) => {
let width = nty.bit_width() / 8;
Ok(Self::number(width as _))
}
ty::Tuple(members) => {
if members.len() == 0 {
Ok(Tree::unit())
} else {
Err(Err::NotYetSupported)
}
ty::Int(nty) => {
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
Ok(Self::number(width as _))
}
ty::Array(ty, len) => {
let len = len
.try_eval_target_usize(tcx, ParamEnv::reveal_all())
.ok_or(Err::NotYetSupported)?;
let elt = Tree::from_ty(*ty, tcx)?;
ty::Uint(nty) => {
let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
Ok(Self::number(width as _))
}
ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
ty::Array(inner_ty, len) => {
let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
return Err(Err::NotYetSupported);
};
let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
assert_eq!(*stride, inner_ty_and_layout.size);
let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
Ok(std::iter::repeat(elt)
.take(len as usize)
.take(*count as usize)
.fold(Tree::unit(), |tree, elt| tree.then(elt)))
}
ty::Adt(adt_def, args_ref) => {
use rustc_middle::ty::AdtKind;
// If the layout is ill-specified, halt.
if !(adt_def.repr().c() || adt_def.repr().int.is_some()) {
return Err(Err::NotYetSupported);
ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
match adt_def.adt_kind() {
AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
}
// Compute a summary of the type's layout.
let layout_summary = LayoutSummary::from_ty(ty, tcx)?;
// The layout begins with this adt's visibility.
let vis = Self::def(Def::Adt(*adt_def));
// And is followed the layout(s) of its variants
Ok(vis.then(match adt_def.adt_kind() {
AdtKind::Struct => Self::from_repr_c_variant(
ty,
*adt_def,
args_ref,
&layout_summary,
None,
adt_def.non_enum_variant(),
tcx,
)?,
AdtKind::Enum => {
trace!(?adt_def, "treeifying enum");
let mut tree = Tree::uninhabited();
for (idx, variant) in adt_def.variants().iter_enumerated() {
let tag = tcx.tag_for_variant((ty, idx));
tree = tree.or(Self::from_repr_c_variant(
ty,
*adt_def,
args_ref,
&layout_summary,
tag,
variant,
tcx,
)?);
}
tree
}
AdtKind::Union => {
// is the layout well-defined?
if !adt_def.repr().c() {
return Err(Err::NotYetSupported);
}
let ty_layout = layout_of(tcx, ty)?;
let mut tree = Tree::uninhabited();
for field in adt_def.all_fields() {
let variant_ty = field.ty(tcx, args_ref);
let variant_layout = layout_of(tcx, variant_ty)?;
let padding_needed = ty_layout.size() - variant_layout.size();
let variant = Self::def(Def::Field(field))
.then(Self::from_ty(variant_ty, tcx)?)
.then(Self::padding(padding_needed));
tree = tree.or(variant);
}
tree
}
}))
}
ty::Ref(lifetime, ty, mutability) => {
let layout = layout_of(tcx, *ty)?;
let align = layout.align();
let size = layout.size();
let ty_and_layout = cx.layout_of(*ty)?;
let align = ty_and_layout.align.abi.bytes() as usize;
let size = ty_and_layout.size.bytes_usize();
Ok(Tree::Ref(Ref {
lifetime: *lifetime,
ty: *ty,
@ -389,80 +281,143 @@ pub(crate) mod rustc {
}
}
fn from_repr_c_variant(
ty: Ty<'tcx>,
adt_def: AdtDef<'tcx>,
args_ref: GenericArgsRef<'tcx>,
layout_summary: &LayoutSummary,
tag: Option<ScalarInt>,
variant_def: &'tcx VariantDef,
tcx: TyCtxt<'tcx>,
/// Constructs a `Tree` from a tuple.
fn from_tuple(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
members: &'tcx List<Ty<'tcx>>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
let mut tree = Tree::unit();
let repr = adt_def.repr();
let min_align = repr.align.unwrap_or(Align::ONE);
let max_align = repr.pack.unwrap_or(Align::MAX);
let variant_span = trace_span!(
"treeifying variant",
min_align = ?min_align,
max_align = ?max_align,
)
.entered();
let mut variant_layout = alloc::Layout::from_size_align(
0,
layout_summary.total_align.bytes().try_into().unwrap(),
)
.unwrap();
// The layout of the variant is prefixed by the tag, if any.
if let Some(tag) = tag {
let tag_layout =
alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap();
tree = tree.then(Self::from_tag(tag, tcx));
variant_layout = variant_layout.extend(tag_layout).unwrap().0;
}
// Next come fields.
let fields_span = trace_span!("treeifying fields").entered();
for field_def in variant_def.fields.iter() {
let field_ty = field_def.ty(tcx, args_ref);
let _span = trace_span!("treeifying field", field = ?field_ty).entered();
// begin with the field's visibility
tree = tree.then(Self::def(Def::Field(field_def)));
// compute the field's layout characteristics
let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
// next comes the field's padding
let padding_needed = variant_layout.padding_needed_for(field_layout.align());
if padding_needed > 0 {
tree = tree.then(Self::padding(padding_needed));
match &ty_and_layout.fields {
FieldsShape::Primitive => {
assert_eq!(members.len(), 1);
let inner_ty = members[0];
let inner_ty_and_layout = cx.layout_of(inner_ty)?;
assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
Self::from_ty(inner_ty_and_layout, cx)
}
// finally, the field's layout
tree = tree.then(Self::from_ty(field_ty, tcx)?);
// extend the variant layout with the field layout
variant_layout = variant_layout.extend(field_layout).unwrap().0;
FieldsShape::Arbitrary { offsets, .. } => {
assert_eq!(offsets.len(), members.len());
Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
}
FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
}
drop(fields_span);
// finally: padding
let padding_span = trace_span!("adding trailing padding").entered();
if layout_summary.total_size > variant_layout.size() {
let padding_needed = layout_summary.total_size - variant_layout.size();
tree = tree.then(Self::padding(padding_needed));
};
drop(padding_span);
drop(variant_span);
Ok(tree)
}
pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
/// Constructs a `Tree` from a struct.
///
/// # Panics
///
/// Panics if `def` is not a struct definition.
fn from_struct(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_struct());
let def = Def::Adt(def);
Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
}
/// Constructs a `Tree` from an enum.
///
/// # Panics
///
/// Panics if `def` is not an enum definition.
fn from_enum(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_enum());
let layout = ty_and_layout.layout;
if let Variants::Multiple { tag_field, .. } = layout.variants() {
// For enums (but not coroutines), the tag field is
// currently always the first field of the layout.
assert_eq!(*tag_field, 0);
}
let variants = def.discriminants(cx.tcx()).try_fold(
Self::uninhabited(),
|variants, (idx, ref discriminant)| {
let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx));
let variant_def = Def::Variant(def.variant(idx));
let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx);
let variant = Self::from_variant(
variant_def,
tag,
variant_ty_and_layout,
layout.size,
cx,
)?;
Result::<Self, Err>::Ok(variants.or(variant))
},
)?;
return Ok(Self::def(Def::Adt(def)).then(variants));
}
/// Constructs a `Tree` from a 'variant-like' layout.
///
/// A 'variant-like' layout includes those of structs and, of course,
/// enum variants. Pragmatically speaking, this method supports anything
/// with `FieldsShape::Arbitrary`.
///
/// Note: This routine assumes that the optional `tag` is the first
/// field, and enum callers should check that `tag_field` is, in fact,
/// `0`.
fn from_variant(
def: Def<'tcx>,
tag: Option<ScalarInt>,
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
total_size: Size,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
// This constructor does not support non-`FieldsShape::Arbitrary`
// layouts.
let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
else {
return Err(Err::NotYetSupported);
};
// When this function is invoked with enum variants,
// `ty_and_layout.size` does not encompass the entire size of the
// enum. We rely on `total_size` for this.
assert!(ty_and_layout.size <= total_size);
let mut size = Size::ZERO;
let mut struct_tree = Self::def(def);
// If a `tag` is provided, place it at the start of the layout.
if let Some(tag) = tag {
size += tag.size();
struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
}
// Append the fields, in memory order, to the layout.
let inverse_memory_index = memory_index.invert_bijective_mapping();
for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
// Add interfield padding.
let padding_needed = offsets[*field_idx] - size;
let padding = Self::padding(padding_needed.bytes_usize());
let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
struct_tree = struct_tree.then(padding).then(field_tree);
size += padding_needed + field_ty_and_layout.size;
}
// Add trailing padding.
let padding_needed = total_size - size;
let trailing_padding = Self::padding(padding_needed.bytes_usize());
Ok(struct_tree.then(trailing_padding))
}
/// Constructs a `Tree` representing the value of a enum tag.
fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
use rustc_target::abi::Endian;
let size = tag.size();
let bits = tag.to_bits(size).unwrap();
@ -479,24 +434,42 @@ pub(crate) mod rustc {
};
Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
}
}
fn layout_of<'tcx>(
ctx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::TyAndLayout;
/// Constructs a `Tree` from a union.
///
/// # Panics
///
/// Panics if `def` is not a union definition.
fn from_union(
ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
def: AdtDef<'tcx>,
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
) -> Result<Self, Err> {
assert!(def.is_union());
let param_env = ParamEnv::reveal_all();
let param_env_and_type = ParamEnvAnd { param_env, value: ty };
let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
let layout = alloc::Layout::from_size_align(
layout.size().bytes_usize(),
layout.align().abi.bytes().try_into().unwrap(),
)
.unwrap();
trace!(?ty, ?layout, "computed layout for type");
Ok(layout)
let union_layout = ty_and_layout.layout;
// This constructor does not support non-`FieldsShape::Union`
// layouts. Fields of this shape are all placed at offset 0.
let FieldsShape::Union(fields) = union_layout.fields() else {
return Err(Err::NotYetSupported);
};
let fields = &def.non_enum_variant().fields;
let fields = fields.iter_enumerated().try_fold(
Self::uninhabited(),
|fields, (idx, ref field_def)| {
let field_def = Def::Field(field_def);
let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
let field = Self::from_ty(field_ty_and_layout, cx)?;
let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
let field_and_padding = field.then(trailing_padding);
Result::<Self, Err>::Ok(fields.or(field_and_padding))
},
)?;
Ok(Self::def(Def::Adt(def)).then(fields))
}
}
}

View File

@ -1,6 +1,6 @@
#![feature(alloc_layout_extra)]
#![feature(never_type)]
#![allow(dead_code, unused_variables)]
#![allow(unused_variables)]
#[macro_use]
extern crate tracing;
@ -49,6 +49,8 @@ pub enum Reason<T> {
DstIsNotYetSupported,
/// The layout of the destination type is bit-incompatible with the source type.
DstIsBitIncompatible,
/// The destination type is uninhabited.
DstUninhabited,
/// The destination type may carry safety invariants.
DstMayHaveSafetyInvariants,
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.

View File

@ -33,6 +33,9 @@ mod rustc {
use super::*;
use crate::layout::tree::rustc::Err;
use rustc_middle::ty::layout::LayoutCx;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::Ty;
use rustc_middle::ty::TyCtxt;
@ -43,12 +46,20 @@ mod rustc {
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
let Self { src, dst, assume, context } = self;
let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
let layout_of = |ty| {
layout_cx
.layout_of(ty)
.map_err(|_| Err::NotYetSupported)
.and_then(|tl| Tree::from_ty(tl, layout_cx))
};
// Convert `src` and `dst` from their rustc representations, to `Tree`-based
// representations. If these conversions fail, conclude that the transmutation is
// unacceptable; the layouts of both the source and destination types must be
// well-defined.
let src = Tree::from_ty(src, context);
let dst = Tree::from_ty(dst, context);
let src = layout_of(src);
let dst = layout_of(dst);
match (src, dst) {
(Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
@ -86,6 +97,10 @@ where
// references.
let src = src.prune(&|def| false);
if src.is_inhabited() && !dst.is_inhabited() {
return Answer::No(Reason::DstUninhabited);
}
trace!(?src, "pruned src");
// Remove all `Def` nodes from `dst`, additionally...

View File

@ -5,8 +5,6 @@ pub(crate) trait QueryContext {
type Def: layout::Def;
type Ref: layout::Ref;
type Scope: Copy;
fn min_align(&self, reference: Self::Ref) -> usize;
}
#[cfg(test)]
@ -31,10 +29,6 @@ pub(crate) mod test {
type Def = Def;
type Ref = !;
type Scope = ();
fn min_align(&self, reference: !) -> usize {
unimplemented!()
}
}
}
@ -48,9 +42,5 @@ mod rustc {
type Ref = layout::rustc::Ref<'tcx>;
type Scope = Ty<'tcx>;
fn min_align(&self, reference: Self::Ref) -> usize {
unimplemented!()
}
}
}

View File

@ -1,6 +1,7 @@
use crate::{
intrinsics,
iter::{from_fn, TrustedLen, TrustedRandomAccess},
num::NonZeroUsize,
ops::{Range, Try},
};
@ -22,7 +23,11 @@ pub struct StepBy<I> {
/// Additionally this type-dependent preprocessing means specialized implementations
/// cannot be used interchangeably.
iter: I,
step: usize,
/// This field is `step - 1`, aka the correct amount to pass to `nth` when iterating.
/// It MUST NOT be `usize::MAX`, as `unsafe` code depends on being able to add one
/// without the risk of overflow. (This is important so that length calculations
/// don't need to check for division-by-zero, for example.)
step_minus_one: usize,
first_take: bool,
}
@ -31,7 +36,16 @@ impl<I> StepBy<I> {
pub(in crate::iter) fn new(iter: I, step: usize) -> StepBy<I> {
assert!(step != 0);
let iter = <I as SpecRangeSetup<I>>::setup(iter, step);
StepBy { iter, step: step - 1, first_take: true }
StepBy { iter, step_minus_one: step - 1, first_take: true }
}
/// The `step` that was originally passed to `Iterator::step_by(step)`,
/// aka `self.step_minus_one + 1`.
#[inline]
fn original_step(&self) -> NonZeroUsize {
// SAFETY: By type invariant, `step_minus_one` cannot be `MAX`, which
// means the addition cannot overflow and the result cannot be zero.
unsafe { NonZeroUsize::new_unchecked(intrinsics::unchecked_add(self.step_minus_one, 1)) }
}
}
@ -81,8 +95,8 @@ where
// The zero-based index starting from the end of the iterator of the
// last element. Used in the `DoubleEndedIterator` implementation.
fn next_back_index(&self) -> usize {
let rem = self.iter.len() % (self.step + 1);
if self.first_take { if rem == 0 { self.step } else { rem - 1 } } else { rem }
let rem = self.iter.len() % self.original_step();
if self.first_take { if rem == 0 { self.step_minus_one } else { rem - 1 } } else { rem }
}
}
@ -209,7 +223,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
#[inline]
default fn spec_next(&mut self) -> Option<I::Item> {
let step_size = if self.first_take { 0 } else { self.step };
let step_size = if self.first_take { 0 } else { self.step_minus_one };
self.first_take = false;
self.iter.nth(step_size)
}
@ -217,22 +231,22 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
#[inline]
default fn spec_size_hint(&self) -> (usize, Option<usize>) {
#[inline]
fn first_size(step: usize) -> impl Fn(usize) -> usize {
move |n| if n == 0 { 0 } else { 1 + (n - 1) / (step + 1) }
fn first_size(step: NonZeroUsize) -> impl Fn(usize) -> usize {
move |n| if n == 0 { 0 } else { 1 + (n - 1) / step }
}
#[inline]
fn other_size(step: usize) -> impl Fn(usize) -> usize {
move |n| n / (step + 1)
fn other_size(step: NonZeroUsize) -> impl Fn(usize) -> usize {
move |n| n / step
}
let (low, high) = self.iter.size_hint();
if self.first_take {
let f = first_size(self.step);
let f = first_size(self.original_step());
(f(low), high.map(f))
} else {
let f = other_size(self.step);
let f = other_size(self.original_step());
(f(low), high.map(f))
}
}
@ -247,10 +261,9 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
}
n -= 1;
}
// n and self.step are indices, we need to add 1 to get the amount of elements
// n and self.step_minus_one are indices, we need to add 1 to get the amount of elements
// When calling `.nth`, we need to subtract 1 again to convert back to an index
// step + 1 can't overflow because `.step_by` sets `self.step` to `step - 1`
let mut step = self.step + 1;
let mut step = self.original_step().get();
// n + 1 could overflow
// thus, if n is usize::MAX, instead of adding one, we call .nth(step)
if n == usize::MAX {
@ -288,8 +301,11 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
R: Try<Output = Acc>,
{
#[inline]
fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth(step)
fn nth<I: Iterator>(
iter: &mut I,
step_minus_one: usize,
) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth(step_minus_one)
}
if self.first_take {
@ -299,7 +315,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
Some(x) => acc = f(acc, x)?,
}
}
from_fn(nth(&mut self.iter, self.step)).try_fold(acc, f)
from_fn(nth(&mut self.iter, self.step_minus_one)).try_fold(acc, f)
}
default fn spec_fold<Acc, F>(mut self, mut acc: Acc, mut f: F) -> Acc
@ -307,8 +323,11 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
F: FnMut(Acc, Self::Item) -> Acc,
{
#[inline]
fn nth<I: Iterator>(iter: &mut I, step: usize) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth(step)
fn nth<I: Iterator>(
iter: &mut I,
step_minus_one: usize,
) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth(step_minus_one)
}
if self.first_take {
@ -318,7 +337,7 @@ unsafe impl<I: Iterator> StepByImpl<I> for StepBy<I> {
Some(x) => acc = f(acc, x),
}
}
from_fn(nth(&mut self.iter, self.step)).fold(acc, f)
from_fn(nth(&mut self.iter, self.step_minus_one)).fold(acc, f)
}
}
@ -336,7 +355,7 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St
// is out of bounds because the length of `self.iter` does not exceed
// `usize::MAX` (because `I: ExactSizeIterator`) and `nth_back` is
// zero-indexed
let n = n.saturating_mul(self.step + 1).saturating_add(self.next_back_index());
let n = n.saturating_mul(self.original_step().get()).saturating_add(self.next_back_index());
self.iter.nth_back(n)
}
@ -348,16 +367,16 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St
#[inline]
fn nth_back<I: DoubleEndedIterator>(
iter: &mut I,
step: usize,
step_minus_one: usize,
) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth_back(step)
move || iter.nth_back(step_minus_one)
}
match self.next_back() {
None => try { init },
Some(x) => {
let acc = f(init, x)?;
from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f)
from_fn(nth_back(&mut self.iter, self.step_minus_one)).try_fold(acc, f)
}
}
}
@ -371,16 +390,16 @@ unsafe impl<I: DoubleEndedIterator + ExactSizeIterator> StepByBackImpl<I> for St
#[inline]
fn nth_back<I: DoubleEndedIterator>(
iter: &mut I,
step: usize,
step_minus_one: usize,
) -> impl FnMut() -> Option<I::Item> + '_ {
move || iter.nth_back(step)
move || iter.nth_back(step_minus_one)
}
match self.next_back() {
None => init,
Some(x) => {
let acc = f(init, x);
from_fn(nth_back(&mut self.iter, self.step)).fold(acc, f)
from_fn(nth_back(&mut self.iter, self.step_minus_one)).fold(acc, f)
}
}
}
@ -424,8 +443,7 @@ macro_rules! spec_int_ranges {
fn spec_next(&mut self) -> Option<$t> {
// if a step size larger than the type has been specified fall back to
// t::MAX, in which case remaining will be at most 1.
// The `+ 1` can't overflow since the constructor substracted 1 from the original value.
let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX);
let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX);
let remaining = self.iter.end;
if remaining > 0 {
let val = self.iter.start;
@ -474,7 +492,7 @@ macro_rules! spec_int_ranges {
{
// if a step size larger than the type has been specified fall back to
// t::MAX, in which case remaining will be at most 1.
let step = <$t>::try_from(self.step + 1).unwrap_or(<$t>::MAX);
let step = <$t>::try_from(self.original_step().get()).unwrap_or(<$t>::MAX);
let remaining = self.iter.end;
let mut acc = init;
let mut val = self.iter.start;
@ -500,7 +518,7 @@ macro_rules! spec_int_ranges_r {
fn spec_next_back(&mut self) -> Option<Self::Item>
where Range<$t>: DoubleEndedIterator + ExactSizeIterator,
{
let step = (self.step + 1) as $t;
let step = self.original_step().get() as $t;
let remaining = self.iter.end;
if remaining > 0 {
let start = self.iter.start;

View File

@ -645,27 +645,6 @@ pub enum Kind {
}
impl Kind {
pub fn parse(string: &str) -> Option<Kind> {
// these strings, including the one-letter aliases, must match the x.py help text
Some(match string {
"build" | "b" => Kind::Build,
"check" | "c" => Kind::Check,
"clippy" => Kind::Clippy,
"fix" => Kind::Fix,
"fmt" => Kind::Format,
"test" | "t" => Kind::Test,
"bench" => Kind::Bench,
"doc" | "d" => Kind::Doc,
"clean" => Kind::Clean,
"dist" => Kind::Dist,
"install" => Kind::Install,
"run" | "r" => Kind::Run,
"setup" => Kind::Setup,
"suggest" => Kind::Suggest,
_ => return None,
})
}
pub fn as_str(&self) -> &'static str {
match self {
Kind::Build => "build",

View File

@ -1911,15 +1911,6 @@ impl Compiler {
pub fn is_snapshot(&self, build: &Build) -> bool {
self.stage == 0 && self.host == build.build
}
/// Returns if this compiler should be treated as a final stage one in the
/// current build session.
/// This takes into account whether we're performing a full bootstrap or
/// not; don't directly compare the stage with `2`!
pub fn is_final_stage(&self, build: &Build) -> bool {
let final_stage = if build.config.full_bootstrap { 2 } else { 1 };
self.stage >= final_stage
}
}
fn envify(s: &str) -> String {

View File

@ -0,0 +1,93 @@
// Test that structs aligned to 128 bits are passed with the correct ABI on powerpc64le.
// This is similar to aarch64-struct-align-128.rs, but for ppc.
//@ compile-flags: --target powerpc64le-unknown-linux-gnu
//@ needs-llvm-components: powerpc
#![feature(no_core, lang_items)]
#![crate_type = "lib"]
#![no_core]
#[lang="sized"]
trait Sized { }
#[lang="freeze"]
trait Freeze { }
#[lang="copy"]
trait Copy { }
#[repr(C)]
pub struct Align8 {
pub a: u64,
pub b: u64,
}
#[repr(transparent)]
pub struct Transparent8 {
a: Align8
}
#[repr(C)]
pub struct Wrapped8 {
a: Align8,
}
extern "C" {
// CHECK: declare void @test_8([2 x i64], [2 x i64], [2 x i64])
fn test_8(a: Align8, b: Transparent8, c: Wrapped8);
}
#[repr(C)]
#[repr(align(16))]
pub struct Align16 {
pub a: u64,
pub b: u64,
}
#[repr(transparent)]
pub struct Transparent16 {
a: Align16
}
#[repr(C)]
pub struct Wrapped16 {
pub a: Align16,
}
extern "C" {
// It's important that this produces [1 x i128] rather than just i128!
// CHECK: declare void @test_16([1 x i128], [1 x i128], [1 x i128])
fn test_16(a: Align16, b: Transparent16, c: Wrapped16);
}
#[repr(C)]
#[repr(align(32))]
pub struct Align32 {
pub a: u64,
pub b: u64,
pub c: u64,
}
#[repr(transparent)]
pub struct Transparent32 {
a: Align32
}
#[repr(C)]
pub struct Wrapped32 {
pub a: Align32,
}
extern "C" {
// CHECK: declare void @test_32([2 x i128], [2 x i128], [2 x i128])
fn test_32(a: Align32, b: Transparent32, c: Wrapped32);
}
pub unsafe fn main(
a1: Align8, a2: Transparent8, a3: Wrapped8,
b1: Align16, b2: Transparent16, b3: Wrapped16,
c1: Align32, c2: Transparent32, c3: Wrapped32,
) {
test_8(a1, a2, a3);
test_16(b1, b2, b3);
test_32(c1, c2, c3);
}

View File

@ -0,0 +1,26 @@
//@ compile-flags: -O
#![crate_type = "lib"]
use std::iter::StepBy;
use std::slice::Iter;
// The constructor for `StepBy` ensures we can never end up needing to do zero
// checks on denominators, so check that the code isn't emitting panic paths.
// CHECK-LABEL: @step_by_len_std
#[no_mangle]
pub fn step_by_len_std(x: &StepBy<Iter<i32>>) -> usize {
// CHECK-NOT: div_by_zero
// CHECK: udiv
// CHECK-NOT: div_by_zero
x.len()
}
// CHECK-LABEL: @step_by_len_naive
#[no_mangle]
pub fn step_by_len_naive(x: Iter<i32>, step_minus_one: usize) -> usize {
// CHECK: udiv
// CHECK: call{{.+}}div_by_zero
x.len() / (step_minus_one + 1)
}

View File

@ -0,0 +1,27 @@
//@ aux-build:block-on.rs
//@ run-pass
//@ check-run-results
//@ revisions: e2021 e2018
//@[e2018] edition:2018
//@[e2021] edition:2021
#![feature(async_closure)]
extern crate block_on;
async fn call_once(f: impl async FnOnce()) { f().await; }
pub async fn async_closure(x: &mut i32) {
let c = async move || {
*x += 1;
};
call_once(c).await;
}
fn main() {
block_on::block_on(async {
let mut x = 0;
async_closure(&mut x).await;
assert_eq!(x, 1);
});
}

View File

@ -0,0 +1,27 @@
//@ aux-build:block-on.rs
//@ edition:2021
//@ run-pass
//@ check-run-results
#![feature(async_closure)]
extern crate block_on;
async fn call_once(f: impl async FnOnce()) {
f().await;
}
async fn async_main() {
let x = &mut 0;
let y = &mut 0;
let c = async || {
*x = 1;
*y = 2;
};
call_once(c).await;
println!("{x} {y}");
}
fn main() {
block_on::block_on(async_main());
}

View File

@ -0,0 +1 @@
1 2

View File

@ -0,0 +1,29 @@
after call
after await
fixed
uncaptured
after call
after await
fixed
uncaptured
after call
after await
fixed
uncaptured
after call
after await
fixed
untouched
after call
drop first
after await
uncaptured
after call
drop first
after await
uncaptured

View File

@ -0,0 +1,29 @@
after call
after await
fixed
uncaptured
after call
after await
fixed
uncaptured
after call
fixed
after await
uncaptured
after call
after await
fixed
untouched
after call
drop first
after await
uncaptured
after call
drop first
after await
uncaptured

View File

@ -0,0 +1,29 @@
after call
after await
fixed
uncaptured
after call
after await
fixed
uncaptured
after call
fixed
after await
uncaptured
after call
after await
fixed
untouched
after call
drop first
after await
uncaptured
after call
drop first
after await
uncaptured

View File

@ -0,0 +1,157 @@
//@ aux-build:block-on.rs
//@ edition:2021
//@ run-pass
//@ check-run-results
//@ revisions: call call_once force_once
// call - Call the closure regularly.
// call_once - Call the closure w/ `async FnOnce`, so exercising the by_move shim.
// force_once - Force the closure mode to `FnOnce`, so exercising what was fixed
// in <https://github.com/rust-lang/rust/pull/123350>.
#![feature(async_closure)]
#![allow(unused_mut)]
extern crate block_on;
#[cfg(any(call, force_once))]
macro_rules! call {
($c:expr) => { ($c)() }
}
#[cfg(call_once)]
async fn call_once(f: impl async FnOnce()) {
f().await
}
#[cfg(call_once)]
macro_rules! call {
($c:expr) => { call_once($c) }
}
#[cfg(not(force_once))]
macro_rules! guidance {
($c:expr) => { $c }
}
#[cfg(force_once)]
fn infer_fnonce(c: impl async FnOnce()) -> impl async FnOnce() { c }
#[cfg(force_once)]
macro_rules! guidance {
($c:expr) => { infer_fnonce($c) }
}
#[derive(Debug)]
struct Drop(&'static str);
impl std::ops::Drop for Drop {
fn drop(&mut self) {
println!("{}", self.0);
}
}
struct S {
a: i32,
b: Drop,
c: Drop,
}
async fn async_main() {
// Precise capture struct
{
let mut s = S { a: 1, b: Drop("fix me up"), c: Drop("untouched") };
let mut c = guidance!(async || {
s.a = 2;
let w = &mut s.b;
w.0 = "fixed";
});
s.c.0 = "uncaptured";
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
println!();
// Precise capture &mut struct
{
let s = &mut S { a: 1, b: Drop("fix me up"), c: Drop("untouched") };
let mut c = guidance!(async || {
s.a = 2;
let w = &mut s.b;
w.0 = "fixed";
});
s.c.0 = "uncaptured";
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
println!();
// Precise capture struct by move
{
let mut s = S { a: 1, b: Drop("fix me up"), c: Drop("untouched") };
let mut c = guidance!(async move || {
s.a = 2;
let w = &mut s.b;
w.0 = "fixed";
});
s.c.0 = "uncaptured";
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
println!();
// Precise capture &mut struct by move
{
let s = &mut S { a: 1, b: Drop("fix me up"), c: Drop("untouched") };
let mut c = guidance!(async move || {
s.a = 2;
let w = &mut s.b;
w.0 = "fixed";
});
// `s` is still captured fully as `&mut S`.
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
println!();
// Precise capture struct, consume field
{
let mut s = S { a: 1, b: Drop("drop first"), c: Drop("untouched") };
let c = guidance!(async move || {
// s.a = 2; // FIXME(async_closures): Figure out why this fails
drop(s.b);
});
s.c.0 = "uncaptured";
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
println!();
// Precise capture struct by move, consume field
{
let mut s = S { a: 1, b: Drop("drop first"), c: Drop("untouched") };
let c = guidance!(async move || {
// s.a = 2; // FIXME(async_closures): Figure out why this fails
drop(s.b);
});
s.c.0 = "uncaptured";
let fut = call!(c);
println!("after call");
fut.await;
println!("after await");
}
}
fn main() {
block_on::block_on(async_main());
}

View File

@ -1,4 +1,4 @@
// Verifies that casting a method to a function pointer works.
// Verifies that casting to a function pointer works.
//@ revisions: cfi kcfi
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
@ -46,6 +46,8 @@ impl Trait1 for Type1 {
fn foo(&self) {}
}
fn foo<T>(_: &T) {}
fn main() {
let type1 = Type1 {};
let f = <Type1 as Trait1>::foo;
@ -53,5 +55,7 @@ fn main() {
// Check again with different optimization barriers
S2 { f: <S as Foo>::foo }.foo(&S);
// Check mismatched #[track_caller]
S2 { f: <S as Foo>::bar }.foo(&S)
S2 { f: <S as Foo>::bar }.foo(&S);
// Check non-method functions
S2 { f: foo }.foo(&S)
}

View File

@ -0,0 +1,244 @@
// This is a non-regression test for issues #108721 and its duplicate #123275 (hopefully, because
// the test is still convoluted and the ICE is fiddly).
//
// `pred_known_to_hold_modulo_regions` prevented "unexpected unsized tail" ICEs with warp/hyper but
// was unknowingly removed in #120463.
//@ build-pass: the ICE happened in codegen
use std::future::Future;
trait TryFuture: Future {
type Ok;
}
impl<F, T> TryFuture for F
where
F: ?Sized + Future<Output = Option<T>>,
{
type Ok = T;
}
trait Executor {}
struct Exec {}
trait HttpBody {
type Data;
}
trait ConnStreamExec<F> {}
impl<F> ConnStreamExec<F> for Exec where H2Stream<F>: Send {}
impl<E, F> ConnStreamExec<F> for E where E: Executor {}
struct H2Stream<F> {
_fut: F,
}
trait NewSvcExec<S, E, W: Watcher<S, E>> {
fn execute_new_svc(&mut self, _fut: NewSvcTask<S, E, W>) {
unimplemented!()
}
}
impl<S, E, W> NewSvcExec<S, E, W> for Exec where W: Watcher<S, E> {}
trait Watcher<S, E> {
type Future;
}
struct NoopWatcher;
impl<S, E> Watcher<S, E> for NoopWatcher
where
S: HttpService,
E: ConnStreamExec<S::Future>,
{
type Future = Option<<<S as HttpService>::ResBody as HttpBody>::Data>;
}
trait Service<Request> {
type Response;
type Future;
}
trait HttpService {
type ResBody: HttpBody;
type Future;
}
struct Body {}
impl HttpBody for Body {
type Data = String;
}
impl<S> HttpService for S
where
S: Service<(), Response = ()>,
{
type ResBody = Body;
type Future = S::Future;
}
trait MakeServiceRef<Target> {
type ResBody;
type Service: HttpService<ResBody = Self::ResBody>;
}
impl<T, Target, S, F> MakeServiceRef<Target> for T
where
T: for<'a> Service<&'a Target, Response = S, Future = F>,
S: HttpService,
{
type Service = S;
type ResBody = S::ResBody;
}
fn make_service_fn<F, Target, Ret>(_f: F) -> MakeServiceFn<F>
where
F: FnMut(&Target) -> Ret,
Ret: Future,
{
unimplemented!()
}
struct MakeServiceFn<F> {
_func: F,
}
impl<'t, F, Ret, Target, Svc> Service<&'t Target> for MakeServiceFn<F>
where
F: FnMut(&Target) -> Ret,
Ret: Future<Output = Option<Svc>>,
{
type Response = Svc;
type Future = Option<()>;
}
struct AddrIncoming {}
struct Server<I, S, E> {
_incoming: I,
_make_service: S,
_protocol: E,
}
impl<I, S, E, B> Server<I, S, E>
where
S: MakeServiceRef<(), ResBody = B>,
B: HttpBody,
E: ConnStreamExec<<S::Service as HttpService>::Future>,
E: NewSvcExec<S::Service, E, NoopWatcher>,
{
fn serve(&mut self) {
let fut = NewSvcTask::new();
self._protocol.execute_new_svc(fut);
}
}
fn serve<S>(_make_service: S) -> Server<AddrIncoming, S, Exec> {
unimplemented!()
}
struct NewSvcTask<S, E, W: Watcher<S, E>> {
_state: State<S, E, W>,
}
struct State<S, E, W: Watcher<S, E>> {
_fut: W::Future,
}
impl<S, E, W: Watcher<S, E>> NewSvcTask<S, E, W> {
fn new() -> Self {
unimplemented!()
}
}
trait Filter {
type Extract;
type Future;
fn map<F>(self, _fun: F) -> MapFilter<Self, F>
where
Self: Sized,
{
unimplemented!()
}
fn wrap_with<W>(self, _wrapper: W) -> W::Wrapped
where
Self: Sized,
W: Wrap<Self>,
{
unimplemented!()
}
}
fn service<F>(_filter: F) -> FilteredService<F>
where
F: Filter,
{
unimplemented!()
}
struct FilteredService<F> {
_filter: F,
}
impl<F> Service<()> for FilteredService<F>
where
F: Filter,
{
type Response = ();
type Future = FilteredFuture<F::Future>;
}
struct FilteredFuture<F> {
_fut: F,
}
struct MapFilter<T, F> {
_filter: T,
_func: F,
}
impl<T, F> Filter for MapFilter<T, F>
where
T: Filter,
F: Func<T::Extract>,
{
type Extract = F::Output;
type Future = MapFilterFuture<T, F>;
}
struct MapFilterFuture<T: Filter, F> {
_extract: T::Future,
_func: F,
}
trait Wrap<F> {
type Wrapped;
}
fn make_filter_fn<F, U>(_func: F) -> FilterFn<F>
where
F: Fn() -> U,
{
unimplemented!()
}
struct FilterFn<F> {
_func: F,
}
impl<F, U> Filter for FilterFn<F>
where
F: Fn() -> U,
U: TryFuture,
U::Ok: Send,
{
type Extract = U::Ok;
type Future = Option<U>;
}
fn trace<F>(_func: F) -> Trace<F>
where
F: Fn(),
{
unimplemented!()
}
struct Trace<F> {
_func: F,
}
impl<FN, F> Wrap<F> for Trace<FN> {
type Wrapped = WithTrace<FN, F>;
}
struct WithTrace<FN, F> {
_filter: F,
_trace: FN,
}
impl<FN, F> Filter for WithTrace<FN, F>
where
F: Filter,
{
type Extract = ();
type Future = (F::Future, fn(F::Extract));
}
trait Func<Args> {
type Output;
}
impl<F, R> Func<()> for F
where
F: Fn() -> R,
{
type Output = R;
}
fn main() {
let make_service = make_service_fn(|_| {
let tracer = trace(|| unimplemented!());
let filter = make_filter_fn(|| std::future::ready(Some(())))
.map(|| "Hello, world")
.wrap_with(tracer);
let svc = service(filter);
std::future::ready(Some(svc))
});
let mut server = serve(make_service);
server.serve();
}

View File

@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `ExplicitlyPadded`
--> $DIR/huge-len.rs:21:41
|
LL | assert::is_maybe_transmutable::<(), ExplicitlyPadded>();
| ^^^^^^^^^^^^^^^^ values of the type `ExplicitlyPadded` are too big for the current architecture
| ^^^^^^^^^^^^^^^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/huge-len.rs:8:14
@ -17,7 +17,7 @@ error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
--> $DIR/huge-len.rs:24:55
|
LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
| ^^ values of the type `ExplicitlyPadded` are too big for the current architecture
| ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/huge-len.rs:8:14

View File

@ -2,7 +2,7 @@ error[E0277]: `[String; 0]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:25:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 0]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 0]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]`
--> $DIR/should_require_well_defined_layout.rs:26:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -44,7 +44,7 @@ error[E0277]: `[String; 1]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:31:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 1]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 1]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]`
--> $DIR/should_require_well_defined_layout.rs:32:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -86,7 +86,7 @@ error[E0277]: `[String; 2]` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:37:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `[String; 2]` is not yet supported.
| ^^ analyzing the transmutability of `[String; 2]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]`
--> $DIR/should_require_well_defined_layout.rs:38:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported.
| ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14

View File

@ -0,0 +1,156 @@
//@ check-pass
//! Checks that niche optimizations are encoded correctly.
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: false,
}
}>
{}
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: true,
}
}>
{}
}
#[repr(u8)] enum V0 { V = 0 }
#[repr(u8)] enum V1 { V = 1 }
#[repr(u8)] enum V2 { V = 2 }
#[repr(u8)] enum V253 { V = 253 }
#[repr(u8)] enum V254 { V = 254 }
#[repr(u8)] enum V255 { V = 255 }
fn bool() {
enum OptionLike {
A(bool),
B,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<bool, OptionLike>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V1, OptionLike>();
assert::is_transmutable::<V2, OptionLike>();
}
fn one_niche() {
#[repr(u8)]
enum N1 {
S = 0,
E = 255 - 1,
}
enum OptionLike {
A(N1),
B,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn one_niche_alt() {
#[repr(u8)]
enum N1 {
S = 1,
E = 255 - 1,
}
enum OptionLike {
A(N1),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn two_niche() {
#[repr(u8)]
enum Niche {
S = 0,
E = 255 - 2,
}
enum OptionLike {
A(Niche),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 1);
};
assert::is_transmutable::<OptionLike, u8>();
assert::is_transmutable::<V0, OptionLike>();
assert::is_transmutable::<V253, OptionLike>();
assert::is_transmutable::<V254, OptionLike>();
assert::is_transmutable::<V255, OptionLike>();
}
fn no_niche() {
use std::mem::MaybeUninit;
#[repr(u8)]
enum Niche {
S = 0,
E = 255 - 1,
}
enum OptionLike {
A(Niche),
B,
C,
}
const _: () = {
assert!(std::mem::size_of::<OptionLike>() == 2);
};
#[repr(C)]
struct Pair<T, U>(T, U);
assert::is_transmutable::<V0, Niche>();
assert::is_transmutable::<V254, Niche>();
assert::is_transmutable::<Pair<V0, Niche>, OptionLike>();
assert::is_transmutable::<Pair<V1, MaybeUninit<u8>>, OptionLike>();
assert::is_transmutable::<Pair<V2, MaybeUninit<u8>>, OptionLike>();
}

View File

@ -0,0 +1,80 @@
//@ check-pass
//! Adapted from https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#explicit-repr-annotation-without-c-compatibility
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::mem::MaybeUninit;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: false,
}
}>
{}
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: false,
lifetimes: false,
safety: true,
validity: true,
}
}>
{}
}
#[repr(u8)] enum V0 { V = 0 }
#[repr(u8)] enum V1 { V = 1 }
fn repr_u8() {
#[repr(u8)]
enum TwoCases {
A(u8, u16), // 0x00 INIT INIT INIT
B(u16), // 0x01 PADD INIT INIT
}
const _: () = {
assert!(std::mem::size_of::<TwoCases>() == 4);
};
#[repr(C)] struct TwoCasesA(V0, u8, u8, u8);
#[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8);
assert::is_transmutable::<TwoCasesA, TwoCases>();
assert::is_transmutable::<TwoCasesB, TwoCases>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
}
fn repr_c_u8() {
#[repr(C, u8)]
enum TwoCases {
A(u8, u16), // 0x00 PADD INIT PADD INIT INIT
B(u16), // 0x01 PADD INIT INIT PADD PADD
}
const _: () = {
assert!(std::mem::size_of::<TwoCases>() == 6);
};
#[repr(C)] struct TwoCasesA(V0, MaybeUninit<u8>, u8, MaybeUninit<u8>, u8, u8);
#[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8, MaybeUninit<u8>, MaybeUninit<u8>);
assert::is_transmutable::<TwoCasesA, TwoCases>();
assert::is_transmutable::<TwoCasesB, TwoCases>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
}

View File

@ -1,5 +1,4 @@
//! An enum must have a well-defined layout to participate in a transmutation.
//@ check-pass
#![crate_type = "lib"]
#![feature(repr128)]
#![feature(transmutability)]
@ -21,23 +20,17 @@ mod assert {
{}
}
fn should_reject_repr_rust() {
fn void() {
enum repr_rust {}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
}
fn should_accept_repr_rust() {
fn singleton() {
enum repr_rust { V }
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn duplex() {
enum repr_rust { A, B }
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
}
@ -116,7 +109,7 @@ fn should_accept_primitive_reprs()
}
}
fn should_accept_repr_C() {
fn should_accept_repr_c() {
#[repr(C)] enum repr_c { V }
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<i128, repr_c>();

View File

@ -1,135 +0,0 @@
error[E0277]: `void::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:27:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:28:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `singleton::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:33:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:34:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `duplex::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:39:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:40:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:13:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -8,7 +8,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst`
--> $DIR/unknown_src_field.rs:19:36
|
LL | assert::is_transmutable::<Src, Dst>();
| ^^^ `Dst` has an unknown layout
| ^^^ analyzing the transmutability of `Dst` is not yet supported
|
note: required by a bound in `is_transmutable`
--> $DIR/unknown_src_field.rs:12:14

View File

@ -0,0 +1,43 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::mem::MaybeUninit;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
{}
}
fn validity() {
// An initialized byte is a valid uninitialized byte.
assert::is_maybe_transmutable::<u8, MaybeUninit<u8>>();
// An uninitialized byte is never a valid initialized byte.
assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>(); //~ ERROR: cannot be safely transmuted
}
fn padding() {
#[repr(align(8))]
struct Align8;
#[repr(u8)]
enum ImplicitlyPadded {
A(Align8),
}
#[repr(u8)]
enum V0 {
V0 = 0,
}
#[repr(C)]
struct ExplicitlyPadded(V0, MaybeUninit<[u8; 7]>);
assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
}

View File

@ -0,0 +1,18 @@
error[E0277]: `MaybeUninit<u8>` cannot be safely transmuted into `u8`
--> $DIR/maybeuninit.rs:21:54
|
LL | assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>();
| ^^ at least one value of `MaybeUninit<u8>` isn't a bit-valid value of `u8`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/maybeuninit.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,47 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
use std::cell::UnsafeCell;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
{}
}
fn value_to_value() {
// We accept value-to-value transmutations of `UnsafeCell`-containing types,
// because owning a value implies exclusive access.
assert::is_maybe_transmutable::<UnsafeCell<u8>, u8>();
assert::is_maybe_transmutable::<u8, UnsafeCell<u8>>();
assert::is_maybe_transmutable::<UnsafeCell<u8>, UnsafeCell<u8>>();
}
fn ref_to_ref() {
// We forbid `UnsafeCell`-containing ref-to-ref transmutations, because the
// two types may use different, incompatible synchronization strategies.
assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
}
fn mut_to_mut() {
// `UnsafeCell` does't matter for `&mut T` to `&mut U`, since exclusive
// borrows can't be used for shared access.
assert::is_maybe_transmutable::<&'static mut u8, &'static mut UnsafeCell<u8>>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut u8>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut UnsafeCell<u8>>();
}
fn mut_to_ref() {
// We don't care about `UnsafeCell` for transmutations in the form `&mut T
// -> &U`, because downgrading a `&mut T` to a `&U` deactivates `&mut T` for
// the lifetime of `&U`.
assert::is_maybe_transmutable::<&'static mut u8, &'static UnsafeCell<u8>>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static u8>();
assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static UnsafeCell<u8>>();
}

View File

@ -0,0 +1,33 @@
error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>`
--> $DIR/unsafecell.rs:27:50
|
LL | assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/unsafecell.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8>`
--> $DIR/unsafecell.rs:29:62
|
LL | assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>();
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/unsafecell.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -1,3 +1,4 @@
//@ check-pass
//! A struct must have a well-defined layout to participate in a transmutation.
#![crate_type = "lib"]
@ -20,47 +21,47 @@ mod assert {
{}
}
fn should_reject_repr_rust()
fn should_accept_repr_rust()
{
fn unit() {
struct repr_rust;
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn tuple() {
struct repr_rust();
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn braces() {
struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn aligned() {
#[repr(align(1))] struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn packed() {
#[repr(packed)] struct repr_rust{}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn nested() {
struct repr_rust;
#[repr(C)] struct repr_c(repr_rust);
assert::is_maybe_transmutable::<repr_c, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_c>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<u128, repr_c>();
}
}
fn should_accept_repr_C()
fn should_accept_repr_c()
{
fn unit() {
#[repr(C)] struct repr_c;

View File

@ -1,267 +0,0 @@
error[E0277]: `should_reject_repr_rust::unit::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:27:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::unit::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:28:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `should_reject_repr_rust::tuple::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:33:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::tuple::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:34:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `should_reject_repr_rust::braces::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:39:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::braces::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:40:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `aligned::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:45:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:46:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `packed::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:51:52
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:52:47
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `nested::repr_c` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:58:49
|
LL | assert::is_maybe_transmutable::<repr_c, ()>();
| ^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c`
--> $DIR/should_require_well_defined_layout.rs:59:47
|
LL | assert::is_maybe_transmutable::<u128, repr_c>();
| ^^^^^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() {
//~^ ERROR: recursive type
assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
//~^ ERROR: cannot be safely transmuted
}

View File

@ -15,7 +15,22 @@ error[E0391]: cycle detected when computing layout of `should_pad_explicitly_pac
= note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::BikeshedIntrinsicFrom<should_pad_explicitly_packed_field::ExplicitlyPadded, core::mem::transmutability::Assume { alignment: false, lifetimes: false, safety: false, validity: false }>`
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
error: aborting due to 2 previous errors
error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
--> $DIR/transmute_infinitely_recursive_type.rs:24:55
|
LL | assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
| ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/transmute_infinitely_recursive_type.rs:14:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
Some errors have detailed explanations: E0072, E0391.
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0072, E0277, E0391.
For more information about an error, try `rustc --explain E0072`.

View File

@ -1,7 +1,13 @@
//@ check-pass
//! This UI test was introduced as check-fail by a buggy bug-fix for an ICE. In
//! fact, this transmutation should be valid.
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code)]
use std::mem::size_of;
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
@ -22,6 +28,7 @@ fn test() {
#[repr(C)]
struct B(u8, u8);
assert_eq!(size_of::<A>(), size_of::<B>());
assert::is_maybe_transmutable::<B, A>();
//~^ ERROR cannot be safely transmuted
}

View File

@ -1,22 +0,0 @@
error[E0277]: `B` cannot be safely transmuted into `A`
--> $DIR/transmute-padding-ice.rs:25:40
|
LL | assert::is_maybe_transmutable::<B, A>();
| ^ the size of `B` is smaller than the size of `A`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/transmute-padding-ice.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<
| ______________^
LL | | Src,
LL | | { Assume { alignment: true, lifetimes: true, safety: true, validity: true } },
LL | | >,
| |_________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,71 @@
#![crate_type = "lib"]
#![feature(transmutability)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
use std::mem::{Assume, BikeshedIntrinsicFrom};
pub fn is_maybe_transmutable<Src, Dst>()
where
Dst: BikeshedIntrinsicFrom<Src, {
Assume {
alignment: true,
lifetimes: true,
safety: true,
validity: true,
}
}>
{}
}
fn void() {
enum Void {}
// This transmutation is vacuously acceptable; since one cannot construct a
// `Void`, unsoundness cannot directly arise from transmuting a void into
// anything else.
assert::is_maybe_transmutable::<Void, u128>();
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
}
// Non-ZST uninhabited types are, nonetheless, uninhabited.
fn yawning_void() {
enum Void {}
struct YawningVoid(Void, u128);
const _: () = {
assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
// Just to be sure the above constant actually evaluated:
assert!(false); //~ ERROR: evaluation of constant value failed
};
// This transmutation is vacuously acceptable; since one cannot construct a
// `Void`, unsoundness cannot directly arise from transmuting a void into
// anything else.
assert::is_maybe_transmutable::<YawningVoid, u128>();
assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
}
// References to uninhabited types are, logically, uninhabited, but for layout
// purposes are not ZSTs, and aren't treated as uninhabited when they appear in
// enum variants.
fn distant_void() {
enum Void {}
enum DistantVoid {
A(&'static Void)
}
const _: () = {
assert!(std::mem::size_of::<DistantVoid>() == std::mem::size_of::<usize>());
// Just to be sure the above constant actually evaluated:
assert!(false); //~ ERROR: evaluation of constant value failed
};
assert::is_maybe_transmutable::<DistantVoid, ()>();
assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
}

View File

@ -0,0 +1,86 @@
error[E0080]: evaluation of constant value failed
--> $DIR/uninhabited.rs:41:9
|
LL | assert!(false);
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9
|
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/uninhabited.rs:65:9
|
LL | assert!(false);
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9
|
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `()` cannot be safely transmuted into `void::Void`
--> $DIR/uninhabited.rs:29:41
|
LL | assert::is_maybe_transmutable::<(), Void>();
| ^^^^ `void::Void` is uninhabited
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void`
--> $DIR/uninhabited.rs:49:41
|
LL | assert::is_maybe_transmutable::<(), Void>();
| ^^^^ `yawning_void::Void` is uninhabited
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
--> $DIR/uninhabited.rs:70:43
|
LL | assert::is_maybe_transmutable::<u128, DistantVoid>();
| ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid`
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/uninhabited.rs:10:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0080, E0277.
For more information about an error, try `rustc --explain E0080`.

View File

@ -25,13 +25,13 @@ fn should_pad_explicitly_aligned_field() {
#[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
#[derive(Clone, Copy)] #[repr(u8)] enum V1u8 { V = 1 }
#[repr(C)]
#[repr(align(1))]
pub union Uninit {
a: (),
b: V1u8,
}
#[repr(C, align(2))]
#[repr(align(2))]
pub union align_2 {
a: V0u8,
}

View File

@ -1,7 +1,7 @@
//! A struct must have a well-defined layout to participate in a transmutation.
//@ check-pass
#![crate_type = "lib"]
#![feature(transmutability)]
#![feature(transmutability, transparent_unions)]
#![allow(dead_code, incomplete_features, non_camel_case_types)]
mod assert {
@ -20,17 +20,17 @@ mod assert {
{}
}
fn should_reject_repr_rust()
fn should_accept_repr_rust()
{
union repr_rust {
a: u8
}
assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
assert::is_maybe_transmutable::<repr_rust, ()>();
assert::is_maybe_transmutable::<u128, repr_rust>();
}
fn should_accept_repr_C()
fn should_accept_repr_c()
{
#[repr(C)]
union repr_c {
@ -41,3 +41,15 @@ fn should_accept_repr_C()
assert::is_maybe_transmutable::<repr_c, ()>();
assert::is_maybe_transmutable::<u128, repr_c>();
}
fn should_accept_transparent()
{
#[repr(transparent)]
union repr_transparent {
a: u8
}
assert::is_maybe_transmutable::<repr_transparent, ()>();
assert::is_maybe_transmutable::<u128, repr_transparent>();
}

View File

@ -27,7 +27,6 @@ fn should_pad_explicitly_packed_field() {
#[derive(Clone, Copy)] #[repr(u8)] enum V2u8 { V = 2 }
#[derive(Clone, Copy)] #[repr(u32)] enum V3u32 { V = 3 }
#[repr(C)]
pub union Uninit {
a: (),
b: V1u8,

View File

@ -1,47 +0,0 @@
error[E0277]: `should_reject_repr_rust::repr_rust` cannot be safely transmuted into `()`
--> $DIR/should_require_well_defined_layout.rs:29:48
|
LL | assert::is_maybe_transmutable::<repr_rust, ()>();
| ^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::repr_rust`
--> $DIR/should_require_well_defined_layout.rs:30:43
|
LL | assert::is_maybe_transmutable::<u128, repr_rust>();
| ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
|
note: required by a bound in `is_maybe_transmutable`
--> $DIR/should_require_well_defined_layout.rs:12:14
|
LL | pub fn is_maybe_transmutable<Src, Dst>()
| --------------------- required by a bound in this function
LL | where
LL | Dst: BikeshedIntrinsicFrom<Src, {
| ______________^
LL | | Assume {
LL | | alignment: true,
LL | | lifetimes: true,
... |
LL | | }
LL | | }>
| |__________^ required by this bound in `is_maybe_transmutable`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.