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 } ;
7
+ use hir:: LangItem ;
8
8
use rustc_const_eval:: transform:: check_consts:: ConstCx ;
9
9
use rustc_hir as hir;
10
10
use rustc_hir:: def_id:: DefId ;
@@ -14,14 +14,14 @@ use rustc_middle::mir::{
14
14
Body , CastKind , NonDivergingIntrinsic , NullOp , Operand , Place , ProjectionElem , Rvalue , Statement , StatementKind ,
15
15
Terminator , TerminatorKind ,
16
16
} ;
17
- use rustc_middle:: traits:: ObligationCause ;
17
+ use rustc_middle:: traits:: { ImplSource , ObligationCause } ;
18
18
use rustc_middle:: ty:: subst:: GenericArgKind ;
19
19
use rustc_middle:: ty:: { self , adjustment:: PointerCast , Ty , TyCtxt } ;
20
20
use rustc_middle:: ty:: { BoundConstness , TraitRef } ;
21
21
use rustc_semver:: RustcVersion ;
22
22
use rustc_span:: symbol:: sym;
23
23
use rustc_span:: Span ;
24
- use rustc_trait_selection:: traits:: SelectionContext ;
24
+ use rustc_trait_selection:: traits:: { ObligationCtxt , SelectionContext } ;
25
25
use std:: borrow:: Cow ;
26
26
27
27
type McfResult = Result < ( ) , ( Span , Cow < ' static , str > ) > ;
@@ -135,9 +135,9 @@ fn check_rvalue<'tcx>(
135
135
match rvalue {
136
136
Rvalue :: ThreadLocalRef ( _) => Err ( ( span, "cannot access thread local storage in const fn" . into ( ) ) ) ,
137
137
Rvalue :: Len ( place) | Rvalue :: Discriminant ( place) | Rvalue :: Ref ( _, _, place) | Rvalue :: AddressOf ( _, place) => {
138
- check_place ( tcx, * place, span, body)
138
+ check_place ( tcx, * place, span, body, false )
139
139
} ,
140
- Rvalue :: CopyForDeref ( place) => check_place ( tcx, * place, span, body) ,
140
+ Rvalue :: CopyForDeref ( place) => check_place ( tcx, * place, span, body, false ) ,
141
141
Rvalue :: Repeat ( operand, _)
142
142
| Rvalue :: Use ( operand)
143
143
| Rvalue :: Cast (
@@ -230,14 +230,14 @@ fn check_statement<'tcx>(
230
230
let span = statement. source_info . span ;
231
231
match & statement. kind {
232
232
StatementKind :: Assign ( box ( place, rval) ) => {
233
- check_place ( tcx, * place, span, body) ?;
233
+ check_place ( tcx, * place, span, body, false ) ?;
234
234
check_rvalue ( tcx, body, def_id, rval, span)
235
235
} ,
236
236
237
- StatementKind :: FakeRead ( box ( _, place) ) => check_place ( tcx, * place, span, body) ,
237
+ StatementKind :: FakeRead ( box ( _, place) ) => check_place ( tcx, * place, span, body, false ) ,
238
238
// just an assignment
239
239
StatementKind :: SetDiscriminant { place, .. } | StatementKind :: Deinit ( place) => {
240
- check_place ( tcx, * * place, span, body)
240
+ check_place ( tcx, * * place, span, body, false )
241
241
} ,
242
242
243
243
StatementKind :: Intrinsic ( box NonDivergingIntrinsic :: Assume ( op) ) => check_operand ( tcx, op, span, body) ,
@@ -263,21 +263,29 @@ fn check_statement<'tcx>(
263
263
264
264
fn check_operand < ' tcx > ( tcx : TyCtxt < ' tcx > , operand : & Operand < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
265
265
match operand {
266
- Operand :: Move ( place) | Operand :: Copy ( place) => check_place ( tcx, * place, span, body) ,
266
+ Operand :: Move ( place) => check_place ( tcx, * place, span, body, true ) ,
267
+ Operand :: Copy ( place) => check_place ( tcx, * place, span, body, false ) ,
267
268
Operand :: Constant ( c) => match c. check_static_ptr ( tcx) {
268
269
Some ( _) => Err ( ( span, "cannot access `static` items in const fn" . into ( ) ) ) ,
269
270
None => Ok ( ( ) ) ,
270
271
} ,
271
272
}
272
273
}
273
274
274
- fn check_place < ' tcx > ( tcx : TyCtxt < ' tcx > , place : Place < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
275
+ fn check_place < ' tcx > ( tcx : TyCtxt < ' tcx > , place : Place < ' tcx > , span : Span , body : & Body < ' tcx > , in_move : bool ) -> McfResult {
275
276
let mut cursor = place. projection . as_ref ( ) ;
276
277
277
278
while let [ ref proj_base @ .., elem] = * cursor {
278
279
cursor = proj_base;
279
280
match elem {
280
- ProjectionElem :: Field ( ..) => {
281
+ ProjectionElem :: Field ( _, ty) => {
282
+ if !is_ty_const_destruct ( tcx, ty, body) && in_move {
283
+ return Err ( (
284
+ span,
285
+ "cannot drop locals with a non constant destructor in const fn" . into ( ) ,
286
+ ) ) ;
287
+ }
288
+
281
289
let base_ty = Place :: ty_from ( place. local , proj_base, body, tcx) . ty ;
282
290
if let Some ( def) = base_ty. ty_adt_def ( ) {
283
291
// No union field accesses in `const fn`
@@ -320,7 +328,7 @@ fn check_terminator<'tcx>(
320
328
"cannot drop locals with a non constant destructor in const fn" . into ( ) ,
321
329
) ) ;
322
330
}
323
- check_place ( tcx, * place, span, body)
331
+ check_place ( tcx, * place, span, body, false )
324
332
} ,
325
333
TerminatorKind :: SwitchInt { discr, targets : _ } => check_operand ( tcx, discr, span, body) ,
326
334
TerminatorKind :: GeneratorDrop | TerminatorKind :: Yield { .. } => {
@@ -408,6 +416,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
408
416
} )
409
417
}
410
418
419
+ #[ expect( clippy:: similar_names) ] // bit too pedantic
411
420
fn is_ty_const_destruct < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , body : & Body < ' tcx > ) -> bool {
412
421
// Avoid selecting for simple cases, such as builtin types.
413
422
if ty:: util:: is_trivially_const_drop ( ty) {
@@ -417,23 +426,24 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>
417
426
let obligation = Obligation :: new (
418
427
tcx,
419
428
ObligationCause :: dummy_with_span ( body. span ) ,
420
- ConstCx :: new ( tcx, body) . param_env . with_constness ( Constness :: Const ) ,
429
+ ConstCx :: new ( tcx, body) . param_env . with_const ( ) ,
421
430
TraitRef :: from_lang_item ( tcx, LangItem :: Destruct , body. span , [ ty] ) . with_constness ( BoundConstness :: ConstIfConst ) ,
422
431
) ;
423
432
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
433
let infcx = tcx. infer_ctxt ( ) . build ( ) ;
437
434
let mut selcx = SelectionContext :: new ( & infcx) ;
438
- selcx. select ( & obligation) . is_ok ( ) && fields_all_const_destruct
435
+ let Some ( impl_src) = selcx. select ( & obligation) . ok ( ) . flatten ( ) else {
436
+ return false ;
437
+ } ;
438
+
439
+ if !matches ! (
440
+ impl_src,
441
+ ImplSource :: ConstDestruct ( _) | ImplSource :: Param ( _, ty:: BoundConstness :: ConstIfConst )
442
+ ) {
443
+ return false ;
444
+ }
445
+
446
+ let ocx = ObligationCtxt :: new ( & infcx) ;
447
+ ocx. register_obligations ( impl_src. nested_obligations ( ) ) ;
448
+ ocx. select_all_or_error ( ) . is_empty ( )
439
449
}
0 commit comments