Skip to content

Commit fc7f9d7

Browse files
authored
[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 #148675.
1 parent 6e0b0ec commit fc7f9d7

File tree

2 files changed

+58
-29
lines changed

2 files changed

+58
-29
lines changed

flang/lib/Semantics/expression.cpp

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class ArgumentAnalyzer {
178178
}
179179
// Find and return a user-defined assignment
180180
std::optional<ProcedureRef> TryDefinedAssignment();
181-
std::optional<ProcedureRef> GetDefinedAssignmentProc();
181+
std::optional<ProcedureRef> GetDefinedAssignmentProc(bool &isAmbiguous);
182182
std::optional<DynamicType> GetType(std::size_t) const;
183183
void Dump(llvm::raw_ostream &);
184184

@@ -191,15 +191,16 @@ class ArgumentAnalyzer {
191191
MaybeExpr AnalyzeExprOrWholeAssumedSizeArray(const parser::Expr &);
192192
bool AreConformable() const;
193193
const Symbol *FindBoundOp(parser::CharBlock, int passIndex,
194-
const Symbol *&generic, bool isSubroutine);
194+
const Symbol *&generic, bool isSubroutine, bool *isAmbiguous = nullptr);
195195
void AddAssignmentConversion(
196196
const DynamicType &lhsType, const DynamicType &rhsType);
197197
bool OkLogicalIntegerAssignment(TypeCategory lhs, TypeCategory rhs);
198198
int GetRank(std::size_t) const;
199199
bool IsBOZLiteral(std::size_t i) const {
200200
return evaluate::IsBOZLiteral(GetExpr(i));
201201
}
202-
void SayNoMatch(const std::string &, bool isAssignment = false);
202+
void SayNoMatch(
203+
const std::string &, bool isAssignment = false, bool isAmbiguous = false);
203204
std::string TypeAsFortran(std::size_t);
204205
bool AnyUntypedOrMissingOperand();
205206

@@ -4781,7 +4782,9 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
47814782
return std::nullopt; // user-defined assignment not allowed for these args
47824783
}
47834784
auto restorer{context_.GetContextualMessages().SetLocation(source_)};
4784-
if (std::optional<ProcedureRef> procRef{GetDefinedAssignmentProc()}) {
4785+
bool isAmbiguous{false};
4786+
if (std::optional<ProcedureRef> procRef{
4787+
GetDefinedAssignmentProc(isAmbiguous)}) {
47854788
if (context_.inWhereBody() && !procRef->proc().IsElemental()) { // C1032
47864789
context_.Say(
47874790
"Defined assignment in WHERE must be elemental, but '%s' is not"_err_en_US,
@@ -4791,9 +4794,11 @@ std::optional<ProcedureRef> ArgumentAnalyzer::TryDefinedAssignment() {
47914794
return std::move(*procRef);
47924795
}
47934796
if (isDefined == Tristate::Yes) {
4794-
if (!lhsType || !rhsType || (lhsRank != rhsRank && rhsRank != 0) ||
4797+
if (isAmbiguous || !lhsType || !rhsType ||
4798+
(lhsRank != rhsRank && rhsRank != 0) ||
47954799
!OkLogicalIntegerAssignment(lhsType->category(), rhsType->category())) {
4796-
SayNoMatch("ASSIGNMENT(=)", true);
4800+
SayNoMatch(
4801+
"ASSIGNMENT(=)", /*isAssignment=*/true, /*isAmbiguous=*/isAmbiguous);
47974802
}
47984803
} else if (!fatalErrors_) {
47994804
CheckAssignmentConformance();
@@ -4822,13 +4827,15 @@ bool ArgumentAnalyzer::OkLogicalIntegerAssignment(
48224827
return true;
48234828
}
48244829

4825-
std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
4830+
std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc(
4831+
bool &isAmbiguous) {
48264832
const Symbol *proc{nullptr};
48274833
bool isProcElemental{false};
48284834
std::optional<int> passedObjectIndex;
48294835
std::string oprNameString{"assignment(=)"};
48304836
parser::CharBlock oprName{oprNameString};
48314837
const auto &scope{context_.context().FindScope(source_)};
4838+
isAmbiguous = false;
48324839
{
48334840
auto restorer{context_.GetContextualMessages().DiscardMessages()};
48344841
if (const Symbol *symbol{scope.FindSymbol(oprName)}) {
@@ -4842,8 +4849,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
48424849
for (std::size_t i{0}; (!proc || isProcElemental) && i < actuals_.size();
48434850
++i) {
48444851
const Symbol *generic{nullptr};
4845-
if (const Symbol *
4846-
binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) {
4852+
if (const Symbol *binding{FindBoundOp(oprName, i, generic,
4853+
/*isSubroutine=*/true, /*isAmbiguous=*/&isAmbiguous)}) {
48474854
// ignore inaccessible type-bound ASSIGNMENT(=) generic
48484855
if (!CheckAccessibleSymbol(scope, DEREF(generic))) {
48494856
const Symbol *resolution{GetBindingResolution(GetType(i), *binding)};
@@ -4967,7 +4974,8 @@ bool ArgumentAnalyzer::AreConformable() const {
49674974

49684975
// Look for a type-bound operator in the type of arg number passIndex.
49694976
const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName,
4970-
int passIndex, const Symbol *&generic, bool isSubroutine) {
4977+
int passIndex, const Symbol *&generic, bool isSubroutine,
4978+
bool *isAmbiguous) {
49714979
const auto *type{GetDerivedTypeSpec(GetType(passIndex))};
49724980
const semantics::Scope *scope{type ? type->scope() : nullptr};
49734981
if (scope) {
@@ -4989,6 +4997,9 @@ const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName,
49894997
// Use the most recent override of the binding, if any
49904998
return scope->FindComponent(binding->name());
49914999
} else {
5000+
if (isAmbiguous) {
5001+
*isAmbiguous = pair.second;
5002+
}
49925003
context_.EmitGenericResolutionError(*generic, pair.second, isSubroutine);
49935004
}
49945005
}
@@ -5072,40 +5083,37 @@ void ArgumentAnalyzer::ConvertBOZAssignmentRHS(const DynamicType &lhsType) {
50725083
}
50735084

50745085
// Report error resolving opr when there is a user-defined one available
5075-
void ArgumentAnalyzer::SayNoMatch(const std::string &opr, bool isAssignment) {
5086+
void ArgumentAnalyzer::SayNoMatch(
5087+
const std::string &opr, bool isAssignment, bool isAmbiguous) {
50765088
std::string type0{TypeAsFortran(0)};
50775089
auto rank0{actuals_[0]->Rank()};
5090+
std::string prefix{"No intrinsic or user-defined "s + opr + " matches"};
5091+
if (isAmbiguous) {
5092+
prefix = "Multiple specific procedures for the generic "s + opr + " match";
5093+
}
50785094
if (actuals_.size() == 1) {
50795095
if (rank0 > 0) {
5080-
context_.Say("No intrinsic or user-defined %s matches "
5081-
"rank %d array of %s"_err_en_US,
5082-
opr, rank0, type0);
5096+
context_.Say("%s rank %d array of %s"_err_en_US, prefix, rank0, type0);
50835097
} else {
5084-
context_.Say("No intrinsic or user-defined %s matches "
5085-
"operand type %s"_err_en_US,
5086-
opr, type0);
5098+
context_.Say("%s operand type %s"_err_en_US, prefix, type0);
50875099
}
50885100
} else {
50895101
std::string type1{TypeAsFortran(1)};
50905102
auto rank1{actuals_[1]->Rank()};
50915103
if (rank0 > 0 && rank1 > 0 && rank0 != rank1) {
5092-
context_.Say("No intrinsic or user-defined %s matches "
5093-
"rank %d array of %s and rank %d array of %s"_err_en_US,
5094-
opr, rank0, type0, rank1, type1);
5104+
context_.Say("%s rank %d array of %s and rank %d array of %s"_err_en_US,
5105+
prefix, rank0, type0, rank1, type1);
50955106
} else if (isAssignment && rank0 != rank1) {
50965107
if (rank0 == 0) {
5097-
context_.Say("No intrinsic or user-defined %s matches "
5098-
"scalar %s and rank %d array of %s"_err_en_US,
5099-
opr, type0, rank1, type1);
5108+
context_.Say("%s scalar %s and rank %d array of %s"_err_en_US, prefix,
5109+
type0, rank1, type1);
51005110
} else {
5101-
context_.Say("No intrinsic or user-defined %s matches "
5102-
"rank %d array of %s and scalar %s"_err_en_US,
5103-
opr, rank0, type0, type1);
5111+
context_.Say("%s rank %d array of %s and scalar %s"_err_en_US, prefix,
5112+
rank0, type0, type1);
51045113
}
51055114
} else {
5106-
context_.Say("No intrinsic or user-defined %s matches "
5107-
"operand types %s and %s"_err_en_US,
5108-
opr, type0, type1);
5115+
context_.Say(
5116+
"%s operand types %s and %s"_err_en_US, prefix, type0, type1);
51095117
}
51105118
}
51115119
}

flang/test/Semantics/bug148675.f90

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
! RUN: %python %S/test_errors.py %s %flang_fc1
2+
module m
3+
type t
4+
integer n
5+
contains
6+
procedure :: assign1 => myassign, assign2 => myassign
7+
generic :: ASSIGNMENT(=) => assign1
8+
generic :: ASSIGNMENT(=) => assign2
9+
end type
10+
contains
11+
subroutine myassign(to, from)
12+
class(t), intent(out) :: to
13+
integer, intent(in) :: from
14+
to%n = from
15+
end
16+
subroutine test
17+
type(t) x
18+
!ERROR: Multiple specific procedures for the generic ASSIGNMENT(=) match operand types TYPE(t) and INTEGER(4)
19+
x = 5
20+
end
21+
end

0 commit comments

Comments
 (0)