1use std::hash::Hash;
4
5use hir_def::ConstParamId;
6use macros::{TypeFoldable, TypeVisitable};
7use rustc_ast_ir::visit::VisitorResult;
8use rustc_type_ir::{
9 BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags, InferConst,
10 TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
11 WithCachedTypeInfo,
12 inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike},
13 relate::Relate,
14};
15
16use crate::{
17 MemoryMap,
18 next_solver::{ClauseKind, ParamEnv, interner::InternedWrapperNoDebug},
19};
20
21use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty};
22
23pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
24pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
25
26#[salsa::interned(constructor = new_)]
27pub struct Const<'db> {
28 #[returns(ref)]
29 kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>,
30}
31
32impl<'db> Const<'db> {
33 pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self {
34 let flags = FlagComputation::for_const_kind(&kind);
35 let cached = WithCachedTypeInfo {
36 internee: kind,
37 flags: flags.flags,
38 outer_exclusive_binder: flags.outer_exclusive_binder,
39 };
40 Const::new_(interner.db(), InternedWrapperNoDebug(cached))
41 }
42
43 pub fn inner(&self) -> &WithCachedTypeInfo<ConstKind<'db>> {
44 crate::with_attached_db(|db| {
45 let inner = &self.kind_(db).0;
46 unsafe { std::mem::transmute(inner) }
49 })
50 }
51
52 pub fn error(interner: DbInterner<'db>) -> Self {
53 Const::new(interner, ConstKind::Error(ErrorGuaranteed))
54 }
55
56 pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self {
57 Const::new(interner, ConstKind::Param(param))
58 }
59
60 pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self {
61 Const::new(interner, ConstKind::Placeholder(placeholder))
62 }
63
64 pub fn new_bound(interner: DbInterner<'db>, index: DebruijnIndex, bound: BoundConst) -> Self {
65 Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound))
66 }
67
68 pub fn new_valtree(
69 interner: DbInterner<'db>,
70 ty: Ty<'db>,
71 memory: Box<[u8]>,
72 memory_map: MemoryMap<'db>,
73 ) -> Self {
74 Const::new(
75 interner,
76 ConstKind::Value(ValueConst {
77 ty,
78 value: Valtree::new(ConstBytes { memory, memory_map }),
79 }),
80 )
81 }
82
83 pub fn is_ct_infer(&self) -> bool {
84 matches!(self.kind(), ConstKind::Infer(_))
85 }
86
87 pub fn is_error(&self) -> bool {
88 matches!(self.kind(), ConstKind::Error(_))
89 }
90
91 pub fn is_trivially_wf(self) -> bool {
92 match self.kind() {
93 ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true,
94 ConstKind::Infer(_)
95 | ConstKind::Unevaluated(..)
96 | ConstKind::Value(_)
97 | ConstKind::Error(_)
98 | ConstKind::Expr(_) => false,
99 }
100 }
101}
102
103impl<'db> std::fmt::Debug for Const<'db> {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 self.inner().internee.fmt(f)
106 }
107}
108
109impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 self.0.internee.fmt(f)
112 }
113}
114
115pub type PlaceholderConst = Placeholder<BoundConst>;
116
117#[derive(Copy, Clone, Hash, Eq, PartialEq)]
118pub struct ParamConst {
119 pub id: ConstParamId,
121 pub index: u32,
122}
123
124impl std::fmt::Debug for ParamConst {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(f, "#{}", self.index)
127 }
128}
129
130impl ParamConst {
131 pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> {
132 let mut candidates = env.caller_bounds().iter().filter_map(|clause| {
133 match clause.kind().skip_binder() {
135 ClauseKind::ConstArgHasType(param_ct, ty) => {
136 assert!(!(param_ct, ty).has_escaping_bound_vars());
137
138 match param_ct.kind() {
139 ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty),
140 _ => None,
141 }
142 }
143 _ => None,
144 }
145 });
146
147 let ty = candidates.next().unwrap_or_else(|| {
154 panic!("cannot find `{self:?}` in param-env: {env:#?}");
155 });
156 assert!(
157 candidates.next().is_none(),
158 "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
159 );
160 ty
161 }
162}
163
164#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable)]
168pub struct ValueConst<'db> {
169 pub ty: Ty<'db>,
170 #[type_visitable(ignore)]
172 #[type_foldable(identity)]
173 pub value: Valtree<'db>,
174}
175
176impl<'db> ValueConst<'db> {
177 pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self {
178 let value = Valtree::new(bytes);
179 ValueConst { ty, value }
180 }
181}
182
183impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
184 fn ty(self) -> Ty<'db> {
185 self.ty
186 }
187
188 fn valtree(self) -> Valtree<'db> {
189 self.value
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
194pub struct ConstBytes<'db> {
195 pub memory: Box<[u8]>,
196 pub memory_map: MemoryMap<'db>,
197}
198
199impl Hash for ConstBytes<'_> {
200 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
201 self.memory.hash(state)
202 }
203}
204
205#[salsa::interned(constructor = new_, debug)]
206pub struct Valtree<'db> {
207 #[returns(ref)]
208 bytes_: ConstBytes<'db>,
209}
210
211impl<'db> Valtree<'db> {
212 pub fn new(bytes: ConstBytes<'db>) -> Self {
213 crate::with_attached_db(|db| unsafe {
214 std::mem::transmute(Valtree::new_(db, bytes))
216 })
217 }
218
219 pub fn inner(&self) -> &ConstBytes<'db> {
220 crate::with_attached_db(|db| {
221 let inner = self.bytes_(db);
222 unsafe { std::mem::transmute(inner) }
225 })
226 }
227}
228
229#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)]
230pub struct ExprConst;
231
232impl rustc_type_ir::inherent::ParamLike for ParamConst {
233 fn index(self) -> u32 {
234 self.index
235 }
236}
237
238impl<'db> IntoKind for Const<'db> {
239 type Kind = ConstKind<'db>;
240
241 fn kind(self) -> Self::Kind {
242 self.inner().internee
243 }
244}
245
246impl<'db> TypeVisitable<DbInterner<'db>> for Const<'db> {
247 fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
248 &self,
249 visitor: &mut V,
250 ) -> V::Result {
251 visitor.visit_const(*self)
252 }
253}
254
255impl<'db> TypeSuperVisitable<DbInterner<'db>> for Const<'db> {
256 fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
257 &self,
258 visitor: &mut V,
259 ) -> V::Result {
260 match self.kind() {
261 ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
262 ConstKind::Value(v) => v.visit_with(visitor),
263 ConstKind::Expr(e) => e.visit_with(visitor),
264 ConstKind::Error(e) => e.visit_with(visitor),
265
266 ConstKind::Param(_)
267 | ConstKind::Infer(_)
268 | ConstKind::Bound(..)
269 | ConstKind::Placeholder(_) => V::Result::output(),
270 }
271 }
272}
273
274impl<'db> TypeFoldable<DbInterner<'db>> for Const<'db> {
275 fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
276 self,
277 folder: &mut F,
278 ) -> Result<Self, F::Error> {
279 folder.try_fold_const(self)
280 }
281 fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
282 folder.fold_const(self)
283 }
284}
285
286impl<'db> TypeSuperFoldable<DbInterner<'db>> for Const<'db> {
287 fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
288 self,
289 folder: &mut F,
290 ) -> Result<Self, F::Error> {
291 let kind = match self.kind() {
292 ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
293 ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
294 ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
295
296 ConstKind::Param(_)
297 | ConstKind::Infer(_)
298 | ConstKind::Bound(..)
299 | ConstKind::Placeholder(_)
300 | ConstKind::Error(_) => return Ok(self),
301 };
302 if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) }
303 }
304 fn super_fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(
305 self,
306 folder: &mut F,
307 ) -> Self {
308 let kind = match self.kind() {
309 ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)),
310 ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)),
311 ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)),
312
313 ConstKind::Param(_)
314 | ConstKind::Infer(_)
315 | ConstKind::Bound(..)
316 | ConstKind::Placeholder(_)
317 | ConstKind::Error(_) => return self,
318 };
319 if kind != self.kind() { Const::new(folder.cx(), kind) } else { self }
320 }
321}
322
323impl<'db> Relate<DbInterner<'db>> for Const<'db> {
324 fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
325 relation: &mut R,
326 a: Self,
327 b: Self,
328 ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
329 relation.consts(a, b)
330 }
331}
332
333impl<'db> Flags for Const<'db> {
334 fn flags(&self) -> rustc_type_ir::TypeFlags {
335 self.inner().flags
336 }
337
338 fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
339 self.inner().outer_exclusive_binder
340 }
341}
342
343impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
344 fn new_infer(interner: DbInterner<'db>, var: InferConst) -> Self {
345 Const::new(interner, ConstKind::Infer(var))
346 }
347
348 fn new_var(interner: DbInterner<'db>, var: ConstVid) -> Self {
349 Const::new(interner, ConstKind::Infer(InferConst::Var(var)))
350 }
351
352 fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst) -> Self {
353 Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
354 }
355
356 fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self {
357 Const::new(
358 interner,
359 ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst { var }),
360 )
361 }
362
363 fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self {
364 Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst { var }))
365 }
366
367 fn new_placeholder(
368 interner: DbInterner<'db>,
369 param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst,
370 ) -> Self {
371 Const::new(interner, ConstKind::Placeholder(param))
372 }
373
374 fn new_unevaluated(
375 interner: DbInterner<'db>,
376 uv: rustc_type_ir::UnevaluatedConst<DbInterner<'db>>,
377 ) -> Self {
378 Const::new(interner, ConstKind::Unevaluated(uv))
379 }
380
381 fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self {
382 Const::new(interner, ConstKind::Expr(expr))
383 }
384
385 fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
386 Const::new(interner, ConstKind::Error(guar))
387 }
388}
389
390#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
391pub struct BoundConst {
392 pub var: BoundVar,
393}
394
395impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundConst {
396 fn var(self) -> BoundVar {
397 self.var
398 }
399
400 fn assert_eq(self, var: BoundVarKind) {
401 var.expect_const()
402 }
403}
404
405impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst {
406 type Bound = BoundConst;
407
408 fn universe(self) -> rustc_type_ir::UniverseIndex {
409 self.universe
410 }
411
412 fn var(self) -> rustc_type_ir::BoundVar {
413 self.bound.var
414 }
415
416 fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
417 Placeholder { universe: ui, bound: self.bound }
418 }
419
420 fn new(ui: rustc_type_ir::UniverseIndex, var: BoundConst) -> Self {
421 Placeholder { universe: ui, bound: var }
422 }
423 fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
424 Placeholder { universe: ui, bound: BoundConst { var } }
425 }
426}
427
428impl<'db> Relate<DbInterner<'db>> for ExprConst {
429 fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
430 _relation: &mut R,
431 a: Self,
432 b: Self,
433 ) -> rustc_type_ir::relate::RelateResult<DbInterner<'db>, Self> {
434 let ExprConst = b;
436 Ok(a)
437 }
438}
439
440impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst {
441 fn args(self) -> <DbInterner<'db> as rustc_type_ir::Interner>::GenericArgs {
442 let ExprConst = self;
444 GenericArgs::default()
445 }
446}