Skip to content

Commit 2b8116d

Browse files
committed
Auto merge of #63994 - Centril:refactor-qualify-consts, r=spastorino,oli-obk
Refactor the `MirPass for QualifyAndPromoteConstants` This is an accumulation of drive-by commits while working on `Vec::new` as a stable `const fn`. The PR is probably easiest read commit-by-commit. r? @oli-obk cc @eddyb @ecstatic-morse -- your two PRs #63812 and #63860 respectively will conflict with this a tiny bit but it should be trivial to reintegrate your changes atop of this.
2 parents 2c0931e + 0a8a3dd commit 2b8116d

File tree

1 file changed

+113
-126
lines changed

1 file changed

+113
-126
lines changed

src/librustc_mir/transform/qualify_consts.rs

+113-126
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ use syntax::feature_gate::{emit_feature_err, GateIssue};
2525
use syntax::symbol::sym;
2626
use syntax_pos::{Span, DUMMY_SP};
2727

28+
use std::borrow::Cow;
2829
use std::cell::Cell;
2930
use std::fmt;
3031
use std::ops::{Deref, Index, IndexMut};
3132
use std::usize;
3233

34+
use rustc::hir::HirId;
3335
use crate::transform::{MirPass, MirSource};
3436
use super::promote_consts::{self, Candidate, TempState};
3537

@@ -1596,51 +1598,24 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
15961598
}
15971599

15981600
let def_id = src.def_id();
1599-
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
1600-
let mut const_promoted_temps = None;
1601-
let mode = match tcx.hir().body_owner_kind(id) {
1602-
hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1603-
hir::BodyOwnerKind::Fn => {
1604-
if tcx.is_const_fn(def_id) {
1605-
Mode::ConstFn
1606-
} else {
1607-
Mode::NonConstFn
1608-
}
1609-
}
1610-
hir::BodyOwnerKind::Const => {
1611-
const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1612-
Mode::Const
1613-
}
1614-
hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1615-
hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1616-
};
1601+
let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1602+
1603+
let mode = determine_mode(tcx, hir_id, def_id);
16171604

