[flang] Be more precise about CHARACTER known length discrepancies

Many intrinsic functions in Fortran require that two or more of their
arguments have types that agree in the values of all of their type
parameters, while others only require the same type category and kind
type parameters but not lengths, including the important case of
CHARACTER.  The intrinsic procedure tables need to be adjusted in
some cases so that discrepancies in character lengths that are known
at compilation time can be diagnosed as errors where they should be,
as in for example MOVE_ALLOC().

Differential Revision: https://reviews.llvm.org/D137032
This commit is contained in:
Peter Klausler 2022-10-13 15:22:55 -07:00
parent 93798fb740
commit d9232e394e
5 changed files with 64 additions and 35 deletions

View File

@ -186,9 +186,14 @@ public:
// 7.3.2.3 & 15.5.2.4 type compatibility.
// x.IsTkCompatibleWith(y) is true if "x => y" or passing actual y to
// dummy argument x would be valid. Be advised, this is not a reflexive
// relation. Kind type parameters must match.
// relation. Kind type parameters must match, but CHARACTER lengths
// need not do so.
bool IsTkCompatibleWith(const DynamicType &) const;
// A stronger compatibility check that does not allow distinct known
// values for CHARACTER lengths for e.g. MOVE_ALLOC().
bool IsTkLenCompatibleWith(const DynamicType &) const;
// EXTENDS_TYPE_OF (16.9.76); ignores type parameter values
std::optional<bool> ExtendsTypeOf(const DynamicType &) const;
// SAME_TYPE_AS (16.9.165); ignores type parameter values

View File

@ -82,8 +82,8 @@ ENUM_CLASS(KindCode, none, defaultIntegerKind,
// match any kind, but all "same" kinds must be equal. For characters, also
// implies that lengths must be equal.
same,
// for character results, take "same" argument kind but not length
sameKindButNotLength,
// for characters that only require the same kind, not length
sameKind,
operand, // match any kind, with promotion (non-standard)
typeless, // BOZ literals are INTEGER with this kind
teamType, // TEAM_TYPE from module ISO_FORTRAN_ENV (for coarrays)
@ -157,8 +157,7 @@ static constexpr TypePattern SameComplex{ComplexType, KindCode::same};
static constexpr TypePattern SameFloating{FloatingType, KindCode::same};
static constexpr TypePattern SameNumeric{NumericType, KindCode::same};
static constexpr TypePattern SameChar{CharType, KindCode::same};
static constexpr TypePattern SameCharNewLen{
CharType, KindCode::sameKindButNotLength};
static constexpr TypePattern SameCharNoLen{CharType, KindCode::sameKind};
static constexpr TypePattern SameLogical{LogicalType, KindCode::same};
static constexpr TypePattern SameRelatable{RelatableType, KindCode::same};
static constexpr TypePattern SameIntrinsic{IntrinsicType, KindCode::same};
@ -471,13 +470,15 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", SameChar, Rank::array}, {"value", SameChar, Rank::scalar},
RequiredDIM, OptionalMASK, SizeDefaultKIND,
{{"array", SameCharNoLen, Rank::array},
{"value", SameCharNoLen, Rank::scalar}, RequiredDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::locReduced, IntrinsicClass::transformationalFunction},
{"findloc",
{{"array", SameChar, Rank::array}, {"value", SameChar, Rank::scalar},
MissingDIM, OptionalMASK, SizeDefaultKIND,
{{"array", SameCharNoLen, Rank::array},
{"value", SameCharNoLen, Rank::scalar}, MissingDIM, OptionalMASK,
SizeDefaultKIND,
{"back", AnyLogical, Rank::scalar, Optionality::optional}},
KINDInt, Rank::vector, IntrinsicClass::transformationalFunction},
{"findloc",
@ -525,7 +526,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"ieor", {{"i", BOZ}, {"j", SameInt}}, SameInt},
{"image_status", {{"image", SameInt}, OptionalTEAM}, DefaultInt},
{"index",
{{"string", SameChar}, {"substring", SameChar},
{{"string", SameCharNoLen}, {"substring", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
@ -565,10 +566,14 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
DefaultingKIND},
KINDInt, Rank::scalar, IntrinsicClass::inquiryFunction},
{"len_trim", {{"string", AnyChar}, DefaultingKIND}, KINDInt},
{"lge", {{"string_a", SameChar}, {"string_b", SameChar}}, DefaultLogical},
{"lgt", {{"string_a", SameChar}, {"string_b", SameChar}}, DefaultLogical},
{"lle", {{"string_a", SameChar}, {"string_b", SameChar}}, DefaultLogical},
{"llt", {{"string_a", SameChar}, {"string_b", SameChar}}, DefaultLogical},
{"lge", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"lgt", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"lle", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"llt", {{"string_a", SameCharNoLen}, {"string_b", SameCharNoLen}},
DefaultLogical},
{"loc", {{"loc_argument", Addressable, Rank::anyOrAssumedRank}},
SubscriptInt, Rank::scalar},
{"log", {{"x", SameFloating}}, SameFloating},
@ -606,9 +611,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"a3", OperandIntOrReal, Rank::elemental, Optionality::repeats}},
OperandIntOrReal},
{"max",
{{"a1", SameChar}, {"a2", SameChar},
{"a3", SameChar, Rank::elemental, Optionality::repeats}},
SameChar},
{{"a1", SameCharNoLen}, {"a2", SameCharNoLen},
{"a3", SameCharNoLen, Rank::elemental, Optionality::repeats}},
SameCharNoLen},
{"maxexponent",
{{"x", AnyReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeNull}}},
@ -645,9 +650,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"a3", OperandIntOrReal, Rank::elemental, Optionality::repeats}},
OperandIntOrReal},
{"min",
{{"a1", SameChar}, {"a2", SameChar},
{"a3", SameChar, Rank::elemental, Optionality::repeats}},
SameChar},
{{"a1", SameCharNoLen}, {"a2", SameCharNoLen},
{"a3", SameCharNoLen, Rank::elemental, Optionality::repeats}},
SameCharNoLen},
{"minexponent",
{{"x", AnyReal, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeNull}}},
@ -675,9 +680,9 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
OperandIntOrReal},
{"nearest", {{"x", SameReal}, {"s", AnyReal}}, SameReal},
{"new_line",
{{"a", SameChar, Rank::anyOrAssumedRank, Optionality::required,
{{"a", SameCharNoLen, Rank::anyOrAssumedRank, Optionality::required,
common::Intent::In, {ArgFlag::canBeNull}}},
SameChar, Rank::scalar, IntrinsicClass::inquiryFunction},
SameCharNoLen, Rank::scalar, IntrinsicClass::inquiryFunction},
{"nint", {{"a", AnyReal}, DefaultingKIND}, KINDInt},
{"norm2", {{"x", SameReal, Rank::array}, OptionalDIM}, SameReal,
Rank::dimReduced, IntrinsicClass::transformationalFunction},
@ -748,8 +753,8 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"identity", SameType, Rank::scalar, Optionality::optional},
{"ordered", AnyLogical, Rank::scalar, Optionality::optional}},
SameType, Rank::scalar, IntrinsicClass::transformationalFunction},
{"repeat", {{"string", SameChar, Rank::scalar}, {"ncopies", AnyInt}},
SameCharNewLen, Rank::scalar, IntrinsicClass::transformationalFunction},
{"repeat", {{"string", SameCharNoLen, Rank::scalar}, {"ncopies", AnyInt}},
SameCharNoLen, Rank::scalar, IntrinsicClass::transformationalFunction},
{"reshape",
{{"source", SameType, Rank::array}, {"shape", AnyInt, Rank::shape},
{"pad", SameType, Rank::array, Optionality::optional},
@ -762,7 +767,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
DefaultLogical, Rank::scalar, IntrinsicClass::inquiryFunction},
{"scale", {{"x", SameReal}, {"i", AnyInt}}, SameReal}, // == IEEE_SCALB()
{"scan",
{{"string", SameChar}, {"set", SameChar},
{{"string", SameCharNoLen}, {"set", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
@ -851,8 +856,8 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
SameType, Rank::vector, IntrinsicClass::transformationalFunction},
{"transpose", {{"matrix", SameType, Rank::matrix}}, SameType, Rank::matrix,
IntrinsicClass::transformationalFunction},
{"trim", {{"string", SameChar, Rank::scalar}}, SameCharNewLen, Rank::scalar,
IntrinsicClass::transformationalFunction},
{"trim", {{"string", SameCharNoLen, Rank::scalar}}, SameCharNoLen,
Rank::scalar, IntrinsicClass::transformationalFunction},
{"ubound",
{{"array", AnyData, Rank::anyOrAssumedRank}, RequiredDIM,
SizeDefaultKIND},
@ -867,7 +872,7 @@ static const IntrinsicInterface genericIntrinsicFunction[]{
{"field", SameType, Rank::conformable}},
SameType, Rank::conformable, IntrinsicClass::transformationalFunction},
{"verify",
{{"string", SameChar}, {"set", SameChar},
{{"string", SameCharNoLen}, {"set", SameCharNoLen},
{"back", AnyLogical, Rank::elemental, Optionality::optional},
DefaultingKIND},
KINDInt},
@ -1687,6 +1692,12 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
argOk = true;
break;
case KindCode::same:
if (!sameArg) {
sameArg = arg;
}
argOk = type->IsTkLenCompatibleWith(sameArg->GetType().value());
break;
case KindCode::sameKind:
if (!sameArg) {
sameArg = arg;
}
@ -1958,7 +1969,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
}
}
break;
case KindCode::sameKindButNotLength:
case KindCode::sameKind:
CHECK(sameArg);
if (std::optional<DynamicType> aType{sameArg->GetType()}) {
resultType = DynamicType{*category, aType->kind()};
@ -2868,7 +2879,7 @@ static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) {
context.messages().Say(at,
"OPERATION= argument of REDUCE() must be a scalar function"_err_en_US);
} else if (result->type().IsPolymorphic() ||
!arrayType->IsTkCompatibleWith(result->type())) {
!arrayType->IsTkLenCompatibleWith(result->type())) {
ok = false;
context.messages().Say(at,
"OPERATION= argument of REDUCE() must have the same type as ARRAY="_err_en_US);

View File

@ -318,13 +318,18 @@ static bool AreCompatibleDerivedTypes(const semantics::DerivedTypeSpec *x,
}
static bool AreCompatibleTypes(const DynamicType &x, const DynamicType &y,
bool ignoreTypeParameterValues) {
bool ignoreTypeParameterValues, bool ignoreLengths) {
if (x.IsUnlimitedPolymorphic()) {
return true;
} else if (y.IsUnlimitedPolymorphic()) {
return false;
} else if (x.category() != y.category()) {
return false;
} else if (x.category() == TypeCategory::Character) {
const auto xLen{x.knownLength()};
const auto yLen{y.knownLength()};
return x.kind() == y.kind() &&
(ignoreLengths || !xLen || !yLen || *xLen == *yLen);
} else if (x.category() != TypeCategory::Derived) {
return x.kind() == y.kind();
} else {
@ -338,13 +343,17 @@ static bool AreCompatibleTypes(const DynamicType &x, const DynamicType &y,
// See 7.3.2.3 (5) & 15.5.2.4
bool DynamicType::IsTkCompatibleWith(const DynamicType &that) const {
return AreCompatibleTypes(*this, that, false);
return AreCompatibleTypes(*this, that, false, true);
}
bool DynamicType::IsTkLenCompatibleWith(const DynamicType &that) const {
return AreCompatibleTypes(*this, that, false, false);
}
// 16.9.165
std::optional<bool> DynamicType::SameTypeAs(const DynamicType &that) const {
bool x{AreCompatibleTypes(*this, that, true)};
bool y{AreCompatibleTypes(that, *this, true)};
bool x{AreCompatibleTypes(*this, that, true, true)};
bool y{AreCompatibleTypes(that, *this, true, true)};
if (x == y) {
return x;
} else {

View File

@ -7,7 +7,7 @@ module m
logical, parameter :: test_eoshift_1 = all(eoshift([1, 2, 3], 1) == [2, 3, 0])
logical, parameter :: test_eoshift_2 = all(eoshift([1, 2, 3], -1) == [0, 1, 2])
logical, parameter :: test_eoshift_3 = all(eoshift([1., 2., 3.], 1) == [2., 3., 0.])
logical, parameter :: test_eoshift_4 = all(eoshift(['ab', 'cd', 'ef'], -1, 'x') == ['x ', 'ab', 'cd'])
logical, parameter :: test_eoshift_4 = all(eoshift(['ab', 'cd', 'ef'], -1, 'x ') == ['x ', 'ab', 'cd'])
logical, parameter :: test_eoshift_5 = all([eoshift(arr, 1, dim=1)] == [2, 0, 4, 0, 6, 0])
logical, parameter :: test_eoshift_6 = all([eoshift(arr, 1, dim=2)] == [3, 4, 5, 6, 0, 0])
logical, parameter :: test_eoshift_7 = all([eoshift(arr, [1, -1, 0])] == [2, 0, 0, 3, 5, 6])

View File

@ -11,6 +11,7 @@ program main
end type
class(t), allocatable :: t1
type(t), allocatable :: t2
character, allocatable :: ca*2, cb*3
! standards conforming
allocate(a(3)[*])
@ -63,4 +64,7 @@ program main
call move_alloc(t1, t2)
call move_alloc(t2, t1) ! ok
!ERROR: Actual argument for 'to=' has bad type or kind 'CHARACTER(KIND=1,LEN=3_8)'
call move_alloc(ca, cb)
end program main