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 rustc_hash::FxHashMap;
18use thin_vec::ThinVec;
19
20use crate::lower::LifetimeLoweringMode;
21use crate::{
22 InferenceDiagnostic, InferenceTyDiagnosticSource, 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#[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 fn push_ty_diagnostics(
46 &self,
47 source: InferenceTyDiagnosticSource,
48 diagnostics: ThinVec<TyLoweringDiagnostic>,
49 ) {
50 self.0.borrow_mut().extend(
51 diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
52 );
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 source: InferenceTyDiagnosticSource,
96 defined_anon_consts: &'a RefCell<ThinVec<AnonConstId<'db>>>,
97}
98
99impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> {
100 #[inline]
101 pub(super) fn new(
102 db: &'db dyn HirDatabase,
103 resolver: &'a Resolver<'db>,
104 store: &'db ExpressionStore,
105 diagnostics: &'a Diagnostics,
106 source: InferenceTyDiagnosticSource,
107 def: ExpressionStoreOwnerId,
108 generic_def: GenericDefId,
109 generics: &'a OnceCell<Generics<'db>>,
110 lifetime_elision: LifetimeElisionKind<'db>,
111 allow_using_generic_params: bool,
112 infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>,
113 defined_anon_consts: &'a RefCell<ThinVec<AnonConstId<'db>>>,
114 lifetime_lowering_mode: LifetimeLoweringMode,
115 ) -> Self {
116 let mut ctx = TyLoweringContext::new(
117 db,
118 resolver,
119 store,
120 def,
121 generic_def,
122 generics,
123 lifetime_elision,
124 lifetime_lowering_mode,
125 )
126 .with_infer_vars_behavior(infer_vars);
127 if !allow_using_generic_params {
128 ctx.forbid_params_after(0, ForbidParamsAfterReason::AnonConst);
129 }
130 Self { ctx, diagnostics, source, defined_anon_consts }
131 }
132
133 #[inline]
134 pub(super) fn at_path<'b>(
135 &'b mut self,
136 path: &'b Path,
137 node: ExprOrPatIdPacked,
138 ) -> PathLoweringContext<'b, 'a, 'db> {
139 let on_diagnostic = PathDiagnosticCallback {
140 data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
141 callback: |data, _, diag| {
142 let data = data.as_ref().right().unwrap();
143 data.diagnostics
144 .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
145 },
146 };
147 PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
148 }
149
150 #[inline]
151 pub(super) fn at_path_forget_diagnostics<'b>(
152 &'b mut self,
153 path: &'b Path,
154 ) -> PathLoweringContext<'b, 'a, 'db> {
155 let on_diagnostic = PathDiagnosticCallback {
156 data: Either::Right(PathDiagnosticCallbackData {
157 diagnostics: self.diagnostics,
158 node: ExprOrPatIdPacked::from(ExprId::from_raw(RawIdx::from_u32(0))),
159 }),
160 callback: |_data, _, _diag| {},
161 };
162 PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
163 }
164
165 #[inline]
166 pub(super) fn forget_diagnostics(&mut self) {
167 self.ctx.diagnostics.clear();
168 }
169}
170
171impl<'db, 'a> Deref for InferenceTyLoweringContext<'db, 'a> {
172 type Target = TyLoweringContext<'db, 'a>;
173
174 #[inline]
175 fn deref(&self) -> &Self::Target {
176 &self.ctx
177 }
178}
179
180impl DerefMut for InferenceTyLoweringContext<'_, '_> {
181 #[inline]
182 fn deref_mut(&mut self) -> &mut Self::Target {
183 &mut self.ctx
184 }
185}
186
187impl Drop for InferenceTyLoweringContext<'_, '_> {
188 #[inline]
189 fn drop(&mut self) {
190 self.diagnostics
191 .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
192 self.defined_anon_consts.borrow_mut().extend(self.ctx.defined_anon_consts.iter().copied());
193 }
194}