44// differ from the time of `rustc` even if the name stays the same.
55
66use crate :: msrvs:: Msrv ;
7- use hir:: { Constness , LangItem } ;
7+ use hir:: LangItem ;
88use rustc_const_eval:: transform:: check_consts:: ConstCx ;
99use rustc_hir as hir;
1010use rustc_hir:: def_id:: DefId ;
@@ -14,14 +14,14 @@ use rustc_middle::mir::{
1414 Body , CastKind , NonDivergingIntrinsic , NullOp , Operand , Place , ProjectionElem , Rvalue , Statement , StatementKind ,
1515 Terminator , TerminatorKind ,
1616} ;
17- use rustc_middle:: traits:: ObligationCause ;
17+ use rustc_middle:: traits:: { ImplSource , ObligationCause } ;
1818use rustc_middle:: ty:: subst:: GenericArgKind ;
1919use rustc_middle:: ty:: { self , adjustment:: PointerCast , Ty , TyCtxt } ;
2020use rustc_middle:: ty:: { BoundConstness , TraitRef } ;
2121use rustc_semver:: RustcVersion ;
2222use rustc_span:: symbol:: sym;
2323use rustc_span:: Span ;
24- use rustc_trait_selection:: traits:: SelectionContext ;
24+ use rustc_trait_selection:: traits:: { ObligationCtxt , SelectionContext } ;
2525use std:: borrow:: Cow ;
2626
2727type McfResult = Result < ( ) , ( Span , Cow < ' static , str > ) > ;
@@ -135,9 +135,9 @@ fn check_rvalue<'tcx>(
135135 match rvalue {
136136 Rvalue :: ThreadLocalRef ( _) => Err ( ( span, "cannot access thread local storage in const fn" . into ( ) ) ) ,
137137 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 )
139139 } ,
140- Rvalue :: CopyForDeref ( place) => check_place ( tcx, * place, span, body) ,
140+ Rvalue :: CopyForDeref ( place) => check_place ( tcx, * place, span, body, false ) ,
141141 Rvalue :: Repeat ( operand, _)
142142 | Rvalue :: Use ( operand)
143143 | Rvalue :: Cast (
@@ -230,14 +230,14 @@ fn check_statement<'tcx>(
230230 let span = statement. source_info . span ;
231231 match & statement. kind {
232232 StatementKind :: Assign ( box ( place, rval) ) => {
233- check_place ( tcx, * place, span, body) ?;
233+ check_place ( tcx, * place, span, body, false ) ?;
234234 check_rvalue ( tcx, body, def_id, rval, span)
235235 } ,
236236
237- StatementKind :: FakeRead ( box ( _, place) ) => check_place ( tcx, * place, span, body) ,
237+ StatementKind :: FakeRead ( box ( _, place) ) => check_place ( tcx, * place, span, body, false ) ,
238238 // just an assignment
239239 StatementKind :: SetDiscriminant { place, .. } | StatementKind :: Deinit ( place) => {
240- check_place ( tcx, * * place, span, body)
240+ check_place ( tcx, * * place, span, body, false )
241241 } ,
242242
243243 StatementKind :: Intrinsic ( box NonDivergingIntrinsic :: Assume ( op) ) => check_operand ( tcx, op, span, body) ,
@@ -263,21 +263,29 @@ fn check_statement<'tcx>(
263263
264264fn check_operand < ' tcx > ( tcx : TyCtxt < ' tcx > , operand : & Operand < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
265265 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 ) ,
267268 Operand :: Constant ( c) => match c. check_static_ptr ( tcx) {
268269 Some ( _) => Err ( ( span, "cannot access `static` items in const fn" . into ( ) ) ) ,
269270 None => Ok ( ( ) ) ,
270271 } ,
271272 }
272273}
273274
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 {
275276 let mut cursor = place. projection . as_ref ( ) ;
276277
277278 while let [ ref proj_base @ .., elem] = * cursor {
278279 cursor = proj_base;
279280 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+
281289 let base_ty = Place :: ty_from ( place. local , proj_base, body, tcx) . ty ;
282290 if let Some ( def) = base_ty. ty_adt_def ( ) {
283291 // No union field accesses in `const fn`
@@ -320,7 +328,7 @@ fn check_terminator<'tcx>(
320328 "cannot drop locals with a non constant destructor in const fn" . into ( ) ,
321329 ) ) ;
322330 }
323- check_place ( tcx, * place, span, body)
331+ check_place ( tcx, * place, span, body, false )
324332 } ,
325333 TerminatorKind :: SwitchInt { discr, targets : _ } => check_operand ( tcx, discr, span, body) ,
326334 TerminatorKind :: GeneratorDrop | TerminatorKind :: Yield { .. } => {
@@ -408,6 +416,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
408416 } )
409417}
410418
419+ #[ expect( clippy:: similar_names) ] // bit too pedantic
411420fn is_ty_const_destruct < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , body : & Body < ' tcx > ) -> bool {
412421 // Avoid selecting for simple cases, such as builtin types.
413422 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>
417426 let obligation = Obligation :: new (
418427 tcx,
419428 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 ( ) ,
421430 TraitRef :: from_lang_item ( tcx, LangItem :: Destruct , body. span , [ ty] ) . with_constness ( BoundConstness :: ConstIfConst ) ,
422431 ) ;
423432
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-
436433 let infcx = tcx. infer_ctxt ( ) . build ( ) ;
437434 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 ( )
439449}
0 commit comments