about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-05-25 07:31:43 +0200
committerGitHub <noreply@github.com>2022-05-25 07:31:43 +0200
commit89bdbd0294ddb4ecfa4b75ccd8446d4d5c4d60e9 (patch)
tree7be240ee0e4ed44a795c363dd9cd504c8ff29079
parentfbb17777fee8048c3bce9019ef1c1e7e42bb303b (diff)
parentee8efc5c4a634a26be59e2a90a8a686b1242ce03 (diff)
downloadrust-89bdbd0294ddb4ecfa4b75ccd8446d4d5c4d60e9.tar.gz
rust-89bdbd0294ddb4ecfa4b75ccd8446d4d5c4d60e9.zip
Rollup merge of #97105 - JulianKnodt:const_dep_gen_const_expr, r=lcnr
Add tests for lint on type dependent on consts

r? `@lcnr`
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs266
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr39
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr34
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs25
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/no_dependence.rs13
6 files changed, 244 insertions, 134 deletions
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index f46e8ff0004..539e94993ae 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -20,6 +20,7 @@
 #![feature(label_break_value)]
 #![feature(let_chains)]
 #![feature(let_else)]
+#![feature(if_let_guard)]
 #![feature(never_type)]
 #![recursion_limit = "512"] // For rustdoc
 
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 27ce08ea045..0dea2c3d8bf 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -39,150 +39,148 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
     let tcx = infcx.tcx;
 
     if tcx.features().generic_const_exprs {
-        match AbstractConst::new(tcx, uv)? {
-            // We are looking at a generic abstract constant.
-            Some(ct) => {
-                if satisfied_from_param_env(tcx, ct, param_env)? {
-                    return Ok(());
-                }
-
-                // We were unable to unify the abstract constant with
-                // a constant found in the caller bounds, there are
-                // now three possible cases here.
-                #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-                enum FailureKind {
-                    /// The abstract const still references an inference
-                    /// variable, in this case we return `TooGeneric`.
-                    MentionsInfer,
-                    /// The abstract const references a generic parameter,
-                    /// this means that we emit an error here.
-                    MentionsParam,
-                    /// The substs are concrete enough that we can simply
-                    /// try and evaluate the given constant.
-                    Concrete,
-                }
-                let mut failure_kind = FailureKind::Concrete;
-                walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
-                    Node::Leaf(leaf) => {
-                        if leaf.has_infer_types_or_consts() {
-                            failure_kind = FailureKind::MentionsInfer;
-                        } else if leaf.has_param_types_or_consts() {
-                            failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
-                        }
-
-                        ControlFlow::CONTINUE
+        if let Some(ct) = AbstractConst::new(tcx, uv)? {
+            if satisfied_from_param_env(tcx, ct, param_env)? {
+                return Ok(());
+            }
+
+            // We were unable to unify the abstract constant with
+            // a constant found in the caller bounds, there are
+            // now three possible cases here.
+            #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+            enum FailureKind {
+                /// The abstract const still references an inference
+                /// variable, in this case we return `TooGeneric`.
+                MentionsInfer,
+                /// The abstract const references a generic parameter,
+                /// this means that we emit an error here.
+                MentionsParam,
+                /// The substs are concrete enough that we can simply
+                /// try and evaluate the given constant.
+                Concrete,
+            }
+            let mut failure_kind = FailureKind::Concrete;
+            walk_abstract_const::<!, _>(tcx, ct, |node| match node.root(tcx) {
+                Node::Leaf(leaf) => {
+                    if leaf.has_infer_types_or_consts() {
+                        failure_kind = FailureKind::MentionsInfer;
+                    } else if leaf.has_param_types_or_consts() {
+                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
                     }
-                    Node::Cast(_, _, ty) => {
-                        if ty.has_infer_types_or_consts() {
-                            failure_kind = FailureKind::MentionsInfer;
-                        } else if ty.has_param_types_or_consts() {
-                            failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
-                        }
 
-                        ControlFlow::CONTINUE
-                    }
-                    Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
-                        ControlFlow::CONTINUE
+                    ControlFlow::CONTINUE
+                }
+                Node::Cast(_, _, ty) => {
+                    if ty.has_infer_types_or_consts() {
+                        failure_kind = FailureKind::MentionsInfer;
+                    } else if ty.has_param_types_or_consts() {
+                        failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam);
                     }
-                });
 
-                match failure_kind {
-                    FailureKind::MentionsInfer => {
-                        return Err(NotConstEvaluatable::MentionsInfer);
-                    }
-                    FailureKind::MentionsParam => {
-                        return Err(NotConstEvaluatable::MentionsParam);
-                    }
-                    FailureKind::Concrete => {
-                        // Dealt with below by the same code which handles this
-                        // without the feature gate.
-                    }
+                    ControlFlow::CONTINUE
                 }
-            }
-            None => {
-                // If we are dealing with a concrete constant, we can
-                // reuse the old code path and try to evaluate
-                // the constant.
-            }
-        }
-    }
+                Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => {
+                    ControlFlow::CONTINUE
+                }
+            });
 
