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) 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" SPDX-License-Identifier = "MIT"
[[annotations]] [[annotations]]
path = "src/librustdoc/html/static/fonts/FiraSans**" path = "src/librustdoc/html/static/fonts/Fira**"
precedence = "override" precedence = "override"
SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."] SPDX-FileCopyrightText = ["2014, Mozilla Foundation", "2014, Telefonica S.A."]
SPDX-License-Identifier = "OFL-1.1" 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. /// Returns tokens that are likely to be typed accidentally instead of the current token.
/// Enables better error recovery when the wrong token is found. /// Enables better error recovery when the wrong token is found.
pub fn similar_tokens(&self) -> Option<Vec<TokenKind>> { pub fn similar_tokens(&self) -> &[TokenKind] {
match *self { match self {
Comma => Some(vec![Dot, Lt, Semi]), Comma => &[Dot, Lt, Semi],
Semi => Some(vec![Colon, Comma]), Semi => &[Colon, Comma],
Colon => Some(vec![Semi]), Colon => &[Semi],
FatArrow => Some(vec![Eq, RArrow, Ge, Gt]), FatArrow => &[Eq, RArrow, Ge, Gt],
_ => None, _ => &[],
} }
} }

View file

@ -1,6 +1,8 @@
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::OpaqueTyOrigin;
use rustc_hir::def_id::LocalDefId; use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _}; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, TyCtxtInferExt as _};
use rustc_macros::extension; use rustc_macros::extension;
use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::fold::fold_regions;
@ -10,6 +12,7 @@ use rustc_middle::ty::{
TypingMode, TypingMode,
}; };
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::ObligationCtxt;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
@ -406,10 +409,6 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
} }
fn get_canonical_args(&self) -> ty::GenericArgsRef<'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() { if let Some(&canonical_args) = self.canonical_args.get() {
return canonical_args; return canonical_args;
} }
@ -417,9 +416,9 @@ impl<'tcx> LazyOpaqueTyEnv<'tcx> {
let &Self { tcx, def_id, .. } = self; let &Self { tcx, def_id, .. } = self;
let origin = tcx.local_opaque_ty_origin(def_id); let origin = tcx.local_opaque_ty_origin(def_id);
let parent = match origin { let parent = match origin {
hir::OpaqueTyOrigin::FnReturn { parent, .. } OpaqueTyOrigin::FnReturn { parent, .. }
| hir::OpaqueTyOrigin::AsyncFn { parent, .. } | OpaqueTyOrigin::AsyncFn { parent, .. }
| hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, | OpaqueTyOrigin::TyAlias { parent, .. } => parent,
}; };
let param_env = tcx.param_env(parent); let param_env = tcx.param_env(parent);
let args = GenericArgs::identity_for_item(tcx, parent).extend_to( 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"); tcx.dcx().span_delayed_bug(tcx.def_span(def_id), "error getting implied bounds");
Default::default() Default::default()
}); });
let implied_bounds = infcx.implied_bounds_tys(param_env, parent, &wf_tys); let outlives_env = OutlivesEnvironment::new(&infcx, parent, param_env, wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
let mut seen = vec![tcx.lifetimes.re_static]; let mut seen = vec![tcx.lifetimes.re_static];
let canonical_args = fold_regions(tcx, args, |r1, _| { 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)) { match p.expect(exp!(Comma)) {
Err(err) => { Err(err) => {
match token::TokenKind::Comma.similar_tokens() { if token::TokenKind::Comma.similar_tokens().contains(&p.token.kind) {
Some(tks) if tks.contains(&p.token.kind) => {
// If a similar token is found, then it may be a typo. We // If a similar token is found, then it may be a typo. We
// consider it as a comma, and continue parsing. // consider it as a comma, and continue parsing.
err.emit(); err.emit();
p.bump(); p.bump();
} } else {
// Otherwise stop the parsing and return the error. // Otherwise stop the parsing and return the error.
_ => return Err(err), return Err(err);
} }
} }
Ok(Recovered::Yes(_)) => (), Ok(Recovered::Yes(_)) => (),

View file

@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return; return;
} }
let idx = generic_args[2] let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch();
.expect_const()
.try_to_valtree()
.expect("expected monomorphic const in codegen")
.0
.unwrap_branch();
assert_eq!(x.layout(), y.layout()); assert_eq!(x.layout(), y.layout());
let layout = x.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, '_> { impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {
fn get_static(&mut self, def_id: DefId) -> &'ll Value { fn get_static(&mut self, def_id: DefId) -> &'ll Value {
// Forward to the `get_static` method of `CodegenCx` // 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::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
} }
llvm::set_linkage(g, llvm::Linkage::InternalLinkage); 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) (s.to_owned(), g)
}) })
.1; .1;
@ -289,7 +291,7 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
let alloc = alloc.inner(); let alloc = alloc.inner();
let value = match alloc.mutability { let value = match alloc.mutability {
Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), 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() 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()))) .global_alloc(self.tcx.vtable_allocation((ty, dyn_ty.principal())))
.unwrap_memory(); .unwrap_memory();
let init = const_alloc_to_llvm(self, alloc, /*static*/ false); 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) (value, AddressSpace::DATA)
} }
GlobalAlloc::Static(def_id) => { GlobalAlloc::Static(def_id) => {
@ -327,7 +329,8 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
let llval = unsafe { let llval = unsafe {
llvm::LLVMConstInBoundsGEP2( llvm::LLVMConstInBoundsGEP2(
self.type_i8(), 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()), &self.const_usize(offset.bytes()),
1, 1,
) )

View file

@ -210,6 +210,14 @@ impl<'ll> CodegenCx<'ll, '_> {
unsafe { llvm::LLVMConstBitCast(val, ty) } 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( pub(crate) fn static_addr_of_mut(
&self, &self,
cv: &'ll Value, cv: &'ll Value,
@ -233,6 +241,34 @@ impl<'ll> CodegenCx<'ll, '_> {
gv 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))] #[instrument(level = "debug", skip(self))]
pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value { pub(crate) fn get_static(&self, def_id: DefId) -> &'ll Value {
let instance = Instance::mono(self.tcx, def_id); let instance = Instance::mono(self.tcx, def_id);
@ -505,24 +541,15 @@ impl<'ll> CodegenCx<'ll, '_> {
} }
impl<'ll> StaticCodegenMethods for 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 { 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) { let gv = self.static_addr_of_impl(cv, align, kind);
unsafe { // static_addr_of_impl returns the bare global variable, which might not be in the default
// Upgrade the alignment in cases where the same constant is used with different // address space. Cast to the default address space if necessary.
// alignment requirements self.const_pointercast(gv, self.type_ptr())
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
} }
fn codegen_static(&self, def_id: DefId) { 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(); .unwrap_or_default();
let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo); let kind = DebugEmissionKind::from_generic(tcx.sess.opts.debuginfo);
let dwarf_version = let dwarf_version = tcx.sess.dwarf_version();
tcx.sess.opts.unstable_opts.dwarf_version.unwrap_or(tcx.sess.target.default_dwarf_version);
let is_dwarf_kind = let is_dwarf_kind =
matches!(tcx.sess.target.debuginfo_kind, DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym); matches!(tcx.sess.target.debuginfo_kind, DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym);
// Don't emit `.debug_pubnames` and `.debug_pubtypes` on DWARFv4 or lower. // 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 .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>( pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>, cx: &CodegenCx<'ll, 'tcx>,
ty: Ty<'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 }; 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 = trait_ref.with_self_ty(cx.tcx, ty);
let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); let trait_ref_self = cx.tcx.erase_regions(trait_ref_self);
let trait_def_id = trait_ref_self.def_id(); let trait_def_id = trait_ref_self.def_id();
@ -1581,6 +1602,9 @@ pub(crate) fn create_vtable_di_node<'ll, 'tcx>(
return; 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 // 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 // merged. Otherwise debuggers will have a hard time mapping from dyn pointer
// to concrete type. // to concrete type.

View file

@ -22,6 +22,7 @@ use rustc_session::config::{self, DebugInfo};
use rustc_span::{ use rustc_span::{
BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol, BytePos, Pos, SourceFile, SourceFileAndLine, SourceFileHash, Span, StableSourceFileId, Symbol,
}; };
use rustc_target::spec::DebuginfoKind;
use smallvec::SmallVec; use smallvec::SmallVec;
use tracing::debug; use tracing::debug;
@ -93,23 +94,24 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
pub(crate) fn finalize(&self, sess: &Session) { pub(crate) fn finalize(&self, sess: &Session) {
unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) }; unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) };
if !sess.target.is_like_msvc {
match sess.target.debuginfo_kind {
DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => {
// Debuginfo generation in LLVM by default uses a higher // Debuginfo generation in LLVM by default uses a higher
// version of dwarf than macOS currently understands. We can // version of dwarf than macOS currently understands. We can
// instruct LLVM to emit an older version of dwarf, however, // instruct LLVM to emit an older version of dwarf, however,
// for macOS to understand. For more info see #11352 // for macOS to understand. For more info see #11352
// This can be overridden using --llvm-opts -dwarf-version,N. // This can be overridden using --llvm-opts -dwarf-version,N.
// Android has the same issue (#22398) // 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( llvm::add_module_flag_u32(
self.llmod, self.llmod,
llvm::ModuleFlagMergeBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
"Dwarf Version", "Dwarf Version",
dwarf_version, sess.dwarf_version(),
); );
} else { }
// Indicate that we want CodeView debug information on MSVC DebuginfoKind::Pdb => {
// Indicate that we want CodeView debug information
llvm::add_module_flag_u32( llvm::add_module_flag_u32(
self.llmod, self.llmod,
llvm::ModuleFlagMergeBehavior::Warning, llvm::ModuleFlagMergeBehavior::Warning,
@ -117,6 +119,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
1, 1,
); );
} }
}
// Prevent bitcode readers from deleting the debug info. // Prevent bitcode readers from deleting the debug info.
llvm::add_module_flag_u32( llvm::add_module_flag_u32(

View file

@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
} }
if name == sym::simd_shuffle_generic { 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 n = idx.len() as u64;
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn); let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);

View file

@ -661,6 +661,79 @@ pub enum MemoryEffects {
InaccessibleMemOnly, 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" { unsafe extern "C" {
type Opaque; type Opaque;
} }
@ -991,7 +1064,10 @@ unsafe extern "C" {
pub fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; 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 LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value;
pub fn LLVMConstBitCast<'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 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) // Operations on global variables, functions, and aliases (globals)
pub fn LLVMIsDeclaration(Global: &Value) -> Bool; pub fn LLVMIsDeclaration(Global: &Value) -> Bool;
@ -1048,6 +1124,7 @@ unsafe extern "C" {
// Operations on instructions // Operations on instructions
pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>;
pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; pub fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock;
pub fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>;
// Operations on call sites // Operations on call sites
pub fn LLVMSetInstructionCallConv(Instr: &Value, CC: c_uint); 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_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_create_temp_dir = couldn't create a temp dir: {$error}
codegen_ssa_dlltool_fail_import_library = codegen_ssa_dlltool_fail_import_library =

View file

@ -615,6 +615,11 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
return ongoing_codegen; 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); let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
// Run the monomorphization collector and partition the collected items into // 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) => { ty::ConstKind::Param(param) => {
write!(output, "{}", param.name) write!(output, "{}", param.name)
} }
ty::ConstKind::Value(ty, valtree) => { ty::ConstKind::Value(cv) => {
match ty.kind() { match cv.ty.kind() {
ty::Int(ity) => { ty::Int(ity) => {
// FIXME: directly extract the bits from a valtree instead of evaluating an let bits = cv
// already evaluated `Const` in order to get the bits.
let bits = ct
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen"); .expect("expected monomorphic const in codegen");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
write!(output, "{val}") write!(output, "{val}")
} }
ty::Uint(_) => { ty::Uint(_) => {
let val = ct let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen"); .expect("expected monomorphic const in codegen");
write!(output, "{val}") write!(output, "{val}")
} }
ty::Bool => { 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}") 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. // avoiding collisions and will make the emitted type names shorter.
let hash_short = tcx.with_stable_hashing_context(|mut hcx| { let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
let mut hasher = StableHasher::new(); let mut hasher = StableHasher::new();
hcx.while_hashing_spans(false, |hcx| { hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
(ty, valtree).hash_stable(hcx, &mut hasher)
});
hasher.finish::<Hash64>() hasher.finish::<Hash64>()
}); });

View file

@ -523,6 +523,10 @@ pub(crate) struct CheckInstalledVisualStudio;
#[diag(codegen_ssa_insufficient_vs_code_product)] #[diag(codegen_ssa_insufficient_vs_code_product)]
pub(crate) struct InsufficientVSCodeProduct; pub(crate) struct InsufficientVSCodeProduct;
#[derive(Diagnostic)]
#[diag(codegen_ssa_cpu_required)]
pub(crate) struct CpuRequired;
#[derive(Diagnostic)] #[derive(Diagnostic)]
#[diag(codegen_ssa_processing_dymutil_failed)] #[diag(codegen_ssa_processing_dymutil_failed)]
#[note] #[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() { mir::Const::Ty(_, c) => match c.kind() {
// A constant that came from a const generic but was then used as an argument to // 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). // 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:#?}"), other => span_bug!(constant.span, "{other:#?}"),
}, },
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate

View file

@ -345,7 +345,7 @@ where
Const::Ty(_, ct) Const::Ty(_, ct)
if matches!( if matches!(
ct.kind(), ct.kind(),
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _) ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
) => ) =>
{ {
None 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 /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished. /// 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)] #[instrument(skip(tcx), level = "debug", ret)]
pub fn valtree_to_const_value<'tcx>( pub fn valtree_to_const_value<'tcx>(
tcx: TyCtxt<'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 = providers.hooks.try_destructure_mir_constant_for_user_output =
const_eval::try_destructure_mir_constant_for_user_output; const_eval::try_destructure_mir_constant_for_user_output;
providers.valtree_to_const_val = |tcx, (ty, valtree)| { providers.valtree_to_const_val = |tcx, cv| {
const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree) 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)| { providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::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::InferCtxtErrorExt;
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits; use rustc_trait_selection::traits;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::fold::TypeFoldable;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
use ty::TypingMode; 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 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); ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?;
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
if infcx.next_trait_solver() { if infcx.next_trait_solver() {
Ok(()) 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::def::{DefKind, Res};
use rustc_hir::intravisit::VisitorExt; use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; 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::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::util; use rustc_infer::traits::util;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; 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::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt,
}; };
@ -416,11 +414,7 @@ fn compare_method_predicate_entailment<'tcx>(
// Finally, resolve all regions. This catches wily misuses of // Finally, resolve all regions. This catches wily misuses of
// lifetime parameters. // lifetime parameters.
let outlives_env = OutlivesEnvironment::with_bounds( let errors = infcx.resolve_regions(impl_m_def_id, param_env, wf_tys);
param_env,
infcx.implied_bounds_tys(param_env, impl_m_def_id, &wf_tys),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() { if !errors.is_empty() {
return Err(infcx return Err(infcx
.tainted_by_errors() .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 // Finally, resolve all regions. This catches wily misuses of
// lifetime parameters. // lifetime parameters.
let outlives_env = OutlivesEnvironment::with_bounds( ocx.resolve_regions_and_report_errors(impl_m_def_id, param_env, wf_tys)?;
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)?;
let mut remapped_types = DefIdMap::default(); let mut remapped_types = DefIdMap::default();
for (def_id, (ty, args)) in collected_types { 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)); 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, param_env, [])
ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
} }
#[instrument(level = "debug", skip(tcx))] #[instrument(level = "debug", skip(tcx))]
@ -2017,8 +2006,7 @@ fn compare_type_predicate_entailment<'tcx>(
// Finally, resolve all regions. This catches wily misuses of // Finally, resolve all regions. This catches wily misuses of
// lifetime parameters. // lifetime parameters.
let outlives_env = OutlivesEnvironment::new(param_env); ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, [])
ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
} }
/// Validate that `ProjectionCandidate`s created for this associated type will /// 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 // Finally, resolve all regions. This catches wily misuses of
// lifetime parameters. // lifetime parameters.
let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, &assumed_wf_types); ocx.resolve_regions_and_report_errors(impl_ty_def_id, param_env, 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)
} }
struct ReplaceTy<'tcx> { struct ReplaceTy<'tcx> {

View file

@ -3,7 +3,6 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::TyCtxtInferExt; 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_lint_defs::builtin::{REFINING_IMPL_TRAIT_INTERNAL, REFINING_IMPL_TRAIT_REACHABLE};
use rustc_middle::span_bug; use rustc_middle::span_bug;
use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCause;
@ -13,7 +12,6 @@ use rustc_middle::ty::{
}; };
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::regions::InferCtxtRegionExt; 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}; 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. /// 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)"); tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
return; return;
} }
let outlives_env = OutlivesEnvironment::with_bounds( let errors = infcx.resolve_regions(impl_m.def_id.expect_local(), param_env, implied_wf_types);
param_env,
infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() { if !errors.is_empty() {
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)"); tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
return; return;

View file

@ -5,7 +5,6 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*; use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::util::CheckRegions; use rustc_middle::ty::util::CheckRegions;
@ -192,7 +191,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
return Err(guar.unwrap()); 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() { if !errors.is_empty() {
let mut guar = None; let mut guar = None;
for error in errors { 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::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor; use rustc_hir::intravisit::Visitor;
use rustc_index::bit_set::DenseBitSet; use rustc_index::bit_set::DenseBitSet;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::infer::{self, TyCtxtInferExt as _};
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_middle::query::Providers; 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, param_env, []) {
if let Err(e) = ocx.resolve_regions_and_report_errors(local_id, &outlives_env) {
return Err(e); return Err(e);
} }

View file

@ -25,11 +25,10 @@ use rustc_middle::{bug, span_bug};
use rustc_session::parse::feature_err; use rustc_session::parse::feature_err;
use rustc_span::{DUMMY_SP, Ident, Span, sym}; use rustc_span::{DUMMY_SP, Ident, Span, sym};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt; 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::{ use rustc_trait_selection::traits::misc::{
ConstParamTyImplementationError, type_allowed_to_implement_const_param_ty, 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::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, self, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt,
@ -128,13 +127,17 @@ where
let infcx_compat = infcx.fork(); let infcx_compat = infcx.fork();
// We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always. // We specifically want to call the non-compat version of `implied_bounds_tys`; we do this always.
let implied_bounds = let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat(
infcx.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, false); &infcx,
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); body_def_id,
param_env,
assumed_wf_types.iter().copied(),
false,
);
lint_redundant_lifetimes(tcx, body_def_id, &outlives_env); 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() { if errors.is_empty() {
return Ok(()); return Ok(());
} }
@ -172,10 +175,14 @@ where
// but that does result in slightly more work when this option is set and // 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. // 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 { if is_bevy && !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat {
let implied_bounds = let outlives_env = OutlivesEnvironment::new_with_implied_bounds_compat(
infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types, true); &infcx,
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); body_def_id,
let errors_compat = infcx_compat.resolve_regions(&outlives_env); param_env,
assumed_wf_types,
true,
);
let errors_compat = infcx_compat.resolve_regions_with_outlives_env(&outlives_env);
if errors_compat.is_empty() { if errors_compat.is_empty() {
Ok(()) Ok(())
} else { } else {
@ -769,12 +776,7 @@ fn test_region_obligations<'tcx>(
add_constraints(&infcx); add_constraints(&infcx);
let outlives_environment = OutlivesEnvironment::with_bounds( let errors = infcx.resolve_regions(id, param_env, wf_tys.iter().copied());
param_env,
infcx.implied_bounds_tys(param_env, id, wf_tys),
);
let errors = infcx.resolve_regions(&outlives_environment);
debug!(?errors, "errors"); debug!(?errors, "errors");
// If we were able to prove that the type outlives the region without // 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::ItemKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt}; use rustc_infer::infer::{self, RegionResolutionError, TyCtxtInferExt};
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
@ -346,8 +345,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
} }
// Finally, resolve all regions. // Finally, resolve all regions.
let outlives_env = OutlivesEnvironment::new(param_env); res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, []));
res = res.and(ocx.resolve_regions_and_report_errors(impl_did, &outlives_env));
} }
res res
} }
@ -564,8 +562,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
} }
// Finally, resolve all regions. // Finally, resolve all regions.
let outlives_env = OutlivesEnvironment::new(param_env); let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []);
let _ = ocx.resolve_regions_and_report_errors(impl_did, &outlives_env);
Ok(CoerceUnsizedInfo { custom_kind: kind }) Ok(CoerceUnsizedInfo { custom_kind: kind })
} }

View file

@ -68,7 +68,6 @@
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::specialization_graph::Node; use rustc_infer::traits::specialization_graph::Node;
use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::trait_def::TraitSpecializationKind;
@ -77,7 +76,6 @@ use rustc_middle::ty::{
}; };
use rustc_span::{ErrorGuaranteed, Span}; use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt; 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 rustc_trait_selection::traits::{self, ObligationCtxt, translate_args_with_cause, wf};
use tracing::{debug, instrument}; use tracing::{debug, instrument};
@ -176,7 +174,6 @@ fn get_impl_args(
let ocx = ObligationCtxt::new_with_diagnostics(infcx); let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let param_env = tcx.param_env(impl1_def_id); let param_env = tcx.param_env(impl1_def_id);
let impl1_span = tcx.def_span(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 impl1_args = GenericArgs::identity_for_item(tcx, impl1_def_id);
let impl2_args = translate_args_with_cause( let impl2_args = translate_args_with_cause(
@ -194,9 +191,8 @@ fn get_impl_args(
return Err(guar); return Err(guar);
} }
let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, &assumed_wf_types); let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds); let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, param_env, assumed_wf_types);
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else { let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
let span = tcx.def_span(impl1_def_id); let span = tcx.def_span(impl1_def_id);
let guar = tcx.dcx().emit_err(GenericArgsOnOverriddenImpl { span }); 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 { fn downgrade_mut_inside_shared(&self) -> bool {
// NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
// across all editions, this may be removed. // 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? /// 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, max_ref_mutbl: MutblCap,
) -> (Ty<'tcx>, ByRef, MutblCap) { ) -> (Ty<'tcx>, ByRef, MutblCap) {
#[cfg(debug_assertions)] #[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!"); span_bug!(pat.span, "Pattern mutability cap violated!");
} }
match adjust_mode { 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 // (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. // but not Rule 5, we'll need to check that here.
debug_assert!(ref_pat_matches_mut_ref); debug_assert!(ref_pat_matches_mut_ref);
let err_msg = "mismatched types"; self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
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();
} }
pat_info.binding_mode = ByRef::No; pat_info.binding_mode = ByRef::No;
@ -2352,30 +2339,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return expected; return expected;
} }
InheritedRefMatchRule::EatInner => { 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. // 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 { } 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 { if pat_mutbl > inh_mut {
// We can't match an inherited shared reference with `&mut`. This will // We can't match an inherited shared reference with `&mut`.
// be a type error later, since we're matching a reference pattern
// against a non-reference type.
// NB: This assumes that `&` patterns can match against mutable // NB: This assumes that `&` patterns can match against mutable
// references (RFC 3627, Rule 5). If we implement a pattern typing // 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. // ruleset with Rule 4 but not Rule 5, we'll need to check that here.
debug_assert!(ref_pat_matches_mut_ref); debug_assert!(ref_pat_matches_mut_ref);
} else { self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
}
pat_info.binding_mode = ByRef::No; pat_info.binding_mode = ByRef::No;
self.typeck_results self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
.borrow_mut()
.skipped_ref_pats_mut()
.insert(pat.hir_id);
self.check_pat(inner, expected, pat_info); self.check_pat(inner, expected, pat_info);
return expected; return expected;
} }
} }
}
InheritedRefMatchRule::EatBoth => { InheritedRefMatchRule::EatBoth => {
// Reset binding mode on old editions // Reset binding mode on old editions
pat_info.binding_mode = ByRef::No; pat_info.binding_mode = ByRef::No;
@ -2447,6 +2444,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ty::new_ref(self.tcx, region, ty, mutbl) 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( fn try_resolve_slice_ty_to_array_ty(
&self, &self,
before: &'tcx [Pat<'tcx>], 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::Param(_)
| ty::ConstKind::Value(_, _) | ty::ConstKind::Value(_)
| ty::ConstKind::Unevaluated(..) | ty::ConstKind::Unevaluated(..)
| ty::ConstKind::Expr(..) | ty::ConstKind::Expr(..)
| ty::ConstKind::Error(_) => ct.super_fold_with(self), | ty::ConstKind::Error(_) => ct.super_fold_with(self),

View file

@ -1055,7 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
| ty::ConstKind::Bound(_, _) | ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_) | ty::ConstKind::Placeholder(_)
| ty::ConstKind::Unevaluated(_) | ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_, _) | ty::ConstKind::Value(_)
| ty::ConstKind::Error(_) | ty::ConstKind::Error(_)
| ty::ConstKind::Expr(_) => ct, | ty::ConstKind::Expr(_) => ct,
} }

View file

@ -31,26 +31,14 @@ use crate::traits::query::OutlivesBound;
pub struct OutlivesEnvironment<'tcx> { pub struct OutlivesEnvironment<'tcx> {
pub param_env: ty::ParamEnv<'tcx>, pub param_env: ty::ParamEnv<'tcx>,
free_region_map: FreeRegionMap<'tcx>, free_region_map: FreeRegionMap<'tcx>,
/// FIXME: Your first reaction may be that this is a bit strange. `RegionBoundPairs`
// Contains the implied region bounds in scope for our current body. /// 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
// Example: /// 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.
// 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.
region_bound_pairs: RegionBoundPairs<'tcx>, region_bound_pairs: RegionBoundPairs<'tcx>,
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
} }
/// "Region-bound pairs" tracks outlives relations that are known to /// "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>>>; pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<'tcx, GenericKind<'tcx>>>;
impl<'tcx> OutlivesEnvironment<'tcx> { impl<'tcx> OutlivesEnvironment<'tcx> {
/// Create a new `OutlivesEnvironment` without extra outlives bounds. /// Create a new `OutlivesEnvironment` from normalized outlives bounds.
#[inline] pub fn from_normalized_bounds(
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(
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>, extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
) -> Self { ) -> Self {
let mut region_relation = TransitiveRelationBuilder::default(); let mut region_relation = TransitiveRelationBuilder::default();
@ -102,18 +85,21 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
OutlivesEnvironment { OutlivesEnvironment {
param_env, param_env,
known_type_outlives,
free_region_map: FreeRegionMap { relation: region_relation.freeze() }, free_region_map: FreeRegionMap { relation: region_relation.freeze() },
region_bound_pairs, region_bound_pairs,
} }
} }
/// Borrows current value of the `free_region_map`.
pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> {
&self.free_region_map &self.free_region_map
} }
/// Borrows current `region_bound_pairs`.
pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> {
&self.region_bound_pairs &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, self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, Ty, TyCtxt,
TypeFoldable as _, TypeVisitableExt, TypeFoldable as _, TypeVisitableExt,
}; };
use rustc_span::DUMMY_SP;
use rustc_type_ir::outlives::{Component, push_outlives_components}; use rustc_type_ir::outlives::{Component, push_outlives_components};
use smallvec::smallvec; use smallvec::smallvec;
use tracing::{debug, instrument}; use tracing::{debug, instrument};
@ -142,25 +141,6 @@ impl<'tcx> InferCtxt<'tcx> {
) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> { ) -> Result<(), (PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>)> {
assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot"); 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. // Must loop since the process of normalizing may itself register region obligations.
for iteration in 0.. { for iteration in 0.. {
let my_region_obligations = self.take_registered_region_obligations(); let my_region_obligations = self.take_registered_region_obligations();
@ -194,7 +174,7 @@ impl<'tcx> InferCtxt<'tcx> {
self.tcx, self.tcx,
outlives_env.region_bound_pairs(), outlives_env.region_bound_pairs(),
None, None,
&normalized_caller_bounds, outlives_env.known_type_outlives(),
); );
let category = origin.to_constraint_category(); let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, 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::{ use rustc_trait_selection::errors::{
AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion, AddPreciseCapturingForOvercapture, impl_trait_overcapture_suggestion,
}; };
use rustc_trait_selection::regions::OutlivesEnvironmentBuildExt;
use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt;
use crate::{LateContext, LateLintPass, fluent_generated as fluent}; 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 (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
let ocx = ObligationCtxt::new(&infcx); let ocx = ObligationCtxt::new(&infcx);
let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default();
let implied_bounds = OutlivesEnvironment::new(&infcx, parent_def_id, param_env, assumed_wf_tys)
infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false);
OutlivesEnvironment::with_bounds(param_env, implied_bounds)
}), }),
}); });
} }

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. // 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 // 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`. // was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
ty::ConstKind::Value(ty, _) => ty, ty::ConstKind::Value(cv) => cv.ty,
_ => *ty, _ => *ty,
} }
} }
@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> {
pub fn is_required_const(&self) -> bool { pub fn is_required_const(&self) -> bool {
match self { match self {
Const::Ty(_, c) => match c.kind() { 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, _ => true,
}, },
Const::Val(..) => false, // already a value, cannot error Const::Val(..) => false, // already a value, cannot error
@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> {
pub fn try_to_scalar(self) -> Option<Scalar> { pub fn try_to_scalar(self) -> Option<Scalar> {
match self { match self {
Const::Ty(_, c) => match c.kind() { 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. // 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 // 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! // 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, _ => None,
}, },
@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> {
match self { match self {
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x), Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
Const::Ty(_, c) => match c.kind() { Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => { ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
Some(valtree.unwrap_leaf())
}
_ => None, _ => None,
}, },
_ => None, _ => None,
@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> {
} }
match c.kind() { 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(_) => { ConstKind::Expr(_) => {
bug!("Normalization of `ty::ConstKind::Expr` is unimplemented") bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
} }
@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> {
typing_env: ty::TypingEnv<'tcx>, typing_env: ty::TypingEnv<'tcx>,
) -> Option<Scalar> { ) -> Option<Scalar> {
if let Const::Ty(_, c) = self if let Const::Ty(_, c) = self
&& let ty::ConstKind::Value(ty, val) = c.kind() && let ty::ConstKind::Value(cv) = c.kind()
&& ty.is_primitive() && cv.ty.is_primitive()
{ {
// Avoid the `valtree_to_const_val` query. Can only be done on primitive types that // 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 // are valtree leaves, and *not* on references. (References should return the
// pointer here, which valtrees don't represent.) // pointer here, which valtrees don't represent.)
Some(val.unwrap_leaf().into()) Some(cv.valtree.unwrap_leaf().into())
} else { } else {
self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar() 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 // A valtree may be a reference. Valtree references correspond to a
// different allocation each time they are evaluated. Valtrees for primitive // different allocation each time they are evaluated. Valtrees for primitive
// types are fine though. // 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, ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
// This can happen if evaluation of a constant failed. The result does not matter // This can happen if evaluation of a constant failed. The result does not matter
// much since compilation is doomed. // much since compilation is doomed.

View file

@ -1441,7 +1441,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
ty::ConstKind::Unevaluated(uv) => { ty::ConstKind::Unevaluated(uv) => {
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) 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`. // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
ty::ConstKind::Error(_) => "Error".to_string(), ty::ConstKind::Error(_) => "Error".to_string(),
// These variants shouldn't exist in the MIR. // 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>; type Cache<V> = DefaultCache<Self, V>;
fn default_span(&self, _: TyCtxt<'_>) -> Span { fn default_span(&self, _: TyCtxt<'_>) -> Span {

View file

@ -1256,9 +1256,9 @@ rustc_queries! {
desc { "evaluating type-level constant" } desc { "evaluating type-level constant" }
} }
/// Converts a type level constant value into `ConstValue` /// Converts a type-level constant value into a MIR constant value.
query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> { query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to mir constant value"} desc { "converting type-level constant value to MIR constant value"}
} }
/// Destructures array, ADT or tuple constants into the constants /// 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_macros::HashStable;
use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo}; use rustc_type_ir::{self as ir, TypeFlags, WithCachedTypeInfo};
use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{self, Ty, TyCtxt};
mod int; mod int;
@ -110,8 +109,8 @@ impl<'tcx> Const<'tcx> {
} }
#[inline] #[inline]
pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> { pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Value(ty, val)) Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree }))
} }
#[inline] #[inline]
@ -214,47 +213,31 @@ impl<'tcx> Const<'tcx> {
Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize) Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize)
} }
/// Panics if self.kind != ty::ConstKind::Value /// Panics if `self.kind != ty::ConstKind::Value`.
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) { pub fn to_value(self) -> ty::Value<'tcx> {
match self.kind() { match self.kind() {
ty::ConstKind::Value(ty, valtree) => (valtree, ty), ty::ConstKind::Value(cv) => cv,
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()), _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
} }
} }
/// Attempts to convert to a `ValTree` /// Attempts to convert to a value.
pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> { ///
/// Note that this does not evaluate the constant.
pub fn try_to_value(self) -> Option<ty::Value<'tcx>> {
match self.kind() { match self.kind() {
ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)), ty::ConstKind::Value(cv) => Some(cv),
_ => None, _ => None,
} }
} }
#[inline] /// Convenience method to extract the value of a usize constant,
pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> { /// useful to get the length of an array type.
let (valtree, ty) = self.try_to_valtree()?; ///
Some((valtree.try_to_scalar()?, ty)) /// Note that this does not evaluate the constant.
}
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
}
#[inline] #[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_valtree()?.0.try_to_target_usize(tcx) self.try_to_value()?.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))
} }
pub fn is_ct_infer(self) -> bool { 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 super::ScalarInt;
use crate::mir::interpret::Scalar; use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt}; 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. /// 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 /// 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 /// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ValTree`. /// lists of (nested) `ValTree`.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[derive(HashStable, TyEncodable, TyDecodable)]
pub enum ValTree<'tcx> { pub enum ValTree<'tcx> {
/// integers, `bool`, `char` are represented as scalars. /// integers, `bool`, `char` are represented as scalars.
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values /// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
@ -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 /// Get the values inside the ValTree as a slice of bytes. This only works for
/// constants with types &str, &[u8], or [u8; _]. /// constants with types &str, &[u8], or [u8; _].
pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [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 ParamConst = ty::ParamConst;
type BoundConst = ty::BoundVar; type BoundConst = ty::BoundVar;
type ValueConst = ty::ValTree<'tcx>; type ValueConst = ty::Value<'tcx>;
type ExprConst = ty::Expr<'tcx>; type ExprConst = ty::Expr<'tcx>;
type Region = Region<'tcx>; type ValTree = ty::ValTree<'tcx>;
type Region = Region<'tcx>;
type EarlyParamRegion = ty::EarlyParamRegion; type EarlyParamRegion = ty::EarlyParamRegion;
type LateParamRegion = ty::LateParamRegion; type LateParamRegion = ty::LateParamRegion;
type BoundRegion = ty::BoundRegion; type BoundRegion = ty::BoundRegion;
@ -1118,15 +1119,18 @@ impl<'tcx> CommonConsts<'tcx> {
}; };
CommonConsts { CommonConsts {
unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())), unit: mk_const(ty::ConstKind::Value(ty::Value {
true_: mk_const(ty::ConstKind::Value( ty: types.unit,
types.bool, valtree: ty::ValTree::zst(),
ty::ValTree::Leaf(ty::ScalarInt::TRUE), })),
)), true_: mk_const(ty::ConstKind::Value(ty::Value {
false_: mk_const(ty::ConstKind::Value( ty: types.bool,
types.bool, valtree: ty::ValTree::Leaf(ty::ScalarInt::TRUE),
ty::ValTree::Leaf(ty::ScalarInt::FALSE), })),
)), 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::HAS_CT_PLACEHOLDER);
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE); 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::Expr(e) => self.add_args(e.args()),
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), 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), _ => Err(err),
} }
} }

View file

@ -60,7 +60,7 @@ pub use self::closure::{
place_to_string_for_capture, place_to_string_for_capture,
}; };
pub use self::consts::{ 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::{ pub use self::context::{
CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,

View file

@ -1484,8 +1484,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
_ => write!(self, "_")?, _ => write!(self, "_")?,
}, },
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
ty::ConstKind::Value(ty, value) => { ty::ConstKind::Value(cv) => {
return self.pretty_print_const_valtree(value, ty, print_ty); return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty);
} }
ty::ConstKind::Bound(debruijn, bound_var) => { ty::ConstKind::Bound(debruijn, bound_var) => {
@ -1637,14 +1637,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
match ty.kind() { match ty.kind() {
// Byte strings (&[u8; N]) // Byte strings (&[u8; N])
ty::Ref(_, inner, _) => { ty::Ref(_, inner, _) => {
if let ty::Array(elem, len) = inner.kind() { if let ty::Array(elem, len) = inner.kind()
if let ty::Uint(ty::UintTy::U8) = elem.kind() { && let ty::Uint(ty::UintTy::U8) = elem.kind()
if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.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()) { match self.tcx().try_get_global_alloc(prov.alloc_id()) {
Some(GlobalAlloc::Memory(alloc)) => { Some(GlobalAlloc::Memory(alloc)) => {
let len = int.to_bits(self.tcx().data_layout.pointer_size); let len = int.to_bits(self.tcx().data_layout.pointer_size);
let range = let range = AllocRange { start: offset, size: Size::from_bytes(len) };
AllocRange { start: offset, size: Size::from_bytes(len) };
if let Ok(byte_str) = if let Ok(byte_str) =
alloc.inner().get_bytes_strip_provenance(&self.tcx(), range) alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
{ {
@ -1664,8 +1665,6 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
return Ok(()); return Ok(());
} }
} }
}
}
ty::FnPtr(..) => { ty::FnPtr(..) => {
// FIXME: We should probably have a helper method to share code with the "Byte strings" // FIXME: We should probably have a helper method to share code with the "Byte strings"
// printing above (which also has to handle pointers to all sorts of things). // printing above (which also has to handle pointers to all sorts of things).
@ -1786,6 +1785,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
Ok(()) Ok(())
} }
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
fn pretty_print_const_valtree( fn pretty_print_const_valtree(
&mut self, &mut self,
valtree: ty::ValTree<'tcx>, 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> { impl<'tcx> fmt::Debug for ty::Const<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// If this is a value, we spend some effort to make it look nice. // 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| { return ty::tls::with(move |tcx| {
// Somehow trying to lift the valtree results in lifetime errors, so we lift the // ValTrees aren't interned, so we lift the entire constant.
// entire constant.
let lifted = tcx.lift(*self).unwrap(); 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") bug!("we checked that this is a valtree")
}; };
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); 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()) 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::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
ConstKind::Value(t, v) => { ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?)
}
ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?), ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
ConstKind::Expr(e) => ConstKind::Expr(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::Placeholder(p) => p.visit_with(visitor),
ConstKind::Unevaluated(uv) => uv.visit_with(visitor), ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
ConstKind::Value(t, v) => { ConstKind::Value(v) => v.visit_with(visitor),
try_visit!(t.visit_with(visitor));
v.visit_with(visitor)
}
ConstKind::Error(e) => e.visit_with(visitor), ConstKind::Error(e) => e.visit_with(visitor),
ConstKind::Expr(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::Bound(..)
| ty::ConstKind::Error(_) => {} | 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::Expr(expr) => stack.extend(expr.args().iter().rev()),
ty::ConstKind::Unevaluated(ct) => { ty::ConstKind::Unevaluated(ct) => {

View file

@ -46,7 +46,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
match c.kind() { match c.kind() {
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty), 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), _ => 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). // 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")] #[instrument(skip(self), level = "debug")]
fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> { fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
let span = self.span; 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. // FIXME: See comment above -- we could fold the region separately or something.
ty::ConstKind::Bound(_, _) ty::ConstKind::Bound(_, _)
| ty::ConstKind::Unevaluated(_) | ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_, _) | ty::ConstKind::Value(_)
| ty::ConstKind::Error(_) | ty::ConstKind::Error(_)
| ty::ConstKind::Expr(_) => return c.super_fold_with(self), | ty::ConstKind::Expr(_) => return c.super_fold_with(self),
}; };

View file

@ -160,9 +160,7 @@ where
ty::ConstKind::Infer(_) => { ty::ConstKind::Infer(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} }
ty::ConstKind::Placeholder(_) ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Error(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
// We can freely ICE here as: // We can freely ICE here as:
@ -199,7 +197,7 @@ where
unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`") unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
} }
ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct), ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
ty::ConstKind::Value(ty, _) => ty, ty::ConstKind::Value(cv) => cv.ty(),
ty::ConstKind::Placeholder(placeholder) => { ty::ConstKind::Placeholder(placeholder) => {
self.cx().find_const_ty_from_env(goal.param_env, 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 span_before_body = this.prev_token.span;
let arm_body; let arm_body;
let is_fat_arrow = this.check(exp!(FatArrow)); let is_fat_arrow = this.check(exp!(FatArrow));
let is_almost_fat_arrow = TokenKind::FatArrow let is_almost_fat_arrow =
.similar_tokens() TokenKind::FatArrow.similar_tokens().contains(&this.token.kind);
.is_some_and(|similar_tokens| similar_tokens.contains(&this.token.kind));
// this avoids the compiler saying that a `,` or `}` was expected even though // 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) // the pattern isn't a never pattern (and thus an arm body is required)

View file

@ -924,13 +924,11 @@ impl<'a> Parser<'a> {
_ => { _ => {
// Attempt to keep parsing if it was a similar separator. // Attempt to keep parsing if it was a similar separator.
if let Some(tokens) = exp.tok.similar_tokens() { if exp.tok.similar_tokens().contains(&self.token.kind) {
if tokens.contains(&self.token.kind) {
self.bump(); self.bump();
} }
} }
} }
}
// If this was a missing `@` in a binding pattern // If this was a missing `@` in a binding pattern
// bail with a suggestion // bail with a suggestion

View file

@ -363,12 +363,7 @@ impl<'a> Parser<'a> {
/// Notifies of an error. The message doesn't actually need to be of type /// 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 /// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now. /// might as well start using it now.
fn err<S1: Into<String>, S2: Into<String>>( fn err(&mut self, description: impl Into<String>, label: impl Into<String>, span: InnerSpan) {
&mut self,
description: S1,
label: S2,
span: InnerSpan,
) {
self.errors.push(ParseError { self.errors.push(ParseError {
description: description.into(), description: description.into(),
note: None, note: None,
@ -382,11 +377,11 @@ impl<'a> Parser<'a> {
/// Notifies of an error. The message doesn't actually need to be of type /// 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 /// String, but I think it does when this eventually uses conditions so it
/// might as well start using it now. /// 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, &mut self,
description: S1, description: impl Into<String>,
label: S2, label: impl Into<String>,
note: S3, note: impl Into<String>,
span: InnerSpan, span: InnerSpan,
) { ) {
self.errors.push(ParseError { 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>). /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
fn encode_const<'tcx>( fn encode_const<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
c: Const<'tcx>, ct: Const<'tcx>,
ct_ty: Ty<'tcx>, ct_ty: Ty<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>, dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions, options: EncodeTyOptions,
@ -111,7 +111,7 @@ fn encode_const<'tcx>(
// L<element-type>[n][<element-value>]E as literal argument // L<element-type>[n][<element-value>]E as literal argument
let mut s = String::from('L'); let mut s = String::from('L');
match c.kind() { match ct.kind() {
// Const parameters // Const parameters
ty::ConstKind::Param(..) => { ty::ConstKind::Param(..) => {
// L<element-type>E as literal argument // L<element-type>E as literal argument
@ -121,18 +121,18 @@ fn encode_const<'tcx>(
} }
// Literal arguments // Literal arguments
ty::ConstKind::Value(ct_ty, ..) => { ty::ConstKind::Value(cv) => {
// L<element-type>[n]<element-value>E as literal argument // L<element-type>[n]<element-value>E as literal argument
// Element type // 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, // The only allowed types of const values are bool, u8, u16, u32,
// u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
// bool value false is encoded as 0 and true as 1. // bool value false is encoded as 0 and true as 1.
match ct_ty.kind() { match cv.ty.kind() {
ty::Int(ity) => { ty::Int(ity) => {
let bits = c let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi"); .expect("expected monomorphic const in cfi");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; 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}"); let _ = write!(s, "{val}");
} }
ty::Uint(_) => { ty::Uint(_) => {
let val = c let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized()) .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi"); .expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}"); let _ = write!(s, "{val}");
} }
ty::Bool => { 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}"); 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 // Close the "L..E" pair
s.push('E'); s.push('E');
compress(dict, DictKey::Const(c), &mut s); compress(dict, DictKey::Const(ct), &mut s);
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. // Transforms a ty:Ty for being encoded and used in the substitution dictionary.
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match t.kind() { match t.kind() {
ty::Array(..) ty::Closure(..)
| ty::Closure(..)
| ty::Coroutine(..) | ty::Coroutine(..)
| ty::CoroutineClosure(..) | ty::CoroutineClosure(..)
| ty::CoroutineWitness(..) | ty::CoroutineWitness(..)
@ -67,6 +66,13 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
| ty::Tuple(..) | ty::Tuple(..)
| ty::UnsafeBinder(_) => t.super_fold_with(self), | 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 => { ty::Bool => {
if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Note: on all platforms that Rust's currently supports, its size and alignment // 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"), "output statistics about monomorphization collection"),
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], 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`)"), "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], dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
dylib_lto: bool = (false, parse_bool, [UNTRACKED], 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) 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 { pub fn stack_protector(&self) -> StackProtector {
if self.target.options.supports_stack_protector { if self.target.options.supports_stack_protector {
self.opts.unstable_opts.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 { if sess.opts.unstable_opts.embed_source {
let dwarf_version = let dwarf_version = sess.dwarf_version();
sess.opts.unstable_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
if dwarf_version < 5 { if dwarf_version < 5 {
sess.dcx().emit_warn(errors::EmbedSourceInsufficientDwarfVersion { dwarf_version }); 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 tcx = tables.tcx;
let ty = ty::Ty::new_static_str(tcx); let ty = ty::Ty::new_static_str(tcx);
let bytes = value.as_bytes(); let bytes = value.as_bytes();
let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); let valtree = ty::ValTree::from_raw_bytes(tcx, bytes);
let val = tcx.valtree_to_const_val((ty, val_tree)); let cv = ty::Value { ty, valtree };
let val = tcx.valtree_to_const_val(cv);
mir::Const::from_value(val, ty).stable(&mut tables) 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; type T = stable_mir::ty::TyConst;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T { fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
let kind = match self.kind() { let ct = tables.tcx.lift(*self).unwrap();
ty::ConstKind::Value(ty, val) => { let kind = match ct.kind() {
let val = match val { ty::ConstKind::Value(cv) => {
ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), let const_val = tables.tcx.valtree_to_const_val(cv);
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));
if matches!(const_val, mir::ConstValue::ZeroSized) { 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 { } else {
stable_mir::ty::TyConstKind::Value( stable_mir::ty::TyConstKind::Value(
ty.stable(tables), cv.ty.stable(tables),
alloc::new_allocation(ty, const_val, 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::Placeholder(_) => unimplemented!(),
ty::ConstKind::Expr(_) => 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) 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> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
// only print integers // only print integers
match ct.kind() { 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 // The `pretty_print_const` formatting depends on -Zverbose-internals
// flag, so we cannot reuse it here. // 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!( write!(
self, 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("_")?, _ => 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> { fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
// We only mangle a typed value if the const can be evaluated. // We only mangle a typed value if the const can be evaluated.
let (ct_ty, valtree) = match ct.kind() { let cv = match ct.kind() {
ty::ConstKind::Value(ty, val) => (ty, val), ty::ConstKind::Value(cv) => cv,
// Should only be encountered within the identity-substituted // Should only be encountered within the identity-substituted
// impl header of an item nested within an impl item. // impl header of an item nested within an impl item.
@ -619,13 +619,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
return Ok(()); return Ok(());
} }
let ty::Value { ty: ct_ty, valtree } = cv;
let start = self.out.len(); let start = self.out.len();
match ct_ty.kind() { match ct_ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => { ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
ct_ty.print(self)?; ct_ty.print(self)?;
let mut bits = ct let mut bits = cv
.try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized()) .try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected const to be monomorphic"); .expect("expected const to be monomorphic");

View file

@ -546,6 +546,7 @@ impl Target {
key!(link_env_remove, list); key!(link_env_remove, list);
key!(asm_args, list); key!(asm_args, list);
key!(cpu); key!(cpu);
key!(need_explicit_cpu, bool);
key!(features); key!(features);
key!(dynamic_linking, bool); key!(dynamic_linking, bool);
key!(direct_access_external_data, Option<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!(link_env_remove);
target_option_val!(asm_args); target_option_val!(asm_args);
target_option_val!(cpu); target_option_val!(cpu);
target_option_val!(need_explicit_cpu);
target_option_val!(features); target_option_val!(features);
target_option_val!(dynamic_linking); target_option_val!(dynamic_linking);
target_option_val!(direct_access_external_data); 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 /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
/// to "generic". /// to "generic".
pub cpu: StaticCow<str>, 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 /// Default target features to pass to LLVM. These features overwrite
/// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`. /// `-Ctarget-cpu` but can be overwritten with `-Ctarget-features`.
/// Corresponds to `llc -mattr=$features`. /// Corresponds to `llc -mattr=$features`.
@ -2686,6 +2689,7 @@ impl Default for TargetOptions {
link_script: None, link_script: None,
asm_args: cvs![], asm_args: cvs![],
cpu: "generic".into(), cpu: "generic".into(),
need_explicit_cpu: false,
features: "".into(), features: "".into(),
direct_access_external_data: None, direct_access_external_data: None,
dynamic_linking: false, dynamic_linking: false,

View file

@ -2023,14 +2023,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
_ => None, _ => None,
}; };
if let Some(tykind) = tykind if let Some(tykind) = tykind
&& let hir::TyKind::Array(_, length) = tykind && let hir::TyKind::Array(_, length_arg) = tykind
&& let Some((scalar, ty)) = sz.found.try_to_scalar() && let Some(length_val) = sz.found.try_to_target_usize(self.tcx)
&& ty == self.tcx.types.usize
{ {
let span = length.span();
Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {
span, span: length_arg.span(),
length: scalar.to_target_usize(&self.tcx).unwrap(), length: length_val,
}) })
} else { } else {
None None

View file

@ -1,10 +1,73 @@
use rustc_hir::def_id::LocalDefId;
use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferCtxt, RegionResolutionError}; use rustc_infer::infer::{InferCtxt, RegionResolutionError};
use rustc_macros::extension; use rustc_macros::extension;
use rustc_middle::traits::ObligationCause; use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::{self, Ty};
use crate::traits::ScrubbedTraitError; 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>)] #[extension(pub trait InferCtxtRegionExt<'tcx>)]
impl<'tcx> InferCtxt<'tcx> { impl<'tcx> InferCtxt<'tcx> {
@ -15,10 +78,25 @@ impl<'tcx> InferCtxt<'tcx> {
/// Prefer this method over `resolve_regions_with_normalize`, unless you are /// Prefer this method over `resolve_regions_with_normalize`, unless you are
/// doing something specific for normalization. /// doing something specific for normalization.
fn resolve_regions( 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, &self,
outlives_env: &OutlivesEnvironment<'tcx>, outlives_env: &OutlivesEnvironment<'tcx>,
) -> Vec<RegionResolutionError<'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); let ty = self.resolve_vars_if_possible(ty);
if self.next_trait_solver() { 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) 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::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!( kind => span_bug!(
obligation.cause.span, obligation.cause.span,
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" "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::fx::{FxIndexMap, FxIndexSet, IndexEntry};
use rustc_data_structures::unord::UnordSet; use rustc_data_structures::unord::UnordSet;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::DefineOpaqueTypes;
use rustc_middle::ty::{Region, RegionVid}; use rustc_middle::ty::{Region, RegionVid};
use tracing::debug; use tracing::debug;
@ -13,6 +14,7 @@ use tracing::debug;
use super::*; use super::*;
use crate::errors::UnableToConstructConstantValue; use crate::errors::UnableToConstructConstantValue;
use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::regions::OutlivesEnvironmentBuildExt;
use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectAndUnifyResult;
// FIXME(twk): this is obviously not nice to duplicate like that // 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:?}"); 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 _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty));
let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone(); 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_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::{Diag, EmissionGuarantee}; use rustc_errors::{Diag, EmissionGuarantee};
use rustc_hir::def::DefKind; 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::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::PredicateObligations; use rustc_infer::traits::PredicateObligations;
use rustc_middle::bug; use rustc_middle::bug;
@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn};
use super::ObligationCtxt; use super::ObligationCtxt;
use crate::error_reporting::traits::suggest_new_overflow_limit; use crate::error_reporting::traits::suggest_new_overflow_limit;
use crate::infer::InferOk; use crate::infer::InferOk;
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect}; use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
use crate::traits::query::evaluate_obligation::InferCtxtExt; 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, // 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 // if that wasn't implemented just for LocalDefId, and we'd need to do
// the normalization ourselves since this is totally fallible... // the normalization ourselves since this is totally fallible...
let outlives_env = OutlivesEnvironment::new(param_env); let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []);
let errors = ocx.resolve_regions(&outlives_env);
if !errors.is_empty() { if !errors.is_empty() {
return false; return false;
} }

View file

@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>(
ty::ConstKind::Param(_) ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _) | ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_) | ty::ConstKind::Placeholder(_)
| ty::ConstKind::Value(_, _) | ty::ConstKind::Value(_)
| ty::ConstKind::Error(_) => return Ok(()), | ty::ConstKind::Error(_) => return Ok(()),
ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
}; };

View file

@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::{ use rustc_infer::infer::canonical::{
Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
}; };
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace}; use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
use rustc_infer::traits::PredicateObligations; use rustc_infer::traits::PredicateObligations;
use rustc_macros::extension; use rustc_macros::extension;
@ -217,14 +216,15 @@ where
/// will result in region constraints getting ignored. /// will result in region constraints getting ignored.
pub fn resolve_regions_and_report_errors( pub fn resolve_regions_and_report_errors(
self, self,
generic_param_scope: LocalDefId, body_id: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>, param_env: ty::ParamEnv<'tcx>,
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
) -> Result<(), ErrorGuaranteed> { ) -> 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() { if errors.is_empty() {
Ok(()) Ok(())
} else { } 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] #[must_use]
pub fn resolve_regions( pub fn resolve_regions(
self, self,
outlives_env: &OutlivesEnvironment<'tcx>, body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
) -> Vec<RegionResolutionError<'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(_) => { ty::ConstKind::Error(_) => {
return ProcessResult::Changed(PendingPredicateObligations::new()); return ProcessResult::Changed(PendingPredicateObligations::new());
} }
ty::ConstKind::Value(ty, _) => ty, ty::ConstKind::Value(cv) => cv.ty,
ty::ConstKind::Unevaluated(uv) => { ty::ConstKind::Unevaluated(uv) => {
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) 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 hir::LangItem;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
use super::outlives_bounds::InferCtxtExt;
use crate::regions::InferCtxtRegionExt; use crate::regions::InferCtxtRegionExt;
use crate::traits::{self, FulfillmentError, ObligationCause}; 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 // Check regions assuming the self type of the impl is WF
let outlives_env = OutlivesEnvironment::with_bounds( let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
param_env,
infcx.implied_bounds_tys(
param_env,
parent_cause.body_id,
&FxIndexSet::from_iter([self_type]),
),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() { if !errors.is_empty() {
infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
continue; continue;
@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>(
} }
// Check regions assuming the self type of the impl is WF // Check regions assuming the self type of the impl is WF
let outlives_env = OutlivesEnvironment::with_bounds( let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
param_env,
infcx.implied_bounds_tys(
param_env,
parent_cause.body_id,
&FxIndexSet::from_iter([self_type]),
),
);
let errors = infcx.resolve_regions(&outlives_env);
if !errors.is_empty() { if !errors.is_empty() {
infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); 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 // We can use the `elaborated_env` here; the region code only
// cares about declarations like `'a: 'b`. // 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 // 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 // still need to use `resolve_regions` as we need the resolved regions in
// the normalized predicates. // the normalized predicates.
let errors = infcx.resolve_regions(&outlives_env); let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []);
if !errors.is_empty() { if !errors.is_empty() {
tcx.dcx().span_delayed_bug( tcx.dcx().span_delayed_bug(
span, span,

View file

@ -1,4 +1,3 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_infer::infer::InferOk; use rustc_infer::infer::InferOk;
use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
@ -12,9 +11,6 @@ use tracing::instrument;
use crate::infer::InferCtxt; use crate::infer::InferCtxt;
use crate::traits::{ObligationCause, ObligationCtxt}; 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 /// Implied bounds are region relationships that we deduce
/// automatically. The idea is that (e.g.) a caller must check that a /// automatically. The idea is that (e.g.) a caller must check that a
/// function's argument types are well-formed immediately before /// function's argument types are well-formed immediately before
@ -110,36 +106,18 @@ fn implied_outlives_bounds<'a, 'tcx>(
bounds bounds
} }
#[extension(pub trait InferCtxtExt<'a, 'tcx>)] #[extension(pub trait InferCtxtExt<'tcx>)]
impl<'a, 'tcx: 'a> InferCtxt<'tcx> { impl<'tcx> InferCtxt<'tcx> {
/// Do *NOT* call this directly. /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`
fn implied_bounds_tys_compat( /// instead if you're interested in the implied bounds for a given signature.
&'a self, fn implied_bounds_tys_with_compat<Tys: IntoIterator<Item = Ty<'tcx>>>(
param_env: ParamEnv<'tcx>, &self,
body_id: LocalDefId, body_id: LocalDefId,
tys: &'a FxIndexSet<Ty<'tcx>>, param_env: ParamEnv<'tcx>,
tys: Tys,
compat: bool, compat: bool,
) -> BoundsCompat<'a, 'tcx> { ) -> impl Iterator<Item = OutlivesBound<'tcx>> {
tys.iter() tys.into_iter()
.flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat)) .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,
)
})
} }
} }

View file

@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Ok(EvaluatedToAmbig); return Ok(EvaluatedToAmbig);
} }
ty::ConstKind::Error(_) => return Ok(EvaluatedToOk), ty::ConstKind::Error(_) => return Ok(EvaluatedToOk),
ty::ConstKind::Value(ty, _) => ty, ty::ConstKind::Value(cv) => cv.ty,
ty::ConstKind::Unevaluated(uv) => { ty::ConstKind::Unevaluated(uv) => {
self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args) self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args)
} }

View file

@ -128,16 +128,16 @@ mod rustc {
pub fn from_const<'tcx>( pub fn from_const<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
c: Const<'tcx>, ct: Const<'tcx>,
) -> Option<Self> { ) -> Option<Self> {
use rustc_middle::ty::ScalarInt; use rustc_middle::ty::ScalarInt;
use rustc_span::sym; use rustc_span::sym;
let Some((cv, ty)) = c.try_to_valtree() else { let Some(cv) = ct.try_to_value() else {
return None; return None;
}; };
let adt_def = ty.ty_adt_def()?; let adt_def = cv.ty.ty_adt_def()?;
assert_eq!( assert_eq!(
tcx.require_lang_item(LangItem::TransmuteOpts, None), tcx.require_lang_item(LangItem::TransmuteOpts, None),
@ -147,7 +147,7 @@ mod rustc {
); );
let variant = adt_def.non_enum_variant(); let variant = adt_def.non_enum_variant();
let fields = match cv { let fields = match cv.valtree {
ValTree::Branch(branch) => branch, ValTree::Branch(branch) => branch,
_ => { _ => {
return Some(Self { return Some(Self {

View file

@ -22,16 +22,16 @@ fn destructure_const<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
const_: ty::Const<'tcx>, const_: ty::Const<'tcx>,
) -> ty::DestructuredConst<'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_) bug!("cannot destructure constant {:?}", const_)
}; };
let branches = match valtree { let branches = match cv.valtree {
ty::ValTree::Branch(b) => b, ty::ValTree::Branch(b) => b,
_ => bug!("cannot destructure constant {:?}", const_), _ => 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) => { ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
// construct the consts for the elements of the array/slice // construct the consts for the elements of the array/slice
let field_consts = branches 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)) 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>, const_: ty::Const<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
cx: &LayoutCx<'tcx>, cx: &LayoutCx<'tcx>,
) -> Result<ty::Const<'tcx>, &'tcx LayoutError<'tcx>> { ) -> Result<ty::Value<'tcx>, &'tcx LayoutError<'tcx>> {
match const_.kind() { match const_.kind() {
ty::ConstKind::Value(..) => Ok(const_), ty::ConstKind::Value(cv) => Ok(cv),
ty::ConstKind::Error(guar) => { ty::ConstKind::Error(guar) => {
return Err(error(cx, LayoutError::ReferencesError(guar))); return Err(error(cx, LayoutError::ReferencesError(guar)));
} }
@ -209,13 +209,12 @@ fn layout_of_uncached<'tcx>(
&mut layout.backend_repr &mut layout.backend_repr
{ {
if let Some(start) = start { if let Some(start) = start {
scalar.valid_range_mut().start = scalar.valid_range_mut().start = extract_const_value(start, ty, cx)?
validate_const_with_value(start, ty, cx)?
.try_to_bits(tcx, cx.typing_env) .try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
} }
if let Some(end) = end { 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) .try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
if !include_end { if !include_end {
@ -348,9 +347,7 @@ fn layout_of_uncached<'tcx>(
// Arrays and slices. // Arrays and slices.
ty::Array(element, count) => { ty::Array(element, count) => {
let count = validate_const_with_value(count, ty, cx)? let count = extract_const_value(count, ty, cx)?
.to_valtree()
.0
.try_to_target_usize(tcx) .try_to_target_usize(tcx)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;

View file

@ -31,7 +31,7 @@ pub enum ConstKind<I: Interner> {
Unevaluated(ty::UnevaluatedConst<I>), Unevaluated(ty::UnevaluatedConst<I>),
/// Used to hold computed value. /// 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 /// A placeholder for a const which could not be computed; this is
/// propagated to avoid useless error messages. /// 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), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var),
Placeholder(placeholder) => write!(f, "{placeholder:?}"), Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => write!(f, "{uv:?}"), Unevaluated(uv) => write!(f, "{uv:?}"),
Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"), Value(val) => write!(f, "{val:?}"),
Error(_) => write!(f, "{{const error}}"), Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{expr:?}"), 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() { match lhs.kind() {
ty::ConstKind::Value(_, lhs_val) => match rhs.kind() { ty::ConstKind::Value(lhs_val) => match rhs.kind() {
ty::ConstKind::Value(_, rhs_val) => lhs_val == rhs_val, ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(),
_ => false, _ => 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> { pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
fn args(self) -> I::GenericArgs; fn args(self) -> I::GenericArgs;
} }

View file

@ -112,8 +112,9 @@ pub trait Interner:
type PlaceholderConst: PlaceholderLike; type PlaceholderConst: PlaceholderLike;
type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
type ValueConst: Copy + Debug + Hash + Eq; type ValueConst: ValueConst<Self>;
type ExprConst: ExprConst<Self>; type ExprConst: ExprConst<Self>;
type ValTree: Copy + Debug + Hash + Eq;
// Kinds of regions // Kinds of regions
type Region: Region<Self>; type Region: Region<Self>;

View file

@ -606,7 +606,9 @@ pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
true true
} }
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2, (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` // 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 // 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. /// 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`. /// 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; /// Does not invalidate any handles or references pointing into the subtree
/// it will not invalidate other handles or references to the root node. /// rooted at the first child of `self`.
/// ///
/// Panics if there is no internal level, i.e., if the root node is a leaf. /// 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) { 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 /// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
/// data described via [`Layout`][]. /// data described via [`Layout`][].
/// ///
/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having /// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers.
/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the /// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
/// allocated memory. /// allocated memory.
/// ///
/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `Allocator`. If an underlying /// In contrast to [`GlobalAlloc`][], `Allocator` allows zero-sized allocations. If an underlying
/// allocator does not support this (like jemalloc) or return a null pointer (such as /// allocator does not support this (like jemalloc) or responds by returning a null pointer
/// `libc::malloc`), this must be caught by the implementation. /// (such as `libc::malloc`), this must be caught by the implementation.
/// ///
/// ### Currently allocated memory /// ### Currently allocated memory
/// ///
/// Some of the methods require that a memory block be *currently allocated* via an allocator. This /// Some of the methods require that a memory block is *currently allocated* by an allocator.
/// means that: /// 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 /// A memory block is deallocated by a call to [`deallocate`],
/// [`shrink`], and /// or by a call to [`grow`] or [`shrink`] that returns `Ok`.
/// /// A call to `grow` or `shrink` that returns `Err`,
/// * the memory block has not been subsequently deallocated, where blocks are either deallocated /// does not deallocate the memory block passed to it.
/// 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.
/// ///
/// [`allocate`]: Allocator::allocate /// [`allocate`]: Allocator::allocate
/// [`grow`]: Allocator::grow /// [`grow`]: Allocator::grow
@ -77,32 +77,28 @@ impl fmt::Display for AllocError {
/// ///
/// ### Memory fitting /// ### Memory fitting
/// ///
/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to /// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the
/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
/// following conditions must hold: /// following conditions must hold:
/// /// * the memory block must be *currently allocated* with alignment of [`layout.align()`], and
/// * The block must be allocated with the same alignment as [`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
/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where: /// - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`].
/// - `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`].
/// ///
/// [`layout.align()`]: Layout::align /// [`layout.align()`]: Layout::align
/// [`layout.size()`]: Layout::size /// [`layout.size()`]: Layout::size
/// ///
/// # Safety /// # Safety
/// ///
/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to /// Memory blocks that are [*currently allocated*] by an allocator,
/// valid memory and retain their validity while they are [*currently allocated*] and the shorter /// must point to valid memory, and retain their validity while until either:
/// of: /// - the memory block is deallocated, or
/// - the borrow-checker lifetime of the allocator type itself. /// - the allocator is dropped.
/// - as long as at least one of the instance and all of its clones has not been dropped.
/// ///
/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this /// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it
/// allocator. A copied or cloned allocator must behave like the same allocator, and /// 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 /// A memory block which is [*currently allocated*] may be passed to
/// method of the allocator. /// any method of the allocator that accepts such an argument.
/// ///
/// [*currently allocated*]: #currently-allocated-memory /// [*currently allocated*]: #currently-allocated-memory
#[unstable(feature = "allocator_api", issue = "32838")] #[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> { impl<T> fmt::Debug for MaybeUninit<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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<{..}>"). // 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 full_name = type_name::<Self>();
let short_name = full_name.split_once("mem::maybe_uninit::").unwrap().1; let prefix_len = full_name.find("MaybeUninit").unwrap();
f.pad(short_name) f.pad(&full_name[prefix_len..])
} }
} }

View file

@ -1322,20 +1322,6 @@ pub enum FpCategory {
Normal, 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 /// Determines if a string of text of that length of that radix could be guaranteed to be
/// stored in the given type T. /// 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 /// 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)] #[cfg_attr(feature = "panic_immediate_abort", inline)]
#[cold] #[cold]
#[track_caller] #[track_caller]
const fn from_str_radix_panic(radix: u32) -> ! { const fn from_ascii_radix_panic(radix: u32) -> ! {
const_panic!( const_panic!(
"from_str_radix_int: must lie in the range `[2, 36]`", "from_ascii_radix: radix 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]` - found {radix}",
radix: u32 = radix, radix: u32 = radix,
) )
} }
macro_rules! from_str_radix { macro_rules! from_str_int_impl {
($signedness:ident $($int_ty:ty)+) => {$( ($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 { 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 /// The string is expected to be an optional
#[doc = sign_dependent_expr!{ #[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 /// 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. /// also represent an error.
/// ///
/// Digits are a subset of these characters, depending on `radix`: /// 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")] #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")]
#[inline] #[inline]
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { 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::IntErrorKind::*;
use self::ParseIntError as PIE; use self::ParseIntError as PIE;
if 2 > radix || radix > 36 { if 2 > radix || radix > 36 {
from_str_radix_panic(radix); from_ascii_radix_panic(radix);
} }
if src.is_empty() { if src.is_empty() {
@ -1415,12 +1522,6 @@ macro_rules! from_str_radix {
#[allow(unused_comparisons)] #[allow(unused_comparisons)]
let is_signed_ty = 0 > <$int_ty>::MIN; 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 { let (is_positive, mut digits) = match src {
[b'+' | b'-'] => { [b'+' | b'-'] => {
return Err(PIE { kind: InvalidDigit }); return Err(PIE { kind: InvalidDigit });
@ -1496,67 +1597,8 @@ macro_rules! from_str_radix {
Ok(result) Ok(result)
} }
} }
)+} )*}
} }
from_str_radix! { unsigned u8 u16 u32 u64 u128 } from_str_int_impl! { signed isize i8 i16 i32 i64 i128 }
from_str_radix! { signed i8 i16 i32 i64 i128 } from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 }
// 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 }

View file

@ -12,6 +12,9 @@ pub use crate::marker::{Copy, Send, Sized, Sync, Unpin};
#[stable(feature = "core_prelude", since = "1.4.0")] #[stable(feature = "core_prelude", since = "1.4.0")]
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::ops::{Drop, Fn, FnMut, FnOnce}; 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 // Re-exported functions
#[stable(feature = "core_prelude", since = "1.4.0")] #[stable(feature = "core_prelude", since = "1.4.0")]

View file

@ -1,6 +1,8 @@
use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::{cmp, ptr}; use crate::{cmp, ptr};
type BufType = [usize; 32];
/// Rotates the range `[mid-left, mid+right)` such that the element at `mid` becomes the first /// 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 /// element. Equivalently, rotates the range `left` elements to the left or `right` elements to the
/// right. /// right.
@ -8,14 +10,82 @@ use crate::{cmp, ptr};
/// # Safety /// # Safety
/// ///
/// The specified range must be valid for reading and writing. /// 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 /// The specified range must be valid for reading and writing.
/// into their final positions one at a time starting at `mid - left` and advancing by `right` steps #[inline]
/// modulo `left + right`, such that only one temporary is needed. Eventually, we arrive back at unsafe fn ptr_rotate_memmove<T>(left: usize, mid: *mut T, right: usize) {
/// `mid - left`. However, if `gcd(left + right, right)` is not 1, the above steps skipped over // The `[T; 0]` here is to ensure this is appropriately aligned for T
/// elements. For example: 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 /// ```text
/// left = 10, right = 6 /// left = 10, right = 6
/// the `^` indicates an element in its final place /// the `^` indicates an element in its final place
@ -39,41 +109,16 @@ use crate::{cmp, ptr};
/// `gcd(left + right, right)` value). The end result is that all elements are finalized once and /// `gcd(left + right, right)` value). The end result is that all elements are finalized once and
/// only once. /// only once.
/// ///
/// Algorithm 2 is used if `left + right` is large but `min(left, right)` is small enough to /// Algorithm 2 can be vectorized by chunking and performing many rounds at once, but there are too
/// 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
/// few rounds on average until `left + right` is enormous, and the worst case of a single /// 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 /// round is always there.
/// `min(left, right)` elements until a smaller rotate problem is left.
/// ///
/// ```text /// # Safety
/// left = 11, right = 4 ///
/// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3] /// The specified range must be valid for reading and writing.
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping the right most elements with elements to the left #[inline]
/// [4 5 6 7 8 9 10 . 0 1 2 3] 11 12 13 14 unsafe fn ptr_rotate_gcd<T>(left: usize, mid: *mut T, right: usize) {
/// ^ ^ ^ ^ ^ ^ ^ ^ swapping these // Algorithm 2
/// [4 5 6 . 0 1 2 3] 7 8 9 10 11 12 13 14
/// 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;
}
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 // 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 // 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 // around 16. 24 was chosen as middle ground. If the size of `T` is larger than 4
@ -157,47 +202,29 @@ pub(super) unsafe fn ptr_rotate<T>(mut left: usize, mut mid: *mut T, mut 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") /// Algorithm 3 utilizes repeated swapping of `min(left, right)` elements.
&& cmp::min(left, right) <= mem::size_of::<BufType>() / mem::size_of::<T>() ///
{ /// ///
// Algorithm 2 /// ```text
// The `[T; 0]` here is to ensure this is appropriately aligned for T /// left = 11, right = 4
let mut rawarray = MaybeUninit::<(BufType, [T; 0])>::uninit(); /// [4 5 6 7 8 9 10 11 12 13 14 . 0 1 2 3]
let buf = rawarray.as_mut_ptr() as *mut T; /// ^ ^ ^ ^ ^ ^ ^ ^ swapping the right most elements with elements to the left
// SAFETY: `mid-left <= mid-left+right < mid+right` /// [4 5 6 7 8 9 10 . 0 1 2 3] 11 12 13 14
let dim = unsafe { mid.sub(left).add(right) }; /// ^ ^ ^ ^ ^ ^ ^ ^ swapping these
if left <= right { /// [4 5 6 . 0 1 2 3] 7 8 9 10 11 12 13 14
// SAFETY: /// we cannot swap any more, but a smaller rotation problem is left to solve
// /// ```
// 1) The `else if` condition about the sizes ensures `[mid-left; left]` will fit in /// when `left < right` the swapping happens from the left instead.
// `buf` without overflow and `buf` was created just above and so cannot be ///
// overlapped with any value of `[mid-left; left]` /// # Safety
// 2) [mid-left, mid+right) are all valid for reading and writing and we don't care ///
// about overlaps here. /// The specified range must be valid for reading and writing.
// 3) The `if` condition about `left <= right` ensures writing `left` elements to #[inline]
// `dim = mid-left+right` is valid because: unsafe fn ptr_rotate_swap<T>(mut left: usize, mut mid: *mut T, mut right: usize) {
// - `buf` is valid and `left` elements were written in it in 1) loop {
// - `dim+left = mid-left+right+left = mid+right` and we write `[dim, dim+left)` if left >= right {
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 {
// Algorithm 3 // Algorithm 3
// There is an alternate way of swapping that involves finding where the last swap // 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 // 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 /// This acquires an exclusive advisory lock; no other file handle to this file may acquire
/// another lock. /// another lock.
/// ///
/// If this file handle, or a clone of it, already holds an advisory lock the exact behavior is /// If this file handle/descriptor, or a clone of it, already holds an advisory lock the exact
/// unspecified and platform dependent, including the possibility that it will deadlock. /// behavior is unspecified and platform dependent, including the possibility that it will
/// However, if this method returns, then an exclusive lock is held. /// 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. /// 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`] /// [`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. /// 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 /// # Platform-specific behavior
/// ///
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag, /// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` flag,
@ -671,19 +674,22 @@ impl File {
self.inner.lock() 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 /// 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 /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
/// unspecified and platform dependent, including the possibility that it will deadlock. /// behavior is unspecified and platform dependent, including the possibility that it will
/// However, if this method returns, then a shared lock is held. /// 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`], /// 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`] /// [`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. /// 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 /// # Platform-specific behavior
/// ///
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` flag, /// 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() 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 /// This acquires an exclusive advisory lock; no other file handle to this file may acquire
/// another lock. /// another lock.
/// ///
/// If this file handle, or a clone of it, already holds an advisory lock, the exact behavior is /// If this file handle/descriptor, or a clone of it, already holds an advisory lock, the exact
/// unspecified and platform dependent, including the possibility that it will deadlock. /// behavior is unspecified and platform dependent, including the possibility that it will
/// However, if this method returns, then an exclusive lock is held. /// 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. /// 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`] /// [`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. /// 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 /// # Platform-specific behavior
/// ///
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_EX` and /// 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() self.inner.try_lock()
} }
/// Acquire a shared advisory lock on the file. /// Try to acquire a shared (non-exclusive) advisory lock on the file.
/// Returns `Ok(false)` if the file is exclusively locked. ///
/// 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 /// 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 /// 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. /// 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`], /// 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`] /// [`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. /// 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 /// # Platform-specific behavior
/// ///
/// This function currently corresponds to the `flock` function on Unix with the `LOCK_SH` and /// 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. /// 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 /// # 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::ffi::{OsStr, OsString};
use crate::io::{self, const_error}; use crate::io::{self, const_error};
use crate::marker::PhantomData;
use crate::mem::{MaybeUninit, size_of}; use crate::mem::{MaybeUninit, size_of};
use crate::os::uefi::env::boot_services; use crate::os::uefi::env::boot_services;
use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
use crate::os::uefi::{self}; use crate::os::uefi::{self};
use crate::path::Path;
use crate::ptr::NonNull; use crate::ptr::NonNull;
use crate::slice; use crate::slice;
use crate::sync::atomic::{AtomicPtr, Ordering}; 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 { pub(crate) const fn as_ptr(&self) -> *mut r_efi::protocols::device_path::Protocol {
self.0.as_ptr() self.0.as_ptr()
} }
pub(crate) const fn borrow<'a>(&'a self) -> BorrowedDevicePath<'a> {
BorrowedDevicePath::new(self.0)
}
} }
impl Drop for OwnedDevicePath { impl Drop for OwnedDevicePath {
@ -293,13 +299,37 @@ impl Drop for OwnedDevicePath {
impl crate::fmt::Debug for OwnedDevicePath { impl crate::fmt::Debug for OwnedDevicePath {
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { 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), Ok(p) => p.fmt(f),
Err(_) => f.debug_struct("OwnedDevicePath").finish_non_exhaustive(), 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> { pub(crate) struct OwnedProtocol<T> {
guid: r_efi::efi::Guid, guid: r_efi::efi::Guid,
handle: NonNull<crate::ffi::c_void>, handle: NonNull<crate::ffi::c_void>,
@ -452,3 +482,21 @@ pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> {
None 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"))] { } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx; mod sgx;
pub use sgx::*; pub use sgx::*;
} else if #[cfg(any( } else if #[cfg(target_os = "solid_asp3")] {
target_os = "uefi",
target_os = "solid_asp3",
))] {
mod unsupported_backslash; mod unsupported_backslash;
pub use unsupported_backslash::*; pub use unsupported_backslash::*;
} else if #[cfg(target_os = "uefi")] {
mod uefi;
pub use uefi::*;
} else { } else {
mod unix; mod unix;
pub use 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": [], "directories": [],
"files": [ "files": [
"FiraMono-Medium.woff2",
"FiraMono-Regular.woff2",
"FiraSans-Italic.woff2",
"FiraSans-LICENSE.txt", "FiraSans-LICENSE.txt",
"FiraSans-Medium.woff2", "FiraSans-Medium.woff2",
"FiraSans-MediumItalic.woff2",
"FiraSans-Regular.woff2" "FiraSans-Regular.woff2"
], ],
"license": { "license": {

View file

@ -86,6 +86,7 @@ impl Step for CrateBootstrap {
SourceType::InTree, SourceType::InTree,
&[], &[],
); );
let crate_name = path.rsplit_once('/').unwrap().1; let crate_name = path.rsplit_once('/').unwrap().1;
run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder); run_cargo_test(cargo, &[], &[], crate_name, crate_name, bootstrap_host, builder);
} }
@ -3106,6 +3107,8 @@ impl Step for Bootstrap {
&[], &[],
); );
cargo.release_build(false);
cargo cargo
.rustflag("-Cdebuginfo=2") .rustflag("-Cdebuginfo=2")
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap")) .env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))

View file

@ -88,12 +88,14 @@ impl HostFlags {
#[derive(Debug)] #[derive(Debug)]
pub struct Cargo { pub struct Cargo {
command: BootstrapCommand, command: BootstrapCommand,
args: Vec<OsString>,
compiler: Compiler, compiler: Compiler,
target: TargetSelection, target: TargetSelection,
rustflags: Rustflags, rustflags: Rustflags,
rustdocflags: Rustflags, rustdocflags: Rustflags,
hostflags: HostFlags, hostflags: HostFlags,
allow_features: String, allow_features: String,
release_build: bool,
} }
impl Cargo { impl Cargo {
@ -121,6 +123,10 @@ impl Cargo {
cargo cargo
} }
pub fn release_build(&mut self, release_build: bool) {
self.release_build = release_build;
}
pub fn compiler(&self) -> Compiler { pub fn compiler(&self) -> Compiler {
self.compiler self.compiler
} }
@ -153,7 +159,7 @@ impl Cargo {
} }
pub fn arg(&mut self, arg: impl AsRef<OsStr>) -> &mut 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 self
} }
@ -342,6 +348,12 @@ impl Cargo {
impl From<Cargo> for BootstrapCommand { impl From<Cargo> for BootstrapCommand {
fn from(mut cargo: Cargo) -> 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; let rustflags = &cargo.rustflags.0;
if !rustflags.is_empty() { if !rustflags.is_empty() {
cargo.command.env("RUSTFLAGS", rustflags); cargo.command.env("RUSTFLAGS", rustflags);
@ -360,6 +372,7 @@ impl From<Cargo> for BootstrapCommand {
if !cargo.allow_features.is_empty() { if !cargo.allow_features.is_empty() {
cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features); cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
} }
cargo.command cargo.command
} }
} }
@ -429,13 +442,6 @@ impl Builder<'_> {
assert_eq!(target, compiler.host); 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 // Remove make-related flags to ensure Cargo can correctly set things up
cargo.env_remove("MAKEFLAGS"); cargo.env_remove("MAKEFLAGS");
cargo.env_remove("MFLAGS"); cargo.env_remove("MFLAGS");
@ -1218,14 +1224,20 @@ impl Builder<'_> {
rustflags.arg("-Zmir_strip_debuginfo=locals-in-tiny-functions"); 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 { Cargo {
command: cargo, command: cargo,
args: vec![],
compiler, compiler,
target, target,
rustflags, rustflags,
rustdocflags, rustdocflags,
hostflags, hostflags,
allow_features, allow_features,
release_build,
} }
} }
} }

View file

@ -41,7 +41,9 @@ jobs:
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4
with: with:
path: book/linkcheck/cache.json 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 - name: Install latest nightly Rust toolchain
if: steps.mdbook-cache.outputs.cache-hit != 'true' if: steps.mdbook-cache.outputs.cache-hit != 'true'
@ -66,7 +68,7 @@ jobs:
uses: actions/cache/save@v4 uses: actions/cache/save@v4
with: with:
path: book/linkcheck/cache.json 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 - name: Deploy to gh-pages
if: github.event_name == 'push' 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` 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 if [[ "$RESULT" -eq 0 ]]; then
echo "Creating new pull request" 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 echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
else 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 echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
fi fi
env: env:

View file

@ -1 +1 @@
ecda83b30f0f68cf5692855dddc0bc38ee8863fc 66d6064f9eb888018775e08f84747ee6f39ba28e

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