Auto merge of #3630 - rust-lang:rustup-2024-05-25, r=saethlin
Automatic Rustup
This commit is contained in:
commit
331bb3f10d
72 changed files with 811 additions and 586 deletions
|
@ -4252,6 +4252,7 @@ dependencies = [
|
|||
"rustc_fluent_macro",
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_mir_build",
|
||||
|
|
|
@ -29,8 +29,8 @@ use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, Debugger
|
|||
use rustc_middle::middle::exported_symbols;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
use rustc_middle::middle::lang_items;
|
||||
use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
|
||||
use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
|
|
@ -139,10 +139,9 @@ where
|
|||
ErrorHandled::TooGeneric(span)
|
||||
}
|
||||
err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar, span),
|
||||
err_inval!(Layout(LayoutError::ReferencesError(guar))) => ErrorHandled::Reported(
|
||||
ReportedErrorInfo::tainted_by_errors(guar),
|
||||
span,
|
||||
),
|
||||
err_inval!(Layout(LayoutError::ReferencesError(guar))) => {
|
||||
ErrorHandled::Reported(ReportedErrorInfo::tainted_by_errors(guar), span)
|
||||
}
|
||||
// Report remaining errors.
|
||||
_ => {
|
||||
let (our_span, frames) = get_span_and_frames();
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
#![feature(yeet_expr)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
pub mod check_consts;
|
||||
pub mod const_eval;
|
||||
mod errors;
|
||||
pub mod interpret;
|
||||
pub mod transform;
|
||||
pub mod util;
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
pub mod check_consts;
|
||||
pub mod validate;
|
|
@ -164,9 +164,8 @@ pub fn check_intrinsic_type(
|
|||
) {
|
||||
let generics = tcx.generics_of(intrinsic_id);
|
||||
let param = |n| {
|
||||
if let Some(&ty::GenericParamDef {
|
||||
name, kind: ty::GenericParamDefKind::Type { .. }, ..
|
||||
}) = generics.opt_param_at(n as usize, tcx)
|
||||
if let &ty::GenericParamDef { name, kind: ty::GenericParamDefKind::Type { .. }, .. } =
|
||||
generics.param_at(n as usize, tcx)
|
||||
{
|
||||
Ty::new_param(tcx, n, name)
|
||||
} else {
|
||||
|
|
|
@ -461,12 +461,10 @@ pub(super) fn explicit_predicates_of<'tcx>(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs {
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
let parent_def_id = tcx.hir().get_parent_item(hir_id);
|
||||
|
||||
if let Some(defaulted_param_def_id) =
|
||||
tcx.hir().opt_const_param_default_param_def_id(hir_id)
|
||||
if matches!(def_kind, DefKind::AnonConst)
|
||||
&& tcx.features().generic_const_exprs
|
||||
&& let Some(defaulted_param_def_id) =
|
||||
tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
|
||||
{
|
||||
// In `generics_of` we set the generics' parent to be our parent's parent which means that
|
||||
// we lose out on the predicates of our actual parent if we dont return those predicates here.
|
||||
|
@ -480,6 +478,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
|
|||
//
|
||||
// In the above code we want the anon const to have predicates in its param env for `T: Trait`
|
||||
// and we would be calling `explicit_predicates_of(Foo)` here
|
||||
let parent_def_id = tcx.local_parent(def_id);
|
||||
let parent_preds = tcx.explicit_predicates_of(parent_def_id);
|
||||
|
||||
// If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter
|
||||
|
@ -512,33 +511,6 @@ pub(super) fn explicit_predicates_of<'tcx>(
|
|||
predicates: { tcx.arena.alloc_from_iter(filtered_predicates) },
|
||||
};
|
||||
}
|
||||
|
||||
let parent_def_kind = tcx.def_kind(parent_def_id);
|
||||
if matches!(parent_def_kind, DefKind::OpaqueTy) {
|
||||
// In `instantiate_identity` we inherit the predicates of our parent.
|
||||
// However, opaque types do not have a parent (see `gather_explicit_predicates_of`), which means
|
||||
// that we lose out on the predicates of our actual parent if we dont return those predicates here.
|
||||
//
|
||||
//
|
||||
// fn foo<T: Trait>() -> impl Iterator<Output = Another<{ <T as Trait>::ASSOC }> > { todo!() }
|
||||
// ^^^^^^^^^^^^^^^^^^^ the def id we are calling
|
||||
// explicit_predicates_of on
|
||||
//
|
||||
// In the above code we want the anon const to have predicates in its param env for `T: Trait`.
|
||||
// However, the anon const cannot inherit predicates from its parent since it's opaque.
|
||||
//
|
||||
// To fix this, we call `explicit_predicates_of` directly on `foo`, the parent's parent.
|
||||
|
||||
// In the above example this is `foo::{opaque#0}` or `impl Iterator`
|
||||
let parent_hir_id = tcx.local_def_id_to_hir_id(parent_def_id.def_id);
|
||||
|
||||
// In the above example this is the function `foo`
|
||||
let item_def_id = tcx.hir().get_parent_item(parent_hir_id);
|
||||
|
||||
// In the above code example we would be calling `explicit_predicates_of(foo)` here
|
||||
return tcx.explicit_predicates_of(item_def_id);
|
||||
}
|
||||
}
|
||||
gather_explicit_predicates_of(tcx, def_id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,10 @@ hir_typeck_rpit_change_return_type = you could change the return type to be a bo
|
|||
hir_typeck_rustcall_incorrect_args =
|
||||
functions with the "rust-call" ABI must take a single non-self tuple argument
|
||||
|
||||
hir_typeck_self_ctor_from_outer_item = can't reference `Self` constructor from outer item
|
||||
.label = the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
.suggestion = replace `Self` with the actual type
|
||||
|
||||
hir_typeck_struct_expr_non_exhaustive =
|
||||
cannot create non-exhaustive {$what} using struct expression
|
||||
|
||||
|
|
|
@ -651,3 +651,31 @@ pub enum SuggestBoxingForReturnImplTrait {
|
|||
ends: Vec<Span>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_self_ctor_from_outer_item, code = E0401)]
|
||||
pub struct SelfCtorFromOuterItem {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub impl_span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: Option<ReplaceWithName>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(hir_typeck_self_ctor_from_outer_item)]
|
||||
pub struct SelfCtorFromOuterItemLint {
|
||||
#[label]
|
||||
pub impl_span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: Option<ReplaceWithName>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[suggestion(hir_typeck_suggestion, code = "{name}", applicability = "machine-applicable")]
|
||||
pub struct ReplaceWithName {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub name: String,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::callee::{self, DeferredCallResolution};
|
||||
use crate::errors::CtorIsPrivate;
|
||||
use crate::errors::{self, CtorIsPrivate};
|
||||
use crate::method::{self, MethodCallee, SelfSource};
|
||||
use crate::rvalue_scopes;
|
||||
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
|
||||
|
@ -21,6 +21,7 @@ use rustc_hir_analysis::hir_ty_lowering::{
|
|||
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
|
||||
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
|
||||
use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
|
||||
use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::error::TypeError;
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
|
@ -1156,6 +1157,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
span,
|
||||
tcx.at(span).type_of(impl_def_id).instantiate_identity(),
|
||||
);
|
||||
|
||||
// Firstly, check that this SelfCtor even comes from the item we're currently
|
||||
// typechecking. This can happen because we never validated the resolution of
|
||||
// SelfCtors, and when we started doing so, we noticed regressions. After
|
||||
// sufficiently long time, we can remove this check and turn it into a hard
|
||||
// error in `validate_res_from_ribs` -- it's just difficult to tell whether the
|
||||
// self type has any generic types during rustc_resolve, which is what we use
|
||||
// to determine if this is a hard error or warning.
|
||||
if std::iter::successors(Some(self.body_id.to_def_id()), |def_id| {
|
||||
self.tcx.generics_of(def_id).parent
|
||||
})
|
||||
.all(|def_id| def_id != impl_def_id)
|
||||
{
|
||||
let sugg = ty.normalized.ty_adt_def().map(|def| errors::ReplaceWithName {
|
||||
span: path_span,
|
||||
name: self.tcx.item_name(def.did()).to_ident_string(),
|
||||
});
|
||||
if ty.raw.has_param() {
|
||||
let guar = self.tcx.dcx().emit_err(errors::SelfCtorFromOuterItem {
|
||||
span: path_span,
|
||||
impl_span: tcx.def_span(impl_def_id),
|
||||
sugg,
|
||||
});
|
||||
return (Ty::new_error(self.tcx, guar), res);
|
||||
} else {
|
||||
self.tcx.emit_node_span_lint(
|
||||
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
|
||||
hir_id,
|
||||
path_span,
|
||||
errors::SelfCtorFromOuterItemLint {
|
||||
impl_span: tcx.def_span(impl_def_id),
|
||||
sugg,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match ty.normalized.ty_adt_def() {
|
||||
Some(adt_def) if adt_def.has_ctor() => {
|
||||
let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap();
|
||||
|
|
|
@ -235,11 +235,17 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti
|
|||
if let Some(item) = tcx.opt_associated_item(def_id.into())
|
||||
&& let ty::AssocKind::Const = item.kind
|
||||
&& let ty::ImplContainer = item.container
|
||||
&& let Some(trait_item) = item.trait_item_def_id
|
||||
&& let Some(trait_item_def_id) = item.trait_item_def_id
|
||||
{
|
||||
let args =
|
||||
tcx.impl_trait_ref(item.container_id(tcx)).unwrap().instantiate_identity().args;
|
||||
Some(tcx.type_of(trait_item).instantiate(tcx, args))
|
||||
let impl_def_id = item.container_id(tcx);
|
||||
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto(
|
||||
tcx,
|
||||
impl_def_id,
|
||||
impl_trait_ref.args,
|
||||
);
|
||||
tcx.check_args_compatible(trait_item_def_id, args)
|
||||
.then(|| tcx.type_of(trait_item_def_id).instantiate(tcx, args))
|
||||
} else {
|
||||
Some(fcx.next_ty_var(span))
|
||||
}
|
||||
|
|
|
@ -381,10 +381,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let maybe_missing_semi = self.check_for_missing_semi(expr, &mut err);
|
||||
|
||||
// We defer to the later error produced by `check_lhs_assignable`.
|
||||
// We only downgrade this if it's the LHS, though.
|
||||
// We only downgrade this if it's the LHS, though, and if this is a
|
||||
// valid assignment statement.
|
||||
if maybe_missing_semi
|
||||
&& let hir::Node::Expr(parent) = self.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::Assign(lhs, _, _) = parent.kind
|
||||
&& let hir::Node::Stmt(stmt) = self.tcx.parent_hir_node(parent.hir_id)
|
||||
&& let hir::StmtKind::Expr(_) | hir::StmtKind::Semi(_) = stmt.kind
|
||||
&& lhs.hir_id == expr.hir_id
|
||||
{
|
||||
err.downgrade_to_delayed_bug();
|
||||
|
|
|
@ -116,8 +116,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc<SerializedDepGraph>, WorkPr
|
|||
|
||||
if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
|
||||
// Decode the list of work_products
|
||||
let Ok(mut work_product_decoder) =
|
||||
MemDecoder::new(&work_products_data[..], start_pos)
|
||||
let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos)
|
||||
else {
|
||||
sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
|
||||
return LoadResult::DataOutOfDate;
|
||||
|
|
|
@ -54,18 +54,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
(ty::Param(expected), ty::Param(found)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
if let Some(param) = generics.opt_type_param(expected, tcx) {
|
||||
let e_span = tcx.def_span(param.def_id);
|
||||
let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
|
||||
if !sp.contains(e_span) {
|
||||
diag.span_label(e_span, "expected type parameter");
|
||||
}
|
||||
}
|
||||
if let Some(param) = generics.opt_type_param(found, tcx) {
|
||||
let f_span = tcx.def_span(param.def_id);
|
||||
let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
|
||||
if !sp.contains(f_span) {
|
||||
diag.span_label(f_span, "found type parameter");
|
||||
}
|
||||
}
|
||||
diag.note(
|
||||
"a type parameter was expected, but a different one was found; \
|
||||
you might be missing a type parameter or trait bound",
|
||||
|
@ -87,10 +83,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
| (ty::Alias(ty::Projection, proj), ty::Param(p))
|
||||
if !tcx.is_impl_trait_in_trait(proj.def_id) =>
|
||||
{
|
||||
let parent = tcx
|
||||
.generics_of(body_owner_def_id)
|
||||
.opt_type_param(p, tcx)
|
||||
.and_then(|param| {
|
||||
let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
|
||||
let p_def_id = param.def_id;
|
||||
let p_span = tcx.def_span(p_def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
|
@ -99,16 +92,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(
|
||||
p_span,
|
||||
format!("{expected}this type parameter"),
|
||||
);
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
p_def_id.as_local().and_then(|id| {
|
||||
let parent = p_def_id.as_local().and_then(|id| {
|
||||
let local_id = tcx.local_def_id_to_hir_id(id);
|
||||
let generics = tcx.parent_hir_node(local_id).generics()?;
|
||||
Some((id, generics))
|
||||
})
|
||||
});
|
||||
let mut note = true;
|
||||
if let Some((local_id, generics)) = parent {
|
||||
|
@ -183,8 +172,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
(ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
|
||||
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
if let Some(param) = generics.opt_type_param(p, tcx) {
|
||||
let p_span = tcx.def_span(param.def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
|
@ -193,7 +181,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
}
|
||||
diag.help("type parameters must be constrained to match other types");
|
||||
if tcx.sess.teach(diag.code.unwrap()) {
|
||||
diag.help(
|
||||
|
@ -233,12 +220,10 @@ impl<T> Trait<T> for X {
|
|||
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
|
||||
) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
if let Some(param) = generics.opt_type_param(p, tcx) {
|
||||
let p_span = tcx.def_span(param.def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "expected this type parameter");
|
||||
}
|
||||
}
|
||||
diag.help(format!(
|
||||
"every closure has a distinct type and so could not always match the \
|
||||
caller-chosen type of parameter `{p}`"
|
||||
|
@ -246,8 +231,7 @@ impl<T> Trait<T> for X {
|
|||
}
|
||||
(ty::Param(p), _) | (_, ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
if let Some(param) = generics.opt_type_param(p, tcx) {
|
||||
let p_span = tcx.def_span(param.def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
|
@ -257,7 +241,6 @@ impl<T> Trait<T> for X {
|
|||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
}
|
||||
}
|
||||
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
|
||||
if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
|
||||
{
|
||||
|
@ -545,10 +528,8 @@ impl<T> Trait<T> for X {
|
|||
return false;
|
||||
};
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let Some(param) = generics.opt_type_param(param_ty, tcx) else {
|
||||
return false;
|
||||
};
|
||||
let Some(def_id) = param.def_id.as_local() else {
|
||||
let def_id = generics.type_param(param_ty, tcx).def_id;
|
||||
let Some(def_id) = def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
|
@ -957,27 +957,14 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
|
||||
return Err((a_vid, b_vid));
|
||||
}
|
||||
// We don't silently want to constrain hidden types here, so we assert that either one side is
|
||||
// an infer var, so it'll get constrained to whatever the other side is, or there are no opaque
|
||||
// types involved.
|
||||
// We don't expect this to actually get hit, but if it does, we now at least know how to write
|
||||
// a test for it.
|
||||
(_, ty::Infer(ty::TyVar(_))) => {}
|
||||
(ty::Infer(ty::TyVar(_)), _) => {}
|
||||
_ if r_a != r_b && (r_a, r_b).has_opaque_types() => {
|
||||
span_bug!(
|
||||
cause.span(),
|
||||
"opaque types got hidden types registered from within subtype predicate: {r_a:?} vs {r_b:?}"
|
||||
)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| {
|
||||
if a_is_expected {
|
||||
Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b))
|
||||
Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::No, a, b))
|
||||
} else {
|
||||
Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a))
|
||||
Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::No, b, a))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
|
|||
};
|
||||
|
||||
let (article, ty, var) = match adt.did() {
|
||||
did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => ("a", "Option", "Some"),
|
||||
did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => {
|
||||
("a", "Option", "Some")
|
||||
}
|
||||
did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
|
||||
did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
|
||||
_ => return,
|
||||
|
|
|
@ -90,6 +90,7 @@ declare_lint_pass! {
|
|||
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
RUST_2021_PRELUDE_COLLISIONS,
|
||||
RUST_2024_INCOMPATIBLE_PAT,
|
||||
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
|
||||
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
SINGLE_USE_LIFETIMES,
|
||||
SOFT_UNSTABLE,
|
||||
|
@ -3149,6 +3150,47 @@ declare_lint! {
|
|||
"detects `#[unstable]` on stable trait implementations for stable types"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `self_constructor_from_outer_item` lint detects cases where the `Self` constructor
|
||||
/// was silently allowed due to a bug in the resolver, and which may produce surprising
|
||||
/// and unintended behavior.
|
||||
///
|
||||
/// Using a `Self` type alias from an outer item was never intended, but was silently allowed.
|
||||
/// This is deprecated -- and is a hard error when the `Self` type alias references generics
|
||||
/// that are not in scope.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(self_constructor_from_outer_item)]
|
||||
///
|
||||
/// struct S0(usize);
|
||||
///
|
||||
/// impl S0 {
|
||||
/// fn foo() {
|
||||
/// const C: S0 = Self(0);
|
||||
/// fn bar() -> S0 {
|
||||
/// Self(0)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// The `Self` type alias should not be reachable because nested items are not associated with
|
||||
/// the scope of the parameters from the parent item.
|
||||
pub SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
|
||||
Warn,
|
||||
"detect unsupported use of `Self` from outer item",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reference: "issue #124186 <https://github.com/rust-lang/rust/issues/124186>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons
|
||||
/// in macro bodies when the macro is invoked in expression position.
|
||||
|
|
|
@ -245,20 +245,6 @@ impl<'tcx> Generics {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` with the given index if available.
|
||||
pub fn opt_param_at(
|
||||
&'tcx self,
|
||||
param_index: usize,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx GenericParamDef> {
|
||||
if let Some(index) = param_index.checked_sub(self.parent_count) {
|
||||
self.own_params.get(index)
|
||||
} else {
|
||||
tcx.generics_of(self.parent.expect("parent_count > 0 but no parent?"))
|
||||
.opt_param_at(param_index, tcx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn params_to(&'tcx self, param_index: usize, tcx: TyCtxt<'tcx>) -> &'tcx [GenericParamDef] {
|
||||
if let Some(index) = param_index.checked_sub(self.parent_count) {
|
||||
&self.own_params[..index]
|
||||
|
@ -290,20 +276,6 @@ impl<'tcx> Generics {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` associated with this `ParamTy` if it belongs to this
|
||||
/// `Generics`.
|
||||
pub fn opt_type_param(
|
||||
&'tcx self,
|
||||
param: ParamTy,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx GenericParamDef> {
|
||||
let param = self.opt_param_at(param.index as usize, tcx)?;
|
||||
match param.kind {
|
||||
GenericParamDefKind::Type { .. } => Some(param),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `GenericParamDef` associated with this `ParamConst`.
|
||||
pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef {
|
||||
let param = self.param_at(param.index as usize, tcx);
|
||||
|
|
|
@ -16,6 +16,7 @@ rustc_errors = { path = "../rustc_errors" }
|
|||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_infer = { path = "../rustc_infer" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_mir_build = { path = "../rustc_mir_build" }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Inlining pass for MIR functions
|
||||
use crate::deref_separator::deref_finder;
|
||||
use rustc_attr::InlineAttr;
|
||||
use rustc_const_eval::transform::validate::validate_types;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
|
@ -21,6 +20,7 @@ use rustc_target::spec::abi::Abi;
|
|||
use crate::cost_checker::CostChecker;
|
||||
use crate::simplify::simplify_cfg;
|
||||
use crate::util;
|
||||
use crate::validate::validate_types;
|
||||
use std::iter;
|
||||
use std::ops::{Range, RangeFrom};
|
||||
|
||||
|
|
|
@ -109,9 +109,9 @@ mod simplify_comparison_integral;
|
|||
mod sroa;
|
||||
mod unreachable_enum_branching;
|
||||
mod unreachable_prop;
|
||||
mod validate;
|
||||
|
||||
use rustc_const_eval::transform::check_consts::{self, ConstCx};
|
||||
use rustc_const_eval::transform::validate;
|
||||
use rustc_const_eval::check_consts::{self, ConstCx};
|
||||
use rustc_mir_dataflow::rustc_peek;
|
||||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
|
|
@ -30,7 +30,7 @@ use std::assert_matches::assert_matches;
|
|||
use std::cell::Cell;
|
||||
use std::{cmp, iter, mem};
|
||||
|
||||
use rustc_const_eval::transform::check_consts::{qualifs, ConstCx};
|
||||
use rustc_const_eval::check_consts::{qualifs, ConstCx};
|
||||
|
||||
/// A `MirPass` for promotion.
|
||||
///
|
||||
|
|
|
@ -685,6 +685,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
check_equal(self, location, *f_ty);
|
||||
}
|
||||
ty::Adt(adt_def, args) => {
|
||||
// see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
|
||||
if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() {
|
||||
self.fail(
|
||||
location,
|
||||
format!("You can't project to field {f:?} of `DynMetadata` because \
|
||||
layout is weird and thinks it doesn't have fields."),
|
||||
);
|
||||
}
|
||||
|
||||
let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT);
|
||||
let Some(field) = adt_def.variant(var).fields.get(f) else {
|
||||
fail_out_of_bounds(self, location);
|
|
@ -44,7 +44,7 @@ fn ascii_escapes(b: &mut Bencher) {
|
|||
assert_fmt(
|
||||
s,
|
||||
r#""some\tmore\tascii\ttext\nthis time with some \"escapes\", also 64 byte""#,
|
||||
21,
|
||||
15,
|
||||
);
|
||||
b.iter(|| {
|
||||
black_box(format!("{:?}", black_box(s)));
|
||||
|
@ -72,7 +72,7 @@ fn mostly_unicode(b: &mut Bencher) {
|
|||
#[bench]
|
||||
fn mixed(b: &mut Bencher) {
|
||||
let s = "\"❤️\"\n\"hűha ez betű\"\n\"кириллических букв\".";
|
||||
assert_fmt(s, r#""\"❤\u{fe0f}\"\n\"hűha ez betű\"\n\"кириллических букв\".""#, 36);
|
||||
assert_fmt(s, r#""\"❤\u{fe0f}\"\n\"hűha ez betű\"\n\"кириллических букв\".""#, 21);
|
||||
b.iter(|| {
|
||||
black_box(format!("{:?}", black_box(s)));
|
||||
});
|
||||
|
|
|
@ -898,6 +898,7 @@ pub trait Ord: Eq + PartialOrd<Self> {
|
|||
/// assert_eq!(2.clamp(-2, 1), 1);
|
||||
/// ```
|
||||
#[must_use]
|
||||
#[inline]
|
||||
#[stable(feature = "clamp", since = "1.50.0")]
|
||||
fn clamp(self, min: Self, max: Self) -> Self
|
||||
where
|
||||
|
|
|
@ -2402,23 +2402,47 @@ impl Display for bool {
|
|||
impl Debug for str {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
f.write_char('"')?;
|
||||
let mut from = 0;
|
||||
for (i, c) in self.char_indices() {
|
||||
|
||||
// substring we know is printable
|
||||
let mut printable_range = 0..0;
|
||||
|
||||
fn needs_escape(b: u8) -> bool {
|
||||
b > 0x7E || b < 0x20 || b == b'\\' || b == b'"'
|
||||
}
|
||||
|
||||
// the loop here first skips over runs of printable ASCII as a fast path.
|
||||
// other chars (unicode, or ASCII that needs escaping) are then handled per-`char`.
|
||||
let mut rest = self;
|
||||
while rest.len() > 0 {
|
||||
let Some(non_printable_start) = rest.as_bytes().iter().position(|&b| needs_escape(b))
|
||||
else {
|
||||
printable_range.end += rest.len();
|
||||
break;
|
||||
};
|
||||
|
||||
printable_range.end += non_printable_start;
|
||||
// SAFETY: the position was derived from an iterator, so is known to be within bounds, and at a char boundary
|
||||
rest = unsafe { rest.get_unchecked(non_printable_start..) };
|
||||
|
||||
let mut chars = rest.chars();
|
||||
if let Some(c) = chars.next() {
|
||||
let esc = c.escape_debug_ext(EscapeDebugExtArgs {
|
||||
escape_grapheme_extended: true,
|
||||
escape_single_quote: false,
|
||||
escape_double_quote: true,
|
||||
});
|
||||
// If char needs escaping, flush backlog so far and write, else skip
|
||||
if esc.len() != 1 {
|
||||
f.write_str(&self[from..i])?;
|
||||
for c in esc {
|
||||
f.write_char(c)?;
|
||||
f.write_str(&self[printable_range.clone()])?;
|
||||
Display::fmt(&esc, f)?;
|
||||
printable_range.start = printable_range.end + c.len_utf8();
|
||||
}
|
||||
from = i + c.len_utf8();
|
||||
printable_range.end += c.len_utf8();
|
||||
}
|
||||
rest = chars.as_str();
|
||||
}
|
||||
f.write_str(&self[from..])?;
|
||||
|
||||
f.write_str(&self[printable_range])?;
|
||||
|
||||
f.write_char('"')
|
||||
}
|
||||
}
|
||||
|
@ -2434,13 +2458,12 @@ impl Display for str {
|
|||
impl Debug for char {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
f.write_char('\'')?;
|
||||
for c in self.escape_debug_ext(EscapeDebugExtArgs {
|
||||
let esc = self.escape_debug_ext(EscapeDebugExtArgs {
|
||||
escape_grapheme_extended: true,
|
||||
escape_single_quote: true,
|
||||
escape_double_quote: false,
|
||||
}) {
|
||||
f.write_char(c)?
|
||||
}
|
||||
});
|
||||
Display::fmt(&esc, f)?;
|
||||
f.write_char('\'')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ extern "rust-intrinsic" {
|
|||
#[rustc_nounwind]
|
||||
pub fn simd_fabs<T>(x: T) -> T;
|
||||
|
||||
/// Elementwise minimum of a vector.
|
||||
/// Elementwise minimum of two vectors.
|
||||
///
|
||||
/// `T` must be a vector of floating-point primitive types.
|
||||
///
|
||||
|
@ -160,7 +160,7 @@ extern "rust-intrinsic" {
|
|||
#[rustc_nounwind]
|
||||
pub fn simd_fmin<T>(x: T, y: T) -> T;
|
||||
|
||||
/// Elementwise maximum of a vector.
|
||||
/// Elementwise maximum of two vectors.
|
||||
///
|
||||
/// `T` must be a vector of floating-point primitive types.
|
||||
///
|
||||
|
@ -387,7 +387,7 @@ extern "rust-intrinsic" {
|
|||
#[rustc_nounwind]
|
||||
pub fn simd_reduce_mul_ordered<T, U>(x: T, y: U) -> U;
|
||||
|
||||
/// Add elements within a vector in arbitrary order. May also be re-associated with
|
||||
/// Multiply elements within a vector in arbitrary order. May also be re-associated with
|
||||
/// unordered additions on the inputs/outputs.
|
||||
///
|
||||
/// `T` must be a vector of integer or floating-point primitive types.
|
||||
|
@ -405,7 +405,7 @@ extern "rust-intrinsic" {
|
|||
#[rustc_nounwind]
|
||||
pub fn simd_reduce_all<T>(x: T) -> bool;
|
||||
|
||||
/// Check if all mask values are true.
|
||||
/// Check if any mask value is true.
|
||||
///
|
||||
/// `T` must be a vector of integer primitive types.
|
||||
///
|
||||
|
|
|
@ -178,8 +178,8 @@ impl<T: ?Sized> Clone for PtrComponents<T> {
|
|||
/// compare equal (since identical vtables can be deduplicated within a codegen unit).
|
||||
#[lang = "dyn_metadata"]
|
||||
pub struct DynMetadata<Dyn: ?Sized> {
|
||||
vtable_ptr: &'static VTable,
|
||||
phantom: crate::marker::PhantomData<Dyn>,
|
||||
_vtable_ptr: &'static VTable,
|
||||
_phantom: crate::marker::PhantomData<Dyn>,
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -191,6 +191,17 @@ extern "C" {
|
|||
}
|
||||
|
||||
impl<Dyn: ?Sized> DynMetadata<Dyn> {
|
||||
/// One of the things that rustc_middle does with this being a lang item is
|
||||
/// give it `FieldsShape::Primitive`, which means that as far as codegen can
|
||||
/// tell, it *is* a reference, and thus doesn't have any fields.
|
||||
/// That means we can't use field access, and have to transmute it instead.
|
||||
#[inline]
|
||||
fn vtable_ptr(self) -> *const VTable {
|
||||
// SAFETY: this layout assumption is hard-coded into the compiler.
|
||||
// If it's somehow not a size match, the transmute will error.
|
||||
unsafe { crate::mem::transmute::<Self, &'static VTable>(self) }
|
||||
}
|
||||
|
||||
/// Returns the size of the type associated with this vtable.
|
||||
#[inline]
|
||||
pub fn size_of(self) -> usize {
|
||||
|
@ -199,7 +210,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
|
|||
// `Send` part!
|
||||
// SAFETY: DynMetadata always contains a valid vtable pointer
|
||||
return unsafe {
|
||||
crate::intrinsics::vtable_size(self.vtable_ptr as *const VTable as *const ())
|
||||
crate::intrinsics::vtable_size(self.vtable_ptr() as *const ())
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -208,7 +219,7 @@ impl<Dyn: ?Sized> DynMetadata<Dyn> {
|
|||
pub fn align_of(self) -> usize {
|
||||
// SAFETY: DynMetadata always contains a valid vtable pointer
|
||||
return unsafe {
|
||||
crate::intrinsics::vtable_align(self.vtable_ptr as *const VTable as *const ())
|
||||
crate::intrinsics::vtable_align(self.vtable_ptr() as *const ())
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -226,7 +237,7 @@ unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
|
|||
|
||||
impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("DynMetadata").field(&(self.vtable_ptr as *const VTable)).finish()
|
||||
f.debug_tuple("DynMetadata").field(&self.vtable_ptr()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +259,7 @@ impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
|
|||
impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
crate::ptr::eq::<VTable>(self.vtable_ptr, other.vtable_ptr)
|
||||
crate::ptr::eq::<VTable>(self.vtable_ptr(), other.vtable_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +267,7 @@ impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {
|
|||
#[inline]
|
||||
#[allow(ambiguous_wide_pointer_comparisons)]
|
||||
fn cmp(&self, other: &Self) -> crate::cmp::Ordering {
|
||||
(self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable))
|
||||
<*const VTable>::cmp(&self.vtable_ptr(), &other.vtable_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,6 +281,6 @@ impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {
|
|||
impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
crate::ptr::hash::<VTable, _>(self.vtable_ptr, hasher)
|
||||
crate::ptr::hash::<VTable, _>(self.vtable_ptr(), hasher)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ pub mod cmath;
|
|||
pub mod os_str;
|
||||
pub mod path;
|
||||
pub mod sync;
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_imports)]
|
||||
pub mod thread_local;
|
||||
|
||||
// FIXME(117276): remove this, move feature implementations into individual
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::cell::UnsafeCell;
|
||||
use crate::hint::unreachable_unchecked;
|
||||
use crate::mem::forget;
|
||||
use crate::ptr;
|
||||
use crate::sys::thread_local::abort_on_dtor_unwind;
|
||||
use crate::sys::thread_local_dtor::register_dtor;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
|
||||
#![cfg_attr(test, allow(unused))]
|
||||
|
||||
// There are three thread-local implementations: "static", "fast", "OS".
|
||||
// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the
|
||||
|
@ -24,94 +25,12 @@ cfg_if::cfg_if! {
|
|||
}
|
||||
}
|
||||
|
||||
// Not used by the fast-local TLS anymore.
|
||||
// FIXME(#110897): remove this.
|
||||
#[allow(unused)]
|
||||
mod lazy {
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::hint;
|
||||
use crate::mem;
|
||||
|
||||
pub struct LazyKeyInner<T> {
|
||||
inner: UnsafeCell<Option<T>>,
|
||||
}
|
||||
|
||||
impl<T> LazyKeyInner<T> {
|
||||
pub const fn new() -> LazyKeyInner<T> {
|
||||
LazyKeyInner { inner: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
pub unsafe fn get(&self) -> Option<&'static T> {
|
||||
// SAFETY: The caller must ensure no reference is ever handed out to
|
||||
// the inner cell nor mutable reference to the Option<T> inside said
|
||||
// cell. This make it safe to hand a reference, though the lifetime
|
||||
// of 'static is itself unsafe, making the get method unsafe.
|
||||
unsafe { (*self.inner.get()).as_ref() }
|
||||
}
|
||||
|
||||
/// The caller must ensure that no reference is active: this method
|
||||
/// needs unique access.
|
||||
pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T {
|
||||
// Execute the initialization up front, *then* move it into our slot,
|
||||
// just in case initialization fails.
|
||||
let value = init();
|
||||
let ptr = self.inner.get();
|
||||
|
||||
// SAFETY:
|
||||
//
|
||||
// note that this can in theory just be `*ptr = Some(value)`, but due to
|
||||
// the compiler will currently codegen that pattern with something like:
|
||||
//
|
||||
// ptr::drop_in_place(ptr)
|
||||
// ptr::write(ptr, Some(value))
|
||||
//
|
||||
// Due to this pattern it's possible for the destructor of the value in
|
||||
// `ptr` (e.g., if this is being recursively initialized) to re-access
|
||||
// TLS, in which case there will be a `&` and `&mut` pointer to the same
|
||||
// value (an aliasing violation). To avoid setting the "I'm running a
|
||||
// destructor" flag we just use `mem::replace` which should sequence the
|
||||
// operations a little differently and make this safe to call.
|
||||
//
|
||||
// The precondition also ensures that we are the only one accessing
|
||||
// `self` at the moment so replacing is fine.
|
||||
unsafe {
|
||||
let _ = mem::replace(&mut *ptr, Some(value));
|
||||
}
|
||||
|
||||
// SAFETY: With the call to `mem::replace` it is guaranteed there is
|
||||
// a `Some` behind `ptr`, not a `None` so `unreachable_unchecked`
|
||||
// will never be reached.
|
||||
unsafe {
|
||||
// After storing `Some` we want to get a reference to the contents of
|
||||
// what we just stored. While we could use `unwrap` here and it should
|
||||
// always work it empirically doesn't seem to always get optimized away,
|
||||
// which means that using something like `try_with` can pull in
|
||||
// panicking code and cause a large size bloat.
|
||||
match *ptr {
|
||||
Some(ref x) => x,
|
||||
None => hint::unreachable_unchecked(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Watch out: unsynchronized internal mutability!
|
||||
///
|
||||
/// # Safety
|
||||
/// Causes UB if any reference to the value is used after this.
|
||||
#[allow(unused)]
|
||||
pub(crate) unsafe fn take(&self) -> Option<T> {
|
||||
let mutable: *mut _ = UnsafeCell::get(&self.inner);
|
||||
// SAFETY: That's the caller's problem.
|
||||
unsafe { mutable.replace(None) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a callback in a scenario which must not unwind (such as a `extern "C"
|
||||
/// fn` declared in a user crate). If the callback unwinds anyway, then
|
||||
/// `rtabort` with a message about thread local panicking on drop.
|
||||
#[inline]
|
||||
pub fn abort_on_dtor_unwind(f: impl FnOnce()) {
|
||||
#[allow(dead_code)]
|
||||
fn abort_on_dtor_unwind(f: impl FnOnce()) {
|
||||
// Using a guard like this is lower cost.
|
||||
let guard = DtorUnwindGuard;
|
||||
f();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use super::lazy::LazyKeyInner;
|
||||
use super::abort_on_dtor_unwind;
|
||||
use crate::cell::Cell;
|
||||
use crate::sys_common::thread_local_key::StaticKey as OsStaticKey;
|
||||
use crate::{fmt, marker, panic, ptr};
|
||||
use crate::marker::PhantomData;
|
||||
use crate::ptr;
|
||||
use crate::sys_common::thread_local_key::StaticKey as OsKey;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow_internal_unstable(thread_local_internals)]
|
||||
|
@ -10,38 +11,9 @@ use crate::{fmt, marker, panic, ptr};
|
|||
#[rustc_macro_transparency = "semitransparent"]
|
||||
pub macro thread_local_inner {
|
||||
// used to generate the `LocalKey` value for const-initialized thread locals
|
||||
(@key $t:ty, const $init:expr) => {{
|
||||
#[inline]
|
||||
#[deny(unsafe_op_in_unsafe_fn)]
|
||||
unsafe fn __getit(
|
||||
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
const INIT_EXPR: $t = $init;
|
||||
|
||||
// On platforms without `#[thread_local]` we fall back to the
|
||||
// same implementation as below for os thread locals.
|
||||
#[inline]
|
||||
const fn __init() -> $t { INIT_EXPR }
|
||||
static __KEY: $crate::thread::local_impl::Key<$t> =
|
||||
$crate::thread::local_impl::Key::new();
|
||||
unsafe {
|
||||
__KEY.get(move || {
|
||||
if let $crate::option::Option::Some(init) = _init {
|
||||
if let $crate::option::Option::Some(value) = init.take() {
|
||||
return value;
|
||||
} else if $crate::cfg!(debug_assertions) {
|
||||
$crate::unreachable!("missing initial value");
|
||||
}
|
||||
}
|
||||
__init()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
$crate::thread::LocalKey::new(__getit)
|
||||
}
|
||||
}},
|
||||
(@key $t:ty, const $init:expr) => {
|
||||
$crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR })
|
||||
},
|
||||
|
||||
// used to generate the `LocalKey` value for `thread_local!`
|
||||
(@key $t:ty, $init:expr) => {
|
||||
|
@ -55,20 +27,11 @@ pub macro thread_local_inner {
|
|||
unsafe fn __getit(
|
||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||
) -> $crate::option::Option<&'static $t> {
|
||||
static __KEY: $crate::thread::local_impl::Key<$t> =
|
||||
$crate::thread::local_impl::Key::new();
|
||||
use $crate::thread::local_impl::Key;
|
||||
|
||||
static __KEY: Key<$t> = Key::new();
|
||||
unsafe {
|
||||
__KEY.get(move || {
|
||||
if let $crate::option::Option::Some(init) = init {
|
||||
if let $crate::option::Option::Some(value) = init.take() {
|
||||
return value;
|
||||
} else if $crate::cfg!(debug_assertions) {
|
||||
$crate::unreachable!("missing default value");
|
||||
}
|
||||
}
|
||||
__init()
|
||||
})
|
||||
__KEY.get(init, __init)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,78 +48,78 @@ pub macro thread_local_inner {
|
|||
|
||||
/// Use a regular global static to store this key; the state provided will then be
|
||||
/// thread-local.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Key<T> {
|
||||
// OS-TLS key that we'll use to key off.
|
||||
os: OsStaticKey,
|
||||
marker: marker::PhantomData<Cell<T>>,
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Key<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Key").finish_non_exhaustive()
|
||||
}
|
||||
os: OsKey,
|
||||
marker: PhantomData<Cell<T>>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for Key<T> {}
|
||||
|
||||
struct Value<T: 'static> {
|
||||
inner: LazyKeyInner<T>,
|
||||
value: T,
|
||||
key: &'static Key<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Key<T> {
|
||||
#[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
|
||||
pub const fn new() -> Key<T> {
|
||||
Key { os: OsStaticKey::new(Some(destroy_value::<T>)), marker: marker::PhantomData }
|
||||
Key { os: OsKey::new(Some(destroy_value::<T>)), marker: PhantomData }
|
||||
}
|
||||
|
||||
/// It is a requirement for the caller to ensure that no mutable
|
||||
/// reference is active when this method is called.
|
||||
pub unsafe fn get(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
|
||||
// SAFETY: See the documentation for this method.
|
||||
/// Get the value associated with this key, initializating it if necessary.
|
||||
///
|
||||
/// # Safety
|
||||
/// * the returned reference must not be used after recursive initialization
|
||||
/// or thread destruction occurs.
|
||||
pub unsafe fn get(
|
||||
&'static self,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
// SAFETY: (FIXME: get should actually be safe)
|
||||
let ptr = unsafe { self.os.get() as *mut Value<T> };
|
||||
if ptr.addr() > 1 {
|
||||
// SAFETY: the check ensured the pointer is safe (its destructor
|
||||
// is not running) + it is coming from a trusted source (self).
|
||||
if let Some(ref value) = unsafe { (*ptr).inner.get() } {
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
unsafe { Some(&(*ptr).value) }
|
||||
} else {
|
||||
// SAFETY: At this point we are sure we have no value and so
|
||||
// initializing (or trying to) is safe.
|
||||
unsafe { self.try_initialize(init) }
|
||||
unsafe { self.try_initialize(ptr, i, f) }
|
||||
}
|
||||
}
|
||||
|
||||
// `try_initialize` is only called once per os thread local variable,
|
||||
// except in corner cases where thread_local dtors reference other
|
||||
// thread_local's, or it is being recursively initialized.
|
||||
unsafe fn try_initialize(&'static self, init: impl FnOnce() -> T) -> Option<&'static T> {
|
||||
// SAFETY: No mutable references are ever handed out meaning getting
|
||||
// the value is ok.
|
||||
let ptr = unsafe { self.os.get() as *mut Value<T> };
|
||||
unsafe fn try_initialize(
|
||||
&'static self,
|
||||
ptr: *mut Value<T>,
|
||||
i: Option<&mut Option<T>>,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Option<&'static T> {
|
||||
if ptr.addr() == 1 {
|
||||
// destructor is running
|
||||
return None;
|
||||
}
|
||||
|
||||
let ptr = if ptr.is_null() {
|
||||
// If the lookup returned null, we haven't initialized our own
|
||||
// local copy, so do that now.
|
||||
let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
|
||||
// SAFETY: At this point we are sure there is no value inside
|
||||
// ptr so setting it will not affect anyone else.
|
||||
let value = i.and_then(Option::take).unwrap_or_else(f);
|
||||
let ptr = Box::into_raw(Box::new(Value { value, key: self }));
|
||||
// SAFETY: (FIXME: get should actually be safe)
|
||||
let old = unsafe { self.os.get() as *mut Value<T> };
|
||||
// SAFETY: `ptr` is a correct pointer that can be destroyed by the key destructor.
|
||||
unsafe {
|
||||
self.os.set(ptr as *mut u8);
|
||||
}
|
||||
ptr
|
||||
} else {
|
||||
// recursive initialization
|
||||
ptr
|
||||
};
|
||||
if !old.is_null() {
|
||||
// If the variable was recursively initialized, drop the old value.
|
||||
// SAFETY: We cannot be inside a `LocalKey::with` scope, as the
|
||||
// initializer has already returned and the next scope only starts
|
||||
// after we return the pointer. Therefore, there can be no references
|
||||
// to the old value.
|
||||
drop(unsafe { Box::from_raw(old) });
|
||||
}
|
||||
|
||||
// SAFETY: ptr has been ensured as non-NUL just above an so can be
|
||||
// dereferenced safely.
|
||||
unsafe { Some((*ptr).inner.initialize(init)) }
|
||||
// SAFETY: We just created this value above.
|
||||
unsafe { Some(&(*ptr).value) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,16 +133,11 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
|
|||
//
|
||||
// Note that to prevent an infinite loop we reset it back to null right
|
||||
// before we return from the destructor ourselves.
|
||||
//
|
||||
// Wrap the call in a catch to ensure unwinding is caught in the event
|
||||
// a panic takes place in a destructor.
|
||||
if let Err(_) = panic::catch_unwind(|| unsafe {
|
||||
let ptr = Box::from_raw(ptr as *mut Value<T>);
|
||||
abort_on_dtor_unwind(|| {
|
||||
let ptr = unsafe { Box::from_raw(ptr as *mut Value<T>) };
|
||||
let key = ptr.key;
|
||||
key.os.set(ptr::without_provenance_mut(1));
|
||||
unsafe { key.os.set(ptr::without_provenance_mut(1)) };
|
||||
drop(ptr);
|
||||
key.os.set(ptr::null_mut());
|
||||
}) {
|
||||
rtabort!("thread local panicked on drop");
|
||||
}
|
||||
unsafe { key.os.set(ptr::null_mut()) };
|
||||
});
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ fn fmt_thousands_sep(mut n: f64, sep: char) -> String {
|
|||
(0, true) => write!(output, "{:06.2}", n / base as f64).unwrap(),
|
||||
(0, false) => write!(output, "{:.2}", n / base as f64).unwrap(),
|
||||
(_, true) => write!(output, "{:03}", n as usize / base).unwrap(),
|
||||
_ => write!(output, "{}", n as usize / base).unwrap()
|
||||
_ => write!(output, "{}", n as usize / base).unwrap(),
|
||||
}
|
||||
if pow != 0 {
|
||||
output.push(sep);
|
||||
|
|
|
@ -6,7 +6,7 @@ and some of the technical details of the build system.
|
|||
Note that this README only covers internal information, not how to use the tool.
|
||||
Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information.
|
||||
|
||||
[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html
|
||||
[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping/intro.html
|
||||
|
||||
## Introduction
|
||||
|
||||
|
|
|
@ -60,7 +60,14 @@ fn check_cli<const N: usize>(paths: [&str; N]) {
|
|||
macro_rules! std {
|
||||
($host:ident => $target:ident, stage = $stage:literal) => {
|
||||
compile::Std::new(
|
||||
Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage },
|
||||
Compiler {
|
||||
host: TargetSelection::from_user(concat!(
|
||||
stringify!($host),
|
||||
"-",
|
||||
stringify!($host)
|
||||
)),
|
||||
stage: $stage,
|
||||
},
|
||||
TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))),
|
||||
)
|
||||
};
|
||||
|
@ -83,7 +90,14 @@ macro_rules! doc_std {
|
|||
macro_rules! rustc {
|
||||
($host:ident => $target:ident, stage = $stage:literal) => {
|
||||
compile::Rustc::new(
|
||||
Compiler { host: TargetSelection::from_user(concat!(stringify!($host), "-", stringify!($host))), stage: $stage },
|
||||
Compiler {
|
||||
host: TargetSelection::from_user(concat!(
|
||||
stringify!($host),
|
||||
"-",
|
||||
stringify!($host)
|
||||
)),
|
||||
stage: $stage,
|
||||
},
|
||||
TargetSelection::from_user(concat!(stringify!($target), "-", stringify!($target))),
|
||||
)
|
||||
};
|
||||
|
@ -141,10 +155,14 @@ fn check_missing_paths_for_x_test_tests() {
|
|||
|
||||
// Skip if not a test directory.
|
||||
if path.ends_with("tests/auxiliary") || !path.is_dir() {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
assert!(tests_remap_paths.iter().any(|item| path.ends_with(*item)), "{} is missing in PATH_REMAP tests list.", path.display());
|
||||
assert!(
|
||||
tests_remap_paths.iter().any(|item| path.ends_with(*item)),
|
||||
"{} is missing in PATH_REMAP tests list.",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,7 +203,8 @@ fn alias_and_path_for_library() {
|
|||
&[std!(A => A, stage = 0), std!(A => A, stage = 1)]
|
||||
);
|
||||
|
||||
let mut cache = run_build(&["library".into(), "core".into()], configure("doc", &["A-A"], &["A-A"]));
|
||||
let mut cache =
|
||||
run_build(&["library".into(), "core".into()], configure("doc", &["A-A"], &["A-A"]));
|
||||
assert_eq!(first(cache.all::<doc::Std>()), &[doc_std!(A => A, stage = 0)]);
|
||||
}
|
||||
|
||||
|
|
|
@ -199,11 +199,15 @@ than building it.
|
|||
if !["A-A", "B-B", "C-C"].contains(&target_str.as_str()) {
|
||||
let mut has_target = false;
|
||||
|
||||
let missing_targets_hashset: HashSet<_> = STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect();
|
||||
let duplicated_targets: Vec<_> = stage0_supported_target_list.intersection(&missing_targets_hashset).collect();
|
||||
let missing_targets_hashset: HashSet<_> =
|
||||
STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect();
|
||||
let duplicated_targets: Vec<_> =
|
||||
stage0_supported_target_list.intersection(&missing_targets_hashset).collect();
|
||||
|
||||
if !duplicated_targets.is_empty() {
|
||||
println!("Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list.");
|
||||
println!(
|
||||
"Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list."
|
||||
);
|
||||
for duplicated_target in duplicated_targets {
|
||||
println!(" {duplicated_target}");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod ci;
|
||||
pub mod git;
|
||||
pub mod metrics;
|
||||
pub mod util;
|
||||
pub mod stage0_parser;
|
||||
pub mod util;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use hir::LangItem;
|
||||
use rustc_attr::StableSince;
|
||||
use rustc_const_eval::transform::check_consts::ConstCx;
|
||||
use rustc_const_eval::check_consts::ConstCx;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
|
|
@ -1 +1 @@
|
|||
78dd504f2fd87c0cfabff7d9174253411caf2f80
|
||||
21e6de7eb64c09102de3f100420a09edc1a2a8d7
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC:
|
||||
thread 'rustc' panicked at compiler/rustc_mir_transform/src/validate.rs:LL:CC:
|
||||
broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]:
|
||||
(*(_2.0: *mut i32)), has deref at the wrong place
|
||||
stack backtrace:
|
||||
|
|
|
@ -228,7 +228,6 @@ run-make/rmeta-preferred/Makefile
|
|||
run-make/rustc-macro-dep-files/Makefile
|
||||
run-make/rustdoc-io-error/Makefile
|
||||
run-make/rustdoc-verify-output-files/Makefile
|
||||
run-make/rustdoc-with-output-option/Makefile
|
||||
run-make/sanitizer-cdylib-link/Makefile
|
||||
run-make/sanitizer-dylib-link/Makefile
|
||||
run-make/sanitizer-staticlib-link/Makefile
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
//@ known-bug: #118403
|
||||
#![feature(generic_const_exprs)]
|
||||
pub struct X<const N: usize> {}
|
||||
impl<const Z: usize> X<Z> {
|
||||
pub fn y<'a, U: 'a>(&'a self) -> impl Iterator<Item = impl Iterator<Item = [u8; Z]> + '_> {
|
||||
(0..1).map(move |_| (0..1).map(move |_| loop {}))
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
//@ known-bug: #121574
|
||||
#![feature(generic_const_exprs)]
|
||||
pub struct DimName<const N: usize> {}
|
||||
impl<const Z: usize> X<Z> {
|
||||
pub fn y<'a, U: 'a>(&'a self) -> impl Iterator<Item = impl Iterator<Item = [u8; Z]> + '_> {
|
||||
"0".as_bytes(move |_| (0..1).map(move |_| loop {}))
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
//@ known-bug: #121574
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
impl<const Z: usize> X<Z> {
|
||||
pub fn y<'a, U: 'a>(&'a self) -> impl Iterator<Item = impl Iterator<Item = [u8; Z]> + '_> {}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
//@ known-bug: rust-lang/rust#124833
|
||||
#![feature(generic_const_items)]
|
||||
|
||||
trait Trait {
|
||||
const C<'a>: &'a str;
|
||||
}
|
||||
|
||||
impl Trait for () {
|
||||
const C<'a>: = "C";
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//@ known-bug: rust-lang/rust#124891
|
||||
|
||||
type Tait = impl FnOnce() -> ();
|
||||
|
||||
fn reify_as_tait() -> Thunk<Tait> {
|
||||
Thunk::new(|cont| cont)
|
||||
}
|
||||
|
||||
struct Thunk<F>(F);
|
||||
|
||||
impl<F> Thunk<F> {
|
||||
fn new(f: F)
|
||||
where
|
||||
F: ContFn,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
trait ContFn {}
|
||||
|
||||
impl<F: FnOnce(Tait) -> ()> ContFn for F {}
|
|
@ -1,8 +0,0 @@
|
|||
include ../tools.mk
|
||||
|
||||
OUTPUT_DIR := "$(TMPDIR)/rustdoc"
|
||||
|
||||
all:
|
||||
$(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --output $(OUTPUT_DIR)
|
||||
|
||||
$(HTMLDOCCK) $(OUTPUT_DIR) src/lib.rs
|
16
tests/run-make/rustdoc-with-output-option/rmake.rs
Normal file
16
tests/run-make/rustdoc-with-output-option/rmake.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use run_make_support::{htmldocck, rustdoc, tmp_dir};
|
||||
|
||||
fn main() {
|
||||
let out_dir = tmp_dir().join("rustdoc");
|
||||
|
||||
rustdoc()
|
||||
.input("src/lib.rs")
|
||||
.crate_name("foobar")
|
||||
.crate_type("lib")
|
||||
// This is intentionally using `--output` option flag and not the `output()` method.
|
||||
.arg("--output")
|
||||
.arg(&out_dir)
|
||||
.run();
|
||||
|
||||
assert!(htmldocck().arg(out_dir).arg("src/lib.rs").status().unwrap().success());
|
||||
}
|
20
tests/rustdoc-json/keyword_private.rs
Normal file
20
tests/rustdoc-json/keyword_private.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Ensure keyword docs are present with --document-private-items
|
||||
|
||||
//@ compile-flags: --document-private-items
|
||||
#![feature(rustdoc_internals)]
|
||||
|
||||
// @!has "$.index[*][?(@.name=='match')]"
|
||||
// @has "$.index[*][?(@.name=='foo')]"
|
||||
// @is "$.index[*][?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]"]'
|
||||
// @is "$.index[*][?(@.name=='foo')].docs" '"this is a test!"'
|
||||
#[doc(keyword = "match")]
|
||||
/// this is a test!
|
||||
pub mod foo {}
|
||||
|
||||
// @!has "$.index[*][?(@.name=='hello')]"
|
||||
// @has "$.index[*][?(@.name=='bar')]"
|
||||
// @is "$.index[*][?(@.name=='bar')].attrs" '["#[doc(keyword = \"hello\")]"]'
|
||||
// @is "$.index[*][?(@.name=='bar')].docs" '"hello"'
|
||||
#[doc(keyword = "hello")]
|
||||
/// hello
|
||||
mod bar {}
|
14
tests/ui/binop/nested-assignment-may-be-deref.rs
Normal file
14
tests/ui/binop/nested-assignment-may-be-deref.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub fn bad(x: &mut bool) {
|
||||
if true
|
||||
*x = true {}
|
||||
//~^ ERROR cannot multiply `bool` by `&mut bool`
|
||||
}
|
||||
|
||||
pub fn bad2(x: &mut bool) {
|
||||
let y: bool;
|
||||
y = true
|
||||
*x = true;
|
||||
//~^ ERROR cannot multiply `bool` by `&mut bool`
|
||||
}
|
||||
|
||||
fn main() {}
|
29
tests/ui/binop/nested-assignment-may-be-deref.stderr
Normal file
29
tests/ui/binop/nested-assignment-may-be-deref.stderr
Normal file
|
@ -0,0 +1,29 @@
|
|||
error[E0369]: cannot multiply `bool` by `&mut bool`
|
||||
--> $DIR/nested-assignment-may-be-deref.rs:3:5
|
||||
|
|
||||
LL | if true
|
||||
| ---- bool
|
||||
LL | *x = true {}
|
||||
| ^- &mut bool
|
||||
|
|
||||
help: you might have meant to write a semicolon here
|
||||
|
|
||||
LL | if true;
|
||||
| +
|
||||
|
||||
error[E0369]: cannot multiply `bool` by `&mut bool`
|
||||
--> $DIR/nested-assignment-may-be-deref.rs:10:5
|
||||
|
|
||||
LL | y = true
|
||||
| ---- bool
|
||||
LL | *x = true;
|
||||
| ^- &mut bool
|
||||
|
|
||||
help: you might have meant to write a semicolon here
|
||||
|
|
||||
LL | y = true;
|
||||
| +
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0369`.
|
|
@ -0,0 +1,13 @@
|
|||
//@ check-pass
|
||||
|
||||
#![feature(generic_const_exprs)]
|
||||
//~^ WARN the feature `generic_const_exprs` is incomplete and may not be safe to use
|
||||
|
||||
pub fn y<'a, U: 'a>() -> impl IntoIterator<Item = impl IntoIterator<Item = [u8; { 1 + 2 }]> + 'a> {
|
||||
[[[1, 2, 3]]]
|
||||
}
|
||||
// Make sure that the `predicates_of` for `{ 1 + 2 }` don't mention the duplicated lifetimes of
|
||||
// the *outer* iterator. Whether they should mention the duplicated lifetimes of the *inner*
|
||||
// iterator are another question, but not really something we need to answer immediately.
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/double-opaque-parent-predicates.rs:3:12
|
||||
|
|
||||
LL | #![feature(generic_const_exprs)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
21
tests/ui/generic-const-items/assoc-const-missing-type.rs
Normal file
21
tests/ui/generic-const-items/assoc-const-missing-type.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Ensure that we properly deal with missing/placeholder types inside GACs.
|
||||
// issue: rust-lang/rust#124833
|
||||
#![feature(generic_const_items)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
const K<T>: T;
|
||||
const Q<'a>: &'a str;
|
||||
}
|
||||
|
||||
impl Trait for () {
|
||||
const K<T> = ();
|
||||
//~^ ERROR missing type for `const` item
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
const Q = "";
|
||||
//~^ ERROR missing type for `const` item
|
||||
//~| ERROR lifetime parameters or bounds on const `Q` do not match the trait declaration
|
||||
}
|
||||
|
||||
fn main() {}
|
48
tests/ui/generic-const-items/assoc-const-missing-type.stderr
Normal file
48
tests/ui/generic-const-items/assoc-const-missing-type.stderr
Normal file
|
@ -0,0 +1,48 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/assoc-const-missing-type.rs:12:18
|
||||
|
|
||||
LL | const K<T> = ();
|
||||
| - ^^ expected type parameter `T`, found `()`
|
||||
| |
|
||||
| expected this type parameter
|
||||
|
|
||||
= note: expected type parameter `T`
|
||||
found unit type `()`
|
||||
|
||||
error: missing type for `const` item
|
||||
--> $DIR/assoc-const-missing-type.rs:12:15
|
||||
|
|
||||
LL | const K<T> = ();
|
||||
| ^ help: provide a type for the associated constant: `()`
|
||||
|
||||
error[E0195]: lifetime parameters or bounds on const `Q` do not match the trait declaration
|
||||
--> $DIR/assoc-const-missing-type.rs:16:12
|
||||
|
|
||||
LL | const Q<'a>: &'a str;
|
||||
| ---- lifetimes in impl do not match this const in trait
|
||||
...
|
||||
LL | const Q = "";
|
||||
| ^ lifetimes do not match const in trait
|
||||
|
||||
error: missing type for `const` item
|
||||
--> $DIR/assoc-const-missing-type.rs:16:12
|
||||
|
|
||||
LL | const Q = "";
|
||||
| ^ help: provide a type for the associated constant: `: &str`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/assoc-const-missing-type.rs:12:18
|
||||
|
|
||||
LL | const K<T> = ();
|
||||
| - ^^ expected type parameter `T`, found `()`
|
||||
| |
|
||||
| expected this type parameter
|
||||
|
|
||||
= note: expected type parameter `T`
|
||||
found unit type `()`
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0195, E0308.
|
||||
For more information about an error, try `rustc --explain E0195`.
|
59
tests/ui/impl-trait/lazy_subtyping_of_opaques.rs
Normal file
59
tests/ui/impl-trait/lazy_subtyping_of_opaques.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
//! This test checks that we allow subtyping predicates that contain opaque types.
|
||||
//! No hidden types are being constrained in the subtyping predicate, but type and
|
||||
//! lifetime variables get subtyped in the generic parameter list of the opaque.
|
||||
|
||||
use std::iter;
|
||||
|
||||
mod either {
|
||||
pub enum Either<L, R> {
|
||||
Left(L),
|
||||
Right(R),
|
||||
}
|
||||
|
||||
impl<L: Iterator, R: Iterator<Item = L::Item>> Iterator for Either<L, R> {
|
||||
type Item = L::Item;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
pub use self::Either::{Left, Right};
|
||||
}
|
||||
|
||||
pub enum BabeConsensusLogRef<'a> {
|
||||
NextEpochData(BabeNextEpochRef<'a>),
|
||||
NextConfigData,
|
||||
}
|
||||
|
||||
impl<'a> BabeConsensusLogRef<'a> {
|
||||
pub fn scale_encoding(
|
||||
&self,
|
||||
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + Clone + 'a {
|
||||
//~^ ERROR is not satisfied
|
||||
//~| ERROR is not satisfied
|
||||
//~| ERROR is not satisfied
|
||||
match self {
|
||||
BabeConsensusLogRef::NextEpochData(digest) => either::Left(either::Left(
|
||||
digest.scale_encoding().map(either::Left).map(either::Left),
|
||||
)),
|
||||
BabeConsensusLogRef::NextConfigData => either::Right(
|
||||
// The Opaque type from ``scale_encoding` gets used opaquely here, while the `R`
|
||||
// generic parameter of `Either` contains type variables that get subtyped and the
|
||||
// opaque type contains lifetime variables that get subtyped.
|
||||
iter::once(either::Right(either::Left([1])))
|
||||
.chain(std::iter::once([1]).map(either::Right).map(either::Right)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BabeNextEpochRef<'a>(&'a ());
|
||||
|
||||
impl<'a> BabeNextEpochRef<'a> {
|
||||
pub fn scale_encoding(
|
||||
&self,
|
||||
) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + Clone + 'a {
|
||||
std::iter::once([1])
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
21
tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr
Normal file
21
tests/ui/impl-trait/lazy_subtyping_of_opaques.stderr
Normal file
|
@ -0,0 +1,21 @@
|
|||
error[E0277]: the trait bound `Either<Either<Map<Map<impl Iterator<Item = impl AsRef<[u8]> + Clone + '_> + Clone + '_, fn(impl AsRef<[u8]> + Clone + '_) -> Either<impl AsRef<[u8]> + Clone + '_, _> {Either::<impl AsRef<[u8]> + Clone + '_, _>::Left}>, fn(Either<impl AsRef<[u8]> + Clone + '_, _>) -> Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either::<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Left}>, _>, std::iter::Chain<std::iter::Once<Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>>, Map<Map<std::iter::Once<[{integer}; 1]>, fn([{integer}; 1]) -> Either<[{integer}; 1], [{integer}; 1]> {Either::<[{integer}; 1], [{integer}; 1]>::Right}>, fn(Either<[{integer}; 1], [{integer}; 1]>) -> Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either::<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Right}>>>: Clone` is not satisfied
|
||||
--> $DIR/lazy_subtyping_of_opaques.rs:30:10
|
||||
|
|
||||
LL | ) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + Clone + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Either<Either<Map<Map<impl Iterator<Item = impl AsRef<[u8]> + Clone + '_> + Clone + '_, fn(impl AsRef<[u8]> + Clone + '_) -> Either<impl AsRef<[u8]> + Clone + '_, _> {Either::<impl AsRef<[u8]> + Clone + '_, _>::Left}>, fn(Either<impl AsRef<[u8]> + Clone + '_, _>) -> Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either::<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Left}>, _>, std::iter::Chain<std::iter::Once<Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>>, Map<Map<std::iter::Once<[{integer}; 1]>, fn([{integer}; 1]) -> Either<[{integer}; 1], [{integer}; 1]> {Either::<[{integer}; 1], [{integer}; 1]>::Right}>, fn(Either<[{integer}; 1], [{integer}; 1]>) -> Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>> {Either::<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>::Right}>>>`
|
||||
|
||||
error[E0277]: the trait bound `Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>: AsRef<[u8]>` is not satisfied
|
||||
--> $DIR/lazy_subtyping_of_opaques.rs:30:31
|
||||
|
|
||||
LL | ) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + Clone + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsRef<[u8]>` is not implemented for `Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>`
|
||||
|
||||
error[E0277]: the trait bound `Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>: Clone` is not satisfied
|
||||
--> $DIR/lazy_subtyping_of_opaques.rs:30:31
|
||||
|
|
||||
LL | ) -> impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + Clone + 'a {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `Either<Either<impl AsRef<[u8]> + Clone + '_, _>, Either<[{integer}; 1], [{integer}; 1]>>`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,7 +1,12 @@
|
|||
struct A<B>(B);
|
||||
impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
//~^ ERROR the size for values of type `B` cannot be known at compilation time
|
||||
//~| ERROR the size for values of type `B` cannot be known at compilation time
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR mismatched types
|
||||
//~| ERROR `main` function not found in crate
|
||||
|
||||
impl<B> A<B> {
|
||||
fn d() {
|
||||
fn d() {
|
||||
Self(1)
|
||||
//~^ ERROR can't reference `Self` constructor from outer item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,79 +1,12 @@
|
|||
error[E0601]: `main` function not found in crate `do_not_ice_on_note_and_explain`
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:2:37
|
||||
error[E0401]: can't reference `Self` constructor from outer item
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:6:13
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
| ^ consider adding a `main` function to `$DIR/do-not-ice-on-note_and_explain.rs`
|
||||
LL | impl<B> A<B> {
|
||||
| ------------ the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
...
|
||||
LL | Self(1)
|
||||
| ^^^^ help: replace `Self` with the actual type: `A`
|
||||
|
||||
error[E0277]: the size for values of type `B` cannot be known at compilation time
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:2:32
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
| - ---- ^ doesn't have a size known at compile-time
|
||||
| | |
|
||||
| | required by a bound introduced by this call
|
||||
| this type parameter needs to be `Sized`
|
||||
|
|
||||
note: required by a bound in `A`
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:1:10
|
||||
|
|
||||
LL | struct A<B>(B);
|
||||
| ^ required by this bound in `A`
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:2:32
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
| ---- ^ expected type parameter `B`, found integer
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
= note: expected type parameter `B`
|
||||
found type `{integer}`
|
||||
note: tuple struct defined here
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:1:8
|
||||
|
|
||||
LL | struct A<B>(B);
|
||||
| ^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:2:27
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
| ^^^^^^^ expected `()`, found `A<B>`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found struct `A<B>`
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1);}}}
|
||||
| +
|
||||
help: try adding a return type
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d() -> A<B>{Self(1)}}}
|
||||
| +++++++
|
||||
|
||||
error[E0277]: the size for values of type `B` cannot be known at compilation time
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:2:27
|
||||
|
|
||||
LL | impl<B>A<B>{fn d(){fn d(){Self(1)}}}
|
||||
| - ^^^^^^^ doesn't have a size known at compile-time
|
||||
| |
|
||||
| this type parameter needs to be `Sized`
|
||||
|
|
||||
note: required by an implicit `Sized` bound in `A`
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:1:10
|
||||
|
|
||||
LL | struct A<B>(B);
|
||||
| ^ required by the implicit `Sized` requirement on this type parameter in `A`
|
||||
help: you could relax the implicit `Sized` bound on `B` if it were used through indirection like `&B` or `Box<B>`
|
||||
--> $DIR/do-not-ice-on-note_and_explain.rs:1:10
|
||||
|
|
||||
LL | struct A<B>(B);
|
||||
| ^ - ...if indirection were used here: `Box<B>`
|
||||
| |
|
||||
| this could be changed to `B: ?Sized`...
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0277, E0308, E0601.
|
||||
For more information about an error, try `rustc --explain E0277`.
|
||||
For more information about this error, try `rustc --explain E0401`.
|
||||
|
|
|
@ -6,8 +6,12 @@ struct S0(usize);
|
|||
impl S0 {
|
||||
fn foo() {
|
||||
const C: S0 = Self(0);
|
||||
//~^ WARN can't reference `Self` constructor from outer item
|
||||
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||
fn bar() -> S0 {
|
||||
Self(0)
|
||||
//~^ WARN can't reference `Self` constructor from outer item
|
||||
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
27
tests/ui/self/self-ctor-nongeneric.stderr
Normal file
27
tests/ui/self/self-ctor-nongeneric.stderr
Normal file
|
@ -0,0 +1,27 @@
|
|||
warning: can't reference `Self` constructor from outer item
|
||||
--> $DIR/self-ctor-nongeneric.rs:8:23
|
||||
|
|
||||
LL | impl S0 {
|
||||
| ------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
LL | fn foo() {
|
||||
LL | const C: S0 = Self(0);
|
||||
| ^^^^ help: replace `Self` with the actual type: `S0`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #124186 <https://github.com/rust-lang/rust/issues/124186>
|
||||
= note: `#[warn(self_constructor_from_outer_item)]` on by default
|
||||
|
||||
warning: can't reference `Self` constructor from outer item
|
||||
--> $DIR/self-ctor-nongeneric.rs:12:13
|
||||
|
|
||||
LL | impl S0 {
|
||||
| ------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
...
|
||||
LL | Self(0)
|
||||
| ^^^^ help: replace `Self` with the actual type: `S0`
|
||||
|
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #124186 <https://github.com/rust-lang/rust/issues/124186>
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
14
tests/ui/self/self-ctor.rs
Normal file
14
tests/ui/self/self-ctor.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
struct S0<T>(T);
|
||||
|
||||
impl<T> S0<T> {
|
||||
fn foo() {
|
||||
const C: S0<i32> = Self(0);
|
||||
//~^ ERROR can't reference `Self` constructor from outer item
|
||||
fn bar() -> S0<i32> {
|
||||
Self(0)
|
||||
//~^ ERROR can't reference `Self` constructor from outer item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
21
tests/ui/self/self-ctor.stderr
Normal file
21
tests/ui/self/self-ctor.stderr
Normal file
|
@ -0,0 +1,21 @@
|
|||
error[E0401]: can't reference `Self` constructor from outer item
|
||||
--> $DIR/self-ctor.rs:5:28
|
||||
|
|
||||
LL | impl<T> S0<T> {
|
||||
| ------------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
LL | fn foo() {
|
||||
LL | const C: S0<i32> = Self(0);
|
||||
| ^^^^ help: replace `Self` with the actual type: `S0`
|
||||
|
||||
error[E0401]: can't reference `Self` constructor from outer item
|
||||
--> $DIR/self-ctor.rs:8:13
|
||||
|
|
||||
LL | impl<T> S0<T> {
|
||||
| ------------- the inner item doesn't inherit generics from this impl, so `Self` is invalid to reference
|
||||
...
|
||||
LL | Self(0)
|
||||
| ^^^^ help: replace `Self` with the actual type: `S0`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0401`.
|
30
tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs
Normal file
30
tests/ui/type-alias-impl-trait/lazy_subtyping_of_opaques.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
//! This test used to ICE rust-lang/rust#124891
|
||||
//! because we added an assertion for catching cases where opaque types get
|
||||
//! registered during the processing of subtyping predicates.
|
||||
|
||||
type Tait = impl FnOnce() -> ();
|
||||
|
||||
fn reify_as_tait() -> Thunk<Tait> {
|
||||
Thunk::new(|cont| cont)
|
||||
//~^ ERROR: mismatched types
|
||||
//~| ERROR: mismatched types
|
||||
}
|
||||
|
||||
struct Thunk<F>(F);
|
||||
|
||||
impl<F> Thunk<F> {
|
||||
fn new(f: F)
|
||||
where
|
||||
F: ContFn,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
trait ContFn {}
|
||||
|
||||
impl<F: FnOnce(Tait) -> ()> ContFn for F {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,26 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/lazy_subtyping_of_opaques.rs:10:23
|
||||
|
|
||||
LL | type Tait = impl FnOnce() -> ();
|
||||
| ------------------- the found opaque type
|
||||
...
|
||||
LL | Thunk::new(|cont| cont)
|
||||
| ^^^^ expected `()`, found opaque type
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found opaque type `Tait`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/lazy_subtyping_of_opaques.rs:10:5
|
||||
|
|
||||
LL | fn reify_as_tait() -> Thunk<Tait> {
|
||||
| ----------- expected `Thunk<_>` because of return type
|
||||
LL | Thunk::new(|cont| cont)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `Thunk<_>`, found `()`
|
||||
|
|
||||
= note: expected struct `Thunk<_>`
|
||||
found unit type `()`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
|
@ -389,8 +389,11 @@ exclude_labels = [
|
|||
|
||||
[autolabel."WG-trait-system-refactor"]
|
||||
trigger_files = [
|
||||
"compiler/rustc_middle/src/traits/solve",
|
||||
"compiler/rustc_next_trait_solver",
|
||||
"compiler/rustc_trait_selection/src/solve",
|
||||
"compiler/rustc_middle/src/traits/solve"
|
||||
"compiler/rustc_type_ir/src/solve",
|
||||
"tests/ui/traits/next-solver",
|
||||
]
|
||||
|
||||
[autolabel."PG-exploit-mitigations"]
|
||||
|
|
Loading…
Add table
Reference in a new issue