[flang] Avoid spurious warnings from reading module files

When processing the literal constants of the various kinds of
INTEGER that are too large by 1 (e.g., 2147483648_4) in expression
analysis, emit a portability warning rather than a fatal error if
the literal constant appears as the operand to a unary minus, since
the folded result will be in range.  And don't emit any warning if
the negated literal is coming from a module file -- f18 wrote the
module file and the warning would simply be confusing, especially to
the programmer that wrote (-2147483647_4-1) in the first place.

Further, emit portability warnings for the canonical expressions for
infinities and NaN (-1./0., 0./0., & 1./0.), but not when they appear
in a module file, for the same reason.  The Fortran language has no
syntax for these special values so we have to emit expressions that
fold to them.

Fixes LLVM bugs https://github.com/llvm/llvm-project/issues/55086 and
https://github.com/llvm/llvm-project/issues/55081.

Differential Revision: https://reviews.llvm.org/D126584
This commit is contained in:
Peter Klausler 2022-05-24 15:06:12 -07:00
parent 0a2d2eed43
commit 73506256bf
9 changed files with 81 additions and 15 deletions

View File

@ -74,6 +74,11 @@ end
`CFI_section`, `CFI_setpointer` or `CFI_allocate`, the lower
bound on that dimension will be set to 1 for consistency with
the `LBOUND()` intrinsic function.
* `-2147483648_4` is, strictly speaking, a non-conforming literal
constant on a machine with 32-bit two's-complement integers as
kind 4, because the grammar of Fortran expressions parses it as a
negation of a literal constant, not a negative literal constant.
This compiler accepts it with a portability warning.
## Extensions, deletions, and legacy features supported by default

View File

@ -259,6 +259,11 @@ public:
std::size_t maxAlignment() const { return maxAlignment_; }
const semantics::DerivedTypeSpec *pdtInstance() const { return pdtInstance_; }
const IntrinsicProcTable &intrinsics() const { return intrinsics_; }
bool inModuleFile() const { return inModuleFile_; }
FoldingContext &set_inModuleFile(bool yes = true) {
inModuleFile_ = yes;
return *this;
}
ConstantSubscript &StartImpliedDo(parser::CharBlock, ConstantSubscript = 1);
std::optional<ConstantSubscript> GetImpliedDo(parser::CharBlock) const;
@ -282,6 +287,7 @@ private:
static constexpr bool bigEndian_{false}; // TODO: configure for target
static constexpr std::size_t maxAlignment_{8}; // TODO: configure for target
const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
bool inModuleFile_{false};
std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
};

View File

