[flang] Safer hermetic module file reading (#121002)
When a hermetic module file is read, use a new scope to hold its dependent modules so that they don't conflict with any modules in the global scope.
This commit is contained in:
parent
c3a0fcc982
commit
038b42ba5b
@ -164,3 +164,13 @@ a diagnostic but we still wouldn't have line numbers.
|
||||
To provide line numbers and character positions or source lines as the user
|
||||
wrote them we would have to save some amount of provenance information in the
|
||||
module file as well.
|
||||
|
||||
## Hermetic modules files
|
||||
|
||||
Top-level module files for libraries can be build with `-fhermetic-module-files`.
|
||||
This option causes these module files to contain copies of all of the non-intrinsic
|
||||
modules on which they depend, so that non-top-level local modules and the
|
||||
modules of dependent libraries need not also be packaged with the library.
|
||||
When the compiler reads a hermetic module file, the copies of the dependent
|
||||
modules are read into their own scope, and will not conflict with other modules
|
||||
of the same name that client code might `USE`.
|
||||
|
@ -73,7 +73,8 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
|
||||
PreviousScalarUse, RedeclaredInaccessibleComponent, ImplicitShared,
|
||||
IndexVarRedefinition, IncompatibleImplicitInterfaces, BadTypeForTarget,
|
||||
VectorSubscriptFinalization, UndefinedFunctionResult, UselessIomsg,
|
||||
MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation)
|
||||
MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation,
|
||||
CompatibleDeclarationsFromDistinctModules)
|
||||
|
||||
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
|
||||
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
|
||||
|
@ -510,6 +510,8 @@ bool AreSameDerivedType(
|
||||
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
|
||||
bool AreSameDerivedTypeIgnoringTypeParameters(
|
||||
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
|
||||
bool AreSameDerivedTypeIgnoringSequence(
|
||||
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
|
||||
|
||||
// For generating "[extern] template class", &c. boilerplate
|
||||
#define EXPAND_FOR_EACH_INTEGER_KIND(M, P, S) \
|
||||
|
@ -110,6 +110,12 @@ public:
|
||||
}
|
||||
Scope &globalScope() { return globalScope_; }
|
||||
Scope &intrinsicModulesScope() { return intrinsicModulesScope_; }
|
||||
Scope *currentHermeticModuleFileScope() {
|
||||
return currentHermeticModuleFileScope_;
|
||||
}
|
||||
void set_currentHermeticModuleFileScope(Scope *scope) {
|
||||
currentHermeticModuleFileScope_ = scope;
|
||||
}
|
||||
parser::Messages &messages() { return messages_; }
|
||||
evaluate::FoldingContext &foldingContext() { return foldingContext_; }
|
||||
parser::AllCookedSources &allCookedSources() { return allCookedSources_; }
|
||||
@ -313,6 +319,7 @@ private:
|
||||
evaluate::TargetCharacteristics targetCharacteristics_;
|
||||
Scope globalScope_;
|
||||
Scope &intrinsicModulesScope_;
|
||||
Scope *currentHermeticModuleFileScope_{nullptr};
|
||||
ScopeIndex scopeIndex_;
|
||||
parser::Messages messages_;
|
||||
evaluate::FoldingContext foldingContext_;
|
||||
|
@ -605,12 +605,12 @@ private:
|
||||
class UseErrorDetails {
|
||||
public:
|
||||
UseErrorDetails(const UseDetails &);
|
||||
UseErrorDetails &add_occurrence(const SourceName &, const Scope &);
|
||||
using listType = std::list<std::pair<SourceName, const Scope *>>;
|
||||
const listType occurrences() const { return occurrences_; };
|
||||
UseErrorDetails &add_occurrence(const SourceName &, const Symbol &);
|
||||
using ListType = std::list<std::pair<SourceName, const Symbol *>>;
|
||||
const ListType occurrences() const { return occurrences_; };
|
||||
|
||||
private:
|
||||
listType occurrences_;
|
||||
ListType occurrences_;
|
||||
};
|
||||
|
||||
// A symbol host-associated from an enclosing scope.
|
||||
|
@ -293,11 +293,13 @@ using SetOfDerivedTypePairs =
|
||||
|
||||
static bool AreSameDerivedType(const semantics::DerivedTypeSpec &,
|
||||
const semantics::DerivedTypeSpec &, bool ignoreTypeParameterValues,
|
||||
bool ignoreLenParameters, SetOfDerivedTypePairs &inProgress);
|
||||
bool ignoreLenParameters, bool ignoreSequence,
|
||||
SetOfDerivedTypePairs &inProgress);
|
||||
|
||||
// F2023 7.5.3.2
|
||||
static bool AreSameComponent(const semantics::Symbol &x,
|
||||
const semantics::Symbol &y, SetOfDerivedTypePairs &inProgress) {
|
||||
const semantics::Symbol &y, bool ignoreSequence,
|
||||
SetOfDerivedTypePairs &inProgress) {
|
||||
if (x.attrs() != y.attrs()) {
|
||||
return false;
|
||||
}
|
||||
@ -325,7 +327,8 @@ static bool AreSameComponent(const semantics::Symbol &x,
|
||||
!yType->IsUnlimitedPolymorphic() ||
|
||||
(!xType->IsUnlimitedPolymorphic() &&
|
||||
!AreSameDerivedType(xType->GetDerivedTypeSpec(),
|
||||
yType->GetDerivedTypeSpec(), false, false, inProgress))) {
|
||||
yType->GetDerivedTypeSpec(), false, false, ignoreSequence,
|
||||
inProgress))) {
|
||||
return false;
|
||||
}
|
||||
} else if (!xType->IsTkLenCompatibleWith(*yType)) {
|
||||
@ -449,7 +452,8 @@ static bool AreTypeParamCompatible(const semantics::DerivedTypeSpec &x,
|
||||
// F2023 7.5.3.2
|
||||
static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
|
||||
const semantics::DerivedTypeSpec &y, bool ignoreTypeParameterValues,
|
||||
bool ignoreLenParameters, SetOfDerivedTypePairs &inProgress) {
|
||||
bool ignoreLenParameters, bool ignoreSequence,
|
||||
SetOfDerivedTypePairs &inProgress) {
|
||||
if (&x == &y) {
|
||||
return true;
|
||||
}
|
||||
@ -472,7 +476,12 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
|
||||
inProgress.insert(thisQuery);
|
||||
const auto &xDetails{xSymbol.get<semantics::DerivedTypeDetails>()};
|
||||
const auto &yDetails{ySymbol.get<semantics::DerivedTypeDetails>()};
|
||||
if (!(xDetails.sequence() && yDetails.sequence()) &&
|
||||
if (xDetails.sequence() != yDetails.sequence() ||
|
||||
xSymbol.attrs().test(semantics::Attr::BIND_C) !=
|
||||
ySymbol.attrs().test(semantics::Attr::BIND_C)) {
|
||||
return false;
|
||||
}
|
||||
if (!ignoreSequence && !(xDetails.sequence() && yDetails.sequence()) &&
|
||||
!(xSymbol.attrs().test(semantics::Attr::BIND_C) &&
|
||||
ySymbol.attrs().test(semantics::Attr::BIND_C))) {
|
||||
// PGI does not enforce this requirement; all other Fortran
|
||||
@ -493,7 +502,8 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
|
||||
const auto yLookup{ySymbol.scope()->find(*yComponentName)};
|
||||
if (xLookup == xSymbol.scope()->end() ||
|
||||
yLookup == ySymbol.scope()->end() ||
|
||||
!AreSameComponent(*xLookup->second, *yLookup->second, inProgress)) {
|
||||
!AreSameComponent(
|
||||
*xLookup->second, *yLookup->second, ignoreSequence, inProgress)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -503,13 +513,19 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
|
||||
bool AreSameDerivedType(
|
||||
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
|
||||
SetOfDerivedTypePairs inProgress;
|
||||
return AreSameDerivedType(x, y, false, false, inProgress);
|
||||
return AreSameDerivedType(x, y, false, false, false, inProgress);
|
||||
}
|
||||
|
||||
bool AreSameDerivedTypeIgnoringTypeParameters(
|
||||
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
|
||||
SetOfDerivedTypePairs inProgress;
|
||||
return AreSameDerivedType(x, y, true, true, inProgress);
|
||||
return AreSameDerivedType(x, y, true, true, false, inProgress);
|
||||
}
|
||||
|
||||
bool AreSameDerivedTypeIgnoringSequence(
|
||||
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
|
||||
SetOfDerivedTypePairs inProgress;
|
||||
return AreSameDerivedType(x, y, false, false, true, inProgress);
|
||||
}
|
||||
|
||||
static bool AreSameDerivedType(
|
||||
@ -536,7 +552,7 @@ static bool AreCompatibleDerivedTypes(const semantics::DerivedTypeSpec *x,
|
||||
} else {
|
||||
SetOfDerivedTypePairs inProgress;
|
||||
if (AreSameDerivedType(*x, *y, ignoreTypeParameterValues,
|
||||
ignoreLenTypeParameters, inProgress)) {
|
||||
ignoreLenTypeParameters, false, inProgress)) {
|
||||
return true;
|
||||
} else {
|
||||
return isPolymorphic &&
|
||||
|
@ -1366,6 +1366,12 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
|
||||
name.ToString(), isIntrinsic.value_or(false))};
|
||||
if (!isIntrinsic.value_or(false) && !ancestor) {
|
||||
// Already present in the symbol table as a usable non-intrinsic module?
|
||||
if (Scope * hermeticScope{context_.currentHermeticModuleFileScope()}) {
|
||||
auto it{hermeticScope->find(name)};
|
||||
if (it != hermeticScope->end()) {
|
||||
return it->second->scope();
|
||||
}
|
||||
}
|
||||
auto it{context_.globalScope().find(name)};
|
||||
if (it != context_.globalScope().end()) {
|
||||
Scope *scope{it->second->scope()};
|
||||
@ -1544,9 +1550,22 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
|
||||
// Process declarations from the module file
|
||||
auto wasModuleFileName{context_.foldingContext().moduleFileName()};
|
||||
context_.foldingContext().set_moduleFileName(name);
|
||||
// Are there multiple modules in the module file due to it having been
|
||||
// created under -fhermetic-module-files? If so, process them first in
|
||||
// their own nested scope that will be visible only to USE statements
|
||||
// within the module file.
|
||||
if (parseTree.v.size() > 1) {
|
||||
parser::Program hermeticModules{std::move(parseTree.v)};
|
||||
parseTree.v.emplace_back(std::move(hermeticModules.v.front()));
|
||||
hermeticModules.v.pop_front();
|
||||
Scope &hermeticScope{topScope.MakeScope(Scope::Kind::Global)};
|
||||
context_.set_currentHermeticModuleFileScope(&hermeticScope);
|
||||
ResolveNames(context_, hermeticModules, hermeticScope);
|
||||
}
|
||||
GetModuleDependences(context_.moduleDependences(), sourceFile->content());
|
||||
ResolveNames(context_, parseTree, topScope);
|
||||
context_.foldingContext().set_moduleFileName(wasModuleFileName);
|
||||
context_.set_currentHermeticModuleFileScope(nullptr);
|
||||
if (!moduleSymbol) {
|
||||
// Submodule symbols' storage are owned by their parents' scopes,
|
||||
// but their names are not in their parents' dictionaries -- we
|
||||
|
@ -2591,9 +2591,11 @@ void ScopeHandler::PopScope() {
|
||||
ConvertToObjectEntity(*pair.second);
|
||||
}
|
||||
funcResultStack_.Pop();
|
||||
// If popping back into a global scope, pop back to the main global scope.
|
||||
SetScope(currScope_->parent().IsGlobal() ? context().globalScope()
|
||||
: currScope_->parent());
|
||||
// If popping back into a global scope, pop back to the top scope.
|
||||
Scope *hermetic{context().currentHermeticModuleFileScope()};
|
||||
SetScope(currScope_->parent().IsGlobal()
|
||||
? (hermetic ? *hermetic : context().globalScope())
|
||||
: currScope_->parent());
|
||||
}
|
||||
void ScopeHandler::SetScope(Scope &scope) {
|
||||
currScope_ = &scope;
|
||||
@ -3204,9 +3206,9 @@ ModuleVisitor::SymbolRename ModuleVisitor::AddUse(
|
||||
// symbol must be either a Use or a Generic formed by merging two uses.
|
||||
// Convert it to a UseError with this additional location.
|
||||
static bool ConvertToUseError(
|
||||
Symbol &symbol, const SourceName &location, const Scope &module) {
|
||||
Symbol &symbol, const SourceName &location, const Symbol &used) {
|
||||
if (auto *ued{symbol.detailsIf<UseErrorDetails>()}) {
|
||||
ued->add_occurrence(location, module);
|
||||
ued->add_occurrence(location, used);
|
||||
return true;
|
||||
}
|
||||
const auto *useDetails{symbol.detailsIf<UseDetails>()};
|
||||
@ -3219,18 +3221,104 @@ static bool ConvertToUseError(
|
||||
}
|
||||
if (useDetails) {
|
||||
symbol.set_details(
|
||||
UseErrorDetails{*useDetails}.add_occurrence(location, module));
|
||||
UseErrorDetails{*useDetails}.add_occurrence(location, used));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Two ultimate symbols are distinct, but they have the same name and come
|
||||
// from modules with the same name. At link time, their mangled names
|
||||
// would conflict, so they had better resolve to the same definition.
|
||||
// Check whether the two ultimate symbols have compatible definitions.
|
||||
// Returns true if no further processing is required in DoAddUse().
|
||||
static bool CheckCompatibleDistinctUltimates(SemanticsContext &context,
|
||||
SourceName location, SourceName localName, const Symbol &localSymbol,
|
||||
const Symbol &localUltimate, const Symbol &useUltimate, bool &isError) {
|
||||
isError = false;
|
||||
if (localUltimate.has<GenericDetails>()) {
|
||||
if (useUltimate.has<GenericDetails>() ||
|
||||
useUltimate.has<SubprogramDetails>() ||
|
||||
useUltimate.has<DerivedTypeDetails>()) {
|
||||
return false; // can try to merge them
|
||||
} else {
|
||||
isError = true;
|
||||
}
|
||||
} else if (useUltimate.has<GenericDetails>()) {
|
||||
if (localUltimate.has<SubprogramDetails>() ||
|
||||
localUltimate.has<DerivedTypeDetails>()) {
|
||||
return false; // can try to merge them
|
||||
} else {
|
||||
isError = true;
|
||||
}
|
||||
} else if (localUltimate.has<SubprogramDetails>()) {
|
||||
if (useUltimate.has<SubprogramDetails>()) {
|
||||
auto localCharacteristics{
|
||||
evaluate::characteristics::Procedure::Characterize(
|
||||
localUltimate, context.foldingContext())};
|
||||
auto useCharacteristics{
|
||||
evaluate::characteristics::Procedure::Characterize(
|
||||
useUltimate, context.foldingContext())};
|
||||
if ((localCharacteristics &&
|
||||
(!useCharacteristics ||
|
||||
*localCharacteristics != *useCharacteristics)) ||
|
||||
(!localCharacteristics && useCharacteristics)) {
|
||||
isError = true;
|
||||
}
|
||||
} else {
|
||||
isError = true;
|
||||
}
|
||||
} else if (useUltimate.has<SubprogramDetails>()) {
|
||||
isError = true;
|
||||
} else if (const auto *localObject{
|
||||
localUltimate.detailsIf<ObjectEntityDetails>()}) {
|
||||
if (const auto *useObject{useUltimate.detailsIf<ObjectEntityDetails>()}) {
|
||||
auto localType{evaluate::DynamicType::From(localUltimate)};
|
||||
auto useType{evaluate::DynamicType::From(useUltimate)};
|
||||
if (localUltimate.size() != useUltimate.size() ||
|
||||
(localType &&
|
||||
(!useType || !localType->IsTkLenCompatibleWith(*useType) ||
|
||||
!useType->IsTkLenCompatibleWith(*localType))) ||
|
||||
(!localType && useType)) {
|
||||
isError = true;
|
||||
} else if (IsNamedConstant(localUltimate)) {
|
||||
isError = !IsNamedConstant(useUltimate) ||
|
||||
!(*localObject->init() == *useObject->init());
|
||||
} else {
|
||||
isError = IsNamedConstant(useUltimate);
|
||||
}
|
||||
} else {
|
||||
isError = true;
|
||||
}
|
||||
} else if (useUltimate.has<ObjectEntityDetails>()) {
|
||||
isError = true;
|
||||
} else if (IsProcedurePointer(localUltimate)) {
|
||||
isError = !IsProcedurePointer(useUltimate);
|
||||
} else if (IsProcedurePointer(useUltimate)) {
|
||||
isError = true;
|
||||
} else if (localUltimate.has<DerivedTypeDetails>()) {
|
||||
isError = !(useUltimate.has<DerivedTypeDetails>() &&
|
||||
evaluate::AreSameDerivedTypeIgnoringSequence(
|
||||
DerivedTypeSpec{localUltimate.name(), localUltimate},
|
||||
DerivedTypeSpec{useUltimate.name(), useUltimate}));
|
||||
} else if (useUltimate.has<DerivedTypeDetails>()) {
|
||||
isError = true;
|
||||
} else if (localUltimate.has<NamelistDetails>() &&
|
||||
useUltimate.has<NamelistDetails>()) {
|
||||
} else if (localUltimate.has<CommonBlockDetails>() &&
|
||||
useUltimate.has<CommonBlockDetails>()) {
|
||||
} else {
|
||||
isError = true;
|
||||
}
|
||||
return true; // don't try to merge generics (or whatever)
|
||||
}
|
||||
|
||||
void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
|
||||
Symbol &originalLocal, const Symbol &useSymbol) {
|
||||
Symbol *localSymbol{&originalLocal};
|
||||
if (auto *details{localSymbol->detailsIf<UseErrorDetails>()}) {
|
||||
details->add_occurrence(location, *useModuleScope_);
|
||||
details->add_occurrence(location, useSymbol);
|
||||
return;
|
||||
}
|
||||
const Symbol &useUltimate{useSymbol.GetUltimate()};
|
||||
@ -3267,6 +3355,40 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
|
||||
return;
|
||||
}
|
||||
|
||||
if (localUltimate.name() == useUltimate.name() &&
|
||||
localUltimate.owner().IsModule() && useUltimate.owner().IsModule() &&
|
||||
localUltimate.owner().GetName() &&
|
||||
localUltimate.owner().GetName() == useUltimate.owner().GetName()) {
|
||||
bool isError{false};
|
||||
if (CheckCompatibleDistinctUltimates(context(), location, localName,
|
||||
*localSymbol, localUltimate, useUltimate, isError)) {
|
||||
if (isError) {
|
||||
// Convert the local symbol to a UseErrorDetails, if possible;
|
||||
// otherwise emit a fatal error.
|
||||
if (!ConvertToUseError(*localSymbol, location, useSymbol)) {
|
||||
context()
|
||||
.Say(location,
|
||||
"'%s' use-associated from '%s' in module '%s' is incompatible with '%s' from another module"_err_en_US,
|
||||
localName, useUltimate.name(),
|
||||
useUltimate.owner().GetName().value(), localUltimate.name())
|
||||
.Attach(useUltimate.name(), "First declaration"_en_US)
|
||||
.Attach(localUltimate.name(), "Other declaration"_en_US);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (auto *msg{context().Warn(
|
||||
common::UsageWarning::CompatibleDeclarationsFromDistinctModules,
|
||||
location,
|
||||
"'%s' is use-associated from '%s' in two distinct instances of module '%s'"_warn_en_US,
|
||||
localName, localUltimate.name(),
|
||||
localUltimate.owner().GetName().value())}) {
|
||||
msg->Attach(localUltimate.name(), "Previous declaration"_en_US)
|
||||
.Attach(useUltimate.name(), "Later declaration"_en_US);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// There are many possible combinations of symbol types that could arrive
|
||||
// with the same (local) name vie USE association from distinct modules.
|
||||
// Fortran allows a generic interface to share its name with a derived type,
|
||||
@ -3328,7 +3450,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
|
||||
EraseSymbol(*localSymbol);
|
||||
CHECK(localSymbol->has<UseDetails>());
|
||||
UseErrorDetails details{localSymbol->get<UseDetails>()};
|
||||
details.add_occurrence(location, *useModuleScope_);
|
||||
details.add_occurrence(location, useSymbol);
|
||||
Symbol *newSymbol{&MakeSymbol(localName, Attrs{}, std::move(details))};
|
||||
// Restore *localSymbol in currScope
|
||||
auto iter{currScope().find(localName)};
|
||||
@ -3365,7 +3487,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
|
||||
if (localGeneric) {
|
||||
combinedDerivedType = CreateLocalUseError();
|
||||
} else {
|
||||
ConvertToUseError(*localSymbol, location, *useModuleScope_);
|
||||
ConvertToUseError(*localSymbol, location, useSymbol);
|
||||
localDerivedType = nullptr;
|
||||
localGeneric = nullptr;
|
||||
combinedDerivedType = localSymbol;
|
||||
@ -3473,7 +3595,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
|
||||
|
||||
// If symbols are not combinable, create a use error.
|
||||
if (cantCombine) {
|
||||
if (!ConvertToUseError(*localSymbol, location, *useModuleScope_)) {
|
||||
if (!ConvertToUseError(*localSymbol, location, useSymbol)) {
|
||||
Say(location,
|
||||
"Cannot use-associate '%s'; it is already declared in this scope"_err_en_US,
|
||||
localName)
|
||||
@ -9465,6 +9587,12 @@ template <typename A> std::set<SourceName> GetUses(const A &x) {
|
||||
}
|
||||
|
||||
bool ResolveNamesVisitor::Pre(const parser::Program &x) {
|
||||
if (Scope * hermetic{context().currentHermeticModuleFileScope()}) {
|
||||
// Processing either the dependent modules or first module of a
|
||||
// hermetic module file; ensure that the hermetic module scope has
|
||||
// its implicit rules map entry.
|
||||
ImplicitRulesVisitor::BeginScope(*hermetic);
|
||||
}
|
||||
std::map<SourceName, const parser::ProgramUnit *> modules;
|
||||
std::set<SourceName> uses;
|
||||
bool disordered{false};
|
||||
|
@ -177,11 +177,11 @@ ProcEntityDetails::ProcEntityDetails(EntityDetails &&d)
|
||||
: EntityDetails(std::move(d)) {}
|
||||
|
||||
UseErrorDetails::UseErrorDetails(const UseDetails &useDetails) {
|
||||
add_occurrence(useDetails.location(), *GetUsedModule(useDetails).scope());
|
||||
add_occurrence(useDetails.location(), useDetails.symbol());
|
||||
}
|
||||
UseErrorDetails &UseErrorDetails::add_occurrence(
|
||||
const SourceName &location, const Scope &module) {
|
||||
occurrences_.push_back(std::make_pair(location, &module));
|
||||
const SourceName &location, const Symbol &used) {
|
||||
occurrences_.push_back(std::make_pair(location, &used));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -557,9 +557,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) {
|
||||
[&](const UseErrorDetails &x) {
|
||||
os << " uses:";
|
||||
char sep{':'};
|
||||
for (const auto &[location, module] : x.occurrences()) {
|
||||
os << sep << " from " << module->GetName().value() << " at "
|
||||
<< location;
|
||||
for (const auto &[location, sym] : x.occurrences()) {
|
||||
os << sep << " from " << sym->name() << " at " << location;
|
||||
sep = ',';
|
||||
}
|
||||
},
|
||||
|
@ -1764,9 +1764,19 @@ bool HadUseError(
|
||||
symbol ? symbol->detailsIf<UseErrorDetails>() : nullptr}) {
|
||||
auto &msg{context.Say(
|
||||
at, "Reference to '%s' is ambiguous"_err_en_US, symbol->name())};
|
||||
for (const auto &[location, module] : details->occurrences()) {
|
||||
msg.Attach(location, "'%s' was use-associated from module '%s'"_en_US, at,
|
||||
module->GetName().value());
|
||||
for (const auto &[location, sym] : details->occurrences()) {
|
||||
const Symbol &ultimate{sym->GetUltimate()};
|
||||
auto &attachment{
|
||||
msg.Attach(location, "'%s' was use-associated from module '%s'"_en_US,
|
||||
at, sym->owner().GetName().value())};
|
||||
if (&*sym != &ultimate) {
|
||||
// For incompatible definitions where one comes from a hermetic
|
||||
// module file's incorporated dependences and the other from another
|
||||
// module of the same name.
|
||||
attachment.Attach(ultimate.name(),
|
||||
"ultimately from '%s' in module '%s'"_en_US, ultimate.name(),
|
||||
ultimate.owner().GetName().value());
|
||||
}
|
||||
}
|
||||
context.SetError(*symbol);
|
||||
return true;
|
||||
|
121
flang/test/Semantics/modfile71.F90
Normal file
121
flang/test/Semantics/modfile71.F90
Normal file
@ -0,0 +1,121 @@
|
||||
!RUN: %flang_fc1 -fsyntax-only -fhermetic-module-files -DSTEP=1 %s
|
||||
!RUN: %flang_fc1 -fsyntax-only -DSTEP=2 %s
|
||||
!RUN: not %flang_fc1 -fsyntax-only -pedantic %s 2>&1 | FileCheck %s
|
||||
|
||||
! Tests that a module captured in a hermetic module file is compatible when
|
||||
! USE'd with a module of the same name USE'd directly.
|
||||
|
||||
#if STEP == 1
|
||||
module modfile71a
|
||||
! not errors
|
||||
integer, parameter :: good_named_const = 123
|
||||
integer :: good_var = 1
|
||||
type :: good_derived
|
||||
integer component
|
||||
end type
|
||||
procedure(), pointer :: good_proc_ptr
|
||||
generic :: gen => bad_subroutine
|
||||
! bad, but okay if unused
|
||||
integer, parameter :: unused_bad_named_const = 123
|
||||
integer :: unused_bad_var = 1
|
||||
type :: unused_bad_derived
|
||||
integer component
|
||||
end type
|
||||
procedure(), pointer :: unused_bad_proc_ptr
|
||||
! errors
|
||||
integer, parameter :: bad_named_const = 123
|
||||
integer :: bad_var = 1
|
||||
type :: bad_derived
|
||||
integer component
|
||||
end type
|
||||
procedure(), pointer :: bad_proc_ptr
|
||||
contains
|
||||
subroutine good_subroutine
|
||||
end
|
||||
subroutine unused_bad_subroutine(x)
|
||||
integer x
|
||||
end
|
||||
subroutine bad_subroutine(x)
|
||||
integer x
|
||||
end
|
||||
end
|
||||
|
||||
module modfile71b
|
||||
use modfile71a ! capture hermetically
|
||||
end
|
||||
|
||||
#elif STEP == 2
|
||||
module modfile71a
|
||||
! not errors
|
||||
integer, parameter :: good_named_const = 123
|
||||
integer :: good_var = 1
|
||||
type :: good_derived
|
||||
integer component
|
||||
end type
|
||||
procedure(), pointer :: good_proc_ptr
|
||||
generic :: gen => bad_subroutine
|
||||
! bad, but okay if unused
|
||||
integer, parameter :: unused_bad_named_const = 666
|
||||
real :: unused_bad_var = 1.
|
||||
type :: unused_bad_derived
|
||||
real component
|
||||
end type
|
||||
real, pointer :: unused_bad_proc_ptr
|
||||
! errors
|
||||
integer, parameter :: bad_named_const = 666
|
||||
real :: bad_var = 1.
|
||||
type :: bad_derived
|
||||
real component
|
||||
end type
|
||||
real, pointer :: bad_proc_ptr
|
||||
contains
|
||||
subroutine good_subroutine
|
||||
end
|
||||
subroutine unused_bad_subroutine(x)
|
||||
real x
|
||||
end
|
||||
subroutine bad_subroutine(x)
|
||||
real x
|
||||
end
|
||||
end
|
||||
|
||||
#else
|
||||
|
||||
!CHECK: warning: 'bad_derived' is use-associated from 'bad_derived' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'bad_named_const' is use-associated from 'bad_named_const' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'bad_proc_ptr' is use-associated from 'bad_proc_ptr' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'bad_subroutine' is use-associated from 'bad_subroutine' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'bad_var' is use-associated from 'bad_var' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'good_derived' is use-associated from 'good_derived' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'good_named_const' is use-associated from 'good_named_const' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'good_proc_ptr' is use-associated from 'good_proc_ptr' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'good_subroutine' is use-associated from 'good_subroutine' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'good_var' is use-associated from 'good_var' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'unused_bad_derived' is use-associated from 'unused_bad_derived' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'unused_bad_named_const' is use-associated from 'unused_bad_named_const' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'unused_bad_proc_ptr' is use-associated from 'unused_bad_proc_ptr' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'unused_bad_subroutine' is use-associated from 'unused_bad_subroutine' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: warning: 'unused_bad_var' is use-associated from 'unused_bad_var' in two distinct instances of module 'modfile71a'
|
||||
!CHECK: error: Reference to 'bad_derived' is ambiguous
|
||||
!CHECK: error: Reference to 'bad_named_const' is ambiguous
|
||||
!CHECK: error: Reference to 'bad_var' is ambiguous
|
||||
!CHECK: error: Reference to 'bad_proc_ptr' is ambiguous
|
||||
!CHECK: error: Reference to 'bad_subroutine' is ambiguous
|
||||
!CHECK-NOT: error:
|
||||
!CHECK-NOT: warning:
|
||||
|
||||
program main
|
||||
use modfile71a
|
||||
use modfile71b
|
||||
type(good_derived) goodx
|
||||
type(bad_derived) badx
|
||||
print *, good_named_const
|
||||
good_var = 1
|
||||
good_proc_ptr => null()
|
||||
call good_subroutine
|
||||
print *, bad_named_const
|
||||
print *, bad_var
|
||||
bad_proc_ptr => null()
|
||||
call bad_subroutine(1)
|
||||
end
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user