Skip to content

Commit

Permalink
Auto merge of #74877 - lcnr:min_const_generics, r=oli-obk
Browse files Browse the repository at this point in the history
Implement the `min_const_generics` feature gate

Implements both rust-lang/lang-team#37 and rust-lang/compiler-team#332.

Adds the new feature gate `#![feature(min_const_generics)]`.
This feature gate adds the following limitations to using const generics:
- generic parameters must only be used in types if they are trivial. (either `N` or `{ N }`)
- generic parameters must be either integers, `bool` or `char`.

We do allow arbitrary expressions in associated consts though, meaning that the following is allowed,
even if `<[u8; 0] as Foo>::ASSOC` is not const evaluatable.
```rust
trait Foo {
    const ASSOC: usize;
}

impl<const N: usize> Foo for [u8; N] {
    const ASSOC: usize = 64 / N;
}
```

r? @varkor cc @eddyb @withoutboats
  • Loading branch information
bors committed Aug 8, 2020
2 parents f3a9de9 + 644c894 commit f9c2177
Show file tree
Hide file tree
Showing 48 changed files with 471 additions and 101 deletions.
24 changes: 24 additions & 0 deletions src/librustc_ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,30 @@ impl Expr {
}
}

/// Is this expr either `N`, or `{ N }`.
///
/// If this is not the case, name resolution does not resolve `N` when using
/// `feature(min_const_generics)` as more complex expressions are not supported.
pub fn is_potential_trivial_const_param(&self) -> bool {
let this = if let ExprKind::Block(ref block, None) = self.kind {
if block.stmts.len() == 1 {
if let StmtKind::Expr(ref expr) = block.stmts[0].kind { expr } else { self }
} else {
self
}
} else {
self
};

if let ExprKind::Path(None, ref path) = this.kind {
if path.segments.len() == 1 && path.segments[0].args.is_none() {
return true;
}
}

false
}

