Auto merge of #103623 - matthiaskrgr:rollup-318yc1t, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #103035 (Even nicer errors from assert_unsafe_precondition) - #103106 (Try to say that memory outside the AM is always exposed) - #103475 (Make param index generation a bit more robust) - #103525 (Move a wf-check into the site where the value is instantiated) - #103564 (library: allow some unused things in Miri) - #103586 (Process registered region obligation in `resolve_regions_with_wf_tys`) - #103592 (rustdoc: remove redundant CSS selector `.notable-traits .notable`) - #103593 (Remove an unused parser function (`Expr::returns`)) - #103611 (Add test for issue 103574) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0da281b606
36 changed files with 431 additions and 140 deletions
|
@ -1112,24 +1112,6 @@ pub struct Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Returns `true` if this expression would be valid somewhere that expects a value;
|
|
||||||
/// for example, an `if` condition.
|
|
||||||
pub fn returns(&self) -> bool {
|
|
||||||
if let ExprKind::Block(ref block, _) = self.kind {
|
|
||||||
match block.stmts.last().map(|last_stmt| &last_stmt.kind) {
|
|
||||||
// Implicit return
|
|
||||||
Some(StmtKind::Expr(_)) => true,
|
|
||||||
// Last statement is an explicit return?
|
|
||||||
Some(StmtKind::Semi(expr)) => matches!(expr.kind, ExprKind::Ret(_)),
|
|
||||||
// This is a block that doesn't end in either an implicit or explicit return.
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is not a block, it is a value.
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is this expr either `N`, or `{ N }`.
|
/// Is this expr either `N`, or `{ N }`.
|
||||||
///
|
///
|
||||||
/// If this is not the case, name resolution does not resolve `N` when using
|
/// If this is not the case, name resolution does not resolve `N` when using
|
||||||
|
|
|
@ -137,3 +137,12 @@ hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(l
|
||||||
hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
|
hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
|
||||||
|
|
||||||
hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
|
hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
|
||||||
|
|
||||||
|
hir_analysis_const_impl_for_non_const_trait =
|
||||||
|
const `impl` for trait `{$trait_name}` which is not marked with `#[const_trait]`
|
||||||
|
.suggestion = mark `{$trait_name}` as const
|
||||||
|
.note = marking a trait with `#[const_trait]` ensures all default method bodies are `const`
|
||||||
|
.adding = adding a non-const method body in the future would be a breaking change
|
||||||
|
|
||||||
|
hir_analysis_const_bound_for_non_const_trait =
|
||||||
|
~const can only be applied to `#[const_trait]` traits
|
||||||
|
|
|
@ -36,7 +36,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::lev_distance::find_best_match_for_name;
|
use rustc_span::lev_distance::find_best_match_for_name;
|
||||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::{sym, Span};
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
use rustc_trait_selection::traits::astconv_object_safety_violations;
|
use rustc_trait_selection::traits::astconv_object_safety_violations;
|
||||||
|
@ -275,6 +275,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
item_segment.args(),
|
item_segment.args(),
|
||||||
item_segment.infer_args,
|
item_segment.infer_args,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
if let Some(b) = item_segment.args().bindings.first() {
|
if let Some(b) = item_segment.args().bindings.first() {
|
||||||
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
|
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
|
||||||
|
@ -324,6 +325,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
generic_args: &'a hir::GenericArgs<'_>,
|
generic_args: &'a hir::GenericArgs<'_>,
|
||||||
infer_args: bool,
|
infer_args: bool,
|
||||||
self_ty: Option<Ty<'tcx>>,
|
self_ty: Option<Ty<'tcx>>,
|
||||||
|
constness: Option<ty::BoundConstness>,
|
||||||
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
|
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
|
||||||
// If the type is parameterized by this region, then replace this
|
// If the type is parameterized by this region, then replace this
|
||||||
// region with the current anon region binding (in other words,
|
// region with the current anon region binding (in other words,
|
||||||
|
@ -534,6 +536,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
&mut substs_ctx,
|
&mut substs_ctx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(ty::BoundConstness::ConstIfConst) = constness
|
||||||
|
&& generics.has_self && !tcx.has_attr(def_id, sym::const_trait)
|
||||||
|
{
|
||||||
|
tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } );
|
||||||
|
}
|
||||||
|
|
||||||
(substs, arg_count)
|
(substs, arg_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,6 +609,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
item_segment.args(),
|
item_segment.args(),
|
||||||
item_segment.infer_args,
|
item_segment.infer_args,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(b) = item_segment.args().bindings.first() {
|
if let Some(b) = item_segment.args().bindings.first() {
|
||||||
|
@ -620,6 +629,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
&self,
|
&self,
|
||||||
trait_ref: &hir::TraitRef<'_>,
|
trait_ref: &hir::TraitRef<'_>,
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
|
constness: ty::BoundConstness,
|
||||||
) -> ty::TraitRef<'tcx> {
|
) -> ty::TraitRef<'tcx> {
|
||||||
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
|
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
|
||||||
|
|
||||||
|
@ -629,6 +639,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
self_ty,
|
self_ty,
|
||||||
trait_ref.path.segments.last().unwrap(),
|
trait_ref.path.segments.last().unwrap(),
|
||||||
true,
|
true,
|
||||||
|
Some(constness),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,6 +666,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
args,
|
args,
|
||||||
infer_args,
|
infer_args,
|
||||||
Some(self_ty),
|
Some(self_ty),
|
||||||
|
Some(constness),
|
||||||
);
|
);
|
||||||
|
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
|
@ -680,6 +692,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
speculative,
|
speculative,
|
||||||
&mut dup_bindings,
|
&mut dup_bindings,
|
||||||
binding_span.unwrap_or(binding.span),
|
binding_span.unwrap_or(binding.span),
|
||||||
|
constness,
|
||||||
);
|
);
|
||||||
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
|
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
|
||||||
}
|
}
|
||||||
|
@ -783,6 +796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
trait_segment: &hir::PathSegment<'_>,
|
trait_segment: &hir::PathSegment<'_>,
|
||||||
is_impl: bool,
|
is_impl: bool,
|
||||||
|
constness: Option<ty::BoundConstness>,
|
||||||
) -> ty::TraitRef<'tcx> {
|
) -> ty::TraitRef<'tcx> {
|
||||||
let (substs, _) = self.create_substs_for_ast_trait_ref(
|
let (substs, _) = self.create_substs_for_ast_trait_ref(
|
||||||
span,
|
span,
|
||||||
|
@ -790,6 +804,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
self_ty,
|
self_ty,
|
||||||
trait_segment,
|
trait_segment,
|
||||||
is_impl,
|
is_impl,
|
||||||
|
constness,
|
||||||
);
|
);
|
||||||
if let Some(b) = trait_segment.args().bindings.first() {
|
if let Some(b) = trait_segment.args().bindings.first() {
|
||||||
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
|
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
|
||||||
|
@ -805,6 +820,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
self_ty: Ty<'tcx>,
|
self_ty: Ty<'tcx>,
|
||||||
trait_segment: &'a hir::PathSegment<'a>,
|
trait_segment: &'a hir::PathSegment<'a>,
|
||||||
is_impl: bool,
|
is_impl: bool,
|
||||||
|
constness: Option<ty::BoundConstness>,
|
||||||
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
|
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
|
||||||
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
|
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
|
||||||
|
|
||||||
|
@ -816,6 +832,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
trait_segment.args(),
|
trait_segment.args(),
|
||||||
trait_segment.infer_args,
|
trait_segment.infer_args,
|
||||||
Some(self_ty),
|
Some(self_ty),
|
||||||
|
constness,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1027,6 +1044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
speculative: bool,
|
speculative: bool,
|
||||||
dup_bindings: &mut FxHashMap<DefId, Span>,
|
dup_bindings: &mut FxHashMap<DefId, Span>,
|
||||||
path_span: Span,
|
path_span: Span,
|
||||||
|
constness: ty::BoundConstness,
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
// Given something like `U: SomeTrait<T = X>`, we want to produce a
|
// Given something like `U: SomeTrait<T = X>`, we want to produce a
|
||||||
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
|
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
|
||||||
|
@ -1122,10 +1140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
trait_ref.substs,
|
trait_ref.substs,
|
||||||
);
|
);
|
||||||
|
|
||||||
debug!(
|
debug!(?substs_trait_ref_and_assoc_item);
|
||||||
"add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
|
|
||||||
substs_trait_ref_and_assoc_item
|
|
||||||
);
|
|
||||||
|
|
||||||
ty::ProjectionTy {
|
ty::ProjectionTy {
|
||||||
item_def_id: assoc_item.def_id,
|
item_def_id: assoc_item.def_id,
|
||||||
|
@ -1146,8 +1161,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
tcx.collect_constrained_late_bound_regions(&projection_ty);
|
tcx.collect_constrained_late_bound_regions(&projection_ty);
|
||||||
let late_bound_in_ty =
|
let late_bound_in_ty =
|
||||||
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
|
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
|
||||||
debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref);
|
debug!(?late_bound_in_trait_ref);
|
||||||
debug!("late_bound_in_ty = {:?}", late_bound_in_ty);
|
debug!(?late_bound_in_ty);
|
||||||
|
|
||||||
// FIXME: point at the type params that don't have appropriate lifetimes:
|
// FIXME: point at the type params that don't have appropriate lifetimes:
|
||||||
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
|
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
|
||||||
|
@ -1648,6 +1663,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
|
|
||||||
// Checks that `bounds` contains exactly one element and reports appropriate
|
// Checks that `bounds` contains exactly one element and reports appropriate
|
||||||
// errors otherwise.
|
// errors otherwise.
|
||||||
|
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
|
||||||
fn one_bound_for_assoc_type<I>(
|
fn one_bound_for_assoc_type<I>(
|
||||||
&self,
|
&self,
|
||||||
all_candidates: impl Fn() -> I,
|
all_candidates: impl Fn() -> I,
|
||||||
|
@ -1677,10 +1693,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
return Err(reported);
|
return Err(reported);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
debug!("one_bound_for_assoc_type: bound = {:?}", bound);
|
debug!(?bound);
|
||||||
|
|
||||||
if let Some(bound2) = next_cand {
|
if let Some(bound2) = next_cand {
|
||||||
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
|
debug!(?bound2);
|
||||||
|
|
||||||
let is_equality = is_equality();
|
let is_equality = is_equality();
|
||||||
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
|
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
|
||||||
|
@ -1776,6 +1792,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
// parameter or `Self`.
|
// parameter or `Self`.
|
||||||
// NOTE: When this function starts resolving `Trait::AssocTy` successfully
|
// NOTE: When this function starts resolving `Trait::AssocTy` successfully
|
||||||
// it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
|
// it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
|
||||||
|
#[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)]
|
||||||
pub fn associated_path_to_ty(
|
pub fn associated_path_to_ty(
|
||||||
&self,
|
&self,
|
||||||
hir_ref_id: hir::HirId,
|
hir_ref_id: hir::HirId,
|
||||||
|
@ -1793,8 +1810,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
Res::Err
|
Res::Err
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);
|
|
||||||
|
|
||||||
// Check if we have an enum variant.
|
// Check if we have an enum variant.
|
||||||
let mut variant_resolution = None;
|
let mut variant_resolution = None;
|
||||||
if let ty::Adt(adt_def, _) = qself_ty.kind() {
|
if let ty::Adt(adt_def, _) = qself_ty.kind() {
|
||||||
|
@ -2050,6 +2065,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
item_def_id: DefId,
|
item_def_id: DefId,
|
||||||
trait_segment: &hir::PathSegment<'_>,
|
trait_segment: &hir::PathSegment<'_>,
|
||||||
item_segment: &hir::PathSegment<'_>,
|
item_segment: &hir::PathSegment<'_>,
|
||||||
|
constness: ty::BoundConstness,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
|
|
||||||
|
@ -2094,8 +2110,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
|
|
||||||
debug!("qpath_to_ty: self_type={:?}", self_ty);
|
debug!("qpath_to_ty: self_type={:?}", self_ty);
|
||||||
|
|
||||||
let trait_ref =
|
let trait_ref = self.ast_path_to_mono_trait_ref(
|
||||||
self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
|
span,
|
||||||
|
trait_def_id,
|
||||||
|
self_ty,
|
||||||
|
trait_segment,
|
||||||
|
false,
|
||||||
|
Some(constness),
|
||||||
|
);
|
||||||
|
|
||||||
let item_substs = self.create_substs_for_associated_item(
|
let item_substs = self.create_substs_for_associated_item(
|
||||||
span,
|
span,
|
||||||
|
@ -2534,12 +2556,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
Res::Def(DefKind::AssocTy, def_id) => {
|
Res::Def(DefKind::AssocTy, def_id) => {
|
||||||
debug_assert!(path.segments.len() >= 2);
|
debug_assert!(path.segments.len() >= 2);
|
||||||
self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
|
self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
|
||||||
|
// HACK: until we support `<Type as ~const Trait>`, assume all of them are.
|
||||||
|
let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
|
||||||
|
ty::BoundConstness::ConstIfConst
|
||||||
|
} else {
|
||||||
|
ty::BoundConstness::NotConst
|
||||||
|
};
|
||||||
self.qpath_to_ty(
|
self.qpath_to_ty(
|
||||||
span,
|
span,
|
||||||
opt_self_ty,
|
opt_self_ty,
|
||||||
def_id,
|
def_id,
|
||||||
&path.segments[path.segments.len() - 2],
|
&path.segments[path.segments.len() - 2],
|
||||||
path.segments.last().unwrap(),
|
path.segments.last().unwrap(),
|
||||||
|
constness,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Res::PrimTy(prim_ty) => {
|
Res::PrimTy(prim_ty) => {
|
||||||
|
@ -2658,6 +2687,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
&GenericArgs::none(),
|
&GenericArgs::none(),
|
||||||
true,
|
true,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
|
EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
|
||||||
.subst(tcx, substs)
|
.subst(tcx, substs)
|
||||||
|
@ -2766,6 +2796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)]
|
||||||
pub fn ty_of_fn(
|
pub fn ty_of_fn(
|
||||||
&self,
|
&self,
|
||||||
hir_id: hir::HirId,
|
hir_id: hir::HirId,
|
||||||
|
@ -2775,8 +2806,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
generics: Option<&hir::Generics<'_>>,
|
generics: Option<&hir::Generics<'_>>,
|
||||||
hir_ty: Option<&hir::Ty<'_>>,
|
hir_ty: Option<&hir::Ty<'_>>,
|
||||||
) -> ty::PolyFnSig<'tcx> {
|
) -> ty::PolyFnSig<'tcx> {
|
||||||
debug!("ty_of_fn");
|
|
||||||
|
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let bound_vars = tcx.late_bound_vars(hir_id);
|
let bound_vars = tcx.late_bound_vars(hir_id);
|
||||||
debug!(?bound_vars);
|
debug!(?bound_vars);
|
||||||
|
@ -2826,7 +2855,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
|
hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("ty_of_fn: output_ty={:?}", output_ty);
|
debug!(?output_ty);
|
||||||
|
|
||||||
let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
|
let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
|
||||||
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
|
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
|
||||||
|
@ -2903,8 +2932,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
|
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
|
||||||
hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
|
hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
|
||||||
|
|
||||||
let trait_ref =
|
let trait_ref = self.instantiate_mono_trait_ref(
|
||||||
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
|
i.of_trait.as_ref()?,
|
||||||
|
self.ast_ty_to_ty(i.self_ty),
|
||||||
|
ty::BoundConstness::NotConst,
|
||||||
|
);
|
||||||
|
|
||||||
let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
|
let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
|
||||||
tcx,
|
tcx,
|
||||||
|
|
|
@ -713,6 +713,10 @@ fn resolve_regions_with_wf_tys<'tcx>(
|
||||||
|
|
||||||
add_constraints(&infcx, region_bound_pairs);
|
add_constraints(&infcx, region_bound_pairs);
|
||||||
|
|
||||||
|
infcx.process_registered_region_obligations(
|
||||||
|
outlives_environment.region_bound_pairs(),
|
||||||
|
param_env,
|
||||||
|
);
|
||||||
let errors = infcx.resolve_regions(&outlives_environment);
|
let errors = infcx.resolve_regions(&outlives_environment);
|
||||||
|
|
||||||
debug!(?errors, "errors");
|
debug!(?errors, "errors");
|
||||||
|
|
|
@ -1143,7 +1143,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
|
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
|
||||||
// Do not try to inference the return type for a impl method coming from a trait
|
// Do not try to infer the return type for a impl method coming from a trait
|
||||||
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
|
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
|
||||||
tcx.hir().get(tcx.hir().get_parent_node(hir_id))
|
tcx.hir().get(tcx.hir().get_parent_node(hir_id))
|
||||||
&& i.of_trait.is_some()
|
&& i.of_trait.is_some()
|
||||||
|
@ -1286,15 +1286,46 @@ fn infer_return_ty_for_fn_sig<'tcx>(
|
||||||
|
|
||||||
fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
|
fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
|
||||||
let icx = ItemCtxt::new(tcx, def_id);
|
let icx = ItemCtxt::new(tcx, def_id);
|
||||||
match tcx.hir().expect_item(def_id.expect_local()).kind {
|
let item = tcx.hir().expect_item(def_id.expect_local());
|
||||||
|
match item.kind {
|
||||||
hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
|
hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
|
||||||
let selfty = tcx.type_of(def_id);
|
let selfty = tcx.type_of(def_id);
|
||||||
<dyn AstConv<'_>>::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty)
|
<dyn AstConv<'_>>::instantiate_mono_trait_ref(
|
||||||
|
&icx,
|
||||||
|
ast_trait_ref,
|
||||||
|
selfty,
|
||||||
|
check_impl_constness(tcx, impl_.constness, ast_trait_ref),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
_ => bug!(),
|
_ => bug!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_impl_constness(
|
||||||
|
tcx: TyCtxt<'_>,
|
||||||
|
constness: hir::Constness,
|
||||||
|
ast_trait_ref: &hir::TraitRef<'_>,
|
||||||
|
) -> ty::BoundConstness {
|
||||||
|
match constness {
|
||||||
|
hir::Constness::Const => {
|
||||||
|
if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) {
|
||||||
|
let trait_name = tcx.item_name(trait_def_id).to_string();
|
||||||
|
tcx.sess.emit_err(errors::ConstImplForNonConstTrait {
|
||||||
|
trait_ref_span: ast_trait_ref.path.span,
|
||||||
|
trait_name,
|
||||||
|
local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()),
|
||||||
|
marking: (),
|
||||||
|
adding: (),
|
||||||
|
});
|
||||||
|
ty::BoundConstness::NotConst
|
||||||
|
} else {
|
||||||
|
ty::BoundConstness::ConstIfConst
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hir::Constness::NotConst => ty::BoundConstness::NotConst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
|
fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
|
||||||
let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
|
let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
|
||||||
let item = tcx.hir().expect_item(def_id.expect_local());
|
let item = tcx.hir().expect_item(def_id.expect_local());
|
||||||
|
|
|
@ -249,6 +249,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||||
// Now create the real type and const parameters.
|
// Now create the real type and const parameters.
|
||||||
let type_start = own_start - has_self as u32 + params.len() as u32;
|
let type_start = own_start - has_self as u32 + params.len() as u32;
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
let mut next_index = || {
|
||||||
|
let prev = i;
|
||||||
|
i += 1;
|
||||||
|
prev as u32 + type_start
|
||||||
|
};
|
||||||
|
|
||||||
const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
|
const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
|
||||||
`struct`, `enum`, `type`, or `trait` definitions";
|
`struct`, `enum`, `type`, or `trait` definitions";
|
||||||
|
@ -278,15 +283,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||||
|
|
||||||
let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
|
let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
|
||||||
|
|
||||||
let param_def = ty::GenericParamDef {
|
Some(ty::GenericParamDef {
|
||||||
index: type_start + i as u32,
|
index: next_index(),
|
||||||
name: param.name.ident().name,
|
name: param.name.ident().name,
|
||||||
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
|
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
|
||||||
pure_wrt_drop: param.pure_wrt_drop,
|
pure_wrt_drop: param.pure_wrt_drop,
|
||||||
kind,
|
kind,
|
||||||
};
|
})
|
||||||
i += 1;
|
|
||||||
Some(param_def)
|
|
||||||
}
|
}
|
||||||
GenericParamKind::Const { default, .. } => {
|
GenericParamKind::Const { default, .. } => {
|
||||||
if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
|
if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
|
||||||
|
@ -297,15 +300,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let param_def = ty::GenericParamDef {
|
Some(ty::GenericParamDef {
|
||||||
index: type_start + i as u32,
|
index: next_index(),
|
||||||
name: param.name.ident().name,
|
name: param.name.ident().name,
|
||||||
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
|
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
|
||||||
pure_wrt_drop: param.pure_wrt_drop,
|
pure_wrt_drop: param.pure_wrt_drop,
|
||||||
kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
|
kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
|
||||||
};
|
})
|
||||||
i += 1;
|
|
||||||
Some(param_def)
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -323,8 +324,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||||
&["<closure_kind>", "<closure_signature>", "<upvars>"][..]
|
&["<closure_kind>", "<closure_signature>", "<upvars>"][..]
|
||||||
};
|
};
|
||||||
|
|
||||||
params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef {
|
params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
|
||||||
index: type_start + i as u32,
|
index: next_index(),
|
||||||
name: Symbol::intern(arg),
|
name: Symbol::intern(arg),
|
||||||
def_id,
|
def_id,
|
||||||
pure_wrt_drop: false,
|
pure_wrt_drop: false,
|
||||||
|
@ -337,7 +338,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||||
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
|
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
|
||||||
if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
|
if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
|
||||||
params.push(ty::GenericParamDef {
|
params.push(ty::GenericParamDef {
|
||||||
index: type_start,
|
index: next_index(),
|
||||||
name: Symbol::intern("<const_ty>"),
|
name: Symbol::intern("<const_ty>"),
|
||||||
def_id,
|
def_id,
|
||||||
pure_wrt_drop: false,
|
pure_wrt_drop: false,
|
||||||
|
|
|
@ -249,3 +249,24 @@ pub struct ExpectedUsedSymbol {
|
||||||
#[primary_span]
|
#[primary_span]
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(hir_analysis_const_impl_for_non_const_trait)]
|
||||||
|
pub struct ConstImplForNonConstTrait {
|
||||||
|
#[primary_span]
|
||||||
|
pub trait_ref_span: Span,
|
||||||
|
pub trait_name: String,
|
||||||
|
#[suggestion(applicability = "machine-applicable", code = "#[const_trait]")]
|
||||||
|
pub local_trait_span: Option<Span>,
|
||||||
|
#[note]
|
||||||
|
pub marking: (),
|
||||||
|
#[note(adding)]
|
||||||
|
pub adding: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(hir_analysis_const_bound_for_non_const_trait)]
|
||||||
|
pub struct ConstBoundForNonConstTrait {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
|
@ -303,32 +303,6 @@ impl<'tcx> WfPredicates<'tcx> {
|
||||||
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
|
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
|
||||||
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
|
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
|
||||||
} else {
|
} else {
|
||||||
if !tcx.has_attr(trait_ref.def_id, rustc_span::sym::const_trait) {
|
|
||||||
if let Some(item) = self.item &&
|
|
||||||
let hir::ItemKind::Impl(impl_) = item.kind &&
|
|
||||||
let Some(trait_) = &impl_.of_trait &&
|
|
||||||
let Some(def_id) = trait_.trait_def_id() &&
|
|
||||||
def_id == trait_ref.def_id
|
|
||||||
{
|
|
||||||
let trait_name = tcx.item_name(def_id);
|
|
||||||
let mut err = tcx.sess.struct_span_err(
|
|
||||||
self.span,
|
|
||||||
&format!("const `impl` for trait `{trait_name}` which is not marked with `#[const_trait]`"),
|
|
||||||
);
|
|
||||||
if def_id.is_local() {
|
|
||||||
let sp = tcx.def_span(def_id).shrink_to_lo();
|
|
||||||
err.span_suggestion(sp, &format!("mark `{trait_name}` as const"), "#[const_trait]", rustc_errors::Applicability::MachineApplicable);
|
|
||||||
}
|
|
||||||
err.note("marking a trait with `#[const_trait]` ensures all default method bodies are `const`");
|
|
||||||
err.note("adding a non-const method body in the future would be a breaking change");
|
|
||||||
err.emit();
|
|
||||||
} else {
|
|
||||||
tcx.sess.span_err(
|
|
||||||
self.span,
|
|
||||||
"~const can only be applied to `#[const_trait]` traits",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
|
self.nominal_obligations(trait_ref.def_id, trait_ref.substs)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ pub const unsafe fn unreachable_unchecked() -> ! {
|
||||||
// SAFETY: the safety contract for `intrinsics::unreachable` must
|
// SAFETY: the safety contract for `intrinsics::unreachable` must
|
||||||
// be upheld by the caller.
|
// be upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
intrinsics::assert_unsafe_precondition!(() => false);
|
intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false);
|
||||||
intrinsics::unreachable()
|
intrinsics::unreachable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2203,7 +2203,7 @@ extern "rust-intrinsic" {
|
||||||
/// the occasional mistake, and this check should help them figure things out.
|
/// the occasional mistake, and this check should help them figure things out.
|
||||||
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
|
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
|
||||||
macro_rules! assert_unsafe_precondition {
|
macro_rules! assert_unsafe_precondition {
|
||||||
($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
|
($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
// allow non_snake_case to allow capturing const generics
|
// allow non_snake_case to allow capturing const generics
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -2211,7 +2211,9 @@ macro_rules! assert_unsafe_precondition {
|
||||||
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
|
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
|
||||||
if !$e {
|
if !$e {
|
||||||
// don't unwind to reduce impact on code size
|
// don't unwind to reduce impact on code size
|
||||||
::core::panicking::panic_str_nounwind("unsafe precondition violated");
|
::core::panicking::panic_str_nounwind(
|
||||||
|
concat!("unsafe precondition(s) violated: ", $name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -2350,7 +2352,10 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
|
||||||
// SAFETY: the safety contract for `copy_nonoverlapping` must be
|
// SAFETY: the safety contract for `copy_nonoverlapping` must be
|
||||||
// upheld by the caller.
|
// upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) =>
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||||
|
and the specified memory ranges do not overlap",
|
||||||
|
[T](src: *const T, dst: *mut T, count: usize) =>
|
||||||
is_aligned_and_not_null(src)
|
is_aligned_and_not_null(src)
|
||||||
&& is_aligned_and_not_null(dst)
|
&& is_aligned_and_not_null(dst)
|
||||||
&& is_nonoverlapping(src, dst, count)
|
&& is_nonoverlapping(src, dst, count)
|
||||||
|
@ -2436,8 +2441,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
|
||||||
|
|
||||||
// SAFETY: the safety contract for `copy` must be upheld by the caller.
|
// SAFETY: the safety contract for `copy` must be upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](src: *const T, dst: *mut T) =>
|
assert_unsafe_precondition!(
|
||||||
is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
|
"ptr::copy requires that both pointer arguments are aligned aligned and non-null",
|
||||||
|
[T](src: *const T, dst: *mut T) =>
|
||||||
|
is_aligned_and_not_null(src) && is_aligned_and_not_null(dst)
|
||||||
|
);
|
||||||
copy(src, dst, count)
|
copy(src, dst, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2505,7 +2513,10 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
|
||||||
|
|
||||||
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
|
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::write_bytes requires that the destination pointer is aligned and non-null",
|
||||||
|
[T](dst: *mut T) => is_aligned_and_not_null(dst)
|
||||||
|
);
|
||||||
write_bytes(dst, val, count)
|
write_bytes(dst, val, count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,10 @@ macro_rules! nonzero_integers {
|
||||||
pub const unsafe fn new_unchecked(n: $Int) -> Self {
|
pub const unsafe fn new_unchecked(n: $Int) -> Self {
|
||||||
// SAFETY: this is guaranteed to be safe by the caller.
|
// SAFETY: this is guaranteed to be safe by the caller.
|
||||||
unsafe {
|
unsafe {
|
||||||
core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0);
|
core::intrinsics::assert_unsafe_precondition!(
|
||||||
|
concat!(stringify!($Ty), "::new_unchecked requires a non-zero argument"),
|
||||||
|
(n: $Int) => n != 0
|
||||||
|
);
|
||||||
Self(n)
|
Self(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,12 @@ impl IndexRange {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
|
pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
|
||||||
// SAFETY: comparisons on usize are pure
|
// SAFETY: comparisons on usize are pure
|
||||||
unsafe { assert_unsafe_precondition!((start: usize, end: usize) => start <= end) };
|
unsafe {
|
||||||
|
assert_unsafe_precondition!(
|
||||||
|
"IndexRange::new_unchecked requires `start <= end`",
|
||||||
|
(start: usize, end: usize) => start <= end
|
||||||
|
)
|
||||||
|
};
|
||||||
IndexRange { start, end }
|
IndexRange { start, end }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,12 @@ impl Alignment {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const unsafe fn new_unchecked(align: usize) -> Self {
|
pub const unsafe fn new_unchecked(align: usize) -> Self {
|
||||||
// SAFETY: Precondition passed to the caller.
|
// SAFETY: Precondition passed to the caller.
|
||||||
unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) };
|
unsafe {
|
||||||
|
assert_unsafe_precondition!(
|
||||||
|
"Alignment::new_unchecked requires a power of two",
|
||||||
|
(align: usize) => align.is_power_of_two()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
// SAFETY: By precondition, this must be a power of two, and
|
// SAFETY: By precondition, this must be a power of two, and
|
||||||
// our variants encompass all possible powers of two.
|
// our variants encompass all possible powers of two.
|
||||||
|
|
|
@ -761,7 +761,10 @@ impl<T: ?Sized> *const T {
|
||||||
// SAFETY: The comparison has no side-effects, and the intrinsic
|
// SAFETY: The comparison has no side-effects, and the intrinsic
|
||||||
// does this check internally in the CTFE implementation.
|
// does this check internally in the CTFE implementation.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin)
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::sub_ptr requires `this >= origin`",
|
||||||
|
[T](this: *const T, origin: *const T) => this >= origin
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let pointee_size = mem::size_of::<T>();
|
let pointee_size = mem::size_of::<T>();
|
||||||
|
|
|
@ -581,12 +581,21 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
|
||||||
/// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
|
/// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
|
||||||
///
|
///
|
||||||
/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any*
|
/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any*
|
||||||
/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize`
|
/// pointer that was previously exposed by passing it to [`expose_addr`][pointer::expose_addr],
|
||||||
/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be
|
/// or a `ptr as usize` cast. In addition, memory which is outside the control of the Rust abstract
|
||||||
/// used, the program has undefined behavior. Note that there is no algorithm that decides which
|
/// machine (MMIO registers, for example) is always considered to be exposed, so long as this memory
|
||||||
/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess
|
/// is disjoint from memory that will be used by the abstract machine such as the stack, heap,
|
||||||
/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined
|
/// and statics.
|
||||||
/// behavior, then that is the guess that will be taken.
|
///
|
||||||
|
/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
|
||||||
|
/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
|
||||||
|
/// and references that have been invalidated due to aliasing accesses cannot be used any more,
|
||||||
|
/// even if they have been exposed!
|
||||||
|
///
|
||||||
|
/// Note that there is no algorithm that decides which provenance will be used. You can think of this
|
||||||
|
/// as "guessing" the right provenance, and the guess will be "maximally in your favor", in the sense
|
||||||
|
/// that if there is any way to avoid undefined behavior (while upholding all aliasing requirements),
|
||||||
|
/// then that is the guess that will be taken.
|
||||||
///
|
///
|
||||||
/// On platforms with multiple address spaces, it is your responsibility to ensure that the
|
/// On platforms with multiple address spaces, it is your responsibility to ensure that the
|
||||||
/// address makes sense in the address space that this pointer will be used with.
|
/// address makes sense in the address space that this pointer will be used with.
|
||||||
|
@ -889,7 +898,10 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
|
||||||
// SAFETY: the caller must guarantee that `x` and `y` are
|
// SAFETY: the caller must guarantee that `x` and `y` are
|
||||||
// valid for writes and properly aligned.
|
// valid for writes and properly aligned.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) =>
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \
|
||||||
|
and the specified memory ranges do not overlap",
|
||||||
|
[T](x: *mut T, y: *mut T, count: usize) =>
|
||||||
is_aligned_and_not_null(x)
|
is_aligned_and_not_null(x)
|
||||||
&& is_aligned_and_not_null(y)
|
&& is_aligned_and_not_null(y)
|
||||||
&& is_nonoverlapping(x, y, count)
|
&& is_nonoverlapping(x, y, count)
|
||||||
|
@ -986,7 +998,10 @@ pub const unsafe fn replace<T>(dst: *mut T, mut src: T) -> T {
|
||||||
// and cannot overlap `src` since `dst` must point to a distinct
|
// and cannot overlap `src` since `dst` must point to a distinct
|
||||||
// allocated object.
|
// allocated object.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::replace requires that the pointer argument is aligned and non-null",
|
||||||
|
[T](dst: *mut T) => is_aligned_and_not_null(dst)
|
||||||
|
);
|
||||||
mem::swap(&mut *dst, &mut src); // cannot overlap
|
mem::swap(&mut *dst, &mut src); // cannot overlap
|
||||||
}
|
}
|
||||||
src
|
src
|
||||||
|
@ -1117,7 +1132,10 @@ pub const unsafe fn read<T>(src: *const T) -> T {
|
||||||
// Also, since we just wrote a valid value into `tmp`, it is guaranteed
|
// Also, since we just wrote a valid value into `tmp`, it is guaranteed
|
||||||
// to be properly initialized.
|
// to be properly initialized.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::read requires that the pointer argument is aligned and non-null",
|
||||||
|
[T](src: *const T) => is_aligned_and_not_null(src)
|
||||||
|
);
|
||||||
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
|
copy_nonoverlapping(src, tmp.as_mut_ptr(), 1);
|
||||||
tmp.assume_init()
|
tmp.assume_init()
|
||||||
}
|
}
|
||||||
|
@ -1311,7 +1329,10 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
||||||
// `dst` cannot overlap `src` because the caller has mutable access
|
// `dst` cannot overlap `src` because the caller has mutable access
|
||||||
// to `dst` while `src` is owned by this function.
|
// to `dst` while `src` is owned by this function.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::write requires that the pointer argument is aligned and non-null",
|
||||||
|
[T](dst: *mut T) => is_aligned_and_not_null(dst)
|
||||||
|
);
|
||||||
copy_nonoverlapping(&src as *const T, dst, 1);
|
copy_nonoverlapping(&src as *const T, dst, 1);
|
||||||
intrinsics::forget(src);
|
intrinsics::forget(src);
|
||||||
}
|
}
|
||||||
|
@ -1475,7 +1496,10 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
||||||
pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||||
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::read_volatile requires that the pointer argument is aligned and non-null",
|
||||||
|
[T](src: *const T) => is_aligned_and_not_null(src)
|
||||||
|
);
|
||||||
intrinsics::volatile_load(src)
|
intrinsics::volatile_load(src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1546,7 +1570,10 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||||
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||||
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
|
assert_unsafe_precondition!(
|
||||||
|
"ptr::write_volatile requires that the pointer argument is aligned and non-null",
|
||||||
|
[T](dst: *mut T) => is_aligned_and_not_null(dst)
|
||||||
|
);
|
||||||
intrinsics::volatile_store(dst, src);
|
intrinsics::volatile_store(dst, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,7 +197,7 @@ impl<T: ?Sized> NonNull<T> {
|
||||||
pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||||
// SAFETY: the caller must guarantee that `ptr` is non-null.
|
// SAFETY: the caller must guarantee that `ptr` is non-null.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T: ?Sized](ptr: *mut T) => !ptr.is_null());
|
assert_unsafe_precondition!("NonNull::new_unchecked requires that the pointer is non-null", [T: ?Sized](ptr: *mut T) => !ptr.is_null());
|
||||||
NonNull { pointer: ptr as _ }
|
NonNull { pointer: ptr as _ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
|
||||||
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
|
||||||
// so the call to `add` is safe.
|
// so the call to `add` is safe.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len());
|
assert_unsafe_precondition!(
|
||||||
|
"slice::get_unchecked requires that the index is within the slice",
|
||||||
|
[T](this: usize, slice: *const [T]) => this < slice.len()
|
||||||
|
);
|
||||||
slice.as_ptr().add(self)
|
slice.as_ptr().add(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +245,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
|
||||||
let this = self;
|
let this = self;
|
||||||
// SAFETY: see comments for `get_unchecked` above.
|
// SAFETY: see comments for `get_unchecked` above.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len());
|
assert_unsafe_precondition!(
|
||||||
|
"slice::get_unchecked_mut requires that the index is within the slice",
|
||||||
|
[T](this: usize, slice: *mut [T]) => this < slice.len()
|
||||||
|
);
|
||||||
slice.as_mut_ptr().add(self)
|
slice.as_mut_ptr().add(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,8 +301,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
|
||||||
// so the call to `add` is safe.
|
// so the call to `add` is safe.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](end: usize, slice: *const [T]) =>
|
assert_unsafe_precondition!(
|
||||||
end <= slice.len());
|
"slice::get_unchecked requires that the index is within the slice",
|
||||||
|
[T](end: usize, slice: *const [T]) => end <= slice.len()
|
||||||
|
);
|
||||||
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len())
|
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start()), self.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,8 +314,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
|
||||||
let end = self.end();
|
let end = self.end();
|
||||||
// SAFETY: see comments for `get_unchecked` above.
|
// SAFETY: see comments for `get_unchecked` above.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](end: usize, slice: *mut [T]) =>
|
assert_unsafe_precondition!(
|
||||||
end <= slice.len());
|
"slice::get_unchecked_mut requires that the index is within the slice",
|
||||||
|
[T](end: usize, slice: *mut [T]) => end <= slice.len()
|
||||||
|
);
|
||||||
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len())
|
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start()), self.len())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,8 +377,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
|
||||||
// so the call to `add` is safe.
|
// so the call to `add` is safe.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) =>
|
assert_unsafe_precondition!(
|
||||||
this.end >= this.start && this.end <= slice.len());
|
"slice::get_unchecked requires that the range is within the slice",
|
||||||
|
[T](this: ops::Range<usize>, slice: *const [T]) =>
|
||||||
|
this.end >= this.start && this.end <= slice.len()
|
||||||
|
);
|
||||||
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
|
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,8 +391,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
|
||||||
let this = ops::Range { start: self.start, end: self.end };
|
let this = ops::Range { start: self.start, end: self.end };
|
||||||
// SAFETY: see comments for `get_unchecked` above.
|
// SAFETY: see comments for `get_unchecked` above.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) =>
|
assert_unsafe_precondition!(
|
||||||
this.end >= this.start && this.end <= slice.len());
|
"slice::get_unchecked_mut requires that the range is within the slice",
|
||||||
|
[T](this: ops::Range<usize>, slice: *mut [T]) =>
|
||||||
|
this.end >= this.start && this.end <= slice.len()
|
||||||
|
);
|
||||||
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
|
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,7 +653,10 @@ impl<T> [T] {
|
||||||
let ptr = this.as_mut_ptr();
|
let ptr = this.as_mut_ptr();
|
||||||
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
|
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len());
|
assert_unsafe_precondition!(
|
||||||
|
"slice::swap_unchecked requires that the indices are within the slice",
|
||||||
|
[T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len()
|
||||||
|
);
|
||||||
ptr::swap(ptr.add(a), ptr.add(b));
|
ptr::swap(ptr.add(a), ptr.add(b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -969,7 +972,10 @@ impl<T> [T] {
|
||||||
let this = self;
|
let this = self;
|
||||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||||
let new_len = unsafe {
|
let new_len = unsafe {
|
||||||
assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
|
assert_unsafe_precondition!(
|
||||||
|
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
|
||||||
|
[T](this: &[T], N: usize) => N != 0 && this.len() % N == 0
|
||||||
|
);
|
||||||
exact_div(self.len(), N)
|
exact_div(self.len(), N)
|
||||||
};
|
};
|
||||||
// SAFETY: We cast a slice of `new_len * N` elements into
|
// SAFETY: We cast a slice of `new_len * N` elements into
|
||||||
|
@ -1109,7 +1115,10 @@ impl<T> [T] {
|
||||||
let this = &*self;
|
let this = &*self;
|
||||||
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
|
||||||
let new_len = unsafe {
|
let new_len = unsafe {
|
||||||
assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
|
assert_unsafe_precondition!(
|
||||||
|
"slice::as_chunks_unchecked_mut requires `N != 0` and the slice to split exactly into `N`-element chunks",
|
||||||
|
[T](this: &[T], N: usize) => N != 0 && this.len() % N == 0
|
||||||
|
);
|
||||||
exact_div(this.len(), N)
|
exact_div(this.len(), N)
|
||||||
};
|
};
|
||||||
// SAFETY: We cast a slice of `new_len * N` elements into
|
// SAFETY: We cast a slice of `new_len * N` elements into
|
||||||
|
@ -1685,7 +1694,10 @@ impl<T> [T] {
|
||||||
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
|
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
|
||||||
// is fine.
|
// is fine.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len);
|
assert_unsafe_precondition!(
|
||||||
|
"slice::split_at_mut_unchecked requires the index to be within the slice",
|
||||||
|
(mid: usize, len: usize) => mid <= len
|
||||||
|
);
|
||||||
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
|
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,8 +92,10 @@ use crate::ptr;
|
||||||
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
|
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
|
||||||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
|
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](data: *const T, len: usize) =>
|
assert_unsafe_precondition!(
|
||||||
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
|
"slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
|
||||||
|
[T](data: *const T, len: usize) => is_aligned_and_not_null(data)
|
||||||
|
&& is_valid_allocation_size::<T>(len)
|
||||||
);
|
);
|
||||||
&*ptr::slice_from_raw_parts(data, len)
|
&*ptr::slice_from_raw_parts(data, len)
|
||||||
}
|
}
|
||||||
|
@ -135,8 +137,10 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
|
||||||
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
|
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
|
||||||
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
|
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
|
assert_unsafe_precondition!(
|
||||||
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
|
"slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
|
||||||
|
[T](data: *mut T, len: usize) => is_aligned_and_not_null(data)
|
||||||
|
&& is_valid_allocation_size::<T>(len)
|
||||||
);
|
);
|
||||||
&mut *ptr::slice_from_raw_parts_mut(data, len)
|
&mut *ptr::slice_from_raw_parts_mut(data, len)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// miri has some special hacks here that make things unused.
|
||||||
|
#![cfg_attr(miri, allow(unused))]
|
||||||
|
|
||||||
use crate::os::unix::prelude::*;
|
use crate::os::unix::prelude::*;
|
||||||
|
|
||||||
use crate::ffi::{CStr, OsStr, OsString};
|
use crate::ffi::{CStr, OsStr, OsString};
|
||||||
|
@ -850,7 +853,6 @@ impl DirEntry {
|
||||||
target_os = "fuchsia",
|
target_os = "fuchsia",
|
||||||
target_os = "redox"
|
target_os = "redox"
|
||||||
)))]
|
)))]
|
||||||
#[cfg_attr(miri, allow(unused))]
|
|
||||||
fn name_cstr(&self) -> &CStr {
|
fn name_cstr(&self) -> &CStr {
|
||||||
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
|
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
|
||||||
}
|
}
|
||||||
|
@ -862,7 +864,6 @@ impl DirEntry {
|
||||||
target_os = "fuchsia",
|
target_os = "fuchsia",
|
||||||
target_os = "redox"
|
target_os = "redox"
|
||||||
))]
|
))]
|
||||||
#[cfg_attr(miri, allow(unused))]
|
|
||||||
fn name_cstr(&self) -> &CStr {
|
fn name_cstr(&self) -> &CStr {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#![feature(is_terminal)]
|
#![feature(is_terminal)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
#![feature(process_exitcode_internals)]
|
#![feature(process_exitcode_internals)]
|
||||||
|
#![feature(panic_can_unwind)]
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
|
||||||
// Public reexports
|
// Public reexports
|
||||||
|
@ -54,6 +55,7 @@ use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
env, io,
|
env, io,
|
||||||
io::prelude::Write,
|
io::prelude::Write,
|
||||||
|
mem::ManuallyDrop,
|
||||||
panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
|
panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
|
||||||
process::{self, Command, Termination},
|
process::{self, Command, Termination},
|
||||||
sync::mpsc::{channel, Sender},
|
sync::mpsc::{channel, Sender},
|
||||||
|
@ -112,6 +114,29 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
|
||||||
process::exit(ERROR_EXIT_CODE);
|
process::exit(ERROR_EXIT_CODE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if !opts.nocapture {
|
||||||
|
// If we encounter a non-unwinding panic, flush any captured output from the current test,
|
||||||
|
// and stop capturing output to ensure that the non-unwinding panic message is visible.
|
||||||
|
// We also acquire the locks for both output streams to prevent output from other threads
|
||||||
|
// from interleaving with the panic message or appearing after it.
|
||||||
|
let builtin_panic_hook = panic::take_hook();
|
||||||
|
let hook = Box::new({
|
||||||
|
move |info: &'_ PanicInfo<'_>| {
|
||||||
|
if !info.can_unwind() {
|
||||||
|
std::mem::forget(std::io::stderr().lock());
|
||||||
|
let mut stdout = ManuallyDrop::new(std::io::stdout().lock());
|
||||||
|
if let Some(captured) = io::set_output_capture(None) {
|
||||||
|
if let Ok(data) = captured.lock() {
|
||||||
|
let _ = stdout.write_all(&data);
|
||||||
|
let _ = stdout.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builtin_panic_hook(info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
panic::set_hook(hook);
|
||||||
|
}
|
||||||
match console::run_tests_console(&opts, tests) {
|
match console::run_tests_console(&opts, tests) {
|
||||||
Ok(true) => {}
|
Ok(true) => {}
|
||||||
Ok(false) => process::exit(ERROR_EXIT_CODE),
|
Ok(false) => process::exit(ERROR_EXIT_CODE),
|
||||||
|
|
|
@ -1293,7 +1293,7 @@ h3.variant {
|
||||||
content: "\00a0\00a0\00a0";
|
content: "\00a0\00a0\00a0";
|
||||||
}
|
}
|
||||||
|
|
||||||
.notable-traits .notable, .notable-traits .docblock {
|
.notable-traits .docblock {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,23 @@ assert-position: (
|
||||||
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
{"x": 951},
|
{"x": 951},
|
||||||
)
|
)
|
||||||
|
// The tooltip should be beside the `i`
|
||||||
|
click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
|
||||||
|
compare-elements-position-near: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
|
||||||
|
{"y": 2}
|
||||||
|
)
|
||||||
|
compare-elements-position-false: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
|
||||||
|
("x")
|
||||||
|
)
|
||||||
|
// The docblock should be flush with the border.
|
||||||
|
assert-css: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
|
||||||
|
{"margin-left": "0px"}
|
||||||
|
)
|
||||||
|
|
||||||
// Now only the `i` should be on the next line.
|
// Now only the `i` should be on the next line.
|
||||||
size: (1055, 600)
|
size: (1055, 600)
|
||||||
|
@ -81,6 +97,27 @@ assert-position: (
|
||||||
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
{"x": 289},
|
{"x": 289},
|
||||||
)
|
)
|
||||||
|
// The tooltip should be below `i`
|
||||||
|
compare-elements-position-near-false: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
|
||||||
|
{"y": 2}
|
||||||
|
)
|
||||||
|
compare-elements-position-false: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
|
||||||
|
("x")
|
||||||
|
)
|
||||||
|
compare-elements-position-near: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']/parent::*",
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']",
|
||||||
|
{"x": 5}
|
||||||
|
)
|
||||||
|
// The docblock should be flush with the border.
|
||||||
|
assert-css: (
|
||||||
|
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits-tooltiptext force-tooltip']/*[@class='docblock']",
|
||||||
|
{"margin-left": "0px"}
|
||||||
|
)
|
||||||
|
|
||||||
// Checking on very small mobile. The `i` should be on its own line.
|
// Checking on very small mobile. The `i` should be on its own line.
|
||||||
size: (365, 600)
|
size: (365, 600)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(marker_trait_attr)]
|
||||||
|
|
||||||
|
#[marker]
|
||||||
|
trait Marker {}
|
||||||
|
|
||||||
|
impl Marker for &'static () {}
|
||||||
|
impl Marker for &'static () {}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#![feature(marker_trait_attr)]
|
||||||
|
|
||||||
|
#[marker]
|
||||||
|
trait Marker {}
|
||||||
|
|
||||||
|
impl Marker for &'_ () {} //~ ERROR type annotations needed
|
||||||
|
impl Marker for &'_ () {} //~ ERROR type annotations needed
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,31 @@
|
||||||
|
error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
|
||||||
|
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:6
|
||||||
|
|
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
note: multiple `impl`s satisfying `&(): Marker` found
|
||||||
|
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
|
||||||
|
|
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
|
||||||
|
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:7:6
|
||||||
|
|
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^
|
||||||
|
|
|
||||||
|
note: multiple `impl`s satisfying `&(): Marker` found
|
||||||
|
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
|
||||||
|
|
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
LL | impl Marker for &'_ () {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0283`.
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
#[marker] trait Marker {}
|
#[marker]
|
||||||
|
trait Marker {}
|
||||||
|
|
||||||
impl<T: Debug> Marker for T {}
|
impl<T: Debug> Marker for T {}
|
||||||
impl<T: Display> Marker for T {}
|
impl<T: Display> Marker for T {}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied
|
error[E0277]: the trait bound `NotDebugOrDisplay: Marker` is not satisfied
|
||||||
--> $DIR/overlap-marker-trait.rs:27:17
|
--> $DIR/overlap-marker-trait.rs:28:17
|
||||||
|
|
|
|
||||||
LL | is_marker::<NotDebugOrDisplay>();
|
LL | is_marker::<NotDebugOrDisplay>();
|
||||||
| ^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay`
|
| ^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay`
|
||||||
|
|
|
|
||||||
note: required by a bound in `is_marker`
|
note: required by a bound in `is_marker`
|
||||||
--> $DIR/overlap-marker-trait.rs:15:17
|
--> $DIR/overlap-marker-trait.rs:16:17
|
||||||
|
|
|
|
||||||
LL | fn is_marker<T: Marker>() { }
|
LL | fn is_marker<T: Marker>() { }
|
||||||
| ^^^^^^ required by this bound in `is_marker`
|
| ^^^^^^ required by this bound in `is_marker`
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
#[marker] trait MyMarker {}
|
#[marker]
|
||||||
|
trait MyMarker {}
|
||||||
|
|
||||||
impl<T: Debug> MyMarker for T {}
|
impl<T: Debug> MyMarker for T {}
|
||||||
impl<T: Display> MyMarker for T {}
|
impl<T: Display> MyMarker for T {}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-2.rs:11:12
|
--> $DIR/super-traits-fail-2.rs:11:19
|
||||||
|
|
|
|
||||||
LL | trait Bar: ~const Foo {}
|
LL | trait Bar: ~const Foo {}
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-2.rs:11:12
|
--> $DIR/super-traits-fail-2.rs:11:19
|
||||||
|
|
|
|
||||||
LL | trait Bar: ~const Foo {}
|
LL | trait Bar: ~const Foo {}
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-3.rs:12:12
|
--> $DIR/super-traits-fail-3.rs:12:19
|
||||||
|
|
|
|
||||||
LL | trait Bar: ~const Foo {}
|
LL | trait Bar: ~const Foo {}
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-3.rs:15:17
|
--> $DIR/super-traits-fail-3.rs:15:24
|
||||||
|
|
|
|
||||||
LL | const fn foo<T: ~const Bar>(x: &T) {
|
LL | const fn foo<T: ~const Bar>(x: &T) {
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-3.rs:12:12
|
--> $DIR/super-traits-fail-3.rs:12:19
|
||||||
|
|
|
|
||||||
LL | trait Bar: ~const Foo {}
|
LL | trait Bar: ~const Foo {}
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: ~const can only be applied to `#[const_trait]` traits
|
error: ~const can only be applied to `#[const_trait]` traits
|
||||||
--> $DIR/super-traits-fail-3.rs:15:17
|
--> $DIR/super-traits-fail-3.rs:15:24
|
||||||
|
|
|
|
||||||
LL | const fn foo<T: ~const Bar>(x: &T) {
|
LL | const fn foo<T: ~const Bar>(x: &T) {
|
||||||
| ^^^^^^^^^^
|
| ^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
22
src/test/ui/wf/issue-103573.rs
Normal file
22
src/test/ui/wf/issue-103573.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
trait TraitA {
|
||||||
|
type TypeA;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TraitD {
|
||||||
|
type TypeD;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TraitB {
|
||||||
|
type TypeB: TraitD;
|
||||||
|
|
||||||
|
fn f(_: &<Self::TypeB as TraitD>::TypeD);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TraitC<E> {
|
||||||
|
type TypeC<'a>: TraitB;
|
||||||
|
|
||||||
|
fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA);
|
||||||
|
//~^ ERROR the trait bound `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA` is not satisfied
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
14
src/test/ui/wf/issue-103573.stderr
Normal file
14
src/test/ui/wf/issue-103573.stderr
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0277]: the trait bound `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA` is not satisfied
|
||||||
|
--> $DIR/issue-103573.rs:18:5
|
||||||
|
|
|
||||||
|
LL | fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitA` is not implemented for `<<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB`
|
||||||
|
|
|
||||||
|
help: consider further restricting the associated type
|
||||||
|
|
|
||||||
|
LL | fn g<'a>(_: &<<Self::TypeC<'a> as TraitB>::TypeB as TraitA>::TypeA) where <<Self as TraitC<E>>::TypeC<'a> as TraitB>::TypeB: TraitA;
|
||||||
|
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue