Skip to content

Commit cadc70c

Browse files
authored
[flang] Prefer non-elemental to elemental defined operator resolution (llvm#124941)
A non-elemental specific procedure must take precedence over an elemental specific procedure in defined operator generic resolution. Fixes llvm#124777.
1 parent 4927a5e commit cadc70c

File tree

4 files changed

+73
-41
lines changed

4 files changed

+73
-41
lines changed

flang/include/flang/Semantics/expression.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ class ExpressionAnalyzer {
348348
bool CheckDataRef(const DataRef &); // ditto
349349
std::optional<Expr<SubscriptInteger>> GetSubstringBound(
350350
const std::optional<parser::ScalarIntExpr> &);
351-
MaybeExpr AnalyzeDefinedOp(const parser::Name &, ActualArguments &&);
351+
MaybeExpr AnalyzeDefinedOp(
352+
const parser::Name &, ActualArguments &&, const Symbol *&);
352353
MaybeExpr FixMisparsedSubstring(const parser::Designator &);
353354

354355
struct CalleeAndArguments {

flang/lib/Semantics/expression.cpp

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,13 +2834,12 @@ std::pair<const Symbol *, bool> ExpressionAnalyzer::ResolveGeneric(
28342834
// Check for generic or explicit INTRINSIC of the same name in outer scopes.
28352835
// See 15.5.5.2 for details.
28362836
if (!symbol.owner().IsGlobal() && !symbol.owner().IsDerivedType()) {
2837-
for (const std::string &n : GetAllNames(context_, symbol.name())) {
2838-
if (const Symbol *outer{symbol.owner().parent().FindSymbol(n)}) {
2839-
auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
2840-
mightBeStructureConstructor)};
2841-
if (pair.first) {
2842-
return pair;
2843-
}
2837+
if (const Symbol *
2838+
outer{symbol.owner().parent().FindSymbol(symbol.name())}) {
2839+
auto pair{ResolveGeneric(*outer, actuals, adjustActuals, isSubroutine,
2840+
mightBeStructureConstructor)};
2841+
if (pair.first) {
2842+
return pair;
28442843
}
28452844
}
28462845
}
@@ -3635,13 +3634,13 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Expr::Concat &x) {
36353634
// The Name represents a user-defined intrinsic operator.
36363635
// If the actuals match one of the specific procedures, return a function ref.
36373636
// Otherwise report the error in messages.
3638-
MaybeExpr ExpressionAnalyzer::AnalyzeDefinedOp(
3639-
const parser::Name &name, ActualArguments &&actuals) {
3637+
MaybeExpr ExpressionAnalyzer::AnalyzeDefinedOp(const parser::Name &name,
3638+
ActualArguments &&actuals, const Symbol *&symbol) {
36403639
if (auto callee{GetCalleeAndArguments(name, std::move(actuals))}) {
3641-
CHECK(std::holds_alternative<ProcedureDesignator>(callee->u));
3642-
return MakeFunctionRef(name.source,
3643-
std::move(std::get<ProcedureDesignator>(callee->u)),
3644-
std::move(callee->arguments));
3640+
auto &proc{std::get<evaluate::ProcedureDesignator>(callee->u)};
3641+
symbol = proc.GetSymbol();
3642+
return MakeFunctionRef(
3643+
name.source, std::move(proc), std::move(callee->arguments));
36453644
} else {
36463645
return std::nullopt;
36473646
}
@@ -4453,38 +4452,45 @@ MaybeExpr ArgumentAnalyzer::TryDefinedOp(
44534452
parser::Messages buffer;
44544453
auto restorer{context_.GetContextualMessages().SetMessages(buffer)};
44554454
const auto &scope{context_.context().FindScope(source_)};
4456-
if (Symbol *symbol{scope.FindSymbol(oprName)}) {
4455+
4456+
auto FoundOne{[&](MaybeExpr &&thisResult, const Symbol &generic,
4457+
const Symbol *resolution) {
44574458
anyPossibilities = true;
4458-
parser::Name name{symbol->name(), symbol};
4459-
if (!fatalErrors_) {
4460-
result = context_.AnalyzeDefinedOp(name, GetActuals());
4461-
}
4462-
if (result) {
4463-
inaccessible = CheckAccessibleSymbol(scope, *symbol);
4464-
if (inaccessible) {
4465-
result.reset();
4459+
if (thisResult) {
4460+
if (auto thisInaccessible{CheckAccessibleSymbol(scope, generic)}) {
4461+
inaccessible = thisInaccessible;
44664462
} else {
4467-
hit.push_back(symbol);
4468-
hitBuffer = std::move(buffer);
4463+
bool isElemental{IsElementalProcedure(DEREF(resolution))};
4464+
bool hitsAreNonElemental{
4465+
!hit.empty() && !IsElementalProcedure(DEREF(hit[0]))};
4466+
if (isElemental && hitsAreNonElemental) {
4467+
// ignore elemental resolutions in favor of a non-elemental one
4468+
} else {
4469+
if (!isElemental && !hitsAreNonElemental) {
4470+
hit.clear();
4471+
}
4472+
result = std::move(thisResult);
4473+
hit.push_back(resolution);
4474+
hitBuffer = std::move(buffer);
4475+
}
44694476
}
44704477
}
4478+
}};
4479+
4480+
if (Symbol * generic{scope.FindSymbol(oprName)}; generic && !fatalErrors_) {
4481+
parser::Name name{generic->name(), generic};
4482+
const Symbol *resultSymbol{nullptr};
4483+
MaybeExpr possibleResult{context_.AnalyzeDefinedOp(
4484+
name, ActualArguments{actuals_}, resultSymbol)};
4485+
FoundOne(std::move(possibleResult), *generic, resultSymbol);
44714486
}
44724487
for (std::size_t passIndex{0}; passIndex < actuals_.size(); ++passIndex) {
44734488
buffer.clear();
44744489
const Symbol *generic{nullptr};
4475-
if (const Symbol *binding{
4476-
FindBoundOp(oprName, passIndex, generic, false)}) {
4477-
anyPossibilities = true;
4478-
if (MaybeExpr thisResult{TryBoundOp(*binding, passIndex)}) {
4479-
if (auto thisInaccessible{
4480-
CheckAccessibleSymbol(scope, DEREF(generic))}) {
4481-
inaccessible = thisInaccessible;
4482-
} else {
4483-
result = std::move(thisResult);
4484-
hit.push_back(binding);
4485-
hitBuffer = std::move(buffer);
4486-
}
4487-
}
4490+
if (const Symbol *
4491+
binding{FindBoundOp(
4492+
oprName, passIndex, generic, /*isSubroutine=*/false)}) {
4493+
FoundOne(TryBoundOp(*binding, passIndex), DEREF(generic), binding);
44884494
}
44894495
}
44904496
}
@@ -4655,7 +4661,8 @@ std::optional<ProcedureRef> ArgumentAnalyzer::GetDefinedAssignmentProc() {
46554661
}
46564662
for (std::size_t i{0}; !proc && i < actuals_.size(); ++i) {
46574663
const Symbol *generic{nullptr};
4658-
if (const Symbol *binding{FindBoundOp(oprName, i, generic, true)}) {
4664+
if (const Symbol *
4665+
binding{FindBoundOp(oprName, i, generic, /*isSubroutine=*/true)}) {
46594666
if (CheckAccessibleSymbol(scope, DEREF(generic))) {
46604667
// ignore inaccessible type-bound ASSIGNMENT(=) generic
46614668
} else if (const Symbol *

flang/test/Semantics/bug12477.f90

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
!RUN: %flang_fc1 -fsyntax-only %s 2>&1 | FileCheck %s --allow-empty
2+
!CHECK-NOT: error:
3+
module m
4+
type t
5+
contains
6+
procedure nonelemental
7+
generic :: operator(+) => nonelemental
8+
end type
9+
interface operator(+)
10+
procedure elemental
11+
end interface
12+
contains
13+
type(t) elemental function elemental (a, b)
14+
class(t), intent(in) :: a, b
15+
elemental = t()
16+
end
17+
type(t) function nonelemental (a, b)
18+
class(t), intent(in) :: a, b(:)
19+
nonelemental = t()
20+
end
21+
end
22+
program main
23+
use m
24+
type(t) x, y(1)
25+
x = x + y ! ok
26+
end

flang/test/Semantics/resolve110.f90

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
! RUN: %python %S/test_errors.py %s %flang_fc1
22
! Exercise ways to define and extend non-type-bound generics
3-
! TODO: crashes compiler (infinite recursion) when build with MSVC
4-
! XFAIL: system-windows
53

64
module m1
75
type :: t1; end type

0 commit comments

Comments
 (0)