joule_ast/
generics.rs

1//! Generic type parameters for Joule AST
2//!
3//! This module defines AST nodes for generic type parameters, bounds, and arguments.
4
5use crate::{Ident, Path, Type};
6use joule_common::Span;
7
8/// Generic parameters on a function or type definition: `<T, U: Clone>`
9#[derive(Debug, Clone, PartialEq)]
10pub struct GenericParams {
11    pub params: Vec<GenericParam>,
12    pub span: Span,
13}
14
15impl GenericParams {
16    pub const fn new(params: Vec<GenericParam>, span: Span) -> Self {
17        Self { params, span }
18    }
19
20    pub const fn empty() -> Self {
21        Self {
22            params: Vec::new(),
23            span: Span::dummy(),
24        }
25    }
26
27    pub const fn is_empty(&self) -> bool {
28        self.params.is_empty()
29    }
30
31    pub const fn len(&self) -> usize {
32        self.params.len()
33    }
34}
35
36impl Default for GenericParams {
37    fn default() -> Self {
38        Self::empty()
39    }
40}
41
42/// A single generic parameter: `T` or `T: Clone + Debug`
43#[derive(Debug, Clone, PartialEq)]
44pub struct GenericParam {
45    pub name: Ident,
46    pub bounds: Vec<TypeBound>,
47    pub default: Option<Box<Type>>,
48    pub span: Span,
49}
50
51impl GenericParam {
52    pub const fn new(name: Ident, span: Span) -> Self {
53        Self {
54            name,
55            bounds: Vec::new(),
56            default: None,
57            span,
58        }
59    }
60
61    pub const fn with_bounds(name: Ident, bounds: Vec<TypeBound>, span: Span) -> Self {
62        Self {
63            name,
64            bounds,
65            default: None,
66            span,
67        }
68    }
69
70    pub fn with_default(name: Ident, bounds: Vec<TypeBound>, default: Type, span: Span) -> Self {
71        Self {
72            name,
73            bounds,
74            default: Some(Box::new(default)),
75            span,
76        }
77    }
78}
79
80/// A type bound on a generic parameter: `Clone` or `Iterator<Item = T>`
81#[derive(Debug, Clone, PartialEq)]
82pub struct TypeBound {
83    pub trait_path: Path,
84    pub generic_args: Option<GenericArgs>,
85    pub span: Span,
86}
87
88impl TypeBound {
89    pub const fn new(trait_path: Path, span: Span) -> Self {
90        Self {
91            trait_path,
92            generic_args: None,
93            span,
94        }
95    }
96
97    pub const fn with_args(trait_path: Path, generic_args: GenericArgs, span: Span) -> Self {
98        Self {
99            trait_path,
100            generic_args: Some(generic_args),
101            span,
102        }
103    }
104}
105
106/// Generic arguments at a use site: `<i32, String>` or `<T = i32>`
107#[derive(Debug, Clone, PartialEq)]
108pub struct GenericArgs {
109    pub args: Vec<GenericArg>,
110    pub span: Span,
111}
112
113impl GenericArgs {
114    pub const fn new(args: Vec<GenericArg>, span: Span) -> Self {
115        Self { args, span }
116    }
117
118    pub const fn empty() -> Self {
119        Self {
120            args: Vec::new(),
121            span: Span::dummy(),
122        }
123    }
124
125    pub const fn is_empty(&self) -> bool {
126        self.args.is_empty()
127    }
128
129    pub const fn len(&self) -> usize {
130        self.args.len()
131    }
132}
133
134impl Default for GenericArgs {
135    fn default() -> Self {
136        Self::empty()
137    }
138}
139
140/// A single generic argument
141#[derive(Debug, Clone, PartialEq)]
142pub enum GenericArg {
143    /// A type argument: `i32`
144    Type(Type),
145    /// A const argument: `10` (for array sizes, etc.)
146    Const(Box<crate::Expr>),
147    /// An associated type binding: `Item = i32`
148    Binding { name: Ident, ty: Type },
149}
150
151impl GenericArg {
152    pub fn span(&self) -> Span {
153        match self {
154            Self::Type(ty) => ty.span(),
155            Self::Const(expr) => expr.span(),
156            Self::Binding { name, ty } => name.span.to(ty.span()),
157        }
158    }
159}
160
161/// Where clause for additional bounds: `where T: Clone, U: Debug`
162#[derive(Debug, Clone, PartialEq)]
163pub struct WhereClause {
164    pub predicates: Vec<WherePredicate>,
165    pub span: Span,
166}
167
168impl WhereClause {
169    pub const fn new(predicates: Vec<WherePredicate>, span: Span) -> Self {
170        Self { predicates, span }
171    }
172
173    pub const fn empty() -> Self {
174        Self {
175            predicates: Vec::new(),
176            span: Span::dummy(),
177        }
178    }
179
180    pub const fn is_empty(&self) -> bool {
181        self.predicates.is_empty()
182    }
183}
184
185impl Default for WhereClause {
186    fn default() -> Self {
187        Self::empty()
188    }
189}
190
191/// A single predicate in a where clause: `T: Clone + Debug`
192#[derive(Debug, Clone, PartialEq)]
193pub struct WherePredicate {
194    pub ty: Type,
195    pub bounds: Vec<TypeBound>,
196    pub span: Span,
197}
198
199impl WherePredicate {
200    pub const fn new(ty: Type, bounds: Vec<TypeBound>, span: Span) -> Self {
201        Self { ty, bounds, span }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use joule_common::Symbol;
209
210    fn make_ident(name: &str) -> Ident {
211        Ident::new(Symbol::intern(name), Span::dummy())
212    }
213
214    fn make_path(name: &str) -> Path {
215        Path {
216            segments: vec![make_ident(name)],
217            span: Span::dummy(),
218        }
219    }
220
221    #[test]
222    fn test_empty_generic_params() {
223        let params = GenericParams::empty();
224        assert!(params.is_empty());
225        assert_eq!(params.len(), 0);
226    }
227
228    #[test]
229    fn test_generic_param_creation() {
230        let name = make_ident("T");
231        let param = GenericParam::new(name.clone(), Span::dummy());
232        assert_eq!(param.name, name);
233        assert!(param.bounds.is_empty());
234        assert!(param.default.is_none());
235    }
236
237    #[test]
238    fn test_generic_param_with_bounds() {
239        let name = make_ident("T");
240        let bound = TypeBound::new(make_path("Clone"), Span::dummy());
241        let param = GenericParam::with_bounds(name.clone(), vec![bound], Span::dummy());
242        assert_eq!(param.name, name);
243        assert_eq!(param.bounds.len(), 1);
244    }
245
246    #[test]
247    fn test_generic_args() {
248        let ty = Type::Path {
249            path: make_path("i32"),
250            generics: None,
251        };
252        let args = GenericArgs::new(vec![GenericArg::Type(ty)], Span::dummy());
253        assert_eq!(args.len(), 1);
254        assert!(!args.is_empty());
255    }
256
257    #[test]
258    fn test_where_clause() {
259        let ty = Type::Path {
260            path: make_path("T"),
261            generics: None,
262        };
263        let bound = TypeBound::new(make_path("Clone"), Span::dummy());
264        let pred = WherePredicate::new(ty, vec![bound], Span::dummy());
265        let clause = WhereClause::new(vec![pred], Span::dummy());
266        assert!(!clause.is_empty());
267    }
268}