lamina_ir/types.rs
1//! # Type System
2//!
3//! Complete type system for the Lamina IR, including
4//! primitive types, composite types, and value representations.
5//!
6//! ## Type Categories
7//!
8//! ### Primitive Types
9//! - **Integers**: Signed and unsigned integers of various sizes
10//! - **Floats**: Single and double precision floating point numbers
11//! - **Booleans**: True/false values
12//! - **Characters**: Single character values
13//! - **Pointers**: Generic pointer type for memory addresses
14//!
15//! ### Composite Types
16//! - **Arrays**: Fixed-size sequences of elements of the same type
17//! - **Structs**: Named collections of fields with different types
18//! - **Tuples**: Anonymous collections of values with different types
19//! - **Named Types**: User-defined type aliases
20//!
21//! ### Special Types
22//! - **Void**: Represents the absence of a value (for functions that don't return)
23//!
24//! ## Value Representation
25//!
26//! Values in the IR can be:
27//! - **Variables**: SSA variables like `%result`
28//! - **Constants**: Literal values like `42`, `true`, `"hello"`
29//! - **Globals**: Global variables like `@message`
30//!
31//! ## FEAT:TODO - Missing Features Compared to LLVM IR
32//!
33//! ### Type System Gaps:
34//! - **Vector/SIMD types**: `<4 x i32>` for SIMD operations
35//! - **Function types**: First-class function types like `i32 (i32, i32)*`
36//! - **Address spaces**: Memory spaces like `i32 addrspace(1)*` for GPUs
37//! - **Packed structs**: `<{ i32, i8 }>` for compact memory layout
38//! - **Union types**: Sum types for different representations
39//! - **Opaque types**: Forward declarations like `type opaque`
40//! - **Type aliases**: Named type abbreviations
41//! - **Metadata types**: Types for debug and optimization info
42//!
43//! ### Missing / Planned Instructions:
44//! - **Floating point**: additional ops beyond the existing arithmetic and comparisons
45//! - **Memory intrinsics**: `memcpy`, `memset`, `memmove`
46//! - **Atomic operations**: richer `atomicrmw`/`cmpxchg`/`fence` variants
47//! - **Exception handling**: `invoke`, `landingpad`, `resume`
48//! - **Control flow**: `switch`, `indirectbr`
49//! - **Vector operations**: additional SIMD arithmetic and operations (kept behind `nightly`)
50//!
51//! ### Advanced Features:
52//! - **Debug information**: `!dbg`, `!llvm.dbg.value`
53//! - **Module features**: `comdat`, `section`, `alignment`
54//! - **Optimization hints**: `!invariant.load`, `!dereferenceable`
55//! - **Target intrinsics**: Architecture-specific operations
56//! - **Profile-guided optimization**: Branch weights and profiling data
57//!
58//! ## IMPLEMENTED: Raw I/O Support
59//!
60//! Lamina provides raw I/O operations for direct system-level
61//! input and output without stdlib dependencies:
62//!
63//! ### I/O Instruction Categories:
64//!
65//! #### **Byte-Level I/O**:
66//! - **`writebyte`**: Write single byte (low 8 bits) to stdout; canonical for portable output
67//! - **`readbyte`**: Read single byte from stdin
68//! - **Use case**: Character I/O, text output, keyboard input
69//!
70//! #### **Buffer-Level I/O**:
71//! - **`write`**: Write buffer contents to stdout
72//! - **`read`**: Read data into buffer from stdin
73//! - **Use case**: Bulk data transfer, file operations, network I/O
74//!
75//! #### **Memory-Value I/O** (NEW):
76//! - **`writeptr`**: Write VALUE stored at pointer to stdout
77//! //! - **Use case**: Memory inspection, binary data output, serialization
78//!
79//! ### WritePtr Semantics (IMPORTANT):
80//!
81//! **What it does:**
82//! ```text
83//! Memory: [42] <- pointer points here
84//! writeptr(pointer) -> outputs: 42 (binary)
85//! ```
86//!
87//! **NOT the pointer address, but the VALUE stored at that address!**
88//!
89//! **Example:**
90//! ```lamina
91//! %buffer = alloc.stack i32 # Allocate memory
92//! store.i32 %buffer, 72 # Store 72 ('H' in ASCII)
93//! %result = writeptr %buffer # Outputs: 'H' (binary 72)
94//! ```
95//!
96//! **Common misconception:**
97//! - `writeptr(ptr)` does NOT write the pointer address
98//! - `writeptr(ptr)` writes the VALUE stored at that address
99//! - Think of it as "write pointer's value", not "write pointer"
100//!
101//! ### I/O Operation Comparison:
102//!
103//! | Instruction | Input/Output | Data Format | Use Case |
104//! |-------------|--------------|-------------|----------|
105//! | `writebyte` | Single char | ASCII | Simple text |
106//! | `write` | Buffer | Raw bytes | Bulk data |
107//! | `writeptr` | Memory value | Binary | Memory inspection |
108//!
109//! ### Memory Workflow Example:
110//!
111//! ```lamina
112//! # Step 1: Allocate memory
113//! %data = alloc.stack i32
114//!
115//! # Step 2: Store a value
116//! store.i32 %data, 65 # ASCII 'A'
117//!
118//! # Step 3: Load the value (optional, for verification)
119//! %value = load.i32 %data
120//!
121//! # Step 4: Output the stored value
122//! %bytes = writeptr %data # Outputs: 'A'
123//! ```
124//!
125//! This workflow demonstrates the complete memory manipulation cycle
126//! that forms the foundation of all data processing in Lamina.
127//!
128//! ## Type Safety
129//!
130//! The type system provides:
131//! - **Type checking**: Operations are only performed on compatible types
132//! - **Memory safety**: Proper handling of pointers and memory access
133//! - **Correctness**: Many classes of errors are caught at compile time
134//!
135//! ## Value States (Semantics Contract)
136//!
137//! The IR distinguishes three special value states that every optimization pass
138//! and code generator must handle correctly:
139//!
140//! ### Undef
141//! An *undef* value represents an uninitialized read — any bit pattern is valid.
142//! Optimizers may replace every occurrence of an undef with any concrete value
143//! of the matching type. Code that reads from uninitialized memory produces
144//! undef. Undef propagates only when the result of an operation is entirely
145//! determined by an undef operand (e.g. `load` of an un-stored alloc).
146//!
147//! ### Poison
148//! A *poison* value is produced when an instruction's preconditions are
149//! violated but the violation is not observable at the point of production —
150//! for example, integer overflow in a signed `add`, an out-of-range shift
151//! count, or using `undef` as an operand to a pure arithmetic operation.
152//! Poison propagates eagerly through pure operations: any instruction whose
153//! operands include poison produces poison. A poison value that reaches a
154//! side-effecting instruction (store, branch condition, return value, call
155//! argument) causes **undefined behavior** — the compiler is free to assume
156//! this path is unreachable. Optimizations may exploit this freedom.
157//!
158//! ### Trap
159//! A *trap* terminates the process at the point of the offending instruction.
160//! The following operations always trap on violation and never produce poison:
161//! - Integer division or remainder by zero (`BinaryOp::Div`, `BinaryOp::Rem`)
162//! - Null-pointer dereference (`Load` / `Store` through a null `Ptr`)
163//! - Out-of-bounds memory access (if bounds information is available)
164//!
165//! All other precondition violations produce poison (see individual instruction
166//! doc comments for details).
167//!
168//! ## Examples
169//!
170//! ```rust
171//! use lamina_ir::{IRBuilder, Type, PrimitiveType, StructField, BinaryOp, CmpOp};
172//! use lamina_ir::builder::{var, i32, bool};
173//!
174//! let mut builder = IRBuilder::new();
175//! builder
176//! .function("type_demo", Type::Void)
177//! // Primitive types
178//! .binary(BinaryOp::Add, "sum", PrimitiveType::I32, i32(10), i32(20))
179//! // Array type
180//! .alloc_stack("arr", Type::Array {
181//! element_type: Box::new(Type::Primitive(PrimitiveType::I32)),
182//! size: 10
183//! })
184//! // Struct type
185//! .alloc_stack("point", Type::Struct(vec![
186//! StructField { name: "x", ty: Type::Primitive(PrimitiveType::I32) },
187//! StructField { name: "y", ty: Type::Primitive(PrimitiveType::I32) }
188//! ]))
189//! .ret_void();
190//! ```
191
192use std::fmt;
193
194/// A type alias for identifiers in the IR.
195///
196/// Uses `&'a str` to avoid allocating `String` objects
197/// and memory efficiency. The lifetime parameter keeps identifiers
198/// valid as long as the source text they were parsed from.
199///
200/// Identifiers are used for variable names, function names, and other symbols.
201/// Using string references avoids unnecessary allocations and keeps the IR lightweight.
202pub type Identifier<'a> = &'a str;
203/// A type alias for basic block labels in the IR.
204///
205/// Labels are used to identify basic blocks for control flow operations
206/// like branches and jumps.
207pub type Label<'a> = &'a str;
208
209/// Primitive types in the Lamina IR.
210///
211/// Primitive types are the fundamental building blocks of the type system.
212/// They represent basic data types that can be directly operated on by
213/// the target architecture.
214///
215/// # Integer Types
216///
217/// - **Signed integers**: `I8`, `I16`, `I32`, `I64`
218/// - **Unsigned integers**: `U8`, `U16`, `U32`, `U64`
219///
220/// # Floating Point Types
221///
222/// - **Single precision**: `F32` (32-bit IEEE 754)
223/// - **Double precision**: `F64` (64-bit IEEE 754)
224///
225/// # Other Types
226///
227/// - **Boolean**: `Bool` (true/false)
228/// - **Character**: `Char` (8-bit character)
229/// - **Pointer**: `Ptr` (generic pointer type)
230///
231/// # Examples
232///
233/// ```rust
234/// use lamina_ir::{IRBuilder, Type, PrimitiveType, BinaryOp, CmpOp};
235/// use lamina_ir::builder::{var, i32, bool};
236///
237/// let mut builder = IRBuilder::new();
238/// builder
239/// .function("primitives", Type::Void)
240/// .binary(BinaryOp::Add, "sum", PrimitiveType::I32, i32(10), i32(20))
241/// .cmp(CmpOp::Gt, "is_positive", PrimitiveType::I32, var("sum"), i32(0))
242/// .ret_void();
243/// ```
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
245pub enum PrimitiveType {
246 /// 8-bit signed integer (-128 to 127)
247 I8,
248 /// 16-bit signed integer (-32,768 to 32,767)
249 I16,
250 /// 32-bit signed integer (-2,147,483,648 to 2,147,483,647)
251 I32,
252 /// 64-bit signed integer (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
253 I64,
254 /// 8-bit unsigned integer (0 to 255)
255 U8,
256 /// 16-bit unsigned integer (0 to 65,535)
257 U16,
258 /// 32-bit unsigned integer (0 to 4,294,967,295)
259 U32,
260 /// 64-bit unsigned integer (0 to 18,446,744,073,709,551,615)
261 U64,
262 /// 32-bit floating point number (IEEE 754 single precision)
263 F32,
264 /// 64-bit floating point number (IEEE 754 double precision)
265 F64,
266 /// Boolean value (true or false)
267 Bool,
268 /// Single character (8-bit)
269 Char,
270 /// Generic pointer type for memory addresses
271 Ptr,
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, Hash)]
275pub struct StructField<'a> {
276 pub name: Identifier<'a>,
277 pub ty: Type<'a>,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
281pub enum Type<'a> {
282 Primitive(PrimitiveType),
283 Named(Identifier<'a>), // `@Vec2`
284 Array {
285 element_type: Box<Type<'a>>,
286 size: u64,
287 },
288 #[cfg(feature = "nightly")]
289 /// SIMD vector type: `<N x T>` where N is the number of lanes and T is the element type
290 /// Examples: `<4 x i32>`, `<8 x f32>`, `<2 x i64>`
291 Vector {
292 element_type: PrimitiveType,
293 lanes: u32,
294 },
295 Struct(Vec<StructField<'a>>),
296 Tuple(Vec<Type<'a>>),
297 Void,
298}
299
300#[derive(Debug, Clone, PartialEq)] // Float comparison requires PartialEq only
301pub enum Literal<'a> {
302 I8(i8),
303 I16(i16),
304 I32(i32),
305 I64(i64),
306 U8(u8),
307 U16(u16),
308 U32(u32),
309 U64(u64),
310 F32(f32),
311 F64(f64),
312 Bool(bool),
313 Char(char),
314 String(&'a str),
315}
316
317#[derive(Debug, Clone, PartialEq)] // Removed Eq, Hash because f32 doesn't support them
318pub enum Value<'a> {
319 Variable(Identifier<'a>),
320 Constant(Literal<'a>),
321 Global(Identifier<'a>),
322}
323
324impl PrimitiveType {
325 /// Returns all valid primitive type names as strings.
326 pub fn all_names() -> &'static [&'static str] {
327 const fn name_for(ty: PrimitiveType) -> &'static str {
328 ty.as_str()
329 }
330 const NAMES: &[&str] = &[
331 name_for(PrimitiveType::I8),
332 name_for(PrimitiveType::I16),
333 name_for(PrimitiveType::I32),
334 name_for(PrimitiveType::I64),
335 name_for(PrimitiveType::U8),
336 name_for(PrimitiveType::U16),
337 name_for(PrimitiveType::U32),
338 name_for(PrimitiveType::U64),
339 name_for(PrimitiveType::F32),
340 name_for(PrimitiveType::F64),
341 name_for(PrimitiveType::Bool),
342 name_for(PrimitiveType::Char),
343 name_for(PrimitiveType::Ptr),
344 ];
345 NAMES
346 }
347
348 /// Returns the string representation of this primitive type.
349 pub const fn as_str(&self) -> &'static str {
350 match self {
351 PrimitiveType::I8 => "i8",
352 PrimitiveType::I16 => "i16",
353 PrimitiveType::I32 => "i32",
354 PrimitiveType::I64 => "i64",
355 PrimitiveType::U8 => "u8",
356 PrimitiveType::U16 => "u16",
357 PrimitiveType::U32 => "u32",
358 PrimitiveType::U64 => "u64",
359 PrimitiveType::F32 => "f32",
360 PrimitiveType::F64 => "f64",
361 PrimitiveType::Bool => "bool",
362 PrimitiveType::Char => "char",
363 PrimitiveType::Ptr => "ptr",
364 }
365 }
366}
367
368impl fmt::Display for PrimitiveType {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 write!(f, "{}", self.as_str())
371 }
372}
373
374impl fmt::Display for Type<'_> {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 match self {
377 Type::Primitive(pt) => write!(f, "{}", pt),
378 Type::Named(id) => write!(f, "@{}", id),
379 Type::Array { element_type, size } => write!(f, "[{} x {}]", size, element_type),
380 #[cfg(feature = "nightly")]
381 Type::Vector {
382 element_type,
383 lanes,
384 } => write!(f, "<{} x {}>", lanes, element_type),
385 Type::Struct(fields) => {
386 write!(f, "struct {{ ")?;
387 for (i, field) in fields.iter().enumerate() {
388 write!(f, "{}: {}", field.name, field.ty)?;
389 if i < fields.len() - 1 {
390 write!(f, ", ")?;
391 }
392 }
393 write!(f, " }}")
394 }
395 Type::Tuple(types) => {
396 write!(f, "tuple(")?;
397 for (i, ty) in types.iter().enumerate() {
398 write!(f, "{}", ty)?;
399 if i < types.len() - 1 {
400 write!(f, ", ")?;
401 }
402 }
403 write!(f, ")")
404 }
405 Type::Void => write!(f, "void"),
406 }
407 }
408}
409
410impl fmt::Display for Literal<'_> {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 match self {
413 Literal::I8(v) => write!(f, "{}", v),
414 Literal::I16(v) => write!(f, "{}", v),
415 Literal::I32(v) => write!(f, "{}", v),
416 Literal::I64(v) => write!(f, "{}", v),
417 Literal::U8(v) => write!(f, "{}", v),
418 Literal::U16(v) => write!(f, "{}", v),
419 Literal::U32(v) => write!(f, "{}", v),
420 Literal::U64(v) => write!(f, "{}", v),
421 Literal::F32(v) => write!(f, "{}", v),
422 Literal::F64(v) => write!(f, "{}", v),
423 Literal::Bool(v) => write!(f, "{}", v),
424 Literal::Char(c) => write!(f, "'{}'", c),
425 Literal::String(s) => write!(f, "\"{}\"", s), // Note: needs escaping for proper output
426 }
427 }
428}
429
430impl fmt::Display for Value<'_> {
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 match self {
433 Value::Variable(id) => write!(f, "%{id}"),
434 Value::Constant(lit) => write!(f, "{}", lit),
435 Value::Global(id) => write!(f, "@{id}"),
436 }
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn test_display_primitive_type() {
446 assert_eq!(format!("{}", PrimitiveType::I8), "i8");
447 assert_eq!(format!("{}", PrimitiveType::I16), "i16");
448 assert_eq!(format!("{}", PrimitiveType::I32), "i32");
449 assert_eq!(format!("{}", PrimitiveType::I64), "i64");
450 assert_eq!(format!("{}", PrimitiveType::U8), "u8");
451 assert_eq!(format!("{}", PrimitiveType::U16), "u16");
452 assert_eq!(format!("{}", PrimitiveType::U32), "u32");
453 assert_eq!(format!("{}", PrimitiveType::U64), "u64");
454 assert_eq!(format!("{}", PrimitiveType::F32), "f32");
455 assert_eq!(format!("{}", PrimitiveType::F64), "f64");
456 assert_eq!(format!("{}", PrimitiveType::Bool), "bool");
457 assert_eq!(format!("{}", PrimitiveType::Char), "char");
458 assert_eq!(format!("{}", PrimitiveType::Ptr), "ptr");
459 }
460
461 #[test]
462 fn test_display_type() {
463 assert_eq!(format!("{}", Type::Primitive(PrimitiveType::I64)), "i64");
464 assert_eq!(format!("{}", Type::Named("MyStruct")), "@MyStruct");
465 let arr_type = Type::Array {
466 element_type: Box::new(Type::Primitive(PrimitiveType::I8)),
467 size: 10,
468 };
469 assert_eq!(format!("{}", arr_type), "[10 x i8]");
470 let struct_type = Type::Struct(vec![
471 StructField {
472 name: "x",
473 ty: Type::Primitive(PrimitiveType::F32),
474 },
475 StructField {
476 name: "y",
477 ty: Type::Primitive(PrimitiveType::F32),
478 },
479 ]);
480 assert_eq!(format!("{}", struct_type), "struct { x: f32, y: f32 }");
481 let tuple_type = Type::Tuple(vec![
482 Type::Primitive(PrimitiveType::I32),
483 Type::Named("MyBool"),
484 ]);
485 assert_eq!(format!("{}", tuple_type), "tuple(i32, @MyBool)");
486 assert_eq!(format!("{}", Type::Void), "void");
487 #[cfg(feature = "nightly")]
488 {
489 let vec_type = Type::Vector {
490 element_type: PrimitiveType::I32,
491 lanes: 4,
492 };
493 assert_eq!(format!("{}", vec_type), "<4 x i32>");
494 let vec_f32 = Type::Vector {
495 element_type: PrimitiveType::F32,
496 lanes: 8,
497 };
498 assert_eq!(format!("{}", vec_f32), "<8 x f32>");
499 }
500 }
501
502 #[test]
503 fn test_display_literal() {
504 assert_eq!(format!("{}", Literal::I8(123)), "123");
505 assert_eq!(format!("{}", Literal::I16(-456)), "-456");
506 assert_eq!(format!("{}", Literal::I32(123)), "123");
507 assert_eq!(format!("{}", Literal::I64(-456)), "-456");
508 assert_eq!(format!("{}", Literal::U8(123)), "123");
509 assert_eq!(format!("{}", Literal::U16(456)), "456");
510 assert_eq!(format!("{}", Literal::U32(123)), "123");
511 assert_eq!(format!("{}", Literal::U64(456)), "456");
512 assert_eq!(format!("{}", Literal::F32(1.25)), "1.25");
513 assert_eq!(format!("{}", Literal::F64(2.5)), "2.5");
514 assert_eq!(format!("{}", Literal::Bool(true)), "true");
515 assert_eq!(format!("{}", Literal::Bool(false)), "false");
516 assert_eq!(format!("{}", Literal::Char('A')), "'A'");
517 assert_eq!(format!("{}", Literal::String("hello")), "\"hello\"");
518 }
519
520 #[test]
521 fn test_display_value() {
522 assert_eq!(format!("{}", Value::Variable("tmp1")), "%tmp1");
523 assert_eq!(format!("{}", Value::Constant(Literal::I32(42))), "42");
524 assert_eq!(
525 format!("{}", Value::Constant(Literal::String("world"))),
526 "\"world\""
527 );
528 assert_eq!(
529 format!("{}", Value::Global("my_global_var")),
530 "@my_global_var"
531 );
532 }
533}