Skip to content

Unreachable pointer dereference fails verification after an unconditional return through if (1) #322

Description

@hei411

Description

PAL fails to verify a function containing an unreachable pointer dereference. The pointer is annotated _plain, so no ownership of its pointee is available—but none should be needed because the dereference cannot execute.

Reproducer

#include "pal.h"

int example(_plain int *p)
{
    if (1)
        return 7;

    return *p;  /* Cannot execute. */
}

Expected behavior

Verification succeeds without requiring ownership of *p. The function always returns 7.

Actual behavior

Translation reports no diagnostics, but verification fails with:

Error 228:
Tactic failed
Cannot prove:
    Pulse.Lib.Reference.pts_to var_p (*?u171*)_
In the context:
    Pulse.Lib.Reference.pts_to var_p var_p

PAL emits:

divergent fn func_example (var_p: (ref Int32.t))
  returns return_1 : Int32.t
{
  let mut var_p = var_p;
  if ((int32_to_bool 1l)) {
    return 7l;
  } else {};
  return (!(!var_p));
}

The available resource describes the generated mutable local holding p, not the object pointed to by p.

Explicit unreachability does not resolve the failure

Adding a ghost statement immediately before the dead dereference:

int example(_plain int *p)
{
    if (1)
        return 7;

    _ghost_stmt(unreachable ());
    return *p;
}

still fails, with a different diagnostic:

Error 228:
Tactic failed
Unexpected unresolved uvars in the term:
    !var_p

Additional investigation

Adding _ensures(false) immediately after if (1), before its then-statement, produces an explicit false postcondition for the generated if. However, verification then fails with a leftover resource for the mutable local holding p.

Adding _ghost_stmt(unreachable ()); in an explicit else branch removes that leftover-resource failure, but verification still fails at the subsequent dereference.

These experiments suggest an interaction between unreachable continuations and resource/type inference. The precise root cause, and whether the fix belongs in PAL’s lowering or Pulse’s handling of unreachable code, remain undetermined.

Reproduction environment

  • PAL main: 545978a
  • F* toolchain: fstar-2026-09-15
  • Regression: test/unreachable_deref/unreachable_deref.c
  • Focused command: make -C test/unreachable_deref -j8

No PAL implementation changes were made during reproduction.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions