[flang-commits] [flang] 8d23614 - [flang] Disallow NULL() as an expression where it cannot appear
Peter Klausler via flang-commits
flang-commits at lists.llvm.org
Fri Dec 2 14:37:03 PST 2022
Author: Peter Klausler
Date: 2022-12-02T14:36:51-08:00
New Revision: 8d23614a2926cbe972a97f9ffb59df723635ab80
URL: https://github.com/llvm/llvm-project/commit/8d23614a2926cbe972a97f9ffb59df723635ab80
DIFF: https://github.com/llvm/llvm-project/commit/8d23614a2926cbe972a97f9ffb59df723635ab80.diff
LOG: [flang] Disallow NULL() as an expression where it cannot appear
A NULL() pointer is generally not a valid expression (as opposed to
a variable) apart from some initialization contexts and some actual
arguments.
Differential Revision: https://reviews.llvm.org/D139047
Added:
Modified:
flang/include/flang/Semantics/expression.h
flang/lib/Semantics/expression.cpp
flang/lib/Semantics/resolve-names.cpp
flang/test/Semantics/null01.f90
Removed:
################################################################################
diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index a6ed85b0dbf9..bb6a14a9cf0e 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -142,12 +142,6 @@ class ExpressionAnalyzer {
// its INTEGER kind type parameter.
std::optional<int> IsImpliedDo(parser::CharBlock) const;
- // Allows a whole assumed-size array to appear for the lifetime of
- // the returned value.
- common::Restorer<bool> AllowWholeAssumedSizeArray() {
- return common::ScopedSet(isWholeAssumedSizeArrayOk_, true);
- }
-
common::Restorer<bool> DoNotUseSavedTypedExprs() {
return common::ScopedSet(useSavedTypedExprs_, false);
}
@@ -255,6 +249,17 @@ class ExpressionAnalyzer {
int IntegerTypeSpecKind(const parser::IntegerTypeSpec &);
private:
+ // Allows a whole assumed-size array to appear for the lifetime of
+ // the returned value.
+ common::Restorer<bool> AllowWholeAssumedSizeArray() {
+ return common::ScopedSet(isWholeAssumedSizeArrayOk_, true);
+ }
+
+ // Allows an Expr to be a null pointer.
+ common::Restorer<bool> AllowNullPointer() {
+ return common::ScopedSet(isNullPointerOk_, true);
+ }
+
MaybeExpr Analyze(const parser::IntLiteralConstant &, bool negated = false);
MaybeExpr Analyze(const parser::RealLiteralConstant &);
MaybeExpr Analyze(const parser::ComplexPart &);
@@ -375,6 +380,7 @@ class ExpressionAnalyzer {
FoldingContext &foldingContext_{context_.foldingContext()};
std::map<parser::CharBlock, int> impliedDos_; // values are INTEGER kinds
bool isWholeAssumedSizeArrayOk_{false};
+ bool isNullPointerOk_{false};
bool useSavedTypedExprs_{true};
bool inWhereBody_{false};
bool inDataStmtConstant_{false};
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 88de21373be6..1398f59aa2a6 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -838,7 +838,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::NamedConstant &n) {
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::NullInit &n) {
- if (MaybeExpr value{Analyze(n.v)}) {
+ auto restorer{AllowNullPointer()};
+ if (MaybeExpr value{Analyze(n.v.value())}) {
// Subtle: when the NullInit is a DataStmtConstant, it might
// be a misparse of a structure constructor without parameters
// or components (e.g., T()). Checking the result to ensure
@@ -1710,6 +1711,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(
bool checkConflicts{true}; // until we hit one
auto &messages{GetContextualMessages()};
+ // NULL() can be a valid component
+ auto restorer{AllowNullPointer()};
+
for (const auto &component :
std::get<std::list<parser::ComponentSpec>>(structure.t)) {
const parser::Expr &expr{
@@ -1842,8 +1846,41 @@ MaybeExpr ExpressionAnalyzer::Analyze(
semantics::CheckStructConstructorPointerComponent(
GetFoldingContext(), *symbol, *value, innermost); // C7104, C7105
result.Add(*symbol, Fold(std::move(*value)));
- } else if (MaybeExpr converted{
- ConvertToType(*symbol, std::move(*value))}) {
+ continue;
+ }
+ if (IsNullPointer(*value)) {
+ if (IsAllocatable(*symbol)) {
+ if (IsBareNullPointer(&*value)) {
+ // NULL() with no arguments allowed by 7.5.10 para 6 for
+ // ALLOCATABLE.
+ result.Add(*symbol, Expr<SomeType>{NullPointer{}});
+ continue;
+ }
+ if (IsNullObjectPointer(*value)) {
+ AttachDeclaration(
+ Say(expr.source,
+ "NULL() with arguments is not standard conforming as the value for allocatable component '%s'"_port_en_US,
+ symbol->name()),
+ *symbol);
+ // proceed to check type & shape
+ } else {
+ AttachDeclaration(
+ Say(expr.source,
+ "A NULL procedure pointer may not be used as the value for component '%s'"_err_en_US,
+ symbol->name()),
+ *symbol);
+ continue;
+ }
+ } else {
+ AttachDeclaration(
+ Say(expr.source,
+ "A NULL pointer may not be used as the value for component '%s'"_err_en_US,
+ symbol->name()),
+ *symbol);
+ continue;
+ }
+ }
+ if (MaybeExpr converted{ConvertToType(*symbol, std::move(*value))}) {
if (auto componentShape{GetShape(GetFoldingContext(), *symbol)}) {
if (auto valueShape{GetShape(GetFoldingContext(), *converted)}) {
if (GetRank(*componentShape) == 0 && GetRank(*valueShape) > 0) {
@@ -1881,9 +1918,6 @@ MaybeExpr ExpressionAnalyzer::Analyze(
symbol->name()),
*symbol);
}
- } else if (IsAllocatable(*symbol) && IsBareNullPointer(&*value)) {
- // NULL() with no arguments allowed by 7.5.10 para 6 for ALLOCATABLE.
- result.Add(*symbol, Expr<SomeType>{NullPointer{}});
} else if (auto symType{DynamicType::From(symbol)}) {
if (IsAllocatable(*symbol) && symType->IsUnlimitedPolymorphic() &&
valueType) {
@@ -2615,7 +2649,11 @@ const Assignment *ExpressionAnalyzer::Analyze(
const parser::PointerAssignmentStmt &x) {
if (!x.typedAssignment) {
MaybeExpr lhs{Analyze(std::get<parser::DataRef>(x.t))};
- MaybeExpr rhs{Analyze(std::get<parser::Expr>(x.t))};
+ MaybeExpr rhs;
+ {
+ auto restorer{AllowNullPointer()};
+ rhs = Analyze(std::get<parser::Expr>(x.t));
+ }
if (!lhs || !rhs) {
x.typedAssignment.Reset(
new GenericAssignmentWrapper{}, GenericAssignmentWrapper::Deleter);
@@ -3084,9 +3122,6 @@ static void FixMisparsedFunctionReference(
template <typename PARSED>
MaybeExpr ExpressionAnalyzer::ExprOrVariable(
const PARSED &x, parser::CharBlock source) {
- if (useSavedTypedExprs_ && x.typedExpr) {
- return x.typedExpr->v;
- }
auto restorer{GetContextualMessages().SetLocation(source)};
if constexpr (std::is_same_v<PARSED, parser::Expr> ||
std::is_same_v<PARSED, parser::Variable>) {
@@ -3138,10 +3173,21 @@ MaybeExpr ExpressionAnalyzer::ExprOrVariable(
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr &expr) {
- return ExprOrVariable(expr, expr.source);
+ if (useSavedTypedExprs_ && expr.typedExpr) {
+ return expr.typedExpr->v;
+ }
+ MaybeExpr result{ExprOrVariable(expr, expr.source)};
+ if (!isNullPointerOk_ && result && IsNullPointer(*result)) {
+ Say(expr.source,
+ "NULL() may not be used as an expression in this context"_err_en_US);
+ }
+ return result;
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Variable &variable) {
+ if (useSavedTypedExprs_ && variable.typedExpr) {
+ return variable.typedExpr->v;
+ }
return ExprOrVariable(variable, variable.GetSource());
}
@@ -3425,8 +3471,6 @@ void ArgumentAnalyzer::Analyze(const parser::Variable &x) {
void ArgumentAnalyzer::Analyze(
const parser::ActualArgSpec &arg, bool isSubroutine) {
- // TODO: Actual arguments that are procedures and procedure pointers need to
- // be detected and represented (they're not expressions).
// TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
std::optional<ActualArgument> actual;
common::visit(common::visitors{
@@ -3796,6 +3840,7 @@ MaybeExpr ArgumentAnalyzer::AnalyzeExprOrWholeAssumedSizeArray(
return context_.Analyze(expr);
}
}
+ auto restorer{context_.AllowNullPointer()};
return context_.Analyze(expr);
}
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5f82f7459a23..e53d3404c600 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -4932,8 +4932,17 @@ void DeclarationVisitor::Post(const parser::ProcComponentDefStmt &) {
bool DeclarationVisitor::Pre(const parser::ProcPointerInit &x) {
if (auto *name{std::get_if<parser::Name>(&x.u)}) {
return !NameIsKnownOrIntrinsic(*name) && !CheckUseError(*name);
+ } else {
+ const auto &null{DEREF(std::get_if<parser::NullInit>(&x.u))};
+ Walk(null);
+ if (auto nullInit{EvaluateExpr(null)}) {
+ if (!evaluate::IsNullPointer(*nullInit)) {
+ Say(null.v.value().source,
+ "Procedure pointer initializer must be a name or intrinsic NULL()"_err_en_US);
+ }
+ }
+ return false;
}
- return true;
}
void DeclarationVisitor::Post(const parser::ProcInterface &x) {
if (auto *name{std::get_if<parser::Name>(&x.u)}) {
@@ -6886,9 +6895,9 @@ void DeclarationVisitor::Initialization(const parser::Name &name,
[&](const parser::NullInit &null) { // => NULL()
Walk(null);
if (auto nullInit{EvaluateExpr(null)}) {
- if (!evaluate::IsNullPointer(*nullInit)) {
- Say(name,
- "Pointer initializer must be intrinsic NULL()"_err_en_US); // C813
+ if (!evaluate::IsNullPointer(*nullInit)) { // C813
+ Say(null.v.value().source,
+ "Pointer initializer must be intrinsic NULL()"_err_en_US);
} else if (IsPointer(ultimate)) {
if (auto *object{ultimate.detailsIf<ObjectEntityDetails>()}) {
object->set_init(std::move(*nullInit));
@@ -6947,14 +6956,14 @@ void DeclarationVisitor::PointerInitialization(
if (IsProcedurePointer(ultimate)) {
auto &details{ultimate.get<ProcEntityDetails>()};
CHECK(!details.init());
- Walk(target);
if (const auto *targetName{std::get_if<parser::Name>(&target.u)}) {
+ Walk(target);
if (!CheckUseError(*targetName) && targetName->symbol) {
// Validation is done in declaration checking.
details.set_init(*targetName->symbol);
}
- } else {
- details.set_init(nullptr); // explicit NULL()
+ } else { // explicit NULL
+ details.set_init(nullptr);
}
} else {
Say(name,
diff --git a/flang/test/Semantics/null01.f90 b/flang/test/Semantics/null01.f90
index 2cae128bbb7c..e2e16fafa140 100644
--- a/flang/test/Semantics/null01.f90
+++ b/flang/test/Semantics/null01.f90
@@ -32,6 +32,7 @@ function f3()
external implicit
type :: dt0
integer, pointer :: ip0
+ integer :: n = 666
end type dt0
type :: dt1
integer, pointer :: ip1(:)
@@ -42,11 +43,15 @@ function f3()
type :: dt3
procedure(s1), pointer, nopass :: pps1
end type dt3
+ type :: dt4
+ real, allocatable :: ra0
+ end type dt4
integer :: j
type(dt0) :: dt0x
type(dt1) :: dt1x
type(dt2) :: dt2x
type(dt3) :: dt3x
+ type(dt4) :: dt4x
integer, pointer :: ip0, ip1(:), ip2(:,:)
integer, allocatable :: ia0, ia1(:), ia2(:,:)
real, pointer :: rp0, rp1(:)
@@ -55,6 +60,7 @@ function f3()
integer, parameter :: ip2r = rank(null(mold=ip2))
integer, parameter :: eight = ip0r + ip1r + ip2r + 5
real(kind=eight) :: r8check
+ logical, pointer :: lp
ip0 => null() ! ok
ip1 => null() ! ok
ip2 => null() ! ok
@@ -68,6 +74,8 @@ function f3()
dt0x = dt0(ip0=null(mold=ip0))
!ERROR: function result type 'REAL(4)' is not compatible with pointer type 'INTEGER(4)'
dt0x = dt0(ip0=null(mold=rp0))
+ !ERROR: A NULL pointer may not be used as the value for component 'n'
+ dt0x = dt0(null(), null())
!ERROR: function result type 'REAL(4)' is not compatible with pointer type 'INTEGER(4)'
dt1x = dt1(ip1=null(mold=rp1))
dt2x = dt2(pps0=null())
@@ -77,6 +85,14 @@ function f3()
!ERROR: Procedure pointer 'pps1' associated with result of reference to function 'null' that is an incompatible procedure pointer: distinct numbers of dummy arguments
dt3x = dt3(pps1=null(mold=dt2x%pps0))
dt3x = dt3(pps1=null(mold=dt3x%pps1))
+ dt4x = dt4(null()) ! ok
+ !PORTABILITY: NULL() with arguments is not standard conforming as the value for allocatable component 'ra0'
+ dt4x = dt4(null(rp0))
+ !PORTABILITY: NULL() with arguments is not standard conforming as the value for allocatable component 'ra0'
+ !ERROR: Rank-1 array value is not compatible with scalar component 'ra0'
+ dt4x = dt4(null(rp1))
+ !ERROR: A NULL procedure pointer may not be used as the value for component 'ra0'
+ dt4x = dt4(null(dt2x%pps0))
call canbenull(null(), null()) ! fine
call canbenull(null(mold=ip0), null(mold=rp0)) ! fine
!ERROR: Null pointer argument requires an explicit interface
@@ -87,4 +103,10 @@ function f3()
print *, sin(null(rp0))
!ERROR: A NULL() pointer is not allowed for 'source=' intrinsic argument
print *, transfer(null(rp0),ip0)
+ !ERROR: NULL() may not be used as an expression in this context
+ select case(null(ip0))
+ end select
+ !ERROR: NULL() may not be used as an expression in this context
+ if (null(lp)) then
+ end if
end subroutine test
More information about the flang-commits
mailing list