[flang-commits] [flang] [flang][parser] handle semicolons uniformly in program unit constructs (PR #181180)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Thu Mar 5 09:28:44 PST 2026


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/181180

>From 026101b50f5d52d956474c323918d442f728235a Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 9 Feb 2026 15:20:51 -0800
Subject: [PATCH 1/5] initial commit

---
 flang/lib/Parser/program-parsers.cpp | 11 ++++++-----
 flang/test/Parser/bug176914.f90      | 16 ++++++++++++++++
 2 files changed, 22 insertions(+), 5 deletions(-)
 create mode 100644 flang/test/Parser/bug176914.f90

diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index dac377c29d9db..7aea0ad59ae96 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -68,9 +68,9 @@ static constexpr auto programUnit{
         construct<ProgramUnit>(indirect(functionSubprogram)) ||
     construct<ProgramUnit>(indirect(Parser<MainProgram>{}))};
 
-static constexpr auto normalProgramUnit{
-    !consumedAllInput >> StartNewSubprogram{} >> programUnit /
-        skipMany(";"_tok) / space / recovery(endOfLine, skipToNextLineIfAny)};
+static constexpr auto normalProgramUnit{!consumedAllInput >>
+    StartNewSubprogram{} >>
+    programUnit / recovery(endOfStmt, skipToNextLineIfAny)};
 
 static constexpr auto globalCompilerDirective{
     construct<ProgramUnit>(indirect(compilerDirective))};
@@ -91,8 +91,9 @@ TYPE_PARSER(
                            "nonstandard usage: empty source file"_port_en_US,
                            skipStuffBeforeStatement >> consumedAllInput >>
                                pure<std::list<ProgramUnit>>()) ||
-        some(globalCompilerDirective || globalOpenACCCompilerDirective ||
-            normalProgramUnit) /
+        some(skipStuffBeforeStatement >> (globalCompilerDirective ||
+                                             globalOpenACCCompilerDirective ||
+                                             normalProgramUnit)) /
             skipStuffBeforeStatement))
 
 // R507 declaration-construct ->
diff --git a/flang/test/Parser/bug176914.f90 b/flang/test/Parser/bug176914.f90
new file mode 100644
index 0000000000000..ee31a1d295ec8
--- /dev/null
+++ b/flang/test/Parser/bug176914.f90
@@ -0,0 +1,16 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program -> ProgramUnit -> MainProgram
+! CHECK-NEXT: | ProgramStmt -> Name = 'semicolon'
+; program semicolon
+use semi;
+IMPLICIT NONE  ;  
+testvar2 = testvar
+;; ; intvar = testvar + testvar2;intvar2=intvar;
+print *,testvar , testvar2, intvar, intvar2
+! CHECK: | EndProgramStmt
+! CHECK: ProgramUnit -> SubroutineSubprogram
+! CHECK-NEXT: | SubroutineStmt
+! CHECK-NEXT: | | Name = 'test'
+end ; ; subroutine test( arg1, arg2 );real arg1, arg2;;;; arg1=arg2; ; ; end;;;
+;;
+! CHECK: | EndSubroutineStmt
\ No newline at end of file

>From c94a659e7f813bd0860ab24d0a488e72224a24a8 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Mon, 23 Feb 2026 14:51:47 -0800
Subject: [PATCH 2/5] refactor program parser

