roup/ir/
clause.rs

1//! Clause semantic types for OpenMP IR
2//!
3//! This module defines types for representing OpenMP clause semantics.
4//! It captures the meaning of clauses, not just their syntax.
5//!
6//! ## Learning Objectives
7//!
8//! - **Large enums**: Modeling many alternatives with discriminated unions
9//! - **Semantic modeling**: Capturing intent, not just tokens
10//! - **FFI readiness**: Using `repr(C)` with explicit discriminants
11//! - **Exhaustive matching**: Compiler ensures all cases handled
12//!
13//! ## Design Philosophy
14//!
15//! OpenMP has many clause modifiers that affect behavior:
16//! - Reduction operators: `+`, `*`, `max`, `min`, etc.
17//! - Map types: `to`, `from`, `tofrom`, `alloc`, etc.
18//! - Schedule kinds: `static`, `dynamic`, `guided`, `auto`
19//! - Depend types: `in`, `out`, `inout`, `mutexinoutset`
20//!
21//! Each modifier is represented as a Rust enum with:
22//! 1. Clear variant names (e.g., `ReductionOperator::Add`)
23//! 2. FFI-compatible layout (`repr(C)`)
24//! 3. Explicit discriminant values for C interop
25//! 4. Display trait for pretty-printing
26//!
27//! ## Corner Cases Handled
28//!
29//! - Unknown/custom operators via `Custom` variants
30//! - Language-specific operators (C++ vs Fortran)
31//! - OpenMP version-specific features
32//! - User-defined reduction operators
33
34use std::fmt;
35
36use super::{Expression, Identifier, Variable};
37
38// ============================================================================
39// Reduction Operators (OpenMP 5.2 spec section 5.5.5)
40// ============================================================================
41
42/// Reduction operator for reduction clauses
43///
44/// OpenMP supports built-in reduction operators and user-defined operators.
45/// This enum covers the standard operators defined in the OpenMP specification.
46///
47/// ## Examples
48///
49/// ```
50/// # use roup::ir::ReductionOperator;
51/// let op = ReductionOperator::Add;
52/// assert_eq!(op.to_string(), "+");
53///
54/// let op = ReductionOperator::Max;
55/// assert_eq!(op.to_string(), "max");
56/// ```
57///
58/// ## Learning: FFI-Compatible Enums
59///
60/// The `repr(C)` attribute ensures this enum has the same memory layout
61/// as a C enum, making it safe to pass across FFI boundaries.
62/// Explicit discriminants ensure stable values across versions.
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64#[repr(C)]
65pub enum ReductionOperator {
66    // Arithmetic operators
67    Add = 0,      // +
68    Multiply = 1, // *
69    Subtract = 2, // -
70
71    // Bitwise operators
72    BitwiseAnd = 10, // &
73    BitwiseOr = 11,  // |
74    BitwiseXor = 12, // ^
75
76    // Logical operators
77    LogicalAnd = 20, // &&
78    LogicalOr = 21,  // ||
79
80    // Min/Max operators
81    Min = 30,
82    Max = 31,
83
84    // C++ specific operators (OpenMP 5.2 supports these)
85    MinusEqual = 40, // -= (non-commutative)
86
87    // User-defined reduction operator
88    Custom = 100,
89}
90
91impl fmt::Display for ReductionOperator {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        match self {
94            ReductionOperator::Add => write!(f, "+"),
95            ReductionOperator::Multiply => write!(f, "*"),
96            ReductionOperator::Subtract => write!(f, "-"),
97            ReductionOperator::BitwiseAnd => write!(f, "&"),
98            ReductionOperator::BitwiseOr => write!(f, "|"),
99            ReductionOperator::BitwiseXor => write!(f, "^"),
100            ReductionOperator::LogicalAnd => write!(f, "&&"),
101            ReductionOperator::LogicalOr => write!(f, "||"),
102            ReductionOperator::Min => write!(f, "min"),
103            ReductionOperator::Max => write!(f, "max"),
104            ReductionOperator::MinusEqual => write!(f, "-="),
105            ReductionOperator::Custom => write!(f, "custom"),
106        }
107    }
108}
109
110// ============================================================================
111// Map Type (OpenMP 5.2 spec section 5.8.3)
112// ============================================================================
113
114/// Map type for map clauses in target directives
115///
116/// Specifies how data is mapped between host and device memory.
117///
118/// ## Examples
119///
120/// ```
121/// # use roup::ir::MapType;
122/// let mt = MapType::To;
123/// assert_eq!(mt.to_string(), "to");
124///
125/// let mt = MapType::ToFrom;
126/// assert_eq!(mt.to_string(), "tofrom");
127/// ```
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129#[repr(C)]
130pub enum MapType {
131    /// Map data to device (host → device)
132    To = 0,
133    /// Map data from device (device → host)
134    From = 1,
135    /// Map data to and from device (bidirectional)
136    ToFrom = 2,
137    /// Allocate device memory without transfer
138    Alloc = 3,
139    /// Release device memory
140    Release = 4,
141    /// Delete device memory
142    Delete = 5,
143}
144
145impl fmt::Display for MapType {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        match self {
148            MapType::To => write!(f, "to"),
149            MapType::From => write!(f, "from"),
150            MapType::ToFrom => write!(f, "tofrom"),
151            MapType::Alloc => write!(f, "alloc"),
152            MapType::Release => write!(f, "release"),
153            MapType::Delete => write!(f, "delete"),
154        }
155    }
156}
157
158// ============================================================================
159// Schedule Kind (OpenMP 5.2 spec section 2.9.2)
160// ============================================================================
161
162/// Schedule kind for loop scheduling
163///
164/// Determines how loop iterations are distributed among threads.
165///
166/// ## Examples
167///
168/// ```
169/// # use roup::ir::ScheduleKind;
170/// let sk = ScheduleKind::Static;
171/// assert_eq!(sk.to_string(), "static");
172///
173/// let sk = ScheduleKind::Dynamic;
174/// assert_eq!(sk.to_string(), "dynamic");
175/// ```
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
177#[repr(C)]
178pub enum ScheduleKind {
179    /// Iterations divided into chunks of specified size, assigned statically
180    Static = 0,
181    /// Iterations divided into chunks, assigned dynamically at runtime
182    Dynamic = 1,
183    /// Similar to dynamic but chunk size decreases exponentially
184    Guided = 2,
185    /// Implementation-defined scheduling
186    Auto = 3,
187    /// Runtime determines schedule via environment variable
188    Runtime = 4,
189}
190
191impl fmt::Display for ScheduleKind {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        match self {
194            ScheduleKind::Static => write!(f, "static"),
195            ScheduleKind::Dynamic => write!(f, "dynamic"),
196            ScheduleKind::Guided => write!(f, "guided"),
197            ScheduleKind::Auto => write!(f, "auto"),
198            ScheduleKind::Runtime => write!(f, "runtime"),
199        }
200    }
201}
202
203// ============================================================================
204// Schedule Modifier (OpenMP 5.2 spec section 2.9.2)
205// ============================================================================
206
207/// Schedule modifier for schedule clause
208///
209/// Modifiers that affect how the schedule is applied.
210///
211/// ## Examples
212///
213/// ```
214/// # use roup::ir::ScheduleModifier;
215/// let sm = ScheduleModifier::Monotonic;
216/// assert_eq!(sm.to_string(), "monotonic");
217/// ```
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
219#[repr(C)]
220pub enum ScheduleModifier {
221    /// Iterations assigned in monotonically increasing order
222    Monotonic = 0,
223    /// No ordering guarantee (allows optimizations)
224    Nonmonotonic = 1,
225    /// SIMD execution of iterations
226    Simd = 2,
227}
228
229impl fmt::Display for ScheduleModifier {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        match self {
232            ScheduleModifier::Monotonic => write!(f, "monotonic"),
233            ScheduleModifier::Nonmonotonic => write!(f, "nonmonotonic"),
234            ScheduleModifier::Simd => write!(f, "simd"),
235        }
236    }
237}
238
239// ============================================================================
240// Depend Type (OpenMP 5.2 spec section 2.17.11)
241// ============================================================================
242
243/// Dependence type for task dependencies
244///
245/// Specifies the type of data dependency between tasks.
246///
247/// ## Examples
248///
249/// ```
250/// # use roup::ir::DependType;
251/// let dt = DependType::In;
252/// assert_eq!(dt.to_string(), "in");
253///
254/// let dt = DependType::Inout;
255/// assert_eq!(dt.to_string(), "inout");
256/// ```
257#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
258#[repr(C)]
259pub enum DependType {
260    /// Read dependency
261    In = 0,
262    /// Write dependency
263    Out = 1,
264    /// Read-write dependency
265    Inout = 2,
266    /// Mutual exclusion with inout
267    Mutexinoutset = 3,
268    /// Dependency on task completion
269    Depobj = 4,
270    /// Source dependency (OpenMP 5.0)
271    Source = 5,
272    /// Sink dependency (OpenMP 5.0)
273    Sink = 6,
274}
275
276impl fmt::Display for DependType {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        match self {
279            DependType::In => write!(f, "in"),
280            DependType::Out => write!(f, "out"),
281            DependType::Inout => write!(f, "inout"),
282            DependType::Mutexinoutset => write!(f, "mutexinoutset"),
283            DependType::Depobj => write!(f, "depobj"),
284            DependType::Source => write!(f, "source"),
285            DependType::Sink => write!(f, "sink"),
286        }
287    }
288}
289
290// ============================================================================
291// Default Kind (OpenMP 5.2 spec section 2.9.3.1)
292// ============================================================================
293
294/// Default data-sharing attribute
295///
296/// Specifies the default data-sharing attribute for variables.
297///
298/// ## Examples
299///
300/// ```
301/// # use roup::ir::DefaultKind;
302/// let dk = DefaultKind::Shared;
303/// assert_eq!(dk.to_string(), "shared");
304///
305/// let dk = DefaultKind::None;
306/// assert_eq!(dk.to_string(), "none");
307/// ```
308#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
309#[repr(C)]
310pub enum DefaultKind {
311    /// Variables are shared by default
312    Shared = 0,
313    /// No default (must specify for each variable)
314    None = 1,
315    /// Variables are private by default (Fortran only)
316    Private = 2,
317    /// Variables are firstprivate by default
318    Firstprivate = 3,
319}
320
321impl fmt::Display for DefaultKind {
322    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323        match self {
324            DefaultKind::Shared => write!(f, "shared"),
325            DefaultKind::None => write!(f, "none"),
326            DefaultKind::Private => write!(f, "private"),
327            DefaultKind::Firstprivate => write!(f, "firstprivate"),
328        }
329    }
330}
331
332// ============================================================================
333// Proc Bind (OpenMP 5.2 spec section 2.6.2)
334// ============================================================================
335
336/// Thread affinity policy
337///
338/// Specifies how threads are bound to processors.
339///
340/// ## Examples
341///
342/// ```
343/// # use roup::ir::ProcBind;
344/// let pb = ProcBind::Close;
345/// assert_eq!(pb.to_string(), "close");
346/// ```
347#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
348#[repr(C)]
349pub enum ProcBind {
350    /// Threads execute close to the master thread
351    Master = 0,
352    /// Threads execute close to the master thread (OpenMP 5.1 deprecates 'master')
353    Close = 1,
354    /// Threads spread out across available processors
355    Spread = 2,
356    /// Implementation-defined binding
357    Primary = 3,
358}
359
360impl fmt::Display for ProcBind {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        match self {
363            ProcBind::Master => write!(f, "master"),
364            ProcBind::Close => write!(f, "close"),
365            ProcBind::Spread => write!(f, "spread"),
366            ProcBind::Primary => write!(f, "primary"),
367        }
368    }
369}
370
371// ============================================================================
372// Atomic Default Memory Order (OpenMP 5.2 spec section 2.17.7)
373// ============================================================================
374
375/// Default memory order for atomic operations
376///
377/// Specifies the default memory ordering semantics for atomic operations.
378///
379/// ## Examples
380///
381/// ```
382/// # use roup::ir::MemoryOrder;
383/// let mo = MemoryOrder::SeqCst;
384/// assert_eq!(mo.to_string(), "seq_cst");
385/// ```
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
387#[repr(C)]
388pub enum MemoryOrder {
389    /// Sequential consistency (strongest)
390    SeqCst = 0,
391    /// Acquire-release ordering
392    AcqRel = 1,
393    /// Release ordering
394    Release = 2,
395    /// Acquire ordering
396    Acquire = 3,
397    /// Relaxed ordering (weakest)
398    Relaxed = 4,
399}
400
401impl fmt::Display for MemoryOrder {
402    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403        match self {
404            MemoryOrder::SeqCst => write!(f, "seq_cst"),
405            MemoryOrder::AcqRel => write!(f, "acq_rel"),
406            MemoryOrder::Release => write!(f, "release"),
407            MemoryOrder::Acquire => write!(f, "acquire"),
408            MemoryOrder::Relaxed => write!(f, "relaxed"),
409        }
410    }
411}
412
413// ============================================================================
414// Atomic Operation (OpenMP 5.2 spec section 2.17.7)
415// ============================================================================
416
417/// Atomic operation type
418///
419/// Specifies the type of atomic operation.
420///
421/// ## Examples
422///
423/// ```
424/// # use roup::ir::AtomicOp;
425/// let ao = AtomicOp::Read;
426/// assert_eq!(ao.to_string(), "read");
427/// ```
428#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
429#[repr(C)]
430pub enum AtomicOp {
431    /// Atomic read
432    Read = 0,
433    /// Atomic write
434    Write = 1,
435    /// Atomic update
436    Update = 2,
437    /// Atomic capture
438    Capture = 3,
439}
440
441impl fmt::Display for AtomicOp {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        match self {
444            AtomicOp::Read => write!(f, "read"),
445            AtomicOp::Write => write!(f, "write"),
446            AtomicOp::Update => write!(f, "update"),
447            AtomicOp::Capture => write!(f, "capture"),
448        }
449    }
450}
451
452// ============================================================================
453// Device Type (OpenMP 5.2 spec section 2.14.1)
454// ============================================================================
455
456/// Device type for device-specific constructs
457///
458/// Specifies the target device type.
459///
460/// ## Examples
461///
462/// ```
463/// # use roup::ir::DeviceType;
464/// let dt = DeviceType::Host;
465/// assert_eq!(dt.to_string(), "host");
466///
467/// let dt = DeviceType::Nohost;
468/// assert_eq!(dt.to_string(), "nohost");
469/// ```
470#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
471#[repr(C)]
472pub enum DeviceType {
473    /// Host device
474    Host = 0,
475    /// Non-host device (accelerator)
476    Nohost = 1,
477    /// Any device
478    Any = 2,
479}
480
481impl fmt::Display for DeviceType {
482    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483        match self {
484            DeviceType::Host => write!(f, "host"),
485            DeviceType::Nohost => write!(f, "nohost"),
486            DeviceType::Any => write!(f, "any"),
487        }
488    }
489}
490
491// ============================================================================
492// Linear Step (OpenMP 5.2 spec section 2.9.2)
493// ============================================================================
494
495/// Linear clause modifier
496///
497/// Specifies how the linear variable is updated.
498///
499/// ## Examples
500///
501/// ```
502/// # use roup::ir::LinearModifier;
503/// let lm = LinearModifier::Val;
504/// assert_eq!(lm.to_string(), "val");
505/// ```
506#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
507#[repr(C)]
508pub enum LinearModifier {
509    /// Linear variable value
510    Val = 0,
511    /// Reference to linear variable
512    Ref = 1,
513    /// Uniform across SIMD lanes
514    Uval = 2,
515}
516
517impl fmt::Display for LinearModifier {
518    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519        match self {
520            LinearModifier::Val => write!(f, "val"),
521            LinearModifier::Ref => write!(f, "ref"),
522            LinearModifier::Uval => write!(f, "uval"),
523        }
524    }
525}
526
527// ============================================================================
528// Lastprivate Modifier (OpenMP 5.2 spec section 2.21.4)
529// ============================================================================
530
531/// Lastprivate clause modifier
532///
533/// Specifies when the lastprivate update occurs.
534///
535/// ## Examples
536///
537/// ```
538/// # use roup::ir::LastprivateModifier;
539/// let lm = LastprivateModifier::Conditional;
540/// assert_eq!(lm.to_string(), "conditional");
541/// ```
542#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
543#[repr(C)]
544pub enum LastprivateModifier {
545    /// Update only if condition is true
546    Conditional = 0,
547}
548
549impl fmt::Display for LastprivateModifier {
550    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
551        match self {
552            LastprivateModifier::Conditional => write!(f, "conditional"),
553        }
554    }
555}
556
557// ============================================================================
558// Order (OpenMP 5.2 spec section 2.9.6)
559// ============================================================================
560
561/// Order clause value
562///
563/// Specifies iteration execution order constraints.
564///
565/// ## Examples
566///
567/// ```
568/// # use roup::ir::OrderKind;
569/// let ok = OrderKind::Concurrent;
570/// assert_eq!(ok.to_string(), "concurrent");
571/// ```
572#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
573#[repr(C)]
574pub enum OrderKind {
575    /// Iterations may execute concurrently
576    Concurrent = 0,
577}
578
579impl fmt::Display for OrderKind {
580    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581        match self {
582            OrderKind::Concurrent => write!(f, "concurrent"),
583        }
584    }
585}
586
587// ============================================================================
588// ClauseItem: Items that appear in clause lists
589// ============================================================================
590
591/// Item that can appear in a clause list
592///
593/// Many OpenMP clauses accept lists of items that can be:
594/// - Simple identifiers: `private(x, y, z)`
595/// - Variables with array sections: `map(to: arr[0:N])`
596/// - Expressions: `if(n > 100)`
597///
598/// ## Examples
599///
600/// ```
601/// # use roup::ir::{ClauseItem, Identifier, Variable, Expression, ParserConfig};
602/// // Simple identifier
603/// let item = ClauseItem::Identifier(Identifier::new("x"));
604/// assert_eq!(item.to_string(), "x");
605///
606/// // Variable with array section
607/// let var = Variable::new("arr");
608/// let item = ClauseItem::Variable(var);
609/// assert_eq!(item.to_string(), "arr");
610///
611/// // Expression
612/// let config = ParserConfig::default();
613/// let expr = Expression::new("n > 100", &config);
614/// let item = ClauseItem::Expression(expr);
615/// assert_eq!(item.to_string(), "n > 100");
616/// ```
617///
618/// ## Learning: Enums with Data
619///
620/// Unlike the modifier enums (which are just unit variants), ClauseItem
621/// is an enum where each variant **contains data**:
622///
623/// ```ignore
624/// enum ClauseItem {
625///     Identifier(Identifier),  // Contains an Identifier
626///     Variable(Variable),       // Contains a Variable
627///     Expression(Expression),   // Contains an Expression
628/// }
629/// ```
630///
631/// This is like a tagged union in C, but type-safe.
632#[derive(Debug, Clone, PartialEq)]
633pub enum ClauseItem {
634    /// Simple identifier (e.g., `x` in `private(x)`)
635    Identifier(Identifier),
636    /// Variable with optional array sections (e.g., `arr[0:N]` in `map(to: arr[0:N])`)
637    Variable(Variable),
638    /// Expression (e.g., `n > 100` in `if(n > 100)`)
639    Expression(Expression),
640}
641
642impl fmt::Display for ClauseItem {
643    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644        match self {
645            ClauseItem::Identifier(id) => write!(f, "{id}"),
646            ClauseItem::Variable(var) => write!(f, "{var}"),
647            ClauseItem::Expression(expr) => write!(f, "{expr}"),
648        }
649    }
650}
651
652impl From<Identifier> for ClauseItem {
653    fn from(id: Identifier) -> Self {
654        ClauseItem::Identifier(id)
655    }
656}
657
658impl From<Variable> for ClauseItem {
659    fn from(var: Variable) -> Self {
660        ClauseItem::Variable(var)
661    }
662}
663
664impl From<Expression> for ClauseItem {
665    fn from(expr: Expression) -> Self {
666        ClauseItem::Expression(expr)
667    }
668}
669
670// ============================================================================
671// ClauseData: Complete clause semantic information
672// ============================================================================
673
674/// Complete semantic data for an OpenMP clause
675///
676/// This enum represents the **meaning** of each OpenMP clause type.
677/// Each variant captures the specific data needed for that clause.
678///
679/// ## Examples
680///
681/// ```
682/// # use roup::ir::{ClauseData, DefaultKind, ReductionOperator, Identifier};
683/// // default(shared)
684/// let clause = ClauseData::Default(DefaultKind::Shared);
685/// assert_eq!(clause.to_string(), "default(shared)");
686///
687/// // reduction(+: sum)
688/// let clause = ClauseData::Reduction {
689///     operator: ReductionOperator::Add,
690///     items: vec![Identifier::new("sum").into()],
691/// };
692/// assert_eq!(clause.to_string(), "reduction(+: sum)");
693/// ```
694///
695/// ## Learning: Large Enums with Complex Data
696///
697/// This enum demonstrates several advanced Rust patterns:
698///
699/// 1. **Many variants**: ~30 variants for different clause types
700/// 2. **Variants with data**: Most variants contain structured data
701/// 3. **Named fields**: Using struct-like syntax for clarity
702/// 4. **Vec for lists**: Variable-length lists of items
703/// 5. **Option for optionals**: Optional parameters
704/// 6. **Composition**: Combines all previous IR types
705///
706/// ## Design Philosophy
707///
708/// Each variant captures exactly what's needed for semantic analysis:
709/// - `Private`: List of variables to make private
710/// - `Reduction`: Operator + list of reduction variables
711/// - `Map`: Map type + list of variables to map
712/// - `Schedule`: Schedule kind + optional modifiers + optional chunk size
713///
714/// This is much richer than the parser's string-based representation.
715#[derive(Debug, Clone, PartialEq)]
716pub enum ClauseData {
717    // ========================================================================
718    // Bare clauses (no parameters)
719    // ========================================================================
720    /// Clause with no parameters (e.g., `nowait`, `nogroup`)
721    Bare(Identifier),
722
723    // ========================================================================
724    // Simple expression clauses
725    // ========================================================================
726    /// Single expression parameter (e.g., `num_threads(4)`)
727    Expression(Expression),
728
729    // ========================================================================
730    // Item list clauses
731    // ========================================================================
732    /// List of items (e.g., `private(x, y, z)`)
733    ItemList(Vec<ClauseItem>),
734
735    // ========================================================================
736    // Data-sharing attribute clauses
737    // ========================================================================
738    /// `private(list)` - Variables are private to each thread
739    Private { items: Vec<ClauseItem> },
740
741    /// `firstprivate(list)` - Variables initialized from master thread
742    Firstprivate { items: Vec<ClauseItem> },
743
744    /// `lastprivate([modifier:] list)` - Variables updated from last iteration
745    Lastprivate {
746        modifier: Option<LastprivateModifier>,
747        items: Vec<ClauseItem>,
748    },
749
750    /// `shared(list)` - Variables shared among all threads
751    Shared { items: Vec<ClauseItem> },
752
753    /// `default(shared|none|...)` - Default data-sharing attribute
754    Default(DefaultKind),
755
756    // ========================================================================
757    // Reduction clause
758    // ========================================================================
759    /// `reduction([modifier,]operator: list)` - Reduction operation
760    Reduction {
761        operator: ReductionOperator,
762        items: Vec<ClauseItem>,
763    },
764
765    // ========================================================================
766    // Device data clauses
767    // ========================================================================
768    /// `map([[mapper(id),] map-type:] list)` - Map variables to device
769    Map {
770        map_type: Option<MapType>,
771        mapper: Option<Identifier>,
772        items: Vec<ClauseItem>,
773    },
774
775    /// `use_device_ptr(list)` - Use device pointers
776    UseDevicePtr { items: Vec<ClauseItem> },
777
778    /// `use_device_addr(list)` - Use device addresses
779    UseDeviceAddr { items: Vec<ClauseItem> },
780
781    /// `is_device_ptr(list)` - Variables are device pointers
782    IsDevicePtr { items: Vec<ClauseItem> },
783
784    /// `has_device_addr(list)` - Variables have device addresses
785    HasDeviceAddr { items: Vec<ClauseItem> },
786
787    // ========================================================================
788    // Task clauses
789    // ========================================================================
790    /// `depend([modifier,] type: list)` - Task dependencies
791    Depend {
792        depend_type: DependType,
793        items: Vec<ClauseItem>,
794    },
795
796    /// `priority(expression)` - Task priority
797    Priority { priority: Expression },
798
799    /// `affinity([modifier:] list)` - Task affinity
800    Affinity { items: Vec<ClauseItem> },
801
802    // ========================================================================
803    // Loop scheduling clauses
804    // ========================================================================
805    /// `schedule([modifier [, modifier]:]kind[, chunk_size])` - Loop schedule
806    Schedule {
807        kind: ScheduleKind,
808        modifiers: Vec<ScheduleModifier>,
809        chunk_size: Option<Expression>,
810    },
811
812    /// `collapse(n)` - Collapse nested loops
813    Collapse { n: Expression },
814
815    /// `ordered[(n)]` - Ordered iterations
816    Ordered { n: Option<Expression> },
817
818    // ========================================================================
819    // SIMD clauses
820    // ========================================================================
821    /// `linear(list[:step])` - Linear variables in SIMD
822    Linear {
823        modifier: Option<LinearModifier>,
824        items: Vec<ClauseItem>,
825        step: Option<Expression>,
826    },
827
828    /// `aligned(list[:alignment])` - Aligned variables
829    Aligned {
830        items: Vec<ClauseItem>,
831        alignment: Option<Expression>,
832    },
833
834    /// `safelen(length)` - Safe SIMD vector length
835    Safelen { length: Expression },
836
837    /// `simdlen(length)` - Preferred SIMD vector length
838    Simdlen { length: Expression },
839
840    // ========================================================================
841    // Conditional clauses
842    // ========================================================================
843    /// `if([directive-name-modifier:] expression)` - Conditional execution
844    If {
845        directive_name: Option<Identifier>,
846        condition: Expression,
847    },
848
849    // ========================================================================
850    // Thread binding clauses
851    // ========================================================================
852    /// `proc_bind(master|close|spread|primary)` - Thread affinity policy
853    ProcBind(ProcBind),
854
855    /// `num_threads(expression)` - Number of threads
856    NumThreads { num: Expression },
857
858    // ========================================================================
859    // Device clauses
860    // ========================================================================
861    /// `device(expression)` - Target device
862    Device { device_num: Expression },
863
864    /// `device_type(host|nohost|any)` - Device type specifier
865    DeviceType(DeviceType),
866
867    // ========================================================================
868    // Atomic clauses
869    // ========================================================================
870    /// `atomic_default_mem_order(seq_cst|acq_rel|...)` - Default memory order
871    AtomicDefaultMemOrder(MemoryOrder),
872
873    /// Atomic operation modifier
874    AtomicOperation {
875        op: AtomicOp,
876        memory_order: Option<MemoryOrder>,
877    },
878
879    // ========================================================================
880    // Order clause
881    // ========================================================================
882    /// `order(concurrent)` - Iteration execution order
883    Order(OrderKind),
884
885    // ========================================================================
886    // Teams clauses
887    // ========================================================================
888    /// `num_teams(expression)` - Number of teams
889    NumTeams { num: Expression },
890
891    /// `thread_limit(expression)` - Thread limit per team
892    ThreadLimit { limit: Expression },
893
894    // ========================================================================
895    // Allocator clauses
896    // ========================================================================
897    /// `allocate([allocator:] list)` - Memory allocator
898    Allocate {
899        allocator: Option<Identifier>,
900        items: Vec<ClauseItem>,
901    },
902
903    /// `allocator(allocator-handle)` - Specify allocator
904    Allocator { allocator: Identifier },
905
906    // ========================================================================
907    // Other clauses
908    // ========================================================================
909    /// `copyin(list)` - Copy master thread value to team threads
910    Copyin { items: Vec<ClauseItem> },
911
912    /// `copyprivate(list)` - Broadcast value from one thread
913    Copyprivate { items: Vec<ClauseItem> },
914
915    /// `dist_schedule(kind[, chunk_size])` - Distribute schedule
916    DistSchedule {
917        kind: ScheduleKind,
918        chunk_size: Option<Expression>,
919    },
920
921    /// `grainsize(expression)` - Taskloop grainsize
922    Grainsize { grain: Expression },
923
924    /// `num_tasks(expression)` - Number of tasks
925    NumTasks { num: Expression },
926
927    /// `filter(thread-num)` - Thread filter for masked construct
928    Filter { thread_num: Expression },
929
930    /// Generic clause with unparsed data (fallback for unknown clauses)
931    Generic {
932        name: Identifier,
933        data: Option<String>,
934    },
935}
936
937impl fmt::Display for ClauseData {
938    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
939        match self {
940            ClauseData::Bare(name) => write!(f, "{name}"),
941            ClauseData::Expression(expr) => write!(f, "{expr}"),
942            ClauseData::ItemList(items) => {
943                for (i, item) in items.iter().enumerate() {
944                    if i > 0 {
945                        write!(f, ", ")?;
946                    }
947                    write!(f, "{item}")?;
948                }
949                Ok(())
950            }
951            ClauseData::Private { items } => {
952                write!(f, "private(")?;
953                for (i, item) in items.iter().enumerate() {
954                    if i > 0 {
955                        write!(f, ", ")?;
956                    }
957                    write!(f, "{item}")?;
958                }
959                write!(f, ")")
960            }
961            ClauseData::Firstprivate { items } => {
962                write!(f, "firstprivate(")?;
963                for (i, item) in items.iter().enumerate() {
964                    if i > 0 {
965                        write!(f, ", ")?;
966                    }
967                    write!(f, "{item}")?;
968                }
969                write!(f, ")")
970            }
971            ClauseData::Lastprivate { modifier, items } => {
972                write!(f, "lastprivate(")?;
973                if let Some(m) = modifier {
974                    write!(f, "{m}: ")?;
975                }
976                for (i, item) in items.iter().enumerate() {
977                    if i > 0 {
978                        write!(f, ", ")?;
979                    }
980                    write!(f, "{item}")?;
981                }
982                write!(f, ")")
983            }
984            ClauseData::Shared { items } => {
985                write!(f, "shared(")?;
986                for (i, item) in items.iter().enumerate() {
987                    if i > 0 {
988                        write!(f, ", ")?;
989                    }
990                    write!(f, "{item}")?;
991                }
992                write!(f, ")")
993            }
994            ClauseData::Default(kind) => write!(f, "default({kind})"),
995            ClauseData::Reduction { operator, items } => {
996                write!(f, "reduction({operator}: ")?;
997                for (i, item) in items.iter().enumerate() {
998                    if i > 0 {
999                        write!(f, ", ")?;
1000                    }
1001                    write!(f, "{item}")?;
1002                }
1003                write!(f, ")")
1004            }
1005            ClauseData::Map {
1006                map_type,
1007                mapper,
1008                items,
1009            } => {
1010                write!(f, "map(")?;
1011                if let Some(mapper_id) = mapper {
1012                    write!(f, "mapper({mapper_id}), ")?;
1013                }
1014                if let Some(mt) = map_type {
1015                    write!(f, "{mt}: ")?;
1016                }
1017                for (i, item) in items.iter().enumerate() {
1018                    if i > 0 {
1019                        write!(f, ", ")?;
1020                    }
1021                    write!(f, "{item}")?;
1022                }
1023                write!(f, ")")
1024            }
1025            ClauseData::Schedule {
1026                kind,
1027                modifiers,
1028                chunk_size,
1029            } => {
1030                write!(f, "schedule(")?;
1031                if !modifiers.is_empty() {
1032                    for (i, m) in modifiers.iter().enumerate() {
1033                        if i > 0 {
1034                            write!(f, ", ")?;
1035                        }
1036                        write!(f, "{m}")?;
1037                    }
1038                    write!(f, ": ")?;
1039                }
1040                write!(f, "{kind}")?;
1041                if let Some(chunk) = chunk_size {
1042                    write!(f, ", {chunk}")?;
1043                }
1044                write!(f, ")")
1045            }
1046            ClauseData::Linear {
1047                modifier,
1048                items,
1049                step,
1050            } => {
1051                write!(f, "linear(")?;
1052                if let Some(m) = modifier {
1053                    write!(f, "{m}: ")?;
1054                }
1055                for (i, item) in items.iter().enumerate() {
1056                    if i > 0 {
1057                        write!(f, ", ")?;
1058                    }
1059                    write!(f, "{item}")?;
1060                }
1061                if let Some(s) = step {
1062                    write!(f, ": {s}")?;
1063                }
1064                write!(f, ")")
1065            }
1066            ClauseData::If {
1067                directive_name,
1068                condition,
1069            } => {
1070                write!(f, "if(")?;
1071                if let Some(name) = directive_name {
1072                    write!(f, "{name}: ")?;
1073                }
1074                write!(f, "{condition})")
1075            }
1076            ClauseData::NumThreads { num } => write!(f, "num_threads({num})"),
1077            ClauseData::ProcBind(pb) => write!(f, "proc_bind({pb})"),
1078            ClauseData::Device { device_num } => write!(f, "device({device_num})"),
1079            ClauseData::DeviceType(dt) => write!(f, "device_type({dt})"),
1080            ClauseData::Collapse { n } => write!(f, "collapse({n})"),
1081            ClauseData::Ordered { n } => {
1082                write!(f, "ordered")?;
1083                if let Some(num) = n {
1084                    write!(f, "({num})")?;
1085                }
1086                Ok(())
1087            }
1088            ClauseData::Depend { depend_type, items } => {
1089                write!(f, "depend({depend_type}: ")?;
1090                for (i, item) in items.iter().enumerate() {
1091                    if i > 0 {
1092                        write!(f, ", ")?;
1093                    }
1094                    write!(f, "{item}")?;
1095                }
1096                write!(f, ")")
1097            }
1098            // Simplified Display for remaining variants (can be expanded as needed)
1099            _ => write!(f, "<clause>"),
1100        }
1101    }
1102}
1103
1104impl ClauseData {
1105    /// Check if this is a default clause
1106    pub fn is_default(&self) -> bool {
1107        matches!(self, ClauseData::Default(_))
1108    }
1109
1110    /// Check if this is a private clause
1111    pub fn is_private(&self) -> bool {
1112        matches!(self, ClauseData::Private { .. })
1113    }
1114
1115    /// Check if this is a firstprivate clause
1116    pub fn is_firstprivate(&self) -> bool {
1117        matches!(self, ClauseData::Firstprivate { .. })
1118    }
1119
1120    /// Check if this is a lastprivate clause
1121    pub fn is_lastprivate(&self) -> bool {
1122        matches!(self, ClauseData::Lastprivate { .. })
1123    }
1124
1125    /// Check if this is a shared clause
1126    pub fn is_shared(&self) -> bool {
1127        matches!(self, ClauseData::Shared { .. })
1128    }
1129
1130    /// Check if this is a reduction clause
1131    pub fn is_reduction(&self) -> bool {
1132        matches!(self, ClauseData::Reduction { .. })
1133    }
1134
1135    /// Check if this is a map clause
1136    pub fn is_map(&self) -> bool {
1137        matches!(self, ClauseData::Map { .. })
1138    }
1139
1140    /// Check if this is an if clause
1141    pub fn is_if(&self) -> bool {
1142        matches!(self, ClauseData::If { .. })
1143    }
1144
1145    /// Check if this is a num_threads clause
1146    pub fn is_num_threads(&self) -> bool {
1147        matches!(self, ClauseData::NumThreads { .. })
1148    }
1149
1150    /// Check if this is a collapse clause
1151    pub fn is_collapse(&self) -> bool {
1152        matches!(self, ClauseData::Collapse { .. })
1153    }
1154
1155    /// Check if this is an ordered clause
1156    pub fn is_ordered(&self) -> bool {
1157        matches!(self, ClauseData::Ordered { .. })
1158    }
1159
1160    /// Check if this is a schedule clause
1161    pub fn is_schedule(&self) -> bool {
1162        matches!(self, ClauseData::Schedule { .. })
1163    }
1164
1165    /// Check if this is a device clause
1166    pub fn is_device(&self) -> bool {
1167        matches!(self, ClauseData::Device { .. })
1168    }
1169
1170    /// Check if this is a depend clause
1171    pub fn is_depend(&self) -> bool {
1172        matches!(self, ClauseData::Depend { .. })
1173    }
1174
1175    /// Check if this is a linear clause
1176    pub fn is_linear(&self) -> bool {
1177        matches!(self, ClauseData::Linear { .. })
1178    }
1179
1180    /// Check if this is a proc_bind clause
1181    pub fn is_proc_bind(&self) -> bool {
1182        matches!(self, ClauseData::ProcBind(_))
1183    }
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188    use super::*;
1189
1190    // Test ReductionOperator
1191    #[test]
1192    fn test_reduction_operator_display() {
1193        assert_eq!(ReductionOperator::Add.to_string(), "+");
1194        assert_eq!(ReductionOperator::Multiply.to_string(), "*");
1195        assert_eq!(ReductionOperator::Subtract.to_string(), "-");
1196        assert_eq!(ReductionOperator::BitwiseAnd.to_string(), "&");
1197        assert_eq!(ReductionOperator::BitwiseOr.to_string(), "|");
1198        assert_eq!(ReductionOperator::BitwiseXor.to_string(), "^");
1199        assert_eq!(ReductionOperator::LogicalAnd.to_string(), "&&");
1200        assert_eq!(ReductionOperator::LogicalOr.to_string(), "||");
1201        assert_eq!(ReductionOperator::Min.to_string(), "min");
1202        assert_eq!(ReductionOperator::Max.to_string(), "max");
1203        assert_eq!(ReductionOperator::MinusEqual.to_string(), "-=");
1204        assert_eq!(ReductionOperator::Custom.to_string(), "custom");
1205    }
1206
1207    #[test]
1208    fn test_reduction_operator_equality() {
1209        assert_eq!(ReductionOperator::Add, ReductionOperator::Add);
1210        assert_ne!(ReductionOperator::Add, ReductionOperator::Multiply);
1211    }
1212
1213    #[test]
1214    fn test_reduction_operator_copy_clone() {
1215        let op1 = ReductionOperator::Max;
1216        let op2 = op1; // Copy
1217        let op3 = op1; // Copy (no need for .clone() on Copy types)
1218        assert_eq!(op1, op2);
1219        assert_eq!(op1, op3);
1220    }
1221
1222    #[test]
1223    fn test_reduction_operator_discriminants() {
1224        // Ensure discriminants are stable for FFI
1225        assert_eq!(ReductionOperator::Add as i32, 0);
1226        assert_eq!(ReductionOperator::Multiply as i32, 1);
1227        assert_eq!(ReductionOperator::BitwiseAnd as i32, 10);
1228        assert_eq!(ReductionOperator::LogicalAnd as i32, 20);
1229        assert_eq!(ReductionOperator::Min as i32, 30);
1230        assert_eq!(ReductionOperator::Custom as i32, 100);
1231    }
1232
1233    // Test MapType
1234    #[test]
1235    fn test_map_type_display() {
1236        assert_eq!(MapType::To.to_string(), "to");
1237        assert_eq!(MapType::From.to_string(), "from");
1238        assert_eq!(MapType::ToFrom.to_string(), "tofrom");
1239        assert_eq!(MapType::Alloc.to_string(), "alloc");
1240        assert_eq!(MapType::Release.to_string(), "release");
1241        assert_eq!(MapType::Delete.to_string(), "delete");
1242    }
1243
1244    #[test]
1245    fn test_map_type_all_variants() {
1246        // Ensure all variants are covered
1247        let all_types = vec![
1248            MapType::To,
1249            MapType::From,
1250            MapType::ToFrom,
1251            MapType::Alloc,
1252            MapType::Release,
1253            MapType::Delete,
1254        ];
1255        for mt in all_types {
1256            assert!(!mt.to_string().is_empty());
1257        }
1258    }
1259
1260    // Test ScheduleKind
1261    #[test]
1262    fn test_schedule_kind_display() {
1263        assert_eq!(ScheduleKind::Static.to_string(), "static");
1264        assert_eq!(ScheduleKind::Dynamic.to_string(), "dynamic");
1265        assert_eq!(ScheduleKind::Guided.to_string(), "guided");
1266        assert_eq!(ScheduleKind::Auto.to_string(), "auto");
1267        assert_eq!(ScheduleKind::Runtime.to_string(), "runtime");
1268    }
1269
1270    #[test]
1271    fn test_schedule_kind_equality() {
1272        assert_eq!(ScheduleKind::Static, ScheduleKind::Static);
1273        assert_ne!(ScheduleKind::Static, ScheduleKind::Dynamic);
1274    }
1275
1276    // Test ScheduleModifier
1277    #[test]
1278    fn test_schedule_modifier_display() {
1279        assert_eq!(ScheduleModifier::Monotonic.to_string(), "monotonic");
1280        assert_eq!(ScheduleModifier::Nonmonotonic.to_string(), "nonmonotonic");
1281        assert_eq!(ScheduleModifier::Simd.to_string(), "simd");
1282    }
1283
1284    #[test]
1285    fn test_schedule_modifier_all_variants() {
1286        let all_mods = vec![
1287            ScheduleModifier::Monotonic,
1288            ScheduleModifier::Nonmonotonic,
1289            ScheduleModifier::Simd,
1290        ];
1291        for sm in all_mods {
1292            assert!(!sm.to_string().is_empty());
1293        }
1294    }
1295
1296    // Test DependType
1297    #[test]
1298    fn test_depend_type_display() {
1299        assert_eq!(DependType::In.to_string(), "in");
1300        assert_eq!(DependType::Out.to_string(), "out");
1301        assert_eq!(DependType::Inout.to_string(), "inout");
1302        assert_eq!(DependType::Mutexinoutset.to_string(), "mutexinoutset");
1303        assert_eq!(DependType::Depobj.to_string(), "depobj");
1304        assert_eq!(DependType::Source.to_string(), "source");
1305        assert_eq!(DependType::Sink.to_string(), "sink");
1306    }
1307
1308    #[test]
1309    fn test_depend_type_all_variants() {
1310        let all_types = vec![
1311            DependType::In,
1312            DependType::Out,
1313            DependType::Inout,
1314            DependType::Mutexinoutset,
1315            DependType::Depobj,
1316            DependType::Source,
1317            DependType::Sink,
1318        ];
1319        for dt in all_types {
1320            assert!(!dt.to_string().is_empty());
1321        }
1322    }
1323
1324    // Test DefaultKind
1325    #[test]
1326    fn test_default_kind_display() {
1327        assert_eq!(DefaultKind::Shared.to_string(), "shared");
1328        assert_eq!(DefaultKind::None.to_string(), "none");
1329        assert_eq!(DefaultKind::Private.to_string(), "private");
1330        assert_eq!(DefaultKind::Firstprivate.to_string(), "firstprivate");
1331    }
1332
1333    #[test]
1334    fn test_default_kind_language_specific() {
1335        // Private is Fortran-only, but we can represent it
1336        let dk = DefaultKind::Private;
1337        assert_eq!(dk.to_string(), "private");
1338    }
1339
1340    // Test ProcBind
1341    #[test]
1342    fn test_proc_bind_display() {
1343        assert_eq!(ProcBind::Master.to_string(), "master");
1344        assert_eq!(ProcBind::Close.to_string(), "close");
1345        assert_eq!(ProcBind::Spread.to_string(), "spread");
1346        assert_eq!(ProcBind::Primary.to_string(), "primary");
1347    }
1348
1349    #[test]
1350    fn test_proc_bind_deprecated_master() {
1351        // OpenMP 5.1+ deprecates 'master', prefers 'primary'
1352        // But we still support both for backwards compatibility
1353        assert_eq!(ProcBind::Master.to_string(), "master");
1354        assert_eq!(ProcBind::Primary.to_string(), "primary");
1355    }
1356
1357    // Test MemoryOrder
1358    #[test]
1359    fn test_memory_order_display() {
1360        assert_eq!(MemoryOrder::SeqCst.to_string(), "seq_cst");
1361        assert_eq!(MemoryOrder::AcqRel.to_string(), "acq_rel");
1362        assert_eq!(MemoryOrder::Release.to_string(), "release");
1363        assert_eq!(MemoryOrder::Acquire.to_string(), "acquire");
1364        assert_eq!(MemoryOrder::Relaxed.to_string(), "relaxed");
1365    }
1366
1367    #[test]
1368    fn test_memory_order_strength() {
1369        // SeqCst is strongest, Relaxed is weakest
1370        // Just verify they all exist
1371        let all_orders = [
1372            MemoryOrder::SeqCst,
1373            MemoryOrder::AcqRel,
1374            MemoryOrder::Release,
1375            MemoryOrder::Acquire,
1376            MemoryOrder::Relaxed,
1377        ];
1378        assert_eq!(all_orders.len(), 5);
1379    }
1380
1381    // Test AtomicOp
1382    #[test]
1383    fn test_atomic_op_display() {
1384        assert_eq!(AtomicOp::Read.to_string(), "read");
1385        assert_eq!(AtomicOp::Write.to_string(), "write");
1386        assert_eq!(AtomicOp::Update.to_string(), "update");
1387        assert_eq!(AtomicOp::Capture.to_string(), "capture");
1388    }
1389
1390    #[test]
1391    fn test_atomic_op_all_variants() {
1392        let all_ops = vec![
1393            AtomicOp::Read,
1394            AtomicOp::Write,
1395            AtomicOp::Update,
1396            AtomicOp::Capture,
1397        ];
1398        for ao in all_ops {
1399            assert!(!ao.to_string().is_empty());
1400        }
1401    }
1402
1403    // Test DeviceType
1404    #[test]
1405    fn test_device_type_display() {
1406        assert_eq!(DeviceType::Host.to_string(), "host");
1407        assert_eq!(DeviceType::Nohost.to_string(), "nohost");
1408        assert_eq!(DeviceType::Any.to_string(), "any");
1409    }
1410
1411    #[test]
1412    fn test_device_type_all_variants() {
1413        let all_types = vec![DeviceType::Host, DeviceType::Nohost, DeviceType::Any];
1414        for dt in all_types {
1415            assert!(!dt.to_string().is_empty());
1416        }
1417    }
1418
1419    // Test LinearModifier
1420    #[test]
1421    fn test_linear_modifier_display() {
1422        assert_eq!(LinearModifier::Val.to_string(), "val");
1423        assert_eq!(LinearModifier::Ref.to_string(), "ref");
1424        assert_eq!(LinearModifier::Uval.to_string(), "uval");
1425    }
1426
1427    // Test LastprivateModifier
1428    #[test]
1429    fn test_lastprivate_modifier_display() {
1430        assert_eq!(LastprivateModifier::Conditional.to_string(), "conditional");
1431    }
1432
1433    // Test OrderKind
1434    #[test]
1435    fn test_order_kind_display() {
1436        assert_eq!(OrderKind::Concurrent.to_string(), "concurrent");
1437    }
1438
1439    // Corner case: enum size consistency for FFI
1440    #[test]
1441    fn test_enum_sizes_for_ffi() {
1442        use std::mem::size_of;
1443
1444        // All enums should be pointer-sized or smaller for FFI
1445        assert!(size_of::<ReductionOperator>() <= size_of::<usize>());
1446        assert!(size_of::<MapType>() <= size_of::<usize>());
1447        assert!(size_of::<ScheduleKind>() <= size_of::<usize>());
1448        assert!(size_of::<DependType>() <= size_of::<usize>());
1449        assert!(size_of::<DefaultKind>() <= size_of::<usize>());
1450        assert!(size_of::<ProcBind>() <= size_of::<usize>());
1451        assert!(size_of::<MemoryOrder>() <= size_of::<usize>());
1452        assert!(size_of::<AtomicOp>() <= size_of::<usize>());
1453        assert!(size_of::<DeviceType>() <= size_of::<usize>());
1454        assert!(size_of::<LinearModifier>() <= size_of::<usize>());
1455        assert!(size_of::<LastprivateModifier>() <= size_of::<usize>());
1456        assert!(size_of::<OrderKind>() <= size_of::<usize>());
1457    }
1458
1459    // Corner case: hash consistency
1460    #[test]
1461    fn test_enum_hash_consistency() {
1462        use std::collections::hash_map::DefaultHasher;
1463        use std::hash::{Hash, Hasher};
1464
1465        let op1 = ReductionOperator::Add;
1466        let op2 = ReductionOperator::Add;
1467
1468        let mut hasher1 = DefaultHasher::new();
1469        let mut hasher2 = DefaultHasher::new();
1470
1471        op1.hash(&mut hasher1);
1472        op2.hash(&mut hasher2);
1473
1474        assert_eq!(hasher1.finish(), hasher2.finish());
1475    }
1476
1477    // Corner case: debug formatting
1478    #[test]
1479    fn test_enum_debug_formatting() {
1480        let op = ReductionOperator::Add;
1481        let debug_str = format!("{op:?}");
1482        assert!(debug_str.contains("Add"));
1483    }
1484
1485    // ========================================================================
1486    // ClauseItem tests
1487    // ========================================================================
1488
1489    #[test]
1490    fn test_clause_item_from_identifier() {
1491        let id = Identifier::new("x");
1492        let item = ClauseItem::from(id);
1493        assert_eq!(item.to_string(), "x");
1494    }
1495
1496    #[test]
1497    fn test_clause_item_from_variable() {
1498        let var = Variable::new("arr");
1499        let item = ClauseItem::from(var);
1500        assert_eq!(item.to_string(), "arr");
1501    }
1502
1503    #[test]
1504    fn test_clause_item_from_expression() {
1505        use crate::ir::ParserConfig;
1506        let config = ParserConfig::default();
1507        let expr = Expression::new("n > 100", &config);
1508        let item = ClauseItem::from(expr);
1509        assert_eq!(item.to_string(), "n > 100");
1510    }
1511
1512    #[test]
1513    fn test_clause_item_display_identifier() {
1514        let item = ClauseItem::Identifier(Identifier::new("my_var"));
1515        assert_eq!(item.to_string(), "my_var");
1516    }
1517
1518    #[test]
1519    fn test_clause_item_display_variable_with_section() {
1520        use crate::ir::ArraySection;
1521        let section = ArraySection::single_index(Expression::unparsed("i"));
1522        let var = Variable::with_sections("arr", vec![section]);
1523        let item = ClauseItem::Variable(var);
1524        assert_eq!(item.to_string(), "arr[i]");
1525    }
1526
1527    #[test]
1528    fn test_clause_item_equality() {
1529        let item1 = ClauseItem::Identifier(Identifier::new("x"));
1530        let item2 = ClauseItem::Identifier(Identifier::new("x"));
1531        let item3 = ClauseItem::Identifier(Identifier::new("y"));
1532        assert_eq!(item1, item2);
1533        assert_ne!(item1, item3);
1534    }
1535
1536    #[test]
1537    fn test_clause_item_clone() {
1538        let item1 = ClauseItem::Identifier(Identifier::new("x"));
1539        let item2 = item1.clone();
1540        assert_eq!(item1, item2);
1541    }
1542
1543    // ========================================================================
1544    // ClauseData tests
1545    // ========================================================================
1546
1547    #[test]
1548    fn test_clause_data_bare() {
1549        let clause = ClauseData::Bare(Identifier::new("nowait"));
1550        assert_eq!(clause.to_string(), "nowait");
1551    }
1552
1553    #[test]
1554    fn test_clause_data_default() {
1555        let clause = ClauseData::Default(DefaultKind::Shared);
1556        assert_eq!(clause.to_string(), "default(shared)");
1557
1558        let clause = ClauseData::Default(DefaultKind::None);
1559        assert_eq!(clause.to_string(), "default(none)");
1560    }
1561
1562    #[test]
1563    fn test_clause_data_private() {
1564        let items = vec![
1565            ClauseItem::Identifier(Identifier::new("x")),
1566            ClauseItem::Identifier(Identifier::new("y")),
1567        ];
1568        let clause = ClauseData::Private { items };
1569        assert_eq!(clause.to_string(), "private(x, y)");
1570    }
1571
1572    #[test]
1573    fn test_clause_data_private_single_item() {
1574        let items = vec![ClauseItem::Identifier(Identifier::new("x"))];
1575        let clause = ClauseData::Private { items };
1576        assert_eq!(clause.to_string(), "private(x)");
1577    }
1578
1579    #[test]
1580    fn test_clause_data_firstprivate() {
1581        let items = vec![
1582            ClauseItem::Identifier(Identifier::new("a")),
1583            ClauseItem::Identifier(Identifier::new("b")),
1584        ];
1585        let clause = ClauseData::Firstprivate { items };
1586        assert_eq!(clause.to_string(), "firstprivate(a, b)");
1587    }
1588
1589    #[test]
1590    fn test_clause_data_lastprivate_without_modifier() {
1591        let items = vec![ClauseItem::Identifier(Identifier::new("x"))];
1592        let clause = ClauseData::Lastprivate {
1593            modifier: None,
1594            items,
1595        };
1596        assert_eq!(clause.to_string(), "lastprivate(x)");
1597    }
1598
1599    #[test]
1600    fn test_clause_data_lastprivate_with_conditional() {
1601        let items = vec![ClauseItem::Identifier(Identifier::new("x"))];
1602        let clause = ClauseData::Lastprivate {
1603            modifier: Some(LastprivateModifier::Conditional),
1604            items,
1605        };
1606        assert_eq!(clause.to_string(), "lastprivate(conditional: x)");
1607    }
1608
1609    #[test]
1610    fn test_clause_data_shared() {
1611        let items = vec![
1612            ClauseItem::Identifier(Identifier::new("data")),
1613            ClauseItem::Identifier(Identifier::new("count")),
1614        ];
1615        let clause = ClauseData::Shared { items };
1616        assert_eq!(clause.to_string(), "shared(data, count)");
1617    }
1618
1619    #[test]
1620    fn test_clause_data_reduction() {
1621        let items = vec![ClauseItem::Identifier(Identifier::new("sum"))];
1622        let clause = ClauseData::Reduction {
1623            operator: ReductionOperator::Add,
1624            items,
1625        };
1626        assert_eq!(clause.to_string(), "reduction(+: sum)");
1627    }
1628
1629    #[test]
1630    fn test_clause_data_reduction_multiple_items() {
1631        let items = vec![
1632            ClauseItem::Identifier(Identifier::new("sum")),
1633            ClauseItem::Identifier(Identifier::new("total")),
1634        ];
1635        let clause = ClauseData::Reduction {
1636            operator: ReductionOperator::Add,
1637            items,
1638        };
1639        assert_eq!(clause.to_string(), "reduction(+: sum, total)");
1640    }
1641
1642    #[test]
1643    fn test_clause_data_reduction_max() {
1644        let items = vec![ClauseItem::Identifier(Identifier::new("max_val"))];
1645        let clause = ClauseData::Reduction {
1646            operator: ReductionOperator::Max,
1647            items,
1648        };
1649        assert_eq!(clause.to_string(), "reduction(max: max_val)");
1650    }
1651
1652    #[test]
1653    fn test_clause_data_map_simple() {
1654        let items = vec![ClauseItem::Variable(Variable::new("arr"))];
1655        let clause = ClauseData::Map {
1656            map_type: Some(MapType::To),
1657            mapper: None,
1658            items,
1659        };
1660        assert_eq!(clause.to_string(), "map(to: arr)");
1661    }
1662
1663    #[test]
1664    fn test_clause_data_map_tofrom() {
1665        let items = vec![ClauseItem::Variable(Variable::new("data"))];
1666        let clause = ClauseData::Map {
1667            map_type: Some(MapType::ToFrom),
1668            mapper: None,
1669            items,
1670        };
1671        assert_eq!(clause.to_string(), "map(tofrom: data)");
1672    }
1673
1674    #[test]
1675    fn test_clause_data_map_without_type() {
1676        let items = vec![ClauseItem::Variable(Variable::new("arr"))];
1677        let clause = ClauseData::Map {
1678            map_type: None,
1679            mapper: None,
1680            items,
1681        };
1682        assert_eq!(clause.to_string(), "map(arr)");
1683    }
1684
1685    #[test]
1686    fn test_clause_data_map_with_mapper() {
1687        let items = vec![ClauseItem::Variable(Variable::new("arr"))];
1688        let clause = ClauseData::Map {
1689            map_type: Some(MapType::To),
1690            mapper: Some(Identifier::new("my_mapper")),
1691            items,
1692        };
1693        assert_eq!(clause.to_string(), "map(mapper(my_mapper), to: arr)");
1694    }
1695
1696    #[test]
1697    fn test_clause_data_schedule_static() {
1698        let clause = ClauseData::Schedule {
1699            kind: ScheduleKind::Static,
1700            modifiers: vec![],
1701            chunk_size: None,
1702        };
1703        assert_eq!(clause.to_string(), "schedule(static)");
1704    }
1705
1706    #[test]
1707    fn test_clause_data_schedule_dynamic_with_chunk() {
1708        let chunk = Expression::unparsed("64");
1709        let clause = ClauseData::Schedule {
1710            kind: ScheduleKind::Dynamic,
1711            modifiers: vec![],
1712            chunk_size: Some(chunk),
1713        };
1714        assert_eq!(clause.to_string(), "schedule(dynamic, 64)");
1715    }
1716
1717    #[test]
1718    fn test_clause_data_schedule_with_modifier() {
1719        let clause = ClauseData::Schedule {
1720            kind: ScheduleKind::Static,
1721            modifiers: vec![ScheduleModifier::Monotonic],
1722            chunk_size: None,
1723        };
1724        assert_eq!(clause.to_string(), "schedule(monotonic: static)");
1725    }
1726
1727    #[test]
1728    fn test_clause_data_schedule_with_multiple_modifiers() {
1729        let clause = ClauseData::Schedule {
1730            kind: ScheduleKind::Dynamic,
1731            modifiers: vec![ScheduleModifier::Nonmonotonic, ScheduleModifier::Simd],
1732            chunk_size: Some(Expression::unparsed("32")),
1733        };
1734        assert_eq!(
1735            clause.to_string(),
1736            "schedule(nonmonotonic, simd: dynamic, 32)"
1737        );
1738    }
1739
1740    #[test]
1741    fn test_clause_data_linear_simple() {
1742        let items = vec![ClauseItem::Identifier(Identifier::new("i"))];
1743        let clause = ClauseData::Linear {
1744            modifier: None,
1745            items,
1746            step: None,
1747        };
1748        assert_eq!(clause.to_string(), "linear(i)");
1749    }
1750
1751    #[test]
1752    fn test_clause_data_linear_with_step() {
1753        let items = vec![ClauseItem::Identifier(Identifier::new("i"))];
1754        let clause = ClauseData::Linear {
1755            modifier: None,
1756            items,
1757            step: Some(Expression::unparsed("2")),
1758        };
1759        assert_eq!(clause.to_string(), "linear(i: 2)");
1760    }
1761
1762    #[test]
1763    fn test_clause_data_linear_with_modifier() {
1764        let items = vec![ClauseItem::Identifier(Identifier::new("i"))];
1765        let clause = ClauseData::Linear {
1766            modifier: Some(LinearModifier::Val),
1767            items,
1768            step: None,
1769        };
1770        assert_eq!(clause.to_string(), "linear(val: i)");
1771    }
1772
1773    #[test]
1774    fn test_clause_data_if_simple() {
1775        let condition = Expression::unparsed("n > 100");
1776        let clause = ClauseData::If {
1777            directive_name: None,
1778            condition,
1779        };
1780        assert_eq!(clause.to_string(), "if(n > 100)");
1781    }
1782
1783    #[test]
1784    fn test_clause_data_if_with_directive_name() {
1785        let condition = Expression::unparsed("n > 100");
1786        let clause = ClauseData::If {
1787            directive_name: Some(Identifier::new("parallel")),
1788            condition,
1789        };
1790        assert_eq!(clause.to_string(), "if(parallel: n > 100)");
1791    }
1792
1793    #[test]
1794    fn test_clause_data_num_threads() {
1795        let clause = ClauseData::NumThreads {
1796            num: Expression::unparsed("4"),
1797        };
1798        assert_eq!(clause.to_string(), "num_threads(4)");
1799    }
1800
1801    #[test]
1802    fn test_clause_data_proc_bind() {
1803        let clause = ClauseData::ProcBind(ProcBind::Close);
1804        assert_eq!(clause.to_string(), "proc_bind(close)");
1805    }
1806
1807    #[test]
1808    fn test_clause_data_device() {
1809        let clause = ClauseData::Device {
1810            device_num: Expression::unparsed("0"),
1811        };
1812        assert_eq!(clause.to_string(), "device(0)");
1813    }
1814
1815    #[test]
1816    fn test_clause_data_device_type() {
1817        let clause = ClauseData::DeviceType(DeviceType::Host);
1818        assert_eq!(clause.to_string(), "device_type(host)");
1819    }
1820
1821    #[test]
1822    fn test_clause_data_collapse() {
1823        let clause = ClauseData::Collapse {
1824            n: Expression::unparsed("2"),
1825        };
1826        assert_eq!(clause.to_string(), "collapse(2)");
1827    }
1828
1829    #[test]
1830    fn test_clause_data_ordered_without_param() {
1831        let clause = ClauseData::Ordered { n: None };
1832        assert_eq!(clause.to_string(), "ordered");
1833    }
1834
1835    #[test]
1836    fn test_clause_data_ordered_with_param() {
1837        let clause = ClauseData::Ordered {
1838            n: Some(Expression::unparsed("2")),
1839        };
1840        assert_eq!(clause.to_string(), "ordered(2)");
1841    }
1842
1843    #[test]
1844    fn test_clause_data_depend() {
1845        let items = vec![ClauseItem::Variable(Variable::new("x"))];
1846        let clause = ClauseData::Depend {
1847            depend_type: DependType::In,
1848            items,
1849        };
1850        assert_eq!(clause.to_string(), "depend(in: x)");
1851    }
1852
1853    #[test]
1854    fn test_clause_data_depend_inout() {
1855        let items = vec![
1856            ClauseItem::Variable(Variable::new("a")),
1857            ClauseItem::Variable(Variable::new("b")),
1858        ];
1859        let clause = ClauseData::Depend {
1860            depend_type: DependType::Inout,
1861            items,
1862        };
1863        assert_eq!(clause.to_string(), "depend(inout: a, b)");
1864    }
1865
1866    #[test]
1867    fn test_clause_data_equality() {
1868        let clause1 = ClauseData::Default(DefaultKind::Shared);
1869        let clause2 = ClauseData::Default(DefaultKind::Shared);
1870        let clause3 = ClauseData::Default(DefaultKind::None);
1871        assert_eq!(clause1, clause2);
1872        assert_ne!(clause1, clause3);
1873    }
1874
1875    #[test]
1876    fn test_clause_data_clone() {
1877        let items = vec![ClauseItem::Identifier(Identifier::new("x"))];
1878        let clause1 = ClauseData::Private { items };
1879        let clause2 = clause1.clone();
1880        assert_eq!(clause1, clause2);
1881    }
1882
1883    // Corner case: empty item lists
1884    #[test]
1885    fn test_clause_data_private_empty_list() {
1886        let clause = ClauseData::Private { items: vec![] };
1887        assert_eq!(clause.to_string(), "private()");
1888    }
1889
1890    #[test]
1891    fn test_clause_data_reduction_empty_list() {
1892        let clause = ClauseData::Reduction {
1893            operator: ReductionOperator::Add,
1894            items: vec![],
1895        };
1896        assert_eq!(clause.to_string(), "reduction(+: )");
1897    }
1898
1899    // Corner case: complex variable items
1900    #[test]
1901    fn test_clause_data_with_array_sections() {
1902        use crate::ir::ArraySection;
1903        let lower = Expression::unparsed("0");
1904        let length = Expression::unparsed("N");
1905        let section = ArraySection {
1906            lower_bound: Some(lower),
1907            length: Some(length),
1908            stride: None,
1909        };
1910        let var = Variable::with_sections("arr", vec![section]);
1911        let items = vec![ClauseItem::Variable(var)];
1912        let clause = ClauseData::Map {
1913            map_type: Some(MapType::To),
1914            mapper: None,
1915            items,
1916        };
1917        assert_eq!(clause.to_string(), "map(to: arr[0:N])");
1918    }
1919
1920    // Corner case: expression items
1921    #[test]
1922    fn test_clause_data_with_expression_items() {
1923        let expr = Expression::unparsed("func(x, y)");
1924        let items = vec![ClauseItem::Expression(expr)];
1925        let clause = ClauseData::ItemList(items);
1926        assert_eq!(clause.to_string(), "func(x, y)");
1927    }
1928
1929    // Corner case: debug formatting
1930    #[test]
1931    fn test_clause_data_debug() {
1932        let clause = ClauseData::Default(DefaultKind::Shared);
1933        let debug_str = format!("{clause:?}");
1934        assert!(debug_str.contains("Default"));
1935        assert!(debug_str.contains("Shared"));
1936    }
1937}