Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions crates/hir/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::{
ty::{NetKind, NetType},
typedef::TypedefId,
},
symbol::DefLoc,
type_infer::{BuiltinTy, Ty},
};

pub struct HirFormatter<'a> {
Expand Down Expand Up @@ -77,6 +79,120 @@ impl<T: HirDisplay> HirDisplay for Arc<T> {
}
}

impl HirDisplay for Ty {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
Ty::Unknown => f.write_str("unknown"),
Ty::Error => f.write_str("error"),
Ty::Void => f.write_str("void"),
Ty::Builtin(BuiltinTy::Data { id, container }) => {
InContainer::new(*container, DataTy::Builtin(*id)).hir_fmt(f)
}
Ty::Struct(struct_ref) => {
InContainer::new(struct_ref.cont_id, DataTy::Struct(*struct_ref)).hir_fmt(f)
}
Ty::Enum(def) => hir_fmt_def_backed_type(f, "enum", *def),
Ty::Union(def) => hir_fmt_def_backed_type(f, "union", *def),
Ty::Queue { elem, size } => {
elem.hir_fmt(f)?;
f.write_str(" [$")?;
if let (Some(size), Some(container)) = (size, ty_expr_container(f.db, elem)) {
f.write_str(":")?;
InContainer::new(container, *size).hir_fmt(f)?;
}
f.write_str("]")
}
Ty::Assoc { key, elem } => {
elem.hir_fmt(f)?;
f.write_str(" [")?;
key.hir_fmt(f)?;
f.write_str("]")
}
Ty::Dynamic(elem) => {
elem.hir_fmt(f)?;
f.write_str(" []")
}
Ty::Event => f.write_str("event"),
Ty::Chandle => f.write_str("chandle"),
Ty::ClassHandle { def, .. } => hir_fmt_def_backed_type(f, "class", *def),
Ty::VirtualInterface { def, modport } => {
f.write_str("virtual interface")?;
if let Some(name) = def.name(f.db) {
f.write_str(" ")?;
f.write_str(name.as_str())?;
}
if let Some(modport) = modport.and_then(|def| def.name(f.db)) {
f.write_str(".")?;
f.write_str(modport.as_str())?;
}
Ok(())
}
Ty::Alias { typedef, target } => {
let container = typedef.cont_id.to_container(f.db);
if let Some(name) = &container.get(typedef.value).name {
f.write_str(name)
} else {
target.hir_fmt(f)
}
}
Ty::Module(module_id) => {
let module = f.db.module(*module_id);
if let Some(name) = &module.name {
f.write_str(name)
} else {
f.write_str("module")
}
}
Ty::GenerateBlock(generate_block_id) => {
let block = f.db.generate_block(*generate_block_id);
if let Some(name) = &block.name {
f.write_str(name)
} else {
f.write_str("generate block")
}
}
Ty::Block(block_id) => {
let block = f.db.block(*block_id);
if let Some(name) = &block.name { f.write_str(name) } else { f.write_str("block") }
}
}
}
}

fn hir_fmt_def_backed_type(
f: &mut HirFormatter<'_>,
keyword: &str,
def: crate::symbol::DefId,
) -> Result<(), HirDisplayError> {
f.write_str(keyword)?;
if let DefLoc::Typedef(typedef) = def.loc(f.db) {
let container = typedef.cont_id.to_container(f.db);
if let Some(name) = &container.get(typedef.value).name {
f.write_str(" ")?;
f.write_str(name)?;
}
}
Ok(())
}

fn ty_expr_container(db: &dyn crate::db::HirDb, ty: &Ty) -> Option<crate::container::ScopeId> {
match ty {
Ty::Builtin(BuiltinTy::Data { container, .. }) => Some(*container),
Ty::Struct(struct_ref) => Some(struct_ref.cont_id),
Ty::Alias { typedef, .. } => Some(typedef.cont_id),
Ty::Enum(def) | Ty::Union(def) => match def.loc(db) {
DefLoc::Decl(decl) => Some(decl.cont_id),
DefLoc::Typedef(typedef) => Some(typedef.cont_id),
DefLoc::SubroutinePort(port) => Some(port.subroutine.into()),
_ => None,
},
Ty::Queue { elem, .. } | Ty::Assoc { elem, .. } | Ty::Dynamic(elem) => {
ty_expr_container(db, elem)
}
_ => None,
}
}

