Merge from rustc

This commit is contained in:
Ralf Jung 2025-02-05 09:36:46 +01:00
commit 7f95fa4cee
378 changed files with 4214 additions and 3006 deletions

View file

@ -186,6 +186,9 @@ name = "anyhow"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
dependencies = [
"backtrace",
]
[[package]]
name = "ar_archive_writer"
@ -1195,6 +1198,17 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "features-status-dump"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"serde",
"serde_json",
"tidy",
]
[[package]]
name = "field-offset"
version = "0.3.6"
@ -5418,6 +5432,7 @@ dependencies = [
"regex",
"rustc-hash 2.1.0",
"semver",
"serde",
"similar",
"termcolor",
"walkdir",

View file

@ -47,6 +47,7 @@ members = [
"src/tools/coverage-dump",
"src/tools/rustc-perf-wrapper",
"src/tools/wasm-component-ld",
"src/tools/features-status-dump",
]
exclude = [

View file

@ -284,9 +284,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
ExprKind::Index(el, er, brackets_span) => {
hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span)
}
ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => {
self.lower_expr_range_closed(e.span, e1, e2)
}
ExprKind::Range(e1, e2, lims) => {
self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims)
}
@ -1512,15 +1509,39 @@ impl<'hir> LoweringContext<'_, 'hir> {
let lang_item = match (e1, e2, lims) {
(None, None, HalfOpen) => hir::LangItem::RangeFull,
(Some(..), None, HalfOpen) => hir::LangItem::RangeFrom,
(Some(..), None, HalfOpen) => {
if self.tcx.features().new_range() {
hir::LangItem::RangeFromCopy
} else {
hir::LangItem::RangeFrom
}
}
(None, Some(..), HalfOpen) => hir::LangItem::RangeTo,
(Some(..), Some(..), HalfOpen) => hir::LangItem::Range,
(Some(..), Some(..), HalfOpen) => {
if self.tcx.features().new_range() {
hir::LangItem::RangeCopy
} else {
hir::LangItem::Range
}
}
(None, Some(..), Closed) => hir::LangItem::RangeToInclusive,
(Some(..), Some(..), Closed) => unreachable!(),
(Some(e1), Some(e2), Closed) => {
if self.tcx.features().new_range() {
hir::LangItem::RangeInclusiveCopy
} else {
return self.lower_expr_range_closed(span, e1, e2);
}
}
(start, None, Closed) => {
self.dcx().emit_err(InclusiveRangeWithNoEnd { span });
match start {
Some(..) => hir::LangItem::RangeFrom,
Some(..) => {
if self.tcx.features().new_range() {
hir::LangItem::RangeFromCopy
} else {
hir::LangItem::RangeFrom
}
}
None => hir::LangItem::RangeFull,
}
}

View file

@ -412,8 +412,12 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
self.visit_pat(p)
fn visit_pattern_type_pattern(&mut self, pat: &'hir hir::TyPat<'hir>) {
self.insert(pat.span, pat.hir_id, Node::TyPat(pat));
self.with_parent(pat.hir_id, |this| {
intravisit::walk_ty_pat(this, pat);
});
}
fn visit_precise_capturing_arg(

View file

@ -1377,7 +1377,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
}
}
TyKind::Pat(ty, pat) => hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_pat(pat)),
TyKind::Pat(ty, pat) => {
hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat))
}
TyKind::MacCall(_) => {
span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now")
}

View file

@ -4,10 +4,10 @@ use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def::{DefKind, Res};
use rustc_middle::span_bug;
use rustc_span::source_map::{Spanned, respan};
use rustc_span::{Ident, Span};
use rustc_span::{Ident, Span, kw};
use super::errors::{
ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
@ -429,4 +429,81 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind })
}
pub(crate) fn lower_ty_pat(&mut self, pattern: &Pat) -> &'hir hir::TyPat<'hir> {
self.arena.alloc(self.lower_ty_pat_mut(pattern))
}
fn lower_ty_pat_mut(&mut self, mut pattern: &Pat) -> hir::TyPat<'hir> {
// loop here to avoid recursion
let pat_hir_id = self.lower_node_id(pattern.id);
let node = loop {
match &pattern.kind {
PatKind::Range(e1, e2, Spanned { node: end, .. }) => {
// FIXME(pattern_types): remove this closure and call `lower_const_arg` instead.
// That requires first modifying the AST to have const args here.
let mut lower_expr = |e: &Expr| -> &_ {
if let ExprKind::Path(None, path) = &e.kind
&& let Some(res) = self
.resolver
.get_partial_res(e.id)
.and_then(|partial_res| partial_res.full_res())
{
self.lower_const_path_to_const_arg(path, res, e.id, e.span)
} else {
let node_id = self.next_node_id();
let def_id = self.create_def(
self.current_hir_id_owner.def_id,
node_id,
kw::Empty,
DefKind::AnonConst,
e.span,
);
let hir_id = self.lower_node_id(node_id);
let ac = self.arena.alloc(hir::AnonConst {
def_id,
hir_id,
body: self.lower_const_body(pattern.span, Some(e)),
span: self.lower_span(pattern.span),
});
self.arena.alloc(hir::ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(ac),
})
}
};
break hir::TyPatKind::Range(
e1.as_deref().map(|e| lower_expr(e)),
e2.as_deref().map(|e| lower_expr(e)),
self.lower_range_end(end, e2.is_some()),
);
}
// return inner to be processed in next loop
PatKind::Paren(inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
PatKind::Err(guar) => break hir::TyPatKind::Err(*guar),
PatKind::Deref(..)
| PatKind::Box(..)
| PatKind::Or(..)
| PatKind::Struct(..)
| PatKind::TupleStruct(..)
| PatKind::Tuple(..)
| PatKind::Ref(..)
| PatKind::Expr(..)
| PatKind::Guard(..)
| PatKind::Slice(_)
| PatKind::Ident(..)
| PatKind::Path(..)
| PatKind::Wild
| PatKind::Never
| PatKind::Rest => {
break hir::TyPatKind::Err(
self.dcx().span_err(pattern.span, "pattern not supported in pattern types"),
);
}
}
};
hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) }
}
}

View file

@ -3112,12 +3112,24 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
drop_span, borrow_span
);
// `TerminatorKind::Return`'s span (the `drop_span` here) `lo` can be subtly wrong and point
// at a single character after the end of the function. This is somehow relied upon in
// existing diagnostics, and changing this in `rustc_mir_build` makes diagnostics worse in
// general. We fix these here.
let sm = self.infcx.tcx.sess.source_map();
let end_of_function = if drop_span.is_empty()
&& let Ok(adjusted_span) = sm.span_extend_prev_while(drop_span, |c| c == '}')
{
adjusted_span
} else {
drop_span
};
self.thread_local_value_does_not_live_long_enough(borrow_span)
.with_span_label(
borrow_span,
"thread-local variables cannot be borrowed beyond the end of the function",
)
.with_span_label(drop_span, "end of enclosing function is here")
.with_span_label(end_of_function, "end of enclosing function is here")
}
#[instrument(level = "debug", skip(self))]

View file

@ -465,6 +465,20 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
unchecked_umul(x, y) => LLVMBuildNUWMul,
}
fn or_disjoint(&mut self, a: &'ll Value, b: &'ll Value) -> &'ll Value {
unsafe {
let or = llvm::LLVMBuildOr(self.llbuilder, a, b, UNNAMED);
// If a and b are both values, then `or` is a value, rather than
// an instruction, so we need to check before setting the flag.
// (See also `LLVMBuildNUWNeg` which also needs a check.)
if llvm::LLVMIsAInstruction(or).is_some() {
llvm::LLVMSetIsDisjoint(or, True);
}
or
}
}
set_math_builder_methods! {
fadd_fast(x, y) => (LLVMBuildFAdd, LLVMRustSetFastMath),
fsub_fast(x, y) => (LLVMBuildFSub, LLVMRustSetFastMath),

View file

@ -1380,6 +1380,9 @@ unsafe extern "C" {
pub fn LLVMBuildFNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value;
// Extra flags on arithmetic
pub fn LLVMSetIsDisjoint(Instr: &Value, IsDisjoint: Bool);
// Memory
pub fn LLVMBuildAlloca<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value;
pub fn LLVMBuildArrayAlloca<'a>(

View file

@ -225,6 +225,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
args[1].val.unaligned_volatile_store(bx, dst);
return Ok(());
}
sym::disjoint_bitor => {
let a = args[0].immediate();
let b = args[1].immediate();
bx.or_disjoint(a, b)
}
sym::exact_div => {
let ty = arg_tys[0];
match int_type_width_signed(ty, bx.tcx()) {

View file

@ -181,6 +181,11 @@ pub trait BuilderMethods<'a, 'tcx>:
fn unchecked_umul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn and(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn or(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
/// Defaults to [`Self::or`], but guarantees `(lhs & rhs) == 0` so some backends
/// can emit something more helpful for optimizations.
fn or_disjoint(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
self.or(lhs, rhs)
}
fn xor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn neg(&mut self, v: Self::Value) -> Self::Value;
fn fneg(&mut self, v: Self::Value) -> Self::Value;

View file

@ -101,6 +101,10 @@ impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> {
s.s.space();
s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
}
pprust_hir::AnnNode::TyPat(pat) => {
s.s.space();
s.synth_comment(format!("ty pat hir_id: {}", pat.hir_id));
}
pprust_hir::AnnNode::Arm(arm) => {
s.s.space();
s.synth_comment(format!("arm hir_id: {}", arm.hir_id));

View file

@ -507,6 +507,8 @@ declare_features! (
(incomplete, generic_const_exprs, "1.56.0", Some(76560)),
/// Allows generic parameters and where-clauses on free & associated const items.
(incomplete, generic_const_items, "1.73.0", Some(113521)),
/// Allows any generic constants being used as pattern type range ends
(incomplete, generic_pattern_types, "CURRENT_RUSTC_VERSION", Some(136574)),
/// Allows registering static items globally, possibly across crates, to iterate over at runtime.
(unstable, global_registration, "1.80.0", Some(125119)),
/// Allows using guards in patterns.
@ -529,6 +531,8 @@ declare_features! (
(unstable, inline_const_pat, "1.58.0", Some(76001)),
/// Allows using `pointer` and `reference` in intra-doc links
(unstable, intra_doc_pointers, "1.51.0", Some(80896)),
// Allows using the `kl` and `widekl` target features and the associated intrinsics
(unstable, keylocker_x86, "CURRENT_RUSTC_VERSION", Some(134813)),
// Allows setting the threshold for the `large_assignments` lint.
(unstable, large_assignments, "1.52.0", Some(83518)),
/// Allow to have type alias types for inter-crate use.
@ -568,6 +572,8 @@ declare_features! (
(unstable, never_type, "1.13.0", Some(35121)),
/// Allows diverging expressions to fall back to `!` rather than `()`.
(unstable, never_type_fallback, "1.41.0", Some(65992)),
/// Switch `..` syntax to use the new (`Copy + IntoIterator`) range types.
(unstable, new_range, "CURRENT_RUSTC_VERSION", Some(123741)),
/// Allows `#![no_core]`.
(unstable, no_core, "1.3.0", Some(29639)),
/// Allows the use of `no_sanitize` attribute.

View file

@ -1418,6 +1418,14 @@ impl<'hir> Block<'hir> {
}
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TyPat<'hir> {
#[stable_hasher(ignore)]
pub hir_id: HirId,
pub kind: TyPatKind<'hir>,
pub span: Span,
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct Pat<'hir> {
#[stable_hasher(ignore)]
@ -1591,6 +1599,15 @@ pub enum PatExprKind<'hir> {
Path(QPath<'hir>),
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum TyPatKind<'hir> {
/// A range pattern (e.g., `1..=2` or `1..2`).
Range(Option<&'hir ConstArg<'hir>>, Option<&'hir ConstArg<'hir>>, RangeEnd),
/// A placeholder for a pattern that wasn't well formed in some way.
Err(ErrorGuaranteed),
}
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum PatKind<'hir> {
/// Represents a wildcard pattern (i.e., `_`).
@ -2313,6 +2330,18 @@ impl Expr<'_> {
[val2],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFromCopy, _),
[val1],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeFromCopy, _),
[val2],
StructTailExpr::None,
),
) => val1.expr.equivalent_for_indexing(val2.expr),
(
ExprKind::Struct(
@ -2325,6 +2354,30 @@ impl Expr<'_> {
[val2, val4],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeCopy, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeCopy, _),
[val2, val4],
StructTailExpr::None,
),
)
| (
ExprKind::Struct(
QPath::LangItem(LangItem::RangeInclusiveCopy, _),
[val1, val3],
StructTailExpr::None,
),
ExprKind::Struct(
QPath::LangItem(LangItem::RangeInclusiveCopy, _),
[val2, val4],
StructTailExpr::None,
),
) => {
val1.expr.equivalent_for_indexing(val2.expr)
&& val3.expr.equivalent_for_indexing(val4.expr)
@ -2354,7 +2407,10 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool {
| LangItem::RangeTo
| LangItem::RangeFrom
| LangItem::RangeFull
| LangItem::RangeToInclusive,
| LangItem::RangeToInclusive
| LangItem::RangeCopy
| LangItem::RangeFromCopy
| LangItem::RangeInclusiveCopy,
..
)
),
@ -3345,7 +3401,7 @@ pub enum TyKind<'hir, Unambig = ()> {
/// Placeholder for a type that has failed to be defined.
Err(rustc_span::ErrorGuaranteed),
/// Pattern types (`pattern_type!(u32 is 1..)`)
Pat(&'hir Ty<'hir>, &'hir Pat<'hir>),
Pat(&'hir Ty<'hir>, &'hir TyPat<'hir>),
/// `TyKind::Infer` means the type should be inferred instead of it having been
/// specified. This can appear anywhere in a type.
///
@ -4331,6 +4387,7 @@ pub enum Node<'hir> {
AssocItemConstraint(&'hir AssocItemConstraint<'hir>),
TraitRef(&'hir TraitRef<'hir>),
OpaqueTy(&'hir OpaqueTy<'hir>),
TyPat(&'hir TyPat<'hir>),
Pat(&'hir Pat<'hir>),
PatField(&'hir PatField<'hir>),
/// Needed as its own node with its own HirId for tracking
@ -4393,6 +4450,7 @@ impl<'hir> Node<'hir> {
| Node::Block(..)
| Node::Ctor(..)
| Node::Pat(..)
| Node::TyPat(..)
| Node::PatExpr(..)
| Node::Arm(..)
| Node::LetStmt(..)

View file

@ -393,10 +393,8 @@ pub trait Visitor<'v>: Sized {
fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result {
walk_expr_field(self, field)
}
fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) {
// Do nothing. Only a few visitors need to know the details of the pattern type,
// and they opt into it. All other visitors will just choke on our fake patterns
// because they aren't in a body.
fn visit_pattern_type_pattern(&mut self, p: &'v TyPat<'v>) -> Self::Result {
walk_ty_pat(self, p)
}
fn visit_generic_param(&mut self, p: &'v GenericParam<'v>) -> Self::Result {
walk_generic_param(self, p)
@ -702,6 +700,18 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) -> V::Res
visitor.visit_expr(arm.body)
}
pub fn walk_ty_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v TyPat<'v>) -> V::Result {
try_visit!(visitor.visit_id(pattern.hir_id));
match pattern.kind {
TyPatKind::Range(lower_bound, upper_bound, _) => {
visit_opt!(visitor, visit_const_arg_unambig, lower_bound);
visit_opt!(visitor, visit_const_arg_unambig, upper_bound);
}
TyPatKind::Err(_) => (),
}
V::Result::output()
}
pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V::Result {
try_visit!(visitor.visit_id(pattern.hir_id));
match pattern.kind {

View file

@ -416,6 +416,11 @@ language_item_table! {
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
// `new_range` types that are `Copy + IntoIterator`
RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None;
RangeCopy, sym::RangeCopy, range_copy_struct, Target::Struct, GenericRequirement::None;
RangeInclusiveCopy, sym::RangeInclusiveCopy, range_inclusive_copy_struct, Target::Struct, GenericRequirement::None;
String, sym::String, string, Target::Struct, GenericRequirement::None;
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}

View file

@ -436,9 +436,6 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t
hir_analysis_parenthesized_fn_trait_expansion =
parenthesized trait syntax expands to `{$expanded_type}`
hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end
hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types
.label = this type is the same as the inner type without a pattern
hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind}
.label = not allowed in type signatures
hir_analysis_precise_capture_self_alias = `Self` can't be captured in `use<...>` precise captures list, since it is an alias

View file

@ -472,7 +472,7 @@ pub fn check_intrinsic_type(
vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
tcx.types.usize,
),
sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
sym::unchecked_div | sym::unchecked_rem | sym::exact_div | sym::disjoint_bitor => {
(1, 0, vec![param(0), param(0)], param(0))
}
sym::unchecked_shl | sym::unchecked_shr => (2, 0, vec![param(0), param(1)], param(0)),

View file

@ -1065,6 +1065,7 @@ fn check_associated_item(
let ty = tcx.type_of(item.def_id).instantiate_identity();
let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty);
wfcx.register_wf_obligation(span, loc, ty.into());
check_sized_if_body(wfcx, item.def_id.expect_local(), ty, Some(span));
Ok(())
}
ty::AssocKind::Fn => {
@ -1189,7 +1190,7 @@ fn check_type_defn<'tcx>(
),
wfcx.param_env,
ty,
tcx.require_lang_item(LangItem::Sized, None),
tcx.require_lang_item(LangItem::Sized, Some(hir_ty.span)),
);
}
@ -1314,7 +1315,7 @@ fn check_item_type(
),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sized, None),
tcx.require_lang_item(LangItem::Sized, Some(ty_span)),
);
}
@ -1644,6 +1645,31 @@ fn check_fn_or_method<'tcx>(
);
}
}
// If the function has a body, additionally require that the return type is sized.
check_sized_if_body(wfcx, def_id, sig.output(), match hir_decl.output {
hir::FnRetTy::Return(ty) => Some(ty.span),
hir::FnRetTy::DefaultReturn(_) => None,
});
}
fn check_sized_if_body<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
def_id: LocalDefId,
ty: Ty<'tcx>,
maybe_span: Option<Span>,
) {
let tcx = wfcx.tcx();
if let Some(body) = tcx.hir().maybe_body_owned_by(def_id) {
let span = maybe_span.unwrap_or(body.value.span);
wfcx.register_bound(
ObligationCause::new(span, def_id, traits::ObligationCauseCode::SizedReturnType),
wfcx.param_env,
ty,
tcx.require_lang_item(LangItem::Sized, Some(span)),
);
}
}
/// The `arbitrary_self_types_pointers` feature implies `arbitrary_self_types`.

View file

@ -198,6 +198,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
{
Some(parent_did)
}
Node::TyPat(_) => Some(parent_did),
_ => None,
}
}

View file

