Skip to content

Commit 0e23349

Browse files
committed
use trait solver instead; created spaghetti code
1 parent e24fc9a commit 0e23349

File tree

3 files changed

+50
-35
lines changed

3 files changed

+50
-35
lines changed

clippy_utils/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern crate rustc_ast;
2121
extern crate rustc_ast_pretty;
2222
extern crate rustc_attr;
2323
extern crate rustc_data_structures;
24+
extern crate rustc_const_eval;
2425
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
2526
#[allow(unused_extern_crates)]
2627
extern crate rustc_driver;

clippy_utils/src/qualify_min_const_fn.rs

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@
44
// differ from the time of `rustc` even if the name stays the same.
55

66
use crate::msrvs::Msrv;
7+
use hir::{Constness, LangItem};
8+
use rustc_const_eval::transform::check_consts::ConstCx;
79
use rustc_hir as hir;
810
use rustc_hir::def_id::DefId;
11+
use rustc_infer::infer::TyCtxtInferExt;
12+
use rustc_infer::traits::Obligation;
913
use rustc_middle::mir::{
1014
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
1115
Terminator, TerminatorKind,
1216
};
17+
use rustc_middle::traits::ObligationCause;
1318
use rustc_middle::ty::subst::GenericArgKind;
1419
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
20+
use rustc_middle::ty::{BoundConstness, TraitRef};
1521
use rustc_semver::RustcVersion;
1622
use rustc_span::symbol::sym;
1723
use rustc_span::Span;
24+
use rustc_trait_selection::traits::SelectionContext;
1825
use std::borrow::Cow;
1926

2027
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)
5259
}
5360

5461
for local in &body.local_decls {
55-
check_ty(tcx, local.ty, local.source_info.span, false)?;
62+
check_ty(tcx, local.ty, local.source_info.span)?;
5663
}
5764
// impl trait is gone in MIR, so check the return type manually
5865
check_ty(
5966
tcx,
6067
tcx.fn_sig(def_id).subst_identity().output().skip_binder(),
6168
body.local_decls.iter().next().unwrap().source_info.span,
62-
false,
6369
)?;
6470

6571
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)
7177
Ok(())
7278
}
7379

74-
fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) -> McfResult {
80+
fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
7581
for arg in ty.walk() {
7682
let ty = match arg.unpack() {
7783
GenericArgKind::Type(ty) => ty,
@@ -81,27 +87,6 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) ->
8187
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
8288
};
8389

84-
// Only do this check if we're in `TerminatorKind::Drop`, otherwise rustc will sometimes overflow
85-
// its stack. This check is unnecessary outside of a `Drop` anyway so it's faster regardless
86-
if in_drop && let ty::Adt(def, subst) = ty.kind() {
87-
if def.has_non_const_dtor(tcx) && in_drop {
88-
return Err((
89-
span,
90-
"cannot drop locals with a non constant destructor in const fn".into(),
91-
));
92-
}
93-
94-
for fields in def.variants().iter().map(|v| &v.fields) {
95-
for field in fields {
96-
check_ty(tcx, field.ty(tcx, subst), span, in_drop)?;
97-
}
98-
}
99-
100-
for field in def.all_fields() {
101-
check_ty(tcx, field.ty(tcx, subst), span, in_drop)?;
102-
}
103-
}
104-
10590
match ty.kind() {
10691
ty::Ref(_, _, hir::Mutability::Mut) => {
10792
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
288273

289274
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
290275
let mut cursor = place.projection.as_ref();
276+
291277
while let [ref proj_base @ .., elem] = *cursor {
292278
cursor = proj_base;
293279
match elem {
@@ -327,20 +313,19 @@ fn check_terminator<'tcx>(
327313
| TerminatorKind::Resume
328314
| TerminatorKind::Terminate
329315
| TerminatorKind::Unreachable => Ok(()),
330-
331316
TerminatorKind::Drop { place, .. } => {
332-
for local in &body.local_decls {
333-
check_ty(tcx, local.ty, span, true)?;
317+
if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
318+
return Err((
319+
span,
320+
"cannot drop locals with a non constant destructor in const fn".into(),
321+
));
334322
}
335323
check_place(tcx, *place, span, body)
336324
},
337-
338325
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
339-
340326
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
341327
Err((span, "const fn generators are unstable".into()))
342328
},
343-
344329
TerminatorKind::Call {
345330
func,
346331
args,
@@ -384,15 +369,13 @@ fn check_terminator<'tcx>(
384369
Err((span, "can only call other const fns within const fn".into()))
385370
}
386371
},
387-
388372
TerminatorKind::Assert {
389373
cond,
390374
expected: _,
391375
msg: _,
392376
target: _,
393377
unwind: _,
394378
} => check_operand(tcx, cond, span, body),
395-
396379
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
397380
}
398381
}
@@ -406,8 +389,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
406389
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
407390

408391
// HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
409-
// doesn't accept the `-dev` version number so we have to strip it
410-
// off.
392+
// doesn't accept the `-dev` version number so we have to strip it off.
411393
let short_version = since
412394
.as_str()
413395
.split('-')
@@ -425,3 +407,33 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
425407
}
426408
})
427409
}
410+
411+
fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
412+
// Avoid selecting for simple cases, such as builtin types.
413+
if ty::util::is_trivially_const_drop(ty) {
414+
return true;
415+
}
416+
417+
let obligation = Obligation::new(
418+
tcx,
419+
ObligationCause::dummy_with_span(body.span),
420+
ConstCx::new(tcx, body).param_env.with_constness(Constness::Const),
421+
TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]).with_constness(BoundConstness::ConstIfConst),
422+
);
423+
424+
let fields_all_const_destruct = if let ty::Adt(def, subst) = ty.kind() && !ty.is_union() {
425+
// This is such a mess even rustfmt doesn't wanna touch it
426+
def.all_fields()
427+
.map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body))
428+
.all(|f| f)
429+
&& def.variants().iter()
430+
.map(|variant| variant.fields.iter().map(|field| is_ty_const_destruct(tcx, field.ty(tcx, subst), body)))
431+
.all(|mut fs| fs.all(|f| f))
432+
} else {
433+
true
434+
};
435+
436+
let infcx = tcx.infer_ctxt().build();
437+
let mut selcx = SelectionContext::new(&infcx);
438+
selcx.select(&obligation).is_ok() && fields_all_const_destruct
439+
}

tests/ui/missing_const_for_fn/cant_be_const.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extern crate proc_macros;
1313

1414
use proc_macros::with_span;
1515

16-
struct Game;
16+
struct Game; // You just lost.
1717

1818
// This should not be linted because it's already const
1919
const fn already_const() -> i32 {
@@ -135,3 +135,5 @@ enum A {
135135
}
136136

137137
fn b(this: A) {}
138+
139+
fn c(this: Vec<u16>) {}

0 commit comments

Comments
 (0)