diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -2753,7 +2753,7 @@ // Applies any semantic checks peculiar to an intrinsic. // TODO: Move the rest of these checks to Semantics/check-call.cpp, which is -// where ASSOCIATED() is now validated. +// where ASSOCIATED() and TRANSFER() are now validated. static bool ApplySpecificChecks(SpecificCall &call, FoldingContext &context) { bool ok{true}; const std::string &name{call.specificIntrinsic.name}; @@ -2929,44 +2929,6 @@ } } } - } else if (name == "transfer") { // 16.9.193 - if (call.arguments.size() >= 2) { - auto source{characteristics::TypeAndShape::Characterize( - call.arguments[0], context)}; - auto mold{characteristics::TypeAndShape::Characterize( - call.arguments[1], context)}; - if (source && mold && mold->Rank() > 0 && - evaluate::ToInt64( - evaluate::Fold( - context, mold->MeasureElementSizeInBytes(context, false))) - .value_or(1) == 0) { - if (auto sourceSize{evaluate::ToInt64(evaluate::Fold( - context, source->MeasureSizeInBytes(context)))}) { - if (*sourceSize > 0) { - context.messages().Say( - "Element size of MOLD= array may not be zero when SOURCE= is not empty"_err_en_US); - ok = false; - } - } else { - context.messages().Say( - "Element size of MOLD= array may not be zero unless SOURCE= is empty"_warn_en_US); - } - } - if (call.arguments.size() > 2) { - if (const Symbol *whole{ - UnwrapWholeSymbolOrComponentDataRef(call.arguments[2])}) { - if (IsOptional(*whole)) { - context.messages().Say( - "SIZE= argument may not be the optional dummy argument '%s'"_err_en_US, - whole->name()); - ok = false; - } else if (IsAllocatableOrPointer(*whole)) { - context.messages().Say( - "SIZE= argument that is allocatable or pointer must be present at execution; parenthesize to silence this warning"_warn_en_US); - } - } - } - } } else if (name == "ucobound") { return CheckDimAgainstCorank(call, context); } diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp --- a/flang/lib/Semantics/check-call.cpp +++ b/flang/lib/Semantics/check-call.cpp @@ -1083,11 +1083,73 @@ } } +// TRANSFER (16.9.193) +static void CheckTransferOperandType(parser::ContextualMessages &messages, + const evaluate::DynamicType &type, const char *which) { + if (type.IsPolymorphic()) { + messages.Say("%s of TRANSFER is polymorphic"_warn_en_US, which); + } else if (!type.IsUnlimitedPolymorphic() && + type.category() == TypeCategory::Derived) { + DirectComponentIterator directs{type.GetDerivedTypeSpec()}; + if (auto bad{std::find_if(directs.begin(), directs.end(), IsDescriptor)}; + bad != directs.end()) { + evaluate::SayWithDeclaration(messages, *bad, + "%s of TRANSFER contains allocatable or pointer component %s"_warn_en_US, + which, bad.BuildResultDesignatorName()); + } + } +} + +static void CheckTransfer(evaluate::ActualArguments &arguments, + evaluate::FoldingContext &context, const Scope *scope) { + if (arguments.size() >= 2) { + if (auto source{characteristics::TypeAndShape::Characterize( + arguments[0], context)}) { + CheckTransferOperandType(context.messages(), source->type(), "Source"); + if (auto mold{characteristics::TypeAndShape::Characterize( + arguments[1], context)}) { + CheckTransferOperandType(context.messages(), mold->type(), "Mold"); + if (mold->Rank() > 0 && + evaluate::ToInt64( + evaluate::Fold( + context, mold->MeasureElementSizeInBytes(context, false))) + .value_or(1) == 0) { + if (auto sourceSize{evaluate::ToInt64(evaluate::Fold( + context, source->MeasureSizeInBytes(context)))}) { + if (*sourceSize > 0) { + context.messages().Say( + "Element size of MOLD= array may not be zero when SOURCE= is not empty"_err_en_US); + } + } else { + context.messages().Say( + "Element size of MOLD= array may not be zero unless SOURCE= is empty"_warn_en_US); + } + } + } + } + if (arguments.size() > 2) { // SIZE= + if (const Symbol * + whole{UnwrapWholeSymbolOrComponentDataRef(arguments[2])}) { + if (IsOptional(*whole)) { + context.messages().Say( + "SIZE= argument may not be the optional dummy argument '%s'"_err_en_US, + whole->name()); + } else if (IsAllocatableOrPointer(*whole)) { + context.messages().Say( + "SIZE= argument that is allocatable or pointer must be present at execution; parenthesize to silence this warning"_warn_en_US); + } + } + } + } +} + static void CheckSpecificIntrinsic(evaluate::ActualArguments &arguments, evaluate::FoldingContext &context, const Scope *scope, const evaluate::SpecificIntrinsic &intrinsic) { if (intrinsic.name == "associated") { CheckAssociated(arguments, context, scope); + } else if (intrinsic.name == "transfer") { + CheckTransfer(arguments, context, scope); } } diff --git a/flang/test/Semantics/transfer01.f90 b/flang/test/Semantics/transfer01.f90 --- a/flang/test/Semantics/transfer01.f90 +++ b/flang/test/Semantics/transfer01.f90 @@ -6,6 +6,10 @@ type empty end type type(empty) :: empty1(1) + type hasdescriptor + real, allocatable :: allocatable + end type + type(hasdescriptor) hasDesc real :: empty2(0) character(0) :: empty3(1) integer, pointer :: source(:) @@ -27,5 +31,6 @@ print *, transfer(1., empty2, size=ia) !WARNING: SIZE= argument that is allocatable or pointer must be present at execution; parenthesize to silence this warning print *, transfer(1., empty2, size=ip) + !WARNING: Source of TRANSFER contains allocatable or pointer component %allocatable + print *, transfer(hasDesc, 1) end -