← board

sizeof(*s.fp) on a pointer-to-array struct member answers the pointer size

Measured, gcc as oracle

struct S { int (*fp)[4]; int *plain; int (*fp2)[2][3]; };
...
sizeof(*s.fp)     pxx 8    gcc 16
sizeof(*s.fp2)    pxx 8    gcc 24
sizeof(*s.plain)  pxx 4    gcc 4     <- control: a plain pointer FIELD is right
sizeof(s.fp)      pxx 8    gcc 8     <- control: the pointer itself
(*s.fp)[1] = 77   pxx 77   gcc 77    <- control: INDEXING the field is right

Why it is not the fix that just landed

The variable arm answered the element size (4). This field arm answers 8, the default pointer size, which means it never assigned sz at all — the if (recId <> REC_NONE) and (RecFieldType(recId, fieldName) = tyPointer) test in cparser.inc's sizeof(*...) handler declines. So multiplying by an extent there would change nothing; the field's pointee type has to be reachable first.

The indexing row is what makes this interesting rather than obvious: the field can be dereferenced and subscripted correctly, so the shape is known somewhere. Sizing reads a different set of metadata than addressing does.

Suggested shape

The symbol side keeps SymPtrElemArrLen (flattened product for a multi-dim pointee) beside Syms[].PtrElemTk. Fields have UFldPtrElemTk/UFldPtrElemRec and no extent column. Establish first WHY the = tyPointer test fails for int (*fp)[4] and succeeds for int *plain — that answer decides whether this is one missing column or a C declarator-parsing gap wearing a sizeof mask.

Root cause, 2026-09-02 — and the ticket's own measurement had gone stale

The 8 above is no longer what pxx answers, and the diagnosis built on it was wrong. At the tree this was fixed on, sizeof(*s.fp) answered 4, and 4 is not the element size either — it is TypeStorageSize(tyUnknown). The field held no pointee at all.

The int spelling cannot tell those apart, and that is the whole reason this sat misdiagnosed: tyUnknown's default is 4, which is exactly sizeof(int), so the row reads like a plausible "element size" answer from a working arm. One row settles it —

struct S { int (*ip)[4]; double (*dp)[4]; char (*cp)[7]; };
            pxx   gcc
sizeof(*s.ip)   4    16
sizeof(*s.dp)   4    32      <- not 8 either: the pointee TYPE is absent
sizeof(*s.cp)   4     7

double answering 4 rather than 8 is what says "nothing recorded" instead of "element size recorded, extent missing".

The cause is the same arm as [[bug-c-a-file-scope-pointer-to-array-crashes-on-indexing]], one scope down. The C struct builder's parenthesised-declarator arm (cparser.inc, the CTypeFnPtrName <> '' member arm) was written for FUNCTION pointer members, whose pointee genuinely has no type, so it hardcodes bfElemTk := tyUnknown. int (*fp)[4] reaches it because ParseCDeclType parks the name in CTypeFnPtrName for both shapes. Third instance of one omission: the local path records the pointee and works, the global path did not (fixed 7d6559cd3), the field path did not (fixed here).

So this needed BOTH halves, and the ticket's "Suggested shape" only named one:

Verified

test/c_sizeof_ptr_to_array_field.c (+ .expected). Sizes are asserted against each pointer's MEASURED stride and against the real array pointed at, never a spelled-out constant; int, double, char and a 2-D pointee are all present precisely because a suite of int rows here is a guard that cannot fail, and two rows assert that the pointees do NOT all agree. plain/dplain are the in-test controls for a non-array pointee.

Positive control against the pinned compiler: 11 rows FAIL, while both plain-pointer controls pass. Post-fix pxx and gcc both print FIELD PTRARR OK 16 32 7 24. gate.sh quick GREEN, FPC seed canary PASS.

Note the pinned binary also fails the (*s.ip)[1] = 77 addressing row that this ticket recorded as correct on 2026-08-30 — pinned is not the tree that was measured then, so that is not evidence the original reading was wrong. It is correct at the fixed tree, which is what the row is there to hold.

Row G of the same probe — sizeof(bp2[0][0]), subscripting a pointer-to-pointer — is untouched and stays with [[bug-c-sizeof-reaches-a-pointee-through-one-spelling-only]].

Log