pub fn to_bound(&self) -> Option<GenericBound> {
match &self.kind {
ExprKind::Path(None, path) => Some(GenericBound::Trait(
Expand Down
8 changes: 7 additions & 1 deletion src/librustc_ast_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,13 @@ fn validate_generic_param_order<'a>(
span,
&format!(
"reorder the parameters: lifetimes, then types{}",
if sess.features_untracked().const_generics { ", then consts" } else { "" },
if sess.features_untracked().const_generics
|| sess.features_untracked().min_const_generics
{
", then consts"
} else {
""
},
),
ordered_params.clone(),
Applicability::MachineApplicable,
Expand Down
7 changes: 4 additions & 3 deletions src/librustc_ast_passes/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,12 +526,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {

fn visit_generic_param(&mut self, param: &'a GenericParam) {
if let GenericParamKind::Const { .. } = param.kind {
gate_feature_post!(
gate_feature_fn!(
&self,
const_generics,
|x: &Features| x.const_generics || x.min_const_generics,
param.ident.span,
sym::min_const_generics,
"const generics are unstable"
)
);
}
visit::walk_generic_param(self, param)
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@ declare_features! (
/// Allows calling `transmute` in const fn
(active, const_fn_transmute, "1.46.0", Some(53605), None),

/// The smallest useful subset of `const_generics`.
(active, min_const_generics, "1.46.0", Some(74878), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_middle/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,9 @@ impl<'tcx> TyCtxt<'tcx> {
/// we still evaluate them eagerly.
#[inline]
pub fn lazy_normalization(self) -> bool {
self.features().const_generics || self.features().lazy_normalization_consts
let features = self.features();
// Note: We do not enable lazy normalization for `features.min_const_generics`.
features.const_generics || features.lazy_normalization_consts
}

#[inline]
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl<'a> Parser<'a> {
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;

self.sess.gated_spans.gate(sym::const_generics, const_span.to(self.prev_token.span));
self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span));

Ok(GenericParam {
ident,
Expand Down
17 changes: 17 additions & 0 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,23 @@ impl<'a> Resolver<'a> {
);
err
}
ResolutionError::ParamInNonTrivialAnonConst(name) => {
let mut err = self.session.struct_span_err(
span,
"generic parameters must not be used inside of non trivial constant values",
);
err.span_label(
span,
&format!(
"non-trivial anonymous constants must not depend on the parameter `{}`",
name
),
);
err.help(
&format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name)
);
err
}
ResolutionError::SelfInTyParamDefault => {
let mut err = struct_span_err!(
self.session,
Expand Down
40 changes: 27 additions & 13 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ crate enum RibKind<'a> {
ItemRibKind(HasGenericParams),

/// We're in a constant item. Can't refer to dynamic stuff.
ConstantItemRibKind,
ConstantItemRibKind(bool),

/// We passed through a module.
ModuleRibKind(Module<'a>),
Expand All @@ -137,7 +137,7 @@ impl RibKind<'_> {
NormalRibKind
| ClosureOrAsyncRibKind
| FnItemRibKind
| ConstantItemRibKind
| ConstantItemRibKind(_)
| ModuleRibKind(_)
| MacroDefinition(_)
| ConstParamTyRibKind => false,
Expand Down Expand Up @@ -426,7 +426,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
}
fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
debug!("visit_anon_const {:?}", constant);
self.with_constant_rib(|this| {
self.with_constant_rib(constant.value.is_potential_trivial_const_param(), |this| {
visit::walk_anon_const(this, constant);
});
}
Expand Down Expand Up @@ -628,7 +628,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
if !check_ns(TypeNS) && check_ns(ValueNS) {
// This must be equivalent to `visit_anon_const`, but we cannot call it
// directly due to visitor lifetimes so we have to copy-paste some code.
self.with_constant_rib(|this| {
self.with_constant_rib(true, |this| {
this.smart_resolve_path(
ty.id,
qself.as_ref(),
Expand Down Expand Up @@ -829,7 +829,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
| ClosureOrAsyncRibKind
| FnItemRibKind
| ItemRibKind(..)
| ConstantItemRibKind
| ConstantItemRibKind(_)
| ModuleRibKind(..)
| ForwardTyParamBanRibKind
| ConstParamTyRibKind => {
Expand Down Expand Up @@ -948,7 +948,14 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
// Only impose the restrictions of `ConstRibKind` for an
// actual constant expression in a provided default.
if let Some(expr) = default {
this.with_constant_rib(|this| this.visit_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_constant_rib(true, |this| {
this.visit_expr(expr)
});
}
}
AssocItemKind::Fn(_, _, generics, _) => {
Expand Down Expand Up @@ -989,7 +996,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.with_item_rib(HasGenericParams::No, |this| {
this.visit_ty(ty);
if let Some(expr) = expr {
this.with_constant_rib(|this| this.visit_expr(expr));
this.with_constant_rib(expr.is_potential_trivial_const_param(), |this| {
this.visit_expr(expr)
});
}
});
}
Expand Down Expand Up @@ -1086,11 +1095,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
}

fn with_constant_rib(&mut self, f: impl FnOnce(&mut Self)) {
fn with_constant_rib(&mut self, trivial: bool, f: impl FnOnce(&mut Self)) {
debug!("with_constant_rib");
self.with_rib(ValueNS, ConstantItemRibKind, |this| {
this.with_rib(TypeNS, ConstantItemRibKind, |this| {
this.with_label_rib(ConstantItemRibKind, f);
self.with_rib(ValueNS, ConstantItemRibKind(trivial), |this| {
this.with_rib(TypeNS, ConstantItemRibKind(trivial), |this| {
this.with_label_rib(ConstantItemRibKind(trivial), f);
})
});
}
Expand Down Expand Up @@ -1220,7 +1229,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
for item in impl_items {
use crate::ResolutionError::*;
match &item.kind {
AssocItemKind::Const(..) => {
AssocItemKind::Const(_default, _ty, _expr) => {
debug!("resolve_implementation AssocItemKind::Const",);
// If this is a trait impl, ensure the const
// exists in trait
Expand All @@ -1231,7 +1240,12 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|n, s| ConstNotMemberOfTrait(n, s),
);

this.with_constant_rib(|this| {
// 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_constant_rib(true, |this| {
visit::walk_assoc_item(this, item, AssocCtxt::Impl)
});
}
Expand Down
32 changes: 29 additions & 3 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ enum ResolutionError<'a> {
ParamInTyOfConstParam(Symbol),
/// constant values inside of type parameter defaults must not depend on generic parameters.
ParamInAnonConstInTyDefault(Symbol),
/// generic parameters must not be used inside of non trivial constant values.
///
/// This error is only emitted when using `min_const_generics`.
ParamInNonTrivialAnonConst(Symbol),
/// Error E0735: type parameters with a default cannot use `Self`
SelfInTyParamDefault,
/// Error E0767: use of unreachable label
Expand Down Expand Up @@ -2507,7 +2511,7 @@ impl<'a> Resolver<'a> {
res_err = Some(CannotCaptureDynamicEnvironmentInFnItem);
}
}
ConstantItemRibKind => {
ConstantItemRibKind(_) => {
// Still doesn't deal with upvars
if record_used {
self.report_error(span, AttemptToUseNonConstantValueInConstant);
Expand Down Expand Up @@ -2546,7 +2550,18 @@ impl<'a> Resolver<'a> {
in_ty_param_default = true;
continue;
}
ConstantItemRibKind => {
ConstantItemRibKind(trivial) => {
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
if !trivial && self.session.features_untracked().min_const_generics {
if record_used {
self.report_error(
span,
ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
);
}
return Res::Err;
}

if in_ty_param_default {
if record_used {
self.report_error(
Expand Down Expand Up @@ -2612,7 +2627,18 @@ impl<'a> Resolver<'a> {
in_ty_param_default = true;
continue;
}
ConstantItemRibKind => {
ConstantItemRibKind(trivial) => {
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
if !trivial && self.session.features_untracked().min_const_generics {
if record_used {
self.report_error(
span,
ResolutionError::ParamInNonTrivialAnonConst(rib_ident.name),
);
}
return Res::Err;
}

if in_ty_param_default {
if record_used {
self.report_error(
Expand Down
1 change: 1 addition & 0 deletions src/librustc_span/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ symbols! {
min_align_of,
min_align_of_val,
min_const_fn,
min_const_generics,
min_const_unsafe_fn,
min_specialization,
minnumf32,
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,9 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
// HACK(eddyb) this provides the correct generics when
// `feature(const_generics)` is enabled, so that const expressions
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
//
// Note that we do not supply the parent generics when using
// `feature(min_const_generics)`.
Some(parent_def_id.to_def_id())
} else {
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
Expand Down
44 changes: 31 additions & 13 deletions src/librustc_typeck/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,21 +326,39 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
GenericParamKind::Const { ty: ref hir_ty, .. } => {
let ty = icx.to_ty(hir_ty);
let err = match ty.peel_refs().kind {
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => None,
let err_ty_str;
let err = if tcx.features().min_const_generics {
match ty.kind {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => {
err_ty_str = format!("`{}`", ty);
Some(err_ty_str.as_str())
}
}
} else {
match ty.peel_refs().kind {
ty::FnPtr(_) => Some("function pointers"),
ty::RawPtr(_) => Some("raw pointers"),
_ => None,
}
};
if let Some(unsupported_type) = err {
tcx.sess
.struct_span_err(
hir_ty.span,
&format!(
"using {} as const generic parameters is forbidden",
unsupported_type
),
)
.emit();
let mut err = tcx.sess.struct_span_err(
hir_ty.span,
&format!(
"using {} as const generic parameters is forbidden",
unsupported_type
),
);

if tcx.features().min_const_generics {
err.note("the only supported types are integers, `bool` and `char`")
.note("more complex types are supported with `#[feature(const_generics)]`").emit()
} else {
err.emit();
}
};
if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
.is_some()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ error[E0658]: const generics are unstable
LL | trait Trait<const T: ()> {}
| ^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics)]` to the crate attributes to enable
= note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
= help: add `#![feature(min_const_generics)]` to the crate attributes to enable

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ error[E0658]: const generics are unstable
LL | struct B<T, const N: T>(PhantomData<[T; N]>);
| ^
|
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
= help: add `#![feature(const_generics)]` to the crate attributes to enable
= note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
= help: add `#![feature(min_const_generics)]` to the crate attributes to enable

error: aborting due to 2 previous errors

Expand Down
Loading

0 comments on commit f9c2177

Please sign in to comment.