Menu ▾ ▴

#4167 Fresh null check is not recognised after a conditional pointer member assignment

open
nobody
None
Front-end
5
10 hours ago
12 hours ago
No

1: Sample code that reproduces the problem.

struct S
{
  _Optional char *p;
};

int read_value(struct S *s, int clear)
{
  if (s->p)
    {
      if (clear)
        s->p = 0;
    }

  if (s->p)
    return *s->p;
  return 0;
}

Save as optional-rechecked-member.c.

2: Exact command used to run SDCC on this sample code.

sdcc -mz80 --std-c99 --stack-auto -S optional-rechecked-member.c

The same result occurs with -mmos65c02, -mstm8 and -mmcs51 in place of -mz80.

3: SDCC version tested.

SDCC 4.6.4 #17038 (Linux), official snapshot dated 20 October 2026. This includes the fix for [bugs:#4148] in [r17036].

4: Copy of the error message or incorrect output, or a clear description of the observed versus expected behaviour.

optional-rechecked-member.c:15: warning 355: pointer to _Optional could not be proven to be non-null at dereference

See Compiler Explorer with SDCC 4.6.0 and trunk (4.6.4 r17013 when checked on 21 October 2026): https://godbolt.org/z/Y9E1Eh1ao

I expected no warning. The second if (s->p) checks the value after the conditional assignment. There is no assignment, call or volatile access between that check and *s->p.

With r17038, removing the first if (s->p), leaving if (clear) s->p = 0; followed by the fresh check, removes warning 355. Compiler Explorer's compiler labelled SDCC trunk was still r17013 when checked on 21 October 2026; it continues to warn for this simplified case because it predates the first-member fix in [r17036]. See https://godbolt.org/z/fWo38nYMd .

Adding a char member before p does not remove it, so both first and subsequent member positions are affected. Replacing the member with a local pointer also avoids the warning.

This is distinct from [bugs:#3957], where analysis must account for an intervening assignment through a possible alias, and from [feature-requests:#1035], where an intervening write to a different member invalidates a previous check. Here the pointer is checked again after the assignment.

The pattern was isolated while investigating CBUtilLib's dictionary warnings. The larger dictionary functions also contain other assignments and calls; this reduction does not establish that this is their only analysis limitation.

Related

Bugs: #3957
Bugs: #4148
Commit: [r17036]
Feature Requests: #1035

Discussion

  • Christopher Bazley

    Christopher Bazley - 12 hours ago
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -39,6 +39,7 @@
     ```text
     optional-rechecked-member.c:15: warning 355: pointer to _Optional could not be proven to be non-null at dereference
     ```
    +See the error in Compiler Explorer: https://godbolt.org/z/Y9E1Eh1ao
    
     I expected no warning. The second `if (s->p)` checks the value after the conditional assignment. There is no assignment, call or volatile access between that check and `*s->p`.
    
     
  • Christopher Bazley

    Christopher Bazley - 12 hours ago
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -39,7 +39,7 @@
     ```text
     optional-rechecked-member.c:15: warning 355: pointer to _Optional could not be proven to be non-null at dereference
     ```
    -See the error in Compiler Explorer: https://godbolt.org/z/Y9E1Eh1ao
    +See Compiler Explorer with SDCC 4.6.0 and trunk (4.6.4 #17013 at the time of writing): https://godbolt.org/z/Y9E1Eh1ao
    
     I expected no warning. The second `if (s->p)` checks the value after the conditional assignment. There is no assignment, call or volatile access between that check and `*s->p`.
    
     
  • Christopher Bazley

    Christopher Bazley - 12 hours ago
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -39,11 +39,13 @@
     ```text
     optional-rechecked-member.c:15: warning 355: pointer to _Optional could not be proven to be non-null at dereference
     ```
    -See Compiler Explorer with SDCC 4.6.0 and trunk (4.6.4 #17013 at the time of writing): https://godbolt.org/z/Y9E1Eh1ao
    +See Compiler Explorer with SDCC 4.6.0 and trunk (4.6.4 r17013 when checked on 21 October 2026): https://godbolt.org/z/Y9E1Eh1ao
    
     I expected no warning. The second `if (s->p)` checks the value after the conditional assignment. There is no assignment, call or volatile access between that check and `*s->p`.
    
    -Removing the first `if (s->p)`, leaving `if (clear) s->p = 0;` followed by the fresh check, removes warning 355. Adding a `char` member before `p` does not remove it, so both first and subsequent member positions are affected. Replacing the member with a local pointer also avoids the warning.
    +With r17038, removing the first `if (s->p)`, leaving `if (clear) s->p = 0;` followed by the fresh check, removes warning 355. Compiler Explorer's compiler labelled SDCC trunk was still r17013 when checked on 21 October 2026; it continues to warn for this simplified case because it predates the first-member fix in [r17036]. See https://godbolt.org/z/fWo38nYMd .
    +
    +Adding a `char` member before `p` does not remove it, so both first and subsequent member positions are affected. Replacing the member with a local pointer also avoids the warning.
    
     This is distinct from [bugs:#3957], where analysis must account for an intervening assignment through a possible alias, and from [feature-requests:#1035], where an intervening write to a different member invalidates a previous check. Here the pointer is checked again after the assignment.
    
     

    Related

    Bugs: #3957
    Commit: [r17036]
    Feature Requests: #1035

  • Christopher Bazley

    Christopher Bazley - 10 hours ago

    Attaching a candidate fix for this issue, created by Codex. It reuses the freshly checked member load, fixing both member positions while preserving warnings after potentially invalidating writes and calls. Bitfields are special-cased because two fields can share the same address but require different bit extraction.

    All six diagnostic configurations and the full Z80 runtime suite passed: 37164 tests, 0 failures. Focused runtime tests also passed on 65C02, STM8 and MCS51.

    @spth please would you be able to take a look at this at some point?

     
  • Christopher Bazley

    Christopher Bazley - 10 hours ago

    (I'm not sure special treatment of _Atomic is justified given that SDCC does not yet support such types, but it does no obvious harm.)

     

    Last edit: Christopher Bazley 10 hours ago

Log in to post a comment.