rustc_trait_selection/traits/
const_evaluatable.rs
1use rustc_hir::def::DefKind;
13use rustc_infer::infer::InferCtxt;
14use rustc_middle::bug;
15use rustc_middle::traits::ObligationCause;
16use rustc_middle::ty::abstract_const::NotConstEvaluatable;
17use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
18use rustc_span::{DUMMY_SP, Span};
19use tracing::{debug, instrument};
20
21use super::EvaluateConstErr;
22use crate::traits::ObligationCtxt;
23
24#[instrument(skip(infcx), level = "debug")]
26pub fn is_const_evaluatable<'tcx>(
27 infcx: &InferCtxt<'tcx>,
28 unexpanded_ct: ty::Const<'tcx>,
29 param_env: ty::ParamEnv<'tcx>,
30 span: Span,
31) -> Result<(), NotConstEvaluatable> {
32 let tcx = infcx.tcx;
33 match tcx.expand_abstract_consts(unexpanded_ct).kind() {
34 ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
35 ty::ConstKind::Param(_)
36 | ty::ConstKind::Bound(_, _)
37 | ty::ConstKind::Placeholder(_)
38 | ty::ConstKind::Value(_)
39 | ty::ConstKind::Error(_) => return Ok(()),
40 ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
41 };
42
43 if tcx.features().generic_const_exprs() {
44 let ct = tcx.expand_abstract_consts(unexpanded_ct);
45
46 let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
47 tcx.def_kind(uv.def) == DefKind::AnonConst
48 } else {
49 false
50 };
51
52 if !is_anon_ct {
53 if satisfied_from_param_env(tcx, infcx, ct, param_env) {
54 return Ok(());
55 }
56 if ct.has_non_region_infer() {
57 return Err(NotConstEvaluatable::MentionsInfer);
58 } else if ct.has_non_region_param() {
59 return Err(NotConstEvaluatable::MentionsParam);
60 }
61 }
62
63 match unexpanded_ct.kind() {
64 ty::ConstKind::Expr(_) => {
65 tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
70 }
71 ty::ConstKind::Unevaluated(_) => {
72 match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
73 Err(EvaluateConstErr::HasGenericsOrInfers) => {
74 Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
75 span,
76 "Missing value for constant, but no error reported?",
77 )))
78 }
79 Err(
80 EvaluateConstErr::EvaluationFailure(e)
81 | EvaluateConstErr::InvalidConstParamTy(e),
82 ) => Err(NotConstEvaluatable::Error(e)),
83 Ok(_) => Ok(()),
84 }
85 }
86 _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
87 }
88 } else if tcx.features().min_generic_const_args() {
89 crate::traits::evaluate_const(infcx, unexpanded_ct, param_env);
93 Ok(())
94 } else {
95 let uv = match unexpanded_ct.kind() {
96 ty::ConstKind::Unevaluated(uv) => uv,
97 ty::ConstKind::Expr(_) => {
98 bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
99 }
100 _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
101 };
102
103 match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
104 Err(_)
108 if tcx.sess.is_nightly_build()
109 && satisfied_from_param_env(
110 tcx,
111 infcx,
112 tcx.expand_abstract_consts(unexpanded_ct),
113 param_env,
114 ) =>
115 {
116 tcx.dcx()
117 .struct_span_fatal(
118 if span == DUMMY_SP { tcx.def_span(uv.def) } else { span },
120 "failed to evaluate generic const expression",
121 )
122 .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
123 .with_span_suggestion_verbose(
124 DUMMY_SP,
125 "consider enabling this feature",
126 "#![feature(generic_const_exprs)]\n",
127 rustc_errors::Applicability::MaybeIncorrect,
128 )
129 .emit()
130 }
131
132 Err(EvaluateConstErr::HasGenericsOrInfers) => {
133 let err = if uv.has_non_region_infer() {
134 NotConstEvaluatable::MentionsInfer
135 } else if uv.has_non_region_param() {
136 NotConstEvaluatable::MentionsParam
137 } else {
138 let guar = infcx.dcx().span_delayed_bug(
139 span,
140 "Missing value for constant, but no error reported?",
141 );
142 NotConstEvaluatable::Error(guar)
143 };
144
145 Err(err)
146 }
147 Err(
148 EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e),
149 ) => Err(NotConstEvaluatable::Error(e)),
150 Ok(_) => Ok(()),
151 }
152 }
153}
154
155#[instrument(skip(infcx, tcx), level = "debug")]
156fn satisfied_from_param_env<'tcx>(
157 tcx: TyCtxt<'tcx>,
158 infcx: &InferCtxt<'tcx>,
159 ct: ty::Const<'tcx>,
160 param_env: ty::ParamEnv<'tcx>,
161) -> bool {
162 struct Visitor<'a, 'tcx> {
166 ct: ty::Const<'tcx>,
167 param_env: ty::ParamEnv<'tcx>,
168
169 infcx: &'a InferCtxt<'tcx>,
170 single_match: Option<Result<ty::Const<'tcx>, ()>>,
171 }
172
173 impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'a, 'tcx> {
174 fn visit_const(&mut self, c: ty::Const<'tcx>) {
175 debug!("is_const_evaluatable: candidate={:?}", c);
176 if self.infcx.probe(|_| {
177 let ocx = ObligationCtxt::new(self.infcx);
178 ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
179 && ocx.select_all_or_error().is_empty()
180 }) {
181 self.single_match = match self.single_match {
182 None => Some(Ok(c)),
183 Some(Ok(o)) if o == c => Some(Ok(c)),
184 Some(_) => Some(Err(())),
185 };
186 }
187
188 if let ty::ConstKind::Expr(e) = c.kind() {
189 e.visit_with(self);
190 } else {
191 }
199 }
200 }
201
202 let mut single_match: Option<Result<ty::Const<'tcx>, ()>> = None;
203
204 for pred in param_env.caller_bounds() {
205 match pred.kind().skip_binder() {
206 ty::ClauseKind::ConstEvaluatable(ce) => {
207 let b_ct = tcx.expand_abstract_consts(ce);
208 let mut v = Visitor { ct, infcx, param_env, single_match };
209 let _ = b_ct.visit_with(&mut v);
210
211 single_match = v.single_match;
212 }
213 _ => {} }
215 }
216
217 if let Some(Ok(c)) = single_match {
218 let ocx = ObligationCtxt::new(infcx);
219 assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
220 assert!(ocx.select_all_or_error().is_empty());
221 return true;
222 }
223
224 debug!("is_const_evaluatable: no");
225 false
226}