class Scope;
class Symbol;
+const Scope *FindModuleContaining(const Scope &);
const Symbol *FindCommonBlockContaining(const Symbol &object);
const Scope *FindProgramUnitContaining(const Scope &);
const Scope *FindProgramUnitContaining(const Symbol &);
const Symbol *IsExternalInPureContext(const Symbol &, const Scope &);
bool HasCoarray(const parser::Expr &);
bool IsPolymorphicAllocatable(const Symbol &);
+// Return an error if component symbol is not accessible from scope (7.5.4.8(2))
+std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
+ const semantics::Scope &, const Symbol &);
// Analysis of image control statements
bool IsImageControlStmt(const parser::ExecutableConstruct &);
}
}
if (symbol) {
+ if (const auto *currScope{context_.globalScope().FindScope(source)}) {
+ if (auto msg{CheckAccessibleComponent(*currScope, *symbol)}) {
+ Say(source, *msg);
+ }
+ }
if (checkConflicts) {
auto componentIter{
std::find(components.begin(), components.end(), *symbol)};
} else if (symbol->has<semantics::ObjectEntityDetails>()) {
// C1594(4)
const auto &innermost{context_.FindScope(expr.source)};
- if (const auto *pureProc{
- semantics::FindPureProcedureContaining(innermost)}) {
- if (const Symbol *
- pointer{semantics::FindPointerComponent(*symbol)}) {
+ if (const auto *pureProc{FindPureProcedureContaining(innermost)}) {
+ if (const Symbol * pointer{FindPointerComponent(*symbol)}) {
if (const Symbol *
- object{semantics::FindExternallyVisibleObject(
- *value, *pureProc)}) {
+ object{FindExternallyVisibleObject(*value, *pureProc)}) {
if (auto *msg{Say(expr.source,
"Externally visible object '%s' may not be "
"associated with pointer component '%s' in a "
bool SetPassNameOn(Symbol &);
bool SetBindNameOn(Symbol &);
void Post(const parser::LanguageBindingSpec &);
- bool Pre(const parser::AccessSpec &);
bool Pre(const parser::IntentSpec &);
bool Pre(const parser::Pass &);
Scope &currScope() { return DEREF(currScope_); }
// The enclosing scope, skipping blocks and derived types.
Scope &InclusiveScope();
- // The global scope, containing program units.
- Scope &GlobalScope();
// Create a new scope and push it on the scope stack.
void PushScope(Scope::Kind kind, Symbol *symbol);
bool Pre(const parser::NamedConstant &);
void Post(const parser::EnumDef &);
bool Pre(const parser::Enumerator &);
+ bool Pre(const parser::AccessSpec &);
bool Pre(const parser::AsynchronousStmt &);
bool Pre(const parser::ContiguousStmt &);
bool Pre(const parser::ExternalStmt &);
const parser::Name &, const std::optional<parser::IntegerTypeSpec> &);
bool CheckUseError(const parser::Name &);
void CheckAccessibility(const SourceName &, bool, Symbol &);
- bool CheckAccessibleComponent(const SourceName &, const Symbol &);
void CheckCommonBlocks();
void CheckSaveStmts();
void CheckEquivalenceSets();
bindName_ = EvaluateExpr(*x.v);
}
}
-bool AttrsVisitor::Pre(const parser::AccessSpec &x) {
- attrs_->set(AccessSpecToAttr(x));
- return false;
-}
bool AttrsVisitor::Pre(const parser::IntentSpec &x) {
CHECK(attrs_);
attrs_->set(IntentSpecToAttr(x));
return *scope;
}
}
- common::die("inclusive scope not found");
-}
-Scope &ScopeHandler::GlobalScope() {
- for (auto *scope = currScope_; scope; scope = &scope->parent()) {
- if (scope->IsGlobal()) {
- return *scope;
- }
- }
- common::die("global scope not found");
+ DIE("inclusive scope not found");
}
+
void ScopeHandler::PushScope(Scope::Kind kind, Symbol *symbol) {
PushScope(currScope().MakeScope(kind, symbol));
}
}
}
-// Check that component is accessible from current scope.
-bool DeclarationVisitor::CheckAccessibleComponent(
- const SourceName &name, const Symbol &symbol) {
- if (!symbol.attrs().test(Attr::PRIVATE)) {
- return true;
- }
- // component must be in a module/submodule because of PRIVATE:
- const Scope *moduleScope{&symbol.owner()};
- CHECK(moduleScope->IsDerivedType());
- while (
- moduleScope->kind() != Scope::Kind::Module && !moduleScope->IsGlobal()) {
- moduleScope = &moduleScope->parent();
- }
- if (moduleScope->kind() == Scope::Kind::Module) {
- for (auto *scope{&currScope()}; !scope->IsGlobal();
- scope = &scope->parent()) {
- if (scope == moduleScope) {
- return true;
- }
- }
- Say(name,
- "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
- name.ToString(), moduleScope->GetName().value());
- } else {
- Say(name,
- "PRIVATE component '%s' is only accessible within its module"_err_en_US,
- name.ToString());
- }
- return false;
-}
-
void DeclarationVisitor::Post(const parser::TypeDeclarationStmt &) {
if (!GetAttrs().HasAny({Attr::POINTER, Attr::ALLOCATABLE})) { // C702
if (const auto *typeSpec{GetDeclTypeSpec()}) {
enumerationState_ = EnumeratorState{};
}
+bool DeclarationVisitor::Pre(const parser::AccessSpec &x) {
+ Attr attr{AccessSpecToAttr(x)};
+ const Scope &scope{
+ currScope().IsDerivedType() ? currScope().parent() : currScope()};
+ if (!scope.IsModule()) { // C817
+ Say(currStmtSource().value(),
+ "%s attribute may only appear in the specification part of a module"_err_en_US,
+ EnumToString(attr));
+ }
+ attrs_->set(attr);
+ return false;
+}
+
bool DeclarationVisitor::Pre(const parser::AsynchronousStmt &x) {
return HandleAttributeStmt(Attr::ASYNCHRONOUS, x.v);
}
// we need to resolve its symbol in the scope of the derived type.
Walk(std::get<parser::ComponentDataSource>(component.t));
if (const auto &kw{std::get<std::optional<parser::Keyword>>(component.t)}) {
- if (Symbol * symbol{FindInTypeOrParents(*typeScope, kw->v)}) {
- if (!kw->v.symbol) {
- kw->v.symbol = symbol;
- }
- CheckAccessibleComponent(kw->v.source, *symbol);
- }
+ FindInTypeOrParents(*typeScope, kw->v);
}
}
return false;
} else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
if (const Scope * scope{derived->scope()}) {
if (Resolve(component, scope->FindComponent(component.source))) {
- if (CheckAccessibleComponent(component.source, *component.symbol)) {
- return &component;
+ if (auto msg{
+ CheckAccessibleComponent(currScope(), *component.symbol)}) {
+ context().Say(component.source, *msg);
}
+ return &component;
} else {
SayDerivedType(component.source,
"Component '%s' not found in derived type '%s'"_err_en_US, *scope);
bool ModuleVisitor::Pre(const parser::AccessStmt &x) {
Attr accessAttr{AccessSpecToAttr(std::get<parser::AccessSpec>(x.t))};
- if (currScope().kind() != Scope::Kind::Module) {
+ if (!currScope().IsModule()) { // C869
Say(currStmtSource().value(),
"%s statement may only appear in the specification part of a module"_err_en_US,
EnumToString(accessAttr));
}
const auto &accessIds{std::get<std::list<parser::AccessId>>(x.t)};
if (accessIds.empty()) {
- if (prevAccessStmt_) {
+ if (prevAccessStmt_) { // C869
Say("The default accessibility of this module has already been declared"_err_en_US)
.Attach(*prevAccessStmt_, "Previous declaration"_en_US);
}
return isSubmodule_ && scope_ ? &scope_->parent() : nullptr;
}
const Scope *ModuleDetails::ancestor() const {
- if (!isSubmodule_ || !scope_) {
- return nullptr;
- }
- for (auto *scope{scope_};;) {
- auto *parent{&scope->parent()};
- if (parent->kind() != Scope::Kind::Module) {
- return scope;
- }
- scope = parent;
- }
+ return isSubmodule_ && scope_ ? FindModuleContaining(*scope_) : nullptr;
}
void ModuleDetails::set_scope(const Scope *scope) {
CHECK(!scope_);
namespace Fortran::semantics {
+// Find this or containing scope that matches predicate
+static const Scope *FindScopeContaining(
+ const Scope &start, std::function<bool(const Scope &)> predicate) {
+ for (const Scope *scope{&start};; scope = &scope->parent()) {
+ if (predicate(*scope)) {
+ return scope;
+ }
+ if (scope->IsGlobal()) {
+ return nullptr;
+ }
+ }
+}
+
+const Scope *FindModuleContaining(const Scope &start) {
+ return FindScopeContaining(
+ start, [](const Scope &scope) { return scope.IsModule(); });
+}
+
const Symbol *FindCommonBlockContaining(const Symbol &object) {
if (const auto *details{object.detailsIf<ObjectEntityDetails>()}) {
return details->commonBlock();
}
const Scope *FindProgramUnitContaining(const Scope &start) {
- const Scope *scope{&start};
- while (scope) {
- switch (scope->kind()) {
+ return FindScopeContaining(start, [](const Scope &scope) {
+ switch (scope.kind()) {
case Scope::Kind::Module:
case Scope::Kind::MainProgram:
case Scope::Kind::Subprogram:
- case Scope::Kind::BlockData: return scope;
- case Scope::Kind::Global: return nullptr;
- case Scope::Kind::DerivedType:
- case Scope::Kind::Block:
- case Scope::Kind::Forall:
- case Scope::Kind::ImpliedDos: scope = &scope->parent();
+ case Scope::Kind::BlockData: return true;
+ default: return false;
}
- }
- return nullptr;
+ });
}
const Scope *FindProgramUnitContaining(const Symbol &symbol) {
bool DoesScopeContain(
const Scope *maybeAncestor, const Scope &maybeDescendent) {
- if (maybeAncestor) {
- const Scope *scope{&maybeDescendent};
- while (!scope->IsGlobal()) {
- scope = &scope->parent();
- if (scope == maybeAncestor) {
- return true;
- }
- }
- }
- return false;
+ return maybeAncestor && !maybeDescendent.IsGlobal() &&
+ FindScopeContaining(maybeDescendent.parent(),
+ [&](const Scope &scope) { return &scope == maybeAncestor; });
}
bool DoesScopeContain(const Scope *maybeAncestor, const Symbol &symbol) {
const Symbol *IsExternalInPureContext(
const Symbol &symbol, const Scope &scope) {
- if (const auto *pureProc{semantics::FindPureProcedureContaining(scope)}) {
+ if (const auto *pureProc{FindPureProcedureContaining(scope)}) {
if (const Symbol * root{GetAssociationRoot(symbol)}) {
if (const Symbol *
visible{FindExternallyVisibleObject(*root, *pureProc)}) {
return IsAllocatable(symbol) && IsPolymorphic(symbol);
}
+std::optional<parser::MessageFormattedText> CheckAccessibleComponent(
+ const Scope &scope, const Symbol &symbol) {
+ CHECK(symbol.owner().IsDerivedType()); // symbol must be a component
+ if (symbol.attrs().test(Attr::PRIVATE)) {
+ if (const Scope * moduleScope{FindModuleContaining(symbol.owner())}) {
+ if (!moduleScope->sourceRange().Contains(scope.sourceRange())) {
+ return parser::MessageFormattedText{
+ "PRIVATE component '%s' is only accessible within module '%s'"_err_en_US,
+ symbol.name(), moduleScope->GetName().value()};
+ }
+ }
+ }
+ return std::nullopt;
+}
+
std::list<SourceName> OrderParameterNames(const Symbol &typeSymbol) {
std::list<SourceName> result;
if (const DerivedTypeSpec * spec{typeSymbol.GetParentTypeSpec()}) {
}
bool IsFunctionResult(const Symbol &symbol) {
- return (symbol.has<semantics::ObjectEntityDetails>() &&
- symbol.get<semantics::ObjectEntityDetails>().isFuncResult()) ||
- (symbol.has<semantics::ProcEntityDetails>() &&
- symbol.get<semantics::ProcEntityDetails>().isFuncResult());
+ return (symbol.has<ObjectEntityDetails>() &&
+ symbol.get<ObjectEntityDetails>().isFuncResult()) ||
+ (symbol.has<ProcEntityDetails>() &&
+ symbol.get<ProcEntityDetails>().isFuncResult());
}
bool IsFunctionResultWithSameNameAsFunction(const Symbol &symbol) {
module m
public
+ type t
+ integer, private :: i
+ end type
!ERROR: The default accessibility of this module has already been declared
- private
+ private !C869
end
-subroutine s
+subroutine s1
!ERROR: PUBLIC statement may only appear in the specification part of a module
- public
+ public !C869
+end
+
+subroutine s2
+ !ERROR: PRIVATE attribute may only appear in the specification part of a module
+ integer, private :: i !C817
+end
+
+subroutine s3
+ type t
+ !ERROR: PUBLIC attribute may only appear in the specification part of a module
+ integer, public :: i !C817
+ end type
+end
+
+module m4
+ interface
+ module subroutine s()
+ end subroutine
+ end interface
+end
+submodule(m4) sm4
+ !ERROR: PUBLIC statement may only appear in the specification part of a module
+ public !C869
+ !ERROR: PUBLIC attribute may only appear in the specification part of a module
+ real, public :: x !C817
+ type :: t
+ !ERROR: PRIVATE attribute may only appear in the specification part of a module
+ real, private :: y !C817
+ end type
end
!ERROR: PRIVATE component 't1' is only accessible within module 'm7'
j = x%t1%i1
end
+
+! 7.5.4.8(2)
+module m8
+ type :: t
+ integer :: i1
+ integer, private :: i2
+ end type
+contains
+ subroutine s0
+ type(t) :: x
+ x = t(i1=2, i2=5) !OK
+ end
+end
+subroutine s8
+ use m8
+ type(t) :: x
+ !ERROR: PRIVATE component 'i2' is only accessible within module 'm8'
+ x = t(2, 5)
+ !ERROR: PRIVATE component 'i2' is only accessible within module 'm8'
+ x = t(i1=2, i2=5)
+end
+
+! 7.5.4.8(2)
+module m9
+ interface
+ module subroutine s()
+ end subroutine
+ end interface
+ type :: t
+ integer :: i1
+ integer, private :: i2
+ end type
+end
+submodule(m9) sm8
+contains
+ module subroutine s
+ type(t) :: x
+ x = t(i1=2, i2=5) !OK
+ end
+end