[flang-commits] [flang] fc7f9d7 - [flang] Better error message for ambiguous ASSIGNMENT(=) (#148720)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 16 09:09:32 PDT 2025
Author: Peter Klausler
Date: 2025-07-16T09:09:29-07:00
New Revision: fc7f9d795d37cd119831e77e475e4690e4120bdb
URL: https://github.com/llvm/llvm-project/commit/fc7f9d795d37cd119831e77e475e4690e4120bdb
DIFF: https://github.com/llvm/llvm-project/commit/fc7f9d795d37cd119831e77e475e4690e4120bdb.diff
LOG: [flang] Better error message for ambiguous ASSIGNMENT(=) (#148720)
When a type-bound generic ASSIGNMENT(=) procedure is ambiguous for a
particular reference, say so, rather than claiming that no specific
procedure matched the types and ranks of the LHS and RHS.
Fixes https://github.com/llvm/llvm-project/issues/148675.
Added:
flang/test/Semantics/bug148675.f90
Modified:
flang/lib/Semantics/expression.cpp
Removed:
################################################################################
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 53ec3827893d0..14473724f0f40 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -178,7 +178,7 @@ class ArgumentAnalyzer {
}
// Find and return a user-defined assignment
std::optional<ProcedureRef> TryDefinedAssignment();
- std::optional<ProcedureRef> GetDefinedAssignmentProc();
+ std::optional<ProcedureRef> GetDefinedAssignmentProc(bool &isAmbiguous);
std::optional<DynamicType> GetType(std::size_t) const;
void Dump(llvm::raw_ostream &);
@@ -191,7 +191,7 @@ class ArgumentAnalyzer {
MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &);
bool AreConformable() const;
const Symbol *FindBoundOp(parser::CharBlock, int passIndex,
- const Symbol *&generic, bool isSubroutine);
+ const Symbol *&generic, bool isSubroutine, bool *isAmbiguous = nullptr);
void AddAssignmentConversion(
const DynamicType &lhsType, const DynamicType &rhsType);
bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs);
@@ -199,7 +199,8 @@ class ArgumentAnalyzer {
bool IsBOZLiteral(std::size_t i) const {
return evaluate::IsBOZLiteral(GetExpr(i));
}
- void SayNoMatch(const std::string &, bool isAssignment = false);
+ void SayNoMatch(
+ const std::string &, bool isAssignment = false, bool isAmbiguous = false);
std::string TypeAsFortran(std::size_t);
bool AnyUntypedOrMissingOperand();
@@ -4781,7 +4782,9 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
return std::nullopt; // user-defined assignment not allowed for these args
}
auto restorer{context_.GetContextualMessages().SetLocation(source_)};
- if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) {
+ bool isAmbiguous{false};
+ if (std::optional<ProcedureRef> procRef{
+ GetDefinedAssignmentProc(isAmbiguous)}) {
if (context_.inWhereBody() && !procRef->proc().IsElemental()) { // C1032
context_.Say(
"Defined assignment in WHERE must be elemental, but '%s' is not"_err_en_US,
@@ -4791,9 +4794,11 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
return std::move(*procRef);
}
if (isDefined == Tristate::Yes) {
- if (!lhsType || !rhsType || (lhsRank != rhsRank && rhsRank != 0) ||
+ if (isAmbiguous || !lhsType || !rhsType ||
+ (lhsRank != rhsRank && rhsRank != 0) ||
!OkLogicalIntegerAssignment(lhsType->category(), rhsType->category())) {
- SayNoMatch("ASSIGNMENT(=)", true);
+ SayNoMatch(
+ "ASSIGNMENT(=)", /*isAssignment=*/true, /*isAmbiguous=*/isAmbiguous);
}
} else if (!fatalErrors_) {
CheckAssignmentConformance();
@@ -4822,13 +4827,15 @@ bool ArgumentAnalyzer::OkLogicalIntegerAssignment(
return true;
}
-std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
+std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc(
+ bool &isAmbiguous) {
const Symbol *proc{nullptr};
bool isProcElemental{false};
std::optional<int> passedObjectIndex;
std::string oprNameString{"assignment(=)"};
parser::CharBlock oprName{oprNameString};
const auto &scope{context_.context().FindScope(source_)};
+ isAmbiguous = false;
{
auto restorer{context_.GetContextualMessages().DiscardMessages()};
if (const Symbol *symbol{scope.FindSymbol(oprName)}) {
@@ -4842,8 +4849,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
for (std::size_t i{0}; (!proc || isProcElemental) && i < actuals_.size();
++i) {
const Symbol *generic{nullptr};
- if (const Symbol *
- binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) {
+ if (const Symbol *binding{FindBoundOp(oprName, i, generic,
+ /*isSubroutine=*/true, /*isAmbiguous=*/&isAmbiguous)}) {
// ignore inaccessible type-bound ASSIGNMENT(=) generic
if (!CheckAccessibleSymbol(scope, DEREF(generic))) {
const Symbol *resolution{GetBindingResolution(GetType(i), *binding)};
@@ -4967,7 +4974,8 @@ bool ArgumentAnalyzer::AreConformable() const {
// Look for a type-bound operator in the type of arg number passIndex.
const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName,
- int passIndex, const Symbol *&generic, bool isSubroutine) {
+ int passIndex, const Symbol *&generic, bool isSubroutine,
+ bool *isAmbiguous) {
const auto *type{GetDerivedTypeSpec(GetType(passIndex))};
const semantics::Scope *scope{type ? type->scope() : nullptr};
if (scope) {
@@ -4989,6 +4997,9 @@ const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName,
// Use the most recent override of the binding, if any
return scope->FindComponent(binding->name());
} else {
+ if (isAmbiguous) {
+ *isAmbiguous = pair.second;
+ }
context_.EmitGenericResolutionError(*generic, pair.second, isSubroutine);
}
}
@@ -5072,40 +5083,37 @@ void ArgumentAnalyzer::ConvertBOZAssignmentRHS(const DynamicType &lhsType) {
}
// Report error resolving opr when there is a user-defined one available
-void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) {
+void ArgumentAnalyzer::SayNoMatch(
+ const std::string &opr, bool isAssignment, bool isAmbiguous) {
std::string type0{TypeAsFortran(0)};
auto rank0{actuals_[0]->Rank()};
+ std::string prefix{"No intrinsic or user-defined "s + opr + " matches"};
+ if (isAmbiguous) {
+ prefix = "Multiple specific procedures for the generic "s + opr + " match";
+ }
if (actuals_.size() == 1) {
if (rank0 > 0) {
- context_.Say("No intrinsic or user-defined %s matches "
- "rank %d array of %s"_err_en_US,
- opr, rank0, type0);
+ context_.Say("%s rank %d array of %s"_err_en_US, prefix, rank0, type0);
} else {
- context_.Say("No intrinsic or user-defined %s matches "
- "operand type %s"_err_en_US,
- opr, type0);
+ context_.Say("%s operand type %s"_err_en_US, prefix, type0);
}
} else {
std::string type1{TypeAsFortran(1)};
auto rank1{actuals_[1]->Rank()};
if (rank0 > 0 && rank1 > 0 && rank0 != rank1) {
- context_.Say("No intrinsic or user-defined %s matches "
- "rank %d array of %s and rank %d array of %s"_err_en_US,
- opr, rank0, type0, rank1, type1);
+ context_.Say("%s rank %d array of %s and rank %d array of %s"_err_en_US,
+ prefix, rank0, type0, rank1, type1);
} else if (isAssignment && rank0 != rank1) {
if (rank0 == 0) {
- context_.Say("No intrinsic or user-defined %s matches "
- "scalar %s and rank %d array of %s"_err_en_US,
- opr, type0, rank1, type1);
+ context_.Say("%s scalar %s and rank %d array of %s"_err_en_US, prefix,
+ type0, rank1, type1);
} else {
- context_.Say("No intrinsic or user-defined %s matches "
- "rank %d array of %s and scalar %s"_err_en_US,
- opr, rank0, type0, type1);
+ context_.Say("%s rank %d array of %s and scalar %s"_err_en_US, prefix,
+ rank0, type0, type1);
}
} else {
- context_.Say("No intrinsic or user-defined %s matches "
- "operand types %s and %s"_err_en_US,
- opr, type0, type1);
+ context_.Say(
+ "%s operand types %s and %s"_err_en_US, prefix, type0, type1);
}
}
}
diff --git a/flang/test/Semantics/bug148675.f90 b/flang/test/Semantics/bug148675.f90
new file mode 100644
index 0000000000000..5ce117e7bb3df
--- /dev/null
+++ b/flang/test/Semantics/bug148675.f90
@@ -0,0 +1,21 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+module m
+ type t
+ integer n
+ contains
+ procedure :: assign1 => myassign, assign2 => myassign
+ generic :: ASSIGNMENT(=) => assign1
+ generic :: ASSIGNMENT(=) => assign2
+ end type
+ contains
+ subroutine myassign(to, from)
+ class(t), intent(out) :: to
+ integer, intent(in) :: from
+ to%n = from
+ end
+ subroutine test
+ type(t) x
+ !ERROR: Multiple specific procedures for the generic ASSIGNMENT(=) match operand types TYPE(t) and INTEGER(4)
+ x = 5
+ end
+end
More information about the flang-commits
mailing list