1
Fork 0

Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-01-31 05:04:21 +00:00
commit 7f414f9c39
195 changed files with 3732 additions and 1322 deletions

View file

@ -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)
==========================

View file

@ -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"

View file

@ -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],
_ => &[],
}
}

View file

@ -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, _| {

View file

@ -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(_)) => (),

View file

@ -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();

View file

@ -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())
}
}

View file

@ -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,
)

View file

@ -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) {

View file

@ -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.

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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 =

View file

@ -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

View file

@ -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>()
});

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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>,

View file

@ -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)

View file

@ -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(())

View file

@ -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> {

View file

@ -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;

View file

@ -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 {

View file

@ -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);
}

View file

@ -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

View file

@ -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 })
}

View file

@ -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 });

View file

@ -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>],

View file

@ -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),

View file

@ -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,
}

View file

@ -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
}
}

View file

@ -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);

View file

@ -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)
}),
});
}

View file

@ -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.

View file

@ -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.

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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),
})),
}
}
}

View file

@ -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),
}

View file

@ -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),
}
}

View file

@ -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,

View file

@ -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>,

View file

@ -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),
}

View file

@ -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) => {

View file

@ -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;

View file

@ -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),
};

View file

@ -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)
}

View file

@ -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)

View file

@ -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();
}
}
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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

View file

@ -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],

View file

@ -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 });

View file

@ -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)
}

View file

@ -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)
}
}

View file

@ -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("_")?,

View file

@ -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");

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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() {

View file

@ -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:?}"

View file

@ -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();

View file

@ -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;
}

View file

@ -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),
};

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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)));
}

View file

@ -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,

View file

@ -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))
}
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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

View file

@ -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)))?;

View file

@ -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:?}"),
}

View file

@ -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,
},

View file

@ -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;
}

View file

@ -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>;

View file

@ -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

View file

@ -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) {

View file

@ -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")]

View file

@ -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..])
}
}

View file

@ -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 }

View file

@ -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")]

View file

@ -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;
}
}
}

View file

@ -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
///

View file

@ -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))
}

View file

@ -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::*;

View 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)
}

View file

@ -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"
}
}
}

View file

@ -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"))

View file

@ -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,
}
}
}

View file

@ -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'

View file

@ -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:

View file

@ -1 +1 @@
ecda83b30f0f68cf5692855dddc0bc38ee8863fc
66d6064f9eb888018775e08f84747ee6f39ba28e

Some files were not shown because too many files have changed in this diff Show more