[flang-commits] [flang] [flang] Add parser support for Fortran 2023 conditional arguments (F2023 R1526-R1528) (PR #191303)

Vineet Kumar via flang-commits flang-commits at lists.llvm.org
Tue Apr 21 09:03:50 PDT 2026


https://github.com/vntkmr updated https://github.com/llvm/llvm-project/pull/191303

>From ffec4e0c3bf7cab89066de8547dc536abe253a69 Mon Sep 17 00:00:00 2001
From: Vineet Kumar <vineetk at hpe.com>
Date: Thu, 9 Apr 2026 15:45:00 -0500
Subject: [PATCH] [flang] Add parser support for Fortran 2023 conditional
 arguments (F2023 R1526-R1528)

Implement parsing, unparsing, and parse tree nodes for the Fortran 2023
conditional argument syntax (F2023 R1526-R1528). This enables calls of the form:

  call sub((flag ? a : b))
  call sub((x > 10 ? a : x > 5 ? b : c))
  call sub((flag ? a : .NIL.))

- Add ConditionalArg and ConditionalArgTail parse tree nodes
- Add Nil empty class for .NIL. representation
- Add ConditionalArg as a new alternative in ActualArg
- Add parser rules for conditional-arg, consequent, and chained branches
- Add unparse support for round-trip printing
- Add dump-parse-tree and FeatureList support
- Add parser tests covering simple, multi-branch, .NIL., BOZ, NULL(),
  keyword arguments, and module procedure cases
---
 flang/examples/FeatureList/FeatureList.cpp   |   4 +
 flang/include/flang/Parser/dump-parse-tree.h |   4 +
 flang/include/flang/Parser/parse-tree.h      |  34 +-
 flang/lib/Parser/program-parsers.cpp         |  23 +-
 flang/lib/Parser/unparse.cpp                 |  21 +
 flang/lib/Semantics/expression.cpp           |   4 +
 flang/test/Parser/conditional-arg.f90        | 453 +++++++++++++++++++
 flang/test/Semantics/conditional-arg.f90     |  29 ++
 8 files changed, 568 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Parser/conditional-arg.f90
 create mode 100644 flang/test/Semantics/conditional-arg.f90

diff --git a/flang/examples/FeatureList/FeatureList.cpp b/flang/examples/FeatureList/FeatureList.cpp
index bee18096f9fb2..c338fd0ab8dfc 100644
--- a/flang/examples/FeatureList/FeatureList.cpp
+++ b/flang/examples/FeatureList/FeatureList.cpp
@@ -312,6 +312,10 @@ struct NodeVisitor {
   READ_FEATURE(Expr::DefinedBinary)
   READ_FEATURE(Expr::ComplexConstructor)
   READ_FEATURE(ConditionalExpr)
+  READ_FEATURE(ConditionalArg)
+  READ_FEATURE(ConditionalArg::Consequent)
+  READ_FEATURE(ConditionalArgTail)
+  READ_FEATURE(ConditionalArgNil)
   READ_FEATURE(External)
   READ_FEATURE(ExternalStmt)
   READ_FEATURE(FailImageStmt)
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index eefab487413da..7edc644d200f3 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -253,6 +253,10 @@ class ParseTreeDumper {
   NODE(parser, ConcurrentControl)
   NODE(parser, ConcurrentHeader)
   NODE(parser, ConditionalExpr)
+  NODE(parser, ConditionalArg)
+  NODE(ConditionalArg, Consequent)
+  NODE(parser, ConditionalArgTail)
+  NODE(parser, ConditionalArgNil)
   NODE(parser, ConnectSpec)
   NODE(ConnectSpec, CharExpr)
   NODE_ENUM(ConnectSpec::CharExpr, Kind)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 960ebbcc99efb..b904b41b115b4 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -251,6 +251,8 @@ struct Call; // R1520 & R1521
 struct CallStmt; // R1521
 struct ProcedureDesignator; // R1522
 struct ActualArg; // R1524
+struct ConditionalArg; // F2023 R1526
+struct ConditionalArgTail; // F2023 R1526
 struct SeparateModuleSubprogram; // R1538
 struct EntryStmt; // R1541
 struct ReturnStmt; // R1542
@@ -3224,15 +3226,43 @@ struct ProcedureDesignator {
 // R1525 alt-return-spec -> * label
 WRAPPER_CLASS(AltReturnSpec, Label);
 
+// .NIL. (part of F2023 R1527)
+EMPTY_CLASS(ConditionalArgNil);
+
+// F2023 R1526 conditional-arg ->
+//   ( scalar-logical-expr ? consequent
+//     [ : scalar-logical-expr ? consequent ]...
+//     : consequent )
+// F2023 R1527 consequent -> consequent-arg | .NIL.
+// F2023 R1528 consequent-arg -> expr | variable
+struct ConditionalArg {
+  TUPLE_CLASS_BOILERPLATE(ConditionalArg);
+  struct Consequent { // F2023 R1527
+    UNION_CLASS_BOILERPLATE(Consequent);
+    // N.B. "variable" is parsed as "expr" and
+    // the distinction is determined by semantics.
+    std::variant<common::Indirection<Expr>, ConditionalArgNil> u;
+  };
+  std::tuple<ScalarLogicalExpr, Consequent,
+      common::Indirection<ConditionalArgTail>>
+      t;
+};
+
+struct ConditionalArgTail {
+  UNION_CLASS_BOILERPLATE(ConditionalArgTail);
+  std::variant<ConditionalArg, ConditionalArg::Consequent> u;
+};
+
 // R1524 actual-arg ->
 //         expr | variable | procedure-name | proc-component-ref |
-//         alt-return-spec
+//         alt-return-spec | conditional-arg
 struct ActualArg {
   WRAPPER_CLASS(PercentRef, Expr); // %REF(x) extension
   WRAPPER_CLASS(PercentVal, Expr); // %VAL(x) extension
   UNION_CLASS_BOILERPLATE(ActualArg);
   ActualArg(Expr &&x) : u{common::Indirection<Expr>(std::move(x))} {}
-  std::variant<common::Indirection<Expr>, AltReturnSpec, PercentRef, PercentVal>
+  std::variant<common::Indirection<Expr>, AltReturnSpec, PercentRef, PercentVal,
+      ConditionalArg>
       u;
 };
 
diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index b26603b5aea45..b4e56213d9f02 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -510,15 +510,34 @@ TYPE_PARSER(construct<ProcedureDesignator>(Parser<ProcComponentRef>{}) ||
 TYPE_PARSER(construct<ActualArgSpec>(
     maybe(keyword / "=" / !"="_ch), Parser<ActualArg>{}))
 
+// F2023 R1527 consequent -> consequent-arg | .NIL.
+// F2023 R1528 consequent-arg -> expr | variable
+// N.B. "variable" is subsumed by "expr" in the parser;
+// semantics determines the distinction.
+constexpr auto consequent{construct<ConditionalArg::Consequent>(
+                              ".NIL." >> construct<ConditionalArgNil>()) ||
+    construct<ConditionalArg::Consequent>(indirect(expr))};
+
+// F2023 R1526 conditional-arg ->
+//   scalar-logical-expr ? consequent : conditional-arg-or-consequent
+// The outer parentheses are added at the ActualArg level.
+TYPE_PARSER(construct<ConditionalArg>(scalarLogicalExpr / "?", consequent / ":",
+    indirect(Parser<ConditionalArgTail>{})))
+
+// conditional-arg-part-or-consequent -> conditional-arg | consequent
+TYPE_PARSER(construct<ConditionalArgTail>(Parser<ConditionalArg>{}) ||
+    construct<ConditionalArgTail>(consequent))
+
 // R1524 actual-arg ->
 //         expr | variable | procedure-name | proc-component-ref |
-//         alt-return-spec
+//         alt-return-spec | conditional-arg
 // N.B. the "procedure-name" and "proc-component-ref" alternatives can't
 // yet be distinguished from "variable", many instances of which can't be
 // distinguished from "expr" anyway (to do so would misparse structure
 // constructors and function calls as array elements).
 // Semantics sorts it all out later.
-TYPE_PARSER(construct<ActualArg>(expr) ||
+TYPE_PARSER(construct<ActualArg>(parenthesized(Parser<ConditionalArg>{})) ||
+    construct<ActualArg>(expr) ||
     construct<ActualArg>(Parser<AltReturnSpec>{}) ||
     extension<LanguageFeature::PercentRefAndVal>(
         "nonstandard usage: %REF"_port_en_US,
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 5ddd0cfc3a1ef..e481ed70d3c7f 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -1739,6 +1739,27 @@ class UnparseVisitor {
   void Unparse(const ActualArg::PercentVal &x) {
     Word("%VAL("), Walk(x.v), Put(')');
   }
+  void UnparseConditionalArgBody(const ConditionalArg &x) {
+    Walk(std::get<ScalarLogicalExpr>(x.t));
+    Put(" ? ");
+    Walk(std::get<ConditionalArg::Consequent>(x.t));
+    Put(" : ");
+    Walk(std::get<common::Indirection<ConditionalArgTail>>(x.t));
+  }
+  void Unparse(const ConditionalArg &x) { // F2023 R1526
+    Put("( ");
+    UnparseConditionalArgBody(x);
+    Put(" )");
+  }
+  void Unparse(const ConditionalArgTail &x) {
+    common::visit(
+        common::visitors{
+            [&](const ConditionalArg &y) { UnparseConditionalArgBody(y); },
+            [&](const ConditionalArg::Consequent &y) { Walk(y); },
+        },
+        x.u);
+  }
+  void Post(const ConditionalArgNil &) { Word(".NIL."); } // part of F2023 R1527
   void Before(const AltReturnSpec &) { // R1525
     Put('*');
   }
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 756bfd551a90a..690fce2b8062d 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -4748,6 +4748,10 @@ void ArgumentAnalyzer::Analyze(
               actual->set_isPercentVal();
             }
           },
+          [&](const parser::ConditionalArg &) {
+            context_.Say(
+                "Fortran 2023 conditional arguments are not yet supported"_err_en_US);
+          },
       },
       std::get<parser::ActualArg>(arg.t).u);
   if (actual) {
diff --git a/flang/test/Parser/conditional-arg.f90 b/flang/test/Parser/conditional-arg.f90
new file mode 100644
index 0000000000000..c5f3de85b0fd6
--- /dev/null
+++ b/flang/test/Parser/conditional-arg.f90
@@ -0,0 +1,453 @@
+! RUN: %flang_fc1 -fdebug-unparse-no-sema %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s -check-prefix=TREE
+
+! Test parsing of conditional arguments (F2023 R1526-R1528)
+
+subroutine test_conditional_arg
+  implicit none
+  integer :: x, a, b, c
+  integer :: arr(5)
+  real :: r1, r2
+  logical :: flag, flag2
+
+  ! Test 1: Simple two-branch conditional arg
+  ! CHECK: CALL sub(( x>0 ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((x > 0 ? a : b))
+
+  ! Test 2: Multi-branch conditional arg
+  ! CHECK: CALL sub(( x>10 ? a : x>5 ? b : c ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'c'
+  call sub((x > 10 ? a : x > 5 ? b : c))
+
+  ! Test 3: .NIL. in else position (absent optional)
+  ! CHECK: CALL sub(( flag ? a : .NIL. ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> ConditionalArgNil
+  call sub((flag ? a : .NIL.))
+
+  ! Test 4: .NIL. in middle branch
+  ! CHECK: CALL sub(( flag ? .NIL. : flag2 ? a : .NIL. ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> ConditionalArgNil
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag2'
+  ! TREE-NEXT: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> ConditionalArgNil
+  call sub((flag ? .NIL. : flag2 ? a : .NIL.))
+
+  ! Test 5: Keyword argument with conditional arg
+  ! CHECK: CALL sub(arg=( flag ? a : b ))
+  ! TREE: ActualArgSpec
+  ! TREE-NEXT: Keyword -> Name = 'arg'
+  ! TREE-NEXT: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub(arg = (flag ? a : b))
+
+  ! Test 6: Multiple arguments, one conditional
+  ! CHECK: CALL sub(arg1=a, arg2=( flag ? b : c ))
+  ! TREE: ActualArg -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'c'
+  call sub(arg1 = a, arg2 = (flag ? b : c))
+
+  ! Test 7: Expression consequent-args
+  ! CHECK: CALL sub(( x>0 ? a+1 : b*2 ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Add
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Multiply
+  call sub((x > 0 ? a+1 : b*2))
+
+  ! Test 8: Real variable consequent-args
+  ! CHECK: CALL sub(( r1>0.0 ? r1 : r2 ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Scalar -> Logical -> Expr -> GT
+  ! TREE: RealLiteralConstant
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'r1'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'r2'
+  call sub((r1 > 0.0 ? r1 : r2))
+
+  ! Test 9: Logical condition with .AND.
+  ! CHECK: CALL sub(( x>0.AND.flag ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> AND
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((x > 0 .and. flag ? a : b))
+
+  ! Test 10: Logical condition with .NOT.
+  ! CHECK: CALL sub(( .NOT.flag ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> NOT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((.not. flag ? a : b))
+
+  ! Test 11: Logical condition with .OR.
+  ! CHECK: CALL sub(( flag.OR.flag2 ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> OR
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((flag .or. flag2 ? a : b))
+
+  ! Test 12: Four-branch conditional arg
+  ! CHECK: CALL sub(( x>30 ? a : x>20 ? b : x>10 ? c : x ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> GT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'c'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  call sub((x > 30 ? a : x > 20 ? b : x > 10 ? c : x))
+
+  ! Test 13: Array element consequent-args (parsed as FunctionReference pre-sema)
+  ! CHECK: CALL sub(( x>0 ? arr(1) : arr(2) ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> FunctionReference -> Call
+  ! TREE-NEXT: ProcedureDesignator -> Name = 'arr'
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> FunctionReference -> Call
+  ! TREE-NEXT: ProcedureDesignator -> Name = 'arr'
+  call sub((x > 0 ? arr(1) : arr(2)))
+
+  ! Test 14: Array element with .NIL.
+  ! CHECK: CALL sub(( flag ? arr(3) : .NIL. ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ConditionalArgTail -> Consequent -> ConditionalArgNil
+  call sub((flag ? arr(3) : .NIL.))
+
+  ! Test 15: Multiple conditional args in one call
+  ! CHECK: CALL sub(( flag ? a : b ), ( flag2 ? c : x ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  call sub((flag ? a : b), (flag2 ? c : x))
+
+  ! Test 16: Two keyword conditional args
+  ! CHECK: CALL sub(p=( flag ? a : b ), q=( x>0 ? c : x ))
+  ! TREE: Keyword -> Name = 'p'
+  ! TREE-NEXT: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE: Keyword -> Name = 'q'
+  ! TREE-NEXT: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'c'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  call sub(p = (flag ? a : b), q = (x > 0 ? c : x))
+
+  ! Test 17: Conditional arg with complex condition (comparison chain)
+  ! CHECK: CALL sub(( x>5.AND.x<20 ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> AND
+  ! TREE-NEXT: Expr -> GT
+  ! TREE: Expr -> LT
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((x > 5 .and. x < 20 ? a : b))
+
+  ! Test 18: Consequent that is a parenthesized expression
+  ! CHECK: CALL sub(( x>0 ? (a+b) : (b-c) ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Parentheses -> Expr -> Add
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Parentheses -> Expr -> Subtract
+  call sub((x > 0 ? (a+b) : (b-c)))
+
+  ! Test 19: .NIL. only in middle of three branches
+  ! CHECK: CALL sub(( flag ? .NIL. : flag2 ? a : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> ConditionalArgNil
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag2'
+  ! TREE-NEXT: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((flag ? .NIL. : flag2 ? a : b))
+
+  ! Test 20: All-NIL except one branch
+  ! CHECK: CALL sub(( flag ? .NIL. : flag2 ? .NIL. : a ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> ConditionalArgNil
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag2'
+  ! TREE-NEXT: Consequent -> ConditionalArgNil
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  call sub((flag ? .NIL. : flag2 ? .NIL. : a))
+
+end subroutine
+
+! Test conditional arg in a function reference context
+subroutine test_func_ref
+  implicit none
+  integer :: x, a, b, result
+  logical :: flag
+
+  ! Test 21: Conditional arg passed to a function
+  ! CHECK: result = func(( flag ? a : b ))
+  ! TREE: FunctionReference -> Call
+  ! TREE-NEXT: ProcedureDesignator -> Name = 'func'
+  ! TREE-NEXT: ActualArgSpec
+  ! TREE-NEXT: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  result = func((flag ? a : b))
+
+  ! Test 22: Conditional arg as one of multiple function args
+  ! CHECK: result = func(a, ( flag ? b : x ))
+  ! TREE: ActualArg -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE-NEXT: Scalar -> Logical -> Expr -> Designator -> DataRef -> Name = 'flag'
+  ! TREE-NEXT: Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  result = func(a, (flag ? b : x))
+
+end subroutine
+
+! Test conditional arg with typeless consequents (BOZ literals, NULL())
+! These should parse successfully; semantic errors are caught later.
+subroutine test_typeless_consequents
+  implicit none
+  integer :: a, b
+  logical :: flag, flag2
+
+  ! Test 23: BOZ literal (hex) as consequent
+  ! CHECK: CALL sub(( flag ? a : z"ff" ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  call sub((flag ? a : z'FF'))
+
+  ! Test 24: BOZ literal (binary) as consequent
+  ! CHECK: CALL sub(( flag ? b"101" : a ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  call sub((flag ? b'101' : a))
+
+  ! Test 25: BOZ literal (octal) as consequent
+  ! CHECK: CALL sub(( flag ? a : o"77" ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  call sub((flag ? a : o'77'))
+
+  ! Test 26: BOZ in both branches
+  ! CHECK: CALL sub(( flag ? z"ff" : z"00" ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  call sub((flag ? z'FF' : z'00'))
+
+  ! Test 27: BOZ in middle branch of multi-branch conditional
+  ! CHECK: CALL sub(( flag ? a : flag2 ? b"101" : b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE: Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+  call sub((flag ? a : flag2 ? b'101' : b))
+
+  ! Test 28: NULL() as consequent
+  ! CHECK: CALL sub(( flag ? a : null() ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ProcedureDesignator -> Name = 'null'
+  call sub((flag ? a : null()))
+
+  ! Test 29: NULL() as first consequent
+  ! CHECK: CALL sub(( flag ? null() : a ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ProcedureDesignator -> Name = 'null'
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+  call sub((flag ? null() : a))
+
+  ! Test 30: NULL() in both branches
+  ! CHECK: CALL sub(( flag ? null() : null() ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ProcedureDesignator -> Name = 'null'
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ProcedureDesignator -> Name = 'null'
+  call sub((flag ? null() : null()))
+
+end subroutine
+
+! Test conditional arg inside a module and accessed via USE
+module m_condarg_parse
+  implicit none
+contains
+  subroutine mod_sub_with_condarg(flag, a, b)
+    logical, intent(in) :: flag
+    integer, intent(in) :: a, b
+    ! Test 31: Conditional arg inside a module procedure
+    ! CHECK: CALL ext(( flag ? a : b ))
+    ! TREE: ActualArg -> ConditionalArg
+    ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+    ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'b'
+    call ext((flag ? a : b))
+  end subroutine
+
+  subroutine mod_sub_with_nil(flag, a)
+    logical, intent(in) :: flag
+    integer, intent(in) :: a
+    ! Test 32: .NIL. conditional arg inside a module procedure
+    ! CHECK: CALL ext(( flag ? a : .NIL. ))
+    ! TREE: ActualArg -> ConditionalArg
+    ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+    ! TREE-NEXT: ConditionalArgTail -> Consequent -> ConditionalArgNil
+    call ext((flag ? a : .NIL.))
+  end subroutine
+
+  subroutine mod_sub_with_boz(flag, a)
+    logical, intent(in) :: flag
+    integer, intent(in) :: a
+    ! Test 33: BOZ conditional arg inside a module procedure
+    ! CHECK: CALL ext(( flag ? a : z"ff" ))
+    ! TREE: ActualArg -> ConditionalArg
+    ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+    ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+    call ext((flag ? a : z'FF'))
+  end subroutine
+
+  subroutine mod_sub_with_null(flag, a)
+    logical, intent(in) :: flag
+    integer, intent(in) :: a
+    ! Test 34: NULL() conditional arg inside a module procedure
+    ! CHECK: CALL ext(( flag ? null() : a ))
+    ! TREE: ActualArg -> ConditionalArg
+    ! TREE: Consequent -> Expr -> FunctionReference -> Call
+    ! TREE: ProcedureDesignator -> Name = 'null'
+    ! TREE: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'a'
+    call ext((flag ? null() : a))
+  end subroutine
+end module
+
+subroutine test_use_module_condarg
+  use m_condarg_parse
+  implicit none
+  integer :: x, y
+  logical :: cond
+
+  ! Test 35: Conditional arg using module procedure
+  ! CHECK: CALL mod_sub_with_condarg(cond, x, y)
+  ! TREE: CallStmt
+  ! TREE-NEXT: Call
+  ! TREE-NEXT: ProcedureDesignator -> Name = 'mod_sub_with_condarg'
+  call mod_sub_with_condarg(cond, x, y)
+
+  ! Test 36: Conditional arg at call site using procedures from USEd module
+  ! CHECK: CALL ext(( cond ? x : y ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'y'
+  call ext((cond ? x : y))
+
+  ! Test 37: BOZ conditional arg at call site after USE
+  ! CHECK: CALL ext(( cond ? x : z"ff" ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> LiteralConstant -> BOZLiteralConstant
+  call ext((cond ? x : z'FF'))
+
+  ! Test 38: NULL() conditional arg at call site after USE
+  ! CHECK: CALL ext(( cond ? null() : x ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> FunctionReference -> Call
+  ! TREE: ProcedureDesignator -> Name = 'null'
+  ! TREE: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  call ext((cond ? null() : x))
+
+  ! Test 39: Multi-branch conditional arg at call site after USE
+  ! CHECK: CALL ext(( cond ? x : cond ? y : .NIL. ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'x'
+  ! TREE-NEXT: ConditionalArgTail -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'y'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> ConditionalArgNil
+  call ext((cond ? x : cond ? y : .NIL.))
+
+end subroutine
+
+! Test conditional arg with various kinds
+subroutine test_kinds
+  implicit none
+  integer(kind=4) :: i4a, i4b
+  integer(kind=8) :: i8a, i8b
+  real(kind=4)    :: r4a, r4b
+  real(kind=8)    :: r8a, r8b
+  logical         :: cond
+
+  ! Test 40: Integer kind=4 consequent-args
+  ! CHECK: CALL sub(( cond ? i4a : i4b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'i4a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'i4b'
+  call sub((cond ? i4a : i4b))
+
+  ! Test 41: Integer kind=8 consequent-args
+  ! CHECK: CALL sub(( cond ? i8a : i8b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'i8a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'i8b'
+  call sub((cond ? i8a : i8b))
+
+  ! Test 42: Real kind=4 consequent-args
+  ! CHECK: CALL sub(( cond ? r4a : r4b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'r4a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'r4b'
+  call sub((cond ? r4a : r4b))
+
+  ! Test 43: Real kind=8 consequent-args
+  ! CHECK: CALL sub(( cond ? r8a : r8b ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 'r8a'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 'r8b'
+  call sub((cond ? r8a : r8b))
+
+end subroutine
+
+! Test conditional arg with character variables
+subroutine test_character
+  implicit none
+  character(len=10) :: s1, s2
+  logical :: flag
+
+  ! Test 44: Character variable consequent-args
+  ! CHECK: CALL sub(( flag ? s1 : s2 ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 's1'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> Expr -> Designator -> DataRef -> Name = 's2'
+  call sub((flag ? s1 : s2))
+
+  ! Test 45: Character variable with .NIL.
+  ! CHECK: CALL sub(( flag ? s1 : .NIL. ))
+  ! TREE: ActualArg -> ConditionalArg
+  ! TREE: Consequent -> Expr -> Designator -> DataRef -> Name = 's1'
+  ! TREE-NEXT: ConditionalArgTail -> Consequent -> ConditionalArgNil
+  call sub((flag ? s1 : .NIL.))
+
+end subroutine
diff --git a/flang/test/Semantics/conditional-arg.f90 b/flang/test/Semantics/conditional-arg.f90
new file mode 100644
index 0000000000000..5add56ca8f754
--- /dev/null
+++ b/flang/test/Semantics/conditional-arg.f90
@@ -0,0 +1,29 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+! Test semantic analysis of conditional arguments (F2023 R1526-R1528)
+
+subroutine test_conditional_arg
+  implicit none
+  integer :: a, b, c
+  logical :: flag, flag2
+
+  ! Simple conditional arg
+  !ERROR: Fortran 2023 conditional arguments are not yet supported
+  call sub((flag ? a : b))
+
+  ! Multi-branch conditional arg
+  !ERROR: Fortran 2023 conditional arguments are not yet supported
+  call sub((flag ? a : flag2 ? b : c))
+
+  ! .NIL. in else position
+  !ERROR: Fortran 2023 conditional arguments are not yet supported
+  call sub((flag ? a : .NIL.))
+
+  ! Keyword argument with conditional arg
+  !ERROR: Fortran 2023 conditional arguments are not yet supported
+  call sub(arg = (flag ? a : b))
+
+  ! .NIL. in both branches
+  !ERROR: Fortran 2023 conditional arguments are not yet supported
+  call sub((flag ? .NIL. : .NIL.))
+
+end subroutine



More information about the flang-commits mailing list