graphcal_compiler/hir/types.rs
1//! HIR type-level reference types.
2//!
3//! The syntax AST preserves source paths (`NamePath` / `IdentPath`) for
4//! type-level references. These HIR types represent the corresponding resolved
5//! boundary: every module-owned reference carries a canonical `ResolvedName`,
6//! while lexical generic parameters carry a `GenericParamId` scoped to their
7//! owning type/function signature.
8
9use crate::registry::time_scale::TimeScale;
10use crate::syntax::ast::{GenericConstraint, MulDivOp};
11use crate::syntax::dimension::Rational;
12use crate::syntax::names::{GenericParamName, ResolvedName, TimeScaleName, namespace};
13use crate::syntax::span::{Span, Spanned};
14
15/// Canonical identity for a generic parameter in a lexical generic scope.
16///
17/// Generic parameters are not module-level symbols, so they should not be
18/// represented as `ResolvedName<GenericParam>`. Their identity is the owning
19/// generic scope plus the parameter leaf name.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub struct GenericParamId {
22 pub owner: GenericParamOwner,
23 pub name: GenericParamName,
24}
25
26impl GenericParamId {
27 /// Create a generic parameter identity from its owner and leaf name.
28 #[must_use]
29 pub const fn new(owner: GenericParamOwner, name: GenericParamName) -> Self {
30 Self { owner, name }
31 }
32}
33
34/// The lexical scope that owns a generic parameter list.
35#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub enum GenericParamOwner {
37 /// Generic parameter on a user-defined `type` declaration.
38 Type(ResolvedName<namespace::StructType>),
39 /// Generic parameter on a function signature.
40 Function(ResolvedName<namespace::Fn>),
41}
42
43/// A resolved generic-parameter definition.
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct GenericParamDef {
46 pub id: Spanned<GenericParamId>,
47 pub constraint: GenericConstraint,
48 pub default: Option<TypeExpr>,
49}
50
51/// Built-in type forms with closed semantic meaning.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53pub enum BuiltinType {
54 /// `Dimensionless`.
55 Dimensionless,
56 /// `Bool`.
57 Bool,
58 /// `Int`.
59 Int,
60 /// `Datetime` or `Datetime<Scale>`.
61 Datetime(TimeScaleName),
62}
63
64impl BuiltinType {
65 /// The default `Datetime` type is UTC.
66 #[must_use]
67 pub const fn datetime_utc() -> Self {
68 Self::Datetime(TimeScaleName::new(TimeScale::UTC))
69 }
70}
71
72/// A resolved type expression that still preserves source-level structure.
73///
74/// This is not TIR's semantic `ResolvedTypeExpr`: HIR keeps references to named
75/// dimensions/types/indexes as canonical identities instead of immediately
76/// collapsing them to registry values such as `Dimension`.
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct TypeExpr {
79 pub kind: TypeExprKind,
80 pub span: Span,
81}
82
83impl TypeExpr {
84 /// Create a HIR type expression.
85 #[must_use]
86 pub const fn new(kind: TypeExprKind, span: Span) -> Self {
87 Self { kind, span }
88 }
89}
90
91/// The resolved shape of a type expression.
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub enum TypeExprKind {
94 /// A built-in type with closed meaning.
95 Builtin(BuiltinType),
96 /// A scalar dimension expression.
97 DimExpr(DimExpr),
98 /// An index name in a type-expression syntactic slot.
99 ///
100 /// This is not a value type. It is accepted only where the surrounding
101 /// generic parameter expects an `Index` argument; declaration annotations
102 /// reject it before TIR construction.
103 Index(IndexRef),
104 /// A user-defined non-generic struct/tagged-union type.
105 Struct(Spanned<ResolvedName<namespace::StructType>>),
106 /// A generic type parameter (`F: Type`).
107 GenericTypeParam(Spanned<GenericParamId>),
108 /// A user-defined generic type application.
109 TypeApplication {
110 name: Spanned<ResolvedName<namespace::StructType>>,
111 type_args: Vec<TypeExpr>,
112 },
113 /// An indexed type expression.
114 Indexed {
115 base: Box<TypeExpr>,
116 indexes: Vec<IndexRef>,
117 },
118}
119
120/// A resolved dimension expression.
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct DimExpr {
123 pub terms: Vec<DimExprItem>,
124 pub span: Span,
125}
126
127/// One term of a dimension expression with its combining operator.
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct DimExprItem {
130 pub op: MulDivOp,
131 pub term: DimTermRef,
132}
133
134/// A resolved dimension term.
135#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct DimTermRef {
137 pub target: DimTermTarget,
138 /// `None` means exponent 1. Rational exponents (`^(1/2)`) are kept exact.
139 pub power: Option<Rational>,
140 pub span: Span,
141}
142
143/// Target of a resolved dimension term.
144#[derive(Debug, Clone, PartialEq, Eq)]
145pub enum DimTermTarget {
146 /// A concrete module-owned dimension declaration.
147 Dimension(Spanned<ResolvedName<namespace::Dim>>),
148 /// A generic dimension parameter (`D: Dim`).
149 GenericParam(Spanned<GenericParamId>),
150}
151
152/// A resolved index reference in an indexed type.
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub enum IndexRef {
155 /// A concrete module-owned index declaration.
156 Concrete(Spanned<ResolvedName<namespace::Index>>),
157 /// A generic index parameter (`I: Index`).
158 GenericParam(Spanned<GenericParamId>),
159 /// A type-level natural-number expression.
160 NatExpr(NatExpr),
161}
162
163/// A resolved type-level natural-number expression.
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub enum NatExpr {
166 /// Integer literal.
167 Literal(u64, Span),
168 /// Generic natural-number parameter (`N: Nat`).
169 Param(Spanned<GenericParamId>),
170 /// Addition.
171 Add(Box<Self>, Box<Self>, Span),
172 /// Multiplication.
173 Mul(Box<Self>, Box<Self>, Span),
174}
175
176impl NatExpr {
177 /// Source span for the expression.
178 #[must_use]
179 pub const fn span(&self) -> Span {
180 match self {
181 Self::Literal(_, span) | Self::Add(_, _, span) | Self::Mul(_, _, span) => *span,
182 Self::Param(param) => param.span,
183 }
184 }
185}