Skip to content

[flang] Fix error from semantics on use associated procedure pointer #107928

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions flang/include/flang/Semantics/scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ class Scope {
const_iterator cend() const { return symbols_.cend(); }

// Return symbols in declaration order (the iterators above are in name order)
// When a generic procedure interface shadows a derived type or specific
// procedure, only the generic's symbol appears in the output.
SymbolVector GetSymbols() const;
MutableSymbolVector GetSymbols();

Expand Down
7 changes: 7 additions & 0 deletions flang/lib/Semantics/compute-offsets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ void ComputeOffsetsHelper::Compute(Scope &scope) {
dependents_.find(symbol) == dependents_.end() &&
equivalenceBlock_.find(symbol) == equivalenceBlock_.end()) {
DoSymbol(*symbol);
if (auto *generic{symbol->detailsIf<GenericDetails>()}) {
if (Symbol * specific{generic->specific()};
specific && !FindCommonBlockContaining(*specific)) {
// might be a shadowed procedure pointer
DoSymbol(*specific);
}
}
}
}
// Ensure that the size is a multiple of the alignment
Expand Down
7 changes: 6 additions & 1 deletion flang/lib/Semantics/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ class ArgumentAnalyzer {
// or procedure pointer reference in a ProcedureDesignator.
MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
const Symbol &last{ref.GetLastSymbol()};
const Symbol &symbol{BypassGeneric(last).GetUltimate()};
const Symbol &specific{BypassGeneric(last)};
const Symbol &symbol{specific.GetUltimate()};
if (semantics::IsProcedure(symbol)) {
if (symbol.attrs().test(semantics::Attr::ABSTRACT)) {
Say("Abstract procedure interface '%s' may not be used as a designator"_err_en_US,
Expand All @@ -226,6 +227,10 @@ MaybeExpr ExpressionAnalyzer::Designate(DataRef &&ref) {
} else if (!symbol.attrs().test(semantics::Attr::INTRINSIC)) {
if (symbol.has<semantics::GenericDetails>()) {
Say("'%s' is not a specific procedure"_err_en_US, last.name());
} else if (IsProcedurePointer(specific)) {
// For procedure pointers, retain associations so that data accesses
// from client modules will work.
return Expr<SomeType>{ProcedureDesignator{specific}};
} else {
return Expr<SomeType>{ProcedureDesignator{symbol}};
}
Expand Down
69 changes: 58 additions & 11 deletions flang/lib/Semantics/resolve-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,20 @@ class ScopeHandler : public ImplicitRulesVisitor {
return *derivedType;
}
}
} else if constexpr (std::is_same_v<ProcEntityDetails, D>) {
if (auto *d{symbol->detailsIf<GenericDetails>()}) {
if (!d->derivedType()) {
// procedure pointer with same name as a generic
auto *specific{d->specific()};
if (!specific) {
specific = &currScope().MakeSymbol(name, attrs, std::move(details));
d->set_specific(*specific);
} else {
SayAlreadyDeclared(name, *specific);
}
return *specific;
}
}
}
if (symbol->CanReplaceDetails(details)) {
// update the existing symbol
Expand Down Expand Up @@ -3035,14 +3049,26 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
return;
}
const Symbol &useUltimate{useSymbol.GetUltimate()};
const auto *useGeneric{useUltimate.detailsIf<GenericDetails>()};
if (localSymbol->has<UnknownDetails>()) {
localSymbol->set_details(UseDetails{localName, useSymbol});
localSymbol->attrs() =
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE, Attr::SAVE};
localSymbol->implicitAttrs() =
localSymbol->attrs() & Attrs{Attr::ASYNCHRONOUS, Attr::VOLATILE};
localSymbol->flags() = useSymbol.flags();
return;
if (useGeneric && useGeneric->specific() &&
IsProcedurePointer(*useGeneric->specific())) {
// We are use-associating a generic that shadows a procedure pointer.
// Local references that might be made to that procedure pointer should
// use a UseDetails symbol for proper data addressing. So create an
// empty local generic now into which the use-associated generic may
// be copied.
localSymbol->set_details(GenericDetails{});
localSymbol->get<GenericDetails>().set_kind(useGeneric->kind());
} else { // just create UseDetails
localSymbol->set_details(UseDetails{localName, useSymbol});
localSymbol->attrs() =
useSymbol.attrs() & ~Attrs{Attr::PUBLIC, Attr::PRIVATE, Attr::SAVE};
localSymbol->implicitAttrs() =
localSymbol->attrs() & Attrs{Attr::ASYNCHRONOUS, Attr::VOLATILE};
localSymbol->flags() = useSymbol.flags();
return;
}
}

Symbol &localUltimate{localSymbol->GetUltimate()};
Expand All @@ -3066,10 +3092,7 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
// - anything other than a derived type, non-generic procedure, or
// generic procedure being combined with something other than an
// prior USE association of itself

auto *localGeneric{localUltimate.detailsIf<GenericDetails>()};
const auto *useGeneric{useUltimate.detailsIf<GenericDetails>()};

Symbol *localDerivedType{nullptr};
if (localUltimate.has<DerivedTypeDetails>()) {
localDerivedType = &localUltimate;
Expand Down Expand Up @@ -3261,6 +3284,15 @@ void ModuleVisitor::DoAddUse(SourceName location, SourceName localName,
// At this point, there must be at least one generic interface.
CHECK(localGeneric || (useGeneric && (localDerivedType || localProcedure)));

// Ensure that a use-associated specific procedure that is a procedure
// pointer is properly represented as a USE association of an entity.
if (IsProcedurePointer(useProcedure)) {
Symbol &combined{currScope().MakeSymbol(localSymbol->name(),
useProcedure->attrs(), UseDetails{localName, *useProcedure})};
combined.flags() |= useProcedure->flags();
combinedProcedure = &combined;
}

if (localGeneric) {
// Create a local copy of a previously use-associated generic so that
// it can be locally extended without corrupting the original.
Expand Down Expand Up @@ -5079,7 +5111,22 @@ bool DeclarationVisitor::HasCycle(

Symbol &DeclarationVisitor::DeclareProcEntity(
const parser::Name &name, Attrs attrs, const Symbol *interface) {
Symbol &symbol{DeclareEntity<ProcEntityDetails>(name, attrs)};
Symbol *proc{nullptr};
if (auto *extant{FindInScope(name)}) {
if (auto *d{extant->detailsIf<GenericDetails>()}; d && !d->derivedType()) {
// procedure pointer with same name as a generic
if (auto *specific{d->specific()}) {
SayAlreadyDeclared(name, *specific);
} else {
// Create the ProcEntityDetails symbol in the scope as the "specific()"
// symbol behind an existing GenericDetails symbol of the same name.
proc = &Resolve(name,
currScope().MakeSymbol(name.source, attrs, ProcEntityDetails{}));
d->set_specific(*proc);
}
}
}
Symbol &symbol{proc ? *proc : DeclareEntity<ProcEntityDetails>(name, attrs)};
if (auto *details{symbol.detailsIf<ProcEntityDetails>()}) {
if (context().HasError(symbol)) {
} else if (HasCycle(symbol, interface)) {
Expand Down
3 changes: 2 additions & 1 deletion flang/lib/Semantics/symbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,9 @@ const Symbol *GenericDetails::CheckSpecific() const {
}
Symbol *GenericDetails::CheckSpecific() {
if (specific_ && !specific_->has<UseErrorDetails>()) {
const Symbol &ultimate{specific_->GetUltimate()};
for (const Symbol &proc : specificProcs_) {
if (&proc == specific_) {
if (&proc.GetUltimate() == &ultimate) {
return nullptr;
}
}
Expand Down
17 changes: 17 additions & 0 deletions flang/test/Semantics/generic10.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
module m
procedure(func), pointer :: foo
interface foo
procedure :: foo
end interface
contains
function func(x)
func = x
end
end

program main
use m
!CHECK: foo => func
foo => func
end
Loading