@@ -28,6 +28,7 @@ use std::ops::ControlFlow;
2828
2929use rustc_errors:: ErrorGuaranteed ;
3030use rustc_hir:: def:: DefKind ;
31+ use rustc_infer:: infer:: canonical:: OriginalQueryValues ;
3132pub use rustc_infer:: traits:: * ;
3233use rustc_middle:: query:: Providers ;
3334use rustc_middle:: span_bug;
@@ -545,75 +546,130 @@ pub fn try_evaluate_const<'tcx>(
545546 | ty:: ConstKind :: Expr ( _) => Err ( EvaluateConstErr :: HasGenericsOrInfers ) ,
546547 ty:: ConstKind :: Unevaluated ( uv) => {
547548 // 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.
549551 //
550- // We use `TypingMode::PostAnalysis` here which is not *technically* correct
552+ // We use `TypingMode::PostAnalysis` here which is not *technically* correct
551553 // to be revealing opaque types here as borrowcheck has not run yet. However,
552554 // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
553555 // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
554556 // As a result we always use a revealed env when resolving the instance to evaluate.
555557 //
556558 // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
557559 // 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 ) ;
578593 ( args, typing_env)
579594 }
580595 }
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 )
586601 }
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+ {
588608 // FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
589609 //
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+ }
601627
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.
602631 let args = GenericArgs :: identity_for_item ( tcx, uv. def ) ;
603632 let typing_env = ty:: TypingEnv :: post_analysis ( tcx, uv. def ) ;
604633 ( args, typing_env)
605634 } 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+
609664 let typing_env = infcx
610665 . typing_env ( tcx. erase_regions ( param_env) )
611666 . with_post_analysis_normalized ( tcx) ;
612667 ( uv. args , typing_env)
613668 } ;
614- let uv = ty:: UnevaluatedConst :: new ( uv. def , args) ;
615669
670+ let uv = ty:: UnevaluatedConst :: new ( uv. def , args) ;
616671 let erased_uv = tcx. erase_regions ( uv) ;
672+
617673 use rustc_middle:: mir:: interpret:: ErrorHandled ;
618674 match tcx. const_eval_resolve_for_typeck ( typing_env, erased_uv, DUMMY_SP ) {
619675 Ok ( Ok ( val) ) => Ok ( ty:: Const :: new_value (
@@ -697,7 +753,51 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697753 args. fold_with ( & mut ReplaceParamAndInferWithPlaceholder { tcx, idx : 0 } )
698754}
699755
700- /// Normalizes the predicates and checks whether they hold in an empty environment. If this
756+ #[ instrument( level = "debug" , skip( tcx) , ret) ]
757+ fn check_constant_safe_to_evaluate < ' tcx > (
758+ tcx : TyCtxt < ' tcx > ,
759+ input : ty:: CanonicalQueryInput < TyCtxt < ' tcx > , ty:: ParamEnvAnd < ' tcx , ty:: UnevaluatedConst < ' tcx > > > ,
760+ ) -> bool {
761+ let ( infcx, param_env_and, _) = tcx. infer_ctxt ( ) . build_with_canonical ( DUMMY_SP , & input) ;
762+ let ( param_env, uv) = param_env_and. into_parts ( ) ;
763+
764+ // We can't just register a wf goal for the constant as that would require evaluating the constant
765+ // which would result in a query cycle.
766+ let mut predicates = tcx. predicates_of ( uv. def ) . instantiate ( tcx, uv. args ) . predicates ;
767+
768+ // Specifically check trait fulfillment to avoid an error when trying to resolve
769+ // associated items.
770+ if let Some ( trait_def_id) = tcx. trait_of_item ( uv. def ) {
771+ let trait_ref = ty:: TraitRef :: from_method ( tcx, trait_def_id, uv. args ) ;
772+ predicates. push ( trait_ref. upcast ( tcx) ) ;
773+ }
774+
775+ let ocx = ObligationCtxt :: new ( & infcx) ;
776+ let predicates = ocx. normalize ( & ObligationCause :: dummy ( ) , param_env, predicates) ;
777+ for predicate in predicates {
778+ let obligation = Obligation :: new ( tcx, ObligationCause :: dummy ( ) , param_env, predicate) ;
779+ ocx. register_obligation ( obligation) ;
780+ }
781+ let errors = ocx. select_all_or_error ( ) ;
782+
783+ if !errors. is_empty ( ) {
784+ return false ;
785+ }
786+
787+ // Leak check for any higher-ranked trait mismatches.
788+ // We only need to do this in the old solver, since the new solver already
789+ // leak-checks.
790+ if !infcx. next_trait_solver ( ) && infcx. leak_check ( ty:: UniverseIndex :: ROOT , None ) . is_err ( ) {
791+ return false ;
792+ }
793+
794+ // We don't check non-higher-ranked region constraints, but we don't need to as region
795+ // constraints cant affect coherence and therefore result in overlapping impls in codegen/ctfe
796+ // so it shouldn't matter to speculatively evaluate constants with failing region constraints.
797+ true
798+ }
799+
800+ /// Normalizes the predicates and checks whether they hold in a given empty. If this
701801/// returns true, then either normalize encountered an error or one of the predicates did not
702802/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
703803/// used during analysis.
@@ -840,6 +940,7 @@ pub fn provide(providers: &mut Providers) {
840940 specialization_enabled_in : specialize:: specialization_enabled_in,
841941 instantiate_and_check_impossible_predicates,
842942 is_impossible_associated_item,
943+ check_constant_safe_to_evaluate,
843944 ..* providers
844945 } ;
845946}
0 commit comments