4
4
// differ from the time of `rustc` even if the name stays the same.
5
5
6
6
use crate :: msrvs:: Msrv ;
7
+ use hir:: { Constness , LangItem } ;
8
+ use rustc_const_eval:: transform:: check_consts:: ConstCx ;
7
9
use rustc_hir as hir;
8
10
use rustc_hir:: def_id:: DefId ;
11
+ use rustc_infer:: infer:: TyCtxtInferExt ;
12
+ use rustc_infer:: traits:: Obligation ;
9
13
use rustc_middle:: mir:: {
10
14
Body , CastKind , NonDivergingIntrinsic , NullOp , Operand , Place , ProjectionElem , Rvalue , Statement , StatementKind ,
11
15
Terminator , TerminatorKind ,
12
16
} ;
17
+ use rustc_middle:: traits:: ObligationCause ;
13
18
use rustc_middle:: ty:: subst:: GenericArgKind ;
14
19
use rustc_middle:: ty:: { self , adjustment:: PointerCast , Ty , TyCtxt } ;
20
+ use rustc_middle:: ty:: { BoundConstness , TraitRef } ;
15
21
use rustc_semver:: RustcVersion ;
16
22
use rustc_span:: symbol:: sym;
17
23
use rustc_span:: Span ;
24
+ use rustc_trait_selection:: traits:: SelectionContext ;
18
25
use std:: borrow:: Cow ;
19
26
20
27
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)
52
59
}
53
60
54
61
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 ) ?;
56
63
}
57
64
// impl trait is gone in MIR, so check the return type manually
58
65
check_ty (
59
66
tcx,
60
67
tcx. fn_sig ( def_id) . subst_identity ( ) . output ( ) . skip_binder ( ) ,
61
68
body. local_decls . iter ( ) . next ( ) . unwrap ( ) . source_info . span ,
62
- false ,
63
69
) ?;
64
70
65
71
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)
71
77
Ok ( ( ) )
72
78
}
73
79
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 {
75
81
for arg in ty. walk ( ) {
76
82
let ty = match arg. unpack ( ) {
77
83
GenericArgKind :: Type ( ty) => ty,
@@ -81,27 +87,6 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, in_drop: bool) ->
81
87
GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => continue ,
82
88
} ;
83
89
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
-
105
90
match ty. kind ( ) {
106
91
ty:: Ref ( _, _, hir:: Mutability :: Mut ) => {
107
92
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
288
273
289
274
fn check_place < ' tcx > ( tcx : TyCtxt < ' tcx > , place : Place < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
290
275
let mut cursor = place. projection . as_ref ( ) ;
276
+
291
277
while let [ ref proj_base @ .., elem] = * cursor {
292
278
cursor = proj_base;
293
279
match elem {
@@ -327,20 +313,19 @@ fn check_terminator<'tcx>(
327
313
| TerminatorKind :: Resume
328
314
| TerminatorKind :: Terminate
329
315
| TerminatorKind :: Unreachable => Ok ( ( ) ) ,
330
-
331
316
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
+ ) ) ;
334
322
}
335
323
check_place ( tcx, * place, span, body)
336
324
} ,
337
-
338
325
TerminatorKind :: SwitchInt { discr, targets : _ } => check_operand ( tcx, discr, span, body) ,
339
-
340
326
TerminatorKind :: GeneratorDrop | TerminatorKind :: Yield { .. } => {
341
327
Err ( ( span, "const fn generators are unstable" . into ( ) ) )
342
328
} ,
343
-
344
329
TerminatorKind :: Call {
345
330
func,
346
331
args,
@@ -384,15 +369,13 @@ fn check_terminator<'tcx>(
384
369
Err ( ( span, "can only call other const fns within const fn" . into ( ) ) )
385
370
}
386
371
} ,
387
-
388
372
TerminatorKind :: Assert {
389
373
cond,
390
374
expected : _,
391
375
msg : _,
392
376
target : _,
393
377
unwind : _,
394
378
} => check_operand ( tcx, cond, span, body) ,
395
-
396
379
TerminatorKind :: InlineAsm { .. } => Err ( ( span, "cannot use inline assembly in const fn" . into ( ) ) ) ,
397
380
}
398
381
}
@@ -406,8 +389,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
406
389
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
407
390
408
391
// 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.
411
393
let short_version = since
412
394
. as_str ( )
413
395
. split ( '-' )
@@ -425,3 +407,33 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
425
407
}
426
408
} )
427
409
}
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
+ }
0 commit comments