-    let future_compat_lint = || {
-        if let Some(local_def_id) = uv.def.did.as_local() {
-            infcx.tcx.struct_span_lint_hir(
-                lint::builtin::CONST_EVALUATABLE_UNCHECKED,
-                infcx.tcx.hir().local_def_id_to_hir_id(local_def_id),
-                span,
-                |err| {
-                    err.build("cannot use constants which depend on generic parameters in types")
-                        .emit();
-                },
-            );
-        }
-    };
-
-    // FIXME: We should only try to evaluate a given constant here if it is fully concrete
-    // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
-    //
-    // We previously did not check this, so we only emit a future compat warning if
-    // const evaluation succeeds and the given constant is still polymorphic for now
-    // and hopefully soon change this to an error.
-    //
-    // See #74595 for more details about this.
-    let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
-
-    if concrete.is_ok() && uv.substs.has_param_types_or_consts() {
-        match infcx.tcx.def_kind(uv.def.did) {
-            DefKind::AnonConst | DefKind::InlineConst => {
-                let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
-
-                if mir_body.is_polymorphic {
-                    future_compat_lint();
+            match failure_kind {
+                FailureKind::MentionsInfer => {
+                    return Err(NotConstEvaluatable::MentionsInfer);
+                }
+                FailureKind::MentionsParam => {
+                    return Err(NotConstEvaluatable::MentionsParam);
                 }
+                // returned below
+                FailureKind::Concrete => {}
             }
-            _ => future_compat_lint(),
         }
-    }
-
-    // If we're evaluating a foreign constant, under a nightly compiler without generic
-    // const exprs, AND it would've passed if that expression had been evaluated with
-    // generic const exprs, then suggest using generic const exprs.
-    if concrete.is_err()
-        && tcx.sess.is_nightly_build()
-        && !uv.def.did.is_local()
-        && !tcx.features().generic_const_exprs
-        && let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
-        && satisfied_from_param_env(tcx, ct, param_env) == Ok(true)
-    {
-        tcx.sess
-            .struct_span_fatal(
-                // Slightly better span than just using `span` alone
-                if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
-                "failed to evaluate generic const expression",
-            )
-            .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
-            .span_suggestion_verbose(
-                rustc_span::DUMMY_SP,
-                "consider enabling this feature",
-                "#![feature(generic_const_exprs)]\n".to_string(),
-                rustc_errors::Applicability::MaybeIncorrect,
-            )
-            .emit()
-    }
-
-    debug!(?concrete, "is_const_evaluatable");
-    match concrete {
-        Err(ErrorHandled::TooGeneric) => Err(match uv.has_infer_types_or_consts() {
-            true => NotConstEvaluatable::MentionsInfer,
-            false => NotConstEvaluatable::MentionsParam,
-        }),
-        Err(ErrorHandled::Linted) => {
-            let reported =
-                infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
-            Err(NotConstEvaluatable::Error(reported))
+        let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
+        match concrete {
+            Err(ErrorHandled::TooGeneric) => Err(if !uv.has_infer_types_or_consts() {
+                infcx
+                    .tcx
+                    .sess
+                    .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv));
+                NotConstEvaluatable::MentionsParam
+            } else {
+                NotConstEvaluatable::MentionsInfer
+            }),
+            Err(ErrorHandled::Linted) => {
+                let reported = infcx
+                    .tcx
+                    .sess
+                    .delay_span_bug(span, "constant in type had error reported as lint");
+                Err(NotConstEvaluatable::Error(reported))
+            }
+            Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+            Ok(_) => Ok(()),
+        }
+    } else {
+        // FIXME: We should only try to evaluate a given constant here if it is fully concrete
+        // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
+        //
+        // We previously did not check this, so we only emit a future compat warning if
+        // const evaluation succeeds and the given constant is still polymorphic for now
+        // and hopefully soon change this to an error.
+        //
+        // See #74595 for more details about this.
+        let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
+
+        match concrete {
+          // If we're evaluating a foreign constant, under a nightly compiler without generic
+          // const exprs, AND it would've passed if that expression had been evaluated with
+          // generic const exprs, then suggest using generic const exprs.
+          Err(_) if tcx.sess.is_nightly_build()
+            && let Ok(Some(ct)) = AbstractConst::new(tcx, uv)
+            && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => {
+              tcx.sess
+                  .struct_span_fatal(
+                      // Slightly better span than just using `span` alone
+                      if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
+                      "failed to evaluate generic const expression",
+                  )
+                  .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
+                  .span_suggestion_verbose(
+                      rustc_span::DUMMY_SP,
+                      "consider enabling this feature",
+                      "#![feature(generic_const_exprs)]\n".to_string(),
+                      rustc_errors::Applicability::MaybeIncorrect,
+                  )
+                  .emit()
+            }
+
+            Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() {
+                NotConstEvaluatable::MentionsInfer
+                } else {
+                NotConstEvaluatable::MentionsParam
+            }),
+            Err(ErrorHandled::Linted) => {
+                let reported =
+                    infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint");
+                Err(NotConstEvaluatable::Error(reported))
+            }
+            Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+            Ok(_) => {
+              if uv.substs.has_param_types_or_consts() {
+                  assert!(matches!(infcx.tcx.def_kind(uv.def.did), DefKind::AnonConst));
+                  let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def);
+
+                  if mir_body.is_polymorphic {
+                      let Some(local_def_id) = uv.def.did.as_local() else { return Ok(()) };
+                      tcx.struct_span_lint_hir(
+                          lint::builtin::CONST_EVALUATABLE_UNCHECKED,
+                          tcx.hir().local_def_id_to_hir_id(local_def_id),
+                          span,
+                          |err| {
+                              err.build("cannot use constants which depend on generic parameters in types").emit();
+                        })
+                  }
+              }
+
+              Ok(())
+            },
         }
