-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[flang] Ensure that portability warnings are conditional #71857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-flang-semantics @llvm/pr-subscribers-flang-driver Author: Peter Klausler (klausler) ChangesBefore emitting a warning message, code should check that the usage in question should be diagnosed by calling ShouldWarn(). A fair number of sites in the code do not, and can emit portability warnings unconditionally, which can confuse a user that hasn't asked for them (-pedantic) and isn't terribly concerned about portability to other compilers. Add calls to ShouldWarn() or IsEnabled() around messages that need them, and add -pedantic to tests that now require it to test their portability messages, and add more expected message lines to those tests when -pedantic causes other diagnostics to fire. Patch is 104.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/71857.diff 78 Files Affected:
diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h
index 94a39c50e049b11..7e518a210f01cd3 100644
--- a/flang/include/flang/Common/Fortran-features.h
+++ b/flang/include/flang/Common/Fortran-features.h
@@ -37,14 +37,23 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
DistinguishableSpecifics, DefaultSave, PointerInSeqType, NonCharacterFormat,
SaveMainProgram, SaveBigMainProgramVariables,
DistinctArrayConstructorLengths, PPCVector, RelaxedIntentInChecking,
- ForwardRefImplicitNoneData, NullActualForAllocatable)
+ ForwardRefImplicitNoneData, NullActualForAllocatable,
+ ActualIntegerConvertedToSmallerKind, HollerithOrCharacterAsBOZ,
+ BindingAsProcedure, StatementFunctionExtensions,
+ UseGenericIntrinsicWhenSpecificDoesntMatch, DataStmtExtensions,
+ RedundantContiguous, InitBlankCommon, EmptyBindCDerivedType,
+ MiscSourceExtensions, AllocateToOtherLength, LongNames, IntrinsicAsSpecific,
+ BenignNameClash, BenignRedundancy, NullMoldAllocatableComponentValue,
+ NopassScalarBase, MiscUseExtensions, ImpliedDoIndexScope,
+ DistinctCommonSizes)
// Portability and suspicious usage warnings for conforming code
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
NonTargetPassedToTarget, PointerToPossibleNoncontiguous,
ShortCharacterActual, ExprPassedToVolatile, ImplicitInterfaceActual,
PolymorphicTransferArg, PointerComponentTransferArg, TransferSizePresence,
- F202XAllocatableBreakingChange)
+ F202XAllocatableBreakingChange, DimMustBePresent, CommonBlockPadding,
+ LogicalVsCBool, BindCCharLength)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/include/flang/Evaluate/common.h b/flang/include/flang/Evaluate/common.h
index d05072742a48ca8..c8d93e0849229f5 100644
--- a/flang/include/flang/Evaluate/common.h
+++ b/flang/include/flang/Evaluate/common.h
@@ -9,6 +9,7 @@
#ifndef FORTRAN_EVALUATE_COMMON_H_
#define FORTRAN_EVALUATE_COMMON_H_
+#include "flang/Common/Fortran-features.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/default-kinds.h"
#include "flang/Common/enum-set.h"
@@ -215,22 +216,27 @@ template <typename A> class Expr;
class FoldingContext {
public:
FoldingContext(const common::IntrinsicTypeDefaultKinds &d,
- const IntrinsicProcTable &t, const TargetCharacteristics &c)
- : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
+ const IntrinsicProcTable &t, const TargetCharacteristics &c,
+ const common::LanguageFeatureControl &lfc)
+ : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
+ languageFeatures_{lfc} {}
FoldingContext(const parser::ContextualMessages &m,
const common::IntrinsicTypeDefaultKinds &d, const IntrinsicProcTable &t,
- const TargetCharacteristics &c)
- : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
+ const TargetCharacteristics &c, const common::LanguageFeatureControl &lfc)
+ : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
+ languageFeatures_{lfc} {}
FoldingContext(const FoldingContext &that)
: messages_{that.messages_}, defaults_{that.defaults_},
intrinsics_{that.intrinsics_},
targetCharacteristics_{that.targetCharacteristics_},
- pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
+ pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
+ languageFeatures_{that.languageFeatures_} {}
FoldingContext(
const FoldingContext &that, const parser::ContextualMessages &m)
: messages_{m}, defaults_{that.defaults_}, intrinsics_{that.intrinsics_},
targetCharacteristics_{that.targetCharacteristics_},
- pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
+ pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
+ languageFeatures_{that.languageFeatures_} {}
parser::ContextualMessages &messages() { return messages_; }
const parser::ContextualMessages &messages() const { return messages_; }
@@ -242,6 +248,9 @@ class FoldingContext {
const TargetCharacteristics &targetCharacteristics() const {
return targetCharacteristics_;
}
+ const common::LanguageFeatureControl &languageFeatures() const {
+ return languageFeatures_;
+ }
bool inModuleFile() const { return inModuleFile_; }
FoldingContext &set_inModuleFile(bool yes = true) {
inModuleFile_ = yes;
@@ -272,6 +281,7 @@ class FoldingContext {
const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
bool inModuleFile_{false};
std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
+ const common::LanguageFeatureControl &languageFeatures_;
};
void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);
diff --git a/flang/include/flang/Lower/Bridge.h b/flang/include/flang/Lower/Bridge.h
index b4ee77a0b166ec9..ecf82ba5bc3bb40 100644
--- a/flang/include/flang/Lower/Bridge.h
+++ b/flang/include/flang/Lower/Bridge.h
@@ -58,10 +58,11 @@ class LoweringBridge {
const Fortran::parser::AllCookedSources &allCooked,
llvm::StringRef triple, fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults) {
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures) {
return LoweringBridge(ctx, semanticsContext, defaultKinds, intrinsics,
targetCharacteristics, allCooked, triple, kindMap,
- loweringOptions, envDefaults);
+ loweringOptions, envDefaults, languageFeatures);
}
//===--------------------------------------------------------------------===//
@@ -99,6 +100,10 @@ class LoweringBridge {
return envDefaults;
}
+ const Fortran::common::LanguageFeatureControl &getLanguageFeatures() const {
+ return languageFeatures;
+ }
+
/// Create a folding context. Careful: this is very expensive.
Fortran::evaluate::FoldingContext createFoldingContext() const;
@@ -132,7 +137,8 @@ class LoweringBridge {
const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple,
fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults);
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures);
LoweringBridge() = delete;
LoweringBridge(const LoweringBridge &) = delete;
@@ -147,6 +153,7 @@ class LoweringBridge {
fir::KindMapping &kindMap;
const Fortran::lower::LoweringOptions &loweringOptions;
const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults;
+ const Fortran::common::LanguageFeatureControl &languageFeatures;
};
} // namespace lower
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 531fc5ccc56c858..2f46ed7dccb6455 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -12,6 +12,7 @@
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/traverse.h"
#include "flang/Evaluate/type.h"
+#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include <set>
@@ -1030,23 +1031,46 @@ class StmtFunctionChecker
using Result = std::optional<parser::Message>;
using Base = AnyTraverse<StmtFunctionChecker, Result>;
StmtFunctionChecker(const Symbol &sf, FoldingContext &context)
- : Base{*this}, sf_{sf}, context_{context} {}
+ : Base{*this}, sf_{sf}, context_{context} {
+ if (!context_.languageFeatures().IsEnabled(
+ common::LanguageFeature::StatementFunctionExtensions)) {
+ severity_ = parser::Severity::Error;
+ } else if (context_.languageFeatures().ShouldWarn(
+ common::LanguageFeature::StatementFunctionExtensions)) {
+ severity_ = parser::Severity::Portability;
+ }
+ }
using Base::operator();
template <typename T> Result operator()(const ArrayConstructor<T> &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain an array constructor"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain an array constructor"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const StructureConstructor &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain a structure constructor"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain a structure constructor"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const TypeParamInquiry &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain a type parameter inquiry"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain a type parameter inquiry"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const ProcedureDesignator &proc) const {
if (const Symbol * symbol{proc.GetSymbol()}) {
@@ -1064,16 +1088,23 @@ class StmtFunctionChecker
if (auto chars{
characteristics::Procedure::Characterize(proc, context_)}) {
if (!chars->CanBeCalledViaImplicitInterface()) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US,
- sf_.name(), symbol->name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{
+ sf_.name(), std::move(msg), sf_.name(), symbol->name()};
+ }
}
}
}
if (proc.Rank() > 0) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not reference a function that returns an array"_port_en_US,
- sf_.name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not reference a function that returns an array"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ }
}
return std::nullopt;
}
@@ -1083,9 +1114,12 @@ class StmtFunctionChecker
return result;
}
if (expr->Rank() > 0 && !UnwrapWholeSymbolOrComponentDataRef(*expr)) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US,
- sf_.name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ }
}
}
return std::nullopt;
@@ -1094,6 +1128,7 @@ class StmtFunctionChecker
private:
const Symbol &sf_;
FoldingContext &context_;
+ std::optional<parser::Severity> severity_;
};
std::optional<parser::Message> CheckStatementFunction(
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index c711b4feaca4831..52655cae8862b52 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2224,12 +2224,15 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
if (const Symbol *whole{
UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
- if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
- messages.Say(
- "The actual argument for DIM= is optional, pointer, or allocatable, and it is assumed to be present and equal to 1 at execution time"_port_en_US);
- } else {
- messages.Say(
- "The actual argument for DIM= is optional, pointer, or allocatable, and may not be absent during execution; parenthesize to silence this warning"_warn_en_US);
+ if (context.languageFeatures().ShouldWarn(
+ common::UsageWarning::DimMustBePresent)) {
+ if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
+ messages.Say(
+ "The actual argument for DIM= is optional, pointer, or allocatable, and it is assumed to be present and equal to 1 at execution time"_warn_en_US);
+ } else {
+ messages.Say(
+ "The actual argument for DIM= is optional, pointer, or allocatable, and may not be absent during execution; parenthesize to silence this warning"_warn_en_US);
+ }
}
}
}
@@ -3180,28 +3183,37 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
// If there was no exact match with a specific, try to match the related
// generic and convert the result to the specific required type.
- for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
- // We only need to check the cases with distinct generic names.
- if (const char *genericName{specIter->second->generic}) {
- if (specIter->second->useGenericAndForceResultType) {
- auto genericRange{genericFuncs_.equal_range(genericName)};
- for (auto genIter{genericRange.first}; genIter != genericRange.second;
- ++genIter) {
- if (auto specificCall{
- matchOrBufferMessages(*genIter->second, specificBuffer)}) {
- // Force the call result type to the specific intrinsic result type
- DynamicType newType{GetReturnType(*specIter->second, defaults_)};
- context.messages().Say(
- "argument types do not match specific intrinsic '%s' "
- "requirements; using '%s' generic instead and converting the "
- "result to %s if needed"_port_en_US,
- call.name, genericName, newType.AsFortran());
- specificCall->specificIntrinsic.name = call.name;
- specificCall->specificIntrinsic.characteristics.value()
- .functionResult.value()
- .SetType(newType);
- return specificCall;
+ if (context.languageFeatures().IsEnabled(common::LanguageFeature::
+ UseGenericIntrinsicWhenSpecificDoesntMatch)) {
+ for (auto specIter{specificRange.first}; specIter != specificRange.second;
+ ++specIter) {
+ // We only need to check the cases with distinct generic names.
+ if (const char *genericName{specIter->second->generic}) {
+ if (specIter->second->useGenericAndForceResultType) {
+ auto genericRange{genericFuncs_.equal_range(genericName)};
+ for (auto genIter{genericRange.first}; genIter != genericRange.second;
+ ++genIter) {
+ if (auto specificCall{
+ matchOrBufferMessages(*genIter->second, specificBuffer)}) {
+ // Force the call result type to the specific intrinsic result
+ // type
+ DynamicType newType{GetReturnType(*specIter->second, defaults_)};
+ if (context.languageFeatures().ShouldWarn(
+ common::LanguageFeature::
+ UseGenericIntrinsicWhenSpecificDoesntMatch)) {
+ context.messages().Say(
+ "Argument types do not match specific intrinsic '%s' "
+ "requirements; using '%s' generic instead and converting "
+ "the "
+ "result to %s if needed"_port_en_US,
+ call.name, genericName, newType.AsFortran());
+ }
+ specificCall->specificIntrinsic.name = call.name;
+ specificCall->specificIntrinsic.characteristics.value()
+ .functionResult.value()
+ .SetType(newType);
+ return specificCall;
+ }
}
}
}
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 73c00c8679c7ec6..f09e62148e53c53 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -278,7 +278,8 @@ bool CodeGenAction::beginSourceFileAction() {
ci.getInvocation().getSemanticsContext().targetCharacteristics(),
ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
kindMap, ci.getInvocation().getLoweringOpts(),
- ci.getInvocation().getFrontendOpts().envDefaults);
+ ci.getInvocation().getFrontendOpts().envDefaults,
+ ci.getInvocation().getFrontendOpts().features);
// Fetch module from lb, so we can set
mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 9875e37393ef869..f64719b64f12e5a 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4901,7 +4901,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::evaluate::FoldingContext
Fortran::lower::LoweringBridge::createFoldingContext() const {
- return {getDefaultKinds(), getIntrinsicTable(), getTargetCharacteristics()};
+ return {getDefaultKinds(), getIntrinsicTable(), getTargetCharacteristics(),
+ getLanguageFeatures()};
}
void Fortran::lower::LoweringBridge::lower(
@@ -4931,11 +4932,13 @@ Fortran::lower::LoweringBridge::LoweringBridge(
const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple,
fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults)
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures)
: semanticsContext{semanticsContext}, defaultKinds{defaultKinds},
intrinsics{intrinsics}, targetCharacteristics{targetCharacteristics},
cooked{&cooked}, context{context}, kindMap{kindMap},
- loweringOptions{loweringOptions}, envDefaults{envDefaults} {
+ loweringOptions{loweringOptions}, envDefaults{envDefaults},
+ languageFeatures{languageFeatures} {
// Register the diagnostic handler.
context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) {
llvm::raw_ostream &os = llvm::errs();
diff --git a/flang/lib/Parser/expr-parsers.cpp b/flang/lib/Parser/expr-parsers.cpp
index 45e6b2869c02bd4..b27366d02308eb5 100644
--- a/flang/lib/Parser/expr-parsers.cpp
+++ b/flang/lib/Parser/expr-parsers.cpp
@@ -77,10 +77,8 @@ constexpr auto primary{instrumented("primary"_en_US,
construct<Expr>(Parser<StructureConstructor>{}),
construct<Expr>(Parser<ArrayConstructor>{}),
// PGI/XLF extension: COMPLEX constructor (x,y)
- extension<LanguageFeature::ComplexConstructor>(
- "nonstandard usage: generalized COMPLEX constructor"_port_en_US,
- construct<Expr>(parenthesized(
- construct<Expr::ComplexConstructor>(expr, "," >> expr)))),
+ construct<Expr>(parenthesized(
+ construct<Expr::ComplexConstructor>(expr, "," >> expr))),
extension<LanguageFeature::PercentLOC>(
"nonstandard usage: %LOC"_port_en_US,
...
[truncated]
|
@llvm/pr-subscribers-flang-fir-hlfir Author: Peter Klausler (klausler) ChangesBefore emitting a warning message, code should check that the usage in question should be diagnosed by calling ShouldWarn(). A fair number of sites in the code do not, and can emit portability warnings unconditionally, which can confuse a user that hasn't asked for them (-pedantic) and isn't terribly concerned about portability to other compilers. Add calls to ShouldWarn() or IsEnabled() around messages that need them, and add -pedantic to tests that now require it to test their portability messages, and add more expected message lines to those tests when -pedantic causes other diagnostics to fire. Patch is 104.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/71857.diff 78 Files Affected:
diff --git a/flang/include/flang/Common/Fortran-features.h b/flang/include/flang/Common/Fortran-features.h
index 94a39c50e049b11..7e518a210f01cd3 100644
--- a/flang/include/flang/Common/Fortran-features.h
+++ b/flang/include/flang/Common/Fortran-features.h
@@ -37,14 +37,23 @@ ENUM_CLASS(LanguageFeature, BackslashEscapes, OldDebugLines,
DistinguishableSpecifics, DefaultSave, PointerInSeqType, NonCharacterFormat,
SaveMainProgram, SaveBigMainProgramVariables,
DistinctArrayConstructorLengths, PPCVector, RelaxedIntentInChecking,
- ForwardRefImplicitNoneData, NullActualForAllocatable)
+ ForwardRefImplicitNoneData, NullActualForAllocatable,
+ ActualIntegerConvertedToSmallerKind, HollerithOrCharacterAsBOZ,
+ BindingAsProcedure, StatementFunctionExtensions,
+ UseGenericIntrinsicWhenSpecificDoesntMatch, DataStmtExtensions,
+ RedundantContiguous, InitBlankCommon, EmptyBindCDerivedType,
+ MiscSourceExtensions, AllocateToOtherLength, LongNames, IntrinsicAsSpecific,
+ BenignNameClash, BenignRedundancy, NullMoldAllocatableComponentValue,
+ NopassScalarBase, MiscUseExtensions, ImpliedDoIndexScope,
+ DistinctCommonSizes)
// Portability and suspicious usage warnings for conforming code
ENUM_CLASS(UsageWarning, Portability, PointerToUndefinable,
NonTargetPassedToTarget, PointerToPossibleNoncontiguous,
ShortCharacterActual, ExprPassedToVolatile, ImplicitInterfaceActual,
PolymorphicTransferArg, PointerComponentTransferArg, TransferSizePresence,
- F202XAllocatableBreakingChange)
+ F202XAllocatableBreakingChange, DimMustBePresent, CommonBlockPadding,
+ LogicalVsCBool, BindCCharLength)
using LanguageFeatures = EnumSet<LanguageFeature, LanguageFeature_enumSize>;
using UsageWarnings = EnumSet<UsageWarning, UsageWarning_enumSize>;
diff --git a/flang/include/flang/Evaluate/common.h b/flang/include/flang/Evaluate/common.h
index d05072742a48ca8..c8d93e0849229f5 100644
--- a/flang/include/flang/Evaluate/common.h
+++ b/flang/include/flang/Evaluate/common.h
@@ -9,6 +9,7 @@
#ifndef FORTRAN_EVALUATE_COMMON_H_
#define FORTRAN_EVALUATE_COMMON_H_
+#include "flang/Common/Fortran-features.h"
#include "flang/Common/Fortran.h"
#include "flang/Common/default-kinds.h"
#include "flang/Common/enum-set.h"
@@ -215,22 +216,27 @@ template <typename A> class Expr;
class FoldingContext {
public:
FoldingContext(const common::IntrinsicTypeDefaultKinds &d,
- const IntrinsicProcTable &t, const TargetCharacteristics &c)
- : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
+ const IntrinsicProcTable &t, const TargetCharacteristics &c,
+ const common::LanguageFeatureControl &lfc)
+ : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
+ languageFeatures_{lfc} {}
FoldingContext(const parser::ContextualMessages &m,
const common::IntrinsicTypeDefaultKinds &d, const IntrinsicProcTable &t,
- const TargetCharacteristics &c)
- : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
+ const TargetCharacteristics &c, const common::LanguageFeatureControl &lfc)
+ : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
+ languageFeatures_{lfc} {}
FoldingContext(const FoldingContext &that)
: messages_{that.messages_}, defaults_{that.defaults_},
intrinsics_{that.intrinsics_},
targetCharacteristics_{that.targetCharacteristics_},
- pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
+ pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
+ languageFeatures_{that.languageFeatures_} {}
FoldingContext(
const FoldingContext &that, const parser::ContextualMessages &m)
: messages_{m}, defaults_{that.defaults_}, intrinsics_{that.intrinsics_},
targetCharacteristics_{that.targetCharacteristics_},
- pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
+ pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
+ languageFeatures_{that.languageFeatures_} {}
parser::ContextualMessages &messages() { return messages_; }
const parser::ContextualMessages &messages() const { return messages_; }
@@ -242,6 +248,9 @@ class FoldingContext {
const TargetCharacteristics &targetCharacteristics() const {
return targetCharacteristics_;
}
+ const common::LanguageFeatureControl &languageFeatures() const {
+ return languageFeatures_;
+ }
bool inModuleFile() const { return inModuleFile_; }
FoldingContext &set_inModuleFile(bool yes = true) {
inModuleFile_ = yes;
@@ -272,6 +281,7 @@ class FoldingContext {
const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
bool inModuleFile_{false};
std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
+ const common::LanguageFeatureControl &languageFeatures_;
};
void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);
diff --git a/flang/include/flang/Lower/Bridge.h b/flang/include/flang/Lower/Bridge.h
index b4ee77a0b166ec9..ecf82ba5bc3bb40 100644
--- a/flang/include/flang/Lower/Bridge.h
+++ b/flang/include/flang/Lower/Bridge.h
@@ -58,10 +58,11 @@ class LoweringBridge {
const Fortran::parser::AllCookedSources &allCooked,
llvm::StringRef triple, fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults) {
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures) {
return LoweringBridge(ctx, semanticsContext, defaultKinds, intrinsics,
targetCharacteristics, allCooked, triple, kindMap,
- loweringOptions, envDefaults);
+ loweringOptions, envDefaults, languageFeatures);
}
//===--------------------------------------------------------------------===//
@@ -99,6 +100,10 @@ class LoweringBridge {
return envDefaults;
}
+ const Fortran::common::LanguageFeatureControl &getLanguageFeatures() const {
+ return languageFeatures;
+ }
+
/// Create a folding context. Careful: this is very expensive.
Fortran::evaluate::FoldingContext createFoldingContext() const;
@@ -132,7 +137,8 @@ class LoweringBridge {
const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple,
fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults);
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures);
LoweringBridge() = delete;
LoweringBridge(const LoweringBridge &) = delete;
@@ -147,6 +153,7 @@ class LoweringBridge {
fir::KindMapping &kindMap;
const Fortran::lower::LoweringOptions &loweringOptions;
const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults;
+ const Fortran::common::LanguageFeatureControl &languageFeatures;
};
} // namespace lower
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 531fc5ccc56c858..2f46ed7dccb6455 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -12,6 +12,7 @@
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/traverse.h"
#include "flang/Evaluate/type.h"
+#include "flang/Semantics/semantics.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include <set>
@@ -1030,23 +1031,46 @@ class StmtFunctionChecker
using Result = std::optional<parser::Message>;
using Base = AnyTraverse<StmtFunctionChecker, Result>;
StmtFunctionChecker(const Symbol &sf, FoldingContext &context)
- : Base{*this}, sf_{sf}, context_{context} {}
+ : Base{*this}, sf_{sf}, context_{context} {
+ if (!context_.languageFeatures().IsEnabled(
+ common::LanguageFeature::StatementFunctionExtensions)) {
+ severity_ = parser::Severity::Error;
+ } else if (context_.languageFeatures().ShouldWarn(
+ common::LanguageFeature::StatementFunctionExtensions)) {
+ severity_ = parser::Severity::Portability;
+ }
+ }
using Base::operator();
template <typename T> Result operator()(const ArrayConstructor<T> &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain an array constructor"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain an array constructor"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const StructureConstructor &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain a structure constructor"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain a structure constructor"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const TypeParamInquiry &) const {
- return parser::Message{sf_.name(),
- "Statement function '%s' should not contain a type parameter inquiry"_port_en_US,
- sf_.name()};
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not contain a type parameter inquiry"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ } else {
+ return std::nullopt;
+ }
}
Result operator()(const ProcedureDesignator &proc) const {
if (const Symbol * symbol{proc.GetSymbol()}) {
@@ -1064,16 +1088,23 @@ class StmtFunctionChecker
if (auto chars{
characteristics::Procedure::Characterize(proc, context_)}) {
if (!chars->CanBeCalledViaImplicitInterface()) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US,
- sf_.name(), symbol->name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{
+ sf_.name(), std::move(msg), sf_.name(), symbol->name()};
+ }
}
}
}
if (proc.Rank() > 0) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not reference a function that returns an array"_port_en_US,
- sf_.name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not reference a function that returns an array"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ }
}
return std::nullopt;
}
@@ -1083,9 +1114,12 @@ class StmtFunctionChecker
return result;
}
if (expr->Rank() > 0 && !UnwrapWholeSymbolOrComponentDataRef(*expr)) {
- return parser::Message(sf_.name(),
- "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US,
- sf_.name());
+ if (severity_) {
+ auto msg{
+ "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US};
+ msg.set_severity(*severity_);
+ return parser::Message{sf_.name(), std::move(msg), sf_.name()};
+ }
}
}
return std::nullopt;
@@ -1094,6 +1128,7 @@ class StmtFunctionChecker
private:
const Symbol &sf_;
FoldingContext &context_;
+ std::optional<parser::Severity> severity_;
};
std::optional<parser::Message> CheckStatementFunction(
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index c711b4feaca4831..52655cae8862b52 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -2224,12 +2224,15 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
if (const Symbol *whole{
UnwrapWholeSymbolOrComponentDataRef(actualForDummy[*dimArg])}) {
if (IsOptional(*whole) || IsAllocatableOrObjectPointer(whole)) {
- if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
- messages.Say(
- "The actual argument for DIM= is optional, pointer, or allocatable, and it is assumed to be present and equal to 1 at execution time"_port_en_US);
- } else {
- messages.Say(
- "The actual argument for DIM= is optional, pointer, or allocatable, and may not be absent during execution; parenthesize to silence this warning"_warn_en_US);
+ if (context.languageFeatures().ShouldWarn(
+ common::UsageWarning::DimMustBePresent)) {
+ if (rank == Rank::scalarIfDim || arrayRank.value_or(-1) == 1) {
+ messages.Say(
+ "The actual argument for DIM= is optional, pointer, or allocatable, and it is assumed to be present and equal to 1 at execution time"_warn_en_US);
+ } else {
+ messages.Say(
+ "The actual argument for DIM= is optional, pointer, or allocatable, and may not be absent during execution; parenthesize to silence this warning"_warn_en_US);
+ }
}
}
}
@@ -3180,28 +3183,37 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::Probe(
// If there was no exact match with a specific, try to match the related
// generic and convert the result to the specific required type.
- for (auto specIter{specificRange.first}; specIter != specificRange.second;
- ++specIter) {
- // We only need to check the cases with distinct generic names.
- if (const char *genericName{specIter->second->generic}) {
- if (specIter->second->useGenericAndForceResultType) {
- auto genericRange{genericFuncs_.equal_range(genericName)};
- for (auto genIter{genericRange.first}; genIter != genericRange.second;
- ++genIter) {
- if (auto specificCall{
- matchOrBufferMessages(*genIter->second, specificBuffer)}) {
- // Force the call result type to the specific intrinsic result type
- DynamicType newType{GetReturnType(*specIter->second, defaults_)};
- context.messages().Say(
- "argument types do not match specific intrinsic '%s' "
- "requirements; using '%s' generic instead and converting the "
- "result to %s if needed"_port_en_US,
- call.name, genericName, newType.AsFortran());
- specificCall->specificIntrinsic.name = call.name;
- specificCall->specificIntrinsic.characteristics.value()
- .functionResult.value()
- .SetType(newType);
- return specificCall;
+ if (context.languageFeatures().IsEnabled(common::LanguageFeature::
+ UseGenericIntrinsicWhenSpecificDoesntMatch)) {
+ for (auto specIter{specificRange.first}; specIter != specificRange.second;
+ ++specIter) {
+ // We only need to check the cases with distinct generic names.
+ if (const char *genericName{specIter->second->generic}) {
+ if (specIter->second->useGenericAndForceResultType) {
+ auto genericRange{genericFuncs_.equal_range(genericName)};
+ for (auto genIter{genericRange.first}; genIter != genericRange.second;
+ ++genIter) {
+ if (auto specificCall{
+ matchOrBufferMessages(*genIter->second, specificBuffer)}) {
+ // Force the call result type to the specific intrinsic result
+ // type
+ DynamicType newType{GetReturnType(*specIter->second, defaults_)};
+ if (context.languageFeatures().ShouldWarn(
+ common::LanguageFeature::
+ UseGenericIntrinsicWhenSpecificDoesntMatch)) {
+ context.messages().Say(
+ "Argument types do not match specific intrinsic '%s' "
+ "requirements; using '%s' generic instead and converting "
+ "the "
+ "result to %s if needed"_port_en_US,
+ call.name, genericName, newType.AsFortran());
+ }
+ specificCall->specificIntrinsic.name = call.name;
+ specificCall->specificIntrinsic.characteristics.value()
+ .functionResult.value()
+ .SetType(newType);
+ return specificCall;
+ }
}
}
}
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 73c00c8679c7ec6..f09e62148e53c53 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -278,7 +278,8 @@ bool CodeGenAction::beginSourceFileAction() {
ci.getInvocation().getSemanticsContext().targetCharacteristics(),
ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple,
kindMap, ci.getInvocation().getLoweringOpts(),
- ci.getInvocation().getFrontendOpts().envDefaults);
+ ci.getInvocation().getFrontendOpts().envDefaults,
+ ci.getInvocation().getFrontendOpts().features);
// Fetch module from lb, so we can set
mlirModule = std::make_unique<mlir::ModuleOp>(lb.getModule());
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 9875e37393ef869..f64719b64f12e5a 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4901,7 +4901,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::evaluate::FoldingContext
Fortran::lower::LoweringBridge::createFoldingContext() const {
- return {getDefaultKinds(), getIntrinsicTable(), getTargetCharacteristics()};
+ return {getDefaultKinds(), getIntrinsicTable(), getTargetCharacteristics(),
+ getLanguageFeatures()};
}
void Fortran::lower::LoweringBridge::lower(
@@ -4931,11 +4932,13 @@ Fortran::lower::LoweringBridge::LoweringBridge(
const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple,
fir::KindMapping &kindMap,
const Fortran::lower::LoweringOptions &loweringOptions,
- const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults)
+ const std::vector<Fortran::lower::EnvironmentDefault> &envDefaults,
+ const Fortran::common::LanguageFeatureControl &languageFeatures)
: semanticsContext{semanticsContext}, defaultKinds{defaultKinds},
intrinsics{intrinsics}, targetCharacteristics{targetCharacteristics},
cooked{&cooked}, context{context}, kindMap{kindMap},
- loweringOptions{loweringOptions}, envDefaults{envDefaults} {
+ loweringOptions{loweringOptions}, envDefaults{envDefaults},
+ languageFeatures{languageFeatures} {
// Register the diagnostic handler.
context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) {
llvm::raw_ostream &os = llvm::errs();
diff --git a/flang/lib/Parser/expr-parsers.cpp b/flang/lib/Parser/expr-parsers.cpp
index 45e6b2869c02bd4..b27366d02308eb5 100644
--- a/flang/lib/Parser/expr-parsers.cpp
+++ b/flang/lib/Parser/expr-parsers.cpp
@@ -77,10 +77,8 @@ constexpr auto primary{instrumented("primary"_en_US,
construct<Expr>(Parser<StructureConstructor>{}),
construct<Expr>(Parser<ArrayConstructor>{}),
// PGI/XLF extension: COMPLEX constructor (x,y)
- extension<LanguageFeature::ComplexConstructor>(
- "nonstandard usage: generalized COMPLEX constructor"_port_en_US,
- construct<Expr>(parenthesized(
- construct<Expr::ComplexConstructor>(expr, "," >> expr)))),
+ construct<Expr>(parenthesized(
+ construct<Expr::ComplexConstructor>(expr, "," >> expr))),
extension<LanguageFeature::PercentLOC>(
"nonstandard usage: %LOC"_port_en_US,
...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clean-up.
Before emitting a warning message, code should check that the usage in question should be diagnosed by calling ShouldWarn(). A fair number of sites in the code do not, and can emit portability warnings unconditionally, which can confuse a user that hasn't asked for them (-pedantic) and isn't terribly concerned about portability *to* other compilers. Add calls to ShouldWarn() or IsEnabled() around messages that need them, and add -pedantic to tests that now require it to test their portability messages, and add more expected message lines to those tests when -pedantic causes other diagnostics to fire.
Before emitting a warning message, code should check that the usage in question should be diagnosed by calling ShouldWarn(). A fair number of sites in the code do not, and can emit portability warnings unconditionally, which can confuse a user that hasn't asked for them (-pedantic) and isn't terribly concerned about portability *to* other compilers. Add calls to ShouldWarn() or IsEnabled() around messages that need them, and add -pedantic to tests that now require it to test their portability messages, and add more expected message lines to those tests when -pedantic causes other diagnostics to fire.
Before emitting a warning message, code should check that the usage in question should be diagnosed by calling ShouldWarn(). A fair number of sites in the code do not, and can emit portability warnings unconditionally, which can confuse a user that hasn't asked for them (-pedantic) and isn't terribly concerned about portability to other compilers.
Add calls to ShouldWarn() or IsEnabled() around messages that need them, and add -pedantic to tests that now require it to test their portability messages, and add more expected message lines to those tests when -pedantic causes other diagnostics to fire.