Skip to content

Commit 6ad05e1

Browse files
committed
[flang] Refine checks for NULL() in expressions
Fix a false positive "NULL can't be an operand here" error message arising in a defined generic interface for an intrinsic operator (==) with multiple spellings. Fixes #163255.
1 parent 7fe0691 commit 6ad05e1

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

flang/lib/Semantics/expression.cpp

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ class ArgumentAnalyzer {
176176

177177
// Find and return a user-defined operator or report an error.
178178
// The provided message is used if there is no such operator.
179-
MaybeExpr TryDefinedOp(
180-
const char *, parser::MessageFixedText, bool isUserOp = false);
179+
MaybeExpr TryDefinedOp(const char *, parser::MessageFixedText,
180+
bool isUserOp = false, bool checkForNullPointer = true);
181181
template <typename E>
182182
MaybeExpr TryDefinedOp(E opr, parser::MessageFixedText msg) {
183183
return TryDefinedOp(
@@ -211,7 +211,8 @@ class ArgumentAnalyzer {
211211
void SayNoMatch(
212212
const std::string &, bool isAssignment = false, bool isAmbiguous = false);
213213
std::string TypeAsFortran(std::size_t);
214-
bool AnyUntypedOrMissingOperand() const;
214+
bool AnyUntypedOperand() const;
215+
bool AnyMissingOperand() const;
215216

216217
ExpressionAnalyzer &context_;
217218
ActualArguments actuals_;
@@ -3695,11 +3696,12 @@ std::optional<characteristics::Procedure> ExpressionAnalyzer::CheckCall(
36953696

36963697
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
36973698
if (MaybeExpr operand{Analyze(x.v.value())}) {
3698-
if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) {
3699+
if (IsNullPointerOrAllocatable(&*operand)) {
3700+
Say("NULL() may not be parenthesized"_err_en_US);
3701+
} else if (const semantics::Symbol *symbol{GetLastSymbol(*operand)}) {
36993702
if (const semantics::Symbol *result{FindFunctionResult(*symbol)}) {
37003703
if (semantics::IsProcedurePointer(*result)) {
3701-
Say("A function reference that returns a procedure "
3702-
"pointer may not be parenthesized"_err_en_US); // C1003
3704+
Say("A function reference that returns a procedure pointer may not be parenthesized"_err_en_US); // C1003
37033705
}
37043706
}
37053707
}
@@ -3788,7 +3790,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::DefinedUnary &x) {
37883790
ArgumentAnalyzer analyzer{*this, name.source};
37893791
analyzer.Analyze(std::get<1>(x.t));
37903792
return analyzer.TryDefinedOp(name.source.ToString().c_str(),
3791-
"No operator %s defined for %s"_err_en_US, true);
3793+
"No operator %s defined for %s"_err_en_US, /*isUserOp=*/true);
37923794
}
37933795

37943796
// Binary (dyadic) operations
@@ -4176,15 +4178,23 @@ MaybeExpr ExpressionAnalyzer::IterativelyAnalyzeSubexpressions(
41764178
} while (!queue.empty());
41774179
// Analyze the collected subexpressions in bottom-up order.
41784180
// On an error, bail out and leave partial results in place.
4179-
MaybeExpr result;
4180-
for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) {
4181-
const parser::Expr &expr{**riter};
4182-
result = ExprOrVariable(expr, expr.source);
4183-
if (!result) {
4184-
return result;
4181+
if (finish.size() == 1) {
4182+
const parser::Expr &expr{DEREF(finish.front())};
4183+
return ExprOrVariable(expr, expr.source);
4184+
} else {
4185+
// NULL() operand catching is deferred to operation analysis so
4186+
// that they can be accepted by defined operators.
4187+
auto restorer{AllowNullPointer()};
4188+
MaybeExpr result;
4189+
for (auto riter{finish.rbegin()}; riter != finish.rend(); ++riter) {
4190+
const parser::Expr &expr{**riter};
4191+
result = ExprOrVariable(expr, expr.source);
4192+
if (!result) {
4193+
return result;
4194+
}
41854195
}
4196+
return result; // last value was from analysis of "top"
41864197
}
4187-
return result; // last value was from analysis of "top"
41884198
}
41894199

41904200
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
@@ -4681,7 +4691,7 @@ bool ArgumentAnalyzer::AnyCUDADeviceData() const {
46814691
// attribute.
46824692
bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
46834693
const char *opr) const {
4684-
if (AnyCUDADeviceData() && !AnyUntypedOrMissingOperand()) {
4694+
if (AnyCUDADeviceData() && !AnyUntypedOperand() && !AnyMissingOperand()) {
46854695
std::string oprNameString{"operator("s + opr + ')'};
46864696
parser::CharBlock oprName{oprNameString};
46874697
parser::Messages buffer;
@@ -4709,9 +4719,9 @@ bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
47094719
return false;
47104720
}
47114721

4712-
MaybeExpr ArgumentAnalyzer::TryDefinedOp(
4713-
const char *opr, parser::MessageFixedText error, bool isUserOp) {
4714-
if (AnyUntypedOrMissingOperand()) {
4722+
MaybeExpr ArgumentAnalyzer::TryDefinedOp(const char *opr,
4723+
parser::MessageFixedText error, bool isUserOp, bool checkForNullPointer) {
4724+
if (AnyMissingOperand()) {
47154725
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
47164726
return std::nullopt;
47174727
}
@@ -4790,7 +4800,9 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
47904800
context_.Say(
47914801
"Operands of %s are not conformable; have rank %d and rank %d"_err_en_US,
47924802
ToUpperCase(opr), actuals_[0]->Rank(), actuals_[1]->Rank());
4793-
} else if (CheckForNullPointer() && CheckForAssumedRank()) {
4803+
} else if (!CheckForAssumedRank()) {
4804+
} else if (checkForNullPointer && !CheckForNullPointer()) {
4805+
} else { // use the supplied error
47944806
context_.Say(error, ToUpperCase(opr), TypeAsFortran(0), TypeAsFortran(1));
47954807
}
47964808
return result;
@@ -4808,15 +4820,16 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
48084820
for (std::size_t i{0}; i < oprs.size(); ++i) {
48094821
parser::Messages buffer;
48104822
auto restorer{context_.GetContextualMessages().SetMessages(buffer)};
4811-
if (MaybeExpr thisResult{TryDefinedOp(oprs[i], error)}) {
4823+
if (MaybeExpr thisResult{TryDefinedOp(oprs[i], error, /*isUserOp=*/false,
4824+
/*checkForNullPointer=*/false)}) {
48124825
result = std::move(thisResult);
48134826
hit.push_back(oprs[i]);
48144827
hitBuffer = std::move(buffer);
48154828
}
48164829
}
48174830
}
4818-
if (hit.empty()) { // for the error
4819-
result = TryDefinedOp(oprs[0], error);
4831+
if (hit.empty()) { // run TryDefinedOp() again just to emit errors
4832+
CHECK(!TryDefinedOp(oprs[0], error).has_value());
48204833
} else if (hit.size() > 1) {
48214834
context_.Say(
48224835
"Matching accessible definitions were found with %zd variant spellings of the generic operator ('%s', '%s')"_err_en_US,
@@ -5232,10 +5245,19 @@ std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) {
52325245
}
52335246
}
52345247

5235-
bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() const {
5248+
bool ArgumentAnalyzer::AnyUntypedOperand() const {
5249+
for (const auto &actual : actuals_) {
5250+
if (actual && !actual->GetType() &&
5251+
!IsBareNullPointer(actual->UnwrapExpr())) {
5252+
return true;
5253+
}
5254+
}
5255+
return false;
5256+
}
5257+
5258+
bool ArgumentAnalyzer::AnyMissingOperand() const {
52365259
for (const auto &actual : actuals_) {
5237-
if (!actual ||
5238-
(!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) {
5260+
if (!actual) {
52395261
return true;
52405262
}
52415263
}

flang/test/Semantics/bug163255.f90

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
!RUN: %flang_fc1 -fdebug-unparse %s | FileCheck %s
2+
module m
3+
type t
4+
end type
5+
interface operator (==)
6+
module procedure equal
7+
end interface
8+
contains
9+
logical function equal(b1, b2)
10+
class(t), pointer, intent(in) :: b1, b2
11+
equal = associated(b1, b2)
12+
end
13+
end module
14+
15+
program test
16+
use m
17+
type(t), target :: target
18+
class(t), pointer :: p => target
19+
!CHECK: IF (equal(p,null(p))) STOP
20+
if (p == null(p)) stop
21+
end

flang/test/Semantics/resolve63.f90

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,12 @@ subroutine s1(x, y)
165165
logical :: l
166166
complex :: z
167167
y = y + z'1' !OK
168-
!ERROR: Operands of + must be numeric; have untyped and COMPLEX(4)
168+
!ERROR: No intrinsic or user-defined OPERATOR(+) matches operand types untyped and COMPLEX(4)
169169
z = z'1' + z
170170
y = +z'1' !OK
171171
!ERROR: Operand of unary - must be numeric; have untyped
172172
y = -z'1'
173-
!ERROR: Operands of + must be numeric; have LOGICAL(4) and untyped
174-
y = x + z'1'
173+
y = x + z'1' ! matches "add" with conversion of untyped to integer
175174
!ERROR: A NULL() pointer is not allowed as an operand here
176175
l = x /= null()
177176
!ERROR: A NULL() pointer is not allowed as a relational operand

0 commit comments

Comments
 (0)