diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h --- a/flang/include/flang/Evaluate/type.h +++ b/flang/include/flang/Evaluate/type.h @@ -22,6 +22,7 @@ #include "integer.h" #include "logical.h" #include "real.h" +#include "flang/Common/Fortran-features.h" #include "flang/Common/Fortran.h" #include "flang/Common/idioms.h" #include "flang/Common/real.h" @@ -472,8 +473,10 @@ std::optional ComparisonType( const DynamicType &, const DynamicType &); -bool IsInteroperableIntrinsicType( - const DynamicType &, bool checkCharLength = true); +bool IsInteroperableIntrinsicType(const DynamicType &, + const common::LanguageFeatureControl * = nullptr, + bool checkCharLength = true); +bool IsCUDAIntrinsicType(const DynamicType &); // Determine whether two derived type specs are sufficiently identical // to be considered the "same" type even if declared separately. diff --git a/flang/include/flang/Semantics/type.h b/flang/include/flang/Semantics/type.h --- a/flang/include/flang/Semantics/type.h +++ b/flang/include/flang/Semantics/type.h @@ -456,7 +456,8 @@ return const_cast(this)->AsDerived(); } -bool IsInteroperableIntrinsicType(const DeclTypeSpec &); +bool IsInteroperableIntrinsicType( + const DeclTypeSpec &, const common::LanguageFeatureControl &); } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_TYPE_H_ diff --git a/flang/lib/Evaluate/type.cpp b/flang/lib/Evaluate/type.cpp --- a/flang/lib/Evaluate/type.cpp +++ b/flang/lib/Evaluate/type.cpp @@ -747,14 +747,15 @@ } } -bool IsInteroperableIntrinsicType( - const DynamicType &type, bool checkCharLength) { +bool IsInteroperableIntrinsicType(const DynamicType &type, + const common::LanguageFeatureControl *features, bool checkCharLength) { switch (type.category()) { case TypeCategory::Integer: return true; case TypeCategory::Real: case TypeCategory::Complex: - return type.kind() >= 4; // no short or half floats + return (features && features->IsEnabled(common::LanguageFeature::CUDA)) || + type.kind() >= 4; // no short or half floats case TypeCategory::Logical: return type.kind() == 1; // C_BOOL case TypeCategory::Character: @@ -768,4 +769,21 @@ } } +bool IsCUDAIntrinsicType(const DynamicType &type) { + switch (type.category()) { + case TypeCategory::Integer: + case TypeCategory::Logical: + return type.kind() <= 8; + case TypeCategory::Real: + return type.kind() >= 2 && type.kind() <= 8; + case TypeCategory::Complex: + return type.kind() == 2 || type.kind() == 4 || type.kind() == 8; + case TypeCategory::Character: + return type.kind() == 1; + default: + // Derived types are tested in Semantics/check-declarations.cpp + return false; + } +} + } // namespace Fortran::evaluate diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -115,6 +115,19 @@ } return msg; } + template parser::Message *WarnIfNotInModuleFile(A &&...x) { + if (FindModuleFileContaining(context_.FindScope(messages_.at()))) { + return nullptr; + } + return messages_.Say(std::forward(x)...); + } + template + parser::Message *WarnIfNotInModuleFile(parser::CharBlock source, A &&...x) { + if (FindModuleFileContaining(context_.FindScope(source))) { + return nullptr; + } + return messages_.Say(source, std::forward(x)...); + } bool IsResultOkToDiffer(const FunctionResult &); void CheckGlobalName(const Symbol &); void CheckExplicitSave(const Symbol &); @@ -217,9 +230,8 @@ void CheckHelper::Check(const Symbol &symbol) { if (symbol.name().size() > common::maxNameLen && - &symbol == &symbol.GetUltimate() && - !FindModuleFileContaining(symbol.owner())) { - messages_.Say(symbol.name(), + &symbol == &symbol.GetUltimate()) { + WarnIfNotInModuleFile(symbol.name(), "%s has length %d, which is greater than the maximum name length " "%d"_port_en_US, symbol.name(), symbol.name().size(), common::maxNameLen); @@ -606,6 +618,7 @@ WarnMissingFinal(symbol); const DeclTypeSpec *type{details.type()}; const DerivedTypeSpec *derived{type ? type->AsDerived() : nullptr}; + bool isComponent{symbol.owner().IsDerivedType()}; if (!details.coshape().empty()) { bool isDeferredCoshape{details.coshape().CanBeDeferredShape()}; if (IsAllocatable(symbol)) { @@ -614,7 +627,7 @@ " coshape"_err_en_US, symbol.name()); } - } else if (symbol.owner().IsDerivedType()) { // C746 + } else if (isComponent) { // C746 std::string deferredMsg{ isDeferredCoshape ? "" : " and have a deferred coshape"}; messages_.Say("Component '%s' is a coarray and must have the ALLOCATABLE" @@ -718,7 +731,7 @@ if (IsPassedViaDescriptor(symbol)) { if (IsAllocatableOrPointer(symbol)) { if (inExplicitInterface) { - messages_.Say( + WarnIfNotInModuleFile( "!DIR$ IGNORE_TKR should not apply to an allocatable or pointer"_warn_en_US); } else { messages_.Say( @@ -726,10 +739,10 @@ } } else if (ignoreTKR.test(common::IgnoreTKR::Rank)) { if (ignoreTKR.count() == 1 && evaluate::IsAssumedRank(symbol)) { - messages_.Say( + WarnIfNotInModuleFile( "!DIR$ IGNORE_TKR(R) is not meaningful for an assumed-rank array"_warn_en_US); } else if (inExplicitInterface) { - messages_.Say( + WarnIfNotInModuleFile( "!DIR$ IGNORE_TKR(R) should not apply to a dummy argument passed via descriptor"_warn_en_US); } else { messages_.Say( @@ -797,9 +810,8 @@ messages_.Say("A dummy argument must not be initialized"_err_en_US); } else if (IsFunctionResult(symbol)) { messages_.Say("A function result must not be initialized"_err_en_US); - } else if (IsInBlankCommon(symbol) && - !FindModuleFileContaining(symbol.owner())) { - messages_.Say( + } else if (IsInBlankCommon(symbol)) { + WarnIfNotInModuleFile( "A variable in blank COMMON should not be initialized"_port_en_US); } } @@ -839,6 +851,156 @@ "'%s' is a data object and may not be EXTERNAL"_err_en_US, symbol.name()); } + + // Check CUDA attributes and special circumstances of being in device + // subprograms + const Scope &progUnit{GetProgramUnitContaining(symbol)}; + const auto *subpDetails{!isComponent && progUnit.symbol() + ? progUnit.symbol()->detailsIf() + : nullptr}; + bool inDeviceSubprogram{IsCUDADeviceContext(&symbol.owner())}; + if (inDeviceSubprogram) { + if (IsSaved(symbol)) { + WarnIfNotInModuleFile( + "'%s' should not have the SAVE attribute or initialization in a device subprogram"_warn_en_US, + symbol.name()); + } + if (IsPointer(symbol)) { + WarnIfNotInModuleFile( + "Pointer '%s' may not be associated in a device subprogram"_warn_en_US, + symbol.name()); + } + if (details.isDummy() && + details.cudaDataAttr().value_or(common::CUDADataAttr::Device) != + common::CUDADataAttr::Device && + details.cudaDataAttr().value_or(common::CUDADataAttr::Device) != + common::CUDADataAttr::Managed) { + WarnIfNotInModuleFile( + "Dummy argument '%s' may not have ATTRIBUTES(%s) in a device subprogram"_warn_en_US, + symbol.name(), + parser::ToUpperCaseLetters( + common::EnumToString(*details.cudaDataAttr()))); + } + } + if (details.cudaDataAttr()) { + if (auto dyType{evaluate::DynamicType::From(symbol)}) { + if (dyType->category() != TypeCategory::Derived) { + if (!IsCUDAIntrinsicType(*dyType)) { + messages_.Say( + "'%s' has intrinsic type '%s' that is not available on the device"_err_en_US, + symbol.name(), dyType->AsFortran()); + } + } + } + auto attr{*details.cudaDataAttr()}; + switch (attr) { + case common::CUDADataAttr::Constant: + if (IsAllocatableOrPointer(symbol) || symbol.attrs().test(Attr::TARGET)) { + messages_.Say( + "Object '%s' with ATTRIBUTES(CONSTANT) may not be allocatable, pointer, or target"_err_en_US, + symbol.name()); + } else if (auto shape{evaluate::GetShape(foldingContext_, symbol)}; + !shape || + !evaluate::AsConstantExtents(foldingContext_, *shape)) { + messages_.Say( + "Object '%s' with ATTRIBUTES(CONSTANT) must have constant array bounds"_err_en_US, + symbol.name()); + } + break; + case common::CUDADataAttr::Device: + if (isComponent && !IsAllocatable(symbol)) { + messages_.Say( + "Component '%s' with ATTRIBUTES(DEVICE) must also be allocatable"_err_en_US, + symbol.name()); + } + break; + case common::CUDADataAttr::Managed: + if (!IsAutomatic(symbol) && !IsAllocatable(symbol) && + !details.isDummy()) { + messages_.Say( + "Object '%s' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, or a dummy argument"_err_en_US, + symbol.name()); + } + break; + case common::CUDADataAttr::Pinned: + if (inDeviceSubprogram) { + WarnIfNotInModuleFile( + "Object '%s' with ATTRIBUTES(PINNED) may not be declared in a device subprogram"_warn_en_US, + symbol.name()); + } else if (IsPointer(symbol)) { + WarnIfNotInModuleFile( + "Object '%s' with ATTRIBUTES(PINNED) may not be a pointer"_warn_en_US, + symbol.name()); + } else if (!IsAllocatable(symbol)) { + WarnIfNotInModuleFile( + "Object '%s' with ATTRIBUTES(PINNED) should also be allocatable"_warn_en_US, + symbol.name()); + } + break; + case common::CUDADataAttr::Shared: + if (IsAllocatableOrPointer(symbol) || symbol.attrs().test(Attr::TARGET)) { + messages_.Say( + "Object '%s' with ATTRIBUTES(SHARED) may not be allocatable, pointer, or target"_err_en_US, + symbol.name()); + } else if (!inDeviceSubprogram) { + messages_.Say( + "Object '%s' with ATTRIBUTES(SHARED) must be declared in a device subprogram"_err_en_US, + symbol.name()); + } + break; + case common::CUDADataAttr::Texture: + messages_.Say( + "ATTRIBUTES(TEXTURE) is obsolete and no longer supported"_err_en_US); + break; + } + if (attr != common::CUDADataAttr::Pinned) { + if (details.commonBlock()) { + messages_.Say( + "Object '%s' with ATTRIBUTES(%s) may not be in COMMON"_err_en_US, + symbol.name(), + parser::ToUpperCaseLetters(common::EnumToString(attr))); + } else if (FindEquivalenceSet(symbol)) { + messages_.Say( + "Object '%s' with ATTRIBUTES(%s) may not be in an equivalence group"_err_en_US, + symbol.name(), + parser::ToUpperCaseLetters(common::EnumToString(attr))); + } + } + if (subpDetails /* not a module variable */ && IsSaved(symbol) && + !inDeviceSubprogram && !IsAllocatable(symbol) && + attr == common::CUDADataAttr::Device) { + messages_.Say( + "Saved object '%s' in host code may not have ATTRIBUTES(DEVICE) unless allocatable"_err_en_US, + symbol.name(), + parser::ToUpperCaseLetters(common::EnumToString(attr))); + } + if (isComponent) { + if (attr == common::CUDADataAttr::Device) { + const DeclTypeSpec *type{symbol.GetType()}; + if (const DerivedTypeSpec * + derived{type ? type->AsDerived() : nullptr}) { + DirectComponentIterator directs{*derived}; + if (auto iter{std::find_if(directs.begin(), directs.end(), + [](const Symbol &) { return false; })}) { + messages_.Say( + "Derived type component '%s' may not have ATTRIBUTES(DEVICE) as it has a direct device component '%s'"_err_en_US, + symbol.name(), iter.BuildResultDesignatorName()); + } + } + } else if (attr == common::CUDADataAttr::Constant || + attr == common::CUDADataAttr::Shared) { + messages_.Say( + "Derived type component '%s' may not have ATTRIBUTES(%s)"_err_en_US, + symbol.name(), + parser::ToUpperCaseLetters(common::EnumToString(attr))); + } + } else if (!subpDetails && symbol.owner().kind() != Scope::Kind::Module && + symbol.owner().kind() != Scope::Kind::MainProgram) { + messages_.Say( + "ATTRIBUTES(%s) may apply only to module, host subprogram, or device subprogram data"_err_en_US, + parser::ToUpperCaseLetters(common::EnumToString(attr))); + } + } } void CheckHelper::CheckPointerInitialization(const Symbol &symbol) { @@ -910,6 +1072,9 @@ bool canBeAssumedShape{arraySpec.CanBeAssumedShape()}; bool canBeAssumedSize{arraySpec.CanBeAssumedSize()}; bool isAssumedRank{arraySpec.IsAssumedRank()}; + bool isCUDAShared{ + GetCUDADataAttr(&symbol).value_or(common::CUDADataAttr::Device) == + common::CUDADataAttr::Shared}; std::optional msg; if (symbol.test(Symbol::Flag::CrayPointee) && !isExplicit && !canBeAssumedSize) { @@ -939,12 +1104,12 @@ } } else if (canBeAssumedShape && !canBeDeferred) { msg = "Assumed-shape array '%s' must be a dummy argument"_err_en_US; - } else if (canBeAssumedSize && !canBeImplied) { // C833 + } else if (canBeAssumedSize && !canBeImplied && !isCUDAShared) { // C833 msg = "Assumed-size array '%s' must be a dummy argument"_err_en_US; } else if (isAssumedRank) { // C837 msg = "Assumed-rank array '%s' must be a dummy argument"_err_en_US; } else if (canBeImplied) { - if (!IsNamedConstant(symbol)) { // C835, C836 + if (!IsNamedConstant(symbol) && !isCUDAShared) { // C835, C836 msg = "Implied-shape array '%s' must be a named constant or a " "dummy argument"_err_en_US; } @@ -1178,6 +1343,50 @@ } CheckExternal(symbol); CheckModuleProcedureDef(symbol); + auto cudaAttrs{details.cudaSubprogramAttrs()}; + if (cudaAttrs && + (*cudaAttrs == common::CUDASubprogramAttrs::Global || + *cudaAttrs == common::CUDASubprogramAttrs::Grid_Global) && + details.isFunction()) { + messages_.Say(symbol.name(), + "A function may not have ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL)"_err_en_US); + } + if (cudaAttrs && *cudaAttrs != common::CUDASubprogramAttrs::Host) { + // CUDA device subprogram checks + if (symbol.attrs().HasAny({Attr::RECURSIVE, Attr::PURE, Attr::ELEMENTAL})) { + messages_.Say(symbol.name(), + "A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL"_err_en_US); + } + if (ClassifyProcedure(symbol) == ProcedureDefinitionClass::Internal) { + messages_.Say(symbol.name(), + "A device subprogram may not be an internal subprogram"_err_en_US); + } else if ((*cudaAttrs == common::CUDASubprogramAttrs::Device || + *cudaAttrs == common::CUDASubprogramAttrs::HostDevice) && + (symbol.owner().kind() != Scope::Kind::Module || + details.isInterface())) { + messages_.Say(symbol.name(), + "An ATTRIBUTES(DEVICE) subprogram must be a top-level module procedure"_err_en_US); + } + } + if ((!details.cudaLaunchBounds().empty() || + !details.cudaClusterDims().empty()) && + !(cudaAttrs && + (*cudaAttrs == common::CUDASubprogramAttrs::Global || + *cudaAttrs == common::CUDASubprogramAttrs::Grid_Global))) { + messages_.Say(symbol.name(), + "A subroutine may not have LAUNCH_BOUNDS() or CLUSTER_DIMS() unless it has ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL)"_err_en_US); + } + if (!IsStmtFunction(symbol)) { + if (const Scope * outerDevice{FindCUDADeviceContext(&symbol.owner())}; + outerDevice && outerDevice->symbol()) { + if (auto *msg{messages_.Say(symbol.name(), + "'%s' may not be an internal procedure of CUDA device subprogram '%s'"_err_en_US, + symbol.name(), outerDevice->symbol()->name())}) { + msg->Attach(outerDevice->symbol()->name(), + "Containing CUDA device subprogram"_en_US); + } + } + } } void CheckHelper::CheckExternal(const Symbol &symbol) { @@ -1206,7 +1415,7 @@ if (chars->HasExplicitInterface()) { std::string whyNot; if (!chars->IsCompatibleWith(*globalChars, &whyNot)) { - msg = messages_.Say( + msg = WarnIfNotInModuleFile( "The global subprogram '%s' is not compatible with its local procedure declaration (%s)"_warn_en_US, global->name(), whyNot); } @@ -1232,7 +1441,7 @@ if (auto previousChars{Characterize(previous)}) { std::string whyNot; if (!chars->IsCompatibleWith(*previousChars, &whyNot)) { - if (auto *msg{messages_.Say( + if (auto *msg{WarnIfNotInModuleFile( "The external interface '%s' is not compatible with an earlier definition (%s)"_warn_en_US, symbol.name(), whyNot)}) { evaluate::AttachDeclaration(msg, previous); @@ -1619,12 +1828,14 @@ return true; // OK } bool isFatal{msg->IsFatal()}; - SayWithDeclaration( - specific, std::move(*msg), MakeOpName(opName), specific.name()); + if (isFatal || !FindModuleFileContaining(specific.owner())) { + SayWithDeclaration( + specific, std::move(*msg), MakeOpName(opName), specific.name()); + } if (isFatal) { context_.SetError(specific); } - return false; + return !isFatal; } // If the number of arguments is wrong for this intrinsic operator, return @@ -1685,15 +1896,24 @@ dataObject == nullptr) { msg = "In %s function '%s', dummy argument '%s' must be a" " data object"_err_en_US; + } else if (dataObject->intent == common::Intent::Out) { + msg = + "In %s function '%s', dummy argument '%s' may not be INTENT(OUT)"_err_en_US; } else if (dataObject->intent != common::Intent::In && !dataObject->attrs.test(DummyDataObject::Attr::Value)) { - msg = "In %s function '%s', dummy argument '%s' must have INTENT(IN)" - " or VALUE attribute"_err_en_US; + msg = + "In %s function '%s', dummy argument '%s' should have INTENT(IN) or VALUE attribute"_warn_en_US; } if (msg) { - SayWithDeclaration(symbol, std::move(*msg), - parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), arg.name); - return false; + bool isFatal{msg->IsFatal()}; + if (isFatal || !FindModuleFileContaining(symbol.owner())) { + SayWithDeclaration(symbol, std::move(*msg), + parser::ToUpperCaseLetters(opName.ToString()), symbol.name(), + arg.name); + } + if (isFatal) { + return false; + } } return true; } @@ -1739,17 +1959,23 @@ " may not be OPTIONAL"_err_en_US; } else if (const auto *dataObject{std::get_if(&arg.u)}) { if (pos == 0) { - if (dataObject->intent != common::Intent::Out && + if (dataObject->intent == common::Intent::In) { + msg = "In defined assignment subroutine '%s', first dummy argument '%s'" + " may not have INTENT(IN)"_err_en_US; + } else if (dataObject->intent != common::Intent::Out && dataObject->intent != common::Intent::InOut) { msg = "In defined assignment subroutine '%s', first dummy argument '%s'" - " must have INTENT(OUT) or INTENT(INOUT)"_err_en_US; + " should have INTENT(OUT) or INTENT(INOUT)"_warn_en_US; } } else if (pos == 1) { - if (dataObject->intent != common::Intent::In && + if (dataObject->intent == common::Intent::Out) { + msg = "In defined assignment subroutine '%s', second dummy" + " argument '%s' may not have INTENT(OUT)"_err_en_US; + } else if (dataObject->intent != common::Intent::In && !dataObject->attrs.test(DummyDataObject::Attr::Value)) { msg = "In defined assignment subroutine '%s', second dummy" - " argument '%s' must have INTENT(IN) or VALUE attribute"_err_en_US; + " argument '%s' should have INTENT(IN) or VALUE attribute"_warn_en_US; } else if (dataObject->attrs.test(DummyDataObject::Attr::Pointer)) { msg = "In defined assignment subroutine '%s', second dummy argument '%s' must not be a pointer"_err_en_US; @@ -1765,9 +1991,14 @@ " must be a data object"_err_en_US; } if (msg) { - SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name); - context_.SetError(symbol); - return false; + bool isFatal{msg->IsFatal()}; + if (isFatal || !FindModuleFileContaining(symbol.owner())) { + SayWithDeclaration(symbol, std::move(*msg), symbol.name(), arg.name); + } + if (isFatal) { + context_.SetError(symbol); + return false; + } } return true; } @@ -1800,10 +2031,10 @@ if (!derivedDetails->finals().empty() && !derivedDetails->GetFinalForRank(rank)) { if (auto *msg{derivedSym == initialDerivedSym - ? messages_.Say(symbol.name(), + ? WarnIfNotInModuleFile(symbol.name(), "'%s' of derived type '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, symbol.name(), derivedSym->name(), rank) - : messages_.Say(symbol.name(), + : WarnIfNotInModuleFile(symbol.name(), "'%s' of derived type '%s' extended from '%s' does not have a FINAL subroutine for its rank (%d)"_warn_en_US, symbol.name(), initialDerivedSym->name(), derivedSym->name(), rank)}) { @@ -2437,15 +2668,17 @@ type->category() == DeclTypeSpec::Character && type->characterTypeSpec().length().isDeferred()) { // ok; F'2018 18.3.6 p2(6) - } else if (derived || IsInteroperableIntrinsicType(*type)) { + } else if (derived || + IsInteroperableIntrinsicType(*type, context_.languageFeatures())) { // F'2018 18.3.6 p2(4,5) - } else if (type->category() == DeclTypeSpec::Logical && IsDummy(symbol) && - evaluate::GetRank(*shape) == 0) { - // Special exception: LOGICAL scalar dummy arguments can be converted - // before a call -- & after if not INTENT(IN) -- without loss of - // information, and are accepted by some older compilers. - messages_.Say(symbol.name(), - "A BIND(C) LOGICAL dummy argument should have the interoperable KIND=C_BOOL"_port_en_US); + } else if (type->category() == DeclTypeSpec::Logical) { + if (IsDummy(symbol)) { + WarnIfNotInModuleFile(symbol.name(), + "A BIND(C) LOGICAL dummy argument should have the interoperable KIND=C_BOOL"_port_en_US); + } else { + WarnIfNotInModuleFile(symbol.name(), + "A BIND(C) LOGICAL object should have the interoperable KIND=C_BOOL"_port_en_US); + } } else if (symbol.attrs().test(Attr::VALUE)) { messages_.Say(symbol.name(), "A BIND(C) VALUE dummy argument must have an interoperable type"_err_en_US); @@ -2457,12 +2690,13 @@ } } if (IsOptional(symbol) && !symbol.attrs().test(Attr::VALUE)) { - messages_.Say(symbol.name(), + WarnIfNotInModuleFile(symbol.name(), "An interoperable procedure with an OPTIONAL dummy argument might not be portable"_port_en_US); } } else if (const auto *proc{symbol.detailsIf()}) { - if (!proc->procInterface() || - !proc->procInterface()->attrs().test(Attr::BIND_C)) { + if (!proc->isDummy() && + (!proc->procInterface() || + !proc->procInterface()->attrs().test(Attr::BIND_C))) { messages_.Say(symbol.name(), "An interface name with BIND attribute must be specified if the BIND attribute is specified in a procedure declaration statement"_err_en_US); context_.SetError(symbol); @@ -2514,10 +2748,21 @@ } context_.SetError(symbol); } - } else if (!IsInteroperableIntrinsicType(*type)) { - messages_.Say(component->name(), - "Each component of an interoperable derived type must have an interoperable type"_err_en_US); - context_.SetError(symbol); + } else if (!IsInteroperableIntrinsicType( + *type, context_.languageFeatures())) { + auto maybeDyType{evaluate::DynamicType::From(*type)}; + if (type->category() == DeclTypeSpec::Logical) { + WarnIfNotInModuleFile(component->name(), + "A LOGICAL component of a BIND(C) type should have the interoperable KIND=C_BOOL"_port_en_US); + } else if (type->category() == DeclTypeSpec::Character && + maybeDyType && maybeDyType->kind() == 1) { + WarnIfNotInModuleFile(component->name(), + "A CHARACTER component of a BIND(C) type should have length 1"_port_en_US); + } else { + messages_.Say(component->name(), + "Each component of an interoperable derived type must have an interoperable type"_err_en_US); + context_.SetError(symbol); + } } } if (auto extents{ @@ -2529,9 +2774,8 @@ } } } - if (derived->componentNames().empty() && - !FindModuleFileContaining(symbol.owner())) { // C1805 - messages_.Say(symbol.name(), + if (derived->componentNames().empty()) { // C1805 + WarnIfNotInModuleFile(symbol.name(), "A derived type with the BIND attribute is empty"_port_en_US); } } diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp --- a/flang/lib/Semantics/type.cpp +++ b/flang/lib/Semantics/type.cpp @@ -797,9 +797,10 @@ return o << x.AsFortran(); } -bool IsInteroperableIntrinsicType(const DeclTypeSpec &type) { +bool IsInteroperableIntrinsicType( + const DeclTypeSpec &type, const common::LanguageFeatureControl &features) { auto dyType{evaluate::DynamicType::From(type)}; - return dyType && IsInteroperableIntrinsicType(*dyType); + return dyType && IsInteroperableIntrinsicType(*dyType, &features); } } // namespace Fortran::semantics diff --git a/flang/module/__cuda_builtins.f90 b/flang/module/__cuda_builtins.f90 new file mode 100644 --- /dev/null +++ b/flang/module/__cuda_builtins.f90 @@ -0,0 +1,19 @@ +!===-- module/__cuda_builtins.f90 ------------------------------------------===! +! +! Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +! See https://llvm.org/LICENSE.txt for license information. +! SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +! +!===------------------------------------------------------------------------===! + +! These CUDA predefined variables are automatically available in device +! subprograms. + +module __CUDA_builtins + use __Fortran_builtins, only: & + threadIdx => __builtin_threadIdx, & + blockDim => __builtin_blockDim, & + blockIdx => __builtin_blockIdx, & + gridDim => __builtin_gridDim, & + warpsize => __builtin_warpsize +end module diff --git a/flang/test/Semantics/bind-c06.f90 b/flang/test/Semantics/bind-c06.f90 --- a/flang/test/Semantics/bind-c06.f90 +++ b/flang/test/Semantics/bind-c06.f90 @@ -65,7 +65,7 @@ end type type, bind(c) :: t10 - !ERROR: Each component of an interoperable derived type must have an interoperable type + !WARNING: A CHARACTER component of a BIND(C) type should have length 1 character(len=2) x end type type, bind(c) :: t11 @@ -73,7 +73,7 @@ character(kind=2) x end type type, bind(c) :: t12 - !ERROR: Each component of an interoperable derived type must have an interoperable type + !PORTABILITY: A LOGICAL component of a BIND(C) type should have the interoperable KIND=C_BOOL logical(kind=8) x end type type, bind(c) :: t13 diff --git a/flang/test/Semantics/cuf02.cuf b/flang/test/Semantics/cuf02.cuf new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/cuf02.cuf @@ -0,0 +1,49 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +module m + interface + !ERROR: An ATTRIBUTES(DEVICE) subprogram must be a top-level module procedure + attributes(device) subroutine exts1 + end + end interface + contains + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + recursive attributes(device) subroutine s1 + end + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + pure attributes(device) subroutine s2 + end + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + elemental attributes(device) subroutine s3 + end + subroutine s4 + contains + !ERROR: A device subprogram may not be an internal subprogram + attributes(device) subroutine inner + end + end + attributes(device) subroutine s5 ! nvfortran crashes on this one + contains + !ERROR: 'inner' may not be an internal procedure of CUDA device subprogram 's5' + subroutine inner + end + end + attributes(device) subroutine s6 + stmtfunc(x) = x + 1. ! ok + end + !ERROR: A function may not have ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL) + attributes(global) real function f1 + end + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + recursive attributes(global) subroutine s7 + end + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + pure attributes(global) subroutine s8 + end + !ERROR: A device subprogram may not be RECURSIVE, PURE, or ELEMENTAL + elemental attributes(global) subroutine s9 + end +end + +!ERROR: An ATTRIBUTES(DEVICE) subprogram must be a top-level module procedure +attributes(device) subroutine exts1 +end diff --git a/flang/test/Semantics/cuf03.cuf b/flang/test/Semantics/cuf03.cuf new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/cuf03.cuf @@ -0,0 +1,59 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +! Exercise CUDA data attribute checks +module m + real, constant :: mc ! ok + real, constant :: mci = 1. ! ok + !ERROR: Object 'mcl' with ATTRIBUTES(CONSTANT) may not be allocatable, pointer, or target + real, constant, allocatable :: mcl + !ERROR: Object 'mcp' with ATTRIBUTES(CONSTANT) may not be allocatable, pointer, or target + real, constant, pointer :: mcp + !ERROR: Object 'mct' with ATTRIBUTES(CONSTANT) may not be allocatable, pointer, or target + real, constant, target :: mct + real, device :: md ! ok + real, device :: mdi = 1. + real, device, allocatable :: mdl ! ok + real, device, pointer :: mdp ! ok at module level + real, device, target :: mdt ! ok + !ERROR: Object 'ms' with ATTRIBUTES(SHARED) must be declared in a device subprogram + real, shared :: ms + !ERROR: Object 'msi' with ATTRIBUTES(SHARED) must be declared in a device subprogram + real, shared :: msi = 1. + !ERROR: Object 'msl' with ATTRIBUTES(SHARED) may not be allocatable, pointer, or target + real, shared, allocatable :: msl + !ERROR: Object 'msp' with ATTRIBUTES(SHARED) may not be allocatable, pointer, or target + real, shared, pointer :: msp + !ERROR: Object 'mst' with ATTRIBUTES(SHARED) may not be allocatable, pointer, or target + real, shared, target :: mst + !ERROR: Object 'msa' with ATTRIBUTES(SHARED) must be declared in a device subprogram + real, shared :: msa(*) + !ERROR: Object 'mm' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, or a dummy argument + real, managed :: mm + !ERROR: Object 'mmi' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, or a dummy argument + real, managed :: mmi = 1. + real, managed, allocatable :: mml ! ok + !ERROR: Object 'mmp' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, or a dummy argument + real, managed, pointer :: mmp ! ok + !ERROR: Object 'mmt' with ATTRIBUTES(MANAGED) must also be allocatable, automatic, or a dummy argument + real, managed, target :: mmt + !WARNING: Object 'mp' with ATTRIBUTES(PINNED) should also be allocatable + real, pinned :: mp + !WARNING: Object 'mpi' with ATTRIBUTES(PINNED) should also be allocatable + real, pinned :: mpi = 1. + real, pinned, allocatable :: mpl ! ok + !ERROR: Object 'mpp' with ATTRIBUTES(PINNED) may not be a pointer + real, pinned, pointer :: mpp + !WARNING: Object 'mpt' with ATTRIBUTES(PINNED) should also be allocatable + real, pinned, target :: mpt ! ok + !ERROR: ATTRIBUTES(TEXTURE) is obsolete and no longer supported + real, texture, pointer :: mt + !ERROR: 'bigint' has intrinsic type 'INTEGER(16)' that is not available on the device + integer(16), device :: bigint + contains + attributes(device) subroutine devsubr(n,da) + integer, intent(in) :: n + real, device :: da(*) ! ok + real, managed :: ma(n) ! ok + !WARNING: Pointer 'dp' may not be associated in a device subprogram + real, device, pointer :: dp + end subroutine +end module diff --git a/flang/test/Semantics/cuf08.cuf b/flang/test/Semantics/cuf08.cuf new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/cuf08.cuf @@ -0,0 +1,23 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +module m + contains + !ERROR: A subroutine may not have LAUNCH_BOUNDS() or CLUSTER_DIMS() unless it has ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL) + launch_bounds(1,2) subroutine bad1; end + !ERROR: A subroutine may not have LAUNCH_BOUNDS() or CLUSTER_DIMS() unless it has ATTRIBUTES(GLOBAL) or ATTRIBUTES(GRID_GLOBAL) + cluster_dims(1,2,3) subroutine bad2; end + attributes(global) launch_bounds(1,2) subroutine good1; end + attributes(global) launch_bounds(1,2,3) subroutine good2; end + !ERROR: LAUNCH_BOUNDS() may only appear once + attributes(global) launch_bounds(1,2) launch_bounds(3,4) subroutine bad3; end + !ERROR: Operands of LAUNCH_BOUNDS() must be 2 or 3 integer constants + attributes(global) launch_bounds(1) subroutine bad4; end + !ERROR: Operands of LAUNCH_BOUNDS() must be 2 or 3 integer constants + attributes(global) launch_bounds(1,2,3,4) subroutine bad5; end + attributes(global) cluster_dims(1,2,3) subroutine good3; end + !ERROR: CLUSTER_DIMS() may only appear once + attributes(global) cluster_dims(1,2,3) cluster_dims(4,5,6) subroutine bad6; end + !ERROR: Operands of CLUSTER_DIMS() must be three integer constants + attributes(global) cluster_dims(1) subroutine bad7; end + !ERROR: Operands of CLUSTER_DIMS() must be three integer constants + attributes(global) cluster_dims(1,2,3,4) subroutine bad8; end +end module diff --git a/flang/test/Semantics/resolve65.f90 b/flang/test/Semantics/resolve65.f90 --- a/flang/test/Semantics/resolve65.f90 +++ b/flang/test/Semantics/resolve65.f90 @@ -5,6 +5,9 @@ implicit none type :: t contains + !ERROR: Generic 'assignment(=)' may not have specific procedures 't%assign_t4' and 't%assign_t5' as their interfaces are not distinguishable + !ERROR: Generic 'assignment(=)' may not have specific procedures 't%assign_t4' and 't%assign_t6' as their interfaces are not distinguishable + !ERROR: Generic 'assignment(=)' may not have specific procedures 't%assign_t5' and 't%assign_t6' as their interfaces are not distinguishable !ERROR: Defined assignment procedure 'binding' must be a subroutine generic :: assignment(=) => binding procedure :: binding => assign_t1 @@ -12,10 +15,14 @@ procedure :: assign_t2 procedure :: assign_t3 !ERROR: Defined assignment subroutine 'assign_t2' must have two dummy arguments - !ERROR: In defined assignment subroutine 'assign_t3', second dummy argument 'y' must have INTENT(IN) or VALUE attribute - !ERROR: In defined assignment subroutine 'assign_t4', first dummy argument 'x' must have INTENT(OUT) or INTENT(INOUT) - generic :: assignment(=) => assign_t, assign_t2, assign_t3, assign_t4 + !WARNING: In defined assignment subroutine 'assign_t3', second dummy argument 'y' should have INTENT(IN) or VALUE attribute + !WARNING: In defined assignment subroutine 'assign_t4', first dummy argument 'x' should have INTENT(OUT) or INTENT(INOUT) + !ERROR: In defined assignment subroutine 'assign_t5', first dummy argument 'x' may not have INTENT(IN) + !ERROR: In defined assignment subroutine 'assign_t6', second dummy argument 'y' may not have INTENT(OUT) + generic :: assignment(=) => assign_t, assign_t2, assign_t3, assign_t4, assign_t5, assign_t6 procedure :: assign_t4 + procedure :: assign_t5 + procedure :: assign_t6 end type type :: t2 contains @@ -41,7 +48,15 @@ end subroutine assign_t4(x, y) class(t) :: x - integer, intent(in) :: y + integer, intent(in) :: y + end + subroutine assign_t5(x, y) + class(t), intent(in) :: x + integer, intent(in) :: y + end + subroutine assign_t6(x, y) + class(t), intent(out) :: x + integer, intent(out) :: y end end diff --git a/flang/test/Semantics/resolve67.f90 b/flang/test/Semantics/resolve67.f90 --- a/flang/test/Semantics/resolve67.f90 +++ b/flang/test/Semantics/resolve67.f90 @@ -41,15 +41,16 @@ end end interface interface operator(<) - !ERROR: In OPERATOR(<) function 'lt1', dummy argument 'x' must have INTENT(IN) or VALUE attribute + !WARNING: In OPERATOR(<) function 'lt1', dummy argument 'x' should have INTENT(IN) or VALUE attribute !ERROR: In OPERATOR(<) function 'lt1', dummy argument 'y' may not be OPTIONAL logical function lt1(x, y) logical :: x real, value, optional :: y end + !ERROR: In OPERATOR(<) function 'lt2', dummy argument 'x' may not be INTENT(OUT) !ERROR: In OPERATOR(<) function 'lt2', dummy argument 'y' must be a data object logical function lt2(x, y) - logical, intent(in) :: x + logical, intent(out) :: x intent(in) :: y interface subroutine y()