Skip to content

Rollup of 9 pull requests #103623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Oct 27, 2022
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b6824ba
Make param index generation a bit more robust
oli-obk Oct 24, 2022
8286ea5
Move a wf-check into the site where the value is instantiated
oli-obk Oct 20, 2022
1c26a27
Split diagnostic details out into a separate function and fluent files
oli-obk Oct 25, 2022
0c3ae7d
Try to say that memory outside the AM is always exposed
saethlin Oct 16, 2022
20ab57e
library: allow some unused things in Miri
RalfJung Oct 26, 2022
bd94763
Update library/core/src/ptr/mod.rs
saethlin Oct 26, 2022
db3b01d
Process registered region obligation in resolve_regions_with_wf_tys
compiler-errors Oct 26, 2022
3dd7009
rustdoc: remove redundant CSS selector `.notable-traits .notable`
notriddle Oct 26, 2022
d380d03
remove unused parser fn
compiler-errors Oct 26, 2022
a7a0b36
rustdoc: add test case for positioning of notable trait tooltip
notriddle Oct 26, 2022
458aaa5
Print the precondition we violated, and visible through output capture
saethlin Oct 14, 2022
2f2a97e
add tests and slight formatting
Rageking8 Oct 27, 2022
2937621
Rollup merge of #103035 - saethlin:assert_unsafe_precondition3, r=thomcc
matthiaskrgr Oct 27, 2022
0cd8714
Rollup merge of #103106 - saethlin:from_exposed_docs, r=thomcc
matthiaskrgr Oct 27, 2022
d7ad6ad
Rollup merge of #103475 - oli-obk:generic_param_indices, r=lcnr
matthiaskrgr Oct 27, 2022
2bd49c3
Rollup merge of #103525 - oli-obk:const_impl_on_non_const_trait, r=lcnr
matthiaskrgr Oct 27, 2022
8a29784
Rollup merge of #103564 - RalfJung:miri-unused, r=thomcc
matthiaskrgr Oct 27, 2022
bf53e71
Rollup merge of #103586 - compiler-errors:issue-103573, r=jackh726
matthiaskrgr Oct 27, 2022
29698dc
Rollup merge of #103592 - notriddle:notriddle/notable-traits-notable,…
matthiaskrgr Oct 27, 2022
0f0c044
Rollup merge of #103593 - compiler-errors:nit-remove-returns, r=fee1-…
matthiaskrgr Oct 27, 2022
2252f7a
Rollup merge of #103611 - Rageking8:fix-103574, r=lcnr
matthiaskrgr Oct 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1112,24 +1112,6 @@ pub struct 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 }`.
///
/// If this is not the case, name resolution does not resolve `N` when using
9 changes: 9 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
Original file line number Diff line number Diff line change
@@ -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_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
68 changes: 50 additions & 18 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{sym, Span};
use rustc_target::spec::abi;
use rustc_trait_selection::traits;
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.infer_args,
None,
None,
);
if let Some(b) = item_segment.args().bindings.first() {
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<'_>,
infer_args: bool,
self_ty: Option<Ty<'tcx>>,
constness: Option<ty::BoundConstness>,
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
// If the type is parameterized by this region, then replace this
// region with the current anon region binding (in other words,
@@ -534,6 +536,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&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)
}

@@ -601,6 +609,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_segment.args(),
item_segment.infer_args,
None,
None,
);

if let Some(b) = item_segment.args().bindings.first() {
@@ -620,6 +629,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
trait_ref: &hir::TraitRef<'_>,
self_ty: Ty<'tcx>,
constness: ty::BoundConstness,
) -> ty::TraitRef<'tcx> {
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,
trait_ref.path.segments.last().unwrap(),
true,
Some(constness),
)
}

@@ -655,6 +666,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
args,
infer_args,
Some(self_ty),
Some(constness),
);

let tcx = self.tcx();
@@ -680,6 +692,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
speculative,
&mut dup_bindings,
binding_span.unwrap_or(binding.span),
constness,
);
// Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
}
@@ -783,13 +796,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty: Ty<'tcx>,
trait_segment: &hir::PathSegment<'_>,
is_impl: bool,
constness: Option<ty::BoundConstness>,
) -> ty::TraitRef<'tcx> {
let (substs, _) = self.create_substs_for_ast_trait_ref(
span,
trait_def_id,
self_ty,
trait_segment,
is_impl,
constness,
);
if let Some(b) = trait_segment.args().bindings.first() {
Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
@@ -805,6 +820,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self_ty: Ty<'tcx>,
trait_segment: &'a hir::PathSegment<'a>,
is_impl: bool,
constness: Option<ty::BoundConstness>,
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
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.infer_args,
Some(self_ty),
constness,
)
}

@@ -1027,6 +1044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
speculative: bool,
dup_bindings: &mut FxHashMap<DefId, Span>,
path_span: Span,
constness: ty::BoundConstness,
) -> Result<(), ErrorGuaranteed> {
// Given something like `U: SomeTrait<T = X>`, we want to produce a
// predicate like `<U as SomeTrait>::T = X`. This is somewhat
@@ -1122,10 +1140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_ref.substs,
);

debug!(
"add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}",
substs_trait_ref_and_assoc_item
);
debug!(?substs_trait_ref_and_assoc_item);

ty::ProjectionTy {
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);
let late_bound_in_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_ty = {:?}", late_bound_in_ty);
debug!(?late_bound_in_trait_ref);
debug!(?late_bound_in_ty);

// FIXME: point at the type params that don't have appropriate lifetimes:
// 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
// errors otherwise.
#[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
fn one_bound_for_assoc_type<I>(
&self,
all_candidates: impl Fn() -> I,
@@ -1677,10 +1693,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return Err(reported);
}
};
debug!("one_bound_for_assoc_type: bound = {:?}", bound);
debug!(?bound);

if let Some(bound2) = next_cand {
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
debug!(?bound2);

let is_equality = is_equality();
let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
@@ -1776,6 +1792,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// parameter or `Self`.
// NOTE: When this function starts resolving `Trait::AssocTy` successfully
// 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(
&self,
hir_ref_id: hir::HirId,
@@ -1793,8 +1810,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Res::Err
};

debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident);

// Check if we have an enum variant.
let mut variant_resolution = None;
if let ty::Adt(adt_def, _) = qself_ty.kind() {
@@ -2050,6 +2065,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_def_id: DefId,
trait_segment: &hir::PathSegment<'_>,
item_segment: &hir::PathSegment<'_>,
constness: ty::BoundConstness,
) -> Ty<'tcx> {
let tcx = self.tcx();

@@ -2094,8 +2110,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

debug!("qpath_to_ty: self_type={:?}", self_ty);

let trait_ref =
self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false);
let trait_ref = self.ast_path_to_mono_trait_ref(
span,
trait_def_id,
self_ty,
trait_segment,
false,
Some(constness),
);

let item_substs = self.create_substs_for_associated_item(
span,
@@ -2534,12 +2556,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
Res::Def(DefKind::AssocTy, def_id) => {
debug_assert!(path.segments.len() >= 2);
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(
span,
opt_self_ty,
def_id,
&path.segments[path.segments.len() - 2],
path.segments.last().unwrap(),
constness,
)
}
Res::PrimTy(prim_ty) => {
@@ -2658,6 +2687,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&GenericArgs::none(),
true,
None,
None,
);
EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
.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(
&self,
hir_id: hir::HirId,
@@ -2775,8 +2806,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
generics: Option<&hir::Generics<'_>>,
hir_ty: Option<&hir::Ty<'_>>,
) -> ty::PolyFnSig<'tcx> {
debug!("ty_of_fn");

let tcx = self.tcx();
let bound_vars = tcx.late_bound_vars(hir_id);
debug!(?bound_vars);
@@ -2826,7 +2855,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
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 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), .. }) =
hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };

let trait_ref =
self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
let trait_ref = self.instantiate_mono_trait_ref(
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(
tcx,
4 changes: 4 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -713,6 +713,10 @@ fn resolve_regions_with_wf_tys<'tcx>(

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

debug!(?errors, "errors");
37 changes: 34 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
@@ -1143,7 +1143,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
}

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), .. }) =
tcx.hir().get(tcx.hir().get_parent_node(hir_id))
&& 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<'_>> {
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| {
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!(),
}
}

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 {
let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
let item = tcx.hir().expect_item(def_id.expect_local());
27 changes: 14 additions & 13 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
@@ -249,6 +249,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
// Now create the real type and const parameters.
let type_start = own_start - has_self as u32 + params.len() as u32;
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 \
`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 param_def = ty::GenericParamDef {
index: type_start + i as u32,
Some(ty::GenericParamDef {
index: next_index(),
name: param.name.ident().name,
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind,
};
i += 1;
Some(param_def)
})
}
GenericParamKind::Const { default, .. } => {
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 {
index: type_start + i as u32,
Some(ty::GenericParamDef {
index: next_index(),
name: param.name.ident().name,
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
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>"][..]
};

params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef {
index: type_start + i as u32,
params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
index: next_index(),
name: Symbol::intern(arg),
def_id,
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));
if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
params.push(ty::GenericParamDef {
index: type_start,
index: next_index(),
name: Symbol::intern("<const_ty>"),
def_id,
pure_wrt_drop: false,
21 changes: 21 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
@@ -249,3 +249,24 @@ pub struct ExpectedUsedSymbol {
#[primary_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,
}
26 changes: 0 additions & 26 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
@@ -303,32 +303,6 @@ impl<'tcx> WfPredicates<'tcx> {
let obligations = if trait_pred.constness == ty::BoundConstness::NotConst {
self.nominal_obligations_without_const(trait_ref.def_id, trait_ref.substs)
} 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)
};

2 changes: 1 addition & 1 deletion library/core/src/hint.rs
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@ pub const unsafe fn unreachable_unchecked() -> ! {
// SAFETY: the safety contract for `intrinsics::unreachable` must
// be upheld by the caller.
unsafe {
intrinsics::assert_unsafe_precondition!(() => false);
intrinsics::assert_unsafe_precondition!("hint::unreachable_unchecked must never be reached", () => false);
intrinsics::unreachable()
}
}
23 changes: 17 additions & 6 deletions library/core/src/intrinsics.rs
Original file line number Diff line number Diff line change
@@ -2203,15 +2203,17 @@ extern "rust-intrinsic" {
/// 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
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) {
// allow non_snake_case to allow capturing const generics
#[allow(non_snake_case)]
#[inline(always)]
fn runtime$(<$($tt)*>)?($($i:$ty),*) {
if !$e {
// 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)]
@@ -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
// upheld by the caller.
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(dst)
&& 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.
unsafe {
assert_unsafe_precondition!([T](src: *const T, dst: *mut T) =>
is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
assert_unsafe_precondition!(
"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)
}
}
@@ -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.
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)
}
}
5 changes: 4 additions & 1 deletion library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
@@ -56,7 +56,10 @@ macro_rules! nonzero_integers {
pub const unsafe fn new_unchecked(n: $Int) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
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)
}
}
7 changes: 6 additions & 1 deletion library/core/src/ops/index_range.rs
Original file line number Diff line number Diff line change
@@ -19,7 +19,12 @@ impl IndexRange {
#[inline]
pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
// 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 }
}

7 changes: 6 additions & 1 deletion library/core/src/ptr/alignment.rs
Original file line number Diff line number Diff line change
@@ -76,7 +76,12 @@ impl Alignment {
#[inline]
pub const unsafe fn new_unchecked(align: usize) -> Self {
// 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
// our variants encompass all possible powers of two.
5 changes: 4 additions & 1 deletion library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
@@ -761,7 +761,10 @@ impl<T: ?Sized> *const T {
// SAFETY: The comparison has no side-effects, and the intrinsic
// does this check internally in the CTFE implementation.
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>();
51 changes: 39 additions & 12 deletions library/core/src/ptr/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
///
/// 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`
/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be
/// used, the program has undefined behavior. 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, then that is the guess that will be taken.
/// pointer that was previously exposed by passing it to [`expose_addr`][pointer::expose_addr],
/// or a `ptr as usize` cast. In addition, memory which is outside the control of the Rust abstract
/// machine (MMIO registers, for example) is always considered to be exposed, so long as this memory
/// is disjoint from memory that will be used by the abstract machine such as the stack, heap,
/// and statics.
///
/// 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
/// 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
// valid for writes and properly aligned.
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(y)
&& 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
// allocated object.
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
}
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
// to be properly initialized.
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);
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
// to `dst` while `src` is owned by this function.
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);
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 {
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
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)
}
}
@@ -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) {
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
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);
}
}
2 changes: 1 addition & 1 deletion library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
@@ -197,7 +197,7 @@ impl<T: ?Sized> NonNull<T> {
pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
// SAFETY: the caller must guarantee that `ptr` is non-null.
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 _ }
}
}
36 changes: 26 additions & 10 deletions library/core/src/slice/index.rs
Original file line number Diff line number Diff line change
@@ -232,7 +232,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
// so the call to `add` is safe.
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)
}
}
@@ -242,7 +245,10 @@ unsafe impl<T> const SliceIndex<[T]> for usize {
let this = self;
// SAFETY: see comments for `get_unchecked` above.
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)
}
}
@@ -295,8 +301,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
// so the call to `add` is safe.

unsafe {
assert_unsafe_precondition!([T](end: usize, slice: *const [T]) =>
end <= slice.len());
assert_unsafe_precondition!(
"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())
}
}
@@ -306,8 +314,10 @@ unsafe impl<T> const SliceIndex<[T]> for ops::IndexRange {
let end = self.end();
// SAFETY: see comments for `get_unchecked` above.
unsafe {
assert_unsafe_precondition!([T](end: usize, slice: *mut [T]) =>
end <= slice.len());
assert_unsafe_precondition!(
"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())
}
}
@@ -367,8 +377,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
// so the call to `add` is safe.

unsafe {
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) =>
this.end >= this.start && this.end <= slice.len());
assert_unsafe_precondition!(
"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)
}
}
@@ -378,8 +391,11 @@ unsafe impl<T> const SliceIndex<[T]> for ops::Range<usize> {
let this = ops::Range { start: self.start, end: self.end };
// SAFETY: see comments for `get_unchecked` above.
unsafe {
assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) =>
this.end >= this.start && this.end <= slice.len());
assert_unsafe_precondition!(
"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)
}
}
20 changes: 16 additions & 4 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
@@ -653,7 +653,10 @@ impl<T> [T] {
let ptr = this.as_mut_ptr();
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
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));
}
}
@@ -969,7 +972,10 @@ impl<T> [T] {
let this = self;
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
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)
};
// SAFETY: We cast a slice of `new_len * N` elements into
@@ -1109,7 +1115,10 @@ impl<T> [T] {
let this = &*self;
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
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)
};
// 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
// is fine.
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))
}
}
12 changes: 8 additions & 4 deletions library/core/src/slice/raw.rs
Original file line number Diff line number Diff line change
@@ -92,8 +92,10 @@ use crate::ptr;
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`.
unsafe {
assert_unsafe_precondition!([T](data: *const T, len: usize) =>
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
assert_unsafe_precondition!(
"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)
}
@@ -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] {
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
unsafe {
assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
is_aligned_and_not_null(data) && is_valid_allocation_size::<T>(len)
assert_unsafe_precondition!(
"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)
}
5 changes: 3 additions & 2 deletions library/std/src/sys/unix/fs.rs
Original file line number Diff line number Diff line change
@@ -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::ffi::{CStr, OsStr, OsString};
@@ -850,7 +853,6 @@ impl DirEntry {
target_os = "fuchsia",
target_os = "redox"
)))]
#[cfg_attr(miri, allow(unused))]
fn name_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
}
@@ -862,7 +864,6 @@ impl DirEntry {
target_os = "fuchsia",
target_os = "redox"
))]
#[cfg_attr(miri, allow(unused))]
fn name_cstr(&self) -> &CStr {
&self.name
}
25 changes: 25 additions & 0 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
#![feature(is_terminal)]
#![feature(staged_api)]
#![feature(process_exitcode_internals)]
#![feature(panic_can_unwind)]
#![feature(test)]

// Public reexports
@@ -54,6 +55,7 @@ use std::{
collections::VecDeque,
env, io,
io::prelude::Write,
mem::ManuallyDrop,
panic::{self, catch_unwind, AssertUnwindSafe, PanicInfo},
process::{self, Command, Termination},
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);
}
} 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) {
Ok(true) => {}
Ok(false) => process::exit(ERROR_EXIT_CODE),
2 changes: 1 addition & 1 deletion src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
@@ -1293,7 +1293,7 @@ h3.variant {
content: "\00a0\00a0\00a0";
}

.notable-traits .notable, .notable-traits .docblock {
.notable-traits .docblock {
margin: 0;
}

39 changes: 38 additions & 1 deletion src/test/rustdoc-gui/notable-trait.goml
Original file line number Diff line number Diff line change
@@ -24,7 +24,23 @@ assert-position: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
{"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.
size: (1055, 600)
@@ -81,6 +97,27 @@ assert-position: (
"//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
{"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.
size: (365, 600)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// check-pass
#![feature(marker_trait_attr)]

#[marker]
trait Marker {}

impl Marker for &'static () {}
impl Marker for &'static () {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
3 changes: 2 additions & 1 deletion src/test/ui/marker_trait_attr/overlap-marker-trait.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@

use std::fmt::{Debug, Display};

#[marker] trait Marker {}
#[marker]
trait Marker {}

impl<T: Debug> Marker for T {}
impl<T: Display> Marker for T {}
4 changes: 2 additions & 2 deletions src/test/ui/marker_trait_attr/overlap-marker-trait.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
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>();
| ^^^^^^^^^^^^^^^^^ the trait `Marker` is not implemented for `NotDebugOrDisplay`
|
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>() { }
| ^^^^^^ required by this bound in `is_marker`
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@

use std::fmt::{Debug, Display};

#[marker] trait MyMarker {}
#[marker]
trait MyMarker {}

impl<T: Debug> MyMarker for T {}
impl<T: Display> MyMarker for T {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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 {}
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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 {}
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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 {}
| ^^^^^^^^^^
| ^^^

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

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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 {}
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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) {
| ^^^^^^^^^^
| ^^^

error: aborting due to previous error

22 changes: 22 additions & 0 deletions src/test/ui/wf/issue-103573.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 14 additions & 0 deletions src/test/ui/wf/issue-103573.stderr
Original file line number Diff line number Diff line change
@@ -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`.