1
Fork 0

Auto merge of #95819 - oli-obk:mir_can't_hold_all_these_lifetimes, r=estebank

Enforce Copy bounds for repeat elements while considering lifetimes

fixes https://github.com/rust-lang/rust/issues/95477

this is a breaking change in order to fix a soundness bug.

Before this PR we only checked whether the repeat element type had an `impl Copy`, but not whether that impl also had the appropriate lifetimes. E.g. if the impl was for `YourType<'static>` and not a general `'a`, then copying any type other than a `'static` one should have been rejected, but wasn't.

r? `@lcnr`
This commit is contained in:
bors 2022-04-29 20:00:47 +00:00
commit a707f40107
24 changed files with 183 additions and 96 deletions

View file

@ -37,18 +37,13 @@ use rustc_middle::ty::{
use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::VariantIdx; use rustc_target::abi::VariantIdx;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use rustc_trait_selection::traits::query::Fallible; use rustc_trait_selection::traits::query::Fallible;
use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; use rustc_trait_selection::traits::PredicateObligation;
use rustc_const_eval::transform::{
check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression,
};
use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor; use rustc_mir_dataflow::ResultsCursor;
@ -1868,41 +1863,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
Operand::Move(place) => { Operand::Move(place) => {
// Make sure that repeated elements implement `Copy`. // Make sure that repeated elements implement `Copy`.
let span = body.source_info(location).span; let span = body.source_info(location).span;
let ty = operand.ty(body, tcx); let ty = place.ty(body, tcx).ty;
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { let trait_ref = ty::TraitRef::new(
let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); tcx.require_lang_item(LangItem::Copy, Some(span)),
let is_const_fn = tcx.mk_substs_trait(ty, &[]),
is_const_fn_in_array_repeat_expression(&ccx, &place, &body); );
debug!("check_rvalue: is_const_fn={:?}", is_const_fn); self.prove_trait_ref(
trait_ref,
let def_id = body.source.def_id().expect_local(); Locations::Single(location),
let obligation = traits::Obligation::new( ConstraintCategory::CopyBound,
ObligationCause::new( );
span,
self.tcx().hir().local_def_id_to_hir_id(def_id),
traits::ObligationCauseCode::RepeatElementCopy {
is_const_fn,
},
),
self.param_env,
ty::Binder::dummy(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()),
);
self.infcx.report_selection_error(
obligation.clone(),
&obligation,
&traits::SelectionError::Unimplemented,
false,
);
}
} }
} }
} }

View file

@ -2227,7 +2227,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
} }
ObligationCauseCode::RepeatElementCopy { is_const_fn } => { ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
err.note( err.note(
"the `Copy` trait is required because the repeated element will be copied", "the `Copy` trait is required because this value will be copied for each element of the array",
); );
if is_const_fn { if is_const_fn {

View file

@ -1292,9 +1292,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return tcx.ty_error(); return tcx.ty_error();
} }
self.check_repeat_element_needs_copy_bound(element, count, element_ty);
tcx.mk_ty(ty::Array(t, count)) tcx.mk_ty(ty::Array(t, count))
} }
fn check_repeat_element_needs_copy_bound(
&self,
element: &hir::Expr<'_>,
count: ty::Const<'tcx>,
element_ty: Ty<'tcx>,
) {
let tcx = self.tcx;
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
match &element.kind {
hir::ExprKind::ConstBlock(..) => return,
hir::ExprKind::Path(qpath) => {
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
{
return;
}
}
_ => {}
}
// If someone calls a const fn, they can extract that call out into a separate constant (or a const
// block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
let is_const_fn = match element.kind {
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
_ => false,
},
_ => false,
};
// If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
// don't copy that one element, we move it. Only check for Copy if the length is larger.
if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
self.require_type_meets(element_ty, element.span, code, lang_item);
}
}
fn check_expr_tuple( fn check_expr_tuple(
&self, &self,
elts: &'tcx [hir::Expr<'tcx>], elts: &'tcx [hir::Expr<'tcx>],

View file

@ -1,22 +1,22 @@
error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
--> $DIR/repeat_empty_ok.rs:8:19 --> $DIR/repeat_empty_ok.rs:8:20
| |
LL | let headers = [Header{value: &[]}; 128]; LL | let headers = [Header{value: &[]}; 128];
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>` | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Header<'_>` with `#[derive(Copy)]` help: consider annotating `Header<'_>` with `#[derive(Copy)]`
| |
LL | #[derive(Copy)] LL | #[derive(Copy)]
| |
error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
--> $DIR/repeat_empty_ok.rs:13:19 --> $DIR/repeat_empty_ok.rs:13:20
| |
LL | let headers = [Header{value: &[0]}; 128]; LL | let headers = [Header{value: &[0]}; 128];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>` | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Header<'_>` with `#[derive(Copy)]` help: consider annotating `Header<'_>` with `#[derive(Copy)]`
| |
LL | #[derive(Copy)] LL | #[derive(Copy)]

