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+ //! `mir_abstract_const` which can then be checked for structural equality with other
10+ //! generic constants mentioned in the `caller_bounds` of the current environment.
111use rustc_hir:: def:: DefKind ;
212use rustc_index:: bit_set:: BitSet ;
313use rustc_index:: vec:: IndexVec ;
@@ -129,13 +139,19 @@ impl AbstractConst<'tcx> {
129139struct AbstractConstBuilder < ' a , ' tcx > {
130140 tcx : TyCtxt < ' tcx > ,
131141 body : & ' a mir:: Body < ' tcx > ,
142+ /// The current WIP node tree.
132143 nodes : IndexVec < NodeId , Node < ' tcx > > ,
133144 locals : IndexVec < mir:: Local , NodeId > ,
145+ /// We only allow field accesses if they access
146+ /// the result of a checked operation.
134147 checked_op_locals : BitSet < mir:: Local > ,
135148}
136149
137150impl < ' a , ' tcx > AbstractConstBuilder < ' a , ' tcx > {
138151 fn new ( tcx : TyCtxt < ' tcx > , body : & ' a mir:: Body < ' tcx > ) -> Option < AbstractConstBuilder < ' a , ' tcx > > {
152+ // We only allow consts without control flow, so
153+ // we check for cycles here which simplifies the
154+ // rest of this implementation.
139155 if body. is_cfg_cyclic ( ) {
140156 return None ;
141157 }
@@ -154,17 +170,21 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
154170 checked_op_locals : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
155171 } )
156172 }
157-
158173 fn operand_to_node ( & mut self , op : & mir:: Operand < ' tcx > ) -> Option < NodeId > {
159174 debug ! ( "operand_to_node: op={:?}" , op) ;
160175 const ZERO_FIELD : mir:: Field = mir:: Field :: from_usize ( 0 ) ;
161176 match op {
162177 mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => {
178+ // Do not allow any projections.
179+ //
180+ // One exception are field accesses on the result of checked operations,
181+ // which are required to support things like `1 + 2`.
163182 if let Some ( p) = p. as_local ( ) {
164183 debug_assert ! ( !self . checked_op_locals. contains( p) ) ;
165184 Some ( self . locals [ p] )
166185 } else if let & [ mir:: ProjectionElem :: Field ( ZERO_FIELD , _) ] = p. projection . as_ref ( ) {
167- // Only allow field accesses on the result of checked operations.
186+ // Only allow field accesses if the given local
187+ // contains the result of a checked operation.
168188 if self . checked_op_locals . contains ( p. local ) {
169189 Some ( self . locals [ p. local ] )
170190 } else {
@@ -238,6 +258,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
238258 }
239259 }
240260
261+ /// Possible return values:
262+ ///
263+ /// - `None`: unsupported terminator, stop building
264+ /// - `Some(None)`: supported terminator, finish building
265+ /// - `Some(Some(block))`: support terminator, build `block` next
241266 fn build_terminator (
242267 & mut self ,
243268 terminator : & mir:: Terminator < ' tcx > ,
@@ -250,7 +275,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
250275 ref func,
251276 ref args,
252277 destination : Some ( ( ref place, target) ) ,
278+ // We do not care about `cleanup` here. Any branch which
279+ // uses `cleanup` will fail const-eval and they therefore
280+ // do not matter when checking for const evaluatability.
281+ //
282+ // Do note that even if `panic::catch_unwind` is made const,
283+ // we still do not have to care about this, as we do not look
284+ // into functions.
253285 cleanup : _,
286+ // Do not allow overloaded operators for now,
287+ // we probably do want to allow this in the future.
288+ //
289+ // This is currently fairly irrelevant as it requires `const Trait`s.
254290 from_hir_call : true ,
255291 fn_span : _,
256292 } => {
@@ -264,10 +300,14 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
264300 self . locals [ local] = self . nodes . push ( Node :: FunctionCall ( func, args) ) ;
265301 Some ( Some ( target) )
266302 }
303+ // We only allow asserts for checked operations.
304+ //
305+ // These asserts seem to all have the form `!_local.0` so
306+ // we only allow exactly that.
267307 TerminatorKind :: Assert { ref cond, expected : false , target, .. } => {
268308 let p = match cond {
269309 mir:: Operand :: Copy ( p) | mir:: Operand :: Move ( p) => p,
270- mir:: Operand :: Constant ( _) => bug ! ( "Unexpected assert" ) ,
310+ mir:: Operand :: Constant ( _) => bug ! ( "unexpected assert" ) ,
271311 } ;
272312
273313 const ONE_FIELD : mir:: Field = mir:: Field :: from_usize ( 1 ) ;
@@ -285,8 +325,11 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
285325 }
286326 }
287327
328+ /// Builds the abstract const by walking the mir from start to finish
329+ /// and bailing out when encountering an unsupported operation.
288330 fn build ( mut self ) -> Option < & ' tcx [ Node < ' tcx > ] > {
289331 let mut block = & self . body . basic_blocks ( ) [ mir:: START_BLOCK ] ;
332+ // We checked for a cyclic cfg above, so this should terminate.
290333 loop {
291334 debug ! ( "AbstractConstBuilder: block={:?}" , block) ;
292335 for stmt in block. statements . iter ( ) {
@@ -340,6 +383,7 @@ pub(super) fn try_unify_abstract_consts<'tcx>(
340383 false
341384}
342385
386+ /// Tries to unify two abstract constants using structural equality.
343387pub ( super ) fn try_unify < ' tcx > (
344388 tcx : TyCtxt < ' tcx > ,
345389 a : AbstractConst < ' tcx > ,
0 commit comments