1use 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#[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}