1use itertools::Itertools;
18use rustc_middle::ty::TyCtxt;
19use std::cell::RefCell;
20use std::path::PathBuf;
21
22use super::translate_ctx::*;
23use charon_lib::ast::*;
24use charon_lib::options::{CliOpts, TranslateOptions};
25use charon_lib::transform::TransformCtx;
26use hax::SInto;
27use macros::VariantIndexArity;
28
29#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct TransItemSource {
34 pub item: RustcItem,
35 pub kind: TransItemSourceKind,
36}
37
38#[derive(Clone, Debug, PartialEq, Eq, Hash)]
41pub enum RustcItem {
42 Poly(hax::DefId),
43 Mono(hax::ItemRef),
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
48pub enum TransItemSourceKind {
49 Global,
50 TraitDecl,
51 TraitImpl(TraitImplSource),
52 Fun,
53 Type,
54 InherentImpl,
56 Module,
58 DefaultedMethod(TraitImplSource, TraitItemName),
61 ClosureMethod(ClosureKind),
63 ClosureAsFnCast,
65 EmptyDropMethod,
68 DropInPlaceMethod(Option<TraitImplSource>),
74 VTable,
76 VTableInstance(TraitImplSource),
78 VTableInstanceInitializer(TraitImplSource),
80 VTableMethod,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, VariantIndexArity)]
88pub enum TraitImplSource {
89 Normal,
91 TraitAlias,
93 Closure(ClosureKind),
95 ImplicitDrop,
98}
99
100impl TransItemSource {
101 pub fn new(item: RustcItem, kind: TransItemSourceKind) -> Self {
102 if let RustcItem::Mono(item) = &item {
103 if item.has_param {
104 panic!("Item is not monomorphic: {item:?}")
105 }
106 }
107 Self { item, kind }
108 }
109
110 pub fn from_item(item: &hax::ItemRef, kind: TransItemSourceKind, monomorphize: bool) -> Self {
113 if monomorphize {
114 Self::monomorphic(item, kind)
115 } else {
116 Self::polymorphic(&item.def_id, kind)
117 }
118 }
119
120 pub fn polymorphic(def_id: &hax::DefId, kind: TransItemSourceKind) -> Self {
122 Self::new(RustcItem::Poly(def_id.clone()), kind)
123 }
124
125 pub fn monomorphic(item: &hax::ItemRef, kind: TransItemSourceKind) -> Self {
127 Self::new(RustcItem::Mono(item.clone()), kind)
128 }
129
130 pub fn def_id(&self) -> &hax::DefId {
131 self.item.def_id()
132 }
133
134 pub(crate) fn with_kind(&self, kind: TransItemSourceKind) -> Self {
136 let mut ret = self.clone();
137 ret.kind = kind;
138 ret
139 }
140
141 pub(crate) fn parent(&self) -> Option<Self> {
144 let parent_kind = match self.kind {
145 TransItemSourceKind::ClosureMethod(kind) => {
146 TransItemSourceKind::TraitImpl(TraitImplSource::Closure(kind))
147 }
148 TransItemSourceKind::EmptyDropMethod => {
149 TransItemSourceKind::TraitImpl(TraitImplSource::ImplicitDrop)
150 }
151 TransItemSourceKind::DefaultedMethod(impl_kind, _)
152 | TransItemSourceKind::DropInPlaceMethod(Some(impl_kind))
153 | TransItemSourceKind::VTableInstance(impl_kind)
154 | TransItemSourceKind::VTableInstanceInitializer(impl_kind) => {
155 TransItemSourceKind::TraitImpl(impl_kind)
156 }
157 TransItemSourceKind::DropInPlaceMethod(None) => TransItemSourceKind::TraitDecl,
158 _ => return None,
159 };
160 Some(self.with_kind(parent_kind))
161 }
162
163 pub(crate) fn is_derived_item(&self) -> bool {
166 use TransItemSourceKind::*;
167 match self.kind {
168 Global
169 | TraitDecl
170 | TraitImpl(TraitImplSource::Normal)
171 | InherentImpl
172 | Module
173 | Fun
174 | Type => false,
175 _ => true,
176 }
177 }
178
179 fn sort_key(&self) -> impl Ord + '_ {
181 let item_id = match &self.item {
182 RustcItem::Poly(_) => None,
183 RustcItem::Mono(item) => Some(item.id()),
184 };
185 (self.def_id().index, &self.kind, item_id)
186 }
187}
188
189impl RustcItem {
190 pub fn def_id(&self) -> &hax::DefId {
191 match self {
192 RustcItem::Poly(def_id) => def_id,
193 RustcItem::Mono(item_ref) => &item_ref.def_id,
194 }
195 }
196}
197
198impl PartialOrd for TransItemSource {
200 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
201 Some(self.cmp(other))
202 }
203}
204impl Ord for TransItemSource {
205 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
206 self.sort_key().cmp(&other.sort_key())
207 }
208}
209
210impl<'tcx, 'ctx> TranslateCtx<'tcx> {
211 pub fn base_kind_for_item(&mut self, def_id: &hax::DefId) -> Option<TransItemSourceKind> {
214 use hax::DefKind::*;
215 Some(match &def_id.kind {
216 Enum { .. } | Struct { .. } | Union { .. } | TyAlias { .. } | ForeignTy => {
217 TransItemSourceKind::Type
218 }
219 Fn { .. } | AssocFn { .. } => TransItemSourceKind::Fun,
220 Const { .. } | Static { .. } | AssocConst { .. } => TransItemSourceKind::Global,
221 Trait { .. } | TraitAlias { .. } => TransItemSourceKind::TraitDecl,
222 Impl { of_trait: true } => TransItemSourceKind::TraitImpl(TraitImplSource::Normal),
223 Impl { of_trait: false } => TransItemSourceKind::InherentImpl,
224 Mod { .. } | ForeignMod { .. } => TransItemSourceKind::Module,
225
226 ExternCrate { .. } | GlobalAsm { .. } | Macro { .. } | Use { .. } => return None,
228 Ctor { .. } | Variant { .. } => return None,
231 AnonConst { .. }
233 | AssocTy { .. }
234 | Closure { .. }
235 | ConstParam { .. }
236 | Field { .. }
237 | InlineConst { .. }
238 | PromotedConst { .. }
239 | LifetimeParam { .. }
240 | OpaqueTy { .. }
241 | SyntheticCoroutineBody { .. }
242 | TyParam { .. } => {
243 let span = self.def_span(def_id);
244 register_error!(
245 self,
246 span,
247 "Cannot register item `{def_id:?}` with kind `{:?}`",
248 def_id.kind
249 );
250 return None;
251 }
252 })
253 }
254
255 #[tracing::instrument(skip(self))]
259 pub fn enqueue_module_item(&mut self, def_id: &hax::DefId) {
260 let Some(kind) = self.base_kind_for_item(def_id) else {
261 return;
262 };
263 let item_src = if self.options.monomorphize_with_hax {
264 if let Ok(def) = self.poly_hax_def(def_id)
265 && !def.has_any_generics()
266 {
267 TransItemSource::monomorphic(def.this(), kind)
269 } else {
270 return;
272 }
273 } else {
274 TransItemSource::polymorphic(def_id, kind)
275 };
276 let _: Option<ItemId> = self.register_and_enqueue(&None, item_src);
277 }
278
279 pub(crate) fn register_no_enqueue<T: TryFrom<ItemId>>(
280 &mut self,
281 dep_src: &Option<DepSource>,
282 src: &TransItemSource,
283 ) -> Option<T> {
284 let item_id = match self.id_map.get(src) {
285 Some(tid) => *tid,
286 None => {
287 use TransItemSourceKind::*;
288 let trans_id = match src.kind {
289 Type | VTable => ItemId::Type(self.translated.type_decls.reserve_slot()),
290 TraitDecl => ItemId::TraitDecl(self.translated.trait_decls.reserve_slot()),
291 TraitImpl(..) => ItemId::TraitImpl(self.translated.trait_impls.reserve_slot()),
292 Global | VTableInstance(..) => {
293 ItemId::Global(self.translated.global_decls.reserve_slot())
294 }
295 Fun
296 | DefaultedMethod(..)
297 | ClosureMethod(..)
298 | ClosureAsFnCast
299 | EmptyDropMethod
300 | DropInPlaceMethod(..)
301 | VTableInstanceInitializer(..)
302 | VTableMethod => ItemId::Fun(self.translated.fun_decls.reserve_slot()),
303 InherentImpl | Module => return None,
304 };
305 self.id_map.insert(src.clone(), trans_id);
307 self.reverse_id_map.insert(trans_id, src.clone());
308 if let Ok(name) = self.translate_name(src) {
310 self.translated.item_names.insert(trans_id, name);
311 }
312 trans_id
313 }
314 };
315 self.errors
316 .borrow_mut()
317 .register_dep_source(dep_src, item_id, src.def_id().is_local);
318 item_id.try_into().ok()
319 }
320
321 pub(crate) fn register_and_enqueue<T: TryFrom<ItemId>>(
323 &mut self,
324 dep_src: &Option<DepSource>,
325 item_src: TransItemSource,
326 ) -> Option<T> {
327 let id = self.register_no_enqueue(dep_src, &item_src);
328 self.items_to_translate.insert(item_src);
329 id
330 }
331
332 pub(crate) fn enqueue_id(&mut self, id: impl Into<ItemId>) {
334 let id = id.into();
335 if self.translated.get_item(id).is_none() {
336 let item_src = self.reverse_id_map[&id].clone();
337 self.items_to_translate.insert(item_src);
338 }
339 }
340
341 pub(crate) fn register_target_info(&mut self) {
342 let target_data = &self.tcx.data_layout;
343 self.translated.target_information = krate::TargetInfo {
344 target_pointer_size: target_data.pointer_size().bytes(),
345 is_little_endian: matches!(target_data.endian, rustc_abi::Endian::Little),
346 }
347 }
348}
349
350impl<'tcx, 'ctx> ItemTransCtx<'tcx, 'ctx> {
352 pub(crate) fn make_dep_source(&self, span: Span) -> Option<DepSource> {
353 Some(DepSource {
354 src_id: self.item_id?,
355 span: self.item_src.def_id().is_local.then_some(span),
356 })
357 }
358
359 pub(crate) fn register_and_enqueue<T: TryFrom<ItemId>>(
361 &mut self,
362 span: Span,
363 item_src: TransItemSource,
364 ) -> T {
365 let dep_src = self.make_dep_source(span);
366 self.t_ctx.register_and_enqueue(&dep_src, item_src).unwrap()
367 }
368
369 pub(crate) fn register_no_enqueue<T: TryFrom<ItemId>>(
370 &mut self,
371 span: Span,
372 src: &TransItemSource,
373 ) -> T {
374 let dep_src = self.make_dep_source(span);
375 self.t_ctx.register_no_enqueue(&dep_src, src).unwrap()
376 }
377
378 pub(crate) fn register_item_maybe_enqueue<T: TryFrom<ItemId>>(
380 &mut self,
381 span: Span,
382 enqueue: bool,
383 item: &hax::ItemRef,
384 kind: TransItemSourceKind,
385 ) -> T {
386 let item = if self.monomorphize() && item.has_param {
387 item.erase(self.hax_state_with_id())
388 } else {
389 item.clone()
390 };
391 let item_src = TransItemSource::from_item(&item, kind, self.monomorphize());
392 if enqueue {
393 self.register_and_enqueue(span, item_src)
394 } else {
395 self.register_no_enqueue(span, &item_src)
396 }
397 }
398
399 pub(crate) fn register_item<T: TryFrom<ItemId>>(
401 &mut self,
402 span: Span,
403 item: &hax::ItemRef,
404 kind: TransItemSourceKind,
405 ) -> T {
406 self.register_item_maybe_enqueue(span, true, item, kind)
407 }
408
409 #[expect(dead_code)]
411 pub(crate) fn register_item_no_enqueue<T: TryFrom<ItemId>>(
412 &mut self,
413 span: Span,
414 item: &hax::ItemRef,
415 kind: TransItemSourceKind,
416 ) -> T {
417 self.register_item_maybe_enqueue(span, false, item, kind)
418 }
419
420 pub(crate) fn translate_item_maybe_enqueue<T: TryFrom<DeclRef<ItemId>>>(
422 &mut self,
423 span: Span,
424 enqueue: bool,
425 item: &hax::ItemRef,
426 kind: TransItemSourceKind,
427 ) -> Result<T, Error> {
428 let id: ItemId = self.register_item_maybe_enqueue(span, enqueue, item, kind);
429 let generics = if self.monomorphize() {
430 Ok(GenericArgs::empty())
431 } else {
432 self.translate_generic_args(span, &item.generic_args, &item.impl_exprs)
433 }?;
434 let item = DeclRef {
435 id,
436 generics: Box::new(generics),
437 };
438 Ok(item.try_into().ok().unwrap())
439 }
440
441 pub(crate) fn translate_item<T: TryFrom<DeclRef<ItemId>>>(
447 &mut self,
448 span: Span,
449 item: &hax::ItemRef,
450 kind: TransItemSourceKind,
451 ) -> Result<T, Error> {
452 self.translate_item_maybe_enqueue(span, true, item, kind)
453 }
454
455 #[expect(dead_code)]
457 pub(crate) fn translate_item_no_enqueue<T: TryFrom<DeclRef<ItemId>>>(
458 &mut self,
459 span: Span,
460 item: &hax::ItemRef,
461 kind: TransItemSourceKind,
462 ) -> Result<T, Error> {
463 self.translate_item_maybe_enqueue(span, false, item, kind)
464 }
465
466 pub(crate) fn translate_type_decl_ref(
468 &mut self,
469 span: Span,
470 item: &hax::ItemRef,
471 ) -> Result<TypeDeclRef, Error> {
472 match self.recognize_builtin_type(item)? {
473 Some(id) => {
474 let generics =
475 self.translate_generic_args(span, &item.generic_args, &item.impl_exprs)?;
476 Ok(TypeDeclRef {
477 id: TypeId::Builtin(id),
478 generics: Box::new(generics),
479 })
480 }
481 None => self.translate_item(span, item, TransItemSourceKind::Type),
482 }
483 }
484
485 pub(crate) fn translate_fun_item(
487 &mut self,
488 span: Span,
489 item: &hax::ItemRef,
490 kind: TransItemSourceKind,
491 ) -> Result<MaybeBuiltinFunDeclRef, Error> {
492 match self.recognize_builtin_fun(item)? {
493 Some(id) => {
494 let generics =
495 self.translate_generic_args(span, &item.generic_args, &item.impl_exprs)?;
496 Ok(MaybeBuiltinFunDeclRef {
497 id: FunId::Builtin(id),
498 generics: Box::new(generics),
499 })
500 }
501 None => self.translate_item(span, item, kind),
502 }
503 }
504
505 #[tracing::instrument(skip(self, span))]
508 pub(crate) fn translate_fn_ptr(
509 &mut self,
510 span: Span,
511 item: &hax::ItemRef,
512 kind: TransItemSourceKind,
513 ) -> Result<RegionBinder<FnPtr>, Error> {
514 let fun_item = self.translate_fun_item(span, item, kind)?;
515 let late_bound = match self.hax_def(item)?.kind() {
516 hax::FullDefKind::Fn { sig, .. } | hax::FullDefKind::AssocFn { sig, .. } => {
517 Some(sig.as_ref().rebind(()))
518 }
519 _ => None,
520 };
521 let bound_generics =
522 self.append_late_bound_to_generics(span, *fun_item.generics, late_bound)?;
523 let fun_id = match &item.in_trait {
524 None => FnPtrKind::Fun(fun_item.id),
526 Some(impl_expr) => {
528 let trait_ref = self.translate_trait_impl_expr(span, impl_expr)?;
529 let name = self.t_ctx.translate_trait_item_name(&item.def_id)?;
530 let method_decl_id = *fun_item
531 .id
532 .as_regular()
533 .expect("methods are not builtin functions");
534 self.mark_method_as_used(trait_ref.trait_decl_ref.skip_binder.id, name);
535 FnPtrKind::Trait(trait_ref.move_under_binder(), name, method_decl_id)
536 }
537 };
538 Ok(bound_generics.map(|generics| FnPtr::new(fun_id, generics)))
539 }
540
541 pub(crate) fn translate_global_decl_ref(
542 &mut self,
543 span: Span,
544 item: &hax::ItemRef,
545 ) -> Result<GlobalDeclRef, Error> {
546 self.translate_item(span, item, TransItemSourceKind::Global)
547 }
548
549 pub(crate) fn translate_trait_decl_ref(
550 &mut self,
551 span: Span,
552 item: &hax::ItemRef,
553 ) -> Result<TraitDeclRef, Error> {
554 self.translate_item(span, item, TransItemSourceKind::TraitDecl)
555 }
556
557 pub(crate) fn translate_trait_impl_ref(
558 &mut self,
559 span: Span,
560 item: &hax::ItemRef,
561 kind: TraitImplSource,
562 ) -> Result<TraitImplRef, Error> {
563 self.translate_item(span, item, TransItemSourceKind::TraitImpl(kind))
564 }
565}
566
567#[tracing::instrument(skip(tcx))]
568pub fn translate<'tcx, 'ctx>(
569 options: &CliOpts,
570 tcx: TyCtxt<'tcx>,
571 sysroot: PathBuf,
572) -> TransformCtx {
573 let mut hax_state = hax::state::State::new(
574 tcx,
575 hax::options::Options {
576 inline_anon_consts: true,
577 bounds_options: hax::options::BoundsOptions {
578 resolve_drop: options.add_drop_bounds,
579 prune_sized: false,
580 },
581 },
582 );
583 hax_state.base.silence_resolution_errors = true;
586
587 let crate_def_id: hax::DefId = rustc_span::def_id::CRATE_DEF_ID
588 .to_def_id()
589 .sinto(&hax_state);
590 let crate_name = crate_def_id.krate.clone();
591 trace!("# Crate: {}", crate_name);
592
593 let mut error_ctx = ErrorCtx::new(!options.abort_on_error, options.error_on_warnings);
594 let translate_options = TranslateOptions::new(&mut error_ctx, options);
595 let mut ctx = TranslateCtx {
596 tcx,
597 sysroot,
598 hax_state,
599 options: translate_options,
600 errors: RefCell::new(error_ctx),
601 translated: TranslatedCrate {
602 crate_name,
603 options: options.clone(),
604 ..TranslatedCrate::default()
605 },
606 method_status: Default::default(),
607 id_map: Default::default(),
608 reverse_id_map: Default::default(),
609 file_to_id: Default::default(),
610 items_to_translate: Default::default(),
611 processed: Default::default(),
612 translate_stack: Default::default(),
613 cached_item_metas: Default::default(),
614 cached_names: Default::default(),
615 };
616 ctx.register_target_info();
617
618 if options.start_from.is_empty() {
619 ctx.enqueue_module_item(&crate_def_id);
621 } else {
622 for path in options.start_from.iter() {
624 let path = path.split("::").collect_vec();
625 let resolved = super::resolve_path::def_path_def_ids(&ctx.hax_state, &path);
626 match resolved {
627 Ok(resolved) => {
628 for def_id in resolved {
629 let def_id: hax::DefId = def_id.sinto(&ctx.hax_state);
630 ctx.enqueue_module_item(&def_id);
631 }
632 }
633 Err(path) => {
634 let path = path.join("::");
635 register_error!(
636 ctx,
637 Span::dummy(),
638 "path {path} does not correspond to any item"
639 );
640 }
641 }
642 }
643 }
644
645 trace!(
646 "Queue after we explored the crate:\n{:?}",
647 &ctx.items_to_translate
648 );
649
650 ctx.translate_unit_metadata_const();
651
652 while let Some(item_src) = ctx.items_to_translate.pop_first() {
662 if ctx.processed.insert(item_src.clone()) {
663 ctx.translate_item(&item_src);
664 }
665 }
666
667 ctx.remove_unused_methods();
670
671 TransformCtx {
673 options: ctx.options,
674 translated: ctx.translated,
675 errors: ctx.errors,
676 }
677}