44// differ from the time of `rustc` even if the name stays the same.
55
66use crate :: msrvs:: Msrv ;
7+ use hir:: LangItem ;
8+ use rustc_const_eval:: transform:: check_consts:: ConstCx ;
79use rustc_hir as hir;
810use rustc_hir:: def_id:: DefId ;
11+ use rustc_infer:: infer:: TyCtxtInferExt ;
12+ use rustc_infer:: traits:: Obligation ;
913use rustc_middle:: mir:: {
1014 Body , CastKind , NonDivergingIntrinsic , NullOp , Operand , Place , ProjectionElem , Rvalue , Statement , StatementKind ,
1115 Terminator , TerminatorKind ,
1216} ;
17+ use rustc_middle:: traits:: { ImplSource , ObligationCause } ;
1318use rustc_middle:: ty:: subst:: GenericArgKind ;
1419use rustc_middle:: ty:: { self , adjustment:: PointerCast , Ty , TyCtxt } ;
20+ use rustc_middle:: ty:: { BoundConstness , TraitRef } ;
1521use rustc_semver:: RustcVersion ;
1622use rustc_span:: symbol:: sym;
1723use rustc_span:: Span ;
24+ use rustc_trait_selection:: traits:: { ObligationCtxt , SelectionContext } ;
1825use std:: borrow:: Cow ;
1926
2027type McfResult = Result < ( ) , ( Span , Cow < ' static , str > ) > ;
@@ -256,7 +263,19 @@ fn check_statement<'tcx>(
256263
257264fn check_operand < ' tcx > ( tcx : TyCtxt < ' tcx > , operand : & Operand < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
258265 match operand {
259- Operand :: Move ( place) | Operand :: Copy ( place) => check_place ( tcx, * place, span, body) ,
266+ Operand :: Move ( place) => {
267+ if !place. projection . as_ref ( ) . is_empty ( )
268+ && !is_ty_const_destruct ( tcx, place. ty ( & body. local_decls , tcx) . ty , body)
269+ {
270+ return Err ( (
271+ span,
272+ "cannot drop locals with a non constant destructor in const fn" . into ( ) ,
273+ ) ) ;
274+ }
275+
276+ check_place ( tcx, * place, span, body)
277+ } ,
278+ Operand :: Copy ( place) => check_place ( tcx, * place, span, body) ,
260279 Operand :: Constant ( c) => match c. check_static_ptr ( tcx) {
261280 Some ( _) => Err ( ( span, "cannot access `static` items in const fn" . into ( ) ) ) ,
262281 None => Ok ( ( ) ) ,
@@ -266,6 +285,7 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
266285
267286fn check_place < ' tcx > ( tcx : TyCtxt < ' tcx > , place : Place < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
268287 let mut cursor = place. projection . as_ref ( ) ;
288+
269289 while let [ ref proj_base @ .., elem] = * cursor {
270290 cursor = proj_base;
271291 match elem {
@@ -305,15 +325,19 @@ fn check_terminator<'tcx>(
305325 | TerminatorKind :: Resume
306326 | TerminatorKind :: Terminate
307327 | TerminatorKind :: Unreachable => Ok ( ( ) ) ,
308-
309- TerminatorKind :: Drop { place, .. } => check_place ( tcx, * place, span, body) ,
310-
328+ TerminatorKind :: Drop { place, .. } => {
329+ if !is_ty_const_destruct ( tcx, place. ty ( & body. local_decls , tcx) . ty , body) {
330+ return Err ( (
331+ span,
332+ "cannot drop locals with a non constant destructor in const fn" . into ( ) ,
333+ ) ) ;
334+ }
335+ Ok ( ( ) )
336+ } ,
311337 TerminatorKind :: SwitchInt { discr, targets : _ } => check_operand ( tcx, discr, span, body) ,
312-
313338 TerminatorKind :: GeneratorDrop | TerminatorKind :: Yield { .. } => {
314339 Err ( ( span, "const fn generators are unstable" . into ( ) ) )
315340 } ,
316-
317341 TerminatorKind :: Call {
318342 func,
319343 args,
@@ -357,15 +381,13 @@ fn check_terminator<'tcx>(
357381 Err ( ( span, "can only call other const fns within const fn" . into ( ) ) )
358382 }
359383 } ,
360-
361384 TerminatorKind :: Assert {
362385 cond,
363386 expected : _,
364387 msg : _,
365388 target : _,
366389 unwind : _,
367390 } => check_operand ( tcx, cond, span, body) ,
368-
369391 TerminatorKind :: InlineAsm { .. } => Err ( ( span, "cannot use inline assembly in const fn" . into ( ) ) ) ,
370392 }
371393}
@@ -379,8 +401,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
379401 // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
380402
381403 // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
382- // doesn't accept the `-dev` version number so we have to strip it
383- // off.
404+ // doesn't accept the `-dev` version number so we have to strip it off.
384405 let short_version = since
385406 . as_str ( )
386407 . split ( '-' )
@@ -398,3 +419,35 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
398419 }
399420 } )
400421}
422+
423+ #[ expect( clippy:: similar_names) ] // bit too pedantic
424+ fn is_ty_const_destruct < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , body : & Body < ' tcx > ) -> bool {
425+ // Avoid selecting for simple cases, such as builtin types.
426+ if ty:: util:: is_trivially_const_drop ( ty) {
427+ return true ;
428+ }
429+
430+ let obligation = Obligation :: new (
431+ tcx,
432+ ObligationCause :: dummy_with_span ( body. span ) ,
433+ ConstCx :: new ( tcx, body) . param_env . with_const ( ) ,
434+ TraitRef :: from_lang_item ( tcx, LangItem :: Destruct , body. span , [ ty] ) . with_constness ( BoundConstness :: ConstIfConst ) ,
435+ ) ;
436+
437+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
438+ let mut selcx = SelectionContext :: new ( & infcx) ;
439+ let Some ( impl_src) = selcx. select ( & obligation) . ok ( ) . flatten ( ) else {
440+ return false ;
441+ } ;
442+
443+ if !matches ! (
444+ impl_src,
445+ ImplSource :: ConstDestruct ( _) | ImplSource :: Param ( _, ty:: BoundConstness :: ConstIfConst )
446+ ) {
447+ return false ;
448+ }
449+
450+ let ocx = ObligationCtxt :: new ( & infcx) ;
451+ ocx. register_obligations ( impl_src. nested_obligations ( ) ) ;
452+ ocx. select_all_or_error ( ) . is_empty ( )
453+ }
0 commit comments