Merge from rustc
This commit is contained in:
commit
7f414f9c39
195 changed files with 3732 additions and 1322 deletions
15
RELEASES.md
15
RELEASES.md
|
@ -1,3 +1,18 @@
|
|||
Version 1.84.1 (2025-01-30)
|
||||
==========================
|
||||
|
||||
<a id="1.84.1"></a>
|
||||
|
||||
- [Fix ICE 132920 in duplicate-crate diagnostics.](https://github.com/rust-lang/rust/pull/133304/)
|
||||
- [Fix errors for overlapping impls in incremental rebuilds.](https://github.com/rust-lang/rust/pull/133828/)
|
||||
- [Fix slow compilation related to the next-generation trait solver.](https://github.com/rust-lang/rust/pull/135618/)
|
||||
- [Fix debuginfo when LLVM's location discriminator value limit is exceeded.](https://github.com/rust-lang/rust/pull/135643/)
|
||||
- Fixes for building Rust from source:
|
||||
- [Only try to distribute `llvm-objcopy` if llvm tools are enabled.](https://github.com/rust-lang/rust/pull/134240/)
|
||||
- [Add Profile Override for Non-Git Sources.](https://github.com/rust-lang/rust/pull/135433/)
|
||||
- [Resolve symlinks of LLVM tool binaries before copying them.](https://github.com/rust-lang/rust/pull/135585/)
|
||||
- [Make it possible to use ci-rustc on tarball sources.](https://github.com/rust-lang/rust/pull/135722/)
|
||||
|
||||
Version 1.84.0 (2025-01-09)
|
||||
==========================
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ SPDX-FileCopyrightText = "2015 Anders Kaseorg <andersk@mit.edu>"
|
|||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/librustdoc/html/static/fonts/FiraSans**"
|
||||
path = "src/librustdoc/html/static/fonts/Fira**"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."]
|
||||
SPDX-License-Identifier = "OFL-1.1"
|
||||
|
|
|
@ -527,13 +527,13 @@ impl TokenKind {
|
|||
|
||||
/// Returns tokens that are likely to be typed accidentally instead of the current token.
|
||||
/// Enables better error recovery when the wrong token is found.
|
||||
pub fn similar_tokens(&self) -> Option<Vec<TokenKind>> {
|
||||
match *self {
|
||||
Comma => Some(vec![Dot, Lt, Semi]),
|
||||
Semi => Some(vec![Colon, Comma]),
|
||||
Colon => Some(vec![Semi]),
|
||||
FatArrow => Some(vec![Eq, RArrow, Ge, Gt]),
|
||||
_ => None,
|
||||
pub fn similar_tokens(&self) -> &[TokenKind] {
|
||||
match self {
|
||||
Comma => &[Dot, Lt, Semi],
|
||||
Semi => &[Colon, Comma],
|
||||
Colon => &[Semi],
|
||||
FatArrow => &[Eq, RArrow, Ge, Gt],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::OpaqueTyOrigin;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::ty::fold::fold_regions;
|
||||
|
@ -10,6 +12,7 @@ use rustc_middle::ty::{
|
|||
TypingMode,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
@ -406,10 +409,6 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
|
|||
}
|
||||
|
||||
fn get_canonical_args(&self) -> ty::GenericArgsRef<'tcx> {
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
|
||||
if let Some(&canonical_args) = self.canonical_args.get() {
|
||||
return canonical_args;
|
||||
}
|
||||
|
@ -417,9 +416,9 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
|
|||
let &Self { tcx, def_id, .. } = self;
|
||||
let origin = tcx.local_opaque_ty_origin(def_id);
|
||||
let parent = match origin {
|
||||
hir::OpaqueTyOrigin::FnReturn { parent, .. }
|
||||
| hir::OpaqueTyOrigin::AsyncFn { parent, .. }
|
||||
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent,
|
||||
OpaqueTyOrigin::FnReturn { parent, .. }
|
||||
| OpaqueTyOrigin::AsyncFn { parent, .. }
|
||||
| OpaqueTyOrigin::TyAlias { parent, .. } => parent,
|
||||
};
|
||||
let param_env = tcx.param_env(parent);
|
||||
let args = GenericArgs::identity_for_item(tcx, parent).extend_to(
|
||||
|
@ -439,8 +438,7 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
|
|||
tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
|
||||
Default::default()
|
||||
});
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
|
||||
|
||||
let mut seen = vec![tcx.lifetimes.re_static];
|
||||
let canonical_args = fold_regions(tcx, args, |r1, _| {
|
||||
|
|
|
@ -101,15 +101,14 @@ fn parse_args<'a>(ecx: &ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a,
|
|||
|
||||
match p.expect(exp!(Comma)) {
|
||||
Err(err) => {
|
||||
match token::TokenKind::Comma.similar_tokens() {
|
||||
Some(tks) if tks.contains(&p.token.kind) => {
|
||||
// If a similar token is found, then it may be a typo. We
|
||||
// consider it as a comma, and continue parsing.
|
||||
err.emit();
|
||||
p.bump();
|
||||
}
|
||||
if token::TokenKind::Comma.similar_tokens().contains(&p.token.kind) {
|
||||
// If a similar token is found, then it may be a typo. We
|
||||
// consider it as a comma, and continue parsing.
|
||||
err.emit();
|
||||
p.bump();
|
||||
} else {
|
||||
// Otherwise stop the parsing and return the error.
|
||||
_ => return Err(err),
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
Ok(Recovered::Yes(_)) => (),
|
||||
|
|
|
@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
let idx = generic_args[2]
|
||||
.expect_const()
|
||||
.try_to_valtree()
|
||||
.expect("expected monomorphic const in codegen")
|
||||
.0
|
||||
.unwrap_branch();
|
||||
let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch();
|
||||
|
||||
assert_eq!(x.layout(), y.layout());
|
||||
let layout = x.layout();
|
||||
|
|
|
@ -1325,7 +1325,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
|||
impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {
|
||||
fn get_static(&mut self, def_id: DefId) -> &'ll Value {
|
||||
// Forward to the `get_static` method of `CodegenCx`
|
||||
self.cx().get_static(def_id)
|
||||
let s = self.cx().get_static(def_id);
|
||||
// Cast to default address space if globals are in a different addrspace
|
||||
self.cx().const_pointercast(s, self.type_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -225,6 +225,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
|
||||
}
|
||||
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
|
||||
// Cast to default address space if globals are in a different addrspace
|
||||
let g = self.const_pointercast(g, self.type_ptr());
|
||||
(s.to_owned(), g)
|
||||
})
|
||||
.1;
|
||||
|
@ -289,7 +291,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
let alloc = alloc.inner();
|
||||
let value = match alloc.mutability {
|
||||
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None),
|
||||
_ => self.static_addr_of(init, alloc.align, None),
|
||||
_ => self.static_addr_of_impl(init, alloc.align, None),
|
||||
};
|
||||
if !self.sess().fewer_names() && llvm::get_value_name(value).is_empty()
|
||||
{
|
||||
|
@ -315,7 +317,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
.global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
|
||||
.unwrap_memory();
|
||||
let init = const_alloc_to_llvm(self, alloc, /*static*/ false);
|
||||
let value = self.static_addr_of(init, alloc.inner().align, None);
|
||||
let value = self.static_addr_of_impl(init, alloc.inner().align, None);
|
||||
(value, AddressSpace::DATA)
|
||||
}
|
||||
GlobalAlloc::Static(def_id) => {
|
||||
|
@ -327,7 +329,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
let llval = unsafe {
|
||||
llvm::LLVMConstInBoundsGEP2(
|
||||
self.type_i8(),
|
||||
self.const_bitcast(base_addr, self.type_ptr_ext(base_addr_space)),
|
||||
// Cast to the required address space if necessary
|
||||
self.const_pointercast(base_addr, self.type_ptr_ext(base_addr_space)),
|
||||
&self.const_usize(offset.bytes()),
|
||||
1,
|
||||
)
|
||||
|
|
|
@ -210,6 +210,14 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
unsafe { llvm::LLVMConstBitCast(val, ty) }
|
||||
}
|
||||
|
||||
pub(crate) fn const_pointercast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe { llvm::LLVMConstPointerCast(val, ty) }
|
||||
}
|
||||
|
||||
/// Create a global variable.
|
||||
///
|
||||
/// The returned global variable is a pointer in the default address space for globals.
|
||||
/// Fails if a symbol with the given name already exists.
|
||||
pub(crate) fn static_addr_of_mut(
|
||||
&self,
|
||||
cv: &'ll Value,
|
||||
|
@ -233,6 +241,34 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
gv
|
||||
}
|
||||
|
||||
/// Create a global constant.
|
||||
///
|
||||
/// The returned global variable is a pointer in the default address space for globals.
|
||||
pub(crate) fn static_addr_of_impl(
|
||||
&self,
|
||||
cv: &'ll Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
|
||||
unsafe {
|
||||
// Upgrade the alignment in cases where the same constant is used with different
|
||||
// alignment requirements
|
||||
let llalign = align.bytes() as u32;
|
||||
if llalign > llvm::LLVMGetAlignment(gv) {
|
||||
llvm::LLVMSetAlignment(gv, llalign);
|
||||
}
|
||||
}
|
||||
return gv;
|
||||
}
|
||||
let gv = self.static_addr_of_mut(cv, align, kind);
|
||||
unsafe {
|
||||
llvm::LLVMSetGlobalConstant(gv, True);
|
||||
}
|
||||
self.const_globals.borrow_mut().insert(cv, gv);
|
||||
gv
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
|
@ -505,24 +541,15 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
}
|
||||
|
||||
impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> {
|
||||
/// Get a pointer to a global variable.
|
||||
///
|
||||
/// The pointer will always be in the default address space. If global variables default to a
|
||||
/// different address space, an addrspacecast is inserted.
|
||||
fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value {
|
||||
if let Some(&gv) = self.const_globals.borrow().get(&cv) {
|
||||
unsafe {
|
||||
// Upgrade the alignment in cases where the same constant is used with different
|
||||
// alignment requirements
|
||||
let llalign = align.bytes() as u32;
|
||||
if llalign > llvm::LLVMGetAlignment(gv) {
|
||||
llvm::LLVMSetAlignment(gv, llalign);
|
||||
}
|
||||
}
|
||||
return gv;
|
||||
}
|
||||
let gv = self.static_addr_of_mut(cv, align, kind);
|
||||
unsafe {
|
||||
llvm::LLVMSetGlobalConstant(gv, True);
|
||||
}
|
||||
self.const_globals.borrow_mut().insert(cv, gv);
|
||||
gv
|
||||
let gv = self.static_addr_of_impl(cv, align, kind);
|
||||
// static_addr_of_impl returns the bare global variable, which might not be in the default
|
||||
// address space. Cast to the default address space if necessary.
|
||||
self.const_pointercast(gv, self.type_ptr())
|
||||
}
|
||||
|
||||
fn codegen_static(&self, def_id: DefId) {
|
||||
|
|
|
@ -919,8 +919,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
.unwrap_or_default();
|
||||
let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo);
|
||||
|
||||
let dwarf_version =
|
||||
tcx.sess.opts.unstable_opts.dwarf_version.unwrap_or(tcx.sess.target.default_dwarf_version);
|
||||
let dwarf_version = tcx.sess.dwarf_version();
|
||||
let is_dwarf_kind =
|
||||
matches!(tcx.sess.target.debuginfo_kind, DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym);
|
||||
// Don't emit `.debug_pubnames` and `.debug_pubtypes` on DWARFv4 or lower.
|
||||
|
@ -1488,6 +1487,26 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
|
|||
.di_node
|
||||
}
|
||||
|
||||
/// Get the global variable for the vtable.
|
||||
///
|
||||
/// When using global variables, we may have created an addrspacecast to get a pointer to the
|
||||
/// default address space if global variables are created in a different address space.
|
||||
/// For modifying the vtable, we need the real global variable. This function accepts either a
|
||||
/// global variable (which is simply returned), or an addrspacecast constant expression.
|
||||
/// If the given value is an addrspacecast, the cast is removed and the global variable behind
|
||||
/// the cast is returned.
|
||||
fn find_vtable_behind_cast<'ll>(vtable: &'ll Value) -> &'ll Value {
|
||||
// The vtable is a global variable, which may be behind an addrspacecast.
|
||||
unsafe {
|
||||
if let Some(c) = llvm::LLVMIsAConstantExpr(vtable) {
|
||||
if llvm::LLVMGetConstOpcode(c) == llvm::Opcode::AddrSpaceCast {
|
||||
return llvm::LLVMGetOperand(c, 0).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
vtable
|
||||
}
|
||||
|
||||
pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
|
@ -1508,6 +1527,8 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
|
|||
|
||||
let Some(trait_ref) = trait_ref else { return };
|
||||
|
||||
// Unwrap potential addrspacecast
|
||||
let vtable = find_vtable_behind_cast(vtable);
|
||||
let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty);
|
||||
let trait_ref_self = cx.tcx.erase_regions(trait_ref_self);
|
||||
let trait_def_id = trait_ref_self.def_id();
|
||||
|
@ -1581,6 +1602,9 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
// Unwrap potential addrspacecast
|
||||
let vtable = find_vtable_behind_cast(vtable);
|
||||
|
||||
// When full debuginfo is enabled, we want to try and prevent vtables from being
|
||||
// merged. Otherwise debuggers will have a hard time mapping from dyn pointer
|
||||
// to concrete type.
|
||||
|
|
|
@ -22,6 +22,7 @@ use rustc_session::config::{self, DebugInfo};
|
|||
use rustc_span::{
|
||||
BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol,
|
||||
};
|
||||
use rustc_target::spec::DebuginfoKind;
|
||||
use smallvec::SmallVec;
|
||||
use tracing::debug;
|
||||
|
||||
|
@ -93,29 +94,31 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
|
||||
pub(crate) fn finalize(&self, sess: &Session) {
|
||||
unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) };
|
||||
if !sess.target.is_like_msvc {
|
||||
// Debuginfo generation in LLVM by default uses a higher
|
||||
// version of dwarf than macOS currently understands. We can
|
||||
// instruct LLVM to emit an older version of dwarf, however,
|
||||
// for macOS to understand. For more info see #11352
|
||||
// This can be overridden using --llvm-opts -dwarf-version,N.
|
||||
// Android has the same issue (#22398)
|
||||
let dwarf_version =
|
||||
sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
|
||||
llvm::add_module_flag_u32(
|
||||
self.llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Warning,
|
||||
"Dwarf Version",
|
||||
dwarf_version,
|
||||
);
|
||||
} else {
|
||||
// Indicate that we want CodeView debug information on MSVC
|
||||
llvm::add_module_flag_u32(
|
||||
self.llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Warning,
|
||||
"CodeView",
|
||||
1,
|
||||
);
|
||||
|
||||
match sess.target.debuginfo_kind {
|
||||
DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => {
|
||||
// Debuginfo generation in LLVM by default uses a higher
|
||||
// version of dwarf than macOS currently understands. We can
|
||||
// instruct LLVM to emit an older version of dwarf, however,
|
||||
// for macOS to understand. For more info see #11352
|
||||
// This can be overridden using --llvm-opts -dwarf-version,N.
|
||||
// Android has the same issue (#22398)
|
||||
llvm::add_module_flag_u32(
|
||||
self.llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Warning,
|
||||
"Dwarf Version",
|
||||
sess.dwarf_version(),
|
||||
);
|
||||
}
|
||||
DebuginfoKind::Pdb => {
|
||||
// Indicate that we want CodeView debug information
|
||||
llvm::add_module_flag_u32(
|
||||
self.llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Warning,
|
||||
"CodeView",
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent bitcode readers from deleting the debug info.
|
||||
|
|
|
@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
}
|
||||
|
||||
if name == sym::simd_shuffle_generic {
|
||||
let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
|
||||
let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
|
||||
let n = idx.len() as u64;
|
||||
|
||||
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);
|
||||
|
|
|
@ -661,6 +661,79 @@ pub enum MemoryEffects {
|
|||
InaccessibleMemOnly,
|
||||
}
|
||||
|
||||
/// LLVMOpcode
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub enum Opcode {
|
||||
Ret = 1,
|
||||
Br = 2,
|
||||
Switch = 3,
|
||||
IndirectBr = 4,
|
||||
Invoke = 5,
|
||||
Unreachable = 7,
|
||||
CallBr = 67,
|
||||
FNeg = 66,
|
||||
Add = 8,
|
||||
FAdd = 9,
|
||||
Sub = 10,
|
||||
FSub = 11,
|
||||
Mul = 12,
|
||||
FMul = 13,
|
||||
UDiv = 14,
|
||||
SDiv = 15,
|
||||
FDiv = 16,
|
||||
URem = 17,
|
||||
SRem = 18,
|
||||
FRem = 19,
|
||||
Shl = 20,
|
||||
LShr = 21,
|
||||
AShr = 22,
|
||||
And = 23,
|
||||
Or = 24,
|
||||
Xor = 25,
|
||||
Alloca = 26,
|
||||
Load = 27,
|
||||
Store = 28,
|
||||
GetElementPtr = 29,
|
||||
Trunc = 30,
|
||||
ZExt = 31,
|
||||
SExt = 32,
|
||||
FPToUI = 33,
|
||||
FPToSI = 34,
|
||||
UIToFP = 35,
|
||||
SIToFP = 36,
|
||||
FPTrunc = 37,
|
||||
FPExt = 38,
|
||||
PtrToInt = 39,
|
||||
IntToPtr = 40,
|
||||
BitCast = 41,
|
||||
AddrSpaceCast = 60,
|
||||
ICmp = 42,
|
||||
FCmp = 43,
|
||||
PHI = 44,
|
||||
Call = 45,
|
||||
Select = 46,
|
||||
UserOp1 = 47,
|
||||
UserOp2 = 48,
|
||||
VAArg = 49,
|
||||
ExtractElement = 50,
|
||||
InsertElement = 51,
|
||||
ShuffleVector = 52,
|
||||
ExtractValue = 53,
|
||||
InsertValue = 54,
|
||||
Freeze = 68,
|
||||
Fence = 55,
|
||||
AtomicCmpXchg = 56,
|
||||
AtomicRMW = 57,
|
||||
Resume = 58,
|
||||
LandingPad = 59,
|
||||
CleanupRet = 61,
|
||||
CatchRet = 62,
|
||||
CatchPad = 63,
|
||||
CleanupPad = 64,
|
||||
CatchSwitch = 65,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
type Opaque;
|
||||
}
|
||||
|
@ -991,7 +1064,10 @@ unsafe extern "C" {
|
|||
pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
|
||||
pub fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
|
||||
pub fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
|
||||
pub fn LLVMConstPointerCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
|
||||
pub fn LLVMGetAggregateElement(ConstantVal: &Value, Idx: c_uint) -> Option<&Value>;
|
||||
pub fn LLVMGetConstOpcode(ConstantVal: &Value) -> Opcode;
|
||||
pub fn LLVMIsAConstantExpr(Val: &Value) -> Option<&Value>;
|
||||
|
||||
// Operations on global variables, functions, and aliases (globals)
|
||||
pub fn LLVMIsDeclaration(Global: &Value) -> Bool;
|
||||
|
@ -1048,6 +1124,7 @@ unsafe extern "C" {
|
|||
// Operations on instructions
|
||||
pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>;
|
||||
pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock;
|
||||
pub fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>;
|
||||
|
||||
// Operations on call sites
|
||||
pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint);
|
||||
|
|
|
@ -30,6 +30,8 @@ codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}
|
|||
|
||||
codegen_ssa_copy_path_buf = unable to copy {$source_file} to {$output_path}: {$error}
|
||||
|
||||
codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C target-cpu`
|
||||
|
||||
codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
|
||||
|
||||
codegen_ssa_dlltool_fail_import_library =
|
||||
|
|
|
@ -615,6 +615,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
|||
return ongoing_codegen;
|
||||
}
|
||||
|
||||
if tcx.sess.target.need_explicit_cpu && tcx.sess.opts.cg.target_cpu.is_none() {
|
||||
// The target has no default cpu, but none is set explicitly
|
||||
tcx.dcx().emit_fatal(errors::CpuRequired);
|
||||
}
|
||||
|
||||
let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
|
||||
|
||||
// Run the monomorphization collector and partition the collected items into
|
||||
|
|
|
@ -673,25 +673,23 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
|
|||
ty::ConstKind::Param(param) => {
|
||||
write!(output, "{}", param.name)
|
||||
}
|
||||
ty::ConstKind::Value(ty, valtree) => {
|
||||
match ty.kind() {
|
||||
ty::ConstKind::Value(cv) => {
|
||||
match cv.ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
// FIXME: directly extract the bits from a valtree instead of evaluating an
|
||||
// already evaluated `Const` in order to get the bits.
|
||||
let bits = ct
|
||||
let bits = cv
|
||||
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
|
||||
.expect("expected monomorphic const in codegen");
|
||||
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
|
||||
write!(output, "{val}")
|
||||
}
|
||||
ty::Uint(_) => {
|
||||
let val = ct
|
||||
let val = cv
|
||||
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
|
||||
.expect("expected monomorphic const in codegen");
|
||||
write!(output, "{val}")
|
||||
}
|
||||
ty::Bool => {
|
||||
let val = ct.try_to_bool().expect("expected monomorphic const in codegen");
|
||||
let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
|
||||
write!(output, "{val}")
|
||||
}
|
||||
_ => {
|
||||
|
@ -703,9 +701,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
|
|||
// avoiding collisions and will make the emitted type names shorter.
|
||||
let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
|
||||
let mut hasher = StableHasher::new();
|
||||
hcx.while_hashing_spans(false, |hcx| {
|
||||
(ty, valtree).hash_stable(hcx, &mut hasher)
|
||||
});
|
||||
hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
|
||||
hasher.finish::<Hash64>()
|
||||
});
|
||||
|
||||
|
|
|
@ -523,6 +523,10 @@ pub(crate) struct CheckInstalledVisualStudio;
|
|||
#[diag(codegen_ssa_insufficient_vs_code_product)]
|
||||
pub(crate) struct InsufficientVSCodeProduct;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_cpu_required)]
|
||||
pub(crate) struct CpuRequired;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(codegen_ssa_processing_dymutil_failed)]
|
||||
#[note]
|
||||
|
|
|
@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
mir::Const::Ty(_, c) => match c.kind() {
|
||||
// A constant that came from a const generic but was then used as an argument to
|
||||
// old-style simd_shuffle (passing as argument instead of as a generic param).
|
||||
rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)),
|
||||
rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)),
|
||||
other => span_bug!(constant.span, "{other:#?}"),
|
||||
},
|
||||
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate
|
||||
|
|
|
@ -345,7 +345,7 @@ where
|
|||
Const::Ty(_, ct)
|
||||
if matches!(
|
||||
ct.kind(),
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _)
|
||||
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
|
||||
) =>
|
||||
{
|
||||
None
|
||||
|
|
|
@ -272,7 +272,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
|
|||
|
||||
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
|
||||
/// construction has finished.
|
||||
// FIXME Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
|
||||
// FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
|
||||
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
|
||||
#[instrument(skip(tcx), level = "debug", ret)]
|
||||
pub fn valtree_to_const_value<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
@ -46,8 +46,13 @@ pub fn provide(providers: &mut Providers) {
|
|||
};
|
||||
providers.hooks.try_destructure_mir_constant_for_user_output =
|
||||
const_eval::try_destructure_mir_constant_for_user_output;
|
||||
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
|
||||
const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree)
|
||||
providers.valtree_to_const_val = |tcx, cv| {
|
||||
const_eval::valtree_to_const_value(
|
||||
tcx,
|
||||
ty::TypingEnv::fully_monomorphized(),
|
||||
cv.ty,
|
||||
cv.valtree,
|
||||
)
|
||||
};
|
||||
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
|
||||
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
|
||||
|
|
|
@ -27,7 +27,6 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC;
|
|||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
|
||||
use rustc_trait_selection::traits;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_type_ir::fold::TypeFoldable;
|
||||
use tracing::{debug, instrument};
|
||||
use ty::TypingMode;
|
||||
|
@ -417,9 +416,7 @@ fn check_opaque_meets_bounds<'tcx>(
|
|||
}
|
||||
|
||||
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?;
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
|
||||
ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?;
|
||||
|
||||
if infcx.next_trait_solver() {
|
||||
Ok(())
|
||||
|
|
|
@ -9,7 +9,6 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::VisitorExt;
|
||||
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit};
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::traits::util;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
|
@ -24,7 +23,6 @@ use rustc_span::Span;
|
|||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::regions::InferCtxtRegionExt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt,
|
||||
};
|
||||
|
@ -416,11 +414,7 @@ fn compare_method_predicate_entailment<'tcx>(
|
|||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys),
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions(impl_m_def_id, param_env, wf_tys);
|
||||
if !errors.is_empty() {
|
||||
return Err(infcx
|
||||
.tainted_by_errors()
|
||||
|
@ -725,11 +719,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
|||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys),
|
||||
);
|
||||
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
|
||||
ocx.resolve_regions_and_report_errors(impl_m_def_id, param_env, wf_tys)?;
|
||||
|
||||
let mut remapped_types = DefIdMap::default();
|
||||
for (def_id, (ty, args)) in collected_types {
|
||||
|
@ -1883,8 +1873,7 @@ fn compare_const_predicate_entailment<'tcx>(
|
|||
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
|
||||
}
|
||||
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
|
||||
ocx.resolve_regions_and_report_errors(impl_ct_def_id, param_env, [])
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
|
@ -2017,8 +2006,7 @@ fn compare_type_predicate_entailment<'tcx>(
|
|||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, [])
|
||||
}
|
||||
|
||||
/// Validate that `ProjectionCandidate`s created for this associated type will
|
||||
|
@ -2147,9 +2135,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
|||
|
||||
// Finally, resolve all regions. This catches wily misuses of
|
||||
// lifetime parameters.
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, &assumed_wf_types);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
|
||||
ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, assumed_wf_types)
|
||||
}
|
||||
|
||||
struct ReplaceTy<'tcx> {
|
||||
|
|
|
@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexSet;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
|
@ -13,7 +12,6 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::regions::InferCtxtRegionExt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{ObligationCtxt, elaborate, normalize_param_env_or_error};
|
||||
|
||||
/// Check that an implementation does not refine an RPITIT from a trait method signature.
|
||||
|
@ -170,11 +168,7 @@ pub(crate) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
|||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
|
||||
return;
|
||||
}
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions(impl_m.def_id.expect_local(), param_env, implied_wf_types);
|
||||
if !errors.is_empty() {
|
||||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
|
||||
return;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc_middle::ty::util::CheckRegions;
|
||||
|
@ -192,7 +191,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
|
|||
return Err(guar.unwrap());
|
||||
}
|
||||
|
||||
let errors = ocx.infcx.resolve_regions(&OutlivesEnvironment::new(adt_env));
|
||||
let errors = ocx.infcx.resolve_regions(adt_def_id, adt_env, []);
|
||||
if !errors.is_empty() {
|
||||
let mut guar = None;
|
||||
for error in errors {
|
||||
|
|
|
@ -80,7 +80,6 @@ use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_index::bit_set::DenseBitSet;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{self, TyCtxtInferExt as _};
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::query::Providers;
|
||||
|
@ -655,8 +654,7 @@ pub fn check_function_signature<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, &outlives_env) {
|
||||
if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, param_env, []) {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,10 @@ use rustc_middle::{bug, span_bug};
|
|||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{DUMMY_SP, Ident, Span, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::regions::InferCtxtRegionExt;
|
||||
use rustc_trait_selection::regions::{InferCtxtRegionExt, OutlivesEnvironmentBuildExt};
|
||||
use rustc_trait_selection::traits::misc::{
|
||||
ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty,
|
||||
};
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
|
||||
|
@ -128,13 +127,17 @@ where
|
|||
let infcx_compat = infcx.fork();
|
||||
|
||||
// We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always.
|
||||
let implied_bounds =
|
||||
infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat(
|
||||
&infcx,
|
||||
body_def_id,
|
||||
param_env,
|
||||
assumed_wf_types.iter().copied(),
|
||||
false,
|
||||
);
|
||||
|
||||
lint_redundant_lifetimes(tcx, body_def_id, &outlives_env);
|
||||
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions_with_outlives_env(&outlives_env);
|
||||
if errors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -172,10 +175,14 @@ where
|
|||
// but that does result in slightly more work when this option is set and
|
||||
// just obscures what we mean here anyways. Let's just be explicit.
|
||||
if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat {
|
||||
let implied_bounds =
|
||||
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
let errors_compat = infcx_compat.resolve_regions(&outlives_env);
|
||||
let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat(
|
||||
&infcx,
|
||||
body_def_id,
|
||||
param_env,
|
||||
assumed_wf_types,
|
||||
true,
|
||||
);
|
||||
let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env);
|
||||
if errors_compat.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -769,12 +776,7 @@ fn test_region_obligations<'tcx>(
|
|||
|
||||
add_constraints(&infcx);
|
||||
|
||||
let outlives_environment = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(param_env, id, wf_tys),
|
||||
);
|
||||
|
||||
let errors = infcx.resolve_regions(&outlives_environment);
|
||||
let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied());
|
||||
debug!(?errors, "errors");
|
||||
|
||||
// If we were able to prove that the type outlives the region without
|
||||
|
|
|
@ -10,7 +10,6 @@ use rustc_hir as hir;
|
|||
use rustc_hir::ItemKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
|
||||
|
@ -346,8 +345,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
|
|||
}
|
||||
|
||||
// Finally, resolve all regions.
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env));
|
||||
res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, []));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
@ -564,8 +562,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
|
|||
}
|
||||
|
||||
// Finally, resolve all regions.
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env);
|
||||
let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []);
|
||||
|
||||
Ok(CoerceUnsizedInfo { custom_kind: kind })
|
||||
}
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_infer::traits::specialization_graph::Node;
|
||||
use rustc_middle::ty::trait_def::TraitSpecializationKind;
|
||||
|
@ -77,7 +76,6 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_span::{ErrorGuaranteed, Span};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCtxt, translate_args_with_cause, wf};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
@ -176,7 +174,6 @@ fn get_impl_args(
|
|||
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
|
||||
let param_env = tcx.param_env(impl1_def_id);
|
||||
let impl1_span = tcx.def_span(impl1_def_id);
|
||||
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;
|
||||
|
||||
let impl1_args = GenericArgs::identity_for_item(tcx, impl1_def_id);
|
||||
let impl2_args = translate_args_with_cause(
|
||||
|
@ -194,9 +191,8 @@ fn get_impl_args(
|
|||
return Err(guar);
|
||||
}
|
||||
|
||||
let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, &assumed_wf_types);
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
|
||||
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
|
||||
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;
|
||||
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, param_env, assumed_wf_types);
|
||||
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
|
||||
let span = tcx.def_span(impl1_def_id);
|
||||
let guar = tcx.dcx().emit_err(GenericArgsOnOverriddenImpl { span });
|
||||
|
|
|
@ -243,8 +243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
fn downgrade_mut_inside_shared(&self) -> bool {
|
||||
// NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
|
||||
// across all editions, this may be removed.
|
||||
self.tcx.features().ref_pat_eat_one_layer_2024()
|
||||
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
|
||||
self.tcx.features().ref_pat_eat_one_layer_2024_structural()
|
||||
}
|
||||
|
||||
/// Experimental pattern feature: when do reference patterns match against inherited references?
|
||||
|
@ -435,7 +434,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
max_ref_mutbl: MutblCap,
|
||||
) -> (Ty<'tcx>, ByRef, MutblCap) {
|
||||
#[cfg(debug_assertions)]
|
||||
if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut {
|
||||
if def_br == ByRef::Yes(Mutability::Mut)
|
||||
&& max_ref_mutbl != MutblCap::Mut
|
||||
&& self.downgrade_mut_inside_shared()
|
||||
{
|
||||
span_bug!(pat.span, "Pattern mutability cap violated!");
|
||||
}
|
||||
match adjust_mode {
|
||||
|
@ -2328,22 +2330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
|
||||
// but not Rule 5, we'll need to check that here.
|
||||
debug_assert!(ref_pat_matches_mut_ref);
|
||||
let err_msg = "mismatched types";
|
||||
let err = if let Some(span) = pat_prefix_span {
|
||||
let mut err = self.dcx().struct_span_err(span, err_msg);
|
||||
err.code(E0308);
|
||||
err.note("cannot match inherited `&` with `&mut` pattern");
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
"replace this `&mut` pattern with `&`",
|
||||
"&",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err
|
||||
} else {
|
||||
self.dcx().struct_span_err(pat.span, err_msg)
|
||||
};
|
||||
err.emit();
|
||||
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
|
||||
}
|
||||
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
|
@ -2352,28 +2339,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return expected;
|
||||
}
|
||||
InheritedRefMatchRule::EatInner => {
|
||||
if let ty::Ref(_, _, r_mutbl) = *expected.kind() {
|
||||
if let ty::Ref(_, _, r_mutbl) = *expected.kind()
|
||||
&& pat_mutbl <= r_mutbl
|
||||
{
|
||||
// Match against the reference type; don't consume the inherited ref.
|
||||
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
|
||||
// NB: The check for compatible pattern and ref type mutability assumes that
|
||||
// `&` patterns can match against mutable references (RFC 3627, Rule 5). If
|
||||
// we implement a pattern typing ruleset with Rule 4 (including the fallback
|
||||
// to matching the inherited ref when the inner ref can't match) but not
|
||||
// Rule 5, we'll need to check that here.
|
||||
debug_assert!(ref_pat_matches_mut_ref);
|
||||
// NB: For RFC 3627's Rule 3, we limit the default binding mode's ref
|
||||
// mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing
|
||||
// ruleset with Rule 4 but not Rule 3, we'll need to check that here.
|
||||
debug_assert!(self.downgrade_mut_inside_shared());
|
||||
let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl());
|
||||
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap);
|
||||
} else {
|
||||
// The expected type isn't a reference, so match against the inherited ref.
|
||||
// The reference pattern can't match against the expected type, so try
|
||||
// matching against the inherited ref instead.
|
||||
if pat_mutbl > inh_mut {
|
||||
// We can't match an inherited shared reference with `&mut`. This will
|
||||
// be a type error later, since we're matching a reference pattern
|
||||
// against a non-reference type.
|
||||
// We can't match an inherited shared reference with `&mut`.
|
||||
// NB: This assumes that `&` patterns can match against mutable
|
||||
// references (RFC 3627, Rule 5). If we implement a pattern typing
|
||||
// ruleset with Rule 4 but not Rule 5, we'll need to check that here.
|
||||
debug_assert!(ref_pat_matches_mut_ref);
|
||||
} else {
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.skipped_ref_pats_mut()
|
||||
.insert(pat.hir_id);
|
||||
self.check_pat(inner, expected, pat_info);
|
||||
return expected;
|
||||
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
|
||||
}
|
||||
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
|
||||
self.check_pat(inner, expected, pat_info);
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
InheritedRefMatchRule::EatBoth => {
|
||||
|
@ -2447,6 +2444,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Ty::new_ref(self.tcx, region, ty, mutbl)
|
||||
}
|
||||
|
||||
fn error_inherited_ref_mutability_mismatch(
|
||||
&self,
|
||||
pat: &'tcx Pat<'tcx>,
|
||||
pat_prefix_span: Option<Span>,
|
||||
) -> ErrorGuaranteed {
|
||||
let err_msg = "mismatched types";
|
||||
let err = if let Some(span) = pat_prefix_span {
|
||||
let mut err = self.dcx().struct_span_err(span, err_msg);
|
||||
err.code(E0308);
|
||||
err.note("cannot match inherited `&` with `&mut` pattern");
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
"replace this `&mut` pattern with `&`",
|
||||
"&",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
err
|
||||
} else {
|
||||
self.dcx().struct_span_err(pat.span, err_msg)
|
||||
};
|
||||
err.emit()
|
||||
}
|
||||
|
||||
fn try_resolve_slice_ty_to_array_ty(
|
||||
&self,
|
||||
before: &'tcx [Pat<'tcx>],
|
||||
|
|
|
@ -170,7 +170,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
|
|||
}
|
||||
|
||||
ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Value(_, _)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Unevaluated(..)
|
||||
| ty::ConstKind::Expr(..)
|
||||
| ty::ConstKind::Error(_) => ct.super_fold_with(self),
|
||||
|
|
|
@ -1055,7 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
| ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Placeholder(_)
|
||||
| ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Value(_, _)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Error(_)
|
||||
| ty::ConstKind::Expr(_) => ct,
|
||||
}
|
||||
|
|
|
@ -31,26 +31,14 @@ use crate::traits::query::OutlivesBound;
|
|||
pub struct OutlivesEnvironment<'tcx> {
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
free_region_map: FreeRegionMap<'tcx>,
|
||||
|
||||
// Contains the implied region bounds in scope for our current body.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ```
|
||||
// fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) {
|
||||
// bar(x, y, |y: &'b T| { .. } // body B1)
|
||||
// } // body B0
|
||||
// ```
|
||||
//
|
||||
// Here, when checking the body B0, the list would be `[T: 'a]`, because we
|
||||
// infer that `T` must outlive `'a` from the implied bounds on the
|
||||
// fn declaration.
|
||||
//
|
||||
// For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we
|
||||
// also can see that -- within the closure body! -- `T` must
|
||||
// outlive `'b`. This is not necessarily true outside the closure
|
||||
// body, since the closure may never be called.
|
||||
/// FIXME: Your first reaction may be that this is a bit strange. `RegionBoundPairs`
|
||||
/// does not contain lifetimes, which are instead in the `FreeRegionMap`, and other
|
||||
/// known type outlives are stored in the `known_type_outlives` set. So why do we
|
||||
/// have these at all? It turns out that removing these and using `known_type_outlives`
|
||||
/// everywhere is just enough of a perf regression to matter. This can/should be
|
||||
/// optimized in the future, though.
|
||||
region_bound_pairs: RegionBoundPairs<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
}
|
||||
|
||||
/// "Region-bound pairs" tracks outlives relations that are known to
|
||||
|
@ -59,15 +47,10 @@ pub struct OutlivesEnvironment<'tcx> {
|
|||
pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<'tcx, GenericKind<'tcx>>>;
|
||||
|
||||
impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
/// Create a new `OutlivesEnvironment` without extra outlives bounds.
|
||||
#[inline]
|
||||
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
|
||||
Self::with_bounds(param_env, vec![])
|
||||
}
|
||||
|
||||
/// Create a new `OutlivesEnvironment` with extra outlives bounds.
|
||||
pub fn with_bounds(
|
||||
/// Create a new `OutlivesEnvironment` from normalized outlives bounds.
|
||||
pub fn from_normalized_bounds(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
|
||||
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
|
||||
) -> Self {
|
||||
let mut region_relation = TransitiveRelationBuilder::default();
|
||||
|
@ -102,18 +85,21 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
|
|||
|
||||
OutlivesEnvironment {
|
||||
param_env,
|
||||
known_type_outlives,
|
||||
free_region_map: FreeRegionMap { relation: region_relation.freeze() },
|
||||
region_bound_pairs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrows current value of the `free_region_map`.
|
||||
pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
|
||||
&self.free_region_map
|
||||
}
|
||||
|
||||
/// Borrows current `region_bound_pairs`.
|
||||
pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
|
||||
&self.region_bound_pairs
|
||||
}
|
||||
|
||||
pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
|
||||
&self.known_type_outlives
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ use rustc_middle::ty::{
|
|||
self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt,
|
||||
TypeFoldable as _, TypeVisitableExt,
|
||||
};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_type_ir::outlives::{Component, push_outlives_components};
|
||||
use smallvec::smallvec;
|
||||
use tracing::{debug, instrument};
|
||||
|
@ -142,25 +141,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> {
|
||||
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
|
||||
|
||||
let normalized_caller_bounds: Vec<_> = outlives_env
|
||||
.param_env
|
||||
.caller_bounds()
|
||||
.iter()
|
||||
.filter_map(|clause| {
|
||||
let outlives = clause.as_type_outlives_clause()?;
|
||||
Some(
|
||||
deeply_normalize_ty(
|
||||
outlives,
|
||||
SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP),
|
||||
)
|
||||
// FIXME(-Znext-solver): How do we accurately report an error span here :(
|
||||
.map_err(|NoSolution| {
|
||||
(outlives, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP))
|
||||
}),
|
||||
)
|
||||
})
|
||||
.try_collect()?;
|
||||
|
||||
// Must loop since the process of normalizing may itself register region obligations.
|
||||
for iteration in 0.. {
|
||||
let my_region_obligations = self.take_registered_region_obligations();
|
||||
|
@ -194,7 +174,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.tcx,
|
||||
outlives_env.region_bound_pairs(),
|
||||
None,
|
||||
&normalized_caller_bounds,
|
||||
outlives_env.known_type_outlives(),
|
||||
);
|
||||
let category = origin.to_constraint_category();
|
||||
outlives.type_must_outlive(origin, sup_type, sub_region, category);
|
||||
|
|
|
@ -25,8 +25,8 @@ use rustc_span::{Span, Symbol};
|
|||
use rustc_trait_selection::errors::{
|
||||
AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion,
|
||||
};
|
||||
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt;
|
||||
|
||||
use crate::{LateContext, LateLintPass, fluent_generated as fluent};
|
||||
|
||||
|
@ -190,9 +190,7 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
|||
let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
|
||||
let ocx = ObligationCtxt::new(&infcx);
|
||||
let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default();
|
||||
let implied_bounds =
|
||||
infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false);
|
||||
OutlivesEnvironment::with_bounds(param_env, implied_bounds)
|
||||
OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys)
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ impl<'tcx> Const<'tcx> {
|
|||
// Dont use the outer ty as on invalid code we can wind up with them not being the same.
|
||||
// this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
|
||||
// was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
|
||||
ty::ConstKind::Value(ty, _) => ty,
|
||||
ty::ConstKind::Value(cv) => cv.ty,
|
||||
_ => *ty,
|
||||
}
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> {
|
|||
pub fn is_required_const(&self) -> bool {
|
||||
match self {
|
||||
Const::Ty(_, c) => match c.kind() {
|
||||
ty::ConstKind::Value(_, _) => false, // already a value, cannot error
|
||||
ty::ConstKind::Value(_) => false, // already a value, cannot error
|
||||
_ => true,
|
||||
},
|
||||
Const::Val(..) => false, // already a value, cannot error
|
||||
|
@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> {
|
|||
pub fn try_to_scalar(self) -> Option<Scalar> {
|
||||
match self {
|
||||
Const::Ty(_, c) => match c.kind() {
|
||||
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
|
||||
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
|
||||
// A valtree of a type where leaves directly represent the scalar const value.
|
||||
// Just checking whether it is a leaf is insufficient as e.g. references are leafs
|
||||
// but the leaf value is the value they point to, not the reference itself!
|
||||
Some(valtree.unwrap_leaf().into())
|
||||
Some(cv.valtree.unwrap_leaf().into())
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> {
|
|||
match self {
|
||||
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
|
||||
Const::Ty(_, c) => match c.kind() {
|
||||
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
|
||||
Some(valtree.unwrap_leaf())
|
||||
}
|
||||
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
|
@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
|
||||
match c.kind() {
|
||||
ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))),
|
||||
ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
|
||||
ConstKind::Expr(_) => {
|
||||
bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
|
||||
}
|
||||
|
@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> {
|
|||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> Option<Scalar> {
|
||||
if let Const::Ty(_, c) = self
|
||||
&& let ty::ConstKind::Value(ty, val) = c.kind()
|
||||
&& ty.is_primitive()
|
||||
&& let ty::ConstKind::Value(cv) = c.kind()
|
||||
&& cv.ty.is_primitive()
|
||||
{
|
||||
// Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
|
||||
// are valtree leaves, and *not* on references. (References should return the
|
||||
// pointer here, which valtrees don't represent.)
|
||||
Some(val.unwrap_leaf().into())
|
||||
Some(cv.valtree.unwrap_leaf().into())
|
||||
} else {
|
||||
self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
|
||||
}
|
||||
|
@ -473,7 +471,7 @@ impl<'tcx> Const<'tcx> {
|
|||
// A valtree may be a reference. Valtree references correspond to a
|
||||
// different allocation each time they are evaluated. Valtrees for primitive
|
||||
// types are fine though.
|
||||
ty::ConstKind::Value(ty, _) => ty.is_primitive(),
|
||||
ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
|
||||
ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
|
||||
// This can happen if evaluation of a constant failed. The result does not matter
|
||||
// much since compilation is doomed.
|
||||
|
|
|
@ -1441,7 +1441,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
|||
ty::ConstKind::Unevaluated(uv) => {
|
||||
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
|
||||
}
|
||||
ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)),
|
||||
ty::ConstKind::Value(cv) => {
|
||||
format!("ty::Valtree({})", fmt_valtree(&cv.valtree))
|
||||
}
|
||||
// No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
|
||||
ty::ConstKind::Error(_) => "Error".to_string(),
|
||||
// These variants shouldn't exist in the MIR.
|
||||
|
|
|
@ -550,7 +550,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
|
||||
impl<'tcx> Key for ty::Value<'tcx> {
|
||||
type Cache<V> = DefaultCache<Self, V>;
|
||||
|
||||
fn default_span(&self, _: TyCtxt<'_>) -> Span {
|
||||
|
|
|
@ -1256,9 +1256,9 @@ rustc_queries! {
|
|||
desc { "evaluating type-level constant" }
|
||||
}
|
||||
|
||||
/// Converts a type level constant value into `ConstValue`
|
||||
query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
|
||||
desc { "converting type-level constant value to mir constant value"}
|
||||
/// Converts a type-level constant value into a MIR constant value.
|
||||
query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
|
||||
desc { "converting type-level constant value to MIR constant value"}
|
||||
}
|
||||
|
||||
/// Destructures array, ADT or tuple constants into the constants
|
||||
|
|
|
@ -5,7 +5,6 @@ use rustc_error_messages::MultiSpan;
|
|||
use rustc_macros::HashStable;
|
||||
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
|
||||
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
|
||||
mod int;
|
||||
|
@ -110,8 +109,8 @@ impl<'tcx> Const<'tcx> {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
|
||||
Const::new(tcx, ty::ConstKind::Value(ty, val))
|
||||
pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
|
||||
Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree }))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -214,47 +213,31 @@ impl<'tcx> Const<'tcx> {
|
|||
Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize)
|
||||
}
|
||||
|
||||
/// Panics if self.kind != ty::ConstKind::Value
|
||||
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
|
||||
/// Panics if `self.kind != ty::ConstKind::Value`.
|
||||
pub fn to_value(self) -> ty::Value<'tcx> {
|
||||
match self.kind() {
|
||||
ty::ConstKind::Value(ty, valtree) => (valtree, ty),
|
||||
ty::ConstKind::Value(cv) => cv,
|
||||
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to convert to a `ValTree`
|
||||
pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> {
|
||||
/// Attempts to convert to a value.
|
||||
///
|
||||
/// Note that this does not evaluate the constant.
|
||||
pub fn try_to_value(self) -> Option<ty::Value<'tcx>> {
|
||||
match self.kind() {
|
||||
ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)),
|
||||
ty::ConstKind::Value(cv) => Some(cv),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
|
||||
let (valtree, ty) = self.try_to_valtree()?;
|
||||
Some((valtree.try_to_scalar()?, ty))
|
||||
}
|
||||
|
||||
pub fn try_to_bool(self) -> Option<bool> {
|
||||
self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
|
||||
}
|
||||
|
||||
/// Convenience method to extract the value of a usize constant,
|
||||
/// useful to get the length of an array type.
|
||||
///
|
||||
/// Note that this does not evaluate the constant.
|
||||
#[inline]
|
||||
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
|
||||
self.try_to_valtree()?.0.try_to_target_usize(tcx)
|
||||
}
|
||||
|
||||
/// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
|
||||
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
|
||||
/// contains const generic parameters or pointers).
|
||||
#[inline]
|
||||
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
|
||||
let (scalar, ty) = self.try_to_scalar()?;
|
||||
let scalar = scalar.try_to_scalar_int().ok()?;
|
||||
let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(ty);
|
||||
let size = tcx.layout_of(input).ok()?.size;
|
||||
Some(scalar.to_bits(size))
|
||||
self.try_to_value()?.try_to_target_usize(tcx)
|
||||
}
|
||||
|
||||
pub fn is_ct_infer(self) -> bool {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
|
||||
use super::ScalarInt;
|
||||
use crate::mir::interpret::Scalar;
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
|
||||
#[derive(HashStable)]
|
||||
/// This datastructure is used to represent the value of constants used in the type system.
|
||||
///
|
||||
/// We explicitly choose a different datastructure from the way values are processed within
|
||||
|
@ -18,6 +16,8 @@ use crate::ty::{self, Ty, TyCtxt};
|
|||
///
|
||||
/// `ValTree` does not have this problem with representation, as it only contains integers or
|
||||
/// lists of (nested) `ValTree`.
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum ValTree<'tcx> {
|
||||
/// integers, `bool`, `char` are represented as scalars.
|
||||
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
|
||||
|
@ -79,10 +79,6 @@ impl<'tcx> ValTree<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
|
||||
self.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
|
||||
}
|
||||
|
||||
/// Get the values inside the ValTree as a slice of bytes. This only works for
|
||||
/// constants with types &str, &[u8], or [u8; _].
|
||||
pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
|
||||
|
@ -107,3 +103,54 @@ impl<'tcx> ValTree<'tcx> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-level constant value.
|
||||
///
|
||||
/// Represents a typed, fully evaluated constant.
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Value<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub valtree: ValTree<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Value<'tcx> {
|
||||
/// Attempts to extract the raw bits from the constant.
|
||||
///
|
||||
/// Fails if the value can't be represented as bits (e.g. because it is a reference
|
||||
/// or an aggregate).
|
||||
#[inline]
|
||||
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
|
||||
let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else {
|
||||
return None;
|
||||
};
|
||||
let scalar = self.valtree.try_to_scalar_int()?;
|
||||
let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty);
|
||||
let size = tcx.layout_of(input).ok()?.size;
|
||||
Some(scalar.to_bits(size))
|
||||
}
|
||||
|
||||
pub fn try_to_bool(self) -> Option<bool> {
|
||||
if !self.ty.is_bool() {
|
||||
return None;
|
||||
}
|
||||
self.valtree.try_to_scalar_int()?.try_to_bool().ok()
|
||||
}
|
||||
|
||||
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
|
||||
if !self.ty.is_usize() {
|
||||
return None;
|
||||
}
|
||||
self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
|
||||
fn ty(self) -> Ty<'tcx> {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn valtree(self) -> ValTree<'tcx> {
|
||||
self.valtree
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,10 +142,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
|
||||
type ParamConst = ty::ParamConst;
|
||||
type BoundConst = ty::BoundVar;
|
||||
type ValueConst = ty::ValTree<'tcx>;
|
||||
type ValueConst = ty::Value<'tcx>;
|
||||
type ExprConst = ty::Expr<'tcx>;
|
||||
type Region = Region<'tcx>;
|
||||
type ValTree = ty::ValTree<'tcx>;
|
||||
|
||||
type Region = Region<'tcx>;
|
||||
type EarlyParamRegion = ty::EarlyParamRegion;
|
||||
type LateParamRegion = ty::LateParamRegion;
|
||||
type BoundRegion = ty::BoundRegion;
|
||||
|
@ -1118,15 +1119,18 @@ impl<'tcx> CommonConsts<'tcx> {
|
|||
};
|
||||
|
||||
CommonConsts {
|
||||
unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())),
|
||||
true_: mk_const(ty::ConstKind::Value(
|
||||
types.bool,
|
||||
ty::ValTree::Leaf(ty::ScalarInt::TRUE),
|
||||
)),
|
||||
false_: mk_const(ty::ConstKind::Value(
|
||||
types.bool,
|
||||
ty::ValTree::Leaf(ty::ScalarInt::FALSE),
|
||||
)),
|
||||
unit: mk_const(ty::ConstKind::Value(ty::Value {
|
||||
ty: types.unit,
|
||||
valtree: ty::ValTree::zst(),
|
||||
})),
|
||||
true_: mk_const(ty::ConstKind::Value(ty::Value {
|
||||
ty: types.bool,
|
||||
valtree: ty::ValTree::Leaf(ty::ScalarInt::TRUE),
|
||||
})),
|
||||
false_: mk_const(ty::ConstKind::Value(ty::Value {
|
||||
ty: types.bool,
|
||||
valtree: ty::ValTree::Leaf(ty::ScalarInt::FALSE),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -381,7 +381,7 @@ impl FlagComputation {
|
|||
self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER);
|
||||
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
|
||||
}
|
||||
ty::ConstKind::Value(ty, _) => self.add_ty(ty),
|
||||
ty::ConstKind::Value(cv) => self.add_ty(cv.ty),
|
||||
ty::ConstKind::Expr(e) => self.add_args(e.args()),
|
||||
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
|
||||
}
|
||||
|
|
|
@ -504,6 +504,9 @@ impl<'tcx> SizeSkeleton<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Pattern types are always the same size as their base.
|
||||
ty::Pat(base, _) => SizeSkeleton::compute(base, tcx, typing_env),
|
||||
|
||||
_ => Err(err),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ pub use self::closure::{
|
|||
place_to_string_for_capture,
|
||||
};
|
||||
pub use self::consts::{
|
||||
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree,
|
||||
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, Value,
|
||||
};
|
||||
pub use self::context::{
|
||||
CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,
|
||||
|
|
|
@ -1484,8 +1484,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
_ => write!(self, "_")?,
|
||||
},
|
||||
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
|
||||
ty::ConstKind::Value(ty, value) => {
|
||||
return self.pretty_print_const_valtree(value, ty, print_ty);
|
||||
ty::ConstKind::Value(cv) => {
|
||||
return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty);
|
||||
}
|
||||
|
||||
ty::ConstKind::Bound(debruijn, bound_var) => {
|
||||
|
@ -1637,33 +1637,32 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
match ty.kind() {
|
||||
// Byte strings (&[u8; N])
|
||||
ty::Ref(_, inner, _) => {
|
||||
if let ty::Array(elem, len) = inner.kind() {
|
||||
if let ty::Uint(ty::UintTy::U8) = elem.kind() {
|
||||
if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() {
|
||||
match self.tcx().try_get_global_alloc(prov.alloc_id()) {
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
let len = int.to_bits(self.tcx().data_layout.pointer_size);
|
||||
let range =
|
||||
AllocRange { start: offset, size: Size::from_bytes(len) };
|
||||
if let Ok(byte_str) =
|
||||
alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
|
||||
{
|
||||
p!(pretty_print_byte_str(byte_str))
|
||||
} else {
|
||||
p!("<too short allocation>")
|
||||
}
|
||||
}
|
||||
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
p!(write("<static({:?})>", def_id))
|
||||
}
|
||||
Some(GlobalAlloc::Function { .. }) => p!("<function>"),
|
||||
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
|
||||
None => p!("<dangling pointer>"),
|
||||
if let ty::Array(elem, len) = inner.kind()
|
||||
&& let ty::Uint(ty::UintTy::U8) = elem.kind()
|
||||
&& let ty::ConstKind::Value(cv) = len.kind()
|
||||
&& let ty::ValTree::Leaf(int) = cv.valtree
|
||||
{
|
||||
match self.tcx().try_get_global_alloc(prov.alloc_id()) {
|
||||
Some(GlobalAlloc::Memory(alloc)) => {
|
||||
let len = int.to_bits(self.tcx().data_layout.pointer_size);
|
||||
let range = AllocRange { start: offset, size: Size::from_bytes(len) };
|
||||
if let Ok(byte_str) =
|
||||
alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
|
||||
{
|
||||
p!(pretty_print_byte_str(byte_str))
|
||||
} else {
|
||||
p!("<too short allocation>")
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
|
||||
Some(GlobalAlloc::Static(def_id)) => {
|
||||
p!(write("<static({:?})>", def_id))
|
||||
}
|
||||
Some(GlobalAlloc::Function { .. }) => p!("<function>"),
|
||||
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
|
||||
None => p!("<dangling pointer>"),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
ty::FnPtr(..) => {
|
||||
|
@ -1786,6 +1785,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
|
||||
fn pretty_print_const_valtree(
|
||||
&mut self,
|
||||
valtree: ty::ValTree<'tcx>,
|
||||
|
|
|
@ -162,16 +162,15 @@ impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> {
|
|||
impl<'tcx> fmt::Debug for ty::Const<'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// If this is a value, we spend some effort to make it look nice.
|
||||
if let ConstKind::Value(_, _) = self.kind() {
|
||||
if let ConstKind::Value(_) = self.kind() {
|
||||
return ty::tls::with(move |tcx| {
|
||||
// Somehow trying to lift the valtree results in lifetime errors, so we lift the
|
||||
// entire constant.
|
||||
// ValTrees aren't interned, so we lift the entire constant.
|
||||
let lifted = tcx.lift(*self).unwrap();
|
||||
let ConstKind::Value(ty, valtree) = lifted.kind() else {
|
||||
let ConstKind::Value(cv) = lifted.kind() else {
|
||||
bug!("we checked that this is a valtree")
|
||||
};
|
||||
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
|
||||
cx.pretty_print_const_valtree(valtree, ty, /*print_ty*/ true)?;
|
||||
cx.pretty_print_const_valtree(cv.valtree, cv.ty, /*print_ty*/ true)?;
|
||||
f.write_str(&cx.into_buffer())
|
||||
});
|
||||
}
|
||||
|
@ -589,9 +588,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
|
|||
}
|
||||
ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
|
||||
ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
|
||||
ConstKind::Value(t, v) => {
|
||||
ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?)
|
||||
}
|
||||
ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
|
||||
ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
|
||||
ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
|
||||
};
|
||||
|
@ -610,10 +607,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
|
|||
}
|
||||
ConstKind::Placeholder(p) => p.visit_with(visitor),
|
||||
ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
|
||||
ConstKind::Value(t, v) => {
|
||||
try_visit!(t.visit_with(visitor));
|
||||
v.visit_with(visitor)
|
||||
}
|
||||
ConstKind::Value(v) => v.visit_with(visitor),
|
||||
ConstKind::Error(e) => e.visit_with(visitor),
|
||||
ConstKind::Expr(e) => e.visit_with(visitor),
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
|
|||
| ty::ConstKind::Bound(..)
|
||||
| ty::ConstKind::Error(_) => {}
|
||||
|
||||
ty::ConstKind::Value(ty, _) => stack.push(ty.into()),
|
||||
ty::ConstKind::Value(cv) => stack.push(cv.ty.into()),
|
||||
|
||||
ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()),
|
||||
ty::ConstKind::Unevaluated(ct) => {
|
||||
|
|
|
@ -46,7 +46,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
|
||||
match c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
|
||||
ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty),
|
||||
ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
|
||||
_ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
|
||||
}
|
||||
}
|
||||
|
@ -214,6 +214,7 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
}
|
||||
|
||||
// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
|
||||
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
|
||||
let span = self.span;
|
||||
|
|
|
@ -522,7 +522,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
|
|||
// FIXME: See comment above -- we could fold the region separately or something.
|
||||
ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Value(_, _)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Error(_)
|
||||
| ty::ConstKind::Expr(_) => return c.super_fold_with(self),
|
||||
};
|
||||
|
|
|
@ -160,9 +160,7 @@ where
|
|||
ty::ConstKind::Infer(_) => {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
ty::ConstKind::Placeholder(_)
|
||||
| ty::ConstKind::Value(_, _)
|
||||
| ty::ConstKind::Error(_) => {
|
||||
ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
// We can freely ICE here as:
|
||||
|
@ -199,7 +197,7 @@ where
|
|||
unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
|
||||
}
|
||||
ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
|
||||
ty::ConstKind::Value(ty, _) => ty,
|
||||
ty::ConstKind::Value(cv) => cv.ty(),
|
||||
ty::ConstKind::Placeholder(placeholder) => {
|
||||
self.cx().find_const_ty_from_env(goal.param_env, placeholder)
|
||||
}
|
||||
|
|
|
@ -3114,9 +3114,8 @@ impl<'a> Parser<'a> {
|
|||
let span_before_body = this.prev_token.span;
|
||||
let arm_body;
|
||||
let is_fat_arrow = this.check(exp!(FatArrow));
|
||||
let is_almost_fat_arrow = TokenKind::FatArrow
|
||||
.similar_tokens()
|
||||
.is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind));
|
||||
let is_almost_fat_arrow =
|
||||
TokenKind::FatArrow.similar_tokens().contains(&this.token.kind);
|
||||
|
||||
// this avoids the compiler saying that a `,` or `}` was expected even though
|
||||
// the pattern isn't a never pattern (and thus an arm body is required)
|
||||
|
|
|
@ -924,10 +924,8 @@ impl<'a> Parser<'a> {
|
|||
|
||||
_ => {
|
||||
// Attempt to keep parsing if it was a similar separator.
|
||||
if let Some(tokens) = exp.tok.similar_tokens() {
|
||||
if tokens.contains(&self.token.kind) {
|
||||
self.bump();
|
||||
}
|
||||
if exp.tok.similar_tokens().contains(&self.token.kind) {
|
||||
self.bump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,12 +363,7 @@ impl<'a> Parser<'a> {
|
|||
/// Notifies of an error. The message doesn't actually need to be of type
|
||||
/// String, but I think it does when this eventually uses conditions so it
|
||||
/// might as well start using it now.
|
||||
fn err<S1: Into<String>, S2: Into<String>>(
|
||||
&mut self,
|
||||
description: S1,
|
||||
label: S2,
|
||||
span: InnerSpan,
|
||||
) {
|
||||
fn err(&mut self, description: impl Into<String>, label: impl Into<String>, span: InnerSpan) {
|
||||
self.errors.push(ParseError {
|
||||
description: description.into(),
|
||||
note: None,
|
||||
|
@ -382,11 +377,11 @@ impl<'a> Parser<'a> {
|
|||
/// Notifies of an error. The message doesn't actually need to be of type
|
||||
/// String, but I think it does when this eventually uses conditions so it
|
||||
/// might as well start using it now.
|
||||
fn err_with_note<S1: Into<String>, S2: Into<String>, S3: Into<String>>(
|
||||
fn err_with_note(
|
||||
&mut self,
|
||||
description: S1,
|
||||
label: S2,
|
||||
note: S3,
|
||||
description: impl Into<String>,
|
||||
label: impl Into<String>,
|
||||
note: impl Into<String>,
|
||||
span: InnerSpan,
|
||||
) {
|
||||
self.errors.push(ParseError {
|
||||
|
|
|
@ -103,7 +103,7 @@ fn encode_args<'tcx>(
|
|||
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
|
||||
fn encode_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
c: Const<'tcx>,
|
||||
ct: Const<'tcx>,
|
||||
ct_ty: Ty<'tcx>,
|
||||
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
|
||||
options: EncodeTyOptions,
|
||||
|
@ -111,7 +111,7 @@ fn encode_const<'tcx>(
|
|||
// L<element-type>[n][<element-value>]E as literal argument
|
||||
let mut s = String::from('L');
|
||||
|
||||
match c.kind() {
|
||||
match ct.kind() {
|
||||
// Const parameters
|
||||
ty::ConstKind::Param(..) => {
|
||||
// L<element-type>E as literal argument
|
||||
|
@ -121,18 +121,18 @@ fn encode_const<'tcx>(
|
|||
}
|
||||
|
||||
// Literal arguments
|
||||
ty::ConstKind::Value(ct_ty, ..) => {
|
||||
ty::ConstKind::Value(cv) => {
|
||||
// L<element-type>[n]<element-value>E as literal argument
|
||||
|
||||
// Element type
|
||||
s.push_str(&encode_ty(tcx, ct_ty, dict, options));
|
||||
s.push_str(&encode_ty(tcx, cv.ty, dict, options));
|
||||
|
||||
// The only allowed types of const values are bool, u8, u16, u32,
|
||||
// u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
|
||||
// bool value false is encoded as 0 and true as 1.
|
||||
match ct_ty.kind() {
|
||||
match cv.ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
let bits = c
|
||||
let bits = cv
|
||||
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
|
||||
.expect("expected monomorphic const in cfi");
|
||||
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
|
||||
|
@ -142,30 +142,30 @@ fn encode_const<'tcx>(
|
|||
let _ = write!(s, "{val}");
|
||||
}
|
||||
ty::Uint(_) => {
|
||||
let val = c
|
||||
let val = cv
|
||||
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
|
||||
.expect("expected monomorphic const in cfi");
|
||||
let _ = write!(s, "{val}");
|
||||
}
|
||||
ty::Bool => {
|
||||
let val = c.try_to_bool().expect("expected monomorphic const in cfi");
|
||||
let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
|
||||
let _ = write!(s, "{val}");
|
||||
}
|
||||
_ => {
|
||||
bug!("encode_const: unexpected type `{:?}`", ct_ty);
|
||||
bug!("encode_const: unexpected type `{:?}`", cv.ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
bug!("encode_const: unexpected kind `{:?}`", c.kind());
|
||||
bug!("encode_const: unexpected kind `{:?}`", ct.kind());
|
||||
}
|
||||
}
|
||||
|
||||
// Close the "L..E" pair
|
||||
s.push('E');
|
||||
|
||||
compress(dict, DictKey::Const(c), &mut s);
|
||||
compress(dict, DictKey::Const(ct), &mut s);
|
||||
|
||||
s
|
||||
}
|
||||
|
|
|
@ -51,8 +51,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
|
|||
// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match t.kind() {
|
||||
ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
ty::Closure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
|
@ -67,6 +66,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
|
|||
| ty::Tuple(..)
|
||||
| ty::UnsafeBinder(_) => t.super_fold_with(self),
|
||||
|
||||
// Don't transform the type of the array length and keep it as `usize`.
|
||||
// This is required for `try_to_target_usize` to work correctly.
|
||||
&ty::Array(inner, len) => {
|
||||
let inner = self.fold_ty(inner);
|
||||
Ty::new_array_with_const_len(self.tcx, inner, len)
|
||||
}
|
||||
|
||||
ty::Bool => {
|
||||
if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
|
||||
// Note: on all platforms that Rust's currently supports, its size and alignment
|
||||
|
|
|
@ -1803,6 +1803,7 @@ options! {
|
|||
"output statistics about monomorphization collection"),
|
||||
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
|
||||
"the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
|
||||
#[rustc_lint_opt_deny_field_access("use `Session::dwarf_version` instead of this field")]
|
||||
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
|
||||
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
||||
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
||||
|
|
|
@ -732,6 +732,11 @@ impl Session {
|
|||
self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
|
||||
}
|
||||
|
||||
/// Returns the DWARF version passed on the CLI or the default for the target.
|
||||
pub fn dwarf_version(&self) -> u32 {
|
||||
self.opts.unstable_opts.dwarf_version.unwrap_or(self.target.default_dwarf_version)
|
||||
}
|
||||
|
||||
pub fn stack_protector(&self) -> StackProtector {
|
||||
if self.target.options.supports_stack_protector {
|
||||
self.opts.unstable_opts.stack_protector
|
||||
|
@ -1263,8 +1268,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
|||
}
|
||||
|
||||
if sess.opts.unstable_opts.embed_source {
|
||||
let dwarf_version =
|
||||
sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
|
||||
let dwarf_version = sess.dwarf_version();
|
||||
|
||||
if dwarf_version < 5 {
|
||||
sess.dcx().emit_warn(errors::EmbedSourceInsufficientDwarfVersion { dwarf_version });
|
||||
|
|
|
@ -450,8 +450,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
|||
let tcx = tables.tcx;
|
||||
let ty = ty::Ty::new_static_str(tcx);
|
||||
let bytes = value.as_bytes();
|
||||
let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes);
|
||||
let val = tcx.valtree_to_const_val((ty, val_tree));
|
||||
let valtree = ty::ValTree::from_raw_bytes(tcx, bytes);
|
||||
let cv = ty::Value { ty, valtree };
|
||||
let val = tcx.valtree_to_const_val(cv);
|
||||
mir::Const::from_value(val, ty).stable(&mut tables)
|
||||
}
|
||||
|
||||
|
|
|
@ -418,23 +418,16 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
|
|||
type T = stable_mir::ty::TyConst;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
|
||||
let kind = match self.kind() {
|
||||
ty::ConstKind::Value(ty, val) => {
|
||||
let val = match val {
|
||||
ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar),
|
||||
ty::ValTree::Branch(branch) => {
|
||||
ty::ValTree::Branch(tables.tcx.lift(branch).unwrap())
|
||||
}
|
||||
};
|
||||
|
||||
let ty = tables.tcx.lift(ty).unwrap();
|
||||
let const_val = tables.tcx.valtree_to_const_val((ty, val));
|
||||
let ct = tables.tcx.lift(*self).unwrap();
|
||||
let kind = match ct.kind() {
|
||||
ty::ConstKind::Value(cv) => {
|
||||
let const_val = tables.tcx.valtree_to_const_val(cv);
|
||||
if matches!(const_val, mir::ConstValue::ZeroSized) {
|
||||
stable_mir::ty::TyConstKind::ZSTValue(ty.stable(tables))
|
||||
stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables))
|
||||
} else {
|
||||
stable_mir::ty::TyConstKind::Value(
|
||||
ty.stable(tables),
|
||||
alloc::new_allocation(ty, const_val, tables),
|
||||
cv.ty.stable(tables),
|
||||
alloc::new_allocation(cv.ty, const_val, tables),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -449,7 +442,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
|
|||
ty::ConstKind::Placeholder(_) => unimplemented!(),
|
||||
ty::ConstKind::Expr(_) => unimplemented!(),
|
||||
};
|
||||
let id = tables.intern_ty_const(tables.tcx.lift(*self).unwrap());
|
||||
let id = tables.intern_ty_const(ct);
|
||||
stable_mir::ty::TyConst::new(kind, id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,14 +274,15 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
|
|||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
// only print integers
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) if ty.is_integral() => {
|
||||
ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
|
||||
// The `pretty_print_const` formatting depends on -Zverbose-internals
|
||||
// flag, so we cannot reuse it here.
|
||||
let signed = matches!(ty.kind(), ty::Int(_));
|
||||
let scalar = cv.valtree.unwrap_leaf();
|
||||
let signed = matches!(cv.ty.kind(), ty::Int(_));
|
||||
write!(
|
||||
self,
|
||||
"{:#?}",
|
||||
ty::ConstInt::new(scalar, signed, ty.is_ptr_sized_integral())
|
||||
ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
|
||||
)?;
|
||||
}
|
||||
_ => self.write_str("_")?,
|
||||
|
|
|
@ -590,8 +590,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
|||
|
||||
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
|
||||
// We only mangle a typed value if the const can be evaluated.
|
||||
let (ct_ty, valtree) = match ct.kind() {
|
||||
ty::ConstKind::Value(ty, val) => (ty, val),
|
||||
let cv = match ct.kind() {
|
||||
ty::ConstKind::Value(cv) => cv,
|
||||
|
||||
// Should only be encountered within the identity-substituted
|
||||
// impl header of an item nested within an impl item.
|
||||
|
@ -619,13 +619,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let ty::Value { ty: ct_ty, valtree } = cv;
|
||||
let start = self.out.len();
|
||||
|
||||
match ct_ty.kind() {
|
||||
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
|
||||
ct_ty.print(self)?;
|
||||
|
||||
let mut bits = ct
|
||||
let mut bits = cv
|
||||
.try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized())
|
||||
.expect("expected const to be monomorphic");
|
||||
|
||||
|
|
|
@ -546,6 +546,7 @@ impl Target {
|
|||
key!(link_env_remove, list);
|
||||
key!(asm_args, list);
|
||||
key!(cpu);
|
||||
key!(need_explicit_cpu, bool);
|
||||
key!(features);
|
||||
key!(dynamic_linking, bool);
|
||||
key!(direct_access_external_data, Option<bool>);
|
||||
|
@ -720,6 +721,7 @@ impl ToJson for Target {
|
|||
target_option_val!(link_env_remove);
|
||||
target_option_val!(asm_args);
|
||||
target_option_val!(cpu);
|
||||
target_option_val!(need_explicit_cpu);
|
||||
target_option_val!(features);
|
||||
target_option_val!(dynamic_linking);
|
||||
target_option_val!(direct_access_external_data);
|
||||
|
|
|
@ -2254,6 +2254,9 @@ pub struct TargetOptions {
|
|||
/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
|
||||
/// to "generic".
|
||||
pub cpu: StaticCow<str>,
|
||||
/// Whether a cpu needs to be explicitly set.
|
||||
/// Set to true if there is no default cpu. Defaults to false.
|
||||
pub need_explicit_cpu: bool,
|
||||
/// Default target features to pass to LLVM. These features overwrite
|
||||
/// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`.
|
||||
/// Corresponds to `llc -mattr=$features`.
|
||||
|
@ -2686,6 +2689,7 @@ impl Default for TargetOptions {
|
|||
link_script: None,
|
||||
asm_args: cvs![],
|
||||
cpu: "generic".into(),
|
||||
need_explicit_cpu: false,
|
||||
features: "".into(),
|
||||
direct_access_external_data: None,
|
||||
dynamic_linking: false,
|
||||
|
|
|
@ -2023,14 +2023,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
_ => None,
|
||||
};
|
||||
if let Some(tykind) = tykind
|
||||
&& let hir::TyKind::Array(_, length) = tykind
|
||||
&& let Some((scalar, ty)) = sz.found.try_to_scalar()
|
||||
&& ty == self.tcx.types.usize
|
||||
&& let hir::TyKind::Array(_, length_arg) = tykind
|
||||
&& let Some(length_val) = sz.found.try_to_target_usize(self.tcx)
|
||||
{
|
||||
let span = length.span();
|
||||
Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {
|
||||
span,
|
||||
length: scalar.to_target_usize(&self.tcx).unwrap(),
|
||||
span: length_arg.span(),
|
||||
length: length_val,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -1,10 +1,73 @@
|
|||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{InferCtxt, RegionResolutionError};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::traits::ObligationCause;
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
use crate::traits::ScrubbedTraitError;
|
||||
use crate::traits::outlives_bounds::InferCtxtExt;
|
||||
|
||||
#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)]
|
||||
impl<'tcx> OutlivesEnvironment<'tcx> {
|
||||
fn new(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
) -> Self {
|
||||
Self::new_with_implied_bounds_compat(
|
||||
infcx,
|
||||
body_id,
|
||||
param_env,
|
||||
assumed_wf_tys,
|
||||
!infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_with_implied_bounds_compat(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
implied_bounds_compat: bool,
|
||||
) -> Self {
|
||||
let mut bounds = vec![];
|
||||
|
||||
for bound in param_env.caller_bounds() {
|
||||
if let Some(mut type_outlives) = bound.as_type_outlives_clause() {
|
||||
if infcx.next_trait_solver() {
|
||||
match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>(
|
||||
infcx.at(&ObligationCause::dummy(), param_env),
|
||||
type_outlives,
|
||||
) {
|
||||
Ok(new) => type_outlives = new,
|
||||
Err(_) => {
|
||||
infcx.dcx().delayed_bug(format!("could not normalize `{bound}`"));
|
||||
}
|
||||
}
|
||||
}
|
||||
bounds.push(type_outlives);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: This needs to be modified so that we normalize the known type
|
||||
// outlives obligations then elaborate them into their region/type components.
|
||||
// Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
|
||||
// if we can normalize `'a`.
|
||||
OutlivesEnvironment::from_normalized_bounds(
|
||||
param_env,
|
||||
bounds,
|
||||
infcx.implied_bounds_tys_with_compat(
|
||||
body_id,
|
||||
param_env,
|
||||
assumed_wf_tys,
|
||||
implied_bounds_compat,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[extension(pub trait InferCtxtRegionExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
|
@ -15,10 +78,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// Prefer this method over `resolve_regions_with_normalize`, unless you are
|
||||
/// doing something specific for normalization.
|
||||
fn resolve_regions(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
) -> Vec<RegionResolutionError<'tcx>> {
|
||||
self.resolve_regions_with_outlives_env(&OutlivesEnvironment::new(
|
||||
self,
|
||||
body_id,
|
||||
param_env,
|
||||
assumed_wf_tys,
|
||||
))
|
||||
}
|
||||
|
||||
/// Don't call this directly unless you know what you're doing.
|
||||
fn resolve_regions_with_outlives_env(
|
||||
&self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
) -> Vec<RegionResolutionError<'tcx>> {
|
||||
self.resolve_regions_with_normalize(outlives_env, |ty, origin| {
|
||||
self.resolve_regions_with_normalize(&outlives_env, |ty, origin| {
|
||||
let ty = self.resolve_vars_if_possible(ty);
|
||||
|
||||
if self.next_trait_solver() {
|
||||
|
|
|
@ -264,7 +264,7 @@ fn fulfillment_error_for_no_solution<'tcx>(
|
|||
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
|
||||
}
|
||||
ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
|
||||
ty::ConstKind::Value(ty, _) => ty,
|
||||
ty::ConstKind::Value(cv) => cv.ty,
|
||||
kind => span_bug!(
|
||||
obligation.cause.span,
|
||||
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::iter;
|
|||
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_middle::ty::{Region, RegionVid};
|
||||
use tracing::debug;
|
||||
|
@ -13,6 +14,7 @@ use tracing::debug;
|
|||
use super::*;
|
||||
use crate::errors::UnableToConstructConstantValue;
|
||||
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
|
||||
use crate::regions::OutlivesEnvironmentBuildExt;
|
||||
use crate::traits::project::ProjectAndUnifyResult;
|
||||
|
||||
// FIXME(twk): this is obviously not nice to duplicate like that
|
||||
|
@ -158,7 +160,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
|||
panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}");
|
||||
}
|
||||
|
||||
let outlives_env = OutlivesEnvironment::new(full_env);
|
||||
let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []);
|
||||
let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty));
|
||||
|
||||
let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone();
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::fmt::Debug;
|
|||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_errors::{Diag, EmissionGuarantee};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_middle::bug;
|
||||
|
@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn};
|
|||
use super::ObligationCtxt;
|
||||
use crate::error_reporting::traits::suggest_new_overflow_limit;
|
||||
use crate::infer::InferOk;
|
||||
use crate::infer::outlives::env::OutlivesEnvironment;
|
||||
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
|
||||
use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
@ -596,8 +595,7 @@ fn try_prove_negated_where_clause<'tcx>(
|
|||
// FIXME: We could use the assumed_wf_types from both impls, I think,
|
||||
// if that wasn't implemented just for LocalDefId, and we'd need to do
|
||||
// the normalization ourselves since this is totally fallible...
|
||||
let outlives_env = OutlivesEnvironment::new(param_env);
|
||||
let errors = ocx.resolve_regions(&outlives_env);
|
||||
let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []);
|
||||
if !errors.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>(
|
|||
ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Placeholder(_)
|
||||
| ty::ConstKind::Value(_, _)
|
||||
| ty::ConstKind::Value(_)
|
||||
| ty::ConstKind::Error(_) => return Ok(()),
|
||||
ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
|
||||
};
|
||||
|
|
|
@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace;
|
|||
use rustc_infer::infer::canonical::{
|
||||
Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
|
||||
};
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
|
||||
use rustc_infer::traits::PredicateObligations;
|
||||
use rustc_macros::extension;
|
||||
|
@ -217,14 +216,15 @@ where
|
|||
/// will result in region constraints getting ignored.
|
||||
pub fn resolve_regions_and_report_errors(
|
||||
self,
|
||||
generic_param_scope: LocalDefId,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let errors = self.infcx.resolve_regions(outlives_env);
|
||||
let errors = self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys);
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors))
|
||||
Err(self.infcx.err_ctxt().report_region_errors(body_id, &errors))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,9 +235,11 @@ where
|
|||
#[must_use]
|
||||
pub fn resolve_regions(
|
||||
self,
|
||||
outlives_env: &OutlivesEnvironment<'tcx>,
|
||||
body_id: LocalDefId,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
) -> Vec<RegionResolutionError<'tcx>> {
|
||||
self.infcx.resolve_regions(outlives_env)
|
||||
self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
|||
ty::ConstKind::Error(_) => {
|
||||
return ProcessResult::Changed(PendingPredicateObligations::new());
|
||||
}
|
||||
ty::ConstKind::Value(ty, _) => ty,
|
||||
ty::ConstKind::Value(cv) => cv.ty,
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
|
||||
}
|
||||
|
|
|
@ -4,13 +4,10 @@ use std::assert_matches::assert_matches;
|
|||
|
||||
use hir::LangItem;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
|
||||
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
|
||||
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
|
||||
|
||||
use super::outlives_bounds::InferCtxtExt;
|
||||
use crate::regions::InferCtxtRegionExt;
|
||||
use crate::traits::{self, FulfillmentError, ObligationCause};
|
||||
|
||||
|
@ -170,15 +167,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
|
|||
}
|
||||
|
||||
// Check regions assuming the self type of the impl is WF
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(
|
||||
param_env,
|
||||
parent_cause.body_id,
|
||||
&FxIndexSet::from_iter([self_type]),
|
||||
),
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
|
||||
if !errors.is_empty() {
|
||||
infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
|
||||
continue;
|
||||
|
@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>(
|
|||
}
|
||||
|
||||
// Check regions assuming the self type of the impl is WF
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
infcx.implied_bounds_tys(
|
||||
param_env,
|
||||
parent_cause.body_id,
|
||||
&FxIndexSet::from_iter([self_type]),
|
||||
),
|
||||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
|
||||
if !errors.is_empty() {
|
||||
infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
|
||||
}
|
||||
|
|
|
@ -290,12 +290,10 @@ fn do_normalize_predicates<'tcx>(
|
|||
|
||||
// We can use the `elaborated_env` here; the region code only
|
||||
// cares about declarations like `'a: 'b`.
|
||||
let outlives_env = OutlivesEnvironment::new(elaborated_env);
|
||||
|
||||
// FIXME: It's very weird that we ignore region obligations but apparently
|
||||
// still need to use `resolve_regions` as we need the resolved regions in
|
||||
// the normalized predicates.
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []);
|
||||
if !errors.is_empty() {
|
||||
tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_infer::infer::InferOk;
|
||||
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
|
||||
use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
|
||||
|
@ -12,9 +11,6 @@ use tracing::instrument;
|
|||
use crate::infer::InferCtxt;
|
||||
use crate::traits::{ObligationCause, ObligationCtxt};
|
||||
|
||||
pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
|
||||
pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
|
||||
|
||||
/// Implied bounds are region relationships that we deduce
|
||||
/// automatically. The idea is that (e.g.) a caller must check that a
|
||||
/// function's argument types are well-formed immediately before
|
||||
|
@ -110,36 +106,18 @@ fn implied_outlives_bounds<'a, 'tcx>(
|
|||
bounds
|
||||
}
|
||||
|
||||
#[extension(pub trait InferCtxtExt<'a, 'tcx>)]
|
||||
impl<'a, 'tcx: 'a> InferCtxt<'tcx> {
|
||||
/// Do *NOT* call this directly.
|
||||
fn implied_bounds_tys_compat(
|
||||
&'a self,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
#[extension(pub trait InferCtxtExt<'tcx>)]
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
/// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`
|
||||
/// instead if you're interested in the implied bounds for a given signature.
|
||||
fn implied_bounds_tys_with_compat<Tys: IntoIterator<Item = Ty<'tcx>>>(
|
||||
&self,
|
||||
body_id: LocalDefId,
|
||||
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
tys: Tys,
|
||||
compat: bool,
|
||||
) -> BoundsCompat<'a, 'tcx> {
|
||||
tys.iter()
|
||||
.flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat))
|
||||
}
|
||||
|
||||
/// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat`
|
||||
/// with `compat` set to `true`, otherwise `false`.
|
||||
fn implied_bounds_tys(
|
||||
&'a self,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
body_id: LocalDefId,
|
||||
tys: &'a FxIndexSet<Ty<'tcx>>,
|
||||
) -> Bounds<'a, 'tcx> {
|
||||
tys.iter().flat_map(move |ty| {
|
||||
implied_outlives_bounds(
|
||||
self,
|
||||
param_env,
|
||||
body_id,
|
||||
*ty,
|
||||
!self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat,
|
||||
)
|
||||
})
|
||||
) -> impl Iterator<Item = OutlivesBound<'tcx>> {
|
||||
tys.into_iter()
|
||||
.flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
return Ok(EvaluatedToAmbig);
|
||||
}
|
||||
ty::ConstKind::Error(_) => return Ok(EvaluatedToOk),
|
||||
ty::ConstKind::Value(ty, _) => ty,
|
||||
ty::ConstKind::Value(cv) => cv.ty,
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args)
|
||||
}
|
||||
|
|
|
@ -128,16 +128,16 @@ mod rustc {
|
|||
pub fn from_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ParamEnv<'tcx>,
|
||||
c: Const<'tcx>,
|
||||
ct: Const<'tcx>,
|
||||
) -> Option<Self> {
|
||||
use rustc_middle::ty::ScalarInt;
|
||||
use rustc_span::sym;
|
||||
|
||||
let Some((cv, ty)) = c.try_to_valtree() else {
|
||||
let Some(cv) = ct.try_to_value() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let adt_def = ty.ty_adt_def()?;
|
||||
let adt_def = cv.ty.ty_adt_def()?;
|
||||
|
||||
assert_eq!(
|
||||
tcx.require_lang_item(LangItem::TransmuteOpts, None),
|
||||
|
@ -147,7 +147,7 @@ mod rustc {
|
|||
);
|
||||
|
||||
let variant = adt_def.non_enum_variant();
|
||||
let fields = match cv {
|
||||
let fields = match cv.valtree {
|
||||
ValTree::Branch(branch) => branch,
|
||||
_ => {
|
||||
return Some(Self {
|
||||
|
|
|
@ -22,16 +22,16 @@ fn destructure_const<'tcx>(
|
|||
tcx: TyCtxt<'tcx>,
|
||||
const_: ty::Const<'tcx>,
|
||||
) -> ty::DestructuredConst<'tcx> {
|
||||
let ty::ConstKind::Value(ct_ty, valtree) = const_.kind() else {
|
||||
let ty::ConstKind::Value(cv) = const_.kind() else {
|
||||
bug!("cannot destructure constant {:?}", const_)
|
||||
};
|
||||
|
||||
let branches = match valtree {
|
||||
let branches = match cv.valtree {
|
||||
ty::ValTree::Branch(b) => b,
|
||||
_ => bug!("cannot destructure constant {:?}", const_),
|
||||
};
|
||||
|
||||
let (fields, variant) = match ct_ty.kind() {
|
||||
let (fields, variant) = match cv.ty.kind() {
|
||||
ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
|
||||
// construct the consts for the elements of the array/slice
|
||||
let field_consts = branches
|
||||
|
|
|
@ -144,13 +144,13 @@ fn univariant_uninterned<'tcx>(
|
|||
cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err))
|
||||
}
|
||||
|
||||
fn validate_const_with_value<'tcx>(
|
||||
fn extract_const_value<'tcx>(
|
||||
const_: ty::Const<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
cx: &LayoutCx<'tcx>,
|
||||
) -> Result<ty::Const<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
) -> Result<ty::Value<'tcx>, &'tcx LayoutError<'tcx>> {
|
||||
match const_.kind() {
|
||||
ty::ConstKind::Value(..) => Ok(const_),
|
||||
ty::ConstKind::Value(cv) => Ok(cv),
|
||||
ty::ConstKind::Error(guar) => {
|
||||
return Err(error(cx, LayoutError::ReferencesError(guar)));
|
||||
}
|
||||
|
@ -209,13 +209,12 @@ fn layout_of_uncached<'tcx>(
|
|||
&mut layout.backend_repr
|
||||
{
|
||||
if let Some(start) = start {
|
||||
scalar.valid_range_mut().start =
|
||||
validate_const_with_value(start, ty, cx)?
|
||||
.try_to_bits(tcx, cx.typing_env)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
scalar.valid_range_mut().start = extract_const_value(start, ty, cx)?
|
||||
.try_to_bits(tcx, cx.typing_env)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
}
|
||||
if let Some(end) = end {
|
||||
let mut end = validate_const_with_value(end, ty, cx)?
|
||||
let mut end = extract_const_value(end, ty, cx)?
|
||||
.try_to_bits(tcx, cx.typing_env)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
if !include_end {
|
||||
|
@ -348,9 +347,7 @@ fn layout_of_uncached<'tcx>(
|
|||
|
||||
// Arrays and slices.
|
||||
ty::Array(element, count) => {
|
||||
let count = validate_const_with_value(count, ty, cx)?
|
||||
.to_valtree()
|
||||
.0
|
||||
let count = extract_const_value(count, ty, cx)?
|
||||
.try_to_target_usize(tcx)
|
||||
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ pub enum ConstKind<I: Interner> {
|
|||
Unevaluated(ty::UnevaluatedConst<I>),
|
||||
|
||||
/// Used to hold computed value.
|
||||
Value(I::Ty, I::ValueConst),
|
||||
Value(I::ValueConst),
|
||||
|
||||
/// A placeholder for a const which could not be computed; this is
|
||||
/// propagated to avoid useless error messages.
|
||||
|
@ -52,7 +52,7 @@ impl<I: Interner> fmt::Debug for ConstKind<I> {
|
|||
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var),
|
||||
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
|
||||
Unevaluated(uv) => write!(f, "{uv:?}"),
|
||||
Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"),
|
||||
Value(val) => write!(f, "{val:?}"),
|
||||
Error(_) => write!(f, "{{const error}}"),
|
||||
Expr(expr) => write!(f, "{expr:?}"),
|
||||
}
|
||||
|
|
|
@ -483,8 +483,8 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
|
|||
};
|
||||
|
||||
match lhs.kind() {
|
||||
ty::ConstKind::Value(_, lhs_val) => match rhs.kind() {
|
||||
ty::ConstKind::Value(_, rhs_val) => lhs_val == rhs_val,
|
||||
ty::ConstKind::Value(lhs_val) => match rhs.kind() {
|
||||
ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(),
|
||||
_ => false,
|
||||
},
|
||||
|
||||
|
|
|
@ -286,6 +286,11 @@ pub trait Const<I: Interner<Const = Self>>:
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ValueConst<I: Interner<ValueConst = Self>>: Copy + Debug + Hash + Eq {
|
||||
fn ty(self) -> I::Ty;
|
||||
fn valtree(self) -> I::ValTree;
|
||||
}
|
||||
|
||||
pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
|
||||
fn args(self) -> I::GenericArgs;
|
||||
}
|
||||
|
|
|
@ -112,8 +112,9 @@ pub trait Interner:
|
|||
type PlaceholderConst: PlaceholderLike;
|
||||
type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
|
||||
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
|
||||
type ValueConst: Copy + Debug + Hash + Eq;
|
||||
type ValueConst: ValueConst<Self>;
|
||||
type ExprConst: ExprConst<Self>;
|
||||
type ValTree: Copy + Debug + Hash + Eq;
|
||||
|
||||
// Kinds of regions
|
||||
type Region: Region<Self>;
|
||||
|
|
|
@ -606,7 +606,9 @@ pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
|
|||
true
|
||||
}
|
||||
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
|
||||
(ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
|
||||
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => {
|
||||
a_val.valtree() == b_val.valtree()
|
||||
}
|
||||
|
||||
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
|
||||
// and is the better alternative to waiting until `generic_const_exprs` can
|
||||
|
|
|
@ -600,8 +600,8 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
|
|||
/// no cleanup is done on any of the keys, values and other children.
|
||||
/// This decreases the height by 1 and is the opposite of `push_internal_level`.
|
||||
///
|
||||
/// Requires exclusive access to the `NodeRef` object but not to the root node;
|
||||
/// it will not invalidate other handles or references to the root node.
|
||||
/// Does not invalidate any handles or references pointing into the subtree
|
||||
/// rooted at the first child of `self`.
|
||||
///
|
||||
/// Panics if there is no internal level, i.e., if the root node is a leaf.
|
||||
pub(super) fn pop_internal_level<A: Allocator + Clone>(&mut self, alloc: A) {
|
||||
|
|
|
@ -49,26 +49,26 @@ impl fmt::Display for AllocError {
|
|||
/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
|
||||
/// data described via [`Layout`][].
|
||||
///
|
||||
/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having
|
||||
/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
|
||||
/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers.
|
||||
/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
|
||||
/// allocated memory.
|
||||
///
|
||||
/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying
|
||||
/// allocator does not support this (like jemalloc) or return a null pointer (such as
|
||||
/// `libc::malloc`), this must be caught by the implementation.
|
||||
/// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying
|
||||
/// allocator does not support this (like jemalloc) or responds by returning a null pointer
|
||||
/// (such as `libc::malloc`), this must be caught by the implementation.
|
||||
///
|
||||
/// ### Currently allocated memory
|
||||
///
|
||||
/// Some of the methods require that a memory block be *currently allocated* via an allocator. This
|
||||
/// means that:
|
||||
/// Some of the methods require that a memory block is *currently allocated* by an allocator.
|
||||
/// This means that:
|
||||
/// * the starting address for that memory block was previously
|
||||
/// returned by [`allocate`], [`grow`], or [`shrink`], and
|
||||
/// * the memory block has not subsequently been deallocated.
|
||||
///
|
||||
/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or
|
||||
/// [`shrink`], and
|
||||
///
|
||||
/// * the memory block has not been subsequently deallocated, where blocks are either deallocated
|
||||
/// directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or
|
||||
/// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer
|
||||
/// remains valid.
|
||||
/// A memory block is deallocated by a call to [`deallocate`],
|
||||
/// or by a call to [`grow`] or [`shrink`] that returns `Ok`.
|
||||
/// A call to `grow` or `shrink` that returns `Err`,
|
||||
/// does not deallocate the memory block passed to it.
|
||||
///
|
||||
/// [`allocate`]: Allocator::allocate
|
||||
/// [`grow`]: Allocator::grow
|
||||
|
@ -77,32 +77,28 @@ impl fmt::Display for AllocError {
|
|||
///
|
||||
/// ### Memory fitting
|
||||
///
|
||||
/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to
|
||||
/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
|
||||
/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the
|
||||
/// following conditions must hold:
|
||||
///
|
||||
/// * The block must be allocated with the same alignment as [`layout.align()`], and
|
||||
///
|
||||
/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
|
||||
/// - `min` is the size of the layout most recently used to allocate the block, and
|
||||
/// - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`].
|
||||
/// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and
|
||||
/// * [`layout.size()`] must fall in the range `min ..= max`, where:
|
||||
/// - `min` is the size of the layout used to allocate the block, and
|
||||
/// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`].
|
||||
///
|
||||
/// [`layout.align()`]: Layout::align
|
||||
/// [`layout.size()`]: Layout::size
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to
|
||||
/// valid memory and retain their validity while they are [*currently allocated*] and the shorter
|
||||
/// of:
|
||||
/// - the borrow-checker lifetime of the allocator type itself.
|
||||
/// - as long as at least one of the instance and all of its clones has not been dropped.
|
||||
/// Memory blocks that are [*currently allocated*] by an allocator,
|
||||
/// must point to valid memory, and retain their validity while until either:
|
||||
/// - the memory block is deallocated, or
|
||||
/// - the allocator is dropped.
|
||||
///
|
||||
/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this
|
||||
/// allocator. A copied or cloned allocator must behave like the same allocator, and
|
||||
/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it
|
||||
/// A copied or cloned allocator must behave like the original allocator.
|
||||
///
|
||||
/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
|
||||
/// method of the allocator.
|
||||
/// A memory block which is [*currently allocated*] may be passed to
|
||||
/// any method of the allocator that accepts such an argument.
|
||||
///
|
||||
/// [*currently allocated*]: #currently-allocated-memory
|
||||
#[unstable(feature = "allocator_api", issue = "32838")]
|
||||
|
|
|
@ -276,10 +276,9 @@ impl<T: Copy> Clone for MaybeUninit<T> {
|
|||
impl<T> fmt::Debug for MaybeUninit<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// NB: there is no `.pad_fmt` so we can't use a simpler `format_args!("MaybeUninit<{..}>").
|
||||
// This needs to be adjusted if `MaybeUninit` moves modules.
|
||||
let full_name = type_name::<Self>();
|
||||
let short_name = full_name.split_once("mem::maybe_uninit::").unwrap().1;
|
||||
f.pad(short_name)
|
||||
let prefix_len = full_name.find("MaybeUninit").unwrap();
|
||||
f.pad(&full_name[prefix_len..])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1322,20 +1322,6 @@ pub enum FpCategory {
|
|||
Normal,
|
||||
}
|
||||
|
||||
macro_rules! from_str_radix_int_impl {
|
||||
($($t:ty)*) => {$(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl FromStr for $t {
|
||||
type Err = ParseIntError;
|
||||
#[inline]
|
||||
fn from_str(src: &str) -> Result<Self, ParseIntError> {
|
||||
<$t>::from_str_radix(src, 10)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
|
||||
|
||||
/// Determines if a string of text of that length of that radix could be guaranteed to be
|
||||
/// stored in the given type T.
|
||||
/// Note that if the radix is known to the compiler, it is just the check of digits.len that
|
||||
|
@ -1351,18 +1337,58 @@ pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8])
|
|||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
const fn from_str_radix_panic(radix: u32) -> ! {
|
||||
const fn from_ascii_radix_panic(radix: u32) -> ! {
|
||||
const_panic!(
|
||||
"from_str_radix_int: must lie in the range `[2, 36]`",
|
||||
"from_str_radix_int: must lie in the range `[2, 36]` - found {radix}",
|
||||
"from_ascii_radix: radix must lie in the range `[2, 36]`",
|
||||
"from_ascii_radix: radix must lie in the range `[2, 36]` - found {radix}",
|
||||
radix: u32 = radix,
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! from_str_radix {
|
||||
macro_rules! from_str_int_impl {
|
||||
($signedness:ident $($int_ty:ty)+) => {$(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl FromStr for $int_ty {
|
||||
type Err = ParseIntError;
|
||||
|
||||
/// Parses an integer from a string slice with decimal digits.
|
||||
///
|
||||
/// The characters are expected to be an optional
|
||||
#[doc = sign_dependent_expr!{
|
||||
$signedness ?
|
||||
if signed {
|
||||
" `+` or `-` "
|
||||
}
|
||||
if unsigned {
|
||||
" `+` "
|
||||
}
|
||||
}]
|
||||
/// sign followed by only digits. Leading and trailing non-digit characters (including
|
||||
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
|
||||
/// also represent an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str(\"+10\"), Ok(10));")]
|
||||
/// ```
|
||||
/// Trailing space returns error:
|
||||
/// ```
|
||||
/// # use std::str::FromStr;
|
||||
/// #
|
||||
#[doc = concat!("assert!(", stringify!($int_ty), "::from_str(\"1 \").is_err());")]
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from_str(src: &str) -> Result<$int_ty, ParseIntError> {
|
||||
<$int_ty>::from_str_radix(src, 10)
|
||||
}
|
||||
}
|
||||
|
||||
impl $int_ty {
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
/// Parses an integer from a string slice with digits in a given base.
|
||||
///
|
||||
/// The string is expected to be an optional
|
||||
#[doc = sign_dependent_expr!{
|
||||
|
@ -1375,7 +1401,7 @@ macro_rules! from_str_radix {
|
|||
}
|
||||
}]
|
||||
/// sign followed by only digits. Leading and trailing non-digit characters (including
|
||||
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
|
||||
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
|
||||
/// also represent an error.
|
||||
///
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
|
@ -1401,11 +1427,92 @@ macro_rules! from_str_radix {
|
|||
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
|
||||
#[inline]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
|
||||
<$int_ty>::from_ascii_radix(src.as_bytes(), radix)
|
||||
}
|
||||
|
||||
/// Parses an integer from an ASCII-byte slice with decimal digits.
|
||||
///
|
||||
/// The characters are expected to be an optional
|
||||
#[doc = sign_dependent_expr!{
|
||||
$signedness ?
|
||||
if signed {
|
||||
" `+` or `-` "
|
||||
}
|
||||
if unsigned {
|
||||
" `+` "
|
||||
}
|
||||
}]
|
||||
/// sign followed by only digits. Leading and trailing non-digit characters (including
|
||||
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
|
||||
/// also represent an error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// #![feature(int_from_ascii)]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii(b\"+10\"), Ok(10));")]
|
||||
/// ```
|
||||
/// Trailing space returns error:
|
||||
/// ```
|
||||
/// # #![feature(int_from_ascii)]
|
||||
/// #
|
||||
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii(b\"1 \").is_err());")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_from_ascii", issue = "134821")]
|
||||
#[inline]
|
||||
pub const fn from_ascii(src: &[u8]) -> Result<$int_ty, ParseIntError> {
|
||||
<$int_ty>::from_ascii_radix(src, 10)
|
||||
}
|
||||
|
||||
/// Parses an integer from an ASCII-byte slice with digits in a given base.
|
||||
///
|
||||
/// The characters are expected to be an optional
|
||||
#[doc = sign_dependent_expr!{
|
||||
$signedness ?
|
||||
if signed {
|
||||
" `+` or `-` "
|
||||
}
|
||||
if unsigned {
|
||||
" `+` "
|
||||
}
|
||||
}]
|
||||
/// sign followed by only digits. Leading and trailing non-digit characters (including
|
||||
/// whitespace) represent an error. Underscores (which are accepted in Rust literals)
|
||||
/// also represent an error.
|
||||
///
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// #![feature(int_from_ascii)]
|
||||
///
|
||||
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_ascii_radix(b\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
/// Trailing space returns error:
|
||||
/// ```
|
||||
/// # #![feature(int_from_ascii)]
|
||||
/// #
|
||||
#[doc = concat!("assert!(", stringify!($int_ty), "::from_ascii_radix(b\"1 \", 10).is_err());")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_from_ascii", issue = "134821")]
|
||||
#[inline]
|
||||
pub const fn from_ascii_radix(src: &[u8], radix: u32) -> Result<$int_ty, ParseIntError> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
||||
if 2 > radix || radix > 36 {
|
||||
from_str_radix_panic(radix);
|
||||
from_ascii_radix_panic(radix);
|
||||
}
|
||||
|
||||
if src.is_empty() {
|
||||
|
@ -1415,12 +1522,6 @@ macro_rules! from_str_radix {
|
|||
#[allow(unused_comparisons)]
|
||||
let is_signed_ty = 0 > <$int_ty>::MIN;
|
||||
|
||||
// all valid digits are ascii, so we will just iterate over the utf8 bytes
|
||||
// and cast them to chars. .to_digit() will safely return None for anything
|
||||
// other than a valid ascii digit for the given radix, including the first-byte
|
||||
// of multi-byte sequences
|
||||
let src = src.as_bytes();
|
||||
|
||||
let (is_positive, mut digits) = match src {
|
||||
[b'+' | b'-'] => {
|
||||
return Err(PIE { kind: InvalidDigit });
|
||||
|
@ -1496,67 +1597,8 @@ macro_rules! from_str_radix {
|
|||
Ok(result)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
)*}
|
||||
}
|
||||
|
||||
from_str_radix! { unsigned u8 u16 u32 u64 u128 }
|
||||
from_str_radix! { signed i8 i16 i32 i64 i128 }
|
||||
|
||||
// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two
|
||||
// identical functions.
|
||||
macro_rules! from_str_radix_size_impl {
|
||||
($($signedness:ident $t:ident $size:ty),*) => {$(
|
||||
impl $size {
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
///
|
||||
/// The string is expected to be an optional
|
||||
#[doc = sign_dependent_expr!{
|
||||
$signedness ?
|
||||
if signed {
|
||||
" `+` or `-` "
|
||||
}
|
||||
if unsigned {
|
||||
" `+` "
|
||||
}
|
||||
}]
|
||||
/// sign followed by only digits. Leading and trailing non-digit characters (including
|
||||
/// whitespace) represent an error. Underscores (which are accepted in rust literals)
|
||||
/// also represent an error.
|
||||
///
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
/// Trailing space returns error:
|
||||
/// ```
|
||||
#[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
|
||||
#[inline]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
|
||||
match <$t>::from_str_radix(src, radix) {
|
||||
Ok(x) => Ok(x as $size),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
})*}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize }
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize }
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize }
|
||||
from_str_int_impl! { signed isize i8 i16 i32 i64 i128 }
|
||||
from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 }
|
||||
|
|
|
@ -12,6 +12,9 @@ pub use crate::marker::{Copy, Send, Sized, Sync, Unpin};
|
|||
#[stable(feature = "core_prelude", since = "1.4.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::ops::{Drop, Fn, FnMut, FnOnce};
|
||||
#[stable(feature = "async_closure", since = "1.85.0")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::ops::{AsyncFn, AsyncFnMut, AsyncFnOnce};
|
||||
|
||||
// Re-exported functions
|
||||
#[stable(feature = "core_prelude", since = "1.4.0")]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
|
||||
use crate::{cmp, ptr};
|
||||
|
||||
type BufType = [usize; 32];
|
||||
|
||||
/// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first
|
||||
/// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the
|
||||
/// right.
|
||||
|
@ -8,14 +10,82 @@ use crate::{cmp, ptr};
|
|||
/// # Safety
|
||||
///
|
||||
/// The specified range must be valid for reading and writing.
|
||||
#[inline]
|
||||
pub(super) unsafe fn ptr_rotate<T>(left: usize, mid: *mut T, right: usize) {
|
||||
if T::IS_ZST {
|
||||
return;
|
||||
}
|
||||
// abort early if the rotate is a no-op
|
||||
if (left == 0) || (right == 0) {
|
||||
return;
|
||||
}
|
||||
// `T` is not a zero-sized type, so it's okay to divide by its size.
|
||||
if !cfg!(feature = "optimize_for_size")
|
||||
&& cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>()
|
||||
{
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { ptr_rotate_memmove(left, mid, right) };
|
||||
} else if !cfg!(feature = "optimize_for_size")
|
||||
&& ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>()))
|
||||
{
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { ptr_rotate_gcd(left, mid, right) }
|
||||
} else {
|
||||
// SAFETY: guaranteed by the caller
|
||||
unsafe { ptr_rotate_swap(left, mid, right) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Algorithm 1 is used if `min(left, right)` is small enough to fit onto a stack buffer. The
|
||||
/// `min(left, right)` elements are copied onto the buffer, `memmove` is applied to the others, and
|
||||
/// the ones on the buffer are moved back into the hole on the opposite side of where they
|
||||
/// originated.
|
||||
///
|
||||
/// # Algorithm
|
||||
/// # Safety
|
||||
///
|
||||
/// Algorithm 1 is used for small values of `left + right` or for large `T`. The elements are moved
|
||||
/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps
|
||||
/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at
|
||||
/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over
|
||||
/// elements. For example:
|
||||
/// The specified range must be valid for reading and writing.
|
||||
#[inline]
|
||||
unsafe fn ptr_rotate_memmove<T>(left: usize, mid: *mut T, right: usize) {
|
||||
// The `[T; 0]` here is to ensure this is appropriately aligned for T
|
||||
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
|
||||
let buf = rawarray.as_mut_ptr() as *mut T;
|
||||
// SAFETY: `mid-left <= mid-left+right < mid+right`
|
||||
let dim = unsafe { mid.sub(left).add(right) };
|
||||
if left <= right {
|
||||
// SAFETY:
|
||||
//
|
||||
// 1) The `if` condition about the sizes ensures `[mid-left; left]` will fit in
|
||||
// `buf` without overflow and `buf` was created just above and so cannot be
|
||||
// overlapped with any value of `[mid-left; left]`
|
||||
// 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
|
||||
// about overlaps here.
|
||||
// 3) The `if` condition about `left <= right` ensures writing `left` elements to
|
||||
// `dim = mid-left+right` is valid because:
|
||||
// - `buf` is valid and `left` elements were written in it in 1)
|
||||
// - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
|
||||
unsafe {
|
||||
// 1)
|
||||
ptr::copy_nonoverlapping(mid.sub(left), buf, left);
|
||||
// 2)
|
||||
ptr::copy(mid, mid.sub(left), right);
|
||||
// 3)
|
||||
ptr::copy_nonoverlapping(buf, dim, left);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: same reasoning as above but with `left` and `right` reversed
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(mid, buf, right);
|
||||
ptr::copy(mid.sub(left), dim, left);
|
||||
ptr::copy_nonoverlapping(buf, mid.sub(left), right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Algorithm 2 is used for small values of `left + right` or for large `T`. The elements
|
||||
/// are moved into their final positions one at a time starting at `mid - left` and advancing by
|
||||
/// `right` steps modulo `left + right`, such that only one temporary is needed. Eventually, we
|
||||
/// arrive back at `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps
|
||||
/// skipped over elements. For example:
|
||||
/// ```text
|
||||
/// left = 10, right = 6
|
||||
/// the `^` indicates an element in its final place
|
||||
|
@ -39,17 +109,104 @@ use crate::{cmp, ptr};
|
|||
/// `gcd(left + right, right)` value). The end result is that all elements are finalized once and
|
||||
/// only once.
|
||||
///
|
||||
/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to
|
||||
/// fit onto a stack buffer. The `min(left, right)` elements are copied onto the buffer, `memmove`
|
||||
/// is applied to the others, and the ones on the buffer are moved back into the hole on the
|
||||
/// opposite side of where they originated.
|
||||
///
|
||||
/// Algorithms that can be vectorized outperform the above once `left + right` becomes large enough.
|
||||
/// Algorithm 1 can be vectorized by chunking and performing many rounds at once, but there are too
|
||||
/// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too
|
||||
/// few rounds on average until `left + right` is enormous, and the worst case of a single
|
||||
/// round is always there. Instead, algorithm 3 utilizes repeated swapping of
|
||||
/// `min(left, right)` elements until a smaller rotate problem is left.
|
||||
/// round is always there.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The specified range must be valid for reading and writing.
|
||||
#[inline]
|
||||
unsafe fn ptr_rotate_gcd<T>(left: usize, mid: *mut T, right: usize) {
|
||||
// Algorithm 2
|
||||
// Microbenchmarks indicate that the average performance for random shifts is better all
|
||||
// the way until about `left + right == 32`, but the worst case performance breaks even
|
||||
// around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
|
||||
// `usize`s, this algorithm also outperforms other algorithms.
|
||||
// SAFETY: callers must ensure `mid - left` is valid for reading and writing.
|
||||
let x = unsafe { mid.sub(left) };
|
||||
// beginning of first round
|
||||
// SAFETY: see previous comment.
|
||||
let mut tmp: T = unsafe { x.read() };
|
||||
let mut i = right;
|
||||
// `gcd` can be found before hand by calculating `gcd(left + right, right)`,
|
||||
// but it is faster to do one loop which calculates the gcd as a side effect, then
|
||||
// doing the rest of the chunk
|
||||
let mut gcd = right;
|
||||
// benchmarks reveal that it is faster to swap temporaries all the way through instead
|
||||
// of reading one temporary once, copying backwards, and then writing that temporary at
|
||||
// the very end. This is possibly due to the fact that swapping or replacing temporaries
|
||||
// uses only one memory address in the loop instead of needing to manage two.
|
||||
loop {
|
||||
// [long-safety-expl]
|
||||
// SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and
|
||||
// writing.
|
||||
//
|
||||
// - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right`
|
||||
// - `i <= left+right-1` is always true
|
||||
// - if `i < left`, `right` is added so `i < left+right` and on the next
|
||||
// iteration `left` is removed from `i` so it doesn't go further
|
||||
// - if `i >= left`, `left` is removed immediately and so it doesn't go further.
|
||||
// - overflows cannot happen for `i` since the function's safety contract ask for
|
||||
// `mid+right-1 = x+left+right` to be valid for writing
|
||||
// - underflows cannot happen because `i` must be bigger or equal to `left` for
|
||||
// a subtraction of `left` to happen.
|
||||
//
|
||||
// So `x+i` is valid for reading and writing if the caller respected the contract
|
||||
tmp = unsafe { x.add(i).replace(tmp) };
|
||||
// instead of incrementing `i` and then checking if it is outside the bounds, we
|
||||
// check if `i` will go outside the bounds on the next increment. This prevents
|
||||
// any wrapping of pointers or `usize`.
|
||||
if i >= left {
|
||||
i -= left;
|
||||
if i == 0 {
|
||||
// end of first round
|
||||
// SAFETY: tmp has been read from a valid source and x is valid for writing
|
||||
// according to the caller.
|
||||
unsafe { x.write(tmp) };
|
||||
break;
|
||||
}
|
||||
// this conditional must be here if `left + right >= 15`
|
||||
if i < gcd {
|
||||
gcd = i;
|
||||
}
|
||||
} else {
|
||||
i += right;
|
||||
}
|
||||
}
|
||||
// finish the chunk with more rounds
|
||||
for start in 1..gcd {
|
||||
// SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for
|
||||
// reading and writing as per the function's safety contract, see [long-safety-expl]
|
||||
// above
|
||||
tmp = unsafe { x.add(start).read() };
|
||||
// [safety-expl-addition]
|
||||
//
|
||||
// Here `start < gcd` so `start < right` so `i < right+right`: `right` being the
|
||||
// greatest common divisor of `(left+right, right)` means that `left = right` so
|
||||
// `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing
|
||||
// according to the function's safety contract.
|
||||
i = start + right;
|
||||
loop {
|
||||
// SAFETY: see [long-safety-expl] and [safety-expl-addition]
|
||||
tmp = unsafe { x.add(i).replace(tmp) };
|
||||
if i >= left {
|
||||
i -= left;
|
||||
if i == start {
|
||||
// SAFETY: see [long-safety-expl] and [safety-expl-addition]
|
||||
unsafe { x.add(start).write(tmp) };
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i += right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements.
|
||||
///
|
||||
/// ///
|
||||
/// ```text
|
||||
/// left = 11, right = 4
|
||||
/// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3]
|
||||
|
@ -60,144 +217,14 @@ use crate::{cmp, ptr};
|
|||
/// we cannot swap any more, but a smaller rotation problem is left to solve
|
||||
/// ```
|
||||
/// when `left < right` the swapping happens from the left instead.
|
||||
pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
|
||||
type BufType = [usize; 32];
|
||||
if T::IS_ZST {
|
||||
return;
|
||||
}
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The specified range must be valid for reading and writing.
|
||||
#[inline]
|
||||
unsafe fn ptr_rotate_swap<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
|
||||
loop {
|
||||
// N.B. the below algorithms can fail if these cases are not checked
|
||||
if (right == 0) || (left == 0) {
|
||||
return;
|
||||
}
|
||||
if !cfg!(feature = "optimize_for_size")
|
||||
&& ((left + right < 24) || (mem::size_of::<T>() > mem::size_of::<[usize; 4]>()))
|
||||
{
|
||||
// Algorithm 1
|
||||
// Microbenchmarks indicate that the average performance for random shifts is better all
|
||||
// the way until about `left + right == 32`, but the worst case performance breaks even
|
||||
// around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
|
||||
// `usize`s, this algorithm also outperforms other algorithms.
|
||||
// SAFETY: callers must ensure `mid - left` is valid for reading and writing.
|
||||
let x = unsafe { mid.sub(left) };
|
||||
// beginning of first round
|
||||
// SAFETY: see previous comment.
|
||||
let mut tmp: T = unsafe { x.read() };
|
||||
let mut i = right;
|
||||
// `gcd` can be found before hand by calculating `gcd(left + right, right)`,
|
||||
// but it is faster to do one loop which calculates the gcd as a side effect, then
|
||||
// doing the rest of the chunk
|
||||
let mut gcd = right;
|
||||
// benchmarks reveal that it is faster to swap temporaries all the way through instead
|
||||
// of reading one temporary once, copying backwards, and then writing that temporary at
|
||||
// the very end. This is possibly due to the fact that swapping or replacing temporaries
|
||||
// uses only one memory address in the loop instead of needing to manage two.
|
||||
loop {
|
||||
// [long-safety-expl]
|
||||
// SAFETY: callers must ensure `[left, left+mid+right)` are all valid for reading and
|
||||
// writing.
|
||||
//
|
||||
// - `i` start with `right` so `mid-left <= x+i = x+right = mid-left+right < mid+right`
|
||||
// - `i <= left+right-1` is always true
|
||||
// - if `i < left`, `right` is added so `i < left+right` and on the next
|
||||
// iteration `left` is removed from `i` so it doesn't go further
|
||||
// - if `i >= left`, `left` is removed immediately and so it doesn't go further.
|
||||
// - overflows cannot happen for `i` since the function's safety contract ask for
|
||||
// `mid+right-1 = x+left+right` to be valid for writing
|
||||
// - underflows cannot happen because `i` must be bigger or equal to `left` for
|
||||
// a subtraction of `left` to happen.
|
||||
//
|
||||
// So `x+i` is valid for reading and writing if the caller respected the contract
|
||||
tmp = unsafe { x.add(i).replace(tmp) };
|
||||
// instead of incrementing `i` and then checking if it is outside the bounds, we
|
||||
// check if `i` will go outside the bounds on the next increment. This prevents
|
||||
// any wrapping of pointers or `usize`.
|
||||
if i >= left {
|
||||
i -= left;
|
||||
if i == 0 {
|
||||
// end of first round
|
||||
// SAFETY: tmp has been read from a valid source and x is valid for writing
|
||||
// according to the caller.
|
||||
unsafe { x.write(tmp) };
|
||||
break;
|
||||
}
|
||||
// this conditional must be here if `left + right >= 15`
|
||||
if i < gcd {
|
||||
gcd = i;
|
||||
}
|
||||
} else {
|
||||
i += right;
|
||||
}
|
||||
}
|
||||
// finish the chunk with more rounds
|
||||
for start in 1..gcd {
|
||||
// SAFETY: `gcd` is at most equal to `right` so all values in `1..gcd` are valid for
|
||||
// reading and writing as per the function's safety contract, see [long-safety-expl]
|
||||
// above
|
||||
tmp = unsafe { x.add(start).read() };
|
||||
// [safety-expl-addition]
|
||||
//
|
||||
// Here `start < gcd` so `start < right` so `i < right+right`: `right` being the
|
||||
// greatest common divisor of `(left+right, right)` means that `left = right` so
|
||||
// `i < left+right` so `x+i = mid-left+i` is always valid for reading and writing
|
||||
// according to the function's safety contract.
|
||||
i = start + right;
|
||||
loop {
|
||||
// SAFETY: see [long-safety-expl] and [safety-expl-addition]
|
||||
tmp = unsafe { x.add(i).replace(tmp) };
|
||||
if i >= left {
|
||||
i -= left;
|
||||
if i == start {
|
||||
// SAFETY: see [long-safety-expl] and [safety-expl-addition]
|
||||
unsafe { x.add(start).write(tmp) };
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i += right;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
// `T` is not a zero-sized type, so it's okay to divide by its size.
|
||||
} else if !cfg!(feature = "optimize_for_size")
|
||||
&& cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>()
|
||||
{
|
||||
// Algorithm 2
|
||||
// The `[T; 0]` here is to ensure this is appropriately aligned for T
|
||||
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit();
|
||||
let buf = rawarray.as_mut_ptr() as *mut T;
|
||||
// SAFETY: `mid-left <= mid-left+right < mid+right`
|
||||
let dim = unsafe { mid.sub(left).add(right) };
|
||||
if left <= right {
|
||||
// SAFETY:
|
||||
//
|
||||
// 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in
|
||||
// `buf` without overflow and `buf` was created just above and so cannot be
|
||||
// overlapped with any value of `[mid-left; left]`
|
||||
// 2) [mid-left, mid+right) are all valid for reading and writing and we don't care
|
||||
// about overlaps here.
|
||||
// 3) The `if` condition about `left <= right` ensures writing `left` elements to
|
||||
// `dim = mid-left+right` is valid because:
|
||||
// - `buf` is valid and `left` elements were written in it in 1)
|
||||
// - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)`
|
||||
unsafe {
|
||||
// 1)
|
||||
ptr::copy_nonoverlapping(mid.sub(left), buf, left);
|
||||
// 2)
|
||||
ptr::copy(mid, mid.sub(left), right);
|
||||
// 3)
|
||||
ptr::copy_nonoverlapping(buf, dim, left);
|
||||
}
|
||||
} else {
|
||||
// SAFETY: same reasoning as above but with `left` and `right` reversed
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(mid, buf, right);
|
||||
ptr::copy(mid.sub(left), dim, left);
|
||||
ptr::copy_nonoverlapping(buf, mid.sub(left), right);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if left >= right {
|
||||
if left >= right {
|
||||
// Algorithm 3
|
||||
// There is an alternate way of swapping that involves finding where the last swap
|
||||
// of this algorithm would be, and swapping using that last chunk instead of swapping
|
||||
|
@ -233,5 +260,8 @@ pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut right:
|
|||
}
|
||||
}
|
||||
}
|
||||
if (right == 0) || (left == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -629,9 +629,9 @@ impl File {
|
|||
/// This acquires an exclusive advisory lock; no other file handle to this file may acquire
|
||||
/// another lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then an exclusive lock is held.
|
||||
/// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact
|
||||
/// behavior is unspecified and platform dependent, including the possibility that it will
|
||||
/// deadlock. However, if this method returns, then an exclusive lock is held.
|
||||
///
|
||||
/// If the file not open for writing, it is unspecified whether this function returns an error.
|
||||
///
|
||||
|
@ -639,6 +639,9 @@ impl File {
|
|||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// The lock will be released when this file (along with any other file descriptors/handles
|
||||
/// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag,
|
||||
|
@ -671,19 +674,22 @@ impl File {
|
|||
self.inner.lock()
|
||||
}
|
||||
|
||||
/// Acquire a shared advisory lock on the file. Blocks until the lock can be acquired.
|
||||
/// Acquire a shared (non-exclusive) advisory lock on the file. Blocks until the lock can be acquired.
|
||||
///
|
||||
/// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
|
||||
/// none may hold an exclusive lock.
|
||||
/// none may hold an exclusive lock at the same time.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then a shared lock is held.
|
||||
/// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
|
||||
/// behavior is unspecified and platform dependent, including the possibility that it will
|
||||
/// deadlock. However, if this method returns, then a shared lock is held.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
|
||||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// The lock will be released when this file (along with any other file descriptors/handles
|
||||
/// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag,
|
||||
|
@ -716,14 +722,18 @@ impl File {
|
|||
self.inner.lock_shared()
|
||||
}
|
||||
|
||||
/// Acquire an exclusive advisory lock on the file. Returns `Ok(false)` if the file is locked.
|
||||
/// Try to acquire an exclusive advisory lock on the file.
|
||||
///
|
||||
/// Returns `Ok(false)` if a different lock is already held on this file (via another
|
||||
/// handle/descriptor).
|
||||
///
|
||||
/// This acquires an exclusive advisory lock; no other file handle to this file may acquire
|
||||
/// another lock.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then an exclusive lock is held.
|
||||
/// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
|
||||
/// behavior is unspecified and platform dependent, including the possibility that it will
|
||||
/// deadlock. However, if this method returns `Ok(true)`, then it has acquired an exclusive
|
||||
/// lock.
|
||||
///
|
||||
/// If the file not open for writing, it is unspecified whether this function returns an error.
|
||||
///
|
||||
|
@ -731,6 +741,9 @@ impl File {
|
|||
/// [`try_lock_shared`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// The lock will be released when this file (along with any other file descriptors/handles
|
||||
/// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and
|
||||
|
@ -764,20 +777,25 @@ impl File {
|
|||
self.inner.try_lock()
|
||||
}
|
||||
|
||||
/// Acquire a shared advisory lock on the file.
|
||||
/// Returns `Ok(false)` if the file is exclusively locked.
|
||||
/// Try to acquire a shared (non-exclusive) advisory lock on the file.
|
||||
///
|
||||
/// Returns `Ok(false)` if an exclusive lock is already held on this file (via another
|
||||
/// handle/descriptor).
|
||||
///
|
||||
/// This acquires a shared advisory lock; more than one file handle may hold a shared lock, but
|
||||
/// none may hold an exclusive lock.
|
||||
/// none may hold an exclusive lock at the same time.
|
||||
///
|
||||
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is
|
||||
/// unspecified and platform dependent, including the possibility that it will deadlock.
|
||||
/// However, if this method returns, then a shared lock is held.
|
||||
/// However, if this method returns `Ok(true)`, then it has acquired a shared lock.
|
||||
///
|
||||
/// Note, this is an advisory lock meant to interact with [`lock`], [`try_lock`],
|
||||
/// [`try_lock`], and [`unlock`]. Its interactions with other methods, such as [`read`]
|
||||
/// and [`write`] are platform specific, and it may or may not cause non-lockholders to block.
|
||||
///
|
||||
/// The lock will be released when this file (along with any other file descriptors/handles
|
||||
/// duplicated or inherited from it) is closed, or if the [`unlock`] method is called.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and
|
||||
|
@ -813,7 +831,12 @@ impl File {
|
|||
|
||||
/// Release all locks on the file.
|
||||
///
|
||||
/// All remaining locks are released when the file handle, and all clones of it, are dropped.
|
||||
/// All locks are released when the file (along with any other file descriptors/handles
|
||||
/// duplicated or inherited from it) is closed. This method allows releasing locks without
|
||||
/// closing the file.
|
||||
///
|
||||
/// If no lock is currently held via this file descriptor/handle, this method may return an
|
||||
/// error, or may return successfully without taking any action.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
|
|
|
@ -14,10 +14,12 @@ use r_efi::protocols::{device_path, device_path_to_text, shell};
|
|||
|
||||
use crate::ffi::{OsStr, OsString};
|
||||
use crate::io::{self, const_error};
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{MaybeUninit, size_of};
|
||||
use crate::os::uefi::env::boot_services;
|
||||
use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
|
||||
use crate::os::uefi::{self};
|
||||
use crate::path::Path;
|
||||
use crate::ptr::NonNull;
|
||||
use crate::slice;
|
||||
use crate::sync::atomic::{AtomicPtr, Ordering};
|
||||
|
@ -278,6 +280,10 @@ impl OwnedDevicePath {
|
|||
pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
|
||||
BorrowedDevicePath::new(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedDevicePath {
|
||||
|
@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath {
|
|||
|
||||
impl crate::fmt::Debug for OwnedDevicePath {
|
||||
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
|
||||
match device_path_to_text(self.0) {
|
||||
match self.borrow().to_text() {
|
||||
Ok(p) => p.fmt(f),
|
||||
Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BorrowedDevicePath<'a> {
|
||||
protocol: NonNull<r_efi::protocols::device_path::Protocol>,
|
||||
phantom: PhantomData<&'a r_efi::protocols::device_path::Protocol>,
|
||||
}
|
||||
|
||||
impl<'a> BorrowedDevicePath<'a> {
|
||||
pub(crate) const fn new(protocol: NonNull<r_efi::protocols::device_path::Protocol>) -> Self {
|
||||
Self { protocol, phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn to_text(&self) -> io::Result<OsString> {
|
||||
device_path_to_text(self.protocol)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::fmt::Debug for BorrowedDevicePath<'a> {
|
||||
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
|
||||
match self.to_text() {
|
||||
Ok(p) => p.fmt(f),
|
||||
Err(_) => f.debug_struct("BorrowedDevicePath").finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct OwnedProtocol<T> {
|
||||
guid: r_efi::efi::Guid,
|
||||
handle: NonNull<crate::ffi::c_void>,
|
||||
|
@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> {
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
/// Get device path protocol associated with shell mapping.
|
||||
///
|
||||
/// returns None in case no such mapping is exists
|
||||
pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result<BorrowedDevicePath<'static>> {
|
||||
let shell =
|
||||
open_shell().ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell not found"))?;
|
||||
let mut path = os_string_to_raw(map.as_os_str())
|
||||
.ok_or(io::const_error!(io::ErrorKind::InvalidFilename, "Invalid UEFI shell mapping"))?;
|
||||
|
||||
// The Device Path Protocol pointer returned by UEFI shell is owned by the shell and is not
|
||||
// freed throughout it's lifetime. So it has a 'static lifetime.
|
||||
let protocol = unsafe { ((*shell.as_ptr()).get_device_path_from_map)(path.as_mut_ptr()) };
|
||||
let protocol = NonNull::new(protocol)
|
||||
.ok_or(io::const_error!(io::ErrorKind::NotFound, "UEFI Shell mapping not found"))?;
|
||||
|
||||
Ok(BorrowedDevicePath::new(protocol))
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ cfg_if::cfg_if! {
|
|||
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
|
||||
mod sgx;
|
||||
pub use sgx::*;
|
||||
} else if #[cfg(any(
|
||||
target_os = "uefi",
|
||||
target_os = "solid_asp3",
|
||||
))] {
|
||||
} else if #[cfg(target_os = "solid_asp3")] {
|
||||
mod unsupported_backslash;
|
||||
pub use unsupported_backslash::*;
|
||||
} else if #[cfg(target_os = "uefi")] {
|
||||
mod uefi;
|
||||
pub use uefi::*;
|
||||
} else {
|
||||
mod unix;
|
||||
pub use unix::*;
|
||||
|
|
105
library/std/src/sys/path/uefi.rs
Normal file
105
library/std/src/sys/path/uefi.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
use crate::ffi::OsStr;
|
||||
use crate::io;
|
||||
use crate::path::{Path, PathBuf, Prefix};
|
||||
use crate::sys::{helpers, unsupported_err};
|
||||
|
||||
const FORWARD_SLASH: u8 = b'/';
|
||||
const COLON: u8 = b':';
|
||||
|
||||
#[inline]
|
||||
pub fn is_sep_byte(b: u8) -> bool {
|
||||
b == b'\\'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_verbatim_sep(b: u8) -> bool {
|
||||
b == b'\\'
|
||||
}
|
||||
|
||||
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub const MAIN_SEP_STR: &str = "\\";
|
||||
pub const MAIN_SEP: char = '\\';
|
||||
|
||||
/// UEFI paths can be of 4 types:
|
||||
///
|
||||
/// 1. Absolute Shell Path: Uses shell mappings (eg: `FS0:`). Does not exist if UEFI shell not present.
|
||||
/// It can be identified with `:`.
|
||||
/// Eg: FS0:\abc\run.efi
|
||||
///
|
||||
/// 2. Absolute Device Path: this is what we want
|
||||
/// It can be identified with `/`.
|
||||
/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi
|
||||
///
|
||||
/// 3: Relative root: path relative to the current volume.
|
||||
/// It will start with `\`.
|
||||
/// Eg: \abc\run.efi
|
||||
///
|
||||
/// 4: Relative
|
||||
/// Eg: run.efi
|
||||
///
|
||||
/// The algorithm is mostly taken from edk2 UEFI shell implementation and is
|
||||
/// somewhat simple. Check for the path type in order.
|
||||
///
|
||||
/// The volume mapping in Absolute Shell Path (not the rest of the path) can be converted to Device
|
||||
/// Path Protocol using `EFI_SHELL->GetDevicePathFromMap`. The rest of the path (Relative root
|
||||
/// path), can just be appended to the remaining path.
|
||||
///
|
||||
/// For Relative root, we get the current volume (either in Shell Mapping, or Device Path Protocol
|
||||
/// form) and join it with the relative root path. We then recurse the function to resolve the Shell
|
||||
/// Mapping if present.
|
||||
///
|
||||
/// For Relative paths, we use the current working directory to construct
|
||||
/// the new path and recurse the function to resolve the Shell mapping if present.
|
||||
///
|
||||
/// Finally, at the end, we get the 2nd form, i.e. Absolute Device Path, which can be used in the
|
||||
/// normal UEFI APIs such as file, process, etc.
|
||||
/// Eg: PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Slave,0x0)/\abc\run.efi
|
||||
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
|
||||
// Absolute Shell Path
|
||||
if path.as_os_str().as_encoded_bytes().contains(&COLON) {
|
||||
let mut path_components = path.components();
|
||||
// Since path is not empty, it has at least one Component
|
||||
let prefix = path_components.next().unwrap();
|
||||
|
||||
let dev_path = helpers::get_device_path_from_map(prefix.as_ref())?;
|
||||
let mut dev_path_text = dev_path.to_text().map_err(|_| unsupported_err())?;
|
||||
|
||||
// UEFI Shell does not seem to end device path with `/`
|
||||
if *dev_path_text.as_encoded_bytes().last().unwrap() != FORWARD_SLASH {
|
||||
dev_path_text.push("/");
|
||||
}
|
||||
|
||||
let mut ans = PathBuf::from(dev_path_text);
|
||||
ans.push(path_components);
|
||||
|
||||
return Ok(ans);
|
||||
}
|
||||
|
||||
// Absolute Device Path
|
||||
if path.as_os_str().as_encoded_bytes().contains(&FORWARD_SLASH) {
|
||||
return Ok(path.to_path_buf());
|
||||
}
|
||||
|
||||
// cur_dir() always returns something
|
||||
let cur_dir = crate::env::current_dir().unwrap();
|
||||
let mut path_components = path.components();
|
||||
|
||||
// Relative Root
|
||||
if path_components.next().unwrap() == crate::path::Component::RootDir {
|
||||
let mut ans = PathBuf::new();
|
||||
ans.push(cur_dir.components().next().unwrap());
|
||||
ans.push(path_components);
|
||||
return absolute(&ans);
|
||||
}
|
||||
|
||||
absolute(&cur_dir.join(path))
|
||||
}
|
||||
|
||||
pub(crate) fn is_absolute(path: &Path) -> bool {
|
||||
let temp = path.as_os_str().as_encoded_bytes();
|
||||
temp.contains(&COLON) || temp.contains(&FORWARD_SLASH)
|
||||
}
|
|
@ -113,8 +113,12 @@
|
|||
{
|
||||
"directories": [],
|
||||
"files": [
|
||||
"FiraMono-Medium.woff2",
|
||||
"FiraMono-Regular.woff2",
|
||||
"FiraSans-Italic.woff2",
|
||||
"FiraSans-LICENSE.txt",
|
||||
"FiraSans-Medium.woff2",
|
||||
"FiraSans-MediumItalic.woff2",
|
||||
"FiraSans-Regular.woff2"
|
||||
],
|
||||
"license": {
|
||||
|
@ -266,4 +270,4 @@
|
|||
],
|
||||
"type": "root"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ impl Step for CrateBootstrap {
|
|||
SourceType::InTree,
|
||||
&[],
|
||||
);
|
||||
|
||||
let crate_name = path.rsplit_once('/').unwrap().1;
|
||||
run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder);
|
||||
}
|
||||
|
@ -3106,6 +3107,8 @@ impl Step for Bootstrap {
|
|||
&[],
|
||||
);
|
||||
|
||||
cargo.release_build(false);
|
||||
|
||||
cargo
|
||||
.rustflag("-Cdebuginfo=2")
|
||||
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
|
||||
|
|
|
@ -88,12 +88,14 @@ impl HostFlags {
|
|||
#[derive(Debug)]
|
||||
pub struct Cargo {
|
||||
command: BootstrapCommand,
|
||||
args: Vec<OsString>,
|
||||
compiler: Compiler,
|
||||
target: TargetSelection,
|
||||
rustflags: Rustflags,
|
||||
rustdocflags: Rustflags,
|
||||
hostflags: HostFlags,
|
||||
allow_features: String,
|
||||
release_build: bool,
|
||||
}
|
||||
|
||||
impl Cargo {
|
||||
|
@ -121,6 +123,10 @@ impl Cargo {
|
|||
cargo
|
||||
}
|
||||
|
||||
pub fn release_build(&mut self, release_build: bool) {
|
||||
self.release_build = release_build;
|
||||
}
|
||||
|
||||
pub fn compiler(&self) -> Compiler {
|
||||
self.compiler
|
||||
}
|
||||
|
@ -153,7 +159,7 @@ impl Cargo {
|
|||
}
|
||||
|
||||
pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Cargo {
|
||||
self.command.arg(arg.as_ref());
|
||||
self.args.push(arg.as_ref().into());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -342,6 +348,12 @@ impl Cargo {
|
|||
|
||||
impl From<Cargo> for BootstrapCommand {
|
||||
fn from(mut cargo: Cargo) -> BootstrapCommand {
|
||||
if cargo.release_build {
|
||||
cargo.args.insert(0, "--release".into());
|
||||
}
|
||||
|
||||
cargo.command.args(cargo.args);
|
||||
|
||||
let rustflags = &cargo.rustflags.0;
|
||||
if !rustflags.is_empty() {
|
||||
cargo.command.env("RUSTFLAGS", rustflags);
|
||||
|
@ -360,6 +372,7 @@ impl From<Cargo> for BootstrapCommand {
|
|||
if !cargo.allow_features.is_empty() {
|
||||
cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
|
||||
}
|
||||
|
||||
cargo.command
|
||||
}
|
||||
}
|
||||
|
@ -429,13 +442,6 @@ impl Builder<'_> {
|
|||
assert_eq!(target, compiler.host);
|
||||
}
|
||||
|
||||
if self.config.rust_optimize.is_release() &&
|
||||
// cargo bench/install do not accept `--release` and miri doesn't want it
|
||||
!matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest)
|
||||
{
|
||||
cargo.arg("--release");
|
||||
}
|
||||
|
||||
// Remove make-related flags to ensure Cargo can correctly set things up
|
||||
cargo.env_remove("MAKEFLAGS");
|
||||
cargo.env_remove("MFLAGS");
|
||||
|
@ -1218,14 +1224,20 @@ impl Builder<'_> {
|
|||
rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions");
|
||||
}
|
||||
|
||||
let release_build = self.config.rust_optimize.is_release() &&
|
||||
// cargo bench/install do not accept `--release` and miri doesn't want it
|
||||
!matches!(cmd_kind, Kind::Bench | Kind::Install | Kind::Miri | Kind::MiriSetup | Kind::MiriTest);
|
||||
|
||||
Cargo {
|
||||
command: cargo,
|
||||
args: vec![],
|
||||
compiler,
|
||||
target,
|
||||
rustflags,
|
||||
rustdocflags,
|
||||
hostflags,
|
||||
allow_features,
|
||||
release_build,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,9 @@ jobs:
|
|||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: book/linkcheck/cache.json
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }}
|
||||
restore-keys: |
|
||||
linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--
|
||||
|
||||
- name: Install latest nightly Rust toolchain
|
||||
if: steps.mdbook-cache.outputs.cache-hit != 'true'
|
||||
|
@ -66,7 +68,7 @@ jobs:
|
|||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: book/linkcheck/cache.json
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ github.run_id }}
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
if: github.event_name == 'push'
|
||||
|
|
|
@ -50,10 +50,10 @@ jobs:
|
|||
RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title`
|
||||
if [[ "$RESULT" -eq 0 ]]; then
|
||||
echo "Creating new pull request"
|
||||
PR_URL=gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'
|
||||
PR_URL=`gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'`
|
||||
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
||||
else
|
||||
PR_URL=gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title
|
||||
PR_URL=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title`
|
||||
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
|
|
|
@ -1 +1 @@
|
|||
ecda83b30f0f68cf5692855dddc0bc38ee8863fc
|
||||
66d6064f9eb888018775e08f84747ee6f39ba28e
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue