Skip to content

Commit 269b0c1

Browse files
committed
[flang] Safer hermetic module file reading
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.
1 parent 40ac34c commit 269b0c1

File tree

8 files changed

+270
-13
lines changed

8 files changed

+270
-13
lines changed

flang/docs/ModFiles.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,13 @@ a diagnostic but we still wouldn't have line numbers.
164164
To provide line numbers and character positions or source lines as the user
165165
wrote them we would have to save some amount of provenance information in the
166166
module file as well.
167+
168+
## Hermetic modules files
169+
170+
Top-level module files for libraries can be build with `-fhermetic-module-files`.
171+
This option causes these module files to contain copies of all of the non-intrinsic
172+
modules on which they depend, so that non-top-level local modules and the
173+
modules of dependent libraries need not also be packaged with the library.
174+
When the compiler reads a hermetic module file, the copies of the dependent
175+
modules are read into their own scope, and will not conflict with other modules
176+
of the same name that client code might `USE`.

flang/include/flang/Common/Fortran-features.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
7373
PreviousScalarUse, RedeclaredInaccessibleComponent, ImplicitShared,
7474
IndexVarRedefinition, IncompatibleImplicitInterfaces, BadTypeForTarget,
7575
VectorSubscriptFinalization, UndefinedFunctionResult, UselessIomsg,
76-
MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation)
76+
MismatchingDummyProcedure, SubscriptedEmptyArray, UnsignedLiteralTruncation,
77+
CompatibleDeclarationsFromDistinctModules)
7778

7879
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
7980
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;

flang/include/flang/Evaluate/type.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,8 @@ bool AreSameDerivedType(
510510
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
511511
bool AreSameDerivedTypeIgnoringTypeParameters(
512512
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
513+
bool AreSameDerivedTypeIgnoringSequence(
514+
const semantics::DerivedTypeSpec &, const semantics::DerivedTypeSpec &);
513515

514516
// For generating "[extern] template class", &c. boilerplate
515517
#define EXPAND_FOR_EACH_INTEGER_KIND(M, P, S) \

flang/include/flang/Semantics/semantics.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ class SemanticsContext {
110110
}
111111
Scope &globalScope() { return globalScope_; }
112112
Scope &intrinsicModulesScope() { return intrinsicModulesScope_; }
113+
Scope *currentHermeticModuleFileScope() {
114+
return currentHermeticModuleFileScope_;
115+
}
116+
void set_currentHermeticModuleFileScope(Scope *scope) {
117+
currentHermeticModuleFileScope_ = scope;
118+
}
113119
parser::Messages &messages() { return messages_; }
114120
evaluate::FoldingContext &foldingContext() { return foldingContext_; }
115121
parser::AllCookedSources &allCookedSources() { return allCookedSources_; }
@@ -313,6 +319,7 @@ class SemanticsContext {
313319
evaluate::TargetCharacteristics targetCharacteristics_;
314320
Scope globalScope_;
315321
Scope &intrinsicModulesScope_;
322+
Scope *currentHermeticModuleFileScope_{nullptr};
316323
ScopeIndex scopeIndex_;
317324
parser::Messages messages_;
318325
evaluate::FoldingContext foldingContext_;

flang/lib/Evaluate/type.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,13 @@ using SetOfDerivedTypePairs =
293293

294294
static bool AreSameDerivedType(const semantics::DerivedTypeSpec &,
295295
const semantics::DerivedTypeSpec &, bool ignoreTypeParameterValues,
296-
bool ignoreLenParameters, SetOfDerivedTypePairs &inProgress);
296+
bool ignoreLenParameters, bool ignoreSequence,
297+
SetOfDerivedTypePairs &inProgress);
297298

298299
// F2023 7.5.3.2
299300
static bool AreSameComponent(const semantics::Symbol &x,
300-
const semantics::Symbol &y, SetOfDerivedTypePairs &inProgress) {
301+
const semantics::Symbol &y, bool ignoreSequence,
302+
SetOfDerivedTypePairs &inProgress) {
301303
if (x.attrs() != y.attrs()) {
302304
return false;
303305
}
@@ -325,7 +327,8 @@ static bool AreSameComponent(const semantics::Symbol &x,
325327
!yType->IsUnlimitedPolymorphic() ||
326328
(!xType->IsUnlimitedPolymorphic() &&
327329
!AreSameDerivedType(xType->GetDerivedTypeSpec(),
328-
yType->GetDerivedTypeSpec(), false, false, inProgress))) {
330+
yType->GetDerivedTypeSpec(), false, false, ignoreSequence,
331+
inProgress))) {
329332
return false;
330333
}
331334
} else if (!xType->IsTkLenCompatibleWith(*yType)) {
@@ -449,7 +452,8 @@ static bool AreTypeParamCompatible(const semantics::DerivedTypeSpec &x,
449452
// F2023 7.5.3.2
450453
static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
451454
const semantics::DerivedTypeSpec &y, bool ignoreTypeParameterValues,
452-
bool ignoreLenParameters, SetOfDerivedTypePairs &inProgress) {
455+
bool ignoreLenParameters, bool ignoreSequence,
456+
SetOfDerivedTypePairs &inProgress) {
453457
if (&x == &y) {
454458
return true;
455459
}
@@ -472,7 +476,12 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
472476
inProgress.insert(thisQuery);
473477
const auto &xDetails{xSymbol.get<semantics::DerivedTypeDetails>()};
474478
const auto &yDetails{ySymbol.get<semantics::DerivedTypeDetails>()};
475-
if (!(xDetails.sequence() && yDetails.sequence()) &&
479+
if (xDetails.sequence() != yDetails.sequence() ||
480+
xSymbol.attrs().test(semantics::Attr::BIND_C) !=
481+
ySymbol.attrs().test(semantics::Attr::BIND_C)) {
482+
return false;
483+
}
484+
if (!ignoreSequence && !(xDetails.sequence() && yDetails.sequence()) &&
476485
!(xSymbol.attrs().test(semantics::Attr::BIND_C) &&
477486
ySymbol.attrs().test(semantics::Attr::BIND_C))) {
478487
// PGI does not enforce this requirement; all other Fortran
@@ -493,7 +502,8 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
493502
const auto yLookup{ySymbol.scope()->find(*yComponentName)};
494503
if (xLookup == xSymbol.scope()->end() ||
495504
yLookup == ySymbol.scope()->end() ||
496-
!AreSameComponent(*xLookup->second, *yLookup->second, inProgress)) {
505+
!AreSameComponent(
506+
*xLookup->second, *yLookup->second, ignoreSequence, inProgress)) {
497507
return false;
498508
}
499509
}
@@ -503,13 +513,19 @@ static bool AreSameDerivedType(const semantics::DerivedTypeSpec &x,
503513
bool AreSameDerivedType(
504514
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
505515
SetOfDerivedTypePairs inProgress;
506-
return AreSameDerivedType(x, y, false, false, inProgress);
516+
return AreSameDerivedType(x, y, false, false, false, inProgress);
507517
}
508518

509519
bool AreSameDerivedTypeIgnoringTypeParameters(
510520
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
511521
SetOfDerivedTypePairs inProgress;
512-
return AreSameDerivedType(x, y, true, true, inProgress);
522+
return AreSameDerivedType(x, y, true, true, false, inProgress);
523+
}
524+
525+
bool AreSameDerivedTypeIgnoringSequence(
526+
const semantics::DerivedTypeSpec &x, const semantics::DerivedTypeSpec &y) {
527+
SetOfDerivedTypePairs inProgress;
528+
return AreSameDerivedType(x, y, false, false, true, inProgress);
513529
}
514530

515531
static bool AreSameDerivedType(
@@ -536,7 +552,7 @@ static bool AreCompatibleDerivedTypes(const semantics::DerivedTypeSpec *x,
536552
} else {
537553
SetOfDerivedTypePairs inProgress;
538554
if (AreSameDerivedType(*x, *y, ignoreTypeParameterValues,
539-
ignoreLenTypeParameters, inProgress)) {
555+
ignoreLenTypeParameters, false, inProgress)) {
540556
return true;
541557
} else {
542558
return isPolymorphic &&

flang/lib/Semantics/mod-file.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,12 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
13661366
name.ToString(), isIntrinsic.value_or(false))};
13671367
if (!isIntrinsic.value_or(false) && !ancestor) {
13681368
// Already present in the symbol table as a usable non-intrinsic module?
1369+
if (Scope * hermeticScope{context_.currentHermeticModuleFileScope()}) {
1370+
auto it{hermeticScope->find(name)};
1371+
if (it != hermeticScope->end()) {
1372+
return it->second->scope();
1373+
}
1374+
}
13691375
auto it{context_.globalScope().find(name)};
13701376
if (it != context_.globalScope().end()) {
13711377
Scope *scope{it->second->scope()};
@@ -1543,9 +1549,22 @@ Scope *ModFileReader::Read(SourceName name, std::optional<bool> isIntrinsic,
15431549
// Process declarations from the module file
15441550
auto wasModuleFileName{context_.foldingContext().moduleFileName()};
15451551
context_.foldingContext().set_moduleFileName(name);
1552+
// Are there multiple modules in the module file due to it having been
1553+
// created under -fhermetic-module-files? If so, process them first in
1554+
// their own nested scope that will be visible only to USE statements
1555+
// within the module file.
1556+
if (parseTree.v.size() > 1) {
1557+
parser::Program hermeticModules{std::move(parseTree.v)};
1558+
parseTree.v.emplace_back(std::move(hermeticModules.v.front()));
1559+
hermeticModules.v.pop_front();
1560+
Scope &hermeticScope{topScope.MakeScope(Scope::Kind::Global)};
1561+
context_.set_currentHermeticModuleFileScope(&hermeticScope);
1562+
ResolveNames(context_, hermeticModules, hermeticScope);
1563+
}
15461564
GetModuleDependences(context_.moduleDependences(), sourceFile->content());
15471565
ResolveNames(context_, parseTree, topScope);
15481566
context_.foldingContext().set_moduleFileName(wasModuleFileName);
1567+
context_.set_currentHermeticModuleFileScope(nullptr);
15491568
if (!moduleSymbol) {
15501569
// Submodule symbols' storage are owned by their parents' scopes,
15511570
// but their names are not in their parents' dictionaries -- we

flang/lib/Semantics/resolve-names.cpp

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2548,9 +2548,11 @@ void ScopeHandler::PopScope() {
25482548
ConvertToObjectEntity(*pair.second);
25492549
}
25502550
funcResultStack_.Pop();
2551-
// If popping back into a global scope, pop back to the main global scope.
2552-
SetScope(currScope_->parent().IsGlobal() ? context().globalScope()
2553-
: currScope_->parent());
2551+
// If popping back into a global scope, pop back to the top scope.
2552+
Scope *hermetic{context().currentHermeticModuleFileScope()};
2553+
SetScope(currScope_->parent().IsGlobal()
2554+
? (hermetic ? *hermetic : context().globalScope())
2555+
: currScope_->parent());
25542556
}
25552557
void ScopeHandler::SetScope(Scope &scope) {
25562558
currScope_ = &scope;
@@ -3179,6 +3181,111 @@ static bool ConvertToUseError(
31793181
}
31803182
}
31813183

3184+
// Two ultimate symbols are distinct, but they have the same name and come
3185+
// from modules with the same name. At link time, their mangled names
3186+
// would conflict, so they had better resolve to the same definition.
3187+
// Check whether the two ultimate symbols have compatible definitions.
3188+
// Returns true if no further processing is required in DoAddUse().
3189+
static bool CheckCompatibleDistinctUltimates(SemanticsContext &context,
3190+
SourceName location, SourceName localName, const Symbol &localSymbol,
3191+
const Symbol &localUltimate, const Symbol &useUltimate) {
3192+
bool bad{false};
3193+
if (localUltimate.has<GenericDetails>()) {
3194+
if (useUltimate.has<GenericDetails>() ||
3195+
useUltimate.has<SubprogramDetails>() ||
3196+
useUltimate.has<DerivedTypeDetails>()) {
3197+
return false; // can try to merge them
3198+
} else {
3199+
bad = true;
3200+
}
3201+
} else if (useUltimate.has<GenericDetails>()) {
3202+
if (localUltimate.has<SubprogramDetails>() ||
3203+
localUltimate.has<DerivedTypeDetails>()) {
3204+
return false; // can try to merge them
3205+
} else {
3206+
bad = true;
3207+
}
3208+
} else if (localUltimate.has<SubprogramDetails>()) {
3209+
if (useUltimate.has<SubprogramDetails>()) {
3210+
auto localCharacteristics{
3211+
evaluate::characteristics::Procedure::Characterize(
3212+
localUltimate, context.foldingContext())};
3213+
auto useCharacteristics{
3214+
evaluate::characteristics::Procedure::Characterize(
3215+
useUltimate, context.foldingContext())};
3216+
if ((localCharacteristics &&
3217+
(!useCharacteristics ||
3218+
*localCharacteristics != *useCharacteristics)) ||
3219+
(!localCharacteristics && useCharacteristics)) {
3220+
bad = true;
3221+
}
3222+
} else {
3223+
bad = true;
3224+
}
3225+
} else if (useUltimate.has<SubprogramDetails>()) {
3226+
bad = true;
3227+
} else if (const auto *localObject{
3228+
localUltimate.detailsIf<ObjectEntityDetails>()}) {
3229+
if (const auto *useObject{useUltimate.detailsIf<ObjectEntityDetails>()}) {
3230+
auto localType{evaluate::DynamicType::From(localUltimate)};
3231+
auto useType{evaluate::DynamicType::From(useUltimate)};
3232+
if (localUltimate.size() != useUltimate.size() ||
3233+
(localType &&
3234+
(!useType || !localType->IsTkLenCompatibleWith(*useType) ||
3235+
!useType->IsTkLenCompatibleWith(*localType))) ||
3236+
(!localType && useType)) {
3237+
bad = true;
3238+
} else if (IsNamedConstant(localUltimate)) {
3239+
bad = !IsNamedConstant(useUltimate) ||
3240+
!(*localObject->init() == *useObject->init());
3241+
} else {
3242+
bad = IsNamedConstant(useUltimate);
3243+
}
3244+
} else {
3245+
bad = true;
3246+
}
3247+
} else if (useUltimate.has<ObjectEntityDetails>()) {
3248+
bad = true;
3249+
} else if (IsProcedurePointer(localUltimate)) {
3250+
bad = !IsProcedurePointer(useUltimate);
3251+
} else if (IsProcedurePointer(useUltimate)) {
3252+
bad = true;
3253+
} else if (localUltimate.has<DerivedTypeDetails>()) {
3254+
bad = !(useUltimate.has<DerivedTypeDetails>() &&
3255+
evaluate::AreSameDerivedTypeIgnoringSequence(
3256+
DerivedTypeSpec{localUltimate.name(), localUltimate},
3257+
DerivedTypeSpec{useUltimate.name(), useUltimate}));
3258+
} else if (useUltimate.has<DerivedTypeDetails>()) {
3259+
bad = true;
3260+
} else if (localUltimate.has<NamelistDetails>() &&
3261+
useUltimate.has<NamelistDetails>()) {
3262+
} else if (localUltimate.has<CommonBlockDetails>() &&
3263+
useUltimate.has<CommonBlockDetails>()) {
3264+
} else {
3265+
bad = true;
3266+
}
3267+
if (bad) {
3268+
context
3269+
.Say(location,
3270+
"'%s' use-associated from '%s' in module '%s' is incompatible with '%s' from another module"_err_en_US,
3271+
localName, useUltimate.name(),
3272+
useUltimate.owner().GetName().value(), localUltimate.name())
3273+
.Attach(useUltimate.name(), "First declaration"_en_US)
3274+
.Attach(localUltimate.name(), "Other declaration"_en_US);
3275+
return true;
3276+
}
3277+
if (auto *msg{context.Warn(
3278+
common::UsageWarning::CompatibleDeclarationsFromDistinctModules,
3279+
location,
3280+
"'%s' is use-associated from '%s' in two distinct instances of module '%s'"_warn_en_US,
3281+
localName, localUltimate.name(),
3282+
localUltimate.owner().GetName().value())}) {
3283+
msg->Attach(localUltimate.name(), "Previous declaration"_en_US)
3284+
.Attach(useUltimate.name(), "Later declaration"_en_US);
3285+
}
3286+
return true;
3287+
}
3288+
31823289
void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
31833290
Symbol &originalLocal, const Symbol &useSymbol) {
31843291
Symbol *localSymbol{&originalLocal};
@@ -3220,6 +3327,16 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
32203327
return;
32213328
}
32223329

3330+
if (localUltimate.name() == useUltimate.name() &&
3331+
localUltimate.owner().IsModule() && useUltimate.owner().IsModule() &&
3332+
localUltimate.owner().GetName() &&
3333+
localUltimate.owner().GetName() == useUltimate.owner().GetName()) {
3334+
if (CheckCompatibleDistinctUltimates(context(), location, localName,
3335+
*localSymbol, localUltimate, useUltimate)) {
3336+
return;
3337+
}
3338+
}
3339+
32233340
// There are many possible combinations of symbol types that could arrive
32243341
// with the same (local) name vie USE association from distinct modules.
32253342
// Fortran allows a generic interface to share its name with a derived type,
@@ -9375,6 +9492,12 @@ template <typename A> std::set<SourceName> GetUses(const A &x) {
93759492
}
93769493

93779494
bool ResolveNamesVisitor::Pre(const parser::Program &x) {
9495+
if (Scope * hermetic{context().currentHermeticModuleFileScope()}) {
9496+
// Processing either the dependent modules or first module of a
9497+
// hermetic module file; ensure that the hermetic module scope has
9498+
// its implicit rules map entry.
9499+
ImplicitRulesVisitor::BeginScope(*hermetic);
9500+
}
93789501
std::map<SourceName, const parser::ProgramUnit *> modules;
93799502
std::set<SourceName> uses;
93809503
bool disordered{false};

0 commit comments

Comments
 (0)