Skip to content

Commit

Permalink
Rollup merge of #74159 - lcnr:const-generic-ty-decl, r=varkor
Browse files Browse the repository at this point in the history
forbid generic params in the type of const params

implements and closes #74152

fixes #74101, closes #71169, fixes #73491, closes #62878

@eddyb and I talked [on zulip](https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/type.20of.20const.20parameters/near/203405696) about this and we probably want to also forbid generic consts in the default
type of a parameter, e.g. `struct Foo<T, U = [u8; std::mem::size_of::<T>()]>`, this is currently still allowed
and I will probably fix that in a followup PR.

r? @varkor @eddyb
  • Loading branch information
Manishearth authored Jul 16, 2020
2 parents 61a3f67 + 09ba0bd commit 6ef0dfa
Show file tree
Hide file tree
Showing 22 changed files with 271 additions and 36 deletions.
1 change: 1 addition & 0 deletions src/librustc_error_codes/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ E0766: include_str!("./error_codes/E0766.md"),
E0767: include_str!("./error_codes/E0767.md"),
E0768: include_str!("./error_codes/E0768.md"),
E0769: include_str!("./error_codes/E0769.md"),
E0770: include_str!("./error_codes/E0770.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_error_codes/error_codes/E0671.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Const parameters cannot depend on type parameters.
The following is therefore invalid:

```compile_fail,E0741
```compile_fail,E0770
#![feature(const_generics)]
fn const_id<T, const N: T>() -> T { // error
Expand Down
15 changes: 15 additions & 0 deletions src/librustc_error_codes/error_codes/E0770.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
The type of a const parameter references other generic parameters.

Erroneous code example:

```compile_fail,E0770
#![feature(const_generics)]
fn foo<T, const N: T>() {} // error!
```

To fix this error, use a concrete type for the const parameter:

```
#![feature(const_generics)]
fn foo<T, const N: usize>() {}
```
6 changes: 3 additions & 3 deletions src/librustc_hir/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2687,7 +2687,7 @@ pub enum Node<'hir> {
Crate(&'hir CrateItem<'hir>),
}

impl Node<'_> {
impl<'hir> Node<'hir> {
pub fn ident(&self) -> Option<Ident> {
match self {
Node::TraitItem(TraitItem { ident, .. })
Expand All @@ -2698,7 +2698,7 @@ impl Node<'_> {
}
}

pub fn fn_decl(&self) -> Option<&FnDecl<'_>> {
pub fn fn_decl(&self) -> Option<&FnDecl<'hir>> {
match self {
Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
| Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(fn_sig, _), .. })
Expand All @@ -2722,7 +2722,7 @@ impl Node<'_> {
}
}

