@@ -19,6 +19,8 @@ pub enum CopyImplementationError<'tcx> {
1919}
2020
2121pub enum ConstParamTyImplementationError < ' tcx > {
22+ TypeNotSized ,
23+ InvalidInnerTyOfBuiltinTy ( Vec < ( Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
2224 InfrigingFields ( Vec < ( & ' tcx ty:: FieldDef , Ty < ' tcx > , InfringingFieldsReason < ' tcx > ) > ) ,
2325 NotAnAdtOrBuiltinAllowed ,
2426}
@@ -89,33 +91,104 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
8991 self_type : Ty < ' tcx > ,
9092 parent_cause : ObligationCause < ' tcx > ,
9193) -> 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) ;
103104
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+ }
105148
106149 _ => return Err ( ConstParamTyImplementationError :: NotAnAdtOrBuiltinAllowed ) ,
107150 } ;
108151
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+ }
119192
120193 Ok ( ( ) )
121194}
0 commit comments