Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow generic type parameters from appearing within certain constants #68356

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -415,6 +415,7 @@ E0743: include_str!("./error_codes/E0743.md"),
E0744: include_str!("./error_codes/E0744.md"),
E0745: include_str!("./error_codes/E0745.md"),
E0746: include_str!("./error_codes/E0746.md"),
E0747: include_str!("./error_codes/E0747.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
58 changes: 58 additions & 0 deletions src/librustc_error_codes/error_codes/E0747.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Any constant items, static items, array length expressions, or enum
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
discriminants can't refer to type parameters.
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved

Erroneous code example:

```compile_fail,E0747
use std::mem::size_of;

fn foo<T>() {
let _ = [0; size_of::<T>()];
}
```

A workaround for array length expressions is to use [`vec!`] instead, which
does support type parameters in it's length expression, though this does
perform dynamic memory allocation. Example:

```
use std::mem::size_of;

fn foo<T>() {
let _ = vec![0; size_of::<T>()];
}
```

Though enum discriminants can't refer to regular type parameters they can still
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
refer to the `Self` type parameter. Example:

```
#[repr(u8)]
enum Alpha {
V1 = 41,
V2 = Self::V1 as u8 + 1,
}
```

Note, associated constants do not have this limitation and they can refer to
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
type parameters. Example:

```
use std::mem::size_of;

trait Foo {
const X: i32;
}

struct Bar<T>(T);

impl<T> Foo for Bar<T> {
const X: i32 = size_of::<T>();
}
```

This is currently a limitation with the compiler and these restrictions may be
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
relaxed in the future, see [issue 43408] for more information.

[`vec!`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
[issue 43408]: https://github.com/rust-lang/rust/issues/43408
11 changes: 11 additions & 0 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,17 @@ impl<'a> Resolver<'a> {
err.span_label(span, "non-constant value");
err
}
ResolutionError::GenericParamsInConst(source) => {
let mut err = struct_span_err!(
self.session,
span,
E0447,
"type parameters can't appear within {}",
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
source.descr()
);
err.span_label(span, "type parameter");
err
}
ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => {
let res = binding.res();
let shadows_what = res.descr();
Expand Down
92 changes: 75 additions & 17 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,36 @@ enum PatBoundCtx {
Or,
}

/// Denotes the location/usage of an anonymous constant.
#[derive(Copy, Clone, PartialEq, Debug)]
crate enum AnonConstUsage {
/// constant item, e.g., `const _: u8 = X;`.
Constant,
/// static item, e.g., `static _: u8 = X;`.
Static,
/// an array length expression, e.g., `[...; X]`.
ArrayLength,
/// an enum discriminant value, e.g., `enum Enum { V = X, }`.
EnumDiscriminant,
/// an associated constant in an impl or trait, e.g., `impl A { const _: u8 = X; }`.
AssocConstant,
/// a const generic argument, e.g., `Struct<{X}>`.
GenericArg,
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
}

impl AnonConstUsage {
crate fn descr(self) -> &'static str {
match self {
AnonConstUsage::Constant => "a constant",
AnonConstUsage::Static => "a static",
AnonConstUsage::ArrayLength => "an array length expression",
AnonConstUsage::EnumDiscriminant => "an enum discriminant",
AnonConstUsage::AssocConstant => "an associated constant",
AnonConstUsage::GenericArg => "a const generic argument",
}
}
}

/// Does this the item (from the item rib scope) allow generic parameters?
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
crate enum HasGenericParams {
Expand Down Expand Up @@ -105,8 +135,8 @@ crate enum RibKind<'a> {
/// We passed through an item scope. Disallow upvars.
ItemRibKind(HasGenericParams),

/// We're in a constant item. Can't refer to dynamic stuff.
ConstantItemRibKind,
/// We're in a constant. Depending on it's usage, it may be able to refer to type parameters.
ConstantRibKind(AnonConstUsage),

/// We passed through a module.
ModuleRibKind(Module<'a>),
Expand All @@ -125,7 +155,7 @@ impl RibKind<'_> {
// variables.
crate fn contains_params(&self) -> bool {
match self {
NormalRibKind | FnItemRibKind | ConstantItemRibKind | ModuleRibKind(_)
NormalRibKind | FnItemRibKind | ConstantRibKind(_) | ModuleRibKind(_)
| MacroDefinition(_) => false,
AssocItemRibKind | ItemRibKind(_) | ForwardTyParamBanRibKind => true,
}
Expand Down Expand Up @@ -382,10 +412,18 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
}
fn visit_anon_const(&mut self, constant: &'tcx AnonConst) {
debug!("visit_anon_const {:?}", constant);
self.with_constant_rib(|this| {
self.with_constant_rib(AnonConstUsage::ArrayLength, |this| {
BenLewis-Seequent marked this conversation as resolved.
Show resolved Hide resolved
visit::walk_anon_const(this, constant);
});
}
fn visit_variant(&mut self, variant: &'tcx Variant) {
self.visit_variant_data(&variant.data);
if let Some(ref disr) = variant.disr_expr {
self.with_constant_rib(AnonConstUsage::EnumDiscriminant, |this| {
visit::walk_anon_const(this, disr);
});
}
}
fn visit_expr(&mut self, expr: &'tcx Expr) {
self.resolve_expr(expr, None);
}
Expand Down Expand Up @@ -562,7 +600,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
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(AnonConstUsage::GenericArg, |this| {
this.smart_resolve_path(
ty.id,
qself.as_ref(),
Expand All @@ -584,7 +622,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
self.visit_ty(ty);
}
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
GenericArg::Const(ct) => self.visit_anon_const(ct),
GenericArg::Const(ct) => {
self.with_constant_rib(AnonConstUsage::GenericArg, |this| {
visit::walk_anon_const(this, ct);
});
}
}
}
}
Expand Down Expand Up @@ -829,9 +871,12 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
// ConstRibKind for an actual constant
// expression in a provided default.
if let Some(ref expr) = *default {
this.with_constant_rib(|this| {
this.visit_expr(expr);
});
this.with_constant_rib(
AnonConstUsage::AssocConstant,
|this| {
this.visit_expr(expr);
},
);
}
}
AssocItemKind::Fn(_, _) => {
Expand Down Expand Up @@ -873,7 +918,12 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
debug!("resolve_item ItemKind::Const");
self.with_item_rib(HasGenericParams::No, |this| {
this.visit_ty(ty);
this.with_constant_rib(|this| {
let usage = if let ItemKind::Static(..) = item.kind {
AnonConstUsage::Static
} else {
AnonConstUsage::Constant
};
this.with_constant_rib(usage, |this| {
this.visit_expr(expr);
});
});
Expand Down Expand Up @@ -971,10 +1021,12 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
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, usage: AnonConstUsage, f: impl FnOnce(&mut Self)) {
debug!("with_constant_rib");
self.with_rib(ValueNS, ConstantItemRibKind, |this| {
this.with_label_rib(ConstantItemRibKind, f);
self.with_rib(ValueNS, ConstantRibKind(usage), |this| {
this.with_rib(TypeNS, ConstantRibKind(usage), |this| {
this.with_label_rib(ConstantRibKind(usage), f);
});
});
}

Expand Down Expand Up @@ -1106,7 +1158,7 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
|this| {
use crate::ResolutionError::*;
match impl_item.kind {
AssocItemKind::Const(..) => {
AssocItemKind::Const(ref ty, ref expr) => {
debug!(
"resolve_implementation AssocItemKind::Const",
);
Expand All @@ -1119,9 +1171,15 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
|n, s| ConstNotMemberOfTrait(n, s),
);

this.with_constant_rib(|this| {
visit::walk_impl_item(this, impl_item)
});
this.visit_ty(ty);

// Only impose the restrictions of ConstRibKind for
// the actual constant expression.
if let Some(expr) = expr.as_deref() {
this.with_constant_rib(AnonConstUsage::AssocConstant, |this| {
this.visit_expr(expr)
});
}
}
AssocItemKind::Fn(..) => {
// If this is a trait impl, ensure the method
Expand Down
57 changes: 52 additions & 5 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// ignore-tidy-filelength
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you split things in a follow up PR?

//! This crate is responsible for the part of name resolution that doesn't require type checker.
//!
//! Module structure of the crate is built here.
Expand Down Expand Up @@ -58,7 +59,7 @@ use std::{cmp, fmt, iter, ptr};
use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding};
use diagnostics::{ImportSuggestion, Suggestion};
use imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver, NameResolution};
use late::{HasGenericParams, PathSource, Rib, RibKind::*};
use late::{AnonConstUsage, HasGenericParams, PathSource, Rib, RibKind::*};
use macros::{LegacyBinding, LegacyScope};

use rustc_error_codes::*;
Expand Down Expand Up @@ -203,6 +204,8 @@ enum ResolutionError<'a> {
CannotCaptureDynamicEnvironmentInFnItem,
/// Error E0435: attempt to use a non-constant value in a constant.
AttemptToUseNonConstantValueInConstant,
/// Error E0747: type parameters can't appear within `{}`.
GenericParamsInConst(AnonConstUsage),
/// Error E0530: `X` bindings cannot shadow `Y`s.
BindingShadowsSomethingUnacceptable(&'a str, Name, &'a NameBinding<'a>),
/// Error E0128: type parameters with a default cannot use forward-declared identifiers.
Expand Down Expand Up @@ -2327,12 +2330,12 @@ impl<'a> Resolver<'a> {
if record_used {
// We don't immediately trigger a resolve error, because
// we want certain other resolution errors (namely those
// emitted for `ConstantItemRibKind` below) to take
// emitted for `ConstantRibKind` below) to take
// precedence.
res_err = Some(CannotCaptureDynamicEnvironmentInFnItem);
}
}
ConstantItemRibKind => {
ConstantRibKind(_) => {
// Still doesn't deal with upvars
if record_used {
self.report_error(span, AttemptToUseNonConstantValueInConstant);
Expand All @@ -2353,11 +2356,55 @@ impl<'a> Resolver<'a> {
| AssocItemRibKind
| ModuleRibKind(..)
| MacroDefinition(..)
| ForwardTyParamBanRibKind
| ConstantItemRibKind => {
| ForwardTyParamBanRibKind => {
// Nothing to do. Continue.
continue;
}
ConstantRibKind(usage) => {
if self.session.features_untracked().const_generics {
// With const generics/lazy normalization any constants can depend
// on generic parameters, including type parameters.
continue;
};
match usage {
AnonConstUsage::AssocConstant | AnonConstUsage::GenericArg => {
// Allowed to use any type parameters.
continue
},
// Enum discriminants can use `Self` to refer to other variants in
// the same enum, but can refer to any other type parameters.
AnonConstUsage::EnumDiscriminant
// In most cases array lengths can't refer to type parameters but
// previously some usages involving `Self` did compile. Example:
//
// impl Foo {
// const A: usize = 32;
//
// pub fn bar() {
// let _ = [0; Self::A];
// }
// }
//
// Though if `Foo` had a type parameter,
// e.g. `impl<T> Foo<T> { ... }` then is wouldn't compile with a
// confusing error message.
| AnonConstUsage::ArrayLength => {
if let Res::SelfTy(..) = res {
continue
}
}
// can't use any type parameters including `Self`.
AnonConstUsage::Constant | AnonConstUsage::Static => {}
}

if record_used {
self.report_error(
span,
ResolutionError::GenericParamsInConst(usage),
);
}
return Res::Err;
}
// This was an attempt to use a type parameter outside its scope.
ItemRibKind(has_generic_params) => has_generic_params,
FnItemRibKind => HasGenericParams::Yes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Foo for Def {

pub fn test<A: Foo, B: Foo>() {
let _array = [4; <A as Foo>::Y];
//~^ ERROR the trait bound `A: Foo` is not satisfied [E0277]
//~^ ERROR type parameters can't appear within an array length expression [E0447]
}

fn main() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
error[E0277]: the trait bound `A: Foo` is not satisfied
--> $DIR/associated-const-type-parameter-arrays-2.rs:16:22
error[E0447]: type parameters can't appear within an array length expression
--> $DIR/associated-const-type-parameter-arrays-2.rs:16:23
|
LL | const Y: usize;
| --------------- required by `Foo::Y`
...
LL | pub fn test<A: Foo, B: Foo>() {
| -- help: consider further restricting this bound: `A: Foo +`
LL | let _array = [4; <A as Foo>::Y];
| ^^^^^^^^^^^^^ the trait `Foo` is not implemented for `A`
| ^ type parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
For more information about this error, try `rustc --explain E0447`.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl Foo for Def {

pub fn test<A: Foo, B: Foo>() {
let _array: [u32; <A as Foo>::Y];
//~^ ERROR the trait bound `A: Foo` is not satisfied [E0277]
//~^ ERROR type parameters can't appear within an array length expression [E0447]
}

fn main() {
Expand Down
Loading