Greetings to all, This patch fixes a bug where the pointer assignment to the derived component of a class entity was resulting in the base entity's vptr being set to the vtable of the target. This resulted in the wrong typebound procedure being called.
The patch corrects the logic in resolve code that determines when regular assignment is used for class pointer assignment and breaks out some codeto handle function targets from trans_pointer_assignment so that function targets are correctly handled. Bootstraps and regtests on FC23/x86_64 - OK for trunk and 7 branch? Cheers Paul 2017-09-30 Paul Thomas <pa...@gcc.gnu.org> PR fortran/82312 * resolve.c (gfc_resolve_code): Simplify condition for class pointer assignments becoming regular assignments by asserting that only class valued targets are permitted. * trans-expr.c (trans_class_pointer_fcn): New function using a block of code from gfc_trans_pointer_assignment. (gfc_trans_pointer_assignment): Call the new function. Tidy up a minor whitespace issue. 2017-09-30 Paul Thomas <pa...@gcc.gnu.org> PR fortran/82312 * gfortran.dg/typebound_proc_36.f90 : New test.
Index: gcc/fortran/resolve.c =================================================================== *** gcc/fortran/resolve.c (revision 253268) --- gcc/fortran/resolve.c (working copy) *************** start: *** 11119,11129 **** /* Assigning a class object always is a regular assign. */ if (code->expr2->ts.type == BT_CLASS && !CLASS_DATA (code->expr2)->attr.dimension - && !(UNLIMITED_POLY (code->expr2) - && code->expr1->ts.type == BT_DERIVED - && (code->expr1->ts.u.derived->attr.sequence - || code->expr1->ts.u.derived->attr.is_bind_c)) && !(gfc_expr_attr (code->expr1).proc_pointer && code->expr2->expr_type == EXPR_VARIABLE && code->expr2->symtree->n.sym->attr.flavor --- 11119,11126 ---- /* Assigning a class object always is a regular assign. */ if (code->expr2->ts.type == BT_CLASS + && code->expr1->ts.type == BT_CLASS && !CLASS_DATA (code->expr2)->attr.dimension && !(gfc_expr_attr (code->expr1).proc_pointer && code->expr2->expr_type == EXPR_VARIABLE && code->expr2->symtree->n.sym->attr.flavor Index: gcc/fortran/trans-expr.c =================================================================== *** gcc/fortran/trans-expr.c (revision 253268) --- gcc/fortran/trans-expr.c (working copy) *************** pointer_assignment_is_proc_pointer (gfc_ *** 8207,8212 **** --- 8207,8245 ---- } + /* Do everything that is needed for a CLASS function expr2. */ + + static tree + trans_class_pointer_fcn (stmtblock_t *block, gfc_se *lse, gfc_se *rse, + gfc_expr *expr1, gfc_expr *expr2) + { + tree expr1_vptr = NULL_TREE; + tree tmp; + + gfc_conv_function_expr (rse, expr2); + rse->expr = gfc_evaluate_now (rse->expr, &rse->pre); + + if (expr1->ts.type != BT_CLASS) + rse->expr = gfc_class_data_get (rse->expr); + else + { + expr1_vptr = trans_class_vptr_len_assignment (block, expr1, + expr2, rse, + NULL, NULL); + gfc_add_block_to_block (block, &rse->pre); + tmp = gfc_create_var (TREE_TYPE (rse->expr), "ptrtemp"); + gfc_add_modify (&lse->pre, tmp, rse->expr); + + gfc_add_modify (&lse->pre, expr1_vptr, + fold_convert (TREE_TYPE (expr1_vptr), + gfc_class_vptr_get (tmp))); + rse->expr = gfc_class_data_get (tmp); + } + + return expr1_vptr; + } + + tree gfc_trans_pointer_assign (gfc_code * code) { *************** gfc_trans_pointer_assignment (gfc_expr * *** 8224,8229 **** --- 8257,8263 ---- stmtblock_t block; tree desc; tree tmp; + tree expr1_vptr = NULL_TREE; bool scalar, non_proc_pointer_assign; gfc_ss *ss; *************** gfc_trans_pointer_assignment (gfc_expr * *** 8257,8263 **** gfc_conv_expr (&lse, expr1); gfc_init_se (&rse, NULL); rse.want_pointer = 1; ! gfc_conv_expr (&rse, expr2); if (non_proc_pointer_assign && expr1->ts.type == BT_CLASS) { --- 8291,8300 ---- gfc_conv_expr (&lse, expr1); gfc_init_se (&rse, NULL); rse.want_pointer = 1; ! if (expr2->expr_type == EXPR_FUNCTION && expr2->ts.type == BT_CLASS) ! trans_class_pointer_fcn (&block, &lse, &rse, expr1, expr2); ! else ! gfc_conv_expr (&rse, expr2); if (non_proc_pointer_assign && expr1->ts.type == BT_CLASS) { *************** gfc_trans_pointer_assignment (gfc_expr * *** 8269,8280 **** if (expr1->symtree->n.sym->attr.proc_pointer && expr1->symtree->n.sym->attr.dummy) lse.expr = build_fold_indirect_ref_loc (input_location, ! lse.expr); if (expr2->symtree && expr2->symtree->n.sym->attr.proc_pointer && expr2->symtree->n.sym->attr.dummy) rse.expr = build_fold_indirect_ref_loc (input_location, ! rse.expr); gfc_add_block_to_block (&block, &lse.pre); gfc_add_block_to_block (&block, &rse.pre); --- 8306,8317 ---- if (expr1->symtree->n.sym->attr.proc_pointer && expr1->symtree->n.sym->attr.dummy) lse.expr = build_fold_indirect_ref_loc (input_location, ! lse.expr); if (expr2->symtree && expr2->symtree->n.sym->attr.proc_pointer && expr2->symtree->n.sym->attr.dummy) rse.expr = build_fold_indirect_ref_loc (input_location, ! rse.expr); gfc_add_block_to_block (&block, &lse.pre); gfc_add_block_to_block (&block, &rse.pre); *************** gfc_trans_pointer_assignment (gfc_expr * *** 8320,8326 **** { gfc_ref* remap; bool rank_remap; - tree expr1_vptr = NULL_TREE; tree strlen_lhs; tree strlen_rhs = NULL_TREE; --- 8357,8362 ---- *************** gfc_trans_pointer_assignment (gfc_expr * *** 8355,8380 **** rse.byref_noassign = 1; if (expr2->expr_type == EXPR_FUNCTION && expr2->ts.type == BT_CLASS) ! { ! gfc_conv_function_expr (&rse, expr2); ! ! if (expr1->ts.type != BT_CLASS) ! rse.expr = gfc_class_data_get (rse.expr); ! else ! { ! expr1_vptr = trans_class_vptr_len_assignment (&block, expr1, ! expr2, &rse, ! NULL, NULL); ! gfc_add_block_to_block (&block, &rse.pre); ! tmp = gfc_create_var (TREE_TYPE (rse.expr), "ptrtemp"); ! gfc_add_modify (&lse.pre, tmp, rse.expr); ! ! gfc_add_modify (&lse.pre, expr1_vptr, ! fold_convert (TREE_TYPE (expr1_vptr), ! gfc_class_vptr_get (tmp))); ! rse.expr = gfc_class_data_get (tmp); ! } ! } else if (expr2->expr_type == EXPR_FUNCTION) { tree bound[GFC_MAX_DIMENSIONS]; --- 8391,8398 ---- rse.byref_noassign = 1; if (expr2->expr_type == EXPR_FUNCTION && expr2->ts.type == BT_CLASS) ! expr1_vptr = trans_class_pointer_fcn (&block, &lse, &rse, ! expr1, expr2); else if (expr2->expr_type == EXPR_FUNCTION) { tree bound[GFC_MAX_DIMENSIONS]; Index: gcc/testsuite/gfortran.dg/typebound_proc_36.f90 =================================================================== *** gcc/testsuite/gfortran.dg/typebound_proc_36.f90 (nonexistent) --- gcc/testsuite/gfortran.dg/typebound_proc_36.f90 (working copy) *************** *** 0 **** --- 1,77 ---- + ! { dg-do run } + ! + ! Test the fix for PR82312.f90 + ! + ! Posted on Stack Overflow: + ! https://stackoverflow.com/questions/46369744 + ! /gfortran-associates-wrong-type-bound-procedure/46388339#46388339 + ! + module minimalisticcase + implicit none + + type, public :: DataStructure + integer :: i + contains + procedure, pass :: init => init_data_structure + procedure, pass :: a => beginning_of_alphabet + end type + + type, public :: DataLogger + type(DataStructure), pointer :: data_structure + contains + procedure, pass :: init => init_data_logger + procedure, pass :: do_something => do_something + end type + + integer :: ctr = 0 + + contains + subroutine init_data_structure(self) + implicit none + class(DataStructure), intent(inout) :: self + write(*,*) 'init_data_structure' + ctr = ctr + 1 + end subroutine + + subroutine beginning_of_alphabet(self) + implicit none + class(DataStructure), intent(inout) :: self + + write(*,*) 'beginning_of_alphabet' + ctr = ctr + 10 + end subroutine + + subroutine init_data_logger(self, data_structure) + implicit none + class(DataLogger), intent(inout) :: self + class(DataStructure), target :: data_structure + write(*,*) 'init_data_logger' + ctr = ctr + 100 + + self%data_structure => data_structure ! Invalid change of 'self' vptr + call self%do_something() + end subroutine + + subroutine do_something(self) + implicit none + class(DataLogger), intent(inout) :: self + + write(*,*) 'do_something' + ctr = ctr + 1000 + + end subroutine + end module + + program main + use minimalisticcase + implicit none + + type(DataStructure) :: data_structure + type(DataLogger) :: data_logger + + call data_structure%init() + call data_structure%a() + call data_logger%init(data_structure) + + if (ctr .ne. 1111) call abort + end program