• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::{ErrorHandled, EvalToConstValueResult, EvalToValTreeResult, GlobalId};
2 
3 use crate::mir;
4 use crate::query::{TyCtxtAt, TyCtxtEnsure};
5 use crate::ty::subst::InternalSubsts;
6 use crate::ty::visit::TypeVisitableExt;
7 use crate::ty::{self, TyCtxt};
8 use rustc_hir::def::DefKind;
9 use rustc_hir::def_id::DefId;
10 use rustc_session::lint;
11 use rustc_span::{Span, DUMMY_SP};
12 
13 impl<'tcx> TyCtxt<'tcx> {
14     /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts
15     /// that can't take any generic arguments like statics, const items or enum discriminants. If a
16     /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned.
17     #[instrument(skip(self), level = "debug")]
const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx>18     pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> {
19         // In some situations def_id will have substitutions within scope, but they aren't allowed
20         // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions
21         // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are
22         // encountered.
23         let substs = InternalSubsts::identity_for_item(self, def_id);
24         let instance = ty::Instance::new(def_id, substs);
25         let cid = GlobalId { instance, promoted: None };
26         let param_env = self.param_env(def_id).with_reveal_all_normalized(self);
27         self.const_eval_global_id(param_env, cid, None)
28     }
29     /// Resolves and evaluates a constant.
30     ///
31     /// The constant can be located on a trait like `<A as B>::C`, in which case the given
32     /// substitutions and environment are used to resolve the constant. Alternatively if the
33     /// constant has generic parameters in scope the substitutions are used to evaluate the value of
34     /// the constant. For example in `fn foo<T>() { let _ = [0; bar::<T>()]; }` the repeat count
35     /// constant `bar::<T>()` requires a substitution for `T`, if the substitution for `T` is still
36     /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is
37     /// returned.
38     #[instrument(level = "debug", skip(self))]
const_eval_resolve( self, param_env: ty::ParamEnv<'tcx>, ct: mir::UnevaluatedConst<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx>39     pub fn const_eval_resolve(
40         self,
41         param_env: ty::ParamEnv<'tcx>,
42         ct: mir::UnevaluatedConst<'tcx>,
43         span: Option<Span>,
44     ) -> EvalToConstValueResult<'tcx> {
45         // Cannot resolve `Unevaluated` constants that contain inference
46         // variables. We reject those here since `resolve`
47         // would fail otherwise.
48         //
49         // When trying to evaluate constants containing inference variables,
50         // use `Infcx::const_eval_resolve` instead.
51         if ct.substs.has_non_region_infer() {
52             bug!("did not expect inference variables here");
53         }
54 
55         match ty::Instance::resolve(
56             self, param_env,
57             // FIXME: maybe have a separate version for resolving mir::UnevaluatedConst?
58             ct.def, ct.substs,
59         ) {
60             Ok(Some(instance)) => {
61                 let cid = GlobalId { instance, promoted: ct.promoted };
62                 self.const_eval_global_id(param_env, cid, span)
63             }
64             Ok(None) => Err(ErrorHandled::TooGeneric),
65             Err(err) => Err(ErrorHandled::Reported(err.into())),
66         }
67     }
68 
69     #[instrument(level = "debug", skip(self))]
const_eval_resolve_for_typeck( self, param_env: ty::ParamEnv<'tcx>, ct: ty::UnevaluatedConst<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx>70     pub fn const_eval_resolve_for_typeck(
71         self,
72         param_env: ty::ParamEnv<'tcx>,
73         ct: ty::UnevaluatedConst<'tcx>,
74         span: Option<Span>,
75     ) -> EvalToValTreeResult<'tcx> {
76         // Cannot resolve `Unevaluated` constants that contain inference
77         // variables. We reject those here since `resolve`
78         // would fail otherwise.
79         //
80         // When trying to evaluate constants containing inference variables,
81         // use `Infcx::const_eval_resolve` instead.
82         if ct.substs.has_non_region_infer() {
83             bug!("did not expect inference variables here");
84         }
85 
86         match ty::Instance::resolve(self, param_env, ct.def, ct.substs) {
87             Ok(Some(instance)) => {
88                 let cid = GlobalId { instance, promoted: None };
89                 self.const_eval_global_id_for_typeck(param_env, cid, span).inspect(|_| {
90                     // We are emitting the lint here instead of in `is_const_evaluatable`
91                     // as we normalize obligations before checking them, and normalization
92                     // uses this function to evaluate this constant.
93                     //
94                     // @lcnr believes that successfully evaluating even though there are
95                     // used generic parameters is a bug of evaluation, so checking for it
96                     // here does feel somewhat sensible.
97                     if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
98                         let def_kind = self.def_kind(instance.def_id());
99                         assert!(
100                             matches!(
101                                 def_kind,
102                                 DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
103                             ),
104                             "{cid:?} is {def_kind:?}",
105                         );
106                         let mir_body = self.mir_for_ctfe(instance.def_id());
107                         if mir_body.is_polymorphic {
108                             let Some(local_def_id) = ct.def.as_local() else { return };
109                             self.struct_span_lint_hir(
110                                 lint::builtin::CONST_EVALUATABLE_UNCHECKED,
111                                 self.hir().local_def_id_to_hir_id(local_def_id),
112                                 self.def_span(ct.def),
113                                 "cannot use constants which depend on generic parameters in types",
114                                 |err| err,
115                             )
116                         }
117                     }
118                 })
119             }
120             Ok(None) => Err(ErrorHandled::TooGeneric),
121             Err(err) => Err(ErrorHandled::Reported(err.into())),
122         }
123     }
124 
const_eval_instance( self, param_env: ty::ParamEnv<'tcx>, instance: ty::Instance<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx>125     pub fn const_eval_instance(
126         self,
127         param_env: ty::ParamEnv<'tcx>,
128         instance: ty::Instance<'tcx>,
129         span: Option<Span>,
130     ) -> EvalToConstValueResult<'tcx> {
131         self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span)
132     }
133 
134     /// Evaluate a constant to a `ConstValue`.
135     #[instrument(skip(self), level = "debug")]
const_eval_global_id( self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx>136     pub fn const_eval_global_id(
137         self,
138         param_env: ty::ParamEnv<'tcx>,
139         cid: GlobalId<'tcx>,
140         span: Option<Span>,
141     ) -> EvalToConstValueResult<'tcx> {
142         let param_env = param_env.with_const();
143         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
144         // improve caching of queries.
145         let inputs = self.erase_regions(param_env.and(cid));
146         if let Some(span) = span {
147             self.at(span).eval_to_const_value_raw(inputs)
148         } else {
149             self.eval_to_const_value_raw(inputs)
150         }
151     }
152 
153     /// Evaluate a constant to a type-level constant.
154     #[instrument(skip(self), level = "debug")]
const_eval_global_id_for_typeck( self, param_env: ty::ParamEnv<'tcx>, cid: GlobalId<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx>155     pub fn const_eval_global_id_for_typeck(
156         self,
157         param_env: ty::ParamEnv<'tcx>,
158         cid: GlobalId<'tcx>,
159         span: Option<Span>,
160     ) -> EvalToValTreeResult<'tcx> {
161         let param_env = param_env.with_const();
162         debug!(?param_env);
163         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
164         // improve caching of queries.
165         let inputs = self.erase_regions(param_env.and(cid));
166         debug!(?inputs);
167         if let Some(span) = span {
168             self.at(span).eval_to_valtree(inputs)
169         } else {
170             self.eval_to_valtree(inputs)
171         }
172     }
173 
174     /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
175     #[inline(always)]
eval_static_initializer( self, def_id: DefId, ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled>176     pub fn eval_static_initializer(
177         self,
178         def_id: DefId,
179     ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
180         self.at(DUMMY_SP).eval_static_initializer(def_id)
181     }
182 }
183 
184 impl<'tcx> TyCtxtAt<'tcx> {
185     /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
186     ///
187     /// The span is entirely ignored here, but still helpful for better query cycle errors.
eval_static_initializer( self, def_id: DefId, ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled>188     pub fn eval_static_initializer(
189         self,
190         def_id: DefId,
191     ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
192         trace!("eval_static_initializer: Need to compute {:?}", def_id);
193         assert!(self.is_static(def_id));
194         let instance = ty::Instance::mono(*self, def_id);
195         let gid = GlobalId { instance, promoted: None };
196         self.eval_to_allocation(gid, ty::ParamEnv::reveal_all())
197     }
198 
199     /// Evaluate anything constant-like, returning the allocation of the final memory.
200     ///
201     /// The span is entirely ignored here, but still helpful for better query cycle errors.
eval_to_allocation( self, gid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled>202     fn eval_to_allocation(
203         self,
204         gid: GlobalId<'tcx>,
205         param_env: ty::ParamEnv<'tcx>,
206     ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> {
207         let param_env = param_env.with_const();
208         trace!("eval_to_allocation: Need to compute {:?}", gid);
209         let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
210         Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
211     }
212 }
213 
214 impl<'tcx> TyCtxtEnsure<'tcx> {
215     /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts
216     /// that can't take any generic arguments like statics, const items or enum discriminants. If a
217     /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned.
218     #[instrument(skip(self), level = "debug")]
const_eval_poly(self, def_id: DefId)219     pub fn const_eval_poly(self, def_id: DefId) {
220         // In some situations def_id will have substitutions within scope, but they aren't allowed
221         // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions
222         // into `const_eval` which will return `ErrorHandled::ToGeneric` if any of them are
223         // encountered.
224         let substs = InternalSubsts::identity_for_item(self.tcx, def_id);
225         let instance = ty::Instance::new(def_id, substs);
226         let cid = GlobalId { instance, promoted: None };
227         let param_env =
228             self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx).with_const();
229         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
230         // improve caching of queries.
231         let inputs = self.tcx.erase_regions(param_env.and(cid));
232         self.eval_to_const_value_raw(inputs)
233     }
234 
235     /// Evaluate a static's initializer, returning the allocation of the initializer's memory.
eval_static_initializer(self, def_id: DefId)236     pub fn eval_static_initializer(self, def_id: DefId) {
237         trace!("eval_static_initializer: Need to compute {:?}", def_id);
238         assert!(self.tcx.is_static(def_id));
239         let instance = ty::Instance::mono(self.tcx, def_id);
240         let gid = GlobalId { instance, promoted: None };
241         let param_env = ty::ParamEnv::reveal_all().with_const();
242         trace!("eval_to_allocation: Need to compute {:?}", gid);
243         self.eval_to_allocation_raw(param_env.and(gid))
244     }
245 }
246