]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/ty/error.rs
review comments
[rust.git] / compiler / rustc_middle / src / ty / error.rs
1 use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter};
2 use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
3 use rustc_errors::pluralize;
4 use rustc_hir as hir;
5 use rustc_hir::def::{CtorOf, DefKind};
6 use rustc_hir::def_id::DefId;
7 use rustc_span::symbol::Symbol;
8 use rustc_target::spec::abi;
9 use std::borrow::Cow;
10 use std::collections::hash_map::DefaultHasher;
11 use std::hash::{Hash, Hasher};
12 use std::path::PathBuf;
13
14 #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)]
15 pub struct ExpectedFound<T> {
16     pub expected: T,
17     pub found: T,
18 }
19
20 impl<T> ExpectedFound<T> {
21     pub fn new(a_is_expected: bool, a: T, b: T) -> Self {
22         if a_is_expected {
23             ExpectedFound { expected: a, found: b }
24         } else {
25             ExpectedFound { expected: b, found: a }
26         }
27     }
28 }
29
30 // Data structures used in type unification
31 #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)]
32 #[rustc_pass_by_value]
33 pub enum TypeError<'tcx> {
34     Mismatch,
35     ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
36     PolarityMismatch(ExpectedFound<ty::ImplPolarity>),
37     UnsafetyMismatch(ExpectedFound<hir::Unsafety>),
38     AbiMismatch(ExpectedFound<abi::Abi>),
39     Mutability,
40     ArgumentMutability(usize),
41     TupleSize(ExpectedFound<usize>),
42     FixedArraySize(ExpectedFound<u64>),
43     ArgCount,
44     FieldMisMatch(Symbol, Symbol),
45
46     RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
47     RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
48     RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>),
49     RegionsPlaceholderMismatch,
50
51     Sorts(ExpectedFound<Ty<'tcx>>),
52     ArgumentSorts(ExpectedFound<Ty<'tcx>>, usize),
53     IntMismatch(ExpectedFound<ty::IntVarValue>),
54     FloatMismatch(ExpectedFound<ty::FloatTy>),
55     Traits(ExpectedFound<DefId>),
56     VariadicMismatch(ExpectedFound<bool>),
57
58     /// Instantiating a type variable with the given type would have
59     /// created a cycle (because it appears somewhere within that
60     /// type).
61     CyclicTy(Ty<'tcx>),
62     CyclicConst(ty::Const<'tcx>),
63     ProjectionMismatched(ExpectedFound<DefId>),
64     ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>),
65     ConstMismatch(ExpectedFound<ty::Const<'tcx>>),
66
67     IntrinsicCast,
68     /// Safe `#[target_feature]` functions are not assignable to safe function pointers.
69     TargetFeatureCast(DefId),
70 }
71
72 impl TypeError<'_> {
73     pub fn involves_regions(self) -> bool {
74         match self {
75             TypeError::RegionsDoesNotOutlive(_, _)
76             | TypeError::RegionsInsufficientlyPolymorphic(_, _)
77             | TypeError::RegionsOverlyPolymorphic(_, _)
78             | TypeError::RegionsPlaceholderMismatch => true,
79             _ => false,
80         }
81     }
82 }
83
84 /// Explains the source of a type err in a short, human readable way. This is meant to be placed
85 /// in parentheses after some larger message. You should also invoke `note_and_explain_type_err()`
86 /// afterwards to present additional details, particularly when it comes to lifetime-related
87 /// errors.
88 impl<'tcx> TypeError<'tcx> {
89     pub fn to_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
90         use self::TypeError::*;
91         fn report_maybe_different(expected: &str, found: &str) -> String {
92             // A naive approach to making sure that we're not reporting silly errors such as:
93             // (expected closure, found closure).
94             if expected == found {
95                 format!("expected {}, found a different {}", expected, found)
96             } else {
97                 format!("expected {}, found {}", expected, found)
98             }
99         }
100
101         let br_string = |br: ty::BoundRegionKind| match br {
102             ty::BrNamed(_, name) => format!(" {}", name),
103             _ => String::new(),
104         };
105
106         match self {
107             CyclicTy(_) => "cyclic type of infinite size".into(),
108             CyclicConst(_) => "encountered a self-referencing constant".into(),
109             Mismatch => "types differ".into(),
110             ConstnessMismatch(values) => {
111                 format!("expected {} bound, found {} bound", values.expected, values.found).into()
112             }
113             PolarityMismatch(values) => {
114                 format!("expected {} polarity, found {} polarity", values.expected, values.found)
115                     .into()
116             }
117             UnsafetyMismatch(values) => {
118                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
119             }
120             AbiMismatch(values) => {
121                 format!("expected {} fn, found {} fn", values.expected, values.found).into()
122             }
123             ArgumentMutability(_) | Mutability => "types differ in mutability".into(),
124             TupleSize(values) => format!(
125                 "expected a tuple with {} element{}, found one with {} element{}",
126                 values.expected,
127                 pluralize!(values.expected),
128                 values.found,
129                 pluralize!(values.found)
130             )
131             .into(),
132             FixedArraySize(values) => format!(
133                 "expected an array with a fixed size of {} element{}, found one with {} element{}",
134                 values.expected,
135                 pluralize!(values.expected),
136                 values.found,
137                 pluralize!(values.found)
138             )
139             .into(),
140             ArgCount => "incorrect number of function parameters".into(),
141             FieldMisMatch(adt, field) => format!("field type mismatch: {}.{}", adt, field).into(),
142             RegionsDoesNotOutlive(..) => "lifetime mismatch".into(),
143             // Actually naming the region here is a bit confusing because context is lacking
144             RegionsInsufficientlyPolymorphic(..) => {
145                 "one type is more general than the other".into()
146             }
147             RegionsOverlyPolymorphic(br, _) => format!(
148                 "expected concrete lifetime, found bound lifetime parameter{}",
149                 br_string(br)
150             )
151             .into(),
152             RegionsPlaceholderMismatch => "one type is more general than the other".into(),
153             ArgumentSorts(values, _) | Sorts(values) => {
154                 let mut expected = values.expected.sort_string(tcx);
155                 let mut found = values.found.sort_string(tcx);
156                 if expected == found {
157                     expected = values.expected.sort_string(tcx);
158                     found = values.found.sort_string(tcx);
159                 }
160                 report_maybe_different(&expected, &found).into()
161             }
162             Traits(values) => {
163                 let (mut expected, mut found) = with_forced_trimmed_paths!((
164                     tcx.def_path_str(values.expected),
165                     tcx.def_path_str(values.found),
166                 ));
167                 if expected == found {
168                     expected = tcx.def_path_str(values.expected);
169                     found = tcx.def_path_str(values.found);
170                 }
171                 report_maybe_different(&format!("trait `{expected}`"), &format!("trait `{found}`"))
172                     .into()
173             }
174             IntMismatch(ref values) => {
175                 let expected = match values.expected {
176                     ty::IntVarValue::IntType(ty) => ty.name_str(),
177                     ty::IntVarValue::UintType(ty) => ty.name_str(),
178                 };
179                 let found = match values.found {
180                     ty::IntVarValue::IntType(ty) => ty.name_str(),
181                     ty::IntVarValue::UintType(ty) => ty.name_str(),
182                 };
183                 format!("expected `{}`, found `{}`", expected, found).into()
184             }
185             FloatMismatch(ref values) => format!(
186                 "expected `{}`, found `{}`",
187                 values.expected.name_str(),
188                 values.found.name_str()
189             )
190             .into(),
191             VariadicMismatch(ref values) => format!(
192                 "expected {} fn, found {} function",
193                 if values.expected { "variadic" } else { "non-variadic" },
194                 if values.found { "variadic" } else { "non-variadic" }
195             )
196             .into(),
197             ProjectionMismatched(ref values) => format!(
198                 "expected {}, found {}",
199                 tcx.def_path_str(values.expected),
200                 tcx.def_path_str(values.found)
201             )
202             .into(),
203             ExistentialMismatch(ref values) => report_maybe_different(
204                 &format!("trait `{}`", values.expected),
205                 &format!("trait `{}`", values.found),
206             )
207             .into(),
208             ConstMismatch(ref values) => {
209                 format!("expected `{}`, found `{}`", values.expected, values.found).into()
210             }
211             IntrinsicCast => "cannot coerce intrinsics to function pointers".into(),
212             TargetFeatureCast(_) => {
213                 "cannot coerce functions with `#[target_feature]` to safe function pointers".into()
214             }
215         }
216     }
217 }
218
219 impl<'tcx> TypeError<'tcx> {
220     pub fn must_include_note(self) -> bool {
221         use self::TypeError::*;
222         match self {
223             CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | ConstnessMismatch(_)
224             | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
225             | ArgumentSorts(..) | Sorts(_) | IntMismatch(_) | FloatMismatch(_)
226             | VariadicMismatch(_) | TargetFeatureCast(_) => false,
227
228             Mutability
229             | ArgumentMutability(_)
230             | TupleSize(_)
231             | ArgCount
232             | FieldMisMatch(..)
233             | RegionsDoesNotOutlive(..)
234             | RegionsInsufficientlyPolymorphic(..)
235             | RegionsOverlyPolymorphic(..)
236             | RegionsPlaceholderMismatch
237             | Traits(_)
238             | ProjectionMismatched(_)
239             | ExistentialMismatch(_)
240             | ConstMismatch(_)
241             | IntrinsicCast => true,
242         }
243     }
244 }
245
246 impl<'tcx> Ty<'tcx> {
247     pub fn sort_string(self, tcx: TyCtxt<'tcx>) -> Cow<'static, str> {
248         match *self.kind() {
249             ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(),
250             ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
251                 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
252                 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
253                 _ => "fn item".into(),
254             },
255             ty::FnPtr(_) => "fn pointer".into(),
256             ty::Dynamic(ref inner, ..) if let Some(principal) = inner.principal() => {
257                 format!("`dyn {}`", tcx.def_path_str(principal.def_id())).into()
258             }
259             ty::Dynamic(..) => "trait object".into(),
260             ty::Closure(..) => "closure".into(),
261             ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
262             ty::GeneratorWitness(..) |
263             ty::GeneratorWitnessMIR(..) => "generator witness".into(),
264             ty::Infer(ty::TyVar(_)) => "inferred type".into(),
265             ty::Infer(ty::IntVar(_)) => "integer".into(),
266             ty::Infer(ty::FloatVar(_)) => "floating-point number".into(),
267             ty::Placeholder(..) => "placeholder type".into(),
268             ty::Bound(..) => "bound type".into(),
269             ty::Infer(ty::FreshTy(_)) => "fresh type".into(),
270             ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(),
271             ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(),
272             ty::Alias(ty::Projection, _) => "associated type".into(),
273             ty::Param(p) => format!("type parameter `{p}`").into(),
274             ty::Alias(ty::Opaque, ..) => "opaque type".into(),
275             ty::Error(_) => "type error".into(),
276             _ => {
277                 let width = tcx.sess.diagnostic_width();
278                 let length_limit = std::cmp::max(width / 4, 15);
279                 format!("`{}`", tcx.ty_string_with_limit(self, length_limit)).into()
280             }
281         }
282     }
283
284     pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
285         match *self.kind() {
286             ty::Infer(_)
287             | ty::Error(_)
288             | ty::Bool
289             | ty::Char
290             | ty::Int(_)
291             | ty::Uint(_)
292             | ty::Float(_)
293             | ty::Str
294             | ty::Never => "type".into(),
295             ty::Tuple(ref tys) if tys.is_empty() => "unit type".into(),
296             ty::Adt(def, _) => def.descr().into(),
297             ty::Foreign(_) => "extern type".into(),
298             ty::Array(..) => "array".into(),
299             ty::Slice(_) => "slice".into(),
300             ty::RawPtr(_) => "raw pointer".into(),
301             ty::Ref(.., mutbl) => match mutbl {
302                 hir::Mutability::Mut => "mutable reference",
303                 _ => "reference",
304             }
305             .into(),
306             ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) {
307                 DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(),
308                 DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(),
309                 _ => "fn item".into(),
310             },
311             ty::FnPtr(_) => "fn pointer".into(),
312             ty::Dynamic(..) => "trait object".into(),
313             ty::Closure(..) => "closure".into(),
314             ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
315             ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => "generator witness".into(),
316             ty::Tuple(..) => "tuple".into(),
317             ty::Placeholder(..) => "higher-ranked type".into(),
318             ty::Bound(..) => "bound type variable".into(),
319             ty::Alias(ty::Projection, _) => "associated type".into(),
320             ty::Param(_) => "type parameter".into(),
321             ty::Alias(ty::Opaque, ..) => "opaque type".into(),
322         }
323     }
324 }
325
326 impl<'tcx> TyCtxt<'tcx> {
327     pub fn ty_string_with_limit(self, ty: Ty<'tcx>, length_limit: usize) -> String {
328         let mut type_limit = 50;
329         let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
330             .pretty_print_type(ty)
331             .expect("could not write to `String`")
332             .into_buffer();
333         if regular.len() <= length_limit {
334             return regular;
335         }
336         let mut short;
337         loop {
338             // Look for the longest properly trimmed path that still fits in length_limit.
339             short = with_forced_trimmed_paths!(
340                 FmtPrinter::new_with_limit(
341                     self,
342                     hir::def::Namespace::TypeNS,
343                     rustc_session::Limit(type_limit),
344                 )
345                 .pretty_print_type(ty)
346                 .expect("could not write to `String`")
347                 .into_buffer()
348             );
349             if short.len() <= length_limit || type_limit == 0 {
350                 break;
351             }
352             type_limit -= 1;
353         }
354         short
355     }
356
357     pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
358         let width = self.sess.diagnostic_width();
359         let length_limit = width.saturating_sub(30);
360         let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS)
361             .pretty_print_type(ty)
362             .expect("could not write to `String`")
363             .into_buffer();
364         if regular.len() <= width {
365             return (regular, None);
366         }
367         let short = self.ty_string_with_limit(ty, length_limit);
368         if regular == short {
369             return (regular, None);
370         }
371         // Multiple types might be shortened in a single error, ensure we create a file for each.
372         let mut s = DefaultHasher::new();
373         ty.hash(&mut s);
374         let hash = s.finish();
375         let path = self.output_filenames(()).temp_path_ext(&format!("long-type-{hash}.txt"), None);
376         match std::fs::write(&path, &regular) {
377             Ok(_) => (short, Some(path)),
378             Err(_) => (regular, None),
379         }
380     }
381 }