[flang] Add support for common blocks

A symbol for a common block has `CommonBlockDetails` which contains
a list of the symbols that are in the common block.

The name of the symbol for the blank common block is the empty string.
That preserves the property that every symbol name is a substring of
the cooked source. We use the 0-length substring starting at the COMMON
statement so that when symbols are sorted by the location of the start
of the name it ends up in the right place.

Some of the checks on members of common blocks don't happen until the
end of the scope. They can't happen earlier because we don't necessarily
know the type and attributes.

Enhance `test_errors.sh` so that multiple errors can be expected for
a single line.

Original-commit: flang-compiler/f18@2c4ca6b5d3
Reviewed-on: https://github.com/flang-compiler/f18/pull/286
This commit is contained in:
Tim Keith 2019-02-14 07:59:20 -08:00
parent 288bd16527
commit 543b15bca4
10 changed files with 363 additions and 18 deletions

View File

@ -173,6 +173,15 @@ void ModFileWriter::PutSymbol(
}
decls_ << '\n';
},
[&](const CommonBlockDetails &x) {
PutLower(decls_ << "common/", symbol);
char sep = '/';
for (const auto *object : x.objects()) {
PutLower(decls_ << sep, *object);
sep = ',';
}
decls_ << '\n';
},
[&](const FinalProcDetails &) {
PutLower(typeBindings << "final::", symbol) << '\n';
},

View File

@ -404,6 +404,8 @@ public:
void SayAlreadyDeclared(const parser::Name &, const Symbol &);
void SayWithDecl(const parser::Name &, const Symbol &, MessageFixedText &&);
void SayDerivedType(const SourceName &, MessageFixedText &&, const Scope &);
void Say2(const SourceName &, MessageFixedText &&, const SourceName &,
MessageFixedText &&);
void Say2(const parser::Name &, MessageFixedText &&, const Symbol &,
MessageFixedText &&);
@ -690,6 +692,10 @@ public:
bool Pre(const parser::StructureConstructor &);
bool Pre(const parser::NamelistStmt::Group &);
bool Pre(const parser::IoControlSpec &);
bool Pre(const parser::CommonStmt::Block &);
void Post(const parser::CommonStmt::Block &);
bool Pre(const parser::CommonBlockObject &);
void Post(const parser::CommonBlockObject &);
protected:
bool BeginDecl();
@ -707,6 +713,7 @@ protected:
bool CheckUseError(const parser::Name &);
void CheckAccessibility(const parser::Name &, bool, const Symbol &);
void CheckScalarIntegerType(const parser::Name &);
void CheckCommonBlocks();
private:
// The attribute corresponding to the statement containing an ObjectDecl
@ -724,6 +731,11 @@ private:
bool sawContains{false}; // currently processing bindings
bool sequence{false}; // is a sequence type
} derivedTypeInfo_;
// Info about common blocks in the current scope
struct {
Symbol *curr{nullptr}; // common block currently being processed
std::set<SourceName> names; // names in any common block of scope
} commonBlockInfo_;
// In a ProcedureDeclarationStmt or ProcComponentDefStmt, this is
// the interface name, if any.
const parser::Name *interfaceName_{nullptr};
@ -739,6 +751,7 @@ private:
Symbol *MakeTypeSymbol(const parser::Name &, Details &&);
bool OkToAddComponent(const parser::Name &, const Symbol * = nullptr);
ParamValue GetParamValue(const parser::TypeParamValue &);
void CheckCommonBlockDerivedType(const SourceName &, const Symbol &);
// Declare an object or procedure entity.
// T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails
@ -771,6 +784,14 @@ private:
} else {
CHECK(!"unexpected kind");
}
} else if (std::is_same_v<ObjectEntityDetails, T> &&
symbol.has<ProcEntityDetails>()) {
SayWithDecl(
name, symbol, "'%s' is already declared as a procedure"_err_en_US);
} else if (std::is_same_v<ProcEntityDetails, T> &&
symbol.has<ObjectEntityDetails>()) {
SayWithDecl(
name, symbol, "'%s' is already declared as an object"_err_en_US);
} else {
SayAlreadyDeclared(name, symbol);
}
@ -901,8 +922,6 @@ public:
template<typename T> bool Pre(const T &) { return true; }
template<typename T> void Post(const T &) {}
bool Pre(const parser::CommonBlockObject &);
void Post(const parser::CommonBlockObject &);
bool Pre(const parser::PrefixSpec &);
void Post(const parser::SpecificationPart &);
bool Pre(const parser::MainProgram &);
@ -1439,10 +1458,14 @@ void ScopeHandler::SayDerivedType(
.Attach(typeSymbol->name(), "Declaration of derived type '%s'"_en_US,
typeSymbol->name().ToString().c_str());
}
void ScopeHandler::Say2(const SourceName &name1, MessageFixedText &&msg1,
const SourceName &name2, MessageFixedText &&msg2) {
Say(name1, std::move(msg1))
.Attach(name2, std::move(msg2), name2.ToString().c_str());
}
void ScopeHandler::Say2(const parser::Name &name, MessageFixedText &&msg1,
const Symbol &symbol, MessageFixedText &&msg2) {
Say(name.source, std::move(msg1))
.Attach(symbol.name(), msg2, symbol.name().ToString().c_str());
Say2(name.source, std::move(msg1), symbol.name(), std::move(msg2));
}
Scope &ScopeHandler::InclusiveScope() {
@ -1473,8 +1496,8 @@ void ScopeHandler::PushScope(Scope &scope) {
}
if (kind != Scope::Kind::DerivedType) {
if (auto *symbol{scope.symbol()}) {
// Create a dummy symbol so we can't create another one with the same name
// It might already be there if we previously pushed the scope.
// Create a dummy symbol so we can't create another one with the same
// name. It might already be there if we previously pushed the scope.
if (!FindInScope(scope, symbol->name())) {
auto &newSymbol{CopySymbol(*symbol)};
if (kind == Scope::Kind::Subprogram) {
@ -3083,6 +3106,123 @@ bool DeclarationVisitor::Pre(const parser::IoControlSpec &x) {
return true;
}
bool DeclarationVisitor::Pre(const parser::CommonStmt::Block &x) {
const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
parser::Name blankCommon;
blankCommon.source = SourceName{currStmtSource()->begin(), std::size_t{0}};
const parser::Name &name{optName ? *optName : blankCommon};
auto *symbol{FindInScope(currScope(), name)};
if (symbol && !symbol->has<CommonBlockDetails>()) {
SayAlreadyDeclared(name, *symbol);
EraseSymbol(name);
symbol = nullptr;
}
if (!symbol) {
symbol = &MakeSymbol(name, CommonBlockDetails{});
}
CHECK(!commonBlockInfo_.curr);
commonBlockInfo_.curr = symbol;
return true;
}
void DeclarationVisitor::Post(const parser::CommonStmt::Block &) {
commonBlockInfo_.curr = nullptr;
}
bool DeclarationVisitor::Pre(const parser::CommonBlockObject &x) {
BeginArraySpec();
return true;
}
void DeclarationVisitor::Post(const parser::CommonBlockObject &x) {
CHECK(commonBlockInfo_.curr);
const auto &name{std::get<parser::Name>(x.t)};
auto &symbol{DeclareObjectEntity(name, Attrs{})};
ClearArraySpec();
if (!symbol.has<ObjectEntityDetails>()) {
return; // error was reported
}
commonBlockInfo_.curr->get<CommonBlockDetails>().add_object(symbol);
if (!IsExplicit(symbol.get<ObjectEntityDetails>().shape())) {
Say(name,
"The shape of common block object '%s' must be explicit"_err_en_US);
return;
}
auto pair{commonBlockInfo_.names.insert(name.source)};
if (!pair.second) {
const SourceName &prev{*pair.first};
Say2(name.source, "'%s' is already in a COMMON block"_err_en_US, prev,
"Previous occurrence of '%s' in a COMMON block"_en_US);
return;
}
}
// Check types of common block objects, now that they are known.
void DeclarationVisitor::CheckCommonBlocks() {
for (const auto &name : commonBlockInfo_.names) {
const auto *symbol{currScope().FindSymbol(name)};
CHECK(symbol);
const auto &attrs{symbol->attrs()};
if (attrs.test(Attr::ALLOCATABLE)) {
Say(name,
"ALLOCATABLE object '%s' may not appear in a COMMON block"_err_en_US);
} else if (attrs.test(Attr::BIND_C)) {
Say(name,
"Variable '%s' with BIND attribute may not appear in a COMMON block"_err_en_US);
} else if (const auto &details{symbol->get<ObjectEntityDetails>()};
details.isDummy()) {
Say(name,
"Dummy argument '%s' may not appear in a COMMON block"_err_en_US);
} else if (const DeclTypeSpec * type{details.type()}) {
if (type->category() == DeclTypeSpec::ClassStar) {
Say(name,
"Unlimited polymorphic pointer '%s' may not appear in a COMMON block"_err_en_US);
} else if (const auto *derived{type->AsDerived()}) {
auto &typeSymbol{derived->typeSymbol()};
if (!typeSymbol.attrs().test(Attr::BIND_C) &&
!typeSymbol.get<DerivedTypeDetails>().sequence()) {
Say(name,
"Derived type '%s' in COMMON block must have the BIND or"
" SEQUENCE attribute"_err_en_US);
}
CheckCommonBlockDerivedType(name, typeSymbol);
}
}
}
commonBlockInfo_ = {};
}
// Check if this derived type can be in a COMMON block.
void DeclarationVisitor::CheckCommonBlockDerivedType(
const SourceName &name, const Symbol &typeSymbol) {
if (const auto *scope{typeSymbol.scope()}) {
for (const auto &pair : *scope) {
const Symbol &component{*pair.second};
if (component.attrs().test(Attr::ALLOCATABLE)) {
Say2(name,
"Derived type variable '%s' may not appear in a COMMON block"
" due to ALLOCATABLE component"_err_en_US,
component.name(), "Component with ALLOCATABLE attribute"_en_US);
return;
}
if (const auto *details{component.detailsIf<ObjectEntityDetails>()}) {
if (details->init()) {
Say2(name,
"Derived type variable '%s' may not appear in a COMMON block"
" due to component with default initialization"_err_en_US,
component.name(), "Component with default initialization"_en_US);
return;
}
if (const auto *type{details->type()}) {
if (const auto *derived{type->AsDerived()}) {
CheckCommonBlockDerivedType(name, derived->typeSymbol());
}
}
}
}
}
}
Symbol *DeclarationVisitor::DeclareLocalEntity(const parser::Name &name) {
auto *prev{FindSymbol(name)};
bool implicit{false};
@ -3627,15 +3767,6 @@ const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec(
// ResolveNamesVisitor implementation
bool ResolveNamesVisitor::Pre(const parser::CommonBlockObject &x) {
BeginArraySpec();
return true;
}
void ResolveNamesVisitor::Post(const parser::CommonBlockObject &x) {
ClearArraySpec();
// TODO: CommonBlockObject
}
bool ResolveNamesVisitor::Pre(const parser::PrefixSpec &x) {
return true; // TODO
}
@ -4005,6 +4136,7 @@ void ResolveNamesVisitor::Post(const parser::SpecificationPart &) {
symbol.set(Symbol::Flag::Subroutine);
}
}
CheckCommonBlocks();
}
void ResolveNamesVisitor::CheckImports() {

View File

@ -153,6 +153,7 @@ std::string DetailsToString(const Details &details) {
[](const ProcBindingDetails &) { return "ProcBinding"; },
[](const GenericBindingDetails &) { return "GenericBinding"; },
[](const NamelistDetails &) { return "Namelist"; },
[](const CommonBlockDetails &) { return "CommonBlockDetails"; },
[](const FinalProcDetails &) { return "FinalProc"; },
[](const TypeParamDetails &) { return "TypeParam"; },
[](const MiscDetails &) { return "Misc"; },
@ -388,6 +389,12 @@ std::ostream &operator<<(std::ostream &os, const Details &details) {
os << ' ' << object->name();
}
},
[&](const CommonBlockDetails &x) {
os << ": ";
for (const auto *object : x.objects()) {
os << ' ' << object->name();
}
},
[&](const FinalProcDetails &) {},
[&](const TypeParamDetails &x) {
if (x.type()) {

View File

@ -258,6 +258,15 @@ private:
SymbolList objects_;
};
class CommonBlockDetails {
public:
SymbolList objects() const { return objects_; }
void add_object(Symbol &object) { objects_.push_back(&object); }
private:
SymbolList objects_;
};
class FinalProcDetails {};
class MiscDetails {
@ -366,7 +375,7 @@ using Details = std::variant<UnknownDetails, MainProgramDetails, ModuleDetails,
ObjectEntityDetails, ProcEntityDetails, AssocEntityDetails,
DerivedTypeDetails, UseDetails, UseErrorDetails, HostAssocDetails,
GenericDetails, ProcBindingDetails, GenericBindingDetails, NamelistDetails,
FinalProcDetails, TypeParamDetails, MiscDetails>;
CommonBlockDetails, FinalProcDetails, TypeParamDetails, MiscDetails>;
std::ostream &operator<<(std::ostream &, const Details &);
std::string DetailsToString(const Details &);

View File

@ -194,6 +194,27 @@ std::ostream &operator<<(std::ostream &o, const ShapeSpec &x) {
return o;
}
std::ostream &operator<<(std::ostream &os, const ArraySpec &arraySpec) {
char sep{'('};
for (auto &shape : arraySpec) {
os << sep << shape;
sep = ',';
}
if (sep == ',') {
os << ')';
}
return os;
}
bool IsExplicit(const ArraySpec &arraySpec) {
for (const auto &shapeSpec : arraySpec) {
if (!shapeSpec.isExplicit()) {
return false;
}
}
return true;
}
ParamValue::ParamValue(MaybeIntExpr &&expr) : expr_{std::move(expr)} {}
ParamValue::ParamValue(SomeIntExpr &&expr) : expr_{std::move(expr)} {}
ParamValue::ParamValue(std::int64_t value)

View File

@ -212,6 +212,7 @@ private:
};
using ArraySpec = std::list<ShapeSpec>;
bool IsExplicit(const ArraySpec &);
class DerivedTypeSpec {
public:

View File

@ -67,6 +67,7 @@ set(ERROR_TESTS
resolve39.f90
resolve40.f90
resolve41.f90
resolve42.f90
)
# These test files have expected symbols in the source
@ -108,6 +109,7 @@ set(MODFILE_TESTS
modfile18.f90
modfile19.f90
modfile20.f90
modfile21.f90
)
set(LABEL_TESTS

View File

@ -0,0 +1,41 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
module m
logical b
common //t
common /cb/ x(2:10) /cb2/a,b,c
common /cb/ y,z
common w
common u,v
complex w
dimension b(4,4)
end
!Expect: m.mod
!module m
! logical(4)::b(1_8:4_8,1_8:4_8)
! common//t,w,u,v
! real(4)::t
! common/cb/x,y,z
! real(4)::x(2_8:10_8)
! common/cb2/a,b,c
! real(4)::a
! real(4)::c
! real(4)::y
! real(4)::z
! complex(4)::w
! real(4)::u
! real(4)::v
!end

View File

@ -0,0 +1,119 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
subroutine s1
!ERROR: The shape of common block object 'z' must be explicit
common x, y(4), z(:)
end
subroutine s2
common /c1/ x, y, z
!ERROR: 'y' is already in a COMMON block
common y
end
subroutine s3
procedure(real) :: x
!ERROR: 'x' is already declared as a procedure
common x
common y
!ERROR: 'y' is already declared as an object
procedure(real) :: y
end
subroutine s4
integer x
!ERROR: 'x' is already declared in this scoping unit
common /x/ y
!ERROR: 's4' is already declared in this scoping unit
common /s4/ z
end
subroutine s5
integer x(2)
!ERROR: The dimensions of 'x' have already been declared
common x(4), y(4)
!ERROR: The dimensions of 'y' have already been declared
real y(2)
end
subroutine s6(x)
!ERROR: Dummy argument 'x' may not appear in a COMMON block
!ERROR: ALLOCATABLE object 'y' may not appear in a COMMON block
common x,y,z
allocatable y
end
module m7
!ERROR: Variable 'z' with BIND attribute may not appear in a COMMON block
common z
integer, bind(c) :: z
end
module m8
type t
end type
class(*), pointer :: x
!ERROR: Unlimited polymorphic pointer 'x' may not appear in a COMMON block
!ERROR: Unlimited polymorphic pointer 'y' may not appear in a COMMON block
common x, y
class(*), pointer :: y
end
module m9
integer x
end
subroutine s9
use m9
!ERROR: 'x' is use-associated from module 'm9' and cannot be re-declared
common x
end
module m10
type t
end type
type(t) :: x
!ERROR: Derived type 'x' in COMMON block must have the BIND or SEQUENCE attribute
common x
end
module m11
type t1
sequence
integer, allocatable :: a
end type
type t2
sequence
type(t1) :: b
integer:: c
end type
type(t2) :: x2
!ERROR: Derived type variable 'x2' may not appear in a COMMON block due to ALLOCATABLE component
common x2
end
module m12
type t1
sequence
integer :: a = 123
end type
type t2
sequence
type(t1) :: b
integer:: c
end type
type(t2) :: x2
!ERROR: Derived type variable 'x2' may not appear in a COMMON block due to component with default initialization
common x2
end

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -47,7 +47,11 @@ fi
# $actual has errors from the compiler; $expect has them from !ERROR comments in source
# Format both as "<line>: <text>" so they can be diffed.
sed -n 's=^[^:]*:\([^:]*\):[^:]*: error: =\1: =p' $log > $actual
{ echo; cat $src; } | cat -n | sed -n 's=^ *\([0-9]*\). *\!ERROR: *=\1: =p' > $expect
awk '
BEGIN { FS = "!ERROR: "; }
/^ *!ERROR: / { errors[nerrors++] = $2; next; }
{ for (i = 0; i < nerrors; ++i) printf "%d: %s\n", NR, errors[i]; nerrors = 0; }
' $src > $expect
if diff -U0 $actual $expect > $diffs; then
echo PASS