use hax_frontend_exporter::{DefKind, DefPathItem, DisambiguatedDefPathItem};
use hax_rust_engine_macros::*;
use crate::interning::{Internable, Interned, InterningTable};
mod compact_serialization;
pub(crate) mod generated_names;
pub mod view;
#[derive_group_for_ast]
struct DefIdInner {
krate: String,
path: Vec<DisambiguatedDefPathItem>,
parent: Option<DefId>,
kind: DefKind,
}
impl From<hax_frontend_exporter::DefId> for DefIdInner {
fn from(value: hax_frontend_exporter::DefId) -> Self {
Self {
krate: value.krate.clone(),
path: value.path.clone(),
parent: value
.parent
.clone()
.map(|def_id| DefIdInner::from(def_id).intern()),
kind: value.kind.clone(),
}
}
}
impl DefIdInner {
fn rename_krate(&self, name: &str) -> Self {
let mut def_id = self.clone();
def_id.krate = name.into();
def_id.parent = def_id.parent.map(|parent: DefId| parent.rename_krate(name));
def_id
}
fn to_debug_string(&self) -> String {
fn disambiguator_suffix(disambiguator: u32) -> String {
if disambiguator == 0 {
"".into()
} else {
format!("__{disambiguator}")
}
}
use itertools::Itertools;
std::iter::once(self.krate.clone())
.chain(self.path.iter().map(|item| match &item.data {
DefPathItem::TypeNs(s)
| DefPathItem::ValueNs(s)
| DefPathItem::MacroNs(s)
| DefPathItem::LifetimeNs(s) => s.clone(),
DefPathItem::Impl => "impl".into(),
other => format!("{other:?}"),
} + &disambiguator_suffix(item.disambiguator)))
.join("::")
}
}
use std::{
cell::{LazyCell, RefCell},
collections::HashMap,
sync::{LazyLock, Mutex},
};
impl Internable for DefIdInner {
fn interning_table() -> &'static Mutex<InterningTable<Self>> {
static TABLE: LazyLock<Mutex<InterningTable<DefIdInner>>> =
LazyLock::new(|| Mutex::new(InterningTable::default()));
&TABLE
}
}
type DefId = Interned<DefIdInner>;
impl DefId {
fn rename_krate(&self, name: &str) -> Self {
(*self).get().rename_krate(name).intern()
}
}
#[derive_group_for_ast]
struct ExplicitDefId {
is_constructor: bool,
def_id: DefId,
}
impl ExplicitDefId {
fn parent(&self) -> Option<Self> {
let def_id = &self.def_id;
let is_constructor = matches!(&def_id.kind, DefKind::Field);
Some(Self {
is_constructor,
def_id: def_id.parent?,
})
}
fn parents(&self) -> impl Iterator<Item = Self> {
std::iter::successors(Some(self.clone()), |id| id.parent())
}
fn rename_krate(&mut self, name: &str) {
self.def_id = self.def_id.rename_krate(name);
}
fn into_global_id_inner(self) -> GlobalIdInner {
GlobalIdInner::Concrete(ConcreteId {
def_id: self,
moved: None,
suffix: None,
})
}
}
#[derive_group_for_ast]
pub struct FreshModule {
id: usize,
hints: Vec<ExplicitDefId>,
label: String,
}
impl FreshModule {
fn view(&self) -> view::View {
self.clone().into()
}
fn rename_krate(&self, name: &str) -> Self {
let hints = self
.hints
.iter()
.map(|hint| {
let mut hint = hint.clone();
hint.rename_krate(name);
hint
})
.collect();
Self {
hints,
id: self.id,
label: self.label.clone(),
}
}
fn to_debug_string(&self) -> String {
format!("fresh_module_{}_{}", self.id, self.label)
}
}
#[derive_group_for_ast]
pub enum ReservedSuffix {
Pre,
Post,
Cast,
}
#[derive_group_for_ast]
pub struct ConcreteId {
def_id: ExplicitDefId,
moved: Option<FreshModule>,
suffix: Option<ReservedSuffix>,
}
#[derive_group_for_ast]
enum GlobalIdInner {
Concrete(ConcreteId),
FreshModule(FreshModule),
Tuple(TupleId),
}
#[derive_group_for_ast]
#[derive(Copy)]
pub enum TupleId {
Type {
length: usize,
},
Constructor {
length: usize,
},
Field {
length: usize,
field: usize,
},
}
impl From<TupleId> for GlobalId {
fn from(tuple_id: TupleId) -> Self {
Self(GlobalIdInner::Tuple(tuple_id).intern())
}
}
impl TupleId {
fn into_owned_concrete_id(self) -> ConcreteId {
fn patch_def_id(template: GlobalId, length: usize, field: usize) -> ConcreteId {
let GlobalIdInner::Concrete(mut concrete_id) = template.0.get().clone() else {
unreachable!()
};
fn inner(did: &mut DefIdInner, length: usize, field: usize) {
for DisambiguatedDefPathItem { data, .. } in &mut did.path {
if let DefPathItem::ValueNs(s) = data
&& s == "1"
{
*s = field.to_string()
}
if let DefPathItem::TypeNs(s) = data
&& s.starts_with("Tuple")
{
*s = format!("Tuple{length}")
}
}
if let Some(parent) = did.parent {
let mut parent = parent.get().clone();
inner(&mut parent, length, field);
did.parent = Some(parent.intern());
}
}
let mut did = concrete_id.def_id.def_id.get().clone();
inner(&mut did, length, field);
concrete_id.def_id.def_id = did.intern();
concrete_id
}
use crate::names::rust_primitives::hax;
match self {
TupleId::Type { length } => patch_def_id(hax::Tuple2, length, 0),
TupleId::Constructor { length } => patch_def_id(hax::Tuple2::Constructor, length, 0),
TupleId::Field { length, field } => patch_def_id(hax::Tuple2::_1, length, field),
}
}
pub fn as_concreteid(self) -> &'static ConcreteId {
thread_local! {
static MEMO: LazyCell<RefCell<HashMap<TupleId, &'static ConcreteId>>> =
LazyCell::new(|| RefCell::new(HashMap::new()));
}
MEMO.with(|memo| {
let mut memo = memo.borrow_mut();
let reference: &'static ConcreteId = memo.entry(self).or_insert_with(|| {
match GlobalIdInner::Concrete(self.into_owned_concrete_id())
.intern()
.get()
{
GlobalIdInner::Concrete(concrete_id) => concrete_id,
GlobalIdInner::FreshModule(_) | GlobalIdInner::Tuple(_) => {
unreachable!()
}
}
});
reference
})
}
}
#[derive_group_for_ast]
#[derive(Copy)]
pub struct GlobalId(Interned<GlobalIdInner>);
impl GlobalId {
pub fn from_frontend(id: hax_frontend_exporter::DefId, is_value: bool) -> Self {
let mut def_id: DefIdInner = id.into();
use hax_frontend_exporter::DefKind as DK;
let mut popped_ctor = false;
if let Some(last) = def_id.path.last()
&& matches!(&last.data, DefPathItem::Ctor)
{
def_id.path.pop();
popped_ctor = true;
if let Some(parent) = def_id.parent.as_ref() {
def_id.parent = parent.parent;
}
}
let is_constructor = is_value
&& (matches!(&def_id.kind, DK::Variant | DK::Union | DK::Struct) || popped_ctor);
let inner = GlobalIdInner::Concrete(ConcreteId {
def_id: ExplicitDefId {
is_constructor,
def_id: def_id.intern(),
},
moved: None,
suffix: None,
});
Self(inner.intern())
}
pub fn krate(self) -> &'static str {
match self.0.get() {
GlobalIdInner::FreshModule(fresh_module) => {
&fresh_module
.hints
.first()
.expect("The hint list should always be non-empty")
.def_id
.krate
}
GlobalIdInner::Concrete(concrete_id) => &concrete_id.def_id.def_id.krate,
GlobalIdInner::Tuple(tuple_id) => &tuple_id.as_concreteid().def_id.def_id.krate,
}
}
pub fn to_debug_string(self) -> String {
match self.0.get() {
GlobalIdInner::Concrete(id) => id.to_debug_string(),
GlobalIdInner::FreshModule(id) => id.to_debug_string(),
GlobalIdInner::Tuple(id) => id.as_concreteid().to_debug_string(),
}
}
pub fn is_constructor(self) -> bool {
self.0.get().is_constructor()
}
pub fn is_projector(self) -> bool {
self.0.get().is_projector()
}
pub fn is_precondition(self) -> bool {
self.0.get().is_precondition()
}
pub fn is_postcondition(self) -> bool {
self.0.get().is_postcondition()
}
pub fn view(self) -> view::View {
match self.0.get() {
GlobalIdInner::FreshModule(id) => id.view(),
GlobalIdInner::Concrete(id) => id.view(),
GlobalIdInner::Tuple(id) => id.as_concreteid().view(),
}
}
pub fn expect_tuple(self) -> Option<TupleId> {
match self.0.get() {
GlobalIdInner::Tuple(tuple_id) => Some(*tuple_id),
_ => None,
}
}
pub fn mod_only_closest_parent(self) -> Self {
match self.0.get() {
GlobalIdInner::FreshModule(_) => self,
GlobalIdInner::Concrete(concrete_id) => concrete_id.mod_only_closest_parent().into(),
GlobalIdInner::Tuple(tuple_id) => {
tuple_id.as_concreteid().mod_only_closest_parent().into()
}
}
}
pub fn rename_krate(self, name: &str) -> Self {
match self.0.get() {
GlobalIdInner::FreshModule(fresh_module) => {
Self(GlobalIdInner::FreshModule(fresh_module.rename_krate(name)).intern())
}
GlobalIdInner::Concrete(concrete_id) => {
let mut concrete_id = concrete_id.clone();
concrete_id.rename_krate(name);
Self(GlobalIdInner::Concrete(concrete_id).intern())
}
GlobalIdInner::Tuple(tuple_id) => {
let mut concrete_id = tuple_id.as_concreteid().clone();
concrete_id.rename_krate(name);
Self(GlobalIdInner::Concrete(concrete_id).intern())
}
}
}
pub fn with_suffix(self, suffix: ReservedSuffix) -> Self {
match self.0.get() {
GlobalIdInner::Concrete(concrete_id) => Self(
GlobalIdInner::Concrete(ConcreteId {
suffix: Some(suffix),
..concrete_id.clone()
})
.intern(),
),
GlobalIdInner::Tuple(_) | GlobalIdInner::FreshModule(_) => self,
}
}
}
impl GlobalIdInner {
fn explicit_def_id(&self) -> Option<ExplicitDefId> {
match self {
GlobalIdInner::Concrete(concrete_id) => Some(concrete_id.def_id.clone()),
_ => None,
}
}
pub fn is_constructor(&self) -> bool {
match self {
GlobalIdInner::Concrete(concrete_id) => concrete_id.def_id.is_constructor,
GlobalIdInner::Tuple(TupleId::Constructor { .. }) => true,
_ => false,
}
}
pub fn is_projector(&self) -> bool {
match self {
GlobalIdInner::Concrete(concrete_id) => {
matches!(concrete_id.def_id.def_id.get().kind, DefKind::Field)
}
GlobalIdInner::Tuple(TupleId::Field { .. }) => true,
_ => false,
}
}
pub fn is_precondition(&self) -> bool {
matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Pre)))
}
pub fn is_postcondition(&self) -> bool {
matches!(self, GlobalIdInner::Concrete(concrete_id) if matches!(concrete_id.suffix, Some(ReservedSuffix::Post)))
}
}
impl From<ConcreteId> for GlobalId {
fn from(concrete_id: ConcreteId) -> Self {
Self(GlobalIdInner::Concrete(concrete_id).intern())
}
}
impl ConcreteId {
fn view(&self) -> view::View {
view::View::from(self.def_id.clone()).with_suffix(self.suffix.clone())
}
fn mod_only_closest_parent(&self) -> Self {
let mut parents = self.def_id.parents().collect::<Vec<_>>();
parents.reverse();
let def_id = parents
.into_iter()
.take_while(|id| matches!(id.def_id.kind, DefKind::Mod))
.last()
.expect("Invariant broken: a DefId must always contain at least on `mod` segment (the crate)");
Self {
def_id,
moved: self.moved.clone(),
suffix: None,
}
}
fn rename_krate(&mut self, name: &str) {
self.def_id.rename_krate(name);
}
fn to_debug_string(&self) -> String {
self.def_id.def_id.get().to_debug_string()
}
}
impl PartialEq<DefId> for GlobalId {
fn eq(&self, other: &DefId) -> bool {
if let GlobalIdInner::Concrete(concrete) = self.0.get() {
&concrete.def_id.def_id == other
} else {
false
}
}
}
impl PartialEq<GlobalId> for DefId {
fn eq(&self, other: &GlobalId) -> bool {
other == self
}
}
impl PartialEq<ExplicitDefId> for GlobalId {
fn eq(&self, other: &ExplicitDefId) -> bool {
self == &other.def_id
}
}
impl PartialEq<GlobalId> for ExplicitDefId {
fn eq(&self, other: &GlobalId) -> bool {
other == &self.def_id
}
}