about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/transform/promote_consts.rs8
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs25
-rw-r--r--src/test/ui/consts/const-eval/double_promotion.rs17
3 files changed, 47 insertions, 3 deletions
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index f4efe33da70..5114fa87974 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -333,6 +333,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                             let operand = Operand::Copy(promoted_place(ty, span));
                             mem::replace(&mut args[index], operand)
                         }
+                        // We expected a `TerminatorKind::Call` for which we'd like to promote an
+                        // argument. `qualify_consts` saw a `TerminatorKind::Call` here, but
+                        // we are seeing a `Goto`. That means that the `promote_temps` method
+                        // already promoted this call away entirely. This case occurs when calling
+                        // a function requiring a constant argument and as that constant value
+                        // providing a value whose computation contains another call to a function
+                        // requiring a constant argument.
+                        TerminatorKind::Goto { .. } => return,
                         _ => bug!()
                     }
                 }
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 78547abf9d9..c4d8ec35868 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -820,7 +820,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
             let fn_ty = func.ty(self.mir, self.tcx);
             let mut callee_def_id = None;
-            let (mut is_shuffle, mut is_const_fn) = (false, false);
+            let mut is_shuffle = false;
+            let mut is_const_fn = false;
+            let mut is_promotable_const_fn = false;
             if let ty::FnDef(def_id, _) = fn_ty.sty {
                 callee_def_id = Some(def_id);
                 match self.tcx.fn_sig(def_id).abi() {
@@ -881,6 +883,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                             // functions without #[rustc_promotable]
                             if self.tcx.is_promotable_const_fn(def_id) {
                                 is_const_fn = true;
+                                is_promotable_const_fn = true;
+                            } else if self.tcx.is_const_fn(def_id) {
+                                is_const_fn = true;
                             }
                         } else {
                             // stable const fn or unstable const fns with their feature gate
@@ -982,7 +987,17 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                     if !constant_arguments.contains(&i) {
                         return
                     }
-                    if this.qualif.is_empty() {
+                    // Since the argument is required to be constant,
+                    // we care about constness, not promotability.
+                    // If we checked for promotability, we'd miss out on
+                    // the results of function calls (which are never promoted
+                    // in runtime code)
+                    // This is not a problem, because the argument explicitly
+                    // requests constness, in contrast to regular promotion
+                    // which happens even without the user requesting it.
+                    // We can error out with a hard error if the argument is not
+                    // constant here.
+                    if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
                         this.promotion_candidates.push(candidate);
                     } else {
                         this.tcx.sess.span_err(this.span,
@@ -1011,7 +1026,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                     // Be conservative about the returned value of a const fn.
                     let tcx = self.tcx;
                     let ty = dest.ty(self.mir, tcx).to_ty(tcx);
-                    self.qualif = Qualif::empty();
+                    if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
+                        self.qualif = Qualif::NOT_PROMOTABLE;
+                    } else {
+                        self.qualif = Qualif::empty();
+                    }
                     self.add_type(ty);
                 }
                 self.assign(dest, location);
diff --git a/src/test/ui/consts/const-eval/double_promotion.rs b/src/test/ui/consts/const-eval/double_promotion.rs
new file mode 100644
index 00000000000..0e75ea8e66b
--- /dev/null
+++ b/src/test/ui/consts/const-eval/double_promotion.rs
@@ -0,0 +1,17 @@
+// compile-pass
+
+#![feature(const_fn, rustc_attrs)]
+
+#[rustc_args_required_const(0)]
+pub const fn a(value: u8) -> u8 {
+    value
+}
+
+#[rustc_args_required_const(0)]
+pub fn b(_: u8) {
+    unimplemented!()
+}
+
+fn main() {
+    let _ = b(a(0));
+}