forked from OSchip/llvm-project
[flang] Check int literals for overflow when kind is known.
improve test Original-commit: flang-compiler/f18@09ce783cd3 Reviewed-on: https://github.com/flang-compiler/f18/pull/352 Tree-same-pre-rewrite: false
This commit is contained in:
parent
1d2a212c38
commit
822810f017
|
@ -60,6 +60,8 @@ Extensions, deletions, and legacy features supported by default
|
|||
need not begin with a comment marker (!).
|
||||
* Classic C-style /*comments*/ are skipped, so multi-language header
|
||||
files are easier to write and use.
|
||||
* $ and \ edit descriptors are supported in FORMAT to suppress newline
|
||||
output on user prompts.
|
||||
|
||||
Extensions supported when enabled by options
|
||||
--------------------------------------------
|
||||
|
|
|
@ -219,16 +219,18 @@ public:
|
|||
return result;
|
||||
}
|
||||
|
||||
static constexpr ValueWithOverflow ReadUnsigned(
|
||||
const char *&pp, std::uint64_t base = 10) {
|
||||
static constexpr ValueWithOverflow Read(
|
||||
const char *&pp, std::uint64_t base = 10, bool isSigned = false) {
|
||||
Integer result;
|
||||
bool overflow{false};
|
||||
const char *p{pp};
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
}
|
||||
if (*p == '+') {
|
||||
++p;
|
||||
bool negate{*p == '-'};
|
||||
if (negate || *p == '+') {
|
||||
while (*++p == ' ' || *p == '\t') {
|
||||
}
|
||||
}
|
||||
Integer radix{base};
|
||||
// This code makes assumptions about local contiguity in regions of the
|
||||
|
@ -256,6 +258,12 @@ public:
|
|||
result = next.value;
|
||||
}
|
||||
pp = p;
|
||||
if (negate) {
|
||||
result = result.Negate().value;
|
||||
overflow |= isSigned && !result.IsNegative() && !result.IsZero();
|
||||
} else {
|
||||
overflow |= isSigned && result.IsNegative();
|
||||
}
|
||||
return {result, overflow};
|
||||
}
|
||||
|
||||
|
|
|
@ -336,7 +336,6 @@ public:
|
|||
NODE(parser, InterfaceStmt)
|
||||
NODE(parser, InternalSubprogram)
|
||||
NODE(parser, InternalSubprogramPart)
|
||||
NODE(parser, IntLiteralConstant)
|
||||
NODE(parser, Intrinsic)
|
||||
NODE(parser, IntrinsicStmt)
|
||||
NODE(parser, IntrinsicTypeSpec)
|
||||
|
@ -636,7 +635,6 @@ public:
|
|||
NODE(parser, SequenceStmt)
|
||||
NODE(parser, Sign)
|
||||
NODE(parser, SignedComplexLiteralConstant)
|
||||
NODE(parser, SignedIntLiteralConstant)
|
||||
NODE(parser, SignedRealLiteralConstant)
|
||||
NODE(parser, SpecificationConstruct)
|
||||
NODE(parser, SpecificationExpr)
|
||||
|
@ -769,6 +767,24 @@ public:
|
|||
|
||||
void Post(const std::uint64_t &x) { --indent_; }
|
||||
|
||||
bool Pre(const parser::IntLiteralConstant &x) {
|
||||
IndentEmptyLine();
|
||||
out_ << "int = '" << std::get<parser::CharBlock>(x.t).ToString() << '\'';
|
||||
++indent_;
|
||||
EndLine();
|
||||
return true;
|
||||
}
|
||||
void Post(const parser::IntLiteralConstant &) { --indent_; }
|
||||
|
||||
bool Pre(const parser::SignedIntLiteralConstant &x) {
|
||||
IndentEmptyLine();
|
||||
out_ << "int = '" << std::get<parser::CharBlock>(x.t).ToString() << '\'';
|
||||
++indent_;
|
||||
EndLine();
|
||||
return true;
|
||||
}
|
||||
void Post(const parser::SignedIntLiteralConstant &) { --indent_; }
|
||||
|
||||
// A few types we want to ignore
|
||||
|
||||
bool Pre(const parser::CharBlock &) { return true; }
|
||||
|
|
|
@ -100,8 +100,8 @@ struct ControlEditDesc {
|
|||
RP,
|
||||
DC,
|
||||
DP,
|
||||
Dollar,
|
||||
Backslash,
|
||||
Dollar, // extension: inhibit newline on output
|
||||
Backslash, // ditto, but only on terminals
|
||||
};
|
||||
ControlEditDesc() = delete;
|
||||
ControlEditDesc(ControlEditDesc &&) = default;
|
||||
|
|
|
@ -561,7 +561,7 @@ TYPE_PARSER(construct<IntegerTypeSpec>("INTEGER" >> maybe(kindSelector)))
|
|||
TYPE_PARSER(construct<KindSelector>(
|
||||
parenthesized(maybe("KIND ="_tok) >> scalarIntConstantExpr)) ||
|
||||
extension<LanguageFeature::StarKind>(construct<KindSelector>(
|
||||
construct<KindSelector::StarSize>("*" >> digitString / spaceCheck))))
|
||||
construct<KindSelector::StarSize>("*" >> digitString64 / spaceCheck))))
|
||||
|
||||
// R707 signed-int-literal-constant -> [sign] int-literal-constant
|
||||
TYPE_PARSER(sourced(construct<SignedIntLiteralConstant>(
|
||||
|
@ -574,7 +574,7 @@ TYPE_PARSER(construct<IntLiteralConstant>(
|
|||
space >> digitString, maybe(underscore >> kindParam) / !underscore))
|
||||
|
||||
// R709 kind-param -> digit-string | scalar-int-constant-name
|
||||
TYPE_PARSER(construct<KindParam>(digitString) ||
|
||||
TYPE_PARSER(construct<KindParam>(digitString64) ||
|
||||
construct<KindParam>(scalar(integer(constant(name)))))
|
||||
|
||||
// R712 sign -> + | -
|
||||
|
@ -602,13 +602,12 @@ constexpr auto exponentPart{
|
|||
TYPE_CONTEXT_PARSER("REAL literal constant"_en_US,
|
||||
space >>
|
||||
construct<RealLiteralConstant>(
|
||||
sourced(
|
||||
(skipDigitString >> "."_ch >>
|
||||
!(some(letter) >>
|
||||
"."_ch /* don't misinterpret 1.AND. */) >>
|
||||
maybe(skipDigitString) >> maybe(exponentPart) >> ok ||
|
||||
"."_ch >> skipDigitString >> maybe(exponentPart) >> ok ||
|
||||
skipDigitString >> exponentPart >> ok) >>
|
||||
sourced((digitString >> "."_ch >>
|
||||
!(some(letter) >>
|
||||
"."_ch /* don't misinterpret 1.AND. */) >>
|
||||
maybe(digitString) >> maybe(exponentPart) >> ok ||
|
||||
"."_ch >> digitString >> maybe(exponentPart) >> ok ||
|
||||
digitString >> exponentPart >> ok) >>
|
||||
construct<RealLiteralConstant::Real>()),
|
||||
maybe(underscore >> kindParam)))
|
||||
|
||||
|
@ -655,7 +654,7 @@ TYPE_PARSER(construct<LengthSelector>(
|
|||
|
||||
// R723 char-length -> ( type-param-value ) | digit-string
|
||||
TYPE_PARSER(construct<CharLength>(parenthesized(typeParamValue)) ||
|
||||
construct<CharLength>(space >> digitString / spaceCheck))
|
||||
construct<CharLength>(space >> digitString64 / spaceCheck))
|
||||
|
||||
// R724 char-literal-constant ->
|
||||
// [kind-param _] ' [rep-char]... ' |
|
||||
|
|
|
@ -735,13 +735,13 @@ struct KindParam {
|
|||
struct SignedIntLiteralConstant {
|
||||
TUPLE_CLASS_BOILERPLATE(SignedIntLiteralConstant);
|
||||
CharBlock source;
|
||||
std::tuple<std::int64_t, std::optional<KindParam>> t;
|
||||
std::tuple<CharBlock, std::optional<KindParam>> t;
|
||||
};
|
||||
|
||||
// R708 int-literal-constant -> digit-string [_ kind-param]
|
||||
struct IntLiteralConstant {
|
||||
TUPLE_CLASS_BOILERPLATE(IntLiteralConstant);
|
||||
std::tuple<std::uint64_t, std::optional<KindParam>> t;
|
||||
std::tuple<CharBlock, std::optional<KindParam>> t;
|
||||
};
|
||||
|
||||
// R712 sign -> + | -
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Fortran::parser {
|
|||
// end-of-statement markers.
|
||||
|
||||
// R611 label -> digit [digit]...
|
||||
constexpr auto label{space >> digitString / spaceCheck};
|
||||
constexpr auto label{space >> digitString64 / spaceCheck};
|
||||
|
||||
template<typename PA> inline constexpr auto unterminatedStatement(const PA &p) {
|
||||
return skipStuffBeforeStatement >>
|
||||
|
|
|
@ -377,6 +377,40 @@ struct BOZLiteral {
|
|||
// R711 digit-string -> digit [digit]...
|
||||
// N.B. not a token -- no space is skipped
|
||||
constexpr struct DigitString {
|
||||
using resultType = CharBlock;
|
||||
static std::optional<resultType> Parse(ParseState &state) {
|
||||
if (std::optional<const char *> ch1{state.PeekAtNextChar()}) {
|
||||
if (IsDecimalDigit(**ch1)) {
|
||||
state.UncheckedAdvance();
|
||||
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
||||
if (!IsDecimalDigit(**p)) {
|
||||
break;
|
||||
}
|
||||
state.UncheckedAdvance();
|
||||
}
|
||||
return CharBlock{*ch1, state.GetLocation()};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
} digitString;
|
||||
|
||||
struct SignedIntLiteralConstantWithoutKind {
|
||||
using resultType = CharBlock;
|
||||
static std::optional<resultType> Parse(ParseState &state) {
|
||||
resultType result{state.GetLocation()};
|
||||
static constexpr auto sign{maybe("+-"_ch / space)};
|
||||
if (sign.Parse(state).has_value()) {
|
||||
if (auto digits{digitString.Parse(state)}) {
|
||||
result.ExtendToCover(*digits);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr struct DigitString64 {
|
||||
using resultType = std::uint64_t;
|
||||
static std::optional<std::uint64_t> Parse(ParseState &state) {
|
||||
std::optional<const char *> firstDigit{digit.Parse(state)};
|
||||
|
@ -402,26 +436,7 @@ constexpr struct DigitString {
|
|||
}
|
||||
return {value};
|
||||
}
|
||||
} digitString;
|
||||
|
||||
constexpr struct SkipDigitString {
|
||||
using resultType = Success;
|
||||
static std::optional<Success> Parse(ParseState &state) {
|
||||
if (std::optional<const char *> ch1{state.PeekAtNextChar()}) {
|
||||
if (IsDecimalDigit(**ch1)) {
|
||||
state.UncheckedAdvance();
|
||||
while (std::optional<const char *> p{state.PeekAtNextChar()}) {
|
||||
if (!IsDecimalDigit(**p)) {
|
||||
break;
|
||||
}
|
||||
state.UncheckedAdvance();
|
||||
}
|
||||
return {Success{}};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
} skipDigitString;
|
||||
} digitString64;
|
||||
|
||||
// R707 signed-int-literal-constant -> [sign] int-literal-constant
|
||||
// N.B. Spaces are consumed before and after the sign, since the sign
|
||||
|
@ -444,22 +459,6 @@ static std::optional<std::int64_t> SignedInteger(
|
|||
return std::make_optional<std::int64_t>(negate ? -value : value);
|
||||
}
|
||||
|
||||
struct SignedIntLiteralConstantWithoutKind {
|
||||
using resultType = std::int64_t;
|
||||
static std::optional<std::int64_t> Parse(ParseState &state) {
|
||||
Location at{state.GetLocation()};
|
||||
static constexpr auto minus{attempt("-"_tok)};
|
||||
static constexpr auto plus{maybe("+"_tok)};
|
||||
bool negate{false};
|
||||
if (minus.Parse(state)) {
|
||||
negate = true;
|
||||
} else if (!plus.Parse(state).has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return SignedInteger(digitString.Parse(state), at, negate, state);
|
||||
}
|
||||
};
|
||||
|
||||
// R710 signed-digit-string -> [sign] digit-string
|
||||
// N.B. Not a complete token -- no space is skipped.
|
||||
// Used only in the exponent parts of real literal constants.
|
||||
|
@ -474,7 +473,7 @@ struct SignedDigitString {
|
|||
if (negate || **sign == '+') {
|
||||
state.UncheckedAdvance();
|
||||
}
|
||||
return SignedInteger(digitString.Parse(state), *sign, negate, state);
|
||||
return SignedInteger(digitString64.Parse(state), *sign, negate, state);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -146,11 +146,11 @@ public:
|
|||
x.u);
|
||||
}
|
||||
void Unparse(const SignedIntLiteralConstant &x) { // R707
|
||||
Walk(std::get<std::int64_t>(x.t));
|
||||
Put(std::get<CharBlock>(x.t).ToString());
|
||||
Walk("_", std::get<std::optional<KindParam>>(x.t));
|
||||
}
|
||||
void Unparse(const IntLiteralConstant &x) { // R708
|
||||
Walk(std::get<std::uint64_t>(x.t));
|
||||
Put(std::get<CharBlock>(x.t).ToString());
|
||||
Walk("_", std::get<std::optional<KindParam>>(x.t));
|
||||
}
|
||||
void Unparse(const Sign &x) { // R712
|
||||
|
|
|
@ -399,17 +399,38 @@ int ExpressionAnalyzer::AnalyzeKindParam(
|
|||
}
|
||||
|
||||
// Common handling of parser::IntLiteralConstant and SignedIntLiteralConstant
|
||||
struct IntTypeVisitor {
|
||||
using Result = MaybeExpr;
|
||||
using Types = IntegerTypes;
|
||||
template<typename T> Result Test() {
|
||||
if (T::kind == kind) {
|
||||
const char *p{digits.begin()};
|
||||
auto value{T::Scalar::Read(p, 10, true)};
|
||||
if (!value.overflow) {
|
||||
return Expr<SomeType>{
|
||||
Expr<SomeInteger>{Expr<T>{Constant<T>{std::move(value.value)}}}};
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
parser::CharBlock digits;
|
||||
int kind;
|
||||
};
|
||||
|
||||
template<typename PARSED>
|
||||
MaybeExpr ExpressionAnalyzer::IntLiteralConstant(const PARSED &x) {
|
||||
int kind{AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t),
|
||||
GetDefaultKind(TypeCategory::Integer))};
|
||||
auto value{std::get<0>(x.t)}; // std::(u)int64_t
|
||||
if (!CheckIntrinsicKind(TypeCategory::Integer, kind)) {
|
||||
return std::nullopt;
|
||||
if (CheckIntrinsicKind(TypeCategory::Integer, kind)) {
|
||||
auto digits{std::get<parser::CharBlock>(x.t)};
|
||||
if (MaybeExpr result{common::SearchTypes(IntTypeVisitor{digits, kind})}) {
|
||||
return result;
|
||||
} else {
|
||||
Say(digits, "Integer literal too large for INTEGER(KIND=%d)"_err_en_US,
|
||||
kind);
|
||||
}
|
||||
}
|
||||
return common::SearchTypes(
|
||||
TypeKindVisitor<TypeCategory::Integer, Constant, std::int64_t>{
|
||||
kind, static_cast<std::int64_t>(value)});
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExpressionAnalyzer::Analyze(const parser::IntLiteralConstant &x) {
|
||||
|
@ -587,7 +608,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
|
|||
default: CRASH_NO_CASE;
|
||||
}
|
||||
CHECK(*p == '"');
|
||||
auto value{BOZLiteralConstant::ReadUnsigned(++p, base)};
|
||||
++p;
|
||||
auto value{BOZLiteralConstant::Read(p, base, false /*unsigned*/)};
|
||||
if (*p != '"') {
|
||||
Say("invalid digit ('%c') in BOZ literal %s"_err_en_US, *p, x.v.data());
|
||||
return std::nullopt;
|
||||
|
|
|
@ -48,25 +48,25 @@ template<int BITS, typename INT = Integer<BITS>> void exhaustiveTesting() {
|
|||
char buffer[64];
|
||||
std::snprintf(buffer, sizeof buffer, " %llu", ullx);
|
||||
const char *p{buffer};
|
||||
auto readcheck{INT::ReadUnsigned(p)};
|
||||
auto readcheck{INT::Read(p)};
|
||||
TEST(!readcheck.overflow)("%s, x=0x%llx", desc, x);
|
||||
MATCH(x, readcheck.value.ToUInt64())("%s, x=0x%llx", desc, x);
|
||||
TEST(!*p)("%s, x=0x%llx", desc, x);
|
||||
std::snprintf(buffer, sizeof buffer, "%llx", ullx);
|
||||
p = buffer;
|
||||
readcheck = INT::ReadUnsigned(p, 16);
|
||||
readcheck = INT::Read(p, 16);
|
||||
TEST(!readcheck.overflow)("%s, x=0x%llx", desc, x);
|
||||
MATCH(x, readcheck.value.ToUInt64())("%s, x=0x%llx", desc, x);
|
||||
TEST(!*p)("%s, x=0x%llx", desc, x);
|
||||
std::string udec{a.UnsignedDecimal()};
|
||||
p = udec.data();
|
||||
readcheck = INT::ReadUnsigned(p);
|
||||
readcheck = INT::Read(p);
|
||||
TEST(!readcheck.overflow)("%s, x=0x%llx", desc, x);
|
||||
MATCH(x, readcheck.value.ToUInt64())("%s, x=0x%llx", desc, x);
|
||||
TEST(!*p)("%s, x=0x%llx", desc, x);
|
||||
std::string hex{a.Hexadecimal()};
|
||||
p = hex.data();
|
||||
readcheck = INT::ReadUnsigned(p, 16);
|
||||
readcheck = INT::Read(p, 16);
|
||||
TEST(!readcheck.overflow)("%s, x=0x%llx", desc, x);
|
||||
MATCH(x, readcheck.value.ToUInt64())("%s, x=0x%llx", desc, x);
|
||||
TEST(!*p)("%s, x=0x%llx", desc, x);
|
||||
|
|
|
@ -25,6 +25,7 @@ set(ERROR_TESTS
|
|||
implicit06.f90
|
||||
implicit07.f90
|
||||
implicit08.f90
|
||||
int-literals.f90
|
||||
kinds02.f90
|
||||
resolve01.f90
|
||||
resolve02.f90
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
!
|
||||
! Licensed under the Apache License, Version 2.0 (the "License");
|
||||
! you may not use this file except in compliance with the License.
|
||||
! You may obtain a copy of the License at
|
||||
!
|
||||
! http://www.apache.org/licenses/LICENSE-2.0
|
||||
!
|
||||
! Unless required by applicable law or agreed to in writing, software
|
||||
! distributed under the License is distributed on an "AS IS" BASIS,
|
||||
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
! See the License for the specific language governing permissions and
|
||||
! limitations under the License.
|
||||
|
||||
! Fortran syntax considers signed int literals in complex literals
|
||||
! to be a distinct production, not an application of unary +/- to
|
||||
! an unsigned int literal, so they're used here to test overflow
|
||||
! on signed int literal constants. The literals are tested here
|
||||
! as part of expressions that name resolution must analyze.
|
||||
|
||||
complex, parameter :: okj1 = 127_1, okz1 = (+127_1, -128_1)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=1)
|
||||
complex, parameter :: badj1 = 128_1
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=1)
|
||||
complex, parameter :: badz1 = (+128_1, 0)
|
||||
complex, parameter :: okj1a = 128_2
|
||||
complex, parameter :: okz1a = (+128_2, 0)
|
||||
|
||||
complex, parameter :: okj2 = 32767_2, okz2 = (+32767_2, -32768_2)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=2)
|
||||
complex, parameter :: badj2 = 32768_2
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=2)
|
||||
complex, parameter :: badz2 = (+32768_2, 0)
|
||||
complex, parameter :: okj2a = 32768_4
|
||||
complex, parameter :: okz2a = (+32768_4, 0)
|
||||
|
||||
complex, parameter :: okj4 = 2147483647_4, okz4 = (+2147483647_4, -2147483648_4)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=4)
|
||||
complex, parameter :: badj4 = 2147483648_4
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=4)
|
||||
complex, parameter :: badz4 = (+2147483648_4, 0)
|
||||
complex, parameter :: okj4a = 2147483648_8
|
||||
complex, parameter :: okz4a = (+2147483648_8, 0)
|
||||
|
||||
complex, parameter :: okj4d = 2147483647, okz4d = (+2147483647, -2147483648)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=4)
|
||||
complex, parameter :: badj4d = 2147483648
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=4)
|
||||
complex, parameter :: badz4d = (+2147483648, 0)
|
||||
|
||||
complex, parameter :: okj8 = 9223372036854775807_8, okz8 = (+9223372036854775807_8, -9223372036854775808_8)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=8)
|
||||
complex, parameter :: badj8 = 9223372036854775808_8
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=8)
|
||||
complex, parameter :: badz8 = (+9223372036854775808_8, 0)
|
||||
complex, parameter :: okj8a = 9223372036854775808_16
|
||||
complex, parameter :: okz8a = (+9223372036854775808_16, 0)
|
||||
|
||||
complex, parameter :: okj16 = 170141183460469231731687303715884105727_16
|
||||
complex, parameter :: okz16 = (+170141183460469231731687303715884105727_16, -170141183460469231731687303715884105728_16)
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=16)
|
||||
complex, parameter :: badj16 = 170141183460469231731687303715884105728_16
|
||||
!ERROR: Integer literal too large for INTEGER(KIND=16)
|
||||
complex, parameter :: badz16 = (+170141183460469231731687303715884105728_16, 0)
|
||||
|
||||
end
|
Loading…
Reference in New Issue