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 crate::hax;
12use crate::hax::{DefPathItem, SInto};
13use charon_lib::ast::*;
14
15impl<'tcx> TranslateCtx<'tcx> {
17 fn register_file(&mut self, filename: FileName, span: rustc_span::Span) -> FileId {
20 match self.file_to_id.get(&filename) {
22 Some(id) => *id,
23 None => {
24 let source_file = self.tcx.sess.source_map().lookup_source_file(span.lo());
25 let crate_name = self.tcx.crate_name(source_file.cnum).to_string();
26 let id = self.translated.files.push_with(|id| File {
27 id,
28 name: filename.clone(),
29 crate_name,
30 contents: source_file.src.as_deref().cloned(),
31 });
32 self.file_to_id.insert(filename, id);
33 id
34 }
35 }
36 }
37
38 pub fn translate_filename(&mut self, name: rustc_span::FileName) -> meta::FileName {
39 match name {
40 rustc_span::FileName::Real(name) => {
41 match name.local_path() {
42 Some(path) => {
43 let path = if let Ok(path) = path.strip_prefix(&self.sysroot) {
44 if let Ok(path) = path.strip_prefix("lib/rustlib/src/rust") {
49 let mut rewritten_path: PathBuf = "/rustc".into();
50 rewritten_path.extend(path);
51 rewritten_path
52 } else {
53 let mut rewritten_path: PathBuf = "/toolchain".into();
55 rewritten_path.extend(path);
56 rewritten_path
57 }
58 } else {
59 let cargo_home = std::env::var("CARGO_HOME")
63 .map(PathBuf::from)
64 .ok()
65 .or_else(|| std::env::home_dir().map(|p| p.join(".cargo")));
66 if let Some(cargo_home) = cargo_home
67 && let Ok(path) = path.strip_prefix(cargo_home)
68 {
69 let mut rewritten_path: PathBuf = "/cargo".into();
71 rewritten_path.extend(path);
72 rewritten_path
73 } else {
74 path.into()
75 }
76 };
77 FileName::Local(path)
78 }
79 None => {
80 let virtual_name = name.path(RemapPathScopeComponents::MACRO);
84 let mut components_iter = virtual_name.components();
85 if let Some(
86 [
87 Component::RootDir,
88 Component::Normal(rustc),
89 Component::Normal(hash),
90 ],
91 ) = components_iter.by_ref().array_chunks().next()
92 && rustc.to_str() == Some("rustc")
93 && hash.len() == 40
94 {
95 let path_without_hash = [Component::RootDir, Component::Normal(rustc)]
96 .into_iter()
97 .chain(components_iter)
98 .collect();
99 FileName::Virtual(path_without_hash)
100 } else {
101 FileName::Virtual(virtual_name.into())
102 }
103 }
104 }
105 }
106 _ => FileName::NotReal(format!("{name:?}")),
109 }
110 }
111
112 pub fn translate_span_data(&mut self, span: rustc_span::Span) -> meta::SpanData {
113 let smap: &rustc_span::source_map::SourceMap = self.tcx.sess.psess.source_map();
114 let filename = smap.span_to_filename(span);
115 let filename = self.translate_filename(filename);
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> 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(Box::new(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 && let Some(pred) = implied_predicates.predicates.first()
478 && let hax::ClauseKind::Trait(p) = &pred.clause.kind.value
479 {
480 assoc_types = Some(p.trait_ref.generic_args.clone());
481 break;
482 }
483 }
484 }
485 }
486 let mut substs = item_ref.generic_args.clone();
488
489 if !(is_preshim && item_ref.generic_args.len() == 1 && assoc_types.is_none()) {
490 let args = if is_preshim {
492 if let Some(mut assoc_types) = assoc_types {
493 substs.append(&mut assoc_types);
494 }
495 bt_ctx.translate_generic_args(span, &substs[1..], &item_ref.impl_exprs)?
496 } else {
497 bt_ctx.translate_generic_args(span, &substs, &item_ref.impl_exprs)?
498 };
499 name.name.push(PathElem::Instantiated(Box::new(Binder {
500 params: GenericParams::empty(),
501 skip_binder: args,
502 kind: BinderKind::Other,
503 })));
504 }
505 }
506 Ok(name)
507 }
508
509 pub(crate) fn translate_trait_item_name(
511 &mut self,
512 def_id: &hax::DefId,
513 ) -> Result<TraitItemName, Error> {
514 let def = self.poly_hax_def(def_id)?;
515 let assoc = match def.kind() {
516 hax::FullDefKind::AssocTy {
517 associated_item, ..
518 }
519 | hax::FullDefKind::AssocConst {
520 associated_item, ..
521 }
522 | hax::FullDefKind::AssocFn {
523 associated_item, ..
524 } => associated_item,
525 _ => panic!("Unexpected def for associated item: {def:?}"),
526 };
527 Ok(TraitItemName(
528 assoc
529 .name
530 .as_ref()
531 .map(|n| n.to_string().into())
532 .unwrap_or_default(),
533 ))
534 }
535
536 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
537 self.options.opacity_for_name(&self.translated, name)
538 }
539}
540
541impl<'tcx> TranslateCtx<'tcx> {
543 fn parse_attr_from_raw(
545 &mut self,
546 def_id: &hax::DefId,
547 raw_attr: RawAttribute,
548 ) -> Result<Attribute, String> {
549 let path = raw_attr.path.split("::").collect_vec();
552 let attr_name = if let &[path_start, attr_name] = path.as_slice()
553 && (path_start == "charon" || path_start == "aeneas" || path_start == "verify")
554 {
555 attr_name
556 } else {
557 return Ok(Attribute::Unknown(raw_attr));
558 };
559
560 match self.parse_special_attr(def_id, attr_name, &raw_attr)? {
561 Some(parsed) => Ok(parsed),
562 None => Err(format!("Unrecognized attribute: `{}`", raw_attr)),
563 }
564 }
565
566 fn parse_special_attr(
568 &mut self,
569 def_id: &hax::DefId,
570 attr_name: &str,
571 raw_attr: &RawAttribute,
572 ) -> Result<Option<Attribute>, String> {
573 let args = raw_attr.args.as_deref();
574 let parsed = match attr_name {
575 "opaque" if args.is_none() => Attribute::Opaque,
577 "exclude" if args.is_none() => Attribute::Exclude,
579 "rename" if let Some(attr) = args => {
581 let Some(attr) = attr
582 .strip_prefix("\"")
583 .and_then(|attr| attr.strip_suffix("\""))
584 else {
585 return Err(format!(
586 "the new name should be between quotes: `rename(\"{attr}\")`."
587 ));
588 };
589
590 if attr.is_empty() {
591 return Err(format!("attribute `rename` should not be empty"));
592 }
593
594 let first_char = attr.chars().nth(0).unwrap();
595 let is_identifier = (first_char.is_alphabetic() || first_char == '_')
596 && attr.chars().all(|c| c.is_alphanumeric() || c == '_');
597 if !is_identifier {
598 return Err(format!(
599 "attribute `rename` should contain a valid identifier"
600 ));
601 }
602
603 Attribute::Rename(attr.to_string())
604 }
605 "variants_prefix" if let Some(attr) = args => {
607 let Some(attr) = attr
608 .strip_prefix("\"")
609 .and_then(|attr| attr.strip_suffix("\""))
610 else {
611 return Err(format!(
612 "the name should be between quotes: `variants_prefix(\"{attr}\")`."
613 ));
614 };
615
616 Attribute::VariantsPrefix(attr.to_string())
617 }
618 "variants_suffix" if let Some(attr) = args => {
620 let Some(attr) = attr
621 .strip_prefix("\"")
622 .and_then(|attr| attr.strip_suffix("\""))
623 else {
624 return Err(format!(
625 "the name should be between quotes: `variants_suffix(\"{attr}\")`."
626 ));
627 };
628
629 Attribute::VariantsSuffix(attr.to_string())
630 }
631 "start_from" => {
633 if matches!(def_id.kind, hax::DefKind::Mod) {
634 return Err("`start_from` on modules has no effect".to_string());
635 }
636 Attribute::Unknown(raw_attr.clone())
637 }
638 "test" if args.is_none() => Attribute::Unknown(raw_attr.clone()),
640 _ => return Ok(None),
641 };
642 Ok(Some(parsed))
643 }
644
645 pub(crate) fn translate_attribute(
648 &mut self,
649 def_id: &hax::DefId,
650 attr: &rustc_hir::Attribute,
651 ) -> Option<Attribute> {
652 use rustc_hir as hir;
653 use rustc_hir::attrs as hir_attrs;
654 match attr {
655 hir::Attribute::Parsed(hir_attrs::AttributeKind::DocComment { comment, .. }) => {
656 Some(Attribute::DocComment(comment.to_string()))
657 }
658 hir::Attribute::Parsed(_) => None,
659 hir::Attribute::Unparsed(attr) => {
660 let raw_attr = RawAttribute {
661 path: attr.path.to_string(),
662 args: match &attr.args {
663 hir::AttrArgs::Empty => None,
664 hir::AttrArgs::Delimited(args) => {
665 Some(rustc_ast_pretty::pprust::tts_to_string(&args.tokens))
666 }
667 hir::AttrArgs::Eq { expr, .. } => {
668 self.tcx.sess.source_map().span_to_snippet(expr.span).ok()
669 }
670 },
671 };
672 match self.parse_attr_from_raw(def_id, raw_attr) {
673 Ok(a) => Some(a),
674 Err(msg) => {
675 let span = self.translate_span(&attr.span.sinto(&self.hax_state));
676 register_error!(self, span, "Error parsing attribute: {msg}");
677 None
678 }
679 }
680 }
681 }
682 }
683
684 pub(crate) fn translate_inline(&self, def: &hax::FullDef) -> Option<InlineAttr> {
685 match def.kind() {
686 hax::FullDefKind::Fn { inline, .. }
687 | hax::FullDefKind::AssocFn { inline, .. }
688 | hax::FullDefKind::Closure { inline, .. } => match inline {
689 hax::InlineAttr::None => None,
690 hax::InlineAttr::Hint => Some(InlineAttr::Hint),
691 hax::InlineAttr::Never => Some(InlineAttr::Never),
692 hax::InlineAttr::Always => Some(InlineAttr::Always),
693 hax::InlineAttr::Force { .. } => Some(InlineAttr::Always),
694 },
695 _ => None,
696 }
697 }
698
699 pub(crate) fn translate_attr_info(&mut self, def: &hax::FullDef) -> AttrInfo {
700 let public = def.visibility.unwrap_or(false);
702 let inline = self.translate_inline(def);
703 let attributes = def
704 .attributes
705 .iter()
706 .filter_map(|attr| self.translate_attribute(def.def_id(), attr))
707 .collect_vec();
708
709 let rename = {
710 let mut renames = attributes.iter().filter_map(|a| a.as_rename()).cloned();
711 let rename = renames.next();
712 if renames.next().is_some() {
713 let span = self.translate_span(&def.span);
714 register_error!(
715 self,
716 span,
717 "There should be at most one `charon::rename(\"...\")` \
718 or `aeneas::rename(\"...\")` attribute per declaration",
719 );
720 }
721 rename
722 };
723
724 AttrInfo {
725 attributes,
726 inline,
727 public,
728 rename,
729 }
730 }
731}
732
733impl<'tcx> TranslateCtx<'tcx> {
735 pub(crate) fn is_extern_item(&mut self, def: &hax::FullDef) -> bool {
737 def.def_id()
738 .parent(&self.hax_state)
739 .is_some_and(|parent| matches!(parent.kind, hax::DefKind::ForeignMod))
740 }
741
742 pub(crate) fn extern_item_symbol_name(&mut self, def: &hax::FullDef) -> Option<String> {
744 if !self.is_extern_item(def) {
745 return None;
746 }
747 let path_item = def.def_id().path_item(&self.hax_state);
748 match path_item.data {
749 hax::DefPathItem::ValueNs(name) | hax::DefPathItem::TypeNs(name) => {
750 Some(name.to_string())
751 }
752 _ => None,
753 }
754 }
755
756 pub(crate) fn translate_item_meta(
758 &mut self,
759 def: &hax::FullDef,
760 item_src: &TransItemSource,
761 name: Name,
762 name_opacity: ItemOpacity,
763 ) -> ItemMeta {
764 if let Some(item_meta) = self.cached_item_metas.get(item_src) {
765 return item_meta.clone();
766 }
767 let span = def.source_span.as_ref().unwrap_or(&def.span);
768 let span = self.translate_span(span);
769 let is_local = def.def_id().is_local();
770 let (attr_info, lang_item) = if !item_src.is_derived_item()
771 || matches!(item_src.kind, TransItemSourceKind::ClosureMethod(..))
772 {
773 let attr_info = self.translate_attr_info(def);
774 let lang_item = def.lang_item.or(def.diagnostic_item).map(|s| s.to_string());
775 (attr_info, lang_item)
776 } else {
777 (AttrInfo::default(), None)
778 };
779
780 let opacity = if attr_info.attributes.iter().any(|attr| attr.is_exclude()) {
781 ItemOpacity::Invisible.max(name_opacity)
782 } else if self.is_extern_item(def)
783 || attr_info.attributes.iter().any(|attr| attr.is_opaque())
784 {
785 ItemOpacity::Opaque.max(name_opacity)
787 } else {
788 name_opacity
789 };
790
791 let item_meta = ItemMeta {
792 name,
793 span,
794 source_text: def.source_text.clone(),
795 attr_info,
796 is_local,
797 opacity,
798 lang_item,
799 };
800 self.cached_item_metas
801 .insert(item_src.clone(), item_meta.clone());
802 item_meta
803 }
804}