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, 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 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}