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 @@ -599,7 +599,26 @@ bool inExecutionPart_{false}; bool inSpecificationPart_{false}; bool inEquivalenceStmt_{false}; - std::set specPartForwardRefs_; + + // Some information is collected from a specification part for deferred + // processing in DeclarationPartVisitor functions (e.g., CheckSaveStmts()) + // that are called by ResolveNamesVisitor::FinishSpecificationPart(). Since + // specification parts can nest (e.g., INTERFACE bodies), the collected + // information that is not contained in the scope needs to be packaged + // and restorable. + struct SpecificationPartState { + std::set forwardRefs; + // Collect equivalence sets and process at end of specification part + std::vector *> equivalenceSets; + // Names of all common block objects in the scope + std::set commonBlockObjects; + // Info about about SAVE statements and attributes in current scope + struct { + std::optional saveAll; // "SAVE" without entity list + std::set entities; // names of entities with save attr + std::set commons; // names of common blocks with save attr + } saveInfo; + } specPartState_; private: Scope *currScope_{nullptr}; @@ -888,16 +907,6 @@ bool sequence{false}; // is a sequence type const Symbol *type{nullptr}; // derived type being defined } derivedTypeInfo_; - // Collect equivalence sets and process at end of specification part - std::vector *> equivalenceSets_; - // Names of all common block objects in the scope - std::set commonBlockObjects_; - // Info about about SAVE statements and attributes in current scope - struct { - std::optional saveAll; // "SAVE" without entity list - std::set entities; // names of entities with save attr - std::set commons; // names of common blocks with save attr - } saveInfo_; // In a ProcedureDeclarationStmt or ProcComponentDefStmt, this is // the interface name, if any. const parser::Name *interfaceName_{nullptr}; @@ -2223,7 +2232,7 @@ ? name.symbol->has() : name.symbol->owner().Contains(currScope())}; if (isHostAssociated) { - specPartForwardRefs_.insert(name.source); + specPartState_.forwardRefs.insert(name.source); } } } @@ -2231,8 +2240,8 @@ std::optional ScopeHandler::HadForwardRef( const Symbol &symbol) const { - auto iter{specPartForwardRefs_.find(symbol.name())}; - if (iter != specPartForwardRefs_.end()) { + auto iter{specPartState_.forwardRefs.find(symbol.name())}; + if (iter != specPartState_.forwardRefs.end()) { return *iter; } return std::nullopt; @@ -4422,7 +4431,7 @@ void DeclarationVisitor::Post(const parser::CommonBlockObject &x) { const auto &name{std::get(x.t)}; DeclareObjectEntity(name); - auto pair{commonBlockObjects_.insert(name.source)}; + auto pair{specPartState_.commonBlockObjects.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, @@ -4434,7 +4443,7 @@ // save equivalence sets to be processed after specification part if (CheckNotInBlock("EQUIVALENCE")) { // C1107 for (const std::list &set : x.v) { - equivalenceSets_.push_back(&set); + specPartState_.equivalenceSets.push_back(&set); } } return false; // don't implicitly declare names yet @@ -4443,7 +4452,7 @@ void DeclarationVisitor::CheckEquivalenceSets() { EquivalenceSets equivSets{context()}; inEquivalenceStmt_ = true; - for (const auto *set : equivalenceSets_) { + for (const auto *set : specPartState_.equivalenceSets) { const auto &source{set->front().v.value().source}; if (set->size() <= 1) { // R871 Say(source, "Equivalence set must have more than one object"_err_en_US); @@ -4465,12 +4474,12 @@ currScope().add_equivalenceSet(std::move(set)); } } - equivalenceSets_.clear(); + specPartState_.equivalenceSets.clear(); } bool DeclarationVisitor::Pre(const parser::SaveStmt &x) { if (x.v.empty()) { - saveInfo_.saveAll = currStmtSource(); + specPartState_.saveInfo.saveAll = currStmtSource(); currScope().set_hasSAVE(); } else { for (const parser::SavedEntity &y : x.v) { @@ -4478,7 +4487,7 @@ const auto &name{std::get(y.t)}; if (kind == parser::SavedEntity::Kind::Common) { MakeCommonBlockSymbol(name); - AddSaveName(saveInfo_.commons, name.source); + AddSaveName(specPartState_.saveInfo.commons, name.source); } else { HandleAttributeStmt(Attr::SAVE, name); } @@ -4488,15 +4497,15 @@ } void DeclarationVisitor::CheckSaveStmts() { - for (const SourceName &name : saveInfo_.entities) { + for (const SourceName &name : specPartState_.saveInfo.entities) { auto *symbol{FindInScope(name)}; if (!symbol) { // error was reported - } else if (saveInfo_.saveAll) { + } else if (specPartState_.saveInfo.saveAll) { // C889 - note that pgi, ifort, xlf do not enforce this constraint Say2(name, "Explicit SAVE of '%s' is redundant due to global SAVE statement"_err_en_US, - *saveInfo_.saveAll, "Global SAVE statement"_en_US); + *specPartState_.saveInfo.saveAll, "Global SAVE statement"_en_US); } else if (auto msg{CheckSaveAttr(*symbol)}) { Say(name, std::move(*msg)); context().SetError(*symbol); @@ -4504,7 +4513,7 @@ SetSaveAttr(*symbol); } } - for (const SourceName &name : saveInfo_.commons) { + for (const SourceName &name : specPartState_.saveInfo.commons) { if (auto *symbol{currScope().FindCommonBlock(name)}) { auto &objects{symbol->get().objects()}; if (objects.empty()) { @@ -4524,7 +4533,7 @@ } } } - if (saveInfo_.saveAll) { + if (specPartState_.saveInfo.saveAll) { // Apply SAVE attribute to applicable symbols for (auto pair : currScope()) { auto &symbol{*pair.second}; @@ -4533,7 +4542,7 @@ } } } - saveInfo_ = {}; + specPartState_.saveInfo = {}; } // If SAVE attribute can't be set on symbol, return error message. @@ -4553,10 +4562,10 @@ } } -// Record SAVEd names in saveInfo_.entities. +// Record SAVEd names in specPartState_.saveInfo.entities. Attrs DeclarationVisitor::HandleSaveName(const SourceName &name, Attrs attrs) { if (attrs.test(Attr::SAVE)) { - AddSaveName(saveInfo_.entities, name); + AddSaveName(specPartState_.saveInfo.entities, name); } return attrs; } @@ -4591,7 +4600,7 @@ } } // check objects in common blocks - for (const auto &name : commonBlockObjects_) { + for (const auto &name : specPartState_.commonBlockObjects) { const auto *symbol{currScope().FindSymbol(name)}; if (!symbol) { continue; @@ -4625,7 +4634,7 @@ } } } - commonBlockObjects_ = {}; + specPartState_.commonBlockObjects = {}; } Symbol &DeclarationVisitor::MakeCommonBlockSymbol(const parser::Name &name) { @@ -6136,14 +6145,14 @@ const auto &[accDecls, ompDecls, compilerDirectives, useStmts, importStmts, implicitPart, decls] = x.t; auto flagRestorer{common::ScopedSet(inSpecificationPart_, true)}; + auto stateRestorer{ + common::ScopedSet(specPartState_, SpecificationPartState{})}; Walk(accDecls); Walk(ompDecls); Walk(compilerDirectives); Walk(useStmts); Walk(importStmts); Walk(implicitPart); - auto setRestorer{ - common::ScopedSet(specPartForwardRefs_, std::set{})}; for (const auto &decl : decls) { if (const auto *spec{ std::get_if(&decl.u)}) { diff --git a/flang/test/Semantics/resolve101.f90 b/flang/test/Semantics/resolve101.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/resolve101.f90 @@ -0,0 +1,13 @@ +! RUN: %S/test_errors.sh %s %t %f18 +! Ensure that spurious errors do not arise from FinishSpecificationPart +! checking on a nested specification part. +real, save :: x +interface + subroutine subr(x) + real, intent(in) :: x + ! SAVE attribute checking should not complain at the + ! end of this specification part about a dummy argument + ! having the SAVE attribute. + end subroutine +end interface +end