Skip to content

Commit

Permalink
[flang] Fix bug accessing implicit variable in specification expression
Browse files Browse the repository at this point in the history
A specification expression can reference an implicitly declared variable
in the host procedure. Because we have to process specification parts
before execution parts, this may be the first time we encounter the
variable. We were assuming the variable was implicitly declared in the
scope where it was encountered, leading to an error because local
variables may not be referenced in specification expressions.

The fix is to tentatively create the implicit variable in the host
procedure because that is the only way the specification expression can
be valid. We mark it with the flag `ImplicitOrError` to indicate that
either it must be implicitly defined in the host (by being mentioned in
the execution part) or else its use turned out to be an error.
We need to apply the implicit type rules of the host, which requires
some changes to implicit typing.

Variables in common blocks are allowed to appear in specification expressions
(because they are not locals) but the common block definition may not appear
until after their use. To handle this we create common block symbols and object
entities for each common block object during the `PreSpecificationConstruct`
pass. This allows us to remove the corresponding code in the main visitor and
`commonBlockInfo_.curr`. The change in order of processing causes some
different error messages to be emitted.

Some cleanup is included with this change:
- In `ExpressionAnalyzer`, if an unresolved name is encountered but
  no error has been reported, emit an internal error.
- Change `ImplicitRulesVisitor` to hide the `ImplicitRules` object
  that implements it. Change the interface to pass in names rather
  than having to get the first character of the name.
- Change `DeclareObjectEntity` to have the `attrs` argument default
  to an empty set; that is the typical case.
- In `Pre(parser::SpecificationPart)` use "structured bindings" to
  give names to the pieces that make up a specification-part.
- Enhance `parser::Unwrap` to unwrap `Statement` and `UnlabeledStatement`
  and make use of that in PreSpecificationConstruct.

Differential Revision: https://reviews.llvm.org/D86322
  • Loading branch information
tskeith committed Aug 24, 2020
1 parent 4dec8ec commit b8bfe35
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 94 deletions.
9 changes: 9 additions & 0 deletions flang/include/flang/Parser/tools.h
Expand Up @@ -74,6 +74,15 @@ struct UnwrapperHelper {
}
}

template <typename A, typename B>
static const A *Unwrap(const UnlabeledStatement<B> &x) {
return Unwrap<A>(x.statement);
}
template <typename A, typename B>
static const A *Unwrap(const Statement<B> &x) {
return Unwrap<A>(x.statement);
}