---
 flang/lib/Parser/program-parsers.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index 7aea0ad59ae96..1dd932ec79aca 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -86,15 +86,14 @@ static constexpr auto globalOpenACCCompilerDirective{
 // Consequently, a program unit END statement should be the last statement
 // on its line.  We parse those END statements via unterminatedStatement()
 // and then skip over the end of the line here.
-TYPE_PARSER(
-    construct<Program>(extension<LanguageFeature::EmptySourceFile>(
-                           "nonstandard usage: empty source file"_port_en_US,
-                           skipStuffBeforeStatement >> consumedAllInput >>
-                               pure<std::list<ProgramUnit>>()) ||
+TYPE_PARSER(construct<Program>(skipStuffBeforeStatement >>
+    (extension<LanguageFeature::EmptySourceFile>(
+         "nonstandard usage: empty source file"_port_en_US,
+         consumedAllInput >> pure<std::list<ProgramUnit>>()) ||
         some(skipStuffBeforeStatement >> (globalCompilerDirective ||
                                              globalOpenACCCompilerDirective ||
                                              normalProgramUnit)) /
-            skipStuffBeforeStatement))
+            skipStuffBeforeStatement)))
 
 // R507 declaration-construct ->
 //        specification-construct | data-stmt | format-stmt |

>From 4cdaac36c54653adb291a72db283eb2988fc4551 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 4 Mar 2026 15:00:00 -0800
Subject: [PATCH 3/5] add tests

---
 flang/test/Parser/bug176914.did-work.0.f90   |  2 ++
 flang/test/Parser/bug176914.did-work.1.f90   | 11 +++++++++++
 flang/test/Parser/bug176914.did-work.2.f90   |  9 +++++++++
 flang/test/Parser/bug176914.did-work.3.f90   |  3 +++
 flang/test/Parser/bug176914.was-broken.0.f90 |  4 ++++
 flang/test/Parser/bug176914.was-broken.1.f90 |  5 +++++
 flang/test/Parser/bug176914.was-broken.2.f90 |  6 ++++++
 7 files changed, 40 insertions(+)
 create mode 100644 flang/test/Parser/bug176914.did-work.0.f90
 create mode 100644 flang/test/Parser/bug176914.did-work.1.f90
 create mode 100644 flang/test/Parser/bug176914.did-work.2.f90
 create mode 100644 flang/test/Parser/bug176914.did-work.3.f90
 create mode 100644 flang/test/Parser/bug176914.was-broken.0.f90
 create mode 100644 flang/test/Parser/bug176914.was-broken.1.f90
 create mode 100644 flang/test/Parser/bug176914.was-broken.2.f90

diff --git a/flang/test/Parser/bug176914.did-work.0.f90 b/flang/test/Parser/bug176914.did-work.0.f90
new file mode 100644
index 0000000000000..4435300fc7bf5
--- /dev/null
+++ b/flang/test/Parser/bug176914.did-work.0.f90
@@ -0,0 +1,2 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program
\ No newline at end of file
diff --git a/flang/test/Parser/bug176914.did-work.1.f90 b/flang/test/Parser/bug176914.did-work.1.f90
new file mode 100644
index 0000000000000..05f55ac37032b
--- /dev/null
+++ b/flang/test/Parser/bug176914.did-work.1.f90
@@ -0,0 +1,11 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+;
+! CHECK: Program -> ProgramUnit -> CompilerDirective
+!DIR$ unknown_directive empty statement
+;
+! CHECK: ProgramUnit -> MainProgram
+program semicolon; end;
+! CHECK: ProgramUnit -> CompilerDirective
+    !DIR$ unknown_directive leading space
+! CHECK: ProgramUnit -> CompilerDirective
+;   !DIR$ unknown_directive leading empty statement
\ No newline at end of file
diff --git a/flang/test/Parser/bug176914.did-work.2.f90 b/flang/test/Parser/bug176914.did-work.2.f90
new file mode 100644
index 0000000000000..0215794bd1599
--- /dev/null
+++ b/flang/test/Parser/bug176914.did-work.2.f90
@@ -0,0 +1,9 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program -> ProgramUnit -> MainProgram
+! CHECK-NEXT: | ProgramStmt -> Name = 'semicolon'
+program semicolon; end
+;
+subroutine sub; end;
+function fn(); end;;
+; ;
+module mod; end;
\ No newline at end of file
diff --git a/flang/test/Parser/bug176914.did-work.3.f90 b/flang/test/Parser/bug176914.did-work.3.f90
new file mode 100644
index 0000000000000..cd38283a5e118
--- /dev/null
+++ b/flang/test/Parser/bug176914.did-work.3.f90
@@ -0,0 +1,3 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program
+;;
diff --git a/flang/test/Parser/bug176914.was-broken.0.f90 b/flang/test/Parser/bug176914.was-broken.0.f90
new file mode 100644
index 0000000000000..6cc6eff981cde
--- /dev/null
+++ b/flang/test/Parser/bug176914.was-broken.0.f90
@@ -0,0 +1,4 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program -> ProgramUnit -> MainProgram
+program semicolon; end
+;
diff --git a/flang/test/Parser/bug176914.was-broken.1.f90 b/flang/test/Parser/bug176914.was-broken.1.f90
new file mode 100644
index 0000000000000..a7ce66b88d98c
--- /dev/null
+++ b/flang/test/Parser/bug176914.was-broken.1.f90
@@ -0,0 +1,5 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program -> ProgramUnit -> MainProgram
+! CHECK: ProgramUnit -> SubroutineSubprogram
+program semicolon; end; subroutine sub; end;
+
diff --git a/flang/test/Parser/bug176914.was-broken.2.f90 b/flang/test/Parser/bug176914.was-broken.2.f90
new file mode 100644
index 0000000000000..6cb832d5f926b
--- /dev/null
+++ b/flang/test/Parser/bug176914.was-broken.2.f90
@@ -0,0 +1,6 @@
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
+! CHECK: Program -> ProgramUnit -> SubroutineSubprogram
+subroutine sub; end
+;
+! CHECK: ProgramUnit -> FunctionSubprogram
+function fn(); end

>From bfae0835bb59f416f4d867d0572863a00d2ab36f Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 4 Mar 2026 15:09:52 -0800
Subject: [PATCH 4/5] add newline at the end of tests

---
 flang/test/Parser/bug176914.did-work.0.f90   | 2 +-
 flang/test/Parser/bug176914.did-work.1.f90   | 2 +-
 flang/test/Parser/bug176914.f90              | 2 +-
 flang/test/Parser/bug176914.was-broken.1.f90 | 1 -
 4 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/flang/test/Parser/bug176914.did-work.0.f90 b/flang/test/Parser/bug176914.did-work.0.f90
index 4435300fc7bf5..0df1a2e527e1b 100644
--- a/flang/test/Parser/bug176914.did-work.0.f90
+++ b/flang/test/Parser/bug176914.did-work.0.f90
@@ -1,2 +1,2 @@
 !RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s
-! CHECK: Program
\ No newline at end of file
+! CHECK: Program
diff --git a/flang/test/Parser/bug176914.did-work.1.f90 b/flang/test/Parser/bug176914.did-work.1.f90
index 05f55ac37032b..d28cd35a59e1a 100644
--- a/flang/test/Parser/bug176914.did-work.1.f90
+++ b/flang/test/Parser/bug176914.did-work.1.f90
@@ -8,4 +8,4 @@ program semicolon; end;
 ! CHECK: ProgramUnit -> CompilerDirective
     !DIR$ unknown_directive leading space
 ! CHECK: ProgramUnit -> CompilerDirective
-;   !DIR$ unknown_directive leading empty statement
\ No newline at end of file
+;   !DIR$ unknown_directive leading empty statement
diff --git a/flang/test/Parser/bug176914.f90 b/flang/test/Parser/bug176914.f90
index ee31a1d295ec8..d7d960e9fcee4 100644
--- a/flang/test/Parser/bug176914.f90
+++ b/flang/test/Parser/bug176914.f90
@@ -13,4 +13,4 @@
 ! CHECK-NEXT: | | Name = 'test'
 end ; ; subroutine test( arg1, arg2 );real arg1, arg2;;;; arg1=arg2; ; ; end;;;
 ;;
-! CHECK: | EndSubroutineStmt
\ No newline at end of file
+! CHECK: | EndSubroutineStmt
diff --git a/flang/test/Parser/bug176914.was-broken.1.f90 b/flang/test/Parser/bug176914.was-broken.1.f90
index a7ce66b88d98c..fef95323a1807 100644
--- a/flang/test/Parser/bug176914.was-broken.1.f90
+++ b/flang/test/Parser/bug176914.was-broken.1.f90
@@ -2,4 +2,3 @@
 ! CHECK: Program -> ProgramUnit -> MainProgram
 ! CHECK: ProgramUnit -> SubroutineSubprogram
 program semicolon; end; subroutine sub; end;
-

>From 9072355dc2f9bdfa390ffd7ac61cce423cc1bd95 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 5 Mar 2026 09:28:10 -0800
Subject: [PATCH 5/5] remove comment

---
 flang/lib/Parser/program-parsers.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index 1dd932ec79aca..775810f8c3c0f 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -81,11 +81,6 @@ static constexpr auto globalOpenACCCompilerDirective{
 
 // R501 program -> program-unit [program-unit]...
 // This is the top-level production for the Fortran language.
-// F'2018 6.3.1 defines a program unit as a sequence of one or more lines,
-// implying that a line can't be part of two distinct program units.
-// Consequently, a program unit END statement should be the last statement
-// on its line.  We parse those END statements via unterminatedStatement()
-// and then skip over the end of the line here.
 TYPE_PARSER(construct<Program>(skipStuffBeforeStatement >>
     (extension<LanguageFeature::EmptySourceFile>(
          "nonstandard usage: empty source file"_port_en_US,



More information about the flang-commits mailing list