16181605
debug!("run_pass: mode={:?}", mode);
1619-
if mode == Mode::NonConstFn || mode == Mode::ConstFn {
1606+
if let Mode::NonConstFn | Mode::ConstFn = mode {
16201607
// This is ugly because Checker holds onto mir,
16211608
// which can't be mutated until its scope ends.
16221609
let (temps, candidates) = {
16231610
let mut checker = Checker::new(tcx, def_id, body, mode);
1624-
if mode == Mode::ConstFn {
1611+
if let Mode::ConstFn = mode {
16251612
if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
16261613
checker.check_const();
16271614
} else if tcx.is_min_const_fn(def_id) {
1628-
// enforce `min_const_fn` for stable const fns
1615+
// Enforce `min_const_fn` for stable `const fn`s.
16291616
use super::qualify_min_const_fn::is_min_const_fn;
16301617
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
1631-
let mut diag = struct_span_err!(
1632-
tcx.sess,
1633-
span,
1634-
E0723,
1635-
"{}",
1636-
err,
1637-
);
1638-
diag.note("for more information, see issue \
1639-
https://github.com/rust-lang/rust/issues/57563");
1640-
diag.help(
1641-
"add `#![feature(const_fn)]` to the crate attributes to enable",
1642-
);
1643-
diag.emit();
1618+
error_min_const_fn_violation(tcx, span, err);
16441619
} else {
16451620
// this should not produce any errors, but better safe than sorry
16461621
// FIXME(#53819)
@@ -1664,107 +1639,119 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
16641639
promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
16651640
);
16661641
} else {
1667-
if !body.control_flow_destroyed.is_empty() {
1668-
let mut locals = body.vars_iter();
1669-
if let Some(local) = locals.next() {
1670-
let span = body.local_decls[local].source_info.span;
1671-
let mut error = tcx.sess.struct_span_err(
1672-
span,
1673-
&format!(
1674-
"new features like let bindings are not permitted in {}s \
1675-
which also use short circuiting operators",
1676-
mode,
1677-
),
1678-
);
1679-
for (span, kind) in body.control_flow_destroyed.iter() {
1680-
error.span_note(
1681-
*span,
1682-
&format!("use of {} here does not actually short circuit due to \
1683-
the const evaluator presently not being able to do control flow. \
1684-
See https://github.com/rust-lang/rust/issues/49146 for more \
1685-
information.", kind),
1686-
);
1687-
}
1688-
for local in locals {
1689-
let span = body.local_decls[local].source_info.span;
1690-
error.span_note(
1691-
span,
1692-
"more locals defined here",
1693-
);
1694-
}
1695-
error.emit();
1696-
}
1697-
}
1698-
let promoted_temps = if mode == Mode::Const {
1699-
// Already computed by `mir_const_qualif`.
1700-
const_promoted_temps.unwrap()
1701-
} else {
1702-
Checker::new(tcx, def_id, body, mode).check_const().1
1642+
check_short_circuiting_in_const_local(tcx, body, mode);
1643+
1644+
let promoted_temps = match mode {
1645+
Mode::Const => tcx.mir_const_qualif(def_id).1,
1646+
_ => Checker::new(tcx, def_id, body, mode).check_const().1,
17031647
};
1648+
remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
1649+
}
17041650

1705-
// In `const` and `static` everything without `StorageDead`
1706-
// is `'static`, we don't have to create promoted MIR fragments,
1707-
// just remove `Drop` and `StorageDead` on "promoted" locals.
1708-
debug!("run_pass: promoted_temps={:?}", promoted_temps);
1709-
for block in body.basic_blocks_mut() {
1710-
block.statements.retain(|statement| {
1711-
match statement.kind {
1712-
StatementKind::StorageDead(index) => {
1713-
!promoted_temps.contains(index)
1714-
}
1715-
_ => true
1716-
}
1717-
});
1718-
let terminator = block.terminator_mut();
1719-
match terminator.kind {
1720-
TerminatorKind::Drop {
1721-
location: Place {
1722-
base: PlaceBase::Local(index),
1723-
projection: None,
1724-
},
1725-
target,
1726-
..
1727-
} => {
1728-
if promoted_temps.contains(index) {
1729-
terminator.kind = TerminatorKind::Goto {
1730-
target,
1731-
};
1732-
}
1733-
}
1734-
_ => {}
1735-
}
1736-
}
1651+
if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
1652+
// `static`s (not `static mut`s) which are not `#[thread_local]` must be `Sync`.
1653+
check_static_is_sync(tcx, body, hir_id);
17371654
}
1655+
}
1656+
}
17381657

1739-
// Statics must be Sync.
1740-
if mode == Mode::Static {
1741-
// `#[thread_local]` statics don't have to be `Sync`.
1742-
for attr in &tcx.get_attrs(def_id)[..] {
1743-
if attr.check_name(sym::thread_local) {
1744-
return;
1745-
}
1658+
fn determine_mode(tcx: TyCtxt<'_>, hir_id: HirId, def_id: DefId) -> Mode {
1659+
match tcx.hir().body_owner_kind(hir_id) {
1660+
hir::BodyOwnerKind::Closure => Mode::NonConstFn,
1661+
hir::BodyOwnerKind::Fn if tcx.is_const_fn(def_id) => Mode::ConstFn,
1662+
hir::BodyOwnerKind::Fn => Mode::NonConstFn,
1663+
hir::BodyOwnerKind::Const => Mode::Const,
1664+
hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1665+
hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1666+
}
1667+
}
1668+
1669+
fn error_min_const_fn_violation(tcx: TyCtxt<'_>, span: Span, msg: Cow<'_, str>) {
1670+
struct_span_err!(tcx.sess, span, E0723, "{}", msg)
1671+
.note("for more information, see issue https://github.com/rust-lang/rust/issues/57563")
1672+
.help("add `#![feature(const_fn)]` to the crate attributes to enable")
1673+
.emit();
1674+
}
1675+
1676+
fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>, mode: Mode) {
1677+
if body.control_flow_destroyed.is_empty() {
1678+
return;
1679+
}
1680+
1681+
let mut locals = body.vars_iter();
1682+
if let Some(local) = locals.next() {
1683+
let span = body.local_decls[local].source_info.span;
1684+
let mut error = tcx.sess.struct_span_err(
1685+
span,
1686+
&format!(
1687+
"new features like let bindings are not permitted in {}s \
1688+
which also use short circuiting operators",
1689+
mode,
1690+
),
1691+
);
1692+
for (span, kind) in body.control_flow_destroyed.iter() {
1693+
error.span_note(
1694+
*span,
1695+
&format!("use of {} here does not actually short circuit due to \
1696+
the const evaluator presently not being able to do control flow. \
1697+
See https://github.com/rust-lang/rust/issues/49146 for more \
1698+
information.", kind),
1699+
);
1700+
}
1701+
for local in locals {
1702+
let span = body.local_decls[local].source_info.span;
1703+
error.span_note(span, "more locals defined here");
1704+
}
1705+
error.emit();
1706+
}
1707+
}
1708+
1709+
/// In `const` and `static` everything without `StorageDead`
1710+
/// is `'static`, we don't have to create promoted MIR fragments,
1711+
/// just remove `Drop` and `StorageDead` on "promoted" locals.
1712+
fn remove_drop_and_storage_dead_on_promoted_locals(
1713+
body: &mut Body<'tcx>,
1714+
promoted_temps: &BitSet<Local>,
1715+
) {
1716+
debug!("run_pass: promoted_temps={:?}", promoted_temps);
1717+
1718+
for block in body.basic_blocks_mut() {
1719+
block.statements.retain(|statement| {
1720+
match statement.kind {
1721+
StatementKind::StorageDead(index) => !promoted_temps.contains(index),
1722+
_ => true
17461723
}
1747-
let ty = body.return_ty();
1748-
tcx.infer_ctxt().enter(|infcx| {
1749-
let param_env = ty::ParamEnv::empty();
1750-
let cause = traits::ObligationCause::new(body.span, id, traits::SharedStatic);
1751-
let mut fulfillment_cx = traits::FulfillmentContext::new();
1752-
fulfillment_cx.register_bound(&infcx,
1753-
param_env,
1754-
ty,
1755-
tcx.require_lang_item(
1756-
lang_items::SyncTraitLangItem,
1757-
Some(body.span)
1758-
),
1759-
cause);
1760-
if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1761-
infcx.report_fulfillment_errors(&err, None, false);
1762-
}
1763-
});
1724+
});
1725+
let terminator = block.terminator_mut();
1726+
match terminator.kind {
1727+
TerminatorKind::Drop {
1728+
location: Place {
1729+
base: PlaceBase::Local(index),
1730+
projection: None,
1731+
},
1732+
target,
1733+
..
1734+
} if promoted_temps.contains(index) => {
1735+
terminator.kind = TerminatorKind::Goto { target };
1736+
}
1737+
_ => {}
17641738
}
17651739
}
17661740
}
17671741

1742+
fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
1743+
let ty = body.return_ty();
1744+
tcx.infer_ctxt().enter(|infcx| {
1745+
let cause = traits::ObligationCause::new(body.span, hir_id, traits::SharedStatic);
1746+
let mut fulfillment_cx = traits::FulfillmentContext::new();
1747+
let sync_def_id = tcx.require_lang_item(lang_items::SyncTraitLangItem, Some(body.span));
1748+
fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), ty, sync_def_id, cause);
1749+
if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1750+
infcx.report_fulfillment_errors(&err, None, false);
1751+
}
1752+
});
1753+
}
1754+
17681755
fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxHashSet<usize>> {
17691756
let attrs = tcx.get_attrs(def_id);
17701757
let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;

0 commit comments

Comments
 (0)