From 61f33bfd2940ea55559b253a66c42937a17d3c87 Mon Sep 17 00:00:00 2001 From: Julian Knodt Date: Thu, 31 Dec 2020 01:58:27 +0100 Subject: [PATCH] first pass at default values for const generics - Adds optional default values to const generic parameters in the AST and HIR - Parses these optional default values - Adds a `const_generics_defaults` feature gate --- compiler/rustc_ast/src/ast.rs | 2 ++ compiler/rustc_ast/src/mut_visit.rs | 3 +- compiler/rustc_ast/src/visit.rs | 7 ++++- compiler/rustc_ast_lowering/src/lib.rs | 5 ++-- .../rustc_ast_passes/src/ast_validation.rs | 18 ++++++++++-- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 +++-- .../rustc_builtin_macros/src/deriving/mod.rs | 3 +- compiler/rustc_feature/src/active.rs | 3 ++ compiler/rustc_hir/src/hir.rs | 2 ++ compiler/rustc_hir/src/intravisit.rs | 7 ++++- compiler/rustc_hir_pretty/src/lib.rs | 8 +++-- compiler/rustc_parse/src/parser/generics.rs | 5 +++- compiler/rustc_parse/src/parser/path.rs | 29 ++++++++++++------- compiler/rustc_resolve/src/late.rs | 3 +- .../rustc_save_analysis/src/dump_visitor.rs | 5 +++- compiler/rustc_save_analysis/src/sig.rs | 6 +++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/wfcheck.rs | 6 ++-- src/librustdoc/clean/mod.rs | 3 +- .../clippy_lints/src/utils/ast_utils.rs | 7 ++++- 20 files changed, 99 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index cf31e566c38..8167bde0322 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -368,6 +368,8 @@ pub enum GenericParamKind { ty: P, /// Span of the `const` keyword. kw_span: Span, + /// Optional default value for the const generic param + default: Option, }, } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3889ede7f4c..97966cc3260 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -790,8 +790,9 @@ pub fn noop_flat_map_generic_param( GenericParamKind::Type { default } => { visit_opt(default, |default| vis.visit_ty(default)); } - GenericParamKind::Const { ty, kw_span: _ } => { + GenericParamKind::Const { ty, kw_span: _, default } => { vis.visit_ty(ty); + visit_opt(default, |default| vis.visit_anon_const(default)); } } smallvec![param] diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a420bb56350..a696626f8c4 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -578,7 +578,12 @@ pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a Generi match param.kind { GenericParamKind::Lifetime => (), GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty, .. } => visitor.visit_ty(ty), + GenericParamKind::Const { ref ty, ref default, .. } => { + visitor.visit_ty(ty); + if let Some(default) = default { + visitor.visit_anon_const(default); + } + } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d17b29089d6..f81dc39842c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2242,13 +2242,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Plain(param.ident), kind) } - GenericParamKind::Const { ref ty, kw_span: _ } => { + GenericParamKind::Const { ref ty, kw_span: _, ref default } => { let ty = self .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| { this.lower_ty(&ty, ImplTraitContext::disallowed()) }); + let default = default.as_ref().map(|def| self.lower_anon_const(def)); - (hir::ParamName::Plain(param.ident), hir::GenericParamKind::Const { ty }) + (hir::ParamName::Plain(param.ident), hir::GenericParamKind::Const { ty, default }) } }; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c40f00bc9e9..fd080fa5992 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -733,7 +733,7 @@ fn validate_generic_param_order( let (ord_kind, ident) = match ¶m.kind { GenericParamKind::Lifetime => (ParamKindOrd::Lifetime, ident), GenericParamKind::Type { default: _ } => (ParamKindOrd::Type, ident), - GenericParamKind::Const { ref ty, kw_span: _ } => { + GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { let ty = pprust::ty_to_string(ty); let unordered = sess.features_untracked().const_generics; (ParamKindOrd::Const { unordered }, Some(format!("const {}: {}", param.ident, ty))) @@ -775,7 +775,7 @@ fn validate_generic_param_order( GenericParamKind::Type { default: None } => (), GenericParamKind::Lifetime => (), // FIXME(const_generics:defaults) - GenericParamKind::Const { ty: _, kw_span: _ } => (), + GenericParamKind::Const { ty: _, kw_span: _, default: _ } => (), } first = false; } @@ -1166,6 +1166,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } } + if !self.session.features_untracked().const_generics_defaults { + if let GenericParamKind::Const { default: Some(ref default), .. } = param.kind { + let mut err = self.err_handler().struct_span_err( + default.value.span, + "default values for const generic parameters are unstable", + ); + err.help( + "add `#![feature(const_generics_defaults)]` \ + to the crate attributes to enable", + ); + err.emit(); + break; + } + } } validate_generic_param_order( diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 333a396a0b4..bdd378b34e1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2668,13 +2668,17 @@ impl<'a> State<'a> { s.print_type(default) } } - ast::GenericParamKind::Const { ref ty, kw_span: _ } => { + ast::GenericParamKind::Const { ref ty, kw_span: _, ref default } => { s.word_space("const"); s.print_ident(param.ident); s.s.space(); s.word_space(":"); s.print_type(ty); - s.print_type_bounds(":", ¶m.bounds) + s.print_type_bounds(":", ¶m.bounds); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): print the `default` value here + todo!(); + } } } }); diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 1651180817b..3c8bf12b3d4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -145,7 +145,8 @@ fn inject_impl_of_structural_trait( *default = None; ast::GenericArg::Type(cx.ty_ident(span, param.ident)) } - ast::GenericParamKind::Const { ty: _, kw_span: _ } => { + ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { + *default = None; ast::GenericArg::Const(cx.const_ident(span, param.ident)) } }) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index c61857a1cd0..70213f346f6 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -623,6 +623,9 @@ declare_features! ( /// `:pat2018` and `:pat2021` macro matchers. (active, edition_macro_pats, "1.51.0", Some(54883), None), + /// Allows const generics to have default values (e.g. `struct Foo(...);`). + (active, const_generics_defaults, "1.51.0", Some(44580), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 47a7651e1d4..ebeb1bae2a3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -418,6 +418,8 @@ pub enum GenericParamKind<'hir> { }, Const { ty: &'hir Ty<'hir>, + /// Optional default value for the const generic param + default: Option, }, } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 03c8b173885..87a2434152f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -877,7 +877,12 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => walk_list!(visitor, visit_ty, default), - GenericParamKind::Const { ref ty } => visitor.visit_ty(ty), + GenericParamKind::Const { ref ty, ref default } => { + visitor.visit_ty(ty); + if let Some(ref default) = default { + visitor.visit_anon_const(default); + } + } } walk_list!(visitor, visit_param_bound, param.bounds); } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0b5eb1d8266..85bc38daa3d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2205,9 +2205,13 @@ impl<'a> State<'a> { self.print_type(&default) } } - GenericParamKind::Const { ref ty } => { + GenericParamKind::Const { ref ty, ref default } => { self.word_space(":"); - self.print_type(ty) + self.print_type(ty); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): print the `default` value here + todo!(); + } } } } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 860e63020bb..ff84dba4177 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -56,12 +56,15 @@ impl<'a> Parser<'a> { self.expect(&token::Colon)?; let ty = self.parse_ty()?; + // Parse optional const generics default value. + let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None }; + Ok(GenericParam { ident, id: ast::DUMMY_NODE_ID, attrs: preceding_attrs.into(), bounds: Vec::new(), - kind: GenericParamKind::Const { ty, kw_span: const_span }, + kind: GenericParamKind::Const { ty, kw_span: const_span, default }, is_placeholder: false, }) } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 60a47ca12b8..43dee391c17 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -515,6 +515,23 @@ impl<'a> Parser<'a> { } } + /// Parse a const argument, e.g. `<3>`. It is assumed the angle brackets will be parsed by + /// the caller. + pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { + // Parse const argument. + let value = if let token::OpenDelim(token::Brace) = self.token.kind { + self.parse_block_expr( + None, + self.token.span, + BlockCheckMode::Default, + ast::AttrVec::new(), + )? + } else { + self.handle_unambiguous_unbraced_const_arg()? + }; + Ok(AnonConst { id: ast::DUMMY_NODE_ID, value }) + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option> { @@ -524,17 +541,7 @@ impl<'a> Parser<'a> { GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. - let value = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, - self.token.span, - BlockCheckMode::Default, - ast::AttrVec::new(), - )? - } else { - self.handle_unambiguous_unbraced_const_arg()? - }; - GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) + GenericArg::Const(self.parse_const_arg()?) } else if self.check_type() { // Parse type argument. match self.parse_ty() { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index dd1874debbd..fbe99a31150 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -586,7 +586,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { // Allow all following defaults to refer to this type parameter. default_ban_rib.bindings.remove(&Ident::with_dummy_span(param.ident.name)); } - GenericParamKind::Const { ref ty, kw_span: _ } => { + GenericParamKind::Const { ref ty, kw_span: _, default: _ } => { + // FIXME(const_generics_defaults): handle `default` value here for bound in ¶m.bounds { self.visit_param_bound(bound); } diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index 40d60a8394b..987badcedde 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -1343,9 +1343,12 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { self.visit_ty(ty); } } - hir::GenericParamKind::Const { ref ty } => { + hir::GenericParamKind::Const { ref ty, ref default } => { self.process_bounds(param.bounds); self.visit_ty(ty); + if let Some(default) = default { + self.visit_anon_const(default); + } } } } diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index ff445d727fa..e7d1c9d3bbe 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -614,9 +614,13 @@ impl<'hir> Sig for hir::Generics<'hir> { start: offset + text.len(), end: offset + text.len() + param_text.as_str().len(), }); - if let hir::GenericParamKind::Const { ref ty } = param.kind { + if let hir::GenericParamKind::Const { ref ty, ref default } = param.kind { param_text.push_str(": "); param_text.push_str(&ty_to_string(&ty)); + if let Some(ref _default) = default { + // FIXME(const_generics_defaults): push the `default` value here + todo!(); + } } if !param.bounds.is_empty() { param_text.push_str(": "); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b040a70437d..64b50a9b70a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -368,6 +368,7 @@ symbols! { const_fn_transmute, const_fn_union, const_generics, + const_generics_defaults, const_if_match, const_impl_trait, const_in_array_repeat_expressions, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 2ae9ded3fa0..18c63cb433d 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -286,9 +286,9 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { // We currently only check wf of const params here. hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (), - // Const parameters are well formed if their - // type is structural match. - hir::GenericParamKind::Const { ty: hir_ty } => { + // Const parameters are well formed if their type is structural match. + // FIXME(const_generics_defaults): we also need to check that the `default` is wf. + hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); let err_ty_str; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f4eb1924e6f..022161164d1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -607,11 +607,12 @@ impl Clean for hir::GenericParam<'_> { synthetic, }, ), - hir::GenericParamKind::Const { ref ty } => ( + hir::GenericParamKind::Const { ref ty, default: _ } => ( self.name.ident().name, GenericParamDefKind::Const { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), ty: ty.clean(cx), + // FIXME(const_generics_defaults): add `default` field here to the docs }, ), }; diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index f0267e4c792..940573e4caa 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -407,6 +407,10 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { } } +pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool { + eq_expr(&l.value, &r.value) +} + pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), @@ -497,7 +501,8 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool { && match (&l.kind, &r.kind) { (Lifetime, Lifetime) => true, (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)), - (Const { ty: l, kw_span: _ }, Const { ty: r, kw_span: _ }) => eq_ty(l, r), + (Const { ty: lt, kw_span: _ , default: ld}, Const { ty: rt, kw_span: _, default: rd }) => + eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)), _ => false, } && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))