impl HirDisplay for PortDirection {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
Expand Down Expand Up @@ -161,12 +277,15 @@ impl HirDisplay for InContainer<DataTy> {
Real::RealTime => f.write_str("realtime"),
},
BuiltinDataTy::String => f.write_str("string"),
BuiltinDataTy::Event => f.write_str("event"),
BuiltinDataTy::Chandle => f.write_str("chandle"),
BuiltinDataTy::Void => f.write_str("void"),
},
DataTy::Named(named) => match named {
NamedDataTy::Ident(expr_id) => self.with_value(expr_id).hir_fmt(f),
NamedDataTy::Field(expr_id) => self.with_value(expr_id).hir_fmt(f),
},
DataTy::Enum => f.write_str("enum"),
DataTy::Struct(struct_ref) => {
let cont = struct_ref.cont_id.to_container(f.db);
let def = cont.get(struct_ref.value);
Expand Down Expand Up @@ -511,6 +630,15 @@ impl HirDisplay for InContainer<Dimension> {
self.with_value(end).hir_fmt(f)?;
}
Dimension::Size(idx) => self.with_value(idx).hir_fmt(f)?,
Dimension::Queue(size) => {
f.write_str("$")?;
if let Some(size) = size {
f.write_str(":")?;
self.with_value(size).hir_fmt(f)?;
}
}
Dimension::Assoc(key) => self.with_value(key).hir_fmt(f)?,
Dimension::Dynamic => {}
}
f.write_char(']')
}
Expand Down
114 changes: 89 additions & 25 deletions crates/hir/src/hir_def/expr/data_ty.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
use itertools::Either;
use smallvec::SmallVec;
use syntax::{
SyntaxToken, TokenKind,
SyntaxNode, SyntaxToken, TokenKind,
ast::{self, AstNode},
};

use super::{ExprId, LowerExprCtx, Selector};
use crate::{container::InContainer, hir_def::aggregate::StructId};
use super::{Expr, ExprId, LowerExprCtx, Selector};
use crate::{
container::InContainer,
hir_def::{aggregate::StructId, lower_ident},
};

// slang exposes enum types directly as `DataType::EnumType`, while struct and
// union types share `DataType::StructUnionType` and are lowered by the owning
// declaration/typedef container into `aggregate::StructDef` with a
// `StructKind`. Unpacked dimensions carry SV array shape: `[]` is dynamic,
// `[$]`/`[$:N]` is a queue, and `[string]`/other builtin key types are
// associative. Plain `[expr]` stays a fixed-size unpacked dimension;
// typedef-key and wildcard associative arrays need scope-aware key lowering and
// are left for a later construct PR.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DataTy {
Builtin(BuiltinDataTyId),
Named(NamedDataTy),
Struct(InContainer<StructId>),
Enum,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand All @@ -24,6 +36,8 @@ pub enum BuiltinDataTy {
Vector { kind: VecKind, signing: bool, dimensions: SmallVec<[Option<Dimension>; 2]> },
Real(Real),
String,
Event,
Chandle,
Void,
}

Expand Down Expand Up @@ -66,6 +80,9 @@ pub enum Real {
pub enum Dimension {
Range(ExprId, ExprId),
Size(ExprId),
Queue(Option<ExprId>),
Assoc(ExprId),
Dynamic,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
Expand All @@ -77,19 +94,15 @@ pub enum NamedDataTy {
impl LowerExprCtx<'_> {
pub(crate) fn lower_data_ty(&mut self, ty: ast::DataType) -> DataTy {
use ast::DataType::*;
let ty = match ty {
KeywordType(ty) => Either::Left(self.lower_keyword_ty(ty)),
NamedType(named_type) => Either::Right(self.lower_named_ty(named_type)),
IntegerType(ty) => Either::Left(self.lower_integer_type(ty)),
ImplicitType(ty) => Either::Left(self.lower_implicit_type(ty)),
EnumType(enum_ty) => Either::Right(self.lower_enum_type(enum_ty)),
match ty {
KeywordType(ty) => DataTy::Builtin(self.db.intern_ty(self.lower_keyword_ty(ty))),
NamedType(named_type) => DataTy::Named(self.lower_named_ty(named_type)),
IntegerType(ty) => DataTy::Builtin(self.db.intern_ty(self.lower_integer_type(ty))),
ImplicitType(ty) => DataTy::Builtin(self.db.intern_ty(self.lower_implicit_type(ty))),
EnumType(enum_ty) => self.lower_enum_type(enum_ty),
StructUnionType(_) | TypeReference(_) | VirtualInterfaceType(_) => {
return self.default_data_ty();
self.default_data_ty()
}
};
match ty {
Either::Left(ty) => DataTy::Builtin(self.db.intern_ty(ty)),
Either::Right(ty) => DataTy::Named(ty),
}
}

Expand All @@ -101,6 +114,8 @@ impl LowerExprCtx<'_> {
ShortRealType(_) => BuiltinDataTy::Real(Real::ShortReal),
RealTimeType(_) => BuiltinDataTy::Real(Real::RealTime),
VoidType(_) => BuiltinDataTy::Void,
EventType(_) => BuiltinDataTy::Event,
CHandleType(_) => BuiltinDataTy::Chandle,
_ => BuiltinDataTy::default(),
}
}
Expand All @@ -118,12 +133,8 @@ impl LowerExprCtx<'_> {
}
}

fn lower_enum_type(&mut self, _enum_ty: ast::EnumType) -> NamedDataTy {
// For now, treat enum types as implicit types
// TODO: properly handle enum member completion
// We return a missing expression since enum types are anonymous
let expr_id = self.alloc_missing();
NamedDataTy::Ident(expr_id)
fn lower_enum_type(&mut self, _enum_ty: ast::EnumType) -> DataTy {
DataTy::Enum
}

fn lower_integer_type(&mut self, ty: ast::IntegerType) -> BuiltinDataTy {
Expand Down Expand Up @@ -171,21 +182,74 @@ impl LowerExprCtx<'_> {

pub(crate) fn lower_dimension(&mut self, dim: ast::VariableDimension) -> Option<Dimension> {
use ast::DimensionSpecifier::*;
match dim.specifier()? {
RangeDimensionSpecifier(spec) => match self.lower_selector(spec.selector()) {
Selector::Bit(idx) => Some(Dimension::Size(idx)),
match dim.specifier() {
None => Some(Dimension::Dynamic),
Some(RangeDimensionSpecifier(spec)) => self.lower_range_dimension(spec),
Some(QueueDimensionSpecifier(spec)) => Some(Dimension::Queue(
spec.max_size_clause().map(|clause| self.lower_expr(clause.expr())),
)),
Some(WildcardDimensionSpecifier(_)) => None,
}
}

fn lower_range_dimension(&mut self, spec: ast::RangeDimensionSpecifier) -> Option<Dimension> {
let selector = spec.selector();
if let ast::Selector::BitSelect(bit_select) = selector {
let expr = bit_select.expr();
if let Some(key) = Self::associative_dimension_key_token(expr) {
let expr_id = lower_ident(Some(key))
.map(Expr::Ident)
.map(|expr| self.exprs.alloc(expr))
.unwrap_or_else(|| self.lower_expr(expr));
return Some(Dimension::Assoc(expr_id));
}
Some(Dimension::Size(self.lower_expr(expr)))
} else {
match self.lower_selector(selector) {
Selector::Range(left, right) => Some(Dimension::Range(left, right)),
_ => None,
},
_ => None,
}
}
}

fn associative_dimension_key_token(expr: ast::Expression) -> Option<SyntaxToken> {
let token = first_token(expr.syntax())?;
is_builtin_dimension_key_token(token.kind()).then_some(token)
}

fn default_data_ty(&self) -> DataTy {
DataTy::Builtin(self.db.intern_ty(BuiltinDataTy::default()))
}
}

fn first_token(node: SyntaxNode<'_>) -> Option<SyntaxToken<'_>> {
for idx in 0..node.child_count() {
if let Some(token) = node.child_token(idx) {
return Some(token);
}
if let Some(token) = node.child_node(idx).and_then(first_token) {
return Some(token);
}
}
None
}

fn is_builtin_dimension_key_token(kind: TokenKind) -> bool {
matches!(
kind,
TokenKind::STRING_KEYWORD
| TokenKind::BYTE_KEYWORD
| TokenKind::SHORT_INT_KEYWORD
| TokenKind::INT_KEYWORD
| TokenKind::LONG_INT_KEYWORD
| TokenKind::INTEGER_KEYWORD
| TokenKind::TIME_KEYWORD
| TokenKind::BIT_KEYWORD
| TokenKind::LOGIC_KEYWORD
| TokenKind::REG_KEYWORD
)
}

impl DataTy {
pub(crate) fn is_ast_missing(ty: ast::DataType) -> bool {
match ty {
Expand Down
Loading
Loading