@@ -23,6 +23,9 @@ use rustc_session::lint;
23
23
use rustc_span:: def_id:: { DefId , LocalDefId } ;
24
24
use rustc_span:: Span ;
25
25
26
+ use std:: cmp;
27
+
28
+ /// Check if a given constant can be evaluated.
26
29
pub fn is_const_evaluatable < ' cx , ' tcx > (
27
30
infcx : & InferCtxt < ' cx , ' tcx > ,
28
31
def : ty:: WithOptConstParam < DefId > ,
@@ -32,23 +35,87 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
32
35
) -> Result < ( ) , ErrorHandled > {
33
36
debug ! ( "is_const_evaluatable({:?}, {:?})" , def, substs) ;
34
37
if infcx. tcx . features ( ) . const_evaluatable_checked {
35
- if let Some ( ct) = AbstractConst :: new ( infcx. tcx , def, substs) ? {
36
- for pred in param_env. caller_bounds ( ) {
37
- match pred. skip_binders ( ) {
38
- ty:: PredicateAtom :: ConstEvaluatable ( b_def, b_substs) => {
39
- debug ! ( "is_const_evaluatable: caller_bound={:?}, {:?}" , b_def, b_substs) ;
40
- if b_def == def && b_substs == substs {
41
- debug ! ( "is_const_evaluatable: caller_bound ~~> ok" ) ;
42
- return Ok ( ( ) ) ;
43
- } else if AbstractConst :: new ( infcx. tcx , b_def, b_substs) ?
44
- . map_or ( false , |b_ct| try_unify ( infcx. tcx , ct, b_ct) )
45
- {
46
- debug ! ( "is_const_evaluatable: abstract_const ~~> ok" ) ;
47
- return Ok ( ( ) ) ;
38
+ let tcx = infcx. tcx ;
39
+ match AbstractConst :: new ( tcx, def, substs) ? {
40
+ // We are looking at a generic abstract constant.
41
+ Some ( ct) => {
42
+ for pred in param_env. caller_bounds ( ) {
43
+ match pred. skip_binders ( ) {
44
+ ty:: PredicateAtom :: ConstEvaluatable ( b_def, b_substs) => {
45
+ debug ! (
46
+ "is_const_evaluatable: caller_bound={:?}, {:?}" ,
47
+ b_def, b_substs
48
+ ) ;
49
+ if b_def == def && b_substs == substs {
50
+ debug ! ( "is_const_evaluatable: caller_bound ~~> ok" ) ;
51
+ return Ok ( ( ) ) ;
52
+ } else if AbstractConst :: new ( tcx, b_def, b_substs) ?
53
+ . map_or ( false , |b_ct| try_unify ( tcx, ct, b_ct) )
54
+ {
55
+ debug ! ( "is_const_evaluatable: abstract_const ~~> ok" ) ;
56
+ return Ok ( ( ) ) ;
57
+ }
48
58
}
59
+ _ => { } // don't care
49
60
}
50
- _ => { } // don't care
51
61
}
62
+
63
+ // We were unable to unify the abstract constant with
64
+ // a constant found in the caller bounds, there are
65
+ // now three possible cases here.
66
+ //
67
+ // - The substs are concrete enough that we can simply
68
+ // try and evaluate the given constant.
69
+ // - The abstract const still references an inference
70
+ // variable, in this case we return `TooGeneric`.
71
+ // - The abstract const references a generic parameter,
72
+ // this means that we emit an error here.
73
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
74
+ enum FailureKind {
75
+ MentionsInfer ,
76
+ MentionsParam ,
77
+ Concrete ,
78
+ }
79
+ let mut failure_kind = FailureKind :: Concrete ;
80
+ walk_abstract_const ( tcx, ct, |node| match node {
81
+ Node :: Leaf ( leaf) => {
82
+ let leaf = leaf. subst ( tcx, ct. substs ) ;
83
+ if leaf. has_infer_types_or_consts ( ) {
84
+ failure_kind = FailureKind :: MentionsInfer ;
85
+ } else if leaf. has_param_types_or_consts ( ) {
86
+ failure_kind = cmp:: min ( failure_kind, FailureKind :: MentionsParam ) ;
87
+ }
88
+ }
89
+ Node :: Binop ( _, _, _) | Node :: UnaryOp ( _, _) | Node :: FunctionCall ( _, _) => ( ) ,
90
+ } ) ;
91
+
92
+ match failure_kind {
93
+ FailureKind :: MentionsInfer => {
94
+ return Err ( ErrorHandled :: TooGeneric ) ;
95
+ }
96
+ FailureKind :: MentionsParam => {
97
+ // FIXME(const_evaluatable_checked): Better error message.
98
+ infcx
99
+ . tcx
100
+ . sess
101
+ . struct_span_err ( span, "unconstrained generic constant" )
102
+ . span_help (
103
+ tcx. def_span ( def. did ) ,
104
+ "consider adding a `where` bound for this expression" ,
105
+ )
106
+ . emit ( ) ;
107
+ return Err ( ErrorHandled :: Reported ( ErrorReported ) ) ;
108
+ }
109
+ FailureKind :: Concrete => {
110
+ // Dealt with below by the same code which handles this
111
+ // without the feature gate.
112
+ }
113
+ }
114
+ }
115
+ None => {
116
+ // If we are dealing with a concrete constant, we can
117
+ // reuse the old code path and try to evaluate
118
+ // the constant.
52
119
}
53
120
}
54
121
}
@@ -95,7 +162,36 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
95
162
}
96
163
97
164
debug ! ( ?concrete, "is_const_evaluatable" ) ;
98
- concrete. map ( drop)
165
+ match concrete {
166
+ Err ( ErrorHandled :: TooGeneric ) if !substs. has_infer_types_or_consts ( ) => {
167
+ // FIXME(const_evaluatable_checked): We really should move
168
+ // emitting this error message to fulfill instead. For
169
+ // now this is easier.
170
+ //
171
+ // This is not a problem without `const_evaluatable_checked` as
172
+ // all `ConstEvaluatable` predicates have to be fulfilled for compilation
173
+ // to succeed.
174
+ //
175
+ // @lcnr: We already emit an error for things like
176
+ // `fn test<const N: usize>() -> [0 - N]` eagerly here,
177
+ // so until we fix this I don't really care.
178
+
179
+ let mut err = infcx
180
+ . tcx
181
+ . sess
182
+ . struct_span_err ( span, "constant expression depends on a generic parameter" ) ;
183
+ // FIXME(const_generics): we should suggest to the user how they can resolve this
184
+ // issue. However, this is currently not actually possible
185
+ // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
186
+ //
187
+ // Note that with `feature(const_evaluatable_checked)` this case should not
188
+ // be reachable.
189
+ err. note ( "this may fail depending on what value the parameter takes" ) ;
190
+ err. emit ( ) ;
191
+ Err ( ErrorHandled :: Reported ( ErrorReported ) )
192
+ }
193
+ c => c. map ( drop) ,
194
+ }
99
195
}
100
196
101
197
/// A tree representing an anonymous constant.
@@ -421,6 +517,33 @@ pub(super) fn try_unify_abstract_consts<'tcx>(
421
517
// on `ErrorReported`.
422
518
}
423
519
520
+ fn walk_abstract_const < ' tcx , F > ( tcx : TyCtxt < ' tcx > , ct : AbstractConst < ' tcx > , mut f : F )
521
+ where
522
+ F : FnMut ( Node < ' tcx > ) ,
523
+ {
524
+ recurse ( tcx, ct, & mut f) ;
525
+ fn recurse < ' tcx > ( tcx : TyCtxt < ' tcx > , ct : AbstractConst < ' tcx > , f : & mut dyn FnMut ( Node < ' tcx > ) ) {
526
+ let root = ct. root ( ) ;
527
+ f ( root) ;
528
+ match root {
529
+ Node :: Leaf ( _) => ( ) ,
530
+ Node :: Binop ( _, l, r) => {
531
+ recurse ( tcx, ct. subtree ( l) , f) ;
532
+ recurse ( tcx, ct. subtree ( r) , f) ;
533
+ }
534
+ Node :: UnaryOp ( _, v) => {
535
+ recurse ( tcx, ct. subtree ( v) , f) ;
536
+ }
537
+ Node :: FunctionCall ( func, args) => {
538
+ recurse ( tcx, ct. subtree ( func) , f) ;
539
+ for & arg in args {
540
+ recurse ( tcx, ct. subtree ( arg) , f) ;
541
+ }
542
+ }
543
+ }
544
+ }
545
+ }
546
+
424
547
/// Tries to unify two abstract constants using structural equality.
425
548
pub ( super ) fn try_unify < ' tcx > (
426
549
tcx : TyCtxt < ' tcx > ,
0 commit comments