@@ -19,6 +19,8 @@ pub enum CopyImplementationError<'tcx> {
19
19
}
20
20
21
21
pub enum ConstParamTyImplementationError < ' tcx > {
22
+ TypeNotSized ,
23
+ InvalidInnerTyOfBuiltinTy ( Vec < ( Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
22
24
InfrigingFields ( Vec < ( & ' tcx ty:: FieldDef , Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
23
25
NotAnAdtOrBuiltinAllowed ,
24
26
}
@@ -89,33 +91,104 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
89
91
self_type : Ty < ' tcx > ,
90
92
parent_cause : ObligationCause < ' tcx > ,
91
93
) -> Result < ( ) , ConstParamTyImplementationError < ' tcx > > {
92
- let ( adt, args) = match self_type. kind ( ) {
93
- // `core` provides these impls.
94
- ty:: Uint ( _)
95
- | ty:: Int ( _)
96
- | ty:: Bool
97
- | ty:: Char
98
- | ty:: Str
99
- | ty:: Array ( ..)
100
- | ty:: Slice ( _)
101
- | ty:: Ref ( .., hir:: Mutability :: Not )
102
- | ty:: Tuple ( _) => return Ok ( ( ) ) ,
94
+ {
95
+ // Check for sizedness before recursing into ADT fields so that if someone tries to write:
96
+ // ```rust
97
+ // #[derive(ConstParamTy)]
98
+ // struct Foo([u8])
99
+ // ```
100
+ // They are told that const parameter types must be sized, instead of it saying that
101
+ // the trait implementation `[u8]: ConstParamTy` is not satisfied.
102
+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
103
+ let ocx = traits:: ObligationCtxt :: new_with_diagnostics ( & infcx) ;
103
104
104
- & ty:: Adt ( adt, args) => ( adt, args) ,
105
+ ocx. register_bound (
106
+ parent_cause. clone ( ) ,
107
+ param_env,
108
+ self_type,
109
+ tcx. require_lang_item ( LangItem :: Sized , Some ( parent_cause. span ) ) ,
110
+ ) ;
111
+
112
+ if !ocx. select_all_or_error ( ) . is_empty ( ) {
113
+ return Err ( ConstParamTyImplementationError :: TypeNotSized ) ;
114
+ }
115
+ } ;
116
+
117
+ let inner_tys: Vec < _ > = match * self_type. kind ( ) {
118
+ // Trivially okay as these types are all:
119
+ // - Sized
120
+ // - Contain no nested types
121
+ // - Have structural equality
122
+ ty:: Uint ( _) | ty:: Int ( _) | ty:: Bool | ty:: Char => return Ok ( ( ) ) ,
123
+
124
+ ty:: Ref ( ..) => return Err ( ConstParamTyImplementationError :: NotAnAdtOrBuiltinAllowed ) ,
125
+
126
+ // Even if we currently require const params to be `Sized` we may aswell handle them correctly
127
+ // here anyway.
128
+ ty:: Slice ( inner_ty) | ty:: Array ( inner_ty, _) => vec ! [ inner_ty] ,
129
+ // `str` morally acts like a newtype around `[u8]`
130
+ ty:: Str => vec ! [ Ty :: new_slice( tcx, tcx. types. u8 ) ] ,
131
+
132
+ ty:: Tuple ( inner_tys) => inner_tys. into_iter ( ) . collect ( ) ,
133
+
134
+ ty:: Adt ( adt, args) if adt. is_enum ( ) || adt. is_struct ( ) => {
135
+ all_fields_implement_trait (
136
+ tcx,
137
+ param_env,
138
+ self_type,
139
+ adt,
140
+ args,
141
+ parent_cause. clone ( ) ,
142
+ hir:: LangItem :: ConstParamTy ,
143
+ )
144
+ . map_err ( ConstParamTyImplementationError :: InfrigingFields ) ?;
145
+
146
+ vec ! [ ]
147
+ }
105
148
106
149
_ => return Err ( ConstParamTyImplementationError :: NotAnAdtOrBuiltinAllowed ) ,
107
150
} ;
108
151
109
- all_fields_implement_trait (
110
- tcx,
111
- param_env,
112
- self_type,
113
- adt,
114
- args,
115
- parent_cause,
116
- hir:: LangItem :: ConstParamTy ,
117
- )
118
- . map_err ( ConstParamTyImplementationError :: InfrigingFields ) ?;
152
+ let mut infringing_inner_tys = vec ! [ ] ;
153
+ for inner_ty in inner_tys {
154
+ // We use an ocx per inner ty for better diagnostics
155
+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
156
+ let ocx = traits:: ObligationCtxt :: new_with_diagnostics ( & infcx) ;
157
+
158
+ ocx. register_bound (
159
+ parent_cause. clone ( ) ,
160
+ param_env,
161
+ inner_ty,
162
+ tcx. require_lang_item ( LangItem :: ConstParamTy , Some ( parent_cause. span ) ) ,
163
+ ) ;
164
+
165
+ let errors = ocx. select_all_or_error ( ) ;
166
+ if !errors. is_empty ( ) {
167
+ infringing_inner_tys. push ( ( inner_ty, InfringingFieldsReason :: Fulfill ( errors) ) ) ;
168
+ continue ;
169
+ }
170
+
171
+ // Check regions assuming the self type of the impl is WF
172
+ let outlives_env = OutlivesEnvironment :: with_bounds (
173
+ param_env,
174
+ infcx. implied_bounds_tys (
175
+ param_env,
176
+ parent_cause. body_id ,
177
+ & FxIndexSet :: from_iter ( [ self_type] ) ,
178
+ ) ,
179
+ ) ;
180
+ let errors = infcx. resolve_regions ( & outlives_env) ;
181
+ if !errors. is_empty ( ) {
182
+ infringing_inner_tys. push ( ( inner_ty, InfringingFieldsReason :: Regions ( errors) ) ) ;
183
+ continue ;
184
+ }
185
+ }
186
+
187
+ if !infringing_inner_tys. is_empty ( ) {
188
+ return Err ( ConstParamTyImplementationError :: InvalidInnerTyOfBuiltinTy (
189
+ infringing_inner_tys,
190
+ ) ) ;
191
+ }
119
192
120
193
Ok ( ( ) )
121
194
}
0 commit comments