From 0e233492d31cd0e213f284414fc4bdc1372df777 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:43:28 -0500 Subject: [PATCH] use trait solver instead; created spaghetti code --- clippy_utils/src/lib.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 80 +++++++++++-------- .../ui/missing_const_for_fn/cant_be_const.rs | 4 +- 3 files changed, 50 insertions(+), 35 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8c883445a798..e600dc58c9da 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -21,6 +21,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; extern crate rustc_data_structures; +extern crate rustc_const_eval; // The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. #[allow(unused_extern_crates)] extern crate rustc_driver; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index a38045cdcd16..cdaf8985d5e7 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -4,17 +4,24 @@ // differ from the time of `rustc` even if the name stays the same. use crate::msrvs::Msrv; +use hir::{Constness, LangItem}; +use rustc_const_eval::transform::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::Obligation; use rustc_middle::mir::{ Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; +use rustc_middle::ty::{BoundConstness, TraitRef}; use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::traits::SelectionContext; use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; @@ -52,14 +59,13 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) } for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span, false)?; + check_ty(tcx, local.ty, local.source_info.span)?; } // impl trait is gone in MIR, so check the return type manually check_ty( tcx, tcx.fn_sig(def_id).subst_identity().output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, - false, )?; for bb in body.basic_blocks.iter() { @@ -71,7 +77,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) Ok(()) } -fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { for arg in ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -81,27 +87,6 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) -> GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; - // Only do this check if we're in `TerminatorKind::Drop`, otherwise rustc will sometimes overflow - // its stack. This check is unnecessary outside of a `Drop` anyway so it's faster regardless - if in_drop && let ty::Adt(def, subst) = ty.kind() { - if def.has_non_const_dtor(tcx) && in_drop { - return Err(( - span, - "cannot drop locals with a non constant destructor in const fn".into(), - )); - } - - for fields in def.variants().iter().map(|v| &v.fields) { - for field in fields { - check_ty(tcx, field.ty(tcx, subst), span, in_drop)?; - } - } - - for field in def.all_fields() { - check_ty(tcx, field.ty(tcx, subst), span, in_drop)?; - } - } - match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { return Err((span, "mutable references in const fn are unstable".into())); @@ -288,6 +273,7 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); + while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; match elem { @@ -327,20 +313,19 @@ fn check_terminator<'tcx>( | TerminatorKind::Resume | TerminatorKind::Terminate | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => { - for local in &body.local_decls { - check_ty(tcx, local.ty, span, true)?; + if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) { + return Err(( + span, + "cannot drop locals with a non constant destructor in const fn".into(), + )); } check_place(tcx, *place, span, body) }, - TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body), - TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) }, - TerminatorKind::Call { func, args, @@ -384,7 +369,6 @@ fn check_terminator<'tcx>( Err((span, "can only call other const fns within const fn".into())) } }, - TerminatorKind::Assert { cond, expected: _, @@ -392,7 +376,6 @@ fn check_terminator<'tcx>( target: _, unwind: _, } => check_operand(tcx, cond, span, body), - TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } @@ -406,8 +389,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver` - // doesn't accept the `-dev` version number so we have to strip it - // off. + // doesn't accept the `-dev` version number so we have to strip it off. let short_version = since .as_str() .split('-') @@ -425,3 +407,33 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { } }) } + +fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool { + // Avoid selecting for simple cases, such as builtin types. + if ty::util::is_trivially_const_drop(ty) { + return true; + } + + let obligation = Obligation::new( + tcx, + ObligationCause::dummy_with_span(body.span), + ConstCx::new(tcx, body).param_env.with_constness(Constness::Const), + TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]).with_constness(BoundConstness::ConstIfConst), + ); + + let fields_all_const_destruct = if let ty::Adt(def, subst) = ty.kind() && !ty.is_union() { + // This is such a mess even rustfmt doesn't wanna touch it + def.all_fields() + .map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body)) + .all(|f| f) + && def.variants().iter() + .map(|variant| variant.fields.iter().map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body))) + .all(|mut fs| fs.all(|f| f)) + } else { + true + }; + + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + selcx.select(&obligation).is_ok() && fields_all_const_destruct +} diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index dffc23177d19..4fe02a9cef87 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -13,7 +13,7 @@ extern crate proc_macros; use proc_macros::with_span; -struct Game; +struct Game; // You just lost. // This should not be linted because it's already const const fn already_const() -> i32 { @@ -135,3 +135,5 @@ enum A { } fn b(this: A) {} + +fn c(this: Vec) {}