https://gcc.gnu.org/g:2aac5a6fa777753277216b30c3d8aa0f6c277f55
commit r16-3135-g2aac5a6fa777753277216b30c3d8aa0f6c277f55 Author: Paul Thomas <pa...@gcc.gnu.org> Date: Mon Aug 11 21:34:07 2025 +0100 Fortran: gfortran rejects procedure binding on PDT [PR121398] 2025-08-11 Paul Thomas <pa...@gcc.gnu.org> gcc/fortran PR fortran/121398 * resolve.cc (check_pdt_args): New function. (check_generic_tbp_ambiguity): Use it to ensure that args to typebound procedures that do not have the same declared type as the containing derived type have 'pass1/2' set to null. This avoids false ambiguity errors. (resolve_typebound_procedure): Do not generate a wrong type error for typebound procedures marked as pass if they are of a different declared type to the containing pdt_type. gcc/testsuite/ PR fortran/121398 * gfortran.dg/pdt_generic_1.f90: New test. Diff: --- gcc/fortran/resolve.cc | 42 ++++++++++++- gcc/testsuite/gfortran.dg/pdt_generic_1.f90 | 94 +++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc index c33bd17da2dc..68aaee846873 100644 --- a/gcc/fortran/resolve.cc +++ b/gcc/fortran/resolve.cc @@ -15604,6 +15604,31 @@ error: } +static gfc_symbol * containing_dt; + +/* Helper function for check_generic_tbp_ambiguity, which ensures that passed + arguments whose declared types are PDT instances only transmit the PASS arg + if they match the enclosing derived type. */ + +static bool +check_pdt_args (gfc_tbp_generic* t, const char *pass) +{ + gfc_formal_arglist *dummy_args; + if (pass && containing_dt != NULL && containing_dt->attr.pdt_type) + { + dummy_args = gfc_sym_get_dummy_args (t->specific->u.specific->n.sym); + while (dummy_args && strcmp (pass, dummy_args->sym->name)) + dummy_args = dummy_args->next; + gcc_assert (strcmp (pass, dummy_args->sym->name) == 0); + if (dummy_args->sym->ts.type == BT_CLASS + && strcmp (CLASS_DATA (dummy_args->sym)->ts.u.derived->name, + containing_dt->name)) + return true; + } + return false; +} + + /* Check if two GENERIC targets are ambiguous and emit an error is they are. */ static bool @@ -15661,6 +15686,17 @@ check_generic_tbp_ambiguity (gfc_tbp_generic* t1, gfc_tbp_generic* t2, pass2 = NULL; } + /* Care must be taken with pdt types and templates because the declared type + of the argument that is not 'no_pass' need not be the same as the + containing derived type. If this is the case, subject the argument to + the full interface check, even though it cannot be used in the type + bound context. */ + pass1 = check_pdt_args (t1, pass1) ? NULL : pass1; + pass2 = check_pdt_args (t2, pass2) ? NULL : pass2; + + if (containing_dt != NULL && containing_dt->attr.pdt_template) + pass1 = pass2 = NULL; + /* Compare the interfaces. */ if (gfc_compare_interfaces (sym1, sym2, sym2->name, !t1->is_operator, 0, NULL, 0, pass1, pass2)) @@ -16108,8 +16144,10 @@ resolve_typebound_procedure (gfc_symtree* stree) goto error; } - /* The derived type is not a PDT template. Resolve as usual. */ + /* The derived type is not a PDT template or type. Resolve as usual. */ if (!resolve_bindings_derived->attr.pdt_template + && !(containing_dt && containing_dt->attr.pdt_type + && CLASS_DATA (me_arg)->ts.u.derived != containing_dt) && (CLASS_DATA (me_arg)->ts.u.derived != resolve_bindings_derived)) { gfc_error ("Argument %qs of %qs with PASS(%s) at %L must be of " @@ -16256,6 +16294,7 @@ resolve_typebound_procedures (gfc_symbol* derived) resolve_bindings_derived = derived; resolve_bindings_result = true; + containing_dt = derived; /* Needed for checks of PDTs. */ if (derived->f2k_derived->tb_sym_root) gfc_traverse_symtree (derived->f2k_derived->tb_sym_root, &resolve_typebound_procedure); @@ -16263,6 +16302,7 @@ resolve_typebound_procedures (gfc_symbol* derived) if (derived->f2k_derived->tb_uop_root) gfc_traverse_symtree (derived->f2k_derived->tb_uop_root, &resolve_typebound_user_op); + containing_dt = NULL; for (op = 0; op != GFC_INTRINSIC_OPS; ++op) { diff --git a/gcc/testsuite/gfortran.dg/pdt_generic_1.f90 b/gcc/testsuite/gfortran.dg/pdt_generic_1.f90 new file mode 100644 index 000000000000..a6c0f6ac584e --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pdt_generic_1.f90 @@ -0,0 +1,94 @@ +! { dg-do run } +! +! Check the fix for pr121398 +! +! Contributed by Damian Rouson <damian@archaeologic.codes> +! +module tensor_m + implicit none + private + public tensor_t + + type tensor_t(k) + integer, kind :: k + integer :: n + contains + procedure, private :: default_real_num_components + procedure, private :: default_real_num_components2 + procedure, private :: double_precision_num_components + procedure, private, pass(self) :: quad_precision_num_components + generic :: num_components => default_real_num_components, & ! Failed ambiguity test + default_real_num_components2, & + double_precision_num_components, & + quad_precision_num_components + end type + + interface + + module function default_real_num_components(self) result(res) + implicit none + class(tensor_t(kind(0.))) self + integer :: res + end function + + module function default_real_num_components2(self, another) result(res) + implicit none + class(tensor_t(kind(0.))) self, another + integer :: res + end function + + module function double_precision_num_components(self) result(res) + implicit none + class(tensor_t(kind(0.0_8))) self + integer :: res + end function + + module function quad_precision_num_components(l, self) result(res) + implicit none + class(tensor_t(kind(0.0_16))) self + integer :: l + integer :: res + end function + + end interface + +end module + +submodule (tensor_m) tensor_m_components +contains + module procedure default_real_num_components + implicit none + self%n = 10 + res = 1 + end + + module procedure default_real_num_components2 + implicit none + self%n = 2 * another%n + res = 1 + end + + module procedure double_precision_num_components + implicit none + self%n = 20 + res = 2 + end + + module procedure quad_precision_num_components + implicit none + self%n = 10 * l + res = l + end +end + + use tensor_m + type (tensor_t(kind(0.))) :: a + type (tensor_t(kind(0.))) :: ap + type (tensor_t(kind(0.0_8))) :: b + type (tensor_t(kind(0.0_16))) :: c + if (a%num_components () /= 1) stop 1 + if (ap%num_components (a) /= 1) stop 2 + if (2 * a%n /= ap%n) stop 3 + if (b%num_components () /= 2 ) stop 4 + if (c%num_components (42) /= 42 ) stop 5 +end