[flang] Extension: accept "var*length(bounds)" (#117399)

A character length specifier in an entity declaration or a component
declaration is required by the standard to follow any array bounds or
coarray bounds that are present. Several Fortran compilers allow the
character length specifier to follow the name and appear before the
bounds.

Fixes https://github.com/llvm/llvm-project/issues/117372.
This commit is contained in:
Peter Klausler
2024-12-02 12:25:47 -08:00
committed by GitHub
parent c1c9929028
commit 8115454aa0
5 changed files with 65 additions and 15 deletions

View File

@@ -391,6 +391,9 @@ end
has the SAVE attribute and was initialized.
* `PRINT namelistname` is accepted and interpreted as
`WRITE(*,NML=namelistname)`, a near-universal extension.
* A character length specifier in a component or entity declaration
is accepted before an array specification (`ch*3(2)`) as well
as afterwards.
### Extensions supported when enabled by options

View File

@@ -1024,10 +1024,18 @@ struct Initialization {
// R739 component-decl ->
// component-name [( component-array-spec )]
// [lbracket coarray-spec rbracket] [* char-length]
// [component-initialization]
// [lbracket coarray-spec rbracket] [* char-length]
// [component-initialization] |
// component-name *char-length [( component-array-spec )]
// [lbracket coarray-spec rbracket] [component-initialization]
struct ComponentDecl {
TUPLE_CLASS_BOILERPLATE(ComponentDecl);
ComponentDecl(Name &&name, CharLength &&length,
std::optional<ComponentArraySpec> &&aSpec,
std::optional<CoarraySpec> &&coaSpec,
std::optional<Initialization> &&init)
: t{std::move(name), std::move(aSpec), std::move(coaSpec),
std::move(length), std::move(init)} {}
std::tuple<Name, std::optional<ComponentArraySpec>,
std::optional<CoarraySpec>, std::optional<CharLength>,
std::optional<Initialization>>
@@ -1381,9 +1389,16 @@ struct AttrSpec {
// R803 entity-decl ->
// object-name [( array-spec )] [lbracket coarray-spec rbracket]
// [* char-length] [initialization] |
// function-name [* char-length]
// function-name [* char-length] |
// (ext.) object-name *char-length [( array-spec )]
// [lbracket coarray-spec rbracket] [initialization]
struct EntityDecl {
TUPLE_CLASS_BOILERPLATE(EntityDecl);
EntityDecl(ObjectName &&name, CharLength &&length,
std::optional<ArraySpec> &&aSpec, std::optional<CoarraySpec> &&coaSpec,
std::optional<Initialization> &&init)
: t{std::move(name), std::move(aSpec), std::move(coaSpec),
std::move(length), std::move(init)} {}
std::tuple<ObjectName, std::optional<ArraySpec>, std::optional<CoarraySpec>,
std::optional<CharLength>, std::optional<Initialization>>
t;

View File

@@ -460,7 +460,7 @@ TYPE_PARSER(construct<ComponentAttrSpec>(accessSpec) ||
construct<ComponentAttrSpec>(allocatable) ||
construct<ComponentAttrSpec>("CODIMENSION" >> coarraySpec) ||
construct<ComponentAttrSpec>(contiguous) ||
construct<ComponentAttrSpec>("DIMENSION" >> Parser<ComponentArraySpec>{}) ||
construct<ComponentAttrSpec>("DIMENSION" >> componentArraySpec) ||
construct<ComponentAttrSpec>(pointer) ||
extension<LanguageFeature::CUDA>(
construct<ComponentAttrSpec>(Parser<common::CUDADataAttr>{})) ||
@@ -471,17 +471,23 @@ TYPE_PARSER(construct<ComponentAttrSpec>(accessSpec) ||
// R739 component-decl ->
// component-name [( component-array-spec )]
// [lbracket coarray-spec rbracket] [* char-length]
// [component-initialization]
// [lbracket coarray-spec rbracket] [* char-length]
// [component-initialization] |
// (ext.) component-name *char-length [(component-array-spec)]
// [lbracket coarray-spec rbracket] [* char-length]
// [component-initialization]
TYPE_CONTEXT_PARSER("component declaration"_en_US,
construct<ComponentDecl>(name, maybe(Parser<ComponentArraySpec>{}),
maybe(coarraySpec), maybe("*" >> charLength), maybe(initialization)))
construct<ComponentDecl>(name, "*" >> charLength, maybe(componentArraySpec),
maybe(coarraySpec), maybe(initialization)) ||
construct<ComponentDecl>(name, maybe(componentArraySpec),
maybe(coarraySpec), maybe("*" >> charLength),
maybe(initialization)))
// The source field of the Name will be replaced with a distinct generated name.
TYPE_CONTEXT_PARSER("%FILL item"_en_US,
extension<LanguageFeature::DECStructures>(
"nonstandard usage: %FILL"_port_en_US,
construct<FillDecl>(space >> sourced("%FILL" >> construct<Name>()),
maybe(Parser<ComponentArraySpec>{}), maybe("*" >> charLength))))
maybe(componentArraySpec), maybe("*" >> charLength))))
TYPE_PARSER(construct<ComponentOrFill>(Parser<ComponentDecl>{}) ||
construct<ComponentOrFill>(Parser<FillDecl>{}))
@@ -658,9 +664,13 @@ TYPE_PARSER(recovery("END ENUM"_tok, constructEndStmtErrorRecovery) >>
// R801 type-declaration-stmt ->
// declaration-type-spec [[, attr-spec]... ::] entity-decl-list
constexpr auto entityDeclWithoutEqInit{construct<EntityDecl>(name,
maybe(arraySpec), maybe(coarraySpec), maybe("*" >> charLength),
!"="_tok >> maybe(initialization))}; // old-style REAL A/0/ still works
constexpr auto entityDeclWithoutEqInit{
construct<EntityDecl>(name, "*" >> charLength, maybe(arraySpec),
maybe(coarraySpec), !"="_tok >> maybe(initialization)) ||
construct<EntityDecl>(name, maybe(arraySpec), maybe(coarraySpec),
maybe("*" >> charLength),
!"="_tok >>
maybe(initialization) /* old-style REAL A/0/ still works */)};
TYPE_PARSER(
construct<TypeDeclarationStmt>(declarationTypeSpec,
defaulted("," >> nonemptyList(Parser<AttrSpec>{})) / "::",
@@ -720,9 +730,13 @@ constexpr auto objectName{name};
// R803 entity-decl ->
// object-name [( array-spec )] [lbracket coarray-spec rbracket]
// [* char-length] [initialization] |
// function-name [* char-length]
TYPE_PARSER(construct<EntityDecl>(objectName, maybe(arraySpec),
maybe(coarraySpec), maybe("*" >> charLength), maybe(initialization)))
// function-name [* char-length] |
// (ext.) object-name *char-length [(array-spec)]
// [lbracket coarray-spec rbracket] [initialization]
TYPE_PARSER(construct<EntityDecl>(objectName, "*" >> charLength,
maybe(arraySpec), maybe(coarraySpec), maybe(initialization)) ||
construct<EntityDecl>(objectName, maybe(arraySpec), maybe(coarraySpec),
maybe("*" >> charLength), maybe(initialization)))
// R806 null-init -> function-reference ... which must resolve to NULL()
TYPE_PARSER(lookAhead(name / "( )") >> construct<NullInit>(expr))

View File

@@ -72,6 +72,7 @@ constexpr Parser<LanguageBindingSpec> languageBindingSpec; // R808, R1528
constexpr Parser<EntityDecl> entityDecl; // R803
constexpr Parser<CoarraySpec> coarraySpec; // R809
constexpr Parser<ArraySpec> arraySpec; // R815
constexpr Parser<ComponentArraySpec> componentArraySpec;
constexpr Parser<ExplicitShapeSpec> explicitShapeSpec; // R816
constexpr Parser<DeferredShapeSpecList> deferredShapeSpecList; // R820
constexpr Parser<AssumedImpliedSpec> assumedImpliedSpec; // R821

View File

@@ -0,0 +1,17 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! Test parsing of alternative order of char-length in an
! entity-decl or component-decl.
program p
type t
!CHECK: CHARACTER c1(2_4)*3/"abc", "def"/
character c1*3(2)/'abc','def'/
end type
integer, parameter :: n=3
!CHECK: CHARACTER v1(2_4)*(3_4)/"ghi", "jkl"/
character v1*(n)(2)/'ghi','jkl'/
!CHECK: CHARACTER :: v2(1_4)*2 = "mn"
character::v2*2(1)='mn'
end