[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