diff --git a/flang/include/flang/Runtime/allocatable.h b/flang/include/flang/Runtime/allocatable.h --- a/flang/include/flang/Runtime/allocatable.h +++ b/flang/include/flang/Runtime/allocatable.h @@ -95,8 +95,9 @@ // with the other APIs for allocatables.) The destination descriptor // must be initialized. std::int32_t RTNAME(MoveAlloc)(Descriptor &to, Descriptor &from, - bool hasStat = false, const Descriptor *errMsg = nullptr, - const char *sourceFile = nullptr, int sourceLine = 0); + const typeInfo::DerivedType *, bool hasStat = false, + const Descriptor *errMsg = nullptr, const char *sourceFile = nullptr, + int sourceLine = 0); // Deallocates an allocatable. Finalizes elements &/or components as needed. // The allocatable is left in an initialized state suitable for reallocation diff --git a/flang/lib/Optimizer/Builder/Runtime/Allocatable.cpp b/flang/lib/Optimizer/Builder/Runtime/Allocatable.cpp --- a/flang/lib/Optimizer/Builder/Runtime/Allocatable.cpp +++ b/flang/lib/Optimizer/Builder/Runtime/Allocatable.cpp @@ -22,9 +22,20 @@ mlir::FunctionType fTy{func.getFunctionType()}; mlir::Value sourceFile{fir::factory::locationToFilename(builder, loc)}; mlir::Value sourceLine{ - fir::factory::locationToLineNo(builder, loc, fTy.getInput(5))}; + fir::factory::locationToLineNo(builder, loc, fTy.getInput(6))}; + mlir::Value declaredTypeDesc; + if (fir::isPolymorphicType(from.getType())) { + fir::ClassType clTy = + fir::dyn_cast_ptrEleTy(from.getType()).dyn_cast(); + mlir::Type derivedType = fir::unwrapInnerType(clTy.getEleTy()); + declaredTypeDesc = + builder.create(loc, mlir::TypeAttr::get(derivedType)); + } else { + declaredTypeDesc = builder.createNullConstant(loc); + } llvm::SmallVector args{fir::runtime::createArguments( - builder, loc, fTy, to, from, hasStat, errMsg, sourceFile, sourceLine)}; + builder, loc, fTy, to, from, declaredTypeDesc, hasStat, errMsg, + sourceFile, sourceLine)}; return builder.create(loc, func, args).getResult(0); } diff --git a/flang/runtime/allocatable.cpp b/flang/runtime/allocatable.cpp --- a/flang/runtime/allocatable.cpp +++ b/flang/runtime/allocatable.cpp @@ -41,7 +41,8 @@ derivedType, nullptr, rank, nullptr, CFI_attribute_allocatable); } -std::int32_t RTNAME(MoveAlloc)(Descriptor &to, Descriptor &from, bool hasStat, +std::int32_t RTNAME(MoveAlloc)(Descriptor &to, Descriptor &from, + const typeInfo::DerivedType *derivedType, bool hasStat, const Descriptor *errMsg, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; @@ -63,7 +64,24 @@ if (from.IsAllocated()) { to = from; from.raw().base_addr = nullptr; + + // Carry over the dynamic type. + if (auto *toAddendum{to.Addendum()}) { + if (const auto *fromAddendum{from.Addendum()}) { + if (const auto *derived{fromAddendum->derivedType()}) { + toAddendum->set_derivedType(derived); + } + } + } + + // Reset from dynamic type if needed. + if (auto *fromAddendum{from.Addendum()}) { + if (derivedType) { + fromAddendum->set_derivedType(derivedType); + } + } } + return StatOk; } diff --git a/flang/test/Lower/Intrinsics/move_alloc.f90 b/flang/test/Lower/Intrinsics/move_alloc.f90 --- a/flang/test/Lower/Intrinsics/move_alloc.f90 +++ b/flang/test/Lower/Intrinsics/move_alloc.f90 @@ -12,7 +12,7 @@ ! CHECK-DAG: %[[a2:.*]] = fir.convert %[[a1]] : (!fir.ref>>>) -> !fir.ref> ! CHECK-DAG: %[[b2:.*]] = fir.convert %[[b1]] : (!fir.ref>>>) -> !fir.ref> call move_alloc(from, to) - ! CHECK: fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %[[false]], %[[errMsg]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, i1, !fir.box, !fir.ref, i32) -> i32 + ! CHECK: fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %{{.*}}, %[[false]], %[[errMsg]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, !fir.ref, i1, !fir.box, !fir.ref, i32) -> i32 ! CHECK-DAG: %[[a3:.*]] = fir.load %[[a1:.*]] : !fir.ref>>> ! CHECK-DAG: %[[a4:.*]] = fir.box_addr %[[a3]] : (!fir.box>>) -> !fir.heap> ! CHECK-DAG: %[[b3:.*]] = fir.load %[[b1]] : !fir.ref>>> @@ -32,7 +32,7 @@ ! CHECK-DAG: %[[a2:.*]] = fir.convert %[[a1]] : (!fir.ref>>>) -> !fir.ref> ! CHECK-DAG: %[[b2:.*]] = fir.convert %[[b1]] : (!fir.ref>>>) -> !fir.ref> call move_alloc(from, to, stat) - ! CHECK: %[[stat:.*]] = fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %[[true]], %[[errMsg]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, i1, !fir.box, !fir.ref, i32) -> i32 + ! CHECK: %[[stat:.*]] = fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %{{.*}}, %[[true]], %[[errMsg]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, !fir.ref, i1, !fir.box, !fir.ref, i32) -> i32 ! CHECK-DAG: %[[a3:.*]] = fir.load %[[a1:.*]] : !fir.ref>>> ! CHECK-DAG: %[[a4:.*]] = fir.box_addr %[[a3]] : (!fir.box>>) -> !fir.heap> ! CHECK-DAG: %[[b3:.*]] = fir.load %[[b1]] : !fir.ref>>> @@ -55,7 +55,7 @@ ! CHECK-DAG: %[[a2:.*]] = fir.convert %[[a1]] : (!fir.ref>>>) -> !fir.ref> ! CHECK-DAG: %[[b2:.*]] = fir.convert %[[b1]] : (!fir.ref>>>) -> !fir.ref> call move_alloc(from, to, stat, errMsg) - ! CHECK: %[[stat:.*]] = fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %[[true]], %[[errMsg3]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, i1, !fir.box, !fir.ref, i32) -> i32 + ! CHECK: %[[stat:.*]] = fir.call @_FortranAMoveAlloc(%[[b2]], %[[a2]], %{{.*}}, %[[true]], %[[errMsg3]], %{{.*}}, %{{.*}}) fastmath : (!fir.ref>, !fir.ref>, !fir.ref, i1, !fir.box, !fir.ref, i32) -> i32 ! CHECK-DAG: %[[a3:.*]] = fir.load %[[a1:.*]] : !fir.ref>>> ! CHECK-DAG: %[[a4:.*]] = fir.box_addr %[[a3]] : (!fir.box>>) -> !fir.heap> ! CHECK-DAG: %[[b3:.*]] = fir.load %[[b1]] : !fir.ref>>> diff --git a/flang/test/Lower/polymorphic.f90 b/flang/test/Lower/polymorphic.f90 --- a/flang/test/Lower/polymorphic.f90 +++ b/flang/test/Lower/polymorphic.f90 @@ -987,6 +987,20 @@ ! CHECK: fir.store %[[EMBOX]] to %[[NEW_BOX]] : !fir.ref>> ! CHECK: fir.call @_QMpolymorphic_testPup_pointer(%[[NEW_BOX]]) {{.*}} : (!fir.ref>>) -> () + subroutine move_alloc_poly(a, b) + class(p1), allocatable :: a, b + + call move_alloc(a, b) + end subroutine + +! CHECK-LABEL: func.func @_QMpolymorphic_testPmove_alloc_poly( +! CHECK-SAME: %[[A:.*]]: !fir.ref>>> {fir.bindc_name = "a"}, %[[B:.*]]: !fir.ref>>> {fir.bindc_name = "b"}) { +! CHECK: %[[TYPE_DESC:.*]] = fir.type_desc !fir.type<_QMpolymorphic_testTp1{a:i32,b:i32}> +! CHECK: %[[B_CONV:.*]] = fir.convert %[[B]] : (!fir.ref>>>) -> !fir.ref> +! CHECK: %[[A_CONV:.*]] = fir.convert %[[A]] : (!fir.ref>>>) -> !fir.ref> +! CHECK: %[[TYPE_DESC_CONV:.*]] = fir.convert %[[TYPE_DESC]] : (!fir.tdesc>) -> !fir.ref +! CHECK: %{{.*}} = fir.call @_FortranAMoveAlloc(%[[B_CONV]], %[[A_CONV]], %[[TYPE_DESC_CONV]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {{.*}} : (!fir.ref>, !fir.ref>, !fir.ref, i1, !fir.box, !fir.ref, i32) -> i32 + end module program test diff --git a/flang/unittests/Optimizer/Builder/Runtime/AllocatableTest.cpp b/flang/unittests/Optimizer/Builder/Runtime/AllocatableTest.cpp --- a/flang/unittests/Optimizer/Builder/Runtime/AllocatableTest.cpp +++ b/flang/unittests/Optimizer/Builder/Runtime/AllocatableTest.cpp @@ -22,5 +22,5 @@ mlir::Value errMsg = firBuilder->create(loc, seqTy); mlir::Value hasStat = firBuilder->createBool(loc, false); fir::runtime::genMoveAlloc(*firBuilder, loc, to, from, hasStat, errMsg); - checkCallOpFromResultBox(to, "_FortranAMoveAlloc", 4); + checkCallOpFromResultBox(to, "_FortranAMoveAlloc", 5); } diff --git a/flang/unittests/Runtime/Allocatable.cpp b/flang/unittests/Runtime/Allocatable.cpp --- a/flang/unittests/Runtime/Allocatable.cpp +++ b/flang/unittests/Runtime/Allocatable.cpp @@ -32,13 +32,13 @@ EXPECT_FALSE(b->IsAllocated()); // Simple move_alloc - RTNAME(MoveAlloc)(*b, *a, false, nullptr, __FILE__, __LINE__); + RTNAME(MoveAlloc)(*b, *a, nullptr, false, nullptr, __FILE__, __LINE__); EXPECT_FALSE(a->IsAllocated()); EXPECT_TRUE(b->IsAllocated()); // move_alloc with stat std::int32_t stat{ - RTNAME(MoveAlloc)(*a, *b, true, nullptr, __FILE__, __LINE__)}; + RTNAME(MoveAlloc)(*a, *b, nullptr, true, nullptr, __FILE__, __LINE__)}; EXPECT_TRUE(a->IsAllocated()); EXPECT_FALSE(b->IsAllocated()); EXPECT_EQ(stat, 0); @@ -47,23 +47,26 @@ auto errMsg{Descriptor::Create( sizeof(char), 64, nullptr, 0, nullptr, CFI_attribute_allocatable)}; errMsg->Allocate(); - RTNAME(MoveAlloc)(*b, *a, false, errMsg.get(), __FILE__, __LINE__); + RTNAME(MoveAlloc)(*b, *a, nullptr, false, errMsg.get(), __FILE__, __LINE__); EXPECT_FALSE(a->IsAllocated()); EXPECT_TRUE(b->IsAllocated()); // move_alloc with stat and errMsg - stat = RTNAME(MoveAlloc)(*a, *b, true, errMsg.get(), __FILE__, __LINE__); + stat = RTNAME(MoveAlloc)( + *a, *b, nullptr, true, errMsg.get(), __FILE__, __LINE__); EXPECT_TRUE(a->IsAllocated()); EXPECT_FALSE(b->IsAllocated()); EXPECT_EQ(stat, 0); // move_alloc with the same deallocated array - stat = RTNAME(MoveAlloc)(*b, *b, true, errMsg.get(), __FILE__, __LINE__); + stat = RTNAME(MoveAlloc)( + *b, *b, nullptr, true, errMsg.get(), __FILE__, __LINE__); EXPECT_FALSE(b->IsAllocated()); EXPECT_EQ(stat, 0); // move_alloc with the same allocated array should fail - stat = RTNAME(MoveAlloc)(*a, *a, true, errMsg.get(), __FILE__, __LINE__); + stat = RTNAME(MoveAlloc)( + *a, *a, nullptr, true, errMsg.get(), __FILE__, __LINE__); EXPECT_EQ(stat, 109); std::string_view errStr{errMsg->OffsetElement(), errMsg->ElementBytes()}; auto trim_pos = errStr.find_last_not_of(' ');