rustc_trait_selection/error_reporting/traits/
mod.rs

1pub mod ambiguity;
2pub mod call_kind;
3mod fulfillment_errors;
4pub mod on_unimplemented;
5mod overflow;
6pub mod suggestions;
7
8use std::{fmt, iter};
9
10use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
11use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err};
12use rustc_hir::def_id::{DefId, LocalDefId};
13use rustc_hir::intravisit::Visitor;
14use rustc_hir::{self as hir, AmbigArg, LangItem};
15use rustc_infer::traits::{
16    DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
17    PredicateObligation, SelectionError,
18};
19use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths};
20use rustc_middle::ty::{self, Ty, TyCtxt};
21use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
22use tracing::{info, instrument};
23
24pub use self::overflow::*;
25use crate::error_reporting::TypeErrCtxt;
26use crate::traits::{FulfillmentError, FulfillmentErrorCode};
27
28// When outputting impl candidates, prefer showing those that are more similar.
29//
30// We also compare candidates after skipping lifetimes, which has a lower
31// priority than exact matches.
32#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
33pub enum CandidateSimilarity {
34    Exact { ignoring_lifetimes: bool },
35    Fuzzy { ignoring_lifetimes: bool },
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct ImplCandidate<'tcx> {
40    pub trait_ref: ty::TraitRef<'tcx>,
41    pub similarity: CandidateSimilarity,
42    impl_def_id: DefId,
43}
44
45enum GetSafeTransmuteErrorAndReason {
46    Silent,
47    Default,
48    Error { err_msg: String, safe_transmute_explanation: Option<String> },
49}
50
51struct UnsatisfiedConst(pub bool);
52
53/// Crude way of getting back an `Expr` from a `Span`.
54pub struct FindExprBySpan<'hir> {
55    pub span: Span,
56    pub result: Option<&'hir hir::Expr<'hir>>,
57    pub ty_result: Option<&'hir hir::Ty<'hir>>,
58    pub include_closures: bool,
59    pub tcx: TyCtxt<'hir>,
60}
61
62impl<'hir> FindExprBySpan<'hir> {
63    pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
64        Self { span, result: None, ty_result: None, tcx, include_closures: false }
65    }
66}
67
68impl<'v> Visitor<'v> for FindExprBySpan<'v> {
69    type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
70
71    fn nested_visit_map(&mut self) -> Self::Map {
72        self.tcx.hir()
73    }
74
75    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
76        if self.span == ex.span {
77            self.result = Some(ex);
78        } else {
79            if let hir::ExprKind::Closure(..) = ex.kind
80                && self.include_closures
81                && let closure_header_sp = self.span.with_hi(ex.span.hi())
82                && closure_header_sp == ex.span
83            {
84                self.result = Some(ex);
85            }
86            hir::intravisit::walk_expr(self, ex);
87        }
88    }
89
90    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
91        if self.span == ty.span {
92            self.ty_result = Some(ty.as_unambig_ty());
93        } else {
94            hir::intravisit::walk_ty(self, ty);
95        }
96    }
97}
98
99/// Summarizes information
100#[derive(Clone)]
101pub enum ArgKind {
102    /// An argument of non-tuple type. Parameters are (name, ty)
103    Arg(String, String),
104
105    /// An argument of tuple type. For a "found" argument, the span is
106    /// the location in the source of the pattern. For an "expected"
107    /// argument, it will be None. The vector is a list of (name, ty)
108    /// strings for the components of the tuple.
109    Tuple(Option<Span>, Vec<(String, String)>),
110}
111
112impl ArgKind {
113    fn empty() -> ArgKind {
114        ArgKind::Arg("_".to_owned(), "_".to_owned())
115    }
116
117    /// Creates an `ArgKind` from the expected type of an
118    /// argument. It has no name (`_`) and an optional source span.
119    pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
120        match t.kind() {
121            ty::Tuple(tys) => ArgKind::Tuple(
122                span,
123                tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
124            ),
125            _ => ArgKind::Arg("_".to_owned(), t.to_string()),
126        }
127    }
128}
129
130#[derive(Copy, Clone)]
131pub enum DefIdOrName {
132    DefId(DefId),
133    Name(&'static str),
134}
135
136impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
137    pub fn report_fulfillment_errors(
138        &self,
139        mut errors: Vec<FulfillmentError<'tcx>>,
140    ) -> ErrorGuaranteed {
141        self.sub_relations
142            .borrow_mut()
143            .add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
144
145        #[derive(Debug)]
146        struct ErrorDescriptor<'tcx> {
147            predicate: ty::Predicate<'tcx>,
148            index: Option<usize>, // None if this is an old error
149        }
150
151        let mut error_map: FxIndexMap<_, Vec<_>> = self
152            .reported_trait_errors
153            .borrow()
154            .iter()
155            .map(|(&span, predicates)| {
156                (
157                    span,
158                    predicates
159                        .0
160                        .iter()
161                        .map(|&predicate| ErrorDescriptor { predicate, index: None })
162                        .collect(),
163                )
164            })
165            .collect();
166
167        // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
168        // with more relevant type information and hide redundant E0282 errors.
169        errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
170            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
171                if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
172            {
173                1
174            }
175            ty::PredicateKind::Coerce(_) => 2,
176            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
177            _ => 0,
178        });
179
180        for (index, error) in errors.iter().enumerate() {
181            // We want to ignore desugarings here: spans are equivalent even
182            // if one is the result of a desugaring and the other is not.
183            let mut span = error.obligation.cause.span;
184            let expn_data = span.ctxt().outer_expn_data();
185            if let ExpnKind::Desugaring(_) = expn_data.kind {
186                span = expn_data.call_site;
187            }
188
189            error_map.entry(span).or_default().push(ErrorDescriptor {
190                predicate: error.obligation.predicate,
191                index: Some(index),
192            });
193        }
194
195        // We do this in 2 passes because we want to display errors in order, though
196        // maybe it *is* better to sort errors by span or something.
197        let mut is_suppressed = vec![false; errors.len()];
198        for (_, error_set) in error_map.iter() {
199            // We want to suppress "duplicate" errors with the same span.
200            for error in error_set {
201                if let Some(index) = error.index {
202                    // Suppress errors that are either:
203                    // 1) strictly implied by another error.
204                    // 2) implied by an error with a smaller index.
205                    for error2 in error_set {
206                        if error2.index.is_some_and(|index2| is_suppressed[index2]) {
207                            // Avoid errors being suppressed by already-suppressed
208                            // errors, to prevent all errors from being suppressed
209                            // at once.
210                            continue;
211                        }
212
213                        if self.error_implies(error2.predicate, error.predicate)
214                            && !(error2.index >= error.index
215                                && self.error_implies(error.predicate, error2.predicate))
216                        {
217                            info!("skipping {:?} (implied by {:?})", error, error2);
218                            is_suppressed[index] = true;
219                            break;
220                        }
221                    }
222                }
223            }
224        }
225
226        let mut reported = None;
227
228        for from_expansion in [false, true] {
229            for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
230                if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
231                    let guar = self.report_fulfillment_error(error);
232                    self.infcx.set_tainted_by_errors(guar);
233                    reported = Some(guar);
234                    // We want to ignore desugarings here: spans are equivalent even
235                    // if one is the result of a desugaring and the other is not.
236                    let mut span = error.obligation.cause.span;
237                    let expn_data = span.ctxt().outer_expn_data();
238                    if let ExpnKind::Desugaring(_) = expn_data.kind {
239                        span = expn_data.call_site;
240                    }
241                    self.reported_trait_errors
242                        .borrow_mut()
243                        .entry(span)
244                        .or_insert_with(|| (vec![], guar))
245                        .0
246                        .push(error.obligation.predicate);
247                }
248            }
249        }
250
251        // It could be that we don't report an error because we have seen an `ErrorReported` from
252        // another source. We should probably be able to fix most of these, but some are delayed
253        // bugs that get a proper error after this function.
254        reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
255    }
256
257    #[instrument(skip(self), level = "debug")]
258    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
259        let mut error = FulfillmentError {
260            obligation: error.obligation.clone(),
261            code: error.code.clone(),
262            root_obligation: error.root_obligation.clone(),
263        };
264        if matches!(
265            error.code,
266            FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
267                | FulfillmentErrorCode::Project(_)
268        ) && self.apply_do_not_recommend(&mut error.obligation)
269        {
270            error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
271        }
272
273        match error.code {
274            FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
275                error.obligation.clone(),
276                &error.root_obligation,
277                selection_error,
278            ),
279            FulfillmentErrorCode::Project(ref e) => {
280                self.report_projection_error(&error.obligation, e)
281            }
282            FulfillmentErrorCode::Ambiguity { overflow: None } => {
283                self.maybe_report_ambiguity(&error.obligation)
284            }
285            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
286                self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
287            }
288            FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
289                .report_mismatched_types(
290                    &error.obligation.cause,
291                    error.obligation.param_env,
292                    expected_found.expected,
293                    expected_found.found,
294                    *err,
295                )
296                .emit(),
297            FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
298                let mut diag = self.report_mismatched_consts(
299                    &error.obligation.cause,
300                    error.obligation.param_env,
301                    expected_found.expected,
302                    expected_found.found,
303                    *err,
304                );
305                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
306                if let ObligationCauseCode::WhereClause(..)
307                | ObligationCauseCode::WhereClauseInExpr(..) = code
308                {
309                    self.note_obligation_cause_code(
310                        error.obligation.cause.body_id,
311                        &mut diag,
312                        error.obligation.predicate,
313                        error.obligation.param_env,
314                        code,
315                        &mut vec![],
316                        &mut Default::default(),
317                    );
318                }
319                diag.emit()
320            }
321            FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
322        }
323    }
324}
325
326/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
327/// string.
328pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
329    use std::fmt::Write;
330
331    let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
332    let mut w = "impl".to_owned();
333
334    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
335
336    // FIXME: Currently only handles ?Sized.
337    //        Needs to support ?Move and ?DynSized when they are implemented.
338    let mut types_without_default_bounds = FxIndexSet::default();
339    let sized_trait = tcx.lang_items().sized_trait();
340
341    let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
342    if !arg_names.is_empty() {
343        types_without_default_bounds.extend(args.types());
344        w.push('<');
345        w.push_str(&arg_names.join(", "));
346        w.push('>');
347    }
348
349    write!(
350        w,
351        " {}{} for {}",
352        tcx.impl_polarity(impl_def_id).as_str(),
353        trait_ref.print_only_trait_path(),
354        tcx.type_of(impl_def_id).instantiate_identity()
355    )
356    .unwrap();
357
358    // The predicates will contain default bounds like `T: Sized`. We need to
359    // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
360    let predicates = tcx.predicates_of(impl_def_id).predicates;
361    let mut pretty_predicates =
362        Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
363
364    for (p, _) in predicates {
365        if let Some(poly_trait_ref) = p.as_trait_clause() {
366            if Some(poly_trait_ref.def_id()) == sized_trait {
367                // FIXME(#120456) - is `swap_remove` correct?
368                types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
369                continue;
370            }
371        }
372        pretty_predicates.push(p.to_string());
373    }
374
375    pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
376
377    if !pretty_predicates.is_empty() {
378        write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
379    }
380
381    w.push(';');
382    Some(w)
383}
384
385impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
386    pub fn report_extra_impl_obligation(
387        &self,
388        error_span: Span,
389        impl_item_def_id: LocalDefId,
390        trait_item_def_id: DefId,
391        requirement: &dyn fmt::Display,
392    ) -> Diag<'a> {
393        let mut err = struct_span_code_err!(
394            self.dcx(),
395            error_span,
396            E0276,
397            "impl has stricter requirements than trait"
398        );
399
400        if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
401            if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
402                let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
403                err.span_label(span, format!("definition of `{item_name}` from trait"));
404            }
405        }
406
407        err.span_label(error_span, format!("impl has extra requirement {requirement}"));
408
409        err
410    }
411}
412
413pub fn report_dyn_incompatibility<'tcx>(
414    tcx: TyCtxt<'tcx>,
415    span: Span,
416    hir_id: Option<hir::HirId>,
417    trait_def_id: DefId,
418    violations: &[DynCompatibilityViolation],
419) -> Diag<'tcx> {
420    let trait_str = tcx.def_path_str(trait_def_id);
421    let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
422        hir::Node::Item(item) => Some(item.ident.span),
423        _ => None,
424    });
425
426    let mut err = struct_span_code_err!(
427        tcx.dcx(),
428        span,
429        E0038,
430        "the {} `{}` is not dyn compatible",
431        tcx.def_descr(trait_def_id),
432        trait_str
433    );
434    err.span_label(span, format!("`{trait_str}` is not dyn compatible"));
435
436    attempt_dyn_to_impl_suggestion(tcx, hir_id, &mut err);
437
438    let mut reported_violations = FxIndexSet::default();
439    let mut multi_span = vec![];
440    let mut messages = vec![];
441    for violation in violations {
442        if let DynCompatibilityViolation::SizedSelf(sp) = &violation
443            && !sp.is_empty()
444        {
445            // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
446            // with a `Span`.
447            reported_violations.insert(DynCompatibilityViolation::SizedSelf(vec![].into()));
448        }
449        if reported_violations.insert(violation.clone()) {
450            let spans = violation.spans();
451            let msg = if trait_span.is_none() || spans.is_empty() {
452                format!("the trait is not dyn compatible because {}", violation.error_msg())
453            } else {
454                format!("...because {}", violation.error_msg())
455            };
456            if spans.is_empty() {
457                err.note(msg);
458            } else {
459                for span in spans {
460                    multi_span.push(span);
461                    messages.push(msg.clone());
462                }
463            }
464        }
465    }
466    let has_multi_span = !multi_span.is_empty();
467    let mut note_span = MultiSpan::from_spans(multi_span.clone());
468    if let (Some(trait_span), true) = (trait_span, has_multi_span) {
469        note_span.push_span_label(trait_span, "this trait is not dyn compatible...");
470    }
471    for (span, msg) in iter::zip(multi_span, messages) {
472        note_span.push_span_label(span, msg);
473    }
474    err.span_note(
475        note_span,
476        "for a trait to be dyn compatible it needs to allow building a vtable\n\
477        for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>",
478    );
479
480    // Only provide the help if its a local trait, otherwise it's not actionable.
481    if trait_span.is_some() {
482        let mut potential_solutions: Vec<_> =
483            reported_violations.into_iter().map(|violation| violation.solution()).collect();
484        potential_solutions.sort();
485        // Allows us to skip suggesting that the same item should be moved to another trait multiple times.
486        potential_solutions.dedup();
487        for solution in potential_solutions {
488            solution.add_to(&mut err);
489        }
490    }
491
492    attempt_dyn_to_enum_suggestion(tcx, trait_def_id, &*trait_str, &mut err);
493
494    err
495}
496
497/// Attempt to suggest converting the `dyn Trait` argument to an enumeration
498/// over the types that implement `Trait`.
499fn attempt_dyn_to_enum_suggestion(
500    tcx: TyCtxt<'_>,
501    trait_def_id: DefId,
502    trait_str: &str,
503    err: &mut Diag<'_>,
504) {
505    let impls_of = tcx.trait_impls_of(trait_def_id);
506
507    if !impls_of.blanket_impls().is_empty() {
508        return;
509    }
510
511    let concrete_impls: Option<Vec<Ty<'_>>> = impls_of
512        .non_blanket_impls()
513        .values()
514        .flatten()
515        .map(|impl_id| {
516            // Don't suggest conversion to enum if the impl types have type parameters.
517            // It's unlikely the user wants to define a generic enum.
518            let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None };
519
520            // Obviously unsized impl types won't be usable in an enum.
521            // Note: this doesn't use `Ty::is_trivially_sized` because that function
522            // defaults to assuming that things are *not* sized, whereas we want to
523            // fall back to assuming that things may be sized.
524            match impl_type.kind() {
525                ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => {
526                    return None;
527                }
528                _ => {}
529            }
530            Some(impl_type)
531        })
532        .collect();
533    let Some(concrete_impls) = concrete_impls else { return };
534
535    const MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM: usize = 9;
536    if concrete_impls.is_empty() || concrete_impls.len() > MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM {
537        return;
538    }
539
540    let externally_visible = if let Some(def_id) = trait_def_id.as_local() {
541        // We may be executing this during typeck, which would result in cycle
542        // if we used effective_visibilities query, which looks into opaque types
543        // (and therefore calls typeck).
544        tcx.resolutions(()).effective_visibilities.is_exported(def_id)
545    } else {
546        false
547    };
548
549    if let [only_impl] = &concrete_impls[..] {
550        let within = if externally_visible { " within this crate" } else { "" };
551        err.help(with_no_trimmed_paths!(format!(
552            "only type `{only_impl}` implements `{trait_str}`{within}; \
553            consider using it directly instead."
554        )));
555    } else {
556        let types = concrete_impls
557            .iter()
558            .map(|t| with_no_trimmed_paths!(format!("  {}", t)))
559            .collect::<Vec<String>>()
560            .join("\n");
561
562        err.help(format!(
563            "the following types implement `{trait_str}`:\n\
564             {types}\n\
565             consider defining an enum where each variant holds one of these types,\n\
566             implementing `{trait_str}` for this new enum and using it instead",
567        ));
568    }
569
570    if externally_visible {
571        err.note(format!(
572            "`{trait_str}` may be implemented in other crates; if you want to support your users \
573             passing their own types here, you can't refer to a specific type",
574        ));
575    }
576}
577
578/// Attempt to suggest that a `dyn Trait` argument or return type be converted
579/// to use `impl Trait`.
580fn attempt_dyn_to_impl_suggestion(tcx: TyCtxt<'_>, hir_id: Option<hir::HirId>, err: &mut Diag<'_>) {
581    let Some(hir_id) = hir_id else { return };
582    let hir::Node::Ty(ty) = tcx.hir_node(hir_id) else { return };
583    let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind else { return };
584
585    // Only suggest converting `dyn` to `impl` if we're in a function signature.
586    // This ensures that we don't suggest converting e.g.
587    //   `type Alias = Box<dyn DynIncompatibleTrait>;` to
588    //   `type Alias = Box<impl DynIncompatibleTrait>;`
589    let Some((_id, first_non_type_parent_node)) =
590        tcx.hir().parent_iter(hir_id).find(|(_id, node)| !matches!(node, hir::Node::Ty(_)))
591    else {
592        return;
593    };
594    if first_non_type_parent_node.fn_sig().is_none() {
595        return;
596    }
597
598    err.span_suggestion_verbose(
599        ty.span.until(trait_ref.span),
600        "consider using an opaque type instead",
601        "impl ",
602        Applicability::MaybeIncorrect,
603    );
604}