@ -255,7 +255,7 @@ protected:
int IntegerTypeSpecKind(const parser::IntegerTypeSpec &);
private:
MaybeExpr Analyze(const parser::IntLiteralConstant &);
MaybeExpr Analyze(const parser::IntLiteralConstant &, bool negated = false);
MaybeExpr Analyze(const parser::RealLiteralConstant &);
MaybeExpr Analyze(const parser::ComplexPart &);
MaybeExpr Analyze(const parser::ComplexLiteralConstant &);
@ -308,7 +308,8 @@ private:
const std::optional<parser::KindParam> &, int defaultKind);
template <typename PARSED>
MaybeExpr ExprOrVariable(const PARSED &, parser::CharBlock source);
template <typename PARSED> MaybeExpr IntLiteralConstant(const PARSED &);
template <typename PARSED>
MaybeExpr IntLiteralConstant(const PARSED &, bool negated = false);
MaybeExpr AnalyzeString(std::string &&, int kind);
std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&);
std::optional<Expr<SubscriptInteger>> TripletPart(

View File

@ -170,6 +170,8 @@ public:
const Scope &FindScope(parser::CharBlock) const;
Scope &FindScope(parser::CharBlock);
bool IsInModuleFile(parser::CharBlock) const;
const ConstructStack &constructStack() const { return constructStack_; }
template <typename N> void PushConstruct(const N &node) {
constructStack_.emplace_back(&node);

View File

@ -1823,7 +1823,22 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
return Expr<T>{Constant<T>{quotAndRem.quotient}};
} else {
auto quotient{folded->first.Divide(folded->second, context.rounding())};
RealFlagWarnings(context, quotient.flags, "division");
// Don't warn about -1./0., 0./0., or 1./0. from a module file
// they are interpreted as canonical Fortran representations of -Inf,
// NaN, and Inf respectively.
bool isCanonicalNaNOrInf{false};
if constexpr (T::category == TypeCategory::Real) {
if (folded->second.IsZero() && context.inModuleFile()) {
using IntType = typename T::Scalar::Word;
auto intNumerator{folded->first.template ToInteger<IntType>()};
isCanonicalNaNOrInf = intNumerator.flags == RealFlags{} &&
intNumerator.value >= IntType{-1} &&
intNumerator.value <= IntType{1};
}
}
if (!isCanonicalNaNOrInf) {
RealFlagWarnings(context, quotient.flags, "division");
}
if (context.flushSubnormalsToZero()) {
quotient.value = quotient.value.FlushSubnormalToZero();
}

View File

@ -423,8 +423,21 @@ struct IntTypeVisitor {
template <typename T> Result Test() {
if (T::kind >= kind) {
const char *p{digits.begin()};
auto value{T::Scalar::Read(p, 10, true /*signed*/)};
if (!value.overflow) {
using Int = typename T::Scalar;
typename Int::ValueWithOverflow num{0, false};
if (isNegated) {
auto unsignedNum{Int::Read(p, 10, false /*unsigned*/)};
num.value = unsignedNum.value.Negate().value;
num.overflow = unsignedNum.overflow || num.value > Int{0};
if (!num.overflow && num.value.Negate().overflow &&
!analyzer.context().IsInModuleFile(digits)) {
analyzer.Say(digits,
"negated maximum INTEGER(KIND=%d) literal"_port_en_US, T::kind);
}
} else {
num = Int::Read(p, 10, true /*signed*/);
}
if (!num.overflow) {
if (T::kind > kind) {
if (!isDefaultKind ||
!analyzer.context().IsEnabled(LanguageFeature::BigIntLiterals)) {
@ -438,7 +451,7 @@ struct IntTypeVisitor {
}
}
return Expr<SomeType>{
Expr<SomeInteger>{Expr<T>{Constant<T>{std::move(value.value)}}}};
Expr<SomeInteger>{Expr<T>{Constant<T>{std::move(num.value)}}}};
}
}
return std::nullopt;
@ -447,17 +460,19 @@ struct IntTypeVisitor {
parser::CharBlock digits;
int kind;
bool isDefaultKind;
bool isNegated;
};
template <typename PARSED>
MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) {
MaybeExpr ExpressionAnalyzer::IntLiteralConstant(
const PARSED &x, bool isNegated) {
const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)};
bool isDefaultKind{!kindParam};
int kind{AnalyzeKindParam(kindParam, GetDefaultKind(TypeCategory::Integer))};
if (CheckIntrinsicKind(TypeCategory::Integer, kind)) {
auto digits{std::get<parser::CharBlock>(x.t)};
if (MaybeExpr result{common::SearchTypes(
IntTypeVisitor{*this, digits, kind, isDefaultKind})}) {
IntTypeVisitor{*this, digits, kind, isDefaultKind, isNegated})}) {
return result;
} else if (isDefaultKind) {
Say(digits,
@ -471,10 +486,11 @@ MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) {
return std::nullopt;
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::IntLiteralConstant &x) {
MaybeExpr ExpressionAnalyzer::Analyze(
const parser::IntLiteralConstant &x, bool isNegated) {
auto restorer{
GetContextualMessages().SetLocation(std::get<parser::CharBlock>(x.t))};
return IntLiteralConstant(x);
return IntLiteralConstant(x, isNegated);
}
MaybeExpr ExpressionAnalyzer::Analyze(
@ -2595,6 +2611,13 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) {
}
MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Negate &x) {
if (const auto *litConst{
std::get_if<parser::LiteralConstant>(&x.v.value().u)}) {
if (const auto *intConst{
std::get_if<parser::IntLiteralConstant>(&litConst->u)}) {
return Analyze(*intConst, true);
}
}
return NumericUnaryHelper(*this, NumericOperator::Subtract, x);
}
@ -3462,10 +3485,10 @@ bool ArgumentAnalyzer::OkLogicalIntegerAssignment(
std::optional<parser::MessageFixedText> msg;
if (lhs == TypeCategory::Integer && rhs == TypeCategory::Logical) {
// allow assignment to LOGICAL from INTEGER as a legacy extension
msg = "nonstandard usage: assignment of LOGICAL to INTEGER"_port_en_US;
msg = "assignment of LOGICAL to INTEGER"_port_en_US;
} else if (lhs == TypeCategory::Logical && rhs == TypeCategory::Integer) {
// ... and assignment to LOGICAL from INTEGER
msg = "nonstandard usage: assignment of INTEGER to LOGICAL"_port_en_US;
msg = "assignment of INTEGER to LOGICAL"_port_en_US;
} else {
return false;
}

View File

@ -1018,9 +1018,13 @@ Scope *ModFileReader::Read(const SourceName &name,
if (!pair.second) {
return nullptr;
}
// Process declarations from the module file
Symbol &modSymbol{*pair.first->second};
modSymbol.set(Symbol::Flag::ModFile);
bool wasInModuleFile{context_.foldingContext().inModuleFile()};
context_.foldingContext().set_inModuleFile(true);
ResolveNames(context_, parseTree, topScope);
context_.foldingContext().set_inModuleFile(wasInModuleFile);
CHECK(modSymbol.has<ModuleDetails>());
CHECK(modSymbol.test(Symbol::Flag::ModFile));
if (isIntrinsic.value_or(false)) {

View File

@ -356,6 +356,16 @@ Scope &SemanticsContext::FindScope(parser::CharBlock source) {
}
}
bool SemanticsContext::IsInModuleFile(parser::CharBlock source) const {
for (const Scope *scope{&FindScope(source)}; !scope->IsGlobal();
scope = &scope->parent()) {
if (scope->IsModuleFile()) {
return true;
}
}
return false;
}
void SemanticsContext::PopConstruct() {
CHECK(!constructStack_.empty());
constructStack_.pop_back();

View File

@ -215,7 +215,7 @@ PROGRAM do_issue_458
! Invalid initial expression
!ERROR: Integer literal is too large for INTEGER(KIND=4)
DO ivar = -2147483648_4, 10, 3
DO ivar = -2147483649_4, 10, 3
PRINT *, "ivar is: ", ivar
END DO
@ -257,7 +257,7 @@ PROGRAM do_issue_458
! Invalid final expression
!ERROR: Integer literal is too large for INTEGER(KIND=4)
DO ivar = 1, -2147483648_4, 3
DO ivar = 1, -2147483649_4, 3
PRINT *, "ivar is: ", ivar
END DO
@ -299,7 +299,7 @@ PROGRAM do_issue_458
! Invalid step expression
!ERROR: Integer literal is too large for INTEGER(KIND=4)
DO ivar = 1, 10, -2147483648_4
DO ivar = 1, 10, -2147483649_4
PRINT *, "ivar is: ", ivar
END DO