-        Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
-        Ok(_) => Ok(()),
     }
 }
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr
new file mode 100644
index 00000000000..4cd86fecd7e
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.full.stderr
@@ -0,0 +1,39 @@
+error: generic parameters may not be used in const operations
+  --> $DIR/dependence_lint.rs:13:32
+   |
+LL |     let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
+   |                                ^ cannot perform const operation using `T`
+   |
+   = note: type parameters may not be used in const expressions
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+  --> $DIR/dependence_lint.rs:20:37
+   |
+LL |     let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
+   |                                     ^ cannot perform const operation using `T`
+   |
+   = note: type parameters may not be used in const expressions
+   = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+warning: cannot use constants which depend on generic parameters in types
+  --> $DIR/dependence_lint.rs:9:9
+   |
+LL |     [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(const_evaluatable_unchecked)]` on by default
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>
+
+warning: cannot use constants which depend on generic parameters in types
+  --> $DIR/dependence_lint.rs:16:9
+   |
+LL |     [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #76200 <https://github.com/rust-lang/rust/issues/76200>
+
+error: aborting due to 2 previous errors; 2 warnings emitted
+
diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr
new file mode 100644
index 00000000000..b13bcdb2c47
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr
@@ -0,0 +1,34 @@
+error: overly complex generic constant
+  --> $DIR/dependence_lint.rs:16:9
+   |
+LL |     [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
+   |
+   = help: consider moving this anonymous constant into a `const` function
+
+error: overly complex generic constant
+  --> $DIR/dependence_lint.rs:20:17
+   |
+LL |     let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
+   |
+   = help: consider moving this anonymous constant into a `const` function
+
+error: unconstrained generic constant
+  --> $DIR/dependence_lint.rs:13:12
+   |
+LL |     let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:`
+
+error: unconstrained generic constant
+  --> $DIR/dependence_lint.rs:9:9
+   |
+LL |     [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); size_of::<*mut T>()]:`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs
new file mode 100644
index 00000000000..dcdfd75def9
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/dependence_lint.rs
@@ -0,0 +1,25 @@
+// revisions: full gce
+
+#![cfg_attr(gce, feature(generic_const_exprs))]
+#![allow(incomplete_features)]
+
+use std::mem::size_of;
+
+fn foo<T>() {
+    [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
+    //[gce]~^ ERROR unconstrained
+    //[full]~^^ WARNING cannot use constants
+    //[full]~| WARNING this was previously accepted
+    let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
+    //[full]~^ ERROR generic parameters may not be used
+    //[gce]~^^ ERROR unconstrained generic
+    [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
+    //[gce]~^ ERROR overly complex
+    //[full]~^^ WARNING cannot use constants
+    //[full]~| WARNING this was previously accepted
+    let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
+    //[full]~^ ERROR generic parameters may not be used
+    //[gce]~^^ ERROR overly complex
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs b/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs
new file mode 100644
index 00000000000..db8dc6ed443
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/no_dependence.rs
@@ -0,0 +1,13 @@
+// check-pass
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn two_args<const N: usize, const M: usize>() -> [u8; M + 2] {
+    [0; M + 2]
+}
+
+fn yay<const N: usize>() -> [u8; 4] {
+     two_args::<N, 2>() // no lint
+}
+
+fn main() {}