Bug 125528 - ICE or wrong-code for ASSOCIATE selector that is a type-bound user-defined operator
Summary: ICE or wrong-code for ASSOCIATE selector that is a type-bound user-defined op...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 16.0
: P3 normal
Target Milestone: ---
Assignee: Jerry DeLisle
URL:
Keywords:
Depends on:
Blocks: 125515
  Show dependency treegraph
 
Reported: 2026-05-31 19:50 UTC by Jerry DeLisle
Modified: 2026-06-21 02:42 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2026-05-31 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jerry DeLisle 2026-05-31 19:50:28 UTC
gfortran fails to compile ASSOCIATE constructs whose
  selector is a type-bound user-defined operator (UDO)
  expression.  Three related cases are affected:

  1. A direct type-bound UDO as the selector
  (associate (g => .grad. s)).
  2. An inherited UDO defined on a parent type
  (associate (q => .sq. s)).
  3. Nested UDO expressions
  (associate (r => .div. (.grad. s))).

  In case 1 gfortran produces an ICE ("Syntax error in
  expression") inside the ASSOCIATE body because the
  associate variable is left without a type.  Cases 2 and 3
  produce similar failures.  Intel Fortran and NAG Fortran
  accept all three cases.

  module m
    implicit none
    type :: scalar_t
      real :: val
    contains
      generic :: operator(.grad.) => do_grad
      procedure, private :: do_grad
      generic :: operator(.sq.)   => do_sq
      procedure, private :: do_sq
      generic :: get => get_val
      procedure :: get_val
    end type

    type, extends(scalar_t) :: vector_t
    contains
      generic :: operator(.div.) => do_div
      procedure, private :: do_div
    end type
  contains
    pure function do_grad (self) result (r)
      class(scalar_t), intent(in) :: self
      type(vector_t) :: r
      r%val = self%val * 2.0
    end function
    pure function do_sq (self) result (r)
      class(scalar_t), intent(in) :: self
      type(scalar_t) :: r
      r%val = self%val * self%val
    end function
    pure function do_div (self) result (r)
      class(vector_t), intent(in) :: self
      type(scalar_t) :: r
      r%val = self%val / 2.0
    end function
    pure function get_val (self) result (r)
      class(scalar_t), intent(in) :: self
      real :: r
      r = self%val
    end function
  end module m

  program p
    use m
    implicit none
    type(scalar_t) :: s
    s%val = 3.0
    associate (g => .grad. s)        ! case 1
      if (abs (g%val - 6.0) > 1.0e-6) stop 1
    end associate
    associate (q => .sq. s)          ! case 2
      if (abs (q%val - 9.0) > 1.0e-6) stop 2
    end associate
    associate (r => .div. (.grad. s)) ! case 3
      if (abs (r%val - 3.0) > 1.0e-6) stop 3
    end associate
  end program p

  $ gfortran associate.f90

  gfortran ICEs or rejects the code.  The program should
  compile and run cleanly.
Comment 1 GCC Commits 2026-06-04 22:56:01 UTC
The master branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:8c57a6b7959dcf16ed5321cd5214f66e7e6ad4d7

commit r17-1357-g8c57a6b7959dcf16ed5321cd5214f66e7e6ad4d7
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Wed May 27 15:10:07 2026 -0700

    fortran: ICE or wrong-code for ASSOCIATE selector that is a type-bound user-defined operator
    
    Three related bugs prevented ASSOCIATE selectors that are type-bound
    user-defined operator expressions from compiling correctly.
    
    Bug 1 (class.cc): find_typebound_proc_uop returned NULL immediately when
    the derived type has no f2k_derived namespace, bypassing the parent-type
    inheritance walk.  This caused inherited UDOs to be silently not found.
    Fix: set root = NULL and let the loop reach the parent type instead.
    
    Bug 2 (resolve.cc): resolve_typebound_procedures called resolve_symbol on
    the parent type only after an early return that fires when the derived type
    has no direct type-bound bindings.  This left parent-type bindings
    unresolved when searched via gfc_find_typebound_user_op.
    Fix: move resolve_symbol(super_type) before the early return.
    
    Bug 3 (match.cc): match_association_list did not handle ASSOCIATE selectors
    of the form .uop. expr or the nested case .uop2. (.uop1. expr).  When the
    selector's type was BT_UNKNOWN at parse time the name of the associate
    variable was left untyped, producing a "Syntax error in expression" ICE in
    the body of the ASSOCIATE construct.
    Fix: add three helpers before match_association_list:
      - resolve_assoc_operand: attempts gfc_resolve_expr on EXPR_FUNCTION
        operands and falls back to gfc_find_dt_in_generic for constructor calls
        whose argument types are not yet known.
      - infer_typebound_uop_type: reads the return type of a type-bound UDO
        directly from specific_st->n.tb->u.specific->n.sym without calling
        gfc_resolve_symbol, avoiding a resolve_symbol_called race condition.
      - extend_assoc_op: walks the expression tree bottom-up, propagating
        types through INTRINSIC_PARENTHESES wrappers before calling the two
        helpers above on each INTRINSIC_USER node.
    When the selector is an INTRINSIC_USER EXPR_OP with BT_UNKNOWN type,
    call extend_assoc_op on the operands, then gfc_extend_expr (errors
    suppressed).  Accept the result when gfc_extend_expr returns MATCH_YES or
    when it returns MATCH_ERROR but has already converted the node to
    EXPR_COMPCALL with a known type (the full resolution pass finishes it).
    
    Assisted by: Claude Sonnet 4.6
    
    PR fortran/125528
    
    gcc/fortran/ChangeLog:
    
            PR fortran/125528
            * class.cc (find_typebound_proc_uop): Set root = NULL instead of
            returning NULL when derived type lacks f2k_derived, so parent-type
            type-bound procedures and operators are still found via inheritance.
            * match.cc (resolve_assoc_operand): New helper.
            (infer_typebound_uop_type): New helper.
            (extend_assoc_op): New helper.
            (match_association_list): Handle ASSOCIATE selectors that are
            type-bound user-defined operator expressions, including nested cases.
            * resolve.cc (resolve_typebound_procedures): Move resolve_symbol
            call for the parent type before the early return so inherited
            type-bound bindings are resolved even when the child type has none
            of its own.
    
    gcc/testsuite/ChangeLog:
    
            PR fortran/125528
            * gfortran.dg/associate_80.f90: New test.
Comment 2 GCC Commits 2026-06-15 16:31:11 UTC
The releases/gcc-16 branch has been updated by Jerry DeLisle <jvdelisle@gcc.gnu.org>:

https://gcc.gnu.org/g:dfe38eb88b4e69be691e3495038d292edff77f36

commit r16-9107-gdfe38eb88b4e69be691e3495038d292edff77f36
Author: Jerry DeLisle <jvdelisle@gcc.gnu.org>
Date:   Wed May 27 15:10:07 2026 -0700

    fortran: ICE or wrong-code for ASSOCIATE selector that is a type-bound user-defined operator
    
    Three related bugs prevented ASSOCIATE selectors that are type-bound
    user-defined operator expressions from compiling correctly.
    
    Bug 1 (class.cc): find_typebound_proc_uop returned NULL immediately when
    the derived type has no f2k_derived namespace, bypassing the parent-type
    inheritance walk.  This caused inherited UDOs to be silently not found.
    Fix: set root = NULL and let the loop reach the parent type instead.
    
    Bug 2 (resolve.cc): resolve_typebound_procedures called resolve_symbol on
    the parent type only after an early return that fires when the derived type
    has no direct type-bound bindings.  This left parent-type bindings
    unresolved when searched via gfc_find_typebound_user_op.
    Fix: move resolve_symbol(super_type) before the early return.
    
    Bug 3 (match.cc): match_association_list did not handle ASSOCIATE selectors
    of the form .uop. expr or the nested case .uop2. (.uop1. expr).  When the
    selector's type was BT_UNKNOWN at parse time the name of the associate
    variable was left untyped, producing a "Syntax error in expression" ICE in
    the body of the ASSOCIATE construct.
    
    Add three helpers before match_association_list:
      - resolve_assoc_operand: attempts gfc_resolve_expr on EXPR_FUNCTION
        operands and falls back to gfc_find_dt_in_generic for constructor calls
        whose argument types are not yet known.
      - infer_typebound_uop_type: reads the return type of a type-bound UDO
        directly from specific_st->n.tb->u.specific->n.sym without calling
        gfc_resolve_symbol, avoiding a resolve_symbol_called race condition.
      - extend_assoc_op: walks the expression tree bottom-up, propagating
        types through INTRINSIC_PARENTHESES wrappers before calling the two
        helpers above on each INTRINSIC_USER node.
    
    When the selector is an INTRINSIC_USER EXPR_OP with BT_UNKNOWN type,
    call extend_assoc_op on the operands, then gfc_extend_expr (errors
    suppressed).  Accept the result when gfc_extend_expr returns MATCH_YES or
    when it returns MATCH_ERROR but has already converted the node to
    EXPR_COMPCALL with a known type (the full resolution pass finishes it).
    
    Assisted-by: Claude Sonnet 4.6
    
            PR fortran/125528
    
    gcc/fortran/ChangeLog:
    
            * class.cc (find_typebound_proc_uop): Set root = NULL instead of
            returning NULL when derived type lacks f2k_derived, so parent-type
            type-bound procedures and operators are still found via inheritance.
            * match.cc (resolve_assoc_operand): New helper.
            (infer_typebound_uop_type): New helper.
            (extend_assoc_op): New helper.
            (match_association_list): Handle ASSOCIATE selectors that are
            type-bound user-defined operator expressions, including nested cases.
            * resolve.cc (resolve_typebound_procedures): Move resolve_symbol
            call for the parent type before the early return so inherited
            type-bound bindings are resolved even when the child type has none
            of its own.
    
    gcc/testsuite/ChangeLog:
    
            * gfortran.dg/associate_80.f90: New test.
    
    (cherry picked from commit 6340639219bb2d1816c94e5de76ec3642453bcb2)
Comment 3 Jerry DeLisle 2026-06-21 02:42:27 UTC
Fixed and closing