@@ -25,11 +25,13 @@ use syntax::feature_gate::{emit_feature_err, GateIssue};
2525use syntax:: symbol:: sym;
2626use syntax_pos:: { Span , DUMMY_SP } ;
2727
28+ use std:: borrow:: Cow ;
2829use std:: cell:: Cell ;
2930use std:: fmt;
3031use std:: ops:: { Deref , Index , IndexMut } ;
3132use std:: usize;
3233
34+ use rustc:: hir:: HirId ;
3335use crate :: transform:: { MirPass , MirSource } ;
3436use 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+
17681755fn 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