Skip to main content

hir_ty/infer/
diagnostics.rs

1//! This file contains the [`Diagnostics`] type used during inference,
2//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces
3//! it and takes care of diagnostics in inference.
4
5use std::cell::{OnceCell, RefCell};
6use std::ops::{Deref, DerefMut};
7
8use either::Either;
9use hir_def::expr_store::path::Path;
10use hir_def::{ExpressionStoreOwnerId, GenericDefId};
11use hir_def::{expr_store::ExpressionStore, type_ref::TypeRefId};
12use hir_def::{
13    hir::{ExprId, ExprOrPatIdPacked},
14    resolver::Resolver,
15};
16use la_arena::RawIdx;
17use rustc_hash::FxHashMap;
18use thin_vec::ThinVec;
19
20use crate::lower::LifetimeLoweringMode;
21use crate::{
22    InferenceDiagnostic, Span, TyLoweringDiagnostic,
23    db::{AnonConstId, HirDatabase},
24    generics::Generics,
25    infer::unify::InferenceTable,
26    lower::{
27        ForbidParamsAfterReason, LifetimeElisionKind, TyLoweringContext, TyLoweringInferVarsCtx,
28        path::{PathDiagnosticCallback, PathLoweringContext},
29    },
30    next_solver::{Const, Region, StoredTy, Ty},
31};
32
33// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
34// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference
35// to our resolver and so we cannot have mutable reference, but we really want to have
36// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess.
37#[derive(Debug, Default, Clone)]
38pub(super) struct Diagnostics(RefCell<ThinVec<InferenceDiagnostic>>);
39
40impl Diagnostics {
41    pub(super) fn push(&self, diagnostic: InferenceDiagnostic) {
42        self.0.borrow_mut().push(diagnostic);
43    }
44
45    pub(super) fn extend(&self, diagnostic: &[InferenceDiagnostic]) {
46        self.0.borrow_mut().extend(diagnostic.iter().cloned());
47    }
48
49    fn push_ty_diagnostics(&self, diagnostics: ThinVec<TyLoweringDiagnostic>) {
50        self.0
51            .borrow_mut()
52            .extend(diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { diag }));
53    }
54
55    pub(super) fn finish(self) -> ThinVec<InferenceDiagnostic> {
56        self.0.into_inner()
57    }
58}
59
60pub(crate) struct PathDiagnosticCallbackData<'a> {
61    node: ExprOrPatIdPacked,
62    diagnostics: &'a Diagnostics,
63}
64
65pub(super) struct InferenceTyLoweringVarsCtx<'a, 'db> {
66    pub(super) table: &'a mut InferenceTable<'db>,
67    pub(super) type_of_type_placeholder: &'a mut FxHashMap<TypeRefId, StoredTy>,
68}
69
70impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> {
71    fn next_ty_var(&mut self, span: Span) -> Ty<'db> {
72        let ty = self.table.infer_ctxt.next_ty_var(span);
73
74        if let Span::TypeRefId(type_ref) = span {
75            self.type_of_type_placeholder.insert(type_ref, ty.store());
76        }
77
78        ty
79    }
80    fn next_const_var(&mut self, span: Span) -> Const<'db> {
81        self.table.infer_ctxt.next_const_var(span)
82    }
83    fn next_region_var(&mut self, span: Span) -> Region<'db> {
84        self.table.infer_ctxt.next_region_var(span)
85    }
86
87    fn as_table(&mut self) -> Option<&mut InferenceTable<'db>> {
88        Some(self.table)
89    }
90}
91
92pub(super) struct InferenceTyLoweringContext<'db, 'a> {
93    ctx: TyLoweringContext<'db, 'a>,
94    diagnostics: &'a Diagnostics,
95    defined_anon_consts: &'a RefCell<ThinVec<AnonConstId<'db>>>,
96}
97
98impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> {
99    #[inline]
100    pub(super) fn new(
101        db: &'db dyn HirDatabase,
102        resolver: &'a Resolver<'db>,
103        store: &'db ExpressionStore,
104        diagnostics: &'a Diagnostics,
105        def: ExpressionStoreOwnerId,
106        generic_def: GenericDefId,
107        generics: &'a OnceCell<Generics<'db>>,
108        lifetime_elision: LifetimeElisionKind<'db>,
109        allow_using_generic_params: bool,
110        infer_vars: &'a mut InferenceTyLoweringVarsCtx<'a, 'db>,
111        defined_anon_consts: &'a RefCell<ThinVec<AnonConstId<'db>>>,
112        lifetime_lowering_mode: LifetimeLoweringMode,
113    ) -> Self {
114        let mut ctx = TyLoweringContext::new(
115            db,
116            resolver,
117            store,
118            def,
119            generic_def,
120            generics,
121            lifetime_elision,
122            lifetime_lowering_mode,
123        )
124        .with_infer_vars_behavior(Some(infer_vars));
125        if !allow_using_generic_params {
126            ctx.forbid_params_after(0, ForbidParamsAfterReason::AnonConst);
127        }
128        Self { ctx, diagnostics, defined_anon_consts }
129    }
130
131    #[inline]
132    pub(super) fn at_path<'b>(
133        &'b mut self,
134        path: &'b Path,
135        node: ExprOrPatIdPacked,
136    ) -> PathLoweringContext<'b, 'a, 'db> {
137        let on_diagnostic = PathDiagnosticCallback {
138            data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
139            callback: |data, _, diag| {
140                let data = data.as_ref().right().unwrap();
141                data.diagnostics
142                    .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
143            },
144        };
145        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
146    }
147
148    #[inline]
149    pub(super) fn at_path_forget_diagnostics<'b>(
150        &'b mut self,
151        path: &'b Path,
152    ) -> PathLoweringContext<'b, 'a, 'db> {
153        let on_diagnostic = PathDiagnosticCallback {
154            data: Either::Right(PathDiagnosticCallbackData {
155                diagnostics: self.diagnostics,
156                node: ExprOrPatIdPacked::from(ExprId::from_raw(RawIdx::from_u32(0))),
157            }),
158            callback: |_data, _, _diag| {},
159        };
160        PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
161    }
162
163    #[inline]
164    pub(super) fn forget_diagnostics(&mut self) {
165        self.ctx.diagnostics.clear();
166    }
167}
168
169impl<'db, 'a> Deref for InferenceTyLoweringContext<'db, 'a> {
170    type Target = TyLoweringContext<'db, 'a>;
171
172    #[inline]
173    fn deref(&self) -> &Self::Target {
174        &self.ctx
175    }
176}
177
178impl DerefMut for InferenceTyLoweringContext<'_, '_> {
179    #[inline]
180    fn deref_mut(&mut self) -> &mut Self::Target {
181        &mut self.ctx
182    }
183}
184
185impl Drop for InferenceTyLoweringContext<'_, '_> {
186    #[inline]
187    fn drop(&mut self) {
188        self.diagnostics.push_ty_diagnostics(std::mem::take(&mut self.ctx.diagnostics));
189        self.defined_anon_consts.borrow_mut().extend(self.ctx.defined_anon_consts.iter().copied());
190    }
191}