Index: flang/lib/Evaluate/characteristics.cpp =================================================================== --- flang/lib/Evaluate/characteristics.cpp +++ flang/lib/Evaluate/characteristics.cpp @@ -275,7 +275,7 @@ } return false; } - if (!type.type().IsTkCompatibleWith(actual.type.type())) { + if (!type.type().IsTkLenCompatibleWith(actual.type.type())) { if (whyNot) { *whyNot = "incompatible dummy data object types: "s + type.type().AsFortran() + " vs " + actual.type.type().AsFortran(); Index: flang/lib/Semantics/check-call.cpp =================================================================== --- flang/lib/Semantics/check-call.cpp +++ flang/lib/Semantics/check-call.cpp @@ -98,34 +98,48 @@ } } -// When a scalar CHARACTER actual argument is known to be short, -// we extend it on the right with spaces and a warning if it is an -// expression, and emit an error if it is a variable. +// When a CHARACTER actual argument is known to be short, +// we extend it on the right with spaces and a warning if +// possible. When it is long, and not required to be equal, +// the usage conforms to the standard and no warning is needed. static void CheckCharacterActual(evaluate::Expr &actual, - const characteristics::TypeAndShape &dummyType, + const characteristics::DummyDataObject &dummy, characteristics::TypeAndShape &actualType, evaluate::FoldingContext &context, parser::ContextualMessages &messages) { - if (dummyType.type().category() == TypeCategory::Character && + if (dummy.type.type().category() == TypeCategory::Character && actualType.type().category() == TypeCategory::Character && - dummyType.type().kind() == actualType.type().kind() && - GetRank(actualType.shape()) == 0) { - if (dummyType.LEN() && actualType.LEN()) { - auto dummyLength{ToInt64(Fold(context, common::Clone(*dummyType.LEN())))}; + dummy.type.type().kind() == actualType.type().kind()) { + if (dummy.type.LEN() && actualType.LEN()) { + auto dummyLength{ + ToInt64(Fold(context, common::Clone(*dummy.type.LEN())))}; auto actualLength{ ToInt64(Fold(context, common::Clone(*actualType.LEN())))}; - if (dummyLength && actualLength && *actualLength < *dummyLength) { - if (evaluate::IsVariable(actual)) { - messages.Say( - "Actual argument variable length '%jd' is less than expected length '%jd'"_err_en_US, - *actualLength, *dummyLength); - } else { + if (dummyLength && actualLength && *actualLength != *dummyLength) { + if (dummy.attrs.test( + characteristics::DummyDataObject::Attr::Allocatable) || + dummy.attrs.test(characteristics::DummyDataObject::Attr::Pointer) || + dummy.type.attrs().test( + characteristics::TypeAndShape::Attr::AssumedRank) || + dummy.type.attrs().test( + characteristics::TypeAndShape::Attr::AssumedShape)) { + // See 15.5.2.4 paragraph 4., 15.5.2.5. messages.Say( - "Actual argument expression length '%jd' is less than expected length '%jd'"_warn_en_US, + "Actual argument variable length '%jd' does not match the expected length '%jd'"_err_en_US, *actualLength, *dummyLength); - auto converted{ConvertToType(dummyType.type(), std::move(actual))}; - CHECK(converted); - actual = std::move(*converted); - actualType.set_LEN(SubscriptIntExpr{*dummyLength}); + } else if (*actualLength < *dummyLength) { + if (evaluate::IsVariable(actual)) { + messages.Say( + "Actual argument variable length '%jd' is less than expected length '%jd'"_warn_en_US, + *actualLength, *dummyLength); + } else { + messages.Say( + "Actual argument expression length '%jd' is less than expected length '%jd'"_warn_en_US, + *actualLength, *dummyLength); + auto converted{ConvertToType(dummy.type.type(), std::move(actual))}; + CHECK(converted); + actual = std::move(*converted); + actualType.set_LEN(SubscriptIntExpr{*dummyLength}); + } } } } @@ -180,7 +194,7 @@ // Basic type & rank checking parser::ContextualMessages &messages{context.messages()}; - CheckCharacterActual(actual, dummy.type, actualType, context, messages); + CheckCharacterActual(actual, dummy, actualType, context, messages); if (allowActualArgumentConversions) { ConvertIntegerActual(actual, dummy.type, actualType, messages); } @@ -1154,7 +1168,7 @@ auto buffer{CheckExplicitInterface( proc, actuals, context, &scope, intrinsic, true)}; if (!buffer.empty()) { - if (treatingExternalAsImplicit && !buffer.empty()) { + if (treatingExternalAsImplicit) { if (auto *msg{messages.Say( "If the procedure's interface were explicit, this reference would be in error"_warn_en_US)}) { buffer.AttachTo(*msg, parser::Severity::Because); Index: flang/test/Lower/character-substrings.f90 =================================================================== --- flang/test/Lower/character-substrings.f90 +++ flang/test/Lower/character-substrings.f90 @@ -40,7 +40,7 @@ ! CHECK: %[[VAL_8:.*]] = arith.subi %[[VAL_7]], %[[VAL_4]] : index ! CHECK: %[[VAL_9:.*]] = fir.shape %[[VAL_3]] : (index) -> !fir.shape<1> ! CHECK: %[[VAL_10:.*]] = arith.constant 5 : i64 -! CHECK: %[[VAL_11:.*]] = arith.constant 7 : i64 +! CHECK: %[[VAL_11:.*]] = arith.constant 5 : i64 ! CHECK: %[[VAL_12:.*]] = arith.constant 1 : i64 ! CHECK: %[[VAL_13:.*]] = arith.subi %[[VAL_10]], %[[VAL_12]] : i64 ! CHECK: %[[VAL_14:.*]] = arith.constant 0 : i64 @@ -63,7 +63,7 @@ character(7) arr(4) - call s(arr(:)(5:7)) + call s(arr(:)(5:5)) end subroutine array_substring_embox ! CHECK-LABEL: func @_QPsubstring_assignment( Index: flang/test/Semantics/call33.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/call33.f90 @@ -0,0 +1,54 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -Werror +module m + contains + subroutine s1(x) + character(3) :: x + end + subroutine s2(x) + character(3) :: x(1) + end + subroutine s3(x) + character(3) :: x(:) + end + subroutine s4(x) + character(3) :: x(..) + end + subroutine s5(x) + character(3), allocatable :: x + end + subroutine s6(x) + character(3), pointer :: x + end +end + +program test + use m + character(2) short, shortarr(1) + character(2), allocatable :: shortalloc + character(2), pointer :: shortptr + character(4) long, longarr(1) + character(4), allocatable :: longalloc + character(4), pointer :: longptr + !WARNING: Actual argument variable length '2' is less than expected length '3' + call s1(short) + !WARNING: Actual argument variable length '2' is less than expected length '3' + call s2(shortarr) + !ERROR: Actual argument variable length '2' does not match the expected length '3' + call s3(shortarr) + !ERROR: Actual argument variable length '2' does not match the expected length '3' + call s4(shortarr) + !ERROR: Actual argument variable length '2' does not match the expected length '3' + call s5(shortalloc) + !ERROR: Actual argument variable length '2' does not match the expected length '3' + call s6(shortptr) + call s1(long) ! ok + call s2(longarr) ! ok + !ERROR: Actual argument variable length '4' does not match the expected length '3' + call s3(longarr) + !ERROR: Actual argument variable length '4' does not match the expected length '3' + call s4(longarr) + !ERROR: Actual argument variable length '4' does not match the expected length '3' + call s5(longalloc) + !ERROR: Actual argument variable length '4' does not match the expected length '3' + call s6(longptr) +end