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:
commit
ab5bda1aa7
66 changed files with 1904 additions and 941 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
93
tests/codegen/powerpc64le-struct-align-128.rs
Normal file
93
tests/codegen/powerpc64le-struct-align-128.rs
Normal 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);
|
||||
}
|
26
tests/codegen/step_by-overflow-checks.rs
Normal file
26
tests/codegen/step_by-overflow-checks.rs
Normal 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)
|
||||
}
|
27
tests/ui/async-await/async-closures/mut-ref-reborrow.rs
Normal file
27
tests/ui/async-await/async-closures/mut-ref-reborrow.rs
Normal 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);
|
||||
});
|
||||
}
|
27
tests/ui/async-await/async-closures/overlapping-projs.rs
Normal file
27
tests/ui/async-await/async-closures/overlapping-projs.rs
Normal 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());
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
1 2
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
157
tests/ui/async-await/async-closures/precise-captures.rs
Normal file
157
tests/ui/async-await/async-closures/precise-captures.rs
Normal 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());
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
156
tests/ui/transmutability/enums/niche_optimization.rs
Normal file
156
tests/ui/transmutability/enums/niche_optimization.rs
Normal 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>();
|
||||
}
|
80
tests/ui/transmutability/enums/repr/padding_differences.rs
Normal file
80
tests/ui/transmutability/enums/repr/padding_differences.rs
Normal 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>();
|
||||
}
|
|
@ -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>();
|
|
@ -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`.
|
|
@ -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
|
||||
|
|
43
tests/ui/transmutability/maybeuninit.rs
Normal file
43
tests/ui/transmutability/maybeuninit.rs
Normal 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>();
|
||||
}
|
18
tests/ui/transmutability/maybeuninit.stderr
Normal file
18
tests/ui/transmutability/maybeuninit.stderr
Normal 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`.
|
47
tests/ui/transmutability/references/unsafecell.rs
Normal file
47
tests/ui/transmutability/references/unsafecell.rs
Normal 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>>();
|
||||
}
|
33
tests/ui/transmutability/references/unsafecell.stderr
Normal file
33
tests/ui/transmutability/references/unsafecell.stderr
Normal 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`.
|
|
@ -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;
|
|
@ -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`.
|
|
@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() {
|
|||
//~^ ERROR: recursive type
|
||||
|
||||
assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
|
||||
//~^ ERROR: cannot be safely transmuted
|
||||
}
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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`.
|
71
tests/ui/transmutability/uninhabited.rs
Normal file
71
tests/ui/transmutability/uninhabited.rs
Normal 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
|
||||
}
|
86
tests/ui/transmutability/uninhabited.stderr
Normal file
86
tests/ui/transmutability/uninhabited.stderr
Normal 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`.
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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`.
|
Loading…
Add table
Reference in a new issue