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 {
89 let uv = match unexpanded_ct.kind() {
90 ty::ConstKind::Unevaluated(uv) => uv,
91 ty::ConstKind::Expr(_) => {
92 bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
93 }
94 _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
95 };
96
97 match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
98 Err(_)
102 if tcx.sess.is_nightly_build()
103 && satisfied_from_param_env(
104 tcx,
105 infcx,
106 tcx.expand_abstract_consts(unexpanded_ct),
107 param_env,
108 ) =>
109 {
110 tcx.dcx()
111 .struct_span_fatal(
112 if span == DUMMY_SP { tcx.def_span(uv.def) } else { span },
114 "failed to evaluate generic const expression",
115 )
116 .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
117 .with_span_suggestion_verbose(
118 DUMMY_SP,
119 "consider enabling this feature",
120 "#![feature(generic_const_exprs)]\n",
121 rustc_errors::Applicability::MaybeIncorrect,
122 )
123 .emit()
124 }
125
126 Err(EvaluateConstErr::HasGenericsOrInfers) => {
127 let err = if uv.has_non_region_infer() {
128 NotConstEvaluatable::MentionsInfer
129 } else if uv.has_non_region_param() {
130 NotConstEvaluatable::MentionsParam
131 } else {
132 let guar = infcx.dcx().span_delayed_bug(
133 span,
134 "Missing value for constant, but no error reported?",
135 );
136 NotConstEvaluatable::Error(guar)
137 };
138
139 Err(err)
140 }
141 Err(
142 EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e),
143 ) => Err(NotConstEvaluatable::Error(e)),
144 Ok(_) => Ok(()),
145 }
146 }
147}
148
149#[instrument(skip(infcx, tcx), level = "debug")]
150fn satisfied_from_param_env<'tcx>(
151 tcx: TyCtxt<'tcx>,
152 infcx: &InferCtxt<'tcx>,
153 ct: ty::Const<'tcx>,
154 param_env: ty::ParamEnv<'tcx>,
155) -> bool {
156 struct Visitor<'a, 'tcx> {
160 ct: ty::Const<'tcx>,
161 param_env: ty::ParamEnv<'tcx>,
162
163 infcx: &'a InferCtxt<'tcx>,
164 single_match: Option<Result<ty::Const<'tcx>, ()>>,
165 }
166
167 impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'a, 'tcx> {
168 fn visit_const(&mut self, c: ty::Const<'tcx>) {
169 debug!("is_const_evaluatable: candidate={:?}", c);
170 if self.infcx.probe(|_| {
171 let ocx = ObligationCtxt::new(self.infcx);
172 ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
173 && ocx.select_all_or_error().is_empty()
174 }) {
175 self.single_match = match self.single_match {
176 None => Some(Ok(c)),
177 Some(Ok(o)) if o == c => Some(Ok(c)),
178 Some(_) => Some(Err(())),
179 };
180 }
181
182 if let ty::ConstKind::Expr(e) = c.kind() {
183 e.visit_with(self);
184 } else {
185 }
193 }
194 }
195
196 let mut single_match: Option<Result<ty::Const<'tcx>, ()>> = None;
197
198 for pred in param_env.caller_bounds() {
199 match pred.kind().skip_binder() {
200 ty::ClauseKind::ConstEvaluatable(ce) => {
201 let b_ct = tcx.expand_abstract_consts(ce);
202 let mut v = Visitor { ct, infcx, param_env, single_match };
203 let _ = b_ct.visit_with(&mut v);
204
205 single_match = v.single_match;
206 }
207 _ => {} }
209 }
210
211 if let Some(Ok(c)) = single_match {
212 let ocx = ObligationCtxt::new(infcx);
213 assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
214 assert!(ocx.select_all_or_error().is_empty());
215 return true;
216 }
217
218 debug!("is_const_evaluatable: no");
219 false
220}