1
1
use crate :: const_eval:: const_variant_index;
2
2
3
3
use rustc:: hir;
4
- use rustc:: lint ;
4
+ use rustc:: hir :: def_id :: DefId ;
5
5
use rustc:: mir:: Field ;
6
6
use rustc:: infer:: InferCtxt ;
7
7
use rustc:: traits:: { ObligationCause , PredicateObligation } ;
@@ -15,23 +15,28 @@ use syntax_pos::Span;
15
15
use std:: cell:: Cell ;
16
16
17
17
use super :: { FieldPat , Pat , PatCtxt , PatKind } ;
18
+ use super :: structural_match:: search_const_rhs_for_structural_match_violation;
18
19
19
20
impl < ' a , ' tcx > PatCtxt < ' a , ' tcx > {
20
21
/// Converts an evaluated constant to a pattern (if possible).
21
22
/// This means aggregate values (like structs and enums) are converted
22
23
/// to a pattern that matches the value (as if you'd compared via structural equality).
24
+ ///
25
+ /// For literals, pass `None` as the `opt_const_def_id`; for a const
26
+ /// identifier, pass its `DefId`.
23
27
pub ( super ) fn const_to_pat (
24
28
& self ,
25
29
cv : & ' tcx ty:: Const < ' tcx > ,
30
+ opt_const_def_id : Option < DefId > ,
26
31
id : hir:: HirId ,
27
32
span : Span ,
28
33
) -> Pat < ' tcx > {
29
- debug ! ( "const_to_pat : cv={:#?} id={:?}" , cv, id) ;
30
- debug ! ( "const_to_pat : cv.ty={:?} span={:?}" , cv. ty, span) ;
34
+ debug ! ( "const_def_to_pat : cv={:#?} const_def_id: {:?} id={:?}" , cv, opt_const_def_id , id) ;
35
+ debug ! ( "const_def_to_pat : cv.ty={:?} span={:?}" , cv. ty, span) ;
31
36
32
37
self . tcx . infer_ctxt ( ) . enter ( |infcx| {
33
38
let mut convert = ConstToPat :: new ( self , id, span, infcx) ;
34
- convert. to_pat ( cv)
39
+ convert. to_pat ( cv, opt_const_def_id )
35
40
} )
36
41
}
37
42
}
@@ -67,85 +72,77 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
67
72
68
73
fn tcx ( & self ) -> TyCtxt < ' tcx > { self . infcx . tcx }
69
74
70
- fn search_for_structural_match_violation ( & self ,
71
- ty : Ty < ' tcx > )
72
- -> Option < ty:: NonStructuralMatchTy < ' tcx > >
75
+ fn search_const_def_for_structural_match_violation ( & self , const_def_id : DefId )
73
76
{
74
- ty:: search_for_structural_match_violation ( self . id , self . span , self . tcx ( ) , ty)
77
+ assert ! ( const_def_id. is_local( ) ) ;
78
+ self . tcx ( ) . infer_ctxt ( ) . enter ( |infcx| {
79
+ search_const_rhs_for_structural_match_violation (
80
+ & infcx, self . param_env , const_def_id, self . id , self . span ) ;
81
+ } ) ;
82
+ }
83
+
84
+ fn search_ty_for_structural_match_violation ( & self , ty : Ty < ' tcx > )
85
+ {
86
+ let structural = ty:: search_type_for_structural_match_violation (
87
+ self . id , self . span , self . tcx ( ) , ty) ;
88
+ debug ! ( "search_ty_for_structural_match_violation ty: {:?} returned: {:?}" , ty, structural) ;
89
+ if let Some ( non_sm_ty) = structural {
90
+
91
+ // double-check there even *is* a semantic `PartialEq` to dispatch to.
92
+ //
93
+ // (If there isn't, then we can safely issue a hard
94
+ // error, because that's never worked, due to compiler
95
+ // using `PartialEq::eq` in this scenario in the past.)
96
+ //
97
+ // Note: To fix rust-lang/rust#65466, one could lift this check
98
+ // *before* any structural-match checking, and unconditionally error
99
+ // if `PartialEq` is not implemented. However, that breaks stable
100
+ // code at the moment, because types like `for <'a> fn(&'a ())` do
101
+ // not *yet* implement `PartialEq`. So for now we leave this here.
102
+ let warn_instead_of_hard_error: bool = {
103
+ let partial_eq_trait_id = self . tcx ( ) . lang_items ( ) . eq_trait ( ) . unwrap ( ) ;
104
+ let obligation: PredicateObligation < ' _ > =
105
+ self . tcx ( ) . predicate_for_trait_def (
106
+ self . param_env ,
107
+ ObligationCause :: misc ( self . span , self . id ) ,
108
+ partial_eq_trait_id,
109
+ 0 ,
110
+ ty,
111
+ & [ ] ) ;
112
+ // FIXME: should this call a `predicate_must_hold` variant instead?
113
+ self . infcx . predicate_may_hold ( & obligation)
114
+ } ;
115
+
116
+ debug ! ( "call report_structural_match_violation non_sm_ty: {:?} id: {:?} warn: {:?}" ,
117
+ non_sm_ty, self . id, warn_instead_of_hard_error) ;
118
+ ty:: report_structural_match_violation (
119
+ self . tcx ( ) , non_sm_ty, self . id , self . span , warn_instead_of_hard_error) ;
120
+ }
75
121
}
76
122
77
123
fn type_marked_structural ( & self , ty : Ty < ' tcx > ) -> bool {
78
124
ty:: type_marked_structural ( self . id , self . span , & self . infcx , ty)
79
125
}
80
126
81
- fn to_pat ( & mut self , cv : & ' tcx ty:: Const < ' tcx > ) -> Pat < ' tcx > {
82
- // This method is just a wrapper handling a validity check; the heavy lifting is
83
- // performed by the recursive `recur` method, which is not meant to be
84
- // invoked except by this method.
85
- //
86
- // once indirect_structural_match is a full fledged error, this
87
- // level of indirection can be eliminated
88
-
127
+ fn to_pat ( & mut self ,
128
+ cv : & ' tcx ty:: Const < ' tcx > ,
129
+ opt_const_def_id : Option < DefId > )
130
+ -> Pat < ' tcx >
131
+ {
89
132
let inlined_const_as_pat = self . recur ( cv) ;
90
133
91
134
if self . include_lint_checks && !self . saw_const_match_error . get ( ) {
92
135
// If we were able to successfully convert the const to some pat,
93
136
// double-check that all types in the const implement `Structural`.
94
-
95
- let structural = self . search_for_structural_match_violation ( cv. ty ) ;
96
- debug ! ( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}" ,
97
- cv. ty, structural) ;
98
- if let Some ( non_sm_ty) = structural {
99
- let adt_def = match non_sm_ty {
100
- ty:: NonStructuralMatchTy :: Adt ( adt_def) => adt_def,
101
- ty:: NonStructuralMatchTy :: Param =>
102
- bug ! ( "use of constant whose type is a parameter inside a pattern" ) ,
103
- } ;
104
- let path = self . tcx ( ) . def_path_str ( adt_def. did ) ;
105
- let msg = format ! (
106
- "to use a constant of type `{}` in a pattern, \
107
- `{}` must be annotated with `#[derive(PartialEq, Eq)]`",
108
- path,
109
- path,
110
- ) ;
111
-
112
- // double-check there even *is* a semantic `PartialEq` to dispatch to.
113
- //
114
- // (If there isn't, then we can safely issue a hard
115
- // error, because that's never worked, due to compiler
116
- // using `PartialEq::eq` in this scenario in the past.)
117
- //
118
- // Note: To fix rust-lang/rust#65466, one could lift this check
119
- // *before* any structural-match checking, and unconditionally error
120
- // if `PartialEq` is not implemented. However, that breaks stable
121
- // code at the moment, because types like `for <'a> fn(&'a ())` do
122
- // not *yet* implement `PartialEq`. So for now we leave this here.
123
- let ty_is_partial_eq: bool = {
124
- let partial_eq_trait_id = self . tcx ( ) . lang_items ( ) . eq_trait ( ) . unwrap ( ) ;
125
- let obligation: PredicateObligation < ' _ > =
126
- self . tcx ( ) . predicate_for_trait_def (
127
- self . param_env ,
128
- ObligationCause :: misc ( self . span , self . id ) ,
129
- partial_eq_trait_id,
130
- 0 ,
131
- cv. ty ,
132
- & [ ] ) ;
133
- // FIXME: should this call a `predicate_must_hold` variant instead?
134
- self . infcx . predicate_may_hold ( & obligation)
135
- } ;
136
-
137
- if !ty_is_partial_eq {
138
- // span_fatal avoids ICE from resolution of non-existent method (rare case).
139
- self . tcx ( ) . sess . span_fatal ( self . span , & msg) ;
140
- } else {
141
- self . tcx ( ) . lint_hir ( lint:: builtin:: INDIRECT_STRUCTURAL_MATCH ,
142
- self . id ,
143
- self . span ,
144
- & msg) ;
137
+ match opt_const_def_id {
138
+ Some ( const_def_id) if const_def_id. is_local ( ) => {
139
+ self . search_const_def_for_structural_match_violation ( const_def_id) ;
140
+ }
141
+ _ => {
142
+ self . search_ty_for_structural_match_violation ( cv. ty ) ;
145
143
}
146
144
}
147
145
}
148
-
149
146
inlined_const_as_pat
150
147
}
151
148
0 commit comments