]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Rollup merge of #104984 - GuillaumeGomez:remote-crate-primitives, r=notriddle
[rust.git] / compiler / rustc_trait_selection / src / traits / const_evaluatable.rs
1 //! Checking that constant values used in types can be successfully evaluated.
2 //!
3 //! For concrete constants, this is fairly simple as we can just try and evaluate it.
4 //!
5 //! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`,
6 //! this is not as easy.
7 //!
8 //! In this case we try to build an abstract representation of this constant using
9 //! `thir_abstract_const` which can then be checked for structural equality with other
10 //! generic constants mentioned in the `caller_bounds` of the current environment.
11 use rustc_hir::def::DefKind;
12 use rustc_infer::infer::InferCtxt;
13 use rustc_middle::mir::interpret::ErrorHandled;
14
15 use rustc_middle::traits::ObligationCause;
16 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
17 use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor};
18
19 use rustc_span::Span;
20 use std::ops::ControlFlow;
21
22 use crate::traits::ObligationCtxt;
23
24 /// Check if a given constant can be evaluated.
25 #[instrument(skip(infcx), level = "debug")]
26 pub fn is_const_evaluatable<'tcx>(
27     infcx: &InferCtxt<'tcx>,
28     ct: ty::Const<'tcx>,
29     param_env: ty::ParamEnv<'tcx>,
30     span: Span,
31 ) -> Result<(), NotConstEvaluatable> {
32     let tcx = infcx.tcx;
33     let uv = match ct.kind() {
34         ty::ConstKind::Unevaluated(uv) => uv,
35         // FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
36         ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
37         ty::ConstKind::Param(_)
38         | ty::ConstKind::Bound(_, _)
39         | ty::ConstKind::Placeholder(_)
40         | ty::ConstKind::Value(_)
41         | ty::ConstKind::Error(_) => return Ok(()),
42         ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
43     };
44
45     if tcx.features().generic_const_exprs {
46         let ct = tcx.expand_abstract_consts(ct);
47
48         let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
49             tcx.def_kind(uv.def.did) == DefKind::AnonConst
50         } else {
51             false
52         };
53
54         if !is_anon_ct {
55             if satisfied_from_param_env(tcx, infcx, ct, param_env) {
56                 return Ok(());
57             }
58             if ct.has_non_region_infer() {
59                 return Err(NotConstEvaluatable::MentionsInfer);
60             } else if ct.has_non_region_param() {
61                 return Err(NotConstEvaluatable::MentionsParam);
62             }
63         }
64
65         let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
66         match concrete {
67             Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
68                 infcx
69                     .tcx
70                     .sess
71                     .delay_span_bug(span, "Missing value for constant, but no error reported?"),
72             )),
73             Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
74             Ok(_) => Ok(()),
75         }
76     } else {
77         // FIXME: We should only try to evaluate a given constant here if it is fully concrete
78         // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
79         //
80         // We previously did not check this, so we only emit a future compat warning if
81         // const evaluation succeeds and the given constant is still polymorphic for now
82         // and hopefully soon change this to an error.
83         //
84         // See #74595 for more details about this.
85         let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
86         match concrete {
87             // If we're evaluating a generic foreign constant, under a nightly compiler while
88             // the current crate does not enable `feature(generic_const_exprs)`, abort
89             // compilation with a useful error.
90             Err(_)
91                 if tcx.sess.is_nightly_build()
92                     && satisfied_from_param_env(
93                         tcx,
94                         infcx,
95                         tcx.expand_abstract_consts(ct),
96                         param_env,
97                     ) =>
98             {
99                 tcx.sess
100                     .struct_span_fatal(
101                         // Slightly better span than just using `span` alone
102                         if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span },
103                         "failed to evaluate generic const expression",
104                     )
105                     .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
106                     .span_suggestion_verbose(
107                         rustc_span::DUMMY_SP,
108                         "consider enabling this feature",
109                         "#![feature(generic_const_exprs)]\n",
110                         rustc_errors::Applicability::MaybeIncorrect,
111                     )
112                     .emit()
113             }
114
115             Err(ErrorHandled::TooGeneric) => {
116                 let err = if uv.has_non_region_infer() {
117                     NotConstEvaluatable::MentionsInfer
118                 } else if uv.has_non_region_param() {
119                     NotConstEvaluatable::MentionsParam
120                 } else {
121                     let guar = infcx.tcx.sess.delay_span_bug(
122                         span,
123                         format!("Missing value for constant, but no error reported?"),
124                     );
125                     NotConstEvaluatable::Error(guar)
126                 };
127
128                 Err(err)
129             }
130             Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
131             Ok(_) => Ok(()),
132         }
133     }
134 }
135
136 #[instrument(skip(infcx, tcx), level = "debug")]
137 fn satisfied_from_param_env<'tcx>(
138     tcx: TyCtxt<'tcx>,
139     infcx: &InferCtxt<'tcx>,
140     ct: ty::Const<'tcx>,
141     param_env: ty::ParamEnv<'tcx>,
142 ) -> bool {
143     // Try to unify with each subtree in the AbstractConst to allow for
144     // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
145     // predicate for `(N + 1) * 2`
146     struct Visitor<'a, 'tcx> {
147         ct: ty::Const<'tcx>,
148         param_env: ty::ParamEnv<'tcx>,
149
150         infcx: &'a InferCtxt<'tcx>,
151     }
152     impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
153         type BreakTy = ();
154         fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
155             if let Ok(()) = self.infcx.commit_if_ok(|_| {
156                 let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
157                 if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
158                     && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct)
159                     && ocx.select_all_or_error().is_empty()
160                 {
161                     Ok(())
162                 } else {
163                     Err(())
164                 }
165             }) {
166                 ControlFlow::BREAK
167             } else if let ty::ConstKind::Expr(e) = c.kind() {
168                 e.visit_with(self)
169             } else {
170                 // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s substs.
171                 // This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const
172                 // with its own `ConstEvaluatable` bound in the param env which we will visit separately.
173                 //
174                 // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
175                 // this will be incorrect. It might be worth investigating making `predicates_of` elaborate
176                 // all of the `ConstEvaluatable` bounds rather than having a visitor here.
177                 ControlFlow::CONTINUE
178             }
179         }
180     }
181
182     for pred in param_env.caller_bounds() {
183         match pred.kind().skip_binder() {
184             ty::PredicateKind::ConstEvaluatable(ce) => {
185                 let b_ct = tcx.expand_abstract_consts(ce);
186                 let mut v = Visitor { ct, infcx, param_env };
187                 let result = b_ct.visit_with(&mut v);
188
189                 if let ControlFlow::Break(()) = result {
190                     debug!("is_const_evaluatable: abstract_const ~~> ok");
191                     return true;
192                 }
193             }
194             _ => {} // don't care
195         }
196     }
197
198     false
199 }