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

via flang-commits flang-commits at lists.llvm.org
Tue Mar 10 10:15:00 PDT 2026


Author: Andre Kuhlenschmidt
Date: 2026-03-10T10:14:54-07:00
New Revision: 3c5d25754635ef5a65a9e1b1405e537f9195b5a5

URL: https://github.com/llvm/llvm-project/commit/3c5d25754635ef5a65a9e1b1405e537f9195b5a5
DIFF: https://github.com/llvm/llvm-project/commit/3c5d25754635ef5a65a9e1b1405e537f9195b5a5.diff

LOG: [flang][parser] handle semicolons uniformly in program unit constructs (#181180)

Modifies the parser so that a semicolon is a valid separator between
programming unit statements, and tweaks how leading semicolons and
spaces are parsed so that they don't count for progress toward
programming unit parsing.
Also adds some tests that document the behavior of what works before and
after the update.

fixes [#176994](https://github.com/llvm/llvm-project/issues/176994)

Added: 
    flang/test/Parser/bug176914.did-work.0.f90
    flang/test/Parser/bug176914.did-work.1.f90
    flang/test/Parser/bug176914.did-work.2.f90
    flang/test/Parser/bug176914.did-work.3.f90
    flang/test/Parser/bug176914.f90
    flang/test/Parser/bug176914.was-broken.0.f90
    flang/test/Parser/bug176914.was-broken.1.f90
    flang/test/Parser/bug176914.was-broken.2.f90

Modified: 
    flang/lib/Parser/program-parsers.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index dac377c29d9db..775810f8c3c0f 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))};
@@ -81,19 +81,14 @@ 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>(extension<LanguageFeature::EmptySourceFile>(
-                           "nonstandard usage: empty source file"_port_en_US,
-                           skipStuffBeforeStatement >> consumedAllInput >>
-                               pure<std::list<ProgramUnit>>()) ||
-        some(globalCompilerDirective || globalOpenACCCompilerDirective ||
-            normalProgramUnit) /
-            skipStuffBeforeStatement))
+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)))
 
 // R507 declaration-construct ->
 //        specification-construct | data-stmt | format-stmt |

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..0df1a2e527e1b
--- /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

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..d28cd35a59e1a
--- /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

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..fcc845dc11edf
--- /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;

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.f90 b/flang/test/Parser/bug176914.f90
new file mode 100644
index 0000000000000..d7d960e9fcee4
--- /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

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..fef95323a1807
--- /dev/null
+++ b/flang/test/Parser/bug176914.was-broken.1.f90
@@ -0,0 +1,4 @@
+!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


        


More information about the flang-commits mailing list