[flang-commits] [flang] [Flang]Fix Free-Form Token Separation: Add error for identifiers without required separator (PR #175726)

Urvi Rav via flang-commits flang-commits at lists.llvm.org
Wed Jan 21 00:05:30 PST 2026


https://github.com/ravurvi20 updated https://github.com/llvm/llvm-project/pull/175726

>From 0ac2b2e40b298c285f968c301c4265717e474cc7 Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Tue, 13 Jan 2026 02:57:10 -0600
Subject: [PATCH 1/8] Added error for identifiers without separator

---
 flang/lib/Parser/Fortran-parsers.cpp        | 16 ++++-----
 flang/lib/Parser/token-parsers.h            | 35 +++++++++++++++++---
 flang/test/Parser/type-keyword-space.f90    | 36 +++++++++++++++++++++
 flang/test/Semantics/Inputs/modfile73-a.f90 |  8 ++---
 flang/test/Semantics/Inputs/modfile73-b.f90 |  4 +--
 5 files changed, 80 insertions(+), 19 deletions(-)
 create mode 100644 flang/test/Parser/type-keyword-space.f90

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 988db5450abc9..e6f61579f8d1c 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -206,15 +206,15 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
         construct<IntrinsicTypeSpec>(
-            construct<IntrinsicTypeSpec::Real>("REAL" >> maybe(kindSelector))),
+            construct<IntrinsicTypeSpec::Real>("REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Complex>(
-            "COMPLEX" >> maybe(kindSelector))),
+            "COMPLEX"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Character>(
-            "CHARACTER" >> maybe(Parser<CharSelector>{}))),
+            "CHARACTER"_kw >> maybe(Parser<CharSelector>{}))),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Logical>(
-            "LOGICAL" >> maybe(kindSelector))),
+            "LOGICAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>(unsignedTypeSpec),
         extension<LanguageFeature::DoubleComplex>(
             "nonstandard usage: DOUBLE COMPLEX"_port_en_US,
@@ -222,7 +222,7 @@ TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
                 construct<IntrinsicTypeSpec::DoubleComplex>())),
         extension<LanguageFeature::Byte>("nonstandard usage: BYTE"_port_en_US,
             construct<IntrinsicTypeSpec>(construct<IntegerTypeSpec>(
-                "BYTE" >> construct<std::optional<KindSelector>>(pure(1)))))))
+                "BYTE"_kw >> construct<std::optional<KindSelector>>(pure(1)))))))
 
 // Extension: Vector type
 // VECTOR(intrinsic-type-spec) | __VECTOR_PAIR | __VECTOR_QUAD
@@ -241,13 +241,13 @@ TYPE_PARSER(construct<IntrinsicVectorTypeSpec>("VECTOR" >>
     parenthesized(construct<VectorElementType>(integerTypeSpec) ||
         construct<VectorElementType>(unsignedTypeSpec) ||
         construct<VectorElementType>(construct<IntrinsicTypeSpec::Real>(
-            "REAL" >> maybe(kindSelector))))))
+            "REAL"_kw >> maybe(kindSelector))))))
 
 // UNSIGNED type
-TYPE_PARSER(construct<UnsignedTypeSpec>("UNSIGNED" >> maybe(kindSelector)))
+TYPE_PARSER(construct<UnsignedTypeSpec>("UNSIGNED"_kw >> maybe(kindSelector)))
 
 // R705 integer-type-spec -> INTEGER [kind-selector]
-TYPE_PARSER(construct<IntegerTypeSpec>("INTEGER" >> maybe(kindSelector)))
+TYPE_PARSER(construct<IntegerTypeSpec>("INTEGER"_kw >> maybe(kindSelector)))
 
 // R706 kind-selector -> ( [KIND =] scalar-int-constant-expr )
 // Legacy extension: kind-selector -> * digit-string
diff --git a/flang/lib/Parser/token-parsers.h b/flang/lib/Parser/token-parsers.h
index 3e0c59b89d964..01a0eb7adc1a6 100644
--- a/flang/lib/Parser/token-parsers.h
+++ b/flang/lib/Parser/token-parsers.h
@@ -85,7 +85,12 @@ inline void MissingSpace(ParseState &state) {
   }
 }
 
-struct SpaceCheck {
+// SpaceCheck verifies proper spacing after keywords.
+// When IsTypeKeyword is true, emits an error for type keywords (REAL, INTEGER,
+// etc.) that cannot be followed by an identifier without a separator.
+// When IsTypeKeyword is false, calls MissingSpace which emits a portability
+// warning for general keywords that can be combined (e.g., ENDSUBROUTINE).
+template <bool IsTypeKeyword = false> struct SpaceCheck {
   using resultType = Success;
   constexpr SpaceCheck() {}
   static std::optional<Success> Parse(ParseState &state) {
@@ -96,13 +101,21 @@ struct SpaceCheck {
         return space.Parse(state);
       }
       if (IsLegalInIdentifier(ch)) {
-        MissingSpace(state);
+        if constexpr (IsTypeKeyword) {
+          if (!state.inFixedForm()) {
+            state.Say(
+                "Unexpected syntax while parsing the statement-function statement"_err_en_US);
+          }
+        } else {
+          MissingSpace(state);
+        }
       }
     }
     return {Success{}};
   }
 };
-constexpr SpaceCheck spaceCheck;
+constexpr SpaceCheck<> spaceCheck;
+constexpr SpaceCheck<true> typeKeywordSpaceCheck;
 
 // Matches a token string.  Spaces in the token string denote where
 // spaces may appear in the source; they can be made mandatory for
@@ -115,7 +128,10 @@ constexpr SpaceCheck spaceCheck;
 // when a string literal appears before the sequencing operator >> or
 // after the sequencing operator /.  The literal "..."_id parses a
 // token that cannot be a prefix of a longer identifier.
