[flang] Semantics for ENTRY

initial test passes

Move some checks to check-declarations

Fix bugs found in testing

Get tests all passing

Allow declaration statements for function result to follow ENTRY

Fix another bug

Original-commit: flang-compiler/f18@e82cfee432
Reviewed-on: https://github.com/flang-compiler/f18/pull/1086
This commit is contained in:
peter klausler 2020-03-19 16:31:10 -07:00
parent 55a500989a
commit c42f6314eb
11 changed files with 727 additions and 241 deletions

View File

@ -61,6 +61,9 @@ public:
bool isFunction() const { return result_ != nullptr; }
bool isInterface() const { return isInterface_; }
void set_isInterface(bool value = true) { isInterface_ = value; }
Scope *entryScope() { return entryScope_; }
const Scope *entryScope() const { return entryScope_; }
void set_entryScope(Scope &scope) { entryScope_ = &scope; }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
const Symbol &result() const {
@ -82,8 +85,10 @@ private:
MaybeExpr bindName_;
std::vector<Symbol *> dummyArgs_; // nullptr -> alternate return indicator
Symbol *result_{nullptr};
Scope *entryScope_{nullptr}; // if ENTRY, points to subprogram's scope
MaybeExpr stmtFunction_;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SubprogramDetails &);
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const SubprogramDetails &);
};
// For SubprogramNameDetails, the kind indicates whether it is the name
@ -115,17 +120,19 @@ public:
void set_type(const DeclTypeSpec &);
void ReplaceType(const DeclTypeSpec &);
bool isDummy() const { return isDummy_; }
void set_isDummy(bool value = true) { isDummy_ = value; }
bool isFuncResult() const { return isFuncResult_; }
void set_funcResult(bool x) { isFuncResult_ = x; }
MaybeExpr bindName() const { return bindName_; }
void set_bindName(MaybeExpr &&expr) { bindName_ = std::move(expr); }
private:
bool isDummy_;
bool isDummy_{false};
bool isFuncResult_{false};
const DeclTypeSpec *type_{nullptr};
MaybeExpr bindName_;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const EntityDetails &);
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const EntityDetails &);
};
// Symbol is associated with a name or expression in a SELECT TYPE or ASSOCIATE.
@ -180,7 +187,8 @@ private:
ArraySpec shape_;
ArraySpec coshape_;
const Symbol *commonBlock_{nullptr}; // common block this object is in
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ObjectEntityDetails &);
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const ObjectEntityDetails &);
};
// Mixin for details with passed-object dummy argument.
@ -217,7 +225,8 @@ public:
private:
ProcInterface interface_;
std::optional<const Symbol *> init_;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ProcEntityDetails &);
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const ProcEntityDetails &);
};
// These derived type details represent the characteristics of a derived
@ -263,7 +272,8 @@ private:
std::list<SourceName> componentNames_;
bool sequence_{false};
bool isForwardReferenced_{false};
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DerivedTypeDetails &);
friend llvm::raw_ostream &operator<<(
llvm::raw_ostream &, const DerivedTypeDetails &);
};
class ProcBindingDetails : public WithPassArg {
@ -570,7 +580,6 @@ public:
bool IsFuncResult() const;
bool IsObjectArray() const;
bool IsSubprogram() const;
bool IsSeparateModuleProc() const;
bool IsFromModFile() const;
bool HasExplicitInterface() const {
return std::visit(
@ -662,7 +671,8 @@ private:
Symbol() {} // only created in class Symbols
const std::string GetDetailsName() const;
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Symbol &);
friend llvm::raw_ostream &DumpForUnparse(llvm::raw_ostream &, const Symbol &, bool);
friend llvm::raw_ostream &DumpForUnparse(
llvm::raw_ostream &, const Symbol &, bool);
// If a derived type's symbol refers to an extended derived type,
// return the parent component's symbol. The scope of the derived type

View File

@ -108,6 +108,7 @@ bool IsSaved(const Symbol &);
bool CanBeTypeBoundProc(const Symbol *);
bool IsInitialized(const Symbol &);
bool HasIntrinsicTypeName(const Symbol &);
bool IsSeparateModuleProcedureInterface(const Symbol *);
// Return an ultimate component of type that matches predicate, or nullptr.
const Symbol *FindUltimateComponent(const DerivedTypeSpec &type,
@ -164,7 +165,7 @@ inline bool IsAssumedRankArray(const Symbol &symbol) {
return details && details->IsAssumedRank();
}
bool IsAssumedLengthCharacter(const Symbol &);
bool IsAssumedLengthExternalCharacterFunction(const Symbol &);
bool IsExternal(const Symbol &);
// Is the symbol modifiable in this scope
std::optional<parser::MessageFixedText> WhyNotModifiable(
const Symbol &, const Scope &);
@ -200,6 +201,11 @@ std::list<SourceName> OrderParameterNames(const Symbol &);
const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &, DerivedTypeSpec &&,
SemanticsContext &, DeclTypeSpec::Category = DeclTypeSpec::TypeDerived);
// When a subprogram defined in a submodule defines a separate module
// procedure whose interface is defined in an ancestor (sub)module,
// returns a pointer to that interface, else null.
const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *);
// Determines whether an object might be visible outside a
// pure function (C1594); returns a non-null Symbol pointer for
// diagnostic purposes if so.

View File

@ -24,6 +24,7 @@ namespace Fortran::semantics {
using evaluate::characteristics::DummyArgument;
using evaluate::characteristics::DummyDataObject;
using evaluate::characteristics::DummyProcedure;
using evaluate::characteristics::FunctionResult;
using evaluate::characteristics::Procedure;
class CheckHelper {
@ -109,6 +110,7 @@ private:
}
}
}
bool IsResultOkToDiffer(const FunctionResult &);
SemanticsContext &context_;
evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
@ -208,7 +210,8 @@ void CheckHelper::Check(const Symbol &symbol) {
}
if (type) { // Section 7.2, paragraph 7
bool canHaveAssumedParameter{IsNamedConstant(symbol) ||
IsAssumedLengthExternalCharacterFunction(symbol) || // C722
(IsAssumedLengthCharacter(symbol) && // C722
IsExternal(symbol)) ||
symbol.test(Symbol::Flag::ParentComp)};
if (!IsStmtFunctionDummy(symbol)) { // C726
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
@ -239,7 +242,7 @@ void CheckHelper::Check(const Symbol &symbol) {
}
}
}
if (IsAssumedLengthExternalCharacterFunction(symbol)) { // C723
if (IsAssumedLengthCharacter(symbol) && IsExternal(symbol)) { // C723
if (symbol.attrs().test(Attr::RECURSIVE)) {
messages_.Say(
"An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US);
@ -270,6 +273,16 @@ void CheckHelper::Check(const Symbol &symbol) {
symbol.Rank() == 0) { // C830
messages_.Say("CONTIGUOUS POINTER must be an array"_err_en_US);
}
if (IsDummy(symbol)) {
if (IsNamedConstant(symbol)) {
messages_.Say(
"A dummy argument may not also be a named constant"_err_en_US);
}
if (IsSaved(symbol)) {
messages_.Say(
"A dummy argument may not have the SAVE attribute"_err_en_US);
}
}
}
void CheckHelper::CheckValue(
@ -600,13 +613,67 @@ private:
SemanticsContext &context;
};
// 15.6.2.6 para 3 - can the result of an ENTRY differ from its function?
bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) {
if (result.attrs.test(FunctionResult::Attr::Allocatable) ||
result.attrs.test(FunctionResult::Attr::Pointer)) {
return false;
}
const auto *typeAndShape{result.GetTypeAndShape()};
if (!typeAndShape || typeAndShape->Rank() != 0) {
return false;
}
auto category{typeAndShape->type().category()};
if (category == TypeCategory::Character ||
category == TypeCategory::Derived) {
return false;
}
int kind{typeAndShape->type().kind()};
return kind == context_.GetDefaultKind(category) ||
(category == TypeCategory::Real &&
kind == context_.doublePrecisionKind());
}
void CheckHelper::CheckSubprogram(
const Symbol &symbol, const SubprogramDetails &) {
const Scope &scope{symbol.owner()};
if (symbol.attrs().test(Attr::MODULE) && scope.IsSubmodule()) {
if (const Symbol * iface{scope.parent().FindSymbol(symbol.name())}) {
const Symbol &symbol, const SubprogramDetails &details) {
if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
SubprogramMatchHelper{context_}.Check(symbol, *iface);
}
if (const Scope * entryScope{details.entryScope()}) {
// ENTRY 15.6.2.6, esp. C1571
std::optional<parser::MessageFixedText> error;
const Symbol *subprogram{entryScope->symbol()};
const SubprogramDetails *subprogramDetails{nullptr};
if (subprogram) {
subprogramDetails = subprogram->detailsIf<SubprogramDetails>();
}
if (entryScope->kind() != Scope::Kind::Subprogram) {
error = "ENTRY may appear only in a subroutine or function"_err_en_US;
} else if (!(entryScope->parent().IsGlobal() ||
entryScope->parent().IsModule() ||
entryScope->parent().IsSubmodule())) {
error = "ENTRY may not appear in an internal subprogram"_err_en_US;
} else if (FindSeparateModuleSubprogramInterface(subprogram)) {
error = "ENTRY may not appear in a separate module procedure"_err_en_US;
} else if (subprogramDetails && details.isFunction() &&
subprogramDetails->isFunction()) {
auto result{FunctionResult::Characterize(
details.result(), context_.intrinsics())};
auto subpResult{FunctionResult::Characterize(
subprogramDetails->result(), context_.intrinsics())};
if (result && subpResult && *result != *subpResult &&
(!IsResultOkToDiffer(*result) || !IsResultOkToDiffer(*subpResult))) {
error =
"Result of ENTRY is not compatible with result of containing function"_err_en_US;
}
}
if (error) {
if (auto *msg{messages_.Say(symbol.name(), *error)}) {
if (subprogram) {
msg->Attach(subprogram->name(), "Containing subprogram"_en_US);
}
}
}
}
}

View File

@ -1889,7 +1889,7 @@ void ExpressionAnalyzer::CheckForBadRecursion(
if (proc.attrs().test(semantics::Attr::NON_RECURSIVE)) { // 15.6.2.1(3)
msg = Say("NON_RECURSIVE procedure '%s' cannot call itself"_err_en_US,
callSite);
} else if (IsAssumedLengthExternalCharacterFunction(proc)) {
} else if (IsAssumedLengthCharacter(proc) && IsExternal(proc)) {
msg = Say( // 15.6.2.1(3)
"Assumed-length CHARACTER(*) function '%s' cannot call itself"_err_en_US,
callSite);
@ -2046,7 +2046,8 @@ static bool IsExternalCalledImplicitly(
if (const auto *symbol{proc.GetSymbol()}) {
return symbol->has<semantics::SubprogramDetails>() &&
symbol->owner().IsGlobal() &&
!symbol->scope()->sourceRange().Contains(callSite);
(!symbol->scope() /*ENTRY*/ ||
!symbol->scope()->sourceRange().Contains(callSite));
} else {
return false;
}

View File

@ -69,8 +69,8 @@ static std::string CheckSum(const std::string_view &);
// Collect symbols needed for a subprogram interface
class SubprogramSymbolCollector {
public:
SubprogramSymbolCollector(const Symbol &symbol)
: symbol_{symbol}, scope_{DEREF(symbol.scope())} {}
SubprogramSymbolCollector(const Symbol &symbol, const Scope &scope)
: symbol_{symbol}, scope_{scope} {}
const SymbolVector &symbols() const { return need_; }
const std::set<SourceName> &imports() const { return imports_; }
void Collect();
@ -335,12 +335,14 @@ void ModFileWriter::PutSubprogram(const Symbol &symbol) {
}
os << '\n';
// walk symbols, collect ones needed
ModFileWriter writer{context_};
// walk symbols, collect ones needed for interface
const Scope &scope{
details.entryScope() ? *details.entryScope() : DEREF(symbol.scope())};
SubprogramSymbolCollector collector{symbol, scope};
collector.Collect();
std::string typeBindingsBuf;
llvm::raw_string_ostream typeBindings{typeBindingsBuf};
SubprogramSymbolCollector collector{symbol};
collector.Collect();
ModFileWriter writer{context_};
for (const Symbol &need : collector.symbols()) {
writer.PutSymbol(typeBindings, need);
}

View File

@ -43,7 +43,7 @@ namespace Fortran::semantics {
using namespace parser::literals;
template<typename T> using Indirection = common::Indirection<T>;
template <typename T> using Indirection = common::Indirection<T>;
using Message = parser::Message;
using Messages = parser::Messages;
using MessageFixedText = parser::MessageFixedText;
@ -114,7 +114,7 @@ public:
// Emit a message about a SourceName
Message &Say(const SourceName &, MessageFixedText &&);
// Emit a formatted message associated with a source location.
template<typename... A>
template <typename... A>
Message &Say(const SourceName &source, MessageFixedText &&msg, A &&... args) {
return context_->Say(source, std::move(msg), std::forward<A>(args)...);
}
@ -142,8 +142,9 @@ public:
BaseVisitor() { DIE("BaseVisitor: default-constructed"); }
BaseVisitor(
SemanticsContext &c, ResolveNamesVisitor &v, ImplicitRulesMap &rules)
: implicitRulesMap_{&rules}, this_{&v}, context_{&c}, messageHandler_{c} {}
template<typename T> void Walk(const T &);
: implicitRulesMap_{&rules}, this_{&v}, context_{&c}, messageHandler_{c} {
}
template <typename T> void Walk(const T &);
MessageHandler &messageHandler() { return messageHandler_; }
const std::optional<SourceName> &currStmtSource() {
@ -158,15 +159,15 @@ public:
// It is not in any scope and always has MiscDetails.
void MakePlaceholder(const parser::Name &, MiscDetails::Kind);
template<typename T> common::IfNoLvalue<T, T> FoldExpr(T &&expr) {
template <typename T> common::IfNoLvalue<T, T> FoldExpr(T &&expr) {
return evaluate::Fold(GetFoldingContext(), std::move(expr));
}
template<typename T> MaybeExpr EvaluateExpr(const T &expr) {
template <typename T> MaybeExpr EvaluateExpr(const T &expr) {
return FoldExpr(AnalyzeExpr(*context_, expr));
}
template<typename T>
template <typename T>
MaybeExpr EvaluateConvertedExpr(
const Symbol &symbol, const T &expr, parser::CharBlock source) {
if (context().HasError(symbol)) {
@ -193,7 +194,7 @@ public:
return FoldExpr(std::move(*converted));
}
template<typename T> MaybeIntExpr EvaluateIntExpr(const T &expr) {
template <typename T> MaybeIntExpr EvaluateIntExpr(const T &expr) {
if (MaybeExpr maybeExpr{EvaluateExpr(expr)}) {
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
return std::move(*intExpr);
@ -202,7 +203,7 @@ public:
return std::nullopt;
}
template<typename T>
template <typename T>
MaybeSubscriptIntExpr EvaluateSubscriptIntExpr(const T &expr) {
if (MaybeIntExpr maybeIntExpr{EvaluateIntExpr(expr)}) {
return FoldExpr(evaluate::ConvertToType<evaluate::SubscriptInteger>(
@ -212,10 +213,10 @@ public:
}
}
template<typename... A> Message &Say(A &&... args) {
template <typename... A> Message &Say(A &&... args) {
return messageHandler_.Say(std::forward<A>(args)...);
}
template<typename... A>
template <typename... A>
Message &Say(
const parser::Name &name, MessageFixedText &&text, const A &... args) {
return messageHandler_.Say(name.source, std::move(text), args...);
@ -281,16 +282,21 @@ protected:
Attr AccessSpecToAttr(const parser::AccessSpec &x) {
switch (x.v) {
case parser::AccessSpec::Kind::Public: return Attr::PUBLIC;
case parser::AccessSpec::Kind::Private: return Attr::PRIVATE;
case parser::AccessSpec::Kind::Public:
return Attr::PUBLIC;
case parser::AccessSpec::Kind::Private:
return Attr::PRIVATE;
}
common::die("unreachable"); // suppress g++ warning
}
Attr IntentSpecToAttr(const parser::IntentSpec &x) {
switch (x.v) {
case parser::IntentSpec::Intent::In: return Attr::INTENT_IN;
case parser::IntentSpec::Intent::Out: return Attr::INTENT_OUT;
case parser::IntentSpec::Intent::InOut: return Attr::INTENT_INOUT;
case parser::IntentSpec::Intent::In:
return Attr::INTENT_IN;
case parser::IntentSpec::Intent::Out:
return Attr::INTENT_OUT;
case parser::IntentSpec::Intent::InOut:
return Attr::INTENT_INOUT;
}
common::die("unreachable"); // suppress g++ warning
}
@ -336,7 +342,7 @@ protected:
}
// Walk the parse tree of a type spec and return the DeclTypeSpec for it.
template<typename T>
template <typename T>
const DeclTypeSpec *ProcessTypeSpec(const T &x, bool allowForward = false) {
auto restorer{common::ScopedSet(state_, State{})};
set_allowForwardReferenceToDerivedType(allowForward);
@ -446,6 +452,8 @@ public:
Scope &currScope() { return DEREF(currScope_); }
// The enclosing scope, skipping blocks and derived types.
// TODO: Will return the scope of a FORALL or implied DO loop; is this ok?
// If not, should call FindProgramUnitContaining() instead.
Scope &InclusiveScope();
// Create a new scope and push it on the scope stack.
@ -454,12 +462,12 @@ public:
void PopScope();
void SetScope(Scope &);
template<typename T> bool Pre(const parser::Statement<T> &x) {
template <typename T> bool Pre(const parser::Statement<T> &x) {
messageHandler().set_currStmtSource(x.source);
currScope_->AddSourceRange(x.source);
return true;
}
template<typename T> void Post(const parser::Statement<T> &) {
template <typename T> void Post(const parser::Statement<T> &) {
messageHandler().set_currStmtSource(std::nullopt);
}
@ -500,19 +508,19 @@ public:
Symbol &MakeSymbol(const SourceName &, Attrs = Attrs{});
Symbol &MakeSymbol(const parser::Name &, Attrs = Attrs{});
template<typename D>
template <typename D>
common::IfNoLvalue<Symbol &, D> MakeSymbol(
const parser::Name &name, D &&details) {
return MakeSymbol(name, Attrs{}, std::move(details));
}
template<typename D>
template <typename D>
common::IfNoLvalue<Symbol &, D> MakeSymbol(
const parser::Name &name, const Attrs &attrs, D &&details) {
return Resolve(name, MakeSymbol(name.source, attrs, std::move(details)));
}
template<typename D>
template <typename D>
common::IfNoLvalue<Symbol &, D> MakeSymbol(
const SourceName &name, const Attrs &attrs, D &&details) {
// Note: don't use FindSymbol here. If this is a derived type scope,
@ -658,6 +666,8 @@ public:
void Post(const parser::SubroutineStmt &);
bool Pre(const parser::FunctionStmt &);
void Post(const parser::FunctionStmt &);
bool Pre(const parser::EntryStmt &);
void Post(const parser::EntryStmt &);
bool Pre(const parser::InterfaceBody::Subroutine &);
void Post(const parser::InterfaceBody::Subroutine &);
bool Pre(const parser::InterfaceBody::Function &);
@ -675,6 +685,7 @@ public:
protected:
// Set when we see a stmt function that is really an array element assignment
bool badStmtFuncFound_{false};
bool inExecutionPart_{false};
private:
// Info about the current function: parse tree of the type in the PrefixSpec;
@ -687,6 +698,7 @@ private:
} funcInfo_;
// Create a subprogram symbol in the current scope and push a new scope.
void CheckExtantExternal(const parser::Name &, Symbol::Flag);
Symbol &PushSubprogramScope(const parser::Name &, Symbol::Flag);
Symbol *GetSpecificFromGeneric(const parser::Name &);
SubprogramDetails &PostSubprogramStmt(const parser::Name &);
@ -903,7 +915,7 @@ private:
// Declare an object or procedure entity.
// T is one of: EntityDetails, ObjectEntityDetails, ProcEntityDetails
template<typename T>
template <typename T>
Symbol &DeclareEntity(const parser::Name &name, Attrs attrs) {
Symbol &symbol{MakeSymbol(name, attrs)};
if (symbol.has<T>()) {
@ -1036,10 +1048,10 @@ private:
};
std::vector<Association> associationStack_;
template<typename T> bool CheckDef(const T &t) {
template <typename T> bool CheckDef(const T &t) {
return CheckDef(std::get<std::optional<parser::Name>>(t));
}
template<typename T> void CheckRef(const T &t) {
template <typename T> void CheckRef(const T &t) {
CheckRef(std::get<std::optional<parser::Name>>(t));
}
bool CheckDef(const std::optional<parser::Name> &);
@ -1128,8 +1140,10 @@ bool OmpVisitor::NeedsScope(const parser::OpenMPBlockConstruct &x) {
switch (beginDir.v) {
case parser::OmpBlockDirective::Directive::TargetData:
case parser::OmpBlockDirective::Directive::Master:
case parser::OmpBlockDirective::Directive::Ordered: return false;
default: return true;
case parser::OmpBlockDirective::Directive::Ordered:
return false;
default:
return true;
}
}
@ -1158,10 +1172,10 @@ public:
SemanticsContext &context, ResolveNamesVisitor &resolver)
: context_{context}, resolver_{resolver} {}
template<typename A> void Walk(const A &x) { parser::Walk(x, *this); }
template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
template<typename A> bool Pre(const A &) { return true; }
template<typename A> void Post(const A &) {}
template <typename A> bool Pre(const A &) { return true; }
template <typename A> void Post(const A &) {}
bool Pre(const parser::SpecificationPart &x) {
Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
@ -1350,8 +1364,8 @@ public:
}
// 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 &) {}
template <typename T> bool Pre(const T &) { return true; }
template <typename T> void Post(const T &) {}
bool Pre(const parser::SpecificationPart &);
void Post(const parser::Program &);
@ -1360,7 +1374,7 @@ public:
void Post(const parser::AllocateObject &);
bool Pre(const parser::PointerAssignmentStmt &);
void Post(const parser::Designator &);
template<typename A, typename B>
template <typename A, typename B>
void Post(const parser::LoopBounds<A, B> &x) {
ResolveName(*parser::Unwrap<parser::Name>(x.name));
}
@ -1465,10 +1479,14 @@ void ImplicitRules::SetTypeMapping(const DeclTypeSpec &type,
// Return '\0' for the char after 'z'.
char ImplicitRules::Incr(char ch) {
switch (ch) {
case 'i': return 'j';
case 'r': return 's';
case 'z': return '\0';
default: return ch + 1;
case 'i':
return 'j';
case 'r':
return 's';
case 'z':
return '\0';
default:
return ch + 1;
}
}
@ -1491,7 +1509,7 @@ void ShowImplicitRule(
}
}
template<typename T> void BaseVisitor::Walk(const T &x) {
template <typename T> void BaseVisitor::Walk(const T &x) {
parser::Walk(x, *this_);
}
@ -1657,14 +1675,17 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
switch (spec->category()) {
case DeclTypeSpec::Numeric:
case DeclTypeSpec::Logical:
case DeclTypeSpec::Character: typeSpec.declTypeSpec = spec; break;
case DeclTypeSpec::Character:
typeSpec.declTypeSpec = spec;
break;
case DeclTypeSpec::TypeDerived:
if (const DerivedTypeSpec * derived{spec->AsDerived()}) {
CheckForAbstractType(derived->typeSymbol()); // C703
typeSpec.declTypeSpec = spec;
}
break;
default: CRASH_NO_CASE;
default:
CRASH_NO_CASE;
}
}
}
@ -2732,6 +2753,7 @@ bool SubprogramVisitor::Pre(const parser::SubroutineStmt &) {
bool SubprogramVisitor::Pre(const parser::FunctionStmt &) {
return BeginAttrs();
}
bool SubprogramVisitor::Pre(const parser::EntryStmt &) { return BeginAttrs(); }
void SubprogramVisitor::Post(const parser::SubroutineStmt &stmt) {
const auto &name{std::get<parser::Name>(stmt.t)};
@ -2768,7 +2790,7 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
&MakeSymbol(*funcResultName, std::move(funcResultDetails));
details.set_result(*funcInfo_.resultSymbol);
// C1560. TODO also enforce on entry names when entry implemented
// C1560.
if (funcInfo_.resultName && funcInfo_.resultName->source == name.source) {
Say(funcInfo_.resultName->source,
"The function name should not appear in RESULT, references to '%s' "
@ -2782,6 +2804,9 @@ void SubprogramVisitor::Post(const parser::FunctionStmt &stmt) {
Resolve(*funcInfo_.resultName, funcInfo_.resultSymbol);
}
name.symbol = currScope().symbol(); // must not be function result symbol
// Clear the RESULT() name now in case an ENTRY statement in the implicit-part
// has a RESULT() suffix.
funcInfo_.resultName = nullptr;
}
SubprogramDetails &SubprogramVisitor::PostSubprogramStmt(
@ -2796,13 +2821,160 @@ SubprogramDetails &SubprogramVisitor::PostSubprogramStmt(
return symbol.get<SubprogramDetails>();
}
void SubprogramVisitor::Post(const parser::EntryStmt &stmt) {
auto attrs{EndAttrs()}; // needs to be called even if early return
Scope &inclusiveScope{InclusiveScope()};
const Symbol *subprogram{inclusiveScope.symbol()};
if (!subprogram) {
CHECK(context().AnyFatalError());
return;
}
const auto &name{std::get<parser::Name>(stmt.t)};
const auto *parentDetails{subprogram->detailsIf<SubprogramDetails>()};
bool inFunction{parentDetails && parentDetails->isFunction()};
const parser::Name *resultName{funcInfo_.resultName};
if (resultName) { // RESULT(result) is present
funcInfo_.resultName = nullptr;
if (!inFunction) {
Say2(resultName->source,
"RESULT(%s) may appear only in a function"_err_en_US,
subprogram->name(), "Containing subprogram"_en_US);
} else if (resultName->source == subprogram->name()) { // C1574
Say2(resultName->source,
"RESULT(%s) may not have the same name as the function"_err_en_US,
subprogram->name(), "Containing function"_en_US);
} else if (const Symbol *
symbol{FindSymbol(inclusiveScope.parent(), *resultName)}) { // C1574
if (const auto *details{symbol->detailsIf<SubprogramDetails>()}) {
if (details->entryScope() == &inclusiveScope) {
Say2(resultName->source,
"RESULT(%s) may not have the same name as an ENTRY in the function"_err_en_US,
symbol->name(), "Conflicting ENTRY"_en_US);
}
}
}
if (Symbol * symbol{FindSymbol(name)}) { // C1570
// When RESULT() appears, ENTRY name can't have been already declared
if (inclusiveScope.Contains(symbol->owner())) {
Say2(name,
"ENTRY name '%s' may not be declared when RESULT() is present"_err_en_US,
*symbol, "Previous declaration of '%s'"_en_US);
}
}
if (resultName->source == name.source) {
// ignore RESULT() hereafter when it's the same name as the ENTRY
resultName = nullptr;
}
}
SubprogramDetails entryDetails;
entryDetails.set_entryScope(inclusiveScope);
if (inFunction) {
// Create the entity to hold the function result, if necessary.
Symbol *resultSymbol{nullptr};
auto &effectiveResultName{*(resultName ? resultName : &name)};
resultSymbol = FindInScope(currScope(), effectiveResultName);
if (resultSymbol) { // C1574
std::visit(
common::visitors{[](EntityDetails &x) { x.set_funcResult(true); },
[](ObjectEntityDetails &x) { x.set_funcResult(true); },
[](ProcEntityDetails &x) { x.set_funcResult(true); },
[&](const auto &) {
Say2(effectiveResultName.source,
"'%s' was previously declared as an item that may not be used as a function result"_err_en_US,
resultSymbol->name(), "Previous declaration of '%s'"_en_US);
}},
resultSymbol->details());
} else if (inExecutionPart_) {
ObjectEntityDetails entity;
entity.set_funcResult(true);
resultSymbol = &MakeSymbol(effectiveResultName, std::move(entity));
ApplyImplicitRules(*resultSymbol);
} else {
EntityDetails entity;
entity.set_funcResult(true);
resultSymbol = &MakeSymbol(effectiveResultName, std::move(entity));
}
if (!resultName) {
name.symbol = nullptr; // symbol will be used for entry point below
}
entryDetails.set_result(*resultSymbol);
}
for (const auto &dummyArg : std::get<std::list<parser::DummyArg>>(stmt.t)) {
if (const auto *dummyName{std::get_if<parser::Name>(&dummyArg.u)}) {
Symbol *dummy{FindSymbol(*dummyName)};
if (dummy) {
std::visit(
common::visitors{[](EntityDetails &x) { x.set_isDummy(); },
[](ObjectEntityDetails &x) { x.set_isDummy(); },
[](ProcEntityDetails &x) { x.set_isDummy(); },
[&](const auto &) {
Say2(dummyName->source,
"ENTRY dummy argument '%s' is previously declared as an item that may not be used as a dummy argument"_err_en_US,
dummy->name(), "Previous declaration of '%s'"_en_US);
}},
dummy->details());
} else {
dummy = &MakeSymbol(*dummyName, EntityDetails(true));
}
entryDetails.add_dummyArg(*dummy);
} else {
if (inFunction) { // C1573
Say(name,
"ENTRY in a function may not have an alternate return dummy argument"_err_en_US);
break;
}
entryDetails.add_alternateReturn();
}
}
Symbol::Flag subpFlag{
inFunction ? Symbol::Flag::Function : Symbol::Flag::Subroutine};
CheckExtantExternal(name, subpFlag);
Scope &outer{inclusiveScope.parent()}; // global or module scope
if (Symbol * extant{FindSymbol(outer, name)}) {
if (extant->has<ProcEntityDetails>()) {
if (!extant->test(subpFlag)) {
Say2(name,
subpFlag == Symbol::Flag::Function
? "'%s' was previously called as a subroutine"_err_en_US
: "'%s' was previously called as a function"_err_en_US,
*extant, "Previous call of '%s'"_en_US);
}
if (extant->attrs().test(Attr::PRIVATE)) {
attrs.set(Attr::PRIVATE);
}
outer.erase(extant->name());
} else {
if (outer.IsGlobal()) {
Say2(name, "'%s' is already defined as a global identifier"_err_en_US,
*extant, "Previous definition of '%s'"_en_US);
} else {
SayAlreadyDeclared(name, *extant);
}
return;
}
}
if (outer.IsModule() && !attrs.test(Attr::PRIVATE)) {
attrs.set(Attr::PUBLIC);
}
Symbol &entrySymbol{MakeSymbol(outer, name.source, attrs)};
entrySymbol.set_details(std::move(entryDetails));
if (outer.IsGlobal()) {
MakeExternal(entrySymbol);
}
SetBindNameOn(entrySymbol);
entrySymbol.set(subpFlag);
Resolve(name, entrySymbol);
}
// A subprogram declared with MODULE PROCEDURE
bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) {
auto *symbol{FindSymbol(name)};
if (symbol && symbol->has<SubprogramNameDetails>()) {
symbol = FindSymbol(currScope().parent(), name);
}
if (!symbol || !symbol->IsSeparateModuleProc()) {
if (!IsSeparateModuleProcedureInterface(symbol)) {
Say(name, "'%s' was not declared a separate module procedure"_err_en_US);
return false;
}
@ -2831,26 +3003,22 @@ bool SubprogramVisitor::BeginMpSubprogram(const parser::Name &name) {
// A subprogram declared with SUBROUTINE or FUNCTION
bool SubprogramVisitor::BeginSubprogram(
const parser::Name &name, Symbol::Flag subpFlag, bool hasModulePrefix) {
if (hasModulePrefix && !inInterfaceBlock()) {
auto *symbol{FindSymbol(currScope().parent(), name)};
if (!symbol || !symbol->IsSeparateModuleProc()) {
if (hasModulePrefix && !inInterfaceBlock() &&
!IsSeparateModuleProcedureInterface(
FindSymbol(currScope().parent(), name))) {
Say(name, "'%s' was not declared a separate module procedure"_err_en_US);
return false;
}
}
PushSubprogramScope(name, subpFlag);
return true;
}
void SubprogramVisitor::EndSubprogram() { PopScope(); }
Symbol &SubprogramVisitor::PushSubprogramScope(
void SubprogramVisitor::CheckExtantExternal(
const parser::Name &name, Symbol::Flag subpFlag) {
auto *symbol{GetSpecificFromGeneric(name)};
if (!symbol) {
if (auto *prev{FindSymbol(name)}) {
if (prev->attrs().test(Attr::EXTERNAL) &&
prev->has<ProcEntityDetails>()) {
if (prev->attrs().test(Attr::EXTERNAL) && prev->has<ProcEntityDetails>()) {
// this subprogram was previously called, now being declared
if (!prev->test(subpFlag)) {
Say2(name,
@ -2862,6 +3030,13 @@ Symbol &SubprogramVisitor::PushSubprogramScope(
EraseSymbol(name);
}
}
}
Symbol &SubprogramVisitor::PushSubprogramScope(
const parser::Name &name, Symbol::Flag subpFlag) {
auto *symbol{GetSpecificFromGeneric(name)};
if (!symbol) {
CheckExtantExternal(name, subpFlag);
symbol = &MakeSymbol(name, SubprogramDetails{});
}
symbol->set(subpFlag);
@ -5055,7 +5230,8 @@ const DeclTypeSpec &ConstructVisitor::ToDeclTypeSpec(
);
}
case common::TypeCategory::Character: CRASH_NO_CASE;
case common::TypeCategory::Character:
CRASH_NO_CASE;
}
}
@ -5751,7 +5927,8 @@ void ResolveNamesVisitor::FinishSpecificationPart() {
void ResolveNamesVisitor::CheckImports() {
auto &scope{currScope()};
switch (scope.GetImportKind()) {
case common::ImportKind::None: break;
case common::ImportKind::None:
break;
case common::ImportKind::All:
// C8102: all entities in host must not be hidden
for (const auto &pair : scope.parent()) {
@ -5866,7 +6043,9 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) {
SetScope(context().globalScope());
ResolveSpecificationParts(root);
FinishSpecificationParts(root);
inExecutionPart_ = true;
ResolveExecutionParts(root);
inExecutionPart_ = false;
ResolveOmpParts(x);
return false;
}
@ -5884,8 +6063,8 @@ public:
}
}
template<typename A> bool Pre(const A &) { return true; }
template<typename A> void Post(const A &) {}
template <typename A> bool Pre(const A &) { return true; }
template <typename A> void Post(const A &) {}
void Post(const parser::FunctionReference &fr) {
resolver_.NoteExecutablePartCall(Symbol::Flag::Function, fr.v);
}
@ -5969,8 +6148,11 @@ bool ResolveNamesVisitor::BeginScopeForNode(const ProgramTree &node) {
case ProgramTree::Kind::Subroutine:
return BeginSubprogram(
node.name(), node.GetSubpFlag(), node.HasModulePrefix());
case ProgramTree::Kind::MpSubprogram: return BeginMpSubprogram(node.name());
case ProgramTree::Kind::Module: BeginModule(node.name(), false); return true;
case ProgramTree::Kind::MpSubprogram:
return BeginMpSubprogram(node.name());
case ProgramTree::Kind::Module:
BeginModule(node.name(), false);
return true;
case ProgramTree::Kind::Submodule:
return BeginSubmodule(node.name(), node.GetParentId());
case ProgramTree::Kind::BlockData:
@ -5988,10 +6170,10 @@ public:
explicit DeferredCheckVisitor(ResolveNamesVisitor &resolver)
: resolver_{resolver} {}
template<typename A> void Walk(const A &x) { parser::Walk(x, *this); }
template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
template<typename A> bool Pre(const A &) { return true; }
template<typename A> void Post(const A &) {}
template <typename A> bool Pre(const A &) { return true; }
template <typename A> void Post(const A &) {}
void Post(const parser::DerivedTypeStmt &x) {
const auto &name{std::get<parser::Name>(x.t)};
@ -6650,4 +6832,4 @@ void ResolveSpecificationParts(
visitor.ResolveSpecificationParts(node);
context.set_location(std::move(originalLocation));
}
}
} // namespace Fortran::semantics

View File

@ -112,10 +112,23 @@ private:
SemanticsContext &context_;
};
class EntryChecker : public virtual BaseChecker {
public:
explicit EntryChecker(SemanticsContext &context) : context_{context} {}
void Leave(const parser::EntryStmt &) {
if (!context_.constructStack().empty()) { // C1571
context_.Say("ENTRY may not appear in an executable construct"_err_en_US);
}
}
private:
SemanticsContext &context_;
};
using StatementSemanticsPass1 = ExprChecker;
using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
ArithmeticIfStmtChecker, AssignmentChecker, CoarrayChecker, DataChecker,
DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker,
DeallocateChecker, DoForallChecker, EntryChecker, IfStmtChecker, IoChecker,
NamelistChecker, NullifyChecker, OmpStructureChecker, PurityChecker,
ReturnStmtChecker, StopChecker>;

View File

@ -92,6 +92,12 @@ llvm::raw_ostream &operator<<(
os << ", " << x.result_->attrs();
}
}
if (x.entryScope_) {
os << " entry";
if (x.entryScope_->symbol()) {
os << " in " << x.entryScope_->symbol()->name();
}
}
char sep{'('};
os << ' ';
for (const Symbol *arg : x.dummyArgs_) {
@ -318,15 +324,6 @@ bool Symbol::IsSubprogram() const {
details_);
}
bool Symbol::IsSeparateModuleProc() const {
if (attrs().test(Attr::MODULE)) {
if (auto *details{detailsIf<SubprogramDetails>()}) {
return details->isInterface();
}
}
return false;
}
bool Symbol::IsFromModFile() const {
return test(Flag::ModFile) ||
(!owner_->IsGlobal() && owner_->symbol()->IsFromModFile());

View File

@ -690,6 +690,15 @@ bool HasIntrinsicTypeName(const Symbol &symbol) {
}
}
bool IsSeparateModuleProcedureInterface(const Symbol *symbol) {
if (symbol && symbol->attrs().test(Attr::MODULE)) {
if (auto *details{symbol->detailsIf<SubprogramDetails>()}) {
return details->isInterface();
}
}
return false;
}
bool IsFinalizable(const Symbol &symbol) {
if (const DeclTypeSpec * type{symbol.GetType()}) {
if (const DerivedTypeSpec * derived{type->AsDerived()}) {
@ -729,11 +738,9 @@ bool IsAssumedLengthCharacter(const Symbol &symbol) {
// C722 and C723: For a function to be assumed length, it must be external and
// of CHARACTER type
bool IsAssumedLengthExternalCharacterFunction(const Symbol &symbol) {
return IsAssumedLengthCharacter(symbol) &&
((symbol.has<SubprogramDetails>() && symbol.owner().IsGlobal()) ||
(symbol.test(Symbol::Flag::Function) &&
symbol.attrs().test(Attr::EXTERNAL)));
bool IsExternal(const Symbol &symbol) {
return (symbol.has<SubprogramDetails>() && symbol.owner().IsGlobal()) ||
symbol.attrs().test(Attr::EXTERNAL);
}
const Symbol *IsExternalInPureContext(
@ -1022,6 +1029,22 @@ const DeclTypeSpec &FindOrInstantiateDerivedType(Scope &scope,
return type;
}
const Symbol *FindSeparateModuleSubprogramInterface(const Symbol *proc) {
if (proc) {
if (const Symbol * submodule{proc->owner().symbol()}) {
if (const auto *details{submodule->detailsIf<ModuleDetails>()}) {
if (const Scope * ancestor{details->ancestor()}) {
const Symbol *iface{ancestor->FindSymbol(proc->name())};
if (IsSeparateModuleProcedureInterface(iface)) {
return iface;
}
}
}
}
}
return nullptr;
}
// ComponentIterator implementation
template<ComponentKind componentKind>

View File

@ -14,6 +14,7 @@ end
! C901
subroutine s2(x)
!ERROR: A dummy argument may not also be a named constant
real, parameter :: x = 0.0
real, parameter :: a(*) = [1, 2, 3]
character, parameter :: c(2) = "ab"

View File

@ -0,0 +1,184 @@
! RUN: %S/test_errors.sh %s %flang %t
! Tests valid and invalid ENTRY statements
module m1
!ERROR: ENTRY may appear only in a subroutine or function
entry badentryinmodule
interface
module subroutine separate
end subroutine
end interface
contains
subroutine modproc
entry entryinmodproc ! ok
block
!ERROR: ENTRY may not appear in an executable construct
entry badentryinblock ! C1571
end block
if (.true.) then
!ERROR: ENTRY may not appear in an executable construct
entry ibadconstr() ! C1571
end if
contains
subroutine internal
!ERROR: ENTRY may not appear in an internal subprogram
entry badentryininternal ! C1571
end subroutine
end subroutine
end module
submodule(m1) m1s1
contains
module procedure separate
!ERROR: ENTRY may not appear in a separate module procedure
entry badentryinsmp ! 1571
end procedure
end submodule
program main
!ERROR: ENTRY may appear only in a subroutine or function
entry badentryinprogram ! C1571
end program
block data bd1
!ERROR: ENTRY may appear only in a subroutine or function
entry badentryinbd ! C1571
end block data
subroutine subr(goodarg1)
real, intent(in) :: goodarg1
real :: goodarg2
!ERROR: A dummy argument may not also be a named constant
integer, parameter :: badarg1 = 1
type :: badarg2
end type
common /badarg3/ x
namelist /badarg4/ x
!ERROR: A dummy argument may not have the SAVE attribute
integer :: badarg5 = 2
entry okargs(goodarg1, goodarg2)
!ERROR: RESULT(br1) may appear only in a function
entry badresult() result(br1) ! C1572
!ERROR: ENTRY dummy argument 'badarg2' is previously declared as an item that may not be used as a dummy argument
!ERROR: ENTRY dummy argument 'badarg4' is previously declared as an item that may not be used as a dummy argument
entry badargs(badarg1,badarg2,badarg3,badarg4,badarg5)
end subroutine
function ifunc()
integer :: ifunc
integer :: ibad1
type :: ibad2
end type
save :: ibad3
real :: weird1
double precision :: weird2
complex :: weird3
logical :: weird4
character :: weird5
type(ibad2) :: weird6
integer :: iarr(1)
integer, allocatable :: alloc
integer, pointer :: ptr
entry iok1()
!ERROR: ENTRY name 'ibad1' may not be declared when RESULT() is present
entry ibad1() result(ibad1res) ! C1570
!ERROR: 'ibad2' was previously declared as an item that may not be used as a function result
entry ibad2()
!ERROR: ENTRY in a function may not have an alternate return dummy argument
entry ibadalt(*) ! C1573
!ERROR: RESULT(ifunc) may not have the same name as the function
entry isameres() result(ifunc) ! C1574
entry iok()
!ERROR: RESULT(iok) may not have the same name as an ENTRY in the function
entry isameres2() result(iok) ! C1574
entry isameres3() result(iok2) ! C1574
entry iok2()
!These cases are all acceptably incompatible
entry iok3() result(weird1)
entry iok4() result(weird2)
entry iok5() result(weird3)
entry iok6() result(weird4)
!ERROR: Result of ENTRY is not compatible with result of containing function
entry ibadt1() result(weird5)
!ERROR: Result of ENTRY is not compatible with result of containing function
entry ibadt2() result(weird6)
!ERROR: Result of ENTRY is not compatible with result of containing function
entry ibadt3() result(iarr)
!ERROR: Result of ENTRY is not compatible with result of containing function
entry ibadt4() result(alloc)
!ERROR: Result of ENTRY is not compatible with result of containing function
entry ibadt5() result(ptr)
call isubr
!ERROR: 'isubr' was previously called as a subroutine
entry isubr()
continue ! force transition to execution part
entry implicit()
implicit = 666 ! ok, just ensure that it works
end function
function chfunc() result(chr)
character(len=1) :: chr
character(len=2) :: chr1
!ERROR: Result of ENTRY is not compatible with result of containing function
entry chfunc1() result(chr1)
end function
subroutine externals
!ERROR: 'subr' is already defined as a global identifier
entry subr
!ERROR: 'ifunc' is already defined as a global identifier
entry ifunc
!ERROR: 'm1' is already defined as a global identifier
entry m1
!ERROR: 'iok1' is already defined as a global identifier
entry iok1
integer :: ix
ix = iproc()
!ERROR: 'iproc' was previously called as a function
entry iproc
end subroutine
module m2
external m2entry2
contains
subroutine m2subr1
entry m2entry1 ! ok
entry m2entry2 ! ok
entry m2entry3 ! ok
end subroutine
end module
subroutine usem2
use m2
interface
subroutine simplesubr
end subroutine
end interface
procedure(simplesubr), pointer :: p
p => m2subr1 ! ok
p => m2entry1 ! ok
p => m2entry2 ! ok
p => m2entry3 ! ok
end subroutine
module m3
interface
module subroutine m3entry1
end subroutine
end interface
contains
subroutine m3subr1
!ERROR: 'm3entry1' is already declared in this scoping unit
entry m3entry1
end subroutine
end module
function inone
implicit none
integer :: inone
!ERROR: No explicit type declared for 'implicitbad1'
entry implicitbad1
inone = 0 ! force transition to execution part
!ERROR: No explicit type declared for 'implicitbad2'
entry implicitbad2
end