2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/check-declarations.cpp ------------------------------===//
|
2019-09-11 06:51:46 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2019-09-11 06:51:46 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-11 06:51:46 +08:00
|
|
|
|
|
|
|
// Static declaration checking
|
|
|
|
|
2019-09-11 08:08:18 +08:00
|
|
|
#include "check-declarations.h"
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
#include "pointer-assignment.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Evaluate/check-expression.h"
|
|
|
|
#include "flang/Evaluate/fold.h"
|
|
|
|
#include "flang/Evaluate/tools.h"
|
|
|
|
#include "flang/Semantics/scope.h"
|
|
|
|
#include "flang/Semantics/semantics.h"
|
|
|
|
#include "flang/Semantics/symbol.h"
|
|
|
|
#include "flang/Semantics/tools.h"
|
|
|
|
#include "flang/Semantics/type.h"
|
2020-01-15 07:59:29 +08:00
|
|
|
#include <algorithm>
|
2021-04-08 04:23:45 +08:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2019-09-11 06:51:46 +08:00
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
namespace characteristics = evaluate::characteristics;
|
|
|
|
using characteristics::DummyArgument;
|
|
|
|
using characteristics::DummyDataObject;
|
|
|
|
using characteristics::DummyProcedure;
|
|
|
|
using characteristics::FunctionResult;
|
|
|
|
using characteristics::Procedure;
|
2019-11-23 06:40:53 +08:00
|
|
|
|
2019-09-19 06:43:12 +08:00
|
|
|
class CheckHelper {
|
|
|
|
public:
|
|
|
|
explicit CheckHelper(SemanticsContext &c) : context_{c} {}
|
2020-09-01 02:54:48 +08:00
|
|
|
CheckHelper(SemanticsContext &c, const Scope &s) : context_{c}, scope_{&s} {}
|
2019-09-19 06:43:12 +08:00
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
SemanticsContext &context() { return context_; }
|
2019-09-21 05:28:15 +08:00
|
|
|
void Check() { Check(context_.globalScope()); }
|
2019-10-25 07:08:06 +08:00
|
|
|
void Check(const ParamValue &, bool canBeAssumed);
|
2020-05-13 00:53:58 +08:00
|
|
|
void Check(const Bound &bound) { CheckSpecExpr(bound.GetExplicit()); }
|
2019-10-30 03:46:25 +08:00
|
|
|
void Check(const ShapeSpec &spec) {
|
2019-09-19 06:43:12 +08:00
|
|
|
Check(spec.lbound());
|
|
|
|
Check(spec.ubound());
|
2019-09-11 06:51:46 +08:00
|
|
|
}
|
2019-10-30 03:46:25 +08:00
|
|
|
void Check(const ArraySpec &);
|
|
|
|
void Check(const DeclTypeSpec &, bool canHaveAssumedTypeParameters);
|
|
|
|
void Check(const Symbol &);
|
|
|
|
void Check(const Scope &);
|
2020-09-10 22:22:52 +08:00
|
|
|
const Procedure *Characterize(const Symbol &);
|
2019-09-19 06:43:12 +08:00
|
|
|
|
|
|
|
private:
|
2020-05-13 00:53:58 +08:00
|
|
|
template <typename A> void CheckSpecExpr(const A &x) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
evaluate::CheckSpecificationExpr(x, DEREF(scope_), foldingContext_);
|
2019-09-11 06:51:46 +08:00
|
|
|
}
|
2019-10-30 03:46:25 +08:00
|
|
|
void CheckValue(const Symbol &, const DerivedTypeSpec *);
|
2021-01-22 06:54:53 +08:00
|
|
|
void CheckVolatile(const Symbol &, const DerivedTypeSpec *);
|
2020-01-14 08:39:00 +08:00
|
|
|
void CheckPointer(const Symbol &);
|
2019-12-06 02:24:18 +08:00
|
|
|
void CheckPassArg(
|
|
|
|
const Symbol &proc, const Symbol *interface, const WithPassArg &);
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckProcBinding(const Symbol &, const ProcBindingDetails &);
|
|
|
|
void CheckObjectEntity(const Symbol &, const ObjectEntityDetails &);
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
void CheckPointerInitialization(const Symbol &);
|
2020-01-15 04:06:52 +08:00
|
|
|
void CheckArraySpec(const Symbol &, const ArraySpec &);
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckProcEntity(const Symbol &, const ProcEntityDetails &);
|
2020-03-18 05:48:36 +08:00
|
|
|
void CheckSubprogram(const Symbol &, const SubprogramDetails &);
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
void CheckAssumedTypeEntity(const Symbol &, const ObjectEntityDetails &);
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckDerivedType(const Symbol &, const DerivedTypeDetails &);
|
2020-10-01 04:34:23 +08:00
|
|
|
bool CheckFinal(
|
|
|
|
const Symbol &subroutine, SourceName, const Symbol &derivedType);
|
|
|
|
bool CheckDistinguishableFinals(const Symbol &f1, SourceName f1name,
|
|
|
|
const Symbol &f2, SourceName f2name, const Symbol &derivedType);
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckGeneric(const Symbol &, const GenericDetails &);
|
2020-09-10 22:22:52 +08:00
|
|
|
void CheckHostAssoc(const Symbol &, const HostAssocDetails &);
|
|
|
|
bool CheckDefinedOperator(
|
|
|
|
SourceName, GenericKind, const Symbol &, const Procedure &);
|
2019-12-03 00:55:44 +08:00
|
|
|
std::optional<parser::MessageFixedText> CheckNumberOfArgs(
|
|
|
|
const GenericKind &, std::size_t);
|
|
|
|
bool CheckDefinedOperatorArg(
|
|
|
|
const SourceName &, const Symbol &, const Procedure &, std::size_t);
|
2019-11-23 06:40:53 +08:00
|
|
|
bool CheckDefinedAssignment(const Symbol &, const Procedure &);
|
|
|
|
bool CheckDefinedAssignmentArg(const Symbol &, const DummyArgument &, int);
|
2020-09-10 22:22:52 +08:00
|
|
|
void CheckSpecificsAreDistinguishable(const Symbol &, const GenericDetails &);
|
2020-01-10 09:12:46 +08:00
|
|
|
void CheckEquivalenceSet(const EquivalenceSet &);
|
|
|
|
void CheckBlockData(const Scope &);
|
2020-09-10 22:22:52 +08:00
|
|
|
void CheckGenericOps(const Scope &);
|
2020-01-14 08:39:00 +08:00
|
|
|
bool CheckConflicting(const Symbol &, Attr, Attr);
|
2020-10-31 04:30:42 +08:00
|
|
|
void WarnMissingFinal(const Symbol &);
|
2019-11-23 06:05:54 +08:00
|
|
|
bool InPure() const {
|
|
|
|
return innermostSymbol_ && IsPureProcedure(*innermostSymbol_);
|
|
|
|
}
|
2021-09-04 06:03:43 +08:00
|
|
|
bool InElemental() const {
|
|
|
|
return innermostSymbol_ && innermostSymbol_->attrs().test(Attr::ELEMENTAL);
|
|
|
|
}
|
2019-11-23 06:05:54 +08:00
|
|
|
bool InFunction() const {
|
|
|
|
return innermostSymbol_ && IsFunction(*innermostSymbol_);
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename... A>
|
2020-08-07 07:56:14 +08:00
|
|
|
void SayWithDeclaration(const Symbol &symbol, A &&...x) {
|
2019-11-23 05:20:58 +08:00
|
|
|
if (parser::Message * msg{messages_.Say(std::forward<A>(x)...)}) {
|
2020-03-18 05:48:36 +08:00
|
|
|
if (messages_.at().begin() != symbol.name().begin()) {
|
2019-11-23 05:20:58 +08:00
|
|
|
evaluate::AttachDeclaration(*msg, symbol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 07:31:10 +08:00
|
|
|
bool IsResultOkToDiffer(const FunctionResult &);
|
2021-04-08 04:23:45 +08:00
|
|
|
void CheckBindCName(const Symbol &);
|
2021-05-25 04:12:19 +08:00
|
|
|
// Check functions for defined I/O procedures
|
|
|
|
void CheckDefinedIoProc(
|
|
|
|
const Symbol &, const GenericDetails &, GenericKind::DefinedIo);
|
|
|
|
bool CheckDioDummyIsData(const Symbol &, const Symbol *, std::size_t);
|
2021-06-03 05:55:41 +08:00
|
|
|
void CheckDioDummyIsDerived(
|
|
|
|
const Symbol &, const Symbol &, GenericKind::DefinedIo ioKind);
|
2021-05-25 04:12:19 +08:00
|
|
|
void CheckDioDummyIsDefaultInteger(const Symbol &, const Symbol &);
|
|
|
|
void CheckDioDummyIsScalar(const Symbol &, const Symbol &);
|
|
|
|
void CheckDioDummyAttrs(const Symbol &, const Symbol &, Attr);
|
|
|
|
void CheckDioDtvArg(const Symbol &, const Symbol *, GenericKind::DefinedIo);
|
|
|
|
void CheckDefaultIntegerArg(const Symbol &, const Symbol *, Attr);
|
|
|
|
void CheckDioAssumedLenCharacterArg(
|
|
|
|
const Symbol &, const Symbol *, std::size_t, Attr);
|
|
|
|
void CheckDioVlistArg(const Symbol &, const Symbol *, std::size_t);
|
|
|
|
void CheckDioArgCount(
|
|
|
|
const Symbol &, GenericKind::DefinedIo ioKind, std::size_t);
|
2021-06-03 05:55:41 +08:00
|
|
|
struct TypeWithDefinedIo {
|
|
|
|
const DerivedTypeSpec *type;
|
|
|
|
GenericKind::DefinedIo ioKind;
|
|
|
|
const Symbol &proc;
|
|
|
|
};
|
|
|
|
void CheckAlreadySeenDefinedIo(
|
|
|
|
const DerivedTypeSpec *, GenericKind::DefinedIo, const Symbol &);
|
2019-09-19 06:43:12 +08:00
|
|
|
|
|
|
|
SemanticsContext &context_;
|
2019-09-21 05:28:15 +08:00
|
|
|
evaluate::FoldingContext &foldingContext_{context_.foldingContext()};
|
|
|
|
parser::ContextualMessages &messages_{foldingContext_.messages()};
|
|
|
|
const Scope *scope_{nullptr};
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
bool scopeIsUninstantiatedPDT_{false};
|
2019-11-13 07:43:09 +08:00
|
|
|
// This symbol is the one attached to the innermost enclosing scope
|
|
|
|
// that has a symbol.
|
|
|
|
const Symbol *innermostSymbol_{nullptr};
|
2020-09-10 22:22:52 +08:00
|
|
|
// Cache of calls to Procedure::Characterize(Symbol)
|
2021-03-19 01:26:23 +08:00
|
|
|
std::map<SymbolRef, std::optional<Procedure>, SymbolAddressCompare>
|
|
|
|
characterizeCache_;
|
2021-04-08 04:23:45 +08:00
|
|
|
// Collection of symbols with BIND(C) names
|
|
|
|
std::map<std::string, SymbolRef> bindC_;
|
2021-06-03 05:55:41 +08:00
|
|
|
// Derived types that have defined input/output procedures
|
|
|
|
std::vector<TypeWithDefinedIo> seenDefinedIoTypes_;
|
2020-09-10 22:22:52 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
class DistinguishabilityHelper {
|
|
|
|
public:
|
|
|
|
DistinguishabilityHelper(SemanticsContext &context) : context_{context} {}
|
|
|
|
void Add(const Symbol &, GenericKind, const Symbol &, const Procedure &);
|
2020-12-03 07:13:49 +08:00
|
|
|
void Check(const Scope &);
|
2020-09-10 22:22:52 +08:00
|
|
|
|
|
|
|
private:
|
2020-12-03 07:13:49 +08:00
|
|
|
void SayNotDistinguishable(const Scope &, const SourceName &, GenericKind,
|
|
|
|
const Symbol &, const Symbol &);
|
|
|
|
void AttachDeclaration(parser::Message &, const Scope &, const Symbol &);
|
2020-09-10 22:22:52 +08:00
|
|
|
|
|
|
|
SemanticsContext &context_;
|
|
|
|
struct ProcedureInfo {
|
|
|
|
GenericKind kind;
|
|
|
|
const Symbol &symbol;
|
|
|
|
const Procedure &procedure;
|
|
|
|
};
|
|
|
|
std::map<SourceName, std::vector<ProcedureInfo>> nameToInfo_;
|
2019-09-19 06:43:12 +08:00
|
|
|
};
|
2019-09-11 06:51:46 +08:00
|
|
|
|
2019-10-25 07:08:06 +08:00
|
|
|
void CheckHelper::Check(const ParamValue &value, bool canBeAssumed) {
|
|
|
|
if (value.isAssumed()) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (!canBeAssumed) { // C795, C721, C726
|
2019-10-25 07:08:06 +08:00
|
|
|
messages_.Say(
|
2020-02-27 12:19:48 +08:00
|
|
|
"An assumed (*) type parameter may be used only for a (non-statement"
|
|
|
|
" function) dummy argument, associate name, named constant, or"
|
|
|
|
" external function result"_err_en_US);
|
2019-10-25 07:08:06 +08:00
|
|
|
}
|
|
|
|
} else {
|
2020-05-13 00:53:58 +08:00
|
|
|
CheckSpecExpr(value.GetExplicit());
|
2019-10-25 07:08:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:46:25 +08:00
|
|
|
void CheckHelper::Check(const ArraySpec &shape) {
|
|
|
|
for (const auto &spec : shape) {
|
2019-10-25 07:08:06 +08:00
|
|
|
Check(spec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:46:25 +08:00
|
|
|
void CheckHelper::Check(
|
|
|
|
const DeclTypeSpec &type, bool canHaveAssumedTypeParameters) {
|
2019-10-25 07:08:06 +08:00
|
|
|
if (type.category() == DeclTypeSpec::Character) {
|
|
|
|
Check(type.characterTypeSpec().length(), canHaveAssumedTypeParameters);
|
2019-11-16 06:26:10 +08:00
|
|
|
} else if (const DerivedTypeSpec * derived{type.AsDerived()}) {
|
|
|
|
for (auto &parm : derived->parameters()) {
|
2019-10-25 07:08:06 +08:00
|
|
|
Check(parm.second, canHaveAssumedTypeParameters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-30 03:46:25 +08:00
|
|
|
void CheckHelper::Check(const Symbol &symbol) {
|
|
|
|
if (context_.HasError(symbol)) {
|
2019-09-21 00:46:00 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-11-23 06:40:53 +08:00
|
|
|
auto restorer{messages_.SetLocation(symbol.name())};
|
2019-09-21 00:46:00 +08:00
|
|
|
context_.set_location(symbol.name());
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
const DeclTypeSpec *type{symbol.GetType()};
|
|
|
|
const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
|
2021-01-22 06:54:53 +08:00
|
|
|
bool isDone{false};
|
2019-11-23 06:05:54 +08:00
|
|
|
std::visit(
|
|
|
|
common::visitors{
|
2021-01-22 06:54:53 +08:00
|
|
|
[&](const UseDetails &x) { isDone = true; },
|
|
|
|
[&](const HostAssocDetails &x) {
|
|
|
|
CheckHostAssoc(symbol, x);
|
|
|
|
isDone = true;
|
|
|
|
},
|
|
|
|
[&](const ProcBindingDetails &x) {
|
|
|
|
CheckProcBinding(symbol, x);
|
|
|
|
isDone = true;
|
|
|
|
},
|
2019-11-23 06:05:54 +08:00
|
|
|
[&](const ObjectEntityDetails &x) { CheckObjectEntity(symbol, x); },
|
|
|
|
[&](const ProcEntityDetails &x) { CheckProcEntity(symbol, x); },
|
2020-03-18 05:48:36 +08:00
|
|
|
[&](const SubprogramDetails &x) { CheckSubprogram(symbol, x); },
|
2019-11-23 06:05:54 +08:00
|
|
|
[&](const DerivedTypeDetails &x) { CheckDerivedType(symbol, x); },
|
|
|
|
[&](const GenericDetails &x) { CheckGeneric(symbol, x); },
|
|
|
|
[](const auto &) {},
|
|
|
|
},
|
|
|
|
symbol.details());
|
2021-01-22 06:54:53 +08:00
|
|
|
if (symbol.attrs().test(Attr::VOLATILE)) {
|
|
|
|
CheckVolatile(symbol, derived);
|
|
|
|
}
|
2021-04-08 04:23:45 +08:00
|
|
|
CheckBindCName(symbol);
|
2021-01-22 06:54:53 +08:00
|
|
|
if (isDone) {
|
|
|
|
return; // following checks do not apply
|
|
|
|
}
|
|
|
|
if (IsPointer(symbol)) {
|
|
|
|
CheckPointer(symbol);
|
|
|
|
}
|
2019-11-23 06:05:54 +08:00
|
|
|
if (InPure()) {
|
2019-11-13 07:43:09 +08:00
|
|
|
if (IsSaved(symbol)) {
|
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"A pure subprogram may not have a variable with the SAVE attribute"_err_en_US);
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::VOLATILE)) {
|
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"A pure subprogram may not have a variable with the VOLATILE attribute"_err_en_US);
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
if (IsProcedure(symbol) && !IsPureProcedure(symbol) && IsDummy(symbol)) {
|
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"A dummy procedure of a pure subprogram must be pure"_err_en_US);
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
if (!IsDummy(symbol) && !IsFunctionResult(symbol)) {
|
|
|
|
if (IsPolymorphicAllocatable(symbol)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(symbol,
|
2019-12-24 09:12:53 +08:00
|
|
|
"Deallocation of polymorphic object '%s' is not permitted in a pure subprogram"_err_en_US,
|
2019-11-13 07:43:09 +08:00
|
|
|
symbol.name());
|
|
|
|
} else if (derived) {
|
|
|
|
if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*bad,
|
2019-12-24 09:12:53 +08:00
|
|
|
"Deallocation of polymorphic object '%s%s' is not permitted in a pure subprogram"_err_en_US,
|
2019-11-13 07:43:09 +08:00
|
|
|
symbol.name(), bad.BuildResultDesignatorName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (type) { // Section 7.2, paragraph 7
|
2019-10-25 07:08:06 +08:00
|
|
|
bool canHaveAssumedParameter{IsNamedConstant(symbol) ||
|
2020-03-29 12:00:16 +08:00
|
|
|
(IsAssumedLengthCharacter(symbol) && // C722
|
2020-03-20 07:31:10 +08:00
|
|
|
IsExternal(symbol)) ||
|
2019-10-25 07:08:06 +08:00
|
|
|
symbol.test(Symbol::Flag::ParentComp)};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (!IsStmtFunctionDummy(symbol)) { // C726
|
2020-02-27 12:19:48 +08:00
|
|
|
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
canHaveAssumedParameter |= object->isDummy() ||
|
|
|
|
(object->isFuncResult() &&
|
|
|
|
type->category() == DeclTypeSpec::Character) ||
|
2020-03-29 12:00:16 +08:00
|
|
|
IsStmtFunctionResult(symbol); // Avoids multiple messages
|
2020-02-27 12:19:48 +08:00
|
|
|
} else {
|
|
|
|
canHaveAssumedParameter |= symbol.has<AssocEntityDetails>();
|
|
|
|
}
|
2019-10-25 07:08:06 +08:00
|
|
|
}
|
|
|
|
Check(*type, canHaveAssumedParameter);
|
2019-11-23 06:05:54 +08:00
|
|
|
if (InPure() && InFunction() && IsFunctionResult(symbol)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (derived && HasImpureFinal(*derived)) { // C1584
|
2019-11-13 07:43:09 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"Result of pure function may not have an impure FINAL subroutine"_err_en_US);
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (type->IsPolymorphic() && IsAllocatable(symbol)) { // C1585
|
2019-11-13 07:43:09 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"Result of pure function may not be both polymorphic and ALLOCATABLE"_err_en_US);
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
|
|
|
if (derived) {
|
|
|
|
if (auto bad{FindPolymorphicAllocatableUltimateComponent(*derived)}) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*bad,
|
2019-12-24 09:12:53 +08:00
|
|
|
"Result of pure function may not have polymorphic ALLOCATABLE ultimate component '%s'"_err_en_US,
|
2019-11-13 07:43:09 +08:00
|
|
|
bad.BuildResultDesignatorName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-21 00:46:00 +08:00
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (IsAssumedLengthCharacter(symbol) && IsExternal(symbol)) { // C723
|
2019-09-21 00:46:00 +08:00
|
|
|
if (symbol.attrs().test(Attr::RECURSIVE)) {
|
2019-10-18 03:28:25 +08:00
|
|
|
messages_.Say(
|
2019-09-21 00:46:00 +08:00
|
|
|
"An assumed-length CHARACTER(*) function cannot be RECURSIVE"_err_en_US);
|
|
|
|
}
|
|
|
|
if (symbol.Rank() > 0) {
|
2019-10-18 03:28:25 +08:00
|
|
|
messages_.Say(
|
2019-09-21 00:46:00 +08:00
|
|
|
"An assumed-length CHARACTER(*) function cannot return an array"_err_en_US);
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::PURE)) {
|
2019-10-18 03:28:25 +08:00
|
|
|
messages_.Say(
|
2019-09-21 00:46:00 +08:00
|
|
|
"An assumed-length CHARACTER(*) function cannot be PURE"_err_en_US);
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::ELEMENTAL)) {
|
2019-10-18 03:28:25 +08:00
|
|
|
messages_.Say(
|
2019-09-21 00:46:00 +08:00
|
|
|
"An assumed-length CHARACTER(*) function cannot be ELEMENTAL"_err_en_US);
|
|
|
|
}
|
|
|
|
if (const Symbol * result{FindFunctionResult(symbol)}) {
|
2019-11-02 04:08:16 +08:00
|
|
|
if (IsPointer(*result)) {
|
2019-10-18 03:28:25 +08:00
|
|
|
messages_.Say(
|
2019-09-21 00:46:00 +08:00
|
|
|
"An assumed-length CHARACTER(*) function cannot return a POINTER"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-10-30 03:46:25 +08:00
|
|
|
if (symbol.attrs().test(Attr::VALUE)) {
|
|
|
|
CheckValue(symbol, derived);
|
|
|
|
}
|
2019-11-02 04:08:16 +08:00
|
|
|
if (symbol.attrs().test(Attr::CONTIGUOUS) && IsPointer(symbol) &&
|
2020-03-29 12:00:16 +08:00
|
|
|
symbol.Rank() == 0) { // C830
|
2019-11-02 04:08:16 +08:00
|
|
|
messages_.Say("CONTIGUOUS POINTER must be an array"_err_en_US);
|
|
|
|
}
|
2020-03-20 07:31:10 +08:00
|
|
|
if (IsDummy(symbol)) {
|
|
|
|
if (IsNamedConstant(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument may not also be a named constant"_err_en_US);
|
|
|
|
}
|
2021-07-20 02:53:20 +08:00
|
|
|
if (!symbol.test(Symbol::Flag::InDataStmt) /*caught elsewhere*/ &&
|
|
|
|
IsSaved(symbol)) {
|
2020-03-20 07:31:10 +08:00
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument may not have the SAVE attribute"_err_en_US);
|
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
} else if (IsFunctionResult(symbol)) {
|
2021-07-20 02:53:20 +08:00
|
|
|
if (!symbol.test(Symbol::Flag::InDataStmt) /*caught elsewhere*/ &&
|
|
|
|
IsSaved(symbol)) {
|
2020-06-19 08:17:04 +08:00
|
|
|
messages_.Say(
|
|
|
|
"A function result may not have the SAVE attribute"_err_en_US);
|
|
|
|
}
|
2020-03-20 07:31:10 +08:00
|
|
|
}
|
2020-05-13 00:53:58 +08:00
|
|
|
if (symbol.owner().IsDerivedType() &&
|
|
|
|
(symbol.attrs().test(Attr::CONTIGUOUS) &&
|
|
|
|
!(IsPointer(symbol) && symbol.Rank() > 0))) { // C752
|
|
|
|
messages_.Say(
|
|
|
|
"A CONTIGUOUS component must be an array with the POINTER attribute"_err_en_US);
|
|
|
|
}
|
2020-07-16 04:02:32 +08:00
|
|
|
if (symbol.owner().IsModule() && IsAutomatic(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"Automatic data object '%s' may not appear in the specification part"
|
|
|
|
" of a module"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
2019-10-30 03:46:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckValue(
|
2020-03-29 12:00:16 +08:00
|
|
|
const Symbol &symbol, const DerivedTypeSpec *derived) { // C863 - C865
|
2019-10-30 03:46:25 +08:00
|
|
|
if (!IsDummy(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may apply only to a dummy argument"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsProcedure(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may apply only to a dummy data object"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsAssumedSizeArray(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may not apply to an assumed-size array"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsCoarray(symbol)) {
|
|
|
|
messages_.Say("VALUE attribute may not apply to a coarray"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
messages_.Say("VALUE attribute may not apply to an ALLOCATABLE"_err_en_US);
|
|
|
|
} else if (IsPointer(symbol)) {
|
|
|
|
messages_.Say("VALUE attribute may not apply to a POINTER"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsIntentInOut(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may not apply to an INTENT(IN OUT) argument"_err_en_US);
|
|
|
|
} else if (IsIntentOut(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may not apply to an INTENT(OUT) argument"_err_en_US);
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::VOLATILE)) {
|
|
|
|
messages_.Say("VALUE attribute may not apply to a VOLATILE"_err_en_US);
|
|
|
|
}
|
2019-11-13 07:43:09 +08:00
|
|
|
if (innermostSymbol_ && IsBindCProcedure(*innermostSymbol_) &&
|
|
|
|
IsOptional(symbol)) {
|
2019-10-30 03:46:25 +08:00
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may not apply to an OPTIONAL in a BIND(C) procedure"_err_en_US);
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (derived) {
|
2019-10-30 03:46:25 +08:00
|
|
|
if (FindCoarrayUltimateComponent(*derived)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VALUE attribute may not apply to a type with a coarray ultimate component"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
void CheckHelper::CheckAssumedTypeEntity( // C709
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
const Symbol &symbol, const ObjectEntityDetails &details) {
|
|
|
|
if (const DeclTypeSpec * type{symbol.GetType()};
|
|
|
|
type && type->category() == DeclTypeSpec::TypeStar) {
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
if (!IsDummy(symbol)) {
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
messages_.Say(
|
|
|
|
"Assumed-type entity '%s' must be a dummy argument"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
} else {
|
|
|
|
if (symbol.attrs().test(Attr::ALLOCATABLE)) {
|
|
|
|
messages_.Say("Assumed-type argument '%s' cannot have the ALLOCATABLE"
|
|
|
|
" attribute"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::POINTER)) {
|
|
|
|
messages_.Say("Assumed-type argument '%s' cannot have the POINTER"
|
|
|
|
" attribute"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::VALUE)) {
|
|
|
|
messages_.Say("Assumed-type argument '%s' cannot have the VALUE"
|
|
|
|
" attribute"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::INTENT_OUT)) {
|
|
|
|
messages_.Say(
|
|
|
|
"Assumed-type argument '%s' cannot be INTENT(OUT)"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
if (IsCoarray(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"Assumed-type argument '%s' cannot be a coarray"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
2020-03-06 09:55:51 +08:00
|
|
|
if (details.IsArray() && details.shape().IsExplicitShape()) {
|
|
|
|
messages_.Say(
|
|
|
|
"Assumed-type array argument 'arg8' must be assumed shape,"
|
|
|
|
" assumed size, or assumed rank"_err_en_US,
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckHelper::CheckObjectEntity(
|
|
|
|
const Symbol &symbol, const ObjectEntityDetails &details) {
|
2020-01-15 04:06:52 +08:00
|
|
|
CheckArraySpec(symbol, details.shape());
|
2019-11-23 06:05:54 +08:00
|
|
|
Check(details.shape());
|
|
|
|
Check(details.coshape());
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
CheckAssumedTypeEntity(symbol, details);
|
2020-10-31 04:30:42 +08:00
|
|
|
WarnMissingFinal(symbol);
|
2019-11-23 06:05:54 +08:00
|
|
|
if (!details.coshape().empty()) {
|
2021-09-22 07:06:30 +08:00
|
|
|
bool isDeferredCoshape{details.coshape().IsDeferredShape()};
|
2019-11-23 06:05:54 +08:00
|
|
|
if (IsAllocatable(symbol)) {
|
2021-09-22 07:06:30 +08:00
|
|
|
if (!isDeferredCoshape) { // C827
|
[flang] New implementation for checks for constraints C741 through C750
Summary:
Most of these checks were already implemented, and I just added references to
them to the code and tests. Also, much of this code was already
reviewed in the old flang/f18 GitHub repository, but I didn't get to
merge it before we switched repositories.
I implemented the check for C747 to not allow coarray components in derived
types that are of type C_PTR, C_FUNPTR, or type TEAM_TYPE.
I implemented the check for C748 that requires a data component whose type has
a coarray ultimate component to be a nonpointer, nonallocatable scalar and not
be a coarray.
I implemented the check for C750 that adds additional restrictions to the
bounds expressions of a derived type component that's an array.
These bounds expressions are sepcification expressions as defined in
10.1.11. There was already code in lib/Evaluate/check-expression.cpp to
check semantics for specification expressions, but it did not check for
the extra requirements of C750.
C750 prohibits specification functions, the intrinsic functions
ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, PRESENT, and SAME_TYPE_AS. It
also requires every specification inquiry reference to be a constant
expression, and requires that the value of the bound not depend on the
value of a variable.
To implement these additional checks, I added code to the intrinsic proc
table to get the intrinsic class of a procedure. I also added an
enumeration to distinguish between specification expressions for
derived type component bounds versus for type parameters. I then
changed the code to pass an enumeration value to
"CheckSpecificationExpr()" to indicate that the expression was a bounds
expression and used this value to determine whether to emit an error
message when violations of C750 are found.
I changed the implementation of IsPureProcedure() to handle statement
functions and changed some references in the code that tested for the
PURE attribute to call IsPureProcedure().
I also fixed some unrelated tests that got new errors when I implemented these
new checks.
Reviewers: tskeith, DavidTruby, sscalpone
Subscribers: jfb, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D79263
2020-05-02 04:00:28 +08:00
|
|
|
messages_.Say("'%s' is an ALLOCATABLE coarray and must have a deferred"
|
|
|
|
" coshape"_err_en_US,
|
|
|
|
symbol.name());
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
[flang] New implementation for checks for constraints C741 through C750
Summary:
Most of these checks were already implemented, and I just added references to
them to the code and tests. Also, much of this code was already
reviewed in the old flang/f18 GitHub repository, but I didn't get to
merge it before we switched repositories.
I implemented the check for C747 to not allow coarray components in derived
types that are of type C_PTR, C_FUNPTR, or type TEAM_TYPE.
I implemented the check for C748 that requires a data component whose type has
a coarray ultimate component to be a nonpointer, nonallocatable scalar and not
be a coarray.
I implemented the check for C750 that adds additional restrictions to the
bounds expressions of a derived type component that's an array.
These bounds expressions are sepcification expressions as defined in
10.1.11. There was already code in lib/Evaluate/check-expression.cpp to
check semantics for specification expressions, but it did not check for
the extra requirements of C750.
C750 prohibits specification functions, the intrinsic functions
ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, PRESENT, and SAME_TYPE_AS. It
also requires every specification inquiry reference to be a constant
expression, and requires that the value of the bound not depend on the
value of a variable.
To implement these additional checks, I added code to the intrinsic proc
table to get the intrinsic class of a procedure. I also added an
enumeration to distinguish between specification expressions for
derived type component bounds versus for type parameters. I then
changed the code to pass an enumeration value to
"CheckSpecificationExpr()" to indicate that the expression was a bounds
expression and used this value to determine whether to emit an error
message when violations of C750 are found.
I changed the implementation of IsPureProcedure() to handle statement
functions and changed some references in the code that tested for the
PURE attribute to call IsPureProcedure().
I also fixed some unrelated tests that got new errors when I implemented these
new checks.
Reviewers: tskeith, DavidTruby, sscalpone
Subscribers: jfb, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D79263
2020-05-02 04:00:28 +08:00
|
|
|
} else if (symbol.owner().IsDerivedType()) { // C746
|
|
|
|
std::string deferredMsg{
|
2021-09-22 07:06:30 +08:00
|
|
|
isDeferredCoshape ? "" : " and have a deferred coshape"};
|
[flang] New implementation for checks for constraints C741 through C750
Summary:
Most of these checks were already implemented, and I just added references to
them to the code and tests. Also, much of this code was already
reviewed in the old flang/f18 GitHub repository, but I didn't get to
merge it before we switched repositories.
I implemented the check for C747 to not allow coarray components in derived
types that are of type C_PTR, C_FUNPTR, or type TEAM_TYPE.
I implemented the check for C748 that requires a data component whose type has
a coarray ultimate component to be a nonpointer, nonallocatable scalar and not
be a coarray.
I implemented the check for C750 that adds additional restrictions to the
bounds expressions of a derived type component that's an array.
These bounds expressions are sepcification expressions as defined in
10.1.11. There was already code in lib/Evaluate/check-expression.cpp to
check semantics for specification expressions, but it did not check for
the extra requirements of C750.
C750 prohibits specification functions, the intrinsic functions
ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, PRESENT, and SAME_TYPE_AS. It
also requires every specification inquiry reference to be a constant
expression, and requires that the value of the bound not depend on the
value of a variable.
To implement these additional checks, I added code to the intrinsic proc
table to get the intrinsic class of a procedure. I also added an
enumeration to distinguish between specification expressions for
derived type component bounds versus for type parameters. I then
changed the code to pass an enumeration value to
"CheckSpecificationExpr()" to indicate that the expression was a bounds
expression and used this value to determine whether to emit an error
message when violations of C750 are found.
I changed the implementation of IsPureProcedure() to handle statement
functions and changed some references in the code that tested for the
PURE attribute to call IsPureProcedure().
I also fixed some unrelated tests that got new errors when I implemented these
new checks.
Reviewers: tskeith, DavidTruby, sscalpone
Subscribers: jfb, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D79263
2020-05-02 04:00:28 +08:00
|
|
|
messages_.Say("Component '%s' is a coarray and must have the ALLOCATABLE"
|
|
|
|
" attribute%s"_err_en_US,
|
|
|
|
symbol.name(), deferredMsg);
|
2019-11-23 06:05:54 +08:00
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (!details.coshape().IsAssumedSize()) { // C828
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2021-09-22 07:06:30 +08:00
|
|
|
"'%s' is a non-ALLOCATABLE coarray and must have an explicit coshape"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (const DeclTypeSpec * type{details.type()}) {
|
|
|
|
if (IsBadCoarrayType(type->AsDerived())) { // C747 & C824
|
|
|
|
messages_.Say(
|
|
|
|
"Coarray '%s' may not have type TEAM_TYPE, C_PTR, or C_FUNPTR"_err_en_US,
|
[flang] New implementation for checks for constraints C741 through C750
Summary:
Most of these checks were already implemented, and I just added references to
them to the code and tests. Also, much of this code was already
reviewed in the old flang/f18 GitHub repository, but I didn't get to
merge it before we switched repositories.
I implemented the check for C747 to not allow coarray components in derived
types that are of type C_PTR, C_FUNPTR, or type TEAM_TYPE.
I implemented the check for C748 that requires a data component whose type has
a coarray ultimate component to be a nonpointer, nonallocatable scalar and not
be a coarray.
I implemented the check for C750 that adds additional restrictions to the
bounds expressions of a derived type component that's an array.
These bounds expressions are sepcification expressions as defined in
10.1.11. There was already code in lib/Evaluate/check-expression.cpp to
check semantics for specification expressions, but it did not check for
the extra requirements of C750.
C750 prohibits specification functions, the intrinsic functions
ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, PRESENT, and SAME_TYPE_AS. It
also requires every specification inquiry reference to be a constant
expression, and requires that the value of the bound not depend on the
value of a variable.
To implement these additional checks, I added code to the intrinsic proc
table to get the intrinsic class of a procedure. I also added an
enumeration to distinguish between specification expressions for
derived type component bounds versus for type parameters. I then
changed the code to pass an enumeration value to
"CheckSpecificationExpr()" to indicate that the expression was a bounds
expression and used this value to determine whether to emit an error
message when violations of C750 are found.
I changed the implementation of IsPureProcedure() to handle statement
functions and changed some references in the code that tested for the
PURE attribute to call IsPureProcedure().
I also fixed some unrelated tests that got new errors when I implemented these
new checks.
Reviewers: tskeith, DavidTruby, sscalpone
Subscribers: jfb, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D79263
2020-05-02 04:00:28 +08:00
|
|
|
symbol.name());
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (details.isDummy()) {
|
|
|
|
if (symbol.attrs().test(Attr::INTENT_OUT)) {
|
|
|
|
if (FindUltimateComponent(symbol, [](const Symbol &x) {
|
|
|
|
return IsCoarray(x) && IsAllocatable(x);
|
2020-03-29 12:00:16 +08:00
|
|
|
})) { // C846
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
|
|
|
"An INTENT(OUT) dummy argument may not be, or contain, an ALLOCATABLE coarray"_err_en_US);
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (IsOrContainsEventOrLockComponent(symbol)) { // C847
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
|
|
|
"An INTENT(OUT) dummy argument may not be, or contain, EVENT_TYPE or LOCK_TYPE"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
[flang] New implementation for checks for constraints C741 through C750
Summary:
Most of these checks were already implemented, and I just added references to
them to the code and tests. Also, much of this code was already
reviewed in the old flang/f18 GitHub repository, but I didn't get to
merge it before we switched repositories.
I implemented the check for C747 to not allow coarray components in derived
types that are of type C_PTR, C_FUNPTR, or type TEAM_TYPE.
I implemented the check for C748 that requires a data component whose type has
a coarray ultimate component to be a nonpointer, nonallocatable scalar and not
be a coarray.
I implemented the check for C750 that adds additional restrictions to the
bounds expressions of a derived type component that's an array.
These bounds expressions are sepcification expressions as defined in
10.1.11. There was already code in lib/Evaluate/check-expression.cpp to
check semantics for specification expressions, but it did not check for
the extra requirements of C750.
C750 prohibits specification functions, the intrinsic functions
ALLOCATED, ASSOCIATED, EXTENDS_TYPE_OF, PRESENT, and SAME_TYPE_AS. It
also requires every specification inquiry reference to be a constant
expression, and requires that the value of the bound not depend on the
value of a variable.
To implement these additional checks, I added code to the intrinsic proc
table to get the intrinsic class of a procedure. I also added an
enumeration to distinguish between specification expressions for
derived type component bounds versus for type parameters. I then
changed the code to pass an enumeration value to
"CheckSpecificationExpr()" to indicate that the expression was a bounds
expression and used this value to determine whether to emit an error
message when violations of C750 are found.
I changed the implementation of IsPureProcedure() to handle statement
functions and changed some references in the code that tested for the
PURE attribute to call IsPureProcedure().
I also fixed some unrelated tests that got new errors when I implemented these
new checks.
Reviewers: tskeith, DavidTruby, sscalpone
Subscribers: jfb, llvm-commits
Tags: #llvm, #flang
Differential Revision: https://reviews.llvm.org/D79263
2020-05-02 04:00:28 +08:00
|
|
|
if (InPure() && !IsStmtFunction(DEREF(innermostSymbol_)) &&
|
|
|
|
!IsPointer(symbol) && !IsIntentIn(symbol) &&
|
2019-11-23 06:05:54 +08:00
|
|
|
!symbol.attrs().test(Attr::VALUE)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (InFunction()) { // C1583
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"non-POINTER dummy argument of pure function must be INTENT(IN) or VALUE"_err_en_US);
|
2019-11-23 06:05:54 +08:00
|
|
|
} else if (IsIntentOut(symbol)) {
|
|
|
|
if (const DeclTypeSpec * type{details.type()}) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (type && type->IsPolymorphic()) { // C1588
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"An INTENT(OUT) dummy argument of a pure subroutine may not be polymorphic"_err_en_US);
|
2019-11-23 06:05:54 +08:00
|
|
|
} else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
|
|
|
if (FindUltimateComponent(*derived, [](const Symbol &x) {
|
|
|
|
const DeclTypeSpec *type{x.GetType()};
|
|
|
|
return type && type->IsPolymorphic();
|
2020-03-29 12:00:16 +08:00
|
|
|
})) { // C1588
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"An INTENT(OUT) dummy argument of a pure subroutine may not have a polymorphic ultimate component"_err_en_US);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (HasImpureFinal(*derived)) { // C1587
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"An INTENT(OUT) dummy argument of a pure subroutine may not have an impure FINAL subroutine"_err_en_US);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (!IsIntentInOut(symbol)) { // C1586
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say(
|
2019-12-24 09:12:53 +08:00
|
|
|
"non-POINTER dummy argument of pure subroutine must have INTENT() or VALUE attribute"_err_en_US);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
2021-04-23 10:29:14 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::INTENT_IN) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_OUT) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_INOUT)) {
|
|
|
|
messages_.Say("INTENT attributes may apply only to a dummy "
|
|
|
|
"argument"_err_en_US); // C843
|
|
|
|
} else if (IsOptional(symbol)) {
|
|
|
|
messages_.Say("OPTIONAL attribute may apply only to a dummy "
|
|
|
|
"argument"_err_en_US); // C849
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
2021-09-04 06:03:43 +08:00
|
|
|
if (InElemental()) {
|
|
|
|
if (details.isDummy()) { // C15100
|
|
|
|
if (details.shape().Rank() > 0) {
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument of an ELEMENTAL procedure must be scalar"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument of an ELEMENTAL procedure may not be ALLOCATABLE"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsCoarray(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument of an ELEMENTAL procedure may not be a coarray"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsPointer(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument of an ELEMENTAL procedure may not be a POINTER"_err_en_US);
|
|
|
|
}
|
|
|
|
if (!symbol.attrs().HasAny(Attrs{Attr::VALUE, Attr::INTENT_IN,
|
|
|
|
Attr::INTENT_INOUT, Attr::INTENT_OUT})) { // C15102
|
|
|
|
messages_.Say(
|
|
|
|
"A dummy argument of an ELEMENTAL procedure must have an INTENT() or VALUE attribute"_err_en_US);
|
|
|
|
}
|
|
|
|
} else if (IsFunctionResult(symbol)) { // C15101
|
|
|
|
if (details.shape().Rank() > 0) {
|
|
|
|
messages_.Say(
|
|
|
|
"The result of an ELEMENTAL function must be scalar"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"The result of an ELEMENTAL function may not be ALLOCATABLE"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsPointer(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"The result of an ELEMENTAL function may not be a POINTER"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-11 01:22:39 +08:00
|
|
|
if (HasDeclarationInitializer(symbol)) { // C808; ignore DATA initialization
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
CheckPointerInitialization(symbol);
|
2020-06-19 08:17:04 +08:00
|
|
|
if (IsAutomatic(symbol)) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
messages_.Say(
|
|
|
|
"An automatic variable or component must not be initialized"_err_en_US);
|
2020-06-19 08:17:04 +08:00
|
|
|
} else if (IsDummy(symbol)) {
|
|
|
|
messages_.Say("A dummy argument must not be initialized"_err_en_US);
|
|
|
|
} else if (IsFunctionResult(symbol)) {
|
|
|
|
messages_.Say("A function result must not be initialized"_err_en_US);
|
|
|
|
} else if (IsInBlankCommon(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"A variable in blank COMMON should not be initialized"_en_US);
|
|
|
|
}
|
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (symbol.owner().kind() == Scope::Kind::BlockData) {
|
2020-06-19 08:17:04 +08:00
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"An ALLOCATABLE variable may not appear in a BLOCK DATA subprogram"_err_en_US);
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
} else if (IsInitialized(symbol) && !FindCommonBlockContaining(symbol)) {
|
2020-06-19 08:17:04 +08:00
|
|
|
messages_.Say(
|
|
|
|
"An initialized variable in BLOCK DATA must be in a COMMON block"_err_en_US);
|
2020-01-10 09:12:46 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
if (const DeclTypeSpec * type{details.type()}) { // C708
|
2020-02-12 04:14:04 +08:00
|
|
|
if (type->IsPolymorphic() &&
|
[flang] Semantic checks for C709, C710, and C711
C709 An assumed-type entity shall be a dummy data object that does not
have the ALLOCATABLE, CODIMENSION, INTENT (OUT), POINTER, or VALUE
attribute and is not an explicit-shape array.
C710 An assumed-type variable name shall not appear in a designator or
expression except as an actual argument corresponding to a dummy
argument that is assumed-type, or as the first argument to the intrinsic
function IS_CONTIGUOUS, LBOUND, PRESENT, RANK, SHAPE, SIZE, or UBOUND,
or the function C_LOC from the intrinsic module ISO_C_BINDING.
C711 An assumed-type actual argument that corresponds to an assumed-rank
dummy argument shall be assumed-shape or assumed-rank.
For C709 I added code to check-declarations.cpp. For this, I had to
distinguish between polymorphic types and assumed-type types to
eliminate multiple messages on the same line.
C710 was already checked, but I added a notation in the source.
For C711 I added code to check-call.cpp and the test call15.f90.
Original-commit: flang-compiler/f18@4a703f2b5a6484208a059dc0b456363c138a661d
Reviewed-on: https://github.com/flang-compiler/f18/pull/985
2020-02-15 07:53:11 +08:00
|
|
|
!(type->IsAssumedType() || IsAllocatableOrPointer(symbol) ||
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
IsDummy(symbol))) {
|
2020-02-12 04:14:04 +08:00
|
|
|
messages_.Say("CLASS entity '%s' must be a dummy argument or have "
|
|
|
|
"ALLOCATABLE or POINTER attribute"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 02:54:48 +08:00
|
|
|
}
|
|
|
|
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
void CheckHelper::CheckPointerInitialization(const Symbol &symbol) {
|
|
|
|
if (IsPointer(symbol) && !context_.HasError(symbol) &&
|
|
|
|
!scopeIsUninstantiatedPDT_) {
|
|
|
|
if (const auto *object{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (object->init()) { // C764, C765; C808
|
2021-05-13 03:10:28 +08:00
|
|
|
if (auto designator{evaluate::AsGenericExpr(symbol)}) {
|
|
|
|
auto restorer{messages_.SetLocation(symbol.name())};
|
|
|
|
context_.set_location(symbol.name());
|
|
|
|
CheckInitialTarget(foldingContext_, *designator, *object->init());
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
}
|
2020-09-01 02:54:48 +08:00
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
} else if (const auto *proc{symbol.detailsIf<ProcEntityDetails>()}) {
|
|
|
|
if (proc->init() && *proc->init()) {
|
|
|
|
// C1519 - must be nonelemental external or module procedure,
|
|
|
|
// or an unrestricted specific intrinsic function.
|
|
|
|
const Symbol &ultimate{(*proc->init())->GetUltimate()};
|
|
|
|
if (ultimate.attrs().test(Attr::INTRINSIC)) {
|
2021-10-26 03:43:17 +08:00
|
|
|
if (const auto intrinsic{
|
|
|
|
context_.intrinsics().IsSpecificIntrinsicFunction(
|
|
|
|
ultimate.name().ToString())};
|
|
|
|
!intrinsic || intrinsic->isRestrictedSpecific) { // C1030
|
2021-06-03 08:09:42 +08:00
|
|
|
context_.Say(
|
2021-10-26 03:43:17 +08:00
|
|
|
"Intrinsic procedure '%s' is not an unrestricted specific "
|
|
|
|
"intrinsic permitted for use as the initializer for procedure "
|
|
|
|
"pointer '%s'"_err_en_US,
|
2021-06-03 08:09:42 +08:00
|
|
|
ultimate.name(), symbol.name());
|
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
} else if (!ultimate.attrs().test(Attr::EXTERNAL) &&
|
|
|
|
ultimate.owner().kind() != Scope::Kind::Module) {
|
|
|
|
context_.Say("Procedure pointer '%s' initializer '%s' is neither "
|
|
|
|
"an external nor a module procedure"_err_en_US,
|
|
|
|
symbol.name(), ultimate.name());
|
|
|
|
} else if (ultimate.attrs().test(Attr::ELEMENTAL)) {
|
|
|
|
context_.Say("Procedure pointer '%s' cannot be initialized with the "
|
|
|
|
"elemental procedure '%s"_err_en_US,
|
|
|
|
symbol.name(), ultimate.name());
|
2020-09-01 02:54:48 +08:00
|
|
|
} else {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
// TODO: Check the "shalls" in the 15.4.3.6 paragraphs 7-10.
|
2020-09-01 02:54:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
|
2020-01-15 04:06:52 +08:00
|
|
|
// The six different kinds of array-specs:
|
|
|
|
// array-spec -> explicit-shape-list | deferred-shape-list
|
|
|
|
// | assumed-shape-list | implied-shape-list
|
|
|
|
// | assumed-size | assumed-rank
|
|
|
|
// explicit-shape -> [ lb : ] ub
|
|
|
|
// deferred-shape -> :
|
|
|
|
// assumed-shape -> [ lb ] :
|
|
|
|
// implied-shape -> [ lb : ] *
|
|
|
|
// assumed-size -> [ explicit-shape-list , ] [ lb : ] *
|
|
|
|
// assumed-rank -> ..
|
|
|
|
// Note:
|
|
|
|
// - deferred-shape is also an assumed-shape
|
|
|
|
// - A single "*" or "lb:*" might be assumed-size or implied-shape-list
|
|
|
|
void CheckHelper::CheckArraySpec(
|
|
|
|
const Symbol &symbol, const ArraySpec &arraySpec) {
|
|
|
|
if (arraySpec.Rank() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool isExplicit{arraySpec.IsExplicitShape()};
|
|
|
|
bool isDeferred{arraySpec.IsDeferredShape()};
|
|
|
|
bool isImplied{arraySpec.IsImpliedShape()};
|
|
|
|
bool isAssumedShape{arraySpec.IsAssumedShape()};
|
|
|
|
bool isAssumedSize{arraySpec.IsAssumedSize()};
|
|
|
|
bool isAssumedRank{arraySpec.IsAssumedRank()};
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (symbol.test(Symbol::Flag::CrayPointee) && !isExplicit && !isAssumedSize) {
|
|
|
|
msg = "Cray pointee '%s' must have must have explicit shape or"
|
|
|
|
" assumed size"_err_en_US;
|
|
|
|
} else if (IsAllocatableOrPointer(symbol) && !isDeferred && !isAssumedRank) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (symbol.owner().IsDerivedType()) { // C745
|
2020-01-15 04:06:52 +08:00
|
|
|
if (IsAllocatable(symbol)) {
|
|
|
|
msg = "Allocatable array component '%s' must have"
|
|
|
|
" deferred shape"_err_en_US;
|
|
|
|
} else {
|
|
|
|
msg = "Array pointer component '%s' must have deferred shape"_err_en_US;
|
|
|
|
}
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (IsAllocatable(symbol)) { // C832
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Allocatable array '%s' must have deferred shape or"
|
|
|
|
" assumed rank"_err_en_US;
|
|
|
|
} else {
|
|
|
|
msg = "Array pointer '%s' must have deferred shape or"
|
|
|
|
" assumed rank"_err_en_US;
|
|
|
|
}
|
|
|
|
}
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
} else if (IsDummy(symbol)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (isImplied && !isAssumedSize) { // C836
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Dummy array argument '%s' may not have implied shape"_err_en_US;
|
|
|
|
}
|
|
|
|
} else if (isAssumedShape && !isDeferred) {
|
|
|
|
msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (isAssumedSize && !isImplied) { // C833
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (isAssumedRank) { // C837
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US;
|
|
|
|
} else if (isImplied) {
|
2021-04-23 10:29:14 +08:00
|
|
|
if (!IsNamedConstant(symbol)) { // C835, C836
|
|
|
|
msg = "Implied-shape array '%s' must be a named constant or a "
|
|
|
|
"dummy argument"_err_en_US;
|
2020-01-15 04:06:52 +08:00
|
|
|
}
|
|
|
|
} else if (IsNamedConstant(symbol)) {
|
|
|
|
if (!isExplicit && !isImplied) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
msg = "Named constant '%s' array must have constant or"
|
2020-01-15 04:06:52 +08:00
|
|
|
" implied shape"_err_en_US;
|
|
|
|
}
|
|
|
|
} else if (!IsAllocatableOrPointer(symbol) && !isExplicit) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (symbol.owner().IsDerivedType()) { // C749
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Component array '%s' without ALLOCATABLE or POINTER attribute must"
|
|
|
|
" have explicit shape"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else { // C816
|
2020-01-15 04:06:52 +08:00
|
|
|
msg = "Array '%s' without ALLOCATABLE or POINTER attribute must have"
|
|
|
|
" explicit shape"_err_en_US;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
context_.Say(std::move(*msg), symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckHelper::CheckProcEntity(
|
|
|
|
const Symbol &symbol, const ProcEntityDetails &details) {
|
|
|
|
if (details.isDummy()) {
|
2021-01-15 23:04:20 +08:00
|
|
|
if (!symbol.attrs().test(Attr::POINTER) && // C843
|
|
|
|
(symbol.attrs().test(Attr::INTENT_IN) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_OUT) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_INOUT))) {
|
|
|
|
messages_.Say("A dummy procedure without the POINTER attribute"
|
|
|
|
" may not have an INTENT attribute"_err_en_US);
|
|
|
|
}
|
2021-09-04 06:03:43 +08:00
|
|
|
if (InElemental()) { // C15100
|
|
|
|
messages_.Say(
|
|
|
|
"An ELEMENTAL subprogram may not have a dummy procedure"_err_en_US);
|
|
|
|
}
|
2021-05-25 04:12:19 +08:00
|
|
|
const Symbol *interface { details.interface().symbol() };
|
2019-11-23 06:05:54 +08:00
|
|
|
if (!symbol.attrs().test(Attr::INTRINSIC) &&
|
|
|
|
(symbol.attrs().test(Attr::ELEMENTAL) ||
|
|
|
|
(interface && !interface->attrs().test(Attr::INTRINSIC) &&
|
|
|
|
interface->attrs().test(Attr::ELEMENTAL)))) {
|
|
|
|
// There's no explicit constraint or "shall" that we can find in the
|
|
|
|
// standard for this check, but it seems to be implied in multiple
|
|
|
|
// sites, and ELEMENTAL non-intrinsic actual arguments *are*
|
|
|
|
// explicitly forbidden. But we allow "PROCEDURE(SIN)::dummy"
|
|
|
|
// because it is explicitly legal to *pass* the specific intrinsic
|
|
|
|
// function SIN as an actual argument.
|
|
|
|
messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US);
|
|
|
|
}
|
2021-04-23 10:29:14 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::INTENT_IN) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_OUT) ||
|
|
|
|
symbol.attrs().test(Attr::INTENT_INOUT)) {
|
|
|
|
messages_.Say("INTENT attributes may apply only to a dummy "
|
|
|
|
"argument"_err_en_US); // C843
|
|
|
|
} else if (IsOptional(symbol)) {
|
|
|
|
messages_.Say("OPTIONAL attribute may apply only to a dummy "
|
|
|
|
"argument"_err_en_US); // C849
|
2019-12-06 02:24:18 +08:00
|
|
|
} else if (symbol.owner().IsDerivedType()) {
|
2020-05-13 00:53:58 +08:00
|
|
|
if (!symbol.attrs().test(Attr::POINTER)) { // C756
|
|
|
|
const auto &name{symbol.name()};
|
|
|
|
messages_.Say(name,
|
|
|
|
"Procedure component '%s' must have POINTER attribute"_err_en_US,
|
|
|
|
name);
|
|
|
|
}
|
2019-12-06 02:24:18 +08:00
|
|
|
CheckPassArg(symbol, details.interface().symbol(), details);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
2020-01-14 08:39:00 +08:00
|
|
|
if (symbol.attrs().test(Attr::POINTER)) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
CheckPointerInitialization(symbol);
|
2020-01-14 08:39:00 +08:00
|
|
|
if (const Symbol * interface{details.interface().symbol()}) {
|
2021-06-03 08:09:42 +08:00
|
|
|
if (interface->attrs().test(Attr::INTRINSIC)) {
|
2021-10-26 03:43:17 +08:00
|
|
|
if (const auto intrinsic{
|
|
|
|
context_.intrinsics().IsSpecificIntrinsicFunction(
|
|
|
|
interface->name().ToString())};
|
|
|
|
!intrinsic || intrinsic->isRestrictedSpecific) { // C1515
|
2021-06-03 08:09:42 +08:00
|
|
|
messages_.Say(
|
2021-10-26 03:43:17 +08:00
|
|
|
"Intrinsic procedure '%s' is not an unrestricted specific "
|
|
|
|
"intrinsic permitted for use as the definition of the interface "
|
|
|
|
"to procedure pointer '%s'"_err_en_US,
|
2021-06-03 08:09:42 +08:00
|
|
|
interface->name(), symbol.name());
|
|
|
|
}
|
|
|
|
} else if (interface->attrs().test(Attr::ELEMENTAL)) {
|
2020-01-14 08:39:00 +08:00
|
|
|
messages_.Say("Procedure pointer '%s' may not be ELEMENTAL"_err_en_US,
|
2020-03-29 12:00:16 +08:00
|
|
|
symbol.name()); // C1517
|
2020-01-14 08:39:00 +08:00
|
|
|
}
|
|
|
|
}
|
2020-06-19 08:17:04 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::SAVE)) {
|
|
|
|
messages_.Say(
|
|
|
|
"Procedure '%s' with SAVE attribute must also have POINTER attribute"_err_en_US,
|
|
|
|
symbol.name());
|
2020-01-14 08:39:00 +08:00
|
|
|
}
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
|
2020-03-18 05:48:36 +08:00
|
|
|
// When a module subprogram has the MODULE prefix the following must match
|
|
|
|
// with the corresponding separate module procedure interface body:
|
|
|
|
// - C1549: characteristics and dummy argument names
|
|
|
|
// - C1550: binding label
|
|
|
|
// - C1551: NON_RECURSIVE prefix
|
|
|
|
class SubprogramMatchHelper {
|
|
|
|
public:
|
2020-09-10 22:22:52 +08:00
|
|
|
explicit SubprogramMatchHelper(CheckHelper &checkHelper)
|
|
|
|
: checkHelper{checkHelper} {}
|
2020-03-18 05:48:36 +08:00
|
|
|
|
|
|
|
void Check(const Symbol &, const Symbol &);
|
|
|
|
|
|
|
|
private:
|
2020-09-10 22:22:52 +08:00
|
|
|
SemanticsContext &context() { return checkHelper.context(); }
|
2020-03-18 05:48:36 +08:00
|
|
|
void CheckDummyArg(const Symbol &, const Symbol &, const DummyArgument &,
|
|
|
|
const DummyArgument &);
|
|
|
|
void CheckDummyDataObject(const Symbol &, const Symbol &,
|
|
|
|
const DummyDataObject &, const DummyDataObject &);
|
|
|
|
void CheckDummyProcedure(const Symbol &, const Symbol &,
|
|
|
|
const DummyProcedure &, const DummyProcedure &);
|
|
|
|
bool CheckSameIntent(
|
|
|
|
const Symbol &, const Symbol &, common::Intent, common::Intent);
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename... A>
|
2020-03-18 05:48:36 +08:00
|
|
|
void Say(
|
|
|
|
const Symbol &, const Symbol &, parser::MessageFixedText &&, A &&...);
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename ATTRS>
|
2020-03-18 05:48:36 +08:00
|
|
|
bool CheckSameAttrs(const Symbol &, const Symbol &, ATTRS, ATTRS);
|
|
|
|
bool ShapesAreCompatible(const DummyDataObject &, const DummyDataObject &);
|
|
|
|
evaluate::Shape FoldShape(const evaluate::Shape &);
|
|
|
|
std::string AsFortran(DummyDataObject::Attr attr) {
|
|
|
|
return parser::ToUpperCaseLetters(DummyDataObject::EnumToString(attr));
|
|
|
|
}
|
|
|
|
std::string AsFortran(DummyProcedure::Attr attr) {
|
|
|
|
return parser::ToUpperCaseLetters(DummyProcedure::EnumToString(attr));
|
|
|
|
}
|
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
CheckHelper &checkHelper;
|
2020-03-18 05:48:36 +08:00
|
|
|
};
|
|
|
|
|
2020-03-20 07:31:10 +08:00
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
|
2020-03-18 05:48:36 +08:00
|
|
|
void CheckHelper::CheckSubprogram(
|
2020-03-20 07:31:10 +08:00
|
|
|
const Symbol &symbol, const SubprogramDetails &details) {
|
|
|
|
if (const Symbol * iface{FindSeparateModuleSubprogramInterface(&symbol)}) {
|
2020-09-10 22:22:52 +08:00
|
|
|
SubprogramMatchHelper{*this}.Check(symbol, *iface);
|
2020-03-20 07:31:10 +08:00
|
|
|
}
|
|
|
|
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() &&
|
2021-06-16 06:17:16 +08:00
|
|
|
subprogramDetails->isFunction() &&
|
|
|
|
!context_.HasError(details.result()) &&
|
|
|
|
!context_.HasError(subprogramDetails->result())) {
|
2020-03-20 07:31:10 +08:00
|
|
|
auto result{FunctionResult::Characterize(
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
details.result(), context_.foldingContext())};
|
2020-03-20 07:31:10 +08:00
|
|
|
auto subpResult{FunctionResult::Characterize(
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
subprogramDetails->result(), context_.foldingContext())};
|
2020-03-20 07:31:10 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-03-18 05:48:36 +08:00
|
|
|
}
|
|
|
|
}
|
2021-09-04 06:03:43 +08:00
|
|
|
if (symbol.attrs().test(Attr::ELEMENTAL)) {
|
|
|
|
// See comment on the similar check in CheckProcEntity()
|
|
|
|
if (details.isDummy()) {
|
|
|
|
messages_.Say("A dummy procedure may not be ELEMENTAL"_err_en_US);
|
|
|
|
} else {
|
|
|
|
for (const Symbol *dummy : details.dummyArgs()) {
|
|
|
|
if (!dummy) { // C15100
|
|
|
|
messages_.Say(
|
|
|
|
"An ELEMENTAL subroutine may not have an alternate return dummy argument"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-23 05:53:50 +08:00
|
|
|
}
|
2020-03-18 05:48:36 +08:00
|
|
|
}
|
|
|
|
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckHelper::CheckDerivedType(
|
2020-10-01 04:34:23 +08:00
|
|
|
const Symbol &derivedType, const DerivedTypeDetails &details) {
|
2021-06-05 04:24:19 +08:00
|
|
|
if (details.isForwardReferenced() && !context_.HasError(derivedType)) {
|
|
|
|
messages_.Say("The derived type '%s' has not been defined"_err_en_US,
|
|
|
|
derivedType.name());
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
const Scope *scope{derivedType.scope()};
|
2020-03-31 08:42:50 +08:00
|
|
|
if (!scope) {
|
2019-11-23 00:15:02 +08:00
|
|
|
CHECK(details.isForwardReferenced());
|
|
|
|
return;
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
CHECK(scope->symbol() == &derivedType);
|
2020-03-31 08:42:50 +08:00
|
|
|
CHECK(scope->IsDerivedType());
|
2020-10-01 04:34:23 +08:00
|
|
|
if (derivedType.attrs().test(Attr::ABSTRACT) && // C734
|
|
|
|
(derivedType.attrs().test(Attr::BIND_C) || details.sequence())) {
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say("An ABSTRACT derived type must be extensible"_err_en_US);
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
if (const DeclTypeSpec * parent{FindParentTypeSpec(derivedType)}) {
|
2019-11-23 06:05:54 +08:00
|
|
|
const DerivedTypeSpec *parentDerived{parent->AsDerived()};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (!IsExtensibleType(parentDerived)) { // C705
|
2019-11-23 06:05:54 +08:00
|
|
|
messages_.Say("The parent type is not extensible"_err_en_US);
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
if (!derivedType.attrs().test(Attr::ABSTRACT) && parentDerived &&
|
2019-11-23 06:05:54 +08:00
|
|
|
parentDerived->typeSymbol().attrs().test(Attr::ABSTRACT)) {
|
|
|
|
ScopeComponentIterator components{*parentDerived};
|
|
|
|
for (const Symbol &component : components) {
|
|
|
|
if (component.attrs().test(Attr::DEFERRED)) {
|
2020-03-31 08:42:50 +08:00
|
|
|
if (scope->FindComponent(component.name()) == &component) {
|
2019-11-23 06:05:54 +08:00
|
|
|
SayWithDeclaration(component,
|
|
|
|
"Non-ABSTRACT extension of ABSTRACT derived type '%s' lacks a binding for DEFERRED procedure '%s'"_err_en_US,
|
|
|
|
parentDerived->typeSymbol().name(), component.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
DerivedTypeSpec derived{derivedType.name(), derivedType};
|
2020-03-31 08:42:50 +08:00
|
|
|
derived.set_scope(*scope);
|
|
|
|
if (FindCoarrayUltimateComponent(derived) && // C736
|
|
|
|
!(parentDerived && FindCoarrayUltimateComponent(*parentDerived))) {
|
|
|
|
messages_.Say(
|
|
|
|
"Type '%s' has a coarray ultimate component so the type at the base "
|
|
|
|
"of its type extension chain ('%s') must be a type that has a "
|
|
|
|
"coarray ultimate component"_err_en_US,
|
2020-10-01 04:34:23 +08:00
|
|
|
derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name());
|
2020-03-31 08:42:50 +08:00
|
|
|
}
|
|
|
|
if (FindEventOrLockPotentialComponent(derived) && // C737
|
|
|
|
!(FindEventOrLockPotentialComponent(*parentDerived) ||
|
|
|
|
IsEventTypeOrLockType(parentDerived))) {
|
|
|
|
messages_.Say(
|
|
|
|
"Type '%s' has an EVENT_TYPE or LOCK_TYPE component, so the type "
|
|
|
|
"at the base of its type extension chain ('%s') must either have an "
|
|
|
|
"EVENT_TYPE or LOCK_TYPE component, or be EVENT_TYPE or "
|
|
|
|
"LOCK_TYPE"_err_en_US,
|
2020-10-01 04:34:23 +08:00
|
|
|
derivedType.name(), scope->GetDerivedTypeBase().GetSymbol()->name());
|
2020-03-31 08:42:50 +08:00
|
|
|
}
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
if (HasIntrinsicTypeName(derivedType)) { // C729
|
2020-03-20 11:07:01 +08:00
|
|
|
messages_.Say("A derived type name cannot be the name of an intrinsic"
|
|
|
|
" type"_err_en_US);
|
|
|
|
}
|
2020-10-01 04:34:23 +08:00
|
|
|
std::map<SourceName, SymbolRef> previous;
|
|
|
|
for (const auto &pair : details.finals()) {
|
|
|
|
SourceName source{pair.first};
|
|
|
|
const Symbol &ref{*pair.second};
|
|
|
|
if (CheckFinal(ref, source, derivedType) &&
|
|
|
|
std::all_of(previous.begin(), previous.end(),
|
|
|
|
[&](std::pair<SourceName, SymbolRef> prev) {
|
|
|
|
return CheckDistinguishableFinals(
|
|
|
|
ref, source, *prev.second, prev.first, derivedType);
|
|
|
|
})) {
|
|
|
|
previous.emplace(source, ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// C786
|
|
|
|
bool CheckHelper::CheckFinal(
|
|
|
|
const Symbol &subroutine, SourceName finalName, const Symbol &derivedType) {
|
|
|
|
if (!IsModuleProcedure(subroutine)) {
|
|
|
|
SayWithDeclaration(subroutine, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must be a module procedure"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const Procedure *proc{Characterize(subroutine)};
|
|
|
|
if (!proc) {
|
|
|
|
return false; // error recovery
|
|
|
|
}
|
|
|
|
if (!proc->IsSubroutine()) {
|
|
|
|
SayWithDeclaration(subroutine, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must be a subroutine"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (proc->dummyArguments.size() != 1) {
|
|
|
|
SayWithDeclaration(subroutine, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must have a single dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const auto &arg{proc->dummyArguments[0]};
|
|
|
|
const Symbol *errSym{&subroutine};
|
|
|
|
if (const auto *details{subroutine.detailsIf<SubprogramDetails>()}) {
|
|
|
|
if (!details->dummyArgs().empty()) {
|
|
|
|
if (const Symbol * argSym{details->dummyArgs()[0]}) {
|
|
|
|
errSym = argSym;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto *ddo{std::get_if<DummyDataObject>(&arg.u)};
|
|
|
|
if (!ddo) {
|
|
|
|
SayWithDeclaration(subroutine, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must have a single dummy argument that is a data object"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool ok{true};
|
|
|
|
if (arg.IsOptional()) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have an OPTIONAL dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->attrs.test(DummyDataObject::Attr::Allocatable)) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have an ALLOCATABLE dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->attrs.test(DummyDataObject::Attr::Pointer)) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have a POINTER dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->intent == common::Intent::Out) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with INTENT(OUT)"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->attrs.test(DummyDataObject::Attr::Value)) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have a dummy argument with the VALUE attribute"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->type.corank() > 0) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have a coarray dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
if (ddo->type.type().IsPolymorphic()) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must not have a polymorphic dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
} else if (ddo->type.type().category() != TypeCategory::Derived ||
|
|
|
|
&ddo->type.type().GetDerivedTypeSpec().typeSymbol() != &derivedType) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must have a TYPE(%s) dummy argument"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name(), derivedType.name());
|
|
|
|
ok = false;
|
|
|
|
} else { // check that all LEN type parameters are assumed
|
|
|
|
for (auto ref : OrderParameterDeclarations(derivedType)) {
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (IsLenTypeParameter(*ref)) {
|
|
|
|
const auto *value{
|
|
|
|
ddo->type.type().GetDerivedTypeSpec().FindParameter(ref->name())};
|
|
|
|
if (!value || !value->isAssumed()) {
|
|
|
|
SayWithDeclaration(*errSym, finalName,
|
|
|
|
"FINAL subroutine '%s' of derived type '%s' must have a dummy argument with an assumed LEN type parameter '%s=*'"_err_en_US,
|
|
|
|
subroutine.name(), derivedType.name(), ref->name());
|
|
|
|
ok = false;
|
2020-10-01 04:34:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckHelper::CheckDistinguishableFinals(const Symbol &f1,
|
|
|
|
SourceName f1Name, const Symbol &f2, SourceName f2Name,
|
|
|
|
const Symbol &derivedType) {
|
|
|
|
const Procedure *p1{Characterize(f1)};
|
|
|
|
const Procedure *p2{Characterize(f2)};
|
|
|
|
if (p1 && p2) {
|
2021-10-19 01:44:39 +08:00
|
|
|
if (characteristics::Distinguishable(
|
|
|
|
context_.languageFeatures(), *p1, *p2)) {
|
2020-10-01 04:34:23 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (auto *msg{messages_.Say(f1Name,
|
|
|
|
"FINAL subroutines '%s' and '%s' of derived type '%s' cannot be distinguished by rank or KIND type parameter value"_err_en_US,
|
|
|
|
f1Name, f2Name, derivedType.name())}) {
|
|
|
|
msg->Attach(f2Name, "FINAL declaration of '%s'"_en_US, f2.name())
|
|
|
|
.Attach(f1.name(), "Definition of '%s'"_en_US, f1Name)
|
|
|
|
.Attach(f2.name(), "Definition of '%s'"_en_US, f2Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
|
2020-08-25 03:53:44 +08:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckHelper::CheckGeneric(
|
|
|
|
const Symbol &symbol, const GenericDetails &details) {
|
2020-09-10 22:22:52 +08:00
|
|
|
CheckSpecificsAreDistinguishable(symbol, details);
|
2021-05-25 04:12:19 +08:00
|
|
|
std::visit(common::visitors{
|
|
|
|
[&](const GenericKind::DefinedIo &io) {
|
|
|
|
CheckDefinedIoProc(symbol, details, io);
|
|
|
|
},
|
|
|
|
[](const auto &) {},
|
|
|
|
},
|
|
|
|
details.kind().u);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the specifics of this generic are distinguishable from each other
|
2020-09-10 22:22:52 +08:00
|
|
|
void CheckHelper::CheckSpecificsAreDistinguishable(
|
|
|
|
const Symbol &generic, const GenericDetails &details) {
|
|
|
|
GenericKind kind{details.kind()};
|
2019-11-23 06:05:54 +08:00
|
|
|
const SymbolVector &specifics{details.specificProcs()};
|
|
|
|
std::size_t count{specifics.size()};
|
2020-09-10 22:22:52 +08:00
|
|
|
if (count < 2 || !kind.IsName()) {
|
2019-11-23 06:05:54 +08:00
|
|
|
return;
|
|
|
|
}
|
2020-09-10 22:22:52 +08:00
|
|
|
DistinguishabilityHelper helper{context_};
|
|
|
|
for (const Symbol &specific : specifics) {
|
|
|
|
if (const Procedure * procedure{Characterize(specific)}) {
|
|
|
|
helper.Add(generic, kind, specific, *procedure);
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
2020-12-03 07:13:49 +08:00
|
|
|
helper.Check(generic.owner());
|
2019-11-23 06:05:54 +08:00
|
|
|
}
|
|
|
|
|
2019-11-23 08:46:11 +08:00
|
|
|
static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
|
|
|
|
auto lhs{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
|
|
|
|
auto rhs{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
|
|
|
|
return Tristate::No ==
|
|
|
|
IsDefinedAssignment(lhs.type(), lhs.Rank(), rhs.type(), rhs.Rank());
|
2019-11-23 06:40:53 +08:00
|
|
|
}
|
|
|
|
|
2019-12-03 00:55:44 +08:00
|
|
|
static bool ConflictsWithIntrinsicOperator(
|
|
|
|
const GenericKind &kind, const Procedure &proc) {
|
2020-09-10 22:22:52 +08:00
|
|
|
if (!kind.IsIntrinsicOperator()) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-03 00:55:44 +08:00
|
|
|
auto arg0{std::get<DummyDataObject>(proc.dummyArguments[0].u).type};
|
|
|
|
auto type0{arg0.type()};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (proc.dummyArguments.size() == 1) { // unary
|
2019-12-03 00:55:44 +08:00
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](common::NumericOperator) { return IsIntrinsicNumeric(type0); },
|
|
|
|
[&](common::LogicalOperator) { return IsIntrinsicLogical(type0); },
|
|
|
|
[](const auto &) -> bool { DIE("bad generic kind"); },
|
|
|
|
},
|
|
|
|
kind.u);
|
2020-03-29 12:00:16 +08:00
|
|
|
} else { // binary
|
2019-12-03 00:55:44 +08:00
|
|
|
int rank0{arg0.Rank()};
|
|
|
|
auto arg1{std::get<DummyDataObject>(proc.dummyArguments[1].u).type};
|
|
|
|
auto type1{arg1.type()};
|
|
|
|
int rank1{arg1.Rank()};
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[&](common::NumericOperator) {
|
|
|
|
return IsIntrinsicNumeric(type0, rank0, type1, rank1);
|
|
|
|
},
|
|
|
|
[&](common::LogicalOperator) {
|
|
|
|
return IsIntrinsicLogical(type0, rank0, type1, rank1);
|
|
|
|
},
|
|
|
|
[&](common::RelationalOperator opr) {
|
|
|
|
return IsIntrinsicRelational(opr, type0, rank0, type1, rank1);
|
|
|
|
},
|
|
|
|
[&](GenericKind::OtherKind x) {
|
|
|
|
CHECK(x == GenericKind::OtherKind::Concat);
|
|
|
|
return IsIntrinsicConcat(type0, rank0, type1, rank1);
|
|
|
|
},
|
|
|
|
[](const auto &) -> bool { DIE("bad generic kind"); },
|
|
|
|
},
|
|
|
|
kind.u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this procedure can be used for defined operators (see 15.4.3.4.2).
|
2020-09-10 22:22:52 +08:00
|
|
|
bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind,
|
|
|
|
const Symbol &specific, const Procedure &proc) {
|
|
|
|
if (context_.HasError(specific)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-03 00:55:44 +08:00
|
|
|
std::optional<parser::MessageFixedText> msg;
|
2021-09-02 23:07:05 +08:00
|
|
|
auto checkDefinedOperatorArgs{
|
|
|
|
[&](SourceName opName, const Symbol &specific, const Procedure &proc) {
|
|
|
|
bool arg0Defined{CheckDefinedOperatorArg(opName, specific, proc, 0)};
|
|
|
|
bool arg1Defined{CheckDefinedOperatorArg(opName, specific, proc, 1)};
|
|
|
|
return arg0Defined && arg1Defined;
|
|
|
|
}};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (specific.attrs().test(Attr::NOPASS)) { // C774
|
2019-12-14 08:22:48 +08:00
|
|
|
msg = "%s procedure '%s' may not have NOPASS attribute"_err_en_US;
|
|
|
|
} else if (!proc.functionResult.has_value()) {
|
2019-12-03 00:55:44 +08:00
|
|
|
msg = "%s procedure '%s' must be a function"_err_en_US;
|
|
|
|
} else if (proc.functionResult->IsAssumedLengthCharacter()) {
|
|
|
|
msg = "%s function '%s' may not have assumed-length CHARACTER(*)"
|
|
|
|
" result"_err_en_US;
|
|
|
|
} else if (auto m{CheckNumberOfArgs(kind, proc.dummyArguments.size())}) {
|
|
|
|
msg = std::move(m);
|
2021-09-02 23:07:05 +08:00
|
|
|
} else if (!checkDefinedOperatorArgs(opName, specific, proc)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return false; // error was reported
|
2019-12-03 00:55:44 +08:00
|
|
|
} else if (ConflictsWithIntrinsicOperator(kind, proc)) {
|
|
|
|
msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US;
|
|
|
|
} else {
|
2020-03-29 12:00:16 +08:00
|
|
|
return true; // OK
|
2019-12-03 00:55:44 +08:00
|
|
|
}
|
2020-09-10 22:22:52 +08:00
|
|
|
SayWithDeclaration(
|
|
|
|
specific, std::move(*msg), MakeOpName(opName), specific.name());
|
|
|
|
context_.SetError(specific);
|
2019-12-03 00:55:44 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the number of arguments is wrong for this intrinsic operator, return
|
|
|
|
// false and return the error message in msg.
|
|
|
|
std::optional<parser::MessageFixedText> CheckHelper::CheckNumberOfArgs(
|
|
|
|
const GenericKind &kind, std::size_t nargs) {
|
2020-09-10 22:22:52 +08:00
|
|
|
if (!kind.IsIntrinsicOperator()) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
std::size_t min{2}, max{2}; // allowed number of args; default is binary
|
|
|
|
std::visit(common::visitors{
|
|
|
|
[&](const common::NumericOperator &x) {
|
|
|
|
if (x == common::NumericOperator::Add ||
|
|
|
|
x == common::NumericOperator::Subtract) {
|
|
|
|
min = 1; // + and - are unary or binary
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[&](const common::LogicalOperator &x) {
|
|
|
|
if (x == common::LogicalOperator::Not) {
|
|
|
|
min = 1; // .NOT. is unary
|
|
|
|
max = 1;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[](const common::RelationalOperator &) {
|
|
|
|
// all are binary
|
|
|
|
},
|
|
|
|
[](const GenericKind::OtherKind &x) {
|
|
|
|
CHECK(x == GenericKind::OtherKind::Concat);
|
|
|
|
},
|
|
|
|
[](const auto &) { DIE("expected intrinsic operator"); },
|
|
|
|
},
|
2019-12-03 00:55:44 +08:00
|
|
|
kind.u);
|
|
|
|
if (nargs >= min && nargs <= max) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else if (max == 1) {
|
|
|
|
return "%s function '%s' must have one dummy argument"_err_en_US;
|
|
|
|
} else if (min == 2) {
|
|
|
|
return "%s function '%s' must have two dummy arguments"_err_en_US;
|
|
|
|
} else {
|
|
|
|
return "%s function '%s' must have one or two dummy arguments"_err_en_US;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckHelper::CheckDefinedOperatorArg(const SourceName &opName,
|
|
|
|
const Symbol &symbol, const Procedure &proc, std::size_t pos) {
|
|
|
|
if (pos >= proc.dummyArguments.size()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto &arg{proc.dummyArguments.at(pos)};
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (arg.IsOptional()) {
|
|
|
|
msg = "In %s function '%s', dummy argument '%s' may not be"
|
|
|
|
" OPTIONAL"_err_en_US;
|
|
|
|
} else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)};
|
|
|
|
dataObject == nullptr) {
|
|
|
|
msg = "In %s function '%s', dummy argument '%s' must be a"
|
|
|
|
" data object"_err_en_US;
|
|
|
|
} else if (dataObject->intent != common::Intent::In &&
|
|
|
|
!dataObject->attrs.test(DummyDataObject::Attr::Value)) {
|
|
|
|
msg = "In %s function '%s', dummy argument '%s' must have INTENT(IN)"
|
|
|
|
" or VALUE attribute"_err_en_US;
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
SayWithDeclaration(symbol, std::move(*msg),
|
|
|
|
parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-11-23 06:40:53 +08:00
|
|
|
// Check if this procedure can be used for defined assignment (see 15.4.3.4.3).
|
|
|
|
bool CheckHelper::CheckDefinedAssignment(
|
|
|
|
const Symbol &specific, const Procedure &proc) {
|
2020-09-10 22:22:52 +08:00
|
|
|
if (context_.HasError(specific)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-11-23 06:40:53 +08:00
|
|
|
std::optional<parser::MessageFixedText> msg;
|
2020-03-29 12:00:16 +08:00
|
|
|
if (specific.attrs().test(Attr::NOPASS)) { // C774
|
2019-12-14 08:22:48 +08:00
|
|
|
msg = "Defined assignment procedure '%s' may not have"
|
|
|
|
" NOPASS attribute"_err_en_US;
|
|
|
|
} else if (!proc.IsSubroutine()) {
|
2019-11-23 06:40:53 +08:00
|
|
|
msg = "Defined assignment procedure '%s' must be a subroutine"_err_en_US;
|
|
|
|
} else if (proc.dummyArguments.size() != 2) {
|
|
|
|
msg = "Defined assignment subroutine '%s' must have"
|
|
|
|
" two dummy arguments"_err_en_US;
|
|
|
|
} else {
|
2021-10-23 07:39:16 +08:00
|
|
|
// Check both arguments even if the first has an error.
|
|
|
|
bool ok0{CheckDefinedAssignmentArg(specific, proc.dummyArguments[0], 0)};
|
|
|
|
bool ok1{CheckDefinedAssignmentArg(specific, proc.dummyArguments[1], 1)};
|
|
|
|
if (!(ok0 && ok1)) {
|
|
|
|
return false; // error was reported
|
|
|
|
} else if (ConflictsWithIntrinsicAssignment(proc)) {
|
|
|
|
msg = "Defined assignment subroutine '%s' conflicts with"
|
|
|
|
" intrinsic assignment"_err_en_US;
|
|
|
|
} else {
|
|
|
|
return true; // OK
|
|
|
|
}
|
2019-11-23 06:40:53 +08:00
|
|
|
}
|
|
|
|
SayWithDeclaration(specific, std::move(msg.value()), specific.name());
|
2020-09-10 22:22:52 +08:00
|
|
|
context_.SetError(specific);
|
2019-11-23 06:40:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CheckHelper::CheckDefinedAssignmentArg(
|
|
|
|
const Symbol &symbol, const DummyArgument &arg, int pos) {
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (arg.IsOptional()) {
|
|
|
|
msg = "In defined assignment subroutine '%s', dummy argument '%s'"
|
|
|
|
" may not be OPTIONAL"_err_en_US;
|
|
|
|
} else if (const auto *dataObject{std::get_if<DummyDataObject>(&arg.u)}) {
|
|
|
|
if (pos == 0) {
|
|
|
|
if (dataObject->intent != common::Intent::Out &&
|
|
|
|
dataObject->intent != common::Intent::InOut) {
|
|
|
|
msg = "In defined assignment subroutine '%s', first dummy argument '%s'"
|
|
|
|
" must have INTENT(OUT) or INTENT(INOUT)"_err_en_US;
|
|
|
|
}
|
|
|
|
} else if (pos == 1) {
|
|
|
|
if (dataObject->intent != common::Intent::In &&
|
|
|
|
!dataObject->attrs.test(DummyDataObject::Attr::Value)) {
|
|
|
|
msg =
|
|
|
|
"In defined assignment subroutine '%s', second dummy"
|
|
|
|
" argument '%s' must have INTENT(IN) or VALUE attribute"_err_en_US;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DIE("pos must be 0 or 1");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
msg = "In defined assignment subroutine '%s', dummy argument '%s'"
|
|
|
|
" must be a data object"_err_en_US;
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name);
|
2020-09-10 22:22:52 +08:00
|
|
|
context_.SetError(symbol);
|
2019-11-23 06:40:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-14 08:39:00 +08:00
|
|
|
// Report a conflicting attribute error if symbol has both of these attributes
|
|
|
|
bool CheckHelper::CheckConflicting(const Symbol &symbol, Attr a1, Attr a2) {
|
|
|
|
if (symbol.attrs().test(a1) && symbol.attrs().test(a2)) {
|
|
|
|
messages_.Say("'%s' may not have both the %s and %s attributes"_err_en_US,
|
2021-05-25 04:12:19 +08:00
|
|
|
symbol.name(), AttrToString(a1), AttrToString(a2));
|
2020-01-14 08:39:00 +08:00
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-31 04:30:42 +08:00
|
|
|
void CheckHelper::WarnMissingFinal(const Symbol &symbol) {
|
|
|
|
const auto *object{symbol.detailsIf<ObjectEntityDetails>()};
|
|
|
|
if (!object || IsPointer(symbol)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const DeclTypeSpec *type{object->type()};
|
|
|
|
const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr};
|
|
|
|
const Symbol *derivedSym{derived ? &derived->typeSymbol() : nullptr};
|
|
|
|
int rank{object->shape().Rank()};
|
|
|
|
const Symbol *initialDerivedSym{derivedSym};
|
|
|
|
while (const auto *derivedDetails{
|
|
|
|
derivedSym ? derivedSym->detailsIf<DerivedTypeDetails>() : nullptr}) {
|
|
|
|
if (!derivedDetails->finals().empty() &&
|
|
|
|
!derivedDetails->GetFinalForRank(rank)) {
|
|
|
|
if (auto *msg{derivedSym == initialDerivedSym
|
|
|
|
? messages_.Say(symbol.name(),
|
|
|
|
"'%s' of derived type '%s' does not have a FINAL subroutine for its rank (%d)"_en_US,
|
|
|
|
symbol.name(), derivedSym->name(), rank)
|
|
|
|
: messages_.Say(symbol.name(),
|
|
|
|
"'%s' of derived type '%s' extended from '%s' does not have a FINAL subroutine for its rank (%d)"_en_US,
|
|
|
|
symbol.name(), initialDerivedSym->name(),
|
|
|
|
derivedSym->name(), rank)}) {
|
|
|
|
msg->Attach(derivedSym->name(),
|
|
|
|
"Declaration of derived type '%s'"_en_US, derivedSym->name());
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
derived = derivedSym->GetParentTypeSpec();
|
|
|
|
derivedSym = derived ? &derived->typeSymbol() : nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
const Procedure *CheckHelper::Characterize(const Symbol &symbol) {
|
|
|
|
auto it{characterizeCache_.find(symbol)};
|
|
|
|
if (it == characterizeCache_.end()) {
|
|
|
|
auto pair{characterizeCache_.emplace(SymbolRef{symbol},
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
Procedure::Characterize(symbol, context_.foldingContext()))};
|
2020-09-10 22:22:52 +08:00
|
|
|
it = pair.first;
|
|
|
|
}
|
|
|
|
return common::GetPtrFromOptional(it->second);
|
2019-11-23 06:40:53 +08:00
|
|
|
}
|
|
|
|
|
2021-01-22 06:54:53 +08:00
|
|
|
void CheckHelper::CheckVolatile(const Symbol &symbol,
|
2020-03-29 12:00:16 +08:00
|
|
|
const DerivedTypeSpec *derived) { // C866 - C868
|
2019-10-30 03:46:25 +08:00
|
|
|
if (IsIntentIn(symbol)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VOLATILE attribute may not apply to an INTENT(IN) argument"_err_en_US);
|
|
|
|
}
|
|
|
|
if (IsProcedure(symbol)) {
|
|
|
|
messages_.Say("VOLATILE attribute may apply only to a variable"_err_en_US);
|
|
|
|
}
|
2021-01-22 06:54:53 +08:00
|
|
|
if (symbol.has<UseDetails>() || symbol.has<HostAssocDetails>()) {
|
2019-10-30 03:46:25 +08:00
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
if (IsCoarray(ultimate)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VOLATILE attribute may not apply to a coarray accessed by USE or host association"_err_en_US);
|
|
|
|
}
|
2019-11-10 01:29:31 +08:00
|
|
|
if (derived) {
|
2019-10-30 03:46:25 +08:00
|
|
|
if (FindCoarrayUltimateComponent(*derived)) {
|
|
|
|
messages_.Say(
|
|
|
|
"VOLATILE attribute may not apply to a type with a coarray ultimate component accessed by USE or host association"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-21 00:46:00 +08:00
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
void CheckHelper::CheckPointer(const Symbol &symbol) { // C852
|
2020-01-14 08:39:00 +08:00
|
|
|
CheckConflicting(symbol, Attr::POINTER, Attr::TARGET);
|
2020-05-13 00:53:58 +08:00
|
|
|
CheckConflicting(symbol, Attr::POINTER, Attr::ALLOCATABLE); // C751
|
2020-01-14 08:39:00 +08:00
|
|
|
CheckConflicting(symbol, Attr::POINTER, Attr::INTRINSIC);
|
2020-10-17 02:48:55 +08:00
|
|
|
// Prohibit constant pointers. The standard does not explicitly prohibit
|
|
|
|
// them, but the PARAMETER attribute requires a entity-decl to have an
|
|
|
|
// initialization that is a constant-expr, and the only form of
|
|
|
|
// initialization that allows a constant-expr is the one that's not a "=>"
|
|
|
|
// pointer initialization. See C811, C807, and section 8.5.13.
|
|
|
|
CheckConflicting(symbol, Attr::POINTER, Attr::PARAMETER);
|
2020-01-14 08:39:00 +08:00
|
|
|
if (symbol.Corank() > 0) {
|
|
|
|
messages_.Say(
|
|
|
|
"'%s' may not have the POINTER attribute because it is a coarray"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 02:24:18 +08:00
|
|
|
// C760 constraints on the passed-object dummy argument
|
2020-05-13 00:53:58 +08:00
|
|
|
// C757 constraints on procedure pointer components
|
2019-12-06 02:24:18 +08:00
|
|
|
void CheckHelper::CheckPassArg(
|
|
|
|
const Symbol &proc, const Symbol *interface, const WithPassArg &details) {
|
|
|
|
if (proc.attrs().test(Attr::NOPASS)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto &name{proc.name()};
|
|
|
|
if (!interface) {
|
|
|
|
messages_.Say(name,
|
|
|
|
"Procedure component '%s' must have NOPASS attribute or explicit interface"_err_en_US,
|
|
|
|
name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto *subprogram{interface->detailsIf<SubprogramDetails>()};
|
|
|
|
if (!subprogram) {
|
|
|
|
messages_.Say(name,
|
|
|
|
"Procedure component '%s' has invalid interface '%s'"_err_en_US, name,
|
|
|
|
interface->name());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::optional<SourceName> passName{details.passName()};
|
|
|
|
const auto &dummyArgs{subprogram->dummyArgs()};
|
|
|
|
if (!passName) {
|
|
|
|
if (dummyArgs.empty()) {
|
|
|
|
messages_.Say(name,
|
|
|
|
proc.has<ProcEntityDetails>()
|
|
|
|
? "Procedure component '%s' with no dummy arguments"
|
|
|
|
" must have NOPASS attribute"_err_en_US
|
|
|
|
: "Procedure binding '%s' with no dummy arguments"
|
|
|
|
" must have NOPASS attribute"_err_en_US,
|
|
|
|
name);
|
2021-03-11 00:09:57 +08:00
|
|
|
context_.SetError(*interface);
|
2019-12-06 02:24:18 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-03-12 00:23:08 +08:00
|
|
|
Symbol *argSym{dummyArgs[0]};
|
|
|
|
if (!argSym) {
|
|
|
|
messages_.Say(interface->name(),
|
|
|
|
"Cannot use an alternate return as the passed-object dummy "
|
|
|
|
"argument"_err_en_US);
|
|
|
|
return;
|
|
|
|
}
|
2019-12-06 02:24:18 +08:00
|
|
|
passName = dummyArgs[0]->name();
|
|
|
|
}
|
|
|
|
std::optional<int> passArgIndex{};
|
|
|
|
for (std::size_t i{0}; i < dummyArgs.size(); ++i) {
|
|
|
|
if (dummyArgs[i] && dummyArgs[i]->name() == *passName) {
|
|
|
|
passArgIndex = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 00:53:58 +08:00
|
|
|
if (!passArgIndex) { // C758
|
2019-12-06 02:24:18 +08:00
|
|
|
messages_.Say(*passName,
|
|
|
|
"'%s' is not a dummy argument of procedure interface '%s'"_err_en_US,
|
|
|
|
*passName, interface->name());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const Symbol &passArg{*dummyArgs[*passArgIndex]};
|
|
|
|
std::optional<parser::MessageFixedText> msg;
|
|
|
|
if (!passArg.has<ObjectEntityDetails>()) {
|
|
|
|
msg = "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" must be a data object"_err_en_US;
|
|
|
|
} else if (passArg.attrs().test(Attr::POINTER)) {
|
|
|
|
msg = "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" may not have the POINTER attribute"_err_en_US;
|
|
|
|
} else if (passArg.attrs().test(Attr::ALLOCATABLE)) {
|
|
|
|
msg = "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" may not have the ALLOCATABLE attribute"_err_en_US;
|
|
|
|
} else if (passArg.attrs().test(Attr::VALUE)) {
|
|
|
|
msg = "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" may not have the VALUE attribute"_err_en_US;
|
|
|
|
} else if (passArg.Rank() > 0) {
|
|
|
|
msg = "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" must be scalar"_err_en_US;
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
messages_.Say(name, std::move(*msg), passName.value(), name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const DeclTypeSpec *type{passArg.GetType()};
|
|
|
|
if (!type) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return; // an error already occurred
|
2019-12-06 02:24:18 +08:00
|
|
|
}
|
|
|
|
const Symbol &typeSymbol{*proc.owner().GetSymbol()};
|
|
|
|
const DerivedTypeSpec *derived{type->AsDerived()};
|
|
|
|
if (!derived || derived->typeSymbol() != typeSymbol) {
|
|
|
|
messages_.Say(name,
|
|
|
|
"Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" must be of type '%s' but is '%s'"_err_en_US,
|
|
|
|
passName.value(), name, typeSymbol.name(), type->AsFortran());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (IsExtensibleType(derived) != type->IsPolymorphic()) {
|
|
|
|
messages_.Say(name,
|
|
|
|
type->IsPolymorphic()
|
|
|
|
? "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" may not be polymorphic because '%s' is not extensible"_err_en_US
|
|
|
|
: "Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" must be polymorphic because '%s' is extensible"_err_en_US,
|
|
|
|
passName.value(), name, typeSymbol.name());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (const auto &[paramName, paramValue] : derived->parameters()) {
|
|
|
|
if (paramValue.isLen() && !paramValue.isAssumed()) {
|
|
|
|
messages_.Say(name,
|
|
|
|
"Passed-object dummy argument '%s' of procedure '%s'"
|
|
|
|
" has non-assumed length parameter '%s'"_err_en_US,
|
|
|
|
passName.value(), name, paramName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-23 06:05:54 +08:00
|
|
|
void CheckHelper::CheckProcBinding(
|
|
|
|
const Symbol &symbol, const ProcBindingDetails &binding) {
|
2019-11-16 06:26:10 +08:00
|
|
|
const Scope &dtScope{symbol.owner()};
|
|
|
|
CHECK(dtScope.kind() == Scope::Kind::DerivedType);
|
2021-06-03 08:28:50 +08:00
|
|
|
if (symbol.attrs().test(Attr::DEFERRED)) {
|
|
|
|
if (const Symbol * dtSymbol{dtScope.symbol()}) {
|
2020-03-31 08:42:50 +08:00
|
|
|
if (!dtSymbol->attrs().test(Attr::ABSTRACT)) { // C733
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*dtSymbol,
|
2019-11-16 06:26:10 +08:00
|
|
|
"Procedure bound to non-ABSTRACT derived type '%s' may not be DEFERRED"_err_en_US,
|
|
|
|
dtSymbol->name());
|
|
|
|
}
|
|
|
|
}
|
2021-06-03 08:28:50 +08:00
|
|
|
if (symbol.attrs().test(Attr::NON_OVERRIDABLE)) {
|
|
|
|
messages_.Say(
|
|
|
|
"Type-bound procedure '%s' may not be both DEFERRED and NON_OVERRIDABLE"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (binding.symbol().attrs().test(Attr::INTRINSIC) &&
|
|
|
|
!context_.intrinsics().IsSpecificIntrinsicFunction(
|
|
|
|
binding.symbol().name().ToString())) {
|
|
|
|
messages_.Say(
|
|
|
|
"Intrinsic procedure '%s' is not a specific intrinsic permitted for use in the definition of binding '%s'"_err_en_US,
|
|
|
|
binding.symbol().name(), symbol.name());
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
|
|
|
if (const Symbol * overridden{FindOverriddenBinding(symbol)}) {
|
|
|
|
if (overridden->attrs().test(Attr::NON_OVERRIDABLE)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-11-16 06:26:10 +08:00
|
|
|
"Override of NON_OVERRIDABLE '%s' is not permitted"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
if (const auto *overriddenBinding{
|
|
|
|
overridden->detailsIf<ProcBindingDetails>()}) {
|
2019-12-24 09:12:53 +08:00
|
|
|
if (!IsPureProcedure(symbol) && IsPureProcedure(*overridden)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-12-24 09:12:53 +08:00
|
|
|
"An overridden pure type-bound procedure binding must also be pure"_err_en_US);
|
2019-11-16 06:26:10 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!binding.symbol().attrs().test(Attr::ELEMENTAL) &&
|
|
|
|
overriddenBinding->symbol().attrs().test(Attr::ELEMENTAL)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-11-16 06:26:10 +08:00
|
|
|
"A type-bound procedure and its override must both, or neither, be ELEMENTAL"_err_en_US);
|
|
|
|
return;
|
|
|
|
}
|
2019-12-06 02:24:18 +08:00
|
|
|
bool isNopass{symbol.attrs().test(Attr::NOPASS)};
|
|
|
|
if (isNopass != overridden->attrs().test(Attr::NOPASS)) {
|
|
|
|
SayWithDeclaration(*overridden,
|
|
|
|
isNopass
|
|
|
|
? "A NOPASS type-bound procedure may not override a passed-argument procedure"_err_en_US
|
|
|
|
: "A passed-argument type-bound procedure may not override a NOPASS procedure"_err_en_US);
|
|
|
|
} else {
|
2020-09-10 22:22:52 +08:00
|
|
|
const auto *bindingChars{Characterize(binding.symbol())};
|
|
|
|
const auto *overriddenChars{Characterize(overriddenBinding->symbol())};
|
2019-12-06 02:24:18 +08:00
|
|
|
if (bindingChars && overriddenChars) {
|
|
|
|
if (isNopass) {
|
|
|
|
if (!bindingChars->CanOverride(*overriddenChars, std::nullopt)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-12-06 02:24:18 +08:00
|
|
|
"A type-bound procedure and its override must have compatible interfaces"_err_en_US);
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
2021-03-11 00:09:57 +08:00
|
|
|
} else if (!context_.HasError(binding.symbol())) {
|
2019-12-06 02:24:18 +08:00
|
|
|
int passIndex{bindingChars->FindPassIndex(binding.passName())};
|
|
|
|
int overriddenPassIndex{
|
|
|
|
overriddenChars->FindPassIndex(overriddenBinding->passName())};
|
|
|
|
if (passIndex != overriddenPassIndex) {
|
|
|
|
SayWithDeclaration(*overridden,
|
|
|
|
"A type-bound procedure and its override must use the same PASS argument"_err_en_US);
|
|
|
|
} else if (!bindingChars->CanOverride(
|
|
|
|
*overriddenChars, passIndex)) {
|
|
|
|
SayWithDeclaration(*overridden,
|
|
|
|
"A type-bound procedure and its override must have compatible interfaces apart from their passed argument"_err_en_US);
|
|
|
|
}
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (symbol.attrs().test(Attr::PRIVATE) &&
|
|
|
|
overridden->attrs().test(Attr::PUBLIC)) {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-11-16 06:26:10 +08:00
|
|
|
"A PRIVATE procedure may not override a PUBLIC procedure"_err_en_US);
|
|
|
|
}
|
|
|
|
} else {
|
2019-11-23 05:20:58 +08:00
|
|
|
SayWithDeclaration(*overridden,
|
2019-11-16 06:26:10 +08:00
|
|
|
"A type-bound procedure binding may not have the same name as a parent component"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
2019-12-06 02:24:18 +08:00
|
|
|
CheckPassArg(symbol, &binding.symbol(), binding);
|
2019-11-16 06:26:10 +08:00
|
|
|
}
|
|
|
|
|
2019-10-30 03:46:25 +08:00
|
|
|
void CheckHelper::Check(const Scope &scope) {
|
2019-10-25 07:08:06 +08:00
|
|
|
scope_ = &scope;
|
2020-12-15 01:05:51 +08:00
|
|
|
common::Restorer<const Symbol *> restorer{innermostSymbol_, innermostSymbol_};
|
2019-11-16 06:26:10 +08:00
|
|
|
if (const Symbol * symbol{scope.symbol()}) {
|
|
|
|
innermostSymbol_ = symbol;
|
2019-11-13 07:43:09 +08:00
|
|
|
}
|
[flang] Improve initializer semantics, esp. for component default values
This patch plugs many holes in static initializer semantics, improves error
messages for default initial values and other component properties in
parameterized derived type instantiations, and cleans up several small
issues noticed during development. We now do proper scalar expansion,
folding, and type, rank, and shape conformance checking for component
default initializers in derived types and PDT instantiations.
The initial values of named constants are now guaranteed to have been folded
when installed in the symbol table, and are no longer folded or
scalar-expanded at each use in expression folding. Semantics documentation
was extended with information about the various kinds of initializations
in Fortran and when each of them are processed in the compiler.
Some necessary concomitant changes have bulked this patch out a bit:
* contextual messages attachments, which are now produced for parameterized
derived type instantiations so that the user can figure out which
instance caused a problem with a component, have been added as part
of ContextualMessages, and their implementation was debugged
* several APIs in evaluate::characteristics was changed so that a FoldingContext
is passed as an argument rather than just its intrinsic procedure table;
this affected client call sites in many files
* new tools in Evaluate/check-expression.cpp to determine when an Expr
actually is a single constant value and to validate a non-pointer
variable initializer or object component default value
* shape conformance checking has additional arguments that control
whether scalar expansion is allowed
* several now-unused functions and data members noticed and removed
* several crashes and bogus errors exposed by testing this new code
were fixed
* a -fdebug-stack-trace option to enable LLVM's stack tracing on
a crash, which might be useful in the future
TL;DR: Initialization processing does more and takes place at the right
times for all of the various kinds of things that can be initialized.
Differential Review: https://reviews.llvm.org/D92783
2020-12-08 04:08:58 +08:00
|
|
|
if (scope.IsParameterizedDerivedTypeInstantiation()) {
|
|
|
|
auto restorer{common::ScopedSet(scopeIsUninstantiatedPDT_, false)};
|
|
|
|
auto restorer2{context_.foldingContext().messages().SetContext(
|
|
|
|
scope.instantiationContext().get())};
|
|
|
|
for (const auto &pair : scope) {
|
|
|
|
CheckPointerInitialization(*pair.second);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
auto restorer{common::ScopedSet(
|
|
|
|
scopeIsUninstantiatedPDT_, scope.IsParameterizedDerivedType())};
|
|
|
|
for (const auto &set : scope.equivalenceSets()) {
|
|
|
|
CheckEquivalenceSet(set);
|
|
|
|
}
|
|
|
|
for (const auto &pair : scope) {
|
|
|
|
Check(*pair.second);
|
|
|
|
}
|
|
|
|
for (const Scope &child : scope.children()) {
|
|
|
|
Check(child);
|
|
|
|
}
|
|
|
|
if (scope.kind() == Scope::Kind::BlockData) {
|
|
|
|
CheckBlockData(scope);
|
|
|
|
}
|
|
|
|
CheckGenericOps(scope);
|
2020-01-10 09:12:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 07:59:29 +08:00
|
|
|
void CheckHelper::CheckEquivalenceSet(const EquivalenceSet &set) {
|
|
|
|
auto iter{
|
|
|
|
std::find_if(set.begin(), set.end(), [](const EquivalenceObject &object) {
|
|
|
|
return FindCommonBlockContaining(object.symbol) != nullptr;
|
|
|
|
})};
|
|
|
|
if (iter != set.end()) {
|
|
|
|
const Symbol &commonBlock{DEREF(FindCommonBlockContaining(iter->symbol))};
|
|
|
|
for (auto &object : set) {
|
|
|
|
if (&object != &*iter) {
|
|
|
|
if (auto *details{object.symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (details->commonBlock()) {
|
2020-03-29 12:00:16 +08:00
|
|
|
if (details->commonBlock() != &commonBlock) { // 8.10.3 paragraph 1
|
2020-01-15 07:59:29 +08:00
|
|
|
if (auto *msg{messages_.Say(object.symbol.name(),
|
|
|
|
"Two objects in the same EQUIVALENCE set may not be members of distinct COMMON blocks"_err_en_US)}) {
|
|
|
|
msg->Attach(iter->symbol.name(),
|
|
|
|
"Other object in EQUIVALENCE set"_en_US)
|
|
|
|
.Attach(details->commonBlock()->name(),
|
|
|
|
"COMMON block containing '%s'"_en_US,
|
|
|
|
object.symbol.name())
|
|
|
|
.Attach(commonBlock.name(),
|
|
|
|
"COMMON block containing '%s'"_en_US,
|
|
|
|
iter->symbol.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Mark all symbols in the equivalence set with the same COMMON
|
2020-01-15 08:16:32 +08:00
|
|
|
// block to prevent spurious error messages about initialization
|
|
|
|
// in BLOCK DATA outside COMMON
|
2020-01-15 07:59:29 +08:00
|
|
|
details->set_commonBlock(commonBlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-28 10:18:45 +08:00
|
|
|
// TODO: Move C8106 (&al.) checks here from resolve-names-utils.cpp
|
2020-01-10 09:12:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckBlockData(const Scope &scope) {
|
|
|
|
// BLOCK DATA subprograms should contain only named common blocks.
|
2020-01-14 06:30:31 +08:00
|
|
|
// C1415 presents a list of statements that shouldn't appear in
|
|
|
|
// BLOCK DATA, but so long as the subprogram contains no executable
|
|
|
|
// code and allocates no storage outside named COMMON, we're happy
|
|
|
|
// (e.g., an ENUM is strictly not allowed).
|
2020-01-10 09:12:46 +08:00
|
|
|
for (const auto &pair : scope) {
|
|
|
|
const Symbol &symbol{*pair.second};
|
|
|
|
if (!(symbol.has<CommonBlockDetails>() || symbol.has<UseDetails>() ||
|
|
|
|
symbol.has<UseErrorDetails>() || symbol.has<DerivedTypeDetails>() ||
|
|
|
|
symbol.has<SubprogramDetails>() ||
|
|
|
|
symbol.has<ObjectEntityDetails>() ||
|
|
|
|
(symbol.has<ProcEntityDetails>() &&
|
|
|
|
!symbol.attrs().test(Attr::POINTER)))) {
|
|
|
|
messages_.Say(symbol.name(),
|
|
|
|
"'%s' may not appear in a BLOCK DATA subprogram"_err_en_US,
|
|
|
|
symbol.name());
|
|
|
|
}
|
|
|
|
}
|
2019-10-25 07:08:06 +08:00
|
|
|
}
|
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
// Check distinguishability of generic assignment and operators.
|
|
|
|
// For these, generics and generic bindings must be considered together.
|
|
|
|
void CheckHelper::CheckGenericOps(const Scope &scope) {
|
|
|
|
DistinguishabilityHelper helper{context_};
|
|
|
|
auto addSpecifics{[&](const Symbol &generic) {
|
|
|
|
const auto *details{generic.GetUltimate().detailsIf<GenericDetails>()};
|
|
|
|
if (!details) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GenericKind kind{details->kind()};
|
|
|
|
if (!kind.IsAssignment() && !kind.IsOperator()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const SymbolVector &specifics{details->specificProcs()};
|
|
|
|
const std::vector<SourceName> &bindingNames{details->bindingNames()};
|
|
|
|
for (std::size_t i{0}; i < specifics.size(); ++i) {
|
|
|
|
const Symbol &specific{*specifics[i]};
|
|
|
|
if (const Procedure * proc{Characterize(specific)}) {
|
|
|
|
auto restorer{messages_.SetLocation(bindingNames[i])};
|
|
|
|
if (kind.IsAssignment()) {
|
|
|
|
if (!CheckDefinedAssignment(specific, *proc)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!CheckDefinedOperator(generic.name(), kind, specific, *proc)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
helper.Add(generic, kind, specific, *proc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}};
|
|
|
|
for (const auto &pair : scope) {
|
|
|
|
const Symbol &symbol{*pair.second};
|
|
|
|
addSpecifics(symbol);
|
|
|
|
const Symbol &ultimate{symbol.GetUltimate()};
|
|
|
|
if (ultimate.has<DerivedTypeDetails>()) {
|
|
|
|
if (const Scope * typeScope{ultimate.scope()}) {
|
|
|
|
for (const auto &pair2 : *typeScope) {
|
|
|
|
addSpecifics(*pair2.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-03 07:13:49 +08:00
|
|
|
helper.Check(scope);
|
2020-09-10 22:22:52 +08:00
|
|
|
}
|
|
|
|
|
2021-04-08 04:23:45 +08:00
|
|
|
static const std::string *DefinesBindCName(const Symbol &symbol) {
|
|
|
|
const auto *subp{symbol.detailsIf<SubprogramDetails>()};
|
|
|
|
if ((subp && !subp->isInterface()) || symbol.has<ObjectEntityDetails>()) {
|
|
|
|
// Symbol defines data or entry point
|
|
|
|
return symbol.GetBindName();
|
|
|
|
} else {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that BIND(C) names are distinct
|
|
|
|
void CheckHelper::CheckBindCName(const Symbol &symbol) {
|
|
|
|
if (const std::string * name{DefinesBindCName(symbol)}) {
|
|
|
|
auto pair{bindC_.emplace(*name, symbol)};
|
|
|
|
if (!pair.second) {
|
|
|
|
const Symbol &other{*pair.first->second};
|
|
|
|
if (DefinesBindCName(other) && !context_.HasError(other)) {
|
|
|
|
if (auto *msg{messages_.Say(
|
|
|
|
"Two symbols have the same BIND(C) name '%s'"_err_en_US,
|
|
|
|
*name)}) {
|
|
|
|
msg->Attach(other.name(), "Conflicting symbol"_en_US);
|
|
|
|
}
|
|
|
|
context_.SetError(symbol);
|
|
|
|
context_.SetError(other);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-25 04:12:19 +08:00
|
|
|
bool CheckHelper::CheckDioDummyIsData(
|
|
|
|
const Symbol &subp, const Symbol *arg, std::size_t position) {
|
|
|
|
if (arg && arg->detailsIf<ObjectEntityDetails>()) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
if (arg) {
|
|
|
|
messages_.Say(arg->name(),
|
|
|
|
"Dummy argument '%s' must be a data object"_err_en_US, arg->name());
|
|
|
|
} else {
|
|
|
|
messages_.Say(subp.name(),
|
|
|
|
"Dummy argument %d of '%s' must be a data object"_err_en_US, position,
|
|
|
|
subp.name());
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-03 05:55:41 +08:00
|
|
|
void CheckHelper::CheckAlreadySeenDefinedIo(const DerivedTypeSpec *derivedType,
|
|
|
|
GenericKind::DefinedIo ioKind, const Symbol &proc) {
|
|
|
|
for (TypeWithDefinedIo definedIoType : seenDefinedIoTypes_) {
|
|
|
|
if (*derivedType == *definedIoType.type && ioKind == definedIoType.ioKind &&
|
|
|
|
proc != definedIoType.proc) {
|
|
|
|
SayWithDeclaration(proc, definedIoType.proc.name(),
|
|
|
|
"Derived type '%s' already has defined input/output procedure"
|
|
|
|
" '%s'"_err_en_US,
|
|
|
|
derivedType->name(),
|
|
|
|
parser::ToUpperCaseLetters(GenericKind::EnumToString(ioKind)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
seenDefinedIoTypes_.emplace_back(
|
|
|
|
TypeWithDefinedIo{derivedType, ioKind, proc});
|
|
|
|
}
|
|
|
|
|
2021-05-25 04:12:19 +08:00
|
|
|
void CheckHelper::CheckDioDummyIsDerived(
|
2021-06-03 05:55:41 +08:00
|
|
|
const Symbol &subp, const Symbol &arg, GenericKind::DefinedIo ioKind) {
|
|
|
|
if (const DeclTypeSpec * type{arg.GetType()}) {
|
2021-06-26 01:40:08 +08:00
|
|
|
if (const DerivedTypeSpec * derivedType{type->AsDerived()}) {
|
2021-06-03 05:55:41 +08:00
|
|
|
CheckAlreadySeenDefinedIo(derivedType, ioKind, subp);
|
2021-06-26 01:40:08 +08:00
|
|
|
bool isPolymorphic{type->IsPolymorphic()};
|
|
|
|
if (isPolymorphic != IsExtensibleType(derivedType)) {
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure must be %s when the derived type is %s"_err_en_US,
|
|
|
|
arg.name(), isPolymorphic ? "TYPE()" : "CLASS()",
|
|
|
|
isPolymorphic ? "not extensible" : "extensible");
|
|
|
|
}
|
2021-06-03 05:55:41 +08:00
|
|
|
} else {
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure must have a"
|
|
|
|
" derived type"_err_en_US,
|
|
|
|
arg.name());
|
|
|
|
}
|
2021-05-25 04:12:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioDummyIsDefaultInteger(
|
|
|
|
const Symbol &subp, const Symbol &arg) {
|
|
|
|
if (const DeclTypeSpec * type{arg.GetType()};
|
|
|
|
type && type->IsNumeric(TypeCategory::Integer)) {
|
|
|
|
if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
|
|
|
|
kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure"
|
|
|
|
" must be an INTEGER of default KIND"_err_en_US,
|
|
|
|
arg.name());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioDummyIsScalar(const Symbol &subp, const Symbol &arg) {
|
|
|
|
if (arg.Rank() > 0 || arg.Corank() > 0) {
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure"
|
|
|
|
" must be a scalar"_err_en_US,
|
|
|
|
arg.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioDtvArg(
|
|
|
|
const Symbol &subp, const Symbol *arg, GenericKind::DefinedIo ioKind) {
|
|
|
|
// Dtv argument looks like: dtv-type-spec, INTENT(INOUT) :: dtv
|
|
|
|
if (CheckDioDummyIsData(subp, arg, 0)) {
|
2021-06-03 05:55:41 +08:00
|
|
|
CheckDioDummyIsDerived(subp, *arg, ioKind);
|
2021-05-25 04:12:19 +08:00
|
|
|
CheckDioDummyAttrs(subp, *arg,
|
|
|
|
ioKind == GenericKind::DefinedIo::ReadFormatted ||
|
|
|
|
ioKind == GenericKind::DefinedIo::ReadUnformatted
|
|
|
|
? Attr::INTENT_INOUT
|
|
|
|
: Attr::INTENT_IN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDefaultIntegerArg(
|
|
|
|
const Symbol &subp, const Symbol *arg, Attr intent) {
|
|
|
|
// Argument looks like: INTEGER, INTENT(intent) :: arg
|
|
|
|
if (CheckDioDummyIsData(subp, arg, 1)) {
|
|
|
|
CheckDioDummyIsDefaultInteger(subp, *arg);
|
|
|
|
CheckDioDummyIsScalar(subp, *arg);
|
|
|
|
CheckDioDummyAttrs(subp, *arg, intent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioAssumedLenCharacterArg(const Symbol &subp,
|
|
|
|
const Symbol *arg, std::size_t argPosition, Attr intent) {
|
|
|
|
// Argument looks like: CHARACTER (LEN=*), INTENT(intent) :: (iotype OR iomsg)
|
|
|
|
if (CheckDioDummyIsData(subp, arg, argPosition)) {
|
|
|
|
CheckDioDummyAttrs(subp, *arg, intent);
|
|
|
|
if (!IsAssumedLengthCharacter(*arg)) {
|
|
|
|
messages_.Say(arg->name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure"
|
|
|
|
" must be assumed-length CHARACTER"_err_en_US,
|
|
|
|
arg->name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioVlistArg(
|
|
|
|
const Symbol &subp, const Symbol *arg, std::size_t argPosition) {
|
|
|
|
// Vlist argument looks like: INTEGER, INTENT(IN) :: v_list(:)
|
|
|
|
if (CheckDioDummyIsData(subp, arg, argPosition)) {
|
|
|
|
CheckDioDummyIsDefaultInteger(subp, *arg);
|
|
|
|
CheckDioDummyAttrs(subp, *arg, Attr::INTENT_IN);
|
|
|
|
if (const auto *objectDetails{arg->detailsIf<ObjectEntityDetails>()}) {
|
|
|
|
if (objectDetails->shape().IsDeferredShape()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
messages_.Say(arg->name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure must be"
|
|
|
|
" deferred shape"_err_en_US,
|
|
|
|
arg->name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioArgCount(
|
|
|
|
const Symbol &subp, GenericKind::DefinedIo ioKind, std::size_t argCount) {
|
|
|
|
const std::size_t requiredArgCount{
|
|
|
|
(std::size_t)(ioKind == GenericKind::DefinedIo::ReadFormatted ||
|
|
|
|
ioKind == GenericKind::DefinedIo::WriteFormatted
|
|
|
|
? 6
|
|
|
|
: 4)};
|
|
|
|
if (argCount != requiredArgCount) {
|
|
|
|
SayWithDeclaration(subp,
|
|
|
|
"Defined input/output procedure '%s' must have"
|
|
|
|
" %d dummy arguments rather than %d"_err_en_US,
|
|
|
|
subp.name(), requiredArgCount, argCount);
|
|
|
|
context_.SetError(subp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckHelper::CheckDioDummyAttrs(
|
|
|
|
const Symbol &subp, const Symbol &arg, Attr goodIntent) {
|
|
|
|
// Defined I/O procedures can't have attributes other than INTENT
|
|
|
|
Attrs attrs{arg.attrs()};
|
|
|
|
if (!attrs.test(goodIntent)) {
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure"
|
|
|
|
" must have intent '%s'"_err_en_US,
|
|
|
|
arg.name(), AttrToString(goodIntent));
|
|
|
|
}
|
|
|
|
attrs = attrs - Attr::INTENT_IN - Attr::INTENT_OUT - Attr::INTENT_INOUT;
|
|
|
|
if (!attrs.empty()) {
|
|
|
|
messages_.Say(arg.name(),
|
|
|
|
"Dummy argument '%s' of a defined input/output procedure may not have"
|
|
|
|
" any attributes"_err_en_US,
|
|
|
|
arg.name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enforce semantics for defined input/output procedures (12.6.4.8.2) and C777
|
|
|
|
void CheckHelper::CheckDefinedIoProc(const Symbol &symbol,
|
|
|
|
const GenericDetails &details, GenericKind::DefinedIo ioKind) {
|
|
|
|
for (auto ref : details.specificProcs()) {
|
|
|
|
const auto *binding{ref->detailsIf<ProcBindingDetails>()};
|
|
|
|
const Symbol &specific{*(binding ? &binding->symbol() : &*ref)};
|
|
|
|
if (ref->attrs().test(Attr::NOPASS)) { // C774
|
|
|
|
messages_.Say("Defined input/output procedure '%s' may not have NOPASS "
|
|
|
|
"attribute"_err_en_US,
|
|
|
|
ref->name());
|
|
|
|
context_.SetError(*ref);
|
|
|
|
}
|
|
|
|
if (const auto *subpDetails{specific.detailsIf<SubprogramDetails>()}) {
|
|
|
|
const std::vector<Symbol *> &dummyArgs{subpDetails->dummyArgs()};
|
|
|
|
CheckDioArgCount(specific, ioKind, dummyArgs.size());
|
|
|
|
int argCount{0};
|
|
|
|
for (auto *arg : dummyArgs) {
|
|
|
|
switch (argCount++) {
|
|
|
|
case 0:
|
|
|
|
// dtv-type-spec, INTENT(INOUT) :: dtv
|
|
|
|
CheckDioDtvArg(specific, arg, ioKind);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// INTEGER, INTENT(IN) :: unit
|
|
|
|
CheckDefaultIntegerArg(specific, arg, Attr::INTENT_IN);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
|
|
|
|
ioKind == GenericKind::DefinedIo::WriteFormatted) {
|
|
|
|
// CHARACTER (LEN=*), INTENT(IN) :: iotype
|
|
|
|
CheckDioAssumedLenCharacterArg(
|
|
|
|
specific, arg, argCount, Attr::INTENT_IN);
|
|
|
|
} else {
|
|
|
|
// INTEGER, INTENT(OUT) :: iostat
|
|
|
|
CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (ioKind == GenericKind::DefinedIo::ReadFormatted ||
|
|
|
|
ioKind == GenericKind::DefinedIo::WriteFormatted) {
|
|
|
|
// INTEGER, INTENT(IN) :: v_list(:)
|
|
|
|
CheckDioVlistArg(specific, arg, argCount);
|
|
|
|
} else {
|
|
|
|
// CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
|
|
|
|
CheckDioAssumedLenCharacterArg(
|
|
|
|
specific, arg, argCount, Attr::INTENT_INOUT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
// INTEGER, INTENT(OUT) :: iostat
|
|
|
|
CheckDefaultIntegerArg(specific, arg, Attr::INTENT_OUT);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
// CHARACTER (LEN=*), INTENT(INOUT) :: iomsg
|
|
|
|
CheckDioAssumedLenCharacterArg(
|
|
|
|
specific, arg, argCount, Attr::INTENT_INOUT);
|
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-18 05:48:36 +08:00
|
|
|
void SubprogramMatchHelper::Check(
|
|
|
|
const Symbol &symbol1, const Symbol &symbol2) {
|
|
|
|
const auto details1{symbol1.get<SubprogramDetails>()};
|
|
|
|
const auto details2{symbol2.get<SubprogramDetails>()};
|
|
|
|
if (details1.isFunction() != details2.isFunction()) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
details1.isFunction()
|
|
|
|
? "Module function '%s' was declared as a subroutine in the"
|
|
|
|
" corresponding interface body"_err_en_US
|
|
|
|
: "Module subroutine '%s' was declared as a function in the"
|
|
|
|
" corresponding interface body"_err_en_US);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto &args1{details1.dummyArgs()};
|
|
|
|
const auto &args2{details2.dummyArgs()};
|
|
|
|
int nargs1{static_cast<int>(args1.size())};
|
|
|
|
int nargs2{static_cast<int>(args2.size())};
|
|
|
|
if (nargs1 != nargs2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Module subprogram '%s' has %d args but the corresponding interface"
|
|
|
|
" body has %d"_err_en_US,
|
|
|
|
nargs1, nargs2);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool nonRecursive1{symbol1.attrs().test(Attr::NON_RECURSIVE)};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (nonRecursive1 != symbol2.attrs().test(Attr::NON_RECURSIVE)) { // C1551
|
2020-03-18 05:48:36 +08:00
|
|
|
Say(symbol1, symbol2,
|
|
|
|
nonRecursive1
|
|
|
|
? "Module subprogram '%s' has NON_RECURSIVE prefix but"
|
|
|
|
" the corresponding interface body does not"_err_en_US
|
|
|
|
: "Module subprogram '%s' does not have NON_RECURSIVE prefix but "
|
|
|
|
"the corresponding interface body does"_err_en_US);
|
|
|
|
}
|
2021-03-25 02:25:22 +08:00
|
|
|
const std::string *bindName1{details1.bindName()};
|
|
|
|
const std::string *bindName2{details2.bindName()};
|
|
|
|
if (!bindName1 && !bindName2) {
|
|
|
|
// OK - neither has a binding label
|
|
|
|
} else if (!bindName1) {
|
2020-03-18 05:48:36 +08:00
|
|
|
Say(symbol1, symbol2,
|
2021-03-25 02:25:22 +08:00
|
|
|
"Module subprogram '%s' does not have a binding label but the"
|
|
|
|
" corresponding interface body does"_err_en_US);
|
|
|
|
} else if (!bindName2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Module subprogram '%s' has a binding label but the"
|
|
|
|
" corresponding interface body does not"_err_en_US);
|
|
|
|
} else if (*bindName1 != *bindName2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Module subprogram '%s' has binding label '%s' but the corresponding"
|
|
|
|
" interface body has '%s'"_err_en_US,
|
|
|
|
*details1.bindName(), *details2.bindName());
|
2020-03-18 05:48:36 +08:00
|
|
|
}
|
2020-09-10 22:22:52 +08:00
|
|
|
const Procedure *proc1{checkHelper.Characterize(symbol1)};
|
|
|
|
const Procedure *proc2{checkHelper.Characterize(symbol2)};
|
2020-03-18 05:48:36 +08:00
|
|
|
if (!proc1 || !proc2) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (proc1->functionResult && proc2->functionResult &&
|
|
|
|
*proc1->functionResult != *proc2->functionResult) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Return type of function '%s' does not match return type of"
|
|
|
|
" the corresponding interface body"_err_en_US);
|
|
|
|
}
|
|
|
|
for (int i{0}; i < nargs1; ++i) {
|
|
|
|
const Symbol *arg1{args1[i]};
|
|
|
|
const Symbol *arg2{args2[i]};
|
|
|
|
if (arg1 && !arg2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument %2$d of '%1$s' is not an alternate return indicator"
|
|
|
|
" but the corresponding argument in the interface body is"_err_en_US,
|
|
|
|
i + 1);
|
|
|
|
} else if (!arg1 && arg2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument %2$d of '%1$s' is an alternate return indicator but"
|
|
|
|
" the corresponding argument in the interface body is not"_err_en_US,
|
|
|
|
i + 1);
|
|
|
|
} else if (arg1 && arg2) {
|
|
|
|
SourceName name1{arg1->name()};
|
|
|
|
SourceName name2{arg2->name()};
|
|
|
|
if (name1 != name2) {
|
|
|
|
Say(*arg1, *arg2,
|
|
|
|
"Dummy argument name '%s' does not match corresponding name '%s'"
|
|
|
|
" in interface body"_err_en_US,
|
|
|
|
name2);
|
|
|
|
} else {
|
|
|
|
CheckDummyArg(
|
|
|
|
*arg1, *arg2, proc1->dummyArguments[i], proc2->dummyArguments[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SubprogramMatchHelper::CheckDummyArg(const Symbol &symbol1,
|
|
|
|
const Symbol &symbol2, const DummyArgument &arg1,
|
|
|
|
const DummyArgument &arg2) {
|
2020-03-29 12:00:16 +08:00
|
|
|
std::visit(common::visitors{
|
|
|
|
[&](const DummyDataObject &obj1, const DummyDataObject &obj2) {
|
|
|
|
CheckDummyDataObject(symbol1, symbol2, obj1, obj2);
|
|
|
|
},
|
|
|
|
[&](const DummyProcedure &proc1, const DummyProcedure &proc2) {
|
|
|
|
CheckDummyProcedure(symbol1, symbol2, proc1, proc2);
|
|
|
|
},
|
|
|
|
[&](const DummyDataObject &, const auto &) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument '%s' is a data object; the corresponding"
|
|
|
|
" argument in the interface body is not"_err_en_US);
|
|
|
|
},
|
|
|
|
[&](const DummyProcedure &, const auto &) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument '%s' is a procedure; the corresponding"
|
|
|
|
" argument in the interface body is not"_err_en_US);
|
|
|
|
},
|
2020-05-07 01:38:28 +08:00
|
|
|
[&](const auto &, const auto &) {
|
|
|
|
llvm_unreachable("Dummy arguments are not data objects or"
|
|
|
|
"procedures");
|
|
|
|
},
|
2020-03-29 12:00:16 +08:00
|
|
|
},
|
2020-03-18 05:48:36 +08:00
|
|
|
arg1.u, arg2.u);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SubprogramMatchHelper::CheckDummyDataObject(const Symbol &symbol1,
|
|
|
|
const Symbol &symbol2, const DummyDataObject &obj1,
|
|
|
|
const DummyDataObject &obj2) {
|
|
|
|
if (!CheckSameIntent(symbol1, symbol2, obj1.intent, obj2.intent)) {
|
|
|
|
} else if (!CheckSameAttrs(symbol1, symbol2, obj1.attrs, obj2.attrs)) {
|
|
|
|
} else if (obj1.type.type() != obj2.type.type()) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument '%s' has type %s; the corresponding argument in the"
|
|
|
|
" interface body has type %s"_err_en_US,
|
|
|
|
obj1.type.type().AsFortran(), obj2.type.type().AsFortran());
|
|
|
|
} else if (!ShapesAreCompatible(obj1, obj2)) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"The shape of dummy argument '%s' does not match the shape of the"
|
|
|
|
" corresponding argument in the interface body"_err_en_US);
|
|
|
|
}
|
|
|
|
// TODO: coshape
|
|
|
|
}
|
|
|
|
|
|
|
|
void SubprogramMatchHelper::CheckDummyProcedure(const Symbol &symbol1,
|
|
|
|
const Symbol &symbol2, const DummyProcedure &proc1,
|
|
|
|
const DummyProcedure &proc2) {
|
|
|
|
if (!CheckSameIntent(symbol1, symbol2, proc1.intent, proc2.intent)) {
|
|
|
|
} else if (!CheckSameAttrs(symbol1, symbol2, proc1.attrs, proc2.attrs)) {
|
|
|
|
} else if (proc1 != proc2) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy procedure '%s' does not match the corresponding argument in"
|
|
|
|
" the interface body"_err_en_US);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SubprogramMatchHelper::CheckSameIntent(const Symbol &symbol1,
|
|
|
|
const Symbol &symbol2, common::Intent intent1, common::Intent intent2) {
|
|
|
|
if (intent1 == intent2) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"The intent of dummy argument '%s' does not match the intent"
|
|
|
|
" of the corresponding argument in the interface body"_err_en_US);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report an error referring to first symbol with declaration of second symbol
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename... A>
|
2020-03-18 05:48:36 +08:00
|
|
|
void SubprogramMatchHelper::Say(const Symbol &symbol1, const Symbol &symbol2,
|
2020-08-07 07:56:14 +08:00
|
|
|
parser::MessageFixedText &&text, A &&...args) {
|
2020-09-10 22:22:52 +08:00
|
|
|
auto &message{context().Say(symbol1.name(), std::move(text), symbol1.name(),
|
2020-03-18 05:48:36 +08:00
|
|
|
std::forward<A>(args)...)};
|
|
|
|
evaluate::AttachDeclaration(message, symbol2);
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename ATTRS>
|
2020-03-18 05:48:36 +08:00
|
|
|
bool SubprogramMatchHelper::CheckSameAttrs(
|
|
|
|
const Symbol &symbol1, const Symbol &symbol2, ATTRS attrs1, ATTRS attrs2) {
|
|
|
|
if (attrs1 == attrs2) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
attrs1.IterateOverMembers([&](auto attr) {
|
|
|
|
if (!attrs2.test(attr)) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument '%s' has the %s attribute; the corresponding"
|
|
|
|
" argument in the interface body does not"_err_en_US,
|
|
|
|
AsFortran(attr));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
attrs2.IterateOverMembers([&](auto attr) {
|
|
|
|
if (!attrs1.test(attr)) {
|
|
|
|
Say(symbol1, symbol2,
|
|
|
|
"Dummy argument '%s' does not have the %s attribute; the"
|
|
|
|
" corresponding argument in the interface body does"_err_en_US,
|
|
|
|
AsFortran(attr));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SubprogramMatchHelper::ShapesAreCompatible(
|
|
|
|
const DummyDataObject &obj1, const DummyDataObject &obj2) {
|
2020-09-10 22:22:52 +08:00
|
|
|
return characteristics::ShapesAreCompatible(
|
2020-03-18 05:48:36 +08:00
|
|
|
FoldShape(obj1.type.shape()), FoldShape(obj2.type.shape()));
|
|
|
|
}
|
|
|
|
|
|
|
|
evaluate::Shape SubprogramMatchHelper::FoldShape(const evaluate::Shape &shape) {
|
|
|
|
evaluate::Shape result;
|
|
|
|
for (const auto &extent : shape) {
|
|
|
|
result.emplace_back(
|
2020-09-10 22:22:52 +08:00
|
|
|
evaluate::Fold(context().foldingContext(), common::Clone(extent)));
|
2020-03-18 05:48:36 +08:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-09-10 22:22:52 +08:00
|
|
|
void DistinguishabilityHelper::Add(const Symbol &generic, GenericKind kind,
|
|
|
|
const Symbol &specific, const Procedure &procedure) {
|
|
|
|
if (!context_.HasError(specific)) {
|
|
|
|
nameToInfo_[generic.name()].emplace_back(
|
|
|
|
ProcedureInfo{kind, specific, procedure});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-03 07:13:49 +08:00
|
|
|
void DistinguishabilityHelper::Check(const Scope &scope) {
|
2020-09-10 22:22:52 +08:00
|
|
|
for (const auto &[name, info] : nameToInfo_) {
|
|
|
|
auto count{info.size()};
|
|
|
|
for (std::size_t i1{0}; i1 < count - 1; ++i1) {
|
2021-06-23 02:29:14 +08:00
|
|
|
const auto &[kind, symbol, proc]{info[i1]};
|
2020-09-10 22:22:52 +08:00
|
|
|
for (std::size_t i2{i1 + 1}; i2 < count; ++i2) {
|
2021-06-23 02:29:14 +08:00
|
|
|
auto distinguishable{kind.IsName()
|
2020-09-10 22:22:52 +08:00
|
|
|
? evaluate::characteristics::Distinguishable
|
|
|
|
: evaluate::characteristics::DistinguishableOpOrAssign};
|
2021-10-19 01:44:39 +08:00
|
|
|
if (!distinguishable(
|
|
|
|
context_.languageFeatures(), proc, info[i2].procedure)) {
|
2021-06-23 02:29:14 +08:00
|
|
|
SayNotDistinguishable(GetTopLevelUnitContaining(scope), name, kind,
|
|
|
|
symbol, info[i2].symbol);
|
2020-09-10 22:22:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-03 07:13:49 +08:00
|
|
|
void DistinguishabilityHelper::SayNotDistinguishable(const Scope &scope,
|
|
|
|
const SourceName &name, GenericKind kind, const Symbol &proc1,
|
|
|
|
const Symbol &proc2) {
|
2020-09-10 22:22:52 +08:00
|
|
|
std::string name1{proc1.name().ToString()};
|
|
|
|
std::string name2{proc2.name().ToString()};
|
|
|
|
if (kind.IsOperator() || kind.IsAssignment()) {
|
|
|
|
// proc1 and proc2 may come from different scopes so qualify their names
|
|
|
|
if (proc1.owner().IsDerivedType()) {
|
|
|
|
name1 = proc1.owner().GetName()->ToString() + '%' + name1;
|
|
|
|
}
|
|
|
|
if (proc2.owner().IsDerivedType()) {
|
|
|
|
name2 = proc2.owner().GetName()->ToString() + '%' + name2;
|
|
|
|
}
|
|
|
|
}
|
2020-12-03 07:13:49 +08:00
|
|
|
parser::Message *msg;
|
|
|
|
if (scope.sourceRange().Contains(name)) {
|
|
|
|
msg = &context_.Say(name,
|
2021-10-15 01:32:59 +08:00
|
|
|
"Generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US,
|
2020-12-03 07:13:49 +08:00
|
|
|
MakeOpName(name), name1, name2);
|
|
|
|
} else {
|
|
|
|
msg = &context_.Say(*GetTopLevelUnitContaining(proc1).GetName(),
|
2021-10-15 01:32:59 +08:00
|
|
|
"USE-associated generic '%s' may not have specific procedures '%s' and '%s' as their interfaces are not distinguishable"_err_en_US,
|
2020-12-03 07:13:49 +08:00
|
|
|
MakeOpName(name), name1, name2);
|
|
|
|
}
|
|
|
|
AttachDeclaration(*msg, scope, proc1);
|
|
|
|
AttachDeclaration(*msg, scope, proc2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// `evaluate::AttachDeclaration` doesn't handle the generic case where `proc`
|
|
|
|
// comes from a different module but is not necessarily use-associated.
|
|
|
|
void DistinguishabilityHelper::AttachDeclaration(
|
|
|
|
parser::Message &msg, const Scope &scope, const Symbol &proc) {
|
|
|
|
const Scope &unit{GetTopLevelUnitContaining(proc)};
|
|
|
|
if (unit == scope) {
|
|
|
|
evaluate::AttachDeclaration(msg, proc);
|
|
|
|
} else {
|
|
|
|
msg.Attach(unit.GetName().value(),
|
|
|
|
"'%s' is USE-associated from module '%s'"_en_US, proc.name(),
|
|
|
|
unit.GetName().value());
|
|
|
|
}
|
2020-09-10 22:22:52 +08:00
|
|
|
}
|
|
|
|
|
2019-09-11 06:51:46 +08:00
|
|
|
void CheckDeclarations(SemanticsContext &context) {
|
2019-09-19 06:43:12 +08:00
|
|
|
CheckHelper{context}.Check();
|
2019-09-11 06:51:46 +08:00
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::semantics
|