Merge pull request #4420 from rust-lang/rustup-2025-06-29
Automatic Rustup
This commit is contained in:
commit
03a7b9f532
67 changed files with 1364 additions and 274 deletions
|
|
@ -407,6 +407,18 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
|||
source_info.span,
|
||||
)
|
||||
}
|
||||
AssertKind::InvalidEnumConstruction(source) => {
|
||||
let source = codegen_operand(fx, source).load_scalar(fx);
|
||||
let location = fx.get_caller_location(source_info).load_scalar(fx);
|
||||
|
||||
codegen_panic_inner(
|
||||
fx,
|
||||
rustc_hir::LangItem::PanicInvalidEnumConstruction,
|
||||
&[source, location],
|
||||
*unwind,
|
||||
source_info.span,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let location = fx.get_caller_location(source_info).load_scalar(fx);
|
||||
|
||||
|
|
|
|||
|
|
@ -776,6 +776,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// `#[track_caller]` adds an implicit argument.
|
||||
(LangItem::PanicNullPointerDereference, vec![location])
|
||||
}
|
||||
AssertKind::InvalidEnumConstruction(source) => {
|
||||
let source = self.codegen_operand(bx, source).immediate();
|
||||
// It's `fn panic_invalid_enum_construction(source: u128)`,
|
||||
// `#[track_caller]` adds an implicit argument.
|
||||
(LangItem::PanicInvalidEnumConstruction, vec![source, location])
|
||||
}
|
||||
_ => {
|
||||
// It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument.
|
||||
(msg.panic_function(), vec![location])
|
||||
|
|
|
|||
|
|
@ -508,6 +508,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
|
|||
found: eval_to_int(found)?,
|
||||
},
|
||||
NullPointerDereference => NullPointerDereference,
|
||||
InvalidEnumConstruction(source) => InvalidEnumConstruction(eval_to_int(source)?),
|
||||
};
|
||||
Err(ConstEvalErrKind::AssertFailure(err)).into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@ fn alloc_caller_location<'tcx>(
|
|||
assert!(!filename.as_str().as_bytes().contains(&0));
|
||||
|
||||
let loc_details = ecx.tcx.sess.opts.unstable_opts.location_detail;
|
||||
let file_wide_ptr = {
|
||||
let filename = {
|
||||
let filename = if loc_details.file { filename.as_str() } else { "<redacted>" };
|
||||
let filename_with_nul = filename.to_owned() + "\0";
|
||||
// This can fail if rustc runs out of memory right here. Trying to emit an error would be
|
||||
// pointless, since that would require allocating more memory than these short strings.
|
||||
let file_ptr = ecx.allocate_bytes_dedup(filename_with_nul.as_bytes()).unwrap();
|
||||
Immediate::new_slice(file_ptr.into(), filename_with_nul.len().try_into().unwrap(), ecx)
|
||||
let file_len = u64::try_from(filename.len()).unwrap();
|
||||
Immediate::new_slice(file_ptr.into(), file_len, ecx)
|
||||
};
|
||||
let line = if loc_details.line { Scalar::from_u32(line) } else { Scalar::from_u32(0) };
|
||||
let col = if loc_details.column { Scalar::from_u32(col) } else { Scalar::from_u32(0) };
|
||||
|
|
@ -41,11 +42,8 @@ fn alloc_caller_location<'tcx>(
|
|||
let location = ecx.allocate(loc_layout, MemoryKind::CallerLocation).unwrap();
|
||||
|
||||
// Initialize fields.
|
||||
ecx.write_immediate(
|
||||
file_wide_ptr,
|
||||
&ecx.project_field(&location, FieldIdx::from_u32(0)).unwrap(),
|
||||
)
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
ecx.write_immediate(filename, &ecx.project_field(&location, FieldIdx::from_u32(0)).unwrap())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
ecx.write_scalar(line, &ecx.project_field(&location, FieldIdx::from_u32(1)).unwrap())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
ecx.write_scalar(col, &ecx.project_field(&location, FieldIdx::from_u32(2)).unwrap())
|
||||
|
|
|
|||
|
|
@ -312,6 +312,7 @@ language_item_table! {
|
|||
PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None;
|
||||
PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None;
|
||||
PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None;
|
||||
PanicInvalidEnumConstruction, sym::panic_invalid_enum_construction, panic_invalid_enum_construction, Target::Fn, GenericRequirement::None;
|
||||
PanicCoroutineResumedDrop, sym::panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_drop, Target::Fn, GenericRequirement::None;
|
||||
PanicAsyncFnResumedDrop, sym::panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_drop, Target::Fn, GenericRequirement::None;
|
||||
PanicAsyncGenFnResumedDrop, sym::panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_drop, Target::Fn, GenericRequirement::None;
|
||||
|
|
|
|||
|
|
@ -190,17 +190,6 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
|
|||
let mut symbols_stream = quote! {};
|
||||
let mut prefill_stream = quote! {};
|
||||
let mut entries = Entries::with_capacity(input.keywords.len() + input.symbols.len() + 10);
|
||||
let mut prev_key: Option<(Span, String)> = None;
|
||||
|
||||
let mut check_order = |span: Span, s: &str, errors: &mut Errors| {
|
||||
if let Some((prev_span, ref prev_str)) = prev_key {
|
||||
if s < prev_str {
|
||||
errors.error(span, format!("Symbol `{s}` must precede `{prev_str}`"));
|
||||
errors.error(prev_span, format!("location of previous symbol `{prev_str}`"));
|
||||
}
|
||||
}
|
||||
prev_key = Some((span, s.to_string()));
|
||||
};
|
||||
|
||||
// Generate the listed keywords.
|
||||
for keyword in input.keywords.iter() {
|
||||
|
|
@ -219,7 +208,6 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) {
|
|||
// Generate the listed symbols.
|
||||
for symbol in input.symbols.iter() {
|
||||
let name = &symbol.name;
|
||||
check_order(symbol.name.span(), &name.to_string(), &mut errors);
|
||||
|
||||
let value = match &symbol.value {
|
||||
Value::SameAsName => name.to_string(),
|
||||
|
|
|
|||
|
|
@ -84,18 +84,3 @@ fn check_dup_symbol_and_keyword() {
|
|||
};
|
||||
test_symbols_macro(input, &["Symbol `splat` is duplicated", "location of previous definition"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_symbol_order() {
|
||||
let input = quote! {
|
||||
Keywords {}
|
||||
Symbols {
|
||||
zebra,
|
||||
aardvark,
|
||||
}
|
||||
};
|
||||
test_symbols_macro(
|
||||
input,
|
||||
&["Symbol `aardvark` must precede `zebra`", "location of previous symbol `zebra`"],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ middle_assert_gen_resume_after_drop = `gen` fn or block cannot be further iterat
|
|||
|
||||
middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked
|
||||
|
||||
middle_assert_invalid_enum_construction =
|
||||
trying to construct an enum from an invalid value `{$source}`
|
||||
|
||||
middle_assert_misaligned_ptr_deref =
|
||||
misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
|
||||
|
||||
|
|
|
|||
|
|
@ -1075,6 +1075,7 @@ pub enum AssertKind<O> {
|
|||
ResumedAfterDrop(CoroutineKind),
|
||||
MisalignedPointerDereference { required: O, found: O },
|
||||
NullPointerDereference,
|
||||
InvalidEnumConstruction(O),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ impl<O> AssertKind<O> {
|
|||
LangItem::PanicGenFnNonePanic
|
||||
}
|
||||
NullPointerDereference => LangItem::PanicNullPointerDereference,
|
||||
InvalidEnumConstruction(_) => LangItem::PanicInvalidEnumConstruction,
|
||||
ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop,
|
||||
ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
|
||||
LangItem::PanicAsyncFnResumedDrop
|
||||
|
|
@ -284,6 +285,9 @@ impl<O> AssertKind<O> {
|
|||
)
|
||||
}
|
||||
NullPointerDereference => write!(f, "\"null pointer dereference occurred\""),
|
||||
InvalidEnumConstruction(source) => {
|
||||
write!(f, "\"trying to construct an enum from an invalid value {{}}\", {source:?}")
|
||||
}
|
||||
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
|
||||
write!(f, "\"coroutine resumed after completion\"")
|
||||
}
|
||||
|
|
@ -367,6 +371,7 @@ impl<O> AssertKind<O> {
|
|||
middle_assert_coroutine_resume_after_panic
|
||||
}
|
||||
NullPointerDereference => middle_assert_null_ptr_deref,
|
||||
InvalidEnumConstruction(_) => middle_assert_invalid_enum_construction,
|
||||
ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
|
||||
middle_assert_async_resume_after_drop
|
||||
}
|
||||
|
|
@ -420,6 +425,9 @@ impl<O> AssertKind<O> {
|
|||
add!("required", format!("{required:#?}"));
|
||||
add!("found", format!("{found:#?}"));
|
||||
}
|
||||
InvalidEnumConstruction(source) => {
|
||||
add!("source", format!("{source:#?}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ macro_rules! make_mir_visitor {
|
|||
self.visit_operand(l, location);
|
||||
self.visit_operand(r, location);
|
||||
}
|
||||
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => {
|
||||
OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) | InvalidEnumConstruction(op) => {
|
||||
self.visit_operand(op, location);
|
||||
}
|
||||
ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference | ResumedAfterDrop(_) => {
|
||||
|
|
|
|||
|
|
@ -1283,15 +1283,15 @@ rustc_queries! {
|
|||
return_result_from_ensure_ok
|
||||
}
|
||||
|
||||
/// Check whether the function has any recursion that could cause the inliner to trigger
|
||||
/// a cycle.
|
||||
query mir_callgraph_reachable(key: (ty::Instance<'tcx>, LocalDefId)) -> bool {
|
||||
/// Return the set of (transitive) callees that may result in a recursive call to `key`.
|
||||
query mir_callgraph_cyclic(key: LocalDefId) -> &'tcx UnordSet<LocalDefId> {
|
||||
fatal_cycle
|
||||
arena_cache
|
||||
desc { |tcx|
|
||||
"computing if `{}` (transitively) calls `{}`",
|
||||
key.0,
|
||||
tcx.def_path_str(key.1),
|
||||
"computing (transitive) callees of `{}` that may recurse",
|
||||
tcx.def_path_str(key),
|
||||
}
|
||||
cache_on_disk_if { true }
|
||||
}
|
||||
|
||||
/// Obtain all the calls into other local functions
|
||||
|
|
|
|||
501
compiler/rustc_mir_transform/src/check_enums.rs
Normal file
501
compiler/rustc_mir_transform/src/check_enums.rs
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
use rustc_abi::{Scalar, Size, TagEncoding, Variants, WrappingRange};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::layout::PrimitiveExt;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypingEnv};
|
||||
use rustc_session::Session;
|
||||
use tracing::debug;
|
||||
|
||||
/// This pass inserts checks for a valid enum discriminant where they are most
|
||||
/// likely to find UB, because checking everywhere like Miri would generate too
|
||||
/// much MIR.
|
||||
pub(super) struct CheckEnums;
|
||||
|
||||
impl<'tcx> crate::MirPass<'tcx> for CheckEnums {
|
||||
fn is_enabled(&self, sess: &Session) -> bool {
|
||||
sess.ub_checks()
|
||||
}
|
||||
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
// This pass emits new panics. If for whatever reason we do not have a panic
|
||||
// implementation, running this pass may cause otherwise-valid code to not compile.
|
||||
if tcx.lang_items().get(LangItem::PanicImpl).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let typing_env = body.typing_env(tcx);
|
||||
let basic_blocks = body.basic_blocks.as_mut();
|
||||
let local_decls = &mut body.local_decls;
|
||||
|
||||
// This operation inserts new blocks. Each insertion changes the Location for all
|
||||
// statements/blocks after. Iterating or visiting the MIR in order would require updating
|
||||
// our current location after every insertion. By iterating backwards, we dodge this issue:
|
||||
// The only Locations that an insertion changes have already been handled.
|
||||
for block in basic_blocks.indices().rev() {
|
||||
for statement_index in (0..basic_blocks[block].statements.len()).rev() {
|
||||
let location = Location { block, statement_index };
|
||||
let statement = &basic_blocks[block].statements[statement_index];
|
||||
let source_info = statement.source_info;
|
||||
|
||||
let mut finder = EnumFinder::new(tcx, local_decls, typing_env);
|
||||
finder.visit_statement(statement, location);
|
||||
|
||||
for check in finder.into_found_enums() {
|
||||
debug!("Inserting enum check");
|
||||
let new_block = split_block(basic_blocks, location);
|
||||
|
||||
match check {
|
||||
EnumCheckType::Direct { source_op, discr, op_size, valid_discrs } => {
|
||||
insert_direct_enum_check(
|
||||
tcx,
|
||||
local_decls,
|
||||
basic_blocks,
|
||||
block,
|
||||
source_op,
|
||||
discr,
|
||||
op_size,
|
||||
valid_discrs,
|
||||
source_info,
|
||||
new_block,
|
||||
)
|
||||
}
|
||||
EnumCheckType::Uninhabited => insert_uninhabited_enum_check(
|
||||
tcx,
|
||||
local_decls,
|
||||
&mut basic_blocks[block],
|
||||
source_info,
|
||||
new_block,
|
||||
),
|
||||
EnumCheckType::WithNiche {
|
||||
source_op,
|
||||
discr,
|
||||
op_size,
|
||||
offset,
|
||||
valid_range,
|
||||
} => insert_niche_check(
|
||||
tcx,
|
||||
local_decls,
|
||||
&mut basic_blocks[block],
|
||||
source_op,
|
||||
valid_range,
|
||||
discr,
|
||||
op_size,
|
||||
offset,
|
||||
source_info,
|
||||
new_block,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_required(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the different kind of enum checks we can insert.
|
||||
enum EnumCheckType<'tcx> {
|
||||
/// We know we try to create an uninhabited enum from an inhabited variant.
|
||||
Uninhabited,
|
||||
/// We know the enum does no niche optimizations and can thus easily compute
|
||||
/// the valid discriminants.
|
||||
Direct {
|
||||
source_op: Operand<'tcx>,
|
||||
discr: TyAndSize<'tcx>,
|
||||
op_size: Size,
|
||||
valid_discrs: Vec<u128>,
|
||||
},
|
||||
/// We try to construct an enum that has a niche.
|
||||
WithNiche {
|
||||
source_op: Operand<'tcx>,
|
||||
discr: TyAndSize<'tcx>,
|
||||
op_size: Size,
|
||||
offset: Size,
|
||||
valid_range: WrappingRange,
|
||||
},
|
||||
}
|
||||
|
||||
struct TyAndSize<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
/// A [Visitor] that finds the construction of enums and evaluates which checks
|
||||
/// we should apply.
|
||||
struct EnumFinder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &'a mut LocalDecls<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
enums: Vec<EnumCheckType<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> EnumFinder<'a, 'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &'a mut LocalDecls<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
) -> Self {
|
||||
EnumFinder { tcx, local_decls, typing_env, enums: Vec::new() }
|
||||
}
|
||||
|
||||
/// Returns the found enum creations and which checks should be inserted.
|
||||
fn into_found_enums(self) -> Vec<EnumCheckType<'tcx>> {
|
||||
self.enums
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for EnumFinder<'a, 'tcx> {
|
||||
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
||||
if let Rvalue::Cast(CastKind::Transmute, op, ty) = rvalue {
|
||||
let ty::Adt(adt_def, _) = ty.kind() else {
|
||||
return;
|
||||
};
|
||||
if !adt_def.is_enum() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(enum_layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else {
|
||||
return;
|
||||
};
|
||||
let Ok(op_layout) = self
|
||||
.tcx
|
||||
.layout_of(self.typing_env.as_query_input(op.ty(self.local_decls, self.tcx)))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
match enum_layout.variants {
|
||||
Variants::Empty if op_layout.is_uninhabited() => return,
|
||||
// An empty enum that tries to be constructed from an inhabited value, this
|
||||
// is never correct.
|
||||
Variants::Empty => {
|
||||
// The enum layout is uninhabited but we construct it from sth inhabited.
|
||||
// This is always UB.
|
||||
self.enums.push(EnumCheckType::Uninhabited);
|
||||
}
|
||||
// Construction of Single value enums is always fine.
|
||||
Variants::Single { .. } => {}
|
||||
// Construction of an enum with multiple variants but no niche optimizations.
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Direct,
|
||||
tag: Scalar::Initialized { value, .. },
|
||||
..
|
||||
} => {
|
||||
let valid_discrs =
|
||||
adt_def.discriminants(self.tcx).map(|(_, discr)| discr.val).collect();
|
||||
|
||||
let discr =
|
||||
TyAndSize { ty: value.to_int_ty(self.tcx), size: value.size(&self.tcx) };
|
||||
self.enums.push(EnumCheckType::Direct {
|
||||
source_op: op.to_copy(),
|
||||
discr,
|
||||
op_size: op_layout.size,
|
||||
valid_discrs,
|
||||
});
|
||||
}
|
||||
// Construction of an enum with multiple variants and niche optimizations.
|
||||
Variants::Multiple {
|
||||
tag_encoding: TagEncoding::Niche { .. },
|
||||
tag: Scalar::Initialized { value, valid_range, .. },
|
||||
tag_field,
|
||||
..
|
||||
} => {
|
||||
let discr =
|
||||
TyAndSize { ty: value.to_int_ty(self.tcx), size: value.size(&self.tcx) };
|
||||
self.enums.push(EnumCheckType::WithNiche {
|
||||
source_op: op.to_copy(),
|
||||
discr,
|
||||
op_size: op_layout.size,
|
||||
offset: enum_layout.fields.offset(tag_field.as_usize()),
|
||||
valid_range,
|
||||
});
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
self.super_rvalue(rvalue, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn split_block(
|
||||
basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
|
||||
location: Location,
|
||||
) -> BasicBlock {
|
||||
let block_data = &mut basic_blocks[location.block];
|
||||
|
||||
// Drain every statement after this one and move the current terminator to a new basic block.
|
||||
let new_block = BasicBlockData {
|
||||
statements: block_data.statements.split_off(location.statement_index),
|
||||
terminator: block_data.terminator.take(),
|
||||
is_cleanup: block_data.is_cleanup,
|
||||
};
|
||||
|
||||
basic_blocks.push(new_block)
|
||||
}
|
||||
|
||||
/// Inserts the cast of an operand (any type) to a u128 value that holds the discriminant value.
|
||||
fn insert_discr_cast_to_u128<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
|
||||
block_data: &mut BasicBlockData<'tcx>,
|
||||
source_op: Operand<'tcx>,
|
||||
discr: TyAndSize<'tcx>,
|
||||
op_size: Size,
|
||||
offset: Option<Size>,
|
||||
source_info: SourceInfo,
|
||||
) -> Place<'tcx> {
|
||||
let get_ty_for_size = |tcx: TyCtxt<'tcx>, size: Size| -> Ty<'tcx> {
|
||||
match size.bytes() {
|
||||
1 => tcx.types.u8,
|
||||
2 => tcx.types.u16,
|
||||
4 => tcx.types.u32,
|
||||
8 => tcx.types.u64,
|
||||
16 => tcx.types.u128,
|
||||
invalid => bug!("Found discriminant with invalid size, has {} bytes", invalid),
|
||||
}
|
||||
};
|
||||
|
||||
let (cast_kind, discr_ty_bits) = if discr.size.bytes() < op_size.bytes() {
|
||||
// The discriminant is less wide than the operand, cast the operand into
|
||||
// [MaybeUninit; N] and then index into it.
|
||||
let mu = Ty::new_maybe_uninit(tcx, tcx.types.u8);
|
||||
let array_len = op_size.bytes();
|
||||
let mu_array_ty = Ty::new_array(tcx, mu, array_len);
|
||||
let mu_array =
|
||||
local_decls.push(LocalDecl::with_source_info(mu_array_ty, source_info)).into();
|
||||
let rvalue = Rvalue::Cast(CastKind::Transmute, source_op, mu_array_ty);
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((mu_array, rvalue))),
|
||||
});
|
||||
|
||||
// Index into the array of MaybeUninit to get something that is actually
|
||||
// as wide as the discriminant.
|
||||
let offset = offset.unwrap_or(Size::ZERO);
|
||||
let smaller_mu_array = mu_array.project_deeper(
|
||||
&[ProjectionElem::Subslice {
|
||||
from: offset.bytes(),
|
||||
to: offset.bytes() + discr.size.bytes(),
|
||||
from_end: false,
|
||||
}],
|
||||
tcx,
|
||||
);
|
||||
|
||||
(CastKind::Transmute, Operand::Copy(smaller_mu_array))
|
||||
} else {
|
||||
let operand_int_ty = get_ty_for_size(tcx, op_size);
|
||||
|
||||
let op_as_int =
|
||||
local_decls.push(LocalDecl::with_source_info(operand_int_ty, source_info)).into();
|
||||
let rvalue = Rvalue::Cast(CastKind::Transmute, source_op, operand_int_ty);
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((op_as_int, rvalue))),
|
||||
});
|
||||
|
||||
(CastKind::IntToInt, Operand::Copy(op_as_int))
|
||||
};
|
||||
|
||||
// Cast the resulting value to the actual discriminant integer type.
|
||||
let rvalue = Rvalue::Cast(cast_kind, discr_ty_bits, discr.ty);
|
||||
let discr_in_discr_ty =
|
||||
local_decls.push(LocalDecl::with_source_info(discr.ty, source_info)).into();
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((discr_in_discr_ty, rvalue))),
|
||||
});
|
||||
|
||||
// Cast the discriminant to a u128 (base for comparisions of enum discriminants).
|
||||
let const_u128 = Ty::new_uint(tcx, ty::UintTy::U128);
|
||||
let rvalue = Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr_in_discr_ty), const_u128);
|
||||
let discr = local_decls.push(LocalDecl::with_source_info(const_u128, source_info)).into();
|
||||
block_data
|
||||
.statements
|
||||
.push(Statement { source_info, kind: StatementKind::Assign(Box::new((discr, rvalue))) });
|
||||
|
||||
discr
|
||||
}
|
||||
|
||||
fn insert_direct_enum_check<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
|
||||
basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
|
||||
current_block: BasicBlock,
|
||||
source_op: Operand<'tcx>,
|
||||
discr: TyAndSize<'tcx>,
|
||||
op_size: Size,
|
||||
discriminants: Vec<u128>,
|
||||
source_info: SourceInfo,
|
||||
new_block: BasicBlock,
|
||||
) {
|
||||
// Insert a new target block that is branched to in case of an invalid discriminant.
|
||||
let invalid_discr_block_data = BasicBlockData::new(None, false);
|
||||
let invalid_discr_block = basic_blocks.push(invalid_discr_block_data);
|
||||
let block_data = &mut basic_blocks[current_block];
|
||||
let discr = insert_discr_cast_to_u128(
|
||||
tcx,
|
||||
local_decls,
|
||||
block_data,
|
||||
source_op,
|
||||
discr,
|
||||
op_size,
|
||||
None,
|
||||
source_info,
|
||||
);
|
||||
|
||||
// Branch based on the discriminant value.
|
||||
block_data.terminator = Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(discr),
|
||||
targets: SwitchTargets::new(
|
||||
discriminants.into_iter().map(|discr| (discr, new_block)),
|
||||
invalid_discr_block,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
// Abort in case of an invalid enum discriminant.
|
||||
basic_blocks[invalid_discr_block].terminator = Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::Assert {
|
||||
cond: Operand::Constant(Box::new(ConstOperand {
|
||||
span: source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::from_bool(false), tcx.types.bool),
|
||||
})),
|
||||
expected: true,
|
||||
target: new_block,
|
||||
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))),
|
||||
// This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
|
||||
// We never want to insert an unwind into unsafe code, because unwinding could
|
||||
// make a failing UB check turn into much worse UB when we start unwinding.
|
||||
unwind: UnwindAction::Unreachable,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn insert_uninhabited_enum_check<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
|
||||
block_data: &mut BasicBlockData<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
new_block: BasicBlock,
|
||||
) {
|
||||
let is_ok: Place<'_> =
|
||||
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
is_ok,
|
||||
Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
|
||||
span: source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::from_bool(false), tcx.types.bool),
|
||||
}))),
|
||||
))),
|
||||
});
|
||||
|
||||
block_data.terminator = Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::Assert {
|
||||
cond: Operand::Copy(is_ok),
|
||||
expected: true,
|
||||
target: new_block,
|
||||
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Constant(Box::new(
|
||||
ConstOperand {
|
||||
span: source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::from_u128(0), tcx.types.u128),
|
||||
},
|
||||
)))),
|
||||
// This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
|
||||
// We never want to insert an unwind into unsafe code, because unwinding could
|
||||
// make a failing UB check turn into much worse UB when we start unwinding.
|
||||
unwind: UnwindAction::Unreachable,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn insert_niche_check<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
|
||||
block_data: &mut BasicBlockData<'tcx>,
|
||||
source_op: Operand<'tcx>,
|
||||
valid_range: WrappingRange,
|
||||
discr: TyAndSize<'tcx>,
|
||||
op_size: Size,
|
||||
offset: Size,
|
||||
source_info: SourceInfo,
|
||||
new_block: BasicBlock,
|
||||
) {
|
||||
let discr = insert_discr_cast_to_u128(
|
||||
tcx,
|
||||
local_decls,
|
||||
block_data,
|
||||
source_op,
|
||||
discr,
|
||||
op_size,
|
||||
Some(offset),
|
||||
source_info,
|
||||
);
|
||||
|
||||
// Compare the discriminant agains the valid_range.
|
||||
let start_const = Operand::Constant(Box::new(ConstOperand {
|
||||
span: source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(ConstValue::from_u128(valid_range.start), tcx.types.u128),
|
||||
}));
|
||||
let end_start_diff_const = Operand::Constant(Box::new(ConstOperand {
|
||||
span: source_info.span,
|
||||
user_ty: None,
|
||||
const_: Const::Val(
|
||||
ConstValue::from_u128(u128::wrapping_sub(valid_range.end, valid_range.start)),
|
||||
tcx.types.u128,
|
||||
),
|
||||
}));
|
||||
|
||||
let discr_diff: Place<'_> =
|
||||
local_decls.push(LocalDecl::with_source_info(tcx.types.u128, source_info)).into();
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
discr_diff,
|
||||
Rvalue::BinaryOp(BinOp::Sub, Box::new((Operand::Copy(discr), start_const))),
|
||||
))),
|
||||
});
|
||||
|
||||
let is_ok: Place<'_> =
|
||||
local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
|
||||
block_data.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
is_ok,
|
||||
Rvalue::BinaryOp(
|
||||
// This is a `WrappingRange`, so make sure to get the wrapping right.
|
||||
BinOp::Le,
|
||||
Box::new((Operand::Copy(discr_diff), end_start_diff_const)),
|
||||
),
|
||||
))),
|
||||
});
|
||||
|
||||
block_data.terminator = Some(Terminator {
|
||||
source_info,
|
||||
kind: TerminatorKind::Assert {
|
||||
cond: Operand::Copy(is_ok),
|
||||
expected: true,
|
||||
target: new_block,
|
||||
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))),
|
||||
// This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
|
||||
// We never want to insert an unwind into unsafe code, because unwinding could
|
||||
// make a failing UB check turn into much worse UB when we start unwinding.
|
||||
unwind: UnwindAction::Unreachable,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -770,14 +770,15 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if callee_def_id.is_local()
|
||||
if let Some(callee_def_id) = callee_def_id.as_local()
|
||||
&& !inliner
|
||||
.tcx()
|
||||
.is_lang_item(inliner.tcx().parent(caller_def_id), rustc_hir::LangItem::FnOnce)
|
||||
{
|
||||
// If we know for sure that the function we're calling will itself try to
|
||||
// call us, then we avoid inlining that function.
|
||||
if inliner.tcx().mir_callgraph_reachable((callee, caller_def_id.expect_local())) {
|
||||
if inliner.tcx().mir_callgraph_cyclic(caller_def_id.expect_local()).contains(&callee_def_id)
|
||||
{
|
||||
debug!("query cycle avoidance");
|
||||
return Err("caller might be reachable from callee");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::mir::TerminatorKind;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt, TypeVisitableExt};
|
||||
|
|
@ -7,137 +8,143 @@ use rustc_session::Limit;
|
|||
use rustc_span::sym;
|
||||
use tracing::{instrument, trace};
|
||||
|
||||
// FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
|
||||
// this query ridiculously often.
|
||||
#[instrument(level = "debug", skip(tcx, root, target))]
|
||||
pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
(root, target): (ty::Instance<'tcx>, LocalDefId),
|
||||
) -> bool {
|
||||
trace!(%root, target = %tcx.def_path_str(target));
|
||||
assert_ne!(
|
||||
root.def_id().expect_local(),
|
||||
target,
|
||||
"you should not call `mir_callgraph_reachable` on immediate self recursion"
|
||||
);
|
||||
assert!(
|
||||
matches!(root.def, InstanceKind::Item(_)),
|
||||
"you should not call `mir_callgraph_reachable` on shims"
|
||||
);
|
||||
assert!(
|
||||
!tcx.is_constructor(root.def_id()),
|
||||
"you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
|
||||
);
|
||||
#[instrument(
|
||||
level = "debug",
|
||||
skip(tcx, typing_env, target, stack, seen, recursion_limiter, caller, recursion_limit)
|
||||
)]
|
||||
fn process<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
caller: ty::Instance<'tcx>,
|
||||
target: LocalDefId,
|
||||
stack: &mut Vec<ty::Instance<'tcx>>,
|
||||
seen: &mut FxHashSet<ty::Instance<'tcx>>,
|
||||
recursion_limiter: &mut FxHashMap<DefId, usize>,
|
||||
recursion_limit: Limit,
|
||||
) -> bool {
|
||||
trace!(%caller);
|
||||
for &(callee, args) in tcx.mir_inliner_callees(caller.def) {
|
||||
let Ok(args) = caller.try_instantiate_mir_and_normalize_erasing_regions(
|
||||
tcx,
|
||||
typing_env,
|
||||
ty::EarlyBinder::bind(args),
|
||||
) else {
|
||||
trace!(?caller, ?typing_env, ?args, "cannot normalize, skipping");
|
||||
continue;
|
||||
};
|
||||
let Ok(Some(callee)) = ty::Instance::try_resolve(tcx, typing_env, callee, args) else {
|
||||
trace!(?callee, "cannot resolve, skipping");
|
||||
continue;
|
||||
};
|
||||
|
||||
// Found a path.
|
||||
if callee.def_id() == target.to_def_id() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if tcx.is_constructor(callee.def_id()) {
|
||||
trace!("constructors always have MIR");
|
||||
// Constructor functions cannot cause a query cycle.
|
||||
continue;
|
||||
}
|
||||
|
||||
match callee.def {
|
||||
InstanceKind::Item(_) => {
|
||||
// If there is no MIR available (either because it was not in metadata or
|
||||
// because it has no MIR because it's an extern function), then the inliner
|
||||
// won't cause cycles on this.
|
||||
if !tcx.is_mir_available(callee.def_id()) {
|
||||
trace!(?callee, "no mir available, skipping");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// These have no own callable MIR.
|
||||
InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => continue,
|
||||
// These have MIR and if that MIR is inlined, instantiated and then inlining is run
|
||||
// again, a function item can end up getting inlined. Thus we'll be able to cause
|
||||
// a cycle that way
|
||||
InstanceKind::VTableShim(_)
|
||||
| InstanceKind::ReifyShim(..)
|
||||
| InstanceKind::FnPtrShim(..)
|
||||
| InstanceKind::ClosureOnceShim { .. }
|
||||
| InstanceKind::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceKind::ThreadLocalShim { .. }
|
||||
| InstanceKind::CloneShim(..) => {}
|
||||
|
||||
// This shim does not call any other functions, thus there can be no recursion.
|
||||
InstanceKind::FnPtrAddrShim(..) => {
|
||||
continue;
|
||||
}
|
||||
InstanceKind::DropGlue(..)
|
||||
| InstanceKind::FutureDropPollShim(..)
|
||||
| InstanceKind::AsyncDropGlue(..)
|
||||
| InstanceKind::AsyncDropGlueCtorShim(..) => {
|
||||
// FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
|
||||
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
|
||||
// needs some more analysis.
|
||||
if callee.has_param() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if seen.insert(callee) {
|
||||
let recursion = recursion_limiter.entry(callee.def_id()).or_default();
|
||||
trace!(?callee, recursion = *recursion);
|
||||
if recursion_limit.value_within_limit(*recursion) {
|
||||
*recursion += 1;
|
||||
stack.push(callee);
|
||||
let found_recursion = ensure_sufficient_stack(|| {
|
||||
process(
|
||||
tcx,
|
||||
typing_env,
|
||||
callee,
|
||||
target,
|
||||
stack,
|
||||
seen,
|
||||
recursion_limiter,
|
||||
recursion_limit,
|
||||
)
|
||||
});
|
||||
if found_recursion {
|
||||
return true;
|
||||
}
|
||||
stack.pop();
|
||||
} else {
|
||||
// Pessimistically assume that there could be recursion.
|
||||
return true;
|
||||
}
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
fn should_recurse<'tcx>(tcx: TyCtxt<'tcx>, callee: ty::Instance<'tcx>) -> bool {
|
||||
match callee.def {
|
||||
// If there is no MIR available (either because it was not in metadata or
|
||||
// because it has no MIR because it's an extern function), then the inliner
|
||||
// won't cause cycles on this.
|
||||
InstanceKind::Item(_) => {
|
||||
if !tcx.is_mir_available(callee.def_id()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// These have no own callable MIR.
|
||||
InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => return false,
|
||||
|
||||
// These have MIR and if that MIR is inlined, instantiated and then inlining is run
|
||||
// again, a function item can end up getting inlined. Thus we'll be able to cause
|
||||
// a cycle that way
|
||||
InstanceKind::VTableShim(_)
|
||||
| InstanceKind::ReifyShim(..)
|
||||
| InstanceKind::FnPtrShim(..)
|
||||
| InstanceKind::ClosureOnceShim { .. }
|
||||
| InstanceKind::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceKind::ThreadLocalShim { .. }
|
||||
| InstanceKind::CloneShim(..) => {}
|
||||
|
||||
// This shim does not call any other functions, thus there can be no recursion.
|
||||
InstanceKind::FnPtrAddrShim(..) => return false,
|
||||
|
||||
// FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to
|
||||
// have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this
|
||||
// needs some more analysis.
|
||||
InstanceKind::DropGlue(..)
|
||||
| InstanceKind::FutureDropPollShim(..)
|
||||
| InstanceKind::AsyncDropGlue(..)
|
||||
| InstanceKind::AsyncDropGlueCtorShim(..) => {
|
||||
if callee.has_param() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
crate::pm::should_run_pass(tcx, &crate::inline::Inline, crate::pm::Optimizations::Allowed)
|
||||
|| crate::inline::ForceInline::should_run_pass_for_callee(tcx, callee.def.def_id())
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "debug",
|
||||
skip(tcx, typing_env, seen, involved, recursion_limiter, recursion_limit),
|
||||
ret
|
||||
)]
|
||||
fn process<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
caller: ty::Instance<'tcx>,
|
||||
target: LocalDefId,
|
||||
seen: &mut FxHashSet<ty::Instance<'tcx>>,
|
||||
involved: &mut FxHashSet<LocalDefId>,
|
||||
recursion_limiter: &mut FxHashMap<DefId, usize>,
|
||||
recursion_limit: Limit,
|
||||
) -> bool {
|
||||
trace!(%caller);
|
||||
let mut cycle_found = false;
|
||||
|
||||
for &(callee, args) in tcx.mir_inliner_callees(caller.def) {
|
||||
let Ok(args) = caller.try_instantiate_mir_and_normalize_erasing_regions(
|
||||
tcx,
|
||||
typing_env,
|
||||
ty::EarlyBinder::bind(args),
|
||||
) else {
|
||||
trace!(?caller, ?typing_env, ?args, "cannot normalize, skipping");
|
||||
continue;
|
||||
};
|
||||
let Ok(Some(callee)) = ty::Instance::try_resolve(tcx, typing_env, callee, args) else {
|
||||
trace!(?callee, "cannot resolve, skipping");
|
||||
continue;
|
||||
};
|
||||
|
||||
// Found a path.
|
||||
if callee.def_id() == target.to_def_id() {
|
||||
cycle_found = true;
|
||||
}
|
||||
|
||||
if tcx.is_constructor(callee.def_id()) {
|
||||
trace!("constructors always have MIR");
|
||||
// Constructor functions cannot cause a query cycle.
|
||||
continue;
|
||||
}
|
||||
|
||||
if !should_recurse(tcx, callee) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if seen.insert(callee) {
|
||||
let recursion = recursion_limiter.entry(callee.def_id()).or_default();
|
||||
trace!(?callee, recursion = *recursion);
|
||||
let found_recursion = if recursion_limit.value_within_limit(*recursion) {
|
||||
*recursion += 1;
|
||||
ensure_sufficient_stack(|| {
|
||||
process(
|
||||
tcx,
|
||||
typing_env,
|
||||
callee,
|
||||
target,
|
||||
seen,
|
||||
involved,
|
||||
recursion_limiter,
|
||||
recursion_limit,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
// Pessimistically assume that there could be recursion.
|
||||
true
|
||||
};
|
||||
if found_recursion {
|
||||
if let Some(callee) = callee.def_id().as_local() {
|
||||
// Calling `optimized_mir` of a non-local definition cannot cycle.
|
||||
involved.insert(callee);
|
||||
}
|
||||
cycle_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cycle_found
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
pub(crate) fn mir_callgraph_cyclic<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
root: LocalDefId,
|
||||
) -> UnordSet<LocalDefId> {
|
||||
assert!(
|
||||
!tcx.is_constructor(root.to_def_id()),
|
||||
"you should not call `mir_callgraph_reachable` on enum/struct constructor functions"
|
||||
);
|
||||
|
||||
// FIXME(-Znext-solver=no): Remove this hack when trait solver overflow can return an error.
|
||||
// In code like that pointed out in #128887, the type complexity we ask the solver to deal with
|
||||
// grows as we recurse into the call graph. If we use the same recursion limit here and in the
|
||||
|
|
@ -146,16 +153,32 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
|||
// the default recursion limits are quite generous for us. If we need to recurse 64 times
|
||||
// into the call graph, we're probably not going to find any useful MIR inlining.
|
||||
let recursion_limit = tcx.recursion_limit() / 2;
|
||||
let mut involved = FxHashSet::default();
|
||||
let typing_env = ty::TypingEnv::post_analysis(tcx, root);
|
||||
let Ok(Some(root_instance)) = ty::Instance::try_resolve(
|
||||
tcx,
|
||||
typing_env,
|
||||
root.to_def_id(),
|
||||
ty::GenericArgs::identity_for_item(tcx, root.to_def_id()),
|
||||
) else {
|
||||
trace!("cannot resolve, skipping");
|
||||
return involved.into();
|
||||
};
|
||||
if !should_recurse(tcx, root_instance) {
|
||||
trace!("cannot walk, skipping");
|
||||
return involved.into();
|
||||
}
|
||||
process(
|
||||
tcx,
|
||||
ty::TypingEnv::post_analysis(tcx, target),
|
||||
typing_env,
|
||||
root_instance,
|
||||
root,
|
||||
target,
|
||||
&mut Vec::new(),
|
||||
&mut FxHashSet::default(),
|
||||
&mut involved,
|
||||
&mut FxHashMap::default(),
|
||||
recursion_limit,
|
||||
)
|
||||
);
|
||||
involved.into()
|
||||
}
|
||||
|
||||
pub(crate) fn mir_inliner_callees<'tcx>(
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ declare_passes! {
|
|||
mod check_inline : CheckForceInline;
|
||||
mod check_call_recursion : CheckCallRecursion, CheckDropRecursion;
|
||||
mod check_alignment : CheckAlignment;
|
||||
mod check_enums : CheckEnums;
|
||||
mod check_const_item_mutation : CheckConstItemMutation;
|
||||
mod check_null : CheckNull;
|
||||
mod check_packed_ref : CheckPackedRef;
|
||||
|
|
@ -215,7 +216,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
optimized_mir,
|
||||
is_mir_available,
|
||||
is_ctfe_mir_available: is_mir_available,
|
||||
mir_callgraph_reachable: inline::cycle::mir_callgraph_reachable,
|
||||
mir_callgraph_cyclic: inline::cycle::mir_callgraph_cyclic,
|
||||
mir_inliner_callees: inline::cycle::mir_inliner_callees,
|
||||
promoted_mir,
|
||||
deduced_param_attrs: deduce_param_attrs::deduced_param_attrs,
|
||||
|
|
@ -666,6 +667,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
|
|||
// Add some UB checks before any UB gets optimized away.
|
||||
&check_alignment::CheckAlignment,
|
||||
&check_null::CheckNull,
|
||||
&check_enums::CheckEnums,
|
||||
// Before inlining: trim down MIR with passes to reduce inlining work.
|
||||
|
||||
// Has to be done before inlining, otherwise actual call will be almost always inlined.
|
||||
|
|
|
|||
|
|
@ -834,6 +834,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
|
|||
mir::AssertKind::NullPointerDereference => {
|
||||
push_mono_lang_item(self, LangItem::PanicNullPointerDereference);
|
||||
}
|
||||
mir::AssertKind::InvalidEnumConstruction(_) => {
|
||||
push_mono_lang_item(self, LangItem::PanicInvalidEnumConstruction);
|
||||
}
|
||||
_ => {
|
||||
push_mono_lang_item(self, msg.panic_function());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -506,6 +506,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
|
|||
AssertKind::NullPointerDereference => {
|
||||
stable_mir::mir::AssertMessage::NullPointerDereference
|
||||
}
|
||||
AssertKind::InvalidEnumConstruction(source) => {
|
||||
stable_mir::mir::AssertMessage::InvalidEnumConstruction(source.stable(tables))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ pub enum AssertMessage {
|
|||
ResumedAfterDrop(CoroutineKind),
|
||||
MisalignedPointerDereference { required: Operand, found: Operand },
|
||||
NullPointerDereference,
|
||||
InvalidEnumConstruction(Operand),
|
||||
}
|
||||
|
||||
impl AssertMessage {
|
||||
|
|
@ -342,6 +343,9 @@ impl AssertMessage {
|
|||
Ok("misaligned pointer dereference")
|
||||
}
|
||||
AssertMessage::NullPointerDereference => Ok("null pointer dereference occurred"),
|
||||
AssertMessage::InvalidEnumConstruction(_) => {
|
||||
Ok("trying to construct an enum from an invalid value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,6 +313,10 @@ fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::R
|
|||
AssertMessage::NullPointerDereference => {
|
||||
write!(writer, "\"null pointer dereference occurred\"")
|
||||
}
|
||||
AssertMessage::InvalidEnumConstruction(op) => {
|
||||
let pretty_op = pretty_operand(op);
|
||||
write!(writer, "\"trying to construct an enum from an invalid value {{}}\",{pretty_op}")
|
||||
}
|
||||
AssertMessage::ResumedAfterReturn(_)
|
||||
| AssertMessage::ResumedAfterPanic(_)
|
||||
| AssertMessage::ResumedAfterDrop(_) => {
|
||||
|
|
|
|||
|
|
@ -367,7 +367,8 @@ macro_rules! make_mir_visitor {
|
|||
}
|
||||
AssertMessage::OverflowNeg(op)
|
||||
| AssertMessage::DivisionByZero(op)
|
||||
| AssertMessage::RemainderByZero(op) => {
|
||||
| AssertMessage::RemainderByZero(op)
|
||||
| AssertMessage::InvalidEnumConstruction(op) => {
|
||||
self.visit_operand(op, location);
|
||||
}
|
||||
AssertMessage::ResumedAfterReturn(_)
|
||||
|
|
|
|||
|
|
@ -150,14 +150,10 @@ symbols! {
|
|||
// As well as the symbols listed, there are symbols for the strings
|
||||
// "0", "1", ..., "9", which are accessible via `sym::integer`.
|
||||
//
|
||||
// The proc macro will abort if symbols are not in alphabetical order (as
|
||||
// defined by `impl Ord for str`) or if any symbols are duplicated. Vim
|
||||
// users can sort the list by selecting it and executing the command
|
||||
// `:'<,'>!LC_ALL=C sort`.
|
||||
//
|
||||
// There is currently no checking that all symbols are used; that would be
|
||||
// nice to have.
|
||||
Symbols {
|
||||
// tidy-alphabetical-start
|
||||
Abi,
|
||||
AcqRel,
|
||||
Acquire,
|
||||
|
|
@ -175,18 +171,18 @@ symbols! {
|
|||
AsyncGenPending,
|
||||
AsyncGenReady,
|
||||
AtomicBool,
|
||||
AtomicI128,
|
||||
AtomicI8,
|
||||
AtomicI16,
|
||||
AtomicI32,
|
||||
AtomicI64,
|
||||
AtomicI8,
|
||||
AtomicI128,
|
||||
AtomicIsize,
|
||||
AtomicPtr,
|
||||
AtomicU128,
|
||||
AtomicU8,
|
||||
AtomicU16,
|
||||
AtomicU32,
|
||||
AtomicU64,
|
||||
AtomicU8,
|
||||
AtomicU128,
|
||||
AtomicUsize,
|
||||
BTreeEntry,
|
||||
BTreeMap,
|
||||
|
|
@ -607,10 +603,10 @@ symbols! {
|
|||
catch_unwind,
|
||||
cause,
|
||||
cdylib,
|
||||
ceilf128,
|
||||
ceilf16,
|
||||
ceilf32,
|
||||
ceilf64,
|
||||
ceilf128,
|
||||
cfg,
|
||||
cfg_accessible,
|
||||
cfg_attr,
|
||||
|
|
@ -747,10 +743,10 @@ symbols! {
|
|||
copy,
|
||||
copy_closures,
|
||||
copy_nonoverlapping,
|
||||
copysignf128,
|
||||
copysignf16,
|
||||
copysignf32,
|
||||
copysignf64,
|
||||
copysignf128,
|
||||
core,
|
||||
core_panic,
|
||||
core_panic_2015_macro,
|
||||
|
|
@ -763,10 +759,10 @@ symbols! {
|
|||
coroutine_state,
|
||||
coroutine_yield,
|
||||
coroutines,
|
||||
cosf128,
|
||||
cosf16,
|
||||
cosf32,
|
||||
cosf64,
|
||||
cosf128,
|
||||
count,
|
||||
coverage,
|
||||
coverage_attribute,
|
||||
|
|
@ -874,8 +870,8 @@ symbols! {
|
|||
dotdot_in_tuple_patterns,
|
||||
dotdoteq_in_patterns,
|
||||
dreg,
|
||||
dreg_low16,
|
||||
dreg_low8,
|
||||
dreg_low16,
|
||||
drop,
|
||||
drop_in_place,
|
||||
drop_types_in_const,
|
||||
|
|
@ -928,16 +924,16 @@ symbols! {
|
|||
exhaustive_integer_patterns,
|
||||
exhaustive_patterns,
|
||||
existential_type,
|
||||
exp2f128,
|
||||
exp2f16,
|
||||
exp2f32,
|
||||
exp2f64,
|
||||
exp2f128,
|
||||
expect,
|
||||
expected,
|
||||
expf128,
|
||||
expf16,
|
||||
expf32,
|
||||
expf64,
|
||||
expf128,
|
||||
explicit_extern_abis,
|
||||
explicit_generic_args_with_impl_trait,
|
||||
explicit_tail_calls,
|
||||
|
|
@ -958,9 +954,6 @@ symbols! {
|
|||
external,
|
||||
external_doc,
|
||||
f,
|
||||
f128,
|
||||
f128_epsilon,
|
||||
f128_nan,
|
||||
f16,
|
||||
f16_epsilon,
|
||||
f16_nan,
|
||||
|
|
@ -999,10 +992,13 @@ symbols! {
|
|||
f64_legacy_const_neg_infinity,
|
||||
f64_legacy_const_radix,
|
||||
f64_nan,
|
||||
fabsf128,
|
||||
f128,
|
||||
f128_epsilon,
|
||||
f128_nan,
|
||||
fabsf16,
|
||||
fabsf32,
|
||||
fabsf64,
|
||||
fabsf128,
|
||||
fadd_algebraic,
|
||||
fadd_fast,
|
||||
fake_variadic,
|
||||
|
|
@ -1024,22 +1020,22 @@ symbols! {
|
|||
flags,
|
||||
float,
|
||||
float_to_int_unchecked,
|
||||
floorf128,
|
||||
floorf16,
|
||||
floorf32,
|
||||
floorf64,
|
||||
fmaf128,
|
||||
floorf128,
|
||||
fmaf16,
|
||||
fmaf32,
|
||||
fmaf64,
|
||||
fmaf128,
|
||||
fmt,
|
||||
fmt_debug,
|
||||
fmul_algebraic,
|
||||
fmul_fast,
|
||||
fmuladdf128,
|
||||
fmuladdf16,
|
||||
fmuladdf32,
|
||||
fmuladdf64,
|
||||
fmuladdf128,
|
||||
fn_align,
|
||||
fn_body,
|
||||
fn_delegation,
|
||||
|
|
@ -1140,13 +1136,12 @@ symbols! {
|
|||
html_root_url,
|
||||
hwaddress,
|
||||
i,
|
||||
i128,
|
||||
i128_legacy_const_max,
|
||||
i128_legacy_const_min,
|
||||
i128_legacy_fn_max_value,
|
||||
i128_legacy_fn_min_value,
|
||||
i128_legacy_mod,
|
||||
i128_type,
|
||||
i8,
|
||||
i8_legacy_const_max,
|
||||
i8_legacy_const_min,
|
||||
i8_legacy_fn_max_value,
|
||||
i8_legacy_fn_min_value,
|
||||
i8_legacy_mod,
|
||||
i16,
|
||||
i16_legacy_const_max,
|
||||
i16_legacy_const_min,
|
||||
|
|
@ -1165,12 +1160,13 @@ symbols! {
|
|||
i64_legacy_fn_max_value,
|
||||
i64_legacy_fn_min_value,
|
||||
i64_legacy_mod,
|
||||
i8,
|
||||
i8_legacy_const_max,
|
||||
i8_legacy_const_min,
|
||||
i8_legacy_fn_max_value,
|
||||
i8_legacy_fn_min_value,
|
||||
i8_legacy_mod,
|
||||
i128,
|
||||
i128_legacy_const_max,
|
||||
i128_legacy_const_min,
|
||||
i128_legacy_fn_max_value,
|
||||
i128_legacy_fn_min_value,
|
||||
i128_legacy_mod,
|
||||
i128_type,
|
||||
ident,
|
||||
if_let,
|
||||
if_let_guard,
|
||||
|
|
@ -1292,19 +1288,19 @@ symbols! {
|
|||
loaded_from_disk,
|
||||
local,
|
||||
local_inner_macros,
|
||||
log10f128,
|
||||
log10f16,
|
||||
log10f32,
|
||||
log10f64,
|
||||
log2f128,
|
||||
log2f16,
|
||||
log2f32,
|
||||
log2f64,
|
||||
log2f128,
|
||||
log10f16,
|
||||
log10f32,
|
||||
log10f64,
|
||||
log10f128,
|
||||
log_syntax,
|
||||
logf128,
|
||||
logf16,
|
||||
logf32,
|
||||
logf64,
|
||||
logf128,
|
||||
loongarch_target_feature,
|
||||
loop_break_value,
|
||||
loop_match,
|
||||
|
|
@ -1334,14 +1330,14 @@ symbols! {
|
|||
match_beginning_vert,
|
||||
match_default_bindings,
|
||||
matches_macro,
|
||||
maximumf128,
|
||||
maximumf16,
|
||||
maximumf32,
|
||||
maximumf64,
|
||||
maxnumf128,
|
||||
maximumf128,
|
||||
maxnumf16,
|
||||
maxnumf32,
|
||||
maxnumf64,
|
||||
maxnumf128,
|
||||
may_dangle,
|
||||
may_unwind,
|
||||
maybe_uninit,
|
||||
|
|
@ -1372,14 +1368,14 @@ symbols! {
|
|||
min_generic_const_args,
|
||||
min_specialization,
|
||||
min_type_alias_impl_trait,
|
||||
minimumf128,
|
||||
minimumf16,
|
||||
minimumf32,
|
||||
minimumf64,
|
||||
minnumf128,
|
||||
minimumf128,
|
||||
minnumf16,
|
||||
minnumf32,
|
||||
minnumf64,
|
||||
minnumf128,
|
||||
mips_target_feature,
|
||||
mir_assume,
|
||||
mir_basic_block,
|
||||
|
|
@ -1586,6 +1582,7 @@ symbols! {
|
|||
panic_implementation,
|
||||
panic_in_cleanup,
|
||||
panic_info,
|
||||
panic_invalid_enum_construction,
|
||||
panic_location,
|
||||
panic_misaligned_pointer_dereference,
|
||||
panic_nounwind,
|
||||
|
|
@ -1633,14 +1630,14 @@ symbols! {
|
|||
post_dash_lto: "post-lto",
|
||||
postfix_match,
|
||||
powerpc_target_feature,
|
||||
powf128,
|
||||
powf16,
|
||||
powf32,
|
||||
powf64,
|
||||
powif128,
|
||||
powf128,
|
||||
powif16,
|
||||
powif32,
|
||||
powif64,
|
||||
powif128,
|
||||
pre_dash_lto: "pre-lto",
|
||||
precise_capturing,
|
||||
precise_capturing_in_traits,
|
||||
|
|
@ -1785,14 +1782,14 @@ symbols! {
|
|||
ropi_rwpi: "ropi-rwpi",
|
||||
rotate_left,
|
||||
rotate_right,
|
||||
round_ties_even_f128,
|
||||
round_ties_even_f16,
|
||||
round_ties_even_f32,
|
||||
round_ties_even_f64,
|
||||
roundf128,
|
||||
round_ties_even_f128,
|
||||
roundf16,
|
||||
roundf32,
|
||||
roundf64,
|
||||
roundf128,
|
||||
rt,
|
||||
rtm_target_feature,
|
||||
rust,
|
||||
|
|
@ -1972,8 +1969,8 @@ symbols! {
|
|||
simd_fexp2,
|
||||
simd_ffi,
|
||||
simd_flog,
|
||||
simd_flog10,
|
||||
simd_flog2,
|
||||
simd_flog10,
|
||||
simd_floor,
|
||||
simd_fma,
|
||||
simd_fmax,
|
||||
|
|
@ -2021,10 +2018,10 @@ symbols! {
|
|||
simd_with_exposed_provenance,
|
||||
simd_xor,
|
||||
since,
|
||||
sinf128,
|
||||
sinf16,
|
||||
sinf32,
|
||||
sinf64,
|
||||
sinf128,
|
||||
size,
|
||||
size_of,
|
||||
size_of_val,
|
||||
|
|
@ -2046,10 +2043,10 @@ symbols! {
|
|||
specialization,
|
||||
speed,
|
||||
spotlight,
|
||||
sqrtf128,
|
||||
sqrtf16,
|
||||
sqrtf32,
|
||||
sqrtf64,
|
||||
sqrtf128,
|
||||
sreg,
|
||||
sreg_low16,
|
||||
sse,
|
||||
|
|
@ -2127,10 +2124,10 @@ symbols! {
|
|||
target_has_atomic,
|
||||
target_has_atomic_equal_alignment,
|
||||
target_has_atomic_load_store,
|
||||
target_has_reliable_f128,
|
||||
target_has_reliable_f128_math,
|
||||
target_has_reliable_f16,
|
||||
target_has_reliable_f16_math,
|
||||
target_has_reliable_f128,
|
||||
target_has_reliable_f128_math,
|
||||
target_os,
|
||||
target_pointer_width,
|
||||
target_thread_local,
|
||||
|
|
@ -2173,10 +2170,10 @@ symbols! {
|
|||
transparent_enums,
|
||||
transparent_unions,
|
||||
trivial_bounds,
|
||||
truncf128,
|
||||
truncf16,
|
||||
truncf32,
|
||||
truncf64,
|
||||
truncf128,
|
||||
try_blocks,
|
||||
try_capture,
|
||||
try_from,
|
||||
|
|
@ -2205,12 +2202,12 @@ symbols! {
|
|||
type_name,
|
||||
type_privacy_lints,
|
||||
typed_swap_nonoverlapping,
|
||||
u128,
|
||||
u128_legacy_const_max,
|
||||
u128_legacy_const_min,
|
||||
u128_legacy_fn_max_value,
|
||||
u128_legacy_fn_min_value,
|
||||
u128_legacy_mod,
|
||||
u8,
|
||||
u8_legacy_const_max,
|
||||
u8_legacy_const_min,
|
||||
u8_legacy_fn_max_value,
|
||||
u8_legacy_fn_min_value,
|
||||
u8_legacy_mod,
|
||||
u16,
|
||||
u16_legacy_const_max,
|
||||
u16_legacy_const_min,
|
||||
|
|
@ -2229,12 +2226,12 @@ symbols! {
|
|||
u64_legacy_fn_max_value,
|
||||
u64_legacy_fn_min_value,
|
||||
u64_legacy_mod,
|
||||
u8,
|
||||
u8_legacy_const_max,
|
||||
u8_legacy_const_min,
|
||||
u8_legacy_fn_max_value,
|
||||
u8_legacy_fn_min_value,
|
||||
u8_legacy_mod,
|
||||
u128,
|
||||
u128_legacy_const_max,
|
||||
u128_legacy_const_min,
|
||||
u128_legacy_fn_max_value,
|
||||
u128_legacy_fn_min_value,
|
||||
u128_legacy_mod,
|
||||
ub_checks,
|
||||
unaligned_volatile_load,
|
||||
unaligned_volatile_store,
|
||||
|
|
@ -2387,6 +2384,7 @@ symbols! {
|
|||
zfh,
|
||||
zfhmin,
|
||||
zmm_reg,
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -622,6 +622,17 @@ impl<I: Interner, T: TypeFoldable<I>> ty::EarlyBinder<I, T> {
|
|||
where
|
||||
A: SliceLike<Item = I::GenericArg>,
|
||||
{
|
||||
// Nothing to fold, so let's avoid visiting things and possibly re-hashing/equating
|
||||
// them when interning. Perf testing found this to be a modest improvement.
|
||||
// See: <https://github.com/rust-lang/rust/pull/142317>
|
||||
if args.is_empty() {
|
||||
assert!(
|
||||
!self.value.has_param(),
|
||||
"{:?} has parameters, but no args were provided in instantiate",
|
||||
self.value,
|
||||
);
|
||||
return self.value;
|
||||
}
|
||||
let mut folder = ArgFolder { cx, args: args.as_slice(), binders_passed: 0 };
|
||||
self.value.fold_with(&mut folder)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1517,9 +1517,7 @@ impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
|
|||
|
||||
// use stable sort to preserve the insertion order.
|
||||
arr.sort();
|
||||
let iter = IntoIterator::into_iter(arr).map(|k| (k, SetValZST::default()));
|
||||
let map = BTreeMap::bulk_build_from_sorted_iter(iter, Global);
|
||||
BTreeSet { map }
|
||||
BTreeSet::from_sorted_iter(IntoIterator::into_iter(arr), Global)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::ffi::CStr;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::ptr::NonNull;
|
||||
|
||||
/// A struct containing information about the location of a panic.
|
||||
///
|
||||
|
|
@ -33,14 +35,13 @@ use crate::fmt;
|
|||
#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
pub struct Location<'a> {
|
||||
// Note: this filename will have exactly one nul byte at its end, but otherwise
|
||||
// it must never contain interior nul bytes. This is relied on for the conversion
|
||||
// to `CStr` below.
|
||||
//
|
||||
// The prefix of the string without the trailing nul byte will be a regular UTF8 `str`.
|
||||
file_bytes_with_nul: &'a [u8],
|
||||
// A raw pointer is used rather than a reference because the pointer is valid for one more byte
|
||||
// than the length stored in this pointer; the additional byte is the NUL-terminator used by
|
||||
// `Location::file_with_nul`.
|
||||
filename: NonNull<str>,
|
||||
line: u32,
|
||||
col: u32,
|
||||
_filename: PhantomData<&'a str>,
|
||||
}
|
||||
|
||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
|
|
@ -143,10 +144,8 @@ impl<'a> Location<'a> {
|
|||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
#[rustc_const_stable(feature = "const_location_fields", since = "1.79.0")]
|
||||
pub const fn file(&self) -> &str {
|
||||
let str_len = self.file_bytes_with_nul.len() - 1;
|
||||
// SAFETY: `file_bytes_with_nul` without the trailing nul byte is guaranteed to be
|
||||
// valid UTF8.
|
||||
unsafe { crate::str::from_raw_parts(self.file_bytes_with_nul.as_ptr(), str_len) }
|
||||
// SAFETY: The filename is valid.
|
||||
unsafe { self.filename.as_ref() }
|
||||
}
|
||||
|
||||
/// Returns the name of the source file as a nul-terminated `CStr`.
|
||||
|
|
@ -157,9 +156,17 @@ impl<'a> Location<'a> {
|
|||
#[unstable(feature = "file_with_nul", issue = "141727")]
|
||||
#[inline]
|
||||
pub const fn file_with_nul(&self) -> &CStr {
|
||||
// SAFETY: `file_bytes_with_nul` is guaranteed to have a trailing nul byte and no
|
||||
// interior nul bytes.
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(self.file_bytes_with_nul) }
|
||||
let filename = self.filename.as_ptr();
|
||||
|
||||
// SAFETY: The filename is valid for `filename_len+1` bytes, so this addition can't
|
||||
// overflow.
|
||||
let cstr_len = unsafe { crate::mem::size_of_val_raw(filename).unchecked_add(1) };
|
||||
|
||||
// SAFETY: The filename is valid for `filename_len+1` bytes.
|
||||
let slice = unsafe { crate::slice::from_raw_parts(filename.cast(), cstr_len) };
|
||||
|
||||
// SAFETY: The filename is guaranteed to have a trailing nul byte and no interior nul bytes.
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(slice) }
|
||||
}
|
||||
|
||||
/// Returns the line number from which the panic originated.
|
||||
|
|
@ -220,3 +227,8 @@ impl fmt::Display for Location<'_> {
|
|||
write!(formatter, "{}:{}:{}", self.file(), self.line, self.col)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
unsafe impl Send for Location<'_> {}
|
||||
#[stable(feature = "panic_hooks", since = "1.10.0")]
|
||||
unsafe impl Sync for Location<'_> {}
|
||||
|
|
|
|||
|
|
@ -314,6 +314,22 @@ fn panic_null_pointer_dereference() -> ! {
|
|||
)
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold, optimize(size))]
|
||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[track_caller]
|
||||
#[lang = "panic_invalid_enum_construction"] // needed by codegen for panic on invalid enum construction.
|
||||
#[rustc_nounwind] // `CheckEnums` MIR pass requires this function to never unwind
|
||||
fn panic_invalid_enum_construction(source: u128) -> ! {
|
||||
if cfg!(feature = "panic_immediate_abort") {
|
||||
super::intrinsics::abort()
|
||||
}
|
||||
|
||||
panic_nounwind_fmt(
|
||||
format_args!("trying to construct an enum from an invalid value {source:#x}"),
|
||||
/* force_no_backtrace */ false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Panics because we cannot unwind out of a function.
|
||||
///
|
||||
/// This is a separate function to avoid the codesize impact of each crate containing the string to
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@
|
|||
#![stable(feature = "raw_ext", since = "1.1.0")]
|
||||
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
pub mod raw;
|
||||
|
|
|
|||
50
library/std/src/os/illumos/net.rs
Normal file
50
library/std/src/os/illumos/net.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//! illumos-specific networking functionality.
|
||||
|
||||
#![unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
|
||||
use crate::io;
|
||||
use crate::os::unix::net;
|
||||
use crate::sealed::Sealed;
|
||||
use crate::sys_common::AsInner;
|
||||
|
||||
/// illumos-specific functionality for `AF_UNIX` sockets [`UnixDatagram`]
|
||||
/// and [`UnixStream`].
|
||||
///
|
||||
/// [`UnixDatagram`]: net::UnixDatagram
|
||||
/// [`UnixStream`]: net::UnixStream
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
pub trait UnixSocketExt: Sealed {
|
||||
/// Enables exclusive binding on the socket.
|
||||
///
|
||||
/// If true and if the socket had been set with `SO_REUSEADDR`,
|
||||
/// it neutralises its effect.
|
||||
/// See [`man 3 tcp`](https://docs.oracle.com/cd/E88353_01/html/E37843/setsockopt-3c.html)
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()>;
|
||||
|
||||
/// Get the bind exclusivity bind state of the socket.
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
fn exclbind(&self) -> io::Result<bool>;
|
||||
}
|
||||
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
impl UnixSocketExt for net::UnixDatagram {
|
||||
fn exclbind(&self) -> io::Result<bool> {
|
||||
self.as_inner().exclbind()
|
||||
}
|
||||
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()> {
|
||||
self.as_inner().set_exclbind(excl)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
impl UnixSocketExt for net::UnixStream {
|
||||
fn exclbind(&self) -> io::Result<bool> {
|
||||
self.as_inner().exclbind()
|
||||
}
|
||||
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()> {
|
||||
self.as_inner().set_exclbind(excl)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@
|
|||
#![stable(feature = "raw_ext", since = "1.1.0")]
|
||||
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
pub mod raw;
|
||||
|
|
|
|||
50
library/std/src/os/solaris/net.rs
Normal file
50
library/std/src/os/solaris/net.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//! solaris-specific networking functionality.
|
||||
|
||||
#![unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
|
||||
use crate::io;
|
||||
use crate::os::unix::net;
|
||||
use crate::sealed::Sealed;
|
||||
use crate::sys_common::AsInner;
|
||||
|
||||
/// solaris-specific functionality for `AF_UNIX` sockets [`UnixDatagram`]
|
||||
/// and [`UnixStream`].
|
||||
///
|
||||
/// [`UnixDatagram`]: net::UnixDatagram
|
||||
/// [`UnixStream`]: net::UnixStream
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
pub trait UnixSocketExt: Sealed {
|
||||
/// Enables exclusive binding on the socket.
|
||||
///
|
||||
/// If true and if the socket had been set with `SO_REUSEADDR`,
|
||||
/// it neutralises its effect.
|
||||
/// See [`man 3 tcp`](https://docs.oracle.com/cd/E88353_01/html/E37843/setsockopt-3c.html)
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()>;
|
||||
|
||||
/// Get the bind exclusivity bind state of the socket.
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
fn exclbind(&self) -> io::Result<bool>;
|
||||
}
|
||||
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
impl UnixSocketExt for net::UnixDatagram {
|
||||
fn exclbind(&self) -> io::Result<bool> {
|
||||
self.as_inner().exclbind()
|
||||
}
|
||||
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()> {
|
||||
self.as_inner().set_exclbind(excl)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "unix_socket_exclbind", issue = "123481")]
|
||||
impl UnixSocketExt for net::UnixStream {
|
||||
fn exclbind(&self) -> io::Result<bool> {
|
||||
self.as_inner().exclbind()
|
||||
}
|
||||
|
||||
fn so_exclbind(&self, excl: bool) -> io::Result<()> {
|
||||
self.as_inner().set_exclbind(excl)
|
||||
}
|
||||
}
|
||||
|
|
@ -522,6 +522,21 @@ impl Socket {
|
|||
Ok(name)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub fn set_exclbind(&self, excl: bool) -> io::Result<()> {
|
||||
// not yet on libc crate
|
||||
const SO_EXCLBIND: i32 = 0x1015;
|
||||
setsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND, excl)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||
pub fn exclbind(&self) -> io::Result<bool> {
|
||||
// not yet on libc crate
|
||||
const SO_EXCLBIND: i32 = 0x1015;
|
||||
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, SO_EXCLBIND)?;
|
||||
Ok(raw != 0)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux",))]
|
||||
pub fn set_passcred(&self, passcred: bool) -> io::Result<()> {
|
||||
setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int)
|
||||
|
|
|
|||
|
|
@ -968,8 +968,8 @@ impl Process {
|
|||
}
|
||||
|
||||
pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
|
||||
// If we've already waited on this process then the pid can be recycled
|
||||
// and used for another process, and we probably shouldn't be signaling
|
||||
// If we've already waited on this process then the pid can be recycled and
|
||||
// used for another process, and we probably shouldn't be sending signals to
|
||||
// random processes, so return Ok because the process has exited already.
|
||||
if self.status.is_some() {
|
||||
return Ok(());
|
||||
|
|
|
|||
|
|
@ -151,8 +151,8 @@ impl Process {
|
|||
}
|
||||
|
||||
pub fn send_signal(&self, signal: i32) -> io::Result<()> {
|
||||
// If we've already waited on this process then the pid can be recycled
|
||||
// and used for another process, and we probably shouldn't be killing
|
||||
// If we've already waited on this process then the pid can be recycled and
|
||||
// used for another process, and we probably shouldn't be sending signals to
|
||||
// random processes, so return Ok because the process has exited already.
|
||||
if self.status.is_some() {
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -10,4 +10,7 @@ python3 ../x.py build --set rust.debug=true opt-dist
|
|||
build-manifest bootstrap
|
||||
|
||||
# Use GCC for building GCC, as it seems to behave badly when built with Clang
|
||||
CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc
|
||||
# Only build GCC on full builds, not try builds
|
||||
if [ "${DIST_TRY_BUILD:-0}" == "0" ]; then
|
||||
CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1703,6 +1703,7 @@ fn render_enum_fields(
|
|||
if v.is_stripped() {
|
||||
continue;
|
||||
}
|
||||
write!(w, "{}", render_attributes_in_pre(v, TAB, cx))?;
|
||||
w.write_str(TAB)?;
|
||||
match v.kind {
|
||||
clean::VariantItem(ref var) => match var.kind {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use crate::versions::{PkgType, Versions};
|
|||
|
||||
static HOSTS: &[&str] = &[
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-pc-windows-gnullvm",
|
||||
"aarch64-pc-windows-msvc",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
|
|
@ -44,6 +45,7 @@ static HOSTS: &[&str] = &[
|
|||
"x86_64-apple-darwin",
|
||||
"x86_64-pc-solaris",
|
||||
"x86_64-pc-windows-gnu",
|
||||
"x86_64-pc-windows-gnullvm",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"x86_64-unknown-freebsd",
|
||||
"x86_64-unknown-illumos",
|
||||
|
|
@ -470,7 +472,7 @@ impl Builder {
|
|||
}
|
||||
// so is rust-mingw if it's available for the target
|
||||
PkgType::RustMingw => {
|
||||
if host.contains("pc-windows-gnu") {
|
||||
if host.ends_with("pc-windows-gnu") {
|
||||
components.push(host_component(pkg));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 409fed7dc1553d49cb9a8c0637d12d65571346ce
|
||||
Subproject commit 930b4f62cfcd1f0eabdb30a56d91bf6844b739bf
|
||||
|
|
@ -595,7 +595,7 @@ Definite bugs found:
|
|||
* [Occasional memory leak in `std::mpsc` channels](https://github.com/rust-lang/rust/issues/121582) (original code in [crossbeam](https://github.com/crossbeam-rs/crossbeam/pull/1084))
|
||||
* [Weak-memory-induced memory leak in Windows thread-local storage](https://github.com/rust-lang/rust/pull/124281)
|
||||
* [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo)
|
||||
* [Mockall reading unintialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
|
||||
* [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
|
||||
* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)
|
||||
|
||||
Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
d41e12f1f4e4884c356f319b881921aa37040de5
|
||||
8141c2265f5f2b26d89abe8df5fa67286f2425d4
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ pub const MIRI_DEFAULT_ARGS: &[&str] = &[
|
|||
"-Zmir-emit-retag",
|
||||
"-Zmir-preserve-ub",
|
||||
"-Zmir-opt-level=0",
|
||||
"-Zmir-enable-passes=-CheckAlignment,-CheckNull",
|
||||
"-Zmir-enable-passes=-CheckAlignment,-CheckNull,-CheckEnums",
|
||||
// Deduplicating diagnostics means we miss events when tracking what happens during an
|
||||
// execution. Let's not do that.
|
||||
"-Zdeduplicate-diagnostics=no",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ fn direct_raw(x: *const (i32, i32)) -> *const i32 {
|
|||
|
||||
// Ensure that if a raw pointer is created via an intermediate
|
||||
// reference, we catch that. (Just in case someone decides to
|
||||
// desugar this differenly or so.)
|
||||
// desugar this differently or so.)
|
||||
fn via_ref(x: *const (i32, i32)) -> *const i32 {
|
||||
unsafe { &(*x).0 as *const i32 } //~ERROR: dangling pointer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,12 @@ impl Bootstrap {
|
|||
let metrics_path = env.build_root().join("build").join("metrics.json");
|
||||
let args = dist_args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>();
|
||||
let cmd = cmd(&args).env("RUST_BACKTRACE", "full");
|
||||
let cmd = add_shared_x_flags(env, cmd);
|
||||
let mut cmd = add_shared_x_flags(env, cmd);
|
||||
if env.is_fast_try_build() {
|
||||
// We set build.extended=false for fast try builds, but we still need Cargo
|
||||
cmd = cmd.arg("cargo");
|
||||
}
|
||||
|
||||
Self { cmd, metrics_path }
|
||||
}
|
||||
|
||||
|
|
@ -189,5 +194,18 @@ impl Bootstrap {
|
|||
}
|
||||
|
||||
fn add_shared_x_flags(env: &Environment, cmd: CmdBuilder) -> CmdBuilder {
|
||||
if env.is_fast_try_build() { cmd.arg("--set").arg("rust.deny-warnings=false") } else { cmd }
|
||||
if env.is_fast_try_build() {
|
||||
// Skip things that cannot be skipped through `x ... --skip`
|
||||
cmd.arg("--set")
|
||||
.arg("rust.llvm-bitcode-linker=false")
|
||||
// Skip wasm-component-ld. This also skips cargo, which we need to re-enable for dist
|
||||
.arg("--set")
|
||||
.arg("build.extended=false")
|
||||
.arg("--set")
|
||||
.arg("rust.codegen-backends=['llvm']")
|
||||
.arg("--set")
|
||||
.arg("rust.deny-warnings=false")
|
||||
} else {
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -407,13 +407,18 @@ fn main() -> anyhow::Result<()> {
|
|||
for target in [
|
||||
"rust-docs",
|
||||
"rustc-docs",
|
||||
"rustc-dev",
|
||||
"rust-dev",
|
||||
"rust-docs-json",
|
||||
"rust-analyzer",
|
||||
"rustc-src",
|
||||
"extended",
|
||||
"clippy",
|
||||
"miri",
|
||||
"rustfmt",
|
||||
"gcc",
|
||||
"generate-copyright",
|
||||
"bootstrap",
|
||||
] {
|
||||
build_args.extend(["--skip".to_string(), target.to_string()]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) ->
|
|||
match result {
|
||||
Ok(FinishedAll) => (),
|
||||
Err(EarlyExit::Timeout) => {
|
||||
println!(" exited early; exceded {:?} timeout", cfg.timeout)
|
||||
println!(" exited early; exceeded {:?} timeout", cfg.timeout)
|
||||
}
|
||||
Err(EarlyExit::MaxFailures) => {
|
||||
println!(" exited early; exceeded {:?} max failures", cfg.max_failures)
|
||||
|
|
|
|||
17
tests/rustdoc/enum/enum-variant-non_exhaustive.rs
Normal file
17
tests/rustdoc/enum/enum-variant-non_exhaustive.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// regression test for https://github.com/rust-lang/rust/issues/142599
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
//@ snapshot type-code 'foo/enum.Type.html' '//pre[@class="rust item-decl"]/code'
|
||||
pub enum Type {
|
||||
#[non_exhaustive]
|
||||
// attribute that should not be shown
|
||||
#[warn(unsafe_code)]
|
||||
Variant,
|
||||
}
|
||||
|
||||
// we would love to use the `following-sibling::` axis
|
||||
// (along with an `h2[@id="aliased-type"]` query),
|
||||
// but unfortunately python doesn't implement that.
|
||||
//@ snapshot type-alias-code 'foo/type.TypeAlias.html' '//pre[@class="rust item-decl"][2]/code'
|
||||
pub type TypeAlias = Type;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<code>pub enum TypeAlias {
|
||||
#[non_exhaustive]
|
||||
Variant,
|
||||
}</code>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<code>pub enum Type {
|
||||
#[non_exhaustive]
|
||||
Variant,
|
||||
}</code>
|
||||
20
tests/ui/mir/enum/convert_non_enum_break.rs
Normal file
20
tests/ui/mir/enum/convert_non_enum_break.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x10000
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u32)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: u16,
|
||||
b: u16,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 0, b: 1 }) };
|
||||
}
|
||||
27
tests/ui/mir/enum/convert_non_enum_niche_break.rs
Normal file
27
tests/ui/mir/enum/convert_non_enum_niche_break.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x5
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Mix {
|
||||
A,
|
||||
B(u16),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Nested {
|
||||
C(Mix),
|
||||
D,
|
||||
E,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: u16,
|
||||
b: u16,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 5, b: 0 }) };
|
||||
}
|
||||
29
tests/ui/mir/enum/convert_non_enum_niche_ok.rs
Normal file
29
tests/ui/mir/enum/convert_non_enum_niche_ok.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Mix {
|
||||
A,
|
||||
B(u16),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Nested {
|
||||
C(Mix),
|
||||
D,
|
||||
E,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: u16,
|
||||
b: u16,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 0, b: 0 }) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 1, b: 0 }) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 2, b: 0 }) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<_, Nested>(Bar { a: 3, b: 0 }) };
|
||||
}
|
||||
20
tests/ui/mir/enum/convert_non_enum_ok.rs
Normal file
20
tests/ui/mir/enum/convert_non_enum_ok.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u32)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: u16,
|
||||
b: u16,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 0, b: 0 }) };
|
||||
let _val: Foo = unsafe { std::mem::transmute::<_, Foo>(Bar { a: 1, b: 0 }) };
|
||||
}
|
||||
20
tests/ui/mir/enum/niche_option_tuple_break.rs
Normal file
20
tests/ui/mir/enum/niche_option_tuple_break.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x3
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: usize,
|
||||
b: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Option<(usize, Foo)> =
|
||||
unsafe { std::mem::transmute::<_, Option<(usize, Foo)>>(Bar { a: 3, b: 3 }) };
|
||||
}
|
||||
21
tests/ui/mir/enum/niche_option_tuple_ok.rs
Normal file
21
tests/ui/mir/enum/niche_option_tuple_ok.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Bar {
|
||||
a: usize,
|
||||
b: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Option<(usize, Foo)> =
|
||||
unsafe { std::mem::transmute::<_, Option<(usize, Foo)>>(Bar { a: 0, b: 0 }) };
|
||||
let _val: Option<(usize, Foo)> =
|
||||
unsafe { std::mem::transmute::<_, Option<(usize, Foo)>>(Bar { a: 1, b: 0 }) };
|
||||
}
|
||||
13
tests/ui/mir/enum/numbered_variants_break.rs
Normal file
13
tests/ui/mir/enum/numbered_variants_break.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x3
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u8, Foo>(3) };
|
||||
}
|
||||
13
tests/ui/mir/enum/numbered_variants_ok.rs
Normal file
13
tests/ui/mir/enum/numbered_variants_ok.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u8, Foo>(0) };
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u8, Foo>(1) };
|
||||
}
|
||||
14
tests/ui/mir/enum/option_with_bigger_niche_break.rs
Normal file
14
tests/ui/mir/enum/option_with_bigger_niche_break.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x0
|
||||
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A = 2,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Option<Foo> = unsafe { std::mem::transmute::<u32, Option<Foo>>(0) };
|
||||
}
|
||||
14
tests/ui/mir/enum/option_with_bigger_niche_ok.rs
Normal file
14
tests/ui/mir/enum/option_with_bigger_niche_ok.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A = 2,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Option<Foo> = unsafe { std::mem::transmute::<u32, Option<Foo>>(2) };
|
||||
let _val: Option<Foo> = unsafe { std::mem::transmute::<u32, Option<Foo>>(3) };
|
||||
}
|
||||
14
tests/ui/mir/enum/plain_no_data_break.rs
Normal file
14
tests/ui/mir/enum/plain_no_data_break.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x1
|
||||
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A = 2,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u32, Foo>(1) };
|
||||
}
|
||||
14
tests/ui/mir/enum/plain_no_data_ok.rs
Normal file
14
tests/ui/mir/enum/plain_no_data_ok.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[repr(u32)]
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A = 2,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u32, Foo>(2) };
|
||||
let _val: Foo = unsafe { std::mem::transmute::<u32, Foo>(3) };
|
||||
}
|
||||
11
tests/ui/mir/enum/single_ok.rs
Normal file
11
tests/ui/mir/enum/single_ok.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Single {
|
||||
A
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Single = unsafe { std::mem::transmute::<(), Single>(()) };
|
||||
}
|
||||
13
tests/ui/mir/enum/single_with_repr_break.rs
Normal file
13
tests/ui/mir/enum/single_with_repr_break.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x1
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Single {
|
||||
A
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Single = unsafe { std::mem::transmute::<u16, Single>(1) };
|
||||
}
|
||||
12
tests/ui/mir/enum/single_with_repr_ok.rs
Normal file
12
tests/ui/mir/enum/single_with_repr_ok.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Single {
|
||||
A
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Single = unsafe { std::mem::transmute::<u16, Single>(0) };
|
||||
}
|
||||
21
tests/ui/mir/enum/with_niche_int_break.rs
Normal file
21
tests/ui/mir/enum/with_niche_int_break.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x4
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Mix {
|
||||
A,
|
||||
B(u16),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Nested {
|
||||
C(Mix),
|
||||
D,
|
||||
E,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Nested = unsafe { std::mem::transmute::<u32, Nested>(4) };
|
||||
}
|
||||
23
tests/ui/mir/enum/with_niche_int_ok.rs
Normal file
23
tests/ui/mir/enum/with_niche_int_ok.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u16)]
|
||||
enum Mix {
|
||||
A,
|
||||
B(u16),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Nested {
|
||||
C(Mix),
|
||||
D,
|
||||
E,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Nested = unsafe { std::mem::transmute::<u32, Nested>(0) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<u32, Nested>(1) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<u32, Nested>(2) };
|
||||
let _val: Nested = unsafe { std::mem::transmute::<u32, Nested>(3) };
|
||||
}
|
||||
14
tests/ui/mir/enum/with_niche_ptr_ok.rs
Normal file
14
tests/ui/mir/enum/with_niche_ptr_ok.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
fn main() {
|
||||
let _val = unsafe {
|
||||
std::mem::transmute::<*const usize, Option<unsafe extern "C" fn()>>(std::ptr::null())
|
||||
};
|
||||
let _val = unsafe {
|
||||
std::mem::transmute::<*const usize, Option<unsafe extern "C" fn()>>(usize::MAX as *const _)
|
||||
};
|
||||
let _val = unsafe { std::mem::transmute::<usize, Option<unsafe extern "C" fn()>>(0) };
|
||||
let _val = unsafe { std::mem::transmute::<usize, Option<unsafe extern "C" fn()>>(1) };
|
||||
let _val = unsafe { std::mem::transmute::<usize, Option<unsafe extern "C" fn()>>(usize::MAX) };
|
||||
}
|
||||
14
tests/ui/mir/enum/wrap_break.rs
Normal file
14
tests/ui/mir/enum/wrap_break.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//@ run-fail
|
||||
//@ compile-flags: -C debug-assertions
|
||||
//@ error-pattern: trying to construct an enum from an invalid value 0x0
|
||||
#![feature(never_type)]
|
||||
#![allow(invalid_value)]
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Wrap {
|
||||
A(!),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Wrap = unsafe { std::mem::transmute::<(), Wrap>(()) };
|
||||
}
|
||||
12
tests/ui/mir/enum/wrap_ok.rs
Normal file
12
tests/ui/mir/enum/wrap_ok.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Wrap {
|
||||
A(u32),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _val: Wrap = unsafe { std::mem::transmute::<u32, Wrap>(2) };
|
||||
let _val: Wrap = unsafe { std::mem::transmute::<u32, Wrap>(u32::MAX) };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue