Auto merge of #80547 - lqd:const_generics_defaults, r=varkor

In which we start to parse const generics defaults

As discussed in this [zulip topic](https://rust-lang.zulipchat.com/#narrow/stream/260443-project-const-generics/topic/const.20generic.20defaults), this PR extracts the parsing parts from `@JulianKnodt's` PR #75384 for a better user-experience using the newly stabilized `min_const_generics` (albeit temporary) as shown in #80507: trying to use default values on const generics currently results in parse errors, as if the user didn't use the correct syntax (which is somewhat true but also misleading).

This PR extracts (and slightly modifies in a couple places) `@JulianKnodt's` parsing code (with attribution if I've done everything correctly), AST and HIR changes, and feature gate setup.

This feature is now marked as "incomplete" and thus will also print out the expected "const generics default values are unstable" error instead of a syntax error. Note that, as I've only extracted the parsing part, the actual feature will not work at all if enabled. There will be ICEs, and inference errors on the const generics default values themselves.

Fixes #80507.

Once this merges, I'll:
- modify the const generics tracking issue to refer to the `const_generics_defaults` gate rather than the older temporary name it uses there.
- create the GH `F-const_generics_defaults` label

r? `@varkor`
This commit is contained in:
bors 2021-01-01 13:17:42 +00:00
commit a609fb45ef
37 changed files with 160 additions and 53 deletions

View File

@ -368,6 +368,8 @@ pub enum GenericParamKind {
ty: P<Ty>,
/// Span of the `const` keyword.
kw_span: Span,
/// Optional default value for the const generic param
default: Option<AnonConst>,
},
}

View File

@ -790,8 +790,9 @@ pub fn noop_flat_map_generic_param<T: MutVisitor>(
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]

View File

@ -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);
}
}
}
}

View File

@ -2242,13 +2242,14 @@ fn lower_generic_param(
(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 })
}
};

View File

@ -733,7 +733,7 @@ fn validate_generic_param_order(
let (ord_kind, ident) = match &param.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)))
@ -774,8 +774,8 @@ fn validate_generic_param_order(
}
GenericParamKind::Type { default: None } => (),
GenericParamKind::Lifetime => (),
// FIXME(const_generics:defaults)
GenericParamKind::Const { ty: _, kw_span: _ } => (),
// FIXME(const_generics_defaults)
GenericParamKind::Const { ty: _, kw_span: _, default: _ } => (),
}
first = false;
}

View File

@ -619,6 +619,10 @@ macro_rules! gate_all {
extended_key_value_attributes,
"arbitrary expressions in key-value attributes are unstable"
);
gate_all!(
const_generics_defaults,
"default values for const generic parameters are experimental"
);
if sess.parse_sess.span_diagnostic.err_count() == 0 {
// Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
// involved, so we only emit errors where there are no other parsing errors.

View File

@ -2668,13 +2668,17 @@ pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::Generic
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(":", &param.bounds)
s.print_type_bounds(":", &param.bounds);
if let Some(ref _default) = default {
// FIXME(const_generics_defaults): print the `default` value here
todo!();
}
}
}
});

View File

@ -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))
}
})

View File

@ -581,7 +581,7 @@ pub fn set(&self, features: &mut Features, span: Span) {
/// Allows `if let` guard in match arms.
(active, if_let_guard, "1.47.0", Some(51114), None),
/// Allows non-trivial generic constants which have to be manually propageted upwards.
/// Allows non-trivial generic constants which have to be manually propagated upwards.
(active, const_evaluatable_checked, "1.48.0", Some(76560), None),
/// Allows basic arithmetic on floating point types in a `const fn`.
@ -623,6 +623,9 @@ pub fn set(&self, features: &mut Features, span: Span) {
/// `: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<const N: usize = 3>(...);`).
(active, const_generics_defaults, "1.51.0", Some(44580), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
@ -647,6 +650,7 @@ pub fn set(&self, features: &mut Features, span: Span) {
sym::repr128,
sym::unsized_locals,
sym::capture_disjoint_fields,
sym::const_generics_defaults,
];
/// Some features are not allowed to be used together at the same time, if

View File

@ -418,6 +418,8 @@ pub enum GenericParamKind<'hir> {
},
Const {
ty: &'hir Ty<'hir>,
/// Optional default value for the const generic param
default: Option<AnonConst>,
},
}

View File

@ -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);
}

View File

@ -2205,9 +2205,13 @@ pub fn print_generic_param(&mut self, param: &GenericParam<'_>) {
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!();
}
}
}
}

View File

@ -958,7 +958,7 @@ fn strip_generic_default_params(
ty::GenericParamDefKind::Type { has_default, .. } => {
Some((param.def_id, has_default))
}
ty::GenericParamDefKind::Const => None, // FIXME(const_generics:defaults)
ty::GenericParamDefKind::Const => None, // FIXME(const_generics_defaults)
})
.peekable();
let has_default = {

View File

@ -1834,7 +1834,7 @@ fn encode_info_for_generics(&mut self, generics: &hir::Generics<'tcx>) {
EntryKind::ConstParam,
true,
);
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
}
}
}

View File

@ -203,7 +203,7 @@ fn generic_args_to_print(
self.tcx().type_of(param.def_id).subst(self.tcx(), substs),
)
}
ty::GenericParamDefKind::Const => false, // FIXME(const_generics:defaults)
ty::GenericParamDefKind::Const => false, // FIXME(const_generics_defaults)
}
})
.count();

View File

@ -5,7 +5,7 @@
self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
};
use rustc_errors::PResult;
use rustc_span::symbol::kw;
use rustc_span::symbol::{kw, sym};
impl<'a> Parser<'a> {
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
@ -56,12 +56,26 @@ fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a,
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;
// Parse optional const generics default value, taking care of feature gating the spans
// with the unstable syntax mechanism.
let default = if self.eat(&token::Eq) {
// The gated span goes from the `=` to the end of the const argument that follows (and
// which could be a block expression).
let start = self.prev_token.span;
let const_arg = self.parse_const_arg()?;
let span = start.to(const_arg.value.span);
self.sess.gated_spans.gate(sym::const_generics_defaults, span);
Some(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,
})
}

View File

@ -515,6 +515,23 @@ pub(super) fn expr_is_valid_const_arg(&self, expr: &P<rustc_ast::Expr>) -> bool
}
}
/// 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<GenericArg>> {
@ -524,17 +541,7 @@ fn parse_generic_arg(&mut self) -> PResult<'a, Option<GenericArg>> {
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() {

View File

@ -443,7 +443,7 @@ fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
let kind = match &p.kind {
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
hir::GenericParamKind::Type { default, .. } if default.is_some() => {
AnnotationKind::Container
}

View File

@ -586,7 +586,8 @@ fn visit_generics(&mut self, generics: &'ast Generics) {
// 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 &param.bounds {
self.visit_param_bound(bound);
}

View File

@ -214,7 +214,7 @@ enum ResolutionError<'a> {
/// Error E0530: `X` bindings cannot shadow `Y`s.
BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>),
/// Error E0128: type parameters with a default cannot use forward-declared identifiers.
ForwardDeclaredTyParam, // FIXME(const_generics:defaults)
ForwardDeclaredTyParam, // FIXME(const_generics_defaults)
/// ERROR E0770: the type of const parameters must not depend on other generic parameters.
ParamInTyOfConstParam(Symbol),
/// constant values inside of type parameter defaults must not depend on generic parameters.

View File

@ -1343,9 +1343,12 @@ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'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);
}
}
}
}

View File

@ -614,9 +614,13 @@ fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<
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(": ");

View File

@ -368,6 +368,7 @@
const_fn_transmute,
const_fn_union,
const_generics,
const_generics_defaults,
const_if_match,
const_impl_trait,
const_in_array_repeat_expressions,

View File

