]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_passes/check_const.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / librustc_passes / check_const.rs
index 94f9c619a3a26493a5ad649406a1a723bb4b4861..b1ebab3f2f8064de56ce69ea0aba654b6aa45a4f 100644 (file)
@@ -7,9 +7,10 @@
 //! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips
 //! through, but errors for structured control flow in a `const` should be emitted here.
 
+use rustc_attr as attr;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::query::Providers;
@@ -23,7 +24,6 @@ use rustc_span::{sym, Span, Symbol};
 enum NonConstExpr {
     Loop(hir::LoopSource),
     Match(hir::MatchSource),
-    OrPattern,
 }
 
 impl NonConstExpr {
@@ -31,7 +31,6 @@ impl NonConstExpr {
         match self {
             Self::Loop(src) => format!("`{}`", src.name()),
             Self::Match(src) => format!("`{}`", src.name()),
-            Self::OrPattern => "or-pattern".to_string(),
         }
     }
 
@@ -40,34 +39,30 @@ impl NonConstExpr {
         use hir::MatchSource::*;
 
         let gates: &[_] = match self {
-            Self::Match(Normal)
-            | Self::Match(IfDesugar { .. })
-            | Self::Match(IfLetDesugar { .. })
-            | Self::OrPattern => &[sym::const_if_match],
-
-            Self::Loop(Loop) => &[sym::const_loop],
-
-            Self::Loop(While)
-            | Self::Loop(WhileLet)
-            | Self::Match(WhileDesugar | WhileLetDesugar) => {
-                &[sym::const_loop, sym::const_if_match]
+            // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
+            // so they are not yet allowed.
+            // Likewise, `?` desugars to a call to `Try::into_result`.
+            Self::Loop(ForLoop) | Self::Match(ForLoopDesugar | TryDesugar | AwaitDesugar) => {
+                return None;
             }
 
-            // A `for` loop's desugaring contains a call to `IntoIterator::into_iter`,
-            // so they are not yet allowed with `#![feature(const_loop)]`.
-            _ => return None,
+            // All other expressions are allowed.
+            Self::Loop(Loop | While | WhileLet)
+            | Self::Match(
+                WhileDesugar | WhileLetDesugar | Normal | IfDesugar { .. } | IfLetDesugar { .. },
+            ) => &[],
         };
 
         Some(gates)
     }
 }
 
-fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: DefId) {
+fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
     let mut vis = CheckConstVisitor::new(tcx);
     tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis.as_deep_visitor());
 }
 
-pub(crate) fn provide(providers: &mut Providers<'_>) {
+pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers { check_mod_const_bodies, ..*providers };
 }
 
@@ -75,35 +70,63 @@ pub(crate) fn provide(providers: &mut Providers<'_>) {
 struct CheckConstVisitor<'tcx> {
     tcx: TyCtxt<'tcx>,
     const_kind: Option<hir::ConstContext>,
+    def_id: Option<LocalDefId>,
 }
 
 impl<'tcx> CheckConstVisitor<'tcx> {
     fn new(tcx: TyCtxt<'tcx>) -> Self {
-        CheckConstVisitor { tcx, const_kind: None }
+        CheckConstVisitor { tcx, const_kind: None, def_id: None }
     }
 
     /// Emits an error when an unsupported expression is found in a const context.
     fn const_check_violated(&self, expr: NonConstExpr, span: Span) {
-        let features = self.tcx.features();
+        let Self { tcx, def_id, const_kind } = *self;
+
+        let features = tcx.features();
         let required_gates = expr.required_feature_gates();
+
+        let is_feature_allowed = |feature_gate| {
+            // All features require that the corresponding gate be enabled,
+            // even if the function has `#[allow_internal_unstable(the_gate)]`.
+            if !tcx.features().enabled(feature_gate) {
+                return false;
+            }
+
+            // If `def_id` is `None`, we don't need to consider stability attributes.
+            let def_id = match def_id {
+                Some(x) => x.to_def_id(),
+                None => return true,
+            };
+
+            // If this crate is not using stability attributes, or this function is not claiming to be a
+            // stable `const fn`, that is all that is required.
+            if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
+                return true;
+            }
+
+            // However, we cannot allow stable `const fn`s to use unstable features without an explicit
+            // opt-in via `allow_internal_unstable`.
+            attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
+                .map_or(false, |mut features| features.any(|name| name == feature_gate))
+        };
+
         match required_gates {
             // Don't emit an error if the user has enabled the requisite feature gates.
-            Some(gates) if gates.iter().all(|&g| features.enabled(g)) => return,
+            Some(gates) if gates.iter().copied().all(is_feature_allowed) => return,
 
             // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a
             // corresponding feature gate. This encourages nightly users to use feature gates when
             // possible.
-            None if self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
-                self.tcx.sess.span_warn(span, "skipping const checks");
+            None if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you => {
+                tcx.sess.span_warn(span, "skipping const checks");
                 return;
             }
 
             _ => {}
         }
 
-        let const_kind = self
-            .const_kind
-            .expect("`const_check_violated` may only be called inside a const context");
+        let const_kind =
+            const_kind.expect("`const_check_violated` may only be called inside a const context");
 
         let msg = format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name());
 
