Skip to content

Commit

Permalink
Rollup merge of #78224 - lcnr:repeat-expr, r=varkor
Browse files Browse the repository at this point in the history
min_const_generics: allow ty param in repeat expr

implements https://rust-lang.zulipchat.com/#narrow/stream/260443-project-const-generics/topic/repeat.20expressions

Even with `min_const_generics` active, now keeps resulting in future compat warnings instead of hard errors.
Const parameters, for example `[0; N + 1]`, still result in hard errors during resolve.
```rust
#![allow(dead_code)]

fn foo<T>() {
    [0; std::mem::size_of::<*mut T>()];
}

struct Foo<T>(T);

impl<T> Foo<T> {
    const ASSOC: usize = 4;

    fn test() {
        [0; Self::ASSOC];
    }
}
```

r? @varkor cc @petrochenkov
  • Loading branch information
JohnTitor authored Oct 29, 2020
2 parents 2008d1b + 83ecbb4 commit 270d2e0
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 39 deletions.
4 changes: 3 additions & 1 deletion compiler/rustc_hir/src/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ pub enum Res<Id = hir::HirId> {
/// ```rust
/// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] {} }
/// ```
/// We do however allow `Self` in repeat expression even if it is generic to not break code
/// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint.
///
/// FIXME(lazy_normalization_consts): Remove this bodge once this feature is stable.
/// FIXME(lazy_normalization_consts): Remove this bodge once that feature is stable.
SelfTy(Option<DefId> /* trait */, Option<(DefId, bool)> /* impl */),
ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]`

Expand Down
88 changes: 70 additions & 18 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ enum PatternSource {
FnParam,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum IsRepeatExpr {
No,
Yes,
}

impl PatternSource {
fn descr(self) -> &'static str {
match self {
Expand Down Expand Up @@ -437,10 +443,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
self.resolve_block(block);
}
fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
debug!("visit_anon_const {:?}", constant);
self.with_constant_rib(constant.value.is_potential_trivial_const_param(), |this| {
visit::walk_anon_const(this, constant);
});
// We deal with repeat expressions explicitly in `resolve_expr`.
self.resolve_anon_const(constant, IsRepeatExpr::No);
}
fn visit_expr(&mut self, expr: &'ast Expr) {
self.resolve_expr(expr, None);
Expand Down Expand Up @@ -647,7 +651,11 @@ impl<'a: 'ast, '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(true, |this| {
//
// Note that we might not be inside of an repeat expression here,
// but considering that `IsRepeatExpr` is only relevant for
// non-trivial constants this is doesn't matter.
self.with_constant_rib(IsRepeatExpr::No, true, |this| {
this.smart_resolve_path(
ty.id,
qself.as_ref(),
Expand Down Expand Up @@ -980,9 +988,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
//
// 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)
});
this.with_constant_rib(
IsRepeatExpr::No,
true,
|this| this.visit_expr(expr),
);
}
}
AssocItemKind::Fn(_, _, generics, _) => {
Expand Down Expand Up @@ -1023,7 +1033,9 @@ impl<'a: 'ast, '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(expr.is_potential_trivial_const_param(), |this| {
// We already forbid generic params because of the above item rib,
// so it doesn't matter whether this is a trivial constant.
this.with_constant_rib(IsRepeatExpr::No, true, |this| {
this.visit_expr(expr)
});
}
Expand Down Expand Up @@ -1122,12 +1134,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
}

fn with_constant_rib(&mut self, trivial: bool, f: impl FnOnce(&mut Self)) {
debug!("with_constant_rib");
self.with_rib(ValueNS, ConstantItemRibKind(trivial), |this| {
this.with_rib(TypeNS, ConstantItemRibKind(trivial), |this| {
this.with_label_rib(ConstantItemRibKind(trivial), f);
})
// HACK(min_const_generics,const_evaluatable_unchecked): We
// want to keep allowing `[0; std::mem::size_of::<*mut T>()]`
// with a future compat lint for now. We do this by adding an
// additional special case for repeat expressions.
//
// Note that we intentionally still forbid `[0; N + 1]` during
// name resolution so that we don't extend the future
// compat lint to new cases.
fn with_constant_rib(
&mut self,
is_repeat: IsRepeatExpr,
is_trivial: bool,
f: impl FnOnce(&mut Self),
) {
debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial);
self.with_rib(ValueNS, ConstantItemRibKind(is_trivial), |this| {
this.with_rib(
TypeNS,
ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial),
|this| {
this.with_label_rib(ConstantItemRibKind(is_trivial), f);
},
)
});
}

Expand Down Expand Up @@ -1272,9 +1301,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
//
// 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)
});
this.with_constant_rib(
IsRepeatExpr::No,
true,
|this| {
visit::walk_assoc_item(
this,
item,
AssocCtxt::Impl,
)
},
);
}
AssocItemKind::Fn(_, _, generics, _) => {
// We also need a new scope for the impl item type parameters.
Expand Down Expand Up @@ -2199,6 +2236,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
debug!("(resolving block) leaving block");
}

fn resolve_anon_const(&mut self, constant: &'ast AnonConst, is_repeat: IsRepeatExpr) {
debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat);
self.with_constant_rib(
is_repeat,
constant.value.is_potential_trivial_const_param(),
|this| {
visit::walk_anon_const(this, constant);
},
);
}

fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
// First, record candidate traits for this expression if it could
// result in the invocation of a method call.
Expand Down Expand Up @@ -2322,6 +2370,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
ExprKind::Async(..) | ExprKind::Closure(..) => {
self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr));
}
ExprKind::Repeat(ref elem, ref ct) => {
self.visit_expr(elem);
self.resolve_anon_const(ct, IsRepeatExpr::Yes);
}
_ => {
visit::walk_expr(self, expr);
}
Expand Down
20 changes: 13 additions & 7 deletions src/test/ui/const-generics/issues/issue-62504.min.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
error: generic `Self` types are currently not permitted in anonymous constants
error[E0308]: mismatched types
--> $DIR/issue-62504.rs:19:21
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
|
= note: expected array `[u32; X]`
found array `[u32; _]`

error: constant expression depends on a generic parameter
--> $DIR/issue-62504.rs:19:25
|
LL | ArrayHolder([0; Self::SIZE])
| ^^^^^^^^^^
|
note: not a concrete type
--> $DIR/issue-62504.rs:17:22
|
LL | impl<const X: usize> ArrayHolder<X> {
| ^^^^^^^^^^^^^^
= note: this may fail depending on what value the parameter takes

error: aborting due to previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
4 changes: 2 additions & 2 deletions src/test/ui/const-generics/issues/issue-62504.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ struct ArrayHolder<const X: usize>([u32; X]);
impl<const X: usize> ArrayHolder<X> {
pub const fn new() -> Self {
ArrayHolder([0; Self::SIZE])
//[full]~^ ERROR constant expression depends on a generic parameter
//[min]~^^ ERROR generic `Self` types are currently
//~^ ERROR constant expression depends on a generic parameter
//[min]~| ERROR mismatched types
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/const-generics/issues/issue-67739.min.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error: generic parameters may not be used in const operations
--> $DIR/issue-67739.rs:12:30
error: constant expression depends on a generic parameter
--> $DIR/issue-67739.rs:12:15
|
LL | [0u8; mem::size_of::<Self::Associated>()];
| ^^^^^^^^^^^^^^^^ cannot perform const operation using `Self`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: type parameters may not be used in const expressions
= note: this may fail depending on what value the parameter takes

error: aborting due to previous error

3 changes: 1 addition & 2 deletions src/test/ui/const-generics/issues/issue-67739.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ pub trait Trait {

fn associated_size(&self) -> usize {
[0u8; mem::size_of::<Self::Associated>()];
//[full]~^ ERROR constant expression depends on a generic parameter
//[min]~^^ ERROR generic parameters may not be used in const operations
//~^ ERROR constant expression depends on a generic parameter
0
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![feature(min_const_generics)]

use std::mem::size_of;

fn test<const N: usize>() {}

fn ok<const M: usize>() -> [u8; M] {
Expand All @@ -22,6 +24,24 @@ fn break3<const N: usize>() {
//~^ ERROR generic parameters may not be used in const operations
}

struct BreakTy0<T>(T, [u8; { size_of::<*mut T>() }]);
//~^ ERROR generic parameters may not be used in const operations

struct BreakTy1<T>(T, [u8; { { size_of::<*mut T>() } }]);
//~^ ERROR generic parameters may not be used in const operations

fn break_ty2<T>() {
let _: [u8; size_of::<*mut T>() + 1];
//~^ ERROR generic parameters may not be used in const operations
}

fn break_ty3<T>() {
let _ = [0; size_of::<*mut T>() + 1];
//~^ WARN cannot use constants which depend on generic parameters in types
//~| WARN this was previously accepted by the compiler but is being phased out
}


trait Foo {
const ASSOC: usize;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,68 @@
error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:9:38
--> $DIR/complex-expression.rs:11:38
|
LL | struct Break0<const N: usize>([u8; { N + 1 }]);
| ^ cannot perform const operation using `N`
|
= help: const parameters may only be used as standalone arguments, i.e. `N`

error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:12:40
--> $DIR/complex-expression.rs:14:40
|
LL | struct Break1<const N: usize>([u8; { { N } }]);
| ^ cannot perform const operation using `N`
|
= help: const parameters may only be used as standalone arguments, i.e. `N`

error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:16:17
--> $DIR/complex-expression.rs:18:17
|
LL | let _: [u8; N + 1];
| ^ cannot perform const operation using `N`
|
= help: const parameters may only be used as standalone arguments, i.e. `N`

error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:21:17
--> $DIR/complex-expression.rs:23:17
|
LL | let _ = [0; N + 1];
| ^ cannot perform const operation using `N`
|
= help: const parameters may only be used as standalone arguments, i.e. `N`

error: aborting due to 4 previous errors
error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:27:45
|
LL | struct BreakTy0<T>(T, [u8; { size_of::<*mut T>() }]);
| ^ cannot perform const operation using `T`
|
= note: type parameters may not be used in const expressions

error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:30:47
|
LL | struct BreakTy1<T>(T, [u8; { { size_of::<*mut T>() } }]);
| ^ cannot perform const operation using `T`
|
= note: type parameters may not be used in const expressions

error: generic parameters may not be used in const operations
--> $DIR/complex-expression.rs:34:32
|
LL | let _: [u8; size_of::<*mut T>() + 1];
| ^ cannot perform const operation using `T`
|
= note: type parameters may not be used in const expressions

warning: cannot use constants which depend on generic parameters in types
--> $DIR/complex-expression.rs:39:17
|
LL | let _ = [0; size_of::<*mut T>() + 1];
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(const_evaluatable_unchecked)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// check-pass
#![feature(min_const_generics)]
#![allow(dead_code)]

fn foo<T>() {
[0; std::mem::size_of::<*mut T>()];
//~^ WARN cannot use constants which depend on generic parameters in types
//~| WARN this was previously accepted by the compiler but is being phased out
}

struct Foo<T>(T);

impl<T> Foo<T> {
const ASSOC: usize = 4;

fn test() {
let _ = [0; Self::ASSOC];
//~^ WARN cannot use constants which depend on generic parameters in types
//~| WARN this was previously accepted by the compiler but is being phased out
}
}

struct Bar<const N: usize>;

impl<const N: usize> Bar<N> {
const ASSOC: usize = 4;

fn test() {
let _ = [0; Self::ASSOC];
//~^ WARN cannot use constants which depend on generic parameters in types
//~| WARN this was previously accepted by the compiler but is being phased out
}
}

fn main() {}
Loading

0 comments on commit 270d2e0

Please sign in to comment.