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:: 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:: { ImplSource , 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:: { ObligationCtxt , SelectionContext } ;
18
25
use std:: borrow:: Cow ;
19
26
20
27
type McfResult = Result < ( ) , ( Span , Cow < ' static , str > ) > ;
@@ -256,7 +263,19 @@ fn check_statement<'tcx>(
256
263
257
264
fn check_operand < ' tcx > ( tcx : TyCtxt < ' tcx > , operand : & Operand < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
258
265
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) ,
260
279
Operand :: Constant ( c) => match c. check_static_ptr ( tcx) {
261
280
Some ( _) => Err ( ( span, "cannot access `static` items in const fn" . into ( ) ) ) ,
262
281
None => Ok ( ( ) ) ,
@@ -266,6 +285,7 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
266
285
267
286
fn check_place < ' tcx > ( tcx : TyCtxt < ' tcx > , place : Place < ' tcx > , span : Span , body : & Body < ' tcx > ) -> McfResult {
268
287
let mut cursor = place. projection . as_ref ( ) ;
288
+
269
289
while let [ ref proj_base @ .., elem] = * cursor {
270
290
cursor = proj_base;
271
291
match elem {
@@ -305,15 +325,19 @@ fn check_terminator<'tcx>(
305
325
| TerminatorKind :: Resume
306
326
| TerminatorKind :: Terminate
307
327
| 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
+ } ,
311
337
TerminatorKind :: SwitchInt { discr, targets : _ } => check_operand ( tcx, discr, span, body) ,
312
-
313
338
TerminatorKind :: GeneratorDrop | TerminatorKind :: Yield { .. } => {
314
339
Err ( ( span, "const fn generators are unstable" . into ( ) ) )
315
340
} ,
316
-
317
341
TerminatorKind :: Call {
318
342
func,
319
343
args,
@@ -357,15 +381,13 @@ fn check_terminator<'tcx>(
357
381
Err ( ( span, "can only call other const fns within const fn" . into ( ) ) )
358
382
}
359
383
} ,
360
-
361
384
TerminatorKind :: Assert {
362
385
cond,
363
386
expected : _,
364
387
msg : _,
365
388
target : _,
366
389
unwind : _,
367
390
} => check_operand ( tcx, cond, span, body) ,
368
-
369
391
TerminatorKind :: InlineAsm { .. } => Err ( ( span, "cannot use inline assembly in const fn" . into ( ) ) ) ,
370
392
}
371
393
}
@@ -379,8 +401,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
379
401
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
380
402
381
403
// 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.
384
405
let short_version = since
385
406
. as_str ( )
386
407
. split ( '-' )
@@ -398,3 +419,35 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
398
419
}
399
420
} )
400
421
}
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