[flang] Distinguish intrinsic from non-intrinsic modules

For "USE, INTRINSIC", search only for intrinsic modules;
for "USE, NON_INTRINSIC", do not recognize intrinsic modules.
Allow modules of both kinds with the same name to be used in
the same source file (but not in the same scoping unit, a
constraint of the standard that is now enforced).

The symbol table's scope tree now has a single instance of
a scope with a new kind, IntrinsicModules, whose children are
the USE'd intrinsic modules (explicit or not).  This separate
"top-level" scope is a child of the single global scope and
it allows both intrinsic and non-intrinsic modules of the same
name to exist in the symbol table.  Intrinsic modules' scopes'
symbols now have the INTRINSIC attribute set.

The search path directories need to make a distinction between
regular directories and the one(s) that point(s) to intrinsic
modules.  I allow for multiple intrinsic module directories in
the second search path, although only one is needed today.

Differential Revision: https://reviews.llvm.org/D118631
This commit is contained in:
Peter Klausler 2022-01-26 09:54:58 -08:00
parent 8cb9c73609
commit 52a1346b78
17 changed files with 193 additions and 50 deletions

View File

@ -32,6 +32,7 @@ struct Options {
int fixedFormColumns{72};
common::LanguageFeatureControl features;
std::vector<std::string> searchDirectories;
std::vector<std::string> intrinsicModuleDirectories;
std::vector<Predefinition> predefinitions;
bool instrumentedParse{false};
bool isModuleFile{false};

View File

@ -149,6 +149,7 @@ public:
return *this;
}
void ClearSearchPath();
void AppendSearchPathDirectory(std::string); // new last directory
const SourceFile *Open(std::string path, llvm::raw_ostream &error,
std::optional<std::string> &&prependPath = std::nullopt);

View File

