1use itertools::Itertools;
3use rustc_middle::mir;
4use rustc_span::RemapPathScopeComponents;
5use std::cmp::Ord;
6use std::path::{Component, PathBuf};
7
8use super::translate_crate::RustcItem;
9use super::translate_ctx::*;
10use super::translate_generics::BindingLevel;
11use charon_lib::ast::*;
12use hax::{DefPathItem, SInto};
13
14impl<'tcx, 'ctx> TranslateCtx<'tcx> {
16 fn register_file(&mut self, filename: FileName, span: rustc_span::Span) -> FileId {
19 match self.file_to_id.get(&filename) {
21 Some(id) => *id,
22 None => {
23 let source_file = self.tcx.sess.source_map().lookup_source_file(span.lo());
24 let crate_name = self.tcx.crate_name(source_file.cnum).to_string();
25 let file = File {
26 name: filename.clone(),
27 crate_name,
28 contents: source_file.src.as_deref().cloned(),
29 };
30 let id = self.translated.files.push(file);
31 self.file_to_id.insert(filename, id);
32 id
33 }
34 }
35 }
36
37 pub fn translate_filename(&mut self, name: rustc_span::FileName) -> meta::FileName {
38 match name {
39 rustc_span::FileName::Real(name) => {
40 match name.local_path() {
41 Some(path) => {
42 let path = if let Ok(path) = path.strip_prefix(&self.sysroot) {
43 if let Ok(path) = path.strip_prefix("lib/rustlib/src/rust") {
48 let mut rewritten_path: PathBuf = "/rustc".into();
49 rewritten_path.extend(path);
50 rewritten_path
51 } else {
52 let mut rewritten_path: PathBuf = "/toolchain".into();
54 rewritten_path.extend(path);
55 rewritten_path
56 }
57 } else {
58 let cargo_home = std::env::var("CARGO_HOME")
62 .map(PathBuf::from)
63 .ok()
64 .or_else(|| std::env::home_dir().map(|p| p.join(".cargo")));
65 if let Some(cargo_home) = cargo_home
66 && let Ok(path) = path.strip_prefix(cargo_home)
67 {
68 let mut rewritten_path: PathBuf = "/cargo".into();
70 rewritten_path.extend(path);
71 rewritten_path
72 } else {
73 path.into()
74 }
75 };
76 FileName::Local(path)
77 }
78 None => {
79 let virtual_name = name.path(RemapPathScopeComponents::MACRO);
83 let mut components_iter = virtual_name.components();
84 if let Some(
85 [
86 Component::RootDir,
87 Component::Normal(rustc),
88 Component::Normal(hash),
89 ],
90 ) = components_iter.by_ref().array_chunks().next()
91 && rustc.to_str() == Some("rustc")
92 && hash.len() == 40
93 {
94 let path_without_hash = [Component::RootDir, Component::Normal(rustc)]
95 .into_iter()
96 .chain(components_iter)
97 .collect();
98 FileName::Virtual(path_without_hash)
99 } else {
100 FileName::Virtual(virtual_name.into())
101 }
102 }
103 }
104 }
105 _ => FileName::NotReal(format!("{name:?}")),
108 }
109 }
110
111 pub fn translate_span_data(&mut self, span: rustc_span::Span) -> meta::SpanData {
112 let smap: &rustc_span::source_map::SourceMap = self.tcx.sess.psess.source_map();
113 let filename = smap.span_to_filename(span);
114 let filename = self.translate_filename(filename);
115 let span = span;
116 let file_id = match &filename {
117 FileName::NotReal(_) => {
118 unimplemented!();
120 }
121 FileName::Virtual(_) | FileName::Local(_) => self.register_file(filename, span),
122 };
123
124 let convert_loc = |pos: rustc_span::BytePos| -> Loc {
125 let loc = smap.lookup_char_pos(pos);
126 Loc {
127 line: loc.line,
128 col: loc.col_display,
129 }
130 };
131 let beg = convert_loc(span.lo());
132 let end = convert_loc(span.hi());
133
134 meta::SpanData { file_id, beg, end }
136 }
137
138 pub fn translate_span_from_source_info(
140 &mut self,
141 source_scopes: &rustc_index::IndexVec<mir::SourceScope, mir::SourceScopeData>,
142 source_info: &mir::SourceInfo,
143 ) -> Span {
144 let data = self.translate_span_data(source_info.span);
146
147 let mut parent_span = None;
149 let mut scope_data = &source_scopes[source_info.scope];
150 while let Some(parent_scope) = scope_data.inlined_parent_scope {
151 scope_data = &source_scopes[parent_scope];
152 parent_span = Some(scope_data.span);
153 }
154
155 if let Some(parent_span) = parent_span {
156 let parent_span = self.translate_span_data(parent_span);
157 Span {
158 data: parent_span,
159 generated_from_span: Some(data),
160 }
161 } else {
162 Span {
163 data,
164 generated_from_span: None,
165 }
166 }
167 }
168
169 pub(crate) fn translate_span(&mut self, span: &rustc_span::Span) -> Span {
170 Span {
171 data: self.translate_span_data(*span),
172 generated_from_span: None,
173 }
174 }
175
176 pub(crate) fn def_span(&mut self, def_id: &hax::DefId) -> Span {
177 let span = def_id.def_span(&self.hax_state);
178 self.translate_span(&span)
179 }
180}
181
182impl<'tcx, 'ctx> TranslateCtx<'tcx> {
184 fn path_elem_for_def(
185 &mut self,
186 span: Span,
187 item: &RustcItem,
188 ) -> Result<Option<PathElem>, Error> {
189 let def_id = item.def_id();
190 let path_elem = def_id.path_item(&self.hax_state);
191 let disambiguator = Disambiguator::new(path_elem.disambiguator as usize);
194 let path_elem = match path_elem.data {
196 DefPathItem::CrateRoot { name, .. } => {
197 error_assert!(self, span, path_elem.disambiguator == 0);
199 Some(PathElem::Ident(name.to_string(), disambiguator))
200 }
201 DefPathItem::TypeNs(symbol)
204 | DefPathItem::ValueNs(symbol)
205 | DefPathItem::MacroNs(symbol) => {
206 Some(PathElem::Ident(symbol.to_string(), disambiguator))
207 }
208 DefPathItem::Impl => {
209 let full_def = self.hax_def_for_item(item)?;
210 let impl_elem = match full_def.kind() {
214 hax::FullDefKind::InherentImpl { ty, .. } => {
216 let item_src =
220 TransItemSource::new(item.clone(), TransItemSourceKind::InherentImpl);
221 let mut bt_ctx = ItemTransCtx::new(item_src, None, self);
222 bt_ctx.translate_item_generics(
223 span,
224 &full_def,
225 &TransItemSourceKind::InherentImpl,
226 )?;
227 let ty = bt_ctx.translate_ty(span, &ty)?;
228 ImplElem::Ty(Binder {
229 kind: BinderKind::InherentImplBlock,
230 params: bt_ctx.into_generics(),
231 skip_binder: ty,
232 })
233 }
234 hax::FullDefKind::TraitImpl { .. } => {
236 let impl_id = {
237 let item_src = TransItemSource::new(
238 item.clone(),
239 TransItemSourceKind::TraitImpl(TraitImplSource::Normal),
240 );
241 self.register_and_enqueue(&None, item_src).unwrap()
242 };
243 ImplElem::Trait(impl_id)
244 }
245 _ => unreachable!(),
246 };
247
248 Some(PathElem::Impl(impl_elem))
249 }
250 DefPathItem::OpaqueTy => None,
252 DefPathItem::Closure => Some(PathElem::Ident("closure".to_string(), disambiguator)),
256 DefPathItem::ForeignMod => None,
259 DefPathItem::Ctor => None,
262 DefPathItem::Use => Some(PathElem::Ident("{use}".to_string(), disambiguator)),
263 DefPathItem::AnonConst => Some(PathElem::Ident("{const}".to_string(), disambiguator)),
264 DefPathItem::PromotedConst => Some(PathElem::Ident(
265 "{promoted_const}".to_string(),
266 disambiguator,
267 )),
268 _ => {
269 raise_error!(
270 self,
271 span,
272 "Unexpected DefPathItem for `{def_id:?}`: {path_elem:?}"
273 );
274 }
275 };
276 Ok(path_elem)
277 }
278
279 fn name_for_item(&mut self, item: &RustcItem) -> Result<Name, Error> {
323 if let Some(name) = self.cached_names.get(item) {
324 return Ok(name.clone());
325 }
326 let def_id = item.def_id();
327 trace!("Computing name for `{def_id:?}`");
328
329 let parent_name = if let Some(parent_id) = def_id.parent(&self.hax_state) {
330 let def = self.hax_def_for_item(item)?;
331 if matches!(item, RustcItem::Mono(..))
332 && let Some(parent_item) = def.typing_parent(&self.hax_state)
333 {
334 self.name_for_item(&RustcItem::Mono(parent_item.clone()))?
335 } else {
336 self.name_for_item(&RustcItem::Poly(parent_id.clone()))?
337 }
338 } else {
339 Name { name: Vec::new() }
340 };
341 let span = self.def_span(def_id);
342 let mut name = parent_name;
343 if let Some(path_elem) = self.path_elem_for_def(span, item)? {
344 name.name.push(path_elem);
345 }
346
347 trace!("Computed name for `{def_id:?}`: `{name:?}`");
348 self.cached_names.insert(item.clone(), name.clone());
349 Ok(name)
350 }
351
352 pub fn name_for_src(&mut self, src: &TransItemSource) -> Result<Name, Error> {
355 let mut name = if let Some(parent) = src.parent() {
356 self.name_for_src(&parent)?
357 } else {
358 self.name_for_item(&src.item)?
359 };
360 match &src.kind {
361 TransItemSourceKind::Type
363 | TransItemSourceKind::Fun
364 | TransItemSourceKind::Global
365 | TransItemSourceKind::TraitImpl(TraitImplSource::Normal)
366 | TransItemSourceKind::TraitDecl
367 | TransItemSourceKind::InherentImpl
368 | TransItemSourceKind::Module => {}
369
370 TransItemSourceKind::TraitImpl(
371 kind @ (TraitImplSource::Closure(..)
372 | TraitImplSource::ImplicitDestruct
373 | TraitImplSource::TraitAlias),
374 ) => {
375 if let TraitImplSource::Closure(..) = kind {
376 let _ = name.name.pop(); }
378 let impl_id = self.register_and_enqueue(&None, src.clone()).unwrap();
379 name.name.push(PathElem::Impl(ImplElem::Trait(impl_id)));
380 }
381 TransItemSourceKind::DefaultedMethod(_, method_name) => {
382 name.name.push(PathElem::Ident(
383 method_name.to_string(),
384 Disambiguator::ZERO,
385 ));
386 }
387 TransItemSourceKind::ClosureMethod(kind) => {
388 let fn_name = kind.method_name().to_string();
389 name.name
390 .push(PathElem::Ident(fn_name, Disambiguator::ZERO));
391 }
392 TransItemSourceKind::DropInPlaceMethod(..) => {
393 name.name.push(PathElem::Ident(
394 "drop_in_place".to_string(),
395 Disambiguator::ZERO,
396 ));
397 }
398 TransItemSourceKind::ClosureAsFnCast => {
399 name.name
400 .push(PathElem::Ident("as_fn".into(), Disambiguator::ZERO));
401 }
402 TransItemSourceKind::VTable
403 | TransItemSourceKind::VTableInstance(..)
404 | TransItemSourceKind::VTableInstanceInitializer(..) => {
405 name.name
406 .push(PathElem::Ident("{vtable}".into(), Disambiguator::ZERO));
407 }
408 TransItemSourceKind::VTableMethod => {
409 name.name.push(PathElem::Ident(
410 "{vtable_method}".into(),
411 Disambiguator::ZERO,
412 ));
413 }
414 TransItemSourceKind::VTableDropShim => {
415 name.name.push(PathElem::Ident(
416 "{vtable_drop_shim}".into(),
417 Disambiguator::ZERO,
418 ));
419 }
420 TransItemSourceKind::VTableDropPreShim => {
421 name.name.push(PathElem::Ident(
422 "{vtable_drop_preshim}".into(),
423 Disambiguator::ZERO,
424 ));
425 }
426 TransItemSourceKind::VTableMethodPreShim(_, method_name) => {
427 name.name.push(PathElem::Ident(
428 method_name.to_string(),
429 Disambiguator::ZERO,
430 ));
431 name.name.push(PathElem::Ident(
432 "{vtable_method_preshim}".into(),
433 Disambiguator::ZERO,
434 ));
435 }
436 }
437 Ok(name)
438 }
439
440 pub fn translate_name(&mut self, src: &TransItemSource) -> Result<Name, Error> {
442 let mut name = self.name_for_src(src)?;
443 if let RustcItem::Mono(item_ref) = &src.item
445 && !item_ref.generic_args.is_empty()
446 {
447 let is_preshim = matches!(
450 src.kind,
451 TransItemSourceKind::VTableDropPreShim
452 | TransItemSourceKind::VTableMethodPreShim(..)
453 );
454
455 let trans_id = self.register_no_enqueue(&None, src).unwrap();
456 let span = self.def_span(&item_ref.def_id);
457 let mut bt_ctx = ItemTransCtx::new(src.clone(), trans_id, self);
458 bt_ctx.binding_levels.push(BindingLevel::new());
459
460 let trait_def = bt_ctx.t_ctx.hax_def_for_item(&src.item)?;
461 let mut assoc_types = None;
462
463 if let hax::FullDefKind::Trait { items, .. } = trait_def.kind() {
465 for item in items {
466 if matches!(item.kind, hax::AssocKind::Type { .. }) {
467 let item_def_id = &item.def_id;
468 let item_def = bt_ctx.hax_def(
470 &trait_def
471 .this()
472 .with_def_id(bt_ctx.hax_state(), item_def_id),
473 )?;
474 if let hax::FullDefKind::AssocTy {
475 implied_predicates, ..
476 } = item_def.kind()
477 {
478 if let Some(pred) = implied_predicates.predicates.first() {
479 if let hax::ClauseKind::Trait(p) = &pred.clause.kind.value {
480 assoc_types = Some(p.trait_ref.generic_args.clone());
481 break;
482 }
483 }
484 }
485 }
486 }
487 }
488 let mut substs = item_ref.generic_args.clone();
490
491 if !(is_preshim && item_ref.generic_args.len() == 1 && matches!(assoc_types, None)) {
492 let args = if is_preshim {
494 if let Some(mut assoc_types) = assoc_types {
495 substs.append(&mut assoc_types);
496 }
497 bt_ctx.translate_generic_args(span, &substs[1..], &item_ref.impl_exprs)?
498 } else {
499 bt_ctx.translate_generic_args(span, &substs, &item_ref.impl_exprs)?
500 };
501 name.name.push(PathElem::Instantiated(Box::new(Binder {
502 params: GenericParams::empty(),
503 skip_binder: args,
504 kind: BinderKind::Other,
505 })));
506 }
507 }
508 Ok(name)
509 }
510
511 pub(crate) fn translate_trait_item_name(
513 &mut self,
514 def_id: &hax::DefId,
515 ) -> Result<TraitItemName, Error> {
516 let def = self.poly_hax_def(def_id)?;
517 let assoc = match def.kind() {
518 hax::FullDefKind::AssocTy {
519 associated_item, ..
520 }
521 | hax::FullDefKind::AssocConst {
522 associated_item, ..
523 }
524 | hax::FullDefKind::AssocFn {
525 associated_item, ..
526 } => associated_item,
527 _ => panic!("Unexpected def for associated item: {def:?}"),
528 };
529 Ok(TraitItemName(
530 assoc
531 .name
532 .as_ref()
533 .map(|n| n.to_string().into())
534 .unwrap_or_default(),
535 ))
536 }
537
538 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
539 self.options.opacity_for_name(&self.translated, name)
540 }
541}
542
543impl<'tcx, 'ctx> TranslateCtx<'tcx> {
545 fn parse_attr_from_raw(
547 &mut self,
548 def_id: &hax::DefId,
549 raw_attr: RawAttribute,
550 ) -> Result<Attribute, String> {
551 let path = raw_attr.path.split("::").collect_vec();
554 let attr_name = if let &[path_start, attr_name] = path.as_slice()
555 && (path_start == "charon" || path_start == "aeneas" || path_start == "verify")
556 {
557 attr_name
558 } else {
559 return Ok(Attribute::Unknown(raw_attr));
560 };
561
562 match self.parse_special_attr(def_id, attr_name, &raw_attr)? {
563 Some(parsed) => Ok(parsed),
564 None => Err(format!(
565 "Unrecognized attribute: `{}`",
566 raw_attr.to_string()
567 )),
568 }
569 }
570
571 fn parse_special_attr(
573 &mut self,
574 def_id: &hax::DefId,
575 attr_name: &str,
576 raw_attr: &RawAttribute,
577 ) -> Result<Option<Attribute>, String> {
578 let args = raw_attr.args.as_deref();
579 let parsed = match attr_name {
580 "opaque" if args.is_none() => Attribute::Opaque,
582 "exclude" if args.is_none() => Attribute::Exclude,
584 "rename" if let Some(attr) = args => {
586 let Some(attr) = attr
587 .strip_prefix("\"")
588 .and_then(|attr| attr.strip_suffix("\""))
589 else {
590 return Err(format!(
591 "the new name should be between quotes: `rename(\"{attr}\")`."
592 ));
593 };
594
595 if attr.is_empty() {
596 return Err(format!("attribute `rename` should not be empty"));
597 }
598
599 let first_char = attr.chars().nth(0).unwrap();
600 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
601 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
602 if !is_identifier {
603 return Err(format!(
604 "attribute `rename` should contain a valid identifier"
605 ));
606 }
607
608 Attribute::Rename(attr.to_string())
609 }
610 "variants_prefix" if let Some(attr) = args => {
612 let Some(attr) = attr
613 .strip_prefix("\"")
614 .and_then(|attr| attr.strip_suffix("\""))
615 else {
616 return Err(format!(
617 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
618 ));
619 };
620
621 Attribute::VariantsPrefix(attr.to_string())
622 }
623 "variants_suffix" if let Some(attr) = args => {
625 let Some(attr) = attr
626 .strip_prefix("\"")
627 .and_then(|attr| attr.strip_suffix("\""))
628 else {
629 return Err(format!(
630 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
631 ));
632 };
633
634 Attribute::VariantsSuffix(attr.to_string())
635 }
636 "start_from" => {
638 if matches!(def_id.kind, hax::DefKind::Mod) {
639 return Err(format!("`start_from` on modules has no effect"));
640 }
641 Attribute::Unknown(raw_attr.clone())
642 }
643 "test" if args.is_none() => Attribute::Unknown(raw_attr.clone()),
645 _ => return Ok(None),
646 };
647 Ok(Some(parsed))
648 }
649
650 pub(crate) fn translate_attribute(
653 &mut self,
654 def_id: &hax::DefId,
655 attr: &rustc_hir::Attribute,
656 ) -> Option<Attribute> {
657 use rustc_hir as hir;
658 use rustc_hir::attrs as hir_attrs;
659 match attr {
660 hir::Attribute::Parsed(hir_attrs::AttributeKind::DocComment { comment, .. }) => {
661 Some(Attribute::DocComment(comment.to_string()))
662 }
663 hir::Attribute::Parsed(_) => None,
664 hir::Attribute::Unparsed(attr) => {
665 let raw_attr = RawAttribute {
666 path: attr.path.to_string(),
667 args: match &attr.args {
668 hir::AttrArgs::Empty => None,
669 hir::AttrArgs::Delimited(args) => {
670 Some(rustc_ast_pretty::pprust::tts_to_string(&args.tokens))
671 }
672 hir::AttrArgs::Eq { expr, .. } => {
673 self.tcx.sess.source_map().span_to_snippet(expr.span).ok()
674 }
675 },
676 };
677 match self.parse_attr_from_raw(def_id, raw_attr) {
678 Ok(a) => Some(a),
679 Err(msg) => {
680 let span = self.translate_span(&attr.span.sinto(&self.hax_state));
681 register_error!(self, span, "Error parsing attribute: {msg}");
682 None
683 }
684 }
685 }
686 }
687 }
688
689 pub(crate) fn translate_inline(&self, def: &hax::FullDef) -> Option<InlineAttr> {
690 match def.kind() {
691 hax::FullDefKind::Fn { inline, .. }
692 | hax::FullDefKind::AssocFn { inline, .. }
693 | hax::FullDefKind::Closure { inline, .. } => match inline {
694 hax::InlineAttr::None => None,
695 hax::InlineAttr::Hint => Some(InlineAttr::Hint),
696 hax::InlineAttr::Never => Some(InlineAttr::Never),
697 hax::InlineAttr::Always => Some(InlineAttr::Always),
698 hax::InlineAttr::Force { .. } => Some(InlineAttr::Always),
699 },
700 _ => None,
701 }
702 }
703
704 pub(crate) fn translate_attr_info(&mut self, def: &hax::FullDef) -> AttrInfo {
705 let public = def.visibility.unwrap_or(false);
707 let inline = self.translate_inline(def);
708 let attributes = def
709 .attributes
710 .iter()
711 .filter_map(|attr| self.translate_attribute(def.def_id(), &attr))
712 .collect_vec();
713
714 let rename = {
715 let mut renames = attributes.iter().filter_map(|a| a.as_rename()).cloned();
716 let rename = renames.next();
717 if renames.next().is_some() {
718 let span = self.translate_span(&def.span);
719 register_error!(
720 self,
721 span,
722 "There should be at most one `charon::rename(\"...\")` \
723 or `aeneas::rename(\"...\")` attribute per declaration",
724 );
725 }
726 rename
727 };
728
729 AttrInfo {
730 attributes,
731 inline,
732 public,
733 rename,
734 }
735 }
736}
737
738impl<'tcx, 'ctx> TranslateCtx<'tcx> {
740 pub(crate) fn is_extern_item(&mut self, def: &hax::FullDef) -> bool {
742 def.def_id()
743 .parent(&self.hax_state)
744 .is_some_and(|parent| matches!(parent.kind, hax::DefKind::ForeignMod { .. }))
745 }
746
747 pub(crate) fn translate_item_meta(
749 &mut self,
750 def: &hax::FullDef,
751 item_src: &TransItemSource,
752 name: Name,
753 name_opacity: ItemOpacity,
754 ) -> ItemMeta {
755 if let Some(item_meta) = self.cached_item_metas.get(&item_src) {
756 return item_meta.clone();
757 }
758 let span = def.source_span.as_ref().unwrap_or(&def.span);
759 let span = self.translate_span(span);
760 let is_local = def.def_id().is_local();
761 let (attr_info, lang_item) = if !item_src.is_derived_item()
762 || matches!(item_src.kind, TransItemSourceKind::ClosureMethod(..))
763 {
764 let attr_info = self.translate_attr_info(def);
765 let lang_item = def
766 .lang_item
767 .clone()
768 .or_else(|| def.diagnostic_item.clone())
769 .map(|s| s.to_string());
770 (attr_info, lang_item)
771 } else {
772 (AttrInfo::default(), None)
773 };
774
775 let opacity = if attr_info.attributes.iter().any(|attr| attr.is_exclude()) {
776 ItemOpacity::Invisible.max(name_opacity)
777 } else if self.is_extern_item(def)
778 || attr_info.attributes.iter().any(|attr| attr.is_opaque())
779 {
780 ItemOpacity::Opaque.max(name_opacity)
782 } else {
783 name_opacity
784 };
785
786 let item_meta = ItemMeta {
787 name,
788 span,
789 source_text: def.source_text.clone(),
790 attr_info,
791 is_local,
792 opacity,
793 lang_item,
794 };
795 self.cached_item_metas
796 .insert(item_src.clone(), item_meta.clone());
797 item_meta
798 }
799}