Auto merge of #122139 - GuillaumeGomez:rollup-37vtwsc, r=GuillaumeGomez
Rollup of 10 pull requests Successful merges: - #121863 (silence mismatched types errors for implied projections) - #122043 (Apply `EarlyBinder` only to `TraitRef` in `ImplTraitHeader`) - #122066 (Add proper cfgs for struct HirIdValidator used only with debug-assert) - #122104 (Rust is a proper name: rust → Rust) - #122110 (Make `x t miri` respect `MIRI_TEMP`) - #122114 (Make not finding core a fatal error) - #122115 (Cancel parsing ever made during recovery) - #122123 (Don't require specifying unrelated assoc types when trait alias is in `dyn` type) - #122126 (Fix `tidy --bless` on ̶X̶e̶n̶i̶x̶ Windows) - #122129 (Set `RustcDocs` to only run on host) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
1c580bcb70
62 changed files with 547 additions and 315 deletions
|
@ -1612,10 +1612,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
.any(|impl_def_id| {
|
||||
let impl_header = tcx.impl_trait_header(impl_def_id);
|
||||
impl_header.is_some_and(|header| {
|
||||
let header = header.instantiate(
|
||||
let trait_ref = header.trait_ref.instantiate(
|
||||
tcx,
|
||||
infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
|
||||
);
|
||||
|
||||
let value = tcx.fold_regions(qself_ty, |_, _| tcx.lifetimes.re_erased);
|
||||
// FIXME: Don't bother dealing with non-lifetime binders here...
|
||||
if value.has_escaping_bound_vars() {
|
||||
|
@ -1624,7 +1625,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
infcx
|
||||
.can_eq(
|
||||
ty::ParamEnv::empty(),
|
||||
header.trait_ref.self_ty(),
|
||||
trait_ref.self_ty(),
|
||||
value,
|
||||
) && header.polarity != ty::ImplPolarity::Negative
|
||||
})
|
||||
|
@ -1677,9 +1678,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
.filter(|header| {
|
||||
// Consider only accessible traits
|
||||
tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
|
||||
&& header.skip_binder().polarity != ty::ImplPolarity::Negative
|
||||
&& header.polarity != ty::ImplPolarity::Negative
|
||||
})
|
||||
.map(|header| header.instantiate_identity().trait_ref.self_ty())
|
||||
.map(|header| header.trait_ref.instantiate_identity().self_ty())
|
||||
// We don't care about blanket impls.
|
||||
.filter(|self_ty| !self_ty.has_non_region_param())
|
||||
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
|
||||
|
|
|
@ -45,10 +45,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
dummy_self,
|
||||
&mut bounds,
|
||||
false,
|
||||
// FIXME: This should be `true`, but we don't really handle
|
||||
// associated type bounds or type aliases in objects in a way
|
||||
// that makes this meaningful, I think.
|
||||
OnlySelfBounds(false),
|
||||
// True so we don't populate `bounds` with associated type bounds, even
|
||||
// though they're disallowed from object types.
|
||||
OnlySelfBounds(true),
|
||||
) {
|
||||
potential_assoc_types.extend(cur_potential_assoc_types);
|
||||
}
|
||||
|
@ -83,9 +82,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let expanded_traits =
|
||||
traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b)| (a, b)));
|
||||
|
||||
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
|
||||
.filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
|
||||
.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
|
||||
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) =
|
||||
expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
|
||||
if regular_traits.len() > 1 {
|
||||
let first_trait = ®ular_traits[0];
|
||||
let additional_trait = ®ular_traits[1];
|
||||
|
@ -158,7 +156,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
for (base_trait_ref, span) in regular_traits_refs_spans {
|
||||
let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
|
||||
for pred in traits::elaborate(tcx, [base_pred]) {
|
||||
for pred in traits::elaborate(tcx, [base_pred]).filter_only_self() {
|
||||
debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
|
||||
|
||||
let bound_predicate = pred.kind();
|
||||
|
@ -312,45 +310,39 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
})
|
||||
});
|
||||
|
||||
let existential_projections = projection_bounds
|
||||
.iter()
|
||||
// We filter out traits that don't have `Self` as their self type above,
|
||||
// we need to do the same for projections.
|
||||
.filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
|
||||
.map(|(bound, _)| {
|
||||
bound.map_bound(|mut b| {
|
||||
assert_eq!(b.projection_ty.self_ty(), dummy_self);
|
||||
let existential_projections = projection_bounds.iter().map(|(bound, _)| {
|
||||
bound.map_bound(|mut b| {
|
||||
assert_eq!(b.projection_ty.self_ty(), dummy_self);
|
||||
|
||||
// Like for trait refs, verify that `dummy_self` did not leak inside default type
|
||||
// parameters.
|
||||
let references_self = b.projection_ty.args.iter().skip(1).any(|arg| {
|
||||
if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
if references_self {
|
||||
let guar = tcx.dcx().span_delayed_bug(
|
||||
span,
|
||||
"trait object projection bounds reference `Self`",
|
||||
);
|
||||
let args: Vec<_> = b
|
||||
.projection_ty
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
return Ty::new_error(tcx, guar).into();
|
||||
}
|
||||
arg
|
||||
})
|
||||
.collect();
|
||||
b.projection_ty.args = tcx.mk_args(&args);
|
||||
// Like for trait refs, verify that `dummy_self` did not leak inside default type
|
||||
// parameters.
|
||||
let references_self = b.projection_ty.args.iter().skip(1).any(|arg| {
|
||||
if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
});
|
||||
if references_self {
|
||||
let guar = tcx
|
||||
.dcx()
|
||||
.span_delayed_bug(span, "trait object projection bounds reference `Self`");
|
||||
let args: Vec<_> = b
|
||||
.projection_ty
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
if arg.walk().any(|arg| arg == dummy_self.into()) {
|
||||
return Ty::new_error(tcx, guar).into();
|
||||
}
|
||||
arg
|
||||
})
|
||||
.collect();
|
||||
b.projection_ty.args = tcx.mk_args(&args);
|
||||
}
|
||||
|
||||
ty::ExistentialProjection::erase_self_ty(tcx, b)
|
||||
})
|
||||
});
|
||||
ty::ExistentialProjection::erase_self_ty(tcx, b)
|
||||
})
|
||||
});
|
||||
|
||||
let regular_trait_predicates = existential_trait_refs
|
||||
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
|
||||
|
|
|
@ -530,11 +530,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
|||
}
|
||||
DefKind::Impl { of_trait } => {
|
||||
if of_trait && let Some(impl_trait_header) = tcx.impl_trait_header(def_id) {
|
||||
check_impl_items_against_trait(
|
||||
tcx,
|
||||
def_id,
|
||||
impl_trait_header.instantiate_identity(),
|
||||
);
|
||||
check_impl_items_against_trait(tcx, def_id, impl_trait_header);
|
||||
check_on_unimplemented(tcx, def_id);
|
||||
}
|
||||
}
|
||||
|
@ -725,10 +721,11 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
impl_id: LocalDefId,
|
||||
impl_trait_header: ty::ImplTraitHeader<'tcx>,
|
||||
) {
|
||||
let trait_ref = impl_trait_header.trait_ref.instantiate_identity();
|
||||
// If the trait reference itself is erroneous (so the compilation is going
|
||||
// to fail), skip checking the items here -- the `impl_item` table in `tcx`
|
||||
// isn't populated for such impls.
|
||||
if impl_trait_header.references_error() {
|
||||
if trait_ref.references_error() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -752,7 +749,7 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let trait_def = tcx.trait_def(impl_trait_header.trait_ref.def_id);
|
||||
let trait_def = tcx.trait_def(trait_ref.def_id);
|
||||
|
||||
for &impl_item in impl_item_refs {
|
||||
let ty_impl_item = tcx.associated_item(impl_item);
|
||||
|
@ -771,10 +768,10 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
));
|
||||
}
|
||||
ty::AssocKind::Fn => {
|
||||
compare_impl_method(tcx, ty_impl_item, ty_trait_item, impl_trait_header.trait_ref);
|
||||
compare_impl_method(tcx, ty_impl_item, ty_trait_item, trait_ref);
|
||||
}
|
||||
ty::AssocKind::Type => {
|
||||
compare_impl_ty(tcx, ty_impl_item, ty_trait_item, impl_trait_header.trait_ref);
|
||||
compare_impl_ty(tcx, ty_impl_item, ty_trait_item, trait_ref);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,7 +791,7 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
let mut must_implement_one_of: Option<&[Ident]> =
|
||||
trait_def.must_implement_one_of.as_deref();
|
||||
|
||||
for &trait_item_id in tcx.associated_item_def_ids(impl_trait_header.trait_ref.def_id) {
|
||||
for &trait_item_id in tcx.associated_item_def_ids(trait_ref.def_id) {
|
||||
let leaf_def = ancestors.leaf_def(tcx, trait_item_id);
|
||||
|
||||
let is_implemented = leaf_def
|
||||
|
@ -872,7 +869,7 @@ fn check_impl_items_against_trait<'tcx>(
|
|||
|
||||
if let Some(missing_items) = must_implement_one_of {
|
||||
let attr_span = tcx
|
||||
.get_attr(impl_trait_header.trait_ref.def_id, sym::rustc_must_implement_one_of)
|
||||
.get_attr(trait_ref.def_id, sym::rustc_must_implement_one_of)
|
||||
.map(|attr| attr.span);
|
||||
|
||||
missing_items_must_implement_one_of_err(
|
||||
|
|
|
@ -247,7 +247,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
|
|||
hir::ItemKind::Impl(impl_) => {
|
||||
let header = tcx.impl_trait_header(def_id);
|
||||
let is_auto = header
|
||||
.is_some_and(|header| tcx.trait_is_auto(header.skip_binder().trait_ref.def_id));
|
||||
.is_some_and(|header| tcx.trait_is_auto(header.trait_ref.skip_binder().def_id));
|
||||
|
||||
crate::impl_wf_check::check_impl_wf(tcx, def_id)?;
|
||||
let mut res = Ok(());
|
||||
|
@ -261,7 +261,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
|
|||
.emit());
|
||||
}
|
||||
// We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
|
||||
match header.map(|h| h.skip_binder().polarity) {
|
||||
match header.map(|h| h.polarity) {
|
||||
// `None` means this is an inherent impl
|
||||
Some(ty::ImplPolarity::Positive) | None => {
|
||||
res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait));
|
||||
|
@ -298,31 +298,31 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
|
|||
hir::ItemKind::Const(ty, ..) => {
|
||||
check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid)
|
||||
}
|
||||
hir::ItemKind::Struct(_, ast_generics) => {
|
||||
hir::ItemKind::Struct(_, hir_generics) => {
|
||||
let res = check_type_defn(tcx, item, false);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
check_variances_for_type_defn(tcx, item, hir_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Union(_, ast_generics) => {
|
||||
hir::ItemKind::Union(_, hir_generics) => {
|
||||
let res = check_type_defn(tcx, item, true);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
check_variances_for_type_defn(tcx, item, hir_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Enum(_, ast_generics) => {
|
||||
hir::ItemKind::Enum(_, hir_generics) => {
|
||||
let res = check_type_defn(tcx, item, true);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
check_variances_for_type_defn(tcx, item, hir_generics);
|
||||
res
|
||||
}
|
||||
hir::ItemKind::Trait(..) => check_trait(tcx, item),
|
||||
hir::ItemKind::TraitAlias(..) => check_trait(tcx, item),
|
||||
// `ForeignItem`s are handled separately.
|
||||
hir::ItemKind::ForeignMod { .. } => Ok(()),
|
||||
hir::ItemKind::TyAlias(hir_ty, ast_generics) => {
|
||||
hir::ItemKind::TyAlias(hir_ty, hir_generics) => {
|
||||
if tcx.type_alias_is_lazy(item.owner_id) {
|
||||
// Bounds of lazy type aliases and of eager ones that contain opaque types are respected.
|
||||
// E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`.
|
||||
let res = check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow);
|
||||
check_variances_for_type_defn(tcx, item, ast_generics);
|
||||
check_variances_for_type_defn(tcx, item, hir_generics);
|
||||
res
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -1277,16 +1277,16 @@ fn check_item_type(
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
|
||||
#[instrument(level = "debug", skip(tcx, hir_self_ty, hir_trait_ref))]
|
||||
fn check_impl<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item: &'tcx hir::Item<'tcx>,
|
||||
ast_self_ty: &hir::Ty<'_>,
|
||||
ast_trait_ref: &Option<hir::TraitRef<'_>>,
|
||||
hir_self_ty: &hir::Ty<'_>,
|
||||
hir_trait_ref: &Option<hir::TraitRef<'_>>,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
|
||||
match ast_trait_ref {
|
||||
Some(ast_trait_ref) => {
|
||||
match hir_trait_ref {
|
||||
Some(hir_trait_ref) => {
|
||||
// `#[rustc_reservation_impl]` impls are not real impls and
|
||||
// therefore don't need to be WF (the trait's `Self: Trait` predicate
|
||||
// won't hold).
|
||||
|
@ -1294,8 +1294,9 @@ fn check_impl<'tcx>(
|
|||
// Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
|
||||
// other `Foo` impls are incoherent.
|
||||
tcx.ensure().coherent_trait(trait_ref.def_id)?;
|
||||
let trait_span = hir_trait_ref.path.span;
|
||||
let trait_ref = wfcx.normalize(
|
||||
ast_trait_ref.path.span,
|
||||
trait_span,
|
||||
Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
|
||||
trait_ref,
|
||||
);
|
||||
|
@ -1306,14 +1307,23 @@ fn check_impl<'tcx>(
|
|||
wfcx.param_env,
|
||||
wfcx.body_def_id,
|
||||
trait_pred,
|
||||
ast_trait_ref.path.span,
|
||||
trait_span,
|
||||
item,
|
||||
);
|
||||
for obligation in &mut obligations {
|
||||
if obligation.cause.span != trait_span {
|
||||
// We already have a better span.
|
||||
continue;
|
||||
}
|
||||
if let Some(pred) = obligation.predicate.to_opt_poly_trait_pred()
|
||||
&& pred.self_ty().skip_binder() == trait_ref.self_ty()
|
||||
&& pred.skip_binder().self_ty() == trait_ref.self_ty()
|
||||
{
|
||||
obligation.cause.span = ast_self_ty.span;
|
||||
obligation.cause.span = hir_self_ty.span;
|
||||
}
|
||||
if let Some(pred) = obligation.predicate.to_opt_poly_projection_pred()
|
||||
&& pred.skip_binder().self_ty() == trait_ref.self_ty()
|
||||
{
|
||||
obligation.cause.span = hir_self_ty.span;
|
||||
}
|
||||
}
|
||||
debug!(?obligations);
|
||||
|
@ -1327,7 +1337,7 @@ fn check_impl<'tcx>(
|
|||
self_ty,
|
||||
);
|
||||
wfcx.register_wf_obligation(
|
||||
ast_self_ty.span,
|
||||
hir_self_ty.span,
|
||||
Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
|
||||
self_ty.into(),
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ use rustc_trait_selection::traits::ObligationCtxt;
|
|||
use rustc_trait_selection::traits::{self, ObligationCause};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn check_trait<'tcx>(
|
||||
pub(super) fn check_trait<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
|
@ -66,10 +66,9 @@ impl<'tcx> Checker<'tcx> {
|
|||
|
||||
fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
// Destructors only work on local ADT types.
|
||||
match header.trait_ref.self_ty().kind() {
|
||||
match checker.impl_header.trait_ref.instantiate_identity().self_ty().kind() {
|
||||
ty::Adt(def, _) if def.did().is_local() => return Ok(()),
|
||||
ty::Error(_) => return Ok(()),
|
||||
_ => {}
|
||||
|
@ -86,7 +85,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
|
|||
let impl_did = checker.impl_def_id;
|
||||
debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
|
||||
|
||||
let self_type = impl_header.trait_ref.self_ty();
|
||||
let self_type = impl_header.trait_ref.instantiate_identity().self_ty();
|
||||
debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
@ -120,7 +119,7 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E
|
|||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
let self_type = header.trait_ref.self_ty();
|
||||
let self_type = header.trait_ref.instantiate_identity().self_ty();
|
||||
assert!(!self_type.has_escaping_bound_vars());
|
||||
|
||||
let param_env = tcx.param_env(impl_did);
|
||||
|
@ -157,9 +156,8 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
|
|||
|
||||
fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
|
||||
let tcx = checker.tcx;
|
||||
let header = checker.impl_header;
|
||||
let impl_did = checker.impl_def_id;
|
||||
let trait_ref = header.trait_ref;
|
||||
let trait_ref = checker.impl_header.trait_ref.instantiate_identity();
|
||||
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
|
||||
|
||||
let span = tcx.def_span(impl_did);
|
||||
|
|
|
@ -134,11 +134,12 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed>
|
|||
let mut res = tcx.ensure().specialization_graph_of(def_id);
|
||||
|
||||
for &impl_def_id in impls {
|
||||
let trait_header = tcx.impl_trait_header(impl_def_id).unwrap().instantiate_identity();
|
||||
let trait_def = tcx.trait_def(trait_header.trait_ref.def_id);
|
||||
let trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
|
||||
let trait_ref = trait_header.trait_ref.instantiate_identity();
|
||||
let trait_def = tcx.trait_def(trait_ref.def_id);
|
||||
|
||||
res = res.and(check_impl(tcx, impl_def_id, trait_header.trait_ref, trait_def));
|
||||
res = res.and(check_object_overlap(tcx, impl_def_id, trait_header.trait_ref));
|
||||
res = res.and(check_impl(tcx, impl_def_id, trait_ref, trait_def));
|
||||
res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref));
|
||||
|
||||
res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def));
|
||||
res = res.and(tcx.ensure().orphan_check_impl(impl_def_id));
|
||||
|
|
|
@ -13,9 +13,10 @@ pub(super) fn check_item(
|
|||
trait_header: ImplTraitHeader<'_>,
|
||||
trait_def: &TraitDef,
|
||||
) -> Result<(), ErrorGuaranteed> {
|
||||
let trait_ref = trait_header.trait_ref;
|
||||
let unsafe_attr =
|
||||
tcx.generics_of(def_id).params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
|
||||
let trait_ref = trait_header.trait_ref.instantiate_identity();
|
||||
|
||||
match (trait_def.unsafety, unsafe_attr, trait_header.unsafety, trait_header.polarity) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, Positive | Reservation) => {
|
||||
let span = tcx.def_span(def_id);
|
||||
|
|
|
@ -1519,10 +1519,7 @@ fn suggest_impl_trait<'tcx>(
|
|||
None
|
||||
}
|
||||
|
||||
fn impl_trait_header(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: LocalDefId,
|
||||
) -> Option<ty::EarlyBinder<ty::ImplTraitHeader<'_>>> {
|
||||
fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTraitHeader<'_>> {
|
||||
let icx = ItemCtxt::new(tcx, def_id);
|
||||
let item = tcx.hir().expect_item(def_id);
|
||||
let impl_ = item.expect_impl();
|
||||
|
@ -1558,11 +1555,11 @@ fn impl_trait_header(
|
|||
} else {
|
||||
icx.astconv().instantiate_mono_trait_ref(ast_trait_ref, selfty)
|
||||
};
|
||||
ty::EarlyBinder::bind(ty::ImplTraitHeader {
|
||||
trait_ref,
|
||||
ty::ImplTraitHeader {
|
||||
trait_ref: ty::EarlyBinder::bind(trait_ref),
|
||||
unsafety: impl_.unsafety,
|
||||
polarity: polarity_of_impl(tcx, def_id, impl_, item.span)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3368,11 +3368,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
"inherent impls can't be candidates, only trait impls can be",
|
||||
)
|
||||
})
|
||||
.filter(|header| {
|
||||
header.skip_binder().polarity == ty::ImplPolarity::Negative
|
||||
})
|
||||
.filter(|header| header.polarity == ty::ImplPolarity::Negative)
|
||||
.any(|header| {
|
||||
let imp = header.instantiate_identity().trait_ref;
|
||||
let imp = header.trait_ref.instantiate_identity();
|
||||
let imp_simp =
|
||||
simplify_type(self.tcx, imp.self_ty(), TreatParams::ForLookup);
|
||||
imp_simp.is_some_and(|s| s == simp_rcvr_ty)
|
||||
|
|
|
@ -77,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
// that name placeholders created in this function. Nested goals from type relations can
|
||||
// also contain placeholders created by this function.
|
||||
let value = self.enter_forall_and_leak_universe(forall);
|
||||
debug!("?value");
|
||||
debug!(?value);
|
||||
f(value)
|
||||
}
|
||||
|
||||
|
|
|
@ -686,6 +686,11 @@ pub fn create_global_ctxt<'tcx>(
|
|||
/// Runs the type-checking, region checking and other miscellaneous analysis
|
||||
/// passes on the crate.
|
||||
fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||
if tcx.sess.opts.unstable_opts.hir_stats {
|
||||
rustc_passes::hir_stats::print_hir_stats(tcx);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
rustc_passes::hir_id_validator::check_crate(tcx);
|
||||
|
||||
let sess = tcx.sess;
|
||||
|
|
|
@ -1077,7 +1077,7 @@ impl CrateError {
|
|||
crate_rejections,
|
||||
});
|
||||
} else {
|
||||
dcx.emit_err(errors::CannotFindCrate {
|
||||
let error = errors::CannotFindCrate {
|
||||
span,
|
||||
crate_name,
|
||||
add_info,
|
||||
|
@ -1091,11 +1091,18 @@ impl CrateError {
|
|||
profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime),
|
||||
locator_triple: locator.triple,
|
||||
is_ui_testing: sess.opts.unstable_opts.ui_testing,
|
||||
});
|
||||
};
|
||||
// The diagnostic for missing core is very good, but it is followed by a lot of
|
||||
// other diagnostics that do not add information.
|
||||
if missing_core {
|
||||
dcx.emit_fatal(error);
|
||||
} else {
|
||||
dcx.emit_err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
CrateError::NotFound(crate_name) => {
|
||||
dcx.emit_err(errors::CannotFindCrate {
|
||||
let error = errors::CannotFindCrate {
|
||||
span,
|
||||
crate_name,
|
||||
add_info: String::new(),
|
||||
|
@ -1105,7 +1112,14 @@ impl CrateError {
|
|||
profiler_runtime: Symbol::intern(&sess.opts.unstable_opts.profiler_runtime),
|
||||
locator_triple: sess.opts.target_triple.clone(),
|
||||
is_ui_testing: sess.opts.unstable_opts.ui_testing,
|
||||
});
|
||||
};
|
||||
// The diagnostic for missing core is very good, but it is followed by a lot of
|
||||
// other diagnostics that do not add information.
|
||||
if missing_core {
|
||||
dcx.emit_fatal(error);
|
||||
} else {
|
||||
dcx.emit_err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1993,9 +1993,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
|
||||
if of_trait && let Some(header) = tcx.impl_trait_header(def_id) {
|
||||
record!(self.tables.impl_trait_header[def_id] <- header);
|
||||
let trait_ref = header.map_bound(|h| h.trait_ref);
|
||||
|
||||
let trait_ref = trait_ref.instantiate_identity();
|
||||
let trait_ref = header.trait_ref.instantiate_identity();
|
||||
let simplified_self_ty = fast_reject::simplify_type(
|
||||
self.tcx,
|
||||
trait_ref.self_ty(),
|
||||
|
|
|
@ -423,7 +423,7 @@ define_tables! {
|
|||
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
|
||||
fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::PolyFnSig<'static>>>>,
|
||||
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
|
||||
impl_trait_header: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::ImplTraitHeader<'static>>>>,
|
||||
impl_trait_header: Table<DefIndex, LazyValue<ty::ImplTraitHeader<'static>>>,
|
||||
const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<rustc_middle::ty::Const<'static>>>>,
|
||||
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
|
||||
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
|
|
|
@ -177,8 +177,8 @@ impl EraseType for Option<mir::DestructuredConstant<'_>> {
|
|||
type Result = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Option<ty::EarlyBinder<ty::ImplTraitHeader<'_>>> {
|
||||
type Result = [u8; size_of::<Option<ty::EarlyBinder<ty::ImplTraitHeader<'static>>>>()];
|
||||
impl EraseType for Option<ty::ImplTraitHeader<'_>> {
|
||||
type Result = [u8; size_of::<Option<ty::ImplTraitHeader<'static>>>()];
|
||||
}
|
||||
|
||||
impl EraseType for Option<ty::EarlyBinder<Ty<'_>>> {
|
||||
|
|
|
@ -840,7 +840,7 @@ rustc_queries! {
|
|||
|
||||
/// Given an `impl_id`, return the trait it implements along with some header information.
|
||||
/// Return `None` if this is an inherent impl.
|
||||
query impl_trait_header(impl_id: DefId) -> Option<ty::EarlyBinder<ty::ImplTraitHeader<'tcx>>> {
|
||||
query impl_trait_header(impl_id: DefId) -> Option<ty::ImplTraitHeader<'tcx>> {
|
||||
desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) }
|
||||
cache_on_disk_if { impl_id.is_local() }
|
||||
separate_provide_extern
|
||||
|
|
|
@ -2310,12 +2310,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self,
|
||||
def_id: impl IntoQueryParam<DefId>,
|
||||
) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
|
||||
Some(self.impl_trait_header(def_id)?.map_bound(|h| h.trait_ref))
|
||||
Some(self.impl_trait_header(def_id)?.trait_ref)
|
||||
}
|
||||
|
||||
pub fn impl_polarity(self, def_id: impl IntoQueryParam<DefId>) -> ty::ImplPolarity {
|
||||
self.impl_trait_header(def_id)
|
||||
.map_or(ty::ImplPolarity::Positive, |h| h.skip_binder().polarity)
|
||||
self.impl_trait_header(def_id).map_or(ty::ImplPolarity::Positive, |h| h.polarity)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,9 +250,9 @@ pub struct ImplHeader<'tcx> {
|
|||
pub predicates: Vec<Predicate<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, TyEncodable, TyDecodable, HashStable)]
|
||||
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
pub struct ImplTraitHeader<'tcx> {
|
||||
pub trait_ref: ty::TraitRef<'tcx>,
|
||||
pub trait_ref: ty::EarlyBinder<ty::TraitRef<'tcx>>,
|
||||
pub polarity: ImplPolarity,
|
||||
pub unsafety: hir::Unsafety,
|
||||
}
|
||||
|
@ -1624,12 +1624,15 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
def_id1: DefId,
|
||||
def_id2: DefId,
|
||||
) -> Option<ImplOverlapKind> {
|
||||
let impl1 = self.impl_trait_header(def_id1).unwrap().instantiate_identity();
|
||||
let impl2 = self.impl_trait_header(def_id2).unwrap().instantiate_identity();
|
||||
let impl1 = self.impl_trait_header(def_id1).unwrap();
|
||||
let impl2 = self.impl_trait_header(def_id2).unwrap();
|
||||
|
||||
let trait_ref1 = impl1.trait_ref.skip_binder();
|
||||
let trait_ref2 = impl2.trait_ref.skip_binder();
|
||||
|
||||
// If either trait impl references an error, they're allowed to overlap,
|
||||
// as one of them essentially doesn't exist.
|
||||
if impl1.references_error() || impl2.references_error() {
|
||||
if trait_ref1.references_error() || trait_ref2.references_error() {
|
||||
return Some(ImplOverlapKind::Permitted { marker: false });
|
||||
}
|
||||
|
||||
|
@ -1650,7 +1653,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
let is_marker_overlap = {
|
||||
let is_marker_impl =
|
||||
|trait_ref: TraitRef<'_>| -> bool { self.trait_def(trait_ref.def_id).is_marker };
|
||||
is_marker_impl(impl1.trait_ref) && is_marker_impl(impl2.trait_ref)
|
||||
is_marker_impl(trait_ref1) && is_marker_impl(trait_ref2)
|
||||
};
|
||||
|
||||
if is_marker_overlap {
|
||||
|
|
|
@ -1361,7 +1361,7 @@ fn create_mono_items_for_default_impls<'tcx>(
|
|||
return;
|
||||
};
|
||||
|
||||
if matches!(impl_.skip_binder().polarity, ty::ImplPolarity::Negative) {
|
||||
if matches!(impl_.polarity, ty::ImplPolarity::Negative) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1385,7 +1385,7 @@ fn create_mono_items_for_default_impls<'tcx>(
|
|||
}
|
||||
};
|
||||
let impl_args = GenericArgs::for_item(tcx, item.owner_id.to_def_id(), only_region_params);
|
||||
let trait_ref = impl_.instantiate(tcx, impl_args).trait_ref;
|
||||
let trait_ref = impl_.trait_ref.instantiate(tcx, impl_args);
|
||||
|
||||
// Unlike 'lazy' monomorphization that begins by collecting items transitively
|
||||
// called by `main` or other global items, when eagerly monomorphizing impl
|
||||
|
|
|
@ -787,13 +787,17 @@ impl<'a> Parser<'a> {
|
|||
let suggest_eq = if self.token.kind == token::Dot
|
||||
&& let _ = self.bump()
|
||||
&& let mut snapshot = self.create_snapshot_for_diagnostic()
|
||||
&& let Ok(_) = snapshot.parse_dot_suffix_expr(
|
||||
colon_sp,
|
||||
self.mk_expr_err(
|
||||
&& let Ok(_) = snapshot
|
||||
.parse_dot_suffix_expr(
|
||||
colon_sp,
|
||||
self.dcx().delayed_bug("error during `:` -> `=` recovery"),
|
||||
),
|
||||
) {
|
||||
self.mk_expr_err(
|
||||
colon_sp,
|
||||
self.dcx()
|
||||
.delayed_bug("error during `:` -> `=` recovery"),
|
||||
),
|
||||
)
|
||||
.map_err(Diag::cancel)
|
||||
{
|
||||
true
|
||||
} else if let Some(op) = self.check_assoc_op()
|
||||
&& op.node.can_continue_expr_unambiguously()
|
||||
|
|
|
@ -1,38 +1,26 @@
|
|||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{HirId, ItemLocalId};
|
||||
use rustc_hir::{intravisit, HirId, ItemLocalId};
|
||||
use rustc_index::bit_set::GrowableBitSet;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||
if tcx.sess.opts.unstable_opts.hir_stats {
|
||||
crate::hir_stats::print_hir_stats(tcx);
|
||||
}
|
||||
let errors = Lock::new(Vec::new());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let errors = Lock::new(Vec::new());
|
||||
tcx.hir().par_for_each_module(|module_id| {
|
||||
let mut v =
|
||||
HirIdValidator { tcx, owner: None, hir_ids_seen: Default::default(), errors: &errors };
|
||||
|
||||
tcx.hir().par_for_each_module(|module_id| {
|
||||
let mut v = HirIdValidator {
|
||||
tcx,
|
||||
owner: None,
|
||||
hir_ids_seen: Default::default(),
|
||||
errors: &errors,
|
||||
};
|
||||
tcx.hir().visit_item_likes_in_module(module_id, &mut v);
|
||||
});
|
||||
|
||||
tcx.hir().visit_item_likes_in_module(module_id, &mut v);
|
||||
});
|
||||
let errors = errors.into_inner();
|
||||
|
||||
let errors = errors.into_inner();
|
||||
|
||||
if !errors.is_empty() {
|
||||
let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2);
|
||||
tcx.dcx().delayed_bug(message);
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2);
|
||||
tcx.dcx().delayed_bug(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +78,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> {
|
|||
self.error(|| {
|
||||
format!(
|
||||
"ItemLocalIds not assigned densely in {pretty_owner}. \
|
||||
Max ItemLocalId = {max}, missing IDs = {missing_items:#?}; seen IDs = {seen_items:#?}"
|
||||
Max ItemLocalId = {max}, missing IDs = {missing_items:#?}; seen IDs = {seen_items:#?}"
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ mod debugger_visualizer;
|
|||
mod diagnostic_items;
|
||||
pub mod entry;
|
||||
mod errors;
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod hir_id_validator;
|
||||
pub mod hir_stats;
|
||||
mod lang_items;
|
||||
|
|
|
@ -166,13 +166,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
|
|||
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
|
||||
if !drcx.args_may_unify(
|
||||
goal.predicate.trait_ref(tcx).args,
|
||||
impl_trait_header.skip_binder().trait_ref.args,
|
||||
impl_trait_header.trait_ref.skip_binder().args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// We have to ignore negative impls when projecting.
|
||||
let impl_polarity = impl_trait_header.skip_binder().polarity;
|
||||
let impl_polarity = impl_trait_header.polarity;
|
||||
match impl_polarity {
|
||||
ty::ImplPolarity::Negative => return Err(NoSolution),
|
||||
ty::ImplPolarity::Reservation => {
|
||||
|
@ -183,7 +183,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
|
|||
|
||||
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
|
||||
let impl_args = ecx.fresh_args_for_item(impl_def_id);
|
||||
let impl_trait_ref = impl_trait_header.instantiate(tcx, impl_args).trait_ref;
|
||||
let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
|
||||
|
||||
ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
|
||||
|
||||
|
|
|
@ -47,14 +47,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
|
||||
if !drcx.args_may_unify(
|
||||
goal.predicate.trait_ref.args,
|
||||
impl_trait_header.skip_binder().trait_ref.args,
|
||||
impl_trait_header.trait_ref.skip_binder().args,
|
||||
) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// An upper bound of the certainty of this goal, used to lower the certainty
|
||||
// of reservation impl to ambiguous during coherence.
|
||||
let impl_polarity = impl_trait_header.skip_binder().polarity;
|
||||
let impl_polarity = impl_trait_header.polarity;
|
||||
let maximal_certainty = match impl_polarity {
|
||||
ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => {
|
||||
match impl_polarity == goal.predicate.polarity {
|
||||
|
@ -70,7 +70,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
|
||||
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
|
||||
let impl_args = ecx.fresh_args_for_item(impl_def_id);
|
||||
let impl_trait_ref = impl_trait_header.instantiate(tcx, impl_args).trait_ref;
|
||||
let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
|
||||
|
||||
ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
|
||||
let where_clause_bounds = tcx
|
||||
|
|
|
@ -1431,45 +1431,64 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
#[extension(pub(super) trait InferCtxtPrivExt<'tcx>)]
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
fn can_match_trait(
|
||||
&self,
|
||||
goal: ty::TraitPredicate<'tcx>,
|
||||
assumption: ty::PolyTraitPredicate<'tcx>,
|
||||
) -> bool {
|
||||
if goal.polarity != assumption.polarity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let trait_goal = goal.trait_ref;
|
||||
let trait_assumption = self.instantiate_binder_with_fresh_vars(
|
||||
DUMMY_SP,
|
||||
infer::BoundRegionConversionTime::HigherRankedType,
|
||||
assumption.to_poly_trait_ref(),
|
||||
);
|
||||
|
||||
self.can_eq(ty::ParamEnv::empty(), trait_goal, trait_assumption)
|
||||
}
|
||||
|
||||
fn can_match_projection(
|
||||
&self,
|
||||
goal: ty::ProjectionPredicate<'tcx>,
|
||||
assumption: ty::PolyProjectionPredicate<'tcx>,
|
||||
) -> bool {
|
||||
let assumption = self.instantiate_binder_with_fresh_vars(
|
||||
DUMMY_SP,
|
||||
infer::BoundRegionConversionTime::HigherRankedType,
|
||||
assumption,
|
||||
);
|
||||
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
self.can_eq(param_env, goal.projection_ty, assumption.projection_ty)
|
||||
&& self.can_eq(param_env, goal.term, assumption.term)
|
||||
}
|
||||
|
||||
// returns if `cond` not occurring implies that `error` does not occur - i.e., that
|
||||
// `error` occurring implies that `cond` occurs.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
|
||||
if cond == error {
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: It should be possible to deal with `ForAll` in a cleaner way.
|
||||
let bound_error = error.kind();
|
||||
let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) {
|
||||
(
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)),
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)),
|
||||
) => (cond, bound_error.rebind(error)),
|
||||
_ => {
|
||||
// FIXME: make this work in other cases too.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
for pred in elaborate(self.tcx, std::iter::once(cond)) {
|
||||
let bound_predicate = pred.kind();
|
||||
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) =
|
||||
bound_predicate.skip_binder()
|
||||
{
|
||||
let error = error.to_poly_trait_ref();
|
||||
let implication = bound_predicate.rebind(implication.trait_ref);
|
||||
// FIXME: I'm just not taking associated types at all here.
|
||||
// Eventually I'll need to implement param-env-aware
|
||||
// `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic.
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
if self.can_sub(param_env, error, implication) {
|
||||
debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if let Some(error) = error.to_opt_poly_trait_pred() {
|
||||
self.enter_forall(error, |error| {
|
||||
elaborate(self.tcx, std::iter::once(cond))
|
||||
.filter_map(|implied| implied.to_opt_poly_trait_pred())
|
||||
.any(|implied| self.can_match_trait(error, implied))
|
||||
})
|
||||
} else if let Some(error) = error.to_opt_poly_projection_pred() {
|
||||
self.enter_forall(error, |error| {
|
||||
elaborate(self.tcx, std::iter::once(cond))
|
||||
.filter_map(|implied| implied.to_opt_poly_projection_pred())
|
||||
.any(|implied| self.can_match_projection(error, implied))
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
|
@ -1888,13 +1907,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
.tcx
|
||||
.all_impls(trait_pred.def_id())
|
||||
.filter_map(|def_id| {
|
||||
let imp = self.tcx.impl_trait_header(def_id).unwrap().skip_binder();
|
||||
let imp = self.tcx.impl_trait_header(def_id).unwrap();
|
||||
if imp.polarity == ty::ImplPolarity::Negative
|
||||
|| !self.tcx.is_user_visible_dep(def_id.krate)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let imp = imp.trait_ref;
|
||||
let imp = imp.trait_ref.skip_binder();
|
||||
|
||||
self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map(
|
||||
|similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id },
|
||||
|
@ -2078,12 +2097,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
.all_impls(def_id)
|
||||
// Ignore automatically derived impls and `!Trait` impls.
|
||||
.filter_map(|def_id| self.tcx.impl_trait_header(def_id))
|
||||
.map(ty::EarlyBinder::instantiate_identity)
|
||||
.filter(|header| {
|
||||
header.polarity != ty::ImplPolarity::Negative
|
||||
|| self.tcx.is_automatically_derived(def_id)
|
||||
.filter_map(|header| {
|
||||
(header.polarity != ty::ImplPolarity::Negative
|
||||
|| self.tcx.is_automatically_derived(def_id))
|
||||
.then(|| header.trait_ref.instantiate_identity())
|
||||
})
|
||||
.map(|header| header.trait_ref)
|
||||
.filter(|trait_ref| {
|
||||
let self_ty = trait_ref.self_ty();
|
||||
// Avoid mentioning type parameters.
|
||||
|
|
|
@ -562,7 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// and so forth that we need to.
|
||||
let impl_trait_header = self.tcx().impl_trait_header(impl_def_id).unwrap();
|
||||
if !drcx
|
||||
.args_may_unify(obligation_args, impl_trait_header.skip_binder().trait_ref.args)
|
||||
.args_may_unify(obligation_args, impl_trait_header.trait_ref.skip_binder().args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -577,7 +577,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
if self.reject_fn_ptr_impls(
|
||||
impl_def_id,
|
||||
obligation,
|
||||
impl_trait_header.skip_binder().trait_ref.self_ty(),
|
||||
impl_trait_header.trait_ref.skip_binder().self_ty(),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ use rustc_middle::ty::_match::MatchAgainstFreshVars;
|
|||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
|
||||
use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Symbol;
|
||||
|
@ -2441,7 +2441,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
fn match_impl(
|
||||
&mut self,
|
||||
impl_def_id: DefId,
|
||||
impl_trait_header: EarlyBinder<ty::ImplTraitHeader<'tcx>>,
|
||||
impl_trait_header: ty::ImplTraitHeader<'tcx>,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> {
|
||||
let placeholder_obligation =
|
||||
|
@ -2450,8 +2450,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
|
||||
let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id);
|
||||
|
||||
let impl_trait_header = impl_trait_header.instantiate(self.tcx(), impl_args);
|
||||
if impl_trait_header.references_error() {
|
||||
let trait_ref = impl_trait_header.trait_ref.instantiate(self.tcx(), impl_args);
|
||||
if trait_ref.references_error() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
|
@ -2464,7 +2464,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
obligation.param_env,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
impl_trait_header.trait_ref,
|
||||
trait_ref,
|
||||
)
|
||||
});
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
|
|||
}
|
||||
}
|
||||
|
||||
let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap().instantiate_identity();
|
||||
let impl1_trait_header = tcx.impl_trait_header(impl1_def_id).unwrap();
|
||||
|
||||
// We determine whether there's a subset relationship by:
|
||||
//
|
||||
|
@ -198,7 +198,7 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
|
|||
fulfill_implication(
|
||||
&infcx,
|
||||
penv,
|
||||
impl1_trait_header.trait_ref,
|
||||
impl1_trait_header.trait_ref.instantiate_identity(),
|
||||
impl1_def_id,
|
||||
impl2_def_id,
|
||||
|_, _| ObligationCause::dummy(),
|
||||
|
|
|
@ -127,7 +127,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
|
|||
}
|
||||
|
||||
// Get components of trait alias.
|
||||
let predicates = tcx.implied_predicates_of(trait_ref.def_id());
|
||||
let predicates = tcx.super_predicates_of(trait_ref.def_id());
|
||||
debug!(?predicates);
|
||||
|
||||
let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
|
||||
|
|
|
@ -223,60 +223,87 @@ enum Elaborate {
|
|||
None,
|
||||
}
|
||||
|
||||
/// Points the cause span of a super predicate at the relevant associated type.
|
||||
///
|
||||
/// Given a trait impl item:
|
||||
///
|
||||
/// ```ignore (incomplete)
|
||||
/// impl TargetTrait for TargetType {
|
||||
/// type Assoc = SomeType;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// And a super predicate of `TargetTrait` that has any of the following forms:
|
||||
///
|
||||
/// 1. `<OtherType as OtherTrait>::Assoc == <TargetType as TargetTrait>::Assoc`
|
||||
/// 2. `<<TargetType as TargetTrait>::Assoc as OtherTrait>::Assoc == OtherType`
|
||||
/// 3. `<TargetType as TargetTrait>::Assoc: OtherTrait`
|
||||
///
|
||||
/// Replace the span of the cause with the span of the associated item:
|
||||
///
|
||||
/// ```ignore (incomplete)
|
||||
/// impl TargetTrait for TargetType {
|
||||
/// type Assoc = SomeType;
|
||||
/// // ^^^^^^^^ this span
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that bounds that can be expressed as associated item bounds are **not**
|
||||
/// super predicates. This means that form 2 and 3 from above are only relevant if
|
||||
/// the [`GenericArgsRef`] of the projection type are not its identity arguments.
|
||||
fn extend_cause_with_original_assoc_item_obligation<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
item: Option<&hir::Item<'tcx>>,
|
||||
cause: &mut traits::ObligationCause<'tcx>,
|
||||
pred: ty::Predicate<'tcx>,
|
||||
) {
|
||||
debug!(
|
||||
"extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}",
|
||||
trait_ref, item, cause, pred
|
||||
);
|
||||
debug!(?item, ?cause, ?pred, "extended_cause_with_original_assoc_item_obligation");
|
||||
let (items, impl_def_id) = match item {
|
||||
Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => {
|
||||
(impl_.items, *owner_id)
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
let fix_span =
|
||||
|impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind {
|
||||
hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span,
|
||||
_ => impl_item_ref.span,
|
||||
};
|
||||
|
||||
let ty_to_impl_span = |ty: Ty<'_>| {
|
||||
if let ty::Alias(ty::Projection, projection_ty) = ty.kind()
|
||||
&& let Some(&impl_item_id) =
|
||||
tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id)
|
||||
&& let Some(impl_item) =
|
||||
items.iter().find(|item| item.id.owner_id.to_def_id() == impl_item_id)
|
||||
{
|
||||
Some(tcx.hir().impl_item(impl_item.id).expect_type().span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// It is fine to skip the binder as we don't care about regions here.
|
||||
match pred.kind().skip_binder() {
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) => {
|
||||
// The obligation comes not from the current `impl` nor the `trait` being implemented,
|
||||
// but rather from a "second order" obligation, where an associated type has a
|
||||
// projection coming from another associated type. See
|
||||
// `tests/ui/associated-types/point-at-type-on-obligation-failure.rs` and
|
||||
// `traits-assoc-type-in-supertrait-bad.rs`.
|
||||
if let Some(ty::Alias(ty::Projection, projection_ty)) =
|
||||
proj.term.ty().map(|ty| ty.kind())
|
||||
&& let Some(&impl_item_id) =
|
||||
tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id)
|
||||
&& let Some(impl_item_span) = items
|
||||
.iter()
|
||||
.find(|item| item.id.owner_id.to_def_id() == impl_item_id)
|
||||
.map(fix_span)
|
||||
// Form 1: The obligation comes not from the current `impl` nor the `trait` being
|
||||
// implemented, but rather from a "second order" obligation, where an associated
|
||||
// type has a projection coming from another associated type.
|
||||
// See `tests/ui/traits/assoc-type-in-superbad.rs` for an example.
|
||||
if let Some(term_ty) = proj.term.ty()
|
||||
&& let Some(impl_item_span) = ty_to_impl_span(term_ty)
|
||||
{
|
||||
cause.span = impl_item_span;
|
||||
}
|
||||
|
||||
// Form 2: A projection obligation for an associated item failed to be met.
|
||||
// We overwrite the span from above to ensure that a bound like
|
||||
// `Self::Assoc1: Trait<OtherAssoc = Self::Assoc2>` gets the same
|
||||
// span for both obligations that it is lowered to.
|
||||
if let Some(impl_item_span) = ty_to_impl_span(proj.self_ty()) {
|
||||
cause.span = impl_item_span;
|
||||
}
|
||||
}
|
||||
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
||||
// An associated item obligation born out of the `trait` failed to be met. An example
|
||||
// can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
|
||||
// Form 3: A trait obligation for an associated item failed to be met.
|
||||
debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
|
||||
if let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = *pred.self_ty().kind()
|
||||
&& let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&def_id)
|
||||
&& let Some(impl_item_span) = items
|
||||
.iter()
|
||||
.find(|item| item.id.owner_id.to_def_id() == impl_item_id)
|
||||
.map(fix_span)
|
||||
{
|
||||
if let Some(impl_item_span) = ty_to_impl_span(pred.self_ty()) {
|
||||
cause.span = impl_item_span;
|
||||
}
|
||||
}
|
||||
|
@ -355,9 +382,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
|
|||
traits::ObligationCauseCode::DerivedObligation,
|
||||
);
|
||||
}
|
||||
extend_cause_with_original_assoc_item_obligation(
|
||||
tcx, trait_ref, item, &mut cause, predicate,
|
||||
);
|
||||
extend_cause_with_original_assoc_item_obligation(tcx, item, &mut cause, predicate);
|
||||
traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate)
|
||||
};
|
||||
|
||||
|
|
|
@ -255,10 +255,9 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<EarlyBinder<Ty<'
|
|||
|
||||
let impl_ = tcx
|
||||
.impl_trait_header(def_id)
|
||||
.unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id))
|
||||
.skip_binder();
|
||||
.unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id));
|
||||
|
||||
let trait_ref = impl_.trait_ref;
|
||||
let trait_ref = impl_.trait_ref.skip_binder();
|
||||
debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);
|
||||
|
||||
let is_marker_like = impl_.polarity == ty::ImplPolarity::Positive
|
||||
|
|
|
@ -259,7 +259,7 @@ impl<T, A: Allocator> RawVec<T, A> {
|
|||
} else {
|
||||
// We could use Layout::array here which ensures the absence of isize and usize overflows
|
||||
// and could hypothetically handle differences between stride and size, but this memory
|
||||
// has already been allocated so we know it can't overflow and currently rust does not
|
||||
// has already been allocated so we know it can't overflow and currently Rust does not
|
||||
// support such types. So we can do better by skipping some checks and avoid an unwrap.
|
||||
const { assert!(mem::size_of::<T>() % mem::align_of::<T>() == 0) };
|
||||
unsafe {
|
||||
|
|
|
@ -6,10 +6,10 @@ pub use crate::core_arch::arch::*;
|
|||
|
||||
/// Inline assembly.
|
||||
///
|
||||
/// Refer to [rust by example] for a usage guide and the [reference] for
|
||||
/// Refer to [Rust By Example] for a usage guide and the [reference] for
|
||||
/// detailed information about the syntax and available options.
|
||||
///
|
||||
/// [rust by example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||
/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||
/// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
|
||||
#[stable(feature = "asm", since = "1.59.0")]
|
||||
#[rustc_builtin_macro]
|
||||
|
@ -19,10 +19,10 @@ pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
|
|||
|
||||
/// Module-level inline assembly.
|
||||
///
|
||||
/// Refer to [rust by example] for a usage guide and the [reference] for
|
||||
/// Refer to [Rust By Example] for a usage guide and the [reference] for
|
||||
/// detailed information about the syntax and available options.
|
||||
///
|
||||
/// [rust by example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||
/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||
/// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
|
||||
#[stable(feature = "global_asm", since = "1.59.0")]
|
||||
#[rustc_builtin_macro]
|
||||
|
|
|
@ -1481,7 +1481,7 @@ pub(crate) mod builtin {
|
|||
/// script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script).
|
||||
///
|
||||
/// When using the `include` macro to include stretches of documentation, remember that the
|
||||
/// included file still needs to be a valid rust syntax. It is also possible to
|
||||
/// included file still needs to be a valid Rust syntax. It is also possible to
|
||||
/// use the [`include_str`] macro as `#![doc = include_str!("...")]` (at the module level) or
|
||||
/// `#[doc = include_str!("...")]` (at the item level) to include documentation from a plain
|
||||
/// text or markdown file.
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::num::NonZero;
|
|||
use crate::{cmp, fmt, hash, mem, num};
|
||||
|
||||
/// A type storing a `usize` which is a power of two, and thus
|
||||
/// represents a possible alignment in the rust abstract machine.
|
||||
/// represents a possible alignment in the Rust abstract machine.
|
||||
///
|
||||
/// Note that particularly large alignments, while representable in this type,
|
||||
/// are likely not to be supported by actual allocators and linkers.
|
||||
|
|
|
@ -23,7 +23,7 @@ use crate::slice;
|
|||
issue = "none",
|
||||
reason = "exposed from core to be reused in std; use the memchr crate"
|
||||
)]
|
||||
/// Pure rust memchr implementation, taken from rust-memchr
|
||||
/// Pure Rust memchr implementation, taken from rust-memchr
|
||||
pub mod memchr;
|
||||
|
||||
#[unstable(
|
||||
|
|
|
@ -19,7 +19,7 @@ use core::task::{Context, Poll};
|
|||
///
|
||||
/// Certain constructs like [`Future`]s can only be used with _exclusive_ access,
|
||||
/// and are often `Send` but not `Sync`, so `Exclusive` can be used as hint to the
|
||||
/// rust compiler that something is `Sync` in practice.
|
||||
/// Rust compiler that something is `Sync` in practice.
|
||||
///
|
||||
/// ## Examples
|
||||
/// Using a non-`Sync` future prevents the wrapping struct from being `Sync`
|
||||
|
|
|
@ -157,7 +157,7 @@ impl OsString {
|
|||
/// # Safety
|
||||
///
|
||||
/// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of
|
||||
/// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same rust version
|
||||
/// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version
|
||||
/// built for the same target platform. For example, reconstructing an `OsString` from bytes sent
|
||||
/// over the network or stored in a file will likely violate these safety rules.
|
||||
///
|
||||
|
@ -213,7 +213,7 @@ impl OsString {
|
|||
/// ASCII.
|
||||
///
|
||||
/// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should
|
||||
/// be treated as opaque and only comparable within the same rust version built for the same
|
||||
/// be treated as opaque and only comparable within the same Rust version built for the same
|
||||
/// target platform. For example, sending the bytes over the network or storing it in a file
|
||||
/// will likely result in incompatible data. See [`OsString`] for more encoding details
|
||||
/// and [`std::ffi`] for platform-specific, specified conversions.
|
||||
|
@ -747,7 +747,7 @@ impl OsStr {
|
|||
/// # Safety
|
||||
///
|
||||
/// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of
|
||||
/// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same rust version
|
||||
/// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version
|
||||
/// built for the same target platform. For example, reconstructing an `OsStr` from bytes sent
|
||||
/// over the network or stored in a file will likely violate these safety rules.
|
||||
///
|
||||
|
@ -955,7 +955,7 @@ impl OsStr {
|
|||
/// ASCII.
|
||||
///
|
||||
/// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should
|
||||
/// be treated as opaque and only comparable within the same rust version built for the same
|
||||
/// be treated as opaque and only comparable within the same Rust version built for the same
|
||||
/// target platform. For example, sending the slice over the network or storing it in a file
|
||||
/// will likely result in incompatible byte slices. See [`OsString`] for more encoding details
|
||||
/// and [`std::ffi`] for platform-specific, specified conversions.
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
//!
|
||||
//! # Contributing changes to the documentation
|
||||
//!
|
||||
//! Check out the rust contribution guidelines [here](
|
||||
//! Check out the Rust contribution guidelines [here](
|
||||
//! https://rustc-dev-guide.rust-lang.org/contributing.html#writing-documentation).
|
||||
//! The source for this documentation can be found on
|
||||
//! [GitHub](https://github.com/rust-lang/rust).
|
||||
|
|
|
@ -179,7 +179,7 @@ impl Sub<Instant> for Instant {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Previous rust versions panicked when `other` was later than `self`. Currently this
|
||||
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
|
||||
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||
/// See [Monotonicity].
|
||||
///
|
||||
|
|
|
@ -59,7 +59,7 @@ pub use core::time::TryFromFloatSecsError;
|
|||
/// experience time dilation (slow down or speed up), but it will never go
|
||||
/// backwards.
|
||||
/// As part of this non-guarantee it is also not specified whether system suspends count as
|
||||
/// elapsed time or not. The behavior varies across platforms and rust versions.
|
||||
/// elapsed time or not. The behavior varies across platforms and Rust versions.
|
||||
///
|
||||
/// Instants are opaque types that can only be compared to one another. There is
|
||||
/// no method to get "the number of seconds" from an instant. Instead, it only
|
||||
|
@ -142,7 +142,7 @@ pub use core::time::TryFromFloatSecsError;
|
|||
/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
|
||||
///
|
||||
/// This workaround obscures programming errors where earlier and later instants are accidentally
|
||||
/// swapped. For this reason future rust versions may reintroduce panics.
|
||||
/// swapped. For this reason future Rust versions may reintroduce panics.
|
||||
///
|
||||
/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
|
||||
/// [`duration_since`]: Instant::duration_since
|
||||
|
@ -290,7 +290,7 @@ impl Instant {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Previous rust versions panicked when `earlier` was later than `self`. Currently this
|
||||
/// Previous Rust versions panicked when `earlier` was later than `self`. Currently this
|
||||
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||
/// See [Monotonicity].
|
||||
///
|
||||
|
@ -365,7 +365,7 @@ impl Instant {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Previous rust versions panicked when the current time was earlier than self. Currently this
|
||||
/// Previous Rust versions panicked when the current time was earlier than self. Currently this
|
||||
/// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
|
||||
/// See [Monotonicity].
|
||||
///
|
||||
|
@ -450,7 +450,7 @@ impl Sub<Instant> for Instant {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Previous rust versions panicked when `other` was later than `self`. Currently this
|
||||
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
|
||||
/// method saturates. Future versions may reintroduce the panic in some circumstances.
|
||||
/// See [Monotonicity].
|
||||
///
|
||||
|
|
|
@ -84,7 +84,7 @@ impl<T: Write> TerseFormatter<T> {
|
|||
if self.test_column % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 {
|
||||
// We insert a new line regularly in order to flush the
|
||||
// screen when dealing with line-buffered output (e.g., piping to
|
||||
// `stamp` in the rust CI).
|
||||
// `stamp` in the Rust CI).
|
||||
self.write_progress()?;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ pub use NamePadding::*;
|
|||
pub use TestFn::*;
|
||||
pub use TestName::*;
|
||||
|
||||
/// Type of the test according to the [rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
|
||||
/// Type of the test according to the [Rust book](https://doc.rust-lang.org/cargo/guide/tests.html)
|
||||
/// conventions.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum TestType {
|
||||
|
|
|
@ -129,6 +129,7 @@ pub struct RustcDocs {
|
|||
impl Step for RustcDocs {
|
||||
type Output = Option<GeneratedTarball>;
|
||||
const DEFAULT: bool = true;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
let builder = run.builder;
|
||||
|
|
|
@ -797,7 +797,10 @@ impl Step for Rustc {
|
|||
cargo.rustdocflag("-Zunstable-options");
|
||||
cargo.rustdocflag("-Znormalize-docs");
|
||||
cargo.rustdocflag("--show-type-layout");
|
||||
cargo.rustdocflag("--generate-link-to-definition");
|
||||
// FIXME: `--generate-link-to-definition` tries to resolve cfged out code
|
||||
// see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
|
||||
// cargo.rustdocflag("--generate-link-to-definition");
|
||||
|
||||
compile::rustc_cargo(builder, &mut cargo, target, compiler.stage);
|
||||
cargo.arg("-Zunstable-options");
|
||||
cargo.arg("-Zskip-rustdoc-fingerprint");
|
||||
|
@ -953,8 +956,10 @@ macro_rules! tool_doc {
|
|||
cargo.rustdocflag("-Arustdoc::private-intra-doc-links");
|
||||
cargo.rustdocflag("--enable-index-page");
|
||||
cargo.rustdocflag("--show-type-layout");
|
||||
cargo.rustdocflag("--generate-link-to-definition");
|
||||
cargo.rustdocflag("-Zunstable-options");
|
||||
// FIXME: `--generate-link-to-definition` tries to resolve cfged out code
|
||||
// see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
|
||||
// cargo.rustdocflag("--generate-link-to-definition");
|
||||
|
||||
let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
|
||||
$(for krate in $crates {
|
||||
|
|
|
@ -81,8 +81,10 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
|
|||
|
||||
// Add a test env var to do environment communication tests.
|
||||
program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into())));
|
||||
|
||||
// Let the tests know where to store temp files (they might run for a different target, which can make this hard to find).
|
||||
program.envs.push(("MIRI_TEMP".into(), Some(env::temp_dir().into())));
|
||||
let miri_temp = env::var_os("MIRI_TEMP").unwrap_or_else(|| env::temp_dir().into());
|
||||
program.envs.push(("MIRI_TEMP".into(), Some(miri_temp)));
|
||||
|
||||
let mut config = Config {
|
||||
target: Some(target.to_owned()),
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
//@ignore-target-windows: File handling is not implemented yet
|
||||
//@compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
// If this test is failing for you locally, you can try
|
||||
// 1. Deleting the files `/tmp/miri_*`
|
||||
// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files
|
||||
|
||||
#![feature(io_error_more)]
|
||||
#![feature(io_error_uncategorized)]
|
||||
|
||||
|
|
|
@ -190,7 +190,11 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) {
|
|||
let blessed_issues_path = tidy_src.join("issues_blessed.txt");
|
||||
let mut blessed_issues_txt = fs::File::create(&blessed_issues_path).unwrap();
|
||||
blessed_issues_txt.write(issues_txt_header.as_bytes()).unwrap();
|
||||
for filename in allowed_issue_names.difference(&remaining_issue_names) {
|
||||
// If we changed paths to use the OS separator, reassert Unix chauvinism for blessing.
|
||||
for filename in allowed_issue_names
|
||||
.difference(&remaining_issue_names)
|
||||
.map(|s| s.replace(std::path::MAIN_SEPARATOR_STR, "/"))
|
||||
{
|
||||
write!(blessed_issues_txt, "\"{filename}\",\n").unwrap();
|
||||
}
|
||||
write!(blessed_issues_txt, "]\n").unwrap();
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
error[E0191]: the value of the associated types `Item`, `Item`, `IntoIter` and `IntoIter` in `IntoIterator` must be specified
|
||||
error[E0191]: the value of the associated types `Item` and `IntoIter` in `IntoIterator` must be specified
|
||||
--> $DIR/overlaping-bound-suggestion.rs:7:13
|
||||
|
|
||||
LL | inner: <IntoIterator<Item: IntoIterator<Item: >>::IntoIterator as Item>::Core,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| | |
|
||||
| | associated types `Item`, `IntoIter` must be specified
|
||||
| associated types `Item`, `IntoIter` must be specified
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: specify the associated types: `IntoIterator<Item: IntoIterator<Item: >, Item = Type, IntoIter = Type>`
|
||||
|
||||
error[E0223]: ambiguous associated type
|
||||
--> $DIR/overlaping-bound-suggestion.rs:7:13
|
||||
|
|
|
@ -11,8 +11,8 @@ where
|
|||
}
|
||||
|
||||
impl<T: Copy + std::ops::Deref> UnsafeCopy<'_, T> for T {
|
||||
//~^ type mismatch resolving `<T as Deref>::Target == T`
|
||||
type Item = T;
|
||||
//~^ type mismatch resolving `<T as Deref>::Target == T`
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
error[E0271]: type mismatch resolving `<T as Deref>::Target == T`
|
||||
--> $DIR/hr-associated-type-projection-1.rs:13:33
|
||||
--> $DIR/hr-associated-type-projection-1.rs:14:17
|
||||
|
|
||||
LL | impl<T: Copy + std::ops::Deref> UnsafeCopy<'_, T> for T {
|
||||
| - ^^^^^^^^^^^^^^^^^ expected type parameter `T`, found associated type
|
||||
| |
|
||||
| expected this type parameter
|
||||
| - expected this type parameter
|
||||
LL | type Item = T;
|
||||
| ^ expected type parameter `T`, found associated type
|
||||
|
|
||||
= note: expected type parameter `T`
|
||||
found associated type `<T as Deref>::Target`
|
||||
|
|
|
@ -8,8 +8,6 @@ LL | extern crate core;
|
|||
= help: consider downloading the target with `rustup target add x86_64-unknown-uefi`
|
||||
= help: consider building the standard library from source with `cargo build -Zbuild-std`
|
||||
|
||||
error: requires `sized` lang_item
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0463`.
|
||||
|
|
|
@ -5,5 +5,4 @@ use std::collections::HashMap;
|
|||
fn main() {
|
||||
for _ in HashMap::new().iter().cloned() {} //~ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
|
||||
//~^ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
|
||||
//~| ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
|
||||
}
|
||||
|
|
|
@ -27,16 +27,6 @@ LL | for _ in HashMap::new().iter().cloned() {}
|
|||
= note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `Iterator`
|
||||
= note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `IntoIterator`
|
||||
|
||||
error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
|
||||
--> $DIR/issue-33941.rs:6:14
|
||||
|
|
||||
LL | for _ in HashMap::new().iter().cloned() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(&_, &_)`, found `&_`
|
||||
|
|
||||
= note: expected tuple `(&_, &_)`
|
||||
found reference `&_`
|
||||
= note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `Iterator`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
||||
|
|
|
@ -4,8 +4,6 @@ error[E0463]: can't find crate for `std`
|
|||
= help: consider downloading the target with `rustup target add thumbv6m-none-eabi`
|
||||
= help: consider building the standard library from source with `cargo build -Zbuild-std`
|
||||
|
||||
error: requires `sized` lang_item
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0463`.
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//~ ERROR can't find crate for `core`
|
||||
//~^ ERROR can't find crate for `compiler_builtins`
|
||||
|
||||
//@ compile-flags: --target thumbv7em-none-eabihf
|
||||
//@ needs-llvm-components: arm
|
||||
|
@ -8,6 +7,5 @@
|
|||
#![no_std]
|
||||
|
||||
extern crate cortex_m;
|
||||
//~^ ERROR can't find crate for `cortex_m`
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -4,16 +4,6 @@ error[E0463]: can't find crate for `core`
|
|||
= help: consider downloading the target with `rustup target add thumbv7em-none-eabihf`
|
||||
= help: consider building the standard library from source with `cargo build -Zbuild-std`
|
||||
|
||||
error[E0463]: can't find crate for `compiler_builtins`
|
||||
|
||||
error[E0463]: can't find crate for `cortex_m`
|
||||
--> $DIR/compiler-builtins-error.rs:10:1
|
||||
|
|
||||
LL | extern crate cortex_m;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ can't find crate
|
||||
|
||||
error: requires `sized` lang_item
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0463`.
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
#![allow(unused)]
|
||||
|
||||
fn test_122112() {
|
||||
// Make sure we don't ICE if parsing in recovery fails
|
||||
let _: std::env::temp_dir().join(&self, push: Box<usize>); //~ ERROR expected one of
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: std::env::temp_dir().join("foo"); //~ ERROR expected one of
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
error: expected one of `!`, `+`, `->`, `::`, `;`, or `=`, found `.`
|
||||
--> $DIR/recover-colon-instead-of-eq-in-local.rs:2:32
|
||||
--> $DIR/recover-colon-instead-of-eq-in-local.rs:5:32
|
||||
|
|
||||
LL | let _: std::env::temp_dir().join(&self, push: Box<usize>);
|
||||
| - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=`
|
||||
| |
|
||||
| while parsing the type for `_`
|
||||
|
||||
error: expected one of `!`, `+`, `->`, `::`, `;`, or `=`, found `.`
|
||||
--> $DIR/recover-colon-instead-of-eq-in-local.rs:9:32
|
||||
|
|
||||
LL | let _: std::env::temp_dir().join("foo");
|
||||
| - ^ expected one of `!`, `+`, `->`, `::`, `;`, or `=`
|
||||
|
@ -7,5 +15,5 @@ LL | let _: std::env::temp_dir().join("foo");
|
|||
| while parsing the type for `_`
|
||||
| help: use `=` if you meant to assign
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
60
tests/ui/trait-bounds/super-assoc-mismatch.rs
Normal file
60
tests/ui/trait-bounds/super-assoc-mismatch.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
trait Super {
|
||||
type Assoc;
|
||||
}
|
||||
impl Super for () {
|
||||
type Assoc = u8;
|
||||
}
|
||||
trait Sub: Super<Assoc = u16> {}
|
||||
|
||||
trait BoundOnSelf: Sub {}
|
||||
impl BoundOnSelf for () {}
|
||||
//~^ ERROR the trait bound `(): Sub` is not satisfied
|
||||
|
||||
trait BoundOnParam<T: Sub> {}
|
||||
impl BoundOnParam<()> for () {}
|
||||
//~^ ERROR the trait bound `(): Sub` is not satisfied
|
||||
|
||||
trait BoundOnAssoc {
|
||||
type Assoc: Sub;
|
||||
}
|
||||
impl BoundOnAssoc for () {
|
||||
type Assoc = ();
|
||||
//~^ ERROR the trait bound `(): Sub` is not satisfied
|
||||
}
|
||||
|
||||
trait BoundOnGat where Self::Assoc<u8>: Sub {
|
||||
type Assoc<T>;
|
||||
}
|
||||
impl BoundOnGat for u8 {
|
||||
type Assoc<T> = ();
|
||||
//~^ ERROR the trait bound `(): Sub` is not satisfied
|
||||
}
|
||||
|
||||
fn trivial_bound() where (): Sub {}
|
||||
//~^ ERROR the trait bound `(): Sub` is not satisfied
|
||||
|
||||
// The following is an edge case where the unsatisfied projection predicate
|
||||
// `<<u8 as MultiAssoc>::Assoc1<()> as SuperGeneric<u16>>::Assoc == <u8 as MultiAssoc>::Assoc2`
|
||||
// contains both associated types of `MultiAssoc`. To suppress the error about the unsatisfied
|
||||
// super projection, the error's span must be equal to the span of the unsatisfied trait error.
|
||||
trait SuperGeneric<T> {
|
||||
type Assoc;
|
||||
}
|
||||
trait SubGeneric<T>: SuperGeneric<T, Assoc = T> {}
|
||||
trait MultiAssoc
|
||||
where
|
||||
Self::Assoc1<()>: SubGeneric<Self::Assoc2>
|
||||
{
|
||||
type Assoc1<T>;
|
||||
type Assoc2;
|
||||
}
|
||||
impl SuperGeneric<u16> for () {
|
||||
type Assoc = u8;
|
||||
}
|
||||
impl MultiAssoc for u8 {
|
||||
type Assoc1<T> = ();
|
||||
//~^ ERROR the trait bound `(): SubGeneric<u16>` is not satisfied
|
||||
type Assoc2 = u16;
|
||||
}
|
||||
|
||||
fn main() {}
|
105
tests/ui/trait-bounds/super-assoc-mismatch.stderr
Normal file
105
tests/ui/trait-bounds/super-assoc-mismatch.stderr
Normal file
|
@ -0,0 +1,105 @@
|
|||
error[E0277]: the trait bound `(): Sub` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:10:22
|
||||
|
|
||||
LL | impl BoundOnSelf for () {}
|
||||
| ^^ the trait `Sub` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:7:1
|
||||
|
|
||||
LL | trait Sub: Super<Assoc = u16> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `BoundOnSelf`
|
||||
--> $DIR/super-assoc-mismatch.rs:9:20
|
||||
|
|
||||
LL | trait BoundOnSelf: Sub {}
|
||||
| ^^^ required by this bound in `BoundOnSelf`
|
||||
|
||||
error[E0277]: the trait bound `(): Sub` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:14:27
|
||||
|
|
||||
LL | impl BoundOnParam<()> for () {}
|
||||
| ^^ the trait `Sub` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:7:1
|
||||
|
|
||||
LL | trait Sub: Super<Assoc = u16> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `BoundOnParam`
|
||||
--> $DIR/super-assoc-mismatch.rs:13:23
|
||||
|
|
||||
LL | trait BoundOnParam<T: Sub> {}
|
||||
| ^^^ required by this bound in `BoundOnParam`
|
||||
|
||||
error[E0277]: the trait bound `(): Sub` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:21:18
|
||||
|
|
||||
LL | type Assoc = ();
|
||||
| ^^ the trait `Sub` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:7:1
|
||||
|
|
||||
LL | trait Sub: Super<Assoc = u16> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `BoundOnAssoc::Assoc`
|
||||
--> $DIR/super-assoc-mismatch.rs:18:17
|
||||
|
|
||||
LL | type Assoc: Sub;
|
||||
| ^^^ required by this bound in `BoundOnAssoc::Assoc`
|
||||
|
||||
error[E0277]: the trait bound `(): Sub` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:29:21
|
||||
|
|
||||
LL | type Assoc<T> = ();
|
||||
| ^^ the trait `Sub` is not implemented for `()`, which is required by `<u8 as BoundOnGat>::Assoc<u8>: Sub`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:7:1
|
||||
|
|
||||
LL | trait Sub: Super<Assoc = u16> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `BoundOnGat`
|
||||
--> $DIR/super-assoc-mismatch.rs:25:41
|
||||
|
|
||||
LL | trait BoundOnGat where Self::Assoc<u8>: Sub {
|
||||
| ^^^ required by this bound in `BoundOnGat`
|
||||
|
||||
error[E0277]: the trait bound `(): Sub` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:33:26
|
||||
|
|
||||
LL | fn trivial_bound() where (): Sub {}
|
||||
| ^^^^^^^ the trait `Sub` is not implemented for `()`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:7:1
|
||||
|
|
||||
LL | trait Sub: Super<Assoc = u16> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: see issue #48214
|
||||
= help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
|
||||
|
||||
error[E0277]: the trait bound `(): SubGeneric<u16>` is not satisfied
|
||||
--> $DIR/super-assoc-mismatch.rs:55:22
|
||||
|
|
||||
LL | type Assoc1<T> = ();
|
||||
| ^^ the trait `SubGeneric<u16>` is not implemented for `()`, which is required by `<u8 as MultiAssoc>::Assoc1<()>: SubGeneric<<u8 as MultiAssoc>::Assoc2>`
|
||||
|
|
||||
help: this trait has no implementations, consider adding one
|
||||
--> $DIR/super-assoc-mismatch.rs:43:1
|
||||
|
|
||||
LL | trait SubGeneric<T>: SuperGeneric<T, Assoc = T> {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `MultiAssoc`
|
||||
--> $DIR/super-assoc-mismatch.rs:46:23
|
||||
|
|
||||
LL | trait MultiAssoc
|
||||
| ---------- required by a bound in this trait
|
||||
LL | where
|
||||
LL | Self::Assoc1<()>: SubGeneric<Self::Assoc2>
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `MultiAssoc`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,16 @@
|
|||
//@ check-pass
|
||||
|
||||
#![feature(trait_alias)]
|
||||
|
||||
trait Foo<T> {}
|
||||
trait Bar { type Assoc; }
|
||||
|
||||
trait Alias<T: Bar> = Foo<T>;
|
||||
|
||||
// Check that an alias only requires us to specify the associated types
|
||||
// of the principal's supertraits. For example, we shouldn't require
|
||||
// specifying the type `Assoc` on trait `Bar` just because we have some
|
||||
// `T: Bar` where clause on the alias... because that makes no sense.
|
||||
fn use_alias<T: Bar>(x: &dyn Alias<T>) {}
|
||||
|
||||
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue