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