@ -387,7 +387,7 @@ pub(crate) fn check_generic_arg_count(
defaults.types += has_default as usize
}
GenericParamDefKind::Const => {
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
}
};
}

View File

@ -486,7 +486,7 @@ fn inferred_kind(
}
GenericParamDefKind::Const => {
let ty = tcx.at(self.span).type_of(param.def_id);
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
if infer_args {
// No const parameters were provided, we can infer all.
self.astconv.ct_infer(ty, Some(param), self.span).into()

View File

@ -1376,7 +1376,7 @@ fn inferred_kind(
}
}
GenericParamDefKind::Const => {
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
// No const parameters were provided, we have to infer them.
self.fcx.var_for_def(self.span, param)
}

View File

@ -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;
@ -785,7 +785,7 @@ fn check_where_clauses<'tcx, 'fcx>(
}
GenericParamDefKind::Const => {
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
fcx.tcx.mk_param_from_def(param)
}
}

View File

@ -228,7 +228,7 @@ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
hir::GenericParamKind::Const { .. } => {
let def_id = self.tcx.hir().local_def_id(param.hir_id);
self.tcx.ensure().type_of(def_id);
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
}
}
}

View File

@ -607,11 +607,12 @@ fn clean(&self, cx: &DocContext<'_>) -> GenericParamDef {
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 for docs
},
),
};
@ -1385,7 +1386,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &DocContext<'_>) -> Type {
if let Some(ct) = const_ {
ct_substs.insert(const_param_def_id.to_def_id(), ct.clean(cx));
}
// FIXME(const_generics:defaults)
// FIXME(const_generics_defaults)
indices.consts += 1;
}
}

View File

@ -1,4 +1,4 @@
fn foo<const SIZE: usize = 5>() {}
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`
//~^ ERROR default values for const generic parameters are experimental
fn main() {}

View File

@ -1,8 +1,12 @@
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`
error[E0658]: default values for const generic parameters are experimental
--> $DIR/default_function_param.rs:1:26
|
LL | fn foo<const SIZE: usize = 5>() {}
| ^ expected one of 7 possible tokens
| ^^^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics_defaults)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -1,4 +1,4 @@
trait Foo<const KIND: bool = true> {}
//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`
//~^ ERROR default values for const generic parameters are experimental
fn main() {}

View File

@ -1,8 +1,12 @@
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `=`
error[E0658]: default values for const generic parameters are experimental
--> $DIR/default_trait_param.rs:1:28
|
LL | trait Foo<const KIND: bool = true> {}
| ^ expected one of 7 possible tokens
| ^^^^^^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics_defaults)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -7,7 +7,7 @@
//[full]~^ ERROR constant values inside of type parameter defaults
//[min]~^^ ERROR generic parameters may not be used in const operations
// FIXME(const_generics:defaults): We still don't know how to we deal with type defaults.
// FIXME(const_generics_defaults): We still don't know how to deal with type defaults.
struct Bar<T = [u8; N], const N: usize>(T);
//~^ ERROR constant values inside of type parameter defaults
//~| ERROR type parameters with a default

View File

@ -0,0 +1,9 @@
#[cfg(FALSE)]
struct A<const N: usize = 3>;
//~^ ERROR default values for const generic parameters are experimental
#[cfg(FALSE)]
fn foo<const B: bool = false>() {}
//~^ ERROR default values for const generic parameters are experimental
fn main() {}

View File

@ -0,0 +1,21 @@
error[E0658]: default values for const generic parameters are experimental
--> $DIR/feature-gate-const_generics_defaults.rs:2:25
|
LL | struct A<const N: usize = 3>;
| ^^^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics_defaults)]` to the crate attributes to enable
error[E0658]: default values for const generic parameters are experimental
--> $DIR/feature-gate-const_generics_defaults.rs:6:22
|
LL | fn foo<const B: bool = false>() {}
| ^^^^^^^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics_defaults)]` to the crate attributes to enable
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -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))