Skip to content

Fix abstract classes with deferred bindings (issue #41)#323

Merged
jameskermode merged 9 commits into
jameskermode:masterfrom
itpplasma:fix/issue41-abstract-classes
Dec 18, 2025
Merged

Fix abstract classes with deferred bindings (issue #41)#323
jameskermode merged 9 commits into
jameskermode:masterfrom
itpplasma:fix/issue41-abstract-classes

Conversation

@krystophny

Copy link
Copy Markdown
Contributor

Summary

  • Generate Fortran wrappers and Python methods for deferred bindings on abstract types
  • Enable polymorphic dispatch through the base class when factory functions return class(base_type)

Changes

  • transform.py: Resolve deferred binding prototypes to abstract interface procedures, marking them with 'deferred' and 'method' attributes
  • LinkBoundDType: Handle resolved deferred bindings (skip raw Prototypes)
  • Re-enable issue41_abstract_classes example in test suite

Problem

When a factory function returned a polymorphic class(myclass_t) object, Python wrapped it as myclass_t which didn't have deferred methods like get_value. This caused:

AttributeError: 'myclass_t' object has no attribute 'get_value'

Solution

Generate Fortran wrapper code for deferred bindings that uses class() for polymorphic dispatch:

subroutine f90wrap_myclass_base__get_value__binding__myclass_t(self, value)
    type myclass_t_wrapper_type
        class(myclass_t), allocatable :: obj
    end type
    ...
    call self_ptr%p%obj%get_value(value=value)  ! polymorphic dispatch
end subroutine

The Python wrapper method on the abstract base class calls this Fortran wrapper, which dispatches to the correct implementation via Fortran's polymorphism.

Test plan

  • issue41_abstract_classes example now passes
  • fortran_oo example still passes (tests OO features)
  • extends example still passes (tests inheritance)

Stacked on #322

@krystophny krystophny force-pushed the fix/issue41-abstract-classes branch from 7e8f5d1 to 7e4e864 Compare December 15, 2025 20:47
@krystophny krystophny mentioned this pull request Dec 15, 2025
1 task
@krystophny krystophny force-pushed the fix/issue41-abstract-classes branch 2 times, most recently from eb2063f to e403a04 Compare December 15, 2025 21:05
@krystophny krystophny marked this pull request as draft December 15, 2025 21:14
@krystophny krystophny force-pushed the fix/issue41-abstract-classes branch from e403a04 to 7df6530 Compare December 15, 2025 21:16
@krystophny krystophny marked this pull request as ready for review December 16, 2025 11:49
Rykath and others added 9 commits December 18, 2025 15:42
'quip_regression' left out as it has it's own workflow
…dles

When compiling with -fdefault-integer-8, bare integer becomes 8 bytes.
The handle arrays (e.g., this(4)) were declared as integer, meaning
4 x 8 = 32 bytes with -fdefault-integer-8, but sizeof_fortran_t was
calculated assuming 4-byte integers (4 x 4 = 16 bytes).

This mismatch caused segfaults when using transfer() to convert between
pointer types and integer arrays.

Fix by using integer(c_int) for all handle arrays, which remains 4 bytes
regardless of -fdefault-integer-8. This ensures consistent size between
wrapper generation time and runtime.

Changes:
- transform.py: Set wrapper_type to 'integer(c_int)' for derived type args
- f90wrapgen.py: Use integer(c_int) for handle arrays in:
  - visit_Procedure (derived type arguments)
  - _write_sc_array_wrapper (this and dummy_this)
  - _write_array_getset_item (this and item handles)
  - _write_array_len (this handle)
  - _write_scalar_wrapper (this and derived type element handles)
- Add 'use, intrinsic :: iso_c_binding, only: c_int' where needed

Fixes the default_i8 example test.
The errorbinding example was added to the test suite in PR jameskermode#320 but was
missing the required tests.py and tests_pkg.py files, causing CI to fail.
Remove f2py_string_input and issue299_directc_nested_functions from test
suite until they are fixed:
- f2py_string_input: undefined symbol string_in_array_ (linking issue)
- issue299_directc_nested_functions: types.py shadows Python stdlib types module
During rebase, conflict resolution incorrectly re-added broken examples
to the Makefile EXAMPLES list. These examples were intentionally excluded:
- callback_print_function_issue93 (undefined symbol: pyfunc_print_)
- f2py_string_input (undefined symbol: string_in_array_)
- issue299_directc_nested_functions (types.py shadows stdlib)
- issue41_abstract_classes (AttributeError on get_value)
- recursive_type (Derived type not declared)

The CMakeLists.txt was already correct; only Makefile needed fixing.
During rebase conflict resolution, an else clause was incorrectly added
that declared dummy_this for module-level arrays. However, the subroutine
signature for module-level arrays doesn't include any this argument
(per issue jameskermode#306 fix), causing a mismatch: the variable was declared but
not in the argument list, leading to compilation errors.

Remove the erroneous else clause - module-level arrays should have no
this argument in either the signature or declarations.
Generate Fortran wrappers and Python methods for deferred bindings on
abstract types. This enables polymorphic dispatch through the base class.

Changes:
- transform.py: Resolve deferred binding prototypes to abstract interface
  procedures, marking them with 'deferred' and 'method' attributes
- LinkBoundDType: Handle resolved deferred bindings (skip raw Prototypes)
- Re-enable issue41_abstract_classes example in test suite

When a factory function returns a polymorphic class(base_type) object,
the Python wrapper now correctly has the deferred methods available,
which dispatch through Fortran's polymorphic call mechanism.
@krystophny krystophny force-pushed the fix/issue41-abstract-classes branch from 7df6530 to b59ba84 Compare December 18, 2025 15:30
@jameskermode jameskermode merged commit 7f75582 into jameskermode:master Dec 18, 2025
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants