Skip to content

Commit fa54b19

Browse files
committed
[arithmetic_side_effects] Fix #10792
1 parent c490974 commit fa54b19

File tree

7 files changed

+86
-58
lines changed

7 files changed

+86
-58
lines changed

clippy_lints/src/enum_clike.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
5151
.const_eval_poly(def_id.to_def_id())
5252
.ok()
5353
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty));
54-
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx.tcx, c)) {
54+
if let Some(Constant::Int(val)) = constant.and_then(|c| miri_to_const(cx, c, None)) {
5555
if let ty::Adt(adt, _) = ty.kind() {
5656
if adt.is_enum() {
5757
ty = adt.repr().discr_type().to_ty(cx.tcx);

clippy_lints/src/matches/overlapping_arms.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
4141
cx.tcx.valtree_to_const_val((ty, min_val_const.to_valtree())),
4242
ty,
4343
);
44-
miri_to_const(cx.tcx, min_constant)?
44+
miri_to_const(cx, min_constant, None)?
4545
},
4646
};
4747
let rhs_const = match rhs {
@@ -52,7 +52,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
5252
cx.tcx.valtree_to_const_val((ty, max_val_const.to_valtree())),
5353
ty,
5454
);
55-
miri_to_const(cx.tcx, max_constant)?
55+
miri_to_const(cx, max_constant, None)?
5656
},
5757
};
5858
let lhs_val = lhs_const.int_value(cx, ty)?;

clippy_lints/src/methods/repeat_once.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
1414
recv: &'tcx Expr<'_>,
1515
repeat_arg: &'tcx Expr<'_>,
1616
) {
17-
if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
17+
if constant_context(cx, cx.typeck_results()).expr(repeat_arg, None) == Some(Constant::Int(1)) {
1818
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
1919
if ty.is_str() {
2020
span_lint_and_sugg(

clippy_lints/src/transmute/transmuting_null.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
1818
// Catching transmute over constants that resolve to `null`.
1919
let mut const_eval_context = constant_context(cx, cx.typeck_results());
2020
if let ExprKind::Path(ref _qpath) = arg.kind &&
21-
let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg)
21+
let Some(Constant::RawPtr(0)) = const_eval_context.expr(arg, None)
2222
{
2323
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
2424
return true;

clippy_utils/src/consts.rs

+64-51
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::mir::interpret::Scalar;
1212
use rustc_middle::ty::SubstsRef;
1313
use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
1414
use rustc_middle::{bug, span_bug};
15-
use rustc_span::symbol::Symbol;
15+
use rustc_span::symbol::{Ident, Symbol};
1616
use std::cmp::Ordering::{self, Equal};
1717
use std::hash::{Hash, Hasher};
1818
use std::iter;
@@ -239,7 +239,7 @@ pub fn constant<'tcx>(
239239
needed_resolution: false,
240240
substs: ty::List::empty(),
241241
};
242-
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
242+
cx.expr(e, None).map(|cst| (cst, cx.needed_resolution))
243243
}
244244

245245
pub fn constant_simple<'tcx>(
@@ -320,9 +320,9 @@ pub struct ConstEvalLateContext<'a, 'tcx> {
320320

321321
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
322322
/// Simple constant folding: Insert an expression, get a constant or none.
323-
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
323+
pub fn expr(&mut self, e: &Expr<'_>, field: Option<Ident>) -> Option<Constant> {
324324
match e.kind {
325-
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
325+
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e), field),
326326
ExprKind::Block(block, _) => self.block(block),
327327
ExprKind::Lit(lit) => {
328328
if is_direct_expn_of(e.span, "cfg").is_some() {
@@ -338,9 +338,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
338338
ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?,
339339
_ => span_bug!(e.span, "typeck error"),
340340
};
341-
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
341+
self.expr(value, None).map(|v| Constant::Repeat(Box::new(v), n))
342342
},
343-
ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
343+
ExprKind::Unary(op, operand) => self.expr(operand, None).and_then(|o| match op {
344344
UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
345345
UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
346346
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
@@ -373,8 +373,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
373373
}
374374
},
375375
ExprKind::Index(arr, index) => self.index(arr, index),
376-
ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
377-
// TODO: add other expressions.
376+
ExprKind::AddrOf(_, _, inner) => self.expr(inner, None).map(|r| Constant::Ref(Box::new(r))),
377+
ExprKind::Field(local_expr, field) => self.expr(local_expr, Some(field)),
378378
_ => None,
379379
}
380380
}
@@ -416,27 +416,24 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
416416
/// Create `Some(Vec![..])` of all constants, unless there is any
417417
/// non-constant part.
418418
fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
419-
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
419+
vec.iter().map(|elem| self.expr(elem, None)).collect::<Option<_>>()
420420
}
421421

422422
/// Lookup a possibly constant expression from an `ExprKind::Path`.
423-
fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
423+
fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, field: Option<Ident>) -> Option<Constant> {
424424
let res = self.typeck_results.qpath_res(qpath, id);
425425
match res {
426426
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
427427
// Check if this constant is based on `cfg!(..)`,
428428
// which is NOT constant for our purposes.
429-
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
430-
let Node::Item(&Item {
431-
kind: ItemKind::Const(_, body_id),
432-
..
433-
}) = node &&
434-
let Node::Expr(&Expr {
435-
kind: ExprKind::Lit(_),
436-
span,
437-
..
438-
}) = self.lcx.tcx.hir().get(body_id.hir_id) &&
439-
is_direct_expn_of(span, "cfg").is_some() {
429+
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
430+
&& let Node::Item(Item { kind: ItemKind::Const(_, body_id), .. }) = node
431+
&& let Node::Expr(Expr { kind: ExprKind::Lit(_), span, .. }) = self.lcx
432+
.tcx
433+
.hir()
434+
.get(body_id.hir_id)
435+
&& is_direct_expn_of(*span, "cfg").is_some()
436+
{
440437
return None;
441438
}
442439

@@ -446,27 +443,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
446443
} else {
447444
EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
448445
};
449-
450446
let result = self
451447
.lcx
452448
.tcx
453449
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None)
454450
.ok()
455451
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
456-
let result = miri_to_const(self.lcx.tcx, result);
452+
let result = miri_to_const(self.lcx, result, field);
457453
if result.is_some() {
458454
self.needed_resolution = true;
459455
}
460456
result
461457
},
462-
// FIXME: cover all usable cases.
463458
_ => None,
464459
}
465460
}
466461

467462
fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
468-
let lhs = self.expr(lhs);
469-
let index = self.expr(index);
463+
let lhs = self.expr(lhs, None);
464+
let index = self.expr(index, None);
470465

471466
match (lhs, index) {
472467
(Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
@@ -492,27 +487,27 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
492487
/// A block can only yield a constant if it only has one constant expression.
493488
fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
494489
if block.stmts.is_empty() {
495-
block.expr.as_ref().and_then(|b| self.expr(b))
490+
block.expr.as_ref().and_then(|b| self.expr(b, None))
496491
} else {
497492
None
498493
}
499494
}
500495

501496
fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
502-
if let Some(Constant::Bool(b)) = self.expr(cond) {
497+
if let Some(Constant::Bool(b)) = self.expr(cond, None) {
503498
if b {
504-
self.expr(then)
499+
self.expr(then, None)
505500
} else {
506-
otherwise.as_ref().and_then(|expr| self.expr(expr))
501+
otherwise.as_ref().and_then(|expr| self.expr(expr, None))
507502
}
508503
} else {
509504
None
510505
}
511506
}
512507

513508
fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
514-
let l = self.expr(left)?;
515-
let r = self.expr(right);
509+
let l = self.expr(left, None)?;
510+
let r = self.expr(right, None);
516511
match (l, r) {
517512
(Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
518513
ty::Int(ity) => {
@@ -603,23 +598,29 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
603598
}
604599
}
605600

606-
pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant> {
601+
pub fn miri_to_const<'tcx>(
602+
lcx: &LateContext<'tcx>,
603+
result: mir::ConstantKind<'tcx>,
604+
field: Option<Ident>,
605+
) -> Option<Constant> {
607606
use rustc_middle::mir::interpret::ConstValue;
608607
match result {
609-
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => {
610-
match result.ty().kind() {
611-
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
612-
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
613-
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
614-
int.try_into().expect("invalid f32 bit representation"),
615-
))),
616-
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
617-
int.try_into().expect("invalid f64 bit representation"),
618-
))),
619-
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
620-
// FIXME: implement other conversions.
621-
_ => None,
622-
}
608+
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
609+
ty::Adt(adt_def, _) if adt_def.is_struct() => {
610+
let dc = lcx.tcx.destructure_mir_constant(lcx.param_env, result);
611+
let &[field] = dc.fields else { return None; };
612+
miri_to_const(lcx, field, None)
613+
},
614+
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
615+
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
616+
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
617+
int.try_into().expect("invalid f32 bit representation"),
618+
))),
619+
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
620+
int.try_into().expect("invalid f64 bit representation"),
621+
))),
622+
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
623+
_ => None,
623624
},
624625
mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
625626
ty::Ref(_, tam, _) => match tam.kind() {
@@ -635,8 +636,22 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
635636
_ => None,
636637
},
637638
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
639+
ty::Adt(adt_def, _) if adt_def.is_struct() => {
640+
let dc = lcx.tcx.destructure_mir_constant(lcx.param_env, result);
641+
if let Some(dc_variant) = dc.variant
642+
&& let Some(local_field) = field
643+
&& let Some(variant) = &adt_def.variants().get(dc_variant)
644+
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == local_field.name)
645+
&& let Some(dc_field) = dc.fields.get(field_idx)
646+
{
647+
miri_to_const(lcx, *dc_field, None)
648+
}
649+
else {
650+
None
651+
}
652+
},
638653
ty::Array(sub_type, len) => match sub_type.kind() {
639-
ty::Float(FloatTy::F32) => match len.kind().try_to_target_usize(tcx) {
654+
ty::Float(FloatTy::F32) => match len.kind().try_to_target_usize(lcx.tcx) {
640655
Some(len) => alloc
641656
.inner()
642657
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
@@ -647,7 +662,7 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
647662
.map(Constant::Vec),
648663
_ => None,
649664
},
650-
ty::Float(FloatTy::F64) => match len.kind().try_to_target_usize(tcx) {
665+
ty::Float(FloatTy::F64) => match len.kind().try_to_target_usize(lcx.tcx) {
651666
Some(len) => alloc
652667
.inner()
653668
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
@@ -658,12 +673,10 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
658673
.map(Constant::Vec),
659674
_ => None,
660675
},
661-
// FIXME: implement other array type conversions.
662676
_ => None,
663677
},
664678
_ => None,
665679
},
666-
// FIXME: implement other conversions.
667680
_ => None,
668681
}
669682
}

clippy_utils/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1497,7 +1497,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
14971497
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
14981498
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
14991499
&& let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
1500-
&& let Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
1500+
&& let Some(min_const) = miri_to_const(cx, min_const_kind, None)
15011501
&& let Some((start_const, _)) = constant(cx, cx.typeck_results(), start)
15021502
{
15031503
start_const == min_const
@@ -1513,7 +1513,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
15131513
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
15141514
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
15151515
&& let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
1516-
&& let Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
1516+
&& let Some(max_const) = miri_to_const(cx, max_const_kind, None)
15171517
&& let Some((end_const, _)) = constant(cx, cx.typeck_results(), end)
15181518
{
15191519
end_const == max_const

tests/ui/arithmetic_side_effects.rs

+15
Original file line numberDiff line numberDiff line change
@@ -466,4 +466,19 @@ pub fn issue_10767() {
466466
&3.5_f32 + &1.3_f32;
467467
}
468468

469+
pub fn issue_10792() {
470+
struct One {
471+
a: u32,
472+
}
473+
struct Two {
474+
b: u32,
475+
c: u64,
476+
}
477+
const ONE: One = One { a: 1 };
478+
const TWO: Two = Two { b: 2, c: 3 };
479+
let _ = 10 / ONE.a;
480+
let _ = 10 / TWO.b;
481+
let _ = 10 / TWO.c;
482+
}
483+
469484
fn main() {}

0 commit comments

Comments
 (0)