1use super::{
2 translate_crate::TransItemSourceKind, translate_ctx::*, translate_generics::BindingLevel,
3};
4
5use charon_lib::ids::Vector;
6use charon_lib::ullbc_ast::*;
7use itertools::Itertools;
8
9fn dummy_public_attr_info() -> AttrInfo {
10 AttrInfo {
11 public: true,
12 ..Default::default()
13 }
14}
15
16fn usize_ty() -> Ty {
17 Ty::new(TyKind::Literal(LiteralTy::UInt(UIntTy::Usize)))
18}
19
20fn dynify<T: TyVisitable>(mut x: T, new_self: Option<Ty>, for_method: bool) -> T {
28 struct ReplaceSelfVisitor {
29 new_self: Option<Ty>,
30 for_method: bool,
31 }
32 impl VarsVisitor for ReplaceSelfVisitor {
33 fn visit_type_var(&mut self, v: TypeDbVar) -> Option<Ty> {
34 if let DeBruijnVar::Bound(DeBruijnId::ZERO, type_id) = v {
35 Some(if let Some(new_id) = type_id.index().checked_sub(1) {
37 TyKind::TypeVar(DeBruijnVar::Bound(DeBruijnId::ZERO, TypeVarId::new(new_id)))
38 .into_ty()
39 } else {
40 self.new_self.clone().expect(
41 "Found unexpected `Self`
42 type when constructing vtable",
43 )
44 })
45 } else {
46 None
47 }
48 }
49
50 fn visit_clause_var(&mut self, v: ClauseDbVar) -> Option<TraitRefKind> {
51 if let DeBruijnVar::Bound(DeBruijnId::ZERO, clause_id) = v {
52 if self.for_method && clause_id == TraitClauseId::ZERO {
53 Some(TraitRefKind::Dyn)
55 } else {
56 panic!("Found unexpected clause var when constructing vtable: {v}")
57 }
58 } else {
59 None
60 }
61 }
62
63 fn visit_self_clause(&mut self) -> Option<TraitRefKind> {
64 Some(TraitRefKind::Dyn)
65 }
66 }
67 x.visit_vars(&mut ReplaceSelfVisitor {
68 new_self,
69 for_method,
70 });
71 x
72}
73
74impl ItemTransCtx<'_, '_> {
76 pub fn check_at_most_one_pred_has_methods(
77 &mut self,
78 span: Span,
79 preds: &hax::GenericPredicates,
80 ) -> Result<(), Error> {
81 for (clause, _) in preds.predicates.iter().skip(1) {
83 if let hax::ClauseKind::Trait(trait_predicate) = clause.kind.hax_skip_binder_ref() {
84 let trait_def_id = &trait_predicate.trait_ref.def_id;
85 let trait_def = self.poly_hax_def(trait_def_id)?;
86 let has_methods = match trait_def.kind() {
87 hax::FullDefKind::Trait { items, .. } => items
88 .iter()
89 .any(|assoc| matches!(assoc.kind, hax::AssocKind::Fn { .. })),
90 hax::FullDefKind::TraitAlias { .. } => false,
91 _ => unreachable!(),
92 };
93 if has_methods {
94 raise_error!(
95 self,
96 span,
97 "`dyn Trait` with multiple method-bearing predicates is not supported"
98 );
99 }
100 }
101 }
102 Ok(())
103 }
104
105 pub fn translate_existential_predicates(
106 &mut self,
107 span: Span,
108 self_ty: &hax::ParamTy,
109 preds: &hax::GenericPredicates,
110 region: &hax::Region,
111 ) -> Result<DynPredicate, Error> {
112 self.check_at_most_one_pred_has_methods(span, preds)?;
116
117 let region = self.translate_region(span, region)?;
119 let region = region.move_under_binder();
120
121 self.binding_levels.push(BindingLevel::new(true));
123
124 let ty_id = self
126 .innermost_binder_mut()
127 .push_type_var(self_ty.index, self_ty.name.clone());
128 let ty = TyKind::TypeVar(DeBruijnVar::new_at_zero(ty_id)).into_ty();
129
130 self.innermost_binder_mut()
131 .params
132 .types_outlive
133 .push(RegionBinder::empty(OutlivesPred(ty.clone(), region)));
134 self.register_predicates(preds, PredicateOrigin::Dyn)?;
135
136 let params = self.binding_levels.pop().unwrap().params;
137 let binder = Binder {
138 params: params,
139 skip_binder: ty,
140 kind: BinderKind::Dyn,
141 };
142 Ok(DynPredicate { binder })
143 }
144}
145
146impl ItemTransCtx<'_, '_> {
148 pub fn trait_is_dyn_compatible(&mut self, def_id: &hax::DefId) -> Result<bool, Error> {
152 let def = self.poly_hax_def(def_id)?;
153 Ok(match def.kind() {
154 hax::FullDefKind::Trait { dyn_self, .. }
155 | hax::FullDefKind::TraitAlias { dyn_self, .. } => dyn_self.is_some(),
156 _ => false,
157 })
158 }
159
160 fn pred_is_for_self(&self, tref: &hax::TraitRef) -> bool {
162 let first_ty = tref
163 .generic_args
164 .iter()
165 .filter_map(|arg| match arg {
166 hax::GenericArg::Type(ty) => Some(ty),
167 _ => None,
168 })
169 .next();
170 match first_ty {
171 None => false,
172 Some(first_ty) => match first_ty.kind() {
173 hax::TyKind::Param(param_ty) if param_ty.index == 0 => {
174 assert_eq!(param_ty.name, "Self");
175 true
176 }
177 _ => false,
178 },
179 }
180 }
181
182 pub fn translate_vtable_struct_ref(
184 &mut self,
185 span: Span,
186 tref: &hax::TraitRef,
187 ) -> Result<Option<TypeDeclRef>, Error> {
188 if !self.trait_is_dyn_compatible(&tref.def_id)? {
189 return Ok(None);
190 }
191 let mut vtable_ref: TypeDeclRef =
194 self.translate_item_no_enqueue(span, tref, TransItemSourceKind::VTable)?;
195 vtable_ref
197 .generics
198 .types
199 .remove_and_shift_ids(TypeVarId::ZERO);
200
201 let assoc_tys: Vec<_> = tref
203 .trait_associated_types(&self.hax_state_with_id())
204 .iter()
205 .map(|ty| self.translate_ty(span, ty))
206 .try_collect()?;
207 vtable_ref.generics.types.extend(assoc_tys);
208
209 Ok(Some(vtable_ref))
210 }
211
212 fn add_method_to_vtable_def(
214 &mut self,
215 span: Span,
216 trait_def: &hax::FullDef,
217 mut mk_field: impl FnMut(String, Ty),
218 item: &hax::AssocItem,
219 ) -> Result<(), Error> {
220 let item_def_id = &item.def_id;
221 let item_def = self.hax_def(
222 &trait_def
223 .this()
224 .with_def_id(&self.t_ctx.hax_state, item_def_id),
225 )?;
226 let hax::FullDefKind::AssocFn {
227 sig,
228 vtable_safe: true,
229 ..
230 } = item_def.kind()
231 else {
232 return Ok(());
233 };
234
235 let item_name = self.t_ctx.translate_trait_item_name(item_def_id)?;
236 let sig = self.translate_fun_sig(span, sig)?;
239 let ty = TyKind::FnPtr(sig).into_ty();
240
241 mk_field(format!("method_{}", item_name.0), ty);
242 Ok(())
243 }
244
245 fn add_supertraits_to_vtable_def(
247 &mut self,
248 span: Span,
249 mut mk_field: impl FnMut(String, Ty),
250 implied_predicates: &hax::GenericPredicates,
251 ) -> Result<(), Error> {
252 let mut counter = (0..).into_iter();
253 for (clause, _span) in &implied_predicates.predicates {
254 if let hax::ClauseKind::Trait(pred) = clause.kind.hax_skip_binder_ref() {
255 if !self.pred_is_for_self(&pred.trait_ref) {
257 continue;
258 }
259 let vtbl_struct = self
260 .translate_region_binder(span, &clause.kind, |ctx, _| {
261 ctx.translate_vtable_struct_ref(span, &pred.trait_ref)
262 })?
263 .erase()
264 .expect("parent trait should be dyn compatible");
265 let ty = Ty::new(TyKind::Ref(
266 Region::Static,
267 Ty::new(TyKind::Adt(vtbl_struct)),
268 RefKind::Shared,
269 ));
270 mk_field(format!("super_trait_{}", counter.next().unwrap()), ty);
271 }
272 }
273 Ok(())
274 }
275
276 fn gen_vtable_struct_fields(
277 &mut self,
278 span: Span,
279 trait_def: &hax::FullDef,
280 implied_predicates: &hax::GenericPredicates,
281 ) -> Result<Vector<FieldId, Field>, Error> {
282 let mut fields = Vector::new();
283 let mut mk_field = |name, ty| {
284 fields.push(Field {
285 span,
286 attr_info: dummy_public_attr_info(),
287 name: Some(name),
288 ty,
289 });
290 };
291
292 mk_field("size".into(), usize_ty());
295 mk_field("align".into(), usize_ty());
297 mk_field("drop".into(), {
299 let self_ty = TyKind::TypeVar(DeBruijnVar::new_at_zero(TypeVarId::ZERO)).into_ty();
300 let self_ptr = TyKind::RawPtr(self_ty, RefKind::Mut).into_ty();
301 Ty::new(TyKind::FnPtr(RegionBinder::empty((
302 [self_ptr].into(),
303 Ty::mk_unit(),
304 ))))
305 });
306
307 if let hax::FullDefKind::Trait { items, .. } = trait_def.kind() {
309 for item in items {
310 self.add_method_to_vtable_def(span, trait_def, &mut mk_field, item)?;
311 }
312 }
313
314 self.add_supertraits_to_vtable_def(span, &mut mk_field, implied_predicates)?;
316
317 Ok(fields)
318 }
319
320 pub(crate) fn translate_vtable_struct(
336 mut self,
337 type_id: TypeDeclId,
338 item_meta: ItemMeta,
339 trait_def: &hax::FullDef,
340 ) -> Result<TypeDecl, Error> {
341 let span = item_meta.span;
342 if !self.trait_is_dyn_compatible(trait_def.def_id())? {
343 raise_error!(
344 self,
345 span,
346 "Trying to compute the vtable type \
347 for a non-dyn-compatible trait"
348 );
349 }
350
351 self.translate_def_generics(span, trait_def)?;
352
353 let (hax::FullDefKind::Trait {
354 dyn_self,
355 implied_predicates,
356 ..
357 }
358 | hax::FullDefKind::TraitAlias {
359 dyn_self,
360 implied_predicates,
361 ..
362 }) = trait_def.kind()
363 else {
364 panic!()
365 };
366 let Some(dyn_self) = dyn_self else {
367 panic!("Trying to generate a vtable for a non-dyn-compatible trait")
368 };
369
370 let mut dyn_self = {
372 let dyn_self = self.translate_ty(span, dyn_self)?;
373 let TyKind::DynTrait(mut dyn_pred) = dyn_self.kind().clone() else {
374 panic!("incorrect `dyn_self`")
375 };
376
377 for (i, ty_constraint) in dyn_pred
380 .binder
381 .params
382 .trait_type_constraints
383 .iter_mut()
384 .enumerate()
385 {
386 let name = format!("Ty{i}");
387 let new_ty = self
388 .the_only_binder_mut()
389 .params
390 .types
391 .push_with(|index| TypeVar { index, name });
392 let new_ty =
395 TyKind::TypeVar(DeBruijnVar::bound(DeBruijnId::new(2), new_ty)).into_ty();
396 ty_constraint.skip_binder.ty = new_ty;
397 }
398 TyKind::DynTrait(dyn_pred).into_ty()
399 };
400
401 let fields = self.gen_vtable_struct_fields(span, trait_def, implied_predicates)?;
404 let mut kind = TypeDeclKind::Struct(fields);
405 let layout = self.generate_naive_layout(span, &kind)?;
406
407 let mut generics = self.into_generics();
410 {
411 dyn_self = dynify(dyn_self, None, false);
412 generics = dynify(generics, Some(dyn_self.clone()), false);
413 kind = dynify(kind, Some(dyn_self.clone()), true);
414 generics.types.remove_and_shift_ids(TypeVarId::ZERO);
415 generics.types.iter_mut().for_each(|ty| {
416 ty.index -= 1;
417 });
418 }
419
420 let dyn_predicate = dyn_self
421 .kind()
422 .as_dyn_trait()
423 .expect("incorrect `dyn_self`");
424 Ok(TypeDecl {
425 def_id: type_id,
426 item_meta: item_meta,
427 generics: generics,
428 src: ItemKind::VTableTy {
429 dyn_predicate: dyn_predicate.clone(),
430 },
431 kind,
432 layout: Some(layout),
433 ptr_metadata: None,
434 })
435 }
436}
437
438impl ItemTransCtx<'_, '_> {
440 pub fn translate_vtable_instance_ref(
441 &mut self,
442 span: Span,
443 trait_ref: &hax::TraitRef,
444 impl_ref: &hax::ItemRef,
445 ) -> Result<Option<GlobalDeclRef>, Error> {
446 if !self.trait_is_dyn_compatible(&trait_ref.def_id)? {
447 return Ok(None);
448 }
449 let vtable_ref: GlobalDeclRef = self.translate_item_no_enqueue(
454 span,
455 impl_ref,
456 TransItemSourceKind::VTableInstance(TraitImplSource::Normal),
457 )?;
458 Ok(Some(vtable_ref))
459 }
460
461 fn get_vtable_instance_info<'a>(
463 &mut self,
464 span: Span,
465 impl_def: &'a hax::FullDef,
466 impl_kind: &TraitImplSource,
467 ) -> Result<(TraitImplRef, TraitDeclRef, TypeDeclRef), Error> {
468 let implemented_trait = match impl_def.kind() {
469 hax::FullDefKind::TraitImpl { trait_pred, .. } => &trait_pred.trait_ref,
470 _ => unreachable!(),
471 };
472 let vtable_struct_ref = self
473 .translate_vtable_struct_ref(span, implemented_trait)?
474 .expect("trait should be dyn-compatible");
475 let implemented_trait = self.translate_trait_decl_ref(span, implemented_trait)?;
476 let impl_ref = self.translate_item(
477 span,
478 impl_def.this(),
479 TransItemSourceKind::TraitImpl(*impl_kind),
480 )?;
481 Ok((impl_ref, implemented_trait, vtable_struct_ref))
482 }
483
484 pub(crate) fn translate_vtable_instance(
497 mut self,
498 global_id: GlobalDeclId,
499 item_meta: ItemMeta,
500 impl_def: &hax::FullDef,
501 impl_kind: &TraitImplSource,
502 ) -> Result<GlobalDecl, Error> {
503 let span = item_meta.span;
504 self.translate_def_generics(span, impl_def)?;
505
506 let (impl_ref, _, vtable_struct_ref) =
507 self.get_vtable_instance_info(span, impl_def, impl_kind)?;
508 let init = self.register_item(
510 span,
511 impl_def.this(),
512 TransItemSourceKind::VTableInstanceInitializer(*impl_kind),
513 );
514
515 Ok(GlobalDecl {
516 def_id: global_id,
517 item_meta,
518 generics: self.into_generics(),
519 kind: ItemKind::VTableInstance { impl_ref },
520 global_kind: GlobalKind::Static,
522 ty: Ty::new(TyKind::Adt(vtable_struct_ref)),
523 init,
524 })
525 }
526
527 fn add_method_to_vtable_value(
528 &mut self,
529 span: Span,
530 impl_def: &hax::FullDef,
531 item: &hax::ImplAssocItem,
532 mut mk_field: impl FnMut(ConstantExprKind),
533 ) -> Result<(), Error> {
534 match self.poly_hax_def(&item.decl_def_id)?.kind() {
536 hax::FullDefKind::AssocFn {
537 vtable_safe: true, ..
538 } => {}
539 _ => return Ok(()),
540 }
541
542 let const_kind = match &item.value {
543 hax::ImplAssocItemValue::Provided {
544 def_id: item_def_id,
545 ..
546 } => {
547 let item_ref = impl_def.this().with_def_id(self.hax_state(), item_def_id);
550 let shim_ref =
551 self.translate_item(span, &item_ref, TransItemSourceKind::VTableMethod)?;
552 ConstantExprKind::FnPtr(shim_ref)
553 }
554 hax::ImplAssocItemValue::DefaultedFn { .. } => ConstantExprKind::Opaque(
555 "shim for provided methods \
556 aren't yet supported"
557 .to_string(),
558 ),
559 _ => return Ok(()),
560 };
561
562 mk_field(const_kind);
563
564 Ok(())
565 }
566
567 fn add_supertraits_to_vtable_value(
568 &mut self,
569 span: Span,
570 trait_def: &hax::FullDef,
571 impl_def: &hax::FullDef,
572 mut mk_field: impl FnMut(ConstantExprKind),
573 ) -> Result<(), Error> {
574 let hax::FullDefKind::TraitImpl {
575 implied_impl_exprs, ..
576 } = impl_def.kind()
577 else {
578 unreachable!()
579 };
580 let hax::FullDefKind::Trait {
581 implied_predicates, ..
582 } = trait_def.kind()
583 else {
584 unreachable!()
585 };
586 for ((clause, _), impl_expr) in implied_predicates.predicates.iter().zip(implied_impl_exprs)
587 {
588 let hax::ClauseKind::Trait(pred) = clause.kind.hax_skip_binder_ref() else {
589 continue;
590 };
591 if !self.pred_is_for_self(&pred.trait_ref) {
593 continue;
594 }
595
596 let vtable_def_ref = self
597 .translate_region_binder(span, &impl_expr.r#trait, |ctx, tref| {
598 ctx.translate_vtable_struct_ref(span, tref)
599 })?
600 .erase()
601 .expect("parent trait should be dyn compatible");
602 let fn_ptr_ty = TyKind::Adt(vtable_def_ref).into_ty();
603 let kind = match &impl_expr.r#impl {
604 hax::ImplExprAtom::Concrete(impl_item) => {
605 let vtable_instance_ref = self
606 .translate_region_binder(span, &impl_expr.r#trait, |ctx, tref| {
607 ctx.translate_vtable_instance_ref(span, tref, impl_item)
608 })?
609 .erase()
610 .expect("parent trait should be dyn compatible");
611 let global = Box::new(ConstantExpr {
612 value: ConstantExprKind::Global(vtable_instance_ref),
613 ty: fn_ptr_ty,
614 });
615 ConstantExprKind::Ref(global)
616 }
617 _ => ConstantExprKind::Opaque("missing supertrait vtable".into()),
619 };
620 mk_field(kind);
621 }
622 Ok(())
623 }
624
625 fn gen_vtable_instance_init_body(
633 &mut self,
634 span: Span,
635 impl_def: &hax::FullDef,
636 vtable_struct_ref: TypeDeclRef,
637 ) -> Result<Body, Error> {
638 let mut locals = Locals {
639 arg_count: 0,
640 locals: Vector::new(),
641 };
642 let ret_ty = Ty::new(TyKind::Adt(vtable_struct_ref.clone()));
643 let ret_place = locals.new_var(Some("ret".into()), ret_ty.clone());
644
645 let hax::FullDefKind::TraitImpl {
646 trait_pred, items, ..
647 } = impl_def.kind()
648 else {
649 unreachable!()
650 };
651 let trait_def = self.hax_def(&trait_pred.trait_ref)?;
652
653 let field_tys = {
656 let vtable_decl_id = vtable_struct_ref.id.as_adt().unwrap().clone();
657 let AnyTransItem::Type(vtable_def) =
658 self.t_ctx.get_or_translate(vtable_decl_id.into())?
659 else {
660 unreachable!()
661 };
662 let TypeDeclKind::Struct(fields) = &vtable_def.kind else {
663 unreachable!()
664 };
665 fields
666 .iter()
667 .map(|f| &f.ty)
668 .cloned()
669 .map(|ty| ty.substitute(&vtable_struct_ref.generics))
670 .collect_vec()
671 };
672
673 let mut statements = vec![];
674 let mut aggregate_fields = vec![];
675 let mut field_ty_iter = field_tys.into_iter();
677 let mut mk_field = |kind| {
678 let ty = field_ty_iter.next().unwrap();
679 aggregate_fields.push(Operand::Const(Box::new(ConstantExpr { value: kind, ty })));
680 };
681
682 mk_field(ConstantExprKind::Opaque("unknown size".to_string()));
684 mk_field(ConstantExprKind::Opaque("unknown align".to_string()));
685 mk_field(ConstantExprKind::Opaque("unknown drop".to_string()));
686
687 for item in items {
688 self.add_method_to_vtable_value(span, impl_def, item, &mut mk_field)?;
689 }
690
691 self.add_supertraits_to_vtable_value(span, &trait_def, impl_def, &mut mk_field)?;
692
693 if field_ty_iter.next().is_some() {
694 raise_error!(
695 self,
696 span,
697 "Missed some fields in vtable value construction"
698 )
699 }
700
701 statements.push(Statement::new(
703 span,
704 StatementKind::Assign(
705 ret_place,
706 Rvalue::Aggregate(
707 AggregateKind::Adt(vtable_struct_ref.clone(), None, None),
708 aggregate_fields,
709 ),
710 ),
711 ));
712
713 let block = BlockData {
714 statements,
715 terminator: Terminator::new(span, TerminatorKind::Return),
716 };
717
718 Ok(Body::Unstructured(GExprBody {
719 span,
720 locals,
721 comments: Vec::new(),
722 body: [block].into(),
723 }))
724 }
725
726 pub(crate) fn translate_vtable_instance_init(
727 mut self,
728 init_func_id: FunDeclId,
729 item_meta: ItemMeta,
730 impl_def: &hax::FullDef,
731 impl_kind: &TraitImplSource,
732 ) -> Result<FunDecl, Error> {
733 let span = item_meta.span;
734 self.translate_def_generics(span, impl_def)?;
735
736 let (impl_ref, _, vtable_struct_ref) =
737 self.get_vtable_instance_info(span, impl_def, impl_kind)?;
738 let init_for = self.register_item(
739 span,
740 impl_def.this(),
741 TransItemSourceKind::VTableInstance(*impl_kind),
742 );
743
744 let sig = FunSig {
746 is_unsafe: false,
747 generics: self.the_only_binder().params.clone(),
748 inputs: vec![],
749 output: Ty::new(TyKind::Adt(vtable_struct_ref.clone())),
750 };
751
752 let body = match impl_kind {
753 TraitImplSource::Normal => {
754 let body = self.gen_vtable_instance_init_body(span, impl_def, vtable_struct_ref)?;
755 Ok(body)
756 }
757 _ => {
758 raise_error!(
759 self,
760 span,
761 "Don't know how to generate a vtable for a virtual impl {impl_kind:?}"
762 );
763 }
764 };
765
766 Ok(FunDecl {
767 def_id: init_func_id,
768 item_meta: item_meta,
769 signature: sig,
770 kind: ItemKind::VTableInstance { impl_ref },
771 is_global_initializer: Some(init_for),
772 body,
773 })
774 }
775
776 }