@ -831,8 +831,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
#[instrument(level = "debug", skip(self))]
fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::Pat<'tcx>) {
intravisit::walk_pat(self, p)
fn visit_pattern_type_pattern(&mut self, p: &'tcx hir::TyPat<'tcx>) {
intravisit::walk_ty_pat(self, p)
}
#[instrument(level = "debug", skip(self))]

View file

@ -18,9 +18,10 @@ use crate::hir_ty_lowering::HirTyLowerer;
mod opaque;
fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
use hir::*;
use rustc_middle::ty::Ty;
let tcx = icx.tcx;
let hir_id = tcx.local_def_id_to_hir_id(def_id);
let node = tcx.hir_node(hir_id);
@ -54,7 +55,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
hir_id: arg_hir_id,
kind: ConstArgKind::Anon(&AnonConst { hir_id: anon_hir_id, .. }),
..
}) if anon_hir_id == hir_id => const_arg_anon_type_of(tcx, arg_hir_id, span),
}) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
// Anon consts outside the type system.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
@ -138,10 +139,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
}
}
fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> {
fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> {
use hir::*;
use rustc_middle::ty::Ty;
let tcx = icx.tcx;
match tcx.parent_hir_node(arg_hir_id) {
// Array length const arguments do not have `type_of` fed as there is never a corresponding
// generic parameter definition.
@ -149,7 +152,15 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
if constant.hir_id == arg_hir_id =>
{
return tcx.types.usize;
tcx.types.usize
}
Node::TyPat(pat) => {
let hir::TyKind::Pat(ty, p) = tcx.parent_hir_node(pat.hir_id).expect_ty().kind else {
bug!()
};
assert_eq!(p.hir_id, pat.hir_id);
icx.lower_ty(ty)
}
// This is not a `bug!` as const arguments in path segments that did not resolve to anything
@ -344,7 +355,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
tcx.typeck(def_id).node_type(hir_id)
}
Node::AnonConst(_) => anon_const_type_of(tcx, def_id),
Node::AnonConst(_) => anon_const_type_of(&icx, def_id),
Node::ConstBlock(_) => {
let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id());

View file

@ -1605,13 +1605,6 @@ pub(crate) struct OpaqueCapturesHigherRankedLifetime {
pub bad_place: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_pattern_type_non_const_range)]
pub(crate) struct NonConstRange {
#[primary_span]
pub span: Span,
}
#[derive(Subdiagnostic)]
pub(crate) enum InvalidReceiverTyHint {
#[note(hir_analysis_invalid_receiver_ty_help_weak_note)]

View file

@ -2,13 +2,6 @@ use rustc_macros::Diagnostic;
use rustc_middle::ty::Ty;
use rustc_span::Span;
#[derive(Diagnostic)]
#[diag(hir_analysis_pattern_type_wild_pat)]
pub(crate) struct WildPatTy {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_invalid_base_type)]
pub(crate) struct InvalidBaseType<'tcx> {

View file

@ -53,7 +53,7 @@ use tracing::{debug, instrument};
use crate::bounds::Bounds;
use crate::check::check_abi_fn_ptr;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, WildPatTy};
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
@ -2435,11 +2435,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let ty_span = ty.span;
let ty = self.lower_ty(ty);
let pat_ty = match pat.kind {
hir::PatKind::Wild => {
let err = self.dcx().emit_err(WildPatTy { span: pat.span });
Ty::new_error(tcx, err)
}
hir::PatKind::Range(start, end, include_end) => {
hir::TyPatKind::Range(start, end, include_end) => {
let ty = match ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Char => ty,
_ => Ty::new_error(
@ -2452,54 +2448,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}),
),
};
let expr_to_const = |expr: &'tcx hir::PatExpr<'tcx>| -> ty::Const<'tcx> {
let (c, c_ty) = match expr.kind {
hir::PatExprKind::Lit { lit, negated } => {
let lit_input =
LitToConstInput { lit: &lit.node, ty, neg: negated };
let ct = tcx.lit_to_const(lit_input);
(ct, ty)
}
hir::PatExprKind::Path(hir::QPath::Resolved(
_,
path @ &hir::Path {
res: Res::Def(DefKind::ConstParam, def_id),
..
},
)) => {
match self.prohibit_generic_args(
path.segments.iter(),
GenericsArgsErrExtend::Param(def_id),
) {
Ok(()) => {
let ty = tcx
.type_of(def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
let ct = self.lower_const_param(def_id, expr.hir_id);
(ct, ty)
}
Err(guar) => (
ty::Const::new_error(tcx, guar),
Ty::new_error(tcx, guar),
),
}
}
_ => {
let err = tcx
.dcx()
.emit_err(crate::errors::NonConstRange { span: expr.span });
(ty::Const::new_error(tcx, err), Ty::new_error(tcx, err))
}
};
self.record_ty(expr.hir_id, c_ty, expr.span);
c
};
let start = start.map(expr_to_const);
let end = end.map(expr_to_const);
let start = start.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let end = end.map(|expr| self.lower_const_arg(expr, FeedConstTy::No));
let include_end = match include_end {
hir::RangeEnd::Included => true,
@ -2509,12 +2459,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let pat = tcx.mk_pat(ty::PatternKind::Range { start, end, include_end });
Ty::new_pat(tcx, ty, pat)
}
hir::PatKind::Err(e) => Ty::new_error(tcx, e),
_ => Ty::new_error_with_message(
tcx,
pat.span,
format!("unsupported pattern for pattern type: {pat:#?}"),
),
hir::TyPatKind::Err(e) => Ty::new_error(tcx, e),
};
self.record_ty(pat.hir_id, ty, pat.span);
pat_ty

View file

@ -18,7 +18,7 @@ use rustc_ast_pretty::pprust::state::MacHeader;
use rustc_ast_pretty::pprust::{Comments, PrintState};
use rustc_hir::{
BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TyPatKind,
};
use rustc_span::source_map::SourceMap;
use rustc_span::{FileName, Ident, Span, Symbol, kw};
@ -35,6 +35,7 @@ pub enum AnnNode<'a> {
SubItem(HirId),
Expr(&'a hir::Expr<'a>),
Pat(&'a hir::Pat<'a>),
TyPat(&'a hir::TyPat<'a>),
Arm(&'a hir::Arm<'a>),
}
@ -198,6 +199,7 @@ impl<'a> State<'a> {
Node::TraitRef(a) => self.print_trait_ref(a),
Node::OpaqueTy(o) => self.print_opaque_ty(o),
Node::Pat(a) => self.print_pat(a),
Node::TyPat(a) => self.print_ty_pat(a),
Node::PatField(a) => self.print_patfield(a),
Node::PatExpr(a) => self.print_pat_expr(a),
Node::Arm(a) => self.print_arm(a),
@ -224,6 +226,16 @@ impl<'a> State<'a> {
Node::Err(_) => self.word("/*ERROR*/"),
}
}
fn print_generic_arg(&mut self, generic_arg: &GenericArg<'_>, elide_lifetimes: bool) {
match generic_arg {
GenericArg::Lifetime(lt) if !elide_lifetimes => self.print_lifetime(lt),
GenericArg::Lifetime(_) => {}
GenericArg::Type(ty) => self.print_type(ty.as_unambig_ty()),
GenericArg::Const(ct) => self.print_const_arg(ct.as_unambig_ct()),
GenericArg::Infer(_inf) => self.word("_"),
}
}
}
impl std::ops::Deref for State<'_> {
@ -448,7 +460,7 @@ impl<'a> State<'a> {
hir::TyKind::Pat(ty, pat) => {
self.print_type(ty);
self.word(" is ");
self.print_pat(pat);
self.print_ty_pat(pat);
}
}
self.end()
@ -1797,13 +1809,7 @@ impl<'a> State<'a> {
if nonelided_generic_args {
start_or_comma(self);
self.commasep(Inconsistent, generic_args.args, |s, generic_arg| {
match generic_arg {
GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt),
GenericArg::Lifetime(_) => {}
GenericArg::Type(ty) => s.print_type(ty.as_unambig_ty()),
GenericArg::Const(ct) => s.print_const_arg(ct.as_unambig_ct()),
GenericArg::Infer(_inf) => s.word("_"),
}
s.print_generic_arg(generic_arg, elide_lifetimes)
});
}
@ -1864,6 +1870,33 @@ impl<'a> State<'a> {
}
}
fn print_ty_pat(&mut self, pat: &hir::TyPat<'_>) {
self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::TyPat(pat));
// Pat isn't normalized, but the beauty of it
// is that it doesn't matter
match pat.kind {
TyPatKind::Range(begin, end, end_kind) => {
if let Some(expr) = begin {
self.print_const_arg(expr);
}
match end_kind {
RangeEnd::Included => self.word("..."),
RangeEnd::Excluded => self.word(".."),
}
if let Some(expr) = end {
self.print_const_arg(expr);
}
}
TyPatKind::Err(_) => {
self.popen();
self.word("/*ERROR*/");
self.pclose();
}
}
self.ann.post(self, AnnNode::TyPat(pat))
}
fn print_pat(&mut self, pat: &hir::Pat<'_>) {
self.maybe_print_comment(pat.span.lo());
self.ann.pre(self, AnnNode::Pat(pat));

View file

@ -117,22 +117,17 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
let return_or_body_span = match decl.output {
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};
fcx.require_type_is_sized(
declared_ret_ty,
return_or_body_span,
ObligationCauseCode::SizedReturnType,
);
// We checked the root's signature during wfcheck, but not the child.
// We checked the root's ret ty during wfcheck, but not the child.
if fcx.tcx.is_typeck_child(fn_def_id.to_def_id()) {
let return_or_body_span = match decl.output {
hir::FnRetTy::DefaultReturn(_) => body.value.span,
hir::FnRetTy::Return(ty) => ty.span,
};
fcx.require_type_is_sized(
declared_ret_ty,
return_or_body_span,
ObligationCauseCode::WellFormed(None),
ObligationCauseCode::SizedReturnType,
);
}

View file

@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => true,
hir::Node::Pat(_) => {
hir::Node::TyPat(_) | hir::Node::Pat(_) => {
self.dcx().span_delayed_bug(expr.span, "place expr not allowed in pattern");
true
}

View file

@ -13,7 +13,7 @@ use rustc_hir::{ExprKind, HirId, Node, QPath};
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
use rustc_hir_analysis::check::potentially_plural_count;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_index::IndexVec;
use rustc_index::{Idx, IndexVec};
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TypeTrace};
use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::error::TypeError;
@ -21,7 +21,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::Session;
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt, SelectionContext};
@ -126,7 +126,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(guar) => Err(guar),
};
if let Err(guar) = has_error {
let err_inputs = self.err_args(args_no_rcvr.len(), guar);
let err_inputs = self.err_args(
method.map_or(args_no_rcvr.len(), |method| method.sig.inputs().len() - 1),
guar,
);
let err_output = Ty::new_error(self.tcx, guar);
let err_inputs = match tuple_arguments {
@ -2374,11 +2377,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let check_for_matched_generics = || {
if matched_inputs.iter().any(|x| x.is_some())
&& params_with_generics.iter().any(|x| x.1.is_some())
&& params_with_generics.iter().any(|(x, _)| x.is_some())
{
for &(idx, generic, _) in &params_with_generics {
for (idx, (generic, _)) in params_with_generics.iter_enumerated() {
// Param has to have a generic and be matched to be relevant
if matched_inputs[idx.into()].is_none() {
if matched_inputs[idx].is_none() {
continue;
}
@ -2386,10 +2389,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
};
for unmatching_idx in idx + 1..params_with_generics.len() {
if matched_inputs[unmatching_idx.into()].is_none()
for unmatching_idx in
idx.plus(1)..ExpectedIdx::from_usize(params_with_generics.len())
{
if matched_inputs[unmatching_idx].is_none()
&& let Some(unmatched_idx_param_generic) =
params_with_generics[unmatching_idx].1
params_with_generics[unmatching_idx].0
&& unmatched_idx_param_generic.name.ident()
== generic.name.ident()
{
@ -2404,61 +2409,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let check_for_matched_generics = check_for_matched_generics();
for &(idx, generic_param, param) in
params_with_generics.iter().filter(|&(idx, _, _)| {
for (idx, &(generic_param, param)) in
params_with_generics.iter_enumerated().filter(|&(idx, _)| {
check_for_matched_generics
|| expected_idx.is_none_or(|expected_idx| expected_idx == *idx)
|| expected_idx
.is_none_or(|expected_idx| expected_idx == idx.as_usize())
})
{
let Some(generic_param) = generic_param else {
spans.push_span_label(param.span, "");
spans.push_span_label(param.span(), "");
continue;
};
let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics
.iter()
.filter(|(other_idx, other_generic_param, _)| {
if *other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[idx.into()].is_none()
&& matched_inputs[(*other_idx).into()].is_none()
{
return false;
}
if matched_inputs[idx.into()].is_some()
&& matched_inputs[(*other_idx).into()].is_some()
{
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
.map(|&(other_idx, _, other_param)| (other_idx, other_param))
.collect();
let other_params_matched: Vec<(ExpectedIdx, FnParam<'_>)> =
params_with_generics
.iter_enumerated()
.filter(|&(other_idx, &(other_generic_param, _))| {
if other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[idx].is_none()
&& matched_inputs[other_idx].is_none()
{
return false;
}
if matched_inputs[idx].is_some()
&& matched_inputs[other_idx].is_some()
{
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
.map(|(other_idx, &(_, other_param))| (other_idx, other_param))
.collect();
if !other_params_matched.is_empty() {
let other_param_matched_names: Vec<String> = other_params_matched
.iter()
.map(|(idx, other_param)| {
if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind
{
format!("`{ident}`")
if let Some(name) = other_param.name() {
format!("`{name}`")
} else {
format!("parameter #{}", idx + 1)
format!("parameter #{}", idx.as_u32() + 1)
}
})
.collect();
let matched_ty = self
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
.resolve_vars_if_possible(formal_and_expected_inputs[idx].1)
.sort_string(self.tcx);
if matched_inputs[idx.into()].is_some() {
if matched_inputs[idx].is_some() {
spans.push_span_label(
param.span,
param.span(),
format!(
"{} need{} to match the {} type of this parameter",
listify(&other_param_matched_names, |n| n.to_string())
@ -2473,7 +2479,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
} else {
spans.push_span_label(
param.span,
param.span(),
format!(
"this parameter needs to match the {} type of {}",
matched_ty,
@ -2484,7 +2490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
generics_with_unmatched_params.push(generic_param);
} else {
spans.push_span_label(param.span, "");
spans.push_span_label(param.span(), "");
}
}
@ -2502,19 +2508,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
{
let param_idents_matching: Vec<String> = params_with_generics
.iter()
.filter(|(_, generic, _)| {
.iter_enumerated()
.filter(|&(_, &(generic, _))| {
if let Some(generic) = generic {
generic.name.ident() == generic_param.name.ident()
} else {
false
}
})
.map(|(idx, _, param)| {
if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
format!("`{ident}`")
.map(|(idx, &(_, param))| {
if let Some(name) = param.name() {
format!("`{name}`")
} else {
format!("parameter #{}", idx + 1)
format!("parameter #{}", idx.as_u32() + 1)
}
})
.collect();
@ -2607,12 +2613,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(params_with_generics) = self.get_hir_params_with_generics(def_id, is_method) {
debug_assert_eq!(params_with_generics.len(), matched_inputs.len());
for &(idx, generic_param, _) in &params_with_generics {
if matched_inputs[idx.into()].is_none() {
for (idx, (generic_param, _)) in params_with_generics.iter_enumerated() {
if matched_inputs[idx].is_none() {
continue;
}
let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else {
let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.to_provided_idx())
else {
continue;
};
@ -2620,32 +2627,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
};
let mut idxs_matched: Vec<usize> = vec![];
for &(other_idx, _, _) in
params_with_generics.iter().filter(|&&(other_idx, other_generic_param, _)| {
let idxs_matched = params_with_generics
.iter_enumerated()
.filter(|&(other_idx, (other_generic_param, _))| {
if other_idx == idx {
return false;
}
let Some(other_generic_param) = other_generic_param else {
return false;
};
if matched_inputs[other_idx.into()].is_some() {
if matched_inputs[other_idx].is_some() {
return false;
}
other_generic_param.name.ident() == generic_param.name.ident()
})
{
idxs_matched.push(other_idx);
}
.count();
if idxs_matched.is_empty() {
if idxs_matched == 0 {
continue;
}
let expected_display_type = self
.resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
.sort_string(self.tcx);
let label = if idxs_matched.len() == params_with_generics.len() - 1 {
let label = if idxs_matched == params_with_generics.len() - 1 {
format!(
"expected all arguments to be this {} type because they need to match the type of this parameter",
expected_display_type
@ -2664,62 +2669,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// Returns the parameters of a function, with their generic parameters if those are the full
/// type of that parameter. Returns `None` if the function body is unavailable (eg is an instrinsic).
/// type of that parameter. Returns `None` if the function has no generics or the body is
/// unavailable (eg is an instrinsic).
fn get_hir_params_with_generics(
&self,
def_id: DefId,
is_method: bool,
) -> Option<Vec<(usize, Option<&hir::GenericParam<'_>>, &hir::Param<'_>)>> {
let fn_node = self.tcx.hir().get_if_local(def_id)?;
let fn_decl = fn_node.fn_decl()?;
let generic_params: Vec<Option<&hir::GenericParam<'_>>> = fn_decl
.inputs
.into_iter()
.skip(if is_method { 1 } else { 0 })
.map(|param| {
if let hir::TyKind::Path(QPath::Resolved(
_,
hir::Path { res: Res::Def(_, res_def_id), .. },
)) = param.kind
{
fn_node
.generics()
.into_iter()
.flat_map(|generics| generics.params)
.find(|param| &param.def_id.to_def_id() == res_def_id)
} else {
None
}
) -> Option<IndexVec<ExpectedIdx, (Option<&hir::GenericParam<'_>>, FnParam<'_>)>> {
let (sig, generics, body_id, param_names) = match self.tcx.hir().get_if_local(def_id)? {
hir::Node::TraitItem(&hir::TraitItem {
generics,
kind: hir::TraitItemKind::Fn(sig, trait_fn),
..
}) => match trait_fn {
hir::TraitFn::Required(params) => (sig, generics, None, Some(params)),
hir::TraitFn::Provided(body) => (sig, generics, Some(body), None),
},
hir::Node::ImplItem(&hir::ImplItem {
generics,
kind: hir::ImplItemKind::Fn(sig, body),
..
})
.collect();
| hir::Node::Item(&hir::Item {
kind: hir::ItemKind::Fn { sig, generics, body, .. },
..
}) => (sig, generics, Some(body), None),
_ => return None,
};
let mut params: Vec<&hir::Param<'_>> = self
.tcx
.hir()
.body(fn_node.body_id()?)
.params
.into_iter()
.skip(if is_method { 1 } else { 0 })
.collect();
// The surrounding code expects variadic functions to not have a parameter representing
// the "..." parameter. This is already true of the FnDecl but not of the body params, so
// we drop it if it exists.
if fn_decl.c_variadic {
params.pop();
// Make sure to remove both the receiver and variadic argument. Both are removed
// when matching parameter types.
let fn_inputs = sig.decl.inputs.get(is_method as usize..)?.iter().map(|param| {
if let hir::TyKind::Path(QPath::Resolved(
_,
&hir::Path { res: Res::Def(_, res_def_id), .. },
)) = param.kind
{
generics.params.iter().find(|param| param.def_id.to_def_id() == res_def_id)
} else {
None
}
});
match (body_id, param_names) {
(Some(_), Some(_)) | (None, None) => unreachable!(),
(Some(body), None) => {
let params = self.tcx.hir().body(body).params;
let params =
params.get(is_method as usize..params.len() - sig.decl.c_variadic as usize)?;
debug_assert_eq!(params.len(), fn_inputs.len());
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Param(param))).collect())
}
(None, Some(params)) => {
let params = params.get(is_method as usize..)?;
debug_assert_eq!(params.len(), fn_inputs.len());
Some(fn_inputs.zip(params.iter().map(|param| FnParam::Name(param))).collect())
}
}
debug_assert_eq!(params.len(), generic_params.len());
Some(
generic_params
.into_iter()
.zip(params)
.enumerate()
.map(|(a, (b, c))| (a, b, c))
.collect(),
)
}
}
@ -2742,3 +2747,27 @@ impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
hir::intravisit::walk_expr(self, ex);
}
}
#[derive(Clone, Copy)]
enum FnParam<'hir> {
Param(&'hir hir::Param<'hir>),
Name(&'hir Ident),
}
impl FnParam<'_> {
fn span(&self) -> Span {
match self {
Self::Param(x) => x.span,
Self::Name(x) => x.span,
}
}
fn name(&self) -> Option<Symbol> {
match self {
Self::Param(x) if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind => {
Some(ident.name)
}
Self::Name(x) if x.name != kw::Empty => Some(x.name),
_ => None,
}
}
}

View file

@ -186,8 +186,6 @@ fn typeck_with_inspect<'tcx>(
let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id)));
fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code);
fcx.require_type_is_sized(expected_type, body.value.span, ObligationCauseCode::ConstSized);
// Gather locals in statics (because of block expressions).
GatherLocalsVisitor::new(&fcx).visit_body(body);

View file

@ -2395,6 +2395,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let lang_item = match parent_expr.kind {
ExprKind::Struct(qpath, _, _) => match *qpath {
QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
QPath::LangItem(LangItem::RangeCopy, ..) => Some(LangItem::RangeCopy),
QPath::LangItem(LangItem::RangeInclusiveCopy, ..) => {
Some(LangItem::RangeInclusiveCopy)
}
QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
QPath::LangItem(LangItem::RangeToInclusive, ..) => {
Some(LangItem::RangeToInclusive)

View file

@ -4,7 +4,7 @@
//! overview of how lints are implemented.
use std::cell::Cell;
use std::{iter, slice};
use std::slice;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync;
@ -718,30 +718,6 @@ impl<'tcx> LateContext<'tcx> {
}
}
/// Check if a `DefId`'s path matches the given absolute type path usage.
///
/// Anonymous scopes such as `extern` imports are matched with `kw::Empty`;
/// inherent `impl` blocks are matched with the name of the type.
///
/// Instead of using this method, it is often preferable to instead use
/// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors
/// as paths get invalidated if the target definition moves.
///
/// # Examples
///
/// ```rust,ignore (no context or def id available)
/// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
/// // The given `def_id` is that of an `Option` type
/// }
/// ```
///
/// Used by clippy, but should be replaced by diagnostic items eventually.
pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
let names = self.get_def_path(def_id);
names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b)
}
/// Gets the absolute path of `def_id` as a vector of `Symbol`.
///
/// # Examples

View file

@ -1,61 +1,3 @@
//! Nodes in the dependency graph.
//!
//! A node in the [dependency graph] is represented by a [`DepNode`].
//! A `DepNode` consists of a [`DepKind`] (which
//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.)
//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which
//! depends on the node's `DepKind`. Together, the kind and the fingerprint
//! fully identify a dependency node, even across multiple compilation sessions.
//! In other words, the value of the fingerprint does not depend on anything
//! that is specific to a given compilation session, like an unpredictable
//! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a
//! pointer. The concept behind this could be compared to how git commit hashes
//! uniquely identify a given commit. The fingerprinting approach has
//! a few advantages:
//!
//! * A `DepNode` can simply be serialized to disk and loaded in another session
//! without the need to do any "rebasing" (like we have to do for Spans and
//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier
//! implementations of the dependency graph).
//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to
//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc.
//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into
//! memory without any post-processing (e.g., "abomination-style" pointer
//! reconstruction).
//! * Because a `DepNode` is self-contained, we can instantiate `DepNodes` that
//! refer to things that do not exist anymore. In previous implementations
//! `DepNode` contained a `DefId`. A `DepNode` referring to something that
//! had been removed between the previous and the current compilation session
//! could not be instantiated because the current compilation session
//! contained no `DefId` for thing that had been removed.
//!
//! `DepNode` definition happens in the `define_dep_nodes!()` macro. This macro
//! defines the `DepKind` enum. Each `DepKind` has its own parameters that are
//! needed at runtime in order to construct a valid `DepNode` fingerprint.
//! However, only `CompileCodegenUnit` and `CompileMonoItem` are constructed
//! explicitly (with `make_compile_codegen_unit` cq `make_compile_mono_item`).
//!
//! Because the macro sees what parameters a given `DepKind` requires, it can
//! "infer" some properties for each kind of `DepNode`:
//!
//! * Whether a `DepNode` of a given kind has any parameters at all. Some
//! `DepNode`s could represent global concepts with only one value.
//! * Whether it is possible, in principle, to reconstruct a query key from a
//! given `DepNode`. Many `DepKind`s only require a single `DefId` parameter,
//! in which case it is possible to map the node's fingerprint back to the
//! `DefId` it was computed from. In other cases, too much information gets
//! lost during fingerprint computation.
//!
//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with
//! `DepNode::new()`, ensures that only valid `DepNode` instances can be
//! constructed. For example, the API does not allow for constructing
//! parameterless `DepNode`s with anything other than a zeroed out fingerprint.
//! More generally speaking, it relieves the user of the `DepNode` API of
//! having to know how to compute the expected fingerprint for a given set of
//! node parameters.
//!
//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId, ModDefId};
use rustc_hir::definitions::DefPathHash;
@ -158,26 +100,14 @@ pub(crate) fn make_compile_mono_item<'tcx>(
}
pub trait DepNodeExt: Sized {
/// Extracts the DefId corresponding to this DepNode. This will work
/// if two conditions are met:
///
/// 1. The Fingerprint of the DepNode actually is a DefPathHash, and
/// 2. the item that the DefPath refers to exists in the current tcx.
///
/// Condition (1) is determined by the DepKind variant of the
/// DepNode. Condition (2) might not be fulfilled if a DepNode
/// refers to something from the previous compilation session that
/// has been removed.
fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId>;
/// Used in testing
fn from_label_string(
tcx: TyCtxt<'_>,
label: &str,
def_path_hash: DefPathHash,
) -> Result<Self, ()>;
/// Used in testing
fn has_label_string(label: &str) -> bool;
}
@ -399,52 +329,46 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
}
}
macro_rules! impl_for_typed_def_id {
($Name:ident, $LocalName:ident) => {
impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for $Name {
#[inline(always)]
fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::DefPathHash
}
impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for ModDefId {
#[inline(always)]
fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::DefPathHash
}
#[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
self.to_def_id().to_fingerprint(tcx)
}
#[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
self.to_def_id().to_fingerprint(tcx)
}
#[inline(always)]
fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
self.to_def_id().to_debug_str(tcx)
}
#[inline(always)]
fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
self.to_def_id().to_debug_str(tcx)
}
#[inline(always)]
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
DefId::recover(tcx, dep_node).map($Name::new_unchecked)
}
}
impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for $LocalName {
#[inline(always)]
fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::DefPathHash
}
#[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
self.to_def_id().to_fingerprint(tcx)
}
#[inline(always)]
fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
self.to_def_id().to_debug_str(tcx)
}
#[inline(always)]
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
LocalDefId::recover(tcx, dep_node).map($LocalName::new_unchecked)
}
}
};
#[inline(always)]
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
DefId::recover(tcx, dep_node).map(ModDefId::new_unchecked)
}
}
impl_for_typed_def_id! { ModDefId, LocalModDefId }
impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for LocalModDefId {
#[inline(always)]
fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::DefPathHash
}
#[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
self.to_def_id().to_fingerprint(tcx)
}
#[inline(always)]
fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
self.to_def_id().to_debug_str(tcx)
}
#[inline(always)]
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
LocalDefId::recover(tcx, dep_node).map(LocalModDefId::new_unchecked)
}
}

View file

@ -937,6 +937,7 @@ impl<'hir> Map<'hir> {
Node::TraitRef(tr) => tr.path.span,
Node::OpaqueTy(op) => op.span,
Node::Pat(pat) => pat.span,
Node::TyPat(pat) => pat.span,
Node::PatField(field) => field.span,
Node::PatExpr(lit) => lit.span,
Node::Arm(arm) => arm.span,
@ -1212,6 +1213,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
Node::TraitRef(_) => node_str("trait ref"),
Node::OpaqueTy(_) => node_str("opaque type"),
Node::Pat(_) => node_str("pat"),
Node::TyPat(_) => node_str("pat ty"),
Node::PatField(_) => node_str("pattern field"),
Node::PatExpr(_) => node_str("pattern literal"),
Node::Param(_) => node_str("param"),

View file

@ -1,10 +1,11 @@
//! The "main crate" of the Rust compiler. This crate contains common
//! type definitions that are used by the other crates in the rustc
//! "family". Some prominent examples (note that each of these modules
//! has their own README with further details).
//! "family". The following are some prominent examples.
//!
//! - **HIR.** The "high-level (H) intermediate representation (IR)" is
//! defined in the [`hir`] module.
//! - **THIR.** The "typed high-level (H) intermediate representation (IR)"
//! is defined in the [`thir`] module.
//! - **MIR.** The "mid-level (M) intermediate representation (IR)" is
//! defined in the [`mir`] module. This module contains only the
//! *definition* of the MIR; the passes that transform and operate
@ -37,7 +38,6 @@
#![feature(box_as_ptr)]
#![feature(box_patterns)]
#![feature(closure_track_caller)]
#![feature(const_type_name)]
#![feature(core_intrinsics)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
@ -50,7 +50,6 @@
#![feature(intra_doc_pointers)]
#![feature(iter_from_coroutine)]
#![feature(let_chains)]
#![feature(macro_metavar_expr)]
#![feature(min_specialization)]
#![feature(negative_impls)]
#![feature(never_type)]

View file

@ -15,7 +15,8 @@ use std::{fmt, io};
use rustc_abi::{AddressSpace, Align, Endian, HasDataLayout, Size};
use rustc_ast::{LitKind, Mutability};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lock;
use rustc_data_structures::sharded::ShardedHashMap;
use rustc_data_structures::sync::{AtomicU64, Lock};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
@ -389,35 +390,39 @@ pub const CTFE_ALLOC_SALT: usize = 0;
pub(crate) struct AllocMap<'tcx> {
/// Maps `AllocId`s to their corresponding allocations.
alloc_map: FxHashMap<AllocId, GlobalAlloc<'tcx>>,
// Note that this map on rustc workloads seems to be rather dense, but in miri workloads should
// be pretty sparse. In #136105 we considered replacing it with a (dense) Vec-based map, but
// since there are workloads where it can be sparse we decided to go with sharding for now. At
// least up to 32 cores the one workload tested didn't exhibit much difference between the two.
//
// Should be locked *after* locking dedup if locking both to avoid deadlocks.
to_alloc: ShardedHashMap<AllocId, GlobalAlloc<'tcx>>,
/// Used to deduplicate global allocations: functions, vtables, string literals, ...
///
/// The `usize` is a "salt" used by Miri to make deduplication imperfect, thus better emulating
/// the actual guarantees.
dedup: FxHashMap<(GlobalAlloc<'tcx>, usize), AllocId>,
dedup: Lock<FxHashMap<(GlobalAlloc<'tcx>, usize), AllocId>>,
/// The `AllocId` to assign to the next requested ID.
/// Always incremented; never gets smaller.
next_id: AllocId,
next_id: AtomicU64,
}
impl<'tcx> AllocMap<'tcx> {
pub(crate) fn new() -> Self {
AllocMap {
alloc_map: Default::default(),
to_alloc: Default::default(),
dedup: Default::default(),
next_id: AllocId(NonZero::new(1).unwrap()),
next_id: AtomicU64::new(1),
}
}
fn reserve(&mut self) -> AllocId {
let next = self.next_id;
self.next_id.0 = self.next_id.0.checked_add(1).expect(
"You overflowed a u64 by incrementing by 1... \
You've just earned yourself a free drink if we ever meet. \
Seriously, how did you do that?!",
);
next
fn reserve(&self) -> AllocId {
// Technically there is a window here where we overflow and then another thread
// increments `next_id` *again* and uses it before we panic and tear down the entire session.
// We consider this fine since such overflows cannot realistically occur.
let next_id = self.next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
AllocId(NonZero::new(next_id).unwrap())
}
}
@ -428,26 +433,34 @@ impl<'tcx> TyCtxt<'tcx> {
/// Make sure to call `set_alloc_id_memory` or `set_alloc_id_same_memory` before returning such
/// an `AllocId` from a query.
pub fn reserve_alloc_id(self) -> AllocId {
self.alloc_map.lock().reserve()
self.alloc_map.reserve()
}
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
/// Should not be used for mutable memory.
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>, salt: usize) -> AllocId {
let mut alloc_map = self.alloc_map.lock();
if let GlobalAlloc::Memory(mem) = alloc {
if mem.inner().mutability.is_mut() {
bug!("trying to dedup-reserve mutable memory");
}
}
let alloc_salt = (alloc, salt);
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc_salt) {
// Locking this *before* `to_alloc` also to ensure correct lock order.
let mut dedup = self.alloc_map.dedup.lock();
if let Some(&alloc_id) = dedup.get(&alloc_salt) {
return alloc_id;
}
let id = alloc_map.reserve();
let id = self.alloc_map.reserve();
debug!("creating alloc {:?} with id {id:?}", alloc_salt.0);
alloc_map.alloc_map.insert(id, alloc_salt.0.clone());
alloc_map.dedup.insert(alloc_salt, id);
let had_previous = self
.alloc_map
.to_alloc
.lock_shard_by_value(&id)
.insert(id, alloc_salt.0.clone())
.is_some();
// We just reserved, so should always be unique.
assert!(!had_previous);
dedup.insert(alloc_salt, id);
id
}
@ -497,7 +510,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// local dangling pointers and allocations in constants/statics.
#[inline]
pub fn try_get_global_alloc(self, id: AllocId) -> Option<GlobalAlloc<'tcx>> {
self.alloc_map.lock().alloc_map.get(&id).cloned()
self.alloc_map.to_alloc.lock_shard_by_value(&id).get(&id).cloned()
}
#[inline]
@ -516,7 +529,9 @@ impl<'tcx> TyCtxt<'tcx> {
/// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
/// call this function twice, even with the same `Allocation` will ICE the compiler.
pub fn set_alloc_id_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) {
if let Some(old) = self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Memory(mem)) {
if let Some(old) =
self.alloc_map.to_alloc.lock_shard_by_value(&id).insert(id, GlobalAlloc::Memory(mem))
{
bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
}
}
@ -524,8 +539,11 @@ impl<'tcx> TyCtxt<'tcx> {
/// Freezes an `AllocId` created with `reserve` by pointing it at a static item. Trying to
/// call this function twice, even with the same `DefId` will ICE the compiler.
pub fn set_nested_alloc_id_static(self, id: AllocId, def_id: LocalDefId) {
if let Some(old) =
self.alloc_map.lock().alloc_map.insert(id, GlobalAlloc::Static(def_id.to_def_id()))
if let Some(old) = self
.alloc_map
.to_alloc
.lock_shard_by_value(&id)
.insert(id, GlobalAlloc::Static(def_id.to_def_id()))
{
bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}");
}

View file

@ -37,9 +37,6 @@ pub mod visit;
macro_rules! thir_with_elements {
(
$($field_name:ident: $field_ty:ty,)*
@elements:
$($name:ident: $id:ty => $value:ty => $format:literal,)*
) => {
$(
@ -55,20 +52,16 @@ macro_rules! thir_with_elements {
/// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
#[derive(Debug, HashStable)]
pub struct Thir<'tcx> {
$(
pub $field_name: $field_ty,
)*
pub body_type: BodyTy<'tcx>,
$(
pub $name: IndexVec<$id, $value>,
)*
}
impl<'tcx> Thir<'tcx> {
pub fn new($($field_name: $field_ty,)*) -> Thir<'tcx> {
pub fn new(body_type: BodyTy<'tcx>) -> Thir<'tcx> {
Thir {
$(
$field_name,
)*
body_type,
$(
$name: IndexVec::new(),
)*
@ -88,9 +81,6 @@ macro_rules! thir_with_elements {
}
thir_with_elements! {
body_type: BodyTy<'tcx>,
@elements:
arms: ArmId => Arm<'tcx> => "a{}",
blocks: BlockId => Block => "b{}",
exprs: ExprId => Expr<'tcx> => "e{}",

View file

@ -1,8 +1,7 @@
use super::{
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
PatKind, Stmt, StmtKind, Thir,
AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand,
Pat, PatKind, Stmt, StmtKind, Thir,
};
use crate::thir::AdtExprBase;
pub trait Visitor<'thir, 'tcx: 'thir>: Sized {
fn thir(&self) -> &'thir Thir<'tcx>;

View file

@ -21,13 +21,12 @@ use rustc_macros::{
};
use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_span::{DUMMY_SP, Span, Symbol};
// FIXME: Remove this import and import via `solve::`
pub use rustc_type_ir::solve::BuiltinImplSource;
use smallvec::{SmallVec, smallvec};
use thin_vec::ThinVec;
pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
use crate::mir::ConstraintCategory;
pub use crate::traits::solve::BuiltinImplSource;
use crate::ty::abstract_const::NotConstEvaluatable;
use crate::ty::{self, AdtKind, GenericArgsRef, Ty};

View file

@ -7,11 +7,10 @@
use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
use rustc_span::Span;
// FIXME: Remove this import and import via `traits::solve`.
pub use rustc_type_ir::solve::NoSolution;
use crate::error::DropCheckOverflow;
use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse};
pub use crate::traits::solve::NoSolution;
use crate::ty::{self, GenericArg, Ty, TyCtxt};
pub mod type_op {

View file

@ -1366,7 +1366,7 @@ pub struct GlobalCtxt<'tcx> {
pub data_layout: TargetDataLayout,
/// Stores memory for globals (statics/consts).
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
pub(crate) alloc_map: interpret::AllocMap<'tcx>,
current_gcx: CurrentGcx,
}
@ -1583,7 +1583,7 @@ impl<'tcx> TyCtxt<'tcx> {
new_solver_evaluation_cache: Default::default(),
canonical_param_env_cache: Default::default(),
data_layout,
alloc_map: Lock::new(interpret::AllocMap::new()),
alloc_map: interpret::AllocMap::new(),
current_gcx,
});

View file

@ -6,9 +6,9 @@ use rustc_middle::ty;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
use tracing::debug;
use crate::thir::cx::Cx;
use crate::thir::cx::ThirBuildCx;
impl<'tcx> Cx<'tcx> {
impl<'tcx> ThirBuildCx<'tcx> {
pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId {
// We have to eagerly lower the "spine" of the statements
// in order to get the lexical scoping correctly.

View file

@ -21,10 +21,9 @@ use rustc_middle::{bug, span_bug};
use rustc_span::{Span, sym};
use tracing::{debug, info, instrument, trace};
use crate::thir::cx::Cx;
use crate::thir::util::UserAnnotatedTyHelpers;
use crate::thir::cx::ThirBuildCx;
impl<'tcx> Cx<'tcx> {
impl<'tcx> ThirBuildCx<'tcx> {
/// Create a THIR expression for the given HIR expression. This expands all
/// adjustments and directly adds the type information from the
/// `typeck_results`. See the [dev-guide] for more details.
@ -142,9 +141,9 @@ impl<'tcx> Cx<'tcx> {
Adjust::Deref(Some(deref)) => {
// We don't need to do call adjust_span here since
// deref coercions always start with a built-in deref.
let call_def_id = deref.method_call(self.tcx());
let call_def_id = deref.method_call(self.tcx);
let overloaded_callee =
Ty::new_fn_def(self.tcx(), call_def_id, self.tcx().mk_args(&[expr.ty.into()]));
Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()]));
expr = Expr {
temp_lifetime,
@ -253,10 +252,10 @@ impl<'tcx> Cx<'tcx> {
// Check to see if this cast is a "coercion cast", where the cast is actually done
// using a coercion (or is a no-op).
if self.typeck_results().is_coercion_cast(source.hir_id) {
if self.typeck_results.is_coercion_cast(source.hir_id) {
// Convert the lexpr to a vexpr.
ExprKind::Use { source: self.mirror_expr(source) }
} else if self.typeck_results().expr_ty(source).is_ref() {
} else if self.typeck_results.expr_ty(source).is_ref() {
// Special cased so that we can type check that the element
// type of the source matches the pointed to type of the
// destination.
@ -266,8 +265,8 @@ impl<'tcx> Cx<'tcx> {
is_from_as_cast: true,
}
} else if let hir::ExprKind::Path(ref qpath) = source.kind
&& let res = self.typeck_results().qpath_res(qpath, source.hir_id)
&& let ty = self.typeck_results().node_type(source.hir_id)
&& let res = self.typeck_results.qpath_res(qpath, source.hir_id)
&& let ty = self.typeck_results.node_type(source.hir_id)
&& let ty::Adt(adt_def, args) = ty.kind()
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res
{
@ -330,7 +329,7 @@ impl<'tcx> Cx<'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
let tcx = self.tcx;
let expr_ty = self.typeck_results().expr_ty(expr);
let expr_ty = self.typeck_results.expr_ty(expr);
let (temp_lifetime, backwards_incompatible) =
self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id);
@ -354,7 +353,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Call(fun, ref args) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
// The callee is something implementing Fn, FnMut, or FnOnce.
// Find the actual method implementation being called and
// build the appropriate UFCS call expression with the
@ -364,7 +363,7 @@ impl<'tcx> Cx<'tcx> {
let method = self.method_callee(expr, fun.span, None);
let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e));
let tupled_args = Expr {
ty: Ty::new_tup_from_iter(tcx, arg_tys),
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
@ -380,7 +379,7 @@ impl<'tcx> Cx<'tcx> {
from_hir_call: true,
fn_span: expr.span,
}
} else if let ty::FnDef(def_id, _) = self.typeck_results().expr_ty(fun).kind()
} else if let ty::FnDef(def_id, _) = self.typeck_results.expr_ty(fun).kind()
&& let Some(intrinsic) = self.tcx.intrinsic(def_id)
&& intrinsic.name == sym::box_new
{
@ -413,7 +412,7 @@ impl<'tcx> Cx<'tcx> {
},
hir::QPath::TypeRelative(_ty, _) => {
if let Some((DefKind::Ctor(_, CtorKind::Fn), ctor_id)) =
self.typeck_results().type_dependent_def(fun.hir_id)
self.typeck_results.type_dependent_def(fun.hir_id)
{
Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
} else {
@ -426,8 +425,8 @@ impl<'tcx> Cx<'tcx> {
None
};
if let Some((adt_def, index)) = adt_data {
let node_args = self.typeck_results().node_args(fun.hir_id);
let user_provided_types = self.typeck_results().user_provided_types();
let node_args = self.typeck_results.node_args(fun.hir_id);
let user_provided_types = self.typeck_results.user_provided_types();
let user_ty =
user_provided_types.get(fun.hir_id).copied().map(|mut u_ty| {
if let ty::UserTypeKind::TypeOf(ref mut did, _) =
@ -457,7 +456,7 @@ impl<'tcx> Cx<'tcx> {
}))
} else {
ExprKind::Call {
ty: self.typeck_results().node_type(fun.hir_id),
ty: self.typeck_results.node_type(fun.hir_id),
fun: self.mirror_expr(fun),
args: self.mirror_exprs(args),
from_hir_call: true,
@ -482,7 +481,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::AssignOp(op, lhs, rhs) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let lhs = self.mirror_expr(lhs);
let rhs = self.mirror_expr(rhs);
self.overloaded_operator(expr, Box::new([lhs, rhs]))
@ -498,7 +497,7 @@ impl<'tcx> Cx<'tcx> {
hir::ExprKind::Lit(lit) => ExprKind::Literal { lit, neg: false },
hir::ExprKind::Binary(op, lhs, rhs) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let lhs = self.mirror_expr(lhs);
let rhs = self.mirror_expr(rhs);
self.overloaded_operator(expr, Box::new([lhs, rhs]))
@ -527,7 +526,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Index(lhs, index, brackets_span) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let lhs = self.mirror_expr(lhs);
let index = self.mirror_expr(index);
self.overloaded_place(
@ -543,7 +542,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Unary(hir::UnOp::Deref, arg) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let arg = self.mirror_expr(arg);
self.overloaded_place(expr, expr_ty, None, Box::new([arg]), expr.span)
} else {
@ -552,7 +551,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Unary(hir::UnOp::Not, arg) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let arg = self.mirror_expr(arg);
self.overloaded_operator(expr, Box::new([arg]))
} else {
@ -561,7 +560,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
if self.typeck_results().is_method_call(expr) {
if self.typeck_results.is_method_call(expr) {
let arg = self.mirror_expr(arg);
self.overloaded_operator(expr, Box::new([arg]))
} else if let hir::ExprKind::Lit(lit) = arg.kind {
@ -574,7 +573,7 @@ impl<'tcx> Cx<'tcx> {
hir::ExprKind::Struct(qpath, fields, ref base) => match expr_ty.kind() {
ty::Adt(adt, args) => match adt.adt_kind() {
AdtKind::Struct | AdtKind::Union => {
let user_provided_types = self.typeck_results().user_provided_types();
let user_provided_types = self.typeck_results.user_provided_types();
let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
ExprKind::Adt(Box::new(AdtExpr {
@ -586,15 +585,14 @@ impl<'tcx> Cx<'tcx> {
base: match base {
hir::StructTailExpr::Base(base) => AdtExprBase::Base(FruInfo {
base: self.mirror_expr(base),
field_types: self.typeck_results().fru_field_types()
[expr.hir_id]
field_types: self.typeck_results.fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
}),
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()[expr.hir_id]
self.typeck_results.fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
@ -605,7 +603,7 @@ impl<'tcx> Cx<'tcx> {
}))
}
AdtKind::Enum => {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let res = self.typeck_results.qpath_res(qpath, expr.hir_id);
match res {
Res::Def(DefKind::Variant, variant_id) => {
assert!(matches!(
@ -615,8 +613,7 @@ impl<'tcx> Cx<'tcx> {
));
let index = adt.variant_index_with_id(variant_id);
let user_provided_types =
self.typeck_results().user_provided_types();
let user_provided_types = self.typeck_results.user_provided_types();
let user_ty =
user_provided_types.get(expr.hir_id).copied().map(Box::new);
debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
@ -629,8 +626,7 @@ impl<'tcx> Cx<'tcx> {
base: match base {
hir::StructTailExpr::DefaultFields(_) => {
AdtExprBase::DefaultFields(
self.typeck_results().fru_field_types()
[expr.hir_id]
self.typeck_results.fru_field_types()[expr.hir_id]
.iter()
.copied()
.collect(),
@ -655,7 +651,7 @@ impl<'tcx> Cx<'tcx> {
},
hir::ExprKind::Closure { .. } => {
let closure_ty = self.typeck_results().expr_ty(expr);
let closure_ty = self.typeck_results.expr_ty(expr);
let (def_id, args, movability) = match *closure_ty.kind() {
ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args), None),
ty::Coroutine(def_id, args) => {
@ -703,7 +699,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::Path(ref qpath) => {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let res = self.typeck_results.qpath_res(qpath, expr.hir_id);
self.convert_path_expr(expr, res)
}
@ -772,7 +768,7 @@ impl<'tcx> Cx<'tcx> {
}
hir::ExprKind::ConstBlock(ref anon_const) => {
let ty = self.typeck_results().node_type(anon_const.hir_id);
let ty = self.typeck_results.node_type(anon_const.hir_id);
let did = anon_const.def_id.to_def_id();
let typeck_root_def_id = tcx.typeck_root_def_id(did);
let parent_args =
@ -783,7 +779,7 @@ impl<'tcx> Cx<'tcx> {
}
// Now comes the rote stuff:
hir::ExprKind::Repeat(v, _) => {
let ty = self.typeck_results().expr_ty(expr);
let ty = self.typeck_results.expr_ty(expr);
let ty::Array(_, count) = ty.kind() else {
span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty);
};
@ -837,7 +833,7 @@ impl<'tcx> Cx<'tcx> {
match_source,
},
hir::ExprKind::Loop(body, ..) => {
let block_ty = self.typeck_results().node_type(body.hir_id);
let block_ty = self.typeck_results.node_type(body.hir_id);
let (temp_lifetime, backwards_incompatible) = self
.rvalue_scopes
.temporary_scope(self.region_scope_tree, body.hir_id.local_id);
@ -957,7 +953,7 @@ impl<'tcx> Cx<'tcx> {
| Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
| Res::Def(DefKind::Const, _)
| Res::Def(DefKind::AssocConst, _) => {
self.typeck_results().user_provided_types().get(hir_id).copied().map(Box::new)
self.typeck_results.user_provided_types().get(hir_id).copied().map(Box::new)
}
// A unit struct/variant which is used as a value (e.g.,
@ -989,17 +985,13 @@ impl<'tcx> Cx<'tcx> {
Some(fn_def) => (fn_def, None),
None => {
let (kind, def_id) =
self.typeck_results().type_dependent_def(expr.hir_id).unwrap_or_else(|| {
self.typeck_results.type_dependent_def(expr.hir_id).unwrap_or_else(|| {
span_bug!(expr.span, "no type-dependent def for method callee")
});
let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(kind, def_id));
debug!("method_callee: user_ty={:?}", user_ty);
(
Ty::new_fn_def(
self.tcx(),
def_id,
self.typeck_results().node_args(expr.hir_id),
),
Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)),
user_ty,
)
}
@ -1025,7 +1017,7 @@ impl<'tcx> Cx<'tcx> {
}
fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKind<'tcx> {
let args = self.typeck_results().node_args(expr.hir_id);
let args = self.typeck_results.node_args(expr.hir_id);
match res {
// A regular function, constructor function or a constant.
Res::Def(DefKind::Fn, _)
@ -1060,7 +1052,7 @@ impl<'tcx> Cx<'tcx> {
let user_provided_types = self.typeck_results.user_provided_types();
let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
debug!("convert_path_expr: user_ty={:?}", user_ty);
let ty = self.typeck_results().node_type(expr.hir_id);
let ty = self.typeck_results.node_type(expr.hir_id);
match ty.kind() {
// A unit struct/variant which is used as a value.
// We return a completely different ExprKind here to account for this special case.

View file

@ -16,15 +16,15 @@ use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
use tracing::instrument;
use crate::thir::pattern::pat_from_hir;
use crate::thir::util::UserAnnotatedTyHelpers;
/// Query implementation for [`TyCtxt::thir_body`].
pub(crate) fn thir_body(
tcx: TyCtxt<'_>,
owner_def: LocalDefId,
) -> Result<(&Steal<Thir<'_>>, ExprId), ErrorGuaranteed> {
let hir = tcx.hir();
let body = hir.body_owned_by(owner_def);
let mut cx = Cx::new(tcx, owner_def);
let mut cx = ThirBuildCx::new(tcx, owner_def);
if let Some(reported) = cx.typeck_results.tainted_by_errors {
return Err(reported);
}
@ -52,8 +52,10 @@ pub(crate) fn thir_body(
Ok((tcx.alloc_steal_thir(cx.thir), expr))
}
struct Cx<'tcx> {
/// Context for lowering HIR to THIR for a single function body (or other kind of body).
struct ThirBuildCx<'tcx> {
tcx: TyCtxt<'tcx>,
/// The THIR data that this context is building.
thir: Thir<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
@ -69,8 +71,8 @@ struct Cx<'tcx> {
body_owner: DefId,
}
impl<'tcx> Cx<'tcx> {
fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Cx<'tcx> {
impl<'tcx> ThirBuildCx<'tcx> {
fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self {
let typeck_results = tcx.typeck(def);
let hir = tcx.hir();
let hir_id = tcx.local_def_id_to_hir_id(def);
@ -94,7 +96,7 @@ impl<'tcx> Cx<'tcx> {
BodyTy::Const(typeck_results.node_type(hir_id))
};
Cx {
Self {
tcx,
thir: Thir::new(body_type),
// FIXME(#132279): We're in a body, we should use a typing
@ -113,7 +115,7 @@ impl<'tcx> Cx<'tcx> {
#[instrument(level = "debug", skip(self))]
fn pattern_from_hir(&mut self, p: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> {
pat_from_hir(self.tcx, self.typing_env, self.typeck_results(), p)
pat_from_hir(self.tcx, self.typing_env, self.typeck_results, p)
}
fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option<Param<'tcx>> {
@ -197,15 +199,12 @@ impl<'tcx> Cx<'tcx> {
Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) }
})
}
}
impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn typeck_results(&self) -> &ty::TypeckResults<'tcx> {
self.typeck_results
fn user_args_applied_to_ty_of_hir_id(
&self,
hir_id: HirId,
) -> Option<ty::CanonicalUserType<'tcx>> {
crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id)
}
}

View file

@ -27,7 +27,6 @@ use tracing::{debug, instrument};
pub(crate) use self::check_match::check_match;
use crate::errors::*;
use crate::fluent_generated as fluent;
use crate::thir::util::UserAnnotatedTyHelpers;
struct PatCtxt<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
@ -155,42 +154,41 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
fn lower_pattern_range_endpoint(
&mut self,
expr: Option<&'tcx hir::PatExpr<'tcx>>,
) -> Result<
(Option<PatRangeBoundary<'tcx>>, Option<Ascription<'tcx>>, Option<LocalDefId>),
ErrorGuaranteed,
> {
match expr {
None => Ok((None, None, None)),
Some(expr) => {
let (kind, ascr, inline_const) = match self.lower_lit(expr) {
PatKind::ExpandedConstant { subpattern, def_id, is_inline: true } => {
(subpattern.kind, None, def_id.as_local())
// Out-parameters collecting extra data to be reapplied by the caller
ascriptions: &mut Vec<Ascription<'tcx>>,
inline_consts: &mut Vec<LocalDefId>,
) -> Result<Option<PatRangeBoundary<'tcx>>, ErrorGuaranteed> {
let Some(expr) = expr else { return Ok(None) };
// Lower the endpoint into a temporary `PatKind` that will then be
// deconstructed to obtain the constant value and other data.
let mut kind: PatKind<'tcx> = self.lower_lit(expr);
// Unpeel any ascription or inline-const wrapper nodes.
loop {
match kind {
PatKind::AscribeUserType { ascription, subpattern } => {
ascriptions.push(ascription);
kind = subpattern.kind;
}
PatKind::ExpandedConstant { is_inline, def_id, subpattern } => {
if is_inline {
inline_consts.extend(def_id.as_local());
}
PatKind::ExpandedConstant { subpattern, is_inline: false, .. } => {
(subpattern.kind, None, None)
}
PatKind::AscribeUserType { ascription, subpattern: box Pat { kind, .. } } => {
(kind, Some(ascription), None)
}
kind => (kind, None, None),
};
let value = match kind {
PatKind::Constant { value } => value,
PatKind::ExpandedConstant { subpattern, .. }
if let PatKind::Constant { value } = subpattern.kind =>
{
value
}
_ => {
let msg = format!(
"found bad range pattern endpoint `{expr:?}` outside of error recovery"
);
return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
}
};
Ok((Some(PatRangeBoundary::Finite(value)), ascr, inline_const))
kind = subpattern.kind;
}
_ => break,
}
}
// The unpeeled kind should now be a constant, giving us the endpoint value.
let PatKind::Constant { value } = kind else {
let msg =
format!("found bad range pattern endpoint `{expr:?}` outside of error recovery");
return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
};
Ok(Some(PatRangeBoundary::Finite(value)))
}
/// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
@ -253,11 +251,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
self.tcx.dcx().span_bug(span, msg);
}
let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
// Collect extra data while lowering the endpoints, to be reapplied later.
let mut ascriptions = vec![];
let mut inline_consts = vec![];
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
let mut lower_endpoint =
|expr| self.lower_pattern_range_endpoint(expr, &mut ascriptions, &mut inline_consts);
let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);
let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env);
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
@ -298,13 +300,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
// If we are handling a range with associated constants (e.g.
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
// constants somewhere. Have them on the range pattern.
for ascription in [lo_ascr, hi_ascr].into_iter().flatten() {
for ascription in ascriptions {
kind = PatKind::AscribeUserType {
ascription,
subpattern: Box::new(Pat { span, ty, kind }),
};
}
for def in [lo_inline, hi_inline].into_iter().flatten() {
for def in inline_consts {
kind = PatKind::ExpandedConstant {
def_id: def.to_def_id(),
is_inline: true,
@ -537,16 +539,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
_ => {
let e = match res {
Res::Def(DefKind::ConstParam, def_id) => {
self.tcx.dcx().emit_err(ConstParamInPattern {
span,
const_span: self.tcx().def_span(def_id),
})
let const_span = self.tcx.def_span(def_id);
self.tcx.dcx().emit_err(ConstParamInPattern { span, const_span })
}
Res::Def(DefKind::Static { .. }, def_id) => {
self.tcx.dcx().emit_err(StaticInPattern {
span,
static_span: self.tcx().def_span(def_id),
})
let static_span = self.tcx.def_span(def_id);
self.tcx.dcx().emit_err(StaticInPattern { span, static_span })
}
_ => self.tcx.dcx().emit_err(NonConstPath { span }),
};
@ -570,6 +568,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
kind
}
fn user_args_applied_to_ty_of_hir_id(
&self,
hir_id: hir::HirId,
) -> Option<ty::CanonicalUserType<'tcx>> {
crate::thir::util::user_args_applied_to_ty_of_hir_id(self.typeck_results, hir_id)
}
/// Takes a HIR Path. If the path is a constant, evaluates it and feeds
/// it to `const_to_pat`. Any other path (like enum variants without fields)
/// is converted to the corresponding pattern via `lower_variant_or_leaf`.
@ -600,12 +605,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
return pattern;
}
let user_provided_types = self.typeck_results().user_provided_types();
let user_provided_types = self.typeck_results.user_provided_types();
if let Some(&user_ty) = user_provided_types.get(id) {
let annotation = CanonicalUserTypeAnnotation {
user_ty: Box::new(user_ty),
span,
inferred_ty: self.typeck_results().node_type(id),
inferred_ty: self.typeck_results.node_type(id),
};
Box::new(Pat {
span,
@ -669,13 +674,3 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
self.const_to_pat(constant, ct_ty, expr.hir_id, lit.span).kind
}
}
impl<'tcx> UserAnnotatedTyHelpers<'tcx> for PatCtxt<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn typeck_results(&self) -> &ty::TypeckResults<'tcx> {
self.typeck_results
}
}

View file

@ -1,33 +1,27 @@
use rustc_hir as hir;
use rustc_middle::bug;
use rustc_middle::ty::{self, CanonicalUserType, TyCtxt};
use rustc_middle::ty::{self, CanonicalUserType};
use tracing::debug;
pub(crate) trait UserAnnotatedTyHelpers<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx>;
fn typeck_results(&self) -> &ty::TypeckResults<'tcx>;
/// Looks up the type associated with this hir-id and applies the
/// user-given generic parameters; the hir-id must map to a suitable
/// type.
fn user_args_applied_to_ty_of_hir_id(
&self,
hir_id: hir::HirId,
) -> Option<CanonicalUserType<'tcx>> {
let user_provided_types = self.typeck_results().user_provided_types();
let mut user_ty = *user_provided_types.get(hir_id)?;
debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
let ty = self.typeck_results().node_type(hir_id);
match ty.kind() {
ty::Adt(adt_def, ..) => {
if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind {
*did = adt_def.did();
}
Some(user_ty)
/// Looks up the type associated with this hir-id and applies the
/// user-given generic parameters; the hir-id must map to a suitable
/// type.
pub(crate) fn user_args_applied_to_ty_of_hir_id<'tcx>(
typeck_results: &ty::TypeckResults<'tcx>,
hir_id: hir::HirId,
) -> Option<CanonicalUserType<'tcx>> {
let user_provided_types = typeck_results.user_provided_types();
let mut user_ty = *user_provided_types.get(hir_id)?;
debug!("user_subts_applied_to_ty_of_hir_id: user_ty={:?}", user_ty);
let ty = typeck_results.node_type(hir_id);
match ty.kind() {
ty::Adt(adt_def, ..) => {
if let ty::UserTypeKind::TypeOf(ref mut did, _) = &mut user_ty.value.kind {
*did = adt_def.did();
}
ty::FnDef(..) => Some(user_ty),
_ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty),
Some(user_ty)
}
ty::FnDef(..) => Some(user_ty),
_ => bug!("ty: {:?} should not have user provided type {:?} recorded ", ty, user_ty),
}
}

View file

@ -162,7 +162,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> {
inner_visitor.check(i.owner_id, |this| intravisit::walk_impl_item(this, i));
}
fn visit_pattern_type_pattern(&mut self, p: &'hir hir::Pat<'hir>) {
self.visit_pat(p)
fn visit_pattern_type_pattern(&mut self, p: &'hir hir::TyPat<'hir>) {
intravisit::walk_ty_pat(self, p)
}
}

View file

@ -1,19 +1,20 @@
//! This module defines the `DepNode` type which the compiler uses to represent
//! nodes in the dependency graph. A `DepNode` consists of a `DepKind` (which
//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc)
//! and a `Fingerprint`, a 128 bit hash value the exact meaning of which
//! This module defines the [`DepNode`] type which the compiler uses to represent
//! nodes in the [dependency graph]. A `DepNode` consists of a [`DepKind`] (which
//! specifies the kind of thing it represents, like a piece of HIR, MIR, etc.)
//! and a [`Fingerprint`], a 128-bit hash value, the exact meaning of which
//! depends on the node's `DepKind`. Together, the kind and the fingerprint
//! fully identify a dependency node, even across multiple compilation sessions.
//! In other words, the value of the fingerprint does not depend on anything
//! that is specific to a given compilation session, like an unpredictable
//! interning key (e.g., NodeId, DefId, Symbol) or the numeric value of a
//! interning key (e.g., `NodeId`, `DefId`, `Symbol`) or the numeric value of a
//! pointer. The concept behind this could be compared to how git commit hashes
//! uniquely identify a given commit and has a few advantages:
//! uniquely identify a given commit. The fingerprinting approach has
//! a few advantages:
//!
//! * A `DepNode` can simply be serialized to disk and loaded in another session
//! without the need to do any "rebasing (like we have to do for Spans and
//! NodeIds) or "retracing" like we had to do for `DefId` in earlier
//! implementations of the dependency graph.
//! without the need to do any "rebasing" (like we have to do for Spans and
//! NodeIds) or "retracing" (like we had to do for `DefId` in earlier
//! implementations of the dependency graph).
//! * A `Fingerprint` is just a bunch of bits, which allows `DepNode` to
//! implement `Copy`, `Sync`, `Send`, `Freeze`, etc.
//! * Since we just have a bit pattern, `DepNode` can be mapped from disk into
@ -26,10 +27,12 @@
//! could not be instantiated because the current compilation session
//! contained no `DefId` for thing that had been removed.
//!
//! `DepNode` definition happens in `rustc_middle` with the `define_dep_nodes!()` macro.
//! This macro defines the `DepKind` enum and a corresponding `DepConstructor` enum. The
//! `DepConstructor` enum links a `DepKind` to the parameters that are needed at runtime in order
//! to construct a valid `DepNode` fingerprint.
//! `DepNode` definition happens in `rustc_middle` with the
//! `define_dep_nodes!()` macro. This macro defines the `DepKind` enum. Each
//! `DepKind` has its own parameters that are needed at runtime in order to
//! construct a valid `DepNode` fingerprint. However, only `CompileCodegenUnit`
//! and `CompileMonoItem` are constructed explicitly (with
//! `make_compile_codegen_unit` and `make_compile_mono_item`).
//!
//! Because the macro sees what parameters a given `DepKind` requires, it can
//! "infer" some properties for each kind of `DepNode`:
@ -41,6 +44,16 @@
//! in which case it is possible to map the node's fingerprint back to the
//! `DefId` it was computed from. In other cases, too much information gets
//! lost during fingerprint computation.
//!
//! `make_compile_codegen_unit` and `make_compile_mono_items`, together with
//! `DepNode::new()`, ensure that only valid `DepNode` instances can be
//! constructed. For example, the API does not allow for constructing
//! parameterless `DepNode`s with anything other than a zeroed out fingerprint.
//! More generally speaking, it relieves the user of the `DepNode` API of
//! having to know how to compute the expected fingerprint for a given set of
//! node parameters.
//!
//! [dependency graph]: https://rustc-dev-guide.rust-lang.org/query.html
use std::fmt;
use std::hash::Hash;

View file

@ -294,9 +294,12 @@ symbols! {
ProceduralMasqueradeDummyType,
Range,
RangeBounds,
RangeCopy,
RangeFrom,
RangeFromCopy,
RangeFull,
RangeInclusive,
RangeInclusiveCopy,
RangeTo,
RangeToInclusive,
Rc,
@ -774,6 +777,7 @@ symbols! {
discriminant_kind,
discriminant_type,
discriminant_value,
disjoint_bitor,
dispatch_from_dyn,
div,
div_assign,
@ -1009,6 +1013,7 @@ symbols! {
generic_const_exprs,
generic_const_items,
generic_param_attrs,
generic_pattern_types,
get_context,
global_alloc_ty,
global_allocator,
@ -1148,6 +1153,7 @@ symbols! {
iterator,
iterator_collect_fn,
kcfi,
keylocker_x86,
keyword,
kind,
kreg,
@ -1363,6 +1369,7 @@ symbols! {
new_lower_hex,
new_octal,
new_pointer,
new_range,
new_unchecked,
new_upper_exp,
new_upper_hex,

View file

@ -409,6 +409,7 @@ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("fma", Stable, &["avx"]),
("fxsr", Stable, &[]),
("gfni", Unstable(sym::avx512_target_feature), &["sse2"]),
("kl", Unstable(sym::keylocker_x86), &["sse2"]),
("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
("lzcnt", Stable, &[]),
("movbe", Stable, &[]),
@ -435,6 +436,7 @@ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("tbm", Unstable(sym::tbm_target_feature), &[]),
("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]),
("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]),
("widekl", Unstable(sym::keylocker_x86), &["kl"]),
("x87", Unstable(sym::x87_target_feature), &[]),
("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]),
("xsave", Stable, &[]),

View file

@ -703,7 +703,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit()
}
}
@ -806,7 +805,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"Async",
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}
}
@ -852,7 +850,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
"",
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}
@ -868,7 +865,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
kind: expected_kind.as_str(),
});
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
return Some(err.emit());
}
}
@ -2826,7 +2822,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
}

View file

@ -185,7 +185,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
suggest_increasing_limit,
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit()
}
}

View file

@ -1765,7 +1765,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};
err.code(E0746);
err.primary_message("return type cannot have an unboxed trait object");
err.primary_message("return type cannot be a trait object without pointer indirection");
err.children.clear();
let span = obligation.cause.span;
@ -1781,25 +1781,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
} else {
("dyn ", span.shrink_to_lo())
};
let alternatively = if visitor
.returns
.iter()
.map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr))
.collect::<FxHashSet<_>>()
.len()
<= 1
{
err.span_suggestion_verbose(
impl_span,
"consider returning an `impl Trait` instead of a `dyn Trait`",
"impl ",
Applicability::MaybeIncorrect,
);
"alternatively, "
} else {
err.help("if there were a single returned type, you could use `impl Trait` instead");
""
};
err.span_suggestion_verbose(
impl_span,
"consider returning an `impl Trait` instead of a `dyn Trait`",
"impl ",
Applicability::MaybeIncorrect,
);
let mut sugg = vec![
(span.shrink_to_lo(), format!("Box<{pre}")),
@ -1831,7 +1819,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
err.multipart_suggestion(
format!(
"{alternatively}box the return type, and wrap all of the returned values in \
"alternatively, box the return type, and wrap all of the returned values in \
`Box::new`",
),
sugg,
@ -1841,41 +1829,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
true
}
pub(super) fn point_at_returns_when_relevant(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
) {
match obligation.cause.code().peel_derives() {
ObligationCauseCode::SizedReturnType => {}
_ => return,
}
let hir = self.tcx.hir();
let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id);
if let hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn { body: body_id, .. }, ..
}) = node
{
let body = hir.body(*body_id);
// Point at all the `return`s in the function as they have failed trait bounds.
let mut visitor = ReturnsVisitor::default();
visitor.visit_body(body);
let typeck_results = self.typeck_results.as_ref().unwrap();
for expr in &visitor.returns {
if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
let ty = self.resolve_vars_if_possible(returned_ty);
if ty.references_error() {
// don't print out the [type error] here
err.downgrade_to_delayed_bug();
} else {
err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
}
}
}
}
}
pub(super) fn report_closure_arg_mismatch(
&self,
span: Span,

View file

@ -4,19 +4,16 @@
//!
//! [^1]: Formerly known as "object safety".
use std::iter;
use std::ops::ControlFlow;
use rustc_abi::BackendRepr;
use rustc_errors::FatalError;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::{
self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt,
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
};
use rustc_span::Span;
use rustc_type_ir::elaborate;
@ -109,14 +106,6 @@ fn dyn_compatibility_violations_for_trait(
violations.push(DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans));
}
if violations.is_empty() {
for item in tcx.associated_items(trait_def_id).in_definition_order() {
if let ty::AssocKind::Fn = item.kind {
check_receiver_correct(tcx, trait_def_id, *item);
}
}
}
violations
}
@ -499,55 +488,6 @@ fn virtual_call_violations_for_method<'tcx>(
errors
}
/// This code checks that `receiver_is_dispatchable` is correctly implemented.
///
/// This check is outlined from the dyn-compatibility check to avoid cycles with
/// layout computation, which relies on knowing whether methods are dyn-compatible.
fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) {
if !is_vtable_safe_method(tcx, trait_def_id, method) {
return;
}
let method_def_id = method.def_id;
let sig = tcx.fn_sig(method_def_id).instantiate_identity();
let typing_env = ty::TypingEnv::non_body_analysis(tcx, method_def_id);
let receiver_ty = tcx.liberate_late_bound_regions(method_def_id, sig.input(0));
if receiver_ty == tcx.types.self_param {
// Assumed OK, may change later if unsized_locals permits `self: Self` as dispatchable.
return;
}
// e.g., `Rc<()>`
let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id);
match tcx.layout_of(typing_env.as_query_input(unit_receiver_ty)).map(|l| l.backend_repr) {
Ok(BackendRepr::Scalar(..)) => (),
abi => {
tcx.dcx().span_delayed_bug(
tcx.def_span(method_def_id),
format!("receiver {unit_receiver_ty:?} when `Self = ()` should have a Scalar ABI; found {abi:?}"),
);
}
}
let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static);
// e.g., `Rc<dyn Trait>`
let trait_object_receiver =
receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id);
match tcx.layout_of(typing_env.as_query_input(trait_object_receiver)).map(|l| l.backend_repr) {
Ok(BackendRepr::ScalarPair(..)) => (),
abi => {
tcx.dcx().span_delayed_bug(
tcx.def_span(method_def_id),
format!(
"receiver {trait_object_receiver:?} when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}"
),
);
}
}
}
/// Performs a type instantiation to produce the version of `receiver_ty` when `Self = self_ty`.
/// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
fn receiver_for_self_ty<'tcx>(
@ -569,49 +509,6 @@ fn receiver_for_self_ty<'tcx>(
result
}
/// Creates the object type for the current trait. For example,
/// if the current trait is `Deref`, then this will be
/// `dyn Deref<Target = Self::Target> + 'static`.
#[instrument(level = "trace", skip(tcx), ret)]
fn object_ty_for_trait<'tcx>(
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
lifetime: ty::Region<'tcx>,
) -> Ty<'tcx> {
let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
debug!(?trait_ref);
let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
));
debug!(?trait_predicate);
let pred: ty::Predicate<'tcx> = trait_ref.upcast(tcx);
let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred])
.filter_map(|pred| {
debug!(?pred);
let pred = pred.as_projection_clause()?;
Some(pred.map_bound(|p| {
ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
tcx, p,
))
}))
})
.collect();
// NOTE: Since #37965, the existential predicates list has depended on the
// list of predicates to be sorted. This is mostly to enforce that the primary
// predicate comes first.
elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
elaborated_predicates.dedup();
let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(
iter::once(trait_predicate).chain(elaborated_predicates),
);
debug!(?existential_predicates);
Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
}
/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
/// in the following way:

View file

@ -8,8 +8,9 @@ use rustc_middle::ty::{
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::Span;
use rustc_session::parse::feature_err;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{Span, sym};
use tracing::{debug, instrument, trace};
use crate::infer::InferCtxt;
@ -704,8 +705,47 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
));
}
ty::Pat(subty, _) => {
ty::Pat(subty, pat) => {
self.require_sized(subty, ObligationCauseCode::Misc);
match *pat {
ty::PatternKind::Range { start, end, include_end: _ } => {
let mut check = |c| {
let cause = self.cause(ObligationCauseCode::Misc);
self.out.push(traits::Obligation::with_depth(
tcx,
cause.clone(),
self.recursion_depth,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::ConstArgHasType(c, subty),
)),
));
if !tcx.features().generic_pattern_types() {
if c.has_param() {
if self.span.is_dummy() {
self.tcx().dcx().delayed_bug(
"feature error should be reported elsewhere, too",
);
} else {
feature_err(
&self.tcx().sess,
sym::generic_pattern_types,
self.span,
"wraparound pattern type ranges cause monomorphization time errors",
)
.emit();
}
}
}
};
if let Some(start) = start {
check(start)
}
if let Some(end) = end {
check(end)
}
}
}
}
ty::Tuple(tys) => {

View file

@ -2118,6 +2118,35 @@ impl<T> UnsafeCell<T> {
pub const fn into_inner(self) -> T {
self.value
}
/// Replace the value in this `UnsafeCell` and return the old value.
///
/// # Safety
///
/// The caller must take care to avoid aliasing and data races.
///
/// - It is Undefined Behavior to allow calls to race with
/// any other access to the wrapped value.
/// - It is Undefined Behavior to call this while any other
/// reference(s) to the wrapped value are alive.
///
/// # Examples
///
/// ```
/// #![feature(unsafe_cell_access)]
/// use std::cell::UnsafeCell;
///
/// let uc = UnsafeCell::new(5);
///
/// let old = unsafe { uc.replace(10) };
/// assert_eq!(old, 5);
/// ```
#[inline]
#[unstable(feature = "unsafe_cell_access", issue = "136327")]
pub const unsafe fn replace(&self, value: T) -> T {
// SAFETY: pointer comes from `&self` so naturally satisfies invariants.
unsafe { ptr::replace(self.get(), value) }
}
}
impl<T: ?Sized> UnsafeCell<T> {
@ -2230,6 +2259,61 @@ impl<T: ?Sized> UnsafeCell<T> {
// no guarantee for user code that this will work in future versions of the compiler!
this as *const T as *mut T
}
/// Get a shared reference to the value within the `UnsafeCell`.
///
/// # Safety
///
/// - It is Undefined Behavior to call this while any mutable
/// reference to the wrapped value is alive.
/// - Mutating the wrapped value while the returned
/// reference is alive is Undefined Behavior.
///
/// # Examples
///
/// ```
/// #![feature(unsafe_cell_access)]
/// use std::cell::UnsafeCell;
///
/// let uc = UnsafeCell::new(5);
///
/// let val = unsafe { uc.as_ref_unchecked() };
/// assert_eq!(val, &5);
/// ```
#[inline]
#[unstable(feature = "unsafe_cell_access", issue = "136327")]
pub const unsafe fn as_ref_unchecked(&self) -> &T {
// SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants.
unsafe { self.get().as_ref_unchecked() }
}
/// Get an exclusive reference to the value within the `UnsafeCell`.
///
/// # Safety
///
/// - It is Undefined Behavior to call this while any other
/// reference(s) to the wrapped value are alive.
/// - Mutating the wrapped value through other means while the
/// returned reference is alive is Undefined Behavior.
///
/// # Examples
///
/// ```
/// #![feature(unsafe_cell_access)]
/// use std::cell::UnsafeCell;
///
/// let uc = UnsafeCell::new(5);
///
/// unsafe { *uc.as_mut_unchecked() += 1; }
/// assert_eq!(uc.into_inner(), 6);
/// ```
#[inline]
#[unstable(feature = "unsafe_cell_access", issue = "136327")]
#[allow(clippy::mut_from_ref)]
pub const unsafe fn as_mut_unchecked(&self) -> &mut T {
// SAFETY: pointer comes from `&self` so naturally satisfies ptr-to-ref invariants.
unsafe { self.get().as_mut_unchecked() }
}
}
#[stable(feature = "unsafe_cell_default", since = "1.10.0")]

View file

@ -37,175 +37,14 @@ pub use self::va_list::{VaList, VaListImpl};
)]
pub mod va_list;
macro_rules! type_alias {
{
$Docfile:tt, $Alias:ident = $Real:ty;
$( $Cfg:tt )*
} => {
#[doc = include_str!($Docfile)]
$( $Cfg )*
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub type $Alias = $Real;
}
}
type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] }
type_alias! { "c_schar.md", c_schar = i8; }
type_alias! { "c_uchar.md", c_uchar = u8; }
type_alias! { "c_short.md", c_short = i16; }
type_alias! { "c_ushort.md", c_ushort = u16; }
type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] }
type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] }
type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] }
type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] }
type_alias! { "c_longlong.md", c_longlong = i64; }
type_alias! { "c_ulonglong.md", c_ulonglong = u64; }
type_alias! { "c_float.md", c_float = f32; }
type_alias! { "c_double.md", c_double = f64; }
/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++).
///
/// This type is currently always [`usize`], however in the future there may be
/// platforms where this is not the case.
mod primitives;
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub use self::primitives::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
c_ulong, c_ulonglong, c_ushort,
};
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_size_t = usize;
/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++).
///
/// This type is currently always [`isize`], however in the future there may be
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ptrdiff_t = isize;
/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type.
///
/// This type is currently always [`isize`], however in the future there may be
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ssize_t = isize;
mod c_char_definition {
cfg_if! {
// These are the targets on which c_char is unsigned. Usually the
// signedness is the same for all target_os values on a given architecture
// but there are some exceptions (see isSignedCharDefault() in clang).
//
// aarch64:
// Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm®
// 64-bit Architecture (AArch64) says C/C++ char is unsigned byte.
// https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings
// arm:
// Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm®
// Architecture says C/C++ char is unsigned byte.
// https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings
// csky:
// Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface
// Standards Manual says ANSI C char is unsigned byte.
// https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf
// Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945).
// hexagon:
// Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application
// Binary Interface User Guide says "By default, the `char` data type is unsigned."
// https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf
// msp430:
// Section 2.1 "Basic Types" in MSP430 Embedded Application Binary
// Interface says "The char type is unsigned by default".
// https://www.ti.com/lit/an/slaa534a/slaa534a.pdf
// powerpc/powerpc64:
// - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC
// Processor Supplement says ANSI C char is unsigned byte
// https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf
// - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application
// Binary Interface Supplement 1.9 says ANSI C is unsigned byte
// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE
// - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification
// says char is unsigned byte
// https://openpowerfoundation.org/specifications/64bitelfabi/
// - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char."
// https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types
// riscv32/riscv64:
// C/C++ type representations section in RISC-V Calling Conventions
// page in RISC-V ELF psABI Document says "char is unsigned."
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations
// s390x:
// - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement
// Version 1.6.1 categorize ISO C char in unsigned integer
// https://github.com/IBM/s390x-abi/releases/tag/v1.6.1
// - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char."
// https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types
// xtensa:
// Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook
// says "`char` type is unsigned by default".
// https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf
//
// On the following operating systems, c_char is signed by default, regardless of architecture.
// Darwin (macOS, iOS, etc.):
// Apple targets' c_char is signed by default even on arm
// https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly
// Windows:
// Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char
// are promoted to int as if from type signed char by default, unless the /J compilation
// option is used."
// https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types
// L4Re:
// The kernel builds with -funsigned-char on all targets (but useserspace follows the
// architecture defaults). As we only have a target for userspace apps so there are no
// special cases for L4Re below.
// https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240
if #[cfg(all(
not(windows),
not(target_vendor = "apple"),
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "csky",
target_arch = "hexagon",
target_arch = "msp430",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "xtensa",
)
))] {
pub(super) type c_char = u8;
} else {
// On every other target, c_char is signed.
pub(super) type c_char = i8;
}
}
}
mod c_int_definition {
cfg_if! {
if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] {
pub(super) type c_int = i16;
pub(super) type c_uint = u16;
} else {
pub(super) type c_int = i32;
pub(super) type c_uint = u32;
}
}
}
mod c_long_definition {
cfg_if! {
if #[cfg(all(target_pointer_width = "64", not(windows)))] {
pub(super) type c_long = i64;
pub(super) type c_ulong = u64;
} else {
// The minimal size of `long` in the C standard is 32 bits
pub(super) type c_long = i32;
pub(super) type c_ulong = u32;
}
}
}
pub use self::primitives::{c_ptrdiff_t, c_size_t, c_ssize_t};
// N.B., for LLVM to recognize the void pointer type and by extension
// functions like malloc(), we need to have it represented as i8* in

View file

@ -0,0 +1,174 @@
//! Defines primitive types that match C's type definitions for FFI compatibility.
//!
//! This module is intentionally standalone to facilitate parsing when retrieving
//! core C types.
macro_rules! type_alias {
{
$Docfile:tt, $Alias:ident = $Real:ty;
$( $Cfg:tt )*
} => {
#[doc = include_str!($Docfile)]
$( $Cfg )*
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub type $Alias = $Real;
}
}
type_alias! { "c_char.md", c_char = c_char_definition::c_char; #[doc(cfg(all()))] }
type_alias! { "c_schar.md", c_schar = i8; }
type_alias! { "c_uchar.md", c_uchar = u8; }
type_alias! { "c_short.md", c_short = i16; }
type_alias! { "c_ushort.md", c_ushort = u16; }
type_alias! { "c_int.md", c_int = c_int_definition::c_int; #[doc(cfg(all()))] }
type_alias! { "c_uint.md", c_uint = c_int_definition::c_uint; #[doc(cfg(all()))] }
type_alias! { "c_long.md", c_long = c_long_definition::c_long; #[doc(cfg(all()))] }
type_alias! { "c_ulong.md", c_ulong = c_long_definition::c_ulong; #[doc(cfg(all()))] }
type_alias! { "c_longlong.md", c_longlong = i64; }
type_alias! { "c_ulonglong.md", c_ulonglong = u64; }
type_alias! { "c_float.md", c_float = f32; }
type_alias! { "c_double.md", c_double = f64; }
mod c_char_definition {
cfg_if! {
// These are the targets on which c_char is unsigned. Usually the
// signedness is the same for all target_os values on a given architecture
// but there are some exceptions (see isSignedCharDefault() in clang).
//
// aarch64:
// Section 10 "Arm C and C++ language mappings" in Procedure Call Standard for the Arm®
// 64-bit Architecture (AArch64) says C/C++ char is unsigned byte.
// https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs64/aapcs64.rst#arm-c-and-c-language-mappings
// arm:
// Section 8 "Arm C and C++ Language Mappings" in Procedure Call Standard for the Arm®
// Architecture says C/C++ char is unsigned byte.
// https://github.com/ARM-software/abi-aa/blob/2024Q3/aapcs32/aapcs32.rst#arm-c-and-c-language-mappings
// csky:
// Section 2.1.2 "Primary Data Type" in C-SKY V2 CPU Applications Binary Interface
// Standards Manual says ANSI C char is unsigned byte.
// https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/C-SKY_V2_CPU_Applications_Binary_Interface_Standards_Manual.pdf
// Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945).
// hexagon:
// Section 3.1 "Basic data type" in Qualcomm Hexagon™ Application
// Binary Interface User Guide says "By default, the `char` data type is unsigned."
// https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf
// msp430:
// Section 2.1 "Basic Types" in MSP430 Embedded Application Binary
// Interface says "The char type is unsigned by default".
// https://www.ti.com/lit/an/slaa534a/slaa534a.pdf
// powerpc/powerpc64:
// - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC
// Processor Supplement says ANSI C char is unsigned byte
// https://refspecs.linuxfoundation.org/elf/elfspec_ppc.pdf
// - PPC64 ELFv1: Section 3.1.4 "Fundamental Types" in 64-bit PowerPC ELF Application
// Binary Interface Supplement 1.9 says ANSI C is unsigned byte
// https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUND-TYPE
// - PPC64 ELFv2: Section 2.1.2.2 "Fundamental Types" in 64-Bit ELF V2 ABI Specification
// says char is unsigned byte
// https://openpowerfoundation.org/specifications/64bitelfabi/
// - AIX: XL C for AIX Language Reference says "By default, char behaves like an unsigned char."
// https://www.ibm.com/docs/en/xl-c-aix/13.1.3?topic=specifiers-character-types
// riscv32/riscv64:
// C/C++ type representations section in RISC-V Calling Conventions
// page in RISC-V ELF psABI Document says "char is unsigned."
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#cc-type-representations
// s390x:
// - ELF: "Table 1.1.: Scalar types" in ELF Application Binary Interface s390x Supplement
// Version 1.6.1 categorize ISO C char in unsigned integer
// https://github.com/IBM/s390x-abi/releases/tag/v1.6.1
// - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char."
// https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types
// xtensa:
// Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook
// says "`char` type is unsigned by default".
// https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf
//
// On the following operating systems, c_char is signed by default, regardless of architecture.
// Darwin (macOS, iOS, etc.):
// Apple targets' c_char is signed by default even on arm
// https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-data-types-and-data-alignment-properly
// Windows:
// Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char
// are promoted to int as if from type signed char by default, unless the /J compilation
// option is used."
// https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types
// L4Re:
// The kernel builds with -funsigned-char on all targets (but useserspace follows the
// architecture defaults). As we only have a target for userspace apps so there are no
// special cases for L4Re below.
// https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240
if #[cfg(all(
not(windows),
not(target_vendor = "apple"),
any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "csky",
target_arch = "hexagon",
target_arch = "msp430",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "s390x",
target_arch = "xtensa",
)
))] {
pub(super) type c_char = u8;
} else {
// On every other target, c_char is signed.
pub(super) type c_char = i8;
}
}
}
mod c_long_definition {
cfg_if! {
if #[cfg(all(target_pointer_width = "64", not(windows)))] {
pub(super) type c_long = i64;
pub(super) type c_ulong = u64;
} else {
// The minimal size of `long` in the C standard is 32 bits
pub(super) type c_long = i32;
pub(super) type c_ulong = u32;
}
}
}
/// Equivalent to C's `size_t` type, from `stddef.h` (or `cstddef` for C++).
///
/// This type is currently always [`usize`], however in the future there may be
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_size_t = usize;
/// Equivalent to C's `ptrdiff_t` type, from `stddef.h` (or `cstddef` for C++).
///
/// This type is currently always [`isize`], however in the future there may be
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ptrdiff_t = isize;
/// Equivalent to C's `ssize_t` (on POSIX) or `SSIZE_T` (on Windows) type.
///
/// This type is currently always [`isize`], however in the future there may be
/// platforms where this is not the case.
#[unstable(feature = "c_size_t", issue = "88345")]
pub type c_ssize_t = isize;
mod c_int_definition {
cfg_if! {
if #[cfg(any(target_arch = "avr", target_arch = "msp430"))] {
pub(super) type c_int = i16;
pub(super) type c_uint = u16;
} else {
pub(super) type c_int = i32;
pub(super) type c_uint = u32;
}
}
}

View file

@ -192,7 +192,8 @@ macro_rules! impl_Debug {
}
// 2 digit decimal look up table
static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
static DEC_DIGITS_LUT: &[u8; 200] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
@ -232,83 +233,89 @@ macro_rules! impl_Display {
#[cfg(not(feature = "optimize_for_size"))]
impl $unsigned {
fn _fmt(mut self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
let mut buf = [MaybeUninit::<u8>::uninit(); SIZE];
let mut curr = SIZE;
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
// Buffer decimals for $unsigned with right alignment.
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
// Count the number of bytes in buf that are not initialized.
let mut offset = buf.len();
// Consume the least-significant decimals from a working copy.
let mut remain = self;
// SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we
// can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show
// that it's OK to copy into `buf_ptr`, notice that at the beginning
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
// each step this is kept the same as `n` is divided. Since `n` is always
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
// is safe to access.
unsafe {
// need at least 16 bits for the 4-characters-at-a-time to work.
#[allow(overflowing_literals)]
#[allow(unused_comparisons)]
// This block will be removed for smaller types at compile time and in the worst
// case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`.
if core::mem::size_of::<$unsigned>() >= 2 {
// eagerly decode 4 characters at a time
while self >= 10000 {
let rem = (self % 10000) as usize;
self /= 10000;
// Format per four digits from the lookup table.
// Four digits need a 16-bit $unsigned or wider.
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
// and the while condition ensures at least 4 more decimals.
unsafe { core::hint::assert_unchecked(offset >= 4) }
// SAFETY: The offset counts down from its initial buf.len()
// without underflow due to the previous precondition.
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
offset -= 4;
let d1 = (rem / 100) << 1;
let d2 = (rem % 100) << 1;
curr -= 4;
// We are allowed to copy to `buf_ptr[curr..curr + 3]` here since
// otherwise `curr < 0`. But then `n` was originally at least `10000^10`
// which is `10^40 > 2^128 > n`.
ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2);
ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2);
}
}
// if we reach here numbers are <= 9999, so at most 4 chars long
let mut n = self as usize; // possibly reduce 64bit math
// decode 2 more chars, if > 2 chars
if n >= 100 {
let d1 = (n % 100) << 1;
n /= 100;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
}
// if we reach here numbers are <= 100, so at most 2 chars long
// The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough.
// decode last 1 or 2 chars
if n < 10 {
curr -= 1;
*buf_ptr.add(curr) = (n as u8) + b'0';
} else {
let d1 = n << 1;
curr -= 2;
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
}
// pull two pairs
let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
let quad = remain % scale;
remain /= scale;
let pair1 = (quad / 100) as usize;
let pair2 = (quad % 100) as usize;
buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
}
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
// UTF-8 since `DEC_DIGITS_LUT` is
let buf_slice = unsafe {
str::from_utf8_unchecked(
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
// Format per two digits from the lookup table.
if remain > 9 {
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
// and the while condition ensures at least 2 more decimals.
unsafe { core::hint::assert_unchecked(offset >= 2) }
// SAFETY: The offset counts down from its initial buf.len()
// without underflow due to the previous precondition.
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
offset -= 2;
let pair = (remain % 100) as usize;
remain /= 100;
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
}
// Format the last remaining digit, if any.
if remain != 0 || self == 0 {
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
// and the if condition ensures (at least) 1 more decimals.
unsafe { core::hint::assert_unchecked(offset >= 1) }
// SAFETY: The offset counts down from its initial buf.len()
// without underflow due to the previous precondition.
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
offset -= 1;
// Either the compiler sees that remain < 10, or it prevents
// a boundary check up next.
let last = (remain & 15) as usize;
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
// not used: remain = 0;
}
// SAFETY: All buf content since offset is set.
let written = unsafe { buf.get_unchecked(offset..) };
// SAFETY: Writes use ASCII from the lookup table exclusively.
let as_str = unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
MaybeUninit::slice_as_ptr(written),
written.len(),
))
};
f.pad_integral(is_nonnegative, "", buf_slice)
f.pad_integral(is_nonnegative, "", as_str)
}
})*
#[cfg(feature = "optimize_for_size")]
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const SIZE: usize = $u::MAX.ilog(10) as usize + 1;
let mut buf = [MaybeUninit::<u8>::uninit(); SIZE];
let mut curr = buf.len();
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
let mut curr = MAX_DEC_N;
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning

View file

@ -110,3 +110,41 @@ impl const CarryingMulAdd for i128 {
(low, high)
}
}
#[const_trait]
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub trait DisjointBitOr: Copy + 'static {
/// See [`super::disjoint_bitor`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn disjoint_bitor(self, other: Self) -> Self;
}
macro_rules! zero {
(bool) => {
false
};
($t:ident) => {
0
};
}
macro_rules! impl_disjoint_bitor {
($($t:ident,)+) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const DisjointBitOr for $t {
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn disjoint_bitor(self, other: Self) -> Self {
// Note that the assume here is required for UB detection in Miri!
// SAFETY: our precondition is that there are no bits in common,
// so this is just telling that to the backend.
unsafe { super::assume((self & other) == zero!($t)) };
self | other
}
}
)+};
}
impl_disjoint_bitor! {
bool,
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
}

