rustc_trait_selection/error_reporting/traits/
mod.rs

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