diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -386,6 +386,8 @@ public: HostAssocDetails(const Symbol &symbol) : symbol_{symbol} {} const Symbol &symbol() const { return symbol_; } + bool implicitOrSpecExprError{false}; + bool implicitOrExplicitTypeError{false}; private: SymbolRef symbol_; @@ -481,6 +483,7 @@ Subroutine, // symbol is a subroutine StmtFunction, // symbol is a statement function (Function is set too) Implicit, // symbol is implicitly typed + ImplicitOrError, // symbol must be implicitly defined or it's an error ModFile, // symbol came from .mod file ParentComp, // symbol is the "parent component" of an extended type CrayPointer, CrayPointee, @@ -488,14 +491,13 @@ LocalityLocalInit, // named in LOCAL_INIT locality-spec LocalityShared, // named in SHARED locality-spec InDataStmt, // initialized in a DATA statement - + InCommonBlock, // appears as a common block object // OpenACC data-sharing attribute AccPrivate, AccFirstPrivate, AccShared, // OpenACC data-mapping attribute AccCopyIn, AccCopyOut, AccCreate, AccDelete, AccPresent, // OpenACC miscellaneous flags AccCommonBlock, AccThreadPrivate, AccReduction, AccNone, AccPreDetermined, - // OpenMP data-sharing attribute OmpShared, OmpPrivate, OmpLinear, OmpFirstPrivate, OmpLastPrivate, // OpenMP data-mapping attribute diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -61,6 +61,7 @@ void CheckSubprogram(const Symbol &, const SubprogramDetails &); void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &); void CheckDerivedType(const Symbol &, const DerivedTypeDetails &); + void CheckHostAssoc(const Symbol &, const HostAssocDetails &); void CheckGeneric(const Symbol &, const GenericDetails &); std::optional> Characterize(const SymbolVector &); bool CheckDefinedOperator(const SourceName &, const GenericKind &, @@ -147,7 +148,10 @@ CheckVolatile(symbol, isAssociated, derived); } if (isAssociated) { - return; // only care about checking VOLATILE on associated symbols + if (const auto *details{symbol.detailsIf()}) { + CheckHostAssoc(symbol, *details); + } + return; // no other checks on associated symbols } if (IsPointer(symbol)) { CheckPointer(symbol); @@ -758,6 +762,21 @@ } } +void CheckHelper::CheckHostAssoc( + const Symbol &symbol, const HostAssocDetails &details) { + const Symbol &hostSymbol{details.symbol()}; + if (hostSymbol.test(Symbol::Flag::ImplicitOrError)) { + if (details.implicitOrSpecExprError) { + messages_.Say("Implicitly declared local '%s' not allowed in" + " specification expression"_err_en_US, + symbol.name()); + } else if (details.implicitOrExplicitTypeError) { + messages_.Say( + "No explicit type declared for '%s'"_err_en_US, symbol.name()); + } + } +} + void CheckHelper::CheckGeneric( const Symbol &symbol, const GenericDetails &details) { const SymbolVector &specifics{details.specificProcs()}; diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -681,7 +681,10 @@ if (std::optional kind{IsImpliedDo(n.source)}) { return AsMaybeExpr(ConvertToKind( *kind, AsExpr(ImpliedDoIndex{n.source}))); - } else if (context_.HasError(n) || !n.symbol) { + } else if (context_.HasError(n)) { + return std::nullopt; + } else if (!n.symbol) { + SayAt(n, "Internal error: unresolved name '%s'"_err_en_US, n.source); return std::nullopt; } else { const Symbol &ultimate{n.symbol->GetUltimate()}; diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -68,8 +68,8 @@ void set_isImplicitNoneType(bool x) { isImplicitNoneType_ = x; } void set_isImplicitNoneExternal(bool x) { isImplicitNoneExternal_ = x; } void set_inheritFromParent(bool x) { inheritFromParent_ = x; } - // Get the implicit type for identifiers starting with ch. May be null. - const DeclTypeSpec *GetType(char ch) const; + // Get the implicit type for this name. May be null. + const DeclTypeSpec *GetType(SourceName) const; // Record the implicit type for the range of characters [fromLetter, // toLetter]. void SetTypeMapping(const DeclTypeSpec &type, parser::Location fromLetter, @@ -385,13 +385,17 @@ bool Pre(const parser::ImplicitSpec &); void Post(const parser::ImplicitSpec &); - ImplicitRules &implicitRules() { return *implicitRules_; } - const ImplicitRules &implicitRules() const { return *implicitRules_; } + const DeclTypeSpec *GetType(SourceName name) { + return implicitRules_->GetType(name); + } bool isImplicitNoneType() const { - return implicitRules().isImplicitNoneType(); + return implicitRules_->isImplicitNoneType(); } bool isImplicitNoneExternal() const { - return implicitRules().isImplicitNoneExternal(); + return implicitRules_->isImplicitNoneExternal(); + } + void set_inheritFromParent(bool x) { + implicitRules_->set_inheritFromParent(x); } protected: @@ -452,6 +456,8 @@ using ImplicitRulesVisitor::Pre; Scope &currScope() { return DEREF(currScope_); } + // The enclosing host procedure if current scope is in an internal procedure + Scope *GetHostProcedure(); // The enclosing scope, skipping blocks and derived types. // TODO: Will return the scope of a FORALL or implied DO loop; is this ok? // If not, should call FindProgramUnitContaining() instead. @@ -583,6 +589,8 @@ const DeclTypeSpec &MakeLogicalType( const std::optional &); + bool inExecutionPart_{false}; + private: Scope *currScope_{nullptr}; }; @@ -689,7 +697,6 @@ protected: // Set when we see a stmt function that is really an array element assignment bool badStmtFuncFound_{false}; - bool inExecutionPart_{false}; private: // Info about the current function: parse tree of the type in the PrefixSpec; @@ -820,7 +827,7 @@ protected: bool BeginDecl(); void EndDecl(); - Symbol &DeclareObjectEntity(const parser::Name &, Attrs); + Symbol &DeclareObjectEntity(const parser::Name &, Attrs = Attrs{}); // Make sure that there's an entity in an enclosing scope called Name Symbol &FindOrDeclareEnclosingEntity(const parser::Name &); // Declare a LOCAL/LOCAL_INIT entity. If there isn't a type specified @@ -918,22 +925,25 @@ void Initialization(const parser::Name &, const parser::Initialization &, bool inComponentDecl); bool PassesLocalityChecks(const parser::Name &name, Symbol &symbol); + bool CheckForHostAssociatedImplicit(const parser::Name &); // Declare an object or procedure entity. // T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails template Symbol &DeclareEntity(const parser::Name &name, Attrs attrs) { Symbol &symbol{MakeSymbol(name, attrs)}; - if (symbol.has()) { - // OK + if (context().HasError(symbol) || symbol.has()) { + return symbol; // OK or error already reported } else if (symbol.has()) { symbol.set_details(T{}); + return symbol; } else if (auto *details{symbol.detailsIf()}) { symbol.set_details(T{std::move(*details)}); + return symbol; } else if (std::is_same_v && (symbol.has() || symbol.has())) { - // OK + return symbol; // OK } else if (auto *details{symbol.detailsIf()}) { Say(name.source, "'%s' is use-associated from module '%s' and cannot be re-declared"_err_en_US, @@ -956,11 +966,17 @@ name, symbol, "'%s' is already declared as a procedure"_err_en_US); } else if (std::is_same_v && symbol.has()) { - SayWithDecl( - name, symbol, "'%s' is already declared as an object"_err_en_US); + if (symbol.test(Symbol::Flag::InCommonBlock)) { + SayWithDecl(name, symbol, + "'%s' may not be a procedure as it is in a COMMON block"_err_en_US); + } else { + SayWithDecl( + name, symbol, "'%s' is already declared as an object"_err_en_US); + } } else { SayAlreadyDeclared(name, symbol); } + context().SetError(symbol); return symbol; } }; @@ -1372,13 +1388,14 @@ } } -const DeclTypeSpec *ImplicitRules::GetType(char ch) const { +const DeclTypeSpec *ImplicitRules::GetType(SourceName name) const { + char ch{name.begin()[0]}; if (isImplicitNoneType_) { return nullptr; } else if (auto it{map_.find(ch)}; it != map_.end()) { return &*it->second; } else if (inheritFromParent_) { - return parent_->GetType(ch); + return parent_->GetType(name); } else if (ch >= 'i' && ch <= 'n') { return &context_.MakeNumericType(TypeCategory::Integer); } else if (ch >= 'a' && ch <= 'z') { @@ -1684,7 +1701,7 @@ "IMPLICIT NONE(TYPE) statement"_err_en_US); return false; } - implicitRules().set_isImplicitNoneType(false); + implicitRules_->set_isImplicitNoneType(false); return true; }, }, @@ -1704,7 +1721,7 @@ return false; } } - implicitRules().SetTypeMapping(*GetDeclTypeSpec(), loLoc, hiLoc); + implicitRules_->SetTypeMapping(*GetDeclTypeSpec(), loLoc, hiLoc); return false; } @@ -1749,7 +1766,7 @@ if (nameSpecs.empty()) { if (!implicitNoneTypeNever) { prevImplicitNoneType_ = currStmtSource(); - implicitRules().set_isImplicitNoneType(true); + implicitRules_->set_isImplicitNoneType(true); if (prevImplicit_) { Say("IMPLICIT NONE statement after IMPLICIT statement"_err_en_US); return false; @@ -1761,13 +1778,13 @@ for (const auto noneSpec : nameSpecs) { switch (noneSpec) { case ImplicitNoneNameSpec::External: - implicitRules().set_isImplicitNoneExternal(true); + implicitRules_->set_isImplicitNoneExternal(true); ++sawExternal; break; case ImplicitNoneNameSpec::Type: if (!implicitNoneTypeNever) { prevImplicitNoneType_ = currStmtSource(); - implicitRules().set_isImplicitNoneType(true); + implicitRules_->set_isImplicitNoneType(true); if (prevImplicit_) { Say("IMPLICIT NONE(TYPE) after IMPLICIT statement"_err_en_US); return false; @@ -1915,16 +1932,24 @@ context().SetError(symbol, msg1.isFatal()); } -Scope &ScopeHandler::InclusiveScope() { - for (auto *scope{&currScope()};; scope = &scope->parent()) { - if (scope->kind() != Scope::Kind::Block && !scope->IsDerivedType() && - !scope->IsStmtFunction()) { - return *scope; +// T may be `Scope` or `const Scope` +template static T &GetInclusiveScope(T &scope) { + for (T *s{&scope};; s = &s->parent()) { + if (s->kind() != Scope::Kind::Block && !s->IsDerivedType() && + !s->IsStmtFunction()) { + return *s; } } DIE("inclusive scope not found"); } +Scope &ScopeHandler::InclusiveScope() { return GetInclusiveScope(currScope()); } + +Scope *ScopeHandler::GetHostProcedure() { + Scope &parent{InclusiveScope().parent()}; + return parent.kind() == Scope::Kind::Subprogram ? &parent : nullptr; +} + Scope &ScopeHandler::NonDerivedTypeScope() { return currScope_->IsDerivedType() ? currScope_->parent() : *currScope_; } @@ -2082,7 +2107,8 @@ } const DeclTypeSpec *ScopeHandler::GetImplicitType(Symbol &symbol) { - const DeclTypeSpec *type{implicitRules().GetType(symbol.name().begin()[0])}; + const auto *type{implicitRulesMap_->at(&GetInclusiveScope(symbol.owner())) + .GetType(symbol.name())}; if (type) { if (const DerivedTypeSpec * derived{type->AsDerived()}) { // Resolve any forward-referenced derived type; a quick no-op else. @@ -2992,7 +3018,7 @@ if (isGeneric()) { GetGenericDetails().AddSpecificProc(*symbol, name.source); } - implicitRules().set_inheritFromParent(false); + set_inheritFromParent(false); } FindSymbol(name)->set(subpFlag); // PushScope() created symbol return *symbol; @@ -3098,12 +3124,10 @@ } void DeclarationVisitor::Post(const parser::DimensionStmt::Declaration &x) { - const auto &name{std::get(x.t)}; - DeclareObjectEntity(name, Attrs{}); + DeclareObjectEntity(std::get(x.t)); } void DeclarationVisitor::Post(const parser::CodimensionDecl &x) { - const auto &name{std::get(x.t)}; - DeclareObjectEntity(name, Attrs{}); + DeclareObjectEntity(std::get(x.t)); } bool DeclarationVisitor::Pre(const parser::Initialization &) { @@ -4232,7 +4256,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) { CHECK(commonBlockInfo_.curr); const auto &name{std::get(x.t)}; - auto &symbol{DeclareObjectEntity(name, Attrs{})}; + Symbol &symbol{DeclareObjectEntity(name)}; ClearArraySpec(); ClearCoarraySpec(); auto *details{symbol.detailsIf()}; @@ -4823,8 +4847,7 @@ } name.symbol = nullptr; } - auto &symbol{DeclareObjectEntity(name, {})}; - + auto &symbol{DeclareObjectEntity(name)}; if (symbol.GetType()) { // type came from explicit type-spec } else if (!prev) { @@ -5418,10 +5441,15 @@ // If implicit types are allowed, ensure name is in the symbol table. // Otherwise, report an error if it hasn't been declared. const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) { - if (Symbol * symbol{FindSymbol(name)}) { + FindSymbol(name); + if (CheckForHostAssociatedImplicit(name)) { + return &name; + } + if (Symbol * symbol{name.symbol}) { if (CheckUseError(name)) { return nullptr; // reported an error } + symbol->set(Symbol::Flag::ImplicitOrError, false); if (IsUplevelReference(*symbol)) { MakeHostAssocSymbol(name, *symbol); } else if (IsDummy(*symbol) || @@ -5448,6 +5476,44 @@ return &name; } +// A specification expression may refer to a symbol in the host procedure that +// is implicitly typed. Because specification parts are processed before +// execution parts, this may be the first time we see the symbol. It can't be a +// local in the current scope (because it's in a specification expression) so +// either it is implicitly declared in the host procedure or it is an error. +// We create a symbol in the host assuming it is the former; if that proves to +// be wrong we report an error later in CheckDeclarations(). +bool DeclarationVisitor::CheckForHostAssociatedImplicit( + const parser::Name &name) { + if (inExecutionPart_) { + return false; + } + if (name.symbol) { + ApplyImplicitRules(*name.symbol); + } + Symbol *hostSymbol; + Scope *host{GetHostProcedure()}; + if (!host) { + return false; + } else if (!name.symbol) { + hostSymbol = &MakeSymbol(*host, name.source, Attrs{}); + ConvertToObjectEntity(*hostSymbol); + ApplyImplicitRules(*hostSymbol); + hostSymbol->set(Symbol::Flag::ImplicitOrError); + } else if (name.symbol->test(Symbol::Flag::ImplicitOrError)) { + hostSymbol = name.symbol; + } else { + return false; + } + Symbol &symbol{MakeHostAssocSymbol(name, *hostSymbol)}; + if (isImplicitNoneType()) { + symbol.get().implicitOrExplicitTypeError = true; + } else { + symbol.get().implicitOrSpecExprError = true; + } + return true; +} + bool DeclarationVisitor::IsUplevelReference(const Symbol &symbol) { const Scope *symbolUnit{FindProgramUnitContaining(symbol)}; if (symbolUnit == FindProgramUnitContaining(currScope())) { @@ -5896,13 +5962,14 @@ } bool ResolveNamesVisitor::Pre(const parser::SpecificationPart &x) { - Walk(std::get<0>(x.t)); - Walk(std::get<1>(x.t)); - Walk(std::get<2>(x.t)); - Walk(std::get<3>(x.t)); - Walk(std::get<4>(x.t)); - Walk(std::get<5>(x.t)); - const std::list &decls{std::get<6>(x.t)}; + const auto &[accDecls, ompDecls, compilerDirectives, useStmts, importStmts, + implicitPart, decls] = x.t; + Walk(accDecls); + Walk(ompDecls); + Walk(compilerDirectives); + Walk(useStmts); + Walk(importStmts); + Walk(implicitPart); for (const auto &decl : decls) { if (const auto *spec{ std::get_if(&decl.u)}) { @@ -5919,7 +5986,6 @@ const parser::SpecificationConstruct &spec) { std::visit( common::visitors{ - [&](const Indirection &) {}, [&](const parser::Statement> &y) { CreateGeneric(std::get(y.statement.value().t)); }, @@ -5932,6 +5998,18 @@ CreateGeneric(**spec); } }, + [&](const parser::Statement &y) { + if (const auto *commonStmt{ + parser::Unwrap(y.statement)}) { + for (const auto &block : commonStmt->blocks) { + for (const auto &object : + std::get>(block.t)) { + DeclareObjectEntity(std::get(object.t)) + .set(Symbol::Flag::InCommonBlock); + } + } + } + }, [&](const auto &) {}, }, spec.u); diff --git a/flang/test/Semantics/block-data01.f90 b/flang/test/Semantics/block-data01.f90 --- a/flang/test/Semantics/block-data01.f90 +++ b/flang/test/Semantics/block-data01.f90 @@ -7,9 +7,8 @@ !ERROR: An initialized variable in BLOCK DATA must be in a COMMON block integer :: notInCommon = 1 integer :: uninitialized ! ok - !ERROR: 'p' may not appear in a BLOCK DATA subprogram + !ERROR: 'p' may not be a procedure as it is in a COMMON block procedure(sin), pointer :: p => cos - !ERROR: 'p' is already declared as a procedure common /block/ pi, p !ERROR: An initialized variable in BLOCK DATA must be in a COMMON block integer :: inDataButNotCommon diff --git a/flang/test/Semantics/implicit11.f90 b/flang/test/Semantics/implicit11.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/implicit11.f90 @@ -0,0 +1,52 @@ +! RUN: %S/test_errors.sh %s %t %f18 + +! Test use of implicitly declared variable in specification expression + +subroutine s1() + m = 1 +contains + subroutine s1a() + implicit none + !ERROR: No explicit type declared for 'n' + real :: a(m, n) + end + subroutine s1b() + !ERROR: Implicitly declared local 'n' not allowed in specification expression + real :: a(m, n) + end +end + +subroutine s2() + type :: t(m, n) + integer, len :: m + integer, len :: n + end type + n = 1 +contains + subroutine s2a() + !ERROR: Implicitly declared local 'm' not allowed in specification expression + type(t(m, n)) :: a + end + subroutine s2b() + implicit none + !ERROR: No explicit type declared for 'm' + character(m) :: a + end +end + +subroutine s3() + m = 1 +contains + subroutine s3a() + implicit none + real :: a(m, n) + !ERROR: No explicit type declared for 'n' + common n + end + subroutine s3b() + ! n is okay here because it is in a common block + real :: a(m, n) + common n + end +end + diff --git a/flang/test/Semantics/offsets03.f90 b/flang/test/Semantics/offsets03.f90 --- a/flang/test/Semantics/offsets03.f90 +++ b/flang/test/Semantics/offsets03.f90 @@ -30,10 +30,10 @@ ! Common block: objects are in order from COMMON statement and not part of module module md !CHECK: Module scope: md size=1 alignment=1 integer(1) :: i - integer(2) :: d1 !CHECK: d1, PUBLIC size=2 offset=8: - integer(4) :: d2 !CHECK: d2, PUBLIC size=4 offset=4: - integer(1) :: d3 !CHECK: d3, PUBLIC size=1 offset=0: - real(2) :: d4 !CHECK: d4, PUBLIC size=2 offset=0: + integer(2) :: d1 !CHECK: d1, PUBLIC (InCommonBlock) size=2 offset=8: + integer(4) :: d2 !CHECK: d2, PUBLIC (InCommonBlock) size=4 offset=4: + integer(1) :: d3 !CHECK: d3, PUBLIC (InCommonBlock) size=1 offset=0: + real(2) :: d4 !CHECK: d4, PUBLIC (InCommonBlock) size=2 offset=0: common /common1/ d3,d2,d1 !CHECK: common1 size=10 offset=0: CommonBlockDetails alignment=4: common /common2/ d4 !CHECK: common2 size=2 offset=0: CommonBlockDetails alignment=2: end diff --git a/flang/test/Semantics/omp-symbol01.f90 b/flang/test/Semantics/omp-symbol01.f90 --- a/flang/test/Semantics/omp-symbol01.f90 +++ b/flang/test/Semantics/omp-symbol01.f90 @@ -21,8 +21,8 @@ !REF: /md use :: md !DEF: /mm/c CommonBlockDetails - !DEF: /mm/x ObjectEntity REAL(4) - !DEF: /mm/y ObjectEntity REAL(4) + !DEF: /mm/x (InCommonBlock) ObjectEntity REAL(4) + !DEF: /mm/y (InCommonBlock) ObjectEntity REAL(4) common /c/x, y !REF: /mm/x !REF: /mm/y diff --git a/flang/test/Semantics/resolve42.f90 b/flang/test/Semantics/resolve42.f90 --- a/flang/test/Semantics/resolve42.f90 +++ b/flang/test/Semantics/resolve42.f90 @@ -11,11 +11,11 @@ end subroutine s3 + !ERROR: 'x' may not be a procedure as it is in a COMMON block procedure(real) :: x - !ERROR: 'x' is already declared as a procedure common x common y - !ERROR: 'y' is already declared as an object + !ERROR: 'y' may not be a procedure as it is in a COMMON block procedure(real) :: y end