roup/ir/
directive.rs

1//! Directive IR types for complete OpenMP directive representation
2//!
3//! This module defines the top-level IR structures that represent complete
4//! OpenMP directives with all their semantic information.
5//!
6//! ## Learning Objectives
7//!
8//! - **Box for heap allocation**: Managing large structures efficiently
9//! - **Complex composition**: Combining all IR types into cohesive structures
10//! - **Metadata handling**: Special cases for specific directive types
11//! - **Query API**: Convenient methods for analyzing directives
12//!
13//! ## Design Philosophy
14//!
15//! A complete OpenMP directive consists of:
16//! 1. **Kind**: What type of directive (parallel, for, task, etc.)
17//! 2. **Clauses**: List of semantic clause data
18//! 3. **Location**: Source position for error reporting
19//! 4. **Language**: C, C++, or Fortran context
20//! 5. **Metadata**: Special information for certain directives
21//!
22//! ## Example
23//!
24//! ```text
25//! #pragma omp parallel for private(i) reduction(+: sum) schedule(static, 64)
26//! ```
27//!
28//! Becomes:
29//! ```ignore
30//! DirectiveIR {
31//!     kind: DirectiveKind::ParallelFor,
32//!     clauses: vec![
33//!         Private { items: [i] },
34//!         Reduction { operator: Add, items: [sum] },
35//!         Schedule { kind: Static, chunk_size: Some(64) },
36//!     ],
37//!     location: SourceLocation { line: 10, column: 1 },
38//!     language: Language::C,
39//!     metadata: None,
40//! }
41//! ```
42
43use std::fmt;
44
45use super::{ClauseData, Language, SourceLocation};
46
47// ============================================================================
48// DirectiveKind: All OpenMP directive types
49// ============================================================================
50
51/// OpenMP directive type
52///
53/// This enum covers all standard OpenMP directives from the 5.2 specification.
54/// Each directive is represented as a unique variant.
55///
56/// ## Examples
57///
58/// ```
59/// # use roup::ir::DirectiveKind;
60/// let kind = DirectiveKind::Parallel;
61/// assert_eq!(kind.to_string(), "parallel");
62///
63/// let kind = DirectiveKind::ParallelFor;
64/// assert_eq!(kind.to_string(), "parallel for");
65/// ```
66///
67/// ## Learning: Large Enum with Clear Organization
68///
69/// This enum demonstrates organizing a large number of variants (70+)
70/// into logical categories using comments and grouping.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72#[repr(C)]
73pub enum DirectiveKind {
74    // ========================================================================
75    // Parallel constructs
76    // ========================================================================
77    /// `#pragma omp parallel`
78    Parallel = 0,
79    /// `#pragma omp parallel for`
80    ParallelFor = 1,
81    /// `#pragma omp parallel for simd`
82    ParallelForSimd = 2,
83    /// `#pragma omp parallel sections`
84    ParallelSections = 3,
85    /// `#pragma omp parallel workshare` (Fortran)
86    ParallelWorkshare = 4,
87    /// `#pragma omp parallel loop`
88    ParallelLoop = 5,
89    /// `#pragma omp parallel masked`
90    ParallelMasked = 6,
91    /// `#pragma omp parallel master` (deprecated in 5.1)
92    ParallelMaster = 7,
93
94    // ========================================================================
95    // Work-sharing constructs
96    // ========================================================================
97    /// `#pragma omp for`
98    For = 10,
99    /// `#pragma omp for simd`
100    ForSimd = 11,
101    /// `#pragma omp sections`
102    Sections = 12,
103    /// `#pragma omp section`
104    Section = 13,
105    /// `#pragma omp single`
106    Single = 14,
107    /// `#pragma omp workshare` (Fortran)
108    Workshare = 15,
109    /// `#pragma omp loop`
110    Loop = 16,
111
112    // ========================================================================
113    // SIMD constructs
114    // ========================================================================
115    /// `#pragma omp simd`
116    Simd = 20,
117    /// `#pragma omp declare simd`
118    DeclareSimd = 21,
119
120    // ========================================================================
121    // Task constructs
122    // ========================================================================
123    /// `#pragma omp task`
124    Task = 30,
125    /// `#pragma omp taskloop`
126    Taskloop = 31,
127    /// `#pragma omp taskloop simd`
128    TaskloopSimd = 32,
129    /// `#pragma omp taskyield`
130    Taskyield = 33,
131    /// `#pragma omp taskwait`
132    Taskwait = 34,
133    /// `#pragma omp taskgroup`
134    Taskgroup = 35,
135
136    // ========================================================================
137    // Target constructs
138    // ========================================================================
139    /// `#pragma omp target`
140    Target = 40,
141    /// `#pragma omp target data`
142    TargetData = 41,
143    /// `#pragma omp target enter data`
144    TargetEnterData = 42,
145    /// `#pragma omp target exit data`
146    TargetExitData = 43,
147    /// `#pragma omp target update`
148    TargetUpdate = 44,
149    /// `#pragma omp target parallel`
150    TargetParallel = 45,
151    /// `#pragma omp target parallel for`
152    TargetParallelFor = 46,
153    /// `#pragma omp target parallel for simd`
154    TargetParallelForSimd = 47,
155    /// `#pragma omp target parallel loop`
156    TargetParallelLoop = 48,
157    /// `#pragma omp target simd`
158    TargetSimd = 49,
159    /// `#pragma omp target teams`
160    TargetTeams = 50,
161    /// `#pragma omp target teams distribute`
162    TargetTeamsDistribute = 51,
163    /// `#pragma omp target teams distribute simd`
164    TargetTeamsDistributeSimd = 52,
165    /// `#pragma omp target teams distribute parallel for`
166    TargetTeamsDistributeParallelFor = 53,
167    /// `#pragma omp target teams distribute parallel for simd`
168    TargetTeamsDistributeParallelForSimd = 54,
169    /// `#pragma omp target teams loop`
170    TargetTeamsLoop = 55,
171
172    // ========================================================================
173    // Teams constructs
174    // ========================================================================
175    /// `#pragma omp teams`
176    Teams = 60,
177    /// `#pragma omp teams distribute`
178    TeamsDistribute = 61,
179    /// `#pragma omp teams distribute simd`
180    TeamsDistributeSimd = 62,
181    /// `#pragma omp teams distribute parallel for`
182    TeamsDistributeParallelFor = 63,
183    /// `#pragma omp teams distribute parallel for simd`
184    TeamsDistributeParallelForSimd = 64,
185    /// `#pragma omp teams loop`
186    TeamsLoop = 65,
187
188    // ========================================================================
189    // Synchronization constructs
190    // ========================================================================
191    /// `#pragma omp barrier`
192    Barrier = 70,
193    /// `#pragma omp critical`
194    Critical = 71,
195    /// `#pragma omp atomic`
196    Atomic = 72,
197    /// `#pragma omp flush`
198    Flush = 73,
199    /// `#pragma omp ordered`
200    Ordered = 74,
201    /// `#pragma omp master`
202    Master = 75,
203    /// `#pragma omp masked`
204    Masked = 76,
205
206    // ========================================================================
207    // Declare constructs
208    // ========================================================================
209    /// `#pragma omp declare reduction`
210    DeclareReduction = 80,
211    /// `#pragma omp declare mapper`
212    DeclareMapper = 81,
213    /// `#pragma omp declare target`
214    DeclareTarget = 82,
215    /// `#pragma omp declare variant`
216    DeclareVariant = 83,
217
218    // ========================================================================
219    // Distribute constructs
220    // ========================================================================
221    /// `#pragma omp distribute`
222    Distribute = 90,
223    /// `#pragma omp distribute simd`
224    DistributeSimd = 91,
225    /// `#pragma omp distribute parallel for`
226    DistributeParallelFor = 92,
227    /// `#pragma omp distribute parallel for simd`
228    DistributeParallelForSimd = 93,
229
230    // ========================================================================
231    // Meta-directives
232    // ========================================================================
233    /// `#pragma omp metadirective`
234    Metadirective = 100,
235
236    // ========================================================================
237    // Other constructs
238    // ========================================================================
239    /// `#pragma omp threadprivate`
240    Threadprivate = 110,
241    /// `#pragma omp allocate`
242    Allocate = 111,
243    /// `#pragma omp requires`
244    Requires = 112,
245    /// `#pragma omp scan`
246    Scan = 113,
247    /// `#pragma omp depobj`
248    Depobj = 114,
249    /// `#pragma omp nothing`
250    Nothing = 115,
251    /// `#pragma omp error`
252    Error = 116,
253
254    /// Unknown or custom directive
255    Unknown = 255,
256}
257
258impl fmt::Display for DirectiveKind {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        match self {
261            // Parallel constructs
262            DirectiveKind::Parallel => write!(f, "parallel"),
263            DirectiveKind::ParallelFor => write!(f, "parallel for"),
264            DirectiveKind::ParallelForSimd => write!(f, "parallel for simd"),
265            DirectiveKind::ParallelSections => write!(f, "parallel sections"),
266            DirectiveKind::ParallelWorkshare => write!(f, "parallel workshare"),
267            DirectiveKind::ParallelLoop => write!(f, "parallel loop"),
268            DirectiveKind::ParallelMasked => write!(f, "parallel masked"),
269            DirectiveKind::ParallelMaster => write!(f, "parallel master"),
270
271            // Work-sharing constructs
272            DirectiveKind::For => write!(f, "for"),
273            DirectiveKind::ForSimd => write!(f, "for simd"),
274            DirectiveKind::Sections => write!(f, "sections"),
275            DirectiveKind::Section => write!(f, "section"),
276            DirectiveKind::Single => write!(f, "single"),
277            DirectiveKind::Workshare => write!(f, "workshare"),
278            DirectiveKind::Loop => write!(f, "loop"),
279
280            // SIMD constructs
281            DirectiveKind::Simd => write!(f, "simd"),
282            DirectiveKind::DeclareSimd => write!(f, "declare simd"),
283
284            // Task constructs
285            DirectiveKind::Task => write!(f, "task"),
286            DirectiveKind::Taskloop => write!(f, "taskloop"),
287            DirectiveKind::TaskloopSimd => write!(f, "taskloop simd"),
288            DirectiveKind::Taskyield => write!(f, "taskyield"),
289            DirectiveKind::Taskwait => write!(f, "taskwait"),
290            DirectiveKind::Taskgroup => write!(f, "taskgroup"),
291
292            // Target constructs
293            DirectiveKind::Target => write!(f, "target"),
294            DirectiveKind::TargetData => write!(f, "target data"),
295            DirectiveKind::TargetEnterData => write!(f, "target enter data"),
296            DirectiveKind::TargetExitData => write!(f, "target exit data"),
297            DirectiveKind::TargetUpdate => write!(f, "target update"),
298            DirectiveKind::TargetParallel => write!(f, "target parallel"),
299            DirectiveKind::TargetParallelFor => write!(f, "target parallel for"),
300            DirectiveKind::TargetParallelForSimd => write!(f, "target parallel for simd"),
301            DirectiveKind::TargetParallelLoop => write!(f, "target parallel loop"),
302            DirectiveKind::TargetSimd => write!(f, "target simd"),
303            DirectiveKind::TargetTeams => write!(f, "target teams"),
304            DirectiveKind::TargetTeamsDistribute => write!(f, "target teams distribute"),
305            DirectiveKind::TargetTeamsDistributeSimd => write!(f, "target teams distribute simd"),
306            DirectiveKind::TargetTeamsDistributeParallelFor => {
307                write!(f, "target teams distribute parallel for")
308            }
309            DirectiveKind::TargetTeamsDistributeParallelForSimd => {
310                write!(f, "target teams distribute parallel for simd")
311            }
312            DirectiveKind::TargetTeamsLoop => write!(f, "target teams loop"),
313
314            // Teams constructs
315            DirectiveKind::Teams => write!(f, "teams"),
316            DirectiveKind::TeamsDistribute => write!(f, "teams distribute"),
317            DirectiveKind::TeamsDistributeSimd => write!(f, "teams distribute simd"),
318            DirectiveKind::TeamsDistributeParallelFor => {
319                write!(f, "teams distribute parallel for")
320            }
321            DirectiveKind::TeamsDistributeParallelForSimd => {
322                write!(f, "teams distribute parallel for simd")
323            }
324            DirectiveKind::TeamsLoop => write!(f, "teams loop"),
325
326            // Synchronization constructs
327            DirectiveKind::Barrier => write!(f, "barrier"),
328            DirectiveKind::Critical => write!(f, "critical"),
329            DirectiveKind::Atomic => write!(f, "atomic"),
330            DirectiveKind::Flush => write!(f, "flush"),
331            DirectiveKind::Ordered => write!(f, "ordered"),
332            DirectiveKind::Master => write!(f, "master"),
333            DirectiveKind::Masked => write!(f, "masked"),
334
335            // Declare constructs
336            DirectiveKind::DeclareReduction => write!(f, "declare reduction"),
337            DirectiveKind::DeclareMapper => write!(f, "declare mapper"),
338            DirectiveKind::DeclareTarget => write!(f, "declare target"),
339            DirectiveKind::DeclareVariant => write!(f, "declare variant"),
340
341            // Distribute constructs
342            DirectiveKind::Distribute => write!(f, "distribute"),
343            DirectiveKind::DistributeSimd => write!(f, "distribute simd"),
344            DirectiveKind::DistributeParallelFor => write!(f, "distribute parallel for"),
345            DirectiveKind::DistributeParallelForSimd => {
346                write!(f, "distribute parallel for simd")
347            }
348
349            // Meta-directives
350            DirectiveKind::Metadirective => write!(f, "metadirective"),
351
352            // Other constructs
353            DirectiveKind::Threadprivate => write!(f, "threadprivate"),
354            DirectiveKind::Allocate => write!(f, "allocate"),
355            DirectiveKind::Requires => write!(f, "requires"),
356            DirectiveKind::Scan => write!(f, "scan"),
357            DirectiveKind::Depobj => write!(f, "depobj"),
358            DirectiveKind::Nothing => write!(f, "nothing"),
359            DirectiveKind::Error => write!(f, "error"),
360
361            DirectiveKind::Unknown => write!(f, "unknown"),
362        }
363    }
364}
365
366impl DirectiveKind {
367    /// Check if this is a parallel construct
368    pub fn is_parallel(&self) -> bool {
369        matches!(
370            self,
371            DirectiveKind::Parallel
372                | DirectiveKind::ParallelFor
373                | DirectiveKind::ParallelForSimd
374                | DirectiveKind::ParallelSections
375                | DirectiveKind::ParallelWorkshare
376                | DirectiveKind::ParallelLoop
377                | DirectiveKind::ParallelMasked
378                | DirectiveKind::ParallelMaster
379        )
380    }
381
382    /// Check if this is a work-sharing construct
383    pub fn is_worksharing(&self) -> bool {
384        matches!(
385            self,
386            DirectiveKind::For
387                | DirectiveKind::ForSimd
388                | DirectiveKind::Sections
389                | DirectiveKind::Section
390                | DirectiveKind::Single
391                | DirectiveKind::Workshare
392        )
393    }
394
395    /// Check if this is a SIMD construct
396    pub fn is_simd(&self) -> bool {
397        matches!(
398            self,
399            DirectiveKind::Simd
400                | DirectiveKind::DeclareSimd
401                | DirectiveKind::ForSimd
402                | DirectiveKind::ParallelForSimd
403                | DirectiveKind::TaskloopSimd
404                | DirectiveKind::TargetSimd
405                | DirectiveKind::TargetParallelForSimd
406                | DirectiveKind::TargetTeamsDistributeSimd
407                | DirectiveKind::TargetTeamsDistributeParallelForSimd
408                | DirectiveKind::TeamsDistributeSimd
409                | DirectiveKind::TeamsDistributeParallelForSimd
410                | DirectiveKind::DistributeSimd
411                | DirectiveKind::DistributeParallelForSimd
412        )
413    }
414
415    /// Check if this is a task construct
416    pub fn is_task(&self) -> bool {
417        matches!(
418            self,
419            DirectiveKind::Task
420                | DirectiveKind::Taskloop
421                | DirectiveKind::TaskloopSimd
422                | DirectiveKind::Taskyield
423                | DirectiveKind::Taskwait
424                | DirectiveKind::Taskgroup
425        )
426    }
427
428    /// Check if this is a target construct
429    pub fn is_target(&self) -> bool {
430        matches!(
431            self,
432            DirectiveKind::Target
433                | DirectiveKind::TargetData
434                | DirectiveKind::TargetEnterData
435                | DirectiveKind::TargetExitData
436                | DirectiveKind::TargetUpdate
437                | DirectiveKind::TargetParallel
438                | DirectiveKind::TargetParallelFor
439                | DirectiveKind::TargetParallelForSimd
440                | DirectiveKind::TargetParallelLoop
441                | DirectiveKind::TargetSimd
442                | DirectiveKind::TargetTeams
443                | DirectiveKind::TargetTeamsDistribute
444                | DirectiveKind::TargetTeamsDistributeSimd
445                | DirectiveKind::TargetTeamsDistributeParallelFor
446                | DirectiveKind::TargetTeamsDistributeParallelForSimd
447                | DirectiveKind::TargetTeamsLoop
448        )
449    }
450
451    /// Check if this is a teams construct
452    pub fn is_teams(&self) -> bool {
453        matches!(
454            self,
455            DirectiveKind::Teams
456                | DirectiveKind::TeamsDistribute
457                | DirectiveKind::TeamsDistributeSimd
458                | DirectiveKind::TeamsDistributeParallelFor
459                | DirectiveKind::TeamsDistributeParallelForSimd
460                | DirectiveKind::TeamsLoop
461                | DirectiveKind::TargetTeams
462                | DirectiveKind::TargetTeamsDistribute
463                | DirectiveKind::TargetTeamsDistributeSimd
464                | DirectiveKind::TargetTeamsDistributeParallelFor
465                | DirectiveKind::TargetTeamsDistributeParallelForSimd
466                | DirectiveKind::TargetTeamsLoop
467        )
468    }
469
470    /// Check if this is a loop construct
471    pub fn is_loop(&self) -> bool {
472        matches!(
473            self,
474            DirectiveKind::For
475                | DirectiveKind::ForSimd
476                | DirectiveKind::Loop
477                | DirectiveKind::ParallelFor
478                | DirectiveKind::ParallelForSimd
479                | DirectiveKind::ParallelLoop
480                | DirectiveKind::Simd
481                | DirectiveKind::Taskloop
482                | DirectiveKind::TaskloopSimd
483                | DirectiveKind::Distribute
484                | DirectiveKind::DistributeSimd
485                | DirectiveKind::DistributeParallelFor
486                | DirectiveKind::DistributeParallelForSimd
487        )
488    }
489
490    /// Check if this is a synchronization construct
491    pub fn is_synchronization(&self) -> bool {
492        matches!(
493            self,
494            DirectiveKind::Barrier
495                | DirectiveKind::Critical
496                | DirectiveKind::Atomic
497                | DirectiveKind::Flush
498                | DirectiveKind::Ordered
499                | DirectiveKind::Master
500                | DirectiveKind::Masked
501        )
502    }
503
504    /// Check if this is a declare construct
505    pub fn is_declare(&self) -> bool {
506        matches!(
507            self,
508            DirectiveKind::DeclareReduction
509                | DirectiveKind::DeclareMapper
510                | DirectiveKind::DeclareTarget
511                | DirectiveKind::DeclareVariant
512                | DirectiveKind::DeclareSimd
513        )
514    }
515
516    /// Check if this directive has a structured block (requires end directive)
517    pub fn has_structured_block(&self) -> bool {
518        !matches!(
519            self,
520            DirectiveKind::Barrier
521                | DirectiveKind::Taskyield
522                | DirectiveKind::Taskwait
523                | DirectiveKind::Flush
524                | DirectiveKind::TargetEnterData
525                | DirectiveKind::TargetExitData
526                | DirectiveKind::TargetUpdate
527                | DirectiveKind::Threadprivate
528                | DirectiveKind::DeclareSimd
529                | DirectiveKind::DeclareReduction
530                | DirectiveKind::DeclareMapper
531                | DirectiveKind::DeclareTarget
532                | DirectiveKind::DeclareVariant
533                | DirectiveKind::Scan
534                | DirectiveKind::Depobj
535                | DirectiveKind::Nothing
536                | DirectiveKind::Error
537                | DirectiveKind::Section
538        )
539    }
540}
541
542// ============================================================================
543// DirectiveIR: Complete directive representation
544// ============================================================================
545
546/// Complete IR representation of an OpenMP directive
547///
548/// This is the top-level structure that combines all IR components:
549/// - Directive type (kind)
550/// - Semantic clause data
551/// - Source location
552/// - Language context
553/// - Optional metadata for special directives
554///
555/// ## Examples
556///
557/// ```
558/// # use roup::ir::{DirectiveIR, DirectiveKind, ClauseData, DefaultKind, Language, SourceLocation};
559/// let dir = DirectiveIR::new(
560///     DirectiveKind::Parallel,
561///     "parallel",
562///     vec![ClauseData::Default(DefaultKind::Shared)],
563///     SourceLocation::new(10, 1),
564///     Language::C,
565/// );
566///
567/// assert_eq!(dir.kind(), DirectiveKind::Parallel);
568/// assert_eq!(dir.clauses().len(), 1);
569/// assert!(dir.kind().is_parallel());
570/// ```
571///
572/// ## Learning: Box for Large Structures
573///
574/// Since `DirectiveIR` can contain a large `Vec<ClauseData>`, and `ClauseData`
575/// variants can themselves be large, we use `Box<[ClauseData]>` instead of
576/// `Vec<ClauseData>` for the final immutable representation. This:
577///
578/// 1. Reduces struct size (Box is one pointer)
579/// 2. Signals immutability (boxed slice can't grow/shrink)
580/// 3. Saves memory (no extra capacity like Vec)
581///
582/// We still accept `Vec` in constructors for convenience, then convert to Box.
583///
584/// ## Memory Model (Safety Fix)
585///
586/// **IMPORTANT**: This struct now stores an owned `name: String` to prevent use-after-free bugs.
587///
588/// **Why?**
589/// - Directive names from line continuations are stored in `Cow::Owned`
590/// - Previously, IR borrowed from this `Cow` via `'a` lifetime
591/// - When `Directive` dropped, `Cow::Owned` was freed → dangling pointers
592/// - **Solution**: DirectiveIR now owns the normalized directive name
593///
594/// **Performance**: Minimal overhead (~50ns String allocation). See `docs/PERFORMANCE_ANALYSIS.md`.
595#[derive(Debug, Clone, PartialEq)]
596pub struct DirectiveIR {
597    /// The kind of directive
598    kind: DirectiveKind,
599
600    /// The normalized directive name (owned to prevent use-after-free)
601    ///
602    /// This is cloned from the parser's `Cow<'a, str>` during conversion.
603    /// Storing it here ensures the IR is self-contained and doesn't depend
604    /// on the parser's lifetime.
605    ///
606    /// Examples: "parallel", "parallel for", "target teams distribute"
607    name: String,
608
609    /// Semantic clause data
610    ///
611    /// Using `Box<[ClauseData]>` instead of `Vec<ClauseData>` for the final representation:
612    /// - Smaller size (one pointer vs three)
613    /// - Signals immutability (can't grow)
614    /// - Saves memory (no unused capacity)
615    clauses: Box<[ClauseData]>,
616
617    /// Source location where this directive appears
618    location: SourceLocation,
619
620    /// Language context (C, C++, Fortran)
621    language: Language,
622}
623
624impl DirectiveIR {
625    /// Create a new directive IR
626    ///
627    /// ## Example
628    ///
629    /// ```
630    /// # use roup::ir::{DirectiveIR, DirectiveKind, ClauseData, ReductionOperator, Identifier, Language, SourceLocation};
631    /// let clauses = vec![
632    ///     ClauseData::Reduction {
633    ///         operator: ReductionOperator::Add,
634    ///         items: vec![Identifier::new("sum").into()],
635    ///     },
636    /// ];
637    ///
638    /// let dir = DirectiveIR::new(
639    ///     DirectiveKind::ParallelFor,
640    ///     "parallel for",
641    ///     clauses,
642    ///     SourceLocation::new(42, 1),
643    ///     Language::C,
644    /// );
645    ///
646    /// assert_eq!(dir.kind(), DirectiveKind::ParallelFor);
647    /// assert_eq!(dir.name(), "parallel for");
648    /// ```
649    pub fn new(
650        kind: DirectiveKind,
651        name: &str,
652        clauses: Vec<ClauseData>,
653        location: SourceLocation,
654        language: Language,
655    ) -> Self {
656        Self {
657            kind,
658            name: name.to_string(),
659            clauses: clauses.into_boxed_slice(),
660            location,
661            language,
662        }
663    }
664
665    // ========================================================================
666    // Convenience constructors
667    // ========================================================================
668
669    /// Create a simple directive with no clauses
670    ///
671    /// ## Example
672    ///
673    /// ```
674    /// # use roup::ir::{DirectiveIR, DirectiveKind, Language, SourceLocation};
675    /// let dir = DirectiveIR::simple(DirectiveKind::Barrier, "barrier", SourceLocation::start(), Language::C);
676    /// assert_eq!(dir.clauses().len(), 0);
677    /// ```
678    pub fn simple(
679        kind: DirectiveKind,
680        name: &str,
681        location: SourceLocation,
682        language: Language,
683    ) -> Self {
684        Self::new(kind, name, vec![], location, language)
685    }
686
687    /// Create a parallel directive with common clauses
688    ///
689    /// Convenience constructor for the most common OpenMP pattern.
690    ///
691    /// ## Example
692    ///
693    /// ```
694    /// # use roup::ir::{DirectiveIR, DefaultKind, Language, SourceLocation};
695    /// let dir = DirectiveIR::parallel(
696    ///     Some(DefaultKind::Shared),
697    ///     SourceLocation::start(),
698    ///     Language::C
699    /// );
700    /// assert!(dir.has_clause(|c| c.is_default()));
701    /// ```
702    pub fn parallel(
703        default: Option<super::DefaultKind>,
704        location: SourceLocation,
705        language: Language,
706    ) -> Self {
707        let mut clauses = vec![];
708        if let Some(kind) = default {
709            clauses.push(ClauseData::Default(kind));
710        }
711        Self::new(
712            DirectiveKind::Parallel,
713            "parallel",
714            clauses,
715            location,
716            language,
717        )
718    }
719
720    /// Create a for loop directive with schedule
721    ///
722    /// ## Example
723    ///
724    /// ```
725    /// # use roup::ir::{DirectiveIR, ScheduleKind, Language, SourceLocation};
726    /// let dir = DirectiveIR::for_loop(
727    ///     ScheduleKind::Static,
728    ///     None,
729    ///     SourceLocation::start(),
730    ///     Language::C
731    /// );
732    /// assert!(dir.has_clause(|c| c.is_schedule()));
733    /// ```
734    pub fn for_loop(
735        schedule: super::ScheduleKind,
736        chunk_size: Option<super::Expression>,
737        location: SourceLocation,
738        language: Language,
739    ) -> Self {
740        let clauses = vec![ClauseData::Schedule {
741            kind: schedule,
742            modifiers: vec![],
743            chunk_size,
744        }];
745        Self::new(DirectiveKind::For, "for", clauses, location, language)
746    }
747
748    /// Create a barrier directive (always simple)
749    ///
750    /// ## Example
751    ///
752    /// ```
753    /// # use roup::ir::{DirectiveIR, DirectiveKind, Language, SourceLocation};
754    /// let dir = DirectiveIR::barrier(SourceLocation::start(), Language::C);
755    /// assert_eq!(dir.kind(), DirectiveKind::Barrier);
756    /// assert_eq!(dir.clauses().len(), 0);
757    /// ```
758    pub fn barrier(location: SourceLocation, language: Language) -> Self {
759        Self::simple(DirectiveKind::Barrier, "barrier", location, language)
760    }
761
762    /// Create a taskwait directive (always simple)
763    pub fn taskwait(location: SourceLocation, language: Language) -> Self {
764        Self::simple(DirectiveKind::Taskwait, "taskwait", location, language)
765    }
766
767    /// Create a taskyield directive (always simple)
768    pub fn taskyield(location: SourceLocation, language: Language) -> Self {
769        Self::simple(DirectiveKind::Taskyield, "taskyield", location, language)
770    }
771
772    // ========================================================================
773    // Query API
774    // ========================================================================
775
776    /// Get the directive kind
777    pub fn kind(&self) -> DirectiveKind {
778        self.kind
779    }
780
781    /// Get the normalized directive name
782    ///
783    /// This returns the directive name as it appears in the source,
784    /// after normalization (e.g., line continuations collapsed).
785    ///
786    /// ## Example
787    ///
788    /// ```
789    /// # use roup::ir::{DirectiveIR, DirectiveKind, Language, SourceLocation};
790    /// let dir = DirectiveIR::simple(DirectiveKind::ParallelFor, "parallel for", SourceLocation::start(), Language::C);
791    /// assert_eq!(dir.name(), "parallel for");
792    /// ```
793    pub fn name(&self) -> &str {
794        &self.name
795    }
796
797    /// Get the clauses
798    pub fn clauses(&self) -> &[ClauseData] {
799        &self.clauses
800    }
801
802    /// Get the source location
803    pub fn location(&self) -> SourceLocation {
804        self.location
805    }
806
807    /// Get the language
808    pub fn language(&self) -> Language {
809        self.language
810    }
811
812    /// Check if this directive has a specific clause type
813    ///
814    /// ## Example
815    ///
816    /// ```
817    /// # use roup::ir::{DirectiveIR, DirectiveKind, ClauseData, DefaultKind, Language, SourceLocation};
818    /// let dir = DirectiveIR::new(
819    ///     DirectiveKind::Parallel,
820    ///     "parallel",
821    ///     vec![ClauseData::Default(DefaultKind::Shared)],
822    ///     SourceLocation::start(),
823    ///     Language::C,
824    /// );
825    ///
826    /// assert!(dir.has_clause(|c| matches!(c, ClauseData::Default(_))));
827    /// assert!(!dir.has_clause(|c| matches!(c, ClauseData::Private { .. })));
828    /// ```
829    pub fn has_clause<F>(&self, predicate: F) -> bool
830    where
831        F: Fn(&ClauseData) -> bool,
832    {
833        self.clauses.iter().any(predicate)
834    }
835
836    /// Find first clause matching predicate
837    pub fn find_clause<F>(&self, predicate: F) -> Option<&ClauseData>
838    where
839        F: Fn(&ClauseData) -> bool,
840    {
841        self.clauses.iter().find(|c| predicate(c))
842    }
843
844    /// Count clauses matching predicate
845    pub fn count_clauses<F>(&self, predicate: F) -> usize
846    where
847        F: Fn(&ClauseData) -> bool,
848    {
849        self.clauses.iter().filter(|c| predicate(c)).count()
850    }
851
852    /// Get all clauses matching predicate
853    pub fn filter_clauses<F>(&self, predicate: F) -> Vec<&ClauseData>
854    where
855        F: Fn(&ClauseData) -> bool,
856    {
857        self.clauses.iter().filter(|c| predicate(c)).collect()
858    }
859}
860
861impl fmt::Display for DirectiveIR {
862    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
863        // Write pragma prefix (already includes "omp ")
864        write!(f, "{}{}", self.language.pragma_prefix(), self.kind)?;
865
866        // Write clauses
867        for clause in self.clauses.iter() {
868            write!(f, " {clause}")?;
869        }
870
871        Ok(())
872    }
873}
874
875// ============================================================================
876// Tests
877// ============================================================================
878
879#[cfg(test)]
880mod tests {
881    use super::*;
882    use crate::ir::{ClauseItem, DefaultKind, Identifier, ReductionOperator};
883
884    // DirectiveKind tests
885    #[test]
886    fn test_directive_kind_display() {
887        assert_eq!(DirectiveKind::Parallel.to_string(), "parallel");
888        assert_eq!(DirectiveKind::ParallelFor.to_string(), "parallel for");
889        assert_eq!(DirectiveKind::For.to_string(), "for");
890        assert_eq!(DirectiveKind::Target.to_string(), "target");
891        assert_eq!(
892            DirectiveKind::TargetTeamsDistributeParallelForSimd.to_string(),
893            "target teams distribute parallel for simd"
894        );
895    }
896
897    #[test]
898    fn test_directive_kind_is_parallel() {
899        assert!(DirectiveKind::Parallel.is_parallel());
900        assert!(DirectiveKind::ParallelFor.is_parallel());
901        assert!(DirectiveKind::ParallelForSimd.is_parallel());
902        assert!(!DirectiveKind::For.is_parallel());
903        assert!(!DirectiveKind::Target.is_parallel());
904    }
905
906    #[test]
907    fn test_directive_kind_is_worksharing() {
908        assert!(DirectiveKind::For.is_worksharing());
909        assert!(DirectiveKind::Sections.is_worksharing());
910        assert!(DirectiveKind::Single.is_worksharing());
911        assert!(!DirectiveKind::Parallel.is_worksharing());
912    }
913
914    #[test]
915    fn test_directive_kind_is_simd() {
916        assert!(DirectiveKind::Simd.is_simd());
917        assert!(DirectiveKind::ForSimd.is_simd());
918        assert!(DirectiveKind::ParallelForSimd.is_simd());
919        assert!(!DirectiveKind::For.is_simd());
920        assert!(!DirectiveKind::Parallel.is_simd());
921    }
922
923    #[test]
924    fn test_directive_kind_is_task() {
925        assert!(DirectiveKind::Task.is_task());
926        assert!(DirectiveKind::Taskloop.is_task());
927        assert!(DirectiveKind::Taskyield.is_task());
928        assert!(!DirectiveKind::Parallel.is_task());
929    }
930
931    #[test]
932    fn test_directive_kind_is_target() {
933        assert!(DirectiveKind::Target.is_target());
934        assert!(DirectiveKind::TargetData.is_target());
935        assert!(DirectiveKind::TargetTeams.is_target());
936        assert!(!DirectiveKind::Teams.is_target());
937        assert!(!DirectiveKind::Parallel.is_target());
938    }
939
940    #[test]
941    fn test_directive_kind_is_teams() {
942        assert!(DirectiveKind::Teams.is_teams());
943        assert!(DirectiveKind::TeamsDistribute.is_teams());
944        assert!(DirectiveKind::TargetTeams.is_teams());
945        assert!(!DirectiveKind::Target.is_teams());
946    }
947
948    #[test]
949    fn test_directive_kind_is_loop() {
950        assert!(DirectiveKind::For.is_loop());
951        assert!(DirectiveKind::Loop.is_loop());
952        assert!(DirectiveKind::Simd.is_loop());
953        assert!(!DirectiveKind::Parallel.is_loop());
954        assert!(!DirectiveKind::Barrier.is_loop());
955    }
956
957    #[test]
958    fn test_directive_kind_is_synchronization() {
959        assert!(DirectiveKind::Barrier.is_synchronization());
960        assert!(DirectiveKind::Critical.is_synchronization());
961        assert!(DirectiveKind::Atomic.is_synchronization());
962        assert!(!DirectiveKind::Parallel.is_synchronization());
963    }
964
965    #[test]
966    fn test_directive_kind_is_declare() {
967        assert!(DirectiveKind::DeclareReduction.is_declare());
968        assert!(DirectiveKind::DeclareTarget.is_declare());
969        assert!(DirectiveKind::DeclareSimd.is_declare());
970        assert!(!DirectiveKind::Parallel.is_declare());
971    }
972
973    #[test]
974    fn test_directive_kind_has_structured_block() {
975        assert!(DirectiveKind::Parallel.has_structured_block());
976        assert!(DirectiveKind::For.has_structured_block());
977        assert!(DirectiveKind::Critical.has_structured_block());
978        assert!(!DirectiveKind::Barrier.has_structured_block());
979        assert!(!DirectiveKind::Taskyield.has_structured_block());
980        assert!(!DirectiveKind::DeclareTarget.has_structured_block());
981    }
982
983    // DirectiveIR tests
984    #[test]
985    fn test_directive_ir_new() {
986        let dir = DirectiveIR::new(
987            DirectiveKind::Parallel,
988            "parallel",
989            vec![],
990            SourceLocation::new(10, 1),
991            Language::C,
992        );
993
994        assert_eq!(dir.kind(), DirectiveKind::Parallel);
995        assert_eq!(dir.clauses().len(), 0);
996        assert_eq!(dir.location(), SourceLocation::new(10, 1));
997        assert_eq!(dir.language(), Language::C);
998    }
999
1000    #[test]
1001    fn test_directive_ir_with_clauses() {
1002        let clauses = vec![
1003            ClauseData::Default(DefaultKind::Shared),
1004            ClauseData::Private {
1005                items: vec![ClauseItem::Identifier(Identifier::new("x"))],
1006            },
1007        ];
1008
1009        let dir = DirectiveIR::new(
1010            DirectiveKind::Parallel,
1011            "parallel",
1012            clauses,
1013            SourceLocation::start(),
1014            Language::C,
1015        );
1016
1017        assert_eq!(dir.clauses().len(), 2);
1018    }
1019
1020    #[test]
1021    fn test_directive_ir_has_clause() {
1022        let dir = DirectiveIR::new(
1023            DirectiveKind::Parallel,
1024            "parallel",
1025            vec![ClauseData::Default(DefaultKind::Shared)],
1026            SourceLocation::start(),
1027            Language::C,
1028        );
1029
1030        assert!(dir.has_clause(|c| matches!(c, ClauseData::Default(_))));
1031        assert!(!dir.has_clause(|c| matches!(c, ClauseData::Private { .. })));
1032    }
1033
1034    #[test]
1035    fn test_directive_ir_find_clause() {
1036        let dir = DirectiveIR::new(
1037            DirectiveKind::Parallel,
1038            "parallel",
1039            vec![
1040                ClauseData::Default(DefaultKind::Shared),
1041                ClauseData::Private { items: vec![] },
1042            ],
1043            SourceLocation::start(),
1044            Language::C,
1045        );
1046
1047        let found = dir.find_clause(|c| matches!(c, ClauseData::Default(_)));
1048        assert!(found.is_some());
1049        assert!(matches!(found.unwrap(), ClauseData::Default(_)));
1050    }
1051
1052    #[test]
1053    fn test_directive_ir_count_clauses() {
1054        let dir = DirectiveIR::new(
1055            DirectiveKind::Parallel,
1056            "parallel",
1057            vec![
1058                ClauseData::Private { items: vec![] },
1059                ClauseData::Default(DefaultKind::Shared),
1060                ClauseData::Private { items: vec![] },
1061            ],
1062            SourceLocation::start(),
1063            Language::C,
1064        );
1065
1066        assert_eq!(
1067            dir.count_clauses(|c| matches!(c, ClauseData::Private { .. })),
1068            2
1069        );
1070        assert_eq!(
1071            dir.count_clauses(|c| matches!(c, ClauseData::Default(_))),
1072            1
1073        );
1074    }
1075
1076    #[test]
1077    fn test_directive_ir_filter_clauses() {
1078        let dir = DirectiveIR::new(
1079            DirectiveKind::Parallel,
1080            "parallel",
1081            vec![
1082                ClauseData::Private { items: vec![] },
1083                ClauseData::Default(DefaultKind::Shared),
1084                ClauseData::Private { items: vec![] },
1085            ],
1086            SourceLocation::start(),
1087            Language::C,
1088        );
1089
1090        let private_clauses = dir.filter_clauses(|c| matches!(c, ClauseData::Private { .. }));
1091        assert_eq!(private_clauses.len(), 2);
1092    }
1093
1094    #[test]
1095    fn test_directive_ir_display() {
1096        let dir = DirectiveIR::new(
1097            DirectiveKind::Parallel,
1098            "parallel",
1099            vec![ClauseData::Default(DefaultKind::Shared)],
1100            SourceLocation::start(),
1101            Language::C,
1102        );
1103
1104        let display = dir.to_string();
1105        assert!(display.contains("pragma"));
1106        assert!(display.contains("omp"));
1107        assert!(display.contains("parallel"));
1108        assert!(display.contains("default"));
1109    }
1110
1111    #[test]
1112    fn test_directive_ir_display_with_reduction() {
1113        let clauses = vec![ClauseData::Reduction {
1114            operator: ReductionOperator::Add,
1115            items: vec![ClauseItem::Identifier(Identifier::new("sum"))],
1116        }];
1117
1118        let dir = DirectiveIR::new(
1119            DirectiveKind::ParallelFor,
1120            "parallel for",
1121            clauses,
1122            SourceLocation::start(),
1123            Language::C,
1124        );
1125
1126        let display = dir.to_string();
1127        assert!(display.contains("parallel for"));
1128        assert!(display.contains("reduction"));
1129        assert!(display.contains("+"));
1130        assert!(display.contains("sum"));
1131    }
1132
1133    #[test]
1134    fn test_directive_ir_clone() {
1135        let dir1 = DirectiveIR::new(
1136            DirectiveKind::Parallel,
1137            "parallel",
1138            vec![ClauseData::Default(DefaultKind::Shared)],
1139            SourceLocation::start(),
1140            Language::C,
1141        );
1142
1143        let dir2 = dir1.clone();
1144        assert_eq!(dir1, dir2);
1145    }
1146
1147    #[test]
1148    fn test_directive_ir_equality() {
1149        let dir1 = DirectiveIR::new(
1150            DirectiveKind::Parallel,
1151            "parallel",
1152            vec![],
1153            SourceLocation::start(),
1154            Language::C,
1155        );
1156
1157        let dir2 = DirectiveIR::new(
1158            DirectiveKind::Parallel,
1159            "parallel",
1160            vec![],
1161            SourceLocation::start(),
1162            Language::C,
1163        );
1164
1165        let dir3 = DirectiveIR::new(
1166            DirectiveKind::For,
1167            "for",
1168            vec![],
1169            SourceLocation::start(),
1170            Language::C,
1171        );
1172
1173        assert_eq!(dir1, dir2);
1174        assert_ne!(dir1, dir3);
1175    }
1176
1177    // Corner case: very long directive name
1178    #[test]
1179    fn test_directive_kind_longest_name() {
1180        let kind = DirectiveKind::TargetTeamsDistributeParallelForSimd;
1181        assert_eq!(
1182            kind.to_string(),
1183            "target teams distribute parallel for simd"
1184        );
1185        assert!(kind.is_target());
1186        assert!(kind.is_teams());
1187        assert!(kind.is_simd());
1188    }
1189
1190    // Corner case: deprecated construct
1191    #[test]
1192    fn test_directive_kind_deprecated() {
1193        let kind = DirectiveKind::ParallelMaster;
1194        assert_eq!(kind.to_string(), "parallel master");
1195        assert!(kind.is_parallel());
1196    }
1197
1198    // Corner case: empty directive
1199    #[test]
1200    fn test_directive_ir_no_clauses() {
1201        let dir = DirectiveIR::new(
1202            DirectiveKind::Barrier,
1203            "barrier",
1204            vec![],
1205            SourceLocation::start(),
1206            Language::C,
1207        );
1208
1209        assert_eq!(dir.clauses().len(), 0);
1210        assert!(!dir.has_clause(|_| true));
1211    }
1212}