rustc_trait_selection/traits/
misc.rs1use hir::LangItem;
4use rustc_ast::Mutability;
5use rustc_hir as hir;
6use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
7use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
8use rustc_span::sym;
9
10use crate::regions::InferCtxtRegionExt;
11use crate::traits::{self, FulfillmentError, Obligation, ObligationCause};
12
13pub enum CopyImplementationError<'tcx> {
14 InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
15 NotAnAdt,
16 HasDestructor,
17 HasUnsafeFields,
18}
19
20pub enum ConstParamTyImplementationError<'tcx> {
21 UnsizedConstParamsFeatureRequired,
22 InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
23 InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
24 NotAnAdtOrBuiltinAllowed,
25}
26
27pub enum InfringingFieldsReason<'tcx> {
28 Fulfill(Vec<FulfillmentError<'tcx>>),
29 Regions(Vec<RegionResolutionError<'tcx>>),
30}
31
32pub fn type_allowed_to_implement_copy<'tcx>(
44 tcx: TyCtxt<'tcx>,
45 param_env: ty::ParamEnv<'tcx>,
46 self_type: Ty<'tcx>,
47 parent_cause: ObligationCause<'tcx>,
48 impl_safety: hir::Safety,
49) -> Result<(), CopyImplementationError<'tcx>> {
50 let (adt, args) = match self_type.kind() {
51 ty::Uint(_)
54 | ty::Int(_)
55 | ty::Bool
56 | ty::Float(_)
57 | ty::Char
58 | ty::RawPtr(..)
59 | ty::Never
60 | ty::Ref(_, _, hir::Mutability::Not)
61 | ty::Array(..) => return Ok(()),
62
63 &ty::Adt(adt, args) => (adt, args),
64
65 _ => return Err(CopyImplementationError::NotAnAdt),
66 };
67
68 all_fields_implement_trait(
69 tcx,
70 param_env,
71 self_type,
72 adt,
73 args,
74 parent_cause,
75 hir::LangItem::Copy,
76 )
77 .map_err(CopyImplementationError::InfringingFields)?;
78
79 if adt.has_dtor(tcx) {
80 return Err(CopyImplementationError::HasDestructor);
81 }
82
83 if impl_safety.is_safe() && self_type.has_unsafe_fields() {
84 return Err(CopyImplementationError::HasUnsafeFields);
85 }
86
87 Ok(())
88}
89
90pub fn type_allowed_to_implement_const_param_ty<'tcx>(
97 tcx: TyCtxt<'tcx>,
98 param_env: ty::ParamEnv<'tcx>,
99 self_type: Ty<'tcx>,
100 parent_cause: ObligationCause<'tcx>,
101) -> Result<(), ConstParamTyImplementationError<'tcx>> {
102 let mut need_unstable_feature_bound = false;
103
104 let inner_tys: Vec<_> = match *self_type.kind() {
105 ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()),
110
111 ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => {
114 need_unstable_feature_bound = true;
115 vec![inner_ty]
116 }
117 ty::Str => {
118 need_unstable_feature_bound = true;
119 vec![Ty::new_slice(tcx, tcx.types.u8)]
120 }
121 ty::Array(inner_ty, _) => vec![inner_ty],
122
123 ty::Tuple(inner_tys) => inner_tys.into_iter().collect(),
125
126 ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => {
127 all_fields_implement_trait(
128 tcx,
129 param_env,
130 self_type,
131 adt,
132 args,
133 parent_cause.clone(),
134 LangItem::ConstParamTy,
135 )
136 .map_err(ConstParamTyImplementationError::InfrigingFields)?;
137
138 vec![]
139 }
140
141 _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
142 };
143
144 let mut infringing_inner_tys = vec![];
145 for inner_ty in inner_tys {
146 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
148 let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
149
150 if need_unstable_feature_bound {
152 ocx.register_obligation(Obligation::new(
153 tcx,
154 parent_cause.clone(),
155 param_env,
156 ty::ClauseKind::UnstableFeature(sym::unsized_const_params),
157 ));
158
159 if !ocx.select_all_or_error().is_empty() {
160 return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
161 }
162 }
163
164 ocx.register_bound(
165 parent_cause.clone(),
166 param_env,
167 inner_ty,
168 tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span),
169 );
170
171 let errors = ocx.select_all_or_error();
172 if !errors.is_empty() {
173 infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors)));
174 continue;
175 }
176
177 let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
179 if !errors.is_empty() {
180 infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
181 continue;
182 }
183 }
184
185 if !infringing_inner_tys.is_empty() {
186 return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(
187 infringing_inner_tys,
188 ));
189 }
190
191 Ok(())
192}
193
194pub fn all_fields_implement_trait<'tcx>(
196 tcx: TyCtxt<'tcx>,
197 param_env: ty::ParamEnv<'tcx>,
198 self_type: Ty<'tcx>,
199 adt: AdtDef<'tcx>,
200 args: ty::GenericArgsRef<'tcx>,
201 parent_cause: ObligationCause<'tcx>,
202 lang_item: LangItem,
203) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> {
204 let trait_def_id = tcx.require_lang_item(lang_item, parent_cause.span);
205
206 let mut infringing = Vec::new();
207 for variant in adt.variants() {
208 for field in &variant.fields {
209 let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
211 let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
212
213 let unnormalized_ty = field.ty(tcx, args);
214 if unnormalized_ty.references_error() {
215 continue;
216 }
217
218 let field_span = tcx.def_span(field.did);
219 let field_ty_span = match tcx.hir_get_if_local(field.did) {
220 Some(hir::Node::Field(field_def)) => field_def.ty.span,
221 _ => field_span,
222 };
223
224 let normalization_cause = if field
230 .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did()))
231 .has_non_region_param()
232 {
233 parent_cause.clone()
234 } else {
235 ObligationCause::dummy_with_span(field_ty_span)
236 };
237 let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
238 let normalization_errors = ocx.select_where_possible();
239
240 if !normalization_errors.is_empty() || ty.references_error() {
245 tcx.dcx().span_delayed_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id)));
246 continue;
247 }
248
249 ocx.register_bound(
250 ObligationCause::dummy_with_span(field_ty_span),
251 param_env,
252 ty,
253 trait_def_id,
254 );
255 let errors = ocx.select_all_or_error();
256 if !errors.is_empty() {
257 infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
258 }
259
260 let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
262 if !errors.is_empty() {
263 infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
264 }
265 }
266 }
267
268 if infringing.is_empty() { Ok(()) } else { Err(infringing) }
269}