Skip to content

Commit 68f4e46

Browse files
authored
[flang] Adjust "doubled operator" expression extension (#93353)
Most Fortran compilers accept "doubled operators" as a language extension. This is the use of a unary '+' or '-' operator that is not the first unparenthesized operator in an expression, as in 'x*-y'. This compiler has implemented this extension, but in a way that's different from other compilers' behavior. I interpreted the unary '+'/'-' as a unary operator in the sense of C/C++, giving it a higher priority than any binary (dyadic) operator. All other compilers with this extension, however, give a unary '+'/'-' a lower precedence than exponentiation ('**'), a binary operator that C/C++ lacks. And this interpretation makes more sense for Fortran, anyway, where the standard conforming '-x**y' must mean '-(x**y)' already. This patch makes 'x*-y**z' parse as 'x*-(y**z)', not 'x*(-y)**z)', and adds a test to ensure that it does.
1 parent e44cea5 commit 68f4e46

File tree

4 files changed

+30
-10
lines changed

4 files changed

+30
-10
lines changed

flang/docs/Extensions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ end
356356
* A derived type that meets (most of) the requirements of an interoperable
357357
derived type can be used as such where an interoperable type is
358358
required, with warnings, even if it lacks the BIND(C) attribute.
359+
* A "mult-operand" in an expression can be preceded by a unary
360+
`+` or `-` operator.
359361

360362
### Extensions supported when enabled by options
361363

flang/include/flang/Common/Fortran-features.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
2424
DoubleComplex, Byte, StarKind, ExponentMatchingKindParam, QuadPrecision,
2525
SlashInitialization, TripletInArrayConstructor, MissingColons,
2626
SignedComplexLiteral, OldStyleParameter, ComplexConstructor, PercentLOC,
27-
SignedPrimary, FileName, Carriagecontrol, Convert, Dispose,
27+
SignedMultOperand, FileName, Carriagecontrol, Convert, Dispose,
2828
IOListLeadingComma, AbbreviatedEditDescriptor, ProgramParentheses,
2929
PercentRefAndVal, OmitFunctionDummies, CrayPointer, Hollerith, ArithmeticIF,
3030
Assign, AssignedGOTO, Pause, OpenACC, OpenMP, CUDA, CruftAfterAmpersand,

flang/lib/Parser/expr-parsers.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,8 @@ constexpr auto primary{instrumented("primary"_en_US,
8787
// R1002 level-1-expr -> [defined-unary-op] primary
8888
// TODO: Reasonable extension: permit multiple defined-unary-ops
8989
constexpr auto level1Expr{sourced(
90-
first(primary, // must come before define op to resolve .TRUE._8 ambiguity
91-
construct<Expr>(construct<Expr::DefinedUnary>(definedOpName, primary)),
92-
extension<LanguageFeature::SignedPrimary>(
93-
"nonstandard usage: signed primary"_port_en_US,
94-
construct<Expr>(construct<Expr::UnaryPlus>("+" >> primary))),
95-
extension<LanguageFeature::SignedPrimary>(
96-
"nonstandard usage: signed primary"_port_en_US,
97-
construct<Expr>(construct<Expr::Negate>("-" >> primary)))))};
90+
primary || // must come before define op to resolve .TRUE._8 ambiguity
91+
construct<Expr>(construct<Expr::DefinedUnary>(definedOpName, primary)))};
9892

9993
// R1004 mult-operand -> level-1-expr [power-op mult-operand]
10094
// R1007 power-op -> **
@@ -105,7 +99,19 @@ struct MultOperand {
10599
static inline std::optional<Expr> Parse(ParseState &);
106100
};
107101

108-
static constexpr auto multOperand{sourced(MultOperand{})};
102+
// Extension: allow + or - before a mult-operand
103+
// Such a unary operand has lower precedence than exponentiation,
104+
// so -x**2 is -(x**2), not (-x)**2; this matches all other
105+
// compilers with this extension.
106+
static constexpr auto standardMultOperand{sourced(MultOperand{})};
107+
static constexpr auto multOperand{standardMultOperand ||
108+
extension<LanguageFeature::SignedMultOperand>(
109+
"nonstandard usage: signed mult-operand"_port_en_US,
110+
construct<Expr>(
111+
construct<Expr::UnaryPlus>("+" >> standardMultOperand))) ||
112+
extension<LanguageFeature::SignedMultOperand>(
113+
"nonstandard usage: signed mult-operand"_port_en_US,
114+
construct<Expr>(construct<Expr::Negate>("-" >> standardMultOperand)))};
109115

110116
inline std::optional<Expr> MultOperand::Parse(ParseState &state) {
111117
std::optional<Expr> result{level1Expr.Parse(state)};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
! RUN: %python %S/test_folding.py %s %flang_fc1
2+
module m
3+
integer, parameter :: j = 2
4+
! standard cases
5+
logical, parameter :: test_1 = -j**2 == -4
6+
logical, parameter :: test_2 = 4-j**2 == 0
7+
! extension cases
8+
logical, parameter :: test_3 = 4+-j**2 == 0 ! not 8
9+
logical, parameter :: test_4 = 2*-j**2 == -8 ! not 8
10+
logical, parameter :: test_5 = -j**2+-j**2 == -8 ! not 8
11+
logical, parameter :: test_6 = j**2*-j**2 == -16 ! not 16
12+
end

0 commit comments

Comments
 (0)