|
| 1 | +/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some |
| 2 | +/// code for promotion or prevent it from evaluating at compile time. So `return true` means |
| 3 | +/// "I found something bad, no reason to go on searching". `false` is only returned if we |
| 4 | +/// definitely cannot find anything bad anywhere. |
| 5 | +/// |
| 6 | +/// The default implementations proceed structurally. |
| 7 | +trait Qualif { |
| 8 | + const IDX: usize; |
| 9 | + |
| 10 | + /// Return the qualification that is (conservatively) correct for any value |
| 11 | + /// of the type, or `None` if the qualification is not value/type-based. |
| 12 | + fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> { |
| 13 | + None |
| 14 | + } |
| 15 | + |
| 16 | + /// Return a mask for the qualification, given a type. This is `false` iff |
| 17 | + /// no value of that type can have the qualification. |
| 18 | + fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool { |
| 19 | + Self::in_any_value_of_ty(cx, ty).unwrap_or(true) |
| 20 | + } |
| 21 | + |
| 22 | + fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool { |
| 23 | + cx.per_local.0[Self::IDX].contains(local) |
| 24 | + } |
| 25 | + |
| 26 | + fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool { |
| 27 | + // FIXME(eddyb) should we do anything here for value properties? |
| 28 | + false |
| 29 | + } |
| 30 | + |
| 31 | + fn in_projection_structurally( |
| 32 | + cx: &ConstCx<'_, 'tcx>, |
| 33 | + place: PlaceRef<'_, 'tcx>, |
| 34 | + ) -> bool { |
| 35 | + if let [proj_base @ .., elem] = place.projection { |
| 36 | + let base_qualif = Self::in_place(cx, PlaceRef { |
| 37 | + base: place.base, |
| 38 | + projection: proj_base, |
| 39 | + }); |
| 40 | + let qualif = base_qualif && Self::mask_for_ty( |
| 41 | + cx, |
| 42 | + Place::ty_from(place.base, proj_base, cx.body, cx.tcx) |
| 43 | + .projection_ty(cx.tcx, elem) |
| 44 | + .ty, |
| 45 | + ); |
| 46 | + match elem { |
| 47 | + ProjectionElem::Deref | |
| 48 | + ProjectionElem::Subslice { .. } | |
| 49 | + ProjectionElem::Field(..) | |
| 50 | + ProjectionElem::ConstantIndex { .. } | |
| 51 | + ProjectionElem::Downcast(..) => qualif, |
| 52 | + |
| 53 | + ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local), |
| 54 | + } |
| 55 | + } else { |
| 56 | + bug!("This should be called if projection is not empty"); |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + fn in_projection( |
| 61 | + cx: &ConstCx<'_, 'tcx>, |
| 62 | + place: PlaceRef<'_, 'tcx>, |
| 63 | + ) -> bool { |
| 64 | + Self::in_projection_structurally(cx, place) |
| 65 | + } |
| 66 | + |
| 67 | + fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool { |
| 68 | + match place { |
| 69 | + PlaceRef { |
| 70 | + base: PlaceBase::Local(local), |
| 71 | + projection: [], |
| 72 | + } => Self::in_local(cx, *local), |
| 73 | + PlaceRef { |
| 74 | + base: PlaceBase::Static(box Static { |
| 75 | + kind: StaticKind::Promoted(..), |
| 76 | + .. |
| 77 | + }), |
| 78 | + projection: [], |
| 79 | + } => bug!("qualifying already promoted MIR"), |
| 80 | + PlaceRef { |
| 81 | + base: PlaceBase::Static(static_), |
| 82 | + projection: [], |
| 83 | + } => { |
| 84 | + Self::in_static(cx, static_) |
| 85 | + }, |
| 86 | + PlaceRef { |
| 87 | + base: _, |
| 88 | + projection: [.., _], |
| 89 | + } => Self::in_projection(cx, place), |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool { |
| 94 | + match *operand { |
| 95 | + Operand::Copy(ref place) | |
| 96 | + Operand::Move(ref place) => Self::in_place(cx, place.as_ref()), |
| 97 | + |
| 98 | + Operand::Constant(ref constant) => { |
| 99 | + if let ConstValue::Unevaluated(def_id, _) = constant.literal.val { |
| 100 | + // Don't peek inside trait associated constants. |
| 101 | + if cx.tcx.trait_of_item(def_id).is_some() { |
| 102 | + Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false) |
| 103 | + } else { |
| 104 | + let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id); |
| 105 | + |
| 106 | + let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX]; |
| 107 | + |
| 108 | + // Just in case the type is more specific than |
| 109 | + // the definition, e.g., impl associated const |
| 110 | + // with type parameters, take it into account. |
| 111 | + qualif && Self::mask_for_ty(cx, constant.literal.ty) |
| 112 | + } |
| 113 | + } else { |
| 114 | + false |
| 115 | + } |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { |
| 121 | + match *rvalue { |
| 122 | + Rvalue::NullaryOp(..) => false, |
| 123 | + |
| 124 | + Rvalue::Discriminant(ref place) | |
| 125 | + Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()), |
| 126 | + |
| 127 | + Rvalue::Use(ref operand) | |
| 128 | + Rvalue::Repeat(ref operand, _) | |
| 129 | + Rvalue::UnaryOp(_, ref operand) | |
| 130 | + Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand), |
| 131 | + |
| 132 | + Rvalue::BinaryOp(_, ref lhs, ref rhs) | |
| 133 | + Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => { |
| 134 | + Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs) |
| 135 | + } |
| 136 | + |
| 137 | + Rvalue::Ref(_, _, ref place) => { |
| 138 | + // Special-case reborrows to be more like a copy of the reference. |
| 139 | + if let box [proj_base @ .., elem] = &place.projection { |
| 140 | + if ProjectionElem::Deref == *elem { |
| 141 | + let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty; |
| 142 | + if let ty::Ref(..) = base_ty.sty { |
| 143 | + return Self::in_place(cx, PlaceRef { |
| 144 | + base: &place.base, |
| 145 | + projection: proj_base, |
| 146 | + }); |
| 147 | + } |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + Self::in_place(cx, place.as_ref()) |
| 152 | + } |
| 153 | + |
| 154 | + Rvalue::Aggregate(_, ref operands) => { |
| 155 | + operands.iter().any(|o| Self::in_operand(cx, o)) |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { |
| 161 | + Self::in_rvalue_structurally(cx, rvalue) |
| 162 | + } |
| 163 | + |
| 164 | + fn in_call( |
| 165 | + cx: &ConstCx<'_, 'tcx>, |
| 166 | + _callee: &Operand<'tcx>, |
| 167 | + _args: &[Operand<'tcx>], |
| 168 | + return_ty: Ty<'tcx>, |
| 169 | + ) -> bool { |
| 170 | + // Be conservative about the returned value of a const fn. |
| 171 | + Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false) |
| 172 | + } |
| 173 | + |
| 174 | + fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool { |
| 175 | + match source { |
| 176 | + ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue), |
| 177 | + ValueSource::DropAndReplace(source) => Self::in_operand(cx, source), |
| 178 | + ValueSource::Call { callee, args, return_ty } => { |
| 179 | + Self::in_call(cx, callee, args, return_ty) |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +/// Constant containing interior mutability (`UnsafeCell<T>`). |
| 186 | +/// This must be ruled out to make sure that evaluating the constant at compile-time |
| 187 | +/// and at *any point* during the run-time would produce the same result. In particular, |
| 188 | +/// promotion of temporaries must not change program behavior; if the promoted could be |
| 189 | +/// written to, that would be a problem. |
| 190 | +struct HasMutInterior; |
| 191 | + |
| 192 | +impl Qualif for HasMutInterior { |
| 193 | + const IDX: usize = 0; |
| 194 | + |
| 195 | + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> { |
| 196 | + Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)) |
| 197 | + } |
| 198 | + |
| 199 | + fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { |
| 200 | + match *rvalue { |
| 201 | + // Returning `true` for `Rvalue::Ref` indicates the borrow isn't |
| 202 | + // allowed in constants (and the `Checker` will error), and/or it |
| 203 | + // won't be promoted, due to `&mut ...` or interior mutability. |
| 204 | + Rvalue::Ref(_, kind, ref place) => { |
| 205 | + let ty = place.ty(cx.body, cx.tcx).ty; |
| 206 | + |
| 207 | + if let BorrowKind::Mut { .. } = kind { |
| 208 | + // In theory, any zero-sized value could be borrowed |
| 209 | + // mutably without consequences. However, only &mut [] |
| 210 | + // is allowed right now, and only in functions. |
| 211 | + if cx.mode == Mode::StaticMut { |
| 212 | + // Inside a `static mut`, &mut [...] is also allowed. |
| 213 | + match ty.sty { |
| 214 | + ty::Array(..) | ty::Slice(_) => {} |
| 215 | + _ => return true, |
| 216 | + } |
| 217 | + } else if let ty::Array(_, len) = ty.sty { |
| 218 | + // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition |
| 219 | + // seems unnecessary, given that this is merely a ZST. |
| 220 | + match len.try_eval_usize(cx.tcx, cx.param_env) { |
| 221 | + Some(0) if cx.mode == Mode::NonConstFn => {}, |
| 222 | + _ => return true, |
| 223 | + } |
| 224 | + } else { |
| 225 | + return true; |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + Rvalue::Aggregate(ref kind, _) => { |
| 231 | + if let AggregateKind::Adt(def, ..) = **kind { |
| 232 | + if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() { |
| 233 | + let ty = rvalue.ty(cx.body, cx.tcx); |
| 234 | + assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true)); |
| 235 | + return true; |
| 236 | + } |
| 237 | + } |
| 238 | + } |
| 239 | + |
| 240 | + _ => {} |
| 241 | + } |
| 242 | + |
| 243 | + Self::in_rvalue_structurally(cx, rvalue) |
| 244 | + } |
| 245 | +} |
| 246 | + |
| 247 | +/// Constant containing an ADT that implements `Drop`. |
| 248 | +/// This must be ruled out (a) because we cannot run `Drop` during compile-time |
| 249 | +/// as that might not be a `const fn`, and (b) because implicit promotion would |
| 250 | +/// remove side-effects that occur as part of dropping that value. |
| 251 | +struct NeedsDrop; |
| 252 | + |
| 253 | +impl Qualif for NeedsDrop { |
| 254 | + const IDX: usize = 1; |
| 255 | + |
| 256 | + fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> { |
| 257 | + Some(ty.needs_drop(cx.tcx, cx.param_env)) |
| 258 | + } |
| 259 | + |
| 260 | + fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool { |
| 261 | + if let Rvalue::Aggregate(ref kind, _) = *rvalue { |
| 262 | + if let AggregateKind::Adt(def, ..) = **kind { |
| 263 | + if def.has_dtor(cx.tcx) { |
| 264 | + return true; |
| 265 | + } |
| 266 | + } |
| 267 | + } |
| 268 | + |
| 269 | + Self::in_rvalue_structurally(cx, rvalue) |
| 270 | + } |
| 271 | +} |
0 commit comments