@ -59,8 +59,8 @@ class Scope {
using mapType = std::map<SourceName, MutableSymbolRef>;
public:
ENUM_CLASS(Kind, Global, Module, MainProgram, Subprogram, BlockData,
DerivedType, Block, Forall, ImpliedDos)
ENUM_CLASS(Kind, Global, IntrinsicModules, Module, MainProgram, Subprogram,
BlockData, DerivedType, Block, Forall, ImpliedDos)
using ImportKind = common::ImportKind;
// Create the Global scope -- the root of the scope tree
@ -87,6 +87,10 @@ public:
}
Kind kind() const { return kind_; }
bool IsGlobal() const { return kind_ == Kind::Global; }
bool IsIntrinsicModules() const { return kind_ == Kind::IntrinsicModules; }
bool IsTopLevel() const {
return kind_ == Kind::Global || kind_ == Kind::IntrinsicModules;
}
bool IsModule() const {
return kind_ == Kind::Module &&
!symbol_->get<ModuleDetails>().isSubmodule();

View File

@ -85,6 +85,9 @@ public:
const std::vector<std::string> &searchDirectories() const {
return searchDirectories_;
}
const std::vector<std::string> &intrinsicModuleDirectories() const {
return intrinsicModuleDirectories_;
}
const std::string &moduleDirectory() const { return moduleDirectory_; }
const std::string &moduleFileSuffix() const { return moduleFileSuffix_; }
bool warnOnNonstandardUsage() const { return warnOnNonstandardUsage_; }
@ -92,6 +95,7 @@ public:
bool debugModuleWriter() const { return debugModuleWriter_; }
const evaluate::IntrinsicProcTable &intrinsics() const { return intrinsics_; }
Scope &globalScope() { return globalScope_; }
Scope &intrinsicModulesScope() { return intrinsicModulesScope_; }
parser::Messages &messages() { return messages_; }
evaluate::FoldingContext &foldingContext() { return foldingContext_; }
parser::AllCookedSources &allCookedSources() { return allCookedSources_; }
@ -105,6 +109,11 @@ public:
searchDirectories_ = x;
return *this;
}
SemanticsContext &set_intrinsicModuleDirectories(
const std::vector<std::string> &x) {
intrinsicModuleDirectories_ = x;
return *this;
}
SemanticsContext &set_moduleDirectory(const std::string &x) {
moduleDirectory_ = x;
return *this;
@ -196,6 +205,7 @@ private:
parser::AllCookedSources &allCookedSources_;
std::optional<parser::CharBlock> location_;
std::vector<std::string> searchDirectories_;
std::vector<std::string> intrinsicModuleDirectories_;
std::string moduleDirectory_{"."s};
std::string moduleFileSuffix_{".mod"};
bool warnOnNonstandardUsage_{false};
@ -203,6 +213,7 @@ private:
bool debugModuleWriter_{false};
const evaluate::IntrinsicProcTable intrinsics_;
Scope globalScope_;
Scope &intrinsicModulesScope_;
parser::Messages messages_;
evaluate::FoldingContext foldingContext_;
ConstructStack constructStack_;

View File

@ -658,8 +658,8 @@ void CompilerInvocation::SetFortranOpts() {
preprocessorOptions.searchDirectoriesFromIntrModPath.begin(),
preprocessorOptions.searchDirectoriesFromIntrModPath.end());
// Add the default intrinsic module directory at the end
fortranOptions.searchDirectories.emplace_back(getIntrinsicDir());
// Add the default intrinsic module directory
fortranOptions.intrinsicModuleDirectories.emplace_back(getIntrinsicDir());
// Add the directory supplied through -J/-module-dir to the list of search
// directories
@ -686,6 +686,7 @@ void CompilerInvocation::SetSemanticsOpts(
semanticsContext_->set_moduleDirectory(moduleDir())
.set_searchDirectories(fortranOptions.searchDirectories)
.set_intrinsicModuleDirectories(fortranOptions.intrinsicModuleDirectories)
.set_warnOnNonstandardUsage(enableConformanceChecks())
.set_warningsAreErrors(warnAsErr())
.set_moduleFileSuffix(moduleFileSuffix());

View File

@ -22,7 +22,7 @@
// recursively build the vector of module scopes
static void moduleNames(const Fortran::semantics::Scope &scope,
llvm::SmallVector<llvm::StringRef, 2> &result) {
if (scope.kind() == Fortran::semantics::Scope::Kind::Global) {
if (scope.IsTopLevel()) {
return;
}
moduleNames(scope.parent(), result);

View File

@ -23,6 +23,7 @@ Parsing::~Parsing() {}
const SourceFile *Parsing::Prescan(const std::string &path, Options options) {
options_ = options;
AllSources &allSources{allCooked_.allSources()};
allSources.ClearSearchPath();
if (options.isModuleFile) {
for (const auto &path : options.searchDirectories) {
allSources.AppendSearchPathDirectory(path);

View File

@ -159,6 +159,8 @@ const char &AllSources::operator[](Provenance at) const {
return origin[origin.covers.MemberOffset(at)];
}
void AllSources::ClearSearchPath() { searchPath_.clear(); }
void AllSources::AppendSearchPathDirectory(std::string directory) {
// gfortran and ifort append to current path, PGI prepends
searchPath_.push_back(directory);

View File

@ -899,8 +899,8 @@ static bool VerifyHeader(llvm::ArrayRef<char> content) {
return expectSum == actualSum;
}
Scope *ModFileReader::Read(
const SourceName &name, Scope *ancestor, bool silent) {
Scope *ModFileReader::Read(const SourceName &name,
std::optional<bool> isIntrinsic, Scope *ancestor, bool silent) {
std::string ancestorName; // empty for module
if (ancestor) {
if (auto *scope{ancestor->FindSubmodule(name)}) {
@ -908,16 +908,37 @@ Scope *ModFileReader::Read(
}
ancestorName = ancestor->GetName().value().ToString();
} else {
auto it{context_.globalScope().find(name)};
if (it != context_.globalScope().end()) {
return it->second->scope();
if (!isIntrinsic.value_or(false)) {
auto it{context_.globalScope().find(name)};
if (it != context_.globalScope().end()) {
return it->second->scope();
}
}
if (isIntrinsic.value_or(true)) {
auto it{context_.intrinsicModulesScope().find(name)};
if (it != context_.intrinsicModulesScope().end()) {
return it->second->scope();
}
}
}
parser::Parsing parsing{context_.allCookedSources()};
parser::Options options;
options.isModuleFile = true;
options.features.Enable(common::LanguageFeature::BackslashEscapes);
options.searchDirectories = context_.searchDirectories();
if (!isIntrinsic.value_or(false)) {
options.searchDirectories = context_.searchDirectories();
// If a directory is in both lists, the intrinsic module directory
// takes precedence.
for (const auto &dir : context_.intrinsicModuleDirectories()) {
std::remove(options.searchDirectories.begin(),
options.searchDirectories.end(), dir);
}
}
if (isIntrinsic.value_or(true)) {
for (const auto &dir : context_.intrinsicModuleDirectories()) {
options.searchDirectories.push_back(dir);
}
}
auto path{ModFileName(name, ancestorName, context_.moduleFileSuffix())};
const auto *sourceFile{parsing.Prescan(path, options)};
if (parsing.messages().AnyFatalError()) {
@ -946,10 +967,21 @@ Scope *ModFileReader::Read(
return nullptr;
}
Scope *parentScope; // the scope this module/submodule goes into
if (!isIntrinsic.has_value()) {
for (const auto &dir : context_.intrinsicModuleDirectories()) {
if (sourceFile->path().size() > dir.size() &&
sourceFile->path().find(dir) == 0) {
isIntrinsic = true;
break;
}
}
}
Scope &topScope{isIntrinsic.value_or(false) ? context_.intrinsicModulesScope()
: context_.globalScope()};
if (!ancestor) {
parentScope = &context_.globalScope();
parentScope = &topScope;
} else if (std::optional<SourceName> parent{GetSubmoduleParent(*parseTree)}) {
parentScope = Read(*parent, ancestor);
parentScope = Read(*parent, false /*not intrinsic*/, ancestor, silent);
} else {
parentScope = ancestor;
}
@ -959,9 +991,12 @@ Scope *ModFileReader::Read(
}
Symbol &modSymbol{*pair.first->second};
modSymbol.set(Symbol::Flag::ModFile);
ResolveNames(context_, *parseTree);
ResolveNames(context_, *parseTree, topScope);
CHECK(modSymbol.has<ModuleDetails>());
CHECK(modSymbol.test(Symbol::Flag::ModFile));
if (isIntrinsic.value_or(false)) {
modSymbol.attrs().set(Attr::INTRINSIC);
}
return modSymbol.scope();
}

View File

@ -81,8 +81,8 @@ public:
// Find and read the module file for a module or submodule.
// If ancestor is specified, look for a submodule of that module.
// Return the Scope for that module/submodule or nullptr on error.
Scope *Read(
const SourceName &, Scope *ancestor = nullptr, bool silent = false);
Scope *Read(const SourceName &, std::optional<bool> isIntrinsic,
Scope *ancestor, bool silent = false);
private:
SemanticsContext &context_;

View File

@ -645,8 +645,13 @@ public:
bool BeginSubmodule(const parser::Name &, const parser::ParentIdentifier &);
void ApplyDefaultAccess();
void AddGenericUse(GenericDetails &, const SourceName &, const Symbol &);
void AddAndCheckExplicitIntrinsicUse(SourceName, bool isIntrinsic);
void ClearUseRenames() { useRenames_.clear(); }
void ClearUseOnly() { useOnly_.clear(); }
void ClearExplicitIntrinsicUses() {
explicitIntrinsicUses_.clear();
explicitNonIntrinsicUses_.clear();
}
private:
// The default access spec for this module.
@ -659,6 +664,10 @@ private:
std::set<std::pair<SourceName, Scope *>> useRenames_;
// Names that have appeared in an ONLY clause of a USE statement
std::set<std::pair<SourceName, Scope *>> useOnly_;
// Module names that have appeared in USE statements with explicit
// INTRINSIC or NON_INTRINSIC keywords
std::set<SourceName> explicitIntrinsicUses_;
std::set<SourceName> explicitNonIntrinsicUses_;
Symbol &SetAccess(const SourceName &, Attr attr, Symbol * = nullptr);
// A rename in a USE statement: local => use
@ -688,7 +697,8 @@ private:
bool IsUseOnly(const SourceName &name) const {
return useOnly_.find({name, useModuleScope_}) != useOnly_.end();
}
Scope *FindModule(const parser::Name &, Scope *ancestor = nullptr);
Scope *FindModule(const parser::Name &, std::optional<bool> isIntrinsic,
Scope *ancestor = nullptr);
};
class InterfaceVisitor : public virtual ScopeHandler {
@ -1365,11 +1375,14 @@ public:
using SubprogramVisitor::Post;
using SubprogramVisitor::Pre;
ResolveNamesVisitor(SemanticsContext &context, ImplicitRulesMap &rules)
: BaseVisitor{context, *this, rules} {
PushScope(context.globalScope());
ResolveNamesVisitor(
SemanticsContext &context, ImplicitRulesMap &rules, Scope &top)
: BaseVisitor{context, *this, rules}, topScope_{top} {
PushScope(top);
}
Scope &topScope() const { return topScope_; }
// Default action for a parse tree node is to visit children.
template <typename T> bool Pre(const T &) { return true; }
template <typename T> void Post(const T &) {}
@ -1427,6 +1440,7 @@ private:
// Kind of procedure we are expecting to see in a ProcedureDesignator
std::optional<Symbol::Flag> expectedProcFlag_;
std::optional<SourceName> prevImportStmt_;
Scope &topScope_;
void PreSpecificationConstruct(const parser::SpecificationConstruct &);
void CreateCommonBlockSymbols(const parser::CommonStmt &);
@ -2480,7 +2494,16 @@ bool ModuleVisitor::Pre(const parser::Rename::Operators &x) {
// Set useModuleScope_ to the Scope of the module being used.
bool ModuleVisitor::Pre(const parser::UseStmt &x) {
useModuleScope_ = FindModule(x.moduleName);
std::optional<bool> isIntrinsic;
if (x.nature) {
isIntrinsic = *x.nature == parser::UseStmt::ModuleNature::Intrinsic;
AddAndCheckExplicitIntrinsicUse(x.moduleName.source, *isIntrinsic);
} else if (currScope().IsModule() && currScope().symbol() &&
currScope().symbol()->attrs().test(Attr::INTRINSIC)) {
// Intrinsic modules USE only other intrinsic modules
isIntrinsic = true;
}
useModuleScope_ = FindModule(x.moduleName, isIntrinsic);
if (!useModuleScope_) {
return false;
}
@ -2662,15 +2685,41 @@ void ModuleVisitor::AddGenericUse(
generic.AddUse(currScope().MakeSymbol(name, {}, UseDetails{name, useSymbol}));
}
// Enforce C1406
void ModuleVisitor::AddAndCheckExplicitIntrinsicUse(
SourceName name, bool isIntrinsic) {
if (isIntrinsic) {
if (auto iter{explicitNonIntrinsicUses_.find(name)};
iter != explicitNonIntrinsicUses_.end()) {
Say(name,
"Cannot USE,INTRINSIC module '%s' in the same scope as USE,NON_INTRINSIC"_err_en_US,
name)
.Attach(*iter, "Previous USE of '%s'"_en_US, *iter);
}
explicitIntrinsicUses_.insert(name);
} else {
if (auto iter{explicitIntrinsicUses_.find(name)};
iter != explicitIntrinsicUses_.end()) {
Say(name,
"Cannot USE,NON_INTRINSIC module '%s' in the same scope as USE,INTRINSIC"_err_en_US,
name)
.Attach(*iter, "Previous USE of '%s'"_en_US, *iter);
}
explicitNonIntrinsicUses_.insert(name);
}
}
bool ModuleVisitor::BeginSubmodule(
const parser::Name &name, const parser::ParentIdentifier &parentId) {
auto &ancestorName{std::get<parser::Name>(parentId.t)};
auto &parentName{std::get<std::optional<parser::Name>>(parentId.t)};
Scope *ancestor{FindModule(ancestorName)};
Scope *ancestor{FindModule(ancestorName, false /*not intrinsic*/)};
if (!ancestor) {
return false;
}
Scope *parentScope{parentName ? FindModule(*parentName, ancestor) : ancestor};
Scope *parentScope{parentName
? FindModule(*parentName, false /*not intrinsic*/, ancestor)
: ancestor};
if (!parentScope) {
return false;
}
@ -2696,9 +2745,10 @@ void ModuleVisitor::BeginModule(const parser::Name &name, bool isSubmodule) {
// If ancestor is present, look for a submodule of that ancestor module.
// May have to read a .mod file to find it.
// If an error occurs, report it and return nullptr.
Scope *ModuleVisitor::FindModule(const parser::Name &name, Scope *ancestor) {
Scope *ModuleVisitor::FindModule(const parser::Name &name,
std::optional<bool> isIntrinsic, Scope *ancestor) {
ModFileReader reader{context()};
Scope *scope{reader.Read(name.source, ancestor)};
Scope *scope{reader.Read(name.source, isIntrinsic, ancestor)};
if (!scope) {
return nullptr;
}
@ -3463,12 +3513,11 @@ bool DeclarationVisitor::Pre(const parser::Initialization &) {
}
void DeclarationVisitor::Post(const parser::EntityDecl &x) {
// TODO: may be under StructureStmt
const auto &name{std::get<parser::ObjectName>(x.t)};
Attrs attrs{attrs_ ? HandleSaveName(name.source, *attrs_) : Attrs{}};
Symbol &symbol{DeclareUnknownEntity(name, attrs)};
symbol.ReplaceName(name.source);
if (auto &init{std::get<std::optional<parser::Initialization>>(x.t)}) {
if (const auto &init{std::get<std::optional<parser::Initialization>>(x.t)}) {
if (ConvertToObjectEntity(symbol)) {
Initialization(name, *init, false);
}
@ -6530,6 +6579,7 @@ bool ResolveNamesVisitor::Pre(const parser::SpecificationPart &x) {
Walk(useStmts);
ClearUseRenames();
ClearUseOnly();
ClearExplicitIntrinsicUses();
Walk(importStmts);
Walk(implicitPart);
for (const auto &decl : decls) {
@ -6828,7 +6878,7 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) {
return true;
}
auto root{ProgramTree::Build(x)};
SetScope(context().globalScope());
SetScope(topScope_);
ResolveSpecificationParts(root);
FinishSpecificationParts(root);
inExecutionPart_ = true;
@ -7120,10 +7170,11 @@ void ResolveNamesVisitor::Post(const parser::Program &) {
// constructed.
static ImplicitRulesMap *sharedImplicitRulesMap{nullptr};
bool ResolveNames(SemanticsContext &context, const parser::Program &program) {
bool ResolveNames(
SemanticsContext &context, const parser::Program &program, Scope &top) {
ImplicitRulesMap implicitRulesMap;
auto restorer{common::ScopedSet(sharedImplicitRulesMap, &implicitRulesMap)};
ResolveNamesVisitor{context, implicitRulesMap}.Walk(program);
ResolveNamesVisitor{context, implicitRulesMap, top}.Walk(program);
return !context.AnyFatalError();
}
@ -7132,7 +7183,8 @@ bool ResolveNames(SemanticsContext &context, const parser::Program &program) {
void ResolveSpecificationParts(
SemanticsContext &context, const Symbol &subprogram) {
auto originalLocation{context.location()};
ResolveNamesVisitor visitor{context, DEREF(sharedImplicitRulesMap)};
ResolveNamesVisitor visitor{
context, DEREF(sharedImplicitRulesMap), context.globalScope()};
const auto &details{subprogram.get<SubprogramNameDetails>()};
ProgramTree &node{details.node()};
const Scope &moduleScope{subprogram.owner()};

View File

@ -23,10 +23,11 @@ struct Program;
namespace Fortran::semantics {
class Scope;
class SemanticsContext;
class Symbol;
bool ResolveNames(SemanticsContext &, const parser::Program &);
bool ResolveNames(SemanticsContext &, const parser::Program &, Scope &top);
void ResolveSpecificationParts(SemanticsContext &, const Symbol &);
void DumpSymbols(llvm::raw_ostream &);

View File

@ -285,7 +285,7 @@ void Scope::add_importName(const SourceName &name) {
// true if name can be imported or host-associated from parent scope.
bool Scope::CanImport(const SourceName &name) const {
if (IsGlobal() || parent_.IsGlobal()) {
if (IsTopLevel() || parent_.IsTopLevel()) {
return false;
}
switch (GetImportKind()) {
@ -306,7 +306,7 @@ const Scope *Scope::FindScope(parser::CharBlock source) const {
Scope *Scope::FindScope(parser::CharBlock source) {
bool isContained{sourceRange_.Contains(source)};
if (!isContained && !IsGlobal() && !IsModuleFile()) {
if (!isContained && !IsTopLevel() && !IsModuleFile()) {
return nullptr;
}
for (auto &child : children_) {
@ -314,7 +314,7 @@ Scope *Scope::FindScope(parser::CharBlock source) {
return scope;
}
}
return isContained ? this : nullptr;
return isContained && !IsTopLevel() ? this : nullptr;
}
void Scope::AddSourceRange(const parser::CharBlock &source) {

View File

@ -165,7 +165,7 @@ using StatementSemanticsPass2 = SemanticsVisitor<AccStructureChecker,
static bool PerformStatementSemantics(
SemanticsContext &context, parser::Program &program) {
ResolveNames(context, program);
ResolveNames(context, program, context.globalScope());
RewriteParseTree(context, program);
ComputeOffsets(context, context.globalScope());
CheckDeclarations(context);
@ -185,9 +185,10 @@ SemanticsContext::SemanticsContext(
: defaultKinds_{defaultKinds}, languageFeatures_{languageFeatures},
allCookedSources_{allCookedSources},
intrinsics_{evaluate::IntrinsicProcTable::Configure(defaultKinds_)},
globalScope_{*this}, foldingContext_{
parser::ContextualMessages{&messages_},
defaultKinds_, intrinsics_} {}
globalScope_{*this}, intrinsicModulesScope_{globalScope_.MakeScope(
Scope::Kind::IntrinsicModules, nullptr)},
foldingContext_{
parser::ContextualMessages{&messages_}, defaultKinds_, intrinsics_} {}
SemanticsContext::~SemanticsContext() {}
@ -246,7 +247,9 @@ Scope &SemanticsContext::FindScope(parser::CharBlock source) {
if (auto *scope{globalScope_.FindScope(source)}) {
return *scope;
} else {
common::die("SemanticsContext::FindScope(): invalid source location");
common::die(
"SemanticsContext::FindScope(): invalid source location for '%s'",
source.ToString().c_str());
}
}
@ -339,8 +342,8 @@ bool SemanticsContext::IsTempName(const std::string &name) {
}
Scope *SemanticsContext::GetBuiltinModule(const char *name) {
return ModFileReader{*this}.Read(
SourceName{name, std::strlen(name)}, nullptr, true /*silence errors*/);
return ModFileReader{*this}.Read(SourceName{name, std::strlen(name)},
true /*intrinsic*/, nullptr, true /*silence errors*/);
}
void SemanticsContext::UseFortranBuiltinsModule() {

View File

@ -348,7 +348,7 @@ bool Symbol::IsSubprogram() const {
bool Symbol::IsFromModFile() const {
return test(Flag::ModFile) ||
(!owner_->IsGlobal() && owner_->symbol()->IsFromModFile());
(!owner_->IsTopLevel() && owner_->symbol()->IsFromModFile());
}
ObjectEntityDetails::ObjectEntityDetails(EntityDetails &&d)
@ -543,7 +543,7 @@ void Symbol::dump() const { llvm::errs() << *this << '\n'; }
// parent scopes. For scopes without corresponding symbols, use the kind
// with an index (e.g. Block1, Block2, etc.).
static void DumpUniqueName(llvm::raw_ostream &os, const Scope &scope) {
if (!scope.IsGlobal()) {
if (!scope.IsTopLevel()) {
DumpUniqueName(os, scope.parent());
os << '/';
if (auto *scopeSymbol{scope.symbol()};

View File

@ -31,16 +31,16 @@ static const Scope *FindScopeContaining(
if (predicate(*scope)) {
return scope;
}
if (scope->IsGlobal()) {
if (scope->IsTopLevel()) {
return nullptr;
}
}
}
const Scope &GetTopLevelUnitContaining(const Scope &start) {
CHECK(!start.IsGlobal());
CHECK(!start.IsTopLevel());
return DEREF(FindScopeContaining(
start, [](const Scope &scope) { return scope.parent().IsGlobal(); }));
start, [](const Scope &scope) { return scope.parent().IsTopLevel(); }));
}
const Scope &GetTopLevelUnitContaining(const Symbol &symbol) {
@ -58,7 +58,7 @@ const Scope *FindModuleFileContaining(const Scope &start) {
}
const Scope &GetProgramUnitContaining(const Scope &start) {
CHECK(!start.IsGlobal());
CHECK(!start.IsTopLevel());
return DEREF(FindScopeContaining(start, [](const Scope &scope) {
switch (scope.kind()) {
case Scope::Kind::Module:
@ -80,7 +80,7 @@ const Scope *FindPureProcedureContaining(const Scope &start) {
// N.B. We only need to examine the innermost containing program unit
// because an internal subprogram of a pure subprogram must also
// be pure (C1592).
if (start.IsGlobal()) {
if (start.IsTopLevel()) {
return nullptr;
} else {
const Scope &scope{GetProgramUnitContaining(start)};
@ -203,7 +203,7 @@ bool IsUseAssociated(const Symbol &symbol, const Scope &scope) {
bool DoesScopeContain(
const Scope *maybeAncestor, const Scope &maybeDescendent) {
return maybeAncestor && !maybeDescendent.IsGlobal() &&
return maybeAncestor && !maybeDescendent.IsTopLevel() &&
FindScopeContaining(maybeDescendent.parent(),
[&](const Scope &scope) { return &scope == maybeAncestor; });
}
@ -1094,6 +1094,7 @@ ProcedureDefinitionClass ClassifyProcedure(const Symbol &symbol) { // 15.2.2
}
switch (ultimate.owner().kind()) {
case Scope::Kind::Global:
case Scope::Kind::IntrinsicModules:
return ProcedureDefinitionClass::External;
case Scope::Kind::Module:
return ProcedureDefinitionClass::Module;

View File

@ -0,0 +1,30 @@
! RUN: %python %S/test_errors.py %s %flang_fc1
! Test intrinsic vs non_intrinsic module coexistence
module iso_fortran_env
integer, parameter :: user_defined_123 = 123
end module
module m1
use, intrinsic :: iso_fortran_env, only: int32
!ERROR: Cannot USE,NON_INTRINSIC module 'iso_fortran_env' in the same scope as USE,INTRINSIC
use, non_intrinsic :: iso_fortran_env, only: user_defined_123
end module
module m2
use, intrinsic :: iso_fortran_env, only: int32
end module
module m3
use, non_intrinsic :: iso_fortran_env, only: user_defined_123
end module
module m4
use :: iso_fortran_env, only: user_defined_123
end module
module m5
!ERROR: Cannot read module file for module 'ieee_arithmetic': Source file 'ieee_arithmetic.mod' was not found
use, non_intrinsic :: ieee_arithmetic, only: ieee_selected_real_kind
end module
module notAnIntrinsicModule
end module
module m6
!ERROR: Cannot read module file for module 'notanintrinsicmodule': Source file 'notanintrinsicmodule.mod' was not found
use, intrinsic :: notAnIntrinsicModule
end module