Permit mutable references in all const contexts
This commit is contained in:
parent
1986b58c64
commit
d118021f8b
35 changed files with 298 additions and 171 deletions
|
@ -268,43 +268,41 @@ impl NonConstOp for CellBorrow {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// This op is for `&mut` borrows in the trailing expression of a constant
|
||||
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
|
||||
/// static or const items.
|
||||
pub struct MutBorrow(pub hir::BorrowKind);
|
||||
|
||||
impl NonConstOp for MutBorrow {
|
||||
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
|
||||
// Forbid everywhere except in const fn with a feature gate
|
||||
if ccx.const_kind() == hir::ConstContext::ConstFn {
|
||||
Status::Unstable(sym::const_mut_refs)
|
||||
} else {
|
||||
Status::Forbidden
|
||||
match ccx.const_kind() {
|
||||
// Mutable statics can handle mutable references in their final value
|
||||
hir::ConstContext::Static(hir::Mutability::Mut) => Status::Allowed,
|
||||
_ => Status::Forbidden,
|
||||
}
|
||||
}
|
||||
|
||||
fn importance(&self) -> DiagnosticImportance {
|
||||
// If there were primary errors (like non-const function calls), do not emit further
|
||||
// errors about mutable references.
|
||||
DiagnosticImportance::Secondary
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
|
||||
let raw = match self.0 {
|
||||
hir::BorrowKind::Raw => "raw ",
|
||||
hir::BorrowKind::Ref => "",
|
||||
};
|
||||
|
||||
let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
|
||||
feature_err(
|
||||
&ccx.tcx.sess.parse_sess,
|
||||
sym::const_mut_refs,
|
||||
span,
|
||||
&format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
|
||||
)
|
||||
} else {
|
||||
let mut err = struct_span_err!(
|
||||
ccx.tcx.sess,
|
||||
span,
|
||||
E0764,
|
||||
"{}mutable references are not allowed in {}s",
|
||||
raw,
|
||||
ccx.const_kind(),
|
||||
);
|
||||
err.span_label(span, format!("`&{}mut` is only allowed in `const fn`", raw));
|
||||
err
|
||||
};
|
||||
let mut err = struct_span_err!(
|
||||
ccx.tcx.sess,
|
||||
span,
|
||||
E0764,
|
||||
"{}mutable references are not allowed in final value of {}s",
|
||||
raw,
|
||||
ccx.const_kind(),
|
||||
);
|
||||
|
||||
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
|
||||
err.note(
|
||||
"References in statics and constants may only refer \
|
||||
|
@ -321,6 +319,29 @@ impl NonConstOp for MutBorrow {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TransientMutBorrow(pub hir::BorrowKind);
|
||||
|
||||
impl NonConstOp for TransientMutBorrow {
|
||||
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
|
||||
Status::Unstable(sym::const_mut_refs)
|
||||
}
|
||||
|
||||
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
|
||||
let raw = match self.0 {
|
||||
hir::BorrowKind::Raw => "raw ",
|
||||
hir::BorrowKind::Ref => "",
|
||||
};
|
||||
|
||||
feature_err(
|
||||
&ccx.tcx.sess.parse_sess,
|
||||
sym::const_mut_refs,
|
||||
span,
|
||||
&format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MutDeref;
|
||||
impl NonConstOp for MutDeref {
|
||||
|
@ -329,7 +350,7 @@ impl NonConstOp for MutDeref {
|
|||
}
|
||||
|
||||
fn importance(&self) -> DiagnosticImportance {
|
||||
// Usually a side-effect of a `MutBorrow` somewhere.
|
||||
// Usually a side-effect of a `TransientMutBorrow` somewhere.
|
||||
DiagnosticImportance::Secondary
|
||||
}
|
||||
|
||||
|
|
|
@ -466,6 +466,29 @@ impl Validator<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
|
||||
match self.const_kind() {
|
||||
// In a const fn all borrows are transient or point to the places given via
|
||||
// references in the arguments (so we already checked them with
|
||||
// TransientMutBorrow/MutBorrow as appropriate).
|
||||
// The borrow checker guarantees that no new non-transient borrows are created.
|
||||
// NOTE: Once we have heap allocations during CTFE we need to figure out
|
||||
// how to prevent `const fn` to create long-lived allocations that point
|
||||
// to mutable memory.
|
||||
hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
|
||||
_ => {
|
||||
// Locals with StorageDead do not live beyond the evaluation and can
|
||||
// thus safely be borrowed without being able to be leaked to the final
|
||||
// value of the constant.
|
||||
if self.local_has_storage_dead(local) {
|
||||
self.check_op(ops::TransientMutBorrow(kind));
|
||||
} else {
|
||||
self.check_op(ops::MutBorrow(kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<'tcx> for Validator<'mir, 'tcx> {
|
||||
|
@ -562,15 +585,15 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
|
|||
|
||||
if !is_allowed {
|
||||
if let BorrowKind::Mut { .. } = kind {
|
||||
self.check_op(ops::MutBorrow(hir::BorrowKind::Ref));
|
||||
self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
|
||||
} else {
|
||||
self.check_op(ops::CellBorrow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rvalue::AddressOf(Mutability::Mut, _) => {
|
||||
self.check_op(ops::MutBorrow(hir::BorrowKind::Raw))
|
||||
Rvalue::AddressOf(Mutability::Mut, ref place) => {
|
||||
self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
|
||||
}
|
||||
|
||||
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue