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