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
Changes from all commits
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
@@ -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
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 item, static item, array length expression, or enum
discriminant cannot refer to type parameters.

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>()];
}
```

While enum discriminants cannot refer to regular type parameters, they can still
refer to the `Self` type parameter. Example:

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

Note that associated constants do not have this limitation and can refer to
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. These restrictions may be
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
@@ -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,
E0747,
"type parameters cannot appear within {}",
source.descr()
);
err.span_label(span, "type parameter");
err
}
ResolutionError::BindingShadowsSomethingUnacceptable(what_binding, name, binding) => {
let res = binding.res();
let shadows_what = res.descr();
126 changes: 101 additions & 25 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
@@ -78,6 +78,39 @@ 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,
/// A typeof expression, which is an unimplemented feature.
Typeof,
}

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",
AnonConstUsage::Typeof => "a typeof expression",
}
}
}

/// Does this the item (from the item rib scope) allow generic parameters?
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
crate enum HasGenericParams {
@@ -105,8 +138,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>),
@@ -125,7 +158,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,
}
@@ -381,10 +414,17 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
self.resolve_block(block);
}
fn visit_anon_const(&mut self, constant: &'tcx AnonConst) {
debug!("visit_anon_const {:?}", constant);
self.with_constant_rib(|this| {
visit::walk_anon_const(this, constant);
});
// All constants should be handled by their parents, so that the applicable `AnonConstUsage`
// of the constant can be assigned.
bug!("unhandled constant: {:?}", 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);
@@ -407,17 +447,29 @@ impl<'a, 'tcx> Visitor<'tcx> for LateResolutionVisitor<'a, '_> {
match ty.kind {
TyKind::Path(ref qself, ref path) => {
self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);
visit::walk_ty(self, ty);
}
TyKind::ImplicitSelf => {
let self_ty = Ident::with_dummy_span(kw::SelfUpper);
let res = self
.resolve_ident_in_lexical_scope(self_ty, TypeNS, Some(ty.id), ty.span)
.map_or(Res::Err, |d| d.res());
self.r.record_partial_res(ty.id, PartialRes::new(res));
visit::walk_ty(self, ty);
}
_ => (),
}
visit::walk_ty(self, ty);
TyKind::Array(ref element_ty, ref count) => {
self.visit_ty(element_ty);
self.with_constant_rib(AnonConstUsage::ArrayLength, |this| {
visit::walk_anon_const(this, count);
});
}
TyKind::Typeof(ref constant) => {
self.with_constant_rib(AnonConstUsage::Typeof, |this| {
visit::walk_anon_const(this, constant);
});
}
_ => visit::walk_ty(self, ty),
};
}
fn visit_poly_trait_ref(&mut self, tref: &'tcx PolyTraitRef, m: &'tcx TraitBoundModifier) {
self.smart_resolve_path(
@@ -560,9 +612,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(),
@@ -584,7 +634,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);
});
}
}
}
}
@@ -829,9 +883,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(_, _) => {
@@ -873,7 +930,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);
});
});
@@ -971,10 +1033,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);
});
});
}

@@ -1106,7 +1170,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",
);
@@ -1119,9 +1183,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
@@ -2019,6 +2089,12 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> {
}
});
}
ExprKind::Repeat(ref element, ref count) => {
self.visit_expr(element);
self.with_constant_rib(AnonConstUsage::ArrayLength, |this| {
visit::walk_anon_const(this, count);
});
}
_ => {
visit::walk_expr(self, expr);
}
Loading