use hax_frontend_exporter::{CtorOf, DefKind, DefPathItem, ImplInfos};
use crate::{
ast::identifiers::global_id::{DefId, ExplicitDefId},
symbol::Symbol,
};
#[derive(Debug, Clone)]
pub enum TypeDefKind {
Struct,
Enum,
Union,
}
#[derive(Debug, Clone)]
pub enum AssocItemContainerKind {
Impl {
inherent: bool,
impl_infos: Option<ImplInfos>,
},
Trait {
trait_alias: bool,
},
}
#[derive(Debug, Clone)]
pub enum ConstructorKind {
Constructor {
ty: PathSegment<TypeDefKind>,
},
}
#[derive(Debug, Clone)]
pub enum AssocItemKind {
Fn,
Const,
Ty,
}
#[derive(Debug, Clone)]
pub enum AnyKind {
TypeDef(TypeDefKind),
AssocItemContainer(AssocItemContainerKind),
Constructor(ConstructorKind),
AssocItem {
kind: AssocItemKind,
container: PathSegment<AssocItemContainerKind>,
},
Fn,
Const,
Use,
AnonConst,
InlineConst,
TraitAlias,
Foreign,
ForeignTy,
TyAlias,
ExternCrate,
Opaque,
Static,
Macro,
Mod,
GlobalAsm,
Field {
named: bool,
parent: PathSegment<ConstructorKind>,
},
Closure,
}
#[derive(Debug, Clone)]
pub enum UnnamedPathSegmentPayload {
Impl,
AnonConst,
InlineConst,
Foreign,
GlobalAsm,
Use,
Opaque,
Closure,
}
#[derive(Debug, Clone)]
pub enum PathSegmentPayload {
Named(Symbol),
Unnamed(UnnamedPathSegmentPayload),
}
mod rustc_invariant_handling {
use std::any::{Any, type_name};
use std::fmt::Debug;
use super::*;
use crate::{
ast::{
diagnostics::{Context, DiagnosticInfo},
span::Span,
},
names,
};
use hax_types::diagnostics::Kind;
#[derive(Clone, Copy)]
pub struct Permit(());
pub trait ErrorDummyValue {
fn error_dummy_value(_: Permit) -> Self;
}
impl ErrorDummyValue for PathSegmentPayload {
fn error_dummy_value(_: Permit) -> Self {
Self::Named(Symbol::new("hax_engine_view_fatal_error"))
}
}
impl ErrorDummyValue for TypeDefKind {
fn error_dummy_value(_: Permit) -> Self {
TypeDefKind::Enum
}
}
impl ErrorDummyValue for ConstructorKind {
fn error_dummy_value(permit: Permit) -> Self {
ConstructorKind::Constructor {
ty: PathSegment::<TypeDefKind>::error_dummy_value(permit),
}
}
}
impl<K: ErrorDummyValue> ErrorDummyValue for PathSegment<K> {
fn error_dummy_value(permit: Permit) -> Self {
Self {
identifier: DefId::error_dummy_value(permit),
payload: PathSegmentPayload::error_dummy_value(permit),
disambiguator: 0,
kind: K::error_dummy_value(permit),
}
}
}
impl ErrorDummyValue for AnyKind {
fn error_dummy_value(_: Permit) -> Self {
Self::Fn
}
}
impl ErrorDummyValue for DefId {
fn error_dummy_value(_: Permit) -> Self {
match names::rust_primitives::hax::failure.0.get() {
crate::ast::identifiers::global_id::GlobalIdInner::Concrete(concrete_id) => {
concrete_id.def_id.def_id
}
_ => unreachable!("Hax generated name for failure is concrete"),
}
}
}
impl ErrorDummyValue for AssocItemContainerKind {
fn error_dummy_value(_: Permit) -> Self {
AssocItemContainerKind::Trait { trait_alias: false }
}
}
impl ErrorDummyValue for bool {
fn error_dummy_value(_: Permit) -> Self {
true
}
}
pub(super) fn error_dummy_value<T: ErrorDummyValue, V: Debug + Any>(
message: &str,
value: &V,
) -> T {
let details = format!(
"A rustc invariant about `DefId` was violated.\nContext: {message}.\nValue (type {}) is:\n{value:#?}",
type_name::<T>()
);
DiagnosticInfo {
context: Context::NameView,
span: Span::dummy(),
kind: Kind::AssertionFailure { details },
}
.emit();
T::error_dummy_value(Permit(()))
}
}
use rustc_invariant_handling::error_dummy_value;
impl PathSegmentPayload {
fn from_named(def_id: &ExplicitDefId) -> Self {
Self::Named(match def_id.def_id.path.last() {
Some(last) => match &last.data {
DefPathItem::TypeNs(s)
| DefPathItem::ValueNs(s)
| DefPathItem::MacroNs(s)
| DefPathItem::LifetimeNs(s) => Symbol::new(s),
_ => return error_dummy_value("PathSegmentPayload::from_named", def_id),
},
None => Symbol::new(&def_id.def_id.krate),
})
}
fn from_unnamed(def_id: &ExplicitDefId) -> Result<Self, &'static str> {
match def_id.def_id.path.last() {
Some(last) => match &last.data {
DefPathItem::TypeNs(_)
| DefPathItem::ValueNs(_)
| DefPathItem::MacroNs(_)
| DefPathItem::LifetimeNs(_) => {
return Err("PathSegmentPayload::from_unnamed, got name");
}
_ => (),
},
None => return Err("PathSegmentPayload::from_unnamed, got a root crate"),
};
Ok(Self::Unnamed(match &def_id.def_id.kind {
DefKind::Use => UnnamedPathSegmentPayload::Use,
DefKind::ForeignMod => UnnamedPathSegmentPayload::Foreign,
DefKind::AnonConst => UnnamedPathSegmentPayload::AnonConst,
DefKind::InlineConst => UnnamedPathSegmentPayload::InlineConst,
DefKind::OpaqueTy => UnnamedPathSegmentPayload::Opaque,
DefKind::GlobalAsm => UnnamedPathSegmentPayload::GlobalAsm,
DefKind::Impl { .. } => UnnamedPathSegmentPayload::Impl,
DefKind::Closure => UnnamedPathSegmentPayload::Closure,
_ => return Err("PathSegmentPayload::from_unnamed, bad kind"),
}))
}
fn from_def_id(def_id: &ExplicitDefId) -> Self {
match &def_id.def_id.kind {
DefKind::Mod
| DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
| DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::AssocTy
| DefKind::Fn
| DefKind::Const
| DefKind::Static { .. }
| DefKind::Ctor { .. }
| DefKind::AssocFn
| DefKind::AssocConst
| DefKind::Macro { .. }
| DefKind::ExternCrate
| DefKind::Field => Self::from_named(def_id),
DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::OpaqueTy
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure => Self::from_unnamed(def_id)
.unwrap_or_else(|message| error_dummy_value(message, def_id)),
DefKind::TyParam
| DefKind::ConstParam
| DefKind::PromotedConst
| DefKind::LifetimeParam
| DefKind::SyntheticCoroutineBody => error_dummy_value(
"PathSegmentPayload::from_def_id, kinds should never appear",
def_id,
),
}
}
}
#[derive(Debug, Clone)]
pub struct PathSegment<Kind = AnyKind> {
identifier: DefId,
payload: PathSegmentPayload,
disambiguator: u32,
kind: Kind,
}
impl<K> PathSegment<K> {
pub fn payload(&self) -> PathSegmentPayload {
self.payload.clone()
}
pub fn disambiguator(&self) -> u32 {
self.disambiguator
}
pub fn kind(&self) -> &K {
&self.kind
}
fn map<U>(self, f: impl Fn(K, &DefId) -> U) -> PathSegment<U> {
let Self {
identifier,
payload,
disambiguator,
kind,
} = self;
let kind = f(kind, &identifier);
PathSegment {
identifier,
payload,
disambiguator,
kind,
}
}
}
impl PathSegment<ConstructorKind> {
pub fn lift(&self) -> PathSegment<AnyKind> {
self.clone().map(|kind, _| AnyKind::Constructor(kind))
}
}
impl PathSegment<TypeDefKind> {
pub fn lift(&self) -> PathSegment<AnyKind> {
self.clone().map(|kind, _| AnyKind::TypeDef(kind))
}
}
impl PathSegment<AssocItemContainerKind> {
pub fn lift(&self) -> PathSegment<AnyKind> {
self.clone()
.map(|kind, _| AnyKind::AssocItemContainer(kind))
}
}
impl PartialEq<PathSegment> for PathSegment {
fn eq(&self, other: &PathSegment) -> bool {
self.identifier == other.identifier && self.disambiguator == other.disambiguator
}
}
impl PathSegment {
fn assert_type_def(self) -> PathSegment<TypeDefKind> {
self.map(|kind, did| match kind {
AnyKind::TypeDef(inner) => inner,
_ => error_dummy_value(&format!("expected TypeDefKind, got {kind:#?}"), did),
})
}
fn assert_assoc_item_container(self) -> PathSegment<AssocItemContainerKind> {
self.map(|kind, did| match kind {
AnyKind::AssocItemContainer(inner) => inner,
_ => error_dummy_value(
&format!("expected AssocItemContainerKind, got {kind:#?}"),
did,
),
})
}
fn assert_constructor(self) -> PathSegment<ConstructorKind> {
self.map(|kind, did| match kind {
AnyKind::Constructor(inner) => inner,
_ => error_dummy_value(&format!("expected ConstructorKind, got {kind:#?}"), did),
})
}
fn from_iterator(it: &mut impl Iterator<Item = ExplicitDefId>) -> Option<Self> {
let def_id = it.next()?;
let mut from_iterator = |context: &str| match Self::from_iterator(it) {
Some(value) => value,
None => error_dummy_value(
&format!("PathSegment::from_iterator, expected parent for {context}."),
&def_id,
),
};
let payload = PathSegmentPayload::from_def_id(&def_id);
let kind = match &def_id.def_id.kind {
DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct if def_id.is_constructor => {
let parent_def_id = ExplicitDefId {
is_constructor: false,
def_id: def_id.def_id,
};
let parent = match Self::from_iterator(&mut std::iter::once(parent_def_id)) {
Some(value) => value,
None => error_dummy_value(
"PathSegment::from_iterator, expected parent for Struct/Ctor.",
&def_id,
),
};
AnyKind::Constructor(ConstructorKind::Constructor {
ty: parent.assert_type_def(),
})
}
DefKind::Ctor(CtorOf::Struct, _) => AnyKind::TypeDef(TypeDefKind::Struct),
DefKind::Variant | DefKind::Ctor(_, _) => {
AnyKind::Constructor(ConstructorKind::Constructor {
ty: from_iterator("Variant/Ctor").assert_type_def(),
})
}
DefKind::Struct => AnyKind::TypeDef(TypeDefKind::Struct),
DefKind::Union => AnyKind::TypeDef(TypeDefKind::Union),
DefKind::Enum => AnyKind::TypeDef(TypeDefKind::Enum),
DefKind::Trait => {
AnyKind::AssocItemContainer(AssocItemContainerKind::Trait { trait_alias: false })
}
DefKind::Impl { of_trait } => AnyKind::AssocItemContainer(
AssocItemContainerKind::Impl { inherent: !of_trait, impl_infos: None },
),
DefKind::Mod => AnyKind::Mod,
DefKind::Fn => AnyKind::Fn,
DefKind::Const => AnyKind::Const,
DefKind::Static { .. } => AnyKind::Static,
DefKind::Use => AnyKind::Use,
DefKind::TyAlias => AnyKind::TyAlias,
DefKind::TraitAlias => AnyKind::TraitAlias,
DefKind::ForeignTy => AnyKind::ForeignTy,
DefKind::ForeignMod => AnyKind::Foreign,
DefKind::Macro { .. } => AnyKind::Macro,
DefKind::AnonConst => AnyKind::AnonConst,
DefKind::OpaqueTy => AnyKind::Opaque,
DefKind::GlobalAsm => AnyKind::GlobalAsm,
DefKind::Closure => AnyKind::Closure,
DefKind::ExternCrate => AnyKind::ExternCrate,
DefKind::Field => AnyKind::Field {
parent: from_iterator("Field").assert_constructor(),
named: match &payload {
PathSegmentPayload::Named(symbol) => {
str::parse::<usize>(symbol.as_ref()).is_ok()
}
PathSegmentPayload::Unnamed(_) => {
error_dummy_value("Field should carry a ValueNs payload.", &def_id)
}
},
},
DefKind::AssocTy => AnyKind::AssocItem {
container: from_iterator("AssocTy").assert_assoc_item_container(),
kind: AssocItemKind::Ty,
},
DefKind::AssocFn => AnyKind::AssocItem {
container: from_iterator("AssocFn").assert_assoc_item_container(),
kind: AssocItemKind::Fn,
},
DefKind::AssocConst => AnyKind::AssocItem {
container: from_iterator("AssocConst").assert_assoc_item_container(),
kind: AssocItemKind::Const,
},
_ => error_dummy_value("PathSegment::from_iterator_opt", &def_id),
};
let identifier = def_id.def_id;
let disambiguator = identifier.path.last().map(|d| d.disambiguator).unwrap_or(0);
Some(Self {
identifier,
payload,
disambiguator,
kind,
})
}
}
impl PathSegment {
pub fn parent(&self) -> Option<PathSegment> {
Some(match self.kind.clone() {
AnyKind::Constructor(ConstructorKind::Constructor { ty }) => {
ty.map(|kind, _| AnyKind::TypeDef(kind))
}
AnyKind::AssocItem { container, .. } => {
container.map(|kind, _| AnyKind::AssocItemContainer(kind))
}
AnyKind::Field { parent, .. } => parent.map(|kind, _| AnyKind::Constructor(kind)),
_ => return None,
})
}
pub fn parents(&self) -> impl Iterator<Item = Self> {
std::iter::successors(Some(self.clone()), |seg| seg.parent())
}
}
mod view_encapsulation {
use crate::ast::{
identifiers::global_id::{FreshModule, ReservedSuffix},
span::Span,
};
use super::*;
pub struct View(Vec<PathSegment>, Option<ReservedSuffix>);
impl View {
pub fn segments(&self) -> &[PathSegment] {
&self.0
}
pub fn last(&self) -> &PathSegment {
self.0
.last()
.expect("Broken invariant: a view always contains at least one path path segments.")
}
pub fn first(&self) -> &PathSegment {
self.0
.first()
.expect("Broken invariant: a view always contains at least one path path segments.")
}
pub fn split_at_module(&self) -> (&[PathSegment], &[PathSegment]) {
let position = self
.segments()
.iter()
.enumerate()
.find(|(_, seg)| !matches!(seg.kind(), AnyKind::Mod))
.map(|(i, _)| i)
.unwrap_or(self.segments().len());
self.segments().split_at(position)
}
pub fn module(&self) -> &PathSegment {
self.0
.iter()
.take_while(|seg| !matches!(seg.kind(), AnyKind::Mod))
.last()
.expect("Broken invariant, a name has at least a crate")
}
pub fn suffix(&self) -> &Option<ReservedSuffix> {
&self.1
}
pub fn with_suffix(mut self, suffix: Option<ReservedSuffix>) -> Self {
self.1 = suffix;
self
}
}
impl From<ExplicitDefId> for View {
fn from(value: ExplicitDefId) -> Self {
let mut it = value.parents();
let mut inner =
std::iter::from_fn(|| PathSegment::from_iterator(&mut it)).collect::<Vec<_>>();
inner.reverse();
debug_assert!(!inner.is_empty()); Self(inner, None)
}
}
impl From<FreshModule> for View {
fn from(value: FreshModule) -> Self {
use crate::ast::diagnostics::{Context, DiagnosticInfo};
(DiagnosticInfo {
context: Context::NameView,
span: Span::dummy(),
kind: hax_types::diagnostics::Kind::Unimplemented {
issue_id: Some(1779),
details: Some(
"Fresh modules are not implemented yet in the Rust engine".into(),
),
},
})
.emit();
value
.hints
.first()
.expect("The list of hints should be non-empty")
.clone()
.into()
}
}
}
pub use view_encapsulation::View;