diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a7198fbf88768..aeb91f82fd3e3 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2942,6 +2942,7 @@ pub struct StaticItem { #[derive(Clone, Encodable, Decodable, Debug)] pub struct ConstItem { pub defaultness: Defaultness, + pub generics: Generics, pub ty: P, pub expr: Option>, } @@ -3053,6 +3054,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) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 53a9c9a046ea0..84b56efd3259a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item( } fn visit_const_item( - 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)); } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d9de5b8e197db..83d88718cc223 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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 { 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 { generics, ty, expr, .. }) => { + visitor.visit_generics(generics); visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ab68436c0939d..b45e06b1ea458 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -231,9 +231,14 @@ 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, + 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 }, @@ -723,11 +728,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, + i.id, + &ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| { + let ty = this.lower_ty( + ty, + // FIXME(fmease): Should we create & use `AssocConstTy` here? + &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; @@ -825,14 +842,18 @@ 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, + 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; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 096cea945b029..6567c306d7208 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1286,6 +1286,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { if ctxt == AssocCtxt::Impl { match &item.kind { + // FIXME(fmease): Do we need the analog of `check_gat_where` here? I don't think so. AssocItemKind::Const(box ConstItem { expr: None, .. }) => { self.session.emit_err(errors::AssocConstWithoutBody { span: item.span, @@ -1338,6 +1339,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_item_named(item.ident, "const"); } + // FIXME(fmease): Do we need to visit the generics of consts here, too? match &item.kind { AssocItemKind::Type(box TyAlias { generics, bounds, ty, .. }) if ctxt == AssocCtxt::Trait => diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b0dbc2c23403e..c9359349bd69a 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -556,6 +556,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_consts, "generic consts are experimental"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 5c01b7ea70a13..8e5de317f2c39 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -32,7 +32,7 @@ impl<'a> State<'a> { } ast::ForeignItemKind::Static(ty, mutbl, body) => { let def = ast::Defaultness::Final; - self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); + self.print_item_const(ident, Some(*mutbl), None, ty, body.as_deref(), vis, def); } ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, @@ -67,6 +67,7 @@ impl<'a> State<'a> { &mut self, ident: Ident, mutbl: Option, + generics: Option<&ast::Generics>, ty: &ast::Ty, body: Option<&ast::Expr>, vis: &ast::Visibility, @@ -82,6 +83,9 @@ impl<'a> State<'a> { }; self.word_space(leading); self.print_ident(ident); + if let Some(generics) = generics { + self.print_generic_params(&generics.params); + } self.word_space(":"); self.print_type(ty); if body.is_some() { @@ -92,6 +96,9 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(body); } + if let Some(generics) = generics { + self.print_where_clause(&generics.where_clause); + } self.word(";"); self.end(); // end the outer cbox } @@ -162,16 +169,18 @@ impl<'a> State<'a> { self.print_item_const( item.ident, Some(*mutbl), + None, ty, body.as_deref(), &item.vis, def, ); } - 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, + Some(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, + Some(generics), + ty, + expr.as_deref(), + vis, + *defaultness, + ); } ast::AssocItemKind::Type(box ast::TyAlias { defaultness, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 6bc4f6fc1fcfc..1580a6f6dd3ca 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -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( diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 264f30fb10a12..7444d42a405f2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -643,7 +643,17 @@ 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(fmease): #prePR Pass the generics as a parameter if this is what + // everyone else does, too. + generics: ast::Generics::default(), + ty, + expr: Some(expr), + } + .into(), + ), ) } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 5185820a7276f..df8d27935a80a 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -422,6 +422,9 @@ 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. + // FIXME(fmease): #prePR Fake tracking issue to somewhat please tidy atm + (incomplete, generic_consts, "CURRENT_RUSTC_VERSION", Some(4294967295), 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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6c419471de1b4..a69fdb8bf7079 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3122,9 +3122,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] @@ -3305,7 +3305,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`). @@ -3358,6 +3358,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) @@ -3553,7 +3554,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 { @@ -3756,9 +3759,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 { @@ -3786,7 +3789,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 { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 347c1f4637f17..a8a94e6a47687 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 270b90fa6b210..8aae8d0c22461 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -519,6 +519,8 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) { check_static_linkage(tcx, id.owner_id.def_id); } DefKind::Const => { + // FIXME(fmease): #prePR Do we need to do the following here? + // let _ = tcx.generics_of(id.owner_id); tcx.ensure().typeck(id.owner_id.def_id); } DefKind::Enum => { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 22e576e345e1f..d944b38f0b4cf 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -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` @@ -262,8 +262,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( @@ -1669,19 +1667,19 @@ fn compare_synthetic_generics<'tcx>( /// ```rust,ignore (pseudo-Rust) /// trait Foo { /// fn foo(); -/// type bar; +/// type Bar; /// fn baz(); -/// type blah; +/// type Blah; /// } /// /// impl Foo for () { /// fn foo() {} /// //~^ error -/// type bar {} +/// type Bar = (); /// //~^ error /// fn baz() {} /// //~^ error -/// type blah = u32; +/// type Blah = u32; /// //~^ error /// } /// ``` @@ -1770,12 +1768,29 @@ 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().subst_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); + + 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(fmease): Shouldn't we take special care of trait bounds and assume/require (dep on loc) +// them to be const??? e.g. in `const K` is the bound const? how can we enforce that? +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); let infcx = tcx.infer_ctxt().build(); - let param_env = tcx.param_env(impl_const_item_def.to_def_id()); + let param_env = tcx.param_env(impl_ct.def_id); let ocx = ObligationCtxt::new(&infcx); // The below is for the most part highly similar to the procedure @@ -1783,22 +1798,23 @@ pub(super) fn compare_impl_const_raw( // 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_substs = impl_trait_ref.substs; + // FIXME(fmease): ^^^ Now more than ever. + let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ct.def_id); + let trait_to_impl_substs = + impl_substs.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.substs); // 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()).subst_identity(); - let trait_ty = tcx.type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs); - 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).subst_identity(); + + let trait_ty = tcx.type_of(trait_ct.def_id).subst(tcx, trait_to_impl_substs); + 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()); // There is no "body" here, so just pass dummy id. let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -1818,7 +1834,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!( @@ -1826,12 +1842,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 }); @@ -1847,9 +1863,63 @@ pub(super) fn compare_impl_const_raw( false, false, ); + // FIXME(fmease): #prePR Check if we also ret early in method pred entail in this situation + // meaning not emitting unsat obligs. Likely tho I presume. return Err(diag.emit()); }; + let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id); + let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id); + + // FIXME(fmease): #prePR Is this correct here where we already have an ocx? + // FIXME(fmease): #prePR What exactly is this fn doing? Why doesn't it suffice to check the + // preds as obtained above? Edit: Prob cuz this is for ck'ing not cmp'ing + if std::env::var("NO_CT_REG_BOUNDS").is_err() { + // FIXME(fmease): #prePR Should we really return early here on failure? + check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?; + } + + let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_substs); + // FIXME(fmease): #prePR It'd be nice to add this fast-path but it's likely unsound rn + // as the ocx may already contain some obligations from above that we don't want + // to just skip!! + // if impl_ct_own_bounds.len() == 0 { + // return Ok(()); + // } + + // 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_substs) + .map(|(predicate, _)| predicate), + ); + + // FIXME(fmease): #prePR #style Get rid of the block smh. + { + let param_env = ty::ParamEnv::new( + tcx.mk_clauses(&hybrid_preds.predicates), + Reveal::UserFacing, + hir::Constness::Const, + ); + let param_env = traits::normalize_param_env_or_error( + tcx, + param_env, + ObligationCause::misc(impl_ct_span, impl_ct_def_id), + ); + + // FIXME(fmease): #prePR Is this correct here where we already have an ocx? + 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)); + } + } + // Check that all obligations are satisfied by the implementation's // version. let errors = ocx.select_all_or_error(); @@ -1858,7 +1928,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>( @@ -1900,7 +1970,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(); @@ -1919,7 +1989,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, @@ -1968,7 +2038,7 @@ fn compare_type_predicate_entailment<'tcx>( /// /// trait X { type Y: Copy } impl X for T { type Y = S; } /// -/// We are able to normalize `::U` to `S`, and so when we check the +/// We are able to normalize `::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 diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f47df4f215b3f..b638f3ec8eafa 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -256,6 +256,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }) | hir::ItemKind::TyAlias(_, generics) => (generics, false), // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type. + // FIXME(fmease): ^ does "elsewhere" handle `generic_consts` properly? _ => return, }; diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index ccc9f808411c3..0afea7e02aca2 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -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), + // FIXME(fmease): I think we should allow defaults for ItemKind::Const _ => (None, Defaults::FutureCompatDisallowed), } } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 12936664130da..af0fadf813eb8 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -153,6 +153,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, @@ -757,6 +758,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 { .. }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 97a3e01c52a31..2b6426b330be0 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -520,12 +520,34 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); } - hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { + hir::ItemKind::Static(..) => { // No lifetime parameters, but implied 'static. self.with(Scope::Elision { s: self.scope }, |this| { intravisit::walk_item(this, item) }); } + // FIXME(fmease): Is there are way to merge this branch with the one below that + // contains the `TyAlias`? + hir::ItemKind::Const(_, generics, _) => { + self.with(Scope::Elision { s: self.scope }, |this| { + // These kinds of items have only early-bound lifetime parameters. + let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); + this.record_late_bound_vars(item.hir_id(), vec![]); + let scope = Scope::Binder { + hir_id: item.hir_id(), + bound_vars, + scope_type: BinderScopeType::Normal, + s: this.scope, + where_bound_origin: None, + }; + this.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, |this| { + intravisit::walk_item(this, item); + }); + }); + }); + } hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { .. }, .. @@ -819,10 +841,22 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) }); } + // FIXME(fmease): Somehow dedup w/ the branch above? Const(_, _) => { - // Only methods and types support generics. - assert!(trait_item.generics.params.is_empty()); - intravisit::walk_trait_item(self, trait_item); + let bound_vars = + (&trait_item.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| intravisit::walk_trait_item(this, trait_item)) + }); } } } @@ -854,10 +888,22 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) }); } + // FIXME(fmease): Somehow dedup w/ the branch above? Const(_, _) => { - // Only methods and types support generics. - assert!(impl_item.generics.params.is_empty()); - intravisit::walk_impl_item(self, impl_item); + let bound_vars: FxIndexMap = + (&impl_item.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| intravisit::walk_impl_item(this, impl_item)) + }); } } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 3755342aef5df..64e61aab9f041 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -405,7 +405,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { + ItemKind::Const(ty, _, body_id) => { if is_suggestable_infer_ty(ty) { infer_placeholder_type( tcx, def_id, body_id, ty.span, item.ident, "constant", diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index f1765174d79de..ca7679cfba06e 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -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 diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a699cd6c942eb..2d8b956771bb6 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -420,12 +420,13 @@ impl<'a> State<'a> { fn print_associated_const( &mut self, ident: Ident, + generics: &hir::Generics<'_>, ty: &hir::Ty<'_>, default: Option, ) { - 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(""); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 72b29f7b6e906..521dc30982a0c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1392,7 +1392,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 }; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6f82ffcfe4ae4..eaa4d6857735e 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -102,7 +102,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))), diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 19f82fbd3bf73..94dd89a494e0e 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -922,7 +922,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) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index b826ced045340..2ec190577fa29 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2069,7 +2069,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, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index b821933e9086f..feef33c01b7d2 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -176,6 +176,7 @@ impl BoxPointers { impl<'tcx> LateLintPass<'tcx> for BoxPointers { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { + // FIXME(fmease): Preexisting: Shouldn't we walk the ty of Consts? even w/o `generic_consts`? match it.kind { hir::ItemKind::Fn(..) | hir::ItemKind::TyAlias(..) @@ -1531,7 +1532,8 @@ 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) => { + // FIXME(fmease): Is it okay to discard the generics here? + 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 cx.tcx.ensure().const_eval_poly(def_id); diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5f2eb890c4eba..b2e7b8551e8a8 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -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 { diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index d828e71c7ac0b..b54122fba8bd1 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -562,7 +562,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, .. }) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9787d98c1a49a..1725f8567ed61 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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,10 @@ 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() {"{}"}` +# FIXME(fmease): #prePR I don't like the wording "const expression" here, maybe "body"? +# FIXME(fmease): #prePr Add a help msg "move it after the `=`" +parse_where_clause_before_const_expr = where clauses are not allowed before const expressions + 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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 96e1c0e3c6d9e..714631a5e3c33 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2692,3 +2692,17 @@ 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_expr)] +pub(crate) struct WhereClauseBeforeConstExpr { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1470180dea714..2b9abc1c6fdbc 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -227,7 +227,7 @@ impl<'a> Parser<'a> { // STATIC ITEM self.bump(); // `static` let m = self.parse_mutability(); - let (ident, ty, expr) = self.parse_item_global(Some(m))?; + let (ident, _, ty, expr) = self.parse_item_global(Some(m))?; (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr }))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM @@ -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_item_global(None)?; + ( + 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,9 +901,10 @@ 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. + // FIXME(fmease): #prePR out of niceness update the grammar to contain whclauses fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> { let ident = self.parse_ident()?; let mut generics = self.parse_generics()?; @@ -1224,24 +1234,96 @@ impl<'a> Parser<'a> { /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`. /// /// When `m` is `"const"`, `$ident` may also be `"_"`. + // FIXME(generic_consts): Update the grammar above! + // FIXME(fmease): #prePR add back recovery for where-clauses before `=` fn parse_item_global( &mut self, m: Option, - ) -> PResult<'a, (Ident, P, Option>)> { + ) -> PResult<'a, (Ident, Generics, P, Option>)> { let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?; + let mut generics = if m.is_none() || self.may_recover() { + self.parse_generics()? + } else { + Generics::default() // FIXME: #prePR consider using `None` here instead + }; + + if !generics.params.is_empty() { + if m.is_none() { + self.sess.gated_spans.gate(sym::generic_consts, generics.span); + } else { + self.sess.emit_err(errors::StaticWithGenerics { span: generics.span }); + } + } + // Parse the type of a `const` or `static mut?` item. // That is, the `":" $ty` fragment. - 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. + // FIXME(fmease): #prePR maybe add UI test for `const X: where String: Clone;` (sic!) + // FIXME(fmease): #prePR I guess we should add a may_recover here too somewhere?? + let ty = match ( + self.eat(&token::Colon), + self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where), + ) { + // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type. (true, false) => self.parse_ty()?, (colon, _) => self.recover_missing_const_type(colon, m), }; + // 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 }; + + // if expr.is_some() && before_where_clause.has_where_token { + // // FIXME(fmease): #prePR Emit a different message for static items! + // self.sess + // .emit_err(errors::WhereClauseBeforeConstExpr { span: before_where_clause.span }); + // } + + let after_where_clause = if m.is_none() || self.may_recover() { + self.parse_where_clause()? + } else { + WhereClause::default() // FIXME: #prePR consider using `None` here instead + }; + + if after_where_clause.has_where_token { + if m.is_none() { + self.sess.gated_spans.gate(sym::generic_consts, generics.span); + } else { + // FIXME(fmease): #prePR define custom msg, StaticWithWhereClause + self.sess.emit_err(errors::StaticWithGenerics { span: generics.span }); + } + } + + // // FIXME(fmease): #prePR Clean up this logic! + // + // if m.is_some() && where_clause.has_where_token { + // self.sess.emit_err(errors::StaticWithGenerics { span: where_clause.span }); + // } + // + // if m.is_none() && (/*before_where_clause.has_where_token || */where_clause.has_where_token) + // { + // // FIXME(fmease): #prePR Wrong span when `before_where_clause.has_where_token` is + // // true. + // self.sess.gated_spans.gate(sym::generic_consts, where_clause.span); + // } + + // 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, + // // FIXME(fmease): #prePR The type alias parser uses `DUMMY_SP` and only during lowering + // // the correct span is computed (`add_ty_alias_where_clause`). We could follow suit if + // // it gains us anything (wrt to better diags, better pp'ing etc.). + // span: after_where_clause.span, + // }; + // generics.where_clause = where_clause; + generics.where_clause = after_where_clause; + self.expect_semi()?; - Ok((id, ty, expr)) + Ok((id, generics, ty, expr)) } /// We were supposed to parse `":" $ty` but the `:` or the type was missing. diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 160528e4074d4..7dec5b0acc882 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -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); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 90cb312edd203..751d59c4d05c0 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -190,6 +190,7 @@ pub(crate) enum RibKind<'a> { /// /// The item may reference generic parameters in trivial constant expressions. /// All other constants aren't allowed to use generic params at all. + // FIXME(fmease): The paragraph above is not quite true any more. ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>), /// We passed through a module. @@ -2401,25 +2402,19 @@ 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 { - 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)), + Some((item.ident, ConstantItemKind::Static)), |this| this.visit_expr(expr), ); } @@ -2427,6 +2422,51 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }); } + ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => { + // FIXME(fmease): What should the ordering of the ribs be? Right now, it's + // STATIC <-- LIFETIME#0 <-- GENERIC <-- LIFETIME#1 <-- {,CONSTANT} + self.with_static_rib(|this| { + this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { + this.with_generic_param_rib( + &generics.params, + RibKind::Item(HasGenericParams::Yes(generics.span)), + LifetimeRibKind::Generics { + binder: item.id, + kind: LifetimeBinderKind::Item, + span: generics.span, + }, + |this| { + this.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Static), + |this| { + this.visit_generics(generics); + this.visit_ty(ty); + }, + ); + + this.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Infer), + |this| { + let Some(expr) = expr else { return; }; + + // FIXME(fmease): This rib setup might not handle some + // situations correctly. Cf. former comment: + // > 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, ConstantItemKind::Const)), + |this| this.visit_expr(expr), + ) + }, + ); + }, + ); + }) + }) + } + ItemKind::Use(ref use_tree) => { self.future_proof_import(use_tree); } @@ -2688,28 +2728,42 @@ 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), - ) - }, - ); - } + // FIXME(fmease): Somehow reuse walk_assoc_item here? + 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::Item, + }, + |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.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Infer), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + ConstantHasGenerics::Yes, + None, + |this| this.visit_expr(expr), + ) + }, + ); + } + }, + ); } AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); @@ -2864,36 +2918,53 @@ 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, .. }) => { + // FIXME(fmease): Somehow reuse walk_assoc_item here? + 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::Item, + }, + |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.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Infer), + |this| { + this.with_constant_rib( + IsRepeatExpr::No, + ConstantHasGenerics::Yes, + None, + |this| this.visit_expr(expr), + ) + }, + ); + } + }, + ); } AssocItemKind::Fn(box Fn { generics, .. }) => { debug!("resolve_implementation AssocItemKind::Fn"); @@ -4430,6 +4501,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) @@ -4449,7 +4521,6 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { ItemKind::Mod(..) | ItemKind::ForeignMod(..) | ItemKind::Static(..) - | ItemKind::Const(..) | ItemKind::Use(..) | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 66a627d5aac7c..51220c197c75c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -782,6 +782,7 @@ symbols! { generic_associated_types, generic_associated_types_extended, generic_const_exprs, + generic_consts, generic_param_attrs, get_context, global_allocator, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9ac1ba0275c19..61ce9f7c6226d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -654,6 +654,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, .. }), .. @@ -719,6 +720,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, .. }), .. diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 870cfa93058aa..6a6c97371d976 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -632,6 +632,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).subst_identity()), @@ -639,6 +643,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { Some(def_id), None, ), + generics, kind: clean::ConstantKind::Extern { def_id }, } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0182d50773d77..ce9f3cead8194 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -256,6 +256,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t Some(def_id), None, ), + generics: Generics::default(), kind: ConstantKind::Anonymous { body: constant.value.body }, } } @@ -267,6 +268,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: Generics::default(), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -1159,11 +1161,20 @@ 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)) => { + // FIXME(fmease): #prePR Do we really need to `enter_impl_trait` for gen consts? + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + AssocConstItem( + generics, + clean_ty(ty, cx), + ConstantKind::Local { def_id: local_did, body: default }, + ) + } + hir::TraitItemKind::Const(ty, None) => { + // FIXME(fmease): #prePR Do we really need to `enter_impl_trait` for gen consts? + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + TyAssocConstItem(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) @@ -1208,8 +1219,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(generics, clean_ty(ty, cx), default) } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); @@ -1250,14 +1262,21 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( None, ); + let mut generics = 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 => { @@ -1348,34 +1367,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| ¶m.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| ¶m.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 @@ -2446,8 +2438,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: clean_generics(generics, cx), kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 65b1b72adc153..22b9ab3100172 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -136,3 +136,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| ¶m.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| ¶m.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; +} diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 26139d5276919..f484e3d8e185f 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -830,9 +830,9 @@ pub(crate) enum ItemKind { ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), /// A required associated constant in a trait declaration. - TyAssocConstItem(Type), + TyAssocConstItem(Generics, Type), /// An associated constant in a trait impl or a provided one in a trait declaration. - AssocConstItem(Type, ConstantKind), + AssocConstItem(Generics, Type, ConstantKind), /// A required associated type in a trait declaration. /// /// The bounds may be non-empty if there is a `where` clause. @@ -877,8 +877,8 @@ impl ItemKind { | MacroItem(_) | ProcMacroItem(_) | PrimitiveItem(_) - | TyAssocConstItem(_) - | AssocConstItem(_, _) + | TyAssocConstItem(..) + | AssocConstItem(..) | TyAssocTypeItem(..) | AssocTypeItem(..) | StrippedItem(_) @@ -1275,7 +1275,7 @@ impl Lifetime { } } -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, @@ -1345,7 +1345,7 @@ impl GenericParamDef { } // maybe use a Generic enum and use Vec? -#[derive(Clone, Debug, Default)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct Generics { pub(crate) params: ThinVec, pub(crate) where_predicates: ThinVec, @@ -2250,9 +2250,13 @@ pub(crate) struct Static { pub(crate) expr: Option, } +// FIXME(fmease): #prePR investigate why this type needs `PartialEq`, `Eq`, `Hash`. +// If it actually doesn't (or we can easily get rid of it), remove the added derives +// from `Generics` and `WherePredicate` again. #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Constant { pub(crate) type_: Type, + pub(crate) generics: Generics, pub(crate) kind: ConstantKind, } @@ -2502,7 +2506,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(fmease): #prePR reduce the size again, it used to be 64 + static_assert_size!(ItemKind, 80); static_assert_size!(PathSegment, 40); static_assert_size!(Type, 32); // tidy-alphabetical-end diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index f923f90545126..846730117455a 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -748,20 +748,23 @@ 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, cx: &Context<'_>, ) { + // @Task impl let tcx = cx.tcx(); write!( w, - "{extra}{vis}const {name}: {ty}", + "{extra}{vis}const {name}{generics}: {ty}", extra = extra, 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 +777,8 @@ 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)))); } + // FIXME(fmease): #prePR Replace param `extra: &str` with `indent: usize` + write!(w, "{}", print_where_clause(generics, cx, extra.len(), Ending::NoNewline)); } fn assoc_type( @@ -986,19 +991,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 { " " } else { "" }, + cx, + ) + } clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type( w, item, @@ -1565,7 +1573,8 @@ fn render_impl( w.write_str(""); } } - 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, "
", id, item_type, in_trait_class); @@ -1578,10 +1587,11 @@ 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 }), diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 383e3c170881a..324124952580c 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1541,12 +1541,19 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle let tcx = cx.tcx(); render_attributes_in_code(w, it, tcx); + // FIXME(fmease): #prePR This is actually not just confined to rustdoc. + // Apparently where-clause clauses go *before* the body for *free* ty aliases. + // Why? I thought the change affected both free and assoc? + // Investigate and update the parsing machinery, AST & HIR pretty-printing and + // rustdoc. 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::Newline), ); // FIXME: The code below now prints diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 91cd55b1113ab..f42438c2fe5bb 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -321,8 +321,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(fmease): #prePR Support generics consts in rustdoc-json + TyAssocConstItem(_generics, ty) => { + ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None } + } + // FIXME(fmease): #prePR Support generics consts in rustdoc-json + AssocConstItem(_generics, ty, default) => { ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) } } TyAssocTypeItem(g, b) => ItemEnum::AssocType { diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs index 4dc750c03b488..3fac448f2d936 100644 --- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs @@ -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 consts. + // FIXME(fmease): This `generics.hwcp` doesn't account for items with a trailing + // `where` keyword but no bounds. + 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(); diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 75f1e95276a69..d82758c98394e 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -286,7 +286,10 @@ 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 { + // FIXME(fmease): Not sure if we need to do some extra work here for generic consts. + // Do we need to explicitly replace params with placeholders or can `is_unfrozen` already + // handle "unbound" ty params etc. on its own? 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 }); diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 3c873a5901d42..0c5c07fdd9611 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -349,7 +349,10 @@ 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( + // FIXME(fmease): Not sure if we need to do some extra work here for generic consts. + // Do we need to explicitly replace params with placeholders or can relate routines + // handle "unbound" ty params etc. on their own? + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( cx, ty, CheckTyContext { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8cc01f1ef9740..7e42924603a41 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -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, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index d1cfdc49658d7..59c727582ad81 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -462,8 +462,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { // Check if this constant is based on `cfg!(..)`, // which is NOT constant for our purposes. + // FIXME(fmease): Ignoring the generics here is certainly not right 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() diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 727b59f1f432a..2f9acfa9be685 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2385,7 +2385,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 { diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 55bf38110a6d5..3ed587caadc6b 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -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 = 1896; -const ROOT_ENTRY_LIMIT: usize = 870; +const ROOT_ENTRY_LIMIT: usize = 871; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/ui/generic-consts/feature-gate-generic_consts.rs b/tests/ui/generic-consts/feature-gate-generic_consts.rs new file mode 100644 index 0000000000000..c936edd2b625f --- /dev/null +++ b/tests/ui/generic-consts/feature-gate-generic_consts.rs @@ -0,0 +1,24 @@ +// FIXME: polish this up + +pub trait Tr { + const K: i32; + //~^ ERROR generic consts are experimental +} + +const K: i32 = 0; +//~^ ERROR generic consts are experimental + +const E<>: i32 = 0; +//~^ ERROR generic consts are experimental + +macro_rules! discard { + ($item:item) => {} +} + +discard! { const FREE: () = (); } +//~^ ERROR generic consts are experimental + +discard! { impl () { const ASSOC: () = (); } } +//~^ ERROR generic consts are experimental + +fn main() {} diff --git a/tests/ui/generic-consts/feature-gate-generic_consts.stderr b/tests/ui/generic-consts/feature-gate-generic_consts.stderr new file mode 100644 index 0000000000000..4d40ac6db99e4 --- /dev/null +++ b/tests/ui/generic-consts/feature-gate-generic_consts.stderr @@ -0,0 +1,43 @@ +error[E0658]: generic consts are experimental + --> $DIR/feature-gate-generic_consts.rs:4:12 + | +LL | const K: i32; + | ^^^ + | + = help: add `#![feature(generic_consts)]` to the crate attributes to enable + +error[E0658]: generic consts are experimental + --> $DIR/feature-gate-generic_consts.rs:8:8 + | +LL | const K: i32 = 0; + | ^^^ + | + = help: add `#![feature(generic_consts)]` to the crate attributes to enable + +error[E0658]: generic consts are experimental + --> $DIR/feature-gate-generic_consts.rs:11:8 + | +LL | const E<>: i32 = 0; + | ^^ + | + = help: add `#![feature(generic_consts)]` to the crate attributes to enable + +error[E0658]: generic consts are experimental + --> $DIR/feature-gate-generic_consts.rs:18:22 + | +LL | discard! { const FREE: () = (); } + | ^^^ + | + = help: add `#![feature(generic_consts)]` to the crate attributes to enable + +error[E0658]: generic consts are experimental + --> $DIR/feature-gate-generic_consts.rs:21:33 + | +LL | discard! { impl () { const ASSOC: () = (); } } + | ^^^^^^^^^^^^^ + | + = help: add `#![feature(generic_consts)]` to the crate attributes to enable + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/generic-statics.rs b/tests/ui/parser/generic-statics.rs new file mode 100644 index 0000000000000..2fb8781fdffb8 --- /dev/null +++ b/tests/ui/parser/generic-statics.rs @@ -0,0 +1,4 @@ +static S: i32 = 0; +//~^ ERROR static items may not have generic parameters + +fn main() {} diff --git a/tests/ui/parser/generic-statics.stderr b/tests/ui/parser/generic-statics.stderr new file mode 100644 index 0000000000000..c757232b061c4 --- /dev/null +++ b/tests/ui/parser/generic-statics.stderr @@ -0,0 +1,8 @@ +error: static items may not have generic parameters + --> $DIR/generic-statics.rs:1:9 + | +LL | static S: i32 = 0; + | ^^^ + +error: aborting due to previous error +