@@ -112,21 +135,10 @@ impl<'tcx> CheckConstVisitor<'tcx> {
             required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
 
         match missing_gates.as_slice() {
-            &[] => struct_span_err!(self.tcx.sess, span, E0744, "{}", msg).emit(),
-
-            // If the user enabled `#![feature(const_loop)]` but not `#![feature(const_if_match)]`,
-            // explain why their `while` loop is being rejected.
-            &[gate @ sym::const_if_match] if required_gates.contains(&sym::const_loop) => {
-                feature_err(&self.tcx.sess.parse_sess, gate, span, &msg)
-                    .note(
-                        "`#![feature(const_loop)]` alone is not sufficient, \
-                           since this loop expression contains an implicit conditional",
-                    )
-                    .emit();
-            }
+            &[] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(),
 
             &[missing_primary, ref missing_secondary @ ..] => {
-                let mut err = feature_err(&self.tcx.sess.parse_sess, missing_primary, span, &msg);
+                let mut err = feature_err(&tcx.sess.parse_sess, missing_primary, span, &msg);
 
                 // If multiple feature gates would be required to enable this expression, include
                 // them as help messages. Don't emit a separate error for each missing feature gate.
@@ -149,10 +161,18 @@ impl<'tcx> CheckConstVisitor<'tcx> {
     }
 
     /// Saves the parent `const_kind` before calling `f` and restores it afterwards.
-    fn recurse_into(&mut self, kind: Option<hir::ConstContext>, f: impl FnOnce(&mut Self)) {
+    fn recurse_into(
+        &mut self,
+        kind: Option<hir::ConstContext>,
+        def_id: Option<LocalDefId>,
+        f: impl FnOnce(&mut Self),
+    ) {
+        let parent_def_id = self.def_id;
         let parent_kind = self.const_kind;
+        self.def_id = def_id;
         self.const_kind = kind;
         f(self);
+        self.def_id = parent_def_id;
         self.const_kind = parent_kind;
     }
 }
@@ -166,22 +186,13 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
 
     fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) {
         let kind = Some(hir::ConstContext::Const);
-        self.recurse_into(kind, |this| intravisit::walk_anon_const(this, anon));
+        self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
     }
 
     fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
         let owner = self.tcx.hir().body_owner_def_id(body.id());
         let kind = self.tcx.hir().body_const_context(owner);
-        self.recurse_into(kind, |this| intravisit::walk_body(this, body));
-    }
-
-    fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
-        if self.const_kind.is_some() {
-            if let hir::PatKind::Or { .. } = p.kind {
-                self.const_check_violated(NonConstExpr::OrPattern, p.span);
-            }
-        }
-        intravisit::walk_pat(self, p)
+        self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body));
     }
 
     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {