Skip to content

[flang][openacc] Make OpenACC block construct parse errors less verbose. #131042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 26, 2025

Conversation

akuhlens
Copy link
Contributor

@akuhlens akuhlens commented Mar 12, 2025

This PR does reduces the verbosity of parser errors for OpenACC block constructs that do not parse correctly because they are missing their trailing end block directive by:

  • Removing the redundant error messages created by parsing 3 different styles of directive tokens.
  • Providing a general mechanism of configuring the max number of contexts printed for every syntax error.
  • Not printing less specific contexts that are at the same location.

Prior to the changes:

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2>&1 | tee acc-data-statement.prior.log | wc -l
262

acc-data-statement.prior.log

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2>&1 | tee acc-data-statement.prior.log | wc -l
73

acc-data-statement.post.log

@llvmbot llvmbot added flang:driver flang Flang issues not falling into any other category openacc flang:parser labels Mar 12, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 12, 2025

@llvm/pr-subscribers-flang-parser

@llvm/pr-subscribers-flang-driver

Author: Andre Kuhlenschmidt (akuhlens)

Changes

This PR does reduces the verbosity of parser errors for OpenACC block constructs that do not parse correctly because they are missing their trailing end block directive by:

  • Removing the redundant error messages created by parsing 3 different styles of directive tokens.
  • Providing a general mechanism of configuring the max number of contexts printed for every syntax error.
  • Not printing less specific contexts that are at the same location.

Prior to the changes:

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2>&1 | tee acc-data-statement.prior.log | wc -l
520

acc-data-statement.prior.log

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2>&1 | tee acc-data-statement.prior.log | wc -l
118

acc-data-statement.post.log

Note there are still several bugs in the parsing that I would like to fix, but I am uploading this to talk with @klausler about where to look for those.

  • Off by one error in location for some open acc data directives. Probably caused by me modifying the "!$ACC" token, but I don't know how to fix it.
  • Duplicated error messages. Under IF and DO constructs
  • Multiple parses for the same error message
  • Flagging one problem as an error in specification part of subroutine.

Full diff: https://github.com/llvm/llvm-project/pull/131042.diff

5 Files Affected:

  • (modified) flang/lib/Parser/message.cpp (+35-5)
  • (modified) flang/lib/Parser/openacc-parsers.cpp (+7-2)
  • (modified) flang/test/Driver/debug-parsing-log.f90 (+9-9)
  • (added) flang/test/Parser/acc-data-statement.f90 (+245)
  • (added) flang/test/Parser/acc.f (+96)
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index 69e4814bf246c..0b8d300189a1b 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -272,6 +272,10 @@ static llvm::raw_ostream::Colors PrefixColor(Severity severity) {
   return llvm::raw_ostream::SAVEDCOLOR;
 }
 
+// FIXME: Make these configurable, based on verbosity level.
+const int MAX_CONTEXTS_EMITTED = 2;
+const bool OMIT_SHARED_CONTEXTS = true;
+
 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
     bool echoSourceLine) const {
   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
@@ -279,12 +283,38 @@ void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
   sources.EmitMessage(o, provenanceRange, ToString(), Prefix(severity()),
       PrefixColor(severity()), echoSourceLine);
   bool isContext{attachmentIsContext_};
+  int contextsEmitted{isContext ? 1 : 0};
+  // Emit attachments.
   for (const Message *attachment{attachment_.get()}; attachment;
-       attachment = attachment->attachment_.get()) {
+      attachment = attachment->attachment_.get()) {
     Severity severity = isContext ? Severity::Context : attachment->severity();
-    sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),
-        attachment->ToString(), Prefix(severity), PrefixColor(severity),
-        echoSourceLine);
+    auto emitAttachment = [&]() {
+      sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),
+          attachment->ToString(), Prefix(severity), PrefixColor(severity),
+          echoSourceLine);
+    };
+    // TODO isContext is not used correctly here.
+    if (attachment->attachmentIsContext_) {
+      // Truncate the number of contexts emitted.
+      if (contextsEmitted <= MAX_CONTEXTS_EMITTED) {
+        emitAttachment();
+        contextsEmitted += 1;
+      }
+      if (OMIT_SHARED_CONTEXTS) {
+        // Skip less specific contexts at the same location.
+        for (const Message *next_attachment{attachment->attachment_.get()};
+            next_attachment && next_attachment->attachmentIsContext_ &&
+            next_attachment->AtSameLocation(*attachment);
+            next_attachment = next_attachment->attachment_.get()) {
+          attachment = next_attachment;
+        }
+        // NB, this loop increments `attachment` one more time after the
+        // previous loop is done advancing it to the last context at the same
+        // location.
+      }
+    } else {
+      emitAttachment();
+    }
   }
 }
 
@@ -298,7 +328,7 @@ bool Message::operator==(const Message &that) const {
   }
   const Message *thatAttachment{that.attachment_.get()};
   for (const Message *attachment{attachment_.get()}; attachment;
-       attachment = attachment->attachment_.get()) {
+      attachment = attachment->attachment_.get()) {
     if (!thatAttachment || !attachment->AtSameLocation(*thatAttachment) ||
         attachment->ToString() != thatAttachment->ToString() ||
         attachment->severity() != thatAttachment->severity()) {
diff --git a/flang/lib/Parser/openacc-parsers.cpp b/flang/lib/Parser/openacc-parsers.cpp
index c78676664e0a3..6739bcc035fc2 100644
--- a/flang/lib/Parser/openacc-parsers.cpp
+++ b/flang/lib/Parser/openacc-parsers.cpp
@@ -19,8 +19,12 @@
 // OpenACC Directives and Clauses
 namespace Fortran::parser {
 
+// Only need to handle ! line comments because prescanning normalizes the
+// other types of line comments from fixed form.
 constexpr auto startAccLine{skipStuffBeforeStatement >>
-    ("!$ACC "_sptok || "C$ACC "_sptok || "*$ACC "_sptok)};
+    withMessage(
+        "expected OpenACC comment '!$ACC' (free-form), 'C$ACC', or '*$ACC' (fixed-form)"_err_en_US,
+        "!$ACC "_sptok)};
 constexpr auto endAccLine{space >> endOfLine};
 
 // Autogenerated clauses parser. Information is taken from ACC.td and the
@@ -225,7 +229,8 @@ TYPE_PARSER(startAccLine >> sourced(construct<AccEndBlockDirective>("END"_tok >>
 
 TYPE_PARSER(construct<OpenACCBlockConstruct>(
     Parser<AccBeginBlockDirective>{} / endAccLine, block,
-    Parser<AccEndBlockDirective>{} / endAccLine))
+    withMessage("expected OpenACC end block directive"_err_en_US,
+        Parser<AccEndBlockDirective>{} / endAccLine)))
 
 // Standalone constructs
 TYPE_PARSER(construct<OpenACCStandaloneConstruct>(
diff --git a/flang/test/Driver/debug-parsing-log.f90 b/flang/test/Driver/debug-parsing-log.f90
index 7297163109450..4e56add386ef8 100644
--- a/flang/test/Driver/debug-parsing-log.f90
+++ b/flang/test/Driver/debug-parsing-log.f90
@@ -12,14 +12,14 @@
 ! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: IMPLICIT statement
 ! CHECK-NEXT:   END PROGRAM
 ! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: implicit part
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: specification part
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: main program
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
+
+
+
+
+
+
+
+
+
 
 END PROGRAM
diff --git a/flang/test/Parser/acc-data-statement.f90 b/flang/test/Parser/acc-data-statement.f90
new file mode 100644
index 0000000000000..1e369a0db3780
--- /dev/null
+++ b/flang/test/Parser/acc-data-statement.f90
@@ -0,0 +1,245 @@
+! RUN: not %flang_fc1 -fsyntax-only -fopenacc %s 2>&1 | FileCheck %s
+program acc_data_test
+    implicit none
+    integer :: a(100), b(100), c(100), d(100)
+    integer :: i, s ! FIXME: if s is named sum you get semantic errors.
+
+    ! Positive tests
+
+    ! Basic data construct in program body
+    !$acc data copy(a, b) create(c)
+    a = 1
+    b = 2
+    c = a + b
+    !$acc end data
+    print *, "After first data region"
+
+    ! Data construct within IF block
+    if (.true.) then
+        !$acc data copyout(a)
+        a = a + 1
+        !$acc end data
+        print *, "Inside if block"
+    end if
+
+    ! Data construct within DO loop
+    do i = 1, 10
+        !$acc data present(a)
+        a(i) = a(i) * 2
+        !$acc end data
+        print *, "Loop iteration", i
+    end do
+
+    ! Nested data constructs
+    !$acc data copyin(a)
+    s = 0
+    !$acc data copy(s)
+    s = s + 1
+    !$acc end data
+    print *, "After nested data"
+    !$acc end data
+
+    ! Negative tests  
+    ! Basic data construct in program body
+    !$acc data copy(a, b) create(d)
+    a = 1
+    b = 2
+    d = a + b
+!   !$acc end data
+    print *, "After first data region"
+
+    ! Data construct within IF block
+    if (.true.) then
+        !$acc data copyout(a)
+        a = a + 1
+!       !$acc end data
+        print *, "Inside if block"
+        ! First error in the file.
+        !CHECK: acc-data-statement.f90:
+        !CHECK-SAME: [[ELINE1:[0-9]+]]:{{[0-9]+}}:
+        !CHECK-SAME: error: expected OpenACC end block directive
+        !CHECK-NEXT: end if
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyout(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: IF construct
+        !CHECK-NEXT: if (.true.) then
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: error: expected OpenACC end block directive
+        !CHECK-NEXT: end if
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyout(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: IF construct
+        !CHECK-NEXT: if (.true.) then
+        !CHECK-NEXT: ^
+    end if
+
+    ! Data construct within DO loop
+    do i = 1, 10
+        !$acc data present(a)
+        a(i) = a(i) * 2
+!       !$acc end data
+        print *, "Loop iteration", i
+        !CHECK: acc-data-statement.f90:
+        !CHECK-NOT:  [[ELINE1]]
+        !CHECK-SAME: [[ELINE2:[0-9]+]]:{{[0-9]+}}:
+        !CHECK-SAME: error: expected OpenACC end block directive
+        !CHECK-NEXT: end do
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data present(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: DO construct
+        !CHECK-NEXT: do i = 1, 10
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: error: expected OpenACC end block directive
+        !CHECK-NEXT: end do 
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data present(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: DO construct
+        !CHECK-NEXT: do i = 1, 10
+        !CHECK-NEXT: ^
+    end do
+
+    ! Nested data constructs
+    !$acc data copyin(a)
+    s = 0
+    !$acc data copy(s)
+    s = s + 1
+!   !$acc end data
+    print *, "After nested data"
+!   !$acc end data
+
+    print *, "Program finished"
+    !CHECK: acc-data-statement.f90:
+    !CHECK-NOT:  [[ELINE2]]
+    !CHECK-SAME: [[ELINE3:[0-9]+]]:{{[0-9]+}}:
+    !CHECK-SAME: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+contains
+    subroutine positive_process_array(x)
+        integer, intent(inout) :: x(:)
+        
+        ! Data construct in subroutine
+        !$acc data copy(x)
+        x = x + 1
+        !$acc end data
+        print *, "Subroutine finished"
+    end subroutine
+
+    function positive_compute_sum(x) result(total)
+        integer, intent(in) :: x(:)
+        integer :: total
+        
+        ! Data construct in function
+        !$acc data copyin(x) copy(total)
+        total = sum(x)
+        !$acc end data
+        print *, "Function finished"
+    end function
+    
+    subroutine negative_process_array(x)
+        integer, intent(inout) :: x(:)
+        
+        ! Data construct in subroutine
+        !$acc data copy(x)
+        x = x + 1
+!       !$acc end data
+        print *, "Subroutine finished"
+        !CHECK: error: expected OpenACC directive
+        !CHECK-NEXT: !$acc data copy(x)
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: specification construct
+        !CHECK-NEXT: !$acc data copy(x)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: specification part
+        !CHECK-NEXT: integer, intent(inout) :: x(:)
+        !CHECK-NEXT: ^
+    end subroutine
+
+    function negative_compute_sum(x) result(total)
+        integer, intent(in) :: x(:)
+        integer :: total
+        total = sum(x)
+        ! Data construct in function
+        !$acc data copyin(x) copy(total)
+        total = total + x
+!       !$acc end data
+        print *, "Function finished"
+        !CHECK: error: expected OpenACC end block directive
+        !CHECK-NEXT: end function
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyin(x) copy(total)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: execution part
+        !CHECK-NEXT: total = sum(x)
+        !CHECK-NEXT: ^
+    end function
+end program acc_data_test
\ No newline at end of file
diff --git a/flang/test/Parser/acc.f b/flang/test/Parser/acc.f
new file mode 100644
index 0000000000000..b0c3927772568
--- /dev/null
+++ b/flang/test/Parser/acc.f
@@ -0,0 +1,96 @@
+! RUN: %flang_fc1 -fsyntax-only -fopenacc %s 2>&1
+C Test file for OpenACC directives in fixed-form Fortran
+      PROGRAM ACCTEST
+      IMPLICIT NONE
+      INTEGER :: N, I, J
+      PARAMETER (N=100)
+      REAL :: A(N), B(N), C(N), D(N)
+      REAL :: SUM
+
+C Initialize arrays
+      DO I = 1, N
+         A(I) = I * 1.0
+         B(I) = I * 2.0
+         C(I) = 0.0
+         D(I) = 1.0
+      END DO
+
+C Basic data construct using C$ACC
+C$ACC DATA COPYIN(A,B) COPYOUT(C)
+      DO I = 1, N
+         C(I) = A(I) + B(I)
+      END DO
+C$ACC END DATA
+
+* Parallel construct with loop using *$ACC
+*$ACC PARALLEL PRESENT(A,B,C)
+*$ACC LOOP
+      DO I = 1, N
+         C(I) = C(I) * 2.0
+      END DO
+*$ACC END PARALLEL
+
+C Nested loops with collapse - C$ACC style
+C$ACC PARALLEL LOOP COLLAPSE(2)
+      DO I = 1, N
+         DO J = 1, N
+            A(J) = A(J) + B(J)
+         END DO
+      END DO
+C$ACC END PARALLEL LOOP
+
+* Combined parallel loop with reduction - *$ACC style
+      SUM = 0.0
+*$ACC PARALLEL LOOP REDUCTION(+:SUM)
+      DO I = 1, N
+         SUM = SUM + C(I)
+      END DO
+*$ACC END PARALLEL LOOP
+
+C Kernels construct - C$ACC with continuation
+C$ACC KERNELS 
+C$ACC+ COPYOUT(A)
+      DO I = 1, N
+         A(I) = A(I) * 2.0
+      END DO
+C$ACC END KERNELS
+
+* Data construct with update - *$ACC with continuation
+*$ACC DATA COPY(B)
+*$ACC+ PRESENT(D)
+      B(1) = 999.0
+*$ACC UPDATE HOST(B(1:1))
+      PRINT *, 'B(1) = ', B(1)
+*$ACC END DATA
+
+C Mixed style directives in nested constructs
+C$ACC DATA COPY(A,B,C)
+*$ACC PARALLEL LOOP
+      DO I = 1, N
+         A(I) = B(I) + C(I)
+      END DO
+*$ACC END PARALLEL LOOP
+C$ACC END DATA
+
+* Subroutine call within data region - *$ACC style
+*$ACC DATA COPY(A,B,C)
+      CALL SUB1(A, B, C, N)
+*$ACC END DATA
+
+      PRINT *, 'Sum = ', SUM
+      END PROGRAM
+
+C Subroutine with mixed ACC directive styles
+      SUBROUTINE SUB1(X, Y, Z, M)
+      INTEGER M, I
+      REAL X(M), Y(M), Z(M)
+
+*$ACC PARALLEL PRESENT(X,Y)
+C$ACC LOOP PRIVATE(I)
+      DO I = 1, M
+         Z(I) = X(I) + Y(I)
+      END DO
+C$ACC END LOOP
+*$ACC END PARALLEL
+      RETURN
+      END SUBROUTINE 
\ No newline at end of file

@llvmbot
Copy link
Member

llvmbot commented Mar 12, 2025

@llvm/pr-subscribers-openacc

Author: Andre Kuhlenschmidt (akuhlens)

Changes

This PR does reduces the verbosity of parser errors for OpenACC block constructs that do not parse correctly because they are missing their trailing end block directive by:

  • Removing the redundant error messages created by parsing 3 different styles of directive tokens.
  • Providing a general mechanism of configuring the max number of contexts printed for every syntax error.
  • Not printing less specific contexts that are at the same location.

Prior to the changes:

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2&gt;&amp;1 | tee acc-data-statement.prior.log | wc -l
520

acc-data-statement.prior.log

$ flang -fc1 -fopenacc -fsyntax-only flang/test/Parser/acc-data-statement.f90 2&gt;&amp;1 | tee acc-data-statement.prior.log | wc -l
118

acc-data-statement.post.log

Note there are still several bugs in the parsing that I would like to fix, but I am uploading this to talk with @klausler about where to look for those.

  • Off by one error in location for some open acc data directives. Probably caused by me modifying the "!$ACC" token, but I don't know how to fix it.
  • Duplicated error messages. Under IF and DO constructs
  • Multiple parses for the same error message
  • Flagging one problem as an error in specification part of subroutine.

Full diff: https://github.com/llvm/llvm-project/pull/131042.diff

5 Files Affected:

  • (modified) flang/lib/Parser/message.cpp (+35-5)
  • (modified) flang/lib/Parser/openacc-parsers.cpp (+7-2)
  • (modified) flang/test/Driver/debug-parsing-log.f90 (+9-9)
  • (added) flang/test/Parser/acc-data-statement.f90 (+245)
  • (added) flang/test/Parser/acc.f (+96)
diff --git a/flang/lib/Parser/message.cpp b/flang/lib/Parser/message.cpp
index 69e4814bf246c..0b8d300189a1b 100644
--- a/flang/lib/Parser/message.cpp
+++ b/flang/lib/Parser/message.cpp
@@ -272,6 +272,10 @@ static llvm::raw_ostream::Colors PrefixColor(Severity severity) {
   return llvm::raw_ostream::SAVEDCOLOR;
 }
 
+// FIXME: Make these configurable, based on verbosity level.
+const int MAX_CONTEXTS_EMITTED = 2;
+const bool OMIT_SHARED_CONTEXTS = true;
+
 void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
     bool echoSourceLine) const {
   std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};
@@ -279,12 +283,38 @@ void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,
   sources.EmitMessage(o, provenanceRange, ToString(), Prefix(severity()),
       PrefixColor(severity()), echoSourceLine);
   bool isContext{attachmentIsContext_};
+  int contextsEmitted{isContext ? 1 : 0};
+  // Emit attachments.
   for (const Message *attachment{attachment_.get()}; attachment;
-       attachment = attachment->attachment_.get()) {
+      attachment = attachment->attachment_.get()) {
     Severity severity = isContext ? Severity::Context : attachment->severity();
-    sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),
-        attachment->ToString(), Prefix(severity), PrefixColor(severity),
-        echoSourceLine);
+    auto emitAttachment = [&]() {
+      sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),
+          attachment->ToString(), Prefix(severity), PrefixColor(severity),
+          echoSourceLine);
+    };
+    // TODO isContext is not used correctly here.
+    if (attachment->attachmentIsContext_) {
+      // Truncate the number of contexts emitted.
+      if (contextsEmitted <= MAX_CONTEXTS_EMITTED) {
+        emitAttachment();
+        contextsEmitted += 1;
+      }
+      if (OMIT_SHARED_CONTEXTS) {
+        // Skip less specific contexts at the same location.
+        for (const Message *next_attachment{attachment->attachment_.get()};
+            next_attachment && next_attachment->attachmentIsContext_ &&
+            next_attachment->AtSameLocation(*attachment);
+            next_attachment = next_attachment->attachment_.get()) {
+          attachment = next_attachment;
+        }
+        // NB, this loop increments `attachment` one more time after the
+        // previous loop is done advancing it to the last context at the same
+        // location.
+      }
+    } else {
+      emitAttachment();
+    }
   }
 }
 
@@ -298,7 +328,7 @@ bool Message::operator==(const Message &that) const {
   }
   const Message *thatAttachment{that.attachment_.get()};
   for (const Message *attachment{attachment_.get()}; attachment;
-       attachment = attachment->attachment_.get()) {
+      attachment = attachment->attachment_.get()) {
     if (!thatAttachment || !attachment->AtSameLocation(*thatAttachment) ||
         attachment->ToString() != thatAttachment->ToString() ||
         attachment->severity() != thatAttachment->severity()) {
diff --git a/flang/lib/Parser/openacc-parsers.cpp b/flang/lib/Parser/openacc-parsers.cpp
index c78676664e0a3..6739bcc035fc2 100644
--- a/flang/lib/Parser/openacc-parsers.cpp
+++ b/flang/lib/Parser/openacc-parsers.cpp
@@ -19,8 +19,12 @@
 // OpenACC Directives and Clauses
 namespace Fortran::parser {
 
+// Only need to handle ! line comments because prescanning normalizes the
+// other types of line comments from fixed form.
 constexpr auto startAccLine{skipStuffBeforeStatement >>
-    ("!$ACC "_sptok || "C$ACC "_sptok || "*$ACC "_sptok)};
+    withMessage(
+        "expected OpenACC comment '!$ACC' (free-form), 'C$ACC', or '*$ACC' (fixed-form)"_err_en_US,
+        "!$ACC "_sptok)};
 constexpr auto endAccLine{space >> endOfLine};
 
 // Autogenerated clauses parser. Information is taken from ACC.td and the
@@ -225,7 +229,8 @@ TYPE_PARSER(startAccLine >> sourced(construct<AccEndBlockDirective>("END"_tok >>
 
 TYPE_PARSER(construct<OpenACCBlockConstruct>(
     Parser<AccBeginBlockDirective>{} / endAccLine, block,
-    Parser<AccEndBlockDirective>{} / endAccLine))
+    withMessage("expected OpenACC end block directive"_err_en_US,
+        Parser<AccEndBlockDirective>{} / endAccLine)))
 
 // Standalone constructs
 TYPE_PARSER(construct<OpenACCStandaloneConstruct>(
diff --git a/flang/test/Driver/debug-parsing-log.f90 b/flang/test/Driver/debug-parsing-log.f90
index 7297163109450..4e56add386ef8 100644
--- a/flang/test/Driver/debug-parsing-log.f90
+++ b/flang/test/Driver/debug-parsing-log.f90
@@ -12,14 +12,14 @@
 ! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: IMPLICIT statement
 ! CHECK-NEXT:   END PROGRAM
 ! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: implicit part
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: specification part
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
-! CHECK-NEXT: {{.*[/\\]}}debug-parsing-log.f90:25:1: in the context: main program
-! CHECK-NEXT:   END PROGRAM
-! CHECK-NEXT:   ^
+
+
+
+
+
+
+
+
+
 
 END PROGRAM
diff --git a/flang/test/Parser/acc-data-statement.f90 b/flang/test/Parser/acc-data-statement.f90
new file mode 100644
index 0000000000000..1e369a0db3780
--- /dev/null
+++ b/flang/test/Parser/acc-data-statement.f90
@@ -0,0 +1,245 @@
+! RUN: not %flang_fc1 -fsyntax-only -fopenacc %s 2>&1 | FileCheck %s
+program acc_data_test
+    implicit none
+    integer :: a(100), b(100), c(100), d(100)
+    integer :: i, s ! FIXME: if s is named sum you get semantic errors.
+
+    ! Positive tests
+
+    ! Basic data construct in program body
+    !$acc data copy(a, b) create(c)
+    a = 1
+    b = 2
+    c = a + b
+    !$acc end data
+    print *, "After first data region"
+
+    ! Data construct within IF block
+    if (.true.) then
+        !$acc data copyout(a)
+        a = a + 1
+        !$acc end data
+        print *, "Inside if block"
+    end if
+
+    ! Data construct within DO loop
+    do i = 1, 10
+        !$acc data present(a)
+        a(i) = a(i) * 2
+        !$acc end data
+        print *, "Loop iteration", i
+    end do
+
+    ! Nested data constructs
+    !$acc data copyin(a)
+    s = 0
+    !$acc data copy(s)
+    s = s + 1
+    !$acc end data
+    print *, "After nested data"
+    !$acc end data
+
+    ! Negative tests  
+    ! Basic data construct in program body
+    !$acc data copy(a, b) create(d)
+    a = 1
+    b = 2
+    d = a + b
+!   !$acc end data
+    print *, "After first data region"
+
+    ! Data construct within IF block
+    if (.true.) then
+        !$acc data copyout(a)
+        a = a + 1
+!       !$acc end data
+        print *, "Inside if block"
+        ! First error in the file.
+        !CHECK: acc-data-statement.f90:
+        !CHECK-SAME: [[ELINE1:[0-9]+]]:{{[0-9]+}}:
+        !CHECK-SAME: error: expected OpenACC end block directive
+        !CHECK-NEXT: end if
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyout(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: IF construct
+        !CHECK-NEXT: if (.true.) then
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: error: expected OpenACC end block directive
+        !CHECK-NEXT: end if
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyout(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: IF construct
+        !CHECK-NEXT: if (.true.) then
+        !CHECK-NEXT: ^
+    end if
+
+    ! Data construct within DO loop
+    do i = 1, 10
+        !$acc data present(a)
+        a(i) = a(i) * 2
+!       !$acc end data
+        print *, "Loop iteration", i
+        !CHECK: acc-data-statement.f90:
+        !CHECK-NOT:  [[ELINE1]]
+        !CHECK-SAME: [[ELINE2:[0-9]+]]:{{[0-9]+}}:
+        !CHECK-SAME: error: expected OpenACC end block directive
+        !CHECK-NEXT: end do
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data present(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: DO construct
+        !CHECK-NEXT: do i = 1, 10
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: error: expected OpenACC end block directive
+        !CHECK-NEXT: end do 
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data present(a)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: DO construct
+        !CHECK-NEXT: do i = 1, 10
+        !CHECK-NEXT: ^
+    end do
+
+    ! Nested data constructs
+    !$acc data copyin(a)
+    s = 0
+    !$acc data copy(s)
+    s = s + 1
+!   !$acc end data
+    print *, "After nested data"
+!   !$acc end data
+
+    print *, "Program finished"
+    !CHECK: acc-data-statement.f90:
+    !CHECK-NOT:  [[ELINE2]]
+    !CHECK-SAME: [[ELINE3:[0-9]+]]:{{[0-9]+}}:
+    !CHECK-SAME: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(s)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copyin(a)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: error: expected OpenACC end block directive
+    !CHECK-NEXT: contains
+    !CHECK-NEXT: ^ 
+    !CHECK-NEXT: in the context: OpenACC construct
+    !CHECK-NEXT: !$acc data copy(a, b) create(d)
+    !CHECK-NEXT: ^
+    !CHECK-NEXT: in the context: execution part
+    !CHECK-NEXT: !$acc data copy(a, b) create(c)
+    !CHECK-NEXT: ^
+contains
+    subroutine positive_process_array(x)
+        integer, intent(inout) :: x(:)
+        
+        ! Data construct in subroutine
+        !$acc data copy(x)
+        x = x + 1
+        !$acc end data
+        print *, "Subroutine finished"
+    end subroutine
+
+    function positive_compute_sum(x) result(total)
+        integer, intent(in) :: x(:)
+        integer :: total
+        
+        ! Data construct in function
+        !$acc data copyin(x) copy(total)
+        total = sum(x)
+        !$acc end data
+        print *, "Function finished"
+    end function
+    
+    subroutine negative_process_array(x)
+        integer, intent(inout) :: x(:)
+        
+        ! Data construct in subroutine
+        !$acc data copy(x)
+        x = x + 1
+!       !$acc end data
+        print *, "Subroutine finished"
+        !CHECK: error: expected OpenACC directive
+        !CHECK-NEXT: !$acc data copy(x)
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: specification construct
+        !CHECK-NEXT: !$acc data copy(x)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: specification part
+        !CHECK-NEXT: integer, intent(inout) :: x(:)
+        !CHECK-NEXT: ^
+    end subroutine
+
+    function negative_compute_sum(x) result(total)
+        integer, intent(in) :: x(:)
+        integer :: total
+        total = sum(x)
+        ! Data construct in function
+        !$acc data copyin(x) copy(total)
+        total = total + x
+!       !$acc end data
+        print *, "Function finished"
+        !CHECK: error: expected OpenACC end block directive
+        !CHECK-NEXT: end function
+        !CHECK-NEXT: ^ 
+        !CHECK-NEXT: in the context: OpenACC construct
+        !CHECK-NEXT: !$acc data copyin(x) copy(total)
+        !CHECK-NEXT: ^
+        !CHECK-NEXT: in the context: execution part
+        !CHECK-NEXT: total = sum(x)
+        !CHECK-NEXT: ^
+    end function
+end program acc_data_test
\ No newline at end of file
diff --git a/flang/test/Parser/acc.f b/flang/test/Parser/acc.f
new file mode 100644
index 0000000000000..b0c3927772568
--- /dev/null
+++ b/flang/test/Parser/acc.f
@@ -0,0 +1,96 @@
+! RUN: %flang_fc1 -fsyntax-only -fopenacc %s 2>&1
+C Test file for OpenACC directives in fixed-form Fortran
+      PROGRAM ACCTEST
+      IMPLICIT NONE
+      INTEGER :: N, I, J
+      PARAMETER (N=100)
+      REAL :: A(N), B(N), C(N), D(N)
+      REAL :: SUM
+
+C Initialize arrays
+      DO I = 1, N
+         A(I) = I * 1.0
+         B(I) = I * 2.0
+         C(I) = 0.0
+         D(I) = 1.0
+      END DO
+
+C Basic data construct using C$ACC
+C$ACC DATA COPYIN(A,B) COPYOUT(C)
+      DO I = 1, N
+         C(I) = A(I) + B(I)
+      END DO
+C$ACC END DATA
+
+* Parallel construct with loop using *$ACC
+*$ACC PARALLEL PRESENT(A,B,C)
+*$ACC LOOP
+      DO I = 1, N
+         C(I) = C(I) * 2.0
+      END DO
+*$ACC END PARALLEL
+
+C Nested loops with collapse - C$ACC style
+C$ACC PARALLEL LOOP COLLAPSE(2)
+      DO I = 1, N
+         DO J = 1, N
+            A(J) = A(J) + B(J)
+         END DO
+      END DO
+C$ACC END PARALLEL LOOP
+
+* Combined parallel loop with reduction - *$ACC style
+      SUM = 0.0
+*$ACC PARALLEL LOOP REDUCTION(+:SUM)
+      DO I = 1, N
+         SUM = SUM + C(I)
+      END DO
+*$ACC END PARALLEL LOOP
+
+C Kernels construct - C$ACC with continuation
+C$ACC KERNELS 
+C$ACC+ COPYOUT(A)
+      DO I = 1, N
+         A(I) = A(I) * 2.0
+      END DO
+C$ACC END KERNELS
+
+* Data construct with update - *$ACC with continuation
+*$ACC DATA COPY(B)
+*$ACC+ PRESENT(D)
+      B(1) = 999.0
+*$ACC UPDATE HOST(B(1:1))
+      PRINT *, 'B(1) = ', B(1)
+*$ACC END DATA
+
+C Mixed style directives in nested constructs
+C$ACC DATA COPY(A,B,C)
+*$ACC PARALLEL LOOP
+      DO I = 1, N
+         A(I) = B(I) + C(I)
+      END DO
+*$ACC END PARALLEL LOOP
+C$ACC END DATA
+
+* Subroutine call within data region - *$ACC style
+*$ACC DATA COPY(A,B,C)
+      CALL SUB1(A, B, C, N)
+*$ACC END DATA
+
+      PRINT *, 'Sum = ', SUM
+      END PROGRAM
+
+C Subroutine with mixed ACC directive styles
+      SUBROUTINE SUB1(X, Y, Z, M)
+      INTEGER M, I
+      REAL X(M), Y(M), Z(M)
+
+*$ACC PARALLEL PRESENT(X,Y)
+C$ACC LOOP PRIVATE(I)
+      DO I = 1, M
+         Z(I) = X(I) + Y(I)
+      END DO
+C$ACC END LOOP
+*$ACC END PARALLEL
+      RETURN
+      END SUBROUTINE 
\ No newline at end of file

This comment was marked as outdated.

@akuhlens akuhlens requested a review from klausler March 13, 2025 00:13
@akuhlens akuhlens force-pushed the andre/open-acc-recovery branch from e60c571 to 990e905 Compare March 17, 2025 22:28
@akuhlens akuhlens requested a review from klausler March 17, 2025 22:54
@akuhlens akuhlens requested a review from klausler March 19, 2025 00:00
@akuhlens akuhlens merged commit 0779406 into llvm:main Mar 26, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:driver flang:parser flang Flang issues not falling into any other category openacc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants