Menu ▾ ▴

#3957 Indirect assignment of a non-null value through a pointer discards non-null inference

open
nobody
None
other
5
2026-09-08
2026-03-23
No

1: Sample code that reproduces the problem.

_Optional int *poi;

void chandler(_Optional int **ppoi)
{
  int i;

  // constrains poi to non-null on the fallthrough path
  if (!poi) return;

  *poi = 1;   // no recommended diagnostic
  *ppoi = &i; // non-null constraint on poi is unaffected
  *poi = 2;   // no recommended diagnostic
}

void monica(_Optional int **ppoi, _Optional int *lpoi)
{
  // constrains poi and lpoi to non-null on the fallthrough path
  if (!poi || !lpoi) return;

  *poi = 1;     // no recommended diagnostic
  *ppoi = lpoi; // non-null constraint on poi is unaffected
  *poi = 2;     // no recommended diagnostic
}

void rachel(_Optional int **ppoi_1, _Optional int **ppoi_2)
{
  int i;

  // constrains *ppoi_1 to non-null on the fallthrough path
  if (!*ppoi_1) return;

  **ppoi_1 = 1;  // no recommended diagnostic
  *ppoi_2 = &i;  // non-null constraint on *ppoi_1 is unaffected
  **ppoi_1 = 2;  // no recommended diagnostic
}

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

sdcc --std=c23 -c /work/bug6.c

3: SDCC version tested (type "sdcc -v" to find it)

SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/r4k/r5k/r6k/sm83/tlcs90/ez80/z80n/r800/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15/mos6502/mos65c02/f8/f8l TD- 4.5.21 #16327 (Linux)

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

The above examples from the Recommended Practice part of subsection 6.5.2 "Type qualifiers" in the _Optional TS are intended to illustrate best-case analysis that might be possible if a translator takes into account whether the value assigned to a pointer (that could alias another pointer) can be null, and therefore whether it is necessary to discard previously-inferred constraints on the value of other pointers that the analyser is tracking.

In chandler, the value of poi, an object declared at file scope that points to an optional-qualified type, is constrained to be non-null on the fallthrough path of an if statement, therefore the implementation is recommended not to produce a diagnostic message for the lvalue *poi in the first assignment. ppoi, a pointer to a pointer to an optional-qualified type, cannot be proven not to point to poi without interprocedural analysis. The assignment of &i to the lvalue *ppoi can modify the value of poi, but the assigned value is not a null pointer. The implementation is therefore recommended not to produce a diagnostic message for the lvalue *poi in the last assignment.

The monica function is similar to chandler except that the assigned value can be proven by flow-sensitive analysis to be non-null, as opposed to its non-null value being a known compile-time constant.

The rachel function is similar to chandler except that the address of the potentially-aliased pointer is passed as an argument instead of the potentially-aliased pointer being a global variable.

SDCC produces diagnostic messages for the final assignment in each function:

/work/bug6.c:12: warning 355: pointer to _Optional could not be proven to be non-null at dereference
/work/bug6.c:22: warning 355: pointer to _Optional could not be proven to be non-null at dereference
/work/bug6.c:34: warning 355: pointer to _Optional could not be proven to be non-null at dereference

Ideally, no diagnostic messages would be produced for these examples.

This is not a bug because it concerns Recommended Practice. I thought it might be useful to report it regardless, in case there is scope for improvement (if the maintainers of SDCC consider that useful).

Discussion

  • Philipp Klaus Krause

    As of current SDCC trunk, since the recent merge of the _Optional branch, we only get the warning in line 34.

     
  • Christopher Bazley

    Confirmed that only the warning at line 34 appears now. Great progress.

    ~/optional-ts/examples $ sdcc -c --std=c23 --stack-auto 3957.c
    3957.c:34: warning 355: pointer to _Optional could not be proven to be non-null at dereference
    ~/optional-ts/examples $ sdcc -v
    SDCC : mcs51/z80/z180/r2k/r2ka/r3ka/r4k/r5k/r6k/sm83/tlcs90/ez80/z80n/r800/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15/mos6502/mos65c02/f8/f8l TD- 4.5.24 #16456 (Mac OS X ppc)
    published under GNU General Public License (GPL)
    
     
  • Christopher Bazley

    I am attaching a proposed bugfix for this issue, created with assistance from Codex. I hope that it will prove acceptable when reviewed by SDCC's maintainers.

    I did the following testing of the proposed patch:

    • complete diagnostic-validation results (support/valdiag) for the default targets.
    • complete runtime regression (support/regression) results for ucz80.

    i.e.
    make -j2
    make -C support/regression clean-results
    make -C support/regression -j2 test-ucz80
    make -C support/valdiag clean
    make -C support/valdiag -j2

    Summary for 'ucz80': 0 failures, 36740 tests, 6407 test cases, 7347620 bytes, 1613638336 ticks
    Summary for 'mcs51': 0 failures, 652 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'mcs51-large': 0 failures, 650 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'mcs51-stack-auto': 0 failures, 651 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'ds390': 0 failures, 638 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'z80': 0 failures, 641 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'z180': 0 failures, 640 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'r2k': 0 failures, 640 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'r4k': 0 failures, 640 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'sm83': 0 failures, 640 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'tlcs90': 0 failures, 640 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 's08': 0 failures, 639 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'mos6502': 0 failures, 639 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'f8': 0 failures, 638 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'pdk13': 0 failures, 641 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'pdk14': 0 failures, 639 tests, 373 test cases, 0 bytes, 0 ticks
    Summary for 'pdk15': 0 failures, 637 tests, 373 test cases, 0 bytes, 0 ticks

     

Log in to post a comment.