template <typename A, typename B> static const A *Unwrap(B &x) {
if constexpr (std::is_same_v<std::decay_t<A>, std::decay_t<B>>) {
return &x;
Expand Down
5 changes: 3 additions & 2 deletions flang/include/flang/Semantics/symbol.h
Expand Up @@ -386,6 +386,8 @@ class HostAssocDetails {
public:
HostAssocDetails(const Symbol &symbol) : symbol_{symbol} {}
const Symbol &symbol() const { return symbol_; }
bool implicitOrSpecExprError{false};
bool implicitOrExplicitTypeError{false};

private:
SymbolRef symbol_;
Expand Down Expand Up @@ -481,21 +483,20 @@ class Symbol {
Subroutine, // symbol is a subroutine
StmtFunction, // symbol is a statement function (Function is set too)
Implicit, // symbol is implicitly typed
ImplicitOrError, // symbol must be implicitly typed or it's an error
ModFile, // symbol came from .mod file
ParentComp, // symbol is the "parent component" of an extended type
CrayPointer, CrayPointee,
LocalityLocal, // named in LOCAL locality-spec
LocalityLocalInit, // named in LOCAL_INIT locality-spec
LocalityShared, // named in SHARED locality-spec
InDataStmt, // initialized in a DATA statement

// OpenACC data-sharing attribute
AccPrivate, AccFirstPrivate, AccShared,
// OpenACC data-mapping attribute
AccCopyIn, AccCopyOut, AccCreate, AccDelete, AccPresent,
// OpenACC miscellaneous flags
AccCommonBlock, AccThreadPrivate, AccReduction, AccNone, AccPreDetermined,

// OpenMP data-sharing attribute
OmpShared, OmpPrivate, OmpLinear, OmpFirstPrivate, OmpLastPrivate,
// OpenMP data-mapping attribute
Expand Down
1 change: 1 addition & 0 deletions flang/include/flang/Semantics/tools.h
Expand Up @@ -100,6 +100,7 @@ bool HasIntrinsicTypeName(const Symbol &);
bool IsSeparateModuleProcedureInterface(const Symbol *);
bool IsAutomatic(const Symbol &);
bool HasAlternateReturns(const Symbol &);
bool InCommonBlock(const Symbol &);

// Return an ultimate component of type that matches predicate, or nullptr.
const Symbol *FindUltimateComponent(const DerivedTypeSpec &type,
Expand Down
21 changes: 20 additions & 1 deletion flang/lib/Semantics/check-declarations.cpp
Expand Up @@ -61,6 +61,7 @@ class CheckHelper {
void CheckSubprogram(const Symbol &, const SubprogramDetails &);
void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &);
void CheckDerivedType(const Symbol &, const DerivedTypeDetails &);
void CheckHostAssoc(const Symbol &, const HostAssocDetails &);
void CheckGeneric(const Symbol &, const GenericDetails &);
std::optional<std::vector<Procedure>> Characterize(const SymbolVector &);
bool CheckDefinedOperator(const SourceName &, const GenericKind &,
Expand Down Expand Up @@ -147,7 +148,10 @@ void CheckHelper::Check(const Symbol &symbol) {
CheckVolatile(symbol, isAssociated, derived);
}
if (isAssociated) {
return; // only care about checking VOLATILE on associated symbols
if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
CheckHostAssoc(symbol, *details);
}
return; // no other checks on associated symbols
}
if (IsPointer(symbol)) {
CheckPointer(symbol);
Expand Down Expand Up @@ -758,6 +762,21 @@ void CheckHelper::CheckDerivedType(
}
}

void CheckHelper::CheckHostAssoc(
const Symbol &symbol, const HostAssocDetails &details) {
const Symbol &hostSymbol{details.symbol()};
if (hostSymbol.test(Symbol::Flag::ImplicitOrError)) {
if (details.implicitOrSpecExprError) {
messages_.Say("Implicitly typed local entity '%s' not allowed in"
" specification expression"_err_en_US,
symbol.name());
} else if (details.implicitOrExplicitTypeError) {
messages_.Say(
"No explicit type declared for '%s'"_err_en_US, symbol.name());
}
}
}

void CheckHelper::CheckGeneric(
const Symbol &symbol, const GenericDetails &details) {
const SymbolVector &specifics{details.specificProcs()};
Expand Down
5 changes: 0 additions & 5 deletions flang/lib/Semantics/compute-offsets.cpp
Expand Up @@ -81,11 +81,6 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
equivalenceBlock_.clear();
}

static bool InCommonBlock(const Symbol &symbol) {
const auto *details{symbol.detailsIf<ObjectEntityDetails>()};
return details && details->commonBlock();
}

void ComputeOffsetsHelper::DoScope(Scope &scope) {
if (scope.symbol() && scope.IsParameterizedDerivedType()) {
return; // only process instantiations of parameterized derived types
Expand Down
5 changes: 4 additions & 1 deletion flang/lib/Semantics/expression.cpp
Expand Up @@ -680,7 +680,10 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
if (std::optional<int> kind{IsImpliedDo(n.source)}) {
return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>(
*kind, AsExpr(ImpliedDoIndex{n.source})));
} else if (context_.HasError(n) || !n.symbol) {
} else if (context_.HasError(n)) {
return std::nullopt;
} else if (!n.symbol) {
SayAt(n, "Internal error: unresolved name '%s'"_err_en_US, n.source);
return std::nullopt;
} else {
const Symbol &ultimate{n.symbol->GetUltimate()};
Expand Down

0 comments on commit b8bfe35

Please sign in to comment.