@@ -28,6 +28,7 @@ use std::ops::ControlFlow;
28
28
29
29
use rustc_errors:: ErrorGuaranteed ;
30
30
use rustc_hir:: def:: DefKind ;
31
+ use rustc_infer:: infer:: canonical:: OriginalQueryValues ;
31
32
pub use rustc_infer:: traits:: * ;
32
33
use rustc_middle:: query:: Providers ;
33
34
use rustc_middle:: span_bug;
@@ -545,75 +546,141 @@ pub fn try_evaluate_const<'tcx>(
545
546
| ty:: ConstKind :: Expr ( _) => Err ( EvaluateConstErr :: HasGenericsOrInfers ) ,
546
547
ty:: ConstKind :: Unevaluated ( uv) => {
547
548
// Postpone evaluation of constants that depend on generic parameters or
548
- // inference variables.
549
+ // inference variables. Also ensure that constants are wf before passing
550
+ // them onto CTFE.
549
551
//
550
- // We use `TypingMode::PostAnalysis` here which is not *technically* correct
552
+ // We use `TypingMode::PostAnalysis` here which is not *technically* correct
551
553
// to be revealing opaque types here as borrowcheck has not run yet. However,
552
554
// CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
553
555
// typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
554
556
// As a result we always use a revealed env when resolving the instance to evaluate.
555
557
//
556
558
// FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
557
559
// instead of having this logic here
558
- let ( args, typing_env) = if tcx. features ( ) . generic_const_exprs ( )
559
- && uv. has_non_region_infer ( )
560
- {
561
- // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
562
- // inference variables and generic parameters to show up in `ty::Const` even though the anon const
563
- // does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
564
- match tcx. thir_abstract_const ( uv. def ) {
565
- Ok ( Some ( ct) ) => {
566
- let ct = tcx. expand_abstract_consts ( ct. instantiate ( tcx, uv. args ) ) ;
567
- if let Err ( e) = ct. error_reported ( ) {
568
- return Err ( EvaluateConstErr :: EvaluationFailure ( e) ) ;
569
- } else if ct. has_non_region_infer ( ) || ct. has_non_region_param ( ) {
570
- // If the anon const *does* actually use generic parameters or inference variables from
571
- // the generic arguments provided for it, then we should *not* attempt to evaluate it.
572
- return Err ( EvaluateConstErr :: HasGenericsOrInfers ) ;
573
- } else {
574
- let args = replace_param_and_infer_args_with_placeholder ( tcx, uv. args ) ;
575
- let typing_env = infcx
576
- . typing_env ( tcx. erase_regions ( param_env) )
577
- . with_post_analysis_normalized ( tcx) ;
560
+ let ( args, typing_env) = if tcx. features ( ) . generic_const_exprs ( ) {
561
+ // We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system
562
+ // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason
563
+ // about if you have to consider gce whatsoever.
564
+ //
565
+ // We don't bother trying to ensure GCE constants are WF before passing them to CTFE as it would cause
566
+ // query cycles on almost every single call to this function.
567
+
568
+ if uv. has_non_region_infer ( ) || uv. has_non_region_param ( ) {
569
+ // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
570
+ // inference variables and generic parameters to show up in `ty::Const` even though the anon const
571
+ // does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
572
+ match tcx. thir_abstract_const ( uv. def ) {
573
+ Ok ( Some ( ct) ) => {
574
+ let ct = tcx. expand_abstract_consts ( ct. instantiate ( tcx, uv. args ) ) ;
575
+ if let Err ( e) = ct. error_reported ( ) {
576
+ return Err ( EvaluateConstErr :: EvaluationFailure ( e) ) ;
577
+ } else if ct. has_non_region_infer ( ) || ct. has_non_region_param ( ) {
578
+ // If the anon const *does* actually use generic parameters or inference variables from
579
+ // the generic arguments provided for it, then we should *not* attempt to evaluate it.
580
+ return Err ( EvaluateConstErr :: HasGenericsOrInfers ) ;
581
+ } else {
582
+ let args =
583
+ replace_param_and_infer_args_with_placeholder ( tcx, uv. args ) ;
584
+ let typing_env = infcx
585
+ . typing_env ( tcx. erase_regions ( param_env) )
586
+ . with_post_analysis_normalized ( tcx) ;
587
+ ( args, typing_env)
588
+ }
589
+ }
590
+ Err ( _) | Ok ( None ) => {
591
+ let args = GenericArgs :: identity_for_item ( tcx, uv. def ) ;
592
+ let typing_env = ty:: TypingEnv :: post_analysis ( tcx, uv. def ) ;
578
593
( args, typing_env)
579
594
}
580
595
}
581
- Err ( _ ) | Ok ( None ) => {
582
- let args = GenericArgs :: identity_for_item ( tcx , uv . def ) ;
583
- let typing_env = ty :: TypingEnv :: post_analysis ( tcx, uv . def ) ;
584
- ( args , typing_env )
585
- }
596
+ } else {
597
+ let typing_env = infcx
598
+ . typing_env ( tcx. erase_regions ( param_env ) )
599
+ . with_post_analysis_normalized ( tcx ) ;
600
+ ( uv . args , typing_env )
586
601
}
587
- } else if tcx. def_kind ( uv. def ) == DefKind :: AnonConst && uv. has_non_region_infer ( ) {
602
+ } else if !tcx. features ( ) . min_generic_const_args ( )
603
+ && !tcx. features ( ) . associated_const_equality ( )
604
+ // We check for anon consts so that when `associated_const_equality` bounds are
605
+ // lowered on stable we still handle them correctly to avoid ICEs in CTFE.
606
+ && tcx. def_kind ( uv. def ) == DefKind :: AnonConst
607
+ {
588
608
// FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
589
609
//
590
- // Diagnostics will sometimes replace the identity args of anon consts in
591
- // array repeat expr counts with inference variables so we have to handle this
592
- // even though it is not something we should ever actually encounter.
593
- //
594
- // Array repeat expr counts are allowed to syntactically use generic parameters
595
- // but must not actually depend on them in order to evalaute successfully. This means
596
- // that it is actually fine to evalaute them in their own environment rather than with
597
- // the actually provided generic arguments.
598
- tcx. dcx ( ) . delayed_bug (
599
- "Encountered anon const with inference variable args but no error reported" ,
600
- ) ;
610
+ // Under `min_const_generics` the only place we can encounter generics in type system
611
+ // consts is for the `const_evaluatable_unchecked` future compat lint. We don't care
612
+ // to handle them in important ways (e.g. deferring evaluation) so we handle it separately.
613
+
614
+ if uv. has_non_region_infer ( ) {
615
+ // Diagnostics will sometimes replace the identity args of anon consts in
616
+ // array repeat expr counts with inference variables so we have to handle this
617
+ // even though it is not something we should ever actually encounter.
618
+ //
619
+ // Array repeat expr counts are allowed to syntactically use generic parameters
620
+ // but must not actually depend on them in order to evalaute successfully. This means
621
+ // that it is actually fine to evalaute them in their own environment rather than with
622
+ // the actually provided generic arguments.
623
+ tcx. dcx ( ) . delayed_bug (
624
+ "Encountered anon const with inference variable args but no error reported" ,
625
+ ) ;
626
+ }
601
627
628
+ // Generic arguments to anon consts in the type system are never meaningful under mcg,
629
+ // there either are no arguments or its a repeat count and the arguments must not be
630
+ // depended on for evaluation.
602
631
let args = GenericArgs :: identity_for_item ( tcx, uv. def ) ;
603
632
let typing_env = ty:: TypingEnv :: post_analysis ( tcx, uv. def ) ;
604
633
( args, typing_env)
605
634
} else {
606
- // FIXME: This codepath is reachable under `associated_const_equality` and in the
607
- // future will be reachable by `min_generic_const_args`. We should handle inference
608
- // variables and generic parameters properly instead of doing nothing.
635
+ // We are only dealing with "truly" generic/uninferred constants here:
636
+ // - `generic_const_exprs` has been handled separately in the first if
637
+ // - `min_const_generics` repeat expr count hacks have been handled in the previous if
638
+ //
639
+ // So we are free to simply defer evaluation here. This does assume that `uv.args` has
640
+ // already been normalized.
641
+ if uv. args . has_non_region_param ( ) || uv. args . has_non_region_infer ( ) {
642
+ return Err ( EvaluateConstErr :: HasGenericsOrInfers ) ;
643
+ }
644
+
645
+ // If we are dealing with a fully monomorphic constant then we should ensure that
646
+ // it is well formed as otherwise CTFE will ICE. For the same reasons as with
647
+ // deferring evaluation of generic/uninferred constants, we do not have to worry
648
+ // about `generic_const_expr`
649
+ // if tcx.def_kind(uv.def) == DefKind::AnonConst
650
+ // && tcx.predicates_of(uv.def).predicates.is_empty()
651
+ // {
652
+ // // ... skip doing any work
653
+ // } else {
654
+ // let input = infcx
655
+ // .canonicalize_query(param_env.and(uv), &mut OriginalQueryValues::default());
656
+ // if !tcx.check_constant_safe_to_evaluate(input) {
657
+ // let e = tcx.dcx().delayed_bug(
658
+ // "Attempted to evaluate illformed constant but no error emitted",
659
+ // );
660
+ // return Err(EvaluateConstErr::EvaluationFailure(e));
661
+ // }
662
+ // }
663
+
609
664
let typing_env = infcx
610
665
. typing_env ( tcx. erase_regions ( param_env) )
611
666
. with_post_analysis_normalized ( tcx) ;
612
667
( uv. args , typing_env)
613
668
} ;
614
- let uv = ty:: UnevaluatedConst :: new ( uv. def , args) ;
615
669
670
+ let uv = ty:: UnevaluatedConst :: new ( uv. def , args) ;
616
671
let erased_uv = tcx. erase_regions ( uv) ;
672
+
673
+ if !tcx. features ( ) . generic_const_exprs ( ) {
674
+ let input = infcx
675
+ . canonicalize_query ( param_env. and ( uv) , & mut OriginalQueryValues :: default ( ) ) ;
676
+ if !tcx. check_constant_safe_to_evaluate ( input) {
677
+ let e = tcx. dcx ( ) . delayed_bug (
678
+ "Attempted to evaluate illformed constant but no error emitted" ,
679
+ ) ;
680
+ return Err ( EvaluateConstErr :: EvaluationFailure ( e) ) ;
681
+ }
682
+ }
683
+
617
684
use rustc_middle:: mir:: interpret:: ErrorHandled ;
618
685
match tcx. const_eval_resolve_for_typeck ( typing_env, erased_uv, DUMMY_SP ) {
619
686
Ok ( Ok ( val) ) => Ok ( ty:: Const :: new_value (
@@ -697,7 +764,51 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697
764
args. fold_with ( & mut ReplaceParamAndInferWithPlaceholder { tcx, idx : 0 } )
698
765
}
699
766
700
- /// Normalizes the predicates and checks whether they hold in an empty environment. If this
767
+ #[ instrument( level = "debug" , skip( tcx) , ret) ]
768
+ fn check_constant_safe_to_evaluate < ' tcx > (
769
+ tcx : TyCtxt < ' tcx > ,
770
+ input : ty:: CanonicalQueryInput < TyCtxt < ' tcx > , ty:: ParamEnvAnd < ' tcx , ty:: UnevaluatedConst < ' tcx > > > ,
771
+ ) -> bool {
772
+ let ( infcx, param_env_and, _) = tcx. infer_ctxt ( ) . build_with_canonical ( DUMMY_SP , & input) ;
773
+ let ( param_env, uv) = param_env_and. into_parts ( ) ;
774
+
775
+ // We can't just register a wf goal for the constant as that would require evaluating the constant
776
+ // which would result in a query cycle.
777
+ let mut predicates = tcx. predicates_of ( uv. def ) . instantiate ( tcx, uv. args ) . predicates ;
778
+
779
+ // Specifically check trait fulfillment to avoid an error when trying to resolve
780
+ // associated items.
781
+ if let Some ( trait_def_id) = tcx. trait_of_item ( uv. def ) {
782
+ let trait_ref = ty:: TraitRef :: from_method ( tcx, trait_def_id, uv. args ) ;
783
+ predicates. push ( trait_ref. upcast ( tcx) ) ;
784
+ }
785
+
786
+ let ocx = ObligationCtxt :: new ( & infcx) ;
787
+ let predicates = ocx. normalize ( & ObligationCause :: dummy ( ) , param_env, predicates) ;
788
+ for predicate in predicates {
789
+ let obligation = Obligation :: new ( tcx, ObligationCause :: dummy ( ) , param_env, predicate) ;
790
+ ocx. register_obligation ( obligation) ;
791
+ }
792
+ let errors = ocx. select_all_or_error ( ) ;
793
+
794
+ if !errors. is_empty ( ) {
795
+ return false ;
796
+ }
797
+
798
+ // Leak check for any higher-ranked trait mismatches.
799
+ // We only need to do this in the old solver, since the new solver already
800
+ // leak-checks.
801
+ if !infcx. next_trait_solver ( ) && infcx. leak_check ( ty:: UniverseIndex :: ROOT , None ) . is_err ( ) {
802
+ return false ;
803
+ }
804
+
805
+ // We don't check non-higher-ranked region constraints, but we don't need to as region
806
+ // constraints cant affect coherence and therefore result in overlapping impls in codegen/ctfe
807
+ // so it shouldn't matter to speculatively evaluate constants with failing region constraints.
808
+ true
809
+ }
810
+
811
+ /// Normalizes the predicates and checks whether they hold in a given empty. If this
701
812
/// returns true, then either normalize encountered an error or one of the predicates did not
702
813
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
703
814
/// used during analysis.
@@ -840,6 +951,7 @@ pub fn provide(providers: &mut Providers) {
840
951
specialization_enabled_in : specialize:: specialization_enabled_in,
841
952
instantiate_and_check_impossible_predicates,
842
953
is_impossible_associated_item,
954
+ check_constant_safe_to_evaluate,
843
955
..* providers
844
956
} ;
845
957
}
0 commit comments