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 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
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 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
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,
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();
Expand Down
126 changes: 101 additions & 25 deletions src/librustc_resolve/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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>),
Expand All @@ -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,
}
Expand Down Expand Up @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -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(),
Expand All @@ -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);
});
}
}
}
}
Expand Down Expand Up @@ -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(_, _) => {
Expand Down Expand Up @@ -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);
});
});
Expand Down Expand Up @@ -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);
});
});
}

Expand Down Expand Up @@ -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",
);
Expand All @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
Loading