View file

@ -1,10 +1,10 @@
error[E0277]: the trait bound `T: Copy` is not satisfied error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/issue-61336-2.rs:6:5 --> $DIR/issue-61336-2.rs:6:6
| |
LL | [x; { N }] LL | [x; { N }]
| ^^^^^^^^^^ the trait `Copy` is not implemented for `T` | ^ the trait `Copy` is not implemented for `T`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider restricting type parameter `T` help: consider restricting type parameter `T`
| |
LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {

View file

@ -1,10 +1,10 @@
error[E0277]: the trait bound `T: Copy` is not satisfied error[E0277]: the trait bound `T: Copy` is not satisfied
--> $DIR/issue-61336.rs:6:5 --> $DIR/issue-61336.rs:6:6
| |
LL | [x; N] LL | [x; N]
| ^^^^^^ the trait `Copy` is not implemented for `T` | ^ the trait `Copy` is not implemented for `T`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider restricting type parameter `T` help: consider restricting type parameter `T`
| |
LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] { LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {

View file

@ -12,5 +12,5 @@ const fn copy() -> u32 {
fn main() { fn main() {
let _: [u32; 2] = [copy(); 2]; let _: [u32; 2] = [copy(); 2];
let _: [Option<Bar>; 2] = [no_copy(); 2]; let _: [Option<Bar>; 2] = [no_copy(); 2];
//~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied //~^ ERROR the trait bound `Bar: Copy` is not satisfied
} }

View file

@ -1,13 +1,17 @@
error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied error[E0277]: the trait bound `Bar: Copy` is not satisfied
--> $DIR/fn-call-in-non-const.rs:14:31 --> $DIR/fn-call-in-non-const.rs:14:32
| |
LL | let _: [Option<Bar>; 2] = [no_copy(); 2]; LL | let _: [Option<Bar>; 2] = [no_copy(); 2];
| ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<Bar>` | ^^^^^^^^^ the trait `Copy` is not implemented for `Bar`
| |
= help: the trait `Copy` is implemented for `Option<T>` = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
help: consider annotating `Bar` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|
error: aborting due to previous error error: aborting due to previous error

View file

@ -11,13 +11,13 @@ mod non_constants {
fn no_impl_copy_empty_value_multiple_elements() { fn no_impl_copy_empty_value_multiple_elements() {
let x = None; let x = None;
let arr: [Option<Bar>; 2] = [x; 2]; let arr: [Option<Bar>; 2] = [x; 2];
//~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277] //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
} }
fn no_impl_copy_value_multiple_elements() { fn no_impl_copy_value_multiple_elements() {
let x = Some(Bar); let x = Some(Bar);
let arr: [Option<Bar>; 2] = [x; 2]; let arr: [Option<Bar>; 2] = [x; 2];
//~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277] //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
} }
} }

View file

@ -1,20 +1,28 @@
error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied error[E0277]: the trait bound `Bar: Copy` is not satisfied
--> $DIR/migrate-fail.rs:13:37 --> $DIR/migrate-fail.rs:13:38
| |
LL | let arr: [Option<Bar>; 2] = [x; 2]; LL | let arr: [Option<Bar>; 2] = [x; 2];
| ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>` | ^ the trait `Copy` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
= note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Bar` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
| |
= help: the trait `Copy` is implemented for `Option<T>`
= note: the `Copy` trait is required because the repeated element will be copied
error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied error[E0277]: the trait bound `Bar: Copy` is not satisfied
--> $DIR/migrate-fail.rs:19:37 --> $DIR/migrate-fail.rs:19:38
| |
LL | let arr: [Option<Bar>; 2] = [x; 2]; LL | let arr: [Option<Bar>; 2] = [x; 2];
| ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>` | ^ the trait `Copy` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
= note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Bar` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
| |
= help: the trait `Copy` is implemented for `Option<T>`
= note: the `Copy` trait is required because the repeated element will be copied
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -10,13 +10,13 @@ mod non_constants {
fn no_impl_copy_empty_value_multiple_elements() { fn no_impl_copy_empty_value_multiple_elements() {
let x = None; let x = None;
let arr: [Option<Bar>; 2] = [x; 2]; let arr: [Option<Bar>; 2] = [x; 2];
//~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277] //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
} }
fn no_impl_copy_value_multiple_elements() { fn no_impl_copy_value_multiple_elements() {
let x = Some(Bar); let x = Some(Bar);
let arr: [Option<Bar>; 2] = [x; 2]; let arr: [Option<Bar>; 2] = [x; 2];
//~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277] //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
} }
} }

View file

@ -1,20 +1,28 @@
error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied error[E0277]: the trait bound `Bar: Copy` is not satisfied
--> $DIR/nll-fail.rs:12:37 --> $DIR/nll-fail.rs:12:38
| |
LL | let arr: [Option<Bar>; 2] = [x; 2]; LL | let arr: [Option<Bar>; 2] = [x; 2];
| ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>` | ^ the trait `Copy` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
= note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Bar` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
| |
= help: the trait `Copy` is implemented for `Option<T>`
= note: the `Copy` trait is required because the repeated element will be copied
error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied error[E0277]: the trait bound `Bar: Copy` is not satisfied
--> $DIR/nll-fail.rs:18:37 --> $DIR/nll-fail.rs:18:38
| |
LL | let arr: [Option<Bar>; 2] = [x; 2]; LL | let arr: [Option<Bar>; 2] = [x; 2];
| ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>` | ^ the trait `Copy` is not implemented for `Bar`
|
= note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
= note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Bar` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
| |
= help: the trait `Copy` is implemented for `Option<T>`
= note: the `Copy` trait is required because the repeated element will be copied
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -3,5 +3,5 @@ struct Foo<T>(T);
fn main() { fn main() {
[Foo(String::new()); 4]; [Foo(String::new()); 4];
//~^ ERROR the trait bound `Foo<String>: Copy` is not satisfied [E0277] //~^ ERROR the trait bound `String: Copy` is not satisfied [E0277]
} }

View file

@ -1,11 +1,18 @@
error[E0277]: the trait bound `Foo<String>: Copy` is not satisfied error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/trait-error.rs:5:5 --> $DIR/trait-error.rs:5:6
| |
LL | [Foo(String::new()); 4]; LL | [Foo(String::new()); 4];
| ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo<String>` | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
| |
= help: the trait `Copy` is implemented for `Foo<T>` note: required because of the requirements on the impl of `Copy` for `Foo<String>`
= note: the `Copy` trait is required because the repeated element will be copied --> $DIR/trait-error.rs:1:10
|
LL | #[derive(Copy, Clone)]
| ^^^^
= note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error error: aborting due to previous error

View file

@ -1,10 +1,10 @@
error[E0277]: the trait bound `String: Copy` is not satisfied error[E0277]: the trait bound `String: Copy` is not satisfied
--> $DIR/const-fn-in-vec.rs:4:32 --> $DIR/const-fn-in-vec.rs:4:33
| |
LL | let strings: [String; 5] = [String::new(); 5]; LL | let strings: [String; 5] = [String::new(); 5];
| ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];` = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information

View file

@ -0,0 +1,19 @@
#![feature(nll)]
#[derive(Clone)]
struct Foo<'a>(fn(&'a ()) -> &'a ());
impl Copy for Foo<'static> {}
fn mk_foo<'a>() -> Foo<'a> {
println!("mk_foo");
Foo(|x| x)
}
fn foo<'a>() -> [Foo<'a>; 100] {
[mk_foo::<'a>(); 100] //~ ERROR lifetime may not live long enough
}
fn main() {
foo();
}

View file

@ -0,0 +1,14 @@
error: lifetime may not live long enough
--> $DIR/copy_modulo_regions.rs:14:5
|
LL | fn foo<'a>() -> [Foo<'a>; 100] {
| -- lifetime `'a` defined here
LL | [mk_foo::<'a>(); 100]
| ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant
= note: the struct `Foo<'a>` is invariant over the parameter `'a`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to previous error

View file

@ -0,0 +1,16 @@
// check-pass
#[derive(Clone, Default)]
struct MaybeCopy<T>(T);
impl Copy for MaybeCopy<u8> {}
fn is_copy<T: Copy>(x: T) {
println!("{}", std::any::type_name::<T>());
}
fn main() {
is_copy(MaybeCopy::default());
[MaybeCopy::default(); 13];
// didn't work, because `Copy` was only checked in the mir
}

View file

@ -1,10 +1,10 @@
error[E0277]: the trait bound `Foo: Copy` is not satisfied error[E0277]: the trait bound `Foo: Copy` is not satisfied
--> $DIR/repeat-to-run-dtor-twice.rs:17:13 --> $DIR/repeat-to-run-dtor-twice.rs:17:15
| |
LL | let _ = [ a; 5 ]; LL | let _ = [ a; 5 ];
| ^^^^^^^^ the trait `Copy` is not implemented for `Foo` | ^ the trait `Copy` is not implemented for `Foo`
| |
= note: the `Copy` trait is required because the repeated element will be copied = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Foo` with `#[derive(Copy)]` help: consider annotating `Foo` with `#[derive(Copy)]`
| |
LL | #[derive(Copy)] LL | #[derive(Copy)]

View file

@ -7,7 +7,7 @@ use std::path::Path;
const ENTRY_LIMIT: usize = 1000; const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually. // FIXME: The following limits should be reduced eventually.
const ROOT_ENTRY_LIMIT: usize = 980; const ROOT_ENTRY_LIMIT: usize = 977;
const ISSUES_ENTRY_LIMIT: usize = 2278; const ISSUES_ENTRY_LIMIT: usize = 2278;
fn check_entries(path: &Path, bad: &mut bool) { fn check_entries(path: &Path, bad: &mut bool) {