-template <bool MandatoryFreeFormSpace = false, bool MustBeComplete = false>
+// The literal "..."_kw parses a type keyword that requires a space
+// before an identifier in free form (emits error).
+template <bool MandatoryFreeFormSpace = false, bool MustBeComplete = false,
+    bool IsTypeKeyword = false>
 class TokenStringMatch {
 public:
   using resultType = Success;
@@ -168,7 +184,11 @@ class TokenStringMatch {
     }
     state.set_anyTokenMatched();
     if (IsLegalInIdentifier(p[-1])) {
-      return spaceCheck.Parse(state);
+      if constexpr (IsTypeKeyword) {
+        return typeKeywordSpaceCheck.Parse(state);
+      } else {
+        return spaceCheck.Parse(state);
+      }
     } else {
       return space.Parse(state);
     }
@@ -193,6 +213,11 @@ constexpr TokenStringMatch<false, true> operator""_id(
   return {str, n};
 }
 
+constexpr TokenStringMatch<false, false, true> operator""_kw(
+    const char str[], std::size_t n) {
+  return {str, n};
+}
+
 template <class PA>
 inline constexpr std::enable_if_t<std::is_class_v<PA>,
     SequenceParser<TokenStringMatch<>, PA>>
diff --git a/flang/test/Parser/type-keyword-space.f90 b/flang/test/Parser/type-keyword-space.f90
new file mode 100644
index 0000000000000..1c525a3857124
--- /dev/null
+++ b/flang/test/Parser/type-keyword-space.f90
@@ -0,0 +1,36 @@
+! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
+
+! Test that a blank is required between type keywords and names in free-form
+! source per Fortran standard 6.3.2.2: "A blank shall be used to separate
+! names, constants, or labels from adjacent keywords, names, constants, or
+! labels."
+
+! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: reala
+program test_real
+  reala(10)
+end program
+
+! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: integerb
+subroutine test_integer
+  integerb(20)
+end subroutine
+
+! CHECK: error: Unexpected syntax while parsing the statement-function statement  
+! CHECK: logicalflag
+subroutine test_logical
+  logicalflag
+end subroutine
+
+! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: complexz
+subroutine test_complex
+  complexz
+end subroutine
+
+! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: characterstr
+subroutine test_character
+  characterstr
+end subroutine
diff --git a/flang/test/Semantics/Inputs/modfile73-a.f90 b/flang/test/Semantics/Inputs/modfile73-a.f90
index ea247c98cdff8..7e20feefb7421 100644
--- a/flang/test/Semantics/Inputs/modfile73-a.f90
+++ b/flang/test/Semantics/Inputs/modfile73-a.f90
@@ -1,7 +1,7 @@
 MODULE modfile73a
 PUBLIC re_alloc, defaults
-integersp
-integerselected_real_kind0
+integer sp
+integer selected_real_kind0
 integer:: i8b = selected_int_kind(8)
 interface
    subroutine alloc_error_report_interf(str,code)
@@ -13,12 +13,12 @@ subroutine alloc_memory_event_interf(bytes,name)
 procedure()alloc_memory_event
   interface de_alloc
   end interface
-  charactercharacter, DEFAULT_ROUTINE
+  character character, DEFAULT_ROUTINE
   type allocDefaults
     logical copy
     logical shrink
     integer imin
-    characterroutine
+    character routine
   end type
   type(allocDefaults)DEFAULT
   integer IERR
diff --git a/flang/test/Semantics/Inputs/modfile73-b.f90 b/flang/test/Semantics/Inputs/modfile73-b.f90
index 743299599c7bd..414f4fb141a29 100644
--- a/flang/test/Semantics/Inputs/modfile73-b.f90
+++ b/flang/test/Semantics/Inputs/modfile73-b.f90
@@ -1,6 +1,6 @@
 module modfile73ba
   use modfile73a, only: re_alloc, de_alloc
-  charactermod_name
+  character mod_name
   type lData1D
      integer refCount
      character   id
@@ -42,7 +42,7 @@ subroutine delete_Data(a1d_data)
 
 module modfile73bb
   use modfile73a, only: re_alloc, de_alloc
-  charactermod_name
+  character mod_name
   type lData1D
      integer refCount
      character   id

>From ec74c9ed8349c98012924c1790de08a02d8aaf97 Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Tue, 13 Jan 2026 03:15:59 -0600
Subject: [PATCH 2/8] updated lit-testcase

---
 flang/test/Parser/type-keyword-space.f90 | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/flang/test/Parser/type-keyword-space.f90 b/flang/test/Parser/type-keyword-space.f90
index 1c525a3857124..a7849dcf724b4 100644
--- a/flang/test/Parser/type-keyword-space.f90
+++ b/flang/test/Parser/type-keyword-space.f90
@@ -1,10 +1,5 @@
 ! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
 
-! Test that a blank is required between type keywords and names in free-form
-! source per Fortran standard 6.3.2.2: "A blank shall be used to separate
-! names, constants, or labels from adjacent keywords, names, constants, or
-! labels."
-
 ! CHECK: error: Unexpected syntax while parsing the statement-function statement
 ! CHECK: reala
 program test_real

>From 498e13cc78dc7ab876f2a069cdf2822165cc77cf Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Tue, 13 Jan 2026 03:29:47 -0600
Subject: [PATCH 3/8] formatting changes

---
 flang/lib/Parser/Fortran-parsers.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index e6f61579f8d1c..0e199aa2d61db 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -206,7 +206,8 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
         construct<IntrinsicTypeSpec>(
-            construct<IntrinsicTypeSpec::Real>("REAL"_kw >> maybe(kindSelector))),
+            construct<IntrinsicTypeSpec::Real>(
+                "REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Complex>(
@@ -221,8 +222,8 @@ TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
             construct<IntrinsicTypeSpec>("DOUBLE COMPLEX"_sptok >>
                 construct<IntrinsicTypeSpec::DoubleComplex>())),
         extension<LanguageFeature::Byte>("nonstandard usage: BYTE"_port_en_US,
-            construct<IntrinsicTypeSpec>(construct<IntegerTypeSpec>(
-                "BYTE"_kw >> construct<std::optional<KindSelector>>(pure(1)))))))
+            construct<IntrinsicTypeSpec>(construct<IntegerTypeSpec>("BYTE"_kw >>
+                construct<std::optional<KindSelector>>(pure(1)))))))
 
 // Extension: Vector type
 // VECTOR(intrinsic-type-spec) | __VECTOR_PAIR | __VECTOR_QUAD

>From 109f51f725fdd259d35a6b9e30511d6bde3b8aba Mon Sep 17 00:00:00 2001
From: Urvi Rav <94829943+ravurvi20 at users.noreply.github.com>
Date: Tue, 13 Jan 2026 15:11:00 +0530
Subject: [PATCH 4/8] formatting changes

---
 flang/lib/Parser/Fortran-parsers.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 0e199aa2d61db..5a0858f8d4519 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -205,8 +205,7 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 // Extensions: DOUBLE COMPLEX, BYTE
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
-        construct<IntrinsicTypeSpec>(
-            construct<IntrinsicTypeSpec::Real>(
+        construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Real>(
                 "REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),

>From f929a807a42106e6703ec20529584aba7b18c742 Mon Sep 17 00:00:00 2001
From: Urvi Rav <94829943+ravurvi20 at users.noreply.github.com>
Date: Tue, 13 Jan 2026 15:15:08 +0530
Subject: [PATCH 5/8] formatting changes

---
 flang/lib/Parser/Fortran-parsers.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 5a0858f8d4519..ae8b9baa7b587 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -206,7 +206,7 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Real>(
-                "REAL"_kw >> maybe(kindSelector))),
+            "REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Complex>(

>From 88204fbcf44e1adb9ec46866874961810c150fcc Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Tue, 13 Jan 2026 03:29:47 -0600
Subject: [PATCH 6/8] formatting changes

---
 flang/lib/Parser/Fortran-parsers.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index ae8b9baa7b587..7b7cddf5a80c5 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -205,8 +205,8 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 // Extensions: DOUBLE COMPLEX, BYTE
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
-        construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Real>(
-            "REAL"_kw >> maybe(kindSelector))),
+        construct<IntrinsicTypeSpec>(
+            construct<IntrinsicTypeSpec::Real>("REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Complex>(

>From be16542b4b2e39422977ef02d5c44969c8e0a2ba Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Wed, 21 Jan 2026 00:33:53 -0600
Subject: [PATCH 7/8] formatting changes

---
 flang/lib/Parser/Fortran-parsers.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 7b7cddf5a80c5..ae8b9baa7b587 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -205,8 +205,8 @@ TYPE_CONTEXT_PARSER("declaration type spec"_en_US,
 // Extensions: DOUBLE COMPLEX, BYTE
 TYPE_CONTEXT_PARSER("intrinsic type spec"_en_US,
     first(construct<IntrinsicTypeSpec>(integerTypeSpec),
-        construct<IntrinsicTypeSpec>(
-            construct<IntrinsicTypeSpec::Real>("REAL"_kw >> maybe(kindSelector))),
+        construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Real>(
+            "REAL"_kw >> maybe(kindSelector))),
         construct<IntrinsicTypeSpec>("DOUBLE PRECISION" >>
             construct<IntrinsicTypeSpec::DoublePrecision>()),
         construct<IntrinsicTypeSpec>(construct<IntrinsicTypeSpec::Complex>(

>From f1239276c5fc803830169e5682ed3401b652e9b8 Mon Sep 17 00:00:00 2001
From: urvi-rav <urvi.rav at hpe.com>
Date: Wed, 21 Jan 2026 02:04:24 -0600
Subject: [PATCH 8/8] Changes in token-parsers.h

---
 flang/lib/Parser/token-parsers.h         |  2 +-
 flang/test/Parser/type-keyword-space.f90 | 10 +++++-----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/flang/lib/Parser/token-parsers.h b/flang/lib/Parser/token-parsers.h
index 01a0eb7adc1a6..11941d0a638f5 100644
--- a/flang/lib/Parser/token-parsers.h
+++ b/flang/lib/Parser/token-parsers.h
@@ -104,7 +104,7 @@ template <bool IsTypeKeyword = false> struct SpaceCheck {
         if constexpr (IsTypeKeyword) {
           if (!state.inFixedForm()) {
             state.Say(
-                "Unexpected syntax while parsing the statement-function statement"_err_en_US);
+                "A blank space or a separator (::) is required after the type keyword"_err_en_US);
           }
         } else {
           MissingSpace(state);
diff --git a/flang/test/Parser/type-keyword-space.f90 b/flang/test/Parser/type-keyword-space.f90
index a7849dcf724b4..dc46a5dedca58 100644
--- a/flang/test/Parser/type-keyword-space.f90
+++ b/flang/test/Parser/type-keyword-space.f90
@@ -1,30 +1,30 @@
 ! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
 
-! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: error: A blank space or a separator (::) is required after the type keyword
 ! CHECK: reala
 program test_real
   reala(10)
 end program
 
-! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: error: A blank space or a separator (::) is required after the type keyword
 ! CHECK: integerb
 subroutine test_integer
   integerb(20)
 end subroutine
 
-! CHECK: error: Unexpected syntax while parsing the statement-function statement  
+! CHECK: error: A blank space or a separator (::) is required after the type keyword  
 ! CHECK: logicalflag
 subroutine test_logical
   logicalflag
 end subroutine
 
-! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: error: A blank space or a separator (::) is required after the type keyword
 ! CHECK: complexz
 subroutine test_complex
   complexz
 end subroutine
 
-! CHECK: error: Unexpected syntax while parsing the statement-function statement
+! CHECK: error: A blank space or a separator (::) is required after the type keyword
 ! CHECK: characterstr
 subroutine test_character
   characterstr



More information about the flang-commits mailing list