pub fn generics(&self) -> Option<&Generics<'_>> {
pub fn generics(&self) -> Option<&'hir Generics<'hir>> {
match self {
Node::TraitItem(TraitItem { generics, .. })
| Node::ImplItem(ImplItem { generics, .. }) => Some(generics),
Expand Down
13 changes: 13 additions & 0 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,19 @@ impl<'a> Resolver<'a> {
);
err
}
ResolutionError::ParamInTyOfConstArg(name) => {
let mut err = struct_span_err!(
self.session,
span,
E0770,
"the type of const parameters must not depend on other generic parameters"
);
err.span_label(
span,
format!("the type must not depend on the parameter `{}`", name),
);
err
}
ResolutionError::SelfInTyParamDefault => {
let mut err = struct_span_err!(
self.session,
Expand Down
14 changes: 12 additions & 2 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ crate enum RibKind<'a> {
/// from the default of a type parameter because they're not declared
/// before said type parameter. Also see the `visit_generics` override.
ForwardTyParamBanRibKind,

/// We are inside of the type of a const parameter. Can't refer to any
/// parameters.
ConstParamTyRibKind,
}

impl RibKind<'_> {
Expand All @@ -135,7 +139,8 @@ impl RibKind<'_> {
| FnItemRibKind
| ConstantItemRibKind
| ModuleRibKind(_)
| MacroDefinition(_) => false,
| MacroDefinition(_)
| ConstParamTyRibKind => false,
AssocItemRibKind | ItemRibKind(_) | ForwardTyParamBanRibKind => true,
}
}
Expand Down Expand Up @@ -576,7 +581,11 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
for bound in &param.bounds {
self.visit_param_bound(bound);
}
self.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
self.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
self.visit_ty(ty);
self.ribs[TypeNS].pop().unwrap();
self.ribs[ValueNS].pop().unwrap();
}
}
}
Expand Down Expand Up @@ -814,7 +823,8 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
| ItemRibKind(..)
| ConstantItemRibKind
| ModuleRibKind(..)
| ForwardTyParamBanRibKind => {
| ForwardTyParamBanRibKind
| ConstParamTyRibKind => {
return false;
}
}
Expand Down
34 changes: 33 additions & 1 deletion src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ enum ResolutionError<'a> {
BindingShadowsSomethingUnacceptable(&'static str, Symbol, &'a NameBinding<'a>),
/// Error E0128: type parameters with a default cannot use forward-declared identifiers.
ForwardDeclaredTyParam, // FIXME(const_generics:defaults)
/// ERROR E0770: the type of const parameters must not depend on other generic parameters.
ParamInTyOfConstArg(Symbol),
/// Error E0735: type parameters with a default cannot use `Self`
SelfInTyParamDefault,
/// Error E0767: use of unreachable label
Expand Down Expand Up @@ -2480,6 +2482,12 @@ impl<'a> Resolver<'a> {
}
return Res::Err;
}
ConstParamTyRibKind => {
if record_used {
self.report_error(span, ParamInTyOfConstArg(rib_ident.name));
}
return Res::Err;
}
}
}
if let Some(res_err) = res_err {
Expand All @@ -2503,6 +2511,15 @@ impl<'a> Resolver<'a> {
// This was an attempt to use a type parameter outside its scope.
ItemRibKind(has_generic_params) => has_generic_params,
FnItemRibKind => HasGenericParams::Yes,
ConstParamTyRibKind => {
if record_used {
self.report_error(
span,
ResolutionError::ParamInTyOfConstArg(rib_ident.name),
);
}
return Res::Err;
}
};

if record_used {
Expand All @@ -2527,9 +2544,24 @@ impl<'a> Resolver<'a> {
}
for rib in ribs {
let has_generic_params = match rib.kind {
NormalRibKind
| ClosureOrAsyncRibKind
| AssocItemRibKind
| ModuleRibKind(..)
| MacroDefinition(..)
| ForwardTyParamBanRibKind
| ConstantItemRibKind => continue,
ItemRibKind(has_generic_params) => has_generic_params,
FnItemRibKind => HasGenericParams::Yes,
_ => continue,
ConstParamTyRibKind => {
if record_used {
self.report_error(
span,
ResolutionError::ParamInTyOfConstArg(rib_ident.name),
);
}
return Res::Err;
}
};

// This was an attempt to use a const parameter outside its scope.
Expand Down
61 changes: 56 additions & 5 deletions src/librustc_typeck/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::weak_lang_items;
use rustc_hir::{GenericParamKind, Node};
use rustc_hir::{GenericParamKind, HirId, Node};
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::hir::map::Map;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
Expand Down Expand Up @@ -1155,6 +1155,35 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
}
}

struct AnonConstInParamListDetector {
in_param_list: bool,
found_anon_const_in_list: bool,
ct: HirId,
}

impl<'v> Visitor<'v> for AnonConstInParamListDetector {
type Map = intravisit::ErasedMap<'v>;

fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}

fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
let prev = self.in_param_list;
self.in_param_list = true;
intravisit::walk_generic_param(self, p);
self.in_param_list = prev;
}

fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
if self.in_param_list && self.ct == c.hir_id {
self.found_anon_const_in_list = true;
} else {
intravisit::walk_anon_const(self, c)
}
}
}

fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
use rustc_hir::*;

Expand All @@ -1176,10 +1205,32 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
let parent_id = tcx.hir().get_parent_item(hir_id);
let parent_def_id = tcx.hir().local_def_id(parent_id);

// 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.
if tcx.lazy_normalization() {
let mut in_param_list = false;
for (_parent, node) in tcx.hir().parent_iter(hir_id) {
if let Some(generics) = node.generics() {
let mut visitor = AnonConstInParamListDetector {
in_param_list: false,
found_anon_const_in_list: false,
ct: hir_id,
};

visitor.visit_generics(generics);
in_param_list = visitor.found_anon_const_in_list;
break;
}
}

if in_param_list {
// We do not allow generic parameters in anon consts if we are inside
// of a param list.
//
// This affects both default type bindings, e.g. `struct<T, U = [u8; std::mem::size_of::<T>()]>(T, U)`,
// and the types of const parameters, e.g. `struct V<const N: usize, const M: [u8; N]>();`.
None
} else if tcx.lazy_normalization() {
// 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.
Some(parent_def_id.to_def_id())
} else {
let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete

// Currently, const parameters cannot depend on other generic parameters,
// as our current implementation can't really support this.
//
// We may want to lift this restriction in the future.

pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
//~^ ERROR: the type of const parameters must not depend on other generic parameters

pub struct SelfDependent<const N: [u8; N]>;
//~^ ERROR: the type of const parameters must not depend on other generic parameters

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0770]: the type of const parameters must not depend on other generic parameters
--> $DIR/const-param-type-depends-on-const-param.rs:9:52
|
LL | pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
| ^ the type must not depend on the parameter `N`

error[E0770]: the type of const parameters must not depend on other generic parameters
--> $DIR/const-param-type-depends-on-const-param.rs:12:40
|
LL | pub struct SelfDependent<const N: [u8; N]>;
| ^ the type must not depend on the parameter `N`

warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/const-param-type-depends-on-const-param.rs:1:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information

error: aborting due to 2 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0770`.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::marker::PhantomData;

struct B<T, const N: T>(PhantomData<[T; N]>); //~ ERROR const generics are unstable
//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]`
//~^ ERROR the type of const parameters must not depend on other generic parameters

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
error[E0770]: the type of const parameters must not depend on other generic parameters
--> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22
|
LL | struct B<T, const N: T>(PhantomData<[T; N]>);
| ^ the type must not depend on the parameter `T`

error[E0658]: const generics are unstable
--> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:19
|
Expand All @@ -7,15 +13,7 @@ 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

error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter
--> $DIR/const-param-type-depends-on-type-param-ungated.rs:3:22
|
LL | struct B<T, const N: T>(PhantomData<[T; N]>);
| ^ `T` may not derive both `PartialEq` and `Eq`
|
= note: it is not currently possible to use a type parameter as the type of a const parameter

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0658, E0741.
Some errors have detailed explanations: E0658, E0770.
For more information about an error, try `rustc --explain E0658`.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![feature(const_generics)]
//~^ WARN the feature `const_generics` is incomplete

// Currently, const parameters cannot depend on type parameters, because there is no way to
// enforce the structural-match property on an arbitrary type parameter. This restriction
// may be relaxed in the future. See https://github.com/rust-lang/rfcs/pull/2000 for more
// details.
// Currently, const parameters cannot depend on other generic parameters,
// as our current implementation can't really support this.
//
// We may want to lift this restriction in the future.

pub struct Dependent<T, const X: T>([(); X]);
//~^ ERROR `T` is not guaranteed to `#[derive(PartialEq, Eq)]`
//~^ ERROR: the type of const parameters must not depend on other generic parameters
//~| ERROR: parameter `T` is never used

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
error[E0770]: the type of const parameters must not depend on other generic parameters
--> $DIR/const-param-type-depends-on-type-param.rs:9:34
|
LL | pub struct Dependent<T, const X: T>([(); X]);
| ^ the type must not depend on the parameter `T`

warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/const-param-type-depends-on-type-param.rs:1:12
|
Expand All @@ -7,14 +13,15 @@ LL | #![feature(const_generics)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information

error[E0741]: `T` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be used as the type of a const parameter
--> $DIR/const-param-type-depends-on-type-param.rs:9:34
error[E0392]: parameter `T` is never used
--> $DIR/const-param-type-depends-on-type-param.rs:9:22
|
LL | pub struct Dependent<T, const X: T>([(); X]);
| ^ `T` may not derive both `PartialEq` and `Eq`
| ^ unused parameter
|
= note: it is not currently possible to use a type parameter as the type of a const parameter
= help: consider removing `T`, referring to it in a field, or using a marker such as `std::marker::PhantomData`

error: aborting due to previous error; 1 warning emitted
error: aborting due to 2 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0741`.
Some errors have detailed explanations: E0392, E0770.
For more information about an error, try `rustc --explain E0392`.
Loading

0 comments on commit 6ef0dfa

Please sign in to comment.