]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Error Reporting Code for the inference engine | |
12 | //! | |
13 | //! Because of the way inference, and in particular region inference, | |
14 | //! works, it often happens that errors are not detected until far after | |
15 | //! the relevant line of code has been type-checked. Therefore, there is | |
16 | //! an elaborate system to track why a particular constraint in the | |
17 | //! inference graph arose so that we can explain to the user what gave | |
18 | //! rise to a particular error. | |
19 | //! | |
20 | //! The basis of the system are the "origin" types. An "origin" is the | |
21 | //! reason that a constraint or inference variable arose. There are | |
22 | //! different "origin" enums for different kinds of constraints/variables | |
23 | //! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has | |
24 | //! a span, but also more information so that we can generate a meaningful | |
25 | //! error message. | |
26 | //! | |
3b2f2976 | 27 | //! Having a catalog of all the different reasons an error can arise is |
1a4d82fc JJ |
28 | //! also useful for other reasons, like cross-referencing FAQs etc, though |
29 | //! we are not really taking advantage of this yet. | |
30 | //! | |
31 | //! # Region Inference | |
32 | //! | |
33 | //! Region inference is particularly tricky because it always succeeds "in | |
34 | //! the moment" and simply registers a constraint. Then, at the end, we | |
35 | //! can compute the full graph and report errors, so we need to be able to | |
36 | //! store and later report what gave rise to the conflicting constraints. | |
37 | //! | |
38 | //! # Subtype Trace | |
39 | //! | |
40 | //! Determining whether `T1 <: T2` often involves a number of subtypes and | |
41 | //! subconstraints along the way. A "TypeTrace" is an extended version | |
42 | //! of an origin that traces the types and other values that were being | |
43 | //! compared. It is not necessarily comprehensive (in fact, at the time of | |
44 | //! this writing it only tracks the root values being compared) but I'd | |
45 | //! like to extend it to include significant "waypoints". For example, if | |
46 | //! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2 | |
47 | //! <: T4` fails, I'd like the trace to include enough information to say | |
48 | //! "in the 2nd element of the tuple". Similarly, failures when comparing | |
49 | //! arguments or return types in fn types should be able to cite the | |
50 | //! specific position, etc. | |
51 | //! | |
52 | //! # Reality vs plan | |
53 | //! | |
54 | //! Of course, there is still a LOT of code in typeck that has yet to be | |
55 | //! ported to this system, and which relies on string concatenation at the | |
56 | //! time of error detection. | |
57 | ||
8bb4bdeb | 58 | use infer; |
0531ce1d | 59 | use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; |
abe05a73 XL |
60 | use super::region_constraints::GenericKind; |
61 | use super::lexical_region_resolve::RegionResolutionError; | |
e9174d1e | 62 | |
8bb4bdeb | 63 | use std::fmt; |
54a0048b | 64 | use hir; |
8bb4bdeb | 65 | use hir::map as hir_map; |
54a0048b | 66 | use hir::def_id::DefId; |
62682a34 | 67 | use middle::region; |
476ff2be | 68 | use traits::{ObligationCause, ObligationCauseCode}; |
abe05a73 | 69 | use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants}; |
54a0048b | 70 | use ty::error::TypeError; |
7cac9316 | 71 | use syntax::ast::DUMMY_NODE_ID; |
32a655c1 | 72 | use syntax_pos::{Pos, Span}; |
cc61c64b | 73 | use errors::{DiagnosticBuilder, DiagnosticStyledString}; |
1a4d82fc | 74 | |
ea8adc8c XL |
75 | use rustc_data_structures::indexed_vec::Idx; |
76 | ||
8bb4bdeb XL |
77 | mod note; |
78 | ||
041b39d2 | 79 | mod need_type_info; |
3b2f2976 | 80 | |
ff7c6d11 | 81 | pub mod nice_region_error; |
041b39d2 | 82 | |
a7813a04 | 83 | impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { |
0531ce1d XL |
84 | pub fn note_and_explain_region( |
85 | self, | |
86 | region_scope_tree: ®ion::ScopeTree, | |
87 | err: &mut DiagnosticBuilder, | |
88 | prefix: &str, | |
89 | region: ty::Region<'tcx>, | |
90 | suffix: &str, | |
91 | ) { | |
9e0c209e | 92 | let (description, span) = match *region { |
c1a9b12d SL |
93 | ty::ReScope(scope) => { |
94 | let new_string; | |
95 | let unknown_scope = || { | |
0531ce1d XL |
96 | format!( |
97 | "{}unknown scope: {:?}{}. Please report a bug.", | |
98 | prefix, scope, suffix | |
99 | ) | |
c1a9b12d | 100 | }; |
ea8adc8c XL |
101 | let span = scope.span(self, region_scope_tree); |
102 | let tag = match self.hir.find(scope.node_id(self, region_scope_tree)) { | |
32a655c1 SL |
103 | Some(hir_map::NodeBlock(_)) => "block", |
104 | Some(hir_map::NodeExpr(expr)) => match expr.node { | |
e9174d1e SL |
105 | hir::ExprCall(..) => "call", |
106 | hir::ExprMethodCall(..) => "method call", | |
9e0c209e | 107 | hir::ExprMatch(.., hir::MatchSource::IfLetDesugar { .. }) => "if let", |
0531ce1d XL |
108 | hir::ExprMatch(.., hir::MatchSource::WhileLetDesugar) => "while let", |
109 | hir::ExprMatch(.., hir::MatchSource::ForLoopDesugar) => "for", | |
e9174d1e | 110 | hir::ExprMatch(..) => "match", |
c1a9b12d SL |
111 | _ => "expression", |
112 | }, | |
32a655c1 | 113 | Some(hir_map::NodeStmt(_)) => "statement", |
0531ce1d XL |
114 | Some(hir_map::NodeItem(it)) => Self::item_scope_tag(&it), |
115 | Some(hir_map::NodeTraitItem(it)) => Self::trait_item_scope_tag(&it), | |
116 | Some(hir_map::NodeImplItem(it)) => Self::impl_item_scope_tag(&it), | |
c1a9b12d | 117 | Some(_) | None => { |
9cc50fc6 SL |
118 | err.span_note(span, &unknown_scope()); |
119 | return; | |
c1a9b12d SL |
120 | } |
121 | }; | |
ea8adc8c XL |
122 | let scope_decorated_tag = match scope.data() { |
123 | region::ScopeData::Node(_) => tag, | |
0531ce1d XL |
124 | region::ScopeData::CallSite(_) => "scope of call-site for function", |
125 | region::ScopeData::Arguments(_) => "scope of function body", | |
ea8adc8c | 126 | region::ScopeData::Destruction(_) => { |
c1a9b12d SL |
127 | new_string = format!("destruction scope surrounding {}", tag); |
128 | &new_string[..] | |
129 | } | |
ea8adc8c | 130 | region::ScopeData::Remainder(r) => { |
0531ce1d XL |
131 | new_string = format!( |
132 | "block suffix following statement {}", | |
133 | r.first_statement_index.index() | |
134 | ); | |
c1a9b12d SL |
135 | &new_string[..] |
136 | } | |
137 | }; | |
0531ce1d | 138 | self.explain_span(scope_decorated_tag, span) |
c1a9b12d | 139 | } |
62682a34 | 140 | |
0531ce1d XL |
141 | ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { |
142 | self.msg_span_from_free_region(region) | |
62682a34 | 143 | } |
62682a34 | 144 | |
c1a9b12d | 145 | ty::ReEmpty => ("the empty lifetime".to_owned(), None), |
62682a34 | 146 | |
e9174d1e SL |
147 | // FIXME(#13998) ReSkolemized should probably print like |
148 | // ReFree rather than dumping Debug output on the user. | |
149 | // | |
150 | // We shouldn't really be having unification failures with ReVar | |
151 | // and ReLateBound though. | |
0531ce1d | 152 | ty::ReSkolemized(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { |
c1a9b12d SL |
153 | (format!("lifetime {:?}", region), None) |
154 | } | |
ff7c6d11 XL |
155 | |
156 | // We shouldn't encounter an error message with ReClosureBound. | |
0531ce1d | 157 | ty::ReCanonical(..) | |
ff7c6d11 | 158 | ty::ReClosureBound(..) => { |
0531ce1d XL |
159 | bug!("encountered unexpected ReClosureBound: {:?}", region,); |
160 | } | |
161 | }; | |
162 | ||
163 | TyCtxt::emit_msg_span(err, prefix, description, span, suffix); | |
164 | } | |
165 | ||
166 | pub fn note_and_explain_free_region( | |
167 | self, | |
168 | err: &mut DiagnosticBuilder, | |
169 | prefix: &str, | |
170 | region: ty::Region<'tcx>, | |
171 | suffix: &str, | |
172 | ) { | |
173 | let (description, span) = self.msg_span_from_free_region(region); | |
174 | ||
175 | TyCtxt::emit_msg_span(err, prefix, description, span, suffix); | |
176 | } | |
177 | ||
178 | fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) { | |
179 | match *region { | |
180 | ty::ReEarlyBound(_) | ty::ReFree(_) => { | |
181 | self.msg_span_from_early_bound_and_free_regions(region) | |
182 | }, | |
183 | ty::ReStatic => ("the static lifetime".to_owned(), None), | |
184 | _ => bug!("{:?}", region), | |
185 | } | |
186 | } | |
187 | ||
188 | fn msg_span_from_early_bound_and_free_regions( | |
189 | self, | |
190 | region: ty::Region<'tcx>, | |
191 | ) -> (String, Option<Span>) { | |
192 | let scope = region.free_region_binding_scope(self); | |
193 | let node = self.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); | |
194 | let unknown; | |
195 | let tag = match self.hir.find(node) { | |
196 | Some(hir_map::NodeBlock(_)) | Some(hir_map::NodeExpr(_)) => "body", | |
197 | Some(hir_map::NodeItem(it)) => Self::item_scope_tag(&it), | |
198 | Some(hir_map::NodeTraitItem(it)) => Self::trait_item_scope_tag(&it), | |
199 | Some(hir_map::NodeImplItem(it)) => Self::impl_item_scope_tag(&it), | |
200 | ||
201 | // this really should not happen, but it does: | |
202 | // FIXME(#27942) | |
203 | Some(_) => { | |
204 | unknown = format!( | |
205 | "unexpected node ({}) for scope {:?}. \ | |
206 | Please report a bug.", | |
207 | self.hir.node_to_string(node), | |
208 | scope | |
ff7c6d11 | 209 | ); |
0531ce1d | 210 | &unknown |
ff7c6d11 | 211 | } |
0531ce1d XL |
212 | None => { |
213 | unknown = format!( | |
214 | "unknown node for scope {:?}. \ | |
215 | Please report a bug.", | |
216 | scope | |
217 | ); | |
218 | &unknown | |
219 | } | |
220 | }; | |
221 | let (prefix, span) = match *region { | |
222 | ty::ReEarlyBound(ref br) => ( | |
223 | format!("the lifetime {} as defined on", br.name), | |
224 | self.sess.codemap().def_span(self.hir.span(node)), | |
225 | ), | |
226 | ty::ReFree(ref fr) => match fr.bound_region { | |
227 | ty::BrAnon(idx) => ( | |
228 | format!("the anonymous lifetime #{} defined on", idx + 1), | |
229 | self.hir.span(node), | |
230 | ), | |
231 | ty::BrFresh(_) => ( | |
232 | "an anonymous lifetime defined on".to_owned(), | |
233 | self.hir.span(node), | |
234 | ), | |
235 | _ => ( | |
236 | format!("the lifetime {} as defined on", fr.bound_region), | |
237 | self.sess.codemap().def_span(self.hir.span(node)), | |
238 | ), | |
239 | }, | |
240 | _ => bug!(), | |
c1a9b12d | 241 | }; |
0531ce1d XL |
242 | let (msg, opt_span) = self.explain_span(tag, span); |
243 | (format!("{} {}", prefix, msg), opt_span) | |
244 | } | |
245 | ||
246 | fn emit_msg_span( | |
247 | err: &mut DiagnosticBuilder, | |
248 | prefix: &str, | |
249 | description: String, | |
250 | span: Option<Span>, | |
251 | suffix: &str, | |
252 | ) { | |
c1a9b12d | 253 | let message = format!("{}{}{}", prefix, description, suffix); |
0531ce1d | 254 | |
c1a9b12d | 255 | if let Some(span) = span { |
9cc50fc6 | 256 | err.span_note(span, &message); |
c1a9b12d | 257 | } else { |
9cc50fc6 | 258 | err.note(&message); |
62682a34 | 259 | } |
62682a34 | 260 | } |
0531ce1d XL |
261 | |
262 | fn item_scope_tag(item: &hir::Item) -> &'static str { | |
263 | match item.node { | |
264 | hir::ItemImpl(..) => "impl", | |
265 | hir::ItemStruct(..) => "struct", | |
266 | hir::ItemUnion(..) => "union", | |
267 | hir::ItemEnum(..) => "enum", | |
268 | hir::ItemTrait(..) => "trait", | |
269 | hir::ItemFn(..) => "function body", | |
270 | _ => "item", | |
271 | } | |
272 | } | |
273 | ||
274 | fn trait_item_scope_tag(item: &hir::TraitItem) -> &'static str { | |
275 | match item.node { | |
276 | hir::TraitItemKind::Method(..) => "method body", | |
277 | hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item", | |
278 | } | |
279 | } | |
280 | ||
281 | fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str { | |
282 | match item.node { | |
283 | hir::ImplItemKind::Method(..) => "method body", | |
284 | hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => "associated item", | |
285 | } | |
286 | } | |
287 | ||
288 | fn explain_span(self, heading: &str, span: Span) -> (String, Option<Span>) { | |
289 | let lo = self.sess.codemap().lookup_char_pos_adj(span.lo()); | |
290 | ( | |
291 | format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), | |
292 | Some(span), | |
293 | ) | |
294 | } | |
62682a34 | 295 | } |
1a4d82fc | 296 | |
a7813a04 | 297 | impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { |
0531ce1d XL |
298 | pub fn report_region_errors( |
299 | &self, | |
300 | region_scope_tree: ®ion::ScopeTree, | |
301 | errors: &Vec<RegionResolutionError<'tcx>>, | |
302 | will_later_be_reported_by_nll: bool, | |
303 | ) { | |
54a0048b SL |
304 | debug!("report_region_errors(): {} errors to start", errors.len()); |
305 | ||
83c7162d | 306 | if will_later_be_reported_by_nll && self.tcx.use_mir_borrowck() { |
ff7c6d11 XL |
307 | // With `#![feature(nll)]`, we want to present a nice user |
308 | // experience, so don't even mention the errors from the | |
309 | // AST checker. | |
0531ce1d | 310 | if self.tcx.features().nll { |
ff7c6d11 XL |
311 | return; |
312 | } | |
313 | ||
83c7162d | 314 | // But with nll, it's nice to have some note for later. |
abe05a73 XL |
315 | for error in errors { |
316 | match *error { | |
0531ce1d XL |
317 | RegionResolutionError::ConcreteFailure(ref origin, ..) |
318 | | RegionResolutionError::GenericBoundFailure(ref origin, ..) => { | |
319 | self.tcx | |
320 | .sess | |
83c7162d | 321 | .span_warn(origin.span(), "not reporting region error due to nll"); |
abe05a73 XL |
322 | } |
323 | ||
324 | RegionResolutionError::SubSupConflict(ref rvo, ..) => { | |
0531ce1d XL |
325 | self.tcx |
326 | .sess | |
83c7162d | 327 | .span_warn(rvo.span(), "not reporting region error due to nll"); |
abe05a73 XL |
328 | } |
329 | } | |
330 | } | |
331 | ||
332 | return; | |
333 | } | |
334 | ||
54a0048b SL |
335 | // try to pre-process the errors, which will group some of them |
336 | // together into a `ProcessedErrors` group: | |
32a655c1 | 337 | let errors = self.process_errors(errors); |
54a0048b | 338 | |
0531ce1d XL |
339 | debug!( |
340 | "report_region_errors: {} errors after preprocessing", | |
341 | errors.len() | |
342 | ); | |
54a0048b | 343 | |
85aaf69f | 344 | for error in errors { |
c30ab7b3 | 345 | debug!("report_region_errors: error = {:?}", error); |
1a4d82fc | 346 | |
ff7c6d11 | 347 | if !self.try_report_nice_region_error(&error) { |
abe05a73 XL |
348 | match error.clone() { |
349 | // These errors could indicate all manner of different | |
350 | // problems with many different solutions. Rather | |
351 | // than generate a "one size fits all" error, what we | |
352 | // attempt to do is go through a number of specific | |
353 | // scenarios and try to find the best way to present | |
354 | // the error. If all of these fails, we fall back to a rather | |
355 | // general bit of code that displays the error information | |
356 | RegionResolutionError::ConcreteFailure(origin, sub, sup) => { | |
0531ce1d XL |
357 | self.report_concrete_failure(region_scope_tree, origin, sub, sup) |
358 | .emit(); | |
abe05a73 XL |
359 | } |
360 | ||
ff7c6d11 XL |
361 | RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { |
362 | self.report_generic_bound_failure( | |
363 | region_scope_tree, | |
364 | origin.span(), | |
365 | Some(origin), | |
366 | param_ty, | |
367 | sub, | |
368 | ); | |
abe05a73 XL |
369 | } |
370 | ||
0531ce1d XL |
371 | RegionResolutionError::SubSupConflict( |
372 | var_origin, | |
373 | sub_origin, | |
374 | sub_r, | |
375 | sup_origin, | |
376 | sup_r, | |
377 | ) => { | |
378 | self.report_sub_sup_conflict( | |
379 | region_scope_tree, | |
380 | var_origin, | |
381 | sub_origin, | |
382 | sub_r, | |
383 | sup_origin, | |
384 | sup_r, | |
385 | ); | |
abe05a73 XL |
386 | } |
387 | } | |
1a4d82fc JJ |
388 | } |
389 | } | |
390 | } | |
391 | ||
392 | // This method goes through all the errors and try to group certain types | |
393 | // of error together, for the purpose of suggesting explicit lifetime | |
394 | // parameters to the user. This is done so that we can have a more | |
395 | // complete view of what lifetimes should be the same. | |
396 | // If the return value is an empty vector, it means that processing | |
54a0048b SL |
397 | // failed (so the return value of this method should not be used). |
398 | // | |
399 | // The method also attempts to weed out messages that seem like | |
400 | // duplicates that will be unhelpful to the end-user. But | |
401 | // obviously it never weeds out ALL errors. | |
0531ce1d XL |
402 | fn process_errors( |
403 | &self, | |
404 | errors: &Vec<RegionResolutionError<'tcx>>, | |
405 | ) -> Vec<RegionResolutionError<'tcx>> { | |
1a4d82fc | 406 | debug!("process_errors()"); |
54a0048b | 407 | |
32a655c1 SL |
408 | // We want to avoid reporting generic-bound failures if we can |
409 | // avoid it: these have a very high rate of being unhelpful in | |
410 | // practice. This is because they are basically secondary | |
411 | // checks that test the state of the region graph after the | |
412 | // rest of inference is done, and the other kinds of errors | |
413 | // indicate that the region constraint graph is internally | |
414 | // inconsistent, so these test results are likely to be | |
415 | // meaningless. | |
416 | // | |
417 | // Therefore, we filter them out of the list unless they are | |
418 | // the only thing in the list. | |
419 | ||
420 | let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { | |
abe05a73 | 421 | RegionResolutionError::GenericBoundFailure(..) => true, |
0531ce1d XL |
422 | RegionResolutionError::ConcreteFailure(..) |
423 | | RegionResolutionError::SubSupConflict(..) => false, | |
32a655c1 | 424 | }; |
1a4d82fc | 425 | |
ea8adc8c | 426 | let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { |
32a655c1 SL |
427 | errors.clone() |
428 | } else { | |
0531ce1d XL |
429 | errors |
430 | .iter() | |
431 | .filter(|&e| !is_bound_failure(e)) | |
432 | .cloned() | |
433 | .collect() | |
ea8adc8c XL |
434 | }; |
435 | ||
436 | // sort the errors by span, for better error message stability. | |
437 | errors.sort_by_key(|u| match *u { | |
abe05a73 XL |
438 | RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), |
439 | RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), | |
440 | RegionResolutionError::SubSupConflict(ref rvo, _, _, _, _) => rvo.span(), | |
ea8adc8c XL |
441 | }); |
442 | errors | |
1a4d82fc JJ |
443 | } |
444 | ||
e9174d1e | 445 | /// Adds a note if the types come from similarly named crates |
0531ce1d XL |
446 | fn check_and_note_conflicting_crates( |
447 | &self, | |
448 | err: &mut DiagnosticBuilder, | |
449 | terr: &TypeError<'tcx>, | |
450 | sp: Span, | |
451 | ) { | |
9cc50fc6 | 452 | let report_path_match = |err: &mut DiagnosticBuilder, did1: DefId, did2: DefId| { |
e9174d1e SL |
453 | // Only external crates, if either is from a local |
454 | // module we could have false positives | |
455 | if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate { | |
54a0048b SL |
456 | let exp_path = self.tcx.item_path_str(did1); |
457 | let found_path = self.tcx.item_path_str(did2); | |
041b39d2 XL |
458 | let exp_abs_path = self.tcx.absolute_item_path_str(did1); |
459 | let found_abs_path = self.tcx.absolute_item_path_str(did2); | |
54a0048b | 460 | // We compare strings because DefPath can be different |
e9174d1e | 461 | // for imported and non-imported crates |
0531ce1d | 462 | if exp_path == found_path || exp_abs_path == found_abs_path { |
ea8adc8c | 463 | let crate_name = self.tcx.crate_name(did1.krate); |
0531ce1d XL |
464 | err.span_note( |
465 | sp, | |
466 | &format!( | |
467 | "Perhaps two different versions \ | |
468 | of crate `{}` are being used?", | |
469 | crate_name | |
470 | ), | |
471 | ); | |
e9174d1e SL |
472 | } |
473 | } | |
474 | }; | |
475 | match *terr { | |
476 | TypeError::Sorts(ref exp_found) => { | |
477 | // if they are both "path types", there's a chance of ambiguity | |
478 | // due to different versions of the same crate | |
479 | match (&exp_found.expected.sty, &exp_found.found.sty) { | |
9e0c209e | 480 | (&ty::TyAdt(exp_adt, _), &ty::TyAdt(found_adt, _)) => { |
9cc50fc6 | 481 | report_path_match(err, exp_adt.did, found_adt.did); |
0531ce1d XL |
482 | } |
483 | _ => (), | |
e9174d1e | 484 | } |
0531ce1d | 485 | } |
e9174d1e | 486 | TypeError::Traits(ref exp_found) => { |
9cc50fc6 | 487 | report_path_match(err, exp_found.expected, exp_found.found); |
0531ce1d XL |
488 | } |
489 | _ => (), // FIXME(#22750) handle traits and stuff | |
e9174d1e SL |
490 | } |
491 | } | |
492 | ||
0531ce1d | 493 | fn note_error_origin(&self, err: &mut DiagnosticBuilder<'tcx>, cause: &ObligationCause<'tcx>) { |
476ff2be SL |
494 | match cause.code { |
495 | ObligationCauseCode::MatchExpressionArm { arm_span, source } => match source { | |
0531ce1d | 496 | hir::MatchSource::IfLetDesugar { .. } => { |
ff7c6d11 XL |
497 | let msg = "`if let` arm with an incompatible type"; |
498 | if self.tcx.sess.codemap().is_multiline(arm_span) { | |
499 | err.span_note(arm_span, msg); | |
500 | } else { | |
501 | err.span_label(arm_span, msg); | |
502 | } | |
5bcae85e SL |
503 | } |
504 | _ => { | |
ff7c6d11 XL |
505 | let msg = "match arm with an incompatible type"; |
506 | if self.tcx.sess.codemap().is_multiline(arm_span) { | |
507 | err.span_note(arm_span, msg); | |
508 | } else { | |
509 | err.span_label(arm_span, msg); | |
510 | } | |
5bcae85e SL |
511 | } |
512 | }, | |
0531ce1d | 513 | _ => (), |
5bcae85e SL |
514 | } |
515 | } | |
516 | ||
cc61c64b XL |
517 | /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` |
518 | /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and | |
519 | /// populate `other_value` with `other_ty`. | |
520 | /// | |
521 | /// ```text | |
522 | /// Foo<Bar<Qux>> | |
523 | /// ^^^^--------^ this is highlighted | |
524 | /// | | | |
525 | /// | this type argument is exactly the same as the other type, not highlighted | |
526 | /// this is highlighted | |
527 | /// Bar<Qux> | |
528 | /// -------- this type is the same as a type argument in the other type, not highlighted | |
529 | /// ``` | |
0531ce1d XL |
530 | fn highlight_outer( |
531 | &self, | |
532 | value: &mut DiagnosticStyledString, | |
533 | other_value: &mut DiagnosticStyledString, | |
534 | name: String, | |
535 | sub: &ty::subst::Substs<'tcx>, | |
536 | pos: usize, | |
537 | other_ty: &Ty<'tcx>, | |
538 | ) { | |
cc61c64b XL |
539 | // `value` and `other_value` hold two incomplete type representation for display. |
540 | // `name` is the path of both types being compared. `sub` | |
541 | value.push_highlighted(name); | |
542 | let len = sub.len(); | |
543 | if len > 0 { | |
544 | value.push_highlighted("<"); | |
545 | } | |
546 | ||
547 | // Output the lifetimes fot the first type | |
0531ce1d XL |
548 | let lifetimes = sub.regions() |
549 | .map(|lifetime| { | |
550 | let s = format!("{}", lifetime); | |
551 | if s.is_empty() { | |
552 | "'_".to_string() | |
553 | } else { | |
554 | s | |
555 | } | |
556 | }) | |
557 | .collect::<Vec<_>>() | |
558 | .join(", "); | |
cc61c64b XL |
559 | if !lifetimes.is_empty() { |
560 | if sub.regions().count() < len { | |
561 | value.push_normal(lifetimes + &", "); | |
562 | } else { | |
563 | value.push_normal(lifetimes); | |
564 | } | |
565 | } | |
566 | ||
567 | // Highlight all the type arguments that aren't at `pos` and compare the type argument at | |
568 | // `pos` and `other_ty`. | |
569 | for (i, type_arg) in sub.types().enumerate() { | |
570 | if i == pos { | |
571 | let values = self.cmp(type_arg, other_ty); | |
572 | value.0.extend((values.0).0); | |
573 | other_value.0.extend((values.1).0); | |
574 | } else { | |
575 | value.push_highlighted(format!("{}", type_arg)); | |
576 | } | |
577 | ||
578 | if len > 0 && i != len - 1 { | |
579 | value.push_normal(", "); | |
580 | } | |
581 | //self.push_comma(&mut value, &mut other_value, len, i); | |
582 | } | |
583 | if len > 0 { | |
584 | value.push_highlighted(">"); | |
585 | } | |
586 | } | |
587 | ||
588 | /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, | |
589 | /// as that is the difference to the other type. | |
590 | /// | |
591 | /// For the following code: | |
592 | /// | |
593 | /// ```norun | |
594 | /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>(); | |
595 | /// ``` | |
596 | /// | |
597 | /// The type error output will behave in the following way: | |
598 | /// | |
599 | /// ```text | |
600 | /// Foo<Bar<Qux>> | |
601 | /// ^^^^--------^ this is highlighted | |
602 | /// | | | |
603 | /// | this type argument is exactly the same as the other type, not highlighted | |
604 | /// this is highlighted | |
605 | /// Bar<Qux> | |
606 | /// -------- this type is the same as a type argument in the other type, not highlighted | |
607 | /// ``` | |
0531ce1d XL |
608 | fn cmp_type_arg( |
609 | &self, | |
610 | mut t1_out: &mut DiagnosticStyledString, | |
611 | mut t2_out: &mut DiagnosticStyledString, | |
612 | path: String, | |
613 | sub: &ty::subst::Substs<'tcx>, | |
614 | other_path: String, | |
615 | other_ty: &Ty<'tcx>, | |
616 | ) -> Option<()> { | |
cc61c64b XL |
617 | for (i, ta) in sub.types().enumerate() { |
618 | if &ta == other_ty { | |
619 | self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); | |
620 | return Some(()); | |
621 | } | |
622 | if let &ty::TyAdt(def, _) = &ta.sty { | |
623 | let path_ = self.tcx.item_path_str(def.did.clone()); | |
624 | if path_ == other_path { | |
625 | self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty); | |
626 | return Some(()); | |
627 | } | |
628 | } | |
629 | } | |
630 | None | |
631 | } | |
632 | ||
633 | /// Add a `,` to the type representation only if it is appropriate. | |
0531ce1d XL |
634 | fn push_comma( |
635 | &self, | |
636 | value: &mut DiagnosticStyledString, | |
637 | other_value: &mut DiagnosticStyledString, | |
638 | len: usize, | |
639 | pos: usize, | |
640 | ) { | |
cc61c64b XL |
641 | if len > 0 && pos != len - 1 { |
642 | value.push_normal(", "); | |
643 | other_value.push_normal(", "); | |
644 | } | |
645 | } | |
646 | ||
647 | /// Compare two given types, eliding parts that are the same between them and highlighting | |
648 | /// relevant differences, and return two representation of those types for highlighted printing. | |
0531ce1d | 649 | fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) { |
ff7c6d11 XL |
650 | fn equals<'tcx>(a: &Ty<'tcx>, b: &Ty<'tcx>) -> bool { |
651 | match (&a.sty, &b.sty) { | |
652 | (a, b) if *a == *b => true, | |
0531ce1d XL |
653 | (&ty::TyInt(_), &ty::TyInfer(ty::InferTy::IntVar(_))) |
654 | | (&ty::TyInfer(ty::InferTy::IntVar(_)), &ty::TyInt(_)) | |
655 | | (&ty::TyInfer(ty::InferTy::IntVar(_)), &ty::TyInfer(ty::InferTy::IntVar(_))) | |
656 | | (&ty::TyFloat(_), &ty::TyInfer(ty::InferTy::FloatVar(_))) | |
657 | | (&ty::TyInfer(ty::InferTy::FloatVar(_)), &ty::TyFloat(_)) | |
658 | | ( | |
659 | &ty::TyInfer(ty::InferTy::FloatVar(_)), | |
660 | &ty::TyInfer(ty::InferTy::FloatVar(_)), | |
661 | ) => true, | |
ff7c6d11 XL |
662 | _ => false, |
663 | } | |
664 | } | |
665 | ||
0531ce1d XL |
666 | fn push_ty_ref<'tcx>( |
667 | r: &ty::Region<'tcx>, | |
668 | tnm: &ty::TypeAndMut<'tcx>, | |
669 | s: &mut DiagnosticStyledString, | |
670 | ) { | |
ff7c6d11 | 671 | let r = &format!("{}", r); |
0531ce1d XL |
672 | s.push_highlighted(format!( |
673 | "&{}{}{}", | |
674 | r, | |
675 | if r == "" { "" } else { " " }, | |
676 | if tnm.mutbl == hir::MutMutable { | |
677 | "mut " | |
678 | } else { | |
679 | "" | |
680 | } | |
681 | )); | |
ff7c6d11 XL |
682 | s.push_normal(format!("{}", tnm.ty)); |
683 | } | |
684 | ||
cc61c64b XL |
685 | match (&t1.sty, &t2.sty) { |
686 | (&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => { | |
687 | let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); | |
688 | let path1 = self.tcx.item_path_str(def1.did.clone()); | |
689 | let path2 = self.tcx.item_path_str(def2.did.clone()); | |
690 | if def1.did == def2.did { | |
691 | // Easy case. Replace same types with `_` to shorten the output and highlight | |
692 | // the differing ones. | |
693 | // let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>(); | |
694 | // Foo<Bar, _> | |
695 | // Foo<Quz, _> | |
696 | // --- ^ type argument elided | |
697 | // | | |
698 | // highlighted in output | |
699 | values.0.push_normal(path1); | |
700 | values.1.push_normal(path2); | |
701 | ||
702 | // Only draw `<...>` if there're lifetime/type arguments. | |
703 | let len = sub1.len(); | |
704 | if len > 0 { | |
705 | values.0.push_normal("<"); | |
706 | values.1.push_normal("<"); | |
707 | } | |
708 | ||
7cac9316 | 709 | fn lifetime_display(lifetime: Region) -> String { |
cc61c64b XL |
710 | let s = format!("{}", lifetime); |
711 | if s.is_empty() { | |
712 | "'_".to_string() | |
713 | } else { | |
714 | s | |
715 | } | |
716 | } | |
717 | // At one point we'd like to elide all lifetimes here, they are irrelevant for | |
718 | // all diagnostics that use this output | |
719 | // | |
720 | // Foo<'x, '_, Bar> | |
721 | // Foo<'y, '_, Qux> | |
722 | // ^^ ^^ --- type arguments are not elided | |
723 | // | | | |
724 | // | elided as they were the same | |
725 | // not elided, they were different, but irrelevant | |
726 | let lifetimes = sub1.regions().zip(sub2.regions()); | |
727 | for (i, lifetimes) in lifetimes.enumerate() { | |
728 | let l1 = lifetime_display(lifetimes.0); | |
729 | let l2 = lifetime_display(lifetimes.1); | |
730 | if l1 == l2 { | |
731 | values.0.push_normal("'_"); | |
732 | values.1.push_normal("'_"); | |
733 | } else { | |
734 | values.0.push_highlighted(l1); | |
735 | values.1.push_highlighted(l2); | |
736 | } | |
737 | self.push_comma(&mut values.0, &mut values.1, len, i); | |
738 | } | |
739 | ||
740 | // We're comparing two types with the same path, so we compare the type | |
741 | // arguments for both. If they are the same, do not highlight and elide from the | |
742 | // output. | |
743 | // Foo<_, Bar> | |
744 | // Foo<_, Qux> | |
745 | // ^ elided type as this type argument was the same in both sides | |
746 | let type_arguments = sub1.types().zip(sub2.types()); | |
747 | let regions_len = sub1.regions().collect::<Vec<_>>().len(); | |
748 | for (i, (ta1, ta2)) in type_arguments.enumerate() { | |
749 | let i = i + regions_len; | |
750 | if ta1 == ta2 { | |
751 | values.0.push_normal("_"); | |
752 | values.1.push_normal("_"); | |
753 | } else { | |
754 | let (x1, x2) = self.cmp(ta1, ta2); | |
755 | (values.0).0.extend(x1.0); | |
756 | (values.1).0.extend(x2.0); | |
757 | } | |
758 | self.push_comma(&mut values.0, &mut values.1, len, i); | |
759 | } | |
760 | ||
761 | // Close the type argument bracket. | |
762 | // Only draw `<...>` if there're lifetime/type arguments. | |
763 | if len > 0 { | |
764 | values.0.push_normal(">"); | |
765 | values.1.push_normal(">"); | |
766 | } | |
767 | values | |
768 | } else { | |
769 | // Check for case: | |
770 | // let x: Foo<Bar<Qux> = foo::<Bar<Qux>>(); | |
771 | // Foo<Bar<Qux> | |
772 | // ------- this type argument is exactly the same as the other type | |
773 | // Bar<Qux> | |
0531ce1d XL |
774 | if self.cmp_type_arg( |
775 | &mut values.0, | |
776 | &mut values.1, | |
777 | path1.clone(), | |
778 | sub1, | |
779 | path2.clone(), | |
780 | &t2, | |
781 | ).is_some() | |
782 | { | |
cc61c64b XL |
783 | return values; |
784 | } | |
785 | // Check for case: | |
786 | // let x: Bar<Qux> = y:<Foo<Bar<Qux>>>(); | |
787 | // Bar<Qux> | |
788 | // Foo<Bar<Qux>> | |
789 | // ------- this type argument is exactly the same as the other type | |
0531ce1d XL |
790 | if self.cmp_type_arg(&mut values.1, &mut values.0, path2, sub2, path1, &t1) |
791 | .is_some() | |
792 | { | |
cc61c64b XL |
793 | return values; |
794 | } | |
795 | ||
796 | // We couldn't find anything in common, highlight everything. | |
797 | // let x: Bar<Qux> = y::<Foo<Zar>>(); | |
0531ce1d XL |
798 | ( |
799 | DiagnosticStyledString::highlighted(format!("{}", t1)), | |
800 | DiagnosticStyledString::highlighted(format!("{}", t2)), | |
801 | ) | |
cc61c64b XL |
802 | } |
803 | } | |
ff7c6d11 | 804 | |
0531ce1d | 805 | // When finding T != &T, highlight only the borrow |
ff7c6d11 XL |
806 | (&ty::TyRef(r1, ref tnm1), _) if equals(&tnm1.ty, &t2) => { |
807 | let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); | |
808 | push_ty_ref(&r1, tnm1, &mut values.0); | |
809 | values.1.push_normal(format!("{}", t2)); | |
810 | values | |
811 | } | |
812 | (_, &ty::TyRef(r2, ref tnm2)) if equals(&t1, &tnm2.ty) => { | |
813 | let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); | |
814 | values.0.push_normal(format!("{}", t1)); | |
815 | push_ty_ref(&r2, tnm2, &mut values.1); | |
816 | values | |
817 | } | |
818 | ||
819 | // When encountering &T != &mut T, highlight only the borrow | |
820 | (&ty::TyRef(r1, ref tnm1), &ty::TyRef(r2, ref tnm2)) if equals(&tnm1.ty, &tnm2.ty) => { | |
821 | let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); | |
822 | push_ty_ref(&r1, tnm1, &mut values.0); | |
823 | push_ty_ref(&r2, tnm2, &mut values.1); | |
824 | values | |
825 | } | |
826 | ||
cc61c64b XL |
827 | _ => { |
828 | if t1 == t2 { | |
829 | // The two types are the same, elide and don't highlight. | |
0531ce1d XL |
830 | ( |
831 | DiagnosticStyledString::normal("_"), | |
832 | DiagnosticStyledString::normal("_"), | |
833 | ) | |
cc61c64b XL |
834 | } else { |
835 | // We couldn't find anything in common, highlight everything. | |
0531ce1d XL |
836 | ( |
837 | DiagnosticStyledString::highlighted(format!("{}", t1)), | |
838 | DiagnosticStyledString::highlighted(format!("{}", t2)), | |
839 | ) | |
cc61c64b XL |
840 | } |
841 | } | |
842 | } | |
843 | } | |
844 | ||
0531ce1d XL |
845 | pub fn note_type_err( |
846 | &self, | |
847 | diag: &mut DiagnosticBuilder<'tcx>, | |
848 | cause: &ObligationCause<'tcx>, | |
849 | secondary_span: Option<(Span, String)>, | |
850 | mut values: Option<ValuePairs<'tcx>>, | |
851 | terr: &TypeError<'tcx>, | |
852 | ) { | |
ff7c6d11 XL |
853 | // For some types of errors, expected-found does not make |
854 | // sense, so just ignore the values we were given. | |
855 | match terr { | |
0531ce1d XL |
856 | TypeError::CyclicTy(_) => { |
857 | values = None; | |
858 | } | |
859 | _ => {} | |
ff7c6d11 XL |
860 | } |
861 | ||
abe05a73 XL |
862 | let (expected_found, exp_found, is_simple_error) = match values { |
863 | None => (None, None, false), | |
8bb4bdeb | 864 | Some(values) => { |
abe05a73 | 865 | let (is_simple_error, exp_found) = match values { |
8bb4bdeb | 866 | ValuePairs::Types(exp_found) => { |
0531ce1d XL |
867 | let is_simple_err = |
868 | exp_found.expected.is_primitive() && exp_found.found.is_primitive(); | |
abe05a73 XL |
869 | |
870 | (is_simple_err, Some(exp_found)) | |
8bb4bdeb | 871 | } |
abe05a73 | 872 | _ => (false, None), |
8bb4bdeb XL |
873 | }; |
874 | let vals = match self.values_str(&values) { | |
875 | Some((expected, found)) => Some((expected, found)), | |
876 | None => { | |
877 | // Derived error. Cancel the emitter. | |
878 | self.tcx.sess.diagnostic().cancel(diag); | |
0531ce1d | 879 | return; |
8bb4bdeb XL |
880 | } |
881 | }; | |
abe05a73 | 882 | (vals, exp_found, is_simple_error) |
5bcae85e SL |
883 | } |
884 | }; | |
885 | ||
2c00a5a8 | 886 | let span = cause.span(&self.tcx); |
5bcae85e | 887 | |
abe05a73 XL |
888 | diag.span_label(span, terr.to_string()); |
889 | if let Some((sp, msg)) = secondary_span { | |
890 | diag.span_label(sp, msg); | |
891 | } | |
892 | ||
5bcae85e | 893 | if let Some((expected, found)) = expected_found { |
8bb4bdeb | 894 | match (terr, is_simple_error, expected == found) { |
cc61c64b | 895 | (&TypeError::Sorts(ref values), false, true) => { |
8bb4bdeb | 896 | diag.note_expected_found_extra( |
0531ce1d XL |
897 | &"type", |
898 | expected, | |
899 | found, | |
8bb4bdeb | 900 | &format!(" ({})", values.expected.sort_string(self.tcx)), |
0531ce1d XL |
901 | &format!(" ({})", values.found.sort_string(self.tcx)), |
902 | ); | |
8bb4bdeb | 903 | } |
abe05a73 XL |
904 | (_, false, _) => { |
905 | if let Some(exp_found) = exp_found { | |
906 | let (def_id, ret_ty) = match exp_found.found.sty { | |
907 | TypeVariants::TyFnDef(def, _) => { | |
908 | (Some(def), Some(self.tcx.fn_sig(def).output())) | |
909 | } | |
0531ce1d | 910 | _ => (None, None), |
abe05a73 XL |
911 | }; |
912 | ||
913 | let exp_is_struct = match exp_found.expected.sty { | |
914 | TypeVariants::TyAdt(def, _) => def.is_struct(), | |
0531ce1d | 915 | _ => false, |
abe05a73 XL |
916 | }; |
917 | ||
918 | if let (Some(def_id), Some(ret_ty)) = (def_id, ret_ty) { | |
83c7162d | 919 | if exp_is_struct && &exp_found.expected == ret_ty.skip_binder() { |
abe05a73 XL |
920 | let message = format!( |
921 | "did you mean `{}(/* fields */)`?", | |
922 | self.tcx.item_path_str(def_id) | |
923 | ); | |
2c00a5a8 | 924 | diag.span_label(span, message); |
abe05a73 XL |
925 | } |
926 | } | |
927 | } | |
928 | ||
cc61c64b | 929 | diag.note_expected_found(&"type", expected, found); |
9e0c209e | 930 | } |
8bb4bdeb | 931 | _ => (), |
5bcae85e SL |
932 | } |
933 | } | |
934 | ||
5bcae85e SL |
935 | self.check_and_note_conflicting_crates(diag, terr, span); |
936 | self.tcx.note_and_explain_type_err(diag, terr, span); | |
abe05a73 XL |
937 | |
938 | // It reads better to have the error origin as the final | |
939 | // thing. | |
940 | self.note_error_origin(diag, &cause); | |
5bcae85e SL |
941 | } |
942 | ||
0531ce1d XL |
943 | pub fn report_and_explain_type_error( |
944 | &self, | |
945 | trace: TypeTrace<'tcx>, | |
946 | terr: &TypeError<'tcx>, | |
947 | ) -> DiagnosticBuilder<'tcx> { | |
948 | debug!( | |
949 | "report_and_explain_type_error(trace={:?}, terr={:?})", | |
950 | trace, terr | |
951 | ); | |
abe05a73 | 952 | |
2c00a5a8 | 953 | let span = trace.cause.span(&self.tcx); |
ff7c6d11 XL |
954 | let failure_code = trace.cause.as_failure_code(terr); |
955 | let mut diag = match failure_code { | |
956 | FailureCode::Error0317(failure_str) => { | |
c30ab7b3 | 957 | struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) |
32a655c1 | 958 | } |
ff7c6d11 | 959 | FailureCode::Error0580(failure_str) => { |
32a655c1 SL |
960 | struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) |
961 | } | |
ff7c6d11 | 962 | FailureCode::Error0308(failure_str) => { |
c30ab7b3 | 963 | struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str) |
32a655c1 | 964 | } |
ff7c6d11 XL |
965 | FailureCode::Error0644(failure_str) => { |
966 | struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) | |
967 | } | |
c30ab7b3 | 968 | }; |
476ff2be | 969 | self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr); |
5bcae85e | 970 | diag |
1a4d82fc JJ |
971 | } |
972 | ||
0531ce1d XL |
973 | fn values_str( |
974 | &self, | |
975 | values: &ValuePairs<'tcx>, | |
976 | ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { | |
1a4d82fc | 977 | match *values { |
cc61c64b | 978 | infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found), |
0531ce1d | 979 | infer::Regions(ref exp_found) => self.expected_found_str(exp_found), |
1a4d82fc | 980 | infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found), |
5bcae85e | 981 | infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found), |
1a4d82fc JJ |
982 | } |
983 | } | |
984 | ||
0531ce1d XL |
985 | fn expected_found_str_ty( |
986 | &self, | |
987 | exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, | |
988 | ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { | |
cc61c64b XL |
989 | let exp_found = self.resolve_type_vars_if_possible(exp_found); |
990 | if exp_found.references_error() { | |
991 | return None; | |
992 | } | |
993 | ||
994 | Some(self.cmp(exp_found.expected, exp_found.found)) | |
995 | } | |
996 | ||
997 | /// Returns a string of the form "expected `{}`, found `{}`". | |
5bcae85e | 998 | fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>( |
1a4d82fc | 999 | &self, |
0531ce1d XL |
1000 | exp_found: &ty::error::ExpectedFound<T>, |
1001 | ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { | |
5bcae85e SL |
1002 | let exp_found = self.resolve_type_vars_if_possible(exp_found); |
1003 | if exp_found.references_error() { | |
1a4d82fc JJ |
1004 | return None; |
1005 | } | |
1006 | ||
0531ce1d XL |
1007 | Some(( |
1008 | DiagnosticStyledString::highlighted(format!("{}", exp_found.expected)), | |
1009 | DiagnosticStyledString::highlighted(format!("{}", exp_found.found)), | |
1010 | )) | |
1a4d82fc JJ |
1011 | } |
1012 | ||
0531ce1d XL |
1013 | pub fn report_generic_bound_failure( |
1014 | &self, | |
1015 | region_scope_tree: ®ion::ScopeTree, | |
1016 | span: Span, | |
1017 | origin: Option<SubregionOrigin<'tcx>>, | |
1018 | bound_kind: GenericKind<'tcx>, | |
1019 | sub: Region<'tcx>, | |
1020 | ) { | |
ea8adc8c XL |
1021 | // Attempt to obtain the span of the parameter so we can |
1022 | // suggest adding an explicit lifetime bound to it. | |
1023 | let type_param_span = match (self.in_progress_tables, bound_kind) { | |
1024 | (Some(ref table), GenericKind::Param(ref param)) => { | |
1025 | let table = table.borrow(); | |
1026 | table.local_id_root.and_then(|did| { | |
1027 | let generics = self.tcx.generics_of(did); | |
1028 | // Account for the case where `did` corresponds to `Self`, which doesn't have | |
1029 | // the expected type argument. | |
1030 | if !param.is_self() { | |
1031 | let type_param = generics.type_param(param, self.tcx); | |
1032 | let hir = &self.tcx.hir; | |
1033 | hir.as_local_node_id(type_param.def_id).map(|id| { | |
0531ce1d | 1034 | // Get the `hir::TyParam` to verify whether it already has any bounds. |
ea8adc8c XL |
1035 | // We do this to avoid suggesting code that ends up as `T: 'a'b`, |
1036 | // instead we suggest `T: 'a + 'b` in that case. | |
1037 | let has_lifetimes = if let hir_map::NodeTyParam(ref p) = hir.get(id) { | |
1038 | p.bounds.len() > 0 | |
1039 | } else { | |
1040 | false | |
1041 | }; | |
1042 | let sp = hir.span(id); | |
1043 | // `sp` only covers `T`, change it so that it covers | |
1044 | // `T:` when appropriate | |
1045 | let sp = if has_lifetimes { | |
0531ce1d XL |
1046 | sp.to(self.tcx |
1047 | .sess | |
1048 | .codemap() | |
1049 | .next_point(self.tcx.sess.codemap().next_point(sp))) | |
ea8adc8c XL |
1050 | } else { |
1051 | sp | |
1052 | }; | |
1053 | (sp, has_lifetimes) | |
1054 | }) | |
1055 | } else { | |
1056 | None | |
1057 | } | |
1058 | }) | |
1059 | } | |
1060 | _ => None, | |
1061 | }; | |
1a4d82fc JJ |
1062 | |
1063 | let labeled_user_string = match bound_kind { | |
0531ce1d XL |
1064 | GenericKind::Param(ref p) => format!("the parameter type `{}`", p), |
1065 | GenericKind::Projection(ref p) => format!("the associated type `{}`", p), | |
1a4d82fc JJ |
1066 | }; |
1067 | ||
ff7c6d11 | 1068 | if let Some(SubregionOrigin::CompareImplMethodObligation { |
0531ce1d XL |
1069 | span, |
1070 | item_name, | |
1071 | impl_item_def_id, | |
1072 | trait_item_def_id, | |
1073 | }) = origin | |
1074 | { | |
1075 | self.report_extra_impl_obligation( | |
1076 | span, | |
1077 | item_name, | |
1078 | impl_item_def_id, | |
1079 | trait_item_def_id, | |
1080 | &format!("`{}: {}`", bound_kind, sub), | |
1081 | ).emit(); | |
c30ab7b3 SL |
1082 | return; |
1083 | } | |
1084 | ||
0531ce1d XL |
1085 | fn binding_suggestion<'tcx, S: fmt::Display>( |
1086 | err: &mut DiagnosticBuilder<'tcx>, | |
1087 | type_param_span: Option<(Span, bool)>, | |
1088 | bound_kind: GenericKind<'tcx>, | |
1089 | sub: S, | |
1090 | ) { | |
1091 | let consider = &format!( | |
1092 | "consider adding an explicit lifetime bound `{}: {}`...", | |
1093 | bound_kind, sub | |
1094 | ); | |
ea8adc8c | 1095 | if let Some((sp, has_lifetimes)) = type_param_span { |
0531ce1d | 1096 | let tail = if has_lifetimes { " + " } else { "" }; |
ea8adc8c XL |
1097 | let suggestion = format!("{}: {}{}", bound_kind, sub, tail); |
1098 | err.span_suggestion_short(sp, consider, suggestion); | |
1099 | } else { | |
1100 | err.help(consider); | |
1101 | } | |
1102 | } | |
1103 | ||
9e0c209e | 1104 | let mut err = match *sub { |
0531ce1d XL |
1105 | ty::ReEarlyBound(_) |
1106 | | ty::ReFree(ty::FreeRegion { | |
1107 | bound_region: ty::BrNamed(..), | |
1108 | .. | |
1109 | }) => { | |
1a4d82fc | 1110 | // Does the required lifetime have a nice name we can print? |
0531ce1d XL |
1111 | let mut err = struct_span_err!( |
1112 | self.tcx.sess, | |
1113 | span, | |
1114 | E0309, | |
1115 | "{} may not live long enough", | |
1116 | labeled_user_string | |
1117 | ); | |
ea8adc8c | 1118 | binding_suggestion(&mut err, type_param_span, bound_kind, sub); |
9cc50fc6 | 1119 | err |
1a4d82fc JJ |
1120 | } |
1121 | ||
1122 | ty::ReStatic => { | |
1123 | // Does the required lifetime have a nice name we can print? | |
0531ce1d XL |
1124 | let mut err = struct_span_err!( |
1125 | self.tcx.sess, | |
1126 | span, | |
1127 | E0310, | |
1128 | "{} may not live long enough", | |
1129 | labeled_user_string | |
1130 | ); | |
ea8adc8c | 1131 | binding_suggestion(&mut err, type_param_span, bound_kind, "'static"); |
9cc50fc6 | 1132 | err |
1a4d82fc JJ |
1133 | } |
1134 | ||
1135 | _ => { | |
1136 | // If not, be less specific. | |
0531ce1d XL |
1137 | let mut err = struct_span_err!( |
1138 | self.tcx.sess, | |
1139 | span, | |
1140 | E0311, | |
1141 | "{} may not live long enough", | |
1142 | labeled_user_string | |
1143 | ); | |
1144 | err.help(&format!( | |
1145 | "consider adding an explicit lifetime bound for `{}`", | |
1146 | bound_kind | |
1147 | )); | |
c1a9b12d | 1148 | self.tcx.note_and_explain_region( |
ea8adc8c | 1149 | region_scope_tree, |
9cc50fc6 | 1150 | &mut err, |
c34b1796 | 1151 | &format!("{} must be valid for ", labeled_user_string), |
1a4d82fc | 1152 | sub, |
0531ce1d XL |
1153 | "...", |
1154 | ); | |
9cc50fc6 | 1155 | err |
1a4d82fc | 1156 | } |
9cc50fc6 | 1157 | }; |
e9174d1e | 1158 | |
ff7c6d11 XL |
1159 | if let Some(origin) = origin { |
1160 | self.note_region_origin(&mut err, &origin); | |
1161 | } | |
9cc50fc6 | 1162 | err.emit(); |
1a4d82fc JJ |
1163 | } |
1164 | ||
0531ce1d XL |
1165 | fn report_sub_sup_conflict( |
1166 | &self, | |
1167 | region_scope_tree: ®ion::ScopeTree, | |
1168 | var_origin: RegionVariableOrigin, | |
1169 | sub_origin: SubregionOrigin<'tcx>, | |
1170 | sub_region: Region<'tcx>, | |
1171 | sup_origin: SubregionOrigin<'tcx>, | |
1172 | sup_region: Region<'tcx>, | |
1173 | ) { | |
9cc50fc6 | 1174 | let mut err = self.report_inference_failure(var_origin); |
1a4d82fc | 1175 | |
0531ce1d XL |
1176 | self.tcx.note_and_explain_region( |
1177 | region_scope_tree, | |
1178 | &mut err, | |
1a4d82fc JJ |
1179 | "first, the lifetime cannot outlive ", |
1180 | sup_region, | |
0531ce1d XL |
1181 | "...", |
1182 | ); | |
1a4d82fc | 1183 | |
2c00a5a8 XL |
1184 | match (&sup_origin, &sub_origin) { |
1185 | (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) => { | |
0531ce1d XL |
1186 | if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) = ( |
1187 | self.values_str(&sup_trace.values), | |
1188 | self.values_str(&sub_trace.values), | |
1189 | ) { | |
2c00a5a8 XL |
1190 | if sub_expected == sup_expected && sub_found == sup_found { |
1191 | self.tcx.note_and_explain_region( | |
1192 | region_scope_tree, | |
1193 | &mut err, | |
1194 | "...but the lifetime must also be valid for ", | |
1195 | sub_region, | |
1196 | "...", | |
1197 | ); | |
0531ce1d XL |
1198 | err.note(&format!( |
1199 | "...so that the {}:\nexpected {}\n found {}", | |
1200 | sup_trace.cause.as_requirement_str(), | |
1201 | sup_expected.content(), | |
1202 | sup_found.content() | |
1203 | )); | |
2c00a5a8 XL |
1204 | err.emit(); |
1205 | return; | |
1206 | } | |
1207 | } | |
1208 | } | |
1209 | _ => {} | |
1210 | } | |
1211 | ||
9cc50fc6 | 1212 | self.note_region_origin(&mut err, &sup_origin); |
1a4d82fc | 1213 | |
0531ce1d XL |
1214 | self.tcx.note_and_explain_region( |
1215 | region_scope_tree, | |
1216 | &mut err, | |
1a4d82fc JJ |
1217 | "but, the lifetime must be valid for ", |
1218 | sub_region, | |
0531ce1d XL |
1219 | "...", |
1220 | ); | |
1a4d82fc | 1221 | |
9cc50fc6 SL |
1222 | self.note_region_origin(&mut err, &sub_origin); |
1223 | err.emit(); | |
1a4d82fc | 1224 | } |
1a4d82fc JJ |
1225 | } |
1226 | ||
a7813a04 | 1227 | impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { |
0531ce1d XL |
1228 | fn report_inference_failure( |
1229 | &self, | |
1230 | var_origin: RegionVariableOrigin, | |
1231 | ) -> DiagnosticBuilder<'tcx> { | |
62682a34 SL |
1232 | let br_string = |br: ty::BoundRegion| { |
1233 | let mut s = br.to_string(); | |
1234 | if !s.is_empty() { | |
1235 | s.push_str(" "); | |
1236 | } | |
1237 | s | |
1238 | }; | |
1a4d82fc JJ |
1239 | let var_description = match var_origin { |
1240 | infer::MiscVariable(_) => "".to_string(), | |
1241 | infer::PatternRegion(_) => " for pattern".to_string(), | |
1242 | infer::AddrOfRegion(_) => " for borrow expression".to_string(), | |
1a4d82fc JJ |
1243 | infer::Autoref(_) => " for autoref".to_string(), |
1244 | infer::Coercion(_) => " for automatic coercion".to_string(), | |
1245 | infer::LateBoundRegion(_, br, infer::FnCall) => { | |
0531ce1d | 1246 | format!(" for lifetime parameter {}in function call", br_string(br)) |
1a4d82fc JJ |
1247 | } |
1248 | infer::LateBoundRegion(_, br, infer::HigherRankedType) => { | |
62682a34 | 1249 | format!(" for lifetime parameter {}in generic type", br_string(br)) |
1a4d82fc | 1250 | } |
0531ce1d XL |
1251 | infer::LateBoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( |
1252 | " for lifetime parameter {}in trait containing associated type `{}`", | |
1253 | br_string(br), | |
1254 | self.tcx.associated_item(def_id).name | |
1255 | ), | |
1256 | infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name), | |
1a4d82fc | 1257 | infer::BoundRegionInCoherence(name) => { |
0531ce1d | 1258 | format!(" for lifetime parameter `{}` in coherence check", name) |
1a4d82fc JJ |
1259 | } |
1260 | infer::UpvarRegion(ref upvar_id, _) => { | |
ea8adc8c XL |
1261 | let var_node_id = self.tcx.hir.hir_to_node_id(upvar_id.var_id); |
1262 | let var_name = self.tcx.hir.name(var_node_id); | |
1263 | format!(" for capture of `{}` by closure", var_name) | |
1a4d82fc | 1264 | } |
abe05a73 | 1265 | infer::NLL(..) => bug!("NLL variable found in lexical phase"), |
1a4d82fc JJ |
1266 | }; |
1267 | ||
0531ce1d XL |
1268 | struct_span_err!( |
1269 | self.tcx.sess, | |
1270 | var_origin.span(), | |
1271 | E0495, | |
1272 | "cannot infer an appropriate lifetime{} \ | |
1273 | due to conflicting requirements", | |
1274 | var_description | |
1275 | ) | |
1a4d82fc | 1276 | } |
1a4d82fc JJ |
1277 | } |
1278 | ||
ff7c6d11 XL |
1279 | enum FailureCode { |
1280 | Error0317(&'static str), | |
1281 | Error0580(&'static str), | |
1282 | Error0308(&'static str), | |
1283 | Error0644(&'static str), | |
1284 | } | |
1285 | ||
476ff2be | 1286 | impl<'tcx> ObligationCause<'tcx> { |
ff7c6d11 XL |
1287 | fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { |
1288 | use self::FailureCode::*; | |
476ff2be SL |
1289 | use traits::ObligationCauseCode::*; |
1290 | match self.code { | |
ff7c6d11 XL |
1291 | CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), |
1292 | MatchExpressionArm { source, .. } => Error0308(match source { | |
0531ce1d | 1293 | hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have incompatible types", |
476ff2be | 1294 | _ => "match arms have incompatible types", |
ff7c6d11 XL |
1295 | }), |
1296 | IfExpression => Error0308("if and else have incompatible types"), | |
1297 | IfExpressionWithNoElse => Error0317("if may be missing an else clause"), | |
ff7c6d11 XL |
1298 | MainFunctionType => Error0580("main function has wrong type"), |
1299 | StartFunctionType => Error0308("start function has wrong type"), | |
1300 | IntrinsicType => Error0308("intrinsic has wrong type"), | |
1301 | MethodReceiver => Error0308("mismatched method receiver"), | |
1302 | ||
1303 | // In the case where we have no more specific thing to | |
1304 | // say, also take a look at the error code, maybe we can | |
1305 | // tailor to that. | |
1306 | _ => match terr { | |
0531ce1d XL |
1307 | TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => { |
1308 | Error0644("closure/generator type that references itself") | |
1309 | } | |
1310 | _ => Error0308("mismatched types"), | |
1311 | }, | |
476ff2be SL |
1312 | } |
1313 | } | |
1314 | ||
1315 | fn as_requirement_str(&self) -> &'static str { | |
1316 | use traits::ObligationCauseCode::*; | |
1317 | match self.code { | |
1318 | CompareImplMethodObligation { .. } => "method type is compatible with trait", | |
1319 | ExprAssignable => "expression is assignable", | |
1320 | MatchExpressionArm { source, .. } => match source { | |
0531ce1d | 1321 | hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types", |
476ff2be SL |
1322 | _ => "match arms have compatible types", |
1323 | }, | |
1324 | IfExpression => "if and else have compatible types", | |
1325 | IfExpressionWithNoElse => "if missing an else returns ()", | |
476ff2be SL |
1326 | MainFunctionType => "`main` function has the correct type", |
1327 | StartFunctionType => "`start` function has the correct type", | |
1328 | IntrinsicType => "intrinsic has the correct type", | |
1329 | MethodReceiver => "method receiver has the correct type", | |
1330 | _ => "types are compatible", | |
1331 | } | |
1332 | } | |
1333 | } |