Auto merge of #100759 - fee1-dead-contrib:const_eval_select_real_intrinsic, r=oli-obk,RalfJung
Make `const_eval_select` a real intrinsic This fixes issues where `track_caller` functions do not have nice panic messages anymore when there is a call to the function, and uses the MIR system to replace the call instead of dispatching via lang items. Fixes #100696.
This commit is contained in:
commit
9358d09a55
@ -13,8 +13,7 @@ use rustc_ast as ast;
|
|||||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_index::vec::Idx;
|
use rustc_index::vec::Idx;
|
||||||
use rustc_middle::mir::AssertKind;
|
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
|
||||||
use rustc_middle::mir::{self, SwitchTargets};
|
|
||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||||
use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
|
use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
|
||||||
|
@ -35,21 +35,7 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
|
|||||||
// All `#[rustc_do_not_const_check]` functions should be hooked here.
|
// All `#[rustc_do_not_const_check]` functions should be hooked here.
|
||||||
let def_id = instance.def_id();
|
let def_id = instance.def_id();
|
||||||
|
|
||||||
if Some(def_id) == self.tcx.lang_items().const_eval_select() {
|
if Some(def_id) == self.tcx.lang_items().panic_display()
|
||||||
// redirect to const_eval_select_ct
|
|
||||||
if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() {
|
|
||||||
return Ok(Some(
|
|
||||||
ty::Instance::resolve(
|
|
||||||
*self.tcx,
|
|
||||||
ty::ParamEnv::reveal_all(),
|
|
||||||
const_eval_select,
|
|
||||||
instance.substs,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else if Some(def_id) == self.tcx.lang_items().panic_display()
|
|
||||||
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
|
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
|
||||||
{
|
{
|
||||||
// &str or &&str
|
// &str or &&str
|
||||||
|
@ -269,8 +269,6 @@ language_item_table! {
|
|||||||
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
|
||||||
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
|
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
|
||||||
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
|
||||||
ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4);
|
|
||||||
ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4);
|
|
||||||
|
|
||||||
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable};
|
|||||||
/// `ValTree` does not have this problem with representation, as it only contains integers or
|
/// `ValTree` does not have this problem with representation, as it only contains integers or
|
||||||
/// lists of (nested) `ValTree`.
|
/// lists of (nested) `ValTree`.
|
||||||
pub enum ValTree<'tcx> {
|
pub enum ValTree<'tcx> {
|
||||||
/// ZSTs, integers, `bool`, `char` are represented as scalars.
|
/// integers, `bool`, `char` are represented as scalars.
|
||||||
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
|
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
|
||||||
/// of these types have the same representation.
|
/// of these types have the same representation.
|
||||||
Leaf(ScalarInt),
|
Leaf(ScalarInt),
|
||||||
@ -27,8 +27,11 @@ pub enum ValTree<'tcx> {
|
|||||||
// dont use SliceOrStr for now
|
// dont use SliceOrStr for now
|
||||||
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
|
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
|
||||||
/// listing their fields' values in order.
|
/// listing their fields' values in order.
|
||||||
|
///
|
||||||
/// Enums are represented by storing their discriminant as a field, followed by all
|
/// Enums are represented by storing their discriminant as a field, followed by all
|
||||||
/// the fields of the variant.
|
/// the fields of the variant.
|
||||||
|
///
|
||||||
|
/// ZST types are represented as an empty slice.
|
||||||
Branch(&'tcx [ValTree<'tcx>]),
|
Branch(&'tcx [ValTree<'tcx>]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +263,7 @@ fn layout_of<'tcx>(
|
|||||||
Ok(layout)
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct LayoutCx<'tcx, C> {
|
pub struct LayoutCx<'tcx, C> {
|
||||||
pub tcx: C,
|
pub tcx: C,
|
||||||
pub param_env: ty::ParamEnv<'tcx>,
|
pub param_env: ty::ParamEnv<'tcx>,
|
||||||
@ -3063,6 +3064,93 @@ fn fn_abi_of_instance<'tcx>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle safe Rust thin and fat pointers.
|
||||||
|
pub fn adjust_for_rust_scalar<'tcx>(
|
||||||
|
cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||||
|
attrs: &mut ArgAttributes,
|
||||||
|
scalar: Scalar,
|
||||||
|
layout: TyAndLayout<'tcx>,
|
||||||
|
offset: Size,
|
||||||
|
is_return: bool,
|
||||||
|
) {
|
||||||
|
// Booleans are always a noundef i1 that needs to be zero-extended.
|
||||||
|
if scalar.is_bool() {
|
||||||
|
attrs.ext(ArgExtension::Zext);
|
||||||
|
attrs.set(ArgAttribute::NoUndef);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scalars which have invalid values cannot be undef.
|
||||||
|
if !scalar.is_always_valid(&cx) {
|
||||||
|
attrs.set(ArgAttribute::NoUndef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only pointer types handled below.
|
||||||
|
let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
|
||||||
|
|
||||||
|
if !valid_range.contains(0) {
|
||||||
|
attrs.set(ArgAttribute::NonNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
|
||||||
|
if let Some(kind) = pointee.safe {
|
||||||
|
attrs.pointee_align = Some(pointee.align);
|
||||||
|
|
||||||
|
// `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
|
||||||
|
// for the entire duration of the function as they can be deallocated
|
||||||
|
// at any time. Same for shared mutable references. If LLVM had a
|
||||||
|
// way to say "dereferenceable on entry" we could use it here.
|
||||||
|
attrs.pointee_size = match kind {
|
||||||
|
PointerKind::UniqueBorrowed
|
||||||
|
| PointerKind::UniqueBorrowedPinned
|
||||||
|
| PointerKind::Frozen => pointee.size,
|
||||||
|
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
|
||||||
|
};
|
||||||
|
|
||||||
|
// `Box`, `&T`, and `&mut T` cannot be undef.
|
||||||
|
// Note that this only applies to the value of the pointer itself;
|
||||||
|
// this attribute doesn't make it UB for the pointed-to data to be undef.
|
||||||
|
attrs.set(ArgAttribute::NoUndef);
|
||||||
|
|
||||||
|
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
|
||||||
|
// `noalias` for it. This can be turned off using an unstable flag.
|
||||||
|
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
|
||||||
|
let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true);
|
||||||
|
|
||||||
|
// `&mut` pointer parameters never alias other parameters,
|
||||||
|
// or mutable global data
|
||||||
|
//
|
||||||
|
// `&T` where `T` contains no `UnsafeCell<U>` is immutable,
|
||||||
|
// and can be marked as both `readonly` and `noalias`, as
|
||||||
|
// LLVM's definition of `noalias` is based solely on memory
|
||||||
|
// dependencies rather than pointer equality
|
||||||
|
//
|
||||||
|
// Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
|
||||||
|
// for UniqueBorrowed arguments, so that the codegen backend can decide whether
|
||||||
|
// or not to actually emit the attribute. It can also be controlled with the
|
||||||
|
// `-Zmutable-noalias` debugging option.
|
||||||
|
let no_alias = match kind {
|
||||||
|
PointerKind::SharedMutable
|
||||||
|
| PointerKind::UniqueBorrowed
|
||||||
|
| PointerKind::UniqueBorrowedPinned => false,
|
||||||
|
PointerKind::UniqueOwned => noalias_for_box,
|
||||||
|
PointerKind::Frozen => !is_return,
|
||||||
|
};
|
||||||
|
if no_alias {
|
||||||
|
attrs.set(ArgAttribute::NoAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == PointerKind::Frozen && !is_return {
|
||||||
|
attrs.set(ArgAttribute::ReadOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == PointerKind::UniqueBorrowed && !is_return {
|
||||||
|
attrs.set(ArgAttribute::NoAliasMutRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
||||||
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
|
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
|
||||||
// arguments of this method, into a separate `struct`.
|
// arguments of this method, into a separate `struct`.
|
||||||
@ -3118,91 +3206,6 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
|||||||
use SpecAbi::*;
|
use SpecAbi::*;
|
||||||
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
|
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
|
||||||
|
|
||||||
// Handle safe Rust thin and fat pointers.
|
|
||||||
let adjust_for_rust_scalar = |attrs: &mut ArgAttributes,
|
|
||||||
scalar: Scalar,
|
|
||||||
layout: TyAndLayout<'tcx>,
|
|
||||||
offset: Size,
|
|
||||||
is_return: bool| {
|
|
||||||
// Booleans are always a noundef i1 that needs to be zero-extended.
|
|
||||||
if scalar.is_bool() {
|
|
||||||
attrs.ext(ArgExtension::Zext);
|
|
||||||
attrs.set(ArgAttribute::NoUndef);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scalars which have invalid values cannot be undef.
|
|
||||||
if !scalar.is_always_valid(self) {
|
|
||||||
attrs.set(ArgAttribute::NoUndef);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only pointer types handled below.
|
|
||||||
let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
|
|
||||||
|
|
||||||
if !valid_range.contains(0) {
|
|
||||||
attrs.set(ArgAttribute::NonNull);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pointee) = layout.pointee_info_at(self, offset) {
|
|
||||||
if let Some(kind) = pointee.safe {
|
|
||||||
attrs.pointee_align = Some(pointee.align);
|
|
||||||
|
|
||||||
// `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
|
|
||||||
// for the entire duration of the function as they can be deallocated
|
|
||||||
// at any time. Same for shared mutable references. If LLVM had a
|
|
||||||
// way to say "dereferenceable on entry" we could use it here.
|
|
||||||
attrs.pointee_size = match kind {
|
|
||||||
PointerKind::UniqueBorrowed
|
|
||||||
| PointerKind::UniqueBorrowedPinned
|
|
||||||
| PointerKind::Frozen => pointee.size,
|
|
||||||
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
|
|
||||||
};
|
|
||||||
|
|
||||||
// `Box`, `&T`, and `&mut T` cannot be undef.
|
|
||||||
// Note that this only applies to the value of the pointer itself;
|
|
||||||
// this attribute doesn't make it UB for the pointed-to data to be undef.
|
|
||||||
attrs.set(ArgAttribute::NoUndef);
|
|
||||||
|
|
||||||
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
|
|
||||||
// `noalias` for it. This can be turned off using an unstable flag.
|
|
||||||
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
|
|
||||||
let noalias_for_box =
|
|
||||||
self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true);
|
|
||||||
|
|
||||||
// `&mut` pointer parameters never alias other parameters,
|
|
||||||
// or mutable global data
|
|
||||||
//
|
|
||||||
// `&T` where `T` contains no `UnsafeCell<U>` is immutable,
|
|
||||||
// and can be marked as both `readonly` and `noalias`, as
|
|
||||||
// LLVM's definition of `noalias` is based solely on memory
|
|
||||||
// dependencies rather than pointer equality
|
|
||||||
//
|
|
||||||
// Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
|
|
||||||
// for UniqueBorrowed arguments, so that the codegen backend can decide whether
|
|
||||||
// or not to actually emit the attribute. It can also be controlled with the
|
|
||||||
// `-Zmutable-noalias` debugging option.
|
|
||||||
let no_alias = match kind {
|
|
||||||
PointerKind::SharedMutable
|
|
||||||
| PointerKind::UniqueBorrowed
|
|
||||||
| PointerKind::UniqueBorrowedPinned => false,
|
|
||||||
PointerKind::UniqueOwned => noalias_for_box,
|
|
||||||
PointerKind::Frozen => !is_return,
|
|
||||||
};
|
|
||||||
if no_alias {
|
|
||||||
attrs.set(ArgAttribute::NoAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == PointerKind::Frozen && !is_return {
|
|
||||||
attrs.set(ArgAttribute::ReadOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == PointerKind::UniqueBorrowed && !is_return {
|
|
||||||
attrs.set(ArgAttribute::NoAliasMutRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
|
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
|
||||||
let is_return = arg_idx.is_none();
|
let is_return = arg_idx.is_none();
|
||||||
|
|
||||||
@ -3218,7 +3221,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
|
|||||||
|
|
||||||
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
|
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
|
||||||
let mut attrs = ArgAttributes::new();
|
let mut attrs = ArgAttributes::new();
|
||||||
adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return);
|
adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return);
|
||||||
attrs
|
attrs
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#![feature(trusted_step)]
|
#![feature(trusted_step)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
#![feature(yeet_expr)]
|
#![feature(yeet_expr)]
|
||||||
|
#![feature(if_let_guard)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -27,10 +28,13 @@ use rustc_hir::intravisit::{self, Visitor};
|
|||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
use rustc_middle::mir::visit::Visitor as _;
|
use rustc_middle::mir::visit::Visitor as _;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase,
|
traversal, AnalysisPhase, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand,
|
||||||
|
Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind,
|
||||||
|
TerminatorKind,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
|
||||||
|
use rustc_span::sym;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod pass_manager;
|
mod pass_manager;
|
||||||
@ -140,6 +144,64 @@ pub fn provide(providers: &mut Providers) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remap_mir_for_const_eval_select<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
mut body: Body<'tcx>,
|
||||||
|
context: hir::Constness,
|
||||||
|
) -> Body<'tcx> {
|
||||||
|
for bb in body.basic_blocks.as_mut().iter_mut() {
|
||||||
|
let terminator = bb.terminator.as_mut().expect("invalid terminator");
|
||||||
|
match terminator.kind {
|
||||||
|
TerminatorKind::Call {
|
||||||
|
func: Operand::Constant(box Constant { ref literal, .. }),
|
||||||
|
ref mut args,
|
||||||
|
destination,
|
||||||
|
target,
|
||||||
|
cleanup,
|
||||||
|
fn_span,
|
||||||
|
..
|
||||||
|
} if let ty::FnDef(def_id, _) = *literal.ty().kind()
|
||||||
|
&& tcx.item_name(def_id) == sym::const_eval_select
|
||||||
|
&& tcx.is_intrinsic(def_id) =>
|
||||||
|
{
|
||||||
|
let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap();
|
||||||
|
let ty = tupled_args.ty(&body.local_decls, tcx);
|
||||||
|
let fields = ty.tuple_fields();
|
||||||
|
let num_args = fields.len();
|
||||||
|
let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt };
|
||||||
|
let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args {
|
||||||
|
Operand::Constant(_) => {
|
||||||
|
// there is no good way of extracting a tuple arg from a constant (const generic stuff)
|
||||||
|
// so we just create a temporary and deconstruct that.
|
||||||
|
let local = body.local_decls.push(LocalDecl::new(ty, fn_span));
|
||||||
|
bb.statements.push(Statement {
|
||||||
|
source_info: SourceInfo::outermost(fn_span),
|
||||||
|
kind: StatementKind::Assign(Box::new((local.into(), Rvalue::Use(tupled_args.clone())))),
|
||||||
|
});
|
||||||
|
(Operand::Move, local.into())
|
||||||
|
}
|
||||||
|
Operand::Move(place) => (Operand::Move, place),
|
||||||
|
Operand::Copy(place) => (Operand::Copy, place),
|
||||||
|
};
|
||||||
|
let place_elems = place.projection;
|
||||||
|
let arguments = (0..num_args).map(|x| {
|
||||||
|
let mut place_elems = place_elems.to_vec();
|
||||||
|
place_elems.push(ProjectionElem::Field(x.into(), fields[x]));
|
||||||
|
let projection = tcx.intern_place_elems(&place_elems);
|
||||||
|
let place = Place {
|
||||||
|
local: place.local,
|
||||||
|
projection,
|
||||||
|
};
|
||||||
|
method(place)
|
||||||
|
}).collect();
|
||||||
|
terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, cleanup, from_hir_call: false, fn_span };
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body
|
||||||
|
}
|
||||||
|
|
||||||
fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||||
let def_id = def_id.expect_local();
|
let def_id = def_id.expect_local();
|
||||||
tcx.mir_keys(()).contains(&def_id)
|
tcx.mir_keys(()).contains(&def_id)
|
||||||
@ -325,7 +387,9 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -
|
|||||||
.body_const_context(def.did)
|
.body_const_context(def.did)
|
||||||
.expect("mir_for_ctfe should not be used for runtime functions");
|
.expect("mir_for_ctfe should not be used for runtime functions");
|
||||||
|
|
||||||
let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
|
let body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
|
||||||
|
|
||||||
|
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const);
|
||||||
|
|
||||||
match context {
|
match context {
|
||||||
// Do not const prop functions, either they get executed at runtime or exported to metadata,
|
// Do not const prop functions, either they get executed at runtime or exported to metadata,
|
||||||
@ -558,8 +622,9 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
|
|||||||
Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
|
Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
|
||||||
}
|
}
|
||||||
debug!("about to call mir_drops_elaborated...");
|
debug!("about to call mir_drops_elaborated...");
|
||||||
let mut body =
|
let body =
|
||||||
tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
|
tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
|
||||||
|
let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
|
||||||
debug!("body: {:#?}", body);
|
debug!("body: {:#?}", body);
|
||||||
run_optimization_passes(tcx, &mut body);
|
run_optimization_passes(tcx, &mut body);
|
||||||
|
|
||||||
|
@ -112,12 +112,6 @@
|
|||||||
//! method in operand position, we treat it as a neighbor of the current
|
//! method in operand position, we treat it as a neighbor of the current
|
||||||
//! mono item. Calls are just a special case of that.
|
//! mono item. Calls are just a special case of that.
|
||||||
//!
|
//!
|
||||||
//! #### Closures
|
|
||||||
//! In a way, closures are a simple case. Since every closure object needs to be
|
|
||||||
//! constructed somewhere, we can reliably discover them by observing
|
|
||||||
//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also
|
|
||||||
//! true for closures inlined from other crates.
|
|
||||||
//!
|
|
||||||
//! #### Drop glue
|
//! #### Drop glue
|
||||||
//! Drop glue mono items are introduced by MIR drop-statements. The
|
//! Drop glue mono items are introduced by MIR drop-statements. The
|
||||||
//! generated mono item will again have drop-glue item neighbors if the
|
//! generated mono item will again have drop-glue item neighbors if the
|
||||||
@ -835,7 +829,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
|||||||
mir::TerminatorKind::Call { ref func, .. } => {
|
mir::TerminatorKind::Call { ref func, .. } => {
|
||||||
let callee_ty = func.ty(self.body, tcx);
|
let callee_ty = func.ty(self.body, tcx);
|
||||||
let callee_ty = self.monomorphize(callee_ty);
|
let callee_ty = self.monomorphize(callee_ty);
|
||||||
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output);
|
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
|
||||||
}
|
}
|
||||||
mir::TerminatorKind::Drop { ref place, .. }
|
mir::TerminatorKind::Drop { ref place, .. }
|
||||||
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {
|
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {
|
||||||
|
@ -510,7 +510,6 @@ symbols! {
|
|||||||
const_deallocate,
|
const_deallocate,
|
||||||
const_eval_limit,
|
const_eval_limit,
|
||||||
const_eval_select,
|
const_eval_select,
|
||||||
const_eval_select_ct,
|
|
||||||
const_evaluatable_checked,
|
const_evaluatable_checked,
|
||||||
const_extern_fn,
|
const_extern_fn,
|
||||||
const_fn,
|
const_fn,
|
||||||
|
@ -31,7 +31,7 @@ use rustc_middle::ty::visit::TypeVisitable;
|
|||||||
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
|
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{self, Span};
|
use rustc_span::{self, sym, Span};
|
||||||
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
|
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -224,6 +224,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
let minimum_input_count = expected_input_tys.len();
|
let minimum_input_count = expected_input_tys.len();
|
||||||
let provided_arg_count = provided_args.len();
|
let provided_arg_count = provided_args.len();
|
||||||
|
|
||||||
|
let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
|
||||||
|
self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
|
||||||
|
&& self.tcx.is_intrinsic(def_id)
|
||||||
|
&& self.tcx.item_name(def_id) == sym::const_eval_select);
|
||||||
|
|
||||||
// We introduce a helper function to demand that a given argument satisfy a given input
|
// We introduce a helper function to demand that a given argument satisfy a given input
|
||||||
// This is more complicated than just checking type equality, as arguments could be coerced
|
// This is more complicated than just checking type equality, as arguments could be coerced
|
||||||
// This version writes those types back so further type checking uses the narrowed types
|
// This version writes those types back so further type checking uses the narrowed types
|
||||||
@ -259,6 +264,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
return Compatibility::Incompatible(coerce_error);
|
return Compatibility::Incompatible(coerce_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
|
||||||
|
// the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
|
||||||
|
// in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
|
||||||
|
//
|
||||||
|
// This check is here because there is currently no way to express a trait bound for `FnDef` types only.
|
||||||
|
if is_const_eval_select && (1..=2).contains(&idx) {
|
||||||
|
if let ty::FnDef(def_id, _) = checked_ty.kind() {
|
||||||
|
if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(provided_arg.span, "this argument must be a `const fn`")
|
||||||
|
.help("consult the documentation on `const_eval_select` for more information")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.tcx
|
||||||
|
.sess
|
||||||
|
.struct_span_err(provided_arg.span, "this argument must be a function item")
|
||||||
|
.note(format!("expected a function item, found {checked_ty}"))
|
||||||
|
.help(
|
||||||
|
"consult the documentation on `const_eval_select` for more information",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Check if the formal type is a supertype of the checked one
|
// 3. Check if the formal type is a supertype of the checked one
|
||||||
// and register any such obligations for future type checks
|
// and register any such obligations for future type checks
|
||||||
let supertype_error = self
|
let supertype_error = self
|
||||||
|
@ -54,7 +54,9 @@
|
|||||||
)]
|
)]
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use crate::marker::{Destruct, DiscriminantKind};
|
#[cfg(bootstrap)]
|
||||||
|
use crate::marker::Destruct;
|
||||||
|
use crate::marker::DiscriminantKind;
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
|
|
||||||
// These imports are used for simplifying intra-doc links
|
// These imports are used for simplifying intra-doc links
|
||||||
@ -2085,6 +2087,65 @@ extern "rust-intrinsic" {
|
|||||||
/// `ptr` must point to a vtable.
|
/// `ptr` must point to a vtable.
|
||||||
/// The intrinsic will return the alignment stored in that vtable.
|
/// The intrinsic will return the alignment stored in that vtable.
|
||||||
pub fn vtable_align(ptr: *const ()) -> usize;
|
pub fn vtable_align(ptr: *const ()) -> usize;
|
||||||
|
|
||||||
|
/// Selects which function to call depending on the context.
|
||||||
|
///
|
||||||
|
/// If this function is evaluated at compile-time, then a call to this
|
||||||
|
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
||||||
|
/// replaced with a call to `called_at_rt` otherwise.
|
||||||
|
///
|
||||||
|
/// # Type Requirements
|
||||||
|
///
|
||||||
|
/// The two functions must be both function items. They cannot be function
|
||||||
|
/// pointers or closures. The first function must be a `const fn`.
|
||||||
|
///
|
||||||
|
/// `arg` will be the tupled arguments that will be passed to either one of
|
||||||
|
/// the two functions, therefore, both functions must accept the same type of
|
||||||
|
/// arguments. Both functions must return RET.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The two functions must behave observably equivalent. Safe code in other
|
||||||
|
/// crates may assume that calling a `const fn` at compile-time and at run-time
|
||||||
|
/// produces the same result. A function that produces a different result when
|
||||||
|
/// evaluated at run-time, or has any other observable side-effects, is
|
||||||
|
/// *unsound*.
|
||||||
|
///
|
||||||
|
/// Here is an example of how this could cause a problem:
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(const_eval_select)]
|
||||||
|
/// #![feature(core_intrinsics)]
|
||||||
|
/// use std::hint::unreachable_unchecked;
|
||||||
|
/// use std::intrinsics::const_eval_select;
|
||||||
|
///
|
||||||
|
/// // Crate A
|
||||||
|
/// pub const fn inconsistent() -> i32 {
|
||||||
|
/// fn runtime() -> i32 { 1 }
|
||||||
|
/// const fn compiletime() -> i32 { 2 }
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
// // ⚠ This code violates the required equivalence of `compiletime`
|
||||||
|
/// // and `runtime`.
|
||||||
|
/// const_eval_select((), compiletime, runtime)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Crate B
|
||||||
|
/// const X: i32 = inconsistent();
|
||||||
|
/// let x = inconsistent();
|
||||||
|
/// if x != X { unsafe { unreachable_unchecked(); }}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This code causes Undefined Behavior when being run, since the
|
||||||
|
/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
|
||||||
|
/// which violates the principle that a `const fn` must behave the same at
|
||||||
|
/// compile-time and at run-time. The unsafe code in crate B is fine.
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
#[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
|
||||||
|
pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
|
||||||
|
where
|
||||||
|
G: FnOnce<ARG, Output = RET>,
|
||||||
|
F: FnOnce<ARG, Output = RET>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some functions are defined here because they accidentally got made
|
// Some functions are defined here because they accidentally got made
|
||||||
@ -2095,6 +2156,11 @@ extern "rust-intrinsic" {
|
|||||||
/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
|
/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
|
||||||
/// and only at runtime.
|
/// and only at runtime.
|
||||||
///
|
///
|
||||||
|
/// This macro should be called as `assert_unsafe_precondition!([Generics](name: Type) => Expression)`
|
||||||
|
/// where the names specified will be moved into the macro as captured variables, and defines an item
|
||||||
|
/// to call `const_eval_select` on. The tokens inside the square brackets are used to denote generics
|
||||||
|
/// for the function declaractions and can be omitted if there is no generics.
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Invoking this macro is only sound if the following code is already UB when the passed
|
/// Invoking this macro is only sound if the following code is already UB when the passed
|
||||||
@ -2109,18 +2175,21 @@ extern "rust-intrinsic" {
|
|||||||
/// the occasional mistake, and this check should help them figure things out.
|
/// the occasional mistake, and this check should help them figure things out.
|
||||||
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
|
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
|
||||||
macro_rules! assert_unsafe_precondition {
|
macro_rules! assert_unsafe_precondition {
|
||||||
($e:expr) => {
|
($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
// Use a closure so that we can capture arbitrary expressions from the invocation
|
// allow non_snake_case to allow capturing const generics
|
||||||
let runtime = || {
|
#[allow(non_snake_case)]
|
||||||
|
#[inline(always)]
|
||||||
|
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
|
||||||
if !$e {
|
if !$e {
|
||||||
// abort instead of panicking to reduce impact on code size
|
// abort instead of panicking to reduce impact on code size
|
||||||
::core::intrinsics::abort();
|
::core::intrinsics::abort();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const fn comptime() {}
|
#[allow(non_snake_case)]
|
||||||
|
const fn comptime$(<$($tt)*>)?($(_:$ty),*) {}
|
||||||
|
|
||||||
::core::intrinsics::const_eval_select((), comptime, runtime);
|
::core::intrinsics::const_eval_select(($($i,)*), comptime, runtime);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2243,7 +2312,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
|
|||||||
// SAFETY: the safety contract for `copy_nonoverlapping` must be
|
// SAFETY: the safety contract for `copy_nonoverlapping` must be
|
||||||
// upheld by the caller.
|
// upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(
|
assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) =>
|
||||||
is_aligned_and_not_null(src)
|
is_aligned_and_not_null(src)
|
||||||
&& is_aligned_and_not_null(dst)
|
&& is_aligned_and_not_null(dst)
|
||||||
&& is_nonoverlapping(src, dst, count)
|
&& is_nonoverlapping(src, dst, count)
|
||||||
@ -2329,7 +2398,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
|||||||
|
|
||||||
// SAFETY: the safety contract for `copy` must be upheld by the caller.
|
// SAFETY: the safety contract for `copy` must be upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!([T](src: *const T, dst: *mut T) =>
|
||||||
|
is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
|
||||||
copy(src, dst, count)
|
copy(src, dst, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2397,63 +2467,12 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
|
|||||||
|
|
||||||
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
|
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
||||||
write_bytes(dst, val, count)
|
write_bytes(dst, val, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selects which function to call depending on the context.
|
#[cfg(bootstrap)]
|
||||||
///
|
|
||||||
/// If this function is evaluated at compile-time, then a call to this
|
|
||||||
/// intrinsic will be replaced with a call to `called_in_const`. It gets
|
|
||||||
/// replaced with a call to `called_at_rt` otherwise.
|
|
||||||
///
|
|
||||||
/// # Type Requirements
|
|
||||||
///
|
|
||||||
/// The two functions must be both function items. They cannot be function
|
|
||||||
/// pointers or closures.
|
|
||||||
///
|
|
||||||
/// `arg` will be the arguments that will be passed to either one of the
|
|
||||||
/// two functions, therefore, both functions must accept the same type of
|
|
||||||
/// arguments. Both functions must return RET.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The two functions must behave observably equivalent. Safe code in other
|
|
||||||
/// crates may assume that calling a `const fn` at compile-time and at run-time
|
|
||||||
/// produces the same result. A function that produces a different result when
|
|
||||||
/// evaluated at run-time, or has any other observable side-effects, is
|
|
||||||
/// *unsound*.
|
|
||||||
///
|
|
||||||
/// Here is an example of how this could cause a problem:
|
|
||||||
/// ```no_run
|
|
||||||
/// #![feature(const_eval_select)]
|
|
||||||
/// #![feature(core_intrinsics)]
|
|
||||||
/// use std::hint::unreachable_unchecked;
|
|
||||||
/// use std::intrinsics::const_eval_select;
|
|
||||||
///
|
|
||||||
/// // Crate A
|
|
||||||
/// pub const fn inconsistent() -> i32 {
|
|
||||||
/// fn runtime() -> i32 { 1 }
|
|
||||||
/// const fn compiletime() -> i32 { 2 }
|
|
||||||
///
|
|
||||||
/// unsafe {
|
|
||||||
// // ⚠ This code violates the required equivalence of `compiletime`
|
|
||||||
/// // and `runtime`.
|
|
||||||
/// const_eval_select((), compiletime, runtime)
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Crate B
|
|
||||||
/// const X: i32 = inconsistent();
|
|
||||||
/// let x = inconsistent();
|
|
||||||
/// if x != X { unsafe { unreachable_unchecked(); }}
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// This code causes Undefined Behavior when being run, since the
|
|
||||||
/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
|
|
||||||
/// which violates the principle that a `const fn` must behave the same at
|
|
||||||
/// compile-time and at run-time. The unsafe code in crate B is fine.
|
|
||||||
#[unstable(
|
#[unstable(
|
||||||
feature = "const_eval_select",
|
feature = "const_eval_select",
|
||||||
issue = "none",
|
issue = "none",
|
||||||
@ -2475,6 +2494,7 @@ where
|
|||||||
called_at_rt.call_once(arg)
|
called_at_rt.call_once(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(bootstrap)]
|
||||||
#[unstable(
|
#[unstable(
|
||||||
feature = "const_eval_select",
|
feature = "const_eval_select",
|
||||||
issue = "none",
|
issue = "none",
|
||||||
|
@ -28,7 +28,7 @@ impl ValidAlign {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) const unsafe fn new_unchecked(align: usize) -> Self {
|
pub(crate) const unsafe fn new_unchecked(align: usize) -> Self {
|
||||||
// SAFETY: Precondition passed to the caller.
|
// SAFETY: Precondition passed to the caller.
|
||||||
unsafe { assert_unsafe_precondition!(align.is_power_of_two()) };
|
unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) };
|
||||||
|
|
||||||
// SAFETY: By precondition, this must be a power of two, and
|
// SAFETY: By precondition, this must be a power of two, and
|
||||||
// our variants encompass all possible powers of two.
|
// our variants encompass all possible powers of two.
|
||||||
|
@ -1033,10 +1033,14 @@ impl f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SAFETY: `u32` is a plain old datatype so we can always... uh...
|
|
||||||
// ...look, just pretend you forgot what you just read.
|
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
|
||||||
// Stability concerns.
|
fn rt_f32_to_u32(x: f32) -> u32 {
|
||||||
let rt_f32_to_u32 = |rt| unsafe { mem::transmute::<f32, u32>(rt) };
|
// SAFETY: `u32` is a plain old datatype so we can always... uh...
|
||||||
|
// ...look, just pretend you forgot what you just read.
|
||||||
|
// Stability concerns.
|
||||||
|
unsafe { mem::transmute(x) }
|
||||||
|
}
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
||||||
}
|
}
|
||||||
@ -1121,10 +1125,14 @@ impl f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SAFETY: `u32` is a plain old datatype so we can always... uh...
|
|
||||||
// ...look, just pretend you forgot what you just read.
|
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
|
||||||
// Stability concerns.
|
fn rt_u32_to_f32(x: u32) -> f32 {
|
||||||
let rt_u32_to_f32 = |rt| unsafe { mem::transmute::<u32, f32>(rt) };
|
// SAFETY: `u32` is a plain old datatype so we can always... uh...
|
||||||
|
// ...look, just pretend you forgot what you just read.
|
||||||
|
// Stability concerns.
|
||||||
|
unsafe { mem::transmute(x) }
|
||||||
|
}
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
|
unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
|
||||||
}
|
}
|
||||||
|
@ -1026,10 +1026,14 @@ impl f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SAFETY: `u64` is a plain old datatype so we can always... uh...
|
|
||||||
// ...look, just pretend you forgot what you just read.
|
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
|
||||||
// Stability concerns.
|
fn rt_f64_to_u64(rt: f64) -> u64 {
|
||||||
let rt_f64_to_u64 = |rt| unsafe { mem::transmute::<f64, u64>(rt) };
|
// SAFETY: `u64` is a plain old datatype so we can always... uh...
|
||||||
|
// ...look, just pretend you forgot what you just read.
|
||||||
|
// Stability concerns.
|
||||||
|
unsafe { mem::transmute::<f64, u64>(rt) }
|
||||||
|
}
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
||||||
}
|
}
|
||||||
@ -1119,10 +1123,14 @@ impl f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SAFETY: `u64` is a plain old datatype so we can always... uh...
|
|
||||||
// ...look, just pretend you forgot what you just read.
|
#[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
|
||||||
// Stability concerns.
|
fn rt_u64_to_f64(rt: u64) -> f64 {
|
||||||
let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) };
|
// SAFETY: `u64` is a plain old datatype so we can always... uh...
|
||||||
|
// ...look, just pretend you forgot what you just read.
|
||||||
|
// Stability concerns.
|
||||||
|
unsafe { mem::transmute::<u64, f64>(rt) }
|
||||||
|
}
|
||||||
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
// SAFETY: We use internal implementations that either always work or fail at compile time.
|
||||||
unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
|
unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ macro_rules! nonzero_integers {
|
|||||||
pub const unsafe fn new_unchecked(n: $Int) -> Self {
|
pub const unsafe fn new_unchecked(n: $Int) -> Self {
|
||||||
// SAFETY: this is guaranteed to be safe by the caller.
|
// SAFETY: this is guaranteed to be safe by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::assert_unsafe_precondition!(n != 0);
|
core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0);
|
||||||
Self(n)
|
Self(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -755,9 +755,12 @@ impl<T: ?Sized> *const T {
|
|||||||
where
|
where
|
||||||
T: Sized,
|
T: Sized,
|
||||||
{
|
{
|
||||||
|
let this = self;
|
||||||
// SAFETY: The comparison has no side-effects, and the intrinsic
|
// SAFETY: The comparison has no side-effects, and the intrinsic
|
||||||
// does this check internally in the CTFE implementation.
|
// does this check internally in the CTFE implementation.
|
||||||
unsafe { assert_unsafe_precondition!(self >= origin) };
|
unsafe {
|
||||||
|
assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin)
|
||||||
|
};
|
||||||
|
|
||||||
let pointee_size = mem::size_of::<T>();
|
let pointee_size = mem::size_of::<T>();
|
||||||
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
|
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
|
||||||
|
@ -886,7 +886,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
|||||||
// SAFETY: the caller must guarantee that `x` and `y` are
|
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||||
// valid for writes and properly aligned.
|
// valid for writes and properly aligned.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(
|
assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) =>
|
||||||
is_aligned_and_not_null(x)
|
is_aligned_and_not_null(x)
|
||||||
&& is_aligned_and_not_null(y)
|
&& is_aligned_and_not_null(y)
|
||||||
&& is_nonoverlapping(x, y, count)
|
&& is_nonoverlapping(x, y, count)
|
||||||
@ -983,7 +983,7 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
|
|||||||
// and cannot overlap `src` since `dst` must point to a distinct
|
// and cannot overlap `src` since `dst` must point to a distinct
|
||||||
// allocated object.
|
// allocated object.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
||||||
mem::swap(&mut *dst, &mut src); // cannot overlap
|
mem::swap(&mut *dst, &mut src); // cannot overlap
|
||||||
}
|
}
|
||||||
src
|
src
|
||||||
@ -1470,7 +1470,7 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
|||||||
pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||||
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(is_aligned_and_not_null(src));
|
assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
|
||||||
intrinsics::volatile_load(src)
|
intrinsics::volatile_load(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1541,7 +1541,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
|||||||
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||||
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
||||||
intrinsics::volatile_store(dst, src);
|
intrinsics::volatile_store(dst, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,12 @@ const fn slice_start_index_len_fail(index: usize, len: usize) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME const-hack
|
// FIXME const-hack
|
||||||
|
#[track_caller]
|
||||||
fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! {
|
fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! {
|
||||||
panic!("range start index {index} out of range for slice of length {len}");
|
panic!("range start index {index} out of range for slice of length {len}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
|
const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
|
||||||
panic!("slice start index is out of range for slice");
|
panic!("slice start index is out of range for slice");
|
||||||
}
|
}
|
||||||
@ -69,10 +71,12 @@ const fn slice_end_index_len_fail(index: usize, len: usize) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME const-hack
|
// FIXME const-hack
|
||||||
|
#[track_caller]
|
||||||
fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! {
|
fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! {
|
||||||
panic!("range end index {index} out of range for slice of length {len}");
|
panic!("range end index {index} out of range for slice of length {len}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
|
const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
|
||||||
panic!("slice end index is out of range for slice");
|
panic!("slice end index is out of range for slice");
|
||||||
}
|
}
|
||||||
@ -88,10 +92,12 @@ const fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME const-hack
|
// FIXME const-hack
|
||||||
|
#[track_caller]
|
||||||
fn slice_index_order_fail_rt(index: usize, end: usize) -> ! {
|
fn slice_index_order_fail_rt(index: usize, end: usize) -> ! {
|
||||||
panic!("slice index starts at {index} but ends at {end}");
|
panic!("slice index starts at {index} but ends at {end}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! {
|
const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! {
|
||||||
panic!("slice index start is larger than end");
|
panic!("slice index start is larger than end");
|
||||||
}
|
}
|
||||||
@ -217,21 +223,23 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
|
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
|
||||||
|
let this = self;
|
||||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||||
// so the call to `add` is safe.
|
// so the call to `add` is safe.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(self < slice.len());
|
assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len());
|
||||||
slice.as_ptr().add(self)
|
slice.as_ptr().add(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
|
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
|
||||||
|
let this = self;
|
||||||
// SAFETY: see comments for `get_unchecked` above.
|
// SAFETY: see comments for `get_unchecked` above.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(self < slice.len());
|
assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len());
|
||||||
slice.as_mut_ptr().add(self)
|
slice.as_mut_ptr().add(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,22 +284,26 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
|
||||||
|
let this = ops::Range { start: self.start, end: self.end };
|
||||||
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
// SAFETY: the caller guarantees that `slice` is not dangling, so it
|
||||||
// cannot be longer than `isize::MAX`. They also guarantee that
|
// cannot be longer than `isize::MAX`. They also guarantee that
|
||||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||||
// so the call to `add` is safe.
|
// so the call to `add` is safe.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len());
|
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) =>
|
||||||
|
this.end >= this.start && this.end <= slice.len());
|
||||||
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
|
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
|
||||||
|
let this = ops::Range { start: self.start, end: self.end };
|
||||||
// SAFETY: see comments for `get_unchecked` above.
|
// SAFETY: see comments for `get_unchecked` above.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len());
|
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) =>
|
||||||
|
this.end >= this.start && this.end <= slice.len());
|
||||||
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
|
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -656,10 +656,11 @@ impl<T> [T] {
|
|||||||
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
|
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
|
||||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||||
pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
|
pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
|
||||||
let ptr = self.as_mut_ptr();
|
let this = self;
|
||||||
|
let ptr = this.as_mut_ptr();
|
||||||
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
|
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(a < self.len() && b < self.len());
|
assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len());
|
||||||
ptr::swap(ptr.add(a), ptr.add(b));
|
ptr::swap(ptr.add(a), ptr.add(b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -972,9 +973,10 @@ impl<T> [T] {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
|
pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
|
||||||
|
let this = self;
|
||||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||||
let new_len = unsafe {
|
let new_len = unsafe {
|
||||||
assert_unsafe_precondition!(N != 0 && self.len() % N == 0);
|
assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
|
||||||
exact_div(self.len(), N)
|
exact_div(self.len(), N)
|
||||||
};
|
};
|
||||||
// SAFETY: We cast a slice of `new_len * N` elements into
|
// SAFETY: We cast a slice of `new_len * N` elements into
|
||||||
@ -1111,10 +1113,11 @@ impl<T> [T] {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
|
pub unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
|
||||||
|
let this = &*self;
|
||||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||||
let new_len = unsafe {
|
let new_len = unsafe {
|
||||||
assert_unsafe_precondition!(N != 0 && self.len() % N == 0);
|
assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
|
||||||
exact_div(self.len(), N)
|
exact_div(this.len(), N)
|
||||||
};
|
};
|
||||||
// SAFETY: We cast a slice of `new_len * N` elements into
|
// SAFETY: We cast a slice of `new_len * N` elements into
|
||||||
// a slice of `new_len` many `N` elements chunks.
|
// a slice of `new_len` many `N` elements chunks.
|
||||||
@ -1687,7 +1690,7 @@ impl<T> [T] {
|
|||||||
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
|
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
|
||||||
// is fine.
|
// is fine.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(mid <= len);
|
assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len);
|
||||||
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
|
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ use crate::ptr;
|
|||||||
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
|
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
|
||||||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
|
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(
|
assert_unsafe_precondition!([T](data: *const T, len: usize) =>
|
||||||
is_aligned_and_not_null(data)
|
is_aligned_and_not_null(data)
|
||||||
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
|
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
|
||||||
);
|
);
|
||||||
@ -134,7 +134,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
|
|||||||
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
|
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
|
||||||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
|
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!(
|
assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
|
||||||
is_aligned_and_not_null(data)
|
is_aligned_and_not_null(data)
|
||||||
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
|
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
|
||||||
);
|
);
|
||||||
|
@ -91,10 +91,12 @@ const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! {
|
const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! {
|
||||||
panic!("failed to slice string");
|
panic!("failed to slice string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
|
fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
|
||||||
const MAX_DISPLAY_LENGTH: usize = 256;
|
const MAX_DISPLAY_LENGTH: usize = 256;
|
||||||
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
|
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
|
||||||
|
@ -10,16 +10,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a
|
|||||||
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
||||||
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
|
||||||
| ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | called_in_const.call_once(arg)
|
|
||||||
| ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $DIR/const-float-bits-reject-conv.rs:27:30
|
::: $DIR/const-float-bits-reject-conv.rs:27:30
|
||||||
|
|
|
|
||||||
LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
|
LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
|
||||||
@ -39,16 +29,6 @@ LL | panic!("const-eval error: cannot use f32::to_bits on a
|
|||||||
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
|
||||||
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
|
||||||
| ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | called_in_const.call_once(arg)
|
|
||||||
| ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $DIR/const-float-bits-reject-conv.rs:28:30
|
::: $DIR/const-float-bits-reject-conv.rs:28:30
|
||||||
|
|
|
|
||||||
LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
|
LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
|
||||||
@ -117,16 +97,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a
|
|||||||
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
||||||
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
|
||||||
| ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | called_in_const.call_once(arg)
|
|
||||||
| ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $DIR/const-float-bits-reject-conv.rs:54:30
|
::: $DIR/const-float-bits-reject-conv.rs:54:30
|
||||||
|
|
|
|
||||||
LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
|
LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
|
||||||
@ -146,16 +116,6 @@ LL | panic!("const-eval error: cannot use f64::to_bits on a
|
|||||||
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
|
||||||
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
||||||
|
|
|
|
||||||
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
|
||||||
| ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | called_in_const.call_once(arg)
|
|
||||||
| ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
||||||
::: $DIR/const-float-bits-reject-conv.rs:55:30
|
::: $DIR/const-float-bits-reject-conv.rs:55:30
|
||||||
|
|
|
|
||||||
LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
|
LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
// See issue #100696.
|
||||||
|
// run-fail
|
||||||
|
// check-run-results
|
||||||
|
fn main() {
|
||||||
|
&""[1..];
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
thread 'main' panicked at 'byte index 1 is out of bounds of ``', $DIR/const-eval-select-backtrace-std.rs:5:6
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
18
src/test/ui/intrinsics/const-eval-select-backtrace.rs
Normal file
18
src/test/ui/intrinsics/const-eval-select-backtrace.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#![feature(core_intrinsics)]
|
||||||
|
// See issue #100696.
|
||||||
|
// run-fail
|
||||||
|
// check-run-results
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn uhoh() {
|
||||||
|
panic!("Aaah!")
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn c() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// safety: this is unsound and just used to test
|
||||||
|
unsafe {
|
||||||
|
std::intrinsics::const_eval_select((), c, uhoh);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
thread 'main' panicked at 'Aaah!', $DIR/const-eval-select-backtrace.rs:16:9
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
@ -5,10 +5,13 @@ use std::intrinsics::const_eval_select;
|
|||||||
|
|
||||||
const fn not_fn_items() {
|
const fn not_fn_items() {
|
||||||
const_eval_select((), || {}, || {});
|
const_eval_select((), || {}, || {});
|
||||||
//~^ ERROR the trait bound
|
//~^ ERROR this argument must be a function item
|
||||||
|
//~| ERROR this argument must be a function item
|
||||||
const_eval_select((), 42, 0xDEADBEEF);
|
const_eval_select((), 42, 0xDEADBEEF);
|
||||||
//~^ ERROR the trait bound
|
//~^ ERROR expected a `FnOnce<()>` closure
|
||||||
//~| ERROR expected a `FnOnce<()>` closure
|
//~| ERROR expected a `FnOnce<()>` closure
|
||||||
|
//~| ERROR this argument must be a function item
|
||||||
|
//~| ERROR this argument must be a function item
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn foo(n: i32) -> i32 {
|
const fn foo(n: i32) -> i32 {
|
||||||
@ -35,4 +38,9 @@ const fn args_ty_mismatch() {
|
|||||||
//~^ ERROR type mismatch
|
//~^ ERROR type mismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn non_const_fn() {
|
||||||
|
const_eval_select((1,), bar, bar);
|
||||||
|
//~^ ERROR this argument must be a `const fn`
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,42 +1,57 @@
|
|||||||
error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]: FnOnce<()>` is not satisfied
|
error: this argument must be a function item
|
||||||
--> $DIR/const-eval-select-bad.rs:7:27
|
|
||||||
|
|
|
||||||
LL | const_eval_select((), || {}, || {});
|
|
||||||
| ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `~const FnOnce<()>` is not implemented for closure `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`
|
|
||||||
note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`, but that implementation is not `const`
|
|
||||||
--> $DIR/const-eval-select-bad.rs:7:27
|
--> $DIR/const-eval-select-bad.rs:7:27
|
||||||
|
|
|
|
||||||
LL | const_eval_select((), || {}, || {});
|
LL | const_eval_select((), || {}, || {});
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
= note: wrap the `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` in a closure with no arguments: `|| { /* code */ }`
|
|
||||||
note: required by a bound in `const_eval_select`
|
|
||||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
|
||||||
|
|
|
|
||||||
LL | F: ~const FnOnce<ARG, Output = RET>,
|
= note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
= help: consult the documentation on `const_eval_select` for more information
|
||||||
|
|
||||||
error[E0277]: the trait bound `{integer}: FnOnce<()>` is not satisfied
|
error: this argument must be a function item
|
||||||
--> $DIR/const-eval-select-bad.rs:9:27
|
--> $DIR/const-eval-select-bad.rs:7:34
|
||||||
|
|
|
||||||
|
LL | const_eval_select((), || {}, || {});
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:34: 7:36]
|
||||||
|
= help: consult the documentation on `const_eval_select` for more information
|
||||||
|
|
||||||
|
error: this argument must be a function item
|
||||||
|
--> $DIR/const-eval-select-bad.rs:10:27
|
||||||
|
|
|
||||||
|
LL | const_eval_select((), 42, 0xDEADBEEF);
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
= note: expected a function item, found {integer}
|
||||||
|
= help: consult the documentation on `const_eval_select` for more information
|
||||||
|
|
||||||
|
error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
|
||||||
|
--> $DIR/const-eval-select-bad.rs:10:27
|
||||||
|
|
|
|
||||||
LL | const_eval_select((), 42, 0xDEADBEEF);
|
LL | const_eval_select((), 42, 0xDEADBEEF);
|
||||||
| ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}`
|
| ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}`
|
||||||
| |
|
| |
|
||||||
| required by a bound introduced by this call
|
| required by a bound introduced by this call
|
||||||
|
|
|
|
||||||
= help: the trait `~const FnOnce<()>` is not implemented for `{integer}`
|
= help: the trait `FnOnce<()>` is not implemented for `{integer}`
|
||||||
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
|
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
|
||||||
note: required by a bound in `const_eval_select`
|
note: required by a bound in `const_eval_select`
|
||||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | F: ~const FnOnce<ARG, Output = RET>,
|
LL | F: FnOnce<ARG, Output = RET>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
||||||
|
|
||||||
|
error: this argument must be a function item
|
||||||
|
--> $DIR/const-eval-select-bad.rs:10:31
|
||||||
|
|
|
||||||
|
LL | const_eval_select((), 42, 0xDEADBEEF);
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: expected a function item, found {integer}
|
||||||
|
= help: consult the documentation on `const_eval_select` for more information
|
||||||
|
|
||||||
error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
|
error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
|
||||||
--> $DIR/const-eval-select-bad.rs:9:31
|
--> $DIR/const-eval-select-bad.rs:10:31
|
||||||
|
|
|
|
||||||
LL | const_eval_select((), 42, 0xDEADBEEF);
|
LL | const_eval_select((), 42, 0xDEADBEEF);
|
||||||
| ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}`
|
| ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}`
|
||||||
@ -48,11 +63,11 @@ LL | const_eval_select((), 42, 0xDEADBEEF);
|
|||||||
note: required by a bound in `const_eval_select`
|
note: required by a bound in `const_eval_select`
|
||||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | G: FnOnce<ARG, Output = RET> + ~const Destruct,
|
LL | G: FnOnce<ARG, Output = RET>,
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
||||||
|
|
||||||
error[E0271]: expected `fn(i32) -> bool {bar}` to be a fn item that returns `i32`, but it returns `bool`
|
error[E0271]: expected `fn(i32) -> bool {bar}` to be a fn item that returns `i32`, but it returns `bool`
|
||||||
--> $DIR/const-eval-select-bad.rs:29:34
|
--> $DIR/const-eval-select-bad.rs:32:34
|
||||||
|
|
|
|
||||||
LL | const_eval_select((1,), foo, bar);
|
LL | const_eval_select((1,), foo, bar);
|
||||||
| ----------------- ^^^ expected `i32`, found `bool`
|
| ----------------- ^^^ expected `i32`, found `bool`
|
||||||
@ -62,11 +77,11 @@ LL | const_eval_select((1,), foo, bar);
|
|||||||
note: required by a bound in `const_eval_select`
|
note: required by a bound in `const_eval_select`
|
||||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | G: FnOnce<ARG, Output = RET> + ~const Destruct,
|
LL | G: FnOnce<ARG, Output = RET>,
|
||||||
| ^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
| ^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
||||||
|
|
||||||
error[E0631]: type mismatch in function arguments
|
error[E0631]: type mismatch in function arguments
|
||||||
--> $DIR/const-eval-select-bad.rs:34:32
|
--> $DIR/const-eval-select-bad.rs:37:32
|
||||||
|
|
|
|
||||||
LL | const fn foo(n: i32) -> i32 {
|
LL | const fn foo(n: i32) -> i32 {
|
||||||
| --------------------------- found signature defined here
|
| --------------------------- found signature defined here
|
||||||
@ -81,10 +96,18 @@ LL | const_eval_select((true,), foo, baz);
|
|||||||
note: required by a bound in `const_eval_select`
|
note: required by a bound in `const_eval_select`
|
||||||
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
||||||
|
|
|
|
||||||
LL | F: ~const FnOnce<ARG, Output = RET>,
|
LL | F: FnOnce<ARG, Output = RET>;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error: this argument must be a `const fn`
|
||||||
|
--> $DIR/const-eval-select-bad.rs:42:29
|
||||||
|
|
|
||||||
|
LL | const_eval_select((1,), bar, bar);
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: consult the documentation on `const_eval_select` for more information
|
||||||
|
|
||||||
|
error: aborting due to 9 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0271, E0277, E0631.
|
Some errors have detailed explanations: E0271, E0277, E0631.
|
||||||
For more information about an error, try `rustc --explain E0271`.
|
For more information about an error, try `rustc --explain E0271`.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user