Skip to content

Commit 88afb6e

Browse files
committed
[flang] Semantic checks for bad usage of whole assumed-size arrays
The semantics pass currently checks for several constraints that apply to the use of whole assumed-size arrays in various contexts, but C1002 wasn't really implemented. This patch implements C1002 by disallowing the use of whole assumed-size arrays in expressions and variables unless specifically allowed by the context. This centralizes the error reporting, which has been improved with a link to the array's declaration. Differential revision: https://reviews.llvm.org/D88691
1 parent 940d7aa commit 88afb6e

File tree

6 files changed

+51
-37
lines changed

6 files changed

+51
-37
lines changed

flang/include/flang/Semantics/expression.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "semantics.h"
1313
#include "flang/Common/Fortran.h"
1414
#include "flang/Common/indirection.h"
15+
#include "flang/Common/restorer.h"
1516
#include "flang/Evaluate/characteristics.h"
1617
#include "flang/Evaluate/check-expression.h"
1718
#include "flang/Evaluate/expression.h"
@@ -139,6 +140,12 @@ class ExpressionAnalyzer {
139140
// its INTEGER kind type parameter.
140141
std::optional<int> IsImpliedDo(parser::CharBlock) const;
141142

143+
// Allows a whole assumed-size array to appear for the lifetime of
144+
// the returned value.
145+
common::Restorer<bool> AllowWholeAssumedSizeArray() {
146+
return common::ScopedSet(isWholeAssumedSizeArrayOk_, true);
147+
}
148+
142149
Expr<SubscriptInteger> AnalyzeKindSelector(common::TypeCategory category,
143150
const std::optional<parser::KindSelector> &);
144151

@@ -372,6 +379,7 @@ class ExpressionAnalyzer {
372379
FoldingContext &foldingContext_{context_.foldingContext()};
373380
std::map<parser::CharBlock, int> impliedDos_; // values are INTEGER kinds
374381
bool fatalErrors_{false};
382+
bool isWholeAssumedSizeArrayOk_{false};
375383
friend class ArgumentAnalyzer;
376384
};
377385

flang/lib/Semantics/assignment.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,6 @@ void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) {
6666
const SomeExpr &rhs{assignment->rhs};
6767
auto lhsLoc{std::get<parser::Variable>(stmt.t).GetSource()};
6868
auto rhsLoc{std::get<parser::Expr>(stmt.t).source};
69-
auto shape{evaluate::GetShape(foldingContext(), lhs)};
70-
if (shape && !shape->empty() && !shape->back().has_value()) { // C1014
71-
Say(lhsLoc,
72-
"Left-hand side of assignment may not be a whole assumed-size array"_err_en_US);
73-
}
7469
if (CheckForPureContext(lhs, rhs, rhsLoc, false)) {
7570
const Scope &scope{context_.FindScope(lhsLoc)};
7671
if (auto whyNot{WhyNotModifiable(lhsLoc, lhs, scope, true)}) {

flang/lib/Semantics/check-io.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,6 @@ void IoChecker::Enter(const parser::InputItem &spec) {
298298
return;
299299
}
300300
CheckForDefinableVariable(*var, "Input");
301-
const auto &name{GetLastName(*var)};
302-
const auto *expr{GetExpr(*var)};
303-
if (name.symbol && IsAssumedSizeArray(*name.symbol) && expr &&
304-
!evaluate::IsArrayElement(*GetExpr(*var))) {
305-
context_.Say(name.source,
306-
"Whole assumed size array '%s' may not be an input item"_err_en_US,
307-
name.source); // C1231
308-
}
309301
}
310302

311303
void IoChecker::Enter(const parser::InquireSpec &spec) {

flang/lib/Semantics/expression.cpp

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class ArgumentAnalyzer {
151151
std::vector<const char *>, parser::MessageFixedText &&);
152152
MaybeExpr TryBoundOp(const Symbol &, int passIndex);
153153
std::optional<ActualArgument> AnalyzeExpr(const parser::Expr &);
154+
MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &);
154155
bool AreConformable() const;
155156
const Symbol *FindBoundOp(parser::CharBlock, int passIndex);
156157
void AddAssignmentConversion(
@@ -673,6 +674,14 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
673674
n.symbol->attrs().reset(semantics::Attr::VOLATILE);
674675
}
675676
}
677+
if (!isWholeAssumedSizeArrayOk_ &&
678+
semantics::IsAssumedSizeArray(*n.symbol)) { // C1002, C1014, C1231
679+
AttachDeclaration(
680+
SayAt(n,
681+
"Whole assumed-size array '%s' may not appear here without subscripts"_err_en_US,
682+
n.source),
683+
*n.symbol);
684+
}
676685
return Designate(DataRef{*n.symbol});
677686
}
678687
}
@@ -885,7 +894,12 @@ std::vector<Subscript> ExpressionAnalyzer::AnalyzeSectionSubscripts(
885894
}
886895

887896
MaybeExpr ExpressionAnalyzer::Analyze(const parser::ArrayElement &ae) {
888-
if (MaybeExpr baseExpr{Analyze(ae.base)}) {
897+
MaybeExpr baseExpr;
898+
{
899+
auto restorer{AllowWholeAssumedSizeArray()};
900+
baseExpr = Analyze(ae.base);
901+
}
902+
if (baseExpr) {
889903
if (ae.subscripts.empty()) {
890904
// will be converted to function call later or error reported
891905
return std::nullopt;
@@ -2713,9 +2727,6 @@ void ArgumentAnalyzer::Analyze(const parser::Variable &x) {
27132727

27142728
void ArgumentAnalyzer::Analyze(
27152729
const parser::ActualArgSpec &arg, bool isSubroutine) {
2716-
// TODO: C1002: Allow a whole assumed-size array to appear if the dummy
2717-
// argument would accept it. Handle by special-casing the context
2718-
// ActualArg -> Variable -> Designator.
27192730
// TODO: Actual arguments that are procedures and procedure pointers need to
27202731
// be detected and represented (they're not expressions).
27212732
// TODO: C1534: Don't allow a "restricted" specific intrinsic to be passed.
@@ -2983,33 +2994,41 @@ void ArgumentAnalyzer::Dump(llvm::raw_ostream &os) {
29832994
}
29842995
}
29852996
}
2997+
29862998
std::optional<ActualArgument> ArgumentAnalyzer::AnalyzeExpr(
29872999
const parser::Expr &expr) {
29883000
source_.ExtendToCover(expr.source);
29893001
if (const Symbol * assumedTypeDummy{AssumedTypeDummy(expr)}) {
29903002
expr.typedExpr.Reset(new GenericExprWrapper{}, GenericExprWrapper::Deleter);
29913003
if (isProcedureCall_) {
29923004
return ActualArgument{ActualArgument::AssumedType{*assumedTypeDummy}};
2993-
} else {
2994-
context_.SayAt(expr.source,
2995-
"TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
2996-
return std::nullopt;
29973005
}
2998-
} else if (MaybeExpr argExpr{context_.Analyze(expr)}) {
2999-
if (!isProcedureCall_ && IsProcedure(*argExpr)) {
3000-
if (IsFunction(*argExpr)) {
3001-
context_.SayAt(
3002-
expr.source, "Function call must have argument list"_err_en_US);
3003-
} else {
3004-
context_.SayAt(
3005-
expr.source, "Subroutine name is not allowed here"_err_en_US);
3006-
}
3007-
return std::nullopt;
3006+
context_.SayAt(expr.source,
3007+
"TYPE(*) dummy argument may only be used as an actual argument"_err_en_US);
3008+
} else if (MaybeExpr argExpr{AnalyzeExprOrWholeAssumedSizeArray(expr)}) {
3009+
if (isProcedureCall_ || !IsProcedure(*argExpr)) {
3010+
return ActualArgument{context_.Fold(std::move(*argExpr))};
3011+
}
3012+
context_.SayAt(expr.source,
3013+
IsFunction(*argExpr) ? "Function call must have argument list"_err_en_US
3014+
: "Subroutine name is not allowed here"_err_en_US);
3015+
}
3016+
return std::nullopt;
3017+
}
3018+
3019+
MaybeExpr ArgumentAnalyzer::AnalyzeExprOrWholeAssumedSizeArray(
3020+
const parser::Expr &expr) {
3021+
// If an expression's parse tree is a whole assumed-size array:
3022+
// Expr -> Designator -> DataRef -> Name
3023+
// treat it as a special case for argument passing and bypass
3024+
// the C1002/C1014 constraint checking in expression semantics.
3025+
if (const auto *name{parser::Unwrap<parser::Name>(expr)}) {
3026+
if (name->symbol && semantics::IsAssumedSizeArray(*name->symbol)) {
3027+
auto restorer{context_.AllowWholeAssumedSizeArray()};
3028+
return context_.Analyze(expr);
30083029
}
3009-
return ActualArgument{context_.Fold(std::move(*argExpr))};
3010-
} else {
3011-
return std::nullopt;
30123030
}
3031+
return context_.Analyze(expr);
30133032
}
30143033

30153034
bool ArgumentAnalyzer::AreConformable() const {

flang/test/Semantics/assign04.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ subroutine s6(x)
9494
x(:3) = [1, 2, 3]
9595
!ERROR: Assumed-size array 'x' must have explicit final subscript upper bound value
9696
x(:) = [1, 2, 3]
97-
!ERROR: Left-hand side of assignment may not be a whole assumed-size array
97+
!ERROR: Whole assumed-size array 'x' may not appear here without subscripts
9898
x = [1, 2, 3]
9999
end
100100

@@ -106,7 +106,7 @@ module m7
106106
subroutine s7(x)
107107
type(t) :: x(*)
108108
x(:3)%i = [1, 2, 3]
109-
!ERROR: Left-hand side of assignment may not be a whole assumed-size array
109+
!ERROR: Whole assumed-size array 'x' may not appear here without subscripts
110110
x%i = [1, 2, 3]
111111
end
112112
end

flang/test/Semantics/io03.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,6 @@ subroutine s(aa, n)
178178
!ERROR: Input variable 'n' must be definable
179179
read(*, *) n
180180

181-
!ERROR: Whole assumed size array 'aa' may not be an input item
181+
!ERROR: Whole assumed-size array 'aa' may not appear here without subscripts
182182
read(*, *) aa
183183
end

0 commit comments

Comments
 (0)