Reject explicit redefinition of struct _IO_FILE in cdef() - #281
Merged
mattip merged 2 commits intoSep 27, 2026
Merged
Conversation
cffi special-cases the struct name `_IO_FILE` internally to back the
opaque `FILE` type (COMMON_TYPES['FILE'] in commontypes.py). When a
cdef() header gives an explicit body for `struct _IO_FILE` -- which
happens with any ordinary preprocessed glibc <stdio.h>, where the real
struct is spelled out -- cffi ends up with two conflicting internal
type-table entries sharing that name. The collision isn't caught
anywhere at cdef()/build time; it only surfaces later, the first time
the type gets realized (e.g. `ffi.new("struct _IO_FILE*")`), as a
process-aborting "Fatal Python error: do_realize_lazy_struct: lost a
struct/union!" instead of a normal Python exception.
Detect the collision in _get_struct_union_enum_type() and raise a
CDefError as soon as cdef() sees the offending struct body, well before
recompiler.py generates anything or the extension is ever loaded.
Fixes python-cffi#149.
akashchamp
added a commit
to akashchamp/cffi
that referenced
this pull request
Sep 25, 2026
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
akashchamp
force-pushed
the
fix-file-special-casing-collision
branch
from
September 25, 2026 17:01
10204d5 to
22a29b9
Compare
arigo
approved these changes
Sep 26, 2026
Contributor
|
Thanks @akashchamp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #149.
COMMON_TYPES['FILE']incommontypes.pyspecial-cases the struct name_IO_FILEinternally to back the opaqueFILEtype. When acdef()header gives an explicit body forstruct _IO_FILE-- which happens with any ordinary preprocessed glibc<stdio.h>, where the real struct is spelled out -- cffi ends up with two conflicting internal type-table entries sharing that name. Nothing catches this atcdef()/build time; it only surfaces later, the first time the type gets realized (e.g.ffi.new("struct _IO_FILE*")), as a process-abortingFatal Python error: do_realize_lazy_struct: lost a struct/union!instead of a normal Python exception -- confirmed as this special-casing collision by @arigo in the thread.This makes
_get_struct_union_enum_type()reject that case immediately with a normal, catchableCDefError, atcdef()parse time, well before the recompiler ever generates anything or the extension gets loaded.Verification:
struct _IO_FILE { int dummy; }; typedef struct _IO_FILE FILE;+ffi.new("struct _IO_FILE*")via an ABI-modeset_source(name, None)build, matching the issue's "out-of-line ABI level" setup):Fatal Python error: do_realize_lazy_struct_lock_held: lost a struct/union!, exit 134.struct _IO_FILEfrom<stdio.h>, from https://github.com/lacraig2/repro_cffi).cffi.CDefErrorfromcdef()instead of crashing.FILEusage still works: plainFILE *parameters, a forward-onlystruct _IO_FILE;declaration, and the workaround already noted in the issue thread (typedef void* FILE;).testing/cffi0/test_parsing.py.python -m pytest testing/cffi0/test_parsing.py testing/cffi1/test_commontypes.py testing/cffi1/test_recompiler.py testing/cffi1/test_realize_c_type.py testing/cffi1/test_new_ffi_1.py(the modules covering parsing, common-type resolution, the recompiler and struct/type realization): 337 passed, 6 skipped.python -m pytest testing/cffi1/test_verify1.py -k "file or File or FILE or stdout"(existingFILE-usage tests): 3 passed.Prepared with AI assistance (Claude Code); verified locally with the commands above.