Auto merge of - fmease:generic-consts, r=cjgillot

Implement generic const items

This implements generic parameters and where-clauses on free and associated const items under the experimental feature gate `generic_const_items`. See rust-lang/lang-team#214.

Tracking issue: .
Fixes .

This PR doesn't include rustfmt support as per [nightly style procedure](https://github.com/rust-lang/style-team/blob/master/nightly-style-procedure.md) and it doesn't add rustdoc JSON support (see [related Zulip topic](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/Rustdoc.20JSON.3A.20Experimental.20rustc.20features)).

CC `@scottmcm` `@compiler-errors` `@WalterSmuts`
r? `@oli-obk`

<details><summary>Resolved Questions</summary>

* Q: Should `const ADD<const N: usize, const M: usize>: usize = N + M; ADD::<0, 1>` trigger the error *generic parameters may not be used in const operations* (which can be unlocked with `#![feature(generic_const_exprs)]`). Currently it doesn't. Or does this fall under [this paragraph](71f71a5397/compiler/rustc_resolve/src/late.rs (L191))?
  * A: No, https://github.com/rust-lang/rust/pull/113522#issuecomment-1628634092
* Q: Should `const UNUSED: () = () where String: Copy;` (with `#![feature(trivial_bounds)]` and with `UNUSED` unused) succeed compilation? Currently it doesn't: *evaluation of constant value failed // entering unreachable code*
  * A: Yes, but postponed until stabilization (latest), https://github.com/rust-lang/rust/pull/113522#issuecomment-1628634092

</details>
This commit is contained in:
bors 2023-07-28 22:04:26 +00:00
commit 04abc370b9
91 changed files with 1645 additions and 360 deletions
compiler
rustc_ast/src
rustc_ast_lowering/src
rustc_ast_passes/src
rustc_ast_pretty/src/pprust/state
rustc_builtin_macros/src
rustc_expand/src
rustc_feature/src
rustc_hir/src
rustc_hir_analysis/src
rustc_hir_pretty/src
rustc_hir_typeck/src
rustc_infer/src/infer/error_reporting
rustc_lint/src
rustc_middle/src/hir/map
rustc_mir_build/src/build
rustc_parse
rustc_passes/src
rustc_resolve/src
rustc_span/src
rustc_trait_selection/src/traits/error_reporting
src
tests

View File

@ -2947,6 +2947,7 @@ pub struct StaticItem {
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct ConstItem {
pub defaultness: Defaultness,
pub generics: Generics,
pub ty: P<Ty>,
pub expr: Option<P<Expr>>,
}
@ -3058,6 +3059,7 @@ impl ItemKind {
match self {
Self::Fn(box Fn { generics, .. })
| Self::TyAlias(box TyAlias { generics, .. })
| Self::Const(box ConstItem { generics, .. })
| Self::Enum(_, generics)
| Self::Struct(_, generics)
| Self::Union(_, generics)

View File

@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
}
fn visit_const_item<T: MutVisitor>(
ConstItem { defaultness, ty, expr }: &mut ConstItem,
ConstItem { defaultness, generics, ty, expr }: &mut ConstItem,
visitor: &mut T,
) {
visit_defaultness(defaultness, visitor);
visitor.visit_generics(generics);
visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr));
}

View File

@ -308,8 +308,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
match &item.kind {
ItemKind::ExternCrate(_) => {}
ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false),
ItemKind::Static(box StaticItem { ty, mutability: _, expr })
| ItemKind::Const(box ConstItem { ty, expr, .. }) => {
ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => {
visitor.visit_ty(ty);
walk_list!(visitor, visit_expr, expr);
}
ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
visitor.visit_generics(generics);
visitor.visit_ty(ty);
walk_list!(visitor, visit_expr, expr);
}
@ -677,7 +681,8 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem,
visitor.visit_ident(ident);
walk_list!(visitor, visit_attribute, attrs);
match kind {
AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
visitor.visit_generics(generics);
visitor.visit_ty(ty);
walk_list!(visitor, visit_expr, expr);
}

View File

@ -231,9 +231,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
hir::ItemKind::Static(ty, *m, body_id)
}
ItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
let (ty, body_id) = self.lower_const_item(ty, span, expr.as_deref());
hir::ItemKind::Const(ty, body_id)
ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
let (generics, (ty, body_id)) = self.lower_generics(
generics,
Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_const_item(ty, span, expr.as_deref()),
);
hir::ItemKind::Const(ty, generics, body_id)
}
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
@ -715,11 +721,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
let trait_item_def_id = hir_id.expect_owner();
let (generics, kind, has_default) = match &i.kind {
AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
let ty =
self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let body = expr.as_ref().map(|x| self.lower_const_body(i.span, Some(x)));
(hir::Generics::empty(), hir::TraitItemKind::Const(ty, body), body.is_some())
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
let (generics, kind) = self.lower_generics(
&generics,
Const::No,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this.lower_ty(
ty,
&ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
let body = expr.as_ref().map(|x| this.lower_const_body(i.span, Some(x)));
hir::TraitItemKind::Const(ty, body)
},
);
(generics, kind, expr.is_some())
}
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
let asyncness = sig.header.asyncness;
@ -817,14 +835,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(hir_id, &i.attrs);
let (generics, kind) = match &i.kind {
AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
let ty =
self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
(
hir::Generics::empty(),
hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())),
)
}
AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
&generics,
Const::No,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
let ty = this
.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
let body = this.lower_const_body(i.span, expr.as_deref());
hir::ImplItemKind::Const(ty, body)
},
),
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
self.current_item = Some(i.span);
let asyncness = sig.header.asyncness;

View File

@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {

View File

@ -30,10 +30,15 @@ impl<'a> State<'a> {
ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
}
ast::ForeignItemKind::Static(ty, mutbl, body) => {
let def = ast::Defaultness::Final;
self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
}
ast::ForeignItemKind::Static(ty, mutbl, body) => self.print_item_const(
ident,
Some(*mutbl),
&ast::Generics::default(),
ty,
body.as_deref(),
vis,
ast::Defaultness::Final,
),
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
defaultness,
generics,
@ -67,6 +72,7 @@ impl<'a> State<'a> {
&mut self,
ident: Ident,
mutbl: Option<ast::Mutability>,
generics: &ast::Generics,
ty: &ast::Ty,
body: Option<&ast::Expr>,
vis: &ast::Visibility,
@ -82,6 +88,7 @@ impl<'a> State<'a> {
};
self.word_space(leading);
self.print_ident(ident);
self.print_generic_params(&generics.params);
self.word_space(":");
self.print_type(ty);
if body.is_some() {
@ -92,6 +99,7 @@ impl<'a> State<'a> {
self.word_space("=");
self.print_expr(body);
}
self.print_where_clause(&generics.where_clause);
self.word(";");
self.end(); // end the outer cbox
}
@ -158,20 +166,21 @@ impl<'a> State<'a> {
self.word(";");
}
ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => {
let def = ast::Defaultness::Final;
self.print_item_const(
item.ident,
Some(*mutbl),
&ast::Generics::default(),
ty,
body.as_deref(),
&item.vis,
def,
ast::Defaultness::Final,
);
}
ast::ItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
self.print_item_const(
item.ident,
None,
generics,
ty,
expr.as_deref(),
&item.vis,
@ -515,8 +524,16 @@ impl<'a> State<'a> {
ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
}
ast::AssocItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
self.print_item_const(ident, None, ty, expr.as_deref(), vis, *defaultness);
ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
self.print_item_const(
ident,
None,
generics,
ty,
expr.as_deref(),
vis,
*defaultness,
);
}
ast::AssocItemKind::Type(box ast::TyAlias {
defaultness,

View File

@ -255,6 +255,7 @@ pub fn expand_test_or_bench(
ast::ItemKind::Const(
ast::ConstItem {
defaultness: ast::Defaultness::Final,
generics: ast::Generics::default(),
ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
// test::TestDescAndFn {
expr: Some(

View File

@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> {
span,
name,
AttrVec::new(),
ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()),
ast::ItemKind::Const(
ast::ConstItem {
defaultness,
// FIXME(generic_const_items): Pass the generics as a parameter.
generics: ast::Generics::default(),
ty,
expr: Some(expr),
}
.into(),
),
)
}

View File

@ -424,6 +424,8 @@ declare_features! (
(incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
/// Allows non-trivial generic constants which have to have wfness manually propagated to callers
(incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
/// Allows generic parameters and where-clauses on free & associated const items.
(incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None),
/// Allows using `..=X` as a patterns in slices.
(active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
/// Allows `if let` guard in match arms.

View File

@ -3130,9 +3130,9 @@ impl<'hir> Item<'hir> {
}
/// Expect an [`ItemKind::Const`] or panic.
#[track_caller]
pub fn expect_const(&self) -> (&'hir Ty<'hir>, BodyId) {
let ItemKind::Const(ty, body) = self.kind else { self.expect_failed("a constant") };
(ty, body)
pub fn expect_const(&self) -> (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId) {
let ItemKind::Const(ty, gen, body) = self.kind else { self.expect_failed("a constant") };
(ty, gen, body)
}
/// Expect an [`ItemKind::Fn`] or panic.
#[track_caller]
@ -3319,7 +3319,7 @@ pub enum ItemKind<'hir> {
/// A `static` item.
Static(&'hir Ty<'hir>, Mutability, BodyId),
/// A `const` item.
Const(&'hir Ty<'hir>, BodyId),
Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId),
/// A function declaration.
Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId),
/// A MBE macro definition (`macro_rules!` or `macro`).
@ -3372,6 +3372,7 @@ impl ItemKind<'_> {
Some(match *self {
ItemKind::Fn(_, ref generics, _)
| ItemKind::TyAlias(_, ref generics)
| ItemKind::Const(_, ref generics, _)
| ItemKind::OpaqueTy(OpaqueTy { ref generics, .. })
| ItemKind::Enum(_, ref generics)
| ItemKind::Struct(_, ref generics)
@ -3567,7 +3568,9 @@ impl<'hir> OwnerNode<'hir> {
match self {
OwnerNode::Item(Item {
kind:
ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body),
ItemKind::Static(_, _, body)
| ItemKind::Const(_, _, body)
| ItemKind::Fn(_, _, body),
..
})
| OwnerNode::TraitItem(TraitItem {
@ -3770,9 +3773,9 @@ impl<'hir> Node<'hir> {
pub fn ty(self) -> Option<&'hir Ty<'hir>> {
match self {
Node::Item(it) => match it.kind {
ItemKind::TyAlias(ty, _) | ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => {
Some(ty)
}
ItemKind::TyAlias(ty, _)
| ItemKind::Static(ty, _, _)
| ItemKind::Const(ty, _, _) => Some(ty),
_ => None,
},
Node::TraitItem(it) => match it.kind {
@ -3800,7 +3803,9 @@ impl<'hir> Node<'hir> {
match self {
Node::Item(Item {
kind:
ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body),
ItemKind::Static(_, _, body)
| ItemKind::Const(_, _, body)
| ItemKind::Fn(_, _, body),
..
})
| Node::TraitItem(TraitItem {

View File

@ -467,11 +467,17 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
ItemKind::Use(ref path, _) => {
visitor.visit_use(path, item.hir_id());
}
ItemKind::Static(ref typ, _, body) | ItemKind::Const(ref typ, body) => {
ItemKind::Static(ref typ, _, body) => {
visitor.visit_id(item.hir_id());
visitor.visit_ty(typ);
visitor.visit_nested_body(body);
}
ItemKind::Const(ref typ, ref generics, body) => {
visitor.visit_id(item.hir_id());
visitor.visit_ty(typ);
visitor.visit_generics(generics);
visitor.visit_nested_body(body);
}
ItemKind::Fn(ref sig, ref generics, body_id) => {
visitor.visit_id(item.hir_id());
visitor.visit_fn(

View File

@ -76,7 +76,7 @@ fn check_method_is_structurally_compatible<'tcx>(
Ok(())
}
/// This function is best explained by example. Consider a trait with it's implementation:
/// This function is best explained by example. Consider a trait with its implementation:
///
/// ```rust
/// trait Trait<'t, T> {
@ -120,7 +120,7 @@ fn check_method_is_structurally_compatible<'tcx>(
/// types:
///
/// ```rust,ignore (pseudo-Rust)
/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
/// <'b> fn(t: &'i0 U0, m: &'b N0) -> Foo
/// ```
///
/// We now want to extract and substitute the type of the *trait*
@ -137,7 +137,7 @@ fn check_method_is_structurally_compatible<'tcx>(
/// Applying this to the trait method type yields:
///
/// ```rust,ignore (pseudo-Rust)
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
/// <'a> fn(t: &'i0 U0, m: &'a N0) -> Foo
/// ```
///
/// This type is also the same but the name of the bound region (`'a`
@ -258,8 +258,6 @@ fn compare_method_predicate_entailment<'tcx>(
// type.
// Compute placeholder form of impl and trait method tys.
let tcx = infcx.tcx;
let mut wf_tys = FxIndexSet::default();
let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars(
@ -1668,19 +1666,19 @@ fn compare_synthetic_generics<'tcx>(
/// ```rust,ignore (pseudo-Rust)
/// trait Foo {
/// fn foo<const N: u8>();
/// type bar<const N: u8>;
/// type Bar<const N: u8>;
/// fn baz<const N: u32>();
/// type blah<T>;
/// type Blah<T>;
/// }
///
/// impl Foo for () {
/// fn foo<const N: u64>() {}
/// //~^ error
/// type bar<const N: u64> {}
/// type Bar<const N: u64> = ();
/// //~^ error
/// fn baz<T>() {}
/// //~^ error
/// type blah<const N: i64> = u32;
/// type Blah<const N: i64> = u32;
/// //~^ error
/// }
/// ```
@ -1769,36 +1767,82 @@ pub(super) fn compare_impl_const_raw(
let trait_const_item = tcx.associated_item(trait_const_item_def);
let impl_trait_ref =
tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref);
let infcx = tcx.infer_ctxt().build();
let param_env = tcx.param_env(impl_const_item_def.to_def_id());
let ocx = ObligationCtxt::new(&infcx);
compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref)
}
/// The equivalent of [compare_method_predicate_entailment], but for associated constants
/// instead of associated functions.
// FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`.
fn compare_const_predicate_entailment<'tcx>(
tcx: TyCtxt<'tcx>,
impl_ct: ty::AssocItem,
trait_ct: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let impl_ct_def_id = impl_ct.def_id.expect_local();
let impl_ct_span = tcx.def_span(impl_ct_def_id);
// The below is for the most part highly similar to the procedure
// for methods above. It is simpler in many respects, especially
// because we shouldn't really have to deal with lifetimes or
// predicates. In fact some of this should probably be put into
// shared functions because of DRY violations...
let trait_to_impl_args = impl_trait_ref.args;
let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id);
let trait_to_impl_args =
impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args);
// Create a parameter environment that represents the implementation's
// method.
// Compute placeholder form of impl and trait const tys.
let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()).instantiate_identity();
let trait_ty = tcx.type_of(trait_const_item_def).instantiate(tcx, trait_to_impl_args);
let mut cause = ObligationCause::new(
impl_c_span,
impl_const_item_def,
ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_const_item_def,
trait_item_def_id: trait_const_item_def,
kind: impl_const_item.kind,
},
let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity();
let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args);
let code = ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_ct_def_id,
trait_item_def_id: trait_ct.def_id,
kind: impl_ct.kind,
};
let mut cause = ObligationCause::new(impl_ct_span, impl_ct_def_id, code.clone());
let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id);
let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id);
check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?;
// The predicates declared by the impl definition, the trait and the
// associated const in the trait are assumed.
let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
hybrid_preds.predicates.extend(
trait_ct_predicates
.instantiate_own(tcx, trait_to_impl_args)
.map(|(predicate, _)| predicate),
);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(
tcx,
param_env,
ObligationCause::misc(impl_ct_span, impl_ct_def_id),
);
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args);
for (predicate, span) in impl_ct_own_bounds {
let cause = ObligationCause::misc(span, impl_ct_def_id);
let predicate = ocx.normalize(&cause, param_env, predicate);
let cause = ObligationCause::new(span, impl_ct_def_id, code.clone());
ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
}
// There is no "body" here, so just pass dummy id.
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
@ -1817,7 +1861,7 @@ pub(super) fn compare_impl_const_raw(
);
// Locate the Span containing just the type of the offending impl
let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const();
let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
cause.span = ty.span;
let mut diag = struct_span_err!(
@ -1825,12 +1869,12 @@ pub(super) fn compare_impl_const_raw(
cause.span,
E0326,
"implemented const `{}` has an incompatible type for trait",
trait_const_item.name
trait_ct.name
);
let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| {
// Add a label to the Span containing just the type of the const
let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const();
let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const();
ty.span
});
@ -1857,7 +1901,7 @@ pub(super) fn compare_impl_const_raw(
}
let outlives_env = OutlivesEnvironment::new(param_env);
ocx.resolve_regions_and_report_errors(impl_const_item_def, &outlives_env)
ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
}
pub(super) fn compare_impl_ty<'tcx>(
@ -1899,7 +1943,7 @@ fn compare_type_predicate_entailment<'tcx>(
return Ok(());
}
// This `HirId` should be used for the `body_id` field on each
// This `DefId` should be used for the `body_id` field on each
// `ObligationCause` (and the `FnCtxt`). This is what
// `regionck_item` expects.
let impl_ty_def_id = impl_ty.def_id.expect_local();
@ -1918,7 +1962,7 @@ fn compare_type_predicate_entailment<'tcx>(
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
let impl_ty_span = tcx.def_span(impl_ty_def_id);
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
let infcx = tcx.infer_ctxt().build();
@ -1963,7 +2007,7 @@ fn compare_type_predicate_entailment<'tcx>(
///
/// trait X { type Y: Copy } impl X for T { type Y = S; }
///
/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
/// We are able to normalize `<T as X>::Y` to `S`, and so when we check the
/// impl is well-formed we have to prove `S: Copy`.
///
/// For default associated types the normalization is not possible (the value

View File

@ -209,6 +209,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
| ItemKind::Struct(..)
| ItemKind::OpaqueTy(..)
| ItemKind::Union(..) => (None, Defaults::Allowed),
ItemKind::Const(..) => (None, Defaults::Deny),
_ => (None, Defaults::FutureCompatDisallowed),
}
}

View File

@ -156,6 +156,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
ItemKind::Fn(.., generics, _)
| ItemKind::TyAlias(_, generics)
| ItemKind::Const(_, generics, _)
| ItemKind::Enum(_, generics)
| ItemKind::Struct(_, generics)
| ItemKind::Union(_, generics) => generics,
@ -762,6 +763,7 @@ pub(super) fn type_param_predicates(
ItemKind::Fn(.., generics, _)
| ItemKind::Impl(&hir::Impl { generics, .. })
| ItemKind::TyAlias(_, generics)
| ItemKind::Const(_, generics, _)
| ItemKind::OpaqueTy(&OpaqueTy {
generics,
origin: hir::OpaqueTyOrigin::TyAlias { .. },

View File

@ -518,7 +518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
| hir::ItemKind::Mod(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::Static(..)
| hir::ItemKind::Const(..)
| hir::ItemKind::GlobalAsm(..) => {
// These sorts of items have no lifetime parameters at all.
intravisit::walk_item(self, item);
@ -583,6 +582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
})
}
hir::ItemKind::TyAlias(_, generics)
| hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::Enum(_, generics)
| hir::ItemKind::Struct(_, generics)
| hir::ItemKind::Union(_, generics)
@ -590,21 +590,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
| hir::ItemKind::TraitAlias(generics, ..)
| hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
// These kinds of items have only early-bound lifetime parameters.
let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
self.record_late_bound_vars(item.hir_id(), vec![]);
let scope = Scope::Binder {
hir_id: item.hir_id(),
bound_vars,
scope_type: BinderScopeType::Normal,
s: self.scope,
where_bound_origin: None,
};
self.with(scope, |this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, |this| {
intravisit::walk_item(this, item);
});
});
self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item));
}
}
}
@ -777,39 +763,24 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
use self::hir::TraitItemKind::*;
match trait_item.kind {
Fn(_, _) => {
self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| {
self.visit_early_late(trait_item.hir_id(), trait_item.generics, |this| {
intravisit::walk_trait_item(this, trait_item)
});
}
Type(bounds, ty) => {
let generics = &trait_item.generics;
let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
self.record_late_bound_vars(trait_item.hir_id(), vec![]);
let scope = Scope::Binder {
hir_id: trait_item.hir_id(),
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, |this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, |this| {
this.visit_generics(generics);
for bound in bounds {
this.visit_param_bound(bound);
}
if let Some(ty) = ty {
this.visit_ty(ty);
}
})
});
}
Const(_, _) => {
// Only methods and types support generics.
assert!(trait_item.generics.params.is_empty());
intravisit::walk_trait_item(self, trait_item);
self.visit_early(trait_item.hir_id(), trait_item.generics, |this| {
this.visit_generics(&trait_item.generics);
for bound in bounds {
this.visit_param_bound(bound);
}
if let Some(ty) = ty {
this.visit_ty(ty);
}
})
}
Const(_, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| {
intravisit::walk_trait_item(this, trait_item)
}),
}
}
@ -817,34 +788,16 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
use self::hir::ImplItemKind::*;
match impl_item.kind {
Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
Fn(..) => self.visit_early_late(impl_item.hir_id(), impl_item.generics, |this| {
intravisit::walk_impl_item(this, impl_item)
}),
Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
this.visit_generics(impl_item.generics);
this.visit_ty(ty);
}),
Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
intravisit::walk_impl_item(this, impl_item)
}),
Type(ty) => {
let generics = &impl_item.generics;
let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> =
generics.params.iter().map(ResolvedArg::early).collect();
self.record_late_bound_vars(impl_item.hir_id(), vec![]);
let scope = Scope::Binder {
hir_id: impl_item.hir_id(),
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, |this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, |this| {
this.visit_generics(generics);
this.visit_ty(ty);
})
});
}
Const(_, _) => {
// Only methods and types support generics.
assert!(impl_item.generics.params.is_empty());
intravisit::walk_impl_item(self, impl_item);
}
}
}
@ -1180,6 +1133,25 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
self.with(scope, walk);
}
fn visit_early<F>(&mut self, hir_id: hir::HirId, generics: &'tcx hir::Generics<'tcx>, walk: F)
where
F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>),
{
let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
self.record_late_bound_vars(hir_id, vec![]);
let scope = Scope::Binder {
hir_id,
bound_vars,
s: self.scope,
scope_type: BinderScopeType::Normal,
where_bound_origin: None,
};
self.with(scope, |this| {
let scope = Scope::TraitRefBoundary { s: this.scope };
this.with(scope, walk)
});
}
#[instrument(level = "debug", skip(self))]
fn resolve_lifetime_ref(
&mut self,

View File

@ -404,7 +404,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
icx.to_ty(ty)
}
}
ItemKind::Const(ty, body_id) => {
ItemKind::Const(ty, _, body_id) => {
if is_suggestable_infer_ty(ty) {
infer_placeholder_type(
tcx, def_id, body_id, ty.span, item.ident, "constant",

View File

@ -130,7 +130,7 @@ fn diagnostic_hir_wf_check<'tcx>(
hir::Node::Item(item) => match item.kind {
hir::ItemKind::TyAlias(ty, _)
| hir::ItemKind::Static(ty, _, _)
| hir::ItemKind::Const(ty, _) => vec![ty],
| hir::ItemKind::Const(ty, _, _) => vec![ty],
hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
Some(t) => t
.path

View File

@ -420,12 +420,13 @@ impl<'a> State<'a> {
fn print_associated_const(
&mut self,
ident: Ident,
generics: &hir::Generics<'_>,
ty: &hir::Ty<'_>,
default: Option<hir::BodyId>,
) {
self.head("");
self.word_space("const");
self.print_ident(ident);
self.print_generic_params(generics.params);
self.word_space(":");
self.print_type(ty);
if let Some(expr) = default {
@ -433,6 +434,7 @@ impl<'a> State<'a> {
self.word_space("=");
self.ann.nested(self, Nested::Body(expr));
}
self.print_where_clause(generics);
self.word(";")
}
@ -532,9 +534,10 @@ impl<'a> State<'a> {
self.word(";");
self.end(); // end the outer cbox
}
hir::ItemKind::Const(ty, expr) => {
hir::ItemKind::Const(ty, generics, expr) => {
self.head("const");
self.print_ident(item.ident);
self.print_generic_params(generics.params);
self.word_space(":");
self.print_type(ty);
self.space();
@ -542,6 +545,7 @@ impl<'a> State<'a> {
self.word_space("=");
self.ann.nested(self, Nested::Body(expr));
self.print_where_clause(generics);
self.word(";");
self.end(); // end the outer cbox
}
@ -836,7 +840,7 @@ impl<'a> State<'a> {
self.print_outer_attributes(self.attrs(ti.hir_id()));
match ti.kind {
hir::TraitItemKind::Const(ty, default) => {
self.print_associated_const(ti.ident, ty, default);
self.print_associated_const(ti.ident, ti.generics, ty, default);
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => {
self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None);
@ -865,7 +869,7 @@ impl<'a> State<'a> {
match ii.kind {
hir::ImplItemKind::Const(ty, expr) => {
self.print_associated_const(ii.ident, ty, Some(expr));
self.print_associated_const(ii.ident, ii.generics, ty, Some(expr));
}
hir::ImplItemKind::Fn(ref sig, body) => {
self.head("");

View File

@ -1394,7 +1394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some((
_,
hir::Node::Local(hir::Local { ty: Some(ty), .. })
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. }),
| hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }),
)) = parent_node
else {
return;

View File

@ -101,7 +101,7 @@ fn primary_body_of(
) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
match node {
Node::Item(item) => match item.kind {
hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
hir::ItemKind::Const(ty, _, body) | hir::ItemKind::Static(ty, _, body) => {
Some((body, Some(ty), None))
}
hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),

View File

@ -924,7 +924,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match opt_def_id {
Some(def_id) => match self.tcx.hir().get_if_local(def_id) {
Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Const(_, body_id), ..
kind: hir::ItemKind::Const(_, _, body_id),
..
})) => match self.tcx.hir().get(body_id.hir_id) {
hir::Node::Expr(expr) => {
if hir::is_range_literal(expr) {

View File

@ -2068,7 +2068,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
visitor.visit_body(body);
visitor.result.map(|r| &r.peel_refs().kind)
}
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => {
Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => {
Some(&ty.peel_refs().kind)
}
_ => None,

View File

@ -1529,9 +1529,10 @@ declare_lint_pass!(
impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
hir::ItemKind::Const(_, body_id) => {
hir::ItemKind::Const(_, _, body_id) => {
let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
// trigger the query once for all constants since that will already report the errors
// FIXME(generic_const_items): Does this work properly with generic const items?
cx.tcx.ensure().const_eval_poly(def_id);
}
hir::ItemKind::Static(_, _, body_id) => {

View File

@ -24,7 +24,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> {
match node {
Node::Item(Item {
owner_id,
kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
kind: ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
..
})
| Node::TraitItem(TraitItem {

View File

@ -570,7 +570,7 @@ fn construct_const<'a, 'tcx>(
// Figure out what primary body this item has.
let (span, const_ty_span) = match tcx.hir().get(hir_id) {
Node::Item(hir::Item {
kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _),
kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _, _),
span,
..
})

View File

@ -690,6 +690,8 @@ parse_single_colon_import_path = expected `::`, found `:`
parse_single_colon_struct_type = found single colon in a struct field type path
.suggestion = write a path separator here
parse_static_with_generics = static items may not have generic parameters
parse_struct_literal_body_without_path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block
@ -847,6 +849,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a
.label = the visibility
.help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
parse_where_clause_before_const_body = where clauses are not allowed before const item bodies
.label = unexpected where clause
.name_label = while parsing this const item
.body_label = the item body
.suggestion = move the body before the where clause
parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
.label = unexpected where clause
.name_label = while parsing this tuple struct

View File

@ -2692,3 +2692,34 @@ pub(crate) struct ExpectedBuiltinIdent {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_static_with_generics)]
pub(crate) struct StaticWithGenerics {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_where_clause_before_const_body)]
pub(crate) struct WhereClauseBeforeConstBody {
#[primary_span]
#[label]
pub span: Span,
#[label(parse_name_label)]
pub name: Span,
#[label(parse_body_label)]
pub body: Span,
#[subdiagnostic]
pub sugg: Option<WhereClauseBeforeConstBodySugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
pub(crate) struct WhereClauseBeforeConstBodySugg {
#[suggestion_part(code = "= {snippet} ")]
pub left: Span,
pub snippet: String,
#[suggestion_part(code = "")]
pub right: Span,
}

View File

@ -226,9 +226,9 @@ impl<'a> Parser<'a> {
} else if self.is_static_global() {
// STATIC ITEM
self.bump(); // `static`
let m = self.parse_mutability();
let (ident, ty, expr) = self.parse_item_global(Some(m))?;
(ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr })))
let mutability = self.parse_mutability();
let (ident, item) = self.parse_static_item(mutability)?;
(ident, ItemKind::Static(Box::new(item)))
} else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
// CONST ITEM
if self.token.is_keyword(kw::Impl) {
@ -236,8 +236,16 @@ impl<'a> Parser<'a> {
self.recover_const_impl(const_span, attrs, def_())?
} else {
self.recover_const_mut(const_span);
let (ident, ty, expr) = self.parse_item_global(None)?;
(ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr })))
let (ident, generics, ty, expr) = self.parse_const_item()?;
(
ident,
ItemKind::Const(Box::new(ConstItem {
defaultness: def_(),
generics,
ty,
expr,
})),
)
}
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
// TRAIT ITEM
@ -878,6 +886,7 @@ impl<'a> Parser<'a> {
self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
AssocItemKind::Const(Box::new(ConstItem {
defaultness: Defaultness::Final,
generics: Generics::default(),
ty,
expr,
}))
@ -892,7 +901,7 @@ impl<'a> Parser<'a> {
/// Parses a `type` alias with the following grammar:
/// ```ebnf
/// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ;
/// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
/// ```
/// The `"type"` has already been eaten.
fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> {
Ok(impl_info)
}
/// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with
/// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
/// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`.
///
/// When `m` is `"const"`, `$ident` may also be `"_"`.
fn parse_item_global(
&mut self,
m: Option<Mutability>,
) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> {
let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
/// ```ebnf
/// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
/// ```
fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> {
let ident = self.parse_ident()?;
// Parse the type of a `const` or `static mut?` item.
// That is, the `":" $ty` fragment.
if self.token.kind == TokenKind::Lt && self.may_recover() {
let generics = self.parse_generics()?;
self.sess.emit_err(errors::StaticWithGenerics { span: generics.span });
}
// Parse the type of a static item. That is, the `":" $ty` fragment.
// FIXME: This could maybe benefit from `.may_recover()`?
let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
{
// If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type.
(true, false) => self.parse_ty()?,
(colon, _) => self.recover_missing_const_type(colon, m),
// If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type.
(colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
};
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
self.expect_semi()?;
Ok((id, ty, expr))
Ok((ident, StaticItem { ty, mutability, expr }))
}
/// Parse a constant item with the prefix `"const"` already parsed.
///
/// ```ebnf
/// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ;
/// ```
fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> {
let ident = self.parse_ident_or_underscore()?;
let mut generics = self.parse_generics()?;
// Check the span for emptiness instead of the list of parameters in order to correctly
// recognize and subsequently flag empty parameter lists (`<>`) as unstable.
if !generics.span.is_empty() {
self.sess.gated_spans.gate(sym::generic_const_items, generics.span);
}
// Parse the type of a constant item. That is, the `":" $ty` fragment.
// FIXME: This could maybe benefit from `.may_recover()`?
let ty = match (
self.eat(&token::Colon),
self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where),
) {
(true, false) => self.parse_ty()?,
// If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type.
(colon, _) => self.recover_missing_global_item_type(colon, None),
};
// Proactively parse a where-clause to be able to provide a good error message in case we
// encounter the item body following it.
let before_where_clause =
if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
let after_where_clause = self.parse_where_clause()?;
// Provide a nice error message if the user placed a where-clause before the item body.
// Users may be tempted to write such code if they are still used to the deprecated
// where-clause location on type aliases and associated types. See also #89122.
if before_where_clause.has_where_token && let Some(expr) = &expr {
self.sess.emit_err(errors::WhereClauseBeforeConstBody {
span: before_where_clause.span,
name: ident.span,
body: expr.span,
sugg: if !after_where_clause.has_where_token {
self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| {
errors::WhereClauseBeforeConstBodySugg {
left: before_where_clause.span.shrink_to_lo(),
snippet: body,
right: before_where_clause.span.shrink_to_hi().to(expr.span),
}
})
} else {
// FIXME(generic_const_items): Provide a structured suggestion to merge the first
// where-clause into the second one.
None
},
});
}
// Merge the predicates of both where-clauses since either one can be relevant.
// If we didn't parse a body (which is valid for associated consts in traits) and we were
// allowed to recover, `before_where_clause` contains the predicates, otherwise they are
// in `after_where_clause`. Further, both of them might contain predicates iff two
// where-clauses were provided which is syntactically ill-formed but we want to recover from
// it and treat them as one large where-clause.
let mut predicates = before_where_clause.predicates;
predicates.extend(after_where_clause.predicates);
let where_clause = WhereClause {
has_where_token: before_where_clause.has_where_token
|| after_where_clause.has_where_token,
predicates,
span: if after_where_clause.has_where_token {
after_where_clause.span
} else {
before_where_clause.span
},
};
if where_clause.has_where_token {
self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span);
}
generics.where_clause = where_clause;
self.expect_semi()?;
Ok((ident, generics, ty, expr))
}
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.
/// This means that the type is missing.
fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> {
fn recover_missing_global_item_type(
&mut self,
colon_present: bool,
m: Option<Mutability>,
) -> P<Ty> {
// Construct the error and stash it away with the hope
// that typeck will later enrich the error with a type.
let kind = match m {

View File

@ -236,7 +236,7 @@ impl<'tcx> ReachableContext<'tcx> {
// Reachable constants will be inlined into other crates
// unconditionally, so we need to make sure that their
// contents are also reachable.
hir::ItemKind::Const(_, init) | hir::ItemKind::Static(_, _, init) => {
hir::ItemKind::Const(_, _, init) | hir::ItemKind::Static(_, _, init) => {
self.visit_nested_body(init);
}

View File

@ -337,6 +337,7 @@ enum LifetimeBinderKind {
PolyTrait,
WhereBound,
Item,
ConstItem,
Function,
Closure,
ImplBlock,
@ -349,7 +350,7 @@ impl LifetimeBinderKind {
BareFnType => "type",
PolyTrait => "bound",
WhereBound => "bound",
Item => "item",
Item | ConstItem => "item",
ImplBlock => "impl block",
Function => "function",
Closure => "closure",
@ -2404,30 +2405,44 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
});
}
ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. })
| ItemKind::Const(box ast::ConstItem { ref ty, ref expr, .. }) => {
ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => {
self.with_static_rib(|this| {
this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
this.visit_ty(ty);
});
this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
if let Some(expr) = expr {
// We already forbid generic params because of the above item rib,
// so it doesn't matter whether this is a trivial constant.
this.resolve_const_body(expr, Some((item.ident, ConstantItemKind::Static)));
}
});
}
ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => {
self.with_generic_param_rib(
&generics.params,
RibKind::Item(HasGenericParams::Yes(generics.span)),
LifetimeRibKind::Generics {
binder: item.id,
kind: LifetimeBinderKind::ConstItem,
span: generics.span,
},
|this| {
this.visit_generics(generics);
this.with_lifetime_rib(
LifetimeRibKind::Elided(LifetimeRes::Static),
|this| this.visit_ty(ty),
);
if let Some(expr) = expr {
let constant_item_kind = match item.kind {
ItemKind::Const(..) => ConstantItemKind::Const,
ItemKind::Static(..) => ConstantItemKind::Static,
_ => unreachable!(),
};
// We already forbid generic params because of the above item rib,
// so it doesn't matter whether this is a trivial constant.
this.with_constant_rib(
IsRepeatExpr::No,
ConstantHasGenerics::Yes,
Some((item.ident, constant_item_kind)),
|this| this.visit_expr(expr),
this.resolve_const_body(
expr,
Some((item.ident, ConstantItemKind::Const)),
);
}
});
});
},
);
}
ItemKind::Use(ref use_tree) => {
@ -2700,28 +2715,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
for item in trait_items {
self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
match &item.kind {
AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
self.visit_ty(ty);
// Only impose the restrictions of `ConstRibKind` for an
// actual constant expression in a provided default.
if let Some(expr) = expr {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
self.with_lifetime_rib(
LifetimeRibKind::Elided(LifetimeRes::Infer),
|this| {
this.with_constant_rib(
IsRepeatExpr::No,
ConstantHasGenerics::Yes,
None,
|this| this.visit_expr(expr),
)
},
);
}
AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
self.with_generic_param_rib(
&generics.params,
RibKind::AssocItem,
LifetimeRibKind::Generics {
binder: item.id,
span: generics.span,
kind: LifetimeBinderKind::ConstItem,
},
|this| {
this.visit_generics(generics);
this.visit_ty(ty);
// Only impose the restrictions of `ConstRibKind` for an
// actual constant expression in a provided default.
if let Some(expr) = expr {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
this.resolve_const_body(expr, None);
}
},
);
}
AssocItemKind::Fn(box Fn { generics, .. }) => {
walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
@ -2876,36 +2894,42 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
use crate::ResolutionError::*;
self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
match &item.kind {
AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
debug!("resolve_implementation AssocItemKind::Const");
// If this is a trait impl, ensure the const
// exists in trait
self.check_trait_item(
item.id,
item.ident,
&item.kind,
ValueNS,
item.span,
seen_trait_items,
|i, s, c| ConstNotMemberOfTrait(i, s, c),
);
self.visit_ty(ty);
if let Some(expr) = expr {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
this.with_constant_rib(
IsRepeatExpr::No,
ConstantHasGenerics::Yes,
None,
|this| this.visit_expr(expr),
)
});
}
self.with_generic_param_rib(
&generics.params,
RibKind::AssocItem,
LifetimeRibKind::Generics {
binder: item.id,
span: generics.span,
kind: LifetimeBinderKind::ConstItem,
},
|this| {
// If this is a trait impl, ensure the const
// exists in trait
this.check_trait_item(
item.id,
item.ident,
&item.kind,
ValueNS,
item.span,
seen_trait_items,
|i, s, c| ConstNotMemberOfTrait(i, s, c),
);
this.visit_generics(generics);
this.visit_ty(ty);
if let Some(expr) = expr {
// We allow arbitrary const expressions inside of associated consts,
// even if they are potentially not const evaluatable.
//
// Type parameters can already be used and as associated consts are
// not used as part of the type system, this is far less surprising.
this.resolve_const_body(expr, None);
}
},
);
}
AssocItemKind::Fn(box Fn { generics, .. }) => {
debug!("resolve_implementation AssocItemKind::Fn");
@ -3063,6 +3087,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
);
}
fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) {
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| {
this.visit_expr(expr)
});
})
}
fn resolve_params(&mut self, params: &'ast [Param]) {
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
@ -4448,6 +4480,7 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> {
fn visit_item(&mut self, item: &'ast Item) {
match &item.kind {
ItemKind::TyAlias(box TyAlias { ref generics, .. })
| ItemKind::Const(box ConstItem { ref generics, .. })
| ItemKind::Fn(box Fn { ref generics, .. })
| ItemKind::Enum(_, ref generics)
| ItemKind::Struct(_, ref generics)
@ -4467,7 +4500,6 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> {
ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Use(..)
| ItemKind::ExternCrate(..)
| ItemKind::MacroDef(..)

View File

@ -2348,6 +2348,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
let mut should_continue = true;
match rib.kind {
LifetimeRibKind::Generics { binder: _, span, kind } => {
// Avoid suggesting placing lifetime parameters on constant items unless the relevant
// feature is enabled. Suggest the parent item as a possible location if applicable.
if let LifetimeBinderKind::ConstItem = kind
&& !self.r.tcx().features().generic_const_items
{
continue;
}
if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name {
suggest_note = false; // Avoid displaying the same help multiple times.
err.span_label(

View File

@ -783,6 +783,7 @@ symbols! {
generic_associated_types,
generic_associated_types_extended,
generic_const_exprs,
generic_const_items,
generic_param_attrs,
get_context,
global_allocator,

View File

@ -655,6 +655,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| hir::ItemKind::Impl(hir::Impl { generics, .. })
| hir::ItemKind::Fn(_, generics, _)
| hir::ItemKind::TyAlias(_, generics)
| hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
..
@ -720,6 +721,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| hir::ItemKind::Impl(hir::Impl { generics, .. })
| hir::ItemKind::Fn(_, generics, _)
| hir::ItemKind::TyAlias(_, generics)
| hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::TraitAlias(generics, _)
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
..

View File

@ -644,6 +644,10 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
}
fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
let mut generics =
clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
clean::simplify::move_bounds_to_generic_parameters(&mut generics);
clean::Constant {
type_: clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
@ -651,6 +655,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
Some(def_id),
None,
),
generics: Box::new(generics),
kind: clean::ConstantKind::Extern { def_id },
}
}

View File

@ -273,6 +273,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t
Some(def_id),
None,
),
generics: Box::new(Generics::default()),
kind: ConstantKind::Anonymous { body: constant.value.body },
}
}
@ -284,6 +285,7 @@ pub(crate) fn clean_middle_const<'tcx>(
// FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant {
type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None),
generics: Box::new(Generics::default()),
kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() },
}
}
@ -1188,11 +1190,18 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
let local_did = trait_item.owner_id.to_def_id();
cx.with_param_env(local_did, |cx| {
let inner = match trait_item.kind {
hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem(
clean_ty(ty, cx),
ConstantKind::Local { def_id: local_did, body: default },
),
hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)),
hir::TraitItemKind::Const(ty, Some(default)) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
AssocConstItem(
Box::new(generics),
clean_ty(ty, cx),
ConstantKind::Local { def_id: local_did, body: default },
)
}
hir::TraitItemKind::Const(ty, None) => {
let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
TyAssocConstItem(Box::new(generics), clean_ty(ty, cx))
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
MethodItem(m, None)
@ -1237,8 +1246,9 @@ pub(crate) fn clean_impl_item<'tcx>(
cx.with_param_env(local_did, |cx| {
let inner = match impl_.kind {
hir::ImplItemKind::Const(ty, expr) => {
let generics = clean_generics(impl_.generics, cx);
let default = ConstantKind::Local { def_id: local_did, body: expr };
AssocConstItem(clean_ty(ty, cx), default)
AssocConstItem(Box::new(generics), clean_ty(ty, cx), default)
}
hir::ImplItemKind::Fn(ref sig, body) => {
let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
@ -1279,14 +1289,21 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
);
let mut generics = Box::new(clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
));
simplify::move_bounds_to_generic_parameters(&mut generics);
let provided = match assoc_item.container {
ty::ImplContainer => true,
ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(),
};
if provided {
AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id })
AssocConstItem(generics, ty, ConstantKind::Extern { def_id: assoc_item.def_id })
} else {
TyAssocConstItem(ty)
TyAssocConstItem(generics, ty)
}
}
ty::AssocKind::Fn => {
@ -1379,34 +1396,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
tcx.generics_of(assoc_item.def_id),
ty::GenericPredicates { parent: None, predicates },
);
// Move bounds that are (likely) directly attached to the parameters of the
// (generic) associated type from the where clause to the respective parameter.
// There is no guarantee that this is what the user actually wrote but we have
// no way of knowing.
let mut where_predicates = ThinVec::new();
for mut pred in generics.where_predicates {
if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.append(bounds);
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.extend(bounds.drain(..).map(|bound| match bound {
GenericBound::Outlives(lifetime) => lifetime,
_ => unreachable!(),
}));
} else {
where_predicates.push(pred);
}
}
generics.where_predicates = where_predicates;
simplify::move_bounds_to_generic_parameters(&mut generics);
if let ty::TraitContainer = assoc_item.container {
// Move bounds that are (likely) directly attached to the associated type
@ -2603,8 +2593,9 @@ fn clean_maybe_renamed_item<'tcx>(
ItemKind::Static(ty, mutability, body_id) => {
StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) })
}
ItemKind::Const(ty, body_id) => ConstantItem(Constant {
ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant {
type_: clean_ty(ty, cx),
generics: Box::new(clean_generics(generics, cx)),
kind: ConstantKind::Local { body: body_id, def_id },
}),
ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {

View File

@ -138,3 +138,38 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
})
.any(|did| trait_is_same_or_supertrait(cx, did, trait_))
}
/// Move bounds that are (likely) directly attached to generic parameters from the where-clause to
/// the respective parameter.
///
/// There is no guarantee that this is what the user actually wrote but we have no way of knowing.
// FIXME(fmease): It'd make a lot of sense to just incorporate this logic into `clean_ty_generics`
// making every of its users benefit from it.
pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics) {
use clean::types::*;
let mut where_predicates = ThinVec::new();
for mut pred in generics.where_predicates.drain(..) {
if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.append(bounds);
} else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
&& let Some(GenericParamDef {
kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
..
}) = generics.params.iter_mut().find(|param| &param.name == arg)
{
param_bounds.extend(bounds.drain(..).map(|bound| match bound {
GenericBound::Outlives(lifetime) => lifetime,
_ => unreachable!(),
}));
} else {
where_predicates.push(pred);
}
}
generics.where_predicates = where_predicates;
}

View File

@ -824,9 +824,9 @@ pub(crate) enum ItemKind {
ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType),
/// A required associated constant in a trait declaration.
TyAssocConstItem(Type),
TyAssocConstItem(Box<Generics>, Type),
/// An associated constant in a trait impl or a provided one in a trait declaration.
AssocConstItem(Type, ConstantKind),
AssocConstItem(Box<Generics>, Type, ConstantKind),
/// A required associated type in a trait declaration.
///
/// The bounds may be non-empty if there is a `where` clause.
@ -871,8 +871,8 @@ impl ItemKind {
| MacroItem(_)
| ProcMacroItem(_)
| PrimitiveItem(_)
| TyAssocConstItem(_)
| AssocConstItem(_, _)
| TyAssocConstItem(..)
| AssocConstItem(..)
| TyAssocTypeItem(..)
| AssocTypeItem(..)
| StrippedItem(_)
@ -1278,7 +1278,7 @@ impl Lifetime {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) enum WherePredicate {
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
@ -1348,7 +1348,7 @@ impl GenericParamDef {
}
// maybe use a Generic enum and use Vec<Generic>?
#[derive(Clone, Debug, Default)]
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct Generics {
pub(crate) params: ThinVec<GenericParamDef>,
pub(crate) where_predicates: ThinVec<WherePredicate>,
@ -2266,6 +2266,7 @@ pub(crate) struct Static {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Constant {
pub(crate) type_: Type,
pub(crate) generics: Box<Generics>,
pub(crate) kind: ConstantKind,
}
@ -2515,7 +2516,8 @@ mod size_asserts {
static_assert_size!(GenericParamDef, 56);
static_assert_size!(Generics, 16);
static_assert_size!(Item, 56);
static_assert_size!(ItemKind, 64);
// FIXME(generic_const_items): Further reduce the size.
static_assert_size!(ItemKind, 72);
static_assert_size!(PathSegment, 40);
static_assert_size!(Type, 32);
// tidy-alphabetical-end

View File

@ -748,20 +748,22 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
fn assoc_const(
w: &mut Buffer,
it: &clean::Item,
generics: &clean::Generics,
ty: &clean::Type,
default: Option<&clean::ConstantKind>,
link: AssocItemLink<'_>,
extra: &str,
indent: usize,
cx: &Context<'_>,
) {
let tcx = cx.tcx();
write!(
w,
"{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}",
extra = extra,
"{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
indent = " ".repeat(indent),
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(),
generics = generics.print(cx),
ty = ty.print(cx),
);
if let Some(default) = default {
@ -774,6 +776,7 @@ fn assoc_const(
// Find a way to print constants here without all that jazz.
write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
}
write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
}
fn assoc_type(
@ -986,19 +989,22 @@ fn render_assoc_item(
clean::MethodItem(m, _) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
}
kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const(
w,
item,
ty,
match kind {
clean::TyAssocConstItem(_) => None,
clean::AssocConstItem(_, default) => Some(default),
_ => unreachable!(),
},
link,
if parent == ItemType::Trait { " " } else { "" },
cx,
),
kind @ (clean::TyAssocConstItem(generics, ty) | clean::AssocConstItem(generics, ty, _)) => {
assoc_const(
w,
item,
generics,
ty,
match kind {
clean::TyAssocConstItem(..) => None,
clean::AssocConstItem(.., default) => Some(default),
_ => unreachable!(),
},
link,
if parent == ItemType::Trait { 4 } else { 0 },
cx,
)
}
clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
w,
item,
@ -1565,7 +1571,8 @@ fn render_impl(
w.write_str("</section>");
}
}
kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
kind @ (clean::TyAssocConstItem(generics, ty)
| clean::AssocConstItem(generics, ty, _)) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
@ -1578,14 +1585,15 @@ fn render_impl(
assoc_const(
w,
item,
generics,
ty,
match kind {
clean::TyAssocConstItem(_) => None,
clean::AssocConstItem(_, default) => Some(default),
clean::TyAssocConstItem(..) => None,
clean::AssocConstItem(.., default) => Some(default),
_ => unreachable!(),
},
link.anchor(if trait_.is_some() { &source_id } else { &id }),
"",
0,
cx,
);
w.write_str("</h4>");

View File

@ -1543,10 +1543,12 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
write!(
w,
"{vis}const {name}: {typ}",
"{vis}const {name}{generics}: {typ}{where_clause}",
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
name = it.name.unwrap(),
generics = c.generics.print(cx),
typ = c.type_.print(cx),
where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline),
);
// FIXME: The code below now prints

View File

@ -219,7 +219,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
match item.kind {
ItemKind::Static(_, _, _)
| ItemKind::Const(_, _)
| ItemKind::Const(_, _, _)
| ItemKind::Fn(_, _, _)
| ItemKind::Macro(_, _)
| ItemKind::TyAlias(_, _)

View File

@ -171,6 +171,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
}
impl FromWithTcx<clean::Constant> for Constant {
// FIXME(generic_const_items): Add support for generic const items.
fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self {
let expr = constant.expr(tcx);
let value = constant.value(tcx);
@ -321,8 +322,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
impls: Vec::new(), // Added in JsonRenderer::item
})
}
TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None },
AssocConstItem(ty, default) => {
// FIXME(generic_const_items): Add support for generic associated consts.
TyAssocConstItem(_generics, ty) => {
ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }
}
// FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(_generics, ty, default) => {
ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
}
TyAssocTypeItem(g, b) => ItemEnum::AssocType {

View File

@ -50,7 +50,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if !item.span.from_expansion();
if let ItemKind::Const(hir_ty, _) = &item.kind;
if let ItemKind::Const(hir_ty, generics, _) = &item.kind;
// Since static items may not have generics, skip generic const items.
// FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
// doesn't account for empty where-clauses that only consist of keyword `where` IINM.
if generics.params.is_empty() && !generics.has_where_clause_predicates;
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if let ty::Array(element_type, cst) = ty.kind();
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();

View File

@ -302,7 +302,7 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER
impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
if let ItemKind::Const(hir_ty, body_id) = it.kind {
if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind {
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
lint(cx, Source::Item { item: it.span });

View File

@ -349,7 +349,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
match item.kind {
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(
cx,
ty,
CheckTyContext {

View File

@ -301,15 +301,17 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
(
Const(box ast::ConstItem {
defaultness: ld,
generics: lg,
ty: lt,
expr: le,
}),
Const(box ast::ConstItem {
defaultness: rd,
generics: rg,
ty: rt,
expr: re,
}),
) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le, re),
(
Fn(box ast::Fn {
defaultness: ld,
@ -476,15 +478,17 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
(
Const(box ast::ConstItem {
defaultness: ld,
generics: lg,
ty: lt,
expr: le,
}),
Const(box ast::ConstItem {
defaultness: rd,
generics: rg,
ty: rt,
expr: re,
}),
) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le, re),
(
Fn(box ast::Fn {
defaultness: ld,

View File

@ -461,7 +461,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
// Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes.
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
&& let Node::Item(Item { kind: ItemKind::Const(_, body_id), .. }) = node
&& let Node::Item(Item { kind: ItemKind::Const(.., body_id), .. }) = node
&& let Node::Expr(Expr { kind: ExprKind::Lit(_), span, .. }) = self.lcx
.tcx
.hir()

View File

@ -2380,7 +2380,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol
for id in tcx.hir().module_items(module) {
if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
&& let item = tcx.hir().item(id)
&& let ItemKind::Const(ty, _body) = item.kind {
&& let ItemKind::Const(ty, _generics, _body) = item.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
if let Res::Def(DefKind::Struct, _) = path.res {

View File

@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1893;
const ROOT_ENTRY_LIMIT: usize = 871;
const ROOT_ENTRY_LIMIT: usize = 872;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files

View File

@ -0,0 +1,38 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
// @has 'generic_const_items/constant.K.html'
// @has - '//*[@class="rust item-decl"]//code' \
// "pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> \
// where \
// String: From<T>;"
pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> = None
where
String: From<T>;
// @has generic_const_items/trait.Trait.html
pub trait Trait<T: ?Sized> {
// @has - '//*[@id="associatedconstant.C"]' \
// "const C<'a>: &'a T \
// where \
// T: 'a + Eq"
const C<'a>: &'a T
where
T: 'a + Eq;
}
pub struct Implementor;
// @has generic_const_items/struct.Implementor.html
// @has - '//h3[@class="code-header"]' 'impl Trait<str> for Implementor'
impl Trait<str> for Implementor {
// @has - '//*[@id="associatedconstant.C"]' \
// "const C<'a>: &'a str = \"C\" \
// where \
// str: 'a"
const C<'a>: &'a str = "C"
// In real code we could've left off this bound but adding it explicitly allows us to test if
// we render where-clauses on associated consts inside impl blocks correctly.
where
str: 'a;
}

View File

@ -0,0 +1,22 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> = None
where
String: From<T>;
pub trait Trait<T: ?Sized> {
const C<'a>: &'a T
where
T: 'a + Eq;
}
pub struct Implementor;
impl Trait<str> for Implementor {
const C<'a>: &'a str = "C"
// In real code we could've left off this bound but adding it explicitly allows us to test if
// we render where-clauses on associated consts inside impl blocks correctly.
where
str: 'a;
}

View File

@ -0,0 +1,26 @@
#![crate_name = "user"]
// aux-crate:generic_const_items=generic-const-items.rs
// edition:2021
// @has 'user/constant.K.html'
// @has - '//*[@class="rust item-decl"]//code' \
// "pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> \
// where \
// String: From<T>;"
pub use generic_const_items::K;
// @has user/trait.Trait.html
// @has - '//*[@id="associatedconstant.C"]' \
// "const C<'a>: &'a T \
// where \
// T: 'a + Eq"
pub use generic_const_items::Trait;
// @has user/struct.Implementor.html
// @has - '//h3[@class="code-header"]' 'impl Trait<str> for Implementor'
// @has - '//*[@id="associatedconstant.C"]' \
// "const C<'a>: &'a str = \"C\" \
// where \
// str: 'a"
pub use generic_const_items::Implementor;

View File

@ -0,0 +1,22 @@
// check-pass
#![feature(generic_const_items, associated_const_equality)]
#![allow(incomplete_features)]
trait Owner {
const C<const N: u32>: u32;
const K<const N: u32>: u32;
}
impl Owner for () {
const C<const N: u32>: u32 = N;
const K<const N: u32>: u32 = N + 1;
}
fn take0<const N: u32>(_: impl Owner<C<N> = { N }>) {}
fn take1(_: impl Owner<K<99> = 100>) {}
fn main() {
take0::<128>(());
take1(());
}

View File

@ -0,0 +1,61 @@
// check-pass
// Basic usage patterns of free & associated generic const items.
#![feature(generic_const_items)]
#![allow(incomplete_features)]
fn main() {
const NULL<T>: Option<T> = None::<T>;
const NOTHING<T>: Option<T> = None; // arg inferred
let _ = NOTHING::<String>;
let _: Option<u8> = NULL; // arg inferred
const IDENTITY<const X: u64>: u64 = X;
const COUNT: u64 = IDENTITY::<48>;
const AMOUNT: u64 = IDENTITY::<COUNT>;
const NUMBER: u64 = IDENTITY::<{ AMOUNT * 2 }>;
let _ = NUMBER;
let _ = IDENTITY::<0>;
let _ = match 0 {
IDENTITY::<1> => 2,
IDENTITY::<{ 1 + 1 }> => 4,
_ => 0,
};
const CREATE<I: Inhabited>: I = I::PROOF;
let _ = CREATE::<u64>;
let _: u64 = CREATE; // arg inferred
let _ = <() as Main<u64>>::MAKE::<u64>;
let _: (u64, u64) = <()>::MAKE; // args inferred
}
pub fn usage<'any>() {
const REGION_POLY<'a>: &'a () = &();
let _: &'any () = REGION_POLY::<'any>;
let _: &'any () = REGION_POLY::<'_>;
let _: &'static () = REGION_POLY;
}
trait Main<O> {
type Output<I>;
const MAKE<I: Inhabited>: Self::Output<I>;
}
impl<O: Inhabited> Main<O> for () {
type Output<I> = (O, I);
const MAKE<I: Inhabited>: Self::Output<I> = (O::PROOF, I::PROOF);
}
trait Inhabited {
const PROOF: Self;
}
impl Inhabited for u64 {
const PROOF: Self = 512;
}

View File

@ -0,0 +1,30 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
trait Trait<P> {
const A: ();
const B<const K: u64, const Q: u64>: u64;
const C<T>: T;
const D<const N: usize>: usize;
const E: usize;
const F<T: PartialEq>: ();
}
impl<P> Trait<P> for () {
const A<T>: () = ();
//~^ ERROR const `A` has 1 type parameter but its trait declaration has 0 type parameters
const B<const K: u64>: u64 = 0;
//~^ ERROR const `B` has 1 const parameter but its trait declaration has 2 const parameters
const C<'a>: &'a str = "";
//~^ ERROR const `C` has 0 type parameters but its trait declaration has 1 type parameter
const D<const N: u16>: u16 = N;
//~^ ERROR const `D` has an incompatible generic parameter for trait `Trait`
const E: usize = 1024
where
P: Copy; //~ ERROR impl has stricter requirements than trait
const F<T: Eq>: () = (); //~ ERROR impl has stricter requirements than trait
}
fn main() {}

View File

@ -0,0 +1,66 @@
error[E0049]: const `A` has 1 type parameter but its trait declaration has 0 type parameters
--> $DIR/compare-impl-item.rs:15:13
|
LL | const A: ();
| - expected 0 type parameters
...
LL | const A<T>: () = ();
| ^ found 1 type parameter
error[E0049]: const `B` has 1 const parameter but its trait declaration has 2 const parameters
--> $DIR/compare-impl-item.rs:17:13
|
LL | const B<const K: u64, const Q: u64>: u64;
| ------------ ------------
| |
| expected 2 const parameters
...
LL | const B<const K: u64>: u64 = 0;
| ^^^^^^^^^^^^ found 1 const parameter
error[E0049]: const `C` has 0 type parameters but its trait declaration has 1 type parameter
--> $DIR/compare-impl-item.rs:19:13
|
LL | const C<T>: T;
| - expected 1 type parameter
...
LL | const C<'a>: &'a str = "";
| ^^ found 0 type parameters
error[E0053]: const `D` has an incompatible generic parameter for trait `Trait`
--> $DIR/compare-impl-item.rs:21:13
|
LL | trait Trait<P> {
| -----
...
LL | const D<const N: usize>: usize;
| -------------- expected const parameter of type `usize`
...
LL | impl<P> Trait<P> for () {
| -----------------------
...
LL | const D<const N: u16>: u16 = N;
| ^^^^^^^^^^^^ found const parameter of type `u16`
error[E0276]: impl has stricter requirements than trait
--> $DIR/compare-impl-item.rs:26:12
|
LL | const E: usize;
| -------------- definition of `E` from trait
...
LL | P: Copy;
| ^^^^ impl has extra requirement `P: Copy`
error[E0276]: impl has stricter requirements than trait
--> $DIR/compare-impl-item.rs:27:16
|
LL | const F<T: PartialEq>: ();
| ------------------------- definition of `F` from trait
...
LL | const F<T: Eq>: () = ();
| ^^ impl has extra requirement `T: Eq`
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0049, E0053, E0276.
For more information about an error, try `rustc --explain E0049`.

View File

@ -0,0 +1,24 @@
// check-pass
// Test that we can call methods from const trait impls inside of generic const items.
#![feature(generic_const_items, const_trait_impl)]
#![allow(incomplete_features)]
#![crate_type = "lib"]
// FIXME(generic_const_items): Interpret `~const` as always-const.
const CREATE<T: ~const Create>: T = T::create();
pub const K0: i32 = CREATE::<i32>;
pub const K1: i32 = CREATE; // arg inferred
#[const_trait]
trait Create {
fn create() -> Self;
}
impl const Create for i32 {
fn create() -> i32 {
4096
}
}

View File

@ -0,0 +1,27 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
trait Tr<P> {
const K: ()
where
P: Copy
where
P: Eq;
//~^ ERROR cannot define duplicate `where` clauses on an item
}
// Test that we error on the first where-clause but also that we don't suggest to swap it with the
// body as it would conflict with the second where-clause.
// FIXME(generic_const_items): We should provide a structured sugg to merge the 1st into the 2nd WC.
impl<P> Tr<P> for () {
const K: ()
where
P: Eq
= ()
where
P: Copy;
//~^^^^^ ERROR where clauses are not allowed before const item bodies
}
fn main() {}

View File

@ -0,0 +1,27 @@
error: cannot define duplicate `where` clauses on an item
--> $DIR/duplicate-where-clause.rs:9:9
|
LL | P: Copy
| - previous `where` clause starts here
LL | where
LL | P: Eq;
| ^
|
help: consider joining the two `where` clauses into one
|
LL | P: Copy,
| ~
error: where clauses are not allowed before const item bodies
--> $DIR/duplicate-where-clause.rs:19:5
|
LL | const K: ()
| - while parsing this const item
LL | / where
LL | | P: Eq
| |_____________^ unexpected where clause
LL | = ()
| -- the item body
error: aborting due to 2 previous errors

View File

@ -0,0 +1,18 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
// Check that we forbid elided lifetimes inside the generics of const items.
const K<T>: () = ()
where
&T: Copy; //~ ERROR `&` without an explicit lifetime name cannot be used here
const I<const S: &str>: &str = "";
//~^ ERROR `&` without an explicit lifetime name cannot be used here
//~| ERROR `&str` is forbidden as the type of a const generic parameter
const B<T: Trait<'_>>: () = (); //~ ERROR `'_` cannot be used here
trait Trait<'a> {}
fn main() {}

View File

@ -0,0 +1,35 @@
error[E0637]: `&` without an explicit lifetime name cannot be used here
--> $DIR/elided-lifetimes.rs:8:5
|
LL | &T: Copy;
| ^ explicit lifetime name needed here
|
help: consider introducing a higher-ranked lifetime here
|
LL | for<'a> &'a T: Copy;
| +++++++ ++
error[E0637]: `&` without an explicit lifetime name cannot be used here
--> $DIR/elided-lifetimes.rs:10:18
|
LL | const I<const S: &str>: &str = "";
| ^ explicit lifetime name needed here
error[E0637]: `'_` cannot be used here
--> $DIR/elided-lifetimes.rs:14:18
|
LL | const B<T: Trait<'_>>: () = ();
| ^^ `'_` is a reserved lifetime name
error: `&str` is forbidden as the type of a const generic parameter
--> $DIR/elided-lifetimes.rs:10:18
|
LL | const I<const S: &str>: &str = "";
| ^^^^
|
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0637`.

View File

@ -0,0 +1,31 @@
// This is a regression test for issue #104400.
// revisions: unconstrained constrained
//[constrained] check-pass
// Test that we can constrain generic const items that appear inside associated consts by
// adding a (makeshift) "evaluatable"-bound to the item.
#![feature(generic_const_items, generic_const_exprs)]
#![allow(incomplete_features)]
trait Trait {
const LEN: usize;
#[cfg(unconstrained)]
const ARRAY: [i32; Self::LEN]; //[unconstrained]~ ERROR unconstrained generic constant
#[cfg(constrained)]
const ARRAY: [i32; Self::LEN]
where
[(); Self::LEN]:;
}
impl Trait for () {
const LEN: usize = 2;
const ARRAY: [i32; Self::LEN] = [360, 720];
}
fn main() {
let [_, _] = <() as Trait>::ARRAY;
}

View File

@ -0,0 +1,10 @@
error: unconstrained generic constant
--> $DIR/evaluatable-bounds.rs:16:5
|
LL | const ARRAY: [i32; Self::LEN];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try adding a `where` bound using this expression: `where [(); Self::LEN]:`
error: aborting due to previous error

View File

@ -0,0 +1,37 @@
pub trait Trait<A> {
const ONE<T>: i32;
//~^ ERROR generic const items are experimental
const TWO: ()
where
A: Copy;
//~^^ ERROR generic const items are experimental
}
const CONST<T>: i32 = 0;
//~^ ERROR generic const items are experimental
const EMPTY<>: i32 = 0;
//~^ ERROR generic const items are experimental
const TRUE: () = ()
where
String: Clone;
//~^^ ERROR generic const items are experimental
// Ensure that we flag generic const items inside macro calls as well:
macro_rules! discard {
($item:item) => {}
}
discard! { const FREE<T>: () = (); }
//~^ ERROR generic const items are experimental
discard! { impl () { const ASSOC<const N: ()>: () = (); } }
//~^ ERROR generic const items are experimental
discard! { impl () { const ASSOC: i32 = 0 where String: Copy; } }
//~^ ERROR generic const items are experimental
fn main() {}

View File

@ -0,0 +1,77 @@
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:2:14
|
LL | const ONE<T>: i32;
| ^^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:6:5
|
LL | / where
LL | | A: Copy;
| |_______________^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:11:12
|
LL | const CONST<T>: i32 = 0;
| ^^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:14:12
|
LL | const EMPTY<>: i32 = 0;
| ^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:18:1
|
LL | / where
LL | | String: Clone;
| |_________________^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:28:22
|
LL | discard! { const FREE<T>: () = (); }
| ^^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:31:33
|
LL | discard! { impl () { const ASSOC<const N: ()>: () = (); } }
| ^^^^^^^^^^^^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error[E0658]: generic const items are experimental
--> $DIR/feature-gate-generic_const_items.rs:34:43
|
LL | discard! { impl () { const ASSOC: i32 = 0 where String: Copy; } }
| ^^^^^^^^^^^^^^^^^^
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
= help: add `#![feature(generic_const_items)]` to the crate attributes to enable
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,15 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
const NONE<T>: Option<T> = None::<T>;
const IGNORE<T>: () = ();
fn none() {
let _ = NONE; //~ ERROR type annotations needed
}
fn ignore() {
let _ = IGNORE; //~ ERROR type annotations needed
}
fn main() {}

View File

@ -0,0 +1,20 @@
error[E0282]: type annotations needed for `Option<T>`
--> $DIR/inference-failure.rs:8:9
|
LL | let _ = NONE;
| ^
|
help: consider giving this pattern a type, where the type for type parameter `T` is specified
|
LL | let _: Option<T> = NONE;
| +++++++++++
error[E0282]: type annotations needed
--> $DIR/inference-failure.rs:12:13
|
LL | let _ = IGNORE;
| ^^^^^^ cannot infer type for type parameter `T` declared on the constant `IGNORE`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0282`.

View File

@ -0,0 +1,18 @@
// run-rustfix
#![feature(generic_const_items)]
#![allow(incomplete_features, dead_code)]
const K<T>: u64
= T::K where
T: Tr<()>;
//~^^^ ERROR where clauses are not allowed before const item bodies
trait Tr<P> {
const K: u64
= 0 where
P: Copy;
//~^^^ ERROR where clauses are not allowed before const item bodies
}
fn main() {}

View File

@ -0,0 +1,20 @@
// run-rustfix
#![feature(generic_const_items)]
#![allow(incomplete_features, dead_code)]
const K<T>: u64
where
T: Tr<()>
= T::K;
//~^^^ ERROR where clauses are not allowed before const item bodies
trait Tr<P> {
const K: u64
where
P: Copy
= 0;
//~^^^ ERROR where clauses are not allowed before const item bodies
}
fn main() {}

View File

@ -0,0 +1,36 @@
error: where clauses are not allowed before const item bodies
--> $DIR/misplaced-where-clause.rs:7:1
|
LL | const K<T>: u64
| - while parsing this const item
LL | / where
LL | | T: Tr<()>
| |_____________^ unexpected where clause
LL | = T::K;
| ---- the item body
|
help: move the body before the where clause
|
LL ~ = T::K where
LL ~ T: Tr<()>;
|
error: where clauses are not allowed before const item bodies
--> $DIR/misplaced-where-clause.rs:14:5
|
LL | const K: u64
| - while parsing this const item
LL | / where
LL | | P: Copy
| |_______________^ unexpected where clause
LL | = 0;
| - the item body
|
help: move the body before the where clause
|
LL ~ = 0 where
LL ~ P: Copy;
|
error: aborting due to 2 previous errors

View File

@ -0,0 +1,14 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
// Check that we emit a *hard* error (not just a lint warning or error for example) for generic
// parameter defaults on free const items since we are not limited by backward compatibility.
#![allow(invalid_type_param_default)] // Should have no effect here.
// FIXME(default_type_parameter_fallback): Consider reallowing them once they work properly.
const NONE<T = ()>: Option<T> = None::<T>; //~ ERROR defaults for type parameters are only allowed
fn main() {
let _ = NONE;
}

View File

@ -0,0 +1,8 @@
error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
--> $DIR/parameter-defaults.rs:10:12
|
LL | const NONE<T = ()>: Option<T> = None::<T>;
| ^^^^^^
error: aborting due to previous error

View File

@ -0,0 +1,12 @@
// FIXME(generic_const_items): This leads to a stack overflow in the compiler!
// known-bug: unknown
// ignore-test
#![feature(generic_const_items)]
#![allow(incomplete_features)]
const RECUR<T>: () = RECUR::<(T,)>;
fn main() {
let _ = RECUR::<()>;
}

View File

@ -0,0 +1,12 @@
#![feature(generic_const_items, trivial_bounds)]
#![allow(incomplete_features)]
// Ensure that we check if trivial bounds on const items hold or not.
const UNUSABLE: () = ()
where
String: Copy;
fn main() {
let _ = UNUSABLE; //~ ERROR the trait bound `String: Copy` is not satisfied
}

View File

@ -0,0 +1,18 @@
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/trivially-unsatisfied-bounds-0.rs:11:13
|
LL | let _ = UNUSABLE;
| ^^^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `UNUSABLE`
--> $DIR/trivially-unsatisfied-bounds-0.rs:8:13
|
LL | const UNUSABLE: () = ()
| -------- required by a bound in this constant
LL | where
LL | String: Copy;
| ^^^^ required by this bound in `UNUSABLE`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,12 @@
#![feature(generic_const_items, trivial_bounds)]
#![allow(incomplete_features, dead_code, trivial_bounds)]
// FIXME(generic_const_items): This looks like a bug to me. I expected that we wouldn't emit any
// errors. I thought we'd skip the evaluation of consts whose bounds don't hold.
const UNUSED: () = ()
where
String: Copy;
//~^^^ ERROR evaluation of constant value failed
fn main() {}

View File

@ -0,0 +1,11 @@
error[E0080]: evaluation of constant value failed
--> $DIR/trivially-unsatisfied-bounds-1.rs:7:1
|
LL | / const UNUSED: () = ()
LL | | where
LL | | String: Copy;
| |_________________^ entering unreachable code
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -0,0 +1,34 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
// Ensure that we check if bounds on const items hold or not.
use std::convert::Infallible;
const C<T: Copy>: () = ();
const K<T>: () = ()
where
Infallible: From<T>;
trait Trait<P> {
const A: u32
where
P: Copy;
const B<T>: u32
where
Infallible: From<T>;
}
impl<P> Trait<P> for () {
const A: u32 = 0;
const B<T>: u32 = 1;
}
fn main() {
let () = C::<String>; //~ ERROR the trait bound `String: Copy` is not satisfied
let () = K::<()>; //~ ERROR the trait bound `Infallible: From<()>` is not satisfied
let _ = <() as Trait<Vec<u8>>>::A; //~ ERROR the trait bound `Vec<u8>: Copy` is not satisfied
let _ = <() as Trait<&'static str>>::B::<()>; //~ ERROR the trait bound `Infallible: From<()>` is not satisfied
}

View File

@ -0,0 +1,62 @@
error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/unsatisfied-bounds.rs:30:18
|
LL | let () = C::<String>;
| ^^^^^^ the trait `Copy` is not implemented for `String`
|
note: required by a bound in `C`
--> $DIR/unsatisfied-bounds.rs:8:12
|
LL | const C<T: Copy>: () = ();
| ^^^^ required by this bound in `C`
error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
--> $DIR/unsatisfied-bounds.rs:31:18
|
LL | let () = K::<()>;
| ^^ the trait `From<()>` is not implemented for `Infallible`
|
= help: the trait `From<!>` is implemented for `Infallible`
note: required by a bound in `K`
--> $DIR/unsatisfied-bounds.rs:12:17
|
LL | const K<T>: () = ()
| - required by a bound in this constant
LL | where
LL | Infallible: From<T>;
| ^^^^^^^ required by this bound in `K`
error[E0277]: the trait bound `Vec<u8>: Copy` is not satisfied
--> $DIR/unsatisfied-bounds.rs:32:13
|
LL | let _ = <() as Trait<Vec<u8>>>::A;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<u8>`
|
note: required by a bound in `Trait::A`
--> $DIR/unsatisfied-bounds.rs:17:12
|
LL | const A: u32
| - required by a bound in this associated constant
LL | where
LL | P: Copy;
| ^^^^ required by this bound in `Trait::A`
error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
--> $DIR/unsatisfied-bounds.rs:33:46
|
LL | let _ = <() as Trait<&'static str>>::B::<()>;
| ^^ the trait `From<()>` is not implemented for `Infallible`
|
= help: the trait `From<!>` is implemented for `Infallible`
note: required by a bound in `Trait::B`
--> $DIR/unsatisfied-bounds.rs:21:21
|
LL | const B<T>: u32
| - required by a bound in this associated constant
LL | where
LL | Infallible: From<T>;
| ^^^^^^^ required by this bound in `Trait::B`
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0277`.

View File

@ -0,0 +1,12 @@
#![feature(generic_const_items, generic_const_exprs)]
#![allow(incomplete_features)]
// Ensure that we check if (makeshift) "evaluatable"-bounds on const items hold or not.
const POSITIVE<const N: usize>: usize = N
where
[(); N - 1]:; //~ ERROR evaluation of `POSITIVE::<0>::{constant#0}` failed
fn main() {
let _ = POSITIVE::<0>;
}

View File

@ -0,0 +1,9 @@
error[E0080]: evaluation of `POSITIVE::<0>::{constant#0}` failed
--> $DIR/unsatisfied-evaluatable-bounds.rs:8:10
|
LL | [(); N - 1]:;
| ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -0,0 +1,17 @@
#![feature(generic_const_items)]
#![allow(incomplete_features)]
// Ensure that we check if outlives-bounds on const items hold or not.
const C<'a, T: 'a>: () = ();
const K<'a, 'b: 'a>: () = ();
fn parametrized0<'any>() {
let () = C::<'static, &'any ()>; //~ ERROR lifetime may not live long enough
}
fn parametrized1<'any>() {
let () = K::<'static, 'any>; //~ ERROR lifetime may not live long enough
}
fn main() {}

View File

@ -0,0 +1,18 @@
error: lifetime may not live long enough
--> $DIR/unsatisfied-outlives-bounds.rs:10:14
|
LL | fn parametrized0<'any>() {
| ---- lifetime `'any` defined here
LL | let () = C::<'static, &'any ()>;
| ^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/unsatisfied-outlives-bounds.rs:14:14
|
LL | fn parametrized1<'any>() {
| ---- lifetime `'any` defined here
LL | let () = K::<'static, 'any>;
| ^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
error: aborting due to 2 previous errors

View File

@ -1,7 +1,12 @@
#![feature(generic_const_items)]
#![allow(incomplete_features, dead_code)]
// check-pass
trait Foo<T> {
const BAR: bool
where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
Self: Sized;
where
Self: Sized;
}
trait Cake {}

View File

@ -1,15 +0,0 @@
error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
--> $DIR/assoc_const_bounds.rs:3:9
|
LL | trait Foo<T> {
| - while parsing this item list starting here
LL | const BAR: bool
| - expected one of 7 possible tokens
LL | where
| ^^^^^ unexpected token
LL | Self: Sized;
LL | }
| - the item list ends here
error: aborting due to previous error

View File

@ -1,7 +1,12 @@
#![feature(generic_const_items)]
#![allow(incomplete_features, dead_code)]
// check-pass
trait Foo {
const BAR: bool
where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
Self: Sized;
where
Self: Sized;
}
fn foo(_: &dyn Foo) {}

View File

@ -1,15 +0,0 @@
error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
--> $DIR/assoc_const_bounds_sized.rs:3:9
|
LL | trait Foo {
| - while parsing this item list starting here
LL | const BAR: bool
| - expected one of 7 possible tokens
LL | where
| ^^^^^ unexpected token
LL | Self: Sized;
LL | }
| - the item list ends here
error: aborting due to previous error

View File

@ -0,0 +1,4 @@
static S<T>: i32 = 0;
//~^ ERROR static items may not have generic parameters
fn main() {}

View File

@ -0,0 +1,8 @@
error: static items may not have generic parameters
--> $DIR/generic-statics.rs:1:9
|
LL | static S<T>: i32 = 0;
| ^^^
error: aborting due to previous error

View File

@ -1,5 +1,5 @@
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:2:14
--> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
|
LL | const A: &str = "";
| ^ expected named lifetime parameter
@ -11,7 +11,7 @@ LL ~ const A: &'a str = "";
|
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:3:14
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
LL | const B: S = S { s: &() };
| ^ expected named lifetime parameter
@ -24,7 +24,7 @@ LL ~ const B: S<'a> = S { s: &() };
|
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:4:15
--> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
|
LL | const C: &'_ str = "";
| ^^ expected named lifetime parameter
@ -38,7 +38,7 @@ LL ~ const C: &'a str = "";
|
error[E0106]: missing lifetime specifiers
--> $DIR/missing-lifetime-in-assoc-const-type.rs:5:14
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
LL | const D: T = T { a: &(), b: &() };
| ^ expected 2 lifetime parameters

View File

@ -0,0 +1,47 @@
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
|
LL | const A: &str = "";
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL | const A<'a>: &'a str = "";
| ++++ ++
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
LL | const B: S = S { s: &() };
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL | const B<'a>: S<'a> = S { s: &() };
| ++++ ++++
error[E0106]: missing lifetime specifier
--> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
|
LL | const C: &'_ str = "";
| ^^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL | const C<'a>: &'a str = "";
| ++++ ~~
error[E0106]: missing lifetime specifiers
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
LL | const D: T = T { a: &(), b: &() };
| ^ expected 2 lifetime parameters
|
help: consider introducing a named lifetime parameter
|
LL | const D<'a>: T<'a, 'a> = T { a: &(), b: &() };
| ++++ ++++++++
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0106`.

View File

@ -1,3 +1,7 @@
// revisions: default generic_const_items
#![cfg_attr(generic_const_items, feature(generic_const_items), allow(incomplete_features))]
trait ZstAssert: Sized {
const A: &str = ""; //~ ERROR missing lifetime specifier
const B: S = S { s: &() }; //~ ERROR missing lifetime specifier