]>
Commit | Line | Data |
---|---|---|
0531ce1d XL |
1 | //! Logic and data structures related to impl specialization, explained in |
2 | //! greater detail below. | |
3 | //! | |
4 | //! At the moment, this implementation support only the simple "chain" rule: | |
5 | //! If any two impls overlap, one must be a strict subset of the other. | |
6 | //! | |
ba9703b0 | 7 | //! See the [rustc dev guide] for a bit more detail on how specialization |
0531ce1d XL |
8 | //! fits together with the rest of the trait machinery. |
9 | //! | |
ba9703b0 | 10 | //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html |
54a0048b | 11 | |
0731742a | 12 | pub mod specialization_graph; |
74b04a01 | 13 | use specialization_graph::GraphExt; |
54a0048b | 14 | |
74b04a01 | 15 | use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; |
9fa01778 | 16 | use crate::traits::select::IntercrateAmbiguityCause; |
dfeec247 | 17 | use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine}; |
dfeec247 XL |
18 | use rustc_data_structures::fx::FxHashSet; |
19 | use rustc_errors::struct_span_err; | |
f9f354fc | 20 | use rustc_hir::def_id::{DefId, LocalDefId}; |
ba9703b0 XL |
21 | use rustc_middle::lint::LintDiagnosticBuilder; |
22 | use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; | |
23 | use rustc_middle::ty::{self, TyCtxt}; | |
74b04a01 | 24 | use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; |
dfeec247 XL |
25 | use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; |
26 | use rustc_span::DUMMY_SP; | |
ff7c6d11 | 27 | |
0731742a | 28 | use super::util::impl_trait_ref_and_oblig; |
dfeec247 | 29 | use super::{FulfillmentContext, SelectionContext}; |
60c5eb7d | 30 | |
54a0048b | 31 | /// Information pertinent to an overlapping impl error. |
0731742a | 32 | #[derive(Debug)] |
a7813a04 | 33 | pub struct OverlapError { |
54a0048b | 34 | pub with_impl: DefId, |
a7813a04 | 35 | pub trait_desc: String, |
cc61c64b | 36 | pub self_desc: Option<String>, |
ea8adc8c | 37 | pub intercrate_ambiguity_causes: Vec<IntercrateAmbiguityCause>, |
0731742a | 38 | pub involves_placeholder: bool, |
54a0048b SL |
39 | } |
40 | ||
41 | /// Given a subst for the requested impl, translate it to a subst | |
42 | /// appropriate for the actual item definition (whether it be in that impl, | |
43 | /// a parent impl, or the trait). | |
7cac9316 | 44 | /// |
54a0048b SL |
45 | /// When we have selected one impl, but are actually using item definitions from |
46 | /// a parent impl providing a default, we need a way to translate between the | |
47 | /// type parameters of the two impls. Here the `source_impl` is the one we've | |
9e0c209e SL |
48 | /// selected, and `source_substs` is a substitution of its generics. |
49 | /// And `target_node` is the impl/trait we're actually going to get the | |
50 | /// definition from. The resulting substitution will map from `target_node`'s | |
51 | /// generics to `source_impl`'s generics as instantiated by `source_subst`. | |
54a0048b SL |
52 | /// |
53 | /// For example, consider the following scenario: | |
54 | /// | |
55 | /// ```rust | |
56 | /// trait Foo { ... } | |
57 | /// impl<T, U> Foo for (T, U) { ... } // target impl | |
58 | /// impl<V> Foo for (V, V) { ... } // source impl | |
59 | /// ``` | |
60 | /// | |
61 | /// Suppose we have selected "source impl" with `V` instantiated with `u32`. | |
62 | /// This function will produce a substitution with `T` and `U` both mapping to `u32`. | |
63 | /// | |
9fa01778 | 64 | /// where-clauses add some trickiness here, because they can be used to "define" |
54a0048b SL |
65 | /// an argument indirectly: |
66 | /// | |
67 | /// ```rust | |
68 | /// impl<'a, I, T: 'a> Iterator for Cloned<I> | |
9fa01778 | 69 | /// where I: Iterator<Item = &'a T>, T: Clone |
54a0048b SL |
70 | /// ``` |
71 | /// | |
72 | /// In a case like this, the substitution for `T` is determined indirectly, | |
73 | /// through associated type projection. We deal with such cases by using | |
74 | /// *fulfillment* to relate the two impls, requiring that all projections are | |
75 | /// resolved. | |
dc9dc135 XL |
76 | pub fn translate_substs<'a, 'tcx>( |
77 | infcx: &InferCtxt<'a, 'tcx>, | |
78 | param_env: ty::ParamEnv<'tcx>, | |
79 | source_impl: DefId, | |
80 | source_substs: SubstsRef<'tcx>, | |
81 | target_node: specialization_graph::Node, | |
82 | ) -> SubstsRef<'tcx> { | |
dfeec247 XL |
83 | debug!( |
84 | "translate_substs({:?}, {:?}, {:?}, {:?})", | |
85 | param_env, source_impl, source_substs, target_node | |
86 | ); | |
87 | let source_trait_ref = | |
88 | infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs); | |
54a0048b | 89 | |
b7449926 | 90 | // translate the Self and Param parts of the substitution, since those |
54a0048b SL |
91 | // vary across impls |
92 | let target_substs = match target_node { | |
93 | specialization_graph::Node::Impl(target_impl) => { | |
3b2f2976 | 94 | // no need to translate if we're targeting the impl we started with |
54a0048b SL |
95 | if source_impl == target_impl { |
96 | return source_substs; | |
97 | } | |
98 | ||
dfeec247 XL |
99 | fulfill_implication(infcx, param_env, source_trait_ref, target_impl).unwrap_or_else( |
100 | |_| { | |
101 | bug!( | |
102 | "When translating substitutions for specialization, the expected \ | |
74b04a01 | 103 | specialization failed to hold" |
dfeec247 XL |
104 | ) |
105 | }, | |
106 | ) | |
54a0048b | 107 | } |
a7813a04 | 108 | specialization_graph::Node::Trait(..) => source_trait_ref.substs, |
54a0048b SL |
109 | }; |
110 | ||
111 | // directly inherent the method generics, since those do not vary across impls | |
9e0c209e SL |
112 | source_substs.rebase_onto(infcx.tcx, source_impl, target_substs) |
113 | } | |
114 | ||
9fa01778 | 115 | /// Is `impl1` a specialization of `impl2`? |
54a0048b SL |
116 | /// |
117 | /// Specialization is determined by the sets of types to which the impls apply; | |
9fa01778 | 118 | /// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies |
54a0048b | 119 | /// to. |
dfeec247 | 120 | pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { |
9e0c209e SL |
121 | debug!("specializes({:?}, {:?})", impl1_def_id, impl2_def_id); |
122 | ||
54a0048b SL |
123 | // The feature gate should prevent introducing new specializations, but not |
124 | // taking advantage of upstream ones. | |
ba9703b0 XL |
125 | let features = tcx.features(); |
126 | let specialization_enabled = features.specialization || features.min_specialization; | |
127 | if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { | |
54a0048b SL |
128 | return false; |
129 | } | |
130 | ||
131 | // We determine whether there's a subset relationship by: | |
132 | // | |
f035d41b | 133 | // - replacing bound vars with placeholders in impl1, |
54a0048b SL |
134 | // - assuming the where clauses for impl1, |
135 | // - instantiating impl2 with fresh inference variables, | |
136 | // - unifying, | |
137 | // - attempting to prove the where clauses for impl2 | |
138 | // | |
139 | // The last three steps are encapsulated in `fulfill_implication`. | |
140 | // | |
141 | // See RFC 1210 for more details and justification. | |
142 | ||
0731742a | 143 | // Currently we do not allow e.g., a negative impl to specialize a positive one |
7cac9316 | 144 | if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { |
54a0048b SL |
145 | return false; |
146 | } | |
147 | ||
0bf4aa26 | 148 | // create a parameter environment corresponding to a (placeholder) instantiation of impl1 |
7cac9316 XL |
149 | let penv = tcx.param_env(impl1_def_id); |
150 | let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); | |
54a0048b | 151 | |
94222f64 | 152 | // Create an infcx, taking the predicates of impl1 as assumptions: |
ea8adc8c | 153 | tcx.infer_ctxt().enter(|infcx| { |
c30ab7b3 SL |
154 | // Normalize the trait reference. The WF rules ought to ensure |
155 | // that this always succeeds. | |
dfeec247 XL |
156 | let impl1_trait_ref = match traits::fully_normalize( |
157 | &infcx, | |
158 | FulfillmentContext::new(), | |
159 | ObligationCause::dummy(), | |
160 | penv, | |
fc512014 | 161 | impl1_trait_ref, |
dfeec247 XL |
162 | ) { |
163 | Ok(impl1_trait_ref) => impl1_trait_ref, | |
164 | Err(err) => { | |
165 | bug!("failed to fully normalize {:?}: {:?}", impl1_trait_ref, err); | |
166 | } | |
167 | }; | |
a7813a04 | 168 | |
a7813a04 | 169 | // Attempt to prove that impl2 applies, given all of the above. |
7cac9316 | 170 | fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() |
ea8adc8c | 171 | }) |
54a0048b SL |
172 | } |
173 | ||
174 | /// Attempt to fulfill all obligations of `target_impl` after unification with | |
175 | /// `source_trait_ref`. If successful, returns a substitution for *all* the | |
176 | /// generics of `target_impl`, including both those needed to unify with | |
177 | /// `source_trait_ref` and those whose identity is determined via a where | |
178 | /// clause in the impl. | |
dc9dc135 XL |
179 | fn fulfill_implication<'a, 'tcx>( |
180 | infcx: &InferCtxt<'a, 'tcx>, | |
181 | param_env: ty::ParamEnv<'tcx>, | |
182 | source_trait_ref: ty::TraitRef<'tcx>, | |
183 | target_impl: DefId, | |
184 | ) -> Result<SubstsRef<'tcx>, ()> { | |
dfeec247 XL |
185 | debug!( |
186 | "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", | |
187 | param_env, source_trait_ref, target_impl | |
188 | ); | |
0731742a | 189 | |
3157f602 | 190 | let selcx = &mut SelectionContext::new(&infcx); |
9e0c209e | 191 | let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); |
ba9703b0 | 192 | let (target_trait_ref, obligations) = |
dfeec247 | 193 | impl_trait_ref_and_oblig(selcx, param_env, target_impl, target_substs); |
3157f602 XL |
194 | |
195 | // do the impls unify? If not, no specialization. | |
ba9703b0 XL |
196 | let more_obligations = |
197 | match infcx.at(&ObligationCause::dummy(), param_env).eq(source_trait_ref, target_trait_ref) | |
198 | { | |
199 | Ok(InferOk { obligations, .. }) => obligations, | |
200 | Err(_) => { | |
201 | debug!( | |
202 | "fulfill_implication: {:?} does not unify with {:?}", | |
203 | source_trait_ref, target_trait_ref | |
204 | ); | |
205 | return Err(()); | |
206 | } | |
207 | }; | |
54a0048b | 208 | |
3157f602 XL |
209 | // attempt to prove all of the predicates for impl2 given those for impl1 |
210 | // (which are packed up in penv) | |
54a0048b | 211 | |
cc61c64b | 212 | infcx.save_and_restore_in_snapshot_flag(|infcx| { |
ff7c6d11 XL |
213 | // If we came from `translate_substs`, we already know that the |
214 | // predicates for our impl hold (after all, we know that a more | |
215 | // specialized impl holds, so our impl must hold too), and | |
216 | // we only want to process the projections to determine the | |
217 | // the types in our substs using RFC 447, so we can safely | |
218 | // ignore region obligations, which allows us to avoid threading | |
219 | // a node-id to assign them with. | |
220 | // | |
221 | // If we came from specialization graph construction, then | |
222 | // we already make a mockery out of the region system, so | |
223 | // why not ignore them a bit earlier? | |
224 | let mut fulfill_cx = FulfillmentContext::new_ignoring_regions(); | |
ba9703b0 | 225 | for oblig in obligations.chain(more_obligations) { |
9e0c209e SL |
226 | fulfill_cx.register_predicate_obligation(&infcx, oblig); |
227 | } | |
3c0e092e XL |
228 | match fulfill_cx.select_all_or_error(infcx).as_slice() { |
229 | [] => { | |
230 | debug!( | |
231 | "fulfill_implication: an impl for {:?} specializes {:?}", | |
232 | source_trait_ref, target_trait_ref | |
233 | ); | |
234 | ||
235 | // Now resolve the *substitution* we built for the target earlier, replacing | |
236 | // the inference variables inside with whatever we got from fulfillment. | |
237 | Ok(infcx.resolve_vars_if_possible(target_substs)) | |
238 | } | |
239 | errors => { | |
9e0c209e | 240 | // no dice! |
dfeec247 XL |
241 | debug!( |
242 | "fulfill_implication: for impls on {:?} and {:?}, \ | |
74b04a01 | 243 | could not fulfill: {:?} given {:?}", |
f035d41b XL |
244 | source_trait_ref, |
245 | target_trait_ref, | |
246 | errors, | |
247 | param_env.caller_bounds() | |
dfeec247 | 248 | ); |
9e0c209e SL |
249 | Err(()) |
250 | } | |
9e0c209e SL |
251 | } |
252 | }) | |
54a0048b | 253 | } |
a7813a04 | 254 | |
7cac9316 | 255 | // Query provider for `specialization_graph_of`. |
416331ca XL |
256 | pub(super) fn specialization_graph_provider( |
257 | tcx: TyCtxt<'_>, | |
0731742a | 258 | trait_id: DefId, |
f9f354fc | 259 | ) -> specialization_graph::Graph { |
7cac9316 XL |
260 | let mut sg = specialization_graph::Graph::new(); |
261 | ||
ba9703b0 | 262 | let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); |
7cac9316 XL |
263 | |
264 | // The coherence checking implementation seems to rely on impls being | |
265 | // iterated over (roughly) in definition order, so we are sorting by | |
0731742a XL |
266 | // negated `CrateNum` (so remote definitions are visited first) and then |
267 | // by a flattened version of the `DefIndex`. | |
dfeec247 XL |
268 | trait_impls |
269 | .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); | |
7cac9316 XL |
270 | |
271 | for impl_def_id in trait_impls { | |
f9f354fc | 272 | if let Some(impl_def_id) = impl_def_id.as_local() { |
7cac9316 | 273 | // This is where impl overlap checking happens: |
f9f354fc | 274 | let insert_result = sg.insert(tcx, impl_def_id.to_def_id()); |
7cac9316 | 275 | // Report error if there was one. |
ff7c6d11 | 276 | let (overlap, used_to_be_allowed) = match insert_result { |
0731742a XL |
277 | Err(overlap) => (Some(overlap), None), |
278 | Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), | |
dfeec247 | 279 | Ok(None) => (None, None), |
ff7c6d11 XL |
280 | }; |
281 | ||
282 | if let Some(overlap) = overlap { | |
ba9703b0 | 283 | report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); |
7cac9316 XL |
284 | } |
285 | } else { | |
286 | let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); | |
287 | sg.record_impl_from_cstore(tcx, parent, impl_def_id) | |
288 | } | |
289 | } | |
290 | ||
f9f354fc | 291 | sg |
7cac9316 | 292 | } |
abe05a73 | 293 | |
3c0e092e XL |
294 | // This function is only used when |
295 | // encountering errors and inlining | |
296 | // it negatively impacts perf. | |
297 | #[cold] | |
298 | #[inline(never)] | |
ba9703b0 XL |
299 | fn report_overlap_conflict( |
300 | tcx: TyCtxt<'_>, | |
301 | overlap: OverlapError, | |
f9f354fc | 302 | impl_def_id: LocalDefId, |
ba9703b0 XL |
303 | used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, |
304 | sg: &mut specialization_graph::Graph, | |
305 | ) { | |
f9f354fc | 306 | let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); |
ba9703b0 XL |
307 | let other_polarity = tcx.impl_polarity(overlap.with_impl); |
308 | match (impl_polarity, other_polarity) { | |
309 | (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { | |
310 | report_negative_positive_conflict( | |
311 | tcx, | |
312 | &overlap, | |
313 | impl_def_id, | |
f9f354fc | 314 | impl_def_id.to_def_id(), |
ba9703b0 XL |
315 | overlap.with_impl, |
316 | sg, | |
317 | ); | |
318 | } | |
319 | ||
320 | (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { | |
321 | report_negative_positive_conflict( | |
322 | tcx, | |
323 | &overlap, | |
324 | impl_def_id, | |
325 | overlap.with_impl, | |
f9f354fc | 326 | impl_def_id.to_def_id(), |
ba9703b0 XL |
327 | sg, |
328 | ); | |
329 | } | |
330 | ||
331 | _ => { | |
332 | report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); | |
333 | } | |
334 | } | |
335 | } | |
336 | ||
337 | fn report_negative_positive_conflict( | |
338 | tcx: TyCtxt<'_>, | |
339 | overlap: &OverlapError, | |
f9f354fc | 340 | local_impl_def_id: LocalDefId, |
ba9703b0 XL |
341 | negative_impl_def_id: DefId, |
342 | positive_impl_def_id: DefId, | |
343 | sg: &mut specialization_graph::Graph, | |
344 | ) { | |
f9f354fc XL |
345 | let impl_span = tcx |
346 | .sess | |
347 | .source_map() | |
348 | .guess_head_span(tcx.span_of_impl(local_impl_def_id.to_def_id()).unwrap()); | |
ba9703b0 XL |
349 | |
350 | let mut err = struct_span_err!( | |
351 | tcx.sess, | |
352 | impl_span, | |
353 | E0751, | |
354 | "found both positive and negative implementation of trait `{}`{}:", | |
355 | overlap.trait_desc, | |
6a06907d | 356 | overlap.self_desc.clone().map_or_else(String::new, |ty| format!(" for type `{}`", ty)) |
ba9703b0 XL |
357 | ); |
358 | ||
359 | match tcx.span_of_impl(negative_impl_def_id) { | |
360 | Ok(span) => { | |
361 | err.span_label( | |
362 | tcx.sess.source_map().guess_head_span(span), | |
363 | "negative implementation here".to_string(), | |
364 | ); | |
365 | } | |
366 | Err(cname) => { | |
367 | err.note(&format!("negative implementation in crate `{}`", cname)); | |
368 | } | |
369 | } | |
370 | ||
371 | match tcx.span_of_impl(positive_impl_def_id) { | |
372 | Ok(span) => { | |
373 | err.span_label( | |
374 | tcx.sess.source_map().guess_head_span(span), | |
375 | "positive implementation here".to_string(), | |
376 | ); | |
377 | } | |
378 | Err(cname) => { | |
379 | err.note(&format!("positive implementation in crate `{}`", cname)); | |
380 | } | |
381 | } | |
382 | ||
383 | sg.has_errored = true; | |
384 | err.emit(); | |
385 | } | |
386 | ||
387 | fn report_conflicting_impls( | |
388 | tcx: TyCtxt<'_>, | |
389 | overlap: OverlapError, | |
f9f354fc | 390 | impl_def_id: LocalDefId, |
ba9703b0 XL |
391 | used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, |
392 | sg: &mut specialization_graph::Graph, | |
393 | ) { | |
f9f354fc XL |
394 | let impl_span = |
395 | tcx.sess.source_map().guess_head_span(tcx.span_of_impl(impl_def_id.to_def_id()).unwrap()); | |
ba9703b0 XL |
396 | |
397 | // Work to be done after we've built the DiagnosticBuilder. We have to define it | |
398 | // now because the struct_lint methods don't return back the DiagnosticBuilder | |
399 | // that's passed in. | |
400 | let decorate = |err: LintDiagnosticBuilder<'_>| { | |
401 | let msg = format!( | |
cdc7bbd5 | 402 | "conflicting implementations of trait `{}`{}{}", |
ba9703b0 | 403 | overlap.trait_desc, |
6a06907d XL |
404 | overlap |
405 | .self_desc | |
406 | .clone() | |
407 | .map_or_else(String::new, |ty| { format!(" for type `{}`", ty) }), | |
ba9703b0 | 408 | match used_to_be_allowed { |
cdc7bbd5 | 409 | Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", |
ba9703b0 XL |
410 | _ => "", |
411 | } | |
412 | ); | |
413 | let mut err = err.build(&msg); | |
414 | match tcx.span_of_impl(overlap.with_impl) { | |
415 | Ok(span) => { | |
416 | err.span_label( | |
417 | tcx.sess.source_map().guess_head_span(span), | |
418 | "first implementation here".to_string(), | |
419 | ); | |
420 | ||
421 | err.span_label( | |
422 | impl_span, | |
423 | format!( | |
424 | "conflicting implementation{}", | |
6a06907d | 425 | overlap.self_desc.map_or_else(String::new, |ty| format!(" for `{}`", ty)) |
ba9703b0 XL |
426 | ), |
427 | ); | |
428 | } | |
429 | Err(cname) => { | |
430 | let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { | |
431 | Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), | |
432 | None => format!("conflicting implementation in crate `{}`", cname), | |
433 | }; | |
434 | err.note(&msg); | |
435 | } | |
436 | } | |
437 | ||
438 | for cause in &overlap.intercrate_ambiguity_causes { | |
439 | cause.add_intercrate_ambiguity_hint(&mut err); | |
440 | } | |
441 | ||
442 | if overlap.involves_placeholder { | |
443 | coherence::add_placeholder_note(&mut err); | |
444 | } | |
445 | err.emit() | |
446 | }; | |
447 | ||
448 | match used_to_be_allowed { | |
449 | None => { | |
450 | sg.has_errored = true; | |
3c0e092e XL |
451 | if overlap.with_impl.is_local() || !tcx.orphan_check_crate(()).contains(&impl_def_id) { |
452 | let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); | |
453 | decorate(LintDiagnosticBuilder::new(err)); | |
454 | } else { | |
455 | tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check"); | |
456 | } | |
ba9703b0 XL |
457 | } |
458 | Some(kind) => { | |
459 | let lint = match kind { | |
460 | FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, | |
461 | FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, | |
462 | }; | |
463 | tcx.struct_span_lint_hir( | |
464 | lint, | |
3dfed10e | 465 | tcx.hir().local_def_id_to_hir_id(impl_def_id), |
ba9703b0 XL |
466 | impl_span, |
467 | decorate, | |
468 | ) | |
469 | } | |
470 | }; | |
471 | } | |
472 | ||
abe05a73 XL |
473 | /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a |
474 | /// string. | |
3c0e092e | 475 | crate fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { |
abe05a73 XL |
476 | use std::fmt::Write; |
477 | ||
ba9703b0 | 478 | let trait_ref = tcx.impl_trait_ref(impl_def_id)?; |
abe05a73 XL |
479 | let mut w = "impl".to_owned(); |
480 | ||
532ac7d7 | 481 | let substs = InternalSubsts::identity_for_item(tcx, impl_def_id); |
abe05a73 XL |
482 | |
483 | // FIXME: Currently only handles ?Sized. | |
484 | // Needs to support ?Move and ?DynSized when they are implemented. | |
485 | let mut types_without_default_bounds = FxHashSet::default(); | |
486 | let sized_trait = tcx.lang_items().sized_trait(); | |
487 | ||
488 | if !substs.is_noop() { | |
489 | types_without_default_bounds.extend(substs.types()); | |
490 | w.push('<'); | |
dfeec247 XL |
491 | w.push_str( |
492 | &substs | |
493 | .iter() | |
494 | .map(|k| k.to_string()) | |
495 | .filter(|k| k != "'_") | |
496 | .collect::<Vec<_>>() | |
497 | .join(", "), | |
498 | ); | |
abe05a73 XL |
499 | w.push('>'); |
500 | } | |
501 | ||
60c5eb7d | 502 | write!(w, " {} for {}", trait_ref.print_only_trait_path(), tcx.type_of(impl_def_id)).unwrap(); |
abe05a73 XL |
503 | |
504 | // The predicates will contain default bounds like `T: Sized`. We need to | |
505 | // remove these bounds, and add `T: ?Sized` to any untouched type parameters. | |
e74abb32 | 506 | let predicates = tcx.predicates_of(impl_def_id).predicates; |
dfeec247 XL |
507 | let mut pretty_predicates = |
508 | Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); | |
0bf4aa26 XL |
509 | |
510 | for (p, _) in predicates { | |
abe05a73 | 511 | if let Some(poly_trait_ref) = p.to_opt_poly_trait_ref() { |
fc512014 XL |
512 | if Some(poly_trait_ref.value.def_id()) == sized_trait { |
513 | types_without_default_bounds.remove(poly_trait_ref.value.self_ty().skip_binder()); | |
abe05a73 XL |
514 | continue; |
515 | } | |
516 | } | |
517 | pretty_predicates.push(p.to_string()); | |
518 | } | |
0bf4aa26 | 519 | |
dfeec247 XL |
520 | pretty_predicates |
521 | .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); | |
0bf4aa26 | 522 | |
abe05a73 XL |
523 | if !pretty_predicates.is_empty() { |
524 | write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); | |
525 | } | |
526 | ||
527 | w.push(';'); | |
528 | Some(w) | |
529 | } |