mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
Merge from rustc
This commit is contained in:
commit
eb8e8c06b6
@ -14,7 +14,7 @@ index d0a119c..76fdece 100644
|
||||
@@ -89,7 +89,6 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
-#![feature(portable_simd)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(lazy_cell)]
|
||||
@ -27,6 +27,6 @@ index d0a119c..76fdece 100644
|
||||
mod slice;
|
||||
mod str;
|
||||
mod str_lossy;
|
||||
--
|
||||
--
|
||||
2.42.1
|
||||
|
||||
|
@ -243,6 +243,7 @@ where
|
||||
T: Tag,
|
||||
{
|
||||
#[inline]
|
||||
#[allow(ambiguous_wide_pointer_comparisons)]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.packed == other.packed
|
||||
}
|
||||
|
@ -295,6 +295,8 @@ hir_analysis_not_supported_delegation =
|
||||
{$descr} is not supported yet
|
||||
.label = callee defined here
|
||||
|
||||
hir_analysis_only_current_traits_adt = `{$name}` is not defined in the current crate
|
||||
|
||||
hir_analysis_only_current_traits_arbitrary = only traits defined in the current crate can be implemented for arbitrary types
|
||||
|
||||
hir_analysis_only_current_traits_foreign = this is not defined in the current crate because this is a foreign trait
|
||||
|
@ -4,7 +4,7 @@
|
||||
use crate::errors;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, AliasKind, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::{self, IsFirstInputType};
|
||||
@ -283,9 +283,14 @@ fn emit_orphan_check_error<'tcx>(
|
||||
let self_ty = trait_ref.self_ty();
|
||||
Err(match err {
|
||||
traits::OrphanCheckErr::NonLocalInputType(tys) => {
|
||||
let (mut opaque, mut foreign, mut name, mut pointer, mut ty_diag) =
|
||||
(Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new());
|
||||
let mut sugg = None;
|
||||
let mut diag = tcx.dcx().create_err(match self_ty.kind() {
|
||||
ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () },
|
||||
_ if self_ty.is_primitive() => {
|
||||
errors::OnlyCurrentTraits::Primitive { span: sp, note: () }
|
||||
}
|
||||
_ => errors::OnlyCurrentTraits::Arbitrary { span: sp, note: () },
|
||||
});
|
||||
|
||||
for &(mut ty, is_target_ty) in &tys {
|
||||
let span = if matches!(is_target_ty, IsFirstInputType::Yes) {
|
||||
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
|
||||
@ -296,113 +301,86 @@ fn emit_orphan_check_error<'tcx>(
|
||||
};
|
||||
|
||||
ty = tcx.erase_regions(ty);
|
||||
ty = match ty.kind() {
|
||||
// Remove the type arguments from the output, as they are not relevant.
|
||||
// You can think of this as the reverse of `resolve_vars_if_possible`.
|
||||
// That way if we had `Vec<MyType>`, we will properly attribute the
|
||||
// problem to `Vec<T>` and avoid confusing the user if they were to see
|
||||
// `MyType` in the error.
|
||||
ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()),
|
||||
_ => ty,
|
||||
};
|
||||
|
||||
fn push_to_foreign_or_name<'tcx>(
|
||||
is_foreign: bool,
|
||||
foreign: &mut Vec<errors::OnlyCurrentTraitsForeign>,
|
||||
name: &mut Vec<errors::OnlyCurrentTraitsName<'tcx>>,
|
||||
span: Span,
|
||||
sname: &'tcx str,
|
||||
) {
|
||||
if is_foreign {
|
||||
foreign.push(errors::OnlyCurrentTraitsForeign { span })
|
||||
} else {
|
||||
name.push(errors::OnlyCurrentTraitsName { span, name: sname });
|
||||
}
|
||||
}
|
||||
|
||||
let is_foreign =
|
||||
!trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No);
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Slice(_) => {
|
||||
push_to_foreign_or_name(
|
||||
is_foreign,
|
||||
&mut foreign,
|
||||
&mut name,
|
||||
span,
|
||||
"slices",
|
||||
);
|
||||
if is_foreign {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsForeign { span },
|
||||
);
|
||||
} else {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsName { span, name: "slices" },
|
||||
);
|
||||
}
|
||||
}
|
||||
ty::Array(..) => {
|
||||
push_to_foreign_or_name(
|
||||
is_foreign,
|
||||
&mut foreign,
|
||||
&mut name,
|
||||
span,
|
||||
"arrays",
|
||||
);
|
||||
if is_foreign {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsForeign { span },
|
||||
);
|
||||
} else {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsName { span, name: "arrays" },
|
||||
);
|
||||
}
|
||||
}
|
||||
ty::Tuple(..) => {
|
||||
push_to_foreign_or_name(
|
||||
is_foreign,
|
||||
&mut foreign,
|
||||
&mut name,
|
||||
span,
|
||||
"tuples",
|
||||
);
|
||||
if is_foreign {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsForeign { span },
|
||||
);
|
||||
} else {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsName { span, name: "tuples" },
|
||||
);
|
||||
}
|
||||
}
|
||||
ty::Alias(ty::Opaque, ..) => {
|
||||
opaque.push(errors::OnlyCurrentTraitsOpaque { span })
|
||||
diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
|
||||
}
|
||||
ty::RawPtr(ptr_ty, mutbl) => {
|
||||
if !self_ty.has_param() {
|
||||
let mut_key = mutbl.prefix_str();
|
||||
sugg = Some(errors::OnlyCurrentTraitsPointerSugg {
|
||||
wrapper_span: self_ty_span,
|
||||
struct_span: full_impl_span.shrink_to_lo(),
|
||||
mut_key,
|
||||
ptr_ty,
|
||||
});
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsPointerSugg {
|
||||
wrapper_span: self_ty_span,
|
||||
struct_span: full_impl_span.shrink_to_lo(),
|
||||
mut_key: mutbl.prefix_str(),
|
||||
ptr_ty,
|
||||
},
|
||||
);
|
||||
}
|
||||
pointer.push(errors::OnlyCurrentTraitsPointer { span, pointer: ty });
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsPointer { span, pointer: ty },
|
||||
);
|
||||
}
|
||||
ty::Adt(adt_def, _) => {
|
||||
diag.subdiagnostic(
|
||||
tcx.dcx(),
|
||||
errors::OnlyCurrentTraitsAdt {
|
||||
span,
|
||||
name: tcx.def_path_str(adt_def.did()),
|
||||
},
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty });
|
||||
}
|
||||
_ => ty_diag.push(errors::OnlyCurrentTraitsTy { span, ty }),
|
||||
}
|
||||
}
|
||||
|
||||
let err_struct = match self_ty.kind() {
|
||||
ty::Adt(..) => errors::OnlyCurrentTraits::Outside {
|
||||
span: sp,
|
||||
note: (),
|
||||
opaque,
|
||||
foreign,
|
||||
name,
|
||||
pointer,
|
||||
ty: ty_diag,
|
||||
sugg,
|
||||
},
|
||||
_ if self_ty.is_primitive() => errors::OnlyCurrentTraits::Primitive {
|
||||
span: sp,
|
||||
note: (),
|
||||
opaque,
|
||||
foreign,
|
||||
name,
|
||||
pointer,
|
||||
ty: ty_diag,
|
||||
sugg,
|
||||
},
|
||||
_ => errors::OnlyCurrentTraits::Arbitrary {
|
||||
span: sp,
|
||||
note: (),
|
||||
opaque,
|
||||
foreign,
|
||||
name,
|
||||
pointer,
|
||||
ty: ty_diag,
|
||||
sugg,
|
||||
},
|
||||
};
|
||||
tcx.dcx().emit_err(err_struct)
|
||||
diag.emit()
|
||||
}
|
||||
traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => {
|
||||
let mut sp = sp;
|
||||
|
@ -1376,7 +1376,7 @@ pub struct TyParamSome<'a> {
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum OnlyCurrentTraits<'a> {
|
||||
pub enum OnlyCurrentTraits {
|
||||
#[diag(hir_analysis_only_current_traits_outside, code = E0117)]
|
||||
Outside {
|
||||
#[primary_span]
|
||||
@ -1384,18 +1384,6 @@ pub enum OnlyCurrentTraits<'a> {
|
||||
span: Span,
|
||||
#[note(hir_analysis_only_current_traits_note)]
|
||||
note: (),
|
||||
#[subdiagnostic]
|
||||
opaque: Vec<OnlyCurrentTraitsOpaque>,
|
||||
#[subdiagnostic]
|
||||
foreign: Vec<OnlyCurrentTraitsForeign>,
|
||||
#[subdiagnostic]
|
||||
name: Vec<OnlyCurrentTraitsName<'a>>,
|
||||
#[subdiagnostic]
|
||||
pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
|
||||
#[subdiagnostic]
|
||||
ty: Vec<OnlyCurrentTraitsTy<'a>>,
|
||||
#[subdiagnostic]
|
||||
sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
|
||||
},
|
||||
#[diag(hir_analysis_only_current_traits_primitive, code = E0117)]
|
||||
Primitive {
|
||||
@ -1404,18 +1392,6 @@ pub enum OnlyCurrentTraits<'a> {
|
||||
span: Span,
|
||||
#[note(hir_analysis_only_current_traits_note)]
|
||||
note: (),
|
||||
#[subdiagnostic]
|
||||
opaque: Vec<OnlyCurrentTraitsOpaque>,
|
||||
#[subdiagnostic]
|
||||
foreign: Vec<OnlyCurrentTraitsForeign>,
|
||||
#[subdiagnostic]
|
||||
name: Vec<OnlyCurrentTraitsName<'a>>,
|
||||
#[subdiagnostic]
|
||||
pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
|
||||
#[subdiagnostic]
|
||||
ty: Vec<OnlyCurrentTraitsTy<'a>>,
|
||||
#[subdiagnostic]
|
||||
sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
|
||||
},
|
||||
#[diag(hir_analysis_only_current_traits_arbitrary, code = E0117)]
|
||||
Arbitrary {
|
||||
@ -1424,18 +1400,6 @@ pub enum OnlyCurrentTraits<'a> {
|
||||
span: Span,
|
||||
#[note(hir_analysis_only_current_traits_note)]
|
||||
note: (),
|
||||
#[subdiagnostic]
|
||||
opaque: Vec<OnlyCurrentTraitsOpaque>,
|
||||
#[subdiagnostic]
|
||||
foreign: Vec<OnlyCurrentTraitsForeign>,
|
||||
#[subdiagnostic]
|
||||
name: Vec<OnlyCurrentTraitsName<'a>>,
|
||||
#[subdiagnostic]
|
||||
pointer: Vec<OnlyCurrentTraitsPointer<'a>>,
|
||||
#[subdiagnostic]
|
||||
ty: Vec<OnlyCurrentTraitsTy<'a>>,
|
||||
#[subdiagnostic]
|
||||
sugg: Option<OnlyCurrentTraitsPointerSugg<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -1445,7 +1409,6 @@ pub struct OnlyCurrentTraitsOpaque {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[label(hir_analysis_only_current_traits_foreign)]
|
||||
pub struct OnlyCurrentTraitsForeign {
|
||||
@ -1477,6 +1440,14 @@ pub struct OnlyCurrentTraitsTy<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[label(hir_analysis_only_current_traits_adt)]
|
||||
pub struct OnlyCurrentTraitsAdt {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(
|
||||
hir_analysis_only_current_traits_pointer_sugg,
|
||||
|
@ -1916,18 +1916,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pat: &'tcx hir::Pat<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) {
|
||||
struct V<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
struct V {
|
||||
pat_hir_ids: Vec<hir::HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for V<'tcx> {
|
||||
type NestedFilter = rustc_middle::hir::nested_filter::All;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for V {
|
||||
fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
|
||||
self.pat_hir_ids.push(p.hir_id);
|
||||
hir::intravisit::walk_pat(self, p);
|
||||
@ -1938,7 +1931,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let err = Ty::new_error(self.tcx, guar);
|
||||
self.write_ty(hir_id, err);
|
||||
self.write_ty(pat.hir_id, err);
|
||||
let mut visitor = V { tcx: self.tcx, pat_hir_ids: vec![] };
|
||||
let mut visitor = V { pat_hir_ids: vec![] };
|
||||
hir::intravisit::walk_pat(&mut visitor, pat);
|
||||
// Mark all the subpatterns as `{type error}` as well. This allows errors for specific
|
||||
// subpatterns to be silenced.
|
||||
|
@ -1632,11 +1632,13 @@ pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> {
|
||||
pub ne: &'a str,
|
||||
pub deref_left: &'a str,
|
||||
pub deref_right: &'a str,
|
||||
pub l_modifiers: &'a str,
|
||||
pub r_modifiers: &'a str,
|
||||
#[suggestion_part(code = "{ne}std::ptr::eq({deref_left}")]
|
||||
pub left: Span,
|
||||
#[suggestion_part(code = ", {deref_right}")]
|
||||
#[suggestion_part(code = "{l_modifiers}, {deref_right}")]
|
||||
pub middle: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
#[suggestion_part(code = "{r_modifiers})")]
|
||||
pub right: Span,
|
||||
}
|
||||
|
||||
@ -1652,11 +1654,13 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
|
||||
ne: &'a str,
|
||||
deref_left: &'a str,
|
||||
deref_right: &'a str,
|
||||
l_modifiers: &'a str,
|
||||
r_modifiers: &'a str,
|
||||
#[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")]
|
||||
left: Span,
|
||||
#[suggestion_part(code = ", {deref_right}")]
|
||||
#[suggestion_part(code = "{l_modifiers}, {deref_right}")]
|
||||
middle: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
#[suggestion_part(code = "{r_modifiers})")]
|
||||
right: Span,
|
||||
},
|
||||
#[multipart_suggestion(
|
||||
@ -1670,13 +1674,15 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
|
||||
deref_right: &'a str,
|
||||
paren_left: &'a str,
|
||||
paren_right: &'a str,
|
||||
l_modifiers: &'a str,
|
||||
r_modifiers: &'a str,
|
||||
#[suggestion_part(code = "({deref_left}")]
|
||||
left_before: Option<Span>,
|
||||
#[suggestion_part(code = "{paren_left}.cast::<()>()")]
|
||||
#[suggestion_part(code = "{l_modifiers}{paren_left}.cast::<()>()")]
|
||||
left_after: Span,
|
||||
#[suggestion_part(code = "({deref_right}")]
|
||||
right_before: Option<Span>,
|
||||
#[suggestion_part(code = "{paren_right}.cast::<()>()")]
|
||||
#[suggestion_part(code = "{r_modifiers}{paren_right}.cast::<()>()")]
|
||||
right_after: Span,
|
||||
},
|
||||
}
|
||||
|
@ -670,7 +670,11 @@ fn lint_wide_pointer<'tcx>(
|
||||
l: &'tcx hir::Expr<'tcx>,
|
||||
r: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(usize, bool)> {
|
||||
let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
|
||||
/* number of refs */ usize,
|
||||
/* modifiers */ String,
|
||||
/* is dyn */ bool,
|
||||
)> {
|
||||
let mut refs = 0;
|
||||
// here we remove any "implicit" references and count the number
|
||||
// of them to correctly suggest the right number of deref
|
||||
@ -678,11 +682,20 @@ fn lint_wide_pointer<'tcx>(
|
||||
ty = *inner_ty;
|
||||
refs += 1;
|
||||
}
|
||||
match ty.kind() {
|
||||
ty::RawPtr(ty, _) => (!ty.is_sized(cx.tcx, cx.param_env))
|
||||
.then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))),
|
||||
_ => None,
|
||||
}
|
||||
|
||||
// get the inner type of a pointer (or akin)
|
||||
let mut modifiers = String::new();
|
||||
ty = match ty.kind() {
|
||||
ty::RawPtr(ty, _) => *ty,
|
||||
ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => {
|
||||
modifiers.push_str(".as_ptr()");
|
||||
args.type_at(0)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
(!ty.is_sized(cx.tcx, cx.param_env))
|
||||
.then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
|
||||
};
|
||||
|
||||
// the left and right operands can have references, remove any explicit references
|
||||
@ -696,10 +709,10 @@ fn lint_wide_pointer<'tcx>(
|
||||
return;
|
||||
};
|
||||
|
||||
let Some((l_ty_refs, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
|
||||
let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
|
||||
return;
|
||||
};
|
||||
let Some((r_ty_refs, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
|
||||
let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@ -724,6 +737,9 @@ fn lint_wide_pointer<'tcx>(
|
||||
let deref_left = &*"*".repeat(l_ty_refs);
|
||||
let deref_right = &*"*".repeat(r_ty_refs);
|
||||
|
||||
let l_modifiers = &*l_modifiers;
|
||||
let r_modifiers = &*r_modifiers;
|
||||
|
||||
cx.emit_span_lint(
|
||||
AMBIGUOUS_WIDE_POINTER_COMPARISONS,
|
||||
e.span,
|
||||
@ -733,6 +749,8 @@ fn lint_wide_pointer<'tcx>(
|
||||
ne,
|
||||
deref_left,
|
||||
deref_right,
|
||||
l_modifiers,
|
||||
r_modifiers,
|
||||
left,
|
||||
middle,
|
||||
right,
|
||||
@ -743,6 +761,8 @@ fn lint_wide_pointer<'tcx>(
|
||||
ne,
|
||||
deref_left,
|
||||
deref_right,
|
||||
l_modifiers,
|
||||
r_modifiers,
|
||||
left,
|
||||
middle,
|
||||
right,
|
||||
@ -751,6 +771,8 @@ fn lint_wide_pointer<'tcx>(
|
||||
AmbiguousWidePointerComparisonsAddrSuggestion::Cast {
|
||||
deref_left,
|
||||
deref_right,
|
||||
l_modifiers,
|
||||
r_modifiers,
|
||||
paren_left: if l_ty_refs != 0 { ")" } else { "" },
|
||||
paren_right: if r_ty_refs != 0 { ")" } else { "" },
|
||||
left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
|
||||
|
@ -140,6 +140,10 @@ impl<'tcx> rustc_type_ir::new::Region<TyCtxt<'tcx>> for Region<'tcx> {
|
||||
fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
|
||||
Region::new_bound(tcx, debruijn, ty::BoundRegion { var, kind: ty::BoundRegionKind::BrAnon })
|
||||
}
|
||||
|
||||
fn new_static(tcx: TyCtxt<'tcx>) -> Self {
|
||||
tcx.lifetimes.re_static
|
||||
}
|
||||
}
|
||||
|
||||
/// Region utilities
|
||||
|
@ -1624,6 +1624,13 @@ impl<'tcx> Ty<'tcx> {
|
||||
|
||||
#[inline]
|
||||
pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
|
||||
debug_assert_eq!(
|
||||
tcx.generics_of(def.did()).count(),
|
||||
args.len(),
|
||||
"wrong number of args for ADT: {:#?} vs {:#?}",
|
||||
tcx.generics_of(def.did()).params,
|
||||
args
|
||||
);
|
||||
Ty::new(tcx, Adt(def, args))
|
||||
}
|
||||
|
||||
|
@ -650,12 +650,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#29623): return `Some(1)` when the values are different.
|
||||
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val })
|
||||
if test_val == case_val =>
|
||||
{
|
||||
fully_matched = true;
|
||||
Some(TestBranch::Success)
|
||||
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
|
||||
if test_val == case_val {
|
||||
fully_matched = true;
|
||||
Some(TestBranch::Success)
|
||||
} else {
|
||||
fully_matched = false;
|
||||
Some(TestBranch::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
|
@ -296,10 +296,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
|
||||
Region::new_anon_bound(self.interner(), self.binder_index, var)
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, t: I::Ty) -> I::Ty
|
||||
where
|
||||
I::Ty: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
|
||||
let kind = match t.kind() {
|
||||
ty::Infer(i) => match i {
|
||||
ty::TyVar(vid) => {
|
||||
@ -378,47 +375,48 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
|
||||
Ty::new_anon_bound(self.interner(), self.binder_index, var)
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: I::Const) -> I::Const
|
||||
where
|
||||
I::Const: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn fold_const(&mut self, c: I::Const) -> I::Const {
|
||||
// We could canonicalize all consts with static types, but the only ones we
|
||||
// *really* need to worry about are the ones that we end up putting into `CanonicalVarKind`
|
||||
// since canonical vars can't reference other canonical vars.
|
||||
let ty = c
|
||||
.ty()
|
||||
.fold_with(&mut RegionsToStatic { interner: self.interner(), binder: ty::INNERMOST });
|
||||
let kind = match c.kind() {
|
||||
ty::ConstKind::Infer(i) => {
|
||||
// FIXME: we should fold the ty too eventually
|
||||
match i {
|
||||
ty::InferConst::Var(vid) => {
|
||||
assert_eq!(
|
||||
self.infcx.root_ct_var(vid),
|
||||
vid,
|
||||
"region vid should have been resolved fully before canonicalization"
|
||||
);
|
||||
assert_eq!(
|
||||
self.infcx.probe_ct_var(vid),
|
||||
None,
|
||||
"region vid should have been resolved fully before canonicalization"
|
||||
);
|
||||
CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), c.ty())
|
||||
}
|
||||
ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect,
|
||||
ty::InferConst::Fresh(_) => todo!(),
|
||||
ty::ConstKind::Infer(i) => match i {
|
||||
ty::InferConst::Var(vid) => {
|
||||
assert_eq!(
|
||||
self.infcx.root_ct_var(vid),
|
||||
vid,
|
||||
"region vid should have been resolved fully before canonicalization"
|
||||
);
|
||||
assert_eq!(
|
||||
self.infcx.probe_ct_var(vid),
|
||||
None,
|
||||
"region vid should have been resolved fully before canonicalization"
|
||||
);
|
||||
CanonicalVarKind::Const(self.infcx.universe_of_ct(vid).unwrap(), ty)
|
||||
}
|
||||
}
|
||||
ty::InferConst::EffectVar(_) => CanonicalVarKind::Effect,
|
||||
ty::InferConst::Fresh(_) => todo!(),
|
||||
},
|
||||
ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
|
||||
CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst(
|
||||
PlaceholderLike::new(placeholder.universe(), self.variables.len().into()),
|
||||
c.ty(),
|
||||
ty,
|
||||
),
|
||||
CanonicalizeMode::Response { .. } => {
|
||||
CanonicalVarKind::PlaceholderConst(placeholder, c.ty())
|
||||
CanonicalVarKind::PlaceholderConst(placeholder, ty)
|
||||
}
|
||||
},
|
||||
ty::ConstKind::Param(_) => match self.canonicalize_mode {
|
||||
CanonicalizeMode::Input => CanonicalVarKind::PlaceholderConst(
|
||||
PlaceholderLike::new(ty::UniverseIndex::ROOT, self.variables.len().into()),
|
||||
c.ty(),
|
||||
ty,
|
||||
),
|
||||
CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
|
||||
},
|
||||
// FIXME: See comment above -- we could fold the region separately or something.
|
||||
ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Unevaluated(_)
|
||||
| ty::ConstKind::Value(_)
|
||||
@ -435,6 +433,35 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
|
||||
}),
|
||||
);
|
||||
|
||||
Const::new_anon_bound(self.interner(), self.binder_index, var, c.ty())
|
||||
Const::new_anon_bound(self.interner(), self.binder_index, var, ty)
|
||||
}
|
||||
}
|
||||
|
||||
struct RegionsToStatic<I> {
|
||||
interner: I,
|
||||
binder: ty::DebruijnIndex,
|
||||
}
|
||||
|
||||
impl<I: Interner> TypeFolder<I> for RegionsToStatic<I> {
|
||||
fn interner(&self) -> I {
|
||||
self.interner
|
||||
}
|
||||
|
||||
fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T>
|
||||
where
|
||||
T: TypeFoldable<I>,
|
||||
I::Binder<T>: TypeSuperFoldable<I>,
|
||||
{
|
||||
self.binder.shift_in(1);
|
||||
let t = t.fold_with(self);
|
||||
self.binder.shift_out(1);
|
||||
t
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: I::Region) -> I::Region {
|
||||
match r.kind() {
|
||||
ty::ReBound(db, _) if self.binder > db => r,
|
||||
_ => Region::new_static(self.interner()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Cli
|
||||
|
||||
session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`
|
||||
|
||||
session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort`
|
||||
|
||||
session_sanitizer_not_supported = {$us} sanitizer is not supported for this target
|
||||
|
||||
session_sanitizers_not_supported = {$us} sanitizers are not supported for this target
|
||||
|
@ -145,6 +145,10 @@ pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi;
|
||||
#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)]
|
||||
pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_sanitizer_kcfi_requires_panic_abort)]
|
||||
pub(crate) struct SanitizerKcfiRequiresPanicAbort;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(session_split_lto_unit_requires_lto)]
|
||||
pub(crate) struct SplitLtoUnitRequiresLto;
|
||||
|
@ -1211,6 +1211,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
||||
sess.dcx().emit_err(errors::SanitizerCfiRequiresLto);
|
||||
}
|
||||
|
||||
// KCFI requires panic=abort
|
||||
if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort {
|
||||
sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort);
|
||||
}
|
||||
|
||||
// LLVM CFI using rustc LTO requires a single codegen unit.
|
||||
if sess.is_sanitizer_cfi_enabled()
|
||||
&& sess.lto() == config::Lto::Fat
|
||||
|
@ -10,6 +10,7 @@
|
||||
use rustc_data_structures::base_n;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{
|
||||
@ -641,9 +642,7 @@ fn encode_ty<'tcx>(
|
||||
}
|
||||
|
||||
// Function types
|
||||
ty::FnDef(def_id, args)
|
||||
| ty::Closure(def_id, args)
|
||||
| ty::CoroutineClosure(def_id, args) => {
|
||||
ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
let mut s = String::new();
|
||||
@ -654,6 +653,18 @@ fn encode_ty<'tcx>(
|
||||
typeid.push_str(&s);
|
||||
}
|
||||
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
let mut s = String::new();
|
||||
let name = encode_ty_name(tcx, *def_id);
|
||||
let _ = write!(s, "u{}{}", name.len(), &name);
|
||||
let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
|
||||
s.push_str(&encode_args(tcx, parent_args, dict, options));
|
||||
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
|
||||
typeid.push_str(&s);
|
||||
}
|
||||
|
||||
ty::Coroutine(def_id, args, ..) => {
|
||||
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
|
||||
// as vendor extended type.
|
||||
@ -1140,45 +1151,102 @@ pub fn typeid_for_instance<'tcx>(
|
||||
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
|
||||
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
|
||||
instance.args = tcx.mk_args_trait(self_ty, List::empty());
|
||||
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
|
||||
instance.args = strip_receiver_auto(tcx, instance.args);
|
||||
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
|
||||
let upcast_ty = match tcx.trait_of_item(def_id) {
|
||||
Some(trait_id) => trait_object_ty(
|
||||
tcx,
|
||||
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
|
||||
),
|
||||
// drop_in_place won't have a defining trait, skip the upcast
|
||||
None => instance.args.type_at(0),
|
||||
};
|
||||
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
|
||||
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
|
||||
} else if let ty::InstanceDef::VTableShim(def_id) = instance.def
|
||||
&& let Some(trait_id) = tcx.trait_of_item(def_id)
|
||||
{
|
||||
// VTableShims may have a trait method, but a concrete Self. This is not suitable for a vtable,
|
||||
// as the caller will not know the concrete Self.
|
||||
let trait_ref = ty::TraitRef::new(tcx, trait_id, instance.args);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
}
|
||||
|
||||
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE)
|
||||
&& let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
|
||||
{
|
||||
let impl_method = tcx.associated_item(instance.def_id());
|
||||
let method_id = impl_method
|
||||
.trait_item_def_id
|
||||
.expect("Part of a trait implementation, but not linked to the def_id?");
|
||||
let trait_method = tcx.associated_item(method_id);
|
||||
let trait_id = trait_ref.skip_binder().def_id;
|
||||
if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
|
||||
&& tcx.object_safety_violations(trait_id).is_empty()
|
||||
if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE) {
|
||||
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
&& let Some(trait_ref) = tcx.impl_trait_ref(impl_id)
|
||||
{
|
||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
||||
// implementatation will not. We need to walk back to the more general trait method
|
||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||
instance.args,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
trait_ref,
|
||||
);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
let impl_method = tcx.associated_item(instance.def_id());
|
||||
let method_id = impl_method
|
||||
.trait_item_def_id
|
||||
.expect("Part of a trait implementation, but not linked to the def_id?");
|
||||
let trait_method = tcx.associated_item(method_id);
|
||||
let trait_id = trait_ref.skip_binder().def_id;
|
||||
if traits::is_vtable_safe_method(tcx, trait_id, trait_method)
|
||||
&& tcx.object_safety_violations(trait_id).is_empty()
|
||||
{
|
||||
// Trait methods will have a Self polymorphic parameter, where the concreteized
|
||||
// implementatation will not. We need to walk back to the more general trait method
|
||||
let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
|
||||
instance.args,
|
||||
ty::ParamEnv::reveal_all(),
|
||||
trait_ref,
|
||||
);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
|
||||
// At the call site, any call to this concrete function through a vtable will be
|
||||
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
|
||||
// original method id, and we've recovered the trait arguments, we can make the callee
|
||||
// instance we're computing the alias set for match the caller instance.
|
||||
//
|
||||
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
|
||||
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
|
||||
// based on which vtables we are putting this method into, as there will be more than one
|
||||
// index value when supertraits are involved.
|
||||
instance.def = ty::InstanceDef::Virtual(method_id, 0);
|
||||
let abstract_trait_args =
|
||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
|
||||
// At the call site, any call to this concrete function through a vtable will be
|
||||
// `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
|
||||
// original method id, and we've recovered the trait arguments, we can make the callee
|
||||
// instance we're computing the alias set for match the caller instance.
|
||||
//
|
||||
// Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
|
||||
// If we ever *do* start encoding the vtable index, we will need to generate an alias set
|
||||
// based on which vtables we are putting this method into, as there will be more than one
|
||||
// index value when supertraits are involved.
|
||||
instance.def = ty::InstanceDef::Virtual(method_id, 0);
|
||||
let abstract_trait_args =
|
||||
tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
instance.args = instance.args.rebase_onto(tcx, impl_id, abstract_trait_args);
|
||||
}
|
||||
} else if tcx.is_closure_like(instance.def_id()) {
|
||||
// We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
|
||||
// instantiate it, and take the type of its only method as our own.
|
||||
let closure_ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
|
||||
let (trait_id, inputs) = match closure_ty.kind() {
|
||||
ty::Closure(..) => {
|
||||
let closure_args = instance.args.as_closure();
|
||||
let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
|
||||
let tuple_args =
|
||||
tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
|
||||
(trait_id, tuple_args)
|
||||
}
|
||||
ty::Coroutine(..) => (
|
||||
tcx.require_lang_item(LangItem::Coroutine, None),
|
||||
instance.args.as_coroutine().resume_ty(),
|
||||
),
|
||||
ty::CoroutineClosure(..) => (
|
||||
tcx.require_lang_item(LangItem::FnOnce, None),
|
||||
tcx.instantiate_bound_regions_with_erased(
|
||||
instance.args.as_coroutine_closure().coroutine_closure_sig(),
|
||||
)
|
||||
.tupled_inputs_ty,
|
||||
),
|
||||
x => bug!("Unexpected type kind for closure-like: {x:?}"),
|
||||
};
|
||||
let trait_ref = ty::TraitRef::new(tcx, trait_id, [closure_ty, inputs]);
|
||||
let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
|
||||
let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
|
||||
// There should be exactly one method on this trait, and it should be the one we're
|
||||
// defining.
|
||||
let call = tcx
|
||||
.associated_items(trait_id)
|
||||
.in_definition_order()
|
||||
.find(|it| it.kind == ty::AssocKind::Fn)
|
||||
.expect("No call-family function on closure-like Fn trait?")
|
||||
.def_id;
|
||||
|
||||
instance.def = ty::InstanceDef::Virtual(call, 0);
|
||||
instance.args = abstract_args;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1191,15 +1259,11 @@ pub fn typeid_for_instance<'tcx>(
|
||||
typeid_for_fnabi(tcx, fn_abi, options)
|
||||
}
|
||||
|
||||
fn strip_receiver_auto<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
) -> ty::GenericArgsRef<'tcx> {
|
||||
let ty = args.type_at(0);
|
||||
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
|
||||
bug!("Tried to strip auto traits from non-dynamic type {ty}");
|
||||
};
|
||||
let new_rcvr = if preds.principal().is_some() {
|
||||
if preds.principal().is_some() {
|
||||
let filtered_preds =
|
||||
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
|
||||
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
|
||||
@ -1210,8 +1274,7 @@ fn strip_receiver_auto<'tcx>(
|
||||
// about it. This technically discards the knowledge that it was a type that was made
|
||||
// into a trait object at some point, but that's not a lot.
|
||||
tcx.types.unit
|
||||
};
|
||||
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), ret)]
|
||||
|
@ -136,31 +136,21 @@ pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Never> {
|
||||
t.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, t: I::Ty) -> I::Ty
|
||||
where
|
||||
I::Ty: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
|
||||
t.super_fold_with(self)
|
||||
}
|
||||
|
||||
// The default region folder is a no-op because `Region` is non-recursive
|
||||
// and has no `super_fold_with` method to call. That also explains the
|
||||
// lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
|
||||
// and has no `super_fold_with` method to call.
|
||||
fn fold_region(&mut self, r: I::Region) -> I::Region {
|
||||
r
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: I::Const) -> I::Const
|
||||
where
|
||||
I::Const: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn fold_const(&mut self, c: I::Const) -> I::Const {
|
||||
c.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate
|
||||
where
|
||||
I::Predicate: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate {
|
||||
p.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
@ -185,31 +175,21 @@ pub trait FallibleTypeFolder<I: Interner>: Sized {
|
||||
t.try_super_fold_with(self)
|
||||
}
|
||||
|
||||
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error>
|
||||
where
|
||||
I::Ty: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> {
|
||||
t.try_super_fold_with(self)
|
||||
}
|
||||
|
||||
// The default region folder is a no-op because `Region` is non-recursive
|
||||
// and has no `super_fold_with` method to call. That also explains the
|
||||
// lack of `I::Region: TypeSuperFoldable<I>` bound on this method.
|
||||
// and has no `super_fold_with` method to call.
|
||||
fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error>
|
||||
where
|
||||
I::Const: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> {
|
||||
c.try_super_fold_with(self)
|
||||
}
|
||||
|
||||
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error>
|
||||
where
|
||||
I::Predicate: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
|
||||
p.try_super_fold_with(self)
|
||||
}
|
||||
}
|
||||
@ -234,10 +214,7 @@ where
|
||||
Ok(self.fold_binder(t))
|
||||
}
|
||||
|
||||
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Never>
|
||||
where
|
||||
I::Ty: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Never> {
|
||||
Ok(self.fold_ty(t))
|
||||
}
|
||||
|
||||
@ -245,17 +222,11 @@ where
|
||||
Ok(self.fold_region(r))
|
||||
}
|
||||
|
||||
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Never>
|
||||
where
|
||||
I::Const: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Never> {
|
||||
Ok(self.fold_const(c))
|
||||
}
|
||||
|
||||
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Never>
|
||||
where
|
||||
I::Predicate: TypeSuperFoldable<I>,
|
||||
{
|
||||
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Never> {
|
||||
Ok(self.fold_predicate(p))
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ use smallvec::SmallVec;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::fold::TypeSuperFoldable;
|
||||
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
|
||||
use crate::{
|
||||
new, BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebugWithInfcx, RegionKind, TyKind,
|
||||
UniverseIndex,
|
||||
};
|
||||
|
||||
pub trait Interner: Sized {
|
||||
pub trait Interner: Sized + Copy {
|
||||
type DefId: Copy + Debug + Hash + Eq;
|
||||
type AdtDef: Copy + Debug + Hash + Eq;
|
||||
|
||||
@ -34,6 +35,7 @@ pub trait Interner: Sized {
|
||||
+ Into<Self::GenericArg>
|
||||
+ IntoKind<Kind = TyKind<Self>>
|
||||
+ TypeSuperVisitable<Self>
|
||||
+ TypeSuperFoldable<Self>
|
||||
+ Flags
|
||||
+ new::Ty<Self>;
|
||||
type Tys: Copy + Debug + Hash + Eq + IntoIterator<Item = Self::Ty>;
|
||||
@ -57,6 +59,7 @@ pub trait Interner: Sized {
|
||||
+ IntoKind<Kind = ConstKind<Self>>
|
||||
+ ConstTy<Self>
|
||||
+ TypeSuperVisitable<Self>
|
||||
+ TypeSuperFoldable<Self>
|
||||
+ Flags
|
||||
+ new::Const<Self>;
|
||||
type AliasConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
|
||||
@ -82,7 +85,13 @@ pub trait Interner: Sized {
|
||||
type PlaceholderRegion: Copy + Debug + Hash + Eq + PlaceholderLike;
|
||||
|
||||
// Predicates
|
||||
type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
|
||||
type Predicate: Copy
|
||||
+ Debug
|
||||
+ Hash
|
||||
+ Eq
|
||||
+ TypeSuperVisitable<Self>
|
||||
+ TypeSuperFoldable<Self>
|
||||
+ Flags;
|
||||
type TraitPredicate: Copy + Debug + Hash + Eq;
|
||||
type RegionOutlivesPredicate: Copy + Debug + Hash + Eq;
|
||||
type TypeOutlivesPredicate: Copy + Debug + Hash + Eq;
|
||||
|
@ -6,6 +6,8 @@ pub trait Ty<I: Interner<Ty = Self>> {
|
||||
|
||||
pub trait Region<I: Interner<Region = Self>> {
|
||||
fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self;
|
||||
|
||||
fn new_static(interner: I) -> Self;
|
||||
}
|
||||
|
||||
pub trait Const<I: Interner<Const = Self>> {
|
||||
|
@ -198,7 +198,6 @@
|
||||
#![feature(multiple_supertrait_upcastable)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(rustc_allow_const_fn_unstable)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(slice_internals)]
|
||||
|
@ -37,7 +37,7 @@
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_str_from_utf8)]
|
||||
#![feature(panic_update_hook)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(slice_flatten)]
|
||||
#![feature(thin_box)]
|
||||
#![feature(strict_provenance)]
|
||||
|
@ -40,10 +40,10 @@
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! Consider a situation where we want to log out a value passed to a function.
|
||||
//! We know the value we're working on implements Debug, but we don't know its
|
||||
//! Consider a situation where we want to log a value passed to a function.
|
||||
//! We know the value we're working on implements `Debug`, but we don't know its
|
||||
//! concrete type. We want to give special treatment to certain types: in this
|
||||
//! case printing out the length of String values prior to their value.
|
||||
//! case printing out the length of `String` values prior to their value.
|
||||
//! We don't know the concrete type of our value at compile time, so we need to
|
||||
//! use runtime reflection instead.
|
||||
//!
|
||||
@ -51,7 +51,7 @@
|
||||
//! use std::fmt::Debug;
|
||||
//! use std::any::Any;
|
||||
//!
|
||||
//! // Logger function for any type that implements Debug.
|
||||
//! // Logger function for any type that implements `Debug`.
|
||||
//! fn log<T: Any + Debug>(value: &T) {
|
||||
//! let value_any = value as &dyn Any;
|
||||
//!
|
||||
|
@ -227,7 +227,7 @@ mod impls {
|
||||
impl_clone! {
|
||||
usize u8 u16 u32 u64 u128
|
||||
isize i8 i16 i32 i64 i128
|
||||
f32 f64
|
||||
f16 f32 f64 f128
|
||||
bool char
|
||||
}
|
||||
|
||||
|
@ -1493,7 +1493,7 @@ mod impls {
|
||||
}
|
||||
|
||||
partial_eq_impl! {
|
||||
bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64
|
||||
bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128
|
||||
}
|
||||
|
||||
macro_rules! eq_impl {
|
||||
@ -1546,7 +1546,7 @@ mod impls {
|
||||
}
|
||||
}
|
||||
|
||||
partial_ord_impl! { f32 f64 }
|
||||
partial_ord_impl! { f16 f32 f64 f128 }
|
||||
|
||||
macro_rules! ord_impl {
|
||||
($($t:ty)*) => ($(
|
||||
|
@ -178,5 +178,9 @@ default_impl! { i32, 0, "Returns the default value of `0`" }
|
||||
default_impl! { i64, 0, "Returns the default value of `0`" }
|
||||
default_impl! { i128, 0, "Returns the default value of `0`" }
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
default_impl! { f16, 0.0f16, "Returns the default value of `0.0`" }
|
||||
default_impl! { f32, 0.0f32, "Returns the default value of `0.0`" }
|
||||
default_impl! { f64, 0.0f64, "Returns the default value of `0.0`" }
|
||||
#[cfg(not(bootstrap))]
|
||||
default_impl! { f128, 0.0f128, "Returns the default value of `0.0`" }
|
||||
|
@ -137,6 +137,7 @@
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_hint_assert_unchecked)]
|
||||
#![feature(const_index_range_slice_index)]
|
||||
#![feature(const_int_from_str)]
|
||||
#![feature(const_intrinsic_copy)]
|
||||
#![feature(const_intrinsic_forget)]
|
||||
#![feature(const_ipv4)]
|
||||
@ -228,6 +229,8 @@
|
||||
#![feature(doc_notable_trait)]
|
||||
#![feature(effects)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(freeze_impls)]
|
||||
#![feature(fundamental)]
|
||||
#![feature(generic_arg_infer)]
|
||||
|
@ -422,7 +422,7 @@ marker_impls! {
|
||||
Copy for
|
||||
usize, u8, u16, u32, u64, u128,
|
||||
isize, i8, i16, i32, i64, i128,
|
||||
f32, f64,
|
||||
f16, f32, f64, f128,
|
||||
bool, char,
|
||||
{T: ?Sized} *const T,
|
||||
{T: ?Sized} *mut T,
|
||||
|
@ -113,8 +113,9 @@ pub enum IntErrorKind {
|
||||
impl ParseIntError {
|
||||
/// Outputs the detailed cause of parsing an integer failing.
|
||||
#[must_use]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
#[stable(feature = "int_error_matching", since = "1.55.0")]
|
||||
pub fn kind(&self) -> &IntErrorKind {
|
||||
pub const fn kind(&self) -> &IntErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
@ -60,32 +60,6 @@ macro_rules! int_impl {
|
||||
#[stable(feature = "int_bits_const", since = "1.53.0")]
|
||||
pub const BITS: u32 = <$UnsignedT>::BITS;
|
||||
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
///
|
||||
/// The string is expected to be an optional `+` or `-` sign followed by digits.
|
||||
/// Leading and trailing whitespace represent an error. Digits are a subset of these characters,
|
||||
/// depending on `radix`:
|
||||
///
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
|
||||
from_str_radix(src, radix)
|
||||
}
|
||||
|
||||
/// Returns the number of ones in the binary representation of `self`.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -6,7 +6,6 @@ use crate::ascii;
|
||||
use crate::hint;
|
||||
use crate::intrinsics;
|
||||
use crate::mem;
|
||||
use crate::ops::{Add, Mul, Sub};
|
||||
use crate::str::FromStr;
|
||||
|
||||
// Used because the `?` operator is not allowed in a const context.
|
||||
@ -1386,51 +1385,19 @@ pub enum FpCategory {
|
||||
Normal,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
trait FromStrRadixHelper:
|
||||
PartialOrd + Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
|
||||
{
|
||||
const MIN: Self;
|
||||
fn from_u32(u: u32) -> Self;
|
||||
fn checked_mul(&self, other: u32) -> Option<Self>;
|
||||
fn checked_sub(&self, other: u32) -> Option<Self>;
|
||||
fn checked_add(&self, other: u32) -> Option<Self>;
|
||||
}
|
||||
|
||||
macro_rules! from_str_radix_int_impl {
|
||||
($($t:ty)*) => {$(
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl FromStr for $t {
|
||||
type Err = ParseIntError;
|
||||
fn from_str(src: &str) -> Result<Self, ParseIntError> {
|
||||
from_str_radix(src, 10)
|
||||
<$t>::from_str_radix(src, 10)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
|
||||
|
||||
macro_rules! impl_helper_for {
|
||||
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
|
||||
const MIN: Self = Self::MIN;
|
||||
#[inline]
|
||||
fn from_u32(u: u32) -> Self { u as Self }
|
||||
#[inline]
|
||||
fn checked_mul(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_mul(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_sub(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_sub(*self, other as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn checked_add(&self, other: u32) -> Option<Self> {
|
||||
Self::checked_add(*self, other as Self)
|
||||
}
|
||||
})*)
|
||||
}
|
||||
impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
|
||||
|
||||
/// Determines if a string of text of that length of that radix could be guaranteed to be
|
||||
/// stored in the given type T.
|
||||
/// Note that if the radix is known to the compiler, it is just the check of digits.len that
|
||||
@ -1438,92 +1405,198 @@ impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
#[unstable(issue = "none", feature = "std_internals")]
|
||||
pub fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool {
|
||||
pub const fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool {
|
||||
radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize
|
||||
}
|
||||
|
||||
fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, ParseIntError> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
||||
assert!(
|
||||
(2..=36).contains(&radix),
|
||||
"from_str_radix_int: must lie in the range `[2, 36]` - found {}",
|
||||
radix
|
||||
);
|
||||
|
||||
if src.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
}
|
||||
|
||||
let is_signed_ty = T::from_u32(0) > T::MIN;
|
||||
|
||||
// all valid digits are ascii, so we will just iterate over the utf8 bytes
|
||||
// and cast them to chars. .to_digit() will safely return None for anything
|
||||
// other than a valid ascii digit for the given radix, including the first-byte
|
||||
// of multi-byte sequences
|
||||
let src = src.as_bytes();
|
||||
|
||||
let (is_positive, digits) = match src[0] {
|
||||
b'+' | b'-' if src[1..].is_empty() => {
|
||||
return Err(PIE { kind: InvalidDigit });
|
||||
}
|
||||
b'+' => (true, &src[1..]),
|
||||
b'-' if is_signed_ty => (false, &src[1..]),
|
||||
_ => (true, src),
|
||||
};
|
||||
|
||||
let mut result = T::from_u32(0);
|
||||
|
||||
if can_not_overflow::<T>(radix, is_signed_ty, digits) {
|
||||
// If the len of the str is short compared to the range of the type
|
||||
// we are parsing into, then we can be certain that an overflow will not occur.
|
||||
// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
|
||||
// above is a faster (conservative) approximation of this.
|
||||
//
|
||||
// Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
|
||||
// `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
|
||||
// `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
|
||||
macro_rules! run_unchecked_loop {
|
||||
($unchecked_additive_op:expr) => {
|
||||
for &c in digits {
|
||||
result = result * T::from_u32(radix);
|
||||
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
|
||||
result = $unchecked_additive_op(result, T::from_u32(x));
|
||||
}
|
||||
};
|
||||
}
|
||||
if is_positive {
|
||||
run_unchecked_loop!(<T as core::ops::Add>::add)
|
||||
} else {
|
||||
run_unchecked_loop!(<T as core::ops::Sub>::sub)
|
||||
};
|
||||
} else {
|
||||
macro_rules! run_checked_loop {
|
||||
($checked_additive_op:ident, $overflow_err:expr) => {
|
||||
for &c in digits {
|
||||
// When `radix` is passed in as a literal, rather than doing a slow `imul`
|
||||
// the compiler can use shifts if `radix` can be expressed as a
|
||||
// sum of powers of 2 (x*10 can be written as x*8 + x*2).
|
||||
// When the compiler can't use these optimisations,
|
||||
// the latency of the multiplication can be hidden by issuing it
|
||||
// before the result is needed to improve performance on
|
||||
// modern out-of-order CPU as multiplication here is slower
|
||||
// than the other instructions, we can get the end result faster
|
||||
// doing multiplication first and let the CPU spends other cycles
|
||||
// doing other computation and get multiplication result later.
|
||||
let mul = result.checked_mul(radix);
|
||||
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
|
||||
result = mul.ok_or_else($overflow_err)?;
|
||||
result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
if is_positive {
|
||||
run_checked_loop!(checked_add, || PIE { kind: PosOverflow })
|
||||
} else {
|
||||
run_checked_loop!(checked_sub, || PIE { kind: NegOverflow })
|
||||
};
|
||||
}
|
||||
Ok(result)
|
||||
#[track_caller]
|
||||
const fn from_str_radix_panic_ct(_radix: u32) -> ! {
|
||||
panic!("from_str_radix_int: must lie in the range `[2, 36]`");
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn from_str_radix_panic_rt(radix: u32) -> ! {
|
||||
panic!("from_str_radix_int: must lie in the range `[2, 36]` - found {}", radix);
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
const fn from_str_radix_assert(radix: u32) {
|
||||
if 2 > radix || radix > 36 {
|
||||
// The only difference between these two functions is their panic message.
|
||||
intrinsics::const_eval_select((radix,), from_str_radix_panic_ct, from_str_radix_panic_rt);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_str_radix {
|
||||
($($int_ty:ty)+) => {$(
|
||||
impl $int_ty {
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
///
|
||||
/// The string is expected to be an optional `+` sign
|
||||
/// followed by digits.
|
||||
/// Leading and trailing whitespace represent an error.
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
///
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> {
|
||||
use self::IntErrorKind::*;
|
||||
use self::ParseIntError as PIE;
|
||||
|
||||
from_str_radix_assert(radix);
|
||||
|
||||
if src.is_empty() {
|
||||
return Err(PIE { kind: Empty });
|
||||
}
|
||||
|
||||
#[allow(unused_comparisons)]
|
||||
let is_signed_ty = 0 > <$int_ty>::MIN;
|
||||
|
||||
// all valid digits are ascii, so we will just iterate over the utf8 bytes
|
||||
// and cast them to chars. .to_digit() will safely return None for anything
|
||||
// other than a valid ascii digit for the given radix, including the first-byte
|
||||
// of multi-byte sequences
|
||||
let src = src.as_bytes();
|
||||
|
||||
let (is_positive, mut digits) = match src {
|
||||
[b'+' | b'-'] => {
|
||||
return Err(PIE { kind: InvalidDigit });
|
||||
}
|
||||
[b'+', rest @ ..] => (true, rest),
|
||||
[b'-', rest @ ..] if is_signed_ty => (false, rest),
|
||||
_ => (true, src),
|
||||
};
|
||||
|
||||
let mut result = 0;
|
||||
|
||||
macro_rules! unwrap_or_PIE {
|
||||
($option:expr, $kind:ident) => {
|
||||
match $option {
|
||||
Some(value) => value,
|
||||
None => return Err(PIE { kind: $kind }),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if can_not_overflow::<$int_ty>(radix, is_signed_ty, digits) {
|
||||
// If the len of the str is short compared to the range of the type
|
||||
// we are parsing into, then we can be certain that an overflow will not occur.
|
||||
// This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
|
||||
// above is a faster (conservative) approximation of this.
|
||||
//
|
||||
// Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
|
||||
// `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
|
||||
// `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
|
||||
macro_rules! run_unchecked_loop {
|
||||
($unchecked_additive_op:tt) => {{
|
||||
while let [c, rest @ ..] = digits {
|
||||
result = result * (radix as $int_ty);
|
||||
let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit);
|
||||
result = result $unchecked_additive_op (x as $int_ty);
|
||||
digits = rest;
|
||||
}
|
||||
}};
|
||||
}
|
||||
if is_positive {
|
||||
run_unchecked_loop!(+)
|
||||
} else {
|
||||
run_unchecked_loop!(-)
|
||||
};
|
||||
} else {
|
||||
macro_rules! run_checked_loop {
|
||||
($checked_additive_op:ident, $overflow_err:ident) => {{
|
||||
while let [c, rest @ ..] = digits {
|
||||
// When `radix` is passed in as a literal, rather than doing a slow `imul`
|
||||
// the compiler can use shifts if `radix` can be expressed as a
|
||||
// sum of powers of 2 (x*10 can be written as x*8 + x*2).
|
||||
// When the compiler can't use these optimisations,
|
||||
// the latency of the multiplication can be hidden by issuing it
|
||||
// before the result is needed to improve performance on
|
||||
// modern out-of-order CPU as multiplication here is slower
|
||||
// than the other instructions, we can get the end result faster
|
||||
// doing multiplication first and let the CPU spends other cycles
|
||||
// doing other computation and get multiplication result later.
|
||||
let mul = result.checked_mul(radix as $int_ty);
|
||||
let x = unwrap_or_PIE!((*c as char).to_digit(radix), InvalidDigit) as $int_ty;
|
||||
result = unwrap_or_PIE!(mul, $overflow_err);
|
||||
result = unwrap_or_PIE!(<$int_ty>::$checked_additive_op(result, x), $overflow_err);
|
||||
digits = rest;
|
||||
}
|
||||
}};
|
||||
}
|
||||
if is_positive {
|
||||
run_checked_loop!(checked_add, PosOverflow)
|
||||
} else {
|
||||
run_checked_loop!(checked_sub, NegOverflow)
|
||||
};
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
)+}
|
||||
}
|
||||
|
||||
from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 }
|
||||
|
||||
// Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two
|
||||
// identical functions.
|
||||
macro_rules! from_str_radix_size_impl {
|
||||
($($t:ident $size:ty),*) => {$(
|
||||
impl $size {
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
///
|
||||
/// The string is expected to be an optional `+` sign
|
||||
/// followed by digits.
|
||||
/// Leading and trailing whitespace represent an error.
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
///
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_int_from_str", issue = "59133")]
|
||||
pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> {
|
||||
match <$t>::from_str_radix(src, radix) {
|
||||
Ok(x) => Ok(x as $size),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
})*}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
from_str_radix_size_impl! { i16 isize, u16 usize }
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
from_str_radix_size_impl! { i32 isize, u32 usize }
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
from_str_radix_size_impl! { i64 isize, u64 usize }
|
||||
|
@ -11,7 +11,6 @@ use crate::ptr;
|
||||
use crate::str::FromStr;
|
||||
use crate::ub_checks;
|
||||
|
||||
use super::from_str_radix;
|
||||
use super::{IntErrorKind, ParseIntError};
|
||||
|
||||
/// A marker trait for primitive types which can be zero.
|
||||
@ -804,7 +803,7 @@ macro_rules! nonzero_integer {
|
||||
impl FromStr for $Ty {
|
||||
type Err = ParseIntError;
|
||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
||||
Self::new(from_str_radix(src, 10)?)
|
||||
Self::new(<$Int>::from_str_radix(src, 10)?)
|
||||
.ok_or(ParseIntError {
|
||||
kind: IntErrorKind::Zero
|
||||
})
|
||||
|
@ -58,33 +58,6 @@ macro_rules! uint_impl {
|
||||
#[stable(feature = "int_bits_const", since = "1.53.0")]
|
||||
pub const BITS: u32 = Self::MAX.count_ones();
|
||||
|
||||
/// Converts a string slice in a given base to an integer.
|
||||
///
|
||||
/// The string is expected to be an optional `+` sign
|
||||
/// followed by digits.
|
||||
/// Leading and trailing whitespace represent an error.
|
||||
/// Digits are a subset of these characters, depending on `radix`:
|
||||
///
|
||||
/// * `0-9`
|
||||
/// * `a-z`
|
||||
/// * `A-Z`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if `radix` is not in the range from 2 to 36.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::from_str_radix(\"A\", 16), Ok(10));")]
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
|
||||
from_str_radix(src, radix)
|
||||
}
|
||||
|
||||
/// Returns the number of ones in the binary representation of `self`.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -1401,8 +1401,6 @@ impl<T: ?Sized> *const T {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
/// #[repr(align(4))]
|
||||
/// struct AlignedI32(i32);
|
||||
@ -1425,7 +1423,6 @@ impl<T: ?Sized> *const T {
|
||||
/// underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1451,7 +1448,6 @@ impl<T: ?Sized> *const T {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1477,7 +1473,6 @@ impl<T: ?Sized> *const T {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1501,7 +1496,7 @@ impl<T: ?Sized> *const T {
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
pub const fn is_aligned(self) -> bool
|
||||
where
|
||||
@ -1522,7 +1517,7 @@ impl<T: ?Sized> *const T {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
/// #[repr(align(4))]
|
||||
@ -1551,7 +1546,7 @@ impl<T: ?Sized> *const T {
|
||||
/// cannot be stricter aligned than the reference's underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1576,7 +1571,7 @@ impl<T: ?Sized> *const T {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1600,7 +1595,7 @@ impl<T: ?Sized> *const T {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// const _: () = {
|
||||
@ -1616,7 +1611,7 @@ impl<T: ?Sized> *const T {
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
pub const fn is_aligned_to(self, align: usize) -> bool {
|
||||
if !align.is_power_of_two() {
|
||||
|
@ -57,7 +57,7 @@ pub trait Pointee {
|
||||
// NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata`
|
||||
// in `library/core/src/ptr/metadata.rs`
|
||||
// in sync with those here:
|
||||
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
|
||||
type Metadata: fmt::Debug + Copy + Send + Sync + Ord + Hash + Unpin;
|
||||
}
|
||||
|
||||
/// Pointers to types implementing this trait alias are “thin”.
|
||||
|
@ -1660,8 +1660,6 @@ impl<T: ?Sized> *mut T {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
/// #[repr(align(4))]
|
||||
/// struct AlignedI32(i32);
|
||||
@ -1684,7 +1682,6 @@ impl<T: ?Sized> *mut T {
|
||||
/// underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
/// #![feature(const_mut_refs)]
|
||||
///
|
||||
@ -1711,7 +1708,6 @@ impl<T: ?Sized> *mut T {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1738,7 +1734,6 @@ impl<T: ?Sized> *mut T {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1762,7 +1757,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
pub const fn is_aligned(self) -> bool
|
||||
where
|
||||
@ -1783,7 +1778,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
/// #[repr(align(4))]
|
||||
@ -1812,7 +1807,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// cannot be stricter aligned than the reference's underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
/// #![feature(const_mut_refs)]
|
||||
///
|
||||
@ -1838,7 +1833,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1863,7 +1858,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// const _: () = {
|
||||
@ -1879,7 +1874,7 @@ impl<T: ?Sized> *mut T {
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
pub const fn is_aligned_to(self, align: usize) -> bool {
|
||||
if !align.is_power_of_two() {
|
||||
|
@ -1288,7 +1288,6 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1313,7 +1312,6 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
/// #![feature(non_null_convenience)]
|
||||
/// #![feature(const_option)]
|
||||
@ -1343,7 +1341,6 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of primitives is less than their size.
|
||||
@ -1369,7 +1366,6 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
/// #![feature(const_option)]
|
||||
/// #![feature(const_nonnull_new)]
|
||||
@ -1394,7 +1390,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// ```
|
||||
///
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[stable(feature = "pointer_is_aligned", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
@ -1417,7 +1413,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
/// #[repr(align(4))]
|
||||
@ -1446,7 +1442,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// cannot be stricter aligned than the reference's underlying allocation.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1471,7 +1467,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// // On some platforms, the alignment of i32 is less than 4.
|
||||
@ -1495,7 +1491,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// runtime and compiletime.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// #![feature(pointer_is_aligned_to)]
|
||||
/// #![feature(const_pointer_is_aligned)]
|
||||
///
|
||||
/// const _: () = {
|
||||
@ -1509,7 +1505,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||
/// ```
|
||||
///
|
||||
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
|
||||
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
|
||||
#[unstable(feature = "pointer_is_aligned_to", issue = "96284")]
|
||||
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
|
@ -418,14 +418,12 @@ impl AtomicBool {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// use std::sync::atomic::{self, AtomicBool};
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
/// // Get a pointer to an allocated value
|
||||
/// let ptr: *mut bool = Box::into_raw(Box::new(false));
|
||||
///
|
||||
/// assert!(ptr.is_aligned_to(align_of::<AtomicBool>()));
|
||||
/// assert!(ptr.cast::<AtomicBool>().is_aligned());
|
||||
///
|
||||
/// {
|
||||
/// // Create an atomic view of the allocated value
|
||||
@ -1216,14 +1214,12 @@ impl<T> AtomicPtr<T> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
/// use std::sync::atomic::{self, AtomicPtr};
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
/// // Get a pointer to an allocated value
|
||||
/// let ptr: *mut *mut u8 = Box::into_raw(Box::new(std::ptr::null_mut()));
|
||||
///
|
||||
/// assert!(ptr.is_aligned_to(align_of::<AtomicPtr<u8>>()));
|
||||
/// assert!(ptr.cast::<AtomicPtr<u8>>().is_aligned());
|
||||
///
|
||||
/// {
|
||||
/// // Create an atomic view of the allocated value
|
||||
@ -2199,14 +2195,12 @@ macro_rules! atomic_int {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(pointer_is_aligned)]
|
||||
#[doc = concat!($extra_feature, "use std::sync::atomic::{self, ", stringify!($atomic_type), "};")]
|
||||
/// use std::mem::align_of;
|
||||
///
|
||||
/// // Get a pointer to an allocated value
|
||||
#[doc = concat!("let ptr: *mut ", stringify!($int_type), " = Box::into_raw(Box::new(0));")]
|
||||
///
|
||||
#[doc = concat!("assert!(ptr.is_aligned_to(align_of::<", stringify!($atomic_type), ">()));")]
|
||||
#[doc = concat!("assert!(ptr.cast::<", stringify!($atomic_type), ">().is_aligned());")]
|
||||
///
|
||||
/// {
|
||||
/// // Create an atomic view of the allocated value
|
||||
|
@ -16,6 +16,7 @@
|
||||
#![feature(const_hash)]
|
||||
#![feature(const_heap)]
|
||||
#![feature(const_intrinsic_copy)]
|
||||
#![feature(const_int_from_str)]
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_nonnull_new)]
|
||||
#![feature(const_pointer_is_aligned)]
|
||||
@ -95,7 +96,7 @@
|
||||
#![feature(const_waker)]
|
||||
#![feature(never_type)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(lazy_cell)]
|
||||
|
@ -214,6 +214,16 @@ fn test_infallible_try_from_int_error() {
|
||||
assert!(func(0).is_ok());
|
||||
}
|
||||
|
||||
const _TEST_CONST_PARSE: () = {
|
||||
let Ok(-0x8000) = i16::from_str_radix("-8000", 16) else { panic!() };
|
||||
let Ok(12345) = u64::from_str_radix("12345", 10) else { panic!() };
|
||||
if let Err(e) = i8::from_str_radix("+", 10) {
|
||||
let IntErrorKind::InvalidDigit = e.kind() else { panic!() };
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
};
|
||||
|
||||
macro_rules! test_impl_from {
|
||||
($fn_name:ident, bool, $target: ty) => {
|
||||
#[test]
|
||||
|
@ -841,11 +841,19 @@ fn ptr_metadata_bounds() {
|
||||
fn static_assert_expected_bounds_for_metadata<Meta>()
|
||||
where
|
||||
// Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs`
|
||||
Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
|
||||
Meta: Debug + Copy + Send + Sync + Ord + std::hash::Hash + Unpin,
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pointee_metadata_debug() {
|
||||
assert_eq!("()", format!("{:?}", metadata::<u32>(&17)));
|
||||
assert_eq!("2", format!("{:?}", metadata::<[u32]>(&[19, 23])));
|
||||
let for_dyn = format!("{:?}", metadata::<dyn Debug>(&29));
|
||||
assert!(for_dyn.starts_with("DynMetadata(0x"), "{:?}", for_dyn);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_metadata() {
|
||||
#[derive(Debug)]
|
||||
|
@ -341,7 +341,7 @@
|
||||
#![feature(panic_can_unwind)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(panic_internals)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(prelude_2024)]
|
||||
#![feature(ptr_as_uninit)]
|
||||
|
@ -571,7 +571,9 @@ pub fn make_tests(
|
||||
&modified_tests,
|
||||
&mut poisoned,
|
||||
)
|
||||
.unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
|
||||
.unwrap_or_else(|reason| {
|
||||
panic!("Could not read tests from {}: {reason}", config.src_base.display())
|
||||
});
|
||||
|
||||
if poisoned {
|
||||
eprintln!();
|
||||
|
@ -1,6 +1,6 @@
|
||||
//@ignore-target-windows: No libc on Windows
|
||||
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use core::ptr;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#![crate_type="rlib"]
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
|
||||
// CHECK-LABEL: is_aligned_to_unchecked
|
||||
// CHECK: decq
|
||||
|
@ -0,0 +1,118 @@
|
||||
// MIR for `constant_eq` after SimplifyCfg-initial
|
||||
|
||||
fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
debug s => _1;
|
||||
debug b => _2;
|
||||
let mut _0: u32;
|
||||
let mut _3: (&str, bool);
|
||||
let mut _4: &str;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: bool;
|
||||
let mut _8: &&str;
|
||||
let mut _9: &bool;
|
||||
let mut _10: bool;
|
||||
|
||||
bb0: {
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
_4 = _1;
|
||||
StorageLive(_5);
|
||||
_5 = _2;
|
||||
_3 = (move _4, move _5);
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
PlaceMention(_3);
|
||||
_7 = <str as PartialEq>::eq((_3.0: &str), const "a") -> [return: bb11, unwind: bb19];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
switchInt((_3.1: bool)) -> [0: bb2, otherwise: bb3];
|
||||
}
|
||||
|
||||
bb2: {
|
||||
_0 = const 5_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb3: {
|
||||
falseEdge -> [real: bb17, imaginary: bb2];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb9];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb6];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb16, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_6 = <str as PartialEq>::eq((_3.0: &str), const "b") -> [return: bb10, unwind: bb19];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
switchInt((_3.1: bool)) -> [0: bb1, otherwise: bb9];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
falseEdge -> [real: bb15, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
switchInt(move _6) -> [0: bb1, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb11: {
|
||||
switchInt(move _7) -> [0: bb7, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_8 = &fake (_3.0: &str);
|
||||
_9 = &fake (_3.1: bool);
|
||||
StorageLive(_10);
|
||||
_10 = const true;
|
||||
switchInt(move _10) -> [0: bb14, otherwise: bb13];
|
||||
}
|
||||
|
||||
bb13: {
|
||||
StorageDead(_10);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
FakeRead(ForMatchGuard, _9);
|
||||
_0 = const 1_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
StorageDead(_10);
|
||||
falseEdge -> [real: bb5, imaginary: bb9];
|
||||
}
|
||||
|
||||
bb15: {
|
||||
_0 = const 2_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb16: {
|
||||
_0 = const 3_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb17: {
|
||||
_0 = const 4_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb18: {
|
||||
StorageDead(_3);
|
||||
return;
|
||||
}
|
||||
|
||||
bb19 (cleanup): {
|
||||
resume;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
// MIR for `disjoint_ranges` after SimplifyCfg-initial
|
||||
|
||||
fn disjoint_ranges(_1: i32, _2: bool) -> u32 {
|
||||
debug x => _1;
|
||||
debug b => _2;
|
||||
let mut _0: u32;
|
||||
let mut _3: bool;
|
||||
let mut _4: bool;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: &i32;
|
||||
let mut _8: bool;
|
||||
|
||||
bb0: {
|
||||
PlaceMention(_1);
|
||||
_5 = Le(const 0_i32, _1);
|
||||
switchInt(move _5) -> [0: bb3, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_0 = const 3_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb9, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_3 = Le(const 10_i32, _1);
|
||||
switchInt(move _3) -> [0: bb5, otherwise: bb7];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt(_1) -> [4294967295: bb6, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb13, imaginary: bb1];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_4 = Le(_1, const 20_i32);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
_6 = Lt(_1, const 10_i32);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
_7 = &fake _1;
|
||||
StorageLive(_8);
|
||||
_8 = _2;
|
||||
switchInt(move _8) -> [0: bb11, otherwise: bb10];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
StorageDead(_8);
|
||||
FakeRead(ForMatchGuard, _7);
|
||||
_0 = const 0_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb11: {
|
||||
StorageDead(_8);
|
||||
falseEdge -> [real: bb1, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_0 = const 1_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb13: {
|
||||
_0 = const 2_u32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
return;
|
||||
}
|
||||
}
|
41
tests/mir-opt/building/match/sort_candidates.rs
Normal file
41
tests/mir-opt/building/match/sort_candidates.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Check specific cases of sorting candidates in match lowering.
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
// EMIT_MIR sort_candidates.constant_eq.SimplifyCfg-initial.after.mir
|
||||
fn constant_eq(s: &str, b: bool) -> u32 {
|
||||
// Check that we only test "a" once
|
||||
|
||||
// CHECK-LABEL: fn constant_eq(
|
||||
// CHECK: bb0: {
|
||||
// CHECK: [[a:_.*]] = const "a";
|
||||
// CHECK-NOT: {{_.*}} = const "a";
|
||||
match (s, b) {
|
||||
("a", _) if true => 1,
|
||||
("b", true) => 2,
|
||||
("a", true) => 3,
|
||||
(_, true) => 4,
|
||||
_ => 5,
|
||||
}
|
||||
}
|
||||
|
||||
// EMIT_MIR sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir
|
||||
fn disjoint_ranges(x: i32, b: bool) -> u32 {
|
||||
// When `(0..=10).contains(x) && !b`, we should jump to the last arm without testing the two
|
||||
// other candidates.
|
||||
|
||||
// CHECK-LABEL: fn disjoint_ranges(
|
||||
// CHECK: debug b => _2;
|
||||
// CHECK: bb0: {
|
||||
// CHECK: switchInt(_2) -> [0: [[jump:bb.*]], otherwise: {{bb.*}}];
|
||||
// CHECK: [[jump]]: {
|
||||
// CHECK-NEXT: _0 = const 3_u32;
|
||||
// CHECK-NEXT: return;
|
||||
match x {
|
||||
0..10 if b => 0,
|
||||
10..=20 => 1,
|
||||
-1 => 2,
|
||||
_ => 3,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,106 +0,0 @@
|
||||
// MIR for `main` after SimplifyCfg-initial
|
||||
|
||||
fn main() -> () {
|
||||
let mut _0: ();
|
||||
let _1: i32;
|
||||
let _3: i32;
|
||||
let mut _4: bool;
|
||||
let mut _5: bool;
|
||||
let mut _6: bool;
|
||||
let mut _7: bool;
|
||||
let mut _8: &i32;
|
||||
let mut _9: bool;
|
||||
scope 1 {
|
||||
debug x => _1;
|
||||
let _2: bool;
|
||||
scope 2 {
|
||||
debug b => _2;
|
||||
}
|
||||
}
|
||||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = const 3_i32;
|
||||
FakeRead(ForLet(None), _1);
|
||||
StorageLive(_2);
|
||||
_2 = const true;
|
||||
FakeRead(ForLet(None), _2);
|
||||
StorageLive(_3);
|
||||
PlaceMention(_1);
|
||||
_6 = Le(const 0_i32, _1);
|
||||
switchInt(move _6) -> [0: bb3, otherwise: bb8];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
_3 = const 3_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb9, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_4 = Le(const 10_i32, _1);
|
||||
switchInt(move _4) -> [0: bb5, otherwise: bb7];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb12, imaginary: bb6];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
switchInt(_1) -> [4294967295: bb6, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
falseEdge -> [real: bb13, imaginary: bb1];
|
||||
}
|
||||
|
||||
bb7: {
|
||||
_5 = Le(_1, const 20_i32);
|
||||
switchInt(move _5) -> [0: bb5, otherwise: bb4];
|
||||
}
|
||||
|
||||
bb8: {
|
||||
_7 = Lt(_1, const 10_i32);
|
||||
switchInt(move _7) -> [0: bb3, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
_8 = &fake _1;
|
||||
StorageLive(_9);
|
||||
_9 = _2;
|
||||
switchInt(move _9) -> [0: bb11, otherwise: bb10];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
StorageDead(_9);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
_3 = const 0_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb11: {
|
||||
StorageDead(_9);
|
||||
falseEdge -> [real: bb1, imaginary: bb4];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
_3 = const 1_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb13: {
|
||||
_3 = const 2_i32;
|
||||
goto -> bb14;
|
||||
}
|
||||
|
||||
bb14: {
|
||||
StorageDead(_3);
|
||||
_0 = const ();
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
// skip-filecheck
|
||||
// Make sure redundant testing paths in `match` expressions are sorted out.
|
||||
|
||||
#![feature(exclusive_range_pattern)]
|
||||
|
||||
// EMIT_MIR match_test.main.SimplifyCfg-initial.after.mir
|
||||
fn main() {
|
||||
let x = 3;
|
||||
let b = true;
|
||||
|
||||
// When `(0..=10).contains(x) && !b`, we should jump to the last arm
|
||||
// without testing two other candidates.
|
||||
match x {
|
||||
0..10 if b => 0,
|
||||
10..=20 => 1,
|
||||
-1 => 2,
|
||||
_ => 3,
|
||||
};
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// It is UB to unwind out of `fn start()` according to
|
||||
// https://doc.rust-lang.org/beta/unstable-book/language-features/start.html so
|
||||
// panic with abort to avoid UB:
|
||||
//@ compile-flags: -Cpanic=abort
|
||||
//@ no-prefer-dynamic so panic=abort works
|
||||
|
||||
#![feature(start, rustc_private)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
// Use #[start] so we don't have a runtime that messes with SIGPIPE.
|
||||
#[start]
|
||||
fn start(argc: isize, argv: *const *const u8) -> isize {
|
||||
assert_eq!(argc, 2, "Must pass SIG_IGN or SIG_DFL as first arg");
|
||||
let arg1 = unsafe { std::ffi::CStr::from_ptr(*argv.offset(1) as *const libc::c_char) }
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
||||
let expected = match arg1 {
|
||||
"SIG_IGN" => libc::SIG_IGN,
|
||||
"SIG_DFL" => libc::SIG_DFL,
|
||||
arg => panic!("Must pass SIG_IGN or SIG_DFL as first arg. Got: {}", arg),
|
||||
};
|
||||
|
||||
let actual = unsafe {
|
||||
let mut actual: libc::sigaction = std::mem::zeroed();
|
||||
libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual);
|
||||
actual.sa_sigaction
|
||||
};
|
||||
|
||||
assert_eq!(actual, expected, "actual and expected SIGPIPE disposition in child differs");
|
||||
|
||||
0
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
//@ revisions: default sig_dfl sig_ign inherit
|
||||
//@ ignore-cross-compile because aux-bin does not yet support it
|
||||
//@ only-unix because SIGPIPE is a unix thing
|
||||
//@ run-pass
|
||||
//@ aux-bin:assert-sigpipe-disposition.rs
|
||||
//@ aux-crate:sigpipe_utils=sigpipe-utils.rs
|
||||
|
||||
// Checks the signal disposition of `SIGPIPE` in child processes, and in our own
|
||||
// process for robustness. Without any `unix_sigpipe` attribute, `SIG_IGN` is
|
||||
// the default. But there is a difference in how `SIGPIPE` is treated in child
|
||||
// processes with and without the attribute. Search for
|
||||
// `unix_sigpipe_attr_specified()` in the code base to learn more.
|
||||
|
||||
#![feature(rustc_private)]
|
||||
#![cfg_attr(any(sig_dfl, sig_ign, inherit), feature(unix_sigpipe))]
|
||||
|
||||
extern crate libc;
|
||||
extern crate sigpipe_utils;
|
||||
|
||||
use sigpipe_utils::*;
|
||||
|
||||
#[cfg_attr(sig_dfl, unix_sigpipe = "sig_dfl")]
|
||||
#[cfg_attr(sig_ign, unix_sigpipe = "sig_ign")]
|
||||
#[cfg_attr(inherit, unix_sigpipe = "inherit")]
|
||||
fn main() {
|
||||
// By default we get SIG_IGN but the child gets SIG_DFL through an explicit
|
||||
// reset before exec:
|
||||
// https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L363-L384
|
||||
#[cfg(default)]
|
||||
let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_DFL");
|
||||
|
||||
// With #[unix_sigpipe = "sig_dfl"] we get SIG_DFL and the child does too
|
||||
// without any special code running before exec.
|
||||
#[cfg(sig_dfl)]
|
||||
let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");
|
||||
|
||||
// With #[unix_sigpipe = "sig_ign"] we get SIG_IGN and the child does too
|
||||
// without any special code running before exec.
|
||||
#[cfg(sig_ign)]
|
||||
let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_IGN");
|
||||
|
||||
// With #[unix_sigpipe = "inherit"] we get SIG_DFL and the child does too
|
||||
// without any special code running before exec.
|
||||
#[cfg(inherit)]
|
||||
let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");
|
||||
|
||||
assert_sigpipe_handler(we_expect);
|
||||
|
||||
assert!(
|
||||
std::process::Command::new("./auxiliary/bin/assert-sigpipe-disposition")
|
||||
.arg(child_expects)
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
);
|
||||
}
|
@ -247,15 +247,15 @@ LL | _ = &&0 == Foo;
|
||||
|
|
||||
= help: the trait `PartialEq<Foo>` is not implemented for `&&{integer}`
|
||||
= help: the following other types implement trait `PartialEq<Rhs>`:
|
||||
f128
|
||||
f16
|
||||
f32
|
||||
f64
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
i8
|
||||
isize
|
||||
and 6 others
|
||||
and 8 others
|
||||
|
||||
error[E0369]: binary operation `==` cannot be applied to type `Foo`
|
||||
--> $DIR/binary-op-suggest-deref.rs:60:13
|
||||
|
@ -0,0 +1,23 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(adt_const_params)]
|
||||
//~^ WARN the feature `adt_const_params` is incomplete
|
||||
#![feature(with_negative_coherence, negative_impls)]
|
||||
|
||||
pub trait A<const K: &'static str> {}
|
||||
pub trait C {}
|
||||
|
||||
|
||||
struct W<T>(T);
|
||||
|
||||
// Negative coherence:
|
||||
// Proving `W<!T>: !A<"">` requires proving `CONST alias-eq ""`, which requires proving
|
||||
// `CONST normalizes-to (?1c: &str)`. The type's region is uniquified, so it ends up being
|
||||
// put in to the canonical vars list with an infer region => ICE.
|
||||
impl<T> C for T where T: A<""> {}
|
||||
impl<T> C for W<T> {}
|
||||
|
||||
impl<T> !A<CONST> for W<T> {}
|
||||
const CONST: &str = "";
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,11 @@
|
||||
warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/regions-in-canonical.rs:3:12
|
||||
|
|
||||
LL | #![feature(adt_const_params)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
@ -1,4 +1,4 @@
|
||||
//@ ignore-cross-compile because we run the compiled code
|
||||
//@ ignore-cross-compile because aux-bin does not yet support it
|
||||
//@ aux-bin: print-it-works.rs
|
||||
//@ run-pass
|
||||
|
||||
|
10
tests/ui/consts/const-eval/parse_ints.rs
Normal file
10
tests/ui/consts/const-eval/parse_ints.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![feature(const_int_from_str)]
|
||||
|
||||
const _OK: () = match i32::from_str_radix("-1234", 10) {
|
||||
Ok(x) => assert!(x == -1234),
|
||||
Err(_) => panic!(),
|
||||
};
|
||||
const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); };
|
||||
const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); };
|
||||
|
||||
fn main () {}
|
31
tests/ui/consts/const-eval/parse_ints.stderr
Normal file
31
tests/ui/consts/const-eval/parse_ints.stderr
Normal file
@ -0,0 +1,31 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
|
|
||||
= note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
|
|
||||
note: inside `core::num::<impl u64>::from_str_radix`
|
||||
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
note: inside `_TOO_LOW`
|
||||
--> $DIR/parse_ints.rs:7:24
|
||||
|
|
||||
LL | const _TOO_LOW: () = { u64::from_str_radix("12345ABCD", 1); };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
|
|
||||
= note: the evaluated program panicked at 'from_str_radix_int: must lie in the range `[2, 36]`', $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
|
|
||||
note: inside `core::num::<impl u64>::from_str_radix`
|
||||
--> $SRC_DIR/core/src/num/mod.rs:LL:COL
|
||||
note: inside `_TOO_HIGH`
|
||||
--> $DIR/parse_ints.rs:8:25
|
||||
|
|
||||
LL | const _TOO_HIGH: () = { u64::from_str_radix("12345ABCD", 37); };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: this error originates in the macro `from_str_radix` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
@ -3,6 +3,7 @@
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::cmp::PartialEq;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
struct A;
|
||||
struct B;
|
||||
@ -50,6 +51,17 @@ fn main() {
|
||||
let _ = a.gt(&b);
|
||||
//~^ WARN ambiguous wide pointer comparison
|
||||
|
||||
{
|
||||
let a = NonNull::<dyn T>::new(a as *mut _).unwrap();
|
||||
let b = NonNull::<dyn T>::new(b as *mut _).unwrap();
|
||||
let _ = a == b;
|
||||
//~^ WARN ambiguous wide pointer comparison
|
||||
let _ = a >= b;
|
||||
//~^ WARN ambiguous wide pointer comparison
|
||||
let _ = &a == &b;
|
||||
//~^ WARN ambiguous wide pointer comparison
|
||||
}
|
||||
|
||||
{
|
||||
// &*const ?Sized
|
||||
let a = &a;
|
||||
|
@ -1,5 +1,5 @@
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:19:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:20:13
|
||||
|
|
||||
LL | let _ = a == b;
|
||||
| ^^^^^^
|
||||
@ -11,7 +11,7 @@ LL | let _ = std::ptr::addr_eq(a, b);
|
||||
| ++++++++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:21:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:22:13
|
||||
|
|
||||
LL | let _ = a != b;
|
||||
| ^^^^^^
|
||||
@ -22,7 +22,7 @@ LL | let _ = !std::ptr::addr_eq(a, b);
|
||||
| +++++++++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:23:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:24:13
|
||||
|
|
||||
LL | let _ = a < b;
|
||||
| ^^^^^
|
||||
@ -33,7 +33,7 @@ LL | let _ = a.cast::<()>() < b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:25:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:26:13
|
||||
|
|
||||
LL | let _ = a <= b;
|
||||
| ^^^^^^
|
||||
@ -44,7 +44,7 @@ LL | let _ = a.cast::<()>() <= b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:27:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:28:13
|
||||
|
|
||||
LL | let _ = a > b;
|
||||
| ^^^^^
|
||||
@ -55,7 +55,7 @@ LL | let _ = a.cast::<()>() > b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:29:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:30:13
|
||||
|
|
||||
LL | let _ = a >= b;
|
||||
| ^^^^^^
|
||||
@ -66,7 +66,7 @@ LL | let _ = a.cast::<()>() >= b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:32:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:33:13
|
||||
|
|
||||
LL | let _ = PartialEq::eq(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -77,7 +77,7 @@ LL | let _ = std::ptr::addr_eq(a, b);
|
||||
| ~~~~~~~~~~~~~~~~~~ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:34:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:35:13
|
||||
|
|
||||
LL | let _ = PartialEq::ne(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -88,7 +88,7 @@ LL | let _ = !std::ptr::addr_eq(a, b);
|
||||
| ~~~~~~~~~~~~~~~~~~~ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:36:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:37:13
|
||||
|
|
||||
LL | let _ = a.eq(&b);
|
||||
| ^^^^^^^^
|
||||
@ -99,7 +99,7 @@ LL | let _ = std::ptr::addr_eq(a, b);
|
||||
| ++++++++++++++++++ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:38:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:39:13
|
||||
|
|
||||
LL | let _ = a.ne(&b);
|
||||
| ^^^^^^^^
|
||||
@ -110,7 +110,7 @@ LL | let _ = !std::ptr::addr_eq(a, b);
|
||||
| +++++++++++++++++++ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:40:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:41:13
|
||||
|
|
||||
LL | let _ = a.cmp(&b);
|
||||
| ^^^^^^^^^
|
||||
@ -121,7 +121,7 @@ LL | let _ = a.cast::<()>().cmp(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:42:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:43:13
|
||||
|
|
||||
LL | let _ = a.partial_cmp(&b);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
@ -132,7 +132,7 @@ LL | let _ = a.cast::<()>().partial_cmp(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:44:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:45:13
|
||||
|
|
||||
LL | let _ = a.le(&b);
|
||||
| ^^^^^^^^
|
||||
@ -143,7 +143,7 @@ LL | let _ = a.cast::<()>().le(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:46:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:47:13
|
||||
|
|
||||
LL | let _ = a.lt(&b);
|
||||
| ^^^^^^^^
|
||||
@ -154,7 +154,7 @@ LL | let _ = a.cast::<()>().lt(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:48:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:49:13
|
||||
|
|
||||
LL | let _ = a.ge(&b);
|
||||
| ^^^^^^^^
|
||||
@ -165,7 +165,7 @@ LL | let _ = a.cast::<()>().ge(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:50:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:51:13
|
||||
|
|
||||
LL | let _ = a.gt(&b);
|
||||
| ^^^^^^^^
|
||||
@ -176,7 +176,40 @@ LL | let _ = a.cast::<()>().gt(&b.cast::<()>());
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:58:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:57:17
|
||||
|
|
||||
LL | let _ = a == b;
|
||||
| ^^^^^^
|
||||
|
|
||||
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
||||
|
|
||||
LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr());
|
||||
| ++++++++++++++++++ ~~~~~~~~~~ ++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:59:17
|
||||
|
|
||||
LL | let _ = a >= b;
|
||||
| ^^^^^^
|
||||
|
|
||||
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
||||
|
|
||||
LL | let _ = a.as_ptr().cast::<()>() >= b.as_ptr().cast::<()>();
|
||||
| ++++++++++++++++++++++ ++++++++++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:61:17
|
||||
|
|
||||
LL | let _ = &a == &b;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
||||
|
|
||||
LL | let _ = std::ptr::addr_eq(a.as_ptr(), b.as_ptr());
|
||||
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:70:17
|
||||
|
|
||||
LL | let _ = a == b;
|
||||
| ^^^^^^
|
||||
@ -187,7 +220,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b);
|
||||
| +++++++++++++++++++ ~~~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:60:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:72:17
|
||||
|
|
||||
LL | let _ = a != b;
|
||||
| ^^^^^^
|
||||
@ -198,7 +231,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b);
|
||||
| ++++++++++++++++++++ ~~~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:62:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:74:17
|
||||
|
|
||||
LL | let _ = a < b;
|
||||
| ^^^^^
|
||||
@ -209,7 +242,7 @@ LL | let _ = (*a).cast::<()>() < (*b).cast::<()>();
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:64:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:76:17
|
||||
|
|
||||
LL | let _ = a <= b;
|
||||
| ^^^^^^
|
||||
@ -220,7 +253,7 @@ LL | let _ = (*a).cast::<()>() <= (*b).cast::<()>();
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:66:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:78:17
|
||||
|
|
||||
LL | let _ = a > b;
|
||||
| ^^^^^
|
||||
@ -231,7 +264,7 @@ LL | let _ = (*a).cast::<()>() > (*b).cast::<()>();
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:68:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:80:17
|
||||
|
|
||||
LL | let _ = a >= b;
|
||||
| ^^^^^^
|
||||
@ -242,7 +275,7 @@ LL | let _ = (*a).cast::<()>() >= (*b).cast::<()>();
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:71:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:83:17
|
||||
|
|
||||
LL | let _ = PartialEq::eq(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
@ -253,7 +286,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b);
|
||||
| ~~~~~~~~~~~~~~~~~~~ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:73:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:85:17
|
||||
|
|
||||
LL | let _ = PartialEq::ne(a, b);
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
@ -264,7 +297,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b);
|
||||
| ~~~~~~~~~~~~~~~~~~~~ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:75:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:87:17
|
||||
|
|
||||
LL | let _ = PartialEq::eq(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -275,7 +308,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b);
|
||||
| ~~~~~~~~~~~~~~~~~~~ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:77:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:89:17
|
||||
|
|
||||
LL | let _ = PartialEq::ne(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -286,7 +319,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b);
|
||||
| ~~~~~~~~~~~~~~~~~~~~ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:79:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:91:17
|
||||
|
|
||||
LL | let _ = a.eq(b);
|
||||
| ^^^^^^^
|
||||
@ -297,7 +330,7 @@ LL | let _ = std::ptr::addr_eq(*a, *b);
|
||||
| +++++++++++++++++++ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:81:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:93:17
|
||||
|
|
||||
LL | let _ = a.ne(b);
|
||||
| ^^^^^^^
|
||||
@ -308,7 +341,7 @@ LL | let _ = !std::ptr::addr_eq(*a, *b);
|
||||
| ++++++++++++++++++++ ~~~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:83:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:95:17
|
||||
|
|
||||
LL | let _ = a.cmp(&b);
|
||||
| ^^^^^^^^^
|
||||
@ -319,7 +352,7 @@ LL | let _ = (*a).cast::<()>().cmp(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:85:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:97:17
|
||||
|
|
||||
LL | let _ = a.partial_cmp(&b);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
@ -330,7 +363,7 @@ LL | let _ = (*a).cast::<()>().partial_cmp(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:87:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:99:17
|
||||
|
|
||||
LL | let _ = a.le(&b);
|
||||
| ^^^^^^^^
|
||||
@ -341,7 +374,7 @@ LL | let _ = (*a).cast::<()>().le(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:89:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:101:17
|
||||
|
|
||||
LL | let _ = a.lt(&b);
|
||||
| ^^^^^^^^
|
||||
@ -352,7 +385,7 @@ LL | let _ = (*a).cast::<()>().lt(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:91:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:103:17
|
||||
|
|
||||
LL | let _ = a.ge(&b);
|
||||
| ^^^^^^^^
|
||||
@ -363,7 +396,7 @@ LL | let _ = (*a).cast::<()>().ge(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:93:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:105:17
|
||||
|
|
||||
LL | let _ = a.gt(&b);
|
||||
| ^^^^^^^^
|
||||
@ -374,7 +407,7 @@ LL | let _ = (*a).cast::<()>().gt(&(*b).cast::<()>());
|
||||
| ++ ++++++++++++++ ++ ++++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:98:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:110:13
|
||||
|
|
||||
LL | let _ = s == s;
|
||||
| ^^^^^^
|
||||
@ -389,7 +422,7 @@ LL | let _ = std::ptr::eq(s, s);
|
||||
| +++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:102:13
|
||||
--> $DIR/wide_pointer_comparisons.rs:114:13
|
||||
|
|
||||
LL | let _ = s == s;
|
||||
| ^^^^^^
|
||||
@ -404,7 +437,7 @@ LL | let _ = std::ptr::eq(s, s);
|
||||
| +++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:106:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:118:17
|
||||
|
|
||||
LL | let _ = a == b;
|
||||
| ^^^^^^
|
||||
@ -419,7 +452,7 @@ LL | let _ = std::ptr::eq(a, b);
|
||||
| +++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:108:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:120:17
|
||||
|
|
||||
LL | let _ = a != b;
|
||||
| ^^^^^^
|
||||
@ -434,7 +467,7 @@ LL | let _ = !std::ptr::eq(a, b);
|
||||
| ++++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:110:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:122:17
|
||||
|
|
||||
LL | let _ = a < b;
|
||||
| ^^^^^
|
||||
@ -445,7 +478,7 @@ LL | let _ = a.cast::<()>() < b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:112:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:124:17
|
||||
|
|
||||
LL | let _ = a <= b;
|
||||
| ^^^^^^
|
||||
@ -456,7 +489,7 @@ LL | let _ = a.cast::<()>() <= b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:114:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:126:17
|
||||
|
|
||||
LL | let _ = a > b;
|
||||
| ^^^^^
|
||||
@ -467,7 +500,7 @@ LL | let _ = a.cast::<()>() > b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:116:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:128:17
|
||||
|
|
||||
LL | let _ = a >= b;
|
||||
| ^^^^^^
|
||||
@ -478,7 +511,7 @@ LL | let _ = a.cast::<()>() >= b.cast::<()>();
|
||||
| +++++++++++++ +++++++++++++
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:119:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:131:17
|
||||
|
|
||||
LL | let _ = PartialEq::eq(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -493,7 +526,7 @@ LL | let _ = std::ptr::eq(a, b);
|
||||
| ~~~~~~~~~~~~~ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:121:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:133:17
|
||||
|
|
||||
LL | let _ = PartialEq::ne(&a, &b);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -508,7 +541,7 @@ LL | let _ = !std::ptr::eq(a, b);
|
||||
| ~~~~~~~~~~~~~~ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:123:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:135:17
|
||||
|
|
||||
LL | let _ = a.eq(&b);
|
||||
| ^^^^^^^^
|
||||
@ -523,7 +556,7 @@ LL | let _ = std::ptr::eq(a, b);
|
||||
| +++++++++++++ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:125:17
|
||||
--> $DIR/wide_pointer_comparisons.rs:137:17
|
||||
|
|
||||
LL | let _ = a.ne(&b);
|
||||
| ^^^^^^^^
|
||||
@ -538,7 +571,7 @@ LL | let _ = !std::ptr::eq(a, b);
|
||||
| ++++++++++++++ ~ ~
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:130:9
|
||||
--> $DIR/wide_pointer_comparisons.rs:142:9
|
||||
|
|
||||
LL | &*a == &*b
|
||||
| ^^^^^^^^^^
|
||||
@ -553,7 +586,7 @@ LL | std::ptr::eq(*a, *b)
|
||||
| ~~~~~~~~~~~~~ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:141:14
|
||||
--> $DIR/wide_pointer_comparisons.rs:153:14
|
||||
|
|
||||
LL | cmp!(a, b);
|
||||
| ^^^^
|
||||
@ -564,7 +597,7 @@ LL | cmp!(std::ptr::addr_eq(a, b));
|
||||
| ++++++++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:147:39
|
||||
--> $DIR/wide_pointer_comparisons.rs:159:39
|
||||
|
|
||||
LL | ($a:ident, $b:ident) => { $a == $b }
|
||||
| ^^^^^^^^
|
||||
@ -579,7 +612,7 @@ LL | ($a:ident, $b:ident) => { std::ptr::addr_eq($a, $b) }
|
||||
| ++++++++++++++++++ ~ +
|
||||
|
||||
warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
|
||||
--> $DIR/wide_pointer_comparisons.rs:157:37
|
||||
--> $DIR/wide_pointer_comparisons.rs:169:37
|
||||
|
|
||||
LL | ($a:expr, $b:expr) => { $a == $b }
|
||||
| ^^
|
||||
@ -591,5 +624,5 @@ LL | cmp!(&a, &b);
|
||||
= help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
|
||||
= note: this warning originates in the macro `cmp` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
warning: 50 warnings emitted
|
||||
warning: 53 warnings emitted
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//@ run-pass
|
||||
//@ compile-flags: -C debug-assertions
|
||||
|
||||
#![feature(strict_provenance, pointer_is_aligned)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
#[repr(packed)]
|
||||
struct Misaligner {
|
||||
|
@ -73,15 +73,15 @@ LL | 5 < String::new();
|
||||
|
|
||||
= help: the trait `PartialOrd<String>` is not implemented for `{integer}`
|
||||
= help: the following other types implement trait `PartialOrd<Rhs>`:
|
||||
f128
|
||||
f16
|
||||
f32
|
||||
f64
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
i8
|
||||
isize
|
||||
and 6 others
|
||||
and 8 others
|
||||
|
||||
error[E0277]: can't compare `{integer}` with `Result<{integer}, _>`
|
||||
--> $DIR/binops.rs:7:7
|
||||
@ -91,15 +91,15 @@ LL | 6 == Ok(1);
|
||||
|
|
||||
= help: the trait `PartialEq<Result<{integer}, _>>` is not implemented for `{integer}`
|
||||
= help: the following other types implement trait `PartialEq<Rhs>`:
|
||||
f128
|
||||
f16
|
||||
f32
|
||||
f64
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
i8
|
||||
isize
|
||||
and 6 others
|
||||
and 8 others
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
//@[cfi]compile-flags: -Clto -Ccodegen-units=1
|
||||
//@[kcfi]needs-llvm-components: x86
|
||||
//@[kcfi]compile-flags: -Zsanitizer=kcfi --cfg kcfi --target x86_64-unknown-none
|
||||
//@[kcfi]compile-flags: -C panic=abort
|
||||
//@[leak]needs-sanitizer-leak
|
||||
//@[leak]compile-flags: -Zsanitizer=leak --cfg leak
|
||||
//@[memory]needs-sanitizer-memory
|
||||
|
33
tests/ui/sanitizer/cfi-async-closures.rs
Normal file
33
tests/ui/sanitizer/cfi-async-closures.rs
Normal file
@ -0,0 +1,33 @@
|
||||
// Check various forms of dynamic closure calls
|
||||
|
||||
//@ edition: 2021
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
#![feature(async_closure)]
|
||||
#![feature(async_fn_traits)]
|
||||
|
||||
use std::ops::AsyncFn;
|
||||
|
||||
#[inline(never)]
|
||||
fn identity<T>(x: T) -> T { x }
|
||||
|
||||
// We can't actually create a `dyn AsyncFn()`, because it's not object-safe, but we should check
|
||||
// that we don't bug out when we encounter one.
|
||||
|
||||
fn main() {
|
||||
let f = identity(async || ());
|
||||
let _ = f.async_call(());
|
||||
let _ = f();
|
||||
let g: Box<dyn FnOnce() -> _> = Box::new(f) as _;
|
||||
let _ = g();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Tests that converting a closure to a function pointer works
|
||||
// The notable thing being tested here is that when the closure does not capture anything,
|
||||
// the call method from its Fn trait takes a ZST representing its environment. The compiler then
|
||||
// uses the assumption that the ZST is non-passed to reify this into a function pointer.
|
||||
//
|
||||
// This checks that the reified function pointer will have the expected alias set at its call-site.
|
||||
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ run-pass
|
||||
|
||||
pub fn main() {
|
||||
let f: &fn() = &((|| ()) as _);
|
||||
f();
|
||||
}
|
83
tests/ui/sanitizer/cfi-closures.rs
Normal file
83
tests/ui/sanitizer/cfi-closures.rs
Normal file
@ -0,0 +1,83 @@
|
||||
// Check various forms of dynamic closure calls
|
||||
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off
|
||||
//@ compile-flags: --test
|
||||
//@ run-pass
|
||||
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(cfg_sanitize)]
|
||||
|
||||
fn foo<'a, T>() -> Box<dyn Fn(&'a T) -> &'a T> {
|
||||
Box::new(|x| x)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_fn_with_params() {
|
||||
let x = 3;
|
||||
let f = foo();
|
||||
f(&x);
|
||||
// FIXME remove once drops are working.
|
||||
std::mem::forget(f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_fn_trait() {
|
||||
let f: &(dyn Fn()) = &(|| {}) as _;
|
||||
f.call(());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_ptr_cast() {
|
||||
let f: &fn() = &((|| ()) as _);
|
||||
f();
|
||||
}
|
||||
|
||||
fn use_fnmut<F: FnMut()>(mut f: F) {
|
||||
f()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_to_fnmut() {
|
||||
let f: &(dyn Fn()) = &(|| {}) as _;
|
||||
use_fnmut(f);
|
||||
}
|
||||
|
||||
fn hrtb_helper(f: &dyn for<'a> Fn(&'a usize)) {
|
||||
f(&10)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hrtb_fn() {
|
||||
hrtb_helper((&|x: &usize| println!("{}", *x)) as _)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fnonce() {
|
||||
let f: Box<dyn FnOnce()> = Box::new(|| {}) as _;
|
||||
f();
|
||||
}
|
||||
|
||||
fn use_closure<C>(call: extern "rust-call" fn(&C, ()) -> i32, f: &C) -> i32 {
|
||||
call(f, ())
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FIXME after KCFI reify support is added, remove this
|
||||
// It will appear to work if you test locally, set -C opt-level=0 to see it fail.
|
||||
#[cfg_attr(sanitize = "kcfi", ignore)]
|
||||
fn closure_addr_taken() {
|
||||
let x = 3i32;
|
||||
let f = || x;
|
||||
let call = Fn::<()>::call;
|
||||
use_closure(call, &f);
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
use std::sync::Arc;
|
||||
|
30
tests/ui/sanitizer/cfi-coroutine.rs
Normal file
30
tests/ui/sanitizer/cfi-coroutine.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// Verifies that we can call dynamic coroutines
|
||||
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -Z panic-abort-tests -C prefer-dynamic=off
|
||||
//@ compile-flags: --test
|
||||
//@ run-pass
|
||||
|
||||
#![feature(coroutines)]
|
||||
#![feature(coroutine_trait)]
|
||||
|
||||
use std::ops::{Coroutine, CoroutineState};
|
||||
use std::pin::{pin, Pin};
|
||||
|
||||
fn main() {
|
||||
let mut coro = |x: i32| {
|
||||
yield x;
|
||||
"done"
|
||||
};
|
||||
let mut abstract_coro: Pin<&mut dyn Coroutine<i32,Yield=i32,Return=&'static str>> = pin!(coro);
|
||||
assert_eq!(abstract_coro.as_mut().resume(2), CoroutineState::Yielded(2));
|
||||
assert_eq!(abstract_coro.as_mut().resume(0), CoroutineState::Complete("done"));
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
73
tests/ui/sanitizer/cfi-supertraits.rs
Normal file
73
tests/ui/sanitizer/cfi-supertraits.rs
Normal file
@ -0,0 +1,73 @@
|
||||
#![feature(trait_upcasting)]
|
||||
// Check that super-traits are callable.
|
||||
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
trait Parent1 {
|
||||
type P1;
|
||||
fn p1(&self) -> Self::P1;
|
||||
}
|
||||
|
||||
trait Parent2 {
|
||||
type P2;
|
||||
fn p2(&self) -> Self::P2;
|
||||
}
|
||||
|
||||
trait Child : Parent1 + Parent2 {
|
||||
type C;
|
||||
fn c(&self) -> Self::C;
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Parent1 for Foo {
|
||||
type P1 = u16;
|
||||
fn p1(&self) -> Self::P1 {
|
||||
println!("p1");
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl Parent2 for Foo {
|
||||
type P2 = u32;
|
||||
fn p2(&self) -> Self::P2 {
|
||||
println!("p2");
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl Child for Foo {
|
||||
type C = u8;
|
||||
fn c(&self) -> Self::C {
|
||||
println!("c");
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Child can access its own methods and super methods.
|
||||
let x = &Foo as &dyn Child<C=u8,P1=u16,P2=u32>;
|
||||
x.c();
|
||||
x.p1();
|
||||
x.p2();
|
||||
// Parents can be created and access their methods.
|
||||
let y = &Foo as &dyn Parent1<P1=u16>;
|
||||
y.p1();
|
||||
let z = &Foo as &dyn Parent2<P2=u32>;
|
||||
z.p2();
|
||||
// Trait upcasting works
|
||||
let x1 = x as &dyn Parent1<P1=u16>;
|
||||
x1.p1();
|
||||
let x2 = x as &dyn Parent2<P2=u32>;
|
||||
x2.p2();
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
trait Foo {
|
||||
|
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
//~^ ERROR expected a pattern, found an expression
|
||||
//~| ERROR cannot find type `T` in this scope
|
||||
//~| ERROR type and const arguments are not allowed on builtin type `str`
|
||||
//~| ERROR expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes`
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
error: expected a pattern, found an expression
|
||||
--> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:31
|
||||
|
|
||||
LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
| ^^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns
|
||||
|
||||
error[E0412]: cannot find type `T` in this scope
|
||||
--> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:55
|
||||
|
|
||||
LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
| ^ not found in this scope
|
||||
|
||||
error[E0109]: type and const arguments are not allowed on builtin type `str`
|
||||
--> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:15
|
||||
|
|
||||
LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
| --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ type and const arguments not allowed
|
||||
| |
|
||||
| not allowed on builtin type `str`
|
||||
|
|
||||
help: primitive type `str` doesn't have generic parameters
|
||||
|
|
||||
LL - let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
LL + let str::as_bytes;
|
||||
|
|
||||
|
||||
error[E0533]: expected unit struct, unit variant or constant, found associated function `str<, T>::as_bytes`
|
||||
--> $DIR/ensure-overriding-bindings-in-pattern-with-ty-err-doesnt-ice.rs:2:9
|
||||
|
|
||||
LL | let str::<{fn str() { let str::T>>::as_bytes; }}, T>::as_bytes;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a unit struct, unit variant or constant
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0109, E0412, E0533.
|
||||
For more information about an error, try `rustc --explain E0109`.
|
@ -4,7 +4,7 @@
|
||||
#![allow(dead_code)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(never_type)]
|
||||
#![feature(pointer_is_aligned)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::mem::size_of;
|
||||
|
Loading…
Reference in New Issue
Block a user