View file

@ -3248,6 +3248,26 @@ pub const fn three_way_compare<T: Copy>(_lhs: T, _rhss: T) -> crate::cmp::Orderi
unimplemented!()
}
/// Combine two values which have no bits in common.
///
/// This allows the backend to implement it as `a + b` *or* `a | b`,
/// depending which is easier to implement on a specific target.
///
/// # Safety
///
/// Requires that `(a & b) == 0`, or equivalently that `(a | b) == (a + b)`.
///
/// Otherwise it's immediate UB.
#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")]
#[rustc_nounwind]
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[miri::intrinsic_fallback_is_spec] // the fallbacks all `assume` to tell Miri
pub const unsafe fn disjoint_bitor<T: ~const fallback::DisjointBitOr>(a: T, b: T) -> T {
// SAFETY: same preconditions as this function.
unsafe { fallback::DisjointBitOr::disjoint_bitor(a, b) }
}
/// Performs checked integer addition.
///
/// Note that, unlike most intrinsics, this is safe to call;

View file

@ -117,6 +117,7 @@
#![feature(const_eval_select)]
#![feature(core_intrinsics)]
#![feature(coverage_attribute)]
#![feature(disjoint_bitor)]
#![feature(internal_impls_macro)]
#![feature(ip)]
#![feature(is_ascii_octdigit)]

View file

@ -1077,6 +1077,9 @@ marker_impls! {
}
/// A common trait implemented by all function pointers.
//
// Note that while the trait is internal and unstable it is nevertheless
// exposed as a public bound of the stable `core::ptr::fn_addr_eq` function.
#[unstable(
feature = "fn_ptr_trait",
issue = "none",

View file

@ -474,6 +474,7 @@ macro_rules! nonzero_integer {
#[$stability:meta]
Self = $Ty:ident,
Primitive = $signedness:ident $Int:ident,
SignedPrimitive = $Sint:ty,
UnsignedPrimitive = $Uint:ty,
// Used in doc comments.
@ -905,6 +906,7 @@ macro_rules! nonzero_integer {
nonzero_integer_signedness_dependent_methods! {
Primitive = $signedness $Int,
SignedPrimitive = $Sint,
UnsignedPrimitive = $Uint,
}
@ -1128,6 +1130,7 @@ macro_rules! nonzero_integer {
(
Self = $Ty:ident,
Primitive = unsigned $Int:ident,
SignedPrimitive = $Sint:ident,
rot = $rot:literal,
rot_op = $rot_op:literal,
rot_result = $rot_result:literal,
@ -1140,6 +1143,7 @@ macro_rules! nonzero_integer {
#[stable(feature = "nonzero", since = "1.28.0")]
Self = $Ty,
Primitive = unsigned $Int,
SignedPrimitive = $Sint,
UnsignedPrimitive = $Int,
rot = $rot,
rot_op = $rot_op,
@ -1154,7 +1158,7 @@ macro_rules! nonzero_integer {
(
Self = $Ty:ident,
Primitive = signed $Int:ident,
UnsignedPrimitive = $UInt:ident,
UnsignedPrimitive = $Uint:ident,
rot = $rot:literal,
rot_op = $rot_op:literal,
rot_result = $rot_result:literal,
@ -1166,7 +1170,8 @@ macro_rules! nonzero_integer {
#[stable(feature = "signed_nonzero", since = "1.34.0")]
Self = $Ty,
Primitive = signed $Int,
UnsignedPrimitive = $UInt,
SignedPrimitive = $Int,
UnsignedPrimitive = $Uint,
rot = $rot,
rot_op = $rot_op,
rot_result = $rot_result,
@ -1286,6 +1291,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods {
// Associated items for unsigned nonzero types only.
(
Primitive = unsigned $Int:ident,
SignedPrimitive = $Sint:ty,
UnsignedPrimitive = $Uint:ty,
) => {
/// The smallest value that can be represented by this non-zero
@ -1620,11 +1626,35 @@ macro_rules! nonzero_integer_signedness_dependent_methods {
// results will be sqrt(1), which is 1, so a result can't be zero.
unsafe { Self::new_unchecked(result) }
}
/// Returns the bit pattern of `self` reinterpreted as a signed integer of the same size.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(integer_sign_cast)]
/// # use std::num::NonZero;
///
#[doc = concat!("let n = NonZero::<", stringify!($Int), ">::MAX;")]
///
#[doc = concat!("assert_eq!(n.cast_signed(), NonZero::new(-1", stringify!($Sint), ").unwrap());")]
/// ```
#[unstable(feature = "integer_sign_cast", issue = "125882")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn cast_signed(self) -> NonZero<$Sint> {
// SAFETY: `self.get()` can't be zero
unsafe { NonZero::new_unchecked(self.get().cast_signed()) }
}
};
// Associated items for signed nonzero types only.
(
Primitive = signed $Int:ident,
SignedPrimitive = $Sint:ty,
UnsignedPrimitive = $Uint:ty,
) => {
/// The smallest value that can be represented by this non-zero
@ -2035,12 +2065,37 @@ macro_rules! nonzero_integer_signedness_dependent_methods {
// SAFETY: negation of nonzero cannot yield zero values.
unsafe { Self::new_unchecked(result) }
}
/// Returns the bit pattern of `self` reinterpreted as an unsigned integer of the same size.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(integer_sign_cast)]
/// # use std::num::NonZero;
///
#[doc = concat!("let n = NonZero::new(-1", stringify!($Int), ").unwrap();")]
///
#[doc = concat!("assert_eq!(n.cast_unsigned(), NonZero::<", stringify!($Uint), ">::MAX);")]
/// ```
#[unstable(feature = "integer_sign_cast", issue = "125882")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn cast_unsigned(self) -> NonZero<$Uint> {
// SAFETY: `self.get()` can't be zero
unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) }
}
};
}
nonzero_integer! {
Self = NonZeroU8,
Primitive = unsigned u8,
SignedPrimitive = i8,
rot = 2,
rot_op = "0x82",
rot_result = "0xa",
@ -2052,6 +2107,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroU16,
Primitive = unsigned u16,
SignedPrimitive = i16,
rot = 4,
rot_op = "0xa003",
rot_result = "0x3a",
@ -2063,6 +2119,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroU32,
Primitive = unsigned u32,
SignedPrimitive = i32,
rot = 8,
rot_op = "0x10000b3",
rot_result = "0xb301",
@ -2074,6 +2131,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroU64,
Primitive = unsigned u64,
SignedPrimitive = i64,
rot = 12,
rot_op = "0xaa00000000006e1",
rot_result = "0x6e10aa",
@ -2085,6 +2143,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroU128,
Primitive = unsigned u128,
SignedPrimitive = i128,
rot = 16,
rot_op = "0x13f40000000000000000000000004f76",
rot_result = "0x4f7613f4",
@ -2097,6 +2156,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroUsize,
Primitive = unsigned usize,
SignedPrimitive = isize,
rot = 4,
rot_op = "0xa003",
rot_result = "0x3a",
@ -2109,6 +2169,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroUsize,
Primitive = unsigned usize,
SignedPrimitive = isize,
rot = 8,
rot_op = "0x10000b3",
rot_result = "0xb301",
@ -2121,6 +2182,7 @@ nonzero_integer! {
nonzero_integer! {
Self = NonZeroUsize,
Primitive = unsigned usize,
SignedPrimitive = isize,
rot = 12,
rot_op = "0xaa00000000006e1",
rot_result = "0x6e10aa",

View file

@ -1187,6 +1187,50 @@ macro_rules! uint_impl {
self % rhs
}
/// Same value as `self | other`, but UB if any bit position is set in both inputs.
///
/// This is a situational micro-optimization for places where you'd rather
/// use addition on some platforms and bitwise or on other platforms, based
/// on exactly which instructions combine better with whatever else you're
/// doing. Note that there's no reason to bother using this for places
/// where it's clear from the operations involved that they can't overlap.
/// For example, if you're combining `u16`s into a `u32` with
/// `((a as u32) << 16) | (b as u32)`, that's fine, as the backend will
/// know those sides of the `|` are disjoint without needing help.
///
/// # Examples
///
/// ```
/// #![feature(disjoint_bitor)]
///
/// // SAFETY: `1` and `4` have no bits in common.
/// unsafe {
#[doc = concat!(" assert_eq!(1_", stringify!($SelfT), ".unchecked_disjoint_bitor(4), 5);")]
/// }
/// ```
///
/// # Safety
///
/// Requires that `(self & other) == 0`, otherwise it's immediate UB.
///
/// Equivalently, requires that `(self | other) == (self + other)`.
#[unstable(feature = "disjoint_bitor", issue = "135758")]
#[rustc_const_unstable(feature = "disjoint_bitor", issue = "135758")]
#[inline]
pub const unsafe fn unchecked_disjoint_bitor(self, other: Self) -> Self {
assert_unsafe_precondition!(
check_language_ub,
concat!(stringify!($SelfT), "::unchecked_disjoint_bitor cannot have overlapping bits"),
(
lhs: $SelfT = self,
rhs: $SelfT = other,
) => (lhs & rhs) == 0,
);
// SAFETY: Same precondition
unsafe { intrinsics::disjoint_bitor(self, other) }
}
/// Returns the logarithm of the number with respect to an arbitrary base,
/// rounded down.
///
@ -2346,15 +2390,22 @@ macro_rules! uint_impl {
/// assert_eq!((sum1, sum0), (9, 6));
/// ```
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
pub const fn carrying_add(self, rhs: Self, carry: bool) -> (Self, bool) {
// note: longer-term this should be done via an intrinsic, but this has been shown
// to generate optimal code for now, and LLVM doesn't have an equivalent intrinsic
let (a, b) = self.overflowing_add(rhs);
let (c, d) = a.overflowing_add(carry as $SelfT);
(c, b | d)
let (a, c1) = self.overflowing_add(rhs);
let (b, c2) = a.overflowing_add(carry as $SelfT);
// Ideally LLVM would know this is disjoint without us telling them,
// but it doesn't <https://github.com/llvm/llvm-project/issues/118162>
// SAFETY: Only one of `c1` and `c2` can be set.
// For c1 to be set we need to have overflowed, but if we did then
// `a` is at most `MAX-1`, which means that `c2` cannot possibly
// overflow because it's adding at most `1` (since it came from `bool`)
(b, unsafe { intrinsics::disjoint_bitor(c1, c2) })
}
/// Calculates `self` + `rhs` with a signed `rhs`.

View file

@ -48,6 +48,7 @@ pub use crate::ops::{Bound, OneSidedRange, RangeBounds, RangeFull, RangeTo, Rang
/// assert_eq!(Range::from(3..5), Range { start: 3, end: 5 });
/// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum());
/// ```
#[cfg_attr(not(bootstrap), lang = "RangeCopy")]
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
#[unstable(feature = "new_range_api", issue = "125687")]
pub struct Range<Idx> {
@ -205,6 +206,7 @@ impl<T> From<legacy::Range<T>> for Range<T> {
/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 });
/// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum());
/// ```
#[cfg_attr(not(bootstrap), lang = "RangeInclusiveCopy")]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[unstable(feature = "new_range_api", issue = "125687")]
pub struct RangeInclusive<Idx> {
@ -388,6 +390,7 @@ impl<T> From<legacy::RangeInclusive<T>> for RangeInclusive<T> {
/// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 });
/// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum());
/// ```
#[cfg_attr(not(bootstrap), lang = "RangeFromCopy")]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[unstable(feature = "new_range_api", issue = "125687")]
pub struct RangeFrom<Idx> {

View file

@ -124,42 +124,41 @@ fn write_str_macro_debug_ascii(bh: &mut Bencher) {
#[bench]
fn write_u128_max(bh: &mut Bencher) {
bh.iter(|| {
test::black_box(format!("{}", u128::MAX));
black_box(format!("{}", black_box(u128::MAX)));
});
}
#[bench]
fn write_u128_min(bh: &mut Bencher) {
bh.iter(|| {
let s = format!("{}", 0u128);
test::black_box(s);
black_box(format!("{}", black_box(u128::MIN)));
});
}
#[bench]
fn write_u64_max(bh: &mut Bencher) {
bh.iter(|| {
test::black_box(format!("{}", u64::MAX));
black_box(format!("{}", black_box(u64::MAX)));
});
}
#[bench]
fn write_u64_min(bh: &mut Bencher) {
bh.iter(|| {
test::black_box(format!("{}", 0u64));
black_box(format!("{}", black_box(u64::MIN)));
});
}
#[bench]
fn write_u8_max(bh: &mut Bencher) {
bh.iter(|| {
test::black_box(format!("{}", u8::MAX));
black_box(format!("{}", black_box(u8::MAX)));
});
}
#[bench]
fn write_u8_min(bh: &mut Bencher) {
bh.iter(|| {
test::black_box(format!("{}", 0u8));
black_box(format!("{}", black_box(u8::MIN)));
});
}

View file

@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust.git"
description = "The Rust Standard Library"
edition = "2021"
autobenches = false
[lib]
crate-type = ["dylib", "rlib"]
@ -130,6 +131,18 @@ name = "pipe-subprocess"
path = "tests/pipe_subprocess.rs"
harness = false
[[test]]
name = "sync"
path = "tests/sync/lib.rs"
[[test]]
name = "floats"
path = "tests/floats/lib.rs"
[[test]]
name = "thread_local"
path = "tests/thread_local/lib.rs"
[[bench]]
name = "stdbenches"
path = "benches/lib.rs"

View file

@ -5,3 +5,5 @@
extern crate test;
mod hash;
mod path;
mod time;

114
library/std/benches/path.rs Normal file
View file

@ -0,0 +1,114 @@
use core::hint::black_box;
use std::collections::{BTreeSet, HashSet};
use std::hash::{DefaultHasher, Hash, Hasher};
use std::path::*;
#[bench]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
let prefix = "my/home";
let mut paths: Vec<_> =
(0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect();
paths.sort();
b.iter(|| {
black_box(paths.as_mut_slice()).sort_unstable();
});
}
#[bench]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_long(b: &mut test::Bencher) {
let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
let paths: Vec<_> =
(0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect();
let mut set = BTreeSet::new();
paths.iter().for_each(|p| {
set.insert(p.as_path());
});
b.iter(|| {
set.remove(paths[500].as_path());
set.insert(paths[500].as_path());
});
}
#[bench]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_cmp_fast_path_short(b: &mut test::Bencher) {
let prefix = "my/home";
let paths: Vec<_> =
(0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect();
let mut set = BTreeSet::new();
paths.iter().for_each(|p| {
set.insert(p.as_path());
});
b.iter(|| {
set.remove(paths[500].as_path());
set.insert(paths[500].as_path());
});
}
#[bench]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_hashset(b: &mut test::Bencher) {
let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
let paths: Vec<_> =
(0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect();
let mut set = HashSet::new();
paths.iter().for_each(|p| {
set.insert(p.as_path());
});
b.iter(|| {
set.remove(paths[500].as_path());
set.insert(black_box(paths[500].as_path()))
});
}
#[bench]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_path_hashset_miss(b: &mut test::Bencher) {
let prefix = "/my/home/is/my/castle/and/my/castle/has/a/rusty/workbench/";
let paths: Vec<_> =
(0..1000).map(|num| PathBuf::from(prefix).join(format!("file {num}.rs"))).collect();
let mut set = HashSet::new();
paths.iter().for_each(|p| {
set.insert(p.as_path());
});
let probe = PathBuf::from(prefix).join("other");
b.iter(|| set.remove(black_box(probe.as_path())));
}
#[bench]
fn bench_hash_path_short(b: &mut test::Bencher) {
let mut hasher = DefaultHasher::new();
let path = Path::new("explorer.exe");
b.iter(|| black_box(path).hash(&mut hasher));
black_box(hasher.finish());
}
#[bench]
fn bench_hash_path_long(b: &mut test::Bencher) {
let mut hasher = DefaultHasher::new();
let path =
Path::new("/aaaaa/aaaaaa/./../aaaaaaaa/bbbbbbbbbbbbb/ccccccccccc/ddddddddd/eeeeeee.fff");
b.iter(|| black_box(path).hash(&mut hasher));
black_box(hasher.finish());
}

View file

@ -0,0 +1,47 @@
use std::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use test::{Bencher, black_box};
macro_rules! bench_instant_threaded {
($bench_name:ident, $thread_count:expr) => {
#[bench]
#[cfg(not(target_arch = "wasm32"))]
fn $bench_name(b: &mut Bencher) -> std::thread::Result<()> {
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
let running = Arc::new(AtomicBool::new(true));
let threads: Vec<_> = (0..$thread_count)
.map(|_| {
let flag = Arc::clone(&running);
std::thread::spawn(move || {
while flag.load(Ordering::Relaxed) {
black_box(Instant::now());
}
})
})
.collect();
b.iter(|| {
let a = Instant::now();
let b = Instant::now();
assert!(b >= a);
});
running.store(false, Ordering::Relaxed);
for t in threads {
t.join()?;
}
Ok(())
}
};
}
bench_instant_threaded!(instant_contention_01_threads, 0);
bench_instant_threaded!(instant_contention_02_threads, 1);
bench_instant_threaded!(instant_contention_04_threads, 3);
bench_instant_threaded!(instant_contention_08_threads, 7);
bench_instant_threaded!(instant_contention_16_threads, 15);

View file

@ -10,9 +10,6 @@
#![stable(feature = "env", since = "1.0.0")]
#[cfg(test)]
mod tests;
use crate::error::Error;
use crate::ffi::{OsStr, OsString};
use crate::path::{Path, PathBuf};

View file

@ -1,120 +0,0 @@
use super::*;
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)]
fn test_self_exe_path() {
let path = current_exe();
assert!(path.is_ok());
let path = path.unwrap();
// Hard to test this function
assert!(path.is_absolute());
}
#[test]
fn test() {
assert!((!Path::new("test-path").is_absolute()));
#[cfg(not(target_env = "sgx"))]
current_dir().unwrap();
}
#[test]
#[cfg(windows)]
fn split_paths_windows() {
use crate::path::PathBuf;
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed).collect::<Vec<_>>()
== parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
}
assert!(check_parse("", &mut [""]));
assert!(check_parse(r#""""#, &mut [""]));
assert!(check_parse(";;", &mut ["", "", ""]));
assert!(check_parse(r"c:\", &mut [r"c:\"]));
assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"]));
assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]));
}
#[test]
#[cfg(unix)]
fn split_paths_unix() {
use crate::path::PathBuf;
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed).collect::<Vec<_>>()
== parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
}
assert!(check_parse("", &mut [""]));
assert!(check_parse("::", &mut ["", "", ""]));
assert!(check_parse("/", &mut ["/"]));
assert!(check_parse("/:", &mut ["/", ""]));
assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
}
#[test]
#[cfg(unix)]
fn join_paths_unix() {
use crate::ffi::OsStr;
fn test_eq(input: &[&str], output: &str) -> bool {
&*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
}
assert!(test_eq(&[], ""));
assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin"));
assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:"));
assert!(join_paths(["/te:st"].iter().cloned()).is_err());
}
#[test]
#[cfg(windows)]
fn join_paths_windows() {
use crate::ffi::OsStr;
fn test_eq(input: &[&str], output: &str) -> bool {
&*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
}
assert!(test_eq(&[], ""));
assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\"));
assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;"));
assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#));
assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err());
}
#[test]
fn args_debug() {
assert_eq!(
format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()),
format!("{:?}", args())
);
}
#[test]
fn args_os_debug() {
assert_eq!(
format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()),
format!("{:?}", args_os())
);
}
#[test]
fn vars_debug() {
assert_eq!(
format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()),
format!("{:?}", vars())
);
}
#[test]
fn vars_os_debug() {
assert_eq!(
format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()),
format!("{:?}", vars_os())
);
}

View file

@ -1,9 +1,6 @@
#![doc = include_str!("../../core/src/error.md")]
#![stable(feature = "rust1", since = "1.0.0")]
#[cfg(test)]
mod tests;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::error::Error;
#[unstable(feature = "error_generic_member_access", issue = "99301")]

View file

@ -4,9 +4,6 @@
//!
//! Mathematically significant numbers are provided in the `consts` sub-module.
#[cfg(test)]
mod tests;
#[unstable(feature = "f128", issue = "116909")]
pub use core::f128::consts;

View file

@ -4,9 +4,6 @@
//!
//! Mathematically significant numbers are provided in the `consts` sub-module.
#[cfg(test)]
mod tests;
#[unstable(feature = "f16", issue = "116909")]
pub use core::f16::consts;

View file

@ -12,9 +12,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[cfg(test)]
mod tests;
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::f32::{

View file

@ -12,9 +12,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[cfg(test)]
mod tests;
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::f64::{

View file

@ -530,6 +530,8 @@ pub use core::option;
pub use core::pin;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::ptr;
#[unstable(feature = "new_range_api", issue = "125687")]
pub use core::range;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::result;
#[stable(feature = "rust1", since = "1.0.0")]

View file

@ -372,18 +372,3 @@ macro_rules! dbg {
($($crate::dbg!($val)),+,)
};
}
/// Verify that floats are within a tolerance of each other, 1.0e-6 by default.
#[cfg(test)]
macro_rules! assert_approx_eq {
($a:expr, $b:expr) => {{ assert_approx_eq!($a, $b, 1.0e-6) }};
($a:expr, $b:expr, $lim:expr) => {{
let (a, b) = (&$a, &$b);
let diff = (*a - *b).abs();
assert!(
diff < $lim,
"{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})",
lim = $lim
);
}};
}

View file

@ -6,9 +6,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![allow(missing_docs)]
#[cfg(test)]
mod tests;
#[stable(feature = "int_error_matching", since = "1.55.0")]
pub use core::num::IntErrorKind;
#[stable(feature = "generic_nonzero", since = "1.79.0")]
@ -29,28 +26,3 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError}
pub use core::num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize};
#[stable(feature = "nonzero", since = "1.28.0")]
pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize};
#[cfg(test)]
use crate::fmt;
#[cfg(test)]
use crate::ops::{Add, Div, Mul, Rem, Sub};
/// Helper function for testing numeric operations
#[cfg(test)]
pub fn test_num<T>(ten: T, two: T)
where
T: PartialEq
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Rem<Output = T>
+ fmt::Debug
+ Copy,
{
assert_eq!(ten.add(two), ten + two);
assert_eq!(ten.sub(two), ten - two);
assert_eq!(ten.mul(two), ten * two);
assert_eq!(ten.div(two), ten / two);
assert_eq!(ten.rem(two), ten % two);
}

View file

@ -529,6 +529,3 @@ pub fn get_backtrace_style() -> Option<BacktraceStyle> {
Err(new) => BacktraceStyle::from_u8(new),
}
}
#[cfg(test)]
mod tests;

View file

@ -67,9 +67,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(test)]
mod tests;
use core::clone::CloneToUninit;
use crate::borrow::{Borrow, Cow};

View file

@ -1,6 +1,3 @@
#[cfg(test)]
mod tests;
use crate::fmt;
// FIXME(nonpoison_mutex,nonpoison_condvar): switch to nonpoison versions once they are available
use crate::sync::{Condvar, Mutex};

View file

@ -350,6 +350,3 @@ unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
#[stable(feature = "lazy_cell", since = "1.80.0")]
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
#[cfg(test)]
mod tests;

View file

@ -137,12 +137,6 @@
#![stable(feature = "rust1", since = "1.0.0")]
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod sync_tests;
// MPSC channels are built as a wrapper around MPMC channels, which
// were ported from the `crossbeam-channel` crate. MPMC channels are
// not exposed publicly, but if you are curious about the implementation,
@ -737,9 +731,10 @@ impl<T> SyncSender<T> {
// Attempts to send for a value on this receiver, returning an error if the
// corresponding channel has hung up, or if it waits more than `timeout`.
//
// This method is currently private and only used for tests.
#[allow(unused)]
fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> {
// This method is currently only used for tests.
#[unstable(issue = "none", feature = "std_internals")]
#[doc(hidden)]
pub fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> {
self.inner.send_timeout(t, timeout)
}
}

View file

@ -681,6 +681,3 @@ unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
}
}
}
#[cfg(test)]
mod tests;

View file

@ -1,6 +1,3 @@
#[cfg(test)]
mod tests;
use crate::fmt;
use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex};
use crate::sys::sync as sys;

View file

@ -1,6 +1,3 @@
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::marker::PhantomData;

View file

@ -3,9 +3,6 @@
//! This primitive is meant to be used to run one-time initialization. An
//! example use case would be for initializing an FFI library.
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
use crate::fmt;
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::sys::sync as sys;

View file

@ -1,6 +1,3 @@
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::marker::PhantomData;

View file

@ -1,6 +1,3 @@
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
use cfg_if::cfg_if;
use crate::cell::UnsafeCell;
@ -324,7 +321,10 @@ impl<T: ?Sized> ReentrantLock<T> {
/// Otherwise, an RAII guard is returned.
///
/// This function does not block.
pub(crate) fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> {
// FIXME maybe make it a public part of the API?
#[unstable(issue = "none", feature = "std_internals")]
#[doc(hidden)]
pub fn try_lock(&self) -> Option<ReentrantLockGuard<'_, T>> {
let this_thread = current_id();
// Safety: We only touch lock_count when we own the inner mutex.
// Additionally, we only call `self.owner.set()` while holding

View file

@ -740,29 +740,27 @@ impl Iterator for ReadDir {
// to `byte_offset` and thus does not require the full extent of `*entry_ptr`
// to be in bounds of the same allocation, only the offset of the field
// being referenced.
macro_rules! entry_field_ptr {
($field:ident) => {
&raw const (*entry_ptr).$field
};
}
// d_name is guaranteed to be null-terminated.
let name = CStr::from_ptr(entry_field_ptr!(d_name).cast());
let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast());
let name_bytes = name.to_bytes();
if name_bytes == b"." || name_bytes == b".." {
continue;
}
// When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as
// a value expression will do the right thing: `byte_offset` to the field and then
// only access those bytes.
#[cfg(not(target_os = "vita"))]
let entry = dirent64_min {
d_ino: *entry_field_ptr!(d_ino) as u64,
d_ino: (*entry_ptr).d_ino as u64,
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
target_os = "aix",
target_os = "nto",
)))]
d_type: *entry_field_ptr!(d_type) as u8,
d_type: (*entry_ptr).d_type as u8,
};
#[cfg(target_os = "vita")]

View file

@ -2,12 +2,6 @@
#![unstable(feature = "thread_local_internals", issue = "none")]
#[cfg(all(test, not(any(target_os = "emscripten", target_os = "wasi"))))]
mod tests;
#[cfg(test)]
mod dynamic_tests;
use crate::cell::{Cell, RefCell};
use crate::error::Error;
use crate::fmt;

View file

@ -31,9 +31,6 @@
#![stable(feature = "time", since = "1.3.0")]
#[cfg(test)]
mod tests;
#[stable(feature = "time", since = "1.3.0")]
pub use core::time::Duration;
#[stable(feature = "duration_checked_float", since = "1.66.0")]

View file

@ -1,163 +1,123 @@
use std::env::*;
use std::ffi::{OsStr, OsString};
use rand::distributions::{Alphanumeric, DistString};
use std::path::Path;
mod common;
use std::thread;
use common::test_rng;
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi", target_env = "sgx"), ignore)]
fn test_self_exe_path() {
let path = current_exe();
assert!(path.is_ok());
let path = path.unwrap();
#[track_caller]
fn make_rand_name() -> OsString {
let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10));
let n = OsString::from(n);
assert!(var_os(&n).is_none());
n
}
fn eq(a: Option<OsString>, b: Option<&str>) {
assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s));
// Hard to test this function
assert!(path.is_absolute());
}
#[test]
fn test_set_var() {
let n = make_rand_name();
set_var(&n, "VALUE");
eq(var_os(&n), Some("VALUE"));
fn test() {
assert!((!Path::new("test-path").is_absolute()));
#[cfg(not(target_env = "sgx"))]
current_dir().unwrap();
}
#[test]
fn test_remove_var() {
let n = make_rand_name();
set_var(&n, "VALUE");
remove_var(&n);
eq(var_os(&n), None);
}
#[test]
fn test_set_var_overwrite() {
let n = make_rand_name();
set_var(&n, "1");
set_var(&n, "2");
eq(var_os(&n), Some("2"));
set_var(&n, "");
eq(var_os(&n), Some(""));
}
#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
fn test_var_big() {
let mut s = "".to_string();
let mut i = 0;
while i < 100 {
s.push_str("aaaaaaaaaa");
i += 1;
}
let n = make_rand_name();
set_var(&n, &s);
eq(var_os(&n), Some(&s));
}
#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
fn test_env_set_get_huge() {
let n = make_rand_name();
let s = "x".repeat(10000);
set_var(&n, &s);
eq(var_os(&n), Some(&s));
remove_var(&n);
eq(var_os(&n), None);
}
#[test]
fn test_env_set_var() {
let n = make_rand_name();
let mut e = vars_os();
set_var(&n, "VALUE");
assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" }));
assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" }));
}
#[test]
#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))]
#[allow(deprecated)]
fn env_home_dir() {
#[cfg(windows)]
fn split_paths_windows() {
use std::path::PathBuf;
fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> {
match var {
Ok(var) => Some(OsString::from(var)),
Err(VarError::NotUnicode(var)) => Some(var),
_ => None,
}
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed).collect::<Vec<_>>()
== parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
}
cfg_if::cfg_if! {
if #[cfg(unix)] {
let oldhome = var_to_os_string(var("HOME"));
assert!(check_parse("", &mut [""]));
assert!(check_parse(r#""""#, &mut [""]));
assert!(check_parse(";;", &mut ["", "", ""]));
assert!(check_parse(r"c:\", &mut [r"c:\"]));
assert!(check_parse(r"c:\;", &mut [r"c:\", ""]));
assert!(check_parse(r"c:\;c:\Program Files\", &mut [r"c:\", r"c:\Program Files\"]));
assert!(check_parse(r#"c:\;c:\"foo"\"#, &mut [r"c:\", r"c:\foo\"]));
assert!(check_parse(r#"c:\;c:\"foo;bar"\;c:\baz"#, &mut [r"c:\", r"c:\foo;bar\", r"c:\baz"]));
}
set_var("HOME", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
#[test]
#[cfg(unix)]
fn split_paths_unix() {
use std::path::PathBuf;
remove_var("HOME");
if cfg!(target_os = "android") {
assert!(home_dir().is_none());
} else {
// When HOME is not set, some platforms return `None`,
// but others return `Some` with a default.
// Just check that it is not "/home/MountainView".
assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView")));
}
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
} else if #[cfg(windows)] {
let oldhome = var_to_os_string(var("HOME"));
let olduserprofile = var_to_os_string(var("USERPROFILE"));
remove_var("HOME");
remove_var("USERPROFILE");
assert!(home_dir().is_some());
set_var("HOME", "/home/PaloAlto");
assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used");
set_var("USERPROFILE", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
remove_var("HOME");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
set_var("USERPROFILE", "");
assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored");
remove_var("USERPROFILE");
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); }
}
fn check_parse(unparsed: &str, parsed: &[&str]) -> bool {
split_paths(unparsed).collect::<Vec<_>>()
== parsed.iter().map(|s| PathBuf::from(*s)).collect::<Vec<_>>()
}
assert!(check_parse("", &mut [""]));
assert!(check_parse("::", &mut ["", "", ""]));
assert!(check_parse("/", &mut ["/"]));
assert!(check_parse("/:", &mut ["/", ""]));
assert!(check_parse("/:/usr/local", &mut ["/", "/usr/local"]));
}
#[test] // miri shouldn't detect any data race in this fn
#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)]
fn test_env_get_set_multithreaded() {
let getter = thread::spawn(|| {
for _ in 0..100 {
let _ = var_os("foo");
}
});
#[test]
#[cfg(unix)]
fn join_paths_unix() {
use std::ffi::OsStr;
let setter = thread::spawn(|| {
for _ in 0..100 {
set_var("foo", "bar");
}
});
fn test_eq(input: &[&str], output: &str) -> bool {
&*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
}
let _ = getter.join();
let _ = setter.join();
assert!(test_eq(&[], ""));
assert!(test_eq(&["/bin", "/usr/bin", "/usr/local/bin"], "/bin:/usr/bin:/usr/local/bin"));
assert!(test_eq(&["", "/bin", "", "", "/usr/bin", ""], ":/bin:::/usr/bin:"));
assert!(join_paths(["/te:st"].iter().cloned()).is_err());
}
#[test]
#[cfg(windows)]
fn join_paths_windows() {
use std::ffi::OsStr;
fn test_eq(input: &[&str], output: &str) -> bool {
&*join_paths(input.iter().cloned()).unwrap() == OsStr::new(output)
}
assert!(test_eq(&[], ""));
assert!(test_eq(&[r"c:\windows", r"c:\"], r"c:\windows;c:\"));
assert!(test_eq(&["", r"c:\windows", "", "", r"c:\", ""], r";c:\windows;;;c:\;"));
assert!(test_eq(&[r"c:\te;st", r"c:\"], r#""c:\te;st";c:\"#));
assert!(join_paths([r#"c:\te"st"#].iter().cloned()).is_err());
}
#[test]
fn args_debug() {
assert_eq!(
format!("Args {{ inner: {:?} }}", args().collect::<Vec<_>>()),
format!("{:?}", args())
);
}
#[test]
fn args_os_debug() {
assert_eq!(
format!("ArgsOs {{ inner: {:?} }}", args_os().collect::<Vec<_>>()),
format!("{:?}", args_os())
);
}
#[test]
fn vars_debug() {
assert_eq!(
format!("Vars {{ inner: {:?} }}", vars().collect::<Vec<_>>()),
format!("{:?}", vars())
);
}
#[test]
fn vars_os_debug() {
assert_eq!(
format!("VarsOs {{ inner: {:?} }}", vars_os().collect::<Vec<_>>()),
format!("{:?}", vars_os())
);
}

View file

@ -0,0 +1,166 @@
// These tests are in a separate integration test as they modify the environment,
// and would otherwise cause some other tests to fail.
use std::env::*;
use std::ffi::{OsStr, OsString};
use rand::distributions::{Alphanumeric, DistString};
mod common;
use std::thread;
use common::test_rng;
#[track_caller]
fn make_rand_name() -> OsString {
let n = format!("TEST{}", Alphanumeric.sample_string(&mut test_rng(), 10));
let n = OsString::from(n);
assert!(var_os(&n).is_none());
n
}
fn eq(a: Option<OsString>, b: Option<&str>) {
assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s));
}
#[test]
fn test_set_var() {
let n = make_rand_name();
set_var(&n, "VALUE");
eq(var_os(&n), Some("VALUE"));
}
#[test]
fn test_remove_var() {
let n = make_rand_name();
set_var(&n, "VALUE");
remove_var(&n);
eq(var_os(&n), None);
}
#[test]
fn test_set_var_overwrite() {
let n = make_rand_name();
set_var(&n, "1");
set_var(&n, "2");
eq(var_os(&n), Some("2"));
set_var(&n, "");
eq(var_os(&n), Some(""));
}
#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
fn test_var_big() {
let mut s = "".to_string();
let mut i = 0;
while i < 100 {
s.push_str("aaaaaaaaaa");
i += 1;
}
let n = make_rand_name();
set_var(&n, &s);
eq(var_os(&n), Some(&s));
}
#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
fn test_env_set_get_huge() {
let n = make_rand_name();
let s = "x".repeat(10000);
set_var(&n, &s);
eq(var_os(&n), Some(&s));
remove_var(&n);
eq(var_os(&n), None);
}
#[test]
fn test_env_set_var() {
let n = make_rand_name();
let mut e = vars_os();
set_var(&n, "VALUE");
assert!(!e.any(|(k, v)| { &*k == &*n && &*v == "VALUE" }));
assert!(vars_os().any(|(k, v)| { &*k == &*n && &*v == "VALUE" }));
}
#[test]
#[cfg_attr(not(any(unix, windows)), ignore, allow(unused))]
#[allow(deprecated)]
fn env_home_dir() {
use std::path::PathBuf;
fn var_to_os_string(var: Result<String, VarError>) -> Option<OsString> {
match var {
Ok(var) => Some(OsString::from(var)),
Err(VarError::NotUnicode(var)) => Some(var),
_ => None,
}
}
cfg_if::cfg_if! {
if #[cfg(unix)] {
let oldhome = var_to_os_string(var("HOME"));
set_var("HOME", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
remove_var("HOME");
if cfg!(target_os = "android") {
assert!(home_dir().is_none());
} else {
// When HOME is not set, some platforms return `None`,
// but others return `Some` with a default.
// Just check that it is not "/home/MountainView".
assert_ne!(home_dir(), Some(PathBuf::from("/home/MountainView")));
}
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
} else if #[cfg(windows)] {
let oldhome = var_to_os_string(var("HOME"));
let olduserprofile = var_to_os_string(var("USERPROFILE"));
remove_var("HOME");
remove_var("USERPROFILE");
assert!(home_dir().is_some());
set_var("HOME", "/home/PaloAlto");
assert_ne!(home_dir(), Some(PathBuf::from("/home/PaloAlto")), "HOME must not be used");
set_var("USERPROFILE", "/home/MountainView");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
remove_var("HOME");
assert_eq!(home_dir(), Some(PathBuf::from("/home/MountainView")));
set_var("USERPROFILE", "");
assert_ne!(home_dir(), Some(PathBuf::from("")), "Empty USERPROFILE must be ignored");
remove_var("USERPROFILE");
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
if let Some(olduserprofile) = olduserprofile { set_var("USERPROFILE", olduserprofile); }
}
}
}
#[test] // miri shouldn't detect any data race in this fn
#[cfg_attr(any(not(miri), target_os = "emscripten"), ignore)]
fn test_env_get_set_multithreaded() {
let getter = thread::spawn(|| {
for _ in 0..100 {
let _ = var_os("foo");
}
});
let setter = thread::spawn(|| {
for _ in 0..100 {
set_var("foo", "bar");
}
});
let _ = getter.join();
let _ = setter.join();
}

View file

@ -1,7 +1,8 @@
use core::error::Request;
#![feature(error_generic_member_access, error_reporter)]
use super::Error;
use crate::fmt;
use std::backtrace::Backtrace;
use std::error::{Error, Report, Request};
use std::fmt;
#[derive(Debug, PartialEq)]
struct A;
@ -38,9 +39,6 @@ fn downcasting() {
}
}
use crate::backtrace::Backtrace;
use crate::error::Report;
#[derive(Debug)]
struct SuperError {
source: SuperErrorSideKick,

View file

@ -1,11 +1,11 @@
// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
#![cfg(reliable_f128)]
use crate::f128::consts;
use crate::num::FpCategory as Fp;
use std::f128::consts;
use std::num::FpCategory as Fp;
#[cfg(reliable_f128_math)]
use crate::ops::Rem;
use crate::ops::{Add, Div, Mul, Sub};
use std::ops::Rem;
use std::ops::{Add, Div, Mul, Sub};
// Note these tolerances make sense around zero, but not for more extreme exponents.
@ -762,8 +762,6 @@ fn test_ln_gamma() {
#[test]
fn test_real_consts() {
use super::consts;
let pi: f128 = consts::PI;
let frac_pi_2: f128 = consts::FRAC_PI_2;
let frac_pi_3: f128 = consts::FRAC_PI_3;

View file

@ -1,8 +1,8 @@
// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
#![cfg(reliable_f16)]
use crate::f16::consts;
use crate::num::{FpCategory as Fp, *};
use std::f16::consts;
use std::num::FpCategory as Fp;
/// Tolerance for results on the order of 10.0e-2
#[allow(unused)]
@ -54,7 +54,7 @@ macro_rules! assert_f16_biteq {
#[test]
fn test_num_f16() {
test_num(10f16, 2f16);
crate::test_num(10f16, 2f16);
}
#[test]
@ -734,7 +734,6 @@ fn test_ln_gamma() {
#[test]
fn test_real_consts() {
// FIXME(f16_f128): add math tests when available
use super::consts;
let pi: f16 = consts::PI;
let frac_pi_2: f16 = consts::FRAC_PI_2;

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