aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics')
-rw-r--r--flang/lib/Semantics/check-acc-structure.cpp226
-rw-r--r--flang/lib/Semantics/check-acc-structure.h16
-rw-r--r--flang/lib/Semantics/check-cuda.cpp9
-rw-r--r--flang/lib/Semantics/check-declarations.cpp4
-rw-r--r--flang/lib/Semantics/check-omp-atomic.cpp13
-rw-r--r--flang/lib/Semantics/expression.cpp77
-rw-r--r--flang/lib/Semantics/openmp-utils.cpp26
-rw-r--r--flang/lib/Semantics/openmp-utils.h1
-rw-r--r--flang/lib/Semantics/pointer-assignment.cpp16
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp4
10 files changed, 325 insertions, 67 deletions
diff --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp
index 9cbea97..77e2b01 100644
--- a/flang/lib/Semantics/check-acc-structure.cpp
+++ b/flang/lib/Semantics/check-acc-structure.cpp
@@ -7,8 +7,15 @@
//===----------------------------------------------------------------------===//
#include "check-acc-structure.h"
#include "flang/Common/enum-set.h"
+#include "flang/Evaluate/tools.h"
#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
+#include "flang/Semantics/type.h"
+#include "flang/Support/Fortran.h"
+#include "llvm/Support/AtomicOrdering.h"
+
+#include <optional>
#define CHECK_SIMPLE_CLAUSE(X, Y) \
void AccStructureChecker::Enter(const parser::AccClause::X &) { \
@@ -342,20 +349,219 @@ void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) {
dirContext_.pop_back();
}
-void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) {
- const parser::AssignmentStmt &assignment{
- std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement};
- const auto &var{std::get<parser::Variable>(assignment.t)};
- const auto &expr{std::get<parser::Expr>(assignment.t)};
+void AccStructureChecker::CheckAtomicStmt(
+ const parser::AssignmentStmt &assign, const std::string &construct) {
+ const auto &var{std::get<parser::Variable>(assign.t)};
+ const auto &expr{std::get<parser::Expr>(assign.t)};
const auto *rhs{GetExpr(context_, expr)};
const auto *lhs{GetExpr(context_, var)};
- if (lhs && rhs) {
- if (lhs->Rank() != 0)
+
+ if (lhs) {
+ if (lhs->Rank() != 0) {
context_.Say(expr.source,
- "LHS of atomic update statement must be scalar"_err_en_US);
- if (rhs->Rank() != 0)
+ "LHS of atomic %s statement must be scalar"_err_en_US, construct);
+ }
+ // TODO: Check if lhs is intrinsic type.
+ }
+ if (rhs) {
+ if (rhs->Rank() != 0) {
context_.Say(var.GetSource(),
- "RHS of atomic update statement must be scalar"_err_en_US);
+ "RHS of atomic %s statement must be scalar"_err_en_US, construct);
+ }
+ // TODO: Check if rhs is intrinsic type.
+ }
+}
+
+static constexpr evaluate::operation::OperatorSet validAccAtomicUpdateOperators{
+ evaluate::operation::Operator::Add, evaluate::operation::Operator::Mul,
+ evaluate::operation::Operator::Sub, evaluate::operation::Operator::Div,
+ evaluate::operation::Operator::And, evaluate::operation::Operator::Or,
+ evaluate::operation::Operator::Eqv, evaluate::operation::Operator::Neqv,
+ evaluate::operation::Operator::Max, evaluate::operation::Operator::Min};
+
+static bool IsValidAtomicUpdateOperation(
+ const evaluate::operation::Operator &op) {
+ return validAccAtomicUpdateOperators.test(op);
+}
+
+// Couldn't reproduce this behavior with evaluate::UnwrapConvertedExpr which
+// is similar but only works within a single type category.
+static SomeExpr GetExprModuloConversion(const SomeExpr &expr) {
+ const auto [op, args]{evaluate::GetTopLevelOperation(expr)};
+ // Check: if it is a conversion then it must have at least one argument.
+ CHECK(((op != evaluate::operation::Operator::Convert &&
+ op != evaluate::operation::Operator::Resize) ||
+ args.size() >= 1) &&
+ "Invalid conversion operation");
+ if ((op == evaluate::operation::Operator::Convert ||
+ op == evaluate::operation::Operator::Resize) &&
+ args.size() >= 1) {
+ return args[0];
+ }
+ return expr;
+}
+
+void AccStructureChecker::CheckAtomicUpdateStmt(
+ const parser::AssignmentStmt &assign, const SomeExpr &updateVar,
+ const SomeExpr *captureVar) {
+ CheckAtomicStmt(assign, "update");
+ const auto &expr{std::get<parser::Expr>(assign.t)};
+ const auto *rhs{GetExpr(context_, expr)};
+ if (rhs) {
+ const auto [op, args]{
+ evaluate::GetTopLevelOperation(GetExprModuloConversion(*rhs))};
+ if (!IsValidAtomicUpdateOperation(op)) {
+ context_.Say(expr.source,
+ "Invalid atomic update operation, can only use: *, +, -, *, /, and, or, eqv, neqv, max, min, iand, ior, ieor"_err_en_US);
+ } else {
+ bool foundUpdateVar{false};
+ for (const auto &arg : args) {
+ if (updateVar == GetExprModuloConversion(arg)) {
+ if (foundUpdateVar) {
+ context_.Say(expr.source,
+ "The updated variable, %s, cannot appear more than once in the atomic update operation"_err_en_US,
+ updateVar.AsFortran());
+ } else {
+ foundUpdateVar = true;
+ }
+ } else if (evaluate::IsVarSubexpressionOf(updateVar, arg)) {
+ // TODO: Get the source location of arg and point to the individual
+ // argument.
+ context_.Say(expr.source,
+ "Arguments to the atomic update operation cannot reference the updated variable, %s, as a subexpression"_err_en_US,
+ updateVar.AsFortran());
+ }
+ }
+ if (!foundUpdateVar) {
+ context_.Say(expr.source,
+ "The RHS of this atomic update statement must reference the updated variable: %s"_err_en_US,
+ updateVar.AsFortran());
+ }
+ }
+ }
+}
+
+void AccStructureChecker::CheckAtomicWriteStmt(
+ const parser::AssignmentStmt &assign, const SomeExpr &updateVar,
+ const SomeExpr *captureVar) {
+ CheckAtomicStmt(assign, "write");
+ const auto &expr{std::get<parser::Expr>(assign.t)};
+ const auto *rhs{GetExpr(context_, expr)};
+ if (rhs) {
+ if (evaluate::IsVarSubexpressionOf(updateVar, *rhs)) {
+ context_.Say(expr.source,
+ "The RHS of this atomic write statement cannot reference the atomic variable: %s"_err_en_US,
+ updateVar.AsFortran());
+ }
+ }
+}
+
+void AccStructureChecker::CheckAtomicCaptureStmt(
+ const parser::AssignmentStmt &assign, const SomeExpr *updateVar,
+ const SomeExpr &captureVar) {
+ CheckAtomicStmt(assign, "capture");
+}
+
+void AccStructureChecker::Enter(const parser::AccAtomicCapture &capture) {
+ const Fortran::parser::AssignmentStmt &stmt1{
+ std::get<Fortran::parser::AccAtomicCapture::Stmt1>(capture.t)
+ .v.statement};
+ const Fortran::parser::AssignmentStmt &stmt2{
+ std::get<Fortran::parser::AccAtomicCapture::Stmt2>(capture.t)
+ .v.statement};
+ const auto &var1{std::get<parser::Variable>(stmt1.t)};
+ const auto &var2{std::get<parser::Variable>(stmt2.t)};
+ const auto *lhs1{GetExpr(context_, var1)};
+ const auto *lhs2{GetExpr(context_, var2)};
+ if (!lhs1 || !lhs2) {
+ // Not enough information to check.
+ return;
+ }
+ if (*lhs1 == *lhs2) {
+ context_.Say(std::get<parser::Verbatim>(capture.t).source,
+ "The variables assigned in this atomic capture construct must be distinct"_err_en_US);
+ return;
+ }
+ const auto &expr1{std::get<parser::Expr>(stmt1.t)};
+ const auto &expr2{std::get<parser::Expr>(stmt2.t)};
+ const auto *rhs1{GetExpr(context_, expr1)};
+ const auto *rhs2{GetExpr(context_, expr2)};
+ if (!rhs1 || !rhs2) {
+ return;
+ }
+ bool stmt1CapturesLhs2{*lhs2 == GetExprModuloConversion(*rhs1)};
+ bool stmt2CapturesLhs1{*lhs1 == GetExprModuloConversion(*rhs2)};
+ if (stmt1CapturesLhs2 && !stmt2CapturesLhs1) {
+ if (*lhs2 == GetExprModuloConversion(*rhs2)) {
+ // a = b; b = b: Doesn't fit the spec.
+ context_.Say(std::get<parser::Verbatim>(capture.t).source,
+ "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US);
+ // TODO: Add attatchment that a = b seems to be a capture,
+ // but b = b is not a valid update or write.
+ } else if (evaluate::IsVarSubexpressionOf(*lhs2, *rhs2)) {
+ // Take v = x; x = <expr w/ x> as capture; update
+ const auto &updateVar{*lhs2};
+ const auto &captureVar{*lhs1};
+ CheckAtomicCaptureStmt(stmt1, &updateVar, captureVar);
+ CheckAtomicUpdateStmt(stmt2, updateVar, &captureVar);
+ } else {
+ // Take v = x; x = <expr w/o x> as capture; write
+ const auto &updateVar{*lhs2};
+ const auto &captureVar{*lhs1};
+ CheckAtomicCaptureStmt(stmt1, &updateVar, captureVar);
+ CheckAtomicWriteStmt(stmt2, updateVar, &captureVar);
+ }
+ } else if (stmt2CapturesLhs1 && !stmt1CapturesLhs2) {
+ if (*lhs1 == GetExprModuloConversion(*rhs1)) {
+ // Error a = a; b = a;
+ context_.Say(var1.GetSource(),
+ "The first assignment in this atomic capture construct doesn't perform a valid update"_err_en_US);
+ // Add attatchment that a = a is not considered an update,
+ // but b = a seems to be a capture.
+ } else {
+ // Take x = <expr>; v = x: as update; capture
+ const auto &updateVar{*lhs1};
+ const auto &captureVar{*lhs2};
+ CheckAtomicUpdateStmt(stmt1, updateVar, &captureVar);
+ CheckAtomicCaptureStmt(stmt2, &updateVar, captureVar);
+ }
+ } else if (stmt1CapturesLhs2 && stmt2CapturesLhs1) {
+ // x1 = x2; x2 = x1; Doesn't fit the spec.
+ context_.Say(std::get<parser::Verbatim>(capture.t).source,
+ "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US);
+ // TODO: Add attatchment that both assignments seem to be captures.
+ } else { // !stmt1CapturesLhs2 && !stmt2CapturesLhs1
+ // a = <expr != b>; b = <expr != a>; Doesn't fit the spec
+ context_.Say(std::get<parser::Verbatim>(capture.t).source,
+ "The assignments in this atomic capture construct do not update a variable and capture either its initial or final value"_err_en_US);
+ // TODO: Add attatchment that neither assignment seems to be a capture.
+ }
+}
+
+void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) {
+ const auto &assign{
+ std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement};
+ const auto &var{std::get<parser::Variable>(assign.t)};
+ if (const auto *updateVar{GetExpr(context_, var)}) {
+ CheckAtomicUpdateStmt(assign, *updateVar, /*captureVar=*/nullptr);
+ }
+}
+
+void AccStructureChecker::Enter(const parser::AccAtomicWrite &x) {
+ const auto &assign{
+ std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement};
+ const auto &var{std::get<parser::Variable>(assign.t)};
+ if (const auto *updateVar{GetExpr(context_, var)}) {
+ CheckAtomicWriteStmt(assign, *updateVar, /*captureVar=*/nullptr);
+ }
+}
+
+void AccStructureChecker::Enter(const parser::AccAtomicRead &x) {
+ const auto &assign{
+ std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement};
+ const auto &var{std::get<parser::Variable>(assign.t)};
+ if (const auto *captureVar{GetExpr(context_, var)}) {
+ CheckAtomicCaptureStmt(assign, /*updateVar=*/nullptr, *captureVar);
}
}
diff --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h
index 6a9aa01..359f155 100644
--- a/flang/lib/Semantics/check-acc-structure.h
+++ b/flang/lib/Semantics/check-acc-structure.h
@@ -63,6 +63,9 @@ public:
void Enter(const parser::OpenACCCacheConstruct &);
void Leave(const parser::OpenACCCacheConstruct &);
void Enter(const parser::AccAtomicUpdate &);
+ void Enter(const parser::AccAtomicCapture &);
+ void Enter(const parser::AccAtomicWrite &);
+ void Enter(const parser::AccAtomicRead &);
void Enter(const parser::OpenACCEndConstruct &);
// Clauses
@@ -80,6 +83,19 @@ public:
#include "llvm/Frontend/OpenACC/ACC.inc"
private:
+ void CheckAtomicStmt(
+ const parser::AssignmentStmt &assign, const std::string &construct);
+ void CheckAtomicUpdateStmt(const parser::AssignmentStmt &assign,
+ const SomeExpr &updateVar, const SomeExpr *captureVar);
+ void CheckAtomicCaptureStmt(const parser::AssignmentStmt &assign,
+ const SomeExpr *updateVar, const SomeExpr &captureVar);
+ void CheckAtomicWriteStmt(const parser::AssignmentStmt &assign,
+ const SomeExpr &updateVar, const SomeExpr *captureVar);
+ void CheckAtomicUpdateVariable(
+ const parser::Variable &updateVar, const parser::Variable &captureVar);
+ void CheckAtomicCaptureVariable(
+ const parser::Variable &captureVar, const parser::Variable &updateVar);
+
bool CheckAllowedModifier(llvm::acc::Clause clause);
bool IsComputeConstruct(llvm::acc::Directive directive) const;
bool IsInsideComputeConstruct() const;
diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp
index b011476..9b48432 100644
--- a/flang/lib/Semantics/check-cuda.cpp
+++ b/flang/lib/Semantics/check-cuda.cpp
@@ -761,14 +761,13 @@ void CUDAChecker::Enter(const parser::AssignmentStmt &x) {
// legal.
if (nbLhs == 0 && nbRhs > 1) {
context_.Say(lhsLoc,
- "More than one reference to a CUDA object on the right hand side of the assigment"_err_en_US);
+ "More than one reference to a CUDA object on the right hand side of the assignment"_err_en_US);
}
- if (Fortran::evaluate::HasCUDADeviceAttrs(assign->lhs) &&
- Fortran::evaluate::HasCUDAImplicitTransfer(assign->rhs)) {
+ if (evaluate::HasCUDADeviceAttrs(assign->lhs) &&
+ evaluate::HasCUDAImplicitTransfer(assign->rhs)) {
if (GetNbOfCUDAManagedOrUnifiedSymbols(assign->lhs) == 1 &&
- GetNbOfCUDAManagedOrUnifiedSymbols(assign->rhs) == 1 &&
- GetNbOfCUDADeviceSymbols(assign->rhs) == 1) {
+ GetNbOfCUDAManagedOrUnifiedSymbols(assign->rhs) == 1 && nbRhs == 1) {
return; // This is a special case handled on the host.
}
context_.Say(lhsLoc, "Unsupported CUDA data transfer"_err_en_US);
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index a2f2906..d769f22 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -2081,7 +2081,7 @@ static bool ConflictsWithIntrinsicAssignment(const Procedure &proc) {
}
static bool ConflictsWithIntrinsicOperator(
- const GenericKind &kind, const Procedure &proc) {
+ const GenericKind &kind, const Procedure &proc, SemanticsContext &context) {
if (!kind.IsIntrinsicOperator()) {
return false;
}
@@ -2167,7 +2167,7 @@ bool CheckHelper::CheckDefinedOperator(SourceName opName, GenericKind kind,
}
} else if (!checkDefinedOperatorArgs(opName, specific, proc)) {
return false; // error was reported
- } else if (ConflictsWithIntrinsicOperator(kind, proc)) {
+ } else if (ConflictsWithIntrinsicOperator(kind, proc, context_)) {
msg = "%s function '%s' conflicts with intrinsic operator"_err_en_US;
}
if (msg) {
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index c5ed879..333fad0 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -197,7 +197,8 @@ static std::pair<parser::CharBlock, parser::CharBlock> SplitAssignmentSource(
}
static bool IsCheckForAssociated(const SomeExpr &cond) {
- return GetTopLevelOperation(cond).first == operation::Operator::Associated;
+ return GetTopLevelOperationIgnoreResizing(cond).first ==
+ operation::Operator::Associated;
}
static bool IsMaybeAtomicWrite(const evaluate::Assignment &assign) {
@@ -399,8 +400,8 @@ OmpStructureChecker::CheckUpdateCapture(
// subexpression of the right-hand side.
// 2. An assignment could be a capture (cbc) if the right-hand side is
// a variable (or a function ref), with potential type conversions.
- bool cbu1{IsSubexpressionOf(as1.lhs, as1.rhs)}; // Can as1 be an update?
- bool cbu2{IsSubexpressionOf(as2.lhs, as2.rhs)}; // Can as2 be an update?
+ bool cbu1{IsVarSubexpressionOf(as1.lhs, as1.rhs)}; // Can as1 be an update?
+ bool cbu2{IsVarSubexpressionOf(as2.lhs, as2.rhs)}; // Can as2 be an update?
bool cbc1{IsVarOrFunctionRef(GetConvertInput(as1.rhs))}; // Can 1 be capture?
bool cbc2{IsVarOrFunctionRef(GetConvertInput(as2.rhs))}; // Can 2 be capture?
@@ -607,7 +608,7 @@ void OmpStructureChecker::CheckAtomicUpdateAssignment(
std::pair<operation::Operator, std::vector<SomeExpr>> top{
operation::Operator::Unknown, {}};
if (auto &&maybeInput{GetConvertInput(update.rhs)}) {
- top = GetTopLevelOperation(*maybeInput);
+ top = GetTopLevelOperationIgnoreResizing(*maybeInput);
}
switch (top.first) {
case operation::Operator::Add:
@@ -657,7 +658,7 @@ void OmpStructureChecker::CheckAtomicUpdateAssignment(
if (IsSameOrConvertOf(arg, atom)) {
++count;
} else {
- if (!subExpr && IsSubexpressionOf(atom, arg)) {
+ if (!subExpr && evaluate::IsVarSubexpressionOf(atom, arg)) {
subExpr = arg;
}
nonAtom.push_back(arg);
@@ -715,7 +716,7 @@ void OmpStructureChecker::CheckAtomicConditionalUpdateAssignment(
CheckAtomicVariable(atom, alsrc);
- auto top{GetTopLevelOperation(cond)};
+ auto top{GetTopLevelOperationIgnoreResizing(cond)};
// Missing arguments to operations would have been diagnosed by now.
switch (top.first) {
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 1447372..92dbe0e 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -165,10 +165,17 @@ public:
bool CheckForNullPointer(const char *where = "as an operand here");
bool CheckForAssumedRank(const char *where = "as an operand here");
+ bool AnyCUDADeviceData() const;
+ // Returns true if an interface has been defined for an intrinsic operator
+ // with one or more device operands.
+ bool HasDeviceDefinedIntrinsicOpOverride(const char *) const;
+ template <typename E> bool HasDeviceDefinedIntrinsicOpOverride(E opr) const {
+ return HasDeviceDefinedIntrinsicOpOverride(
+ context_.context().languageFeatures().GetNames(opr));
+ }
+
// Find and return a user-defined operator or report an error.
// The provided message is used if there is no such operator.
- // If a definedOpSymbolPtr is provided, the caller must check
- // for its accessibility.
MaybeExpr TryDefinedOp(
const char *, parser::MessageFixedText, bool isUserOp = false);
template <typename E>
@@ -183,6 +190,8 @@ public:
void Dump(llvm::raw_ostream &);
private:
+ bool HasDeviceDefinedIntrinsicOpOverride(
+ const std::vector<const char *> &) const;
MaybeExpr TryDefinedOp(
const std::vector<const char *> &, parser::MessageFixedText);
MaybeExpr TryBoundOp(const Symbol &, int passIndex);
@@ -202,7 +211,7 @@ private:
void SayNoMatch(
const std::string &, bool isAssignment = false, bool isAmbiguous = false);
std::string TypeAsFortran(std::size_t);
- bool AnyUntypedOrMissingOperand();
+ bool AnyUntypedOrMissingOperand() const;
ExpressionAnalyzer &context_;
ActualArguments actuals_;
@@ -4497,13 +4506,20 @@ void ArgumentAnalyzer::Analyze(
bool ArgumentAnalyzer::IsIntrinsicRelational(RelationalOperator opr,
const DynamicType &leftType, const DynamicType &rightType) const {
CHECK(actuals_.size() == 2);
- return semantics::IsIntrinsicRelational(
- opr, leftType, GetRank(0), rightType, GetRank(1));
+ return !(context_.context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CUDA) &&
+ HasDeviceDefinedIntrinsicOpOverride(opr)) &&
+ semantics::IsIntrinsicRelational(
+ opr, leftType, GetRank(0), rightType, GetRank(1));
}
bool ArgumentAnalyzer::IsIntrinsicNumeric(NumericOperator opr) const {
std::optional<DynamicType> leftType{GetType(0)};
- if (actuals_.size() == 1) {
+ if (context_.context().languageFeatures().IsEnabled(
+ common::LanguageFeature::CUDA) &&
+ HasDeviceDefinedIntrinsicOpOverride(AsFortran(opr))) {
+ return false;
+ } else if (actuals_.size() == 1) {
if (IsBOZLiteral(0)) {
return opr == NumericOperator::Add; // unary '+'
} else {
@@ -4617,6 +4633,53 @@ bool ArgumentAnalyzer::CheckForAssumedRank(const char *where) {
return true;
}
+bool ArgumentAnalyzer::AnyCUDADeviceData() const {
+ for (const std::optional<ActualArgument> &arg : actuals_) {
+ if (arg) {
+ if (const Expr<SomeType> *expr{arg->UnwrapExpr()}) {
+ if (HasCUDADeviceAttrs(*expr)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// Some operations can be defined with explicit non-type-bound interfaces
+// that would erroneously conflict with intrinsic operations in their
+// types and ranks but have one or more dummy arguments with the DEVICE
+// attribute.
+bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
+ const char *opr) const {
+ if (AnyCUDADeviceData() && !AnyUntypedOrMissingOperand()) {
+ std::string oprNameString{"operator("s + opr + ')'};
+ parser::CharBlock oprName{oprNameString};
+ parser::Messages buffer;
+ auto restorer{context_.GetContextualMessages().SetMessages(buffer)};
+ const auto &scope{context_.context().FindScope(source_)};
+ if (Symbol * generic{scope.FindSymbol(oprName)}) {
+ parser::Name name{generic->name(), generic};
+ const Symbol *resultSymbol{nullptr};
+ if (context_.AnalyzeDefinedOp(
+ name, ActualArguments{actuals_}, resultSymbol)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool ArgumentAnalyzer::HasDeviceDefinedIntrinsicOpOverride(
+ const std::vector<const char *> &oprNames) const {
+ for (const char *opr : oprNames) {
+ if (HasDeviceDefinedIntrinsicOpOverride(opr)) {
+ return true;
+ }
+ }
+ return false;
+}
+
MaybeExpr ArgumentAnalyzer::TryDefinedOp(
const char *opr, parser::MessageFixedText error, bool isUserOp) {
if (AnyUntypedOrMissingOperand()) {
@@ -5135,7 +5198,7 @@ std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) {
}
}
-bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() {
+bool ArgumentAnalyzer::AnyUntypedOrMissingOperand() const {
for (const auto &actual : actuals_) {
if (!actual ||
(!actual->GetType() && !IsBareNullPointer(actual->UnwrapExpr()))) {
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index da14507..7a492a4 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -270,28 +270,6 @@ struct DesignatorCollector : public evaluate::Traverse<DesignatorCollector,
}
};
-struct VariableFinder : public evaluate::AnyTraverse<VariableFinder> {
- using Base = evaluate::AnyTraverse<VariableFinder>;
- VariableFinder(const SomeExpr &v) : Base(*this), var(v) {}
-
- using Base::operator();
-
- template <typename T>
- bool operator()(const evaluate::Designator<T> &x) const {
- auto copy{x};
- return evaluate::AsGenericExpr(std::move(copy)) == var;
- }
-
- template <typename T>
- bool operator()(const evaluate::FunctionRef<T> &x) const {
- auto copy{x};
- return evaluate::AsGenericExpr(std::move(copy)) == var;
- }
-
-private:
- const SomeExpr &var;
-};
-
std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr) {
return DesignatorCollector{}(expr);
}
@@ -380,10 +358,6 @@ const SomeExpr *HasStorageOverlap(
return nullptr;
}
-bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super) {
- return VariableFinder{sub}(super);
-}
-
// Check if the ActionStmt is actually a [Pointer]AssignmentStmt. This is
// to separate cases where the source has something that looks like an
// assignment, but is semantically wrong (diagnosed by general semantic
diff --git a/flang/lib/Semantics/openmp-utils.h b/flang/lib/Semantics/openmp-utils.h
index 001fbeb..b8ad9ed 100644
--- a/flang/lib/Semantics/openmp-utils.h
+++ b/flang/lib/Semantics/openmp-utils.h
@@ -72,7 +72,6 @@ std::optional<bool> IsContiguous(
std::vector<SomeExpr> GetAllDesignators(const SomeExpr &expr);
const SomeExpr *HasStorageOverlap(
const SomeExpr &base, llvm::ArrayRef<SomeExpr> exprs);
-bool IsSubexpressionOf(const SomeExpr &sub, const SomeExpr &super);
bool IsAssignment(const parser::ActionStmt *x);
bool IsPointerAssignment(const evaluate::Assignment &x);
const parser::Block &GetInnermostExecPart(const parser::Block &block);
diff --git a/flang/lib/Semantics/pointer-assignment.cpp b/flang/lib/Semantics/pointer-assignment.cpp
index 0908769..e767bf8 100644
--- a/flang/lib/Semantics/pointer-assignment.cpp
+++ b/flang/lib/Semantics/pointer-assignment.cpp
@@ -270,18 +270,18 @@ bool PointerAssignmentChecker::Check(const evaluate::FunctionRef<T> &f) {
std::optional<MessageFixedText> msg;
const auto &funcResult{proc->functionResult}; // C1025
if (!funcResult) {
- msg = "%s is associated with the non-existent result of reference to"
- " procedure"_err_en_US;
+ msg =
+ "%s is associated with the non-existent result of reference to procedure"_err_en_US;
} else if (CharacterizeProcedure()) {
// Shouldn't be here in this function unless lhs is an object pointer.
- msg = "Procedure %s is associated with the result of a reference to"
- " function '%s' that does not return a procedure pointer"_err_en_US;
+ msg =
+ "Procedure %s is associated with the result of a reference to function '%s' that does not return a procedure pointer"_err_en_US;
} else if (funcResult->IsProcedurePointer()) {
- msg = "Object %s is associated with the result of a reference to"
- " function '%s' that is a procedure pointer"_err_en_US;
+ msg =
+ "Object %s is associated with the result of a reference to function '%s' that is a procedure pointer"_err_en_US;
} else if (!funcResult->attrs.test(FunctionResult::Attr::Pointer)) {
- msg = "%s is associated with the result of a reference to function '%s'"
- " that is a not a pointer"_err_en_US;
+ msg =
+ "%s is associated with the result of a reference to function '%s' that is not a pointer"_err_en_US;
} else if (isContiguous_ &&
!funcResult->attrs.test(FunctionResult::Attr::Contiguous)) {
auto restorer{common::ScopedSet(lhs_, symbol)};
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 4c3e509..aa006b1 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1545,6 +1545,7 @@ void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
void AccAttributeVisitor::Post(const parser::Name &name) {
auto *symbol{name.symbol};
if (symbol && !dirContext_.empty() && GetContext().withinConstruct) {
+ symbol = &symbol->GetUltimate();
if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() &&
!symbol->has<SubprogramDetails>() && !IsObjectWithDSA(*symbol)) {
if (Symbol * found{currScope().FindSymbol(name.source)}) {
@@ -1553,8 +1554,7 @@ void AccAttributeVisitor::Post(const parser::Name &name) {
} else if (GetContext().defaultDSA == Symbol::Flag::AccNone) {
// 2.5.14.
context_.Say(name.source,
- "The DEFAULT(NONE) clause requires that '%s' must be listed in "
- "a data-mapping clause"_err_en_US,
+ "The DEFAULT(NONE) clause requires that '%s' must be listed in a data-mapping clause"_err_en_US,
symbol->name());
}
}