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