Skip to content

Commit

Permalink
Auto merge of rust-lang#79270 - RalfJung:array-repeat-consts, r=oli-obk
Browse files Browse the repository at this point in the history
Acknowledge that `[CONST; N]` is stable

When `const_in_array_repeat_expressions` (RFC 2203) got unstably implemented as part of rust-lang#61749, accidentally, the special case of repeating a *constant* got stabilized immediately. That is why the following code works on stable:

```rust
const EMPTY: Vec<i32> = Vec::new();

pub const fn bar() -> [Vec<i32>; 2] {
    [EMPTY; 2]
}

fn main() {
    let x = bar();
}
```

In contrast, if we had written `[expr; 2]` for some expression that is not *literally* a constant but could be evaluated at compile-time (e.g. `(EMPTY,).0`), this would have failed.

We could take back this stabilization as it was clearly accidental. However, I propose we instead just officially accept this and stabilize a small subset of RFC 2203, while leaving the more complex case of general expressions that could be evaluated at compile-time unstable. Making that case work well is pretty much blocked on inline `const` expressions (to avoid relying too much on [implicit promotion](https://github.com/rust-lang/const-eval/blob/master/promotion.md)), so it could take a bit until it comes to full fruition. `[CONST; N]` is an uncontroversial subset of this feature that has no semantic ambiguities, does not rely on promotion, and basically provides the full expressive power of RFC 2203 but without the convenience (people have to define constants to repeat them, possibly using associated consts if generics are involved).

Well, I said "no semantic ambiguities", that is only almost true... the one point I am not sure about is `[CONST; 0]`. There are two possible behaviors here: either this is equivalent to `let x = CONST; [x; 0]`, or it is a NOP (if we argue that the constant is never actually instantiated). The difference between the two is that if `CONST` has a destructor, it should run in the former case (but currently doesn't, due to rust-lang#74836); but should not run if it is considered a NOP. For regular `[x; 0]` there seems to be consensus on running drop (there isn't really an alternative); any opinions for the `CONST` special case? Should this instantiate the const only to immediately run its destructors? That seems somewhat silly to me. After all, the `let`-expansion does *not* work in general, for `N > 1`.

Cc `@rust-lang/lang` `@rust-lang/wg-const-eval`
Cc rust-lang#49147
  • Loading branch information
bors committed Dec 21, 2020
2 parents 1e88a17 + 7f3e18c commit 11c94a1
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 37 deletions.
78 changes: 41 additions & 37 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1988,44 +1988,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// If the length is larger than 1, the repeat expression will need to copy the
// element, so we require the `Copy` trait.
if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
if let Operand::Move(_) = operand {
// While this is located in `nll::typeck` this error is not an NLL error, it's
// a required check to make sure that repeated elements implement `Copy`.
let span = body.source_info(location).span;
let ty = operand.ty(body, tcx);
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env);
// To determine if `const_in_array_repeat_expressions` feature gate should
// be mentioned, need to check if the rvalue is promotable.
let should_suggest =
should_suggest_const_in_array_repeat_expressions_attribute(
&ccx, operand,
);
debug!("check_rvalue: should_suggest={:?}", should_suggest);

let def_id = body.source.def_id().expect_local();
self.infcx.report_selection_error(
&traits::Obligation::new(
ObligationCause::new(
span,
self.tcx().hir().local_def_id_to_hir_id(def_id),
traits::ObligationCauseCode::RepeatVec(should_suggest),
),
self.param_env,
ty::Binder::bind(ty::TraitRef::new(
self.tcx().require_lang_item(
LangItem::Copy,
Some(self.last_span),
match operand {
Operand::Copy(..) | Operand::Constant(..) => {
// These are always okay: direct use of a const, or a value that can evidently be copied.
}
Operand::Move(_) => {
// Make sure that repeated elements implement `Copy`.
let span = body.source_info(location).span;
let ty = operand.ty(body, tcx);
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env);
// To determine if `const_in_array_repeat_expressions` feature gate should
// be mentioned, need to check if the rvalue is promotable.
let should_suggest =
should_suggest_const_in_array_repeat_expressions_attribute(
&ccx, operand,
);
debug!("check_rvalue: should_suggest={:?}", should_suggest);

let def_id = body.source.def_id().expect_local();
self.infcx.report_selection_error(
&traits::Obligation::new(
ObligationCause::new(
span,
self.tcx().hir().local_def_id_to_hir_id(def_id),
traits::ObligationCauseCode::RepeatVec(should_suggest),
),
tcx.mk_substs_trait(ty, &[]),
))
.without_const()
.to_predicate(self.tcx()),
),
&traits::SelectionError::Unimplemented,
false,
false,
);
self.param_env,
ty::Binder::bind(ty::TraitRef::new(
self.tcx().require_lang_item(
LangItem::Copy,
Some(self.last_span),
),
tcx.mk_substs_trait(ty, &[]),
))
.without_const()
.to_predicate(self.tcx()),
),
&traits::SelectionError::Unimplemented,
false,
false,
);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// run-pass

// Repeating a *constant* of non-Copy type (not just a constant expression) is already stable.

const EMPTY: Vec<i32> = Vec::new();

pub fn bar() -> [Vec<i32>; 2] {
[EMPTY; 2]
}

struct Bomb;

impl Drop for Bomb {
fn drop(&mut self) {
panic!("BOOM!");
}
}

const BOOM: Bomb = Bomb;

fn main() {
let _x = bar();

// Make sure the destructor does not get called for empty arrays. `[CONST; N]` should
// instantiate (and then later drop) the const exactly `N` times.
let _x = [BOOM; 0];
}

0 comments on commit 11c94a1

Please sign in to comment.