]>
Commit | Line | Data |
---|---|---|
f9f354fc XL |
1 | use crate::traits::{ObligationCause, ObligationCauseCode}; |
2 | use crate::ty::diagnostics::suggest_constraining_type_param; | |
fc512014 | 3 | use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; |
3dfed10e | 4 | use rustc_ast as ast; |
f9f354fc XL |
5 | use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; |
6 | use rustc_errors::{pluralize, DiagnosticBuilder}; | |
dfeec247 XL |
7 | use rustc_hir as hir; |
8 | use rustc_hir::def_id::DefId; | |
f9f354fc XL |
9 | use rustc_span::symbol::{sym, Symbol}; |
10 | use rustc_span::{BytePos, MultiSpan, Span}; | |
83c7162d | 11 | use rustc_target::spec::abi; |
e9174d1e | 12 | |
60c5eb7d XL |
13 | use std::borrow::Cow; |
14 | use std::fmt; | |
f9f354fc | 15 | use std::ops::Deref; |
e9174d1e | 16 | |
60c5eb7d | 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)] |
e9174d1e SL |
18 | pub struct ExpectedFound<T> { |
19 | pub expected: T, | |
cc61c64b | 20 | pub found: T, |
e9174d1e SL |
21 | } |
22 | ||
74b04a01 XL |
23 | impl<T> ExpectedFound<T> { |
24 | pub fn new(a_is_expected: bool, a: T, b: T) -> Self { | |
25 | if a_is_expected { | |
26 | ExpectedFound { expected: a, found: b } | |
27 | } else { | |
28 | ExpectedFound { expected: b, found: a } | |
29 | } | |
30 | } | |
31 | } | |
32 | ||
e9174d1e | 33 | // Data structures used in type unification |
60c5eb7d | 34 | #[derive(Clone, Debug, TypeFoldable)] |
e9174d1e SL |
35 | pub enum TypeError<'tcx> { |
36 | Mismatch, | |
37 | UnsafetyMismatch(ExpectedFound<hir::Unsafety>), | |
38 | AbiMismatch(ExpectedFound<abi::Abi>), | |
39 | Mutability, | |
e9174d1e | 40 | TupleSize(ExpectedFound<usize>), |
ea8adc8c | 41 | FixedArraySize(ExpectedFound<u64>), |
e9174d1e | 42 | ArgCount, |
ea8adc8c | 43 | |
7cac9316 | 44 | RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>), |
fc512014 XL |
45 | RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>), |
46 | RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>), | |
0731742a | 47 | RegionsPlaceholderMismatch, |
ea8adc8c | 48 | |
e9174d1e | 49 | Sorts(ExpectedFound<Ty<'tcx>>), |
e9174d1e | 50 | IntMismatch(ExpectedFound<ty::IntVarValue>), |
b039eaaf | 51 | FloatMismatch(ExpectedFound<ast::FloatTy>), |
e9174d1e | 52 | Traits(ExpectedFound<DefId>), |
e9174d1e | 53 | VariadicMismatch(ExpectedFound<bool>), |
ff7c6d11 XL |
54 | |
55 | /// Instantiating a type variable with the given type would have | |
56 | /// created a cycle (because it appears somewhere within that | |
57 | /// type). | |
58 | CyclicTy(Ty<'tcx>), | |
1b1a35ee | 59 | CyclicConst(&'tcx ty::Const<'tcx>), |
041b39d2 | 60 | ProjectionMismatched(ExpectedFound<DefId>), |
fc512014 | 61 | ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::Binder<ty::ExistentialPredicate<'tcx>>>>), |
e74abb32 | 62 | ObjectUnsafeCoercion(DefId), |
48663c56 | 63 | ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>), |
e1599b0c XL |
64 | |
65 | IntrinsicCast, | |
f9f354fc XL |
66 | /// Safe `#[target_feature]` functions are not assignable to safe function pointers. |
67 | TargetFeatureCast(DefId), | |
e9174d1e SL |
68 | } |
69 | ||
e9174d1e SL |
70 | pub enum UnconstrainedNumeric { |
71 | UnconstrainedFloat, | |
72 | UnconstrainedInt, | |
73 | Neither, | |
74 | } | |
75 | ||
76 | /// Explains the source of a type err in a short, human readable way. This is meant to be placed | |
77 | /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()` | |
78 | /// afterwards to present additional details, particularly when it comes to lifetime-related | |
79 | /// errors. | |
80 | impl<'tcx> fmt::Display for TypeError<'tcx> { | |
0bf4aa26 | 81 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
e9174d1e | 82 | use self::TypeError::*; |
60c5eb7d XL |
83 | fn report_maybe_different( |
84 | f: &mut fmt::Formatter<'_>, | |
85 | expected: &str, | |
86 | found: &str, | |
87 | ) -> fmt::Result { | |
e9174d1e SL |
88 | // A naive approach to making sure that we're not reporting silly errors such as: |
89 | // (expected closure, found closure). | |
90 | if expected == found { | |
91 | write!(f, "expected {}, found a different {}", expected, found) | |
92 | } else { | |
93 | write!(f, "expected {}, found {}", expected, found) | |
94 | } | |
95 | } | |
96 | ||
fc512014 | 97 | let br_string = |br: ty::BoundRegionKind| match br { |
dfeec247 XL |
98 | ty::BrNamed(_, name) => format!(" {}", name), |
99 | _ => String::new(), | |
532ac7d7 XL |
100 | }; |
101 | ||
e9174d1e | 102 | match *self { |
ff7c6d11 | 103 | CyclicTy(_) => write!(f, "cyclic type of infinite size"), |
1b1a35ee | 104 | CyclicConst(_) => write!(f, "encountered a self-referencing constant"), |
e9174d1e SL |
105 | Mismatch => write!(f, "types differ"), |
106 | UnsafetyMismatch(values) => { | |
dfeec247 | 107 | write!(f, "expected {} fn, found {} fn", values.expected, values.found) |
e9174d1e SL |
108 | } |
109 | AbiMismatch(values) => { | |
dfeec247 | 110 | write!(f, "expected {} fn, found {} fn", values.expected, values.found) |
e9174d1e | 111 | } |
9e0c209e | 112 | Mutability => write!(f, "types differ in mutability"), |
dfeec247 XL |
113 | TupleSize(values) => write!( |
114 | f, | |
115 | "expected a tuple with {} element{}, \ | |
dc9dc135 | 116 | found one with {} element{}", |
dfeec247 XL |
117 | values.expected, |
118 | pluralize!(values.expected), | |
119 | values.found, | |
120 | pluralize!(values.found) | |
121 | ), | |
122 | FixedArraySize(values) => write!( | |
123 | f, | |
124 | "expected an array with a fixed size of {} element{}, \ | |
dc9dc135 | 125 | found one with {} element{}", |
dfeec247 XL |
126 | values.expected, |
127 | pluralize!(values.expected), | |
128 | values.found, | |
129 | pluralize!(values.found) | |
130 | ), | |
131 | ArgCount => write!(f, "incorrect number of function parameters"), | |
132 | RegionsDoesNotOutlive(..) => write!(f, "lifetime mismatch"), | |
133 | RegionsInsufficientlyPolymorphic(br, _) => write!( | |
134 | f, | |
135 | "expected bound lifetime parameter{}, found concrete lifetime", | |
136 | br_string(br) | |
137 | ), | |
138 | RegionsOverlyPolymorphic(br, _) => write!( | |
139 | f, | |
140 | "expected concrete lifetime, found bound lifetime parameter{}", | |
141 | br_string(br) | |
142 | ), | |
143 | RegionsPlaceholderMismatch => write!(f, "one type is more general than the other"), | |
e9174d1e | 144 | Sorts(values) => ty::tls::with(|tcx| { |
dfeec247 XL |
145 | report_maybe_different( |
146 | f, | |
147 | &values.expected.sort_string(tcx), | |
148 | &values.found.sort_string(tcx), | |
149 | ) | |
e9174d1e SL |
150 | }), |
151 | Traits(values) => ty::tls::with(|tcx| { | |
dfeec247 XL |
152 | report_maybe_different( |
153 | f, | |
154 | &format!("trait `{}`", tcx.def_path_str(values.expected)), | |
155 | &format!("trait `{}`", tcx.def_path_str(values.found)), | |
156 | ) | |
e9174d1e | 157 | }), |
e9174d1e | 158 | IntMismatch(ref values) => { |
dfeec247 | 159 | write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) |
e9174d1e SL |
160 | } |
161 | FloatMismatch(ref values) => { | |
dfeec247 | 162 | write!(f, "expected `{:?}`, found `{:?}`", values.expected, values.found) |
e9174d1e | 163 | } |
dfeec247 XL |
164 | VariadicMismatch(ref values) => write!( |
165 | f, | |
166 | "expected {} fn, found {} function", | |
167 | if values.expected { "variadic" } else { "non-variadic" }, | |
168 | if values.found { "variadic" } else { "non-variadic" } | |
169 | ), | |
041b39d2 | 170 | ProjectionMismatched(ref values) => ty::tls::with(|tcx| { |
dfeec247 XL |
171 | write!( |
172 | f, | |
173 | "expected {}, found {}", | |
174 | tcx.def_path_str(values.expected), | |
175 | tcx.def_path_str(values.found) | |
176 | ) | |
041b39d2 | 177 | }), |
dfeec247 XL |
178 | ExistentialMismatch(ref values) => report_maybe_different( |
179 | f, | |
180 | &format!("trait `{}`", values.expected), | |
181 | &format!("trait `{}`", values.found), | |
182 | ), | |
48663c56 | 183 | ConstMismatch(ref values) => { |
dc9dc135 | 184 | write!(f, "expected `{}`, found `{}`", values.expected, values.found) |
48663c56 | 185 | } |
dfeec247 | 186 | IntrinsicCast => write!(f, "cannot coerce intrinsics to function pointers"), |
f9f354fc XL |
187 | TargetFeatureCast(_) => write!( |
188 | f, | |
189 | "cannot coerce functions with `#[target_feature]` to safe function pointers" | |
190 | ), | |
e74abb32 | 191 | ObjectUnsafeCoercion(_) => write!(f, "coercion to object-unsafe trait object"), |
e9174d1e SL |
192 | } |
193 | } | |
194 | } | |
195 | ||
60c5eb7d XL |
196 | impl<'tcx> TypeError<'tcx> { |
197 | pub fn must_include_note(&self) -> bool { | |
198 | use self::TypeError::*; | |
199 | match self { | |
1b1a35ee XL |
200 | CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) |
201 | | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | |
202 | | VariadicMismatch(_) | TargetFeatureCast(_) => false, | |
60c5eb7d | 203 | |
dfeec247 XL |
204 | Mutability |
205 | | TupleSize(_) | |
206 | | ArgCount | |
207 | | RegionsDoesNotOutlive(..) | |
208 | | RegionsInsufficientlyPolymorphic(..) | |
209 | | RegionsOverlyPolymorphic(..) | |
210 | | RegionsPlaceholderMismatch | |
211 | | Traits(_) | |
212 | | ProjectionMismatched(_) | |
dfeec247 XL |
213 | | ExistentialMismatch(_) |
214 | | ConstMismatch(_) | |
215 | | IntrinsicCast | |
216 | | ObjectUnsafeCoercion(_) => true, | |
60c5eb7d XL |
217 | } |
218 | } | |
219 | } | |
220 | ||
dc9dc135 XL |
221 | impl<'tcx> ty::TyS<'tcx> { |
222 | pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> { | |
1b1a35ee | 223 | match *self.kind() { |
dfeec247 XL |
224 | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { |
225 | format!("`{}`", self).into() | |
226 | } | |
60c5eb7d | 227 | ty::Tuple(ref tys) if tys.is_empty() => format!("`{}`", self).into(), |
e9174d1e | 228 | |
532ac7d7 XL |
229 | ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), |
230 | ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), | |
60c5eb7d | 231 | ty::Array(t, n) => { |
29967ef6 | 232 | let n = tcx.lift(n).unwrap(); |
416331ca | 233 | match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { |
60c5eb7d | 234 | _ if t.is_simple_ty() => format!("array `{}`", self).into(), |
1b1a35ee | 235 | Some(n) => format!("array of {} element{}", n, pluralize!(n)).into(), |
dc9dc135 XL |
236 | None => "array".into(), |
237 | } | |
ea8adc8c | 238 | } |
60c5eb7d | 239 | ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(), |
0bf4aa26 XL |
240 | ty::Slice(_) => "slice".into(), |
241 | ty::RawPtr(_) => "*-ptr".into(), | |
60c5eb7d | 242 | ty::Ref(_, ty, mutbl) => { |
94b46f34 | 243 | let tymut = ty::TypeAndMut { ty, mutbl }; |
5bcae85e | 244 | let tymut_string = tymut.to_string(); |
dfeec247 XL |
245 | if tymut_string != "_" |
246 | && (ty.is_simple_text() || tymut_string.len() < "mutable reference".len()) | |
247 | { | |
60c5eb7d | 248 | format!("`&{}`", tymut_string).into() |
dfeec247 XL |
249 | } else { |
250 | // Unknown type name, it's long or has type arguments | |
60c5eb7d | 251 | match mutbl { |
dfeec247 | 252 | hir::Mutability::Mut => "mutable reference", |
60c5eb7d | 253 | _ => "reference", |
dfeec247 XL |
254 | } |
255 | .into() | |
5bcae85e SL |
256 | } |
257 | } | |
0bf4aa26 XL |
258 | ty::FnDef(..) => "fn item".into(), |
259 | ty::FnPtr(_) => "fn pointer".into(), | |
b7449926 | 260 | ty::Dynamic(ref inner, ..) => { |
0731742a | 261 | if let Some(principal) = inner.principal() { |
dfeec247 | 262 | format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() |
0731742a | 263 | } else { |
dfeec247 | 264 | "trait object".into() |
0731742a | 265 | } |
e9174d1e | 266 | } |
0bf4aa26 XL |
267 | ty::Closure(..) => "closure".into(), |
268 | ty::Generator(..) => "generator".into(), | |
269 | ty::GeneratorWitness(..) => "generator witness".into(), | |
270 | ty::Tuple(..) => "tuple".into(), | |
271 | ty::Infer(ty::TyVar(_)) => "inferred type".into(), | |
0731742a XL |
272 | ty::Infer(ty::IntVar(_)) => "integer".into(), |
273 | ty::Infer(ty::FloatVar(_)) => "floating-point number".into(), | |
a1dfa0c6 XL |
274 | ty::Placeholder(..) => "placeholder type".into(), |
275 | ty::Bound(..) => "bound type".into(), | |
0bf4aa26 XL |
276 | ty::Infer(ty::FreshTy(_)) => "fresh type".into(), |
277 | ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(), | |
278 | ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(), | |
279 | ty::Projection(_) => "associated type".into(), | |
60c5eb7d | 280 | ty::Param(p) => format!("type parameter `{}`", p).into(), |
0bf4aa26 | 281 | ty::Opaque(..) => "opaque type".into(), |
f035d41b | 282 | ty::Error(_) => "type error".into(), |
e9174d1e SL |
283 | } |
284 | } | |
60c5eb7d XL |
285 | |
286 | pub fn prefix_string(&self) -> Cow<'static, str> { | |
1b1a35ee | 287 | match *self.kind() { |
dfeec247 | 288 | ty::Infer(_) |
f035d41b | 289 | | ty::Error(_) |
dfeec247 XL |
290 | | ty::Bool |
291 | | ty::Char | |
292 | | ty::Int(_) | |
293 | | ty::Uint(_) | |
294 | | ty::Float(_) | |
295 | | ty::Str | |
296 | | ty::Never => "type".into(), | |
60c5eb7d XL |
297 | ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(), |
298 | ty::Adt(def, _) => def.descr().into(), | |
299 | ty::Foreign(_) => "extern type".into(), | |
300 | ty::Array(..) => "array".into(), | |
301 | ty::Slice(_) => "slice".into(), | |
302 | ty::RawPtr(_) => "raw pointer".into(), | |
303 | ty::Ref(.., mutbl) => match mutbl { | |
dfeec247 XL |
304 | hir::Mutability::Mut => "mutable reference", |
305 | _ => "reference", | |
306 | } | |
307 | .into(), | |
60c5eb7d XL |
308 | ty::FnDef(..) => "fn item".into(), |
309 | ty::FnPtr(_) => "fn pointer".into(), | |
310 | ty::Dynamic(..) => "trait object".into(), | |
311 | ty::Closure(..) => "closure".into(), | |
312 | ty::Generator(..) => "generator".into(), | |
313 | ty::GeneratorWitness(..) => "generator witness".into(), | |
314 | ty::Tuple(..) => "tuple".into(), | |
315 | ty::Placeholder(..) => "higher-ranked type".into(), | |
316 | ty::Bound(..) => "bound type variable".into(), | |
317 | ty::Projection(_) => "associated type".into(), | |
60c5eb7d XL |
318 | ty::Param(_) => "type parameter".into(), |
319 | ty::Opaque(..) => "opaque type".into(), | |
320 | } | |
321 | } | |
e9174d1e SL |
322 | } |
323 | ||
dc9dc135 | 324 | impl<'tcx> TyCtxt<'tcx> { |
e1599b0c XL |
325 | pub fn note_and_explain_type_err( |
326 | self, | |
327 | db: &mut DiagnosticBuilder<'_>, | |
328 | err: &TypeError<'tcx>, | |
f9f354fc | 329 | cause: &ObligationCause<'tcx>, |
e1599b0c | 330 | sp: Span, |
60c5eb7d | 331 | body_owner_def_id: DefId, |
e1599b0c | 332 | ) { |
e9174d1e | 333 | use self::TypeError::*; |
f9f354fc | 334 | debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); |
e1599b0c | 335 | match err { |
e9174d1e | 336 | Sorts(values) => { |
1b1a35ee | 337 | match (values.expected.kind(), values.found.kind()) { |
29967ef6 XL |
338 | (ty::Closure(..), ty::Closure(..)) => { |
339 | db.note("no two closures, even if identical, have the same type"); | |
340 | db.help("consider boxing your closure and/or using it as a trait object"); | |
341 | } | |
342 | (ty::Opaque(..), ty::Opaque(..)) => { | |
343 | // Issue #63167 | |
344 | db.note("distinct uses of `impl Trait` result in different opaque types"); | |
345 | } | |
dfeec247 XL |
346 | (ty::Float(_), ty::Infer(ty::IntVar(_))) => { |
347 | if let Ok( | |
348 | // Issue #53280 | |
349 | snippet, | |
350 | ) = self.sess.source_map().span_to_snippet(sp) | |
351 | { | |
352 | if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') { | |
353 | db.span_suggestion( | |
354 | sp, | |
355 | "use a float literal", | |
356 | format!("{}.0", snippet), | |
f9f354fc | 357 | MachineApplicable, |
dfeec247 XL |
358 | ); |
359 | } | |
b7449926 | 360 | } |
dfeec247 | 361 | } |
60c5eb7d XL |
362 | (ty::Param(expected), ty::Param(found)) => { |
363 | let generics = self.generics_of(body_owner_def_id); | |
364 | let e_span = self.def_span(generics.type_param(expected, self).def_id); | |
365 | if !sp.contains(e_span) { | |
366 | db.span_label(e_span, "expected type parameter"); | |
367 | } | |
368 | let f_span = self.def_span(generics.type_param(found, self).def_id); | |
369 | if !sp.contains(f_span) { | |
370 | db.span_label(f_span, "found type parameter"); | |
371 | } | |
dfeec247 XL |
372 | db.note( |
373 | "a type parameter was expected, but a different one was found; \ | |
29967ef6 | 374 | you might be missing a type parameter or trait bound", |
dfeec247 XL |
375 | ); |
376 | db.note( | |
377 | "for more information, visit \ | |
29967ef6 XL |
378 | https://doc.rust-lang.org/book/ch10-02-traits.html\ |
379 | #traits-as-parameters", | |
dfeec247 | 380 | ); |
e1599b0c XL |
381 | } |
382 | (ty::Projection(_), ty::Projection(_)) => { | |
383 | db.note("an associated type was expected, but a different one was found"); | |
384 | } | |
f9f354fc XL |
385 | (ty::Param(p), ty::Projection(proj)) | (ty::Projection(proj), ty::Param(p)) => { |
386 | let generics = self.generics_of(body_owner_def_id); | |
387 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
388 | if !sp.contains(p_span) { | |
389 | db.span_label(p_span, "this type parameter"); | |
390 | } | |
391 | let hir = self.hir(); | |
392 | let mut note = true; | |
393 | if let Some(generics) = generics | |
394 | .type_param(p, self) | |
395 | .def_id | |
396 | .as_local() | |
3dfed10e | 397 | .map(|id| hir.local_def_id_to_hir_id(id)) |
f9f354fc XL |
398 | .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) |
399 | .as_ref() | |
400 | .and_then(|node| node.generics()) | |
401 | { | |
402 | // Synthesize the associated type restriction `Add<Output = Expected>`. | |
403 | // FIXME: extract this logic for use in other diagnostics. | |
404 | let trait_ref = proj.trait_ref(self); | |
405 | let path = | |
406 | self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs); | |
407 | let item_name = self.item_name(proj.item_def_id); | |
408 | let path = if path.ends_with('>') { | |
409 | format!("{}, {} = {}>", &path[..path.len() - 1], item_name, p) | |
410 | } else { | |
411 | format!("{}<{} = {}>", path, item_name, p) | |
412 | }; | |
413 | note = !suggest_constraining_type_param( | |
414 | self, | |
415 | generics, | |
416 | db, | |
417 | &format!("{}", proj.self_ty()), | |
418 | &path, | |
419 | None, | |
420 | ); | |
421 | } | |
422 | if note { | |
423 | db.note("you might be missing a type parameter or trait bound"); | |
424 | } | |
e1599b0c | 425 | } |
f9f354fc XL |
426 | (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) |
427 | | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { | |
60c5eb7d XL |
428 | let generics = self.generics_of(body_owner_def_id); |
429 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
430 | if !sp.contains(p_span) { | |
431 | db.span_label(p_span, "this type parameter"); | |
432 | } | |
e1599b0c XL |
433 | db.help("type parameters must be constrained to match other types"); |
434 | if self.sess.teach(&db.get_code().unwrap()) { | |
dfeec247 XL |
435 | db.help( |
436 | "given a type parameter `T` and a method `foo`: | |
e1599b0c XL |
437 | ``` |
438 | trait Trait<T> { fn foo(&self) -> T; } | |
439 | ``` | |
440 | the only ways to implement method `foo` are: | |
441 | - constrain `T` with an explicit type: | |
442 | ``` | |
443 | impl Trait<String> for X { | |
444 | fn foo(&self) -> String { String::new() } | |
445 | } | |
446 | ``` | |
447 | - add a trait bound to `T` and call a method on that trait that returns `Self`: | |
448 | ``` | |
449 | impl<T: std::default::Default> Trait<T> for X { | |
450 | fn foo(&self) -> T { <T as std::default::Default>::default() } | |
451 | } | |
452 | ``` | |
453 | - change `foo` to return an argument of type `T`: | |
454 | ``` | |
455 | impl<T> Trait<T> for X { | |
456 | fn foo(&self, x: T) -> T { x } | |
457 | } | |
dfeec247 XL |
458 | ```", |
459 | ); | |
e1599b0c | 460 | } |
dfeec247 XL |
461 | db.note( |
462 | "for more information, visit \ | |
29967ef6 XL |
463 | https://doc.rust-lang.org/book/ch10-02-traits.html\ |
464 | #traits-as-parameters", | |
dfeec247 | 465 | ); |
e1599b0c | 466 | } |
1b1a35ee XL |
467 | (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { |
468 | let generics = self.generics_of(body_owner_def_id); | |
469 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
470 | if !sp.contains(p_span) { | |
471 | db.span_label(p_span, "this type parameter"); | |
472 | } | |
473 | db.help(&format!( | |
474 | "every closure has a distinct type and so could not always match the \ | |
475 | caller-chosen type of parameter `{}`", | |
476 | p | |
477 | )); | |
478 | } | |
f9f354fc XL |
479 | (ty::Param(p), _) | (_, ty::Param(p)) => { |
480 | let generics = self.generics_of(body_owner_def_id); | |
481 | let p_span = self.def_span(generics.type_param(p, self).def_id); | |
482 | if !sp.contains(p_span) { | |
483 | db.span_label(p_span, "this type parameter"); | |
e1599b0c | 484 | } |
f9f354fc XL |
485 | } |
486 | (ty::Projection(proj_ty), _) => { | |
487 | self.expected_projection( | |
488 | db, | |
489 | proj_ty, | |
490 | values, | |
491 | body_owner_def_id, | |
492 | &cause.code, | |
dfeec247 | 493 | ); |
e1599b0c | 494 | } |
f9f354fc XL |
495 | (_, ty::Projection(proj_ty)) => { |
496 | let msg = format!( | |
e1599b0c | 497 | "consider constraining the associated type `{}` to `{}`", |
dfeec247 | 498 | values.found, values.expected, |
dfeec247 | 499 | ); |
f9f354fc XL |
500 | if !self.suggest_constraint( |
501 | db, | |
502 | &msg, | |
503 | body_owner_def_id, | |
504 | proj_ty, | |
505 | values.expected, | |
506 | ) { | |
507 | db.help(&msg); | |
508 | db.note( | |
509 | "for more information, visit \ | |
510 | https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", | |
511 | ); | |
512 | } | |
0bf4aa26 | 513 | } |
e1599b0c | 514 | _ => {} |
b7449926 | 515 | } |
e1599b0c XL |
516 | debug!( |
517 | "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})", | |
1b1a35ee XL |
518 | values.expected, |
519 | values.expected.kind(), | |
520 | values.found, | |
521 | values.found.kind(), | |
e1599b0c | 522 | ); |
dfeec247 | 523 | } |
ff7c6d11 XL |
524 | CyclicTy(ty) => { |
525 | // Watch out for various cases of cyclic types and try to explain. | |
526 | if ty.is_closure() || ty.is_generator() { | |
dfeec247 XL |
527 | db.note( |
528 | "closures cannot capture themselves or take themselves as argument;\n\ | |
74b04a01 XL |
529 | this error may be the result of a recent compiler bug-fix,\n\ |
530 | see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\ | |
531 | for more information", | |
dfeec247 | 532 | ); |
ff7c6d11 XL |
533 | } |
534 | } | |
f9f354fc XL |
535 | TargetFeatureCast(def_id) => { |
536 | let attrs = self.get_attrs(*def_id); | |
537 | let target_spans = attrs | |
538 | .deref() | |
539 | .iter() | |
540 | .filter(|attr| attr.has_name(sym::target_feature)) | |
541 | .map(|attr| attr.span); | |
542 | db.note( | |
543 | "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" | |
544 | ); | |
545 | db.span_labels(target_spans, "`#[target_feature]` added here"); | |
546 | } | |
e9174d1e SL |
547 | _ => {} |
548 | } | |
549 | } | |
f9f354fc XL |
550 | |
551 | fn suggest_constraint( | |
1b1a35ee | 552 | self, |
f9f354fc XL |
553 | db: &mut DiagnosticBuilder<'_>, |
554 | msg: &str, | |
555 | body_owner_def_id: DefId, | |
556 | proj_ty: &ty::ProjectionTy<'tcx>, | |
557 | ty: Ty<'tcx>, | |
558 | ) -> bool { | |
559 | let assoc = self.associated_item(proj_ty.item_def_id); | |
1b1a35ee | 560 | let trait_ref = proj_ty.trait_ref(self); |
f9f354fc XL |
561 | if let Some(item) = self.hir().get_if_local(body_owner_def_id) { |
562 | if let Some(hir_generics) = item.generics() { | |
563 | // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`. | |
564 | // This will also work for `impl Trait`. | |
1b1a35ee | 565 | let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() { |
f9f354fc | 566 | let generics = self.generics_of(body_owner_def_id); |
1b1a35ee | 567 | generics.type_param(param_ty, self).def_id |
f9f354fc XL |
568 | } else { |
569 | return false; | |
570 | }; | |
571 | ||
572 | // First look in the `where` clause, as this might be | |
573 | // `fn foo<T>(x: T) where T: Trait`. | |
574 | for predicate in hir_generics.where_clause.predicates { | |
575 | if let hir::WherePredicate::BoundPredicate(pred) = predicate { | |
576 | if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = | |
577 | pred.bounded_ty.kind | |
578 | { | |
579 | if path.res.opt_def_id() == Some(def_id) { | |
580 | // This predicate is binding type param `A` in `<A as T>::Foo` to | |
581 | // something, potentially `T`. | |
582 | } else { | |
583 | continue; | |
584 | } | |
585 | } else { | |
586 | continue; | |
587 | } | |
588 | ||
589 | if self.constrain_generic_bound_associated_type_structured_suggestion( | |
590 | db, | |
591 | &trait_ref, | |
592 | pred.bounds, | |
593 | &assoc, | |
594 | ty, | |
595 | msg, | |
596 | ) { | |
597 | return true; | |
598 | } | |
599 | } | |
600 | } | |
601 | for param in hir_generics.params { | |
602 | if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) | |
603 | == Some(def_id) | |
604 | { | |
605 | // This is type param `A` in `<A as T>::Foo`. | |
606 | return self.constrain_generic_bound_associated_type_structured_suggestion( | |
607 | db, | |
608 | &trait_ref, | |
609 | param.bounds, | |
610 | &assoc, | |
611 | ty, | |
612 | msg, | |
613 | ); | |
614 | } | |
615 | } | |
616 | } | |
617 | } | |
618 | false | |
619 | } | |
620 | ||
621 | /// An associated type was expected and a different type was found. | |
622 | /// | |
623 | /// We perform a few different checks to see what we can suggest: | |
624 | /// | |
625 | /// - In the current item, look for associated functions that return the expected type and | |
626 | /// suggest calling them. (Not a structured suggestion.) | |
627 | /// - If any of the item's generic bounds can be constrained, we suggest constraining the | |
628 | /// associated type to the found type. | |
629 | /// - If the associated type has a default type and was expected inside of a `trait`, we | |
630 | /// mention that this is disallowed. | |
631 | /// - If all other things fail, and the error is not because of a mismatch between the `trait` | |
632 | /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc | |
633 | /// fn that returns the type. | |
634 | fn expected_projection( | |
1b1a35ee | 635 | self, |
f9f354fc XL |
636 | db: &mut DiagnosticBuilder<'_>, |
637 | proj_ty: &ty::ProjectionTy<'tcx>, | |
638 | values: &ExpectedFound<Ty<'tcx>>, | |
639 | body_owner_def_id: DefId, | |
640 | cause_code: &ObligationCauseCode<'_>, | |
641 | ) { | |
642 | let msg = format!( | |
643 | "consider constraining the associated type `{}` to `{}`", | |
644 | values.expected, values.found | |
645 | ); | |
646 | let body_owner = self.hir().get_if_local(body_owner_def_id); | |
647 | let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); | |
648 | ||
649 | // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. | |
650 | let callable_scope = match body_owner { | |
651 | Some( | |
652 | hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) | |
653 | | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) | |
654 | | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), | |
655 | ) => true, | |
656 | _ => false, | |
657 | }; | |
658 | let impl_comparison = matches!( | |
659 | cause_code, | |
660 | ObligationCauseCode::CompareImplMethodObligation { .. } | |
661 | | ObligationCauseCode::CompareImplTypeObligation { .. } | |
662 | | ObligationCauseCode::CompareImplConstObligation | |
663 | ); | |
664 | let assoc = self.associated_item(proj_ty.item_def_id); | |
665 | if !callable_scope || impl_comparison { | |
666 | // We do not want to suggest calling functions when the reason of the | |
667 | // type error is a comparison of an `impl` with its `trait` or when the | |
668 | // scope is outside of a `Body`. | |
669 | } else { | |
670 | // If we find a suitable associated function that returns the expected type, we don't | |
671 | // want the more general suggestion later in this method about "consider constraining | |
672 | // the associated type or calling a method that returns the associated type". | |
673 | let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( | |
674 | db, | |
675 | assoc.container.id(), | |
676 | current_method_ident, | |
677 | proj_ty.item_def_id, | |
678 | values.expected, | |
679 | ); | |
680 | // Possibly suggest constraining the associated type to conform to the | |
681 | // found type. | |
682 | if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) | |
683 | || point_at_assoc_fn | |
684 | { | |
685 | return; | |
686 | } | |
687 | } | |
688 | ||
1b1a35ee | 689 | if let ty::Opaque(def_id, _) = *proj_ty.self_ty().kind() { |
f9f354fc XL |
690 | // When the expected `impl Trait` is not defined in the current item, it will come from |
691 | // a return type. This can occur when dealing with `TryStream` (#71035). | |
692 | if self.constrain_associated_type_structured_suggestion( | |
693 | db, | |
694 | self.def_span(def_id), | |
695 | &assoc, | |
696 | values.found, | |
697 | &msg, | |
698 | ) { | |
699 | return; | |
700 | } | |
701 | } | |
702 | ||
703 | if self.point_at_associated_type(db, body_owner_def_id, values.found) { | |
704 | return; | |
705 | } | |
706 | ||
707 | if !impl_comparison { | |
708 | // Generic suggestion when we can't be more specific. | |
709 | if callable_scope { | |
710 | db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); | |
711 | } else { | |
712 | db.help(&msg); | |
713 | } | |
714 | db.note( | |
715 | "for more information, visit \ | |
716 | https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", | |
717 | ); | |
718 | } | |
719 | if self.sess.teach(&db.get_code().unwrap()) { | |
720 | db.help( | |
721 | "given an associated type `T` and a method `foo`: | |
722 | ``` | |
723 | trait Trait { | |
724 | type T; | |
725 | fn foo(&self) -> Self::T; | |
726 | } | |
727 | ``` | |
728 | the only way of implementing method `foo` is to constrain `T` with an explicit associated type: | |
729 | ``` | |
730 | impl Trait for X { | |
731 | type T = String; | |
732 | fn foo(&self) -> Self::T { String::new() } | |
733 | } | |
734 | ```", | |
735 | ); | |
736 | } | |
737 | } | |
738 | ||
739 | fn point_at_methods_that_satisfy_associated_type( | |
1b1a35ee | 740 | self, |
f9f354fc XL |
741 | db: &mut DiagnosticBuilder<'_>, |
742 | assoc_container_id: DefId, | |
743 | current_method_ident: Option<Symbol>, | |
744 | proj_ty_item_def_id: DefId, | |
745 | expected: Ty<'tcx>, | |
746 | ) -> bool { | |
747 | let items = self.associated_items(assoc_container_id); | |
748 | // Find all the methods in the trait that could be called to construct the | |
749 | // expected associated type. | |
750 | // FIXME: consider suggesting the use of associated `const`s. | |
751 | let methods: Vec<(Span, String)> = items | |
752 | .items | |
753 | .iter() | |
754 | .filter(|(name, item)| { | |
755 | ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident | |
756 | }) | |
757 | .filter_map(|(_, item)| { | |
758 | let method = self.fn_sig(item.def_id); | |
1b1a35ee | 759 | match *method.output().skip_binder().kind() { |
f9f354fc XL |
760 | ty::Projection(ty::ProjectionTy { item_def_id, .. }) |
761 | if item_def_id == proj_ty_item_def_id => | |
762 | { | |
763 | Some(( | |
764 | self.sess.source_map().guess_head_span(self.def_span(item.def_id)), | |
765 | format!("consider calling `{}`", self.def_path_str(item.def_id)), | |
766 | )) | |
767 | } | |
768 | _ => None, | |
769 | } | |
770 | }) | |
771 | .collect(); | |
772 | if !methods.is_empty() { | |
773 | // Use a single `help:` to show all the methods in the trait that can | |
774 | // be used to construct the expected associated type. | |
775 | let mut span: MultiSpan = | |
776 | methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into(); | |
777 | let msg = format!( | |
778 | "{some} method{s} {are} available that return{r} `{ty}`", | |
779 | some = if methods.len() == 1 { "a" } else { "some" }, | |
780 | s = pluralize!(methods.len()), | |
781 | are = if methods.len() == 1 { "is" } else { "are" }, | |
782 | r = if methods.len() == 1 { "s" } else { "" }, | |
783 | ty = expected | |
784 | ); | |
785 | for (sp, label) in methods.into_iter() { | |
786 | span.push_span_label(sp, label); | |
787 | } | |
788 | db.span_help(span, &msg); | |
789 | return true; | |
790 | } | |
791 | false | |
792 | } | |
793 | ||
794 | fn point_at_associated_type( | |
1b1a35ee | 795 | self, |
f9f354fc XL |
796 | db: &mut DiagnosticBuilder<'_>, |
797 | body_owner_def_id: DefId, | |
798 | found: Ty<'tcx>, | |
799 | ) -> bool { | |
3dfed10e XL |
800 | let hir_id = |
801 | match body_owner_def_id.as_local().map(|id| self.hir().local_def_id_to_hir_id(id)) { | |
802 | Some(hir_id) => hir_id, | |
803 | None => return false, | |
804 | }; | |
f9f354fc XL |
805 | // When `body_owner` is an `impl` or `trait` item, look in its associated types for |
806 | // `expected` and point at it. | |
807 | let parent_id = self.hir().get_parent_item(hir_id); | |
808 | let item = self.hir().find(parent_id); | |
809 | debug!("expected_projection parent item {:?}", item); | |
810 | match item { | |
811 | Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { | |
812 | // FIXME: account for `#![feature(specialization)]` | |
813 | for item in &items[..] { | |
814 | match item.kind { | |
f035d41b | 815 | hir::AssocItemKind::Type => { |
f9f354fc XL |
816 | // FIXME: account for returning some type in a trait fn impl that has |
817 | // an assoc type as a return type (#72076). | |
818 | if let hir::Defaultness::Default { has_value: true } = item.defaultness | |
819 | { | |
820 | if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { | |
821 | db.span_label( | |
822 | item.span, | |
823 | "associated type defaults can't be assumed inside the \ | |
824 | trait defining them", | |
825 | ); | |
826 | return true; | |
827 | } | |
828 | } | |
829 | } | |
830 | _ => {} | |
831 | } | |
832 | } | |
833 | } | |
834 | Some(hir::Node::Item(hir::Item { | |
835 | kind: hir::ItemKind::Impl { items, .. }, .. | |
836 | })) => { | |
837 | for item in &items[..] { | |
1b1a35ee XL |
838 | if let hir::AssocItemKind::Type = item.kind { |
839 | if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { | |
840 | db.span_label(item.span, "expected this associated type"); | |
841 | return true; | |
f9f354fc | 842 | } |
f9f354fc XL |
843 | } |
844 | } | |
845 | } | |
846 | _ => {} | |
847 | } | |
848 | false | |
849 | } | |
850 | ||
851 | /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` | |
852 | /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. | |
853 | fn constrain_generic_bound_associated_type_structured_suggestion( | |
1b1a35ee | 854 | self, |
f9f354fc XL |
855 | db: &mut DiagnosticBuilder<'_>, |
856 | trait_ref: &ty::TraitRef<'tcx>, | |
857 | bounds: hir::GenericBounds<'_>, | |
858 | assoc: &ty::AssocItem, | |
859 | ty: Ty<'tcx>, | |
860 | msg: &str, | |
861 | ) -> bool { | |
862 | // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. | |
863 | bounds.iter().any(|bound| match bound { | |
864 | hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { | |
865 | // Relate the type param against `T` in `<A as T>::Foo`. | |
866 | ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) | |
867 | && self.constrain_associated_type_structured_suggestion( | |
868 | db, ptr.span, assoc, ty, msg, | |
869 | ) | |
870 | } | |
871 | _ => false, | |
872 | }) | |
873 | } | |
874 | ||
875 | /// Given a span corresponding to a bound, provide a structured suggestion to set an | |
876 | /// associated type to a given type `ty`. | |
877 | fn constrain_associated_type_structured_suggestion( | |
1b1a35ee | 878 | self, |
f9f354fc XL |
879 | db: &mut DiagnosticBuilder<'_>, |
880 | span: Span, | |
881 | assoc: &ty::AssocItem, | |
882 | ty: Ty<'tcx>, | |
883 | msg: &str, | |
884 | ) -> bool { | |
885 | if let Ok(has_params) = | |
886 | self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>')) | |
887 | { | |
888 | let (span, sugg) = if has_params { | |
889 | let pos = span.hi() - BytePos(1); | |
890 | let span = Span::new(pos, pos, span.ctxt()); | |
891 | (span, format!(", {} = {}", assoc.ident, ty)) | |
892 | } else { | |
893 | (span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) | |
894 | }; | |
895 | db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); | |
896 | return true; | |
897 | } | |
898 | false | |
899 | } | |
e9174d1e | 900 | } |