Skip to content

[flang][hlfir] Fixed missing deallocation for components of function … #67768

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 2 commits into from
Oct 2, 2023
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
47 changes: 29 additions & 18 deletions flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,14 +428,36 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
}

if (allocatedResult) {
// The result must be optionally destroyed (if it is of a derived type
// that may need finalization or deallocation of the components).
// For an allocatable result we have to free the memory allocated
// for the top-level entity. Note that the Destroy calls below
// do not deallocate the top-level entity. The two clean-ups
// must be pushed in reverse order, so that the final order is:
// Destroy(desc)
// free(desc->base_addr)
allocatedResult->match(
[&](const fir::MutableBoxValue &box) {
if (box.isAllocatable()) {
// 9.7.3.2 point 4. Deallocate allocatable results. Note that
// finalization was done independently by calling
// genDerivedTypeDestroy above and is not triggered by this inline
// deallocation.
fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, box]() {
fir::factory::genFreememIfAllocated(*bldr, loc, box);
});
}
},
[](const auto &) {});

// 7.5.6.3 point 5. Derived-type finalization for nonpointer function.
// Check if the derived-type is finalizable if it is a monomorphic
// derived-type.
// For polymorphic and unlimited polymorphic enities call the runtime
// in any cases.
std::optional<Fortran::evaluate::DynamicType> retTy =
caller.getCallDescription().proc().GetType();
bool cleanupWithDestroy = false;
// With HLFIR lowering, isElemental must be set to true
// if we are producing an elemental call. In this case,
// the elemental results must not be destroyed, instead,
Expand All @@ -451,34 +473,23 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
fir::runtime::genDerivedTypeDestroy(*bldr, loc,
fir::getBase(*allocatedResult));
});
cleanupWithDestroy = true;
} else {
const Fortran::semantics::DerivedTypeSpec &typeSpec =
retTy->GetDerivedTypeSpec();
if (Fortran::semantics::IsFinalizable(typeSpec)) {
// If the result type may require finalization
// or have allocatable components, we need to make sure
// everything is properly finalized/deallocated.
if (Fortran::semantics::MayRequireFinalization(typeSpec) ||
// We can use DerivedTypeDestroy even if finalization is not needed.
hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) {
auto *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {
mlir::Value box = bldr->createBox(loc, *allocatedResult);
fir::runtime::genDerivedTypeDestroy(*bldr, loc, box);
});
cleanupWithDestroy = true;
}
}
}
allocatedResult->match(
[&](const fir::MutableBoxValue &box) {
if (box.isAllocatable() && !cleanupWithDestroy) {
// 9.7.3.2 point 4. Deallocate allocatable results. Note that
// finalization was done independently by calling
// genDerivedTypeDestroy above and is not triggered by this inline
// deallocation.
fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, box]() {
fir::factory::genFreememIfAllocated(*bldr, loc, box);
});
}
},
[](const auto &) {});
return *allocatedResult;
}

Expand Down
197 changes: 197 additions & 0 deletions flang/test/Lower/HLFIR/function-return-destroy.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
! RUN: bbc -emit-hlfir -polymorphic-type %s -o - -I nowhere | FileCheck %s

module types
type t1
real :: x
end type t1
type t2
real, allocatable :: x
end type t2
type t3
real, pointer :: p
end type t3
type t4
type(t1) :: c
end type t4
type t5
type(t2) :: c
end type t5
type t6
contains
final :: finalize_t6
end type t6
type, extends(t1) :: t7
end type t7
type, extends(t2) :: t8
end type t8
type, extends(t6) :: t9
end type t9
contains
subroutine finalize_t6(x)
type(t6), intent(inout) :: x
end subroutine finalize_t6
end module types

subroutine test1
use types
interface
function ret_type_t1
use types
type(t1) :: ret_type_t1
end function ret_type_t1
end interface
type(t1) :: x
x = ret_type_t1()
end subroutine test1
! CHECK-LABEL: func.func @_QPtest1() {
! CHECK-NOT: fir.call{{.*}}Destroy

subroutine test1a
use types
interface
function ret_type_t1a
use types
type(t1), allocatable :: ret_type_t1a
end function ret_type_t1a
end interface
type(t1), allocatable :: x
x = ret_type_t1a()
end subroutine test1a
! CHECK-LABEL: func.func @_QPtest1a() {
! CHECK-NOT: fir.call{{.*}}Destroy
! CHECK: fir.if %{{.*}} {
! CHECK-NEXT: fir.freemem %{{.*}} : !fir.heap<!fir.type<_QMtypesTt1{x:f32}>>
! CHECK-NOT: fir.call{{.*}}Destroy
! CHECK: fir.if %{{.*}} {
! CHECK: fir.call @_FortranAAllocatableDeallocate({{.*}}) fastmath<contract> : (!fir.ref<!fir.box<none>>, i1, !fir.box<none>, !fir.ref<i8>, i32) -> i32
! CHECK-NOT: fir.call{{.*}}Destroy

subroutine test1c
use types
interface
function ret_class_t1
use types
class(t1), allocatable :: ret_class_t1
end function ret_class_t1
end interface
type(t1) :: x
x = ret_class_t1()
end subroutine test1c
! CHECK-LABEL: func.func @_QPtest1c() {
! CHECK: fir.call @_FortranADestroy
! CHECK: fir.if %{{.*}} {
! CHECK-NEXT: fir.freemem %{{.*}} : !fir.heap<!fir.type<_QMtypesTt1{x:f32}>>

subroutine test2
use types
interface
function ret_type_t2
use types
type(t2) :: ret_type_t2
end function ret_type_t2
end interface
type(t2) :: x
x = ret_type_t2()
end subroutine test2
! CHECK-LABEL: func.func @_QPtest2() {
! CHECK: fir.call @_FortranADestroy

subroutine test3
use types
interface
function ret_type_t3
use types
type(t3) :: ret_type_t3
end function ret_type_t3
end interface
type(t3) :: x
x = ret_type_t3()
end subroutine test3
! CHECK-LABEL: func.func @_QPtest3() {
! CHECK-NOT: fir.call{{.*}}Destroy

subroutine test4
use types
interface
function ret_type_t4
use types
type(t4) :: ret_type_t4
end function ret_type_t4
end interface
type(t4) :: x
x = ret_type_t4()
end subroutine test4
! CHECK-LABEL: func.func @_QPtest4() {
! CHECK-NOT: fir.call{{.*}}Destroy

subroutine test5
use types
interface
function ret_type_t5
use types
type(t5) :: ret_type_t5
end function ret_type_t5
end interface
type(t5) :: x
x = ret_type_t5()
end subroutine test5
! CHECK-LABEL: func.func @_QPtest5() {
! CHECK: fir.call @_FortranADestroy

subroutine test6
use types
interface
function ret_type_t6
use types
type(t6) :: ret_type_t6
end function ret_type_t6
end interface
type(t6) :: x
x = ret_type_t6()
end subroutine test6
! CHECK-LABEL: func.func @_QPtest6() {
! CHECK: fir.call @_FortranADestroy
! CHECK: fir.call @_FortranADestroy

subroutine test7
use types
interface
function ret_type_t7
use types
type(t7) :: ret_type_t7
end function ret_type_t7
end interface
type(t7) :: x
x = ret_type_t7()
end subroutine test7
! CHECK-LABEL: func.func @_QPtest7() {
! CHECK-NOT: fir.call{{.*}}Destroy

subroutine test8
use types
interface
function ret_type_t8
use types
type(t8) :: ret_type_t8
end function ret_type_t8
end interface
type(t8) :: x
x = ret_type_t8()
end subroutine test8
! CHECK-LABEL: func.func @_QPtest8() {
! CHECK: fir.call @_FortranADestroy

subroutine test9
use types
interface
function ret_type_t9
use types
type(t9) :: ret_type_t9
end function ret_type_t9
end interface
type(t9) :: x
x = ret_type_t9()
end subroutine test9
! CHECK-LABEL: func.func @_QPtest9() {
! CHECK: fir.call @_FortranADestroy
! CHECK: fir.call @_FortranADestroy