Don't store LateContext in ConstEvalLateContext

This commit is contained in:
Jason Newcomb 2024-07-31 17:51:09 -04:00
parent 3779062955
commit d2cb227eb4
6 changed files with 75 additions and 78 deletions

View File

@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
.const_eval_poly(def_id.to_def_id()) .const_eval_poly(def_id.to_def_id())
.ok() .ok()
.map(|val| rustc_middle::mir::Const::from_value(val, ty)); .map(|val| rustc_middle::mir::Const::from_value(val, ty));
if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) { if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
if let ty::Adt(adt, _) = ty.kind() { if let ty::Adt(adt, _) = ty.kind() {
if adt.is_enum() { if adt.is_enum() {
ty = adt.repr().discr_type().to_ty(cx.tcx); ty = adt.repr().discr_type().to_ty(cx.tcx);

View File

@ -37,13 +37,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
constant(cx, cx.typeck_results(), lhs)? constant(cx, cx.typeck_results(), lhs)?
} else { } else {
let min_val_const = ty.numeric_min_val(cx.tcx)?; let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))? mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
}; };
let rhs_const = if let Some(rhs) = rhs { let rhs_const = if let Some(rhs) = rhs {
constant(cx, cx.typeck_results(), rhs)? constant(cx, cx.typeck_results(), rhs)?
} else { } else {
let max_val_const = ty.numeric_max_val(cx.tcx)?; let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))? mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
}; };
let lhs_val = lhs_const.int_value(cx, ty)?; let lhs_val = lhs_const.int_value(cx, ty)?;
let rhs_val = rhs_const.int_value(cx, ty)?; let rhs_val = rhs_const.int_value(cx, ty)?;

View File

@ -20,7 +20,7 @@ use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42` // Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
fn comparison_to_const<'tcx>( fn comparison_to_const<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
typeck: &TypeckResults<'tcx>, typeck: &'tcx TypeckResults<'tcx>,
expr: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>,
) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
if let ExprKind::Binary(operator, left, right) = expr.kind if let ExprKind::Binary(operator, left, right) = expr.kind

View File

@ -34,7 +34,7 @@ fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>)
fn check_op<'tcx>( fn check_op<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
tck: &TypeckResults<'tcx>, tck: &'tcx TypeckResults<'tcx>,
op: &Expr<'tcx>, op: &Expr<'tcx>,
other: &Expr<'tcx>, other: &Expr<'tcx>,
parent: &Expr<'tcx>, parent: &Expr<'tcx>,

View File

@ -14,10 +14,12 @@ use rustc_lexer::tokenize;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::interpret::{alloc_range, Scalar};
use rustc_middle::mir::ConstValue; use rustc_middle::mir::ConstValue;
use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::ty::{
self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy,
};
use rustc_middle::{bug, mir, span_bug}; use rustc_middle::{bug, mir, span_bug};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::Ident;
use rustc_span::{sym, SyntaxContext}; use rustc_span::{sym, SyntaxContext};
use rustc_target::abi::Size; use rustc_target::abi::Size;
use std::cmp::Ordering; use std::cmp::Ordering;
@ -339,25 +341,25 @@ impl ConstantSource {
/// Attempts to check whether the expression is a constant representing an empty slice, str, array, /// Attempts to check whether the expression is a constant representing an empty slice, str, array,
/// etc… /// etc…
pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> { pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> {
ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) ConstEvalLateContext::new(lcx.tcx, lcx.param_env, lcx.typeck_results()).expr_is_empty(e)
} }
/// Attempts to evaluate the expression as a constant. /// Attempts to evaluate the expression as a constant.
pub fn constant<'tcx>( pub fn constant<'tcx>(
lcx: &LateContext<'tcx>, lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
e: &Expr<'_>, e: &Expr<'_>,
) -> Option<Constant<'tcx>> { ) -> Option<Constant<'tcx>> {
ConstEvalLateContext::new(lcx, typeck_results).expr(e) ConstEvalLateContext::new(lcx.tcx, lcx.param_env, typeck_results).expr(e)
} }
/// Attempts to evaluate the expression as a constant. /// Attempts to evaluate the expression as a constant.
pub fn constant_with_source<'tcx>( pub fn constant_with_source<'tcx>(
lcx: &LateContext<'tcx>, lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
e: &Expr<'_>, e: &Expr<'_>,
) -> Option<(Constant<'tcx>, ConstantSource)> { ) -> Option<(Constant<'tcx>, ConstantSource)> {
let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results); let mut ctxt = ConstEvalLateContext::new(lcx.tcx, lcx.param_env, typeck_results);
let res = ctxt.expr(e); let res = ctxt.expr(e);
res.map(|x| (x, ctxt.source)) res.map(|x| (x, ctxt.source))
} }
@ -365,7 +367,7 @@ pub fn constant_with_source<'tcx>(
/// Attempts to evaluate an expression only if its value is not dependent on other items. /// Attempts to evaluate an expression only if its value is not dependent on other items.
pub fn constant_simple<'tcx>( pub fn constant_simple<'tcx>(
lcx: &LateContext<'tcx>, lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
e: &Expr<'_>, e: &Expr<'_>,
) -> Option<Constant<'tcx>> { ) -> Option<Constant<'tcx>> {
constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c)) constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
@ -373,7 +375,7 @@ pub fn constant_simple<'tcx>(
pub fn constant_full_int<'tcx>( pub fn constant_full_int<'tcx>(
lcx: &LateContext<'tcx>, lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
e: &Expr<'_>, e: &Expr<'_>,
) -> Option<FullInt> { ) -> Option<FullInt> {
constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e)) constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
@ -417,20 +419,20 @@ impl Ord for FullInt {
} }
} }
pub struct ConstEvalLateContext<'a, 'tcx> { pub struct ConstEvalLateContext<'tcx> {
lcx: &'a LateContext<'tcx>, tcx: TyCtxt<'tcx>,
typeck_results: &'a ty::TypeckResults<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
source: ConstantSource, source: ConstantSource,
args: GenericArgsRef<'tcx>, args: GenericArgsRef<'tcx>,
} }
impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { impl<'tcx> ConstEvalLateContext<'tcx> {
pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { pub fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self {
Self { Self {
lcx, tcx,
typeck_results, typeck_results,
param_env: lcx.param_env, param_env,
source: ConstantSource::Local, source: ConstantSource::Local,
args: List::empty(), args: List::empty(),
} }
@ -439,18 +441,19 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Simple constant folding: Insert an expression, get a constant or none. /// Simple constant folding: Insert an expression, get a constant or none.
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> { pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind { match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr(e), ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
let is_core_crate = if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() { let is_core_crate = if let Some(def_id) = self.typeck_results.qpath_res(qpath, e.hir_id()).opt_def_id()
self.lcx.tcx.crate_name(def_id.krate) == sym::core {
self.tcx.crate_name(def_id.krate) == sym::core
} else { } else {
false false
}; };
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |self_, result| {
let result = mir_to_const(this.lcx, result)?; let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant // If source is already Constant we wouldn't want to override it with CoreConstant
this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) { self_.source = if is_core_crate && !matches!(self_.source, ConstantSource::Constant) {
ConstantSource::CoreConstant ConstantSource::CoreConstant
} else { } else {
ConstantSource::Constant ConstantSource::Constant
@ -470,7 +473,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(value, _) => { ExprKind::Repeat(value, _) => {
let n = match self.typeck_results.expr_ty(e).kind() { let n = match self.typeck_results.expr_ty(e).kind() {
ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?, ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
_ => span_bug!(e.span, "typeck error"), _ => span_bug!(e.span, "typeck error"),
}; };
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
@ -486,21 +489,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
// We only handle a few const functions for now. // We only handle a few const functions for now.
if args.is_empty() if args.is_empty()
&& let ExprKind::Path(qpath) = &callee.kind && let ExprKind::Path(qpath) = &callee.kind
&& let res = self.typeck_results.qpath_res(qpath, callee.hir_id) && let Some(did) = self.typeck_results.qpath_res(qpath, callee.hir_id).opt_def_id()
&& let Some(def_id) = res.opt_def_id()
&& let def_path = self.lcx.get_def_path(def_id)
&& let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>()
&& let ["core", "num", int_impl, "max_value"] = *def_path
{ {
let value = match int_impl { match self.tcx.get_diagnostic_name(did) {
"<impl i8>" => i8::MAX as u128, Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)),
"<impl i16>" => i16::MAX as u128, Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)),
"<impl i32>" => i32::MAX as u128, Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)),
"<impl i64>" => i64::MAX as u128, Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)),
"<impl i128>" => i128::MAX as u128, Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)),
_ => return None, _ => None,
}; }
Some(Constant::Int(value))
} else { } else {
None None
} }
@ -512,9 +510,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if let Some(Constant::Adt(constant)) = &self.expr(local_expr) if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
&& let ty::Adt(adt_def, _) = constant.ty().kind() && let ty::Adt(adt_def, _) = constant.ty().kind()
&& adt_def.is_struct() && adt_def.is_struct()
&& let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field) && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field)
{ {
mir_to_const(self.lcx, desired_field) mir_to_const(self.tcx, desired_field)
} else { } else {
result result
} }
@ -528,7 +526,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// leaves the local crate. /// leaves the local crate.
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> { pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
match e.kind { match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::DropTemps(e) => self.expr_is_empty(e),
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
if !self if !self
@ -539,8 +537,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
{ {
return None; return None;
} }
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |self_, result| {
mir_is_empty(this.lcx, result) mir_is_empty(self_.tcx, result)
}) })
}, },
ExprKind::Lit(lit) => { ExprKind::Lit(lit) => {
@ -557,7 +555,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
ExprKind::Repeat(..) => { ExprKind::Repeat(..) => {
if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() {
Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
} else { } else {
span_bug!(e.span, "typeck error"); span_bug!(e.span, "typeck error");
} }
@ -574,8 +572,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
Int(value) => { Int(value) => {
let value = !value; let value = !value;
match *ty.kind() { match *ty.kind() {
ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))), ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))),
ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))), ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))),
_ => None, _ => None,
} }
}, },
@ -590,7 +588,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let ty::Int(ity) = *ty.kind() else { return None }; let ty::Int(ity) = *ty.kind() else { return None };
let (min, _) = ity.min_max()?; let (min, _) = ity.min_max()?;
// sign extend // sign extend
let value = sext(self.lcx.tcx, value, ity); let value = sext(self.tcx, value, ity);
// Applying unary - to the most negative value of any signed integer type panics. // Applying unary - to the most negative value of any signed integer type panics.
if value == min { if value == min {
@ -599,7 +597,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let value = value.checked_neg()?; let value = value.checked_neg()?;
// clear unused bits // clear unused bits
Some(Int(unsext(self.lcx.tcx, value, ity))) Some(Int(unsext(self.tcx, value, ity)))
}, },
F32(f) => Some(F32(-f)), F32(f) => Some(F32(-f)),
F64(f) => Some(F64(-f)), F64(f) => Some(F64(-f)),
@ -623,7 +621,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
// Check if this constant is based on `cfg!(..)`, // Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes. // which is NOT constant for our purposes.
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) if let Some(node) = self.tcx.hir().get_if_local(def_id)
&& let Node::Item(Item { && let Node::Item(Item {
kind: ItemKind::Const(.., body_id), kind: ItemKind::Const(.., body_id),
.. ..
@ -632,7 +630,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
kind: ExprKind::Lit(_), kind: ExprKind::Lit(_),
span, span,
.. ..
}) = self.lcx.tcx.hir_node(body_id.hir_id) }) = self.tcx.hir_node(body_id.hir_id)
&& is_direct_expn_of(*span, "cfg").is_some() && is_direct_expn_of(*span, "cfg").is_some()
{ {
return None; return None;
@ -642,10 +640,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let args = if self.args.is_empty() { let args = if self.args.is_empty() {
args args
} else { } else {
EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args) EarlyBinder::bind(args).instantiate(self.tcx, self.args)
}; };
let result = self let result = self
.lcx
.tcx .tcx
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span()) .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
.ok() .ok()
@ -685,7 +682,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
/// A block can only yield a constant if it only has one constant expression. /// A block can only yield a constant if it has exactly one constant expression.
fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> { fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if block.stmts.is_empty() if block.stmts.is_empty()
&& let Some(expr) = block.expr && let Some(expr) = block.expr
@ -696,7 +693,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
&& let expr_lo = expr_span.lo() && let expr_lo = expr_span.lo()
&& expr_lo >= span.lo && expr_lo >= span.lo
&& let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx) && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
&& let Some(src) = src.as_str() && let Some(src) = src.as_str()
{ {
use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
@ -739,8 +736,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
ty::Int(ity) => { ty::Int(ity) => {
let (ty_min_value, _) = ity.min_max()?; let (ty_min_value, _) = ity.min_max()?;
let bits = ity.bits(); let bits = ity.bits();
let l = sext(self.lcx.tcx, l, ity); let l = sext(self.tcx, l, ity);
let r = sext(self.lcx.tcx, r, ity); let r = sext(self.tcx, r, ity);
// Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
// the right-hand argument is -1 always panics, even with overflow-checks disabled // the right-hand argument is -1 always panics, even with overflow-checks disabled
@ -751,7 +748,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
return None; return None;
} }
let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity)); let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
match op.node { match op.node {
// When +, * or binary - create a value greater than the maximum value, or less than // When +, * or binary - create a value greater than the maximum value, or less than
// the minimum value that can be stored, it panics. // the minimum value that can be stored, it panics.
@ -845,7 +842,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
} }
} }
pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> { pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
let mir::Const::Val(val, _) = result else { let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts. // We only work on evaluated consts.
return None; return None;
@ -863,13 +860,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
_ => None, _ => None,
}, },
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => { (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?; let data = val.try_get_slice_bytes_for_diagnostics(tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str) String::from_utf8(data.to_owned()).ok().map(Constant::Str)
}, },
(_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)), (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
(ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => { (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let len = len.try_to_target_usize(lcx.tcx)?; let len = len.try_to_target_usize(tcx)?;
let ty::Float(flt) = sub_type.kind() else { let ty::Float(flt) = sub_type.kind() else {
return None; return None;
}; };
@ -877,7 +874,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
let mut res = Vec::new(); let mut res = Vec::new();
for idx in 0..len { for idx in 0..len {
let range = alloc_range(offset + size * idx, size); let range = alloc_range(offset + size * idx, size);
let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?; let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
res.push(match flt { res.push(match flt {
FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)), FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)),
FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)), FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
@ -891,7 +888,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
} }
} }
fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> { fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
let mir::Const::Val(val, _) = result else { let mir::Const::Val(val, _) = result else {
// We only work on evaluated consts. // We only work on evaluated consts.
return None; return None;
@ -902,26 +899,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
if let ConstValue::Indirect { alloc_id, offset } = val { if let ConstValue::Indirect { alloc_id, offset } = val {
// Get the length from the slice, using the same formula as // Get the length from the slice, using the same formula as
// [`ConstValue::try_get_slice_bytes_for_diagnostics`]. // [`ConstValue::try_get_slice_bytes_for_diagnostics`].
let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let ptr_size = lcx.tcx.data_layout.pointer_size; let ptr_size = tcx.data_layout.pointer_size;
if a.size() < offset + 2 * ptr_size { if a.size() < offset + 2 * ptr_size {
// (partially) dangling reference // (partially) dangling reference
return None; return None;
} }
let len = a let len = a
.read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false)
.ok()? .ok()?
.to_target_usize(&lcx.tcx) .to_target_usize(&tcx)
.ok()?; .ok()?;
Some(len == 0) Some(len == 0)
} else { } else {
None None
} }
}, },
ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0),
_ => None, _ => None,
}, },
(ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0),
(ConstValue::ZeroSized, _) => Some(true), (ConstValue::ZeroSized, _) => Some(true),
_ => None, _ => None,
} }
@ -929,12 +926,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
fn field_of_struct<'tcx>( fn field_of_struct<'tcx>(
adt_def: ty::AdtDef<'tcx>, adt_def: ty::AdtDef<'tcx>,
lcx: &LateContext<'tcx>, tcx: TyCtxt<'tcx>,
result: mir::Const<'tcx>, result: mir::Const<'tcx>,
field: &Ident, field: &Ident,
) -> Option<mir::Const<'tcx>> { ) -> Option<mir::Const<'tcx>> {
if let mir::Const::Val(result, ty) = result if let mir::Const::Val(result, ty) = result
&& let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty) && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty)
&& let Some(dc_variant) = dc.variant && let Some(dc_variant) = dc.variant
&& let Some(variant) = adt_def.variants().get(dc_variant) && let Some(variant) = adt_def.variants().get(dc_variant)
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name) && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)

View File

@ -1577,7 +1577,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind() if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0) && let bnd_ty = subst.type_at(0)
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx) && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
&& let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx)) && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx))
&& let Some(start_const) = constant(cx, cx.typeck_results(), start) && let Some(start_const) = constant(cx, cx.typeck_results(), start)
{ {
start_const == min_const start_const == min_const
@ -1590,7 +1590,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
if let rustc_ty::Adt(_, subst) = ty.kind() if let rustc_ty::Adt(_, subst) = ty.kind()
&& let bnd_ty = subst.type_at(0) && let bnd_ty = subst.type_at(0)
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx) && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
&& let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx)) && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx))
&& let Some(end_const) = constant(cx, cx.typeck_results(), end) && let Some(end_const) = constant(cx, cx.typeck_results(), end)
{ {
end_const == max_const end_const == max_const