Skip to content

Commit e34d603

Browse files
authored
[flang] More information on generic resolution failures (llvm#164738)
When a generic procedure reference does not match any of its specific procedures, run through them and emit the errors for each attempted match, so that the user has more information to resolve the problem by adjusting the actual arguments.
1 parent 1e237b1 commit e34d603

File tree

7 files changed

+123
-53
lines changed

7 files changed

+123
-53
lines changed

flang/include/flang/Evaluate/call.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class ActualArgument {
112112
int Rank() const;
113113
bool operator==(const ActualArgument &) const;
114114
llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
115+
std::string AsFortran() const;
115116

116117
std::optional<parser::CharBlock> keyword() const { return keyword_; }
117118
ActualArgument &set_keyword(parser::CharBlock x) {

flang/include/flang/Semantics/expression.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,16 @@ class ExpressionAnalyzer {
367367
using AdjustActuals =
368368
std::optional<std::function<bool(const Symbol &, ActualArguments &)>>;
369369
const Symbol *ResolveForward(const Symbol &);
370-
std::pair<const Symbol *, bool /* failure due ambiguity */> ResolveGeneric(
371-
const Symbol &, const ActualArguments &, const AdjustActuals &,
372-
bool isSubroutine, bool mightBeStructureConstructor = false);
373-
void EmitGenericResolutionError(
374-
const Symbol &, bool dueToNullActuals, bool isSubroutine);
370+
struct GenericResolution {
371+
const Symbol *specific{nullptr};
372+
bool failedDueToAmbiguity{false};
373+
SymbolVector tried{};
374+
};
375+
GenericResolution ResolveGeneric(const Symbol &, const ActualArguments &,
376+
const AdjustActuals &, bool isSubroutine, SymbolVector &&tried,
377+
bool mightBeStructureConstructor = false);
378+
void EmitGenericResolutionError(const Symbol &, bool dueToNullActuals,
379+
bool isSubroutine, ActualArguments &, const SymbolVector &);
375380
const Symbol &AccessSpecific(
376381
const Symbol &originalGeneric, const Symbol &specific);
377382
std::optional<CalleeAndArguments> GetCalleeAndArguments(const parser::Name &,

flang/lib/Evaluate/formatting.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,13 @@ llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const {
252252
return o;
253253
}
254254

255+
std::string ActualArgument::AsFortran() const {
256+
std::string result;
257+
llvm::raw_string_ostream sstream(result);
258+
AsFortran(sstream);
259+
return result;
260+
}
261+
255262
llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const {
256263
return o << name;
257264
}

flang/lib/Semantics/check-call.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2241,10 +2241,9 @@ static void CheckSpecificIntrinsic(const characteristics::Procedure &proc,
22412241
}
22422242
}
22432243

2244-
static parser::Messages CheckExplicitInterface(
2245-
const characteristics::Procedure &proc, evaluate::ActualArguments &actuals,
2246-
SemanticsContext &context, const Scope *scope,
2247-
const evaluate::SpecificIntrinsic *intrinsic,
2244+
parser::Messages CheckExplicitInterface(const characteristics::Procedure &proc,
2245+
evaluate::ActualArguments &actuals, SemanticsContext &context,
2246+
const Scope *scope, const evaluate::SpecificIntrinsic *intrinsic,
22482247
bool allowActualArgumentConversions, bool extentErrors,
22492248
bool ignoreImplicitVsExplicit) {
22502249
evaluate::FoldingContext &foldingContext{context.foldingContext()};

flang/lib/Semantics/check-call.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,8 @@
1212
#define FORTRAN_SEMANTICS_CHECK_CALL_H_
1313

1414
#include "flang/Evaluate/call.h"
15+
#include "flang/Parser/message.h"
1516

16-
namespace Fortran::parser {
17-
class Messages;
18-
class ContextualMessages;
19-
} // namespace Fortran::parser
2017
namespace Fortran::evaluate::characteristics {
2118
struct Procedure;
2219
}
@@ -47,6 +44,12 @@ bool CheckArgumentIsConstantExprInRange(
4744
const evaluate::ActualArguments &actuals, int index, int lowerBound,
4845
int upperBound, parser::ContextualMessages &messages);
4946

47+
parser::Messages CheckExplicitInterface(
48+
const evaluate::characteristics::Procedure &, evaluate::ActualArguments &,
49+
SemanticsContext &, const Scope *, const evaluate::SpecificIntrinsic *,
50+
bool allowActualArgumentConversions, bool extentErrors,
51+
bool ignoreImplicitVsExplicit);
52+
5053
// Checks actual arguments for the purpose of resolving a generic interface.
5154
bool CheckInterfaceForGeneric(const evaluate::characteristics::Procedure &,
5255
evaluate::ActualArguments &, SemanticsContext &,

flang/lib/Semantics/expression.cpp

Lines changed: 74 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2552,11 +2552,12 @@ auto ExpressionAnalyzer::AnalyzeProcedureComponentRef(
25522552
}
25532553
return true;
25542554
}};
2555-
auto pair{
2556-
ResolveGeneric(generic, arguments, adjustment, isSubroutine)};
2557-
sym = pair.first;
2555+
auto result{ResolveGeneric(
2556+
generic, arguments, adjustment, isSubroutine, SymbolVector{})};
2557+
sym = result.specific;
25582558
if (!sym) {
2559-
EmitGenericResolutionError(generic, pair.second, isSubroutine);
2559+
EmitGenericResolutionError(generic, result.failedDueToAmbiguity,
2560+
isSubroutine, arguments, result.tried);
25602561
return std::nullopt;
25612562
}
25622563
// re-resolve the name to the specific binding
@@ -2886,10 +2887,10 @@ const Symbol *ExpressionAnalyzer::ResolveForward(const Symbol &symbol) {
28862887

28872888
// Resolve a call to a generic procedure with given actual arguments.
28882889
// adjustActuals is called on procedure bindings to handle pass arg.
2889-
std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
2890-
const Symbol &symbol, const ActualArguments &actuals,
2891-
const AdjustActuals &adjustActuals, bool isSubroutine,
2892-
bool mightBeStructureConstructor) {
2890+
auto ExpressionAnalyzer::ResolveGeneric(const Symbol &symbol,
2891+
const ActualArguments &actuals, const AdjustActuals &adjustActuals,
2892+
bool isSubroutine, SymbolVector &&tried, bool mightBeStructureConstructor)
2893+
-> GenericResolution {
28932894
const Symbol &ultimate{symbol.GetUltimate()};
28942895
// Check for a match with an explicit INTRINSIC
28952896
const Symbol *explicitIntrinsic{nullptr};
@@ -2948,7 +2949,7 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
29482949
// cannot be unambiguously distinguished
29492950
// Underspecified external procedure actual arguments can
29502951
// also lead to ambiguity.
2951-
return {nullptr, true /* due to ambiguity */};
2952+
return {nullptr, true /* due to ambiguity */, std::move(tried)};
29522953
}
29532954
}
29542955
if (!procedure->IsElemental()) {
@@ -2959,6 +2960,8 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
29592960
}
29602961
crtMatchingDistance = ComputeCudaMatchingDistance(
29612962
context_.languageFeatures(), *procedure, localActuals);
2963+
} else {
2964+
tried.push_back(*specific);
29622965
}
29632966
}
29642967
}
@@ -3038,11 +3041,12 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
30383041
// Check parent derived type
30393042
if (const auto *parentScope{symbol.owner().GetDerivedTypeParent()}) {
30403043
if (const Symbol * extended{parentScope->FindComponent(symbol.name())}) {
3041-
auto pair{ResolveGeneric(
3042-
*extended, actuals, adjustActuals, isSubroutine, false)};
3043-
if (pair.first) {
3044-
return pair;
3044+
auto result{ResolveGeneric(*extended, actuals, adjustActuals,
3045+
isSubroutine, std::move(tried), false)};
3046+
if (result.specific != nullptr) {
3047+
return result;
30453048
}
3049+
tried = std::move(result.tried);
30463050
}
30473051
}
30483052
// Structure constructor?
@@ -3054,14 +3058,15 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
30543058
if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) {
30553059
if (const Symbol *
30563060
outer{symbol.owner().parent().FindSymbol(symbol.name())}) {
3057-
auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
3058-
mightBeStructureConstructor)};
3059-
if (pair.first) {
3060-
return pair;
3061+
auto result{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
3062+
std::move(tried), mightBeStructureConstructor)};
3063+
if (result.specific) {
3064+
return result;
30613065
}
3066+
tried = std::move(result.tried);
30623067
}
30633068
}
3064-
return {nullptr, false};
3069+
return {nullptr, false, std::move(tried)};
30653070
}
30663071

30673072
const Symbol &ExpressionAnalyzer::AccessSpecific(
@@ -3098,16 +3103,39 @@ const Symbol &ExpressionAnalyzer::AccessSpecific(
30983103
}
30993104
}
31003105

3101-
void ExpressionAnalyzer::EmitGenericResolutionError(
3102-
const Symbol &symbol, bool dueToAmbiguity, bool isSubroutine) {
3103-
Say(dueToAmbiguity
3104-
? "The actual arguments to the generic procedure '%s' matched multiple specific procedures, perhaps due to use of NULL() without MOLD= or an actual procedure with an implicit interface"_err_en_US
3105-
: semantics::IsGenericDefinedOp(symbol)
3106-
? "No specific procedure of generic operator '%s' matches the actual arguments"_err_en_US
3107-
: isSubroutine
3108-
? "No specific subroutine of generic '%s' matches the actual arguments"_err_en_US
3109-
: "No specific function of generic '%s' matches the actual arguments"_err_en_US,
3110-
symbol.name());
3106+
void ExpressionAnalyzer::EmitGenericResolutionError(const Symbol &symbol,
3107+
bool dueToAmbiguity, bool isSubroutine, ActualArguments &arguments,
3108+
const SymbolVector &tried) {
3109+
if (auto *msg{Say(dueToAmbiguity
3110+
? "The actual arguments to the generic procedure '%s' matched multiple specific procedures, perhaps due to use of NULL() without MOLD= or an actual procedure with an implicit interface"_err_en_US
3111+
: semantics::IsGenericDefinedOp(symbol)
3112+
? "No specific procedure of generic operator '%s' matches the actual arguments"_err_en_US
3113+
: isSubroutine
3114+
? "No specific subroutine of generic '%s' matches the actual arguments"_err_en_US
3115+
: "No specific function of generic '%s' matches the actual arguments"_err_en_US,
3116+
symbol.name())}) {
3117+
parser::ContextualMessages &messages{GetContextualMessages()};
3118+
semantics::Scope &scope{context_.FindScope(messages.at())};
3119+
for (const Symbol &specific : tried) {
3120+
if (auto procChars{characteristics::Procedure::Characterize(
3121+
specific, GetFoldingContext())}) {
3122+
if (procChars->HasExplicitInterface()) {
3123+
if (auto reasons{semantics::CheckExplicitInterface(*procChars,
3124+
arguments, context_, &scope, /*intrinsic=*/nullptr,
3125+
/*allocActualArgumentConversions=*/false,
3126+
/*extentErrors=*/false,
3127+
/*ignoreImplicitVsExplicit=*/false)};
3128+
!reasons.empty()) {
3129+
reasons.AttachTo(
3130+
msg->Attach(specific.name(),
3131+
"Specific procedure '%s' does not match the actual arguments because"_en_US,
3132+
specific.name()),
3133+
parser::Severity::None);
3134+
}
3135+
}
3136+
}
3137+
}
3138+
}
31113139
}
31123140

31133141
auto ExpressionAnalyzer::GetCalleeAndArguments(
@@ -3146,12 +3174,14 @@ auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name,
31463174
bool isGenericInterface{ultimate.has<semantics::GenericDetails>()};
31473175
bool isExplicitIntrinsic{ultimate.attrs().test(semantics::Attr::INTRINSIC)};
31483176
const Symbol *resolution{nullptr};
3177+
SymbolVector tried;
31493178
if (isGenericInterface || isExplicitIntrinsic) {
31503179
ExpressionAnalyzer::AdjustActuals noAdjustment;
3151-
auto pair{ResolveGeneric(*symbol, arguments, noAdjustment, isSubroutine,
3152-
mightBeStructureConstructor)};
3153-
resolution = pair.first;
3154-
dueToAmbiguity = pair.second;
3180+
auto result{ResolveGeneric(*symbol, arguments, noAdjustment, isSubroutine,
3181+
SymbolVector{}, mightBeStructureConstructor)};
3182+
resolution = result.specific;
3183+
dueToAmbiguity = result.failedDueToAmbiguity;
3184+
tried = std::move(result.tried);
31553185
if (resolution) {
31563186
if (context_.GetPPCBuiltinsScope() &&
31573187
resolution->name().ToString().rfind("__ppc_", 0) == 0) {
@@ -3182,7 +3212,8 @@ auto ExpressionAnalyzer::GetCalleeAndArguments(const parser::Name &name,
31823212
std::move(specificCall->arguments)};
31833213
} else {
31843214
if (isGenericInterface) {
3185-
EmitGenericResolutionError(*symbol, dueToAmbiguity, isSubroutine);
3215+
EmitGenericResolutionError(
3216+
*symbol, dueToAmbiguity, isSubroutine, arguments, tried);
31863217
}
31873218
return std::nullopt;
31883219
}
@@ -4955,8 +4986,10 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc(
49554986
auto restorer{context_.GetContextualMessages().DiscardMessages()};
49564987
if (const Symbol *symbol{scope.FindSymbol(oprName)}) {
49574988
ExpressionAnalyzer::AdjustActuals noAdjustment;
4958-
proc =
4959-
context_.ResolveGeneric(*symbol, actuals_, noAdjustment, true).first;
4989+
proc = context_
4990+
.ResolveGeneric(
4991+
*symbol, actuals_, noAdjustment, true, SymbolVector{})
4992+
.specific;
49604993
if (proc) {
49614994
isProcElemental = IsElementalProcedure(*proc);
49624995
}
@@ -5105,17 +5138,18 @@ const Symbol *ArgumentAnalyzer::FindBoundOp(parser::CharBlock oprName,
51055138
[&](const Symbol &proc, ActualArguments &) {
51065139
return passIndex == GetPassIndex(proc).value_or(-1);
51075140
}};
5108-
auto pair{
5109-
context_.ResolveGeneric(*generic, actuals_, adjustment, isSubroutine)};
5110-
if (const Symbol *binding{pair.first}) {
5141+
auto result{context_.ResolveGeneric(
5142+
*generic, actuals_, adjustment, isSubroutine, SymbolVector{})};
5143+
if (const Symbol *binding{result.specific}) {
51115144
CHECK(binding->has<semantics::ProcBindingDetails>());
51125145
// Use the most recent override of the binding, if any
51135146
return scope->FindComponent(binding->name());
51145147
} else {
51155148
if (isAmbiguous) {
5116-
*isAmbiguous = pair.second;
5149+
*isAmbiguous = result.failedDueToAmbiguity;
51175150
}
5118-
context_.EmitGenericResolutionError(*generic, pair.second, isSubroutine);
5151+
context_.EmitGenericResolutionError(*generic, result.failedDueToAmbiguity,
5152+
isSubroutine, actuals_, result.tried);
51195153
}
51205154
}
51215155
return nullptr;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
! RUN: not %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s
2+
module m
3+
interface generic
4+
procedure :: sub1, sub2
5+
end interface
6+
contains
7+
subroutine sub1(x)
8+
end
9+
subroutine sub2(j)
10+
end
11+
end
12+
13+
program test
14+
use m
15+
!CHECK: error: No specific subroutine of generic 'generic' matches the actual arguments
16+
!CHECK: Specific procedure 'sub1' does not match the actual arguments
17+
!CHECK: Actual argument type 'REAL(8)' is not compatible with dummy argument type 'REAL(4)'
18+
!CHECK: Specific procedure 'sub2' does not match the actual arguments
19+
!CHECK: Actual argument type 'REAL(8)' is not compatible with dummy argument type 'INTEGER(4)'
20+
call generic(1.d0)
21+
end

0 commit comments

Comments
 (0)