44// differ from the time of `rustc` even if the name stays the same.
55
66use crate :: msrvs:: Msrv ;
7+ use hir:: { Constness , 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:: 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:: SelectionContext ;
1825use std:: borrow:: Cow ;
1926
2027type 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
289274fn 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+ }
0 commit comments