roup/ir/
types.rs

1//! Basic IR types: SourceLocation and Language
2//!
3//! This file introduces fundamental concepts:
4//! - **Structs**: Composite data types with named fields
5//! - **Copy trait**: Types that can be copied bitwise
6//! - **repr(C)**: C-compatible memory layout for FFI
7//! - **Derive macros**: Automatic trait implementations
8//! - **Documentation**: How to document Rust code properly
9
10use std::fmt;
11
12// ============================================================================
13// SourceLocation: Track where code appears in source files
14// ============================================================================
15
16/// Source code location information
17///
18/// Stores the line and column number where a directive or clause appears
19/// in the original source file. This is useful for:
20///
21/// - **Error reporting**: "Error at line 42, column 5"
22/// - **IDE integration**: Jump to source location
23/// - **Debugging**: Know where IR came from
24///
25/// ## Learning: The `Copy` Trait
26///
27/// This struct implements `Copy`, meaning it can be duplicated by simple
28/// bitwise copy (like `memcpy` in C). This is efficient for small types.
29///
30/// Types that implement `Copy` must also implement `Clone`. The difference:
31/// - `Copy`: Implicit duplication (assignment creates a copy)
32/// - `Clone`: Explicit duplication (call `.clone()` method)
33///
34/// ## Learning: `repr(C)`
35///
36/// The `#[repr(C)]` attribute tells Rust to lay out this struct in memory
37/// exactly like C would. This is critical for FFI (Foreign Function Interface).
38///
39/// Without `repr(C)`, Rust might reorder fields for optimization.
40/// With `repr(C)`, fields appear in declaration order.
41///
42/// ## Example
43///
44/// ```
45/// use roup::ir::SourceLocation;
46///
47/// let loc = SourceLocation { line: 42, column: 5 };
48/// let copy = loc; // Implicitly copied due to Copy trait
49/// assert_eq!(loc.line, 42);
50/// assert_eq!(copy.line, 42); // Original still valid
51/// ```
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53#[repr(C)]
54pub struct SourceLocation {
55    /// Line number (1-indexed, as in editors)
56    pub line: u32,
57
58    /// Column number (1-indexed, as in editors)
59    pub column: u32,
60}
61
62impl SourceLocation {
63    /// Create a new source location
64    ///
65    /// ## Example
66    ///
67    /// ```
68    /// use roup::ir::SourceLocation;
69    ///
70    /// let loc = SourceLocation::new(10, 5);
71    /// assert_eq!(loc.line, 10);
72    /// assert_eq!(loc.column, 5);
73    /// ```
74    pub const fn new(line: u32, column: u32) -> Self {
75        Self { line, column }
76    }
77
78    /// Create a location at the start of a file
79    ///
80    /// ## Example
81    ///
82    /// ```
83    /// use roup::ir::SourceLocation;
84    ///
85    /// let start = SourceLocation::start();
86    /// assert_eq!(start.line, 1);
87    /// assert_eq!(start.column, 1);
88    /// ```
89    pub const fn start() -> Self {
90        Self::new(1, 1)
91    }
92}
93
94impl Default for SourceLocation {
95    /// Default location is at the start of a file (1, 1)
96    fn default() -> Self {
97        Self::start()
98    }
99}
100
101impl fmt::Display for SourceLocation {
102    /// Format as "line:column" for error messages
103    ///
104    /// ## Example
105    ///
106    /// ```
107    /// use roup::ir::SourceLocation;
108    ///
109    /// let loc = SourceLocation::new(42, 5);
110    /// assert_eq!(format!("{}", loc), "42:5");
111    /// ```
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{}:{}", self.line, self.column)
114    }
115}
116
117// ============================================================================
118// Language: Identify source programming language
119// ============================================================================
120
121/// Source programming language
122///
123/// OpenMP supports multiple host languages: C, C++, and Fortran.
124/// The IR needs to track the source language because:
125///
126/// - **Pragma syntax**: C/C++ use `#pragma omp`, Fortran uses `!$omp`
127/// - **Expression parsing**: Different languages have different expression syntax
128/// - **Type systems**: Languages have different type semantics
129/// - **Pretty-printing**: Need to output correct syntax for the language
130///
131/// ## Learning: Enums as Tagged Unions
132///
133/// In Rust, `enum` is much more powerful than in C. Each variant can:
134/// - Carry no data (like these variants)
135/// - Carry data (we'll see this in later types)
136/// - Have different types of data per variant
137///
138/// ## Learning: `repr(C)` on Enums
139///
140/// For FFI, we need stable discriminant values. `#[repr(C)]` ensures:
141/// - Discriminant is a C-compatible integer
142/// - Size and alignment match C expectations
143/// - Variants have predictable numeric values
144///
145/// We explicitly assign values (0, 1, 2, 3) so C code can rely on them.
146///
147/// ## Example
148///
149/// ```
150/// use roup::ir::Language;
151///
152/// let lang = Language::C;
153/// assert_eq!(lang as u32, 0);
154///
155/// let cpp = Language::Cpp;
156/// assert_eq!(cpp as u32, 1);
157/// ```
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159#[repr(C)]
160pub enum Language {
161    /// C language
162    ///
163    /// Uses `#pragma omp` syntax
164    C = 0,
165
166    /// C++ language
167    ///
168    /// Uses `#pragma omp` syntax (same as C)
169    Cpp = 1,
170
171    /// Fortran language
172    ///
173    /// Uses `!$omp` syntax
174    Fortran = 2,
175
176    /// Unknown or unspecified language
177    ///
178    /// Used when language cannot be determined
179    Unknown = 3,
180}
181
182impl Language {
183    /// Get the pragma prefix for this language
184    ///
185    /// Returns the string used to start OpenMP directives.
186    ///
187    /// ## Example
188    ///
189    /// ```
190    /// use roup::ir::Language;
191    ///
192    /// assert_eq!(Language::C.pragma_prefix(), "#pragma omp ");
193    /// assert_eq!(Language::Cpp.pragma_prefix(), "#pragma omp ");
194    /// assert_eq!(Language::Fortran.pragma_prefix(), "!$omp ");
195    /// ```
196    pub const fn pragma_prefix(self) -> &'static str {
197        match self {
198            Language::C | Language::Cpp => "#pragma omp ",
199            Language::Fortran => "!$omp ",
200            Language::Unknown => "#pragma omp ", // Default to C syntax
201        }
202    }
203
204    /// Check if this language uses C-style syntax
205    ///
206    /// Both C and C++ use the same OpenMP syntax.
207    ///
208    /// ## Example
209    ///
210    /// ```
211    /// use roup::ir::Language;
212    ///
213    /// assert!(Language::C.is_c_family());
214    /// assert!(Language::Cpp.is_c_family());
215    /// assert!(!Language::Fortran.is_c_family());
216    /// ```
217    pub const fn is_c_family(self) -> bool {
218        matches!(self, Language::C | Language::Cpp)
219    }
220
221    /// Check if this language is Fortran
222    ///
223    /// ## Example
224    ///
225    /// ```
226    /// use roup::ir::Language;
227    ///
228    /// assert!(!Language::C.is_fortran());
229    /// assert!(Language::Fortran.is_fortran());
230    /// ```
231    pub const fn is_fortran(self) -> bool {
232        matches!(self, Language::Fortran)
233    }
234}
235
236impl Default for Language {
237    /// Default to unknown language
238    fn default() -> Self {
239        Language::Unknown
240    }
241}
242
243impl fmt::Display for Language {
244    /// Format language name for display
245    ///
246    /// ## Example
247    ///
248    /// ```
249    /// use roup::ir::Language;
250    ///
251    /// assert_eq!(format!("{}", Language::C), "C");
252    /// assert_eq!(format!("{}", Language::Cpp), "C++");
253    /// assert_eq!(format!("{}", Language::Fortran), "Fortran");
254    /// ```
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match self {
257            Language::C => write!(f, "C"),
258            Language::Cpp => write!(f, "C++"),
259            Language::Fortran => write!(f, "Fortran"),
260            Language::Unknown => write!(f, "Unknown"),
261        }
262    }
263}
264
265// ============================================================================
266// Tests
267// ============================================================================
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272
273    // ------------------------------------------------------------------------
274    // SourceLocation tests
275    // ------------------------------------------------------------------------
276
277    #[test]
278    fn source_location_new_creates_correct_location() {
279        let loc = SourceLocation::new(42, 5);
280        assert_eq!(loc.line, 42);
281        assert_eq!(loc.column, 5);
282    }
283
284    #[test]
285    fn source_location_start_is_one_one() {
286        let start = SourceLocation::start();
287        assert_eq!(start.line, 1);
288        assert_eq!(start.column, 1);
289    }
290
291    #[test]
292    fn source_location_default_is_start() {
293        let default = SourceLocation::default();
294        let start = SourceLocation::start();
295        assert_eq!(default, start);
296    }
297
298    #[test]
299    fn source_location_copy_works() {
300        let loc1 = SourceLocation::new(10, 20);
301        let loc2 = loc1; // Should copy, not move
302        assert_eq!(loc1.line, 10); // loc1 still valid
303        assert_eq!(loc2.line, 10); // loc2 has same value
304    }
305
306    #[test]
307    fn source_location_display_formats_correctly() {
308        let loc = SourceLocation::new(42, 5);
309        assert_eq!(format!("{loc}"), "42:5");
310    }
311
312    #[test]
313    fn source_location_equality_works() {
314        let loc1 = SourceLocation::new(42, 5);
315        let loc2 = SourceLocation::new(42, 5);
316        let loc3 = SourceLocation::new(42, 6);
317
318        assert_eq!(loc1, loc2);
319        assert_ne!(loc1, loc3);
320    }
321
322    // ------------------------------------------------------------------------
323    // Language tests
324    // ------------------------------------------------------------------------
325
326    #[test]
327    fn language_has_correct_discriminants() {
328        assert_eq!(Language::C as u32, 0);
329        assert_eq!(Language::Cpp as u32, 1);
330        assert_eq!(Language::Fortran as u32, 2);
331        assert_eq!(Language::Unknown as u32, 3);
332    }
333
334    #[test]
335    fn language_pragma_prefix_c_and_cpp() {
336        assert_eq!(Language::C.pragma_prefix(), "#pragma omp ");
337        assert_eq!(Language::Cpp.pragma_prefix(), "#pragma omp ");
338    }
339
340    #[test]
341    fn language_pragma_prefix_fortran() {
342        assert_eq!(Language::Fortran.pragma_prefix(), "!$omp ");
343    }
344
345    #[test]
346    fn language_pragma_prefix_unknown_defaults_to_c() {
347        assert_eq!(Language::Unknown.pragma_prefix(), "#pragma omp ");
348    }
349
350    #[test]
351    fn language_is_c_family() {
352        assert!(Language::C.is_c_family());
353        assert!(Language::Cpp.is_c_family());
354        assert!(!Language::Fortran.is_c_family());
355        assert!(!Language::Unknown.is_c_family());
356    }
357
358    #[test]
359    fn language_is_fortran() {
360        assert!(!Language::C.is_fortran());
361        assert!(!Language::Cpp.is_fortran());
362        assert!(Language::Fortran.is_fortran());
363        assert!(!Language::Unknown.is_fortran());
364    }
365
366    #[test]
367    fn language_display_formats_correctly() {
368        assert_eq!(format!("{}", Language::C), "C");
369        assert_eq!(format!("{}", Language::Cpp), "C++");
370        assert_eq!(format!("{}", Language::Fortran), "Fortran");
371        assert_eq!(format!("{}", Language::Unknown), "Unknown");
372    }
373
374    #[test]
375    fn language_default_is_unknown() {
376        assert_eq!(Language::default(), Language::Unknown);
377    }
378
379    #[test]
380    fn language_copy_works() {
381        let lang1 = Language::C;
382        let lang2 = lang1; // Should copy, not move
383        assert_eq!(lang1, Language::C); // lang1 still valid
384        assert_eq!(lang2, Language::C); // lang2 has same value
385    }
386
387    #[test]
388    fn language_equality_works() {
389        assert_eq!(Language::C, Language::C);
390        assert_ne!(Language::C, Language::Cpp);
391        assert_ne!(Language::Fortran, Language::Unknown);
392    }
393}