Auto merge of #99735 - JohnTitor:rollup-d93jyr2, r=JohnTitor
Rollup of 9 pull requests Successful merges: - #92390 (Constify a few `(Partial)Ord` impls) - #97077 (Simplify some code that depend on Deref) - #98710 (correct the output of a `capacity` method example) - #99084 (clarify how write_bytes can lead to UB due to invalid values) - #99178 (Lighten up const_prop_lint, reusing const_prop) - #99673 (don't ICE on invalid dyn calls) - #99703 (Expose size_hint() for TokenStream's iterator) - #99709 (`Inherited` always has `TypeckResults` available) - #99713 (Fix sidebar background) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
a86705942c
15 changed files with 157 additions and 471 deletions
|
@ -435,18 +435,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
LocalRef::Place(place) => place,
|
||||
LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx),
|
||||
LocalRef::Operand(..) => {
|
||||
if let Some(elem) = place_ref
|
||||
.projection
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|elem| matches!(elem.1, mir::ProjectionElem::Deref))
|
||||
{
|
||||
base = elem.0 + 1;
|
||||
if place_ref.has_deref() {
|
||||
base = 1;
|
||||
let cg_base = self.codegen_consume(
|
||||
bx,
|
||||
mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref },
|
||||
mir::PlaceRef { projection: &place_ref.projection[..0], ..place_ref },
|
||||
);
|
||||
|
||||
cg_base.deref(bx.cx())
|
||||
} else {
|
||||
bug!("using operand local {:?} as place", place_ref);
|
||||
|
|
|
@ -571,8 +571,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
// Now determine the actual method to call. We can do that in two different ways and
|
||||
// compare them to ensure everything fits.
|
||||
let ty::VtblEntry::Method(fn_inst) = self.get_vtable_entries(vptr)?[idx] else {
|
||||
span_bug!(self.cur_span(), "dyn call index points at something that is not a method")
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
|
||||
throw_ub_format!("`dyn` call trying to call something that is not a method")
|
||||
};
|
||||
if cfg!(debug_assertions) {
|
||||
let tcx = *self.tcx;
|
||||
|
|
|
@ -1461,6 +1461,14 @@ impl<'tcx> Place<'tcx> {
|
|||
self.projection.iter().any(|elem| elem.is_indirect())
|
||||
}
|
||||
|
||||
/// If MirPhase >= Derefered and if projection contains Deref,
|
||||
/// It's guaranteed to be in the first place
|
||||
pub fn has_deref(&self) -> bool {
|
||||
// To make sure this is not accidently used in wrong mir phase
|
||||
debug_assert!(!self.projection[1..].contains(&PlaceElem::Deref));
|
||||
self.projection.first() == Some(&PlaceElem::Deref)
|
||||
}
|
||||
|
||||
/// Finds the innermost `Local` from this `Place`, *if* it is either a local itself or
|
||||
/// a single deref of a local.
|
||||
#[inline(always)]
|
||||
|
@ -1533,6 +1541,12 @@ impl<'tcx> PlaceRef<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// If MirPhase >= Derefered and if projection contains Deref,
|
||||
/// It's guaranteed to be in the first place
|
||||
pub fn has_deref(&self) -> bool {
|
||||
self.projection.first() == Some(&PlaceElem::Deref)
|
||||
}
|
||||
|
||||
/// If this place represents a local variable like `_X` with no
|
||||
/// projections, return `Some(_X)`.
|
||||
#[inline]
|
||||
|
|
|
@ -15,22 +15,9 @@ pub struct AddRetag;
|
|||
/// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
|
||||
/// copies. Data races are UB.)
|
||||
fn is_stable(place: PlaceRef<'_>) -> bool {
|
||||
place.projection.iter().all(|elem| {
|
||||
match elem {
|
||||
// Which place this evaluates to can change with any memory write,
|
||||
// so cannot assume this to be stable.
|
||||
ProjectionElem::Deref => false,
|
||||
// Array indices are interesting, but MIR building generates a *fresh*
|
||||
// temporary for every array access, so the index cannot be changed as
|
||||
// a side-effect.
|
||||
ProjectionElem::Index { .. } |
|
||||
// The rest is completely boring, they just offset by a constant.
|
||||
ProjectionElem::Field { .. } |
|
||||
ProjectionElem::ConstantIndex { .. } |
|
||||
ProjectionElem::Subslice { .. } |
|
||||
ProjectionElem::Downcast { .. } => true,
|
||||
}
|
||||
})
|
||||
// Which place this evaluates to can change with any memory write,
|
||||
// so cannot assume deref to be stable.
|
||||
!place.has_deref()
|
||||
}
|
||||
|
||||
/// Determine whether this type may contain a reference (or box), and thus needs retagging.
|
||||
|
@ -91,11 +78,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
|
|||
};
|
||||
let place_base_raw = |place: &Place<'tcx>| {
|
||||
// If this is a `Deref`, get the type of what we are deref'ing.
|
||||
let deref_base =
|
||||
place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref));
|
||||
if let Some(deref_base) = deref_base {
|
||||
let base_proj = &place.projection[..deref_base];
|
||||
let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty;
|
||||
if place.has_deref() {
|
||||
let ty = &local_decls[place.local].ty;
|
||||
ty.is_unsafe_ptr()
|
||||
} else {
|
||||
// Not a deref, and thus not raw.
|
||||
|
|
|
@ -155,18 +155,18 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
|
|||
}
|
||||
}
|
||||
|
||||
struct ConstPropMachine<'mir, 'tcx> {
|
||||
pub struct ConstPropMachine<'mir, 'tcx> {
|
||||
/// The virtual call stack.
|
||||
stack: Vec<Frame<'mir, 'tcx>>,
|
||||
/// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
|
||||
written_only_inside_own_block_locals: FxHashSet<Local>,
|
||||
pub written_only_inside_own_block_locals: FxHashSet<Local>,
|
||||
/// Locals that need to be cleared after every block terminates.
|
||||
only_propagate_inside_block_locals: BitSet<Local>,
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
pub only_propagate_inside_block_locals: BitSet<Local>,
|
||||
pub can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
}
|
||||
|
||||
impl ConstPropMachine<'_, '_> {
|
||||
fn new(
|
||||
pub fn new(
|
||||
only_propagate_inside_block_locals: BitSet<Local>,
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
) -> Self {
|
||||
|
@ -816,7 +816,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
|
||||
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum ConstPropMode {
|
||||
pub enum ConstPropMode {
|
||||
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
|
||||
FullConstProp,
|
||||
/// The `Local` can only be propagated into and from its own block.
|
||||
|
@ -828,7 +828,7 @@ enum ConstPropMode {
|
|||
NoPropagation,
|
||||
}
|
||||
|
||||
struct CanConstProp {
|
||||
pub struct CanConstProp {
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
// False at the beginning. Once set, no more assignments are allowed to that local.
|
||||
found_assignment: BitSet<Local>,
|
||||
|
@ -838,7 +838,7 @@ struct CanConstProp {
|
|||
|
||||
impl CanConstProp {
|
||||
/// Returns true if `local` can be propagated
|
||||
fn check<'tcx>(
|
||||
pub fn check<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
//! Propagates constants for early reporting of statically known
|
||||
//! assertion failures
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use crate::const_prop::CanConstProp;
|
||||
use crate::const_prop::ConstPropMachine;
|
||||
use crate::const_prop::ConstPropMode;
|
||||
use crate::MirLint;
|
||||
use rustc_const_eval::const_eval::ConstEvalErr;
|
||||
use rustc_const_eval::interpret::{
|
||||
self, InterpCx, InterpResult, LocalState, LocalValue, MemoryKind, OpTy, Scalar,
|
||||
ScalarMaybeUninit, StackPopCleanup,
|
||||
};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::{
|
||||
AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
|
||||
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
|
||||
StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
|
||||
AssertKind, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, Location, Operand, Place,
|
||||
Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator,
|
||||
TerminatorKind, UnOp, RETURN_PLACE,
|
||||
};
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::subst::{InternalSubsts, Subst};
|
||||
|
@ -22,42 +27,15 @@ use rustc_middle::ty::{
|
|||
TypeVisitable,
|
||||
};
|
||||
use rustc_session::lint;
|
||||
use rustc_span::{def_id::DefId, Span};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
use rustc_trait_selection::traits;
|
||||
|
||||
use crate::MirLint;
|
||||
use rustc_const_eval::const_eval::ConstEvalErr;
|
||||
use rustc_const_eval::interpret::{
|
||||
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
|
||||
LocalState, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, ScalarMaybeUninit,
|
||||
StackPopCleanup, StackPopUnwind,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
|
||||
/// The maximum number of bytes that we'll allocate space for a local or the return value.
|
||||
/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
|
||||
/// Severely regress performance.
|
||||
const MAX_ALLOC_LIMIT: u64 = 1024;
|
||||
|
||||
/// Macro for machine-specific `InterpError` without allocation.
|
||||
/// (These will never be shown to the user, but they help diagnose ICEs.)
|
||||
macro_rules! throw_machine_stop_str {
|
||||
($($tt:tt)*) => {{
|
||||
// We make a new local type for it. The type itself does not carry any information,
|
||||
// but its vtable (for the `MachineStopType` trait) does.
|
||||
struct Zst;
|
||||
// Printing this type shows the desired string.
|
||||
impl std::fmt::Display for Zst {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, $($tt)*)
|
||||
}
|
||||
}
|
||||
impl rustc_middle::mir::interpret::MachineStopType for Zst {}
|
||||
throw_machine_stop!(Zst)
|
||||
}};
|
||||
}
|
||||
|
||||
pub struct ConstProp;
|
||||
|
||||
impl<'tcx> MirLint<'tcx> for ConstProp {
|
||||
|
@ -151,172 +129,6 @@ impl<'tcx> MirLint<'tcx> for ConstProp {
|
|||
}
|
||||
}
|
||||
|
||||
struct ConstPropMachine<'mir, 'tcx> {
|
||||
/// The virtual call stack.
|
||||
stack: Vec<Frame<'mir, 'tcx>>,
|
||||
/// `OnlyInsideOwnBlock` locals that were written in the current block get erased at the end.
|
||||
written_only_inside_own_block_locals: FxHashSet<Local>,
|
||||
/// Locals that need to be cleared after every block terminates.
|
||||
only_propagate_inside_block_locals: BitSet<Local>,
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
}
|
||||
|
||||
impl ConstPropMachine<'_, '_> {
|
||||
fn new(
|
||||
only_propagate_inside_block_locals: BitSet<Local>,
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
written_only_inside_own_block_locals: Default::default(),
|
||||
only_propagate_inside_block_locals,
|
||||
can_const_prop,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
|
||||
compile_time_machine!(<'mir, 'tcx>);
|
||||
const PANIC_ON_ALLOC_FAIL: bool = true; // all allocations are small (see `MAX_ALLOC_LIMIT`)
|
||||
|
||||
type MemoryKind = !;
|
||||
|
||||
fn load_mir(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
_instance: ty::InstanceDef<'tcx>,
|
||||
) -> InterpResult<'tcx, &'tcx Body<'tcx>> {
|
||||
throw_machine_stop_str!("calling functions isn't supported in ConstProp")
|
||||
}
|
||||
|
||||
fn find_mir_or_eval_fn(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_instance: ty::Instance<'tcx>,
|
||||
_abi: CallAbi,
|
||||
_args: &[OpTy<'tcx>],
|
||||
_destination: &PlaceTy<'tcx>,
|
||||
_target: Option<BasicBlock>,
|
||||
_unwind: StackPopUnwind,
|
||||
) -> InterpResult<'tcx, Option<(&'mir Body<'tcx>, ty::Instance<'tcx>)>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn call_intrinsic(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_instance: ty::Instance<'tcx>,
|
||||
_args: &[OpTy<'tcx>],
|
||||
_destination: &PlaceTy<'tcx>,
|
||||
_target: Option<BasicBlock>,
|
||||
_unwind: StackPopUnwind,
|
||||
) -> InterpResult<'tcx> {
|
||||
throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
|
||||
}
|
||||
|
||||
fn assert_panic(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_msg: &rustc_middle::mir::AssertMessage<'tcx>,
|
||||
_unwind: Option<rustc_middle::mir::BasicBlock>,
|
||||
) -> InterpResult<'tcx> {
|
||||
bug!("panics terminators are not evaluated in ConstProp")
|
||||
}
|
||||
|
||||
fn binary_ptr_op(
|
||||
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
_bin_op: BinOp,
|
||||
_left: &ImmTy<'tcx>,
|
||||
_right: &ImmTy<'tcx>,
|
||||
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
|
||||
// We can't do this because aliasing of memory can differ between const eval and llvm
|
||||
throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
|
||||
}
|
||||
|
||||
fn access_local<'a>(
|
||||
frame: &'a Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
|
||||
local: Local,
|
||||
) -> InterpResult<'tcx, &'a interpret::Operand<Self::Provenance>> {
|
||||
let l = &frame.locals[local];
|
||||
|
||||
if matches!(
|
||||
l.value,
|
||||
LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit))
|
||||
) {
|
||||
// For us "uninit" means "we don't know its value, might be initiailized or not".
|
||||
// So stop here.
|
||||
throw_machine_stop_str!("tried to access a local with unknown value")
|
||||
}
|
||||
|
||||
l.access()
|
||||
}
|
||||
|
||||
fn access_local_mut<'a>(
|
||||
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
|
||||
frame: usize,
|
||||
local: Local,
|
||||
) -> InterpResult<'tcx, &'a mut interpret::Operand<Self::Provenance>> {
|
||||
if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
|
||||
throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
|
||||
}
|
||||
if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
|
||||
trace!(
|
||||
"mutating local {:?} which is restricted to its block. \
|
||||
Will remove it from const-prop after block is finished.",
|
||||
local
|
||||
);
|
||||
ecx.machine.written_only_inside_own_block_locals.insert(local);
|
||||
}
|
||||
ecx.machine.stack[frame].locals[local].access_mut()
|
||||
}
|
||||
|
||||
fn before_access_global(
|
||||
_tcx: TyCtxt<'tcx>,
|
||||
_machine: &Self,
|
||||
_alloc_id: AllocId,
|
||||
alloc: ConstAllocation<'tcx, Self::Provenance, Self::AllocExtra>,
|
||||
_static_def_id: Option<DefId>,
|
||||
is_write: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
if is_write {
|
||||
throw_machine_stop_str!("can't write to global");
|
||||
}
|
||||
// If the static allocation is mutable, then we can't const prop it as its content
|
||||
// might be different at runtime.
|
||||
if alloc.inner().mutability == Mutability::Mut {
|
||||
throw_machine_stop_str!("can't access mutable globals in ConstProp");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn expose_ptr(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
_ptr: Pointer<AllocId>,
|
||||
) -> InterpResult<'tcx> {
|
||||
throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn init_frame_extra(
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
frame: Frame<'mir, 'tcx>,
|
||||
) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn stack<'a>(
|
||||
ecx: &'a InterpCx<'mir, 'tcx, Self>,
|
||||
) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
|
||||
&ecx.machine.stack
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn stack_mut<'a>(
|
||||
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
|
||||
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
|
||||
&mut ecx.machine.stack
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds optimization opportunities on the MIR.
|
||||
struct ConstPropagator<'mir, 'tcx> {
|
||||
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
|
||||
|
@ -711,139 +523,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The mode that `ConstProp` is allowed to run in for a given `Local`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum ConstPropMode {
|
||||
/// The `Local` can be propagated into and reads of this `Local` can also be propagated.
|
||||
FullConstProp,
|
||||
/// The `Local` can only be propagated into and from its own block.
|
||||
OnlyInsideOwnBlock,
|
||||
/// The `Local` can be propagated into but reads cannot be propagated.
|
||||
OnlyPropagateInto,
|
||||
/// The `Local` cannot be part of propagation at all. Any statement
|
||||
/// referencing it either for reading or writing will not get propagated.
|
||||
NoPropagation,
|
||||
}
|
||||
|
||||
struct CanConstProp {
|
||||
can_const_prop: IndexVec<Local, ConstPropMode>,
|
||||
// False at the beginning. Once set, no more assignments are allowed to that local.
|
||||
found_assignment: BitSet<Local>,
|
||||
// Cache of locals' information
|
||||
local_kinds: IndexVec<Local, LocalKind>,
|
||||
}
|
||||
|
||||
impl CanConstProp {
|
||||
/// Returns true if `local` can be propagated
|
||||
fn check<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> IndexVec<Local, ConstPropMode> {
|
||||
let mut cpv = CanConstProp {
|
||||
can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
|
||||
found_assignment: BitSet::new_empty(body.local_decls.len()),
|
||||
local_kinds: IndexVec::from_fn_n(
|
||||
|local| body.local_kind(local),
|
||||
body.local_decls.len(),
|
||||
),
|
||||
};
|
||||
for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
|
||||
let ty = body.local_decls[local].ty;
|
||||
match tcx.layout_of(param_env.and(ty)) {
|
||||
Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
|
||||
// Either the layout fails to compute, then we can't use this local anyway
|
||||
// or the local is too large, then we don't want to.
|
||||
_ => {
|
||||
*val = ConstPropMode::NoPropagation;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Cannot use args at all
|
||||
// Cannot use locals because if x < y { y - x } else { x - y } would
|
||||
// lint for x != y
|
||||
// FIXME(oli-obk): lint variables until they are used in a condition
|
||||
// FIXME(oli-obk): lint if return value is constant
|
||||
if cpv.local_kinds[local] == LocalKind::Arg {
|
||||
*val = ConstPropMode::OnlyPropagateInto;
|
||||
trace!(
|
||||
"local {:?} can't be const propagated because it's a function argument",
|
||||
local
|
||||
);
|
||||
} else if cpv.local_kinds[local] == LocalKind::Var {
|
||||
*val = ConstPropMode::OnlyInsideOwnBlock;
|
||||
trace!(
|
||||
"local {:?} will only be propagated inside its block, because it's a user variable",
|
||||
local
|
||||
);
|
||||
}
|
||||
}
|
||||
cpv.visit_body(&body);
|
||||
cpv.can_const_prop
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<'_> for CanConstProp {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
|
||||
use rustc_middle::mir::visit::PlaceContext::*;
|
||||
match context {
|
||||
// Projections are fine, because `&mut foo.x` will be caught by
|
||||
// `MutatingUseContext::Borrow` elsewhere.
|
||||
MutatingUse(MutatingUseContext::Projection)
|
||||
// These are just stores, where the storing is not propagatable, but there may be later
|
||||
// mutations of the same local via `Store`
|
||||
| MutatingUse(MutatingUseContext::Call)
|
||||
| MutatingUse(MutatingUseContext::AsmOutput)
|
||||
| MutatingUse(MutatingUseContext::Deinit)
|
||||
// Actual store that can possibly even propagate a value
|
||||
| MutatingUse(MutatingUseContext::SetDiscriminant)
|
||||
| MutatingUse(MutatingUseContext::Store) => {
|
||||
if !self.found_assignment.insert(local) {
|
||||
match &mut self.can_const_prop[local] {
|
||||
// If the local can only get propagated in its own block, then we don't have
|
||||
// to worry about multiple assignments, as we'll nuke the const state at the
|
||||
// end of the block anyway, and inside the block we overwrite previous
|
||||
// states as applicable.
|
||||
ConstPropMode::OnlyInsideOwnBlock => {}
|
||||
ConstPropMode::NoPropagation => {}
|
||||
ConstPropMode::OnlyPropagateInto => {}
|
||||
other @ ConstPropMode::FullConstProp => {
|
||||
trace!(
|
||||
"local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
|
||||
local, other,
|
||||
);
|
||||
*other = ConstPropMode::OnlyInsideOwnBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reading constants is allowed an arbitrary number of times
|
||||
NonMutatingUse(NonMutatingUseContext::Copy)
|
||||
| NonMutatingUse(NonMutatingUseContext::Move)
|
||||
| NonMutatingUse(NonMutatingUseContext::Inspect)
|
||||
| NonMutatingUse(NonMutatingUseContext::Projection)
|
||||
| NonUse(_) => {}
|
||||
|
||||
// These could be propagated with a smarter analysis or just some careful thinking about
|
||||
// whether they'd be fine right now.
|
||||
MutatingUse(MutatingUseContext::Yield)
|
||||
| MutatingUse(MutatingUseContext::Drop)
|
||||
| MutatingUse(MutatingUseContext::Retag)
|
||||
// These can't ever be propagated under any scheme, as we can't reason about indirect
|
||||
// mutation.
|
||||
| NonMutatingUse(NonMutatingUseContext::SharedBorrow)
|
||||
| NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
|
||||
| NonMutatingUse(NonMutatingUseContext::UniqueBorrow)
|
||||
| NonMutatingUse(NonMutatingUseContext::AddressOf)
|
||||
| MutatingUse(MutatingUseContext::Borrow)
|
||||
| MutatingUse(MutatingUseContext::AddressOf) => {
|
||||
trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
|
||||
self.can_const_prop[local] = ConstPropMode::NoPropagation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||
fn visit_body(&mut self, body: &Body<'tcx>) {
|
||||
for (bb, data) in body.basic_blocks().iter_enumerated() {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use super::callee::DeferredCallResolution;
|
||||
use super::MaybeInProgressTables;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
|
@ -29,7 +28,7 @@ use std::ops::Deref;
|
|||
pub struct Inherited<'a, 'tcx> {
|
||||
pub(super) infcx: InferCtxt<'a, 'tcx>,
|
||||
|
||||
pub(super) typeck_results: super::MaybeInProgressTables<'a, 'tcx>,
|
||||
pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>,
|
||||
|
||||
pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
|
||||
|
||||
|
@ -110,11 +109,11 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> {
|
|||
let tcx = infcx.tcx;
|
||||
let item_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let body_id = tcx.hir().maybe_body_owned_by(item_id);
|
||||
let typeck_results =
|
||||
infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results");
|
||||
|
||||
Inherited {
|
||||
typeck_results: MaybeInProgressTables {
|
||||
maybe_typeck_results: infcx.in_progress_typeck_results,
|
||||
},
|
||||
typeck_results,
|
||||
infcx,
|
||||
fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)),
|
||||
locals: RefCell::new(Default::default()),
|
||||
|
|
|
@ -128,8 +128,7 @@ use rustc_target::spec::abi::Abi;
|
|||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
|
||||
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::require_c_abi_if_c_variadic;
|
||||
use crate::util::common::indenter;
|
||||
|
@ -900,32 +899,6 @@ enum TupleArgumentsFlag {
|
|||
TupleArguments,
|
||||
}
|
||||
|
||||
/// A wrapper for `InferCtxt`'s `in_progress_typeck_results` field.
|
||||
#[derive(Copy, Clone)]
|
||||
struct MaybeInProgressTables<'a, 'tcx> {
|
||||
maybe_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MaybeInProgressTables<'a, 'tcx> {
|
||||
fn borrow(self) -> Ref<'a, ty::TypeckResults<'tcx>> {
|
||||
match self.maybe_typeck_results {
|
||||
Some(typeck_results) => typeck_results.borrow(),
|
||||
None => bug!(
|
||||
"MaybeInProgressTables: inh/fcx.typeck_results.borrow() with no typeck results"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow_mut(self) -> RefMut<'a, ty::TypeckResults<'tcx>> {
|
||||
match self.maybe_typeck_results {
|
||||
Some(typeck_results) => typeck_results.borrow_mut(),
|
||||
None => bug!(
|
||||
"MaybeInProgressTables: inh/fcx.typeck_results.borrow_mut() with no typeck results"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) {
|
||||
tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id));
|
||||
}
|
||||
|
|
|
@ -317,11 +317,11 @@ use crate::vec::Vec;
|
|||
///
|
||||
/// ```text
|
||||
/// 0
|
||||
/// 5
|
||||
/// 10
|
||||
/// 20
|
||||
/// 20
|
||||
/// 40
|
||||
/// 8
|
||||
/// 16
|
||||
/// 16
|
||||
/// 32
|
||||
/// 32
|
||||
/// ```
|
||||
///
|
||||
/// At first, we have no memory allocated at all, but as we append to the
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use crate::marker::Destruct;
|
||||
|
||||
use self::Ordering::*;
|
||||
|
||||
/// Trait for equality comparisons which are [partial equivalence
|
||||
|
@ -603,7 +605,8 @@ impl Ordering {
|
|||
pub struct Reverse<T>(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T);
|
||||
|
||||
#[stable(feature = "reverse_cmp_key", since = "1.19.0")]
|
||||
impl<T: PartialOrd> PartialOrd for Reverse<T> {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl<T: ~const PartialOrd> const PartialOrd for Reverse<T> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> {
|
||||
other.0.partial_cmp(&self.0)
|
||||
|
@ -761,6 +764,7 @@ impl<T: Clone> Clone for Reverse<T> {
|
|||
#[doc(alias = ">=")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_diagnostic_item = "Ord"]
|
||||
#[const_trait]
|
||||
pub trait Ord: Eq + PartialOrd<Self> {
|
||||
/// This method returns an [`Ordering`] between `self` and `other`.
|
||||
///
|
||||
|
@ -796,8 +800,15 @@ pub trait Ord: Eq + PartialOrd<Self> {
|
|||
fn max(self, other: Self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
Self: ~const Destruct,
|
||||
{
|
||||
max_by(self, other, Ord::cmp)
|
||||
// HACK(fee1-dead): go back to using `self.max_by(other, Ord::cmp)`
|
||||
// when trait methods are allowed to be used when a const closure is
|
||||
// expected.
|
||||
match self.cmp(&other) {
|
||||
Ordering::Less | Ordering::Equal => other,
|
||||
Ordering::Greater => self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compares and returns the minimum of two values.
|
||||
|
@ -816,8 +827,15 @@ pub trait Ord: Eq + PartialOrd<Self> {
|
|||
fn min(self, other: Self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
Self: ~const Destruct,
|
||||
{
|
||||
min_by(self, other, Ord::cmp)
|
||||
// HACK(fee1-dead): go back to using `self.min_by(other, Ord::cmp)`
|
||||
// when trait methods are allowed to be used when a const closure is
|
||||
// expected.
|
||||
match self.cmp(&other) {
|
||||
Ordering::Less | Ordering::Equal => self,
|
||||
Ordering::Greater => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// Restrict a value to a certain interval.
|
||||
|
@ -841,6 +859,8 @@ pub trait Ord: Eq + PartialOrd<Self> {
|
|||
fn clamp(self, min: Self, max: Self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
Self: ~const Destruct,
|
||||
Self: ~const PartialOrd,
|
||||
{
|
||||
assert!(min <= max);
|
||||
if self < min {
|
||||
|
@ -862,7 +882,8 @@ pub macro Ord($item:item) {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for Ordering {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const Ord for Ordering {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Ordering) -> Ordering {
|
||||
(*self as i32).cmp(&(*other as i32))
|
||||
|
@ -870,7 +891,8 @@ impl Ord for Ordering {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for Ordering {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for Ordering {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Ordering) -> Option<Ordering> {
|
||||
(*self as i32).partial_cmp(&(*other as i32))
|
||||
|
@ -1187,8 +1209,9 @@ pub macro PartialOrd($item:item) {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "cmp_min")]
|
||||
pub fn min<T: Ord>(v1: T, v2: T) -> T {
|
||||
pub const fn min<T: ~const Ord + ~const Destruct>(v1: T, v2: T) -> T {
|
||||
v1.min(v2)
|
||||
}
|
||||
|
||||
|
@ -1250,8 +1273,9 @@ pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
|
|||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "cmp_max")]
|
||||
pub fn max<T: Ord>(v1: T, v2: T) -> T {
|
||||
pub const fn max<T: ~const Ord + ~const Destruct>(v1: T, v2: T) -> T {
|
||||
v1.max(v2)
|
||||
}
|
||||
|
||||
|
@ -1304,7 +1328,8 @@ mod impls {
|
|||
macro_rules! partial_eq_impl {
|
||||
($($t:ty)*) => ($(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialEq for $t {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialEq for $t {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$t) -> bool { (*self) == (*other) }
|
||||
#[inline]
|
||||
|
@ -1314,7 +1339,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialEq for () {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialEq for () {
|
||||
#[inline]
|
||||
fn eq(&self, _other: &()) -> bool {
|
||||
true
|
||||
|
@ -1341,7 +1367,8 @@ mod impls {
|
|||
macro_rules! partial_ord_impl {
|
||||
($($t:ty)*) => ($(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for $t {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for $t {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
|
||||
match (*self <= *other, *self >= *other) {
|
||||
|
@ -1364,7 +1391,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for () {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for () {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, _: &()) -> Option<Ordering> {
|
||||
Some(Equal)
|
||||
|
@ -1372,7 +1400,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for bool {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for bool {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &bool) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
|
@ -1384,7 +1413,8 @@ mod impls {
|
|||
macro_rules! ord_impl {
|
||||
($($t:ty)*) => ($(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl PartialOrd for $t {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for $t {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
|
@ -1400,7 +1430,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for $t {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const Ord for $t {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &$t) -> Ordering {
|
||||
// The order here is important to generate more optimal assembly.
|
||||
|
@ -1414,7 +1445,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for () {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const Ord for () {
|
||||
#[inline]
|
||||
fn cmp(&self, _other: &()) -> Ordering {
|
||||
Equal
|
||||
|
@ -1422,7 +1454,8 @@ mod impls {
|
|||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Ord for bool {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const Ord for bool {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &bool) -> Ordering {
|
||||
// Casting to i8's and converting the difference to an Ordering generates
|
||||
|
@ -1441,7 +1474,8 @@ mod impls {
|
|||
ord_impl! { char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
|
||||
|
||||
#[unstable(feature = "never_type", issue = "35121")]
|
||||
impl PartialEq for ! {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialEq for ! {
|
||||
fn eq(&self, _: &!) -> bool {
|
||||
*self
|
||||
}
|
||||
|
@ -1451,14 +1485,16 @@ mod impls {
|
|||
impl Eq for ! {}
|
||||
|
||||
#[unstable(feature = "never_type", issue = "35121")]
|
||||
impl PartialOrd for ! {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const PartialOrd for ! {
|
||||
fn partial_cmp(&self, _: &!) -> Option<Ordering> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "never_type", issue = "35121")]
|
||||
impl Ord for ! {
|
||||
#[rustc_const_unstable(feature = "const_cmp", issue = "92391")]
|
||||
impl const Ord for ! {
|
||||
fn cmp(&self, _: &!) -> Ordering {
|
||||
*self
|
||||
}
|
||||
|
|
|
@ -2566,14 +2566,23 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
|||
///
|
||||
/// * `dst` must be properly aligned.
|
||||
///
|
||||
/// Additionally, the caller must ensure that writing `count *
|
||||
/// size_of::<T>()` bytes to the given region of memory results in a valid
|
||||
/// value of `T`. Using a region of memory typed as a `T` that contains an
|
||||
/// invalid value of `T` is undefined behavior.
|
||||
///
|
||||
/// Note that even if the effectively copied size (`count * size_of::<T>()`) is
|
||||
/// `0`, the pointer must be non-null and properly aligned.
|
||||
///
|
||||
/// Additionally, note that changing `*dst` in this way can easily lead to undefined behavior (UB)
|
||||
/// later if the written bytes are not a valid representation of some `T`. For instance, the
|
||||
/// following is an **incorrect** use of this function:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// unsafe {
|
||||
/// let mut value: u8 = 0;
|
||||
/// let ptr: *mut bool = &mut value as *mut u8 as *mut bool;
|
||||
/// let _bool = ptr.read(); // This is fine, `ptr` points to a valid `bool`.
|
||||
/// ptr.write_bytes(42u8, 1); // This function itself does not cause UB...
|
||||
/// let _bool = ptr.read(); // ...but it makes this operation UB! ⚠️
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [valid]: crate::ptr#safety
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -2590,38 +2599,6 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
|||
/// }
|
||||
/// assert_eq!(vec, [0xfefefefe, 0xfefefefe, 0, 0]);
|
||||
/// ```
|
||||
///
|
||||
/// Creating an invalid value:
|
||||
///
|
||||
/// ```
|
||||
/// use std::ptr;
|
||||
///
|
||||
/// let mut v = Box::new(0i32);
|
||||
///
|
||||
/// unsafe {
|
||||
/// // Leaks the previously held value by overwriting the `Box<T>` with
|
||||
/// // a null pointer.
|
||||
/// ptr::write_bytes(&mut v as *mut Box<i32>, 0, 1);
|
||||
/// }
|
||||
///
|
||||
/// // At this point, using or dropping `v` results in undefined behavior.
|
||||
/// // drop(v); // ERROR
|
||||
///
|
||||
/// // Even leaking `v` "uses" it, and hence is undefined behavior.
|
||||
/// // mem::forget(v); // ERROR
|
||||
///
|
||||
/// // In fact, `v` is invalid according to basic type layout invariants, so *any*
|
||||
/// // operation touching it is undefined behavior.
|
||||
/// // let v2 = v; // ERROR
|
||||
///
|
||||
/// unsafe {
|
||||
/// // Let us instead put in a valid value
|
||||
/// ptr::write(&mut v as *mut Box<i32>, Box::new(42i32));
|
||||
/// }
|
||||
///
|
||||
/// // Now the box is fine
|
||||
/// assert_eq!(*v, 42);
|
||||
/// ```
|
||||
#[doc(alias = "memset")]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(bootstrap), rustc_allowed_through_unstable_modules)]
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
#![feature(const_cell_into_inner)]
|
||||
#![feature(const_char_convert)]
|
||||
#![feature(const_clone)]
|
||||
#![feature(const_cmp)]
|
||||
#![feature(const_discriminant)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_float_bits_conv)]
|
||||
|
|
|
@ -382,6 +382,14 @@ pub mod token_stream {
|
|||
bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.0.count()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
|
||||
|
|
|
@ -1855,7 +1855,6 @@ in storage.js plus the media query with (min-width: 701px)
|
|||
the sidebar stays visible for screen readers, which is useful for navigation. */
|
||||
left: -1000px;
|
||||
margin-left: 0;
|
||||
background-color: rgba(0,0,0,0);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
z-index: 11;
|
||||
|
|
|
@ -40,3 +40,25 @@ assert-position: ("#method\.must_use", {"y": 45})
|
|||
click: ".sidebar-menu-toggle"
|
||||
scroll-to: ".block.keyword li:nth-child(1)"
|
||||
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 543})
|
||||
|
||||
// Now checking the background color of the sidebar.
|
||||
local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
|
||||
reload:
|
||||
|
||||
// Open the sidebar menu.
|
||||
click: ".sidebar-menu-toggle"
|
||||
assert-css: (".sidebar", {"background-color": "rgb(80, 80, 80)"})
|
||||
|
||||
local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "ayu"}
|
||||
reload:
|
||||
|
||||
// Open the sidebar menu.
|
||||
click: ".sidebar-menu-toggle"
|
||||
assert-css: (".sidebar", {"background-color": "rgb(20, 25, 31)"})
|
||||
|
||||
local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "light"}
|
||||
reload:
|
||||
|
||||
// Open the sidebar menu.
|
||||
click: ".sidebar-menu-toggle"
|
||||
assert-css: (".sidebar", {"background-color": "rgb(245, 245, 245)"})
|
||||
|
|
Loading…
Add table
Reference in a new issue