Skip to content

fix: Add Dict and List to Any_to_bool precondition#675

Merged
MikaelMayer merged 33 commits intomainfrom
fix/pin-any-precondition
Mar 27, 2026
Merged

fix: Add Dict and List to Any_to_bool precondition#675
MikaelMayer merged 33 commits intomainfrom
fix/pin-any-precondition

Conversation

@MikaelMayer
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer commented Mar 26, 2026

Problem

Any_to_bool only accepted bool, none, string, and int in its precondition and body (returning false for everything else). This caused a soundness issue:

After if results:, the solver knows results must satisfy Any_to_bool's precondition and its body must return true. Since Dict and List were not handled, the solver could prove that results is never a Dict or List. Then PIn's precondition (requires isfrom_Dict || isfrom_ListAny) was provably unsatisfiable:

❌ always false and is reachable from declaration entry

This is a false positive — in Python, non-empty dicts and lists are truthy.

Fix

Add isfrom_Dict and isfrom_ListAny to Any_to_bool's precondition and body: empty dict/list → false, non-empty → true.

Additional changes

  • Add # strata-args: directive for per-file flags in test Python files
  • Deprecate pyAnalyze (non-Laurel) in CI — default to pyAnalyzeLaurel

Test

test_pin_any.py: def test_in_on_any(results: Any): if results: assert 'key' in results

  • Before: assert_assert(124)_calls_PIn_0: ❌ always false and is reachable
  • After: assert_assert(124)_calls_PIn_0: ❓ unknown

@MikaelMayer MikaelMayer force-pushed the fix/pin-any-precondition branch from e3d708d to 8b71992 Compare March 26, 2026 17:39
@MikaelMayer MikaelMayer changed the title fix: Handle 'in' operator on opaque Any values without false positive fix: Add Dict and List to Any_to_bool precondition Mar 26, 2026
@MikaelMayer

This comment was marked as outdated.

@MikaelMayer

This comment was marked as outdated.

@MikaelMayer MikaelMayer reopened this Mar 26, 2026
Any_to_bool only accepted bool, none, string, and int in its
precondition and body (returning false for everything else).
This caused a soundness issue: after `if results:`, the solver
knew results must satisfy Any_to_bool's precondition (bool/none/
string/int) and its body must return true. Since Dict and List
were not in the precondition, the solver could prove that results
is never Dict or List. Then PIn's precondition (requires Dict or
List) was provably unsatisfiable — reported as:
  ❌ always false and is reachable from declaration entry

In Python, non-empty dicts and lists are truthy. Add isfrom_Dict
and isfrom_ListAny to Any_to_bool's precondition and body
(empty → false, non-empty → true).

Test: test_pin_any.py — `if results: assert 'key' in results`
Before: PIn precondition is ❌ always false and is reachable
After:  PIn precondition is ❓ unknown
@MikaelMayer MikaelMayer force-pushed the fix/pin-any-precondition branch from 8b71992 to 496d5e1 Compare March 26, 2026 18:44
test_variable_reassign and test_while_loop had assertions that were
previously provably false (❌) because Any_to_bool excluded Dict/List.
With the fix, these become unknown (❓) — the solver can no longer
rule out Dict/List values after a truthiness check.
Remove Lean #eval test (requires fake fixture). Use run_py_analyze.sh
framework instead: test_pin_any.py in tests/ with expected output in
expected_laurel/. Empty expected file — CI will show the correct output.
… output

Add '# strata-args:' comment directive to Python test files for passing
extra flags to strata (e.g. --check-mode bugFinding). The shell script
parses this and appends the args to the strata invocation.

test_pin_any.py uses bugFinding mode to verify that PIn precondition
is not provably false after a truthiness check on an opaque Any.
@MikaelMayer MikaelMayer marked this pull request as ready for review March 27, 2026 01:41
@MikaelMayer MikaelMayer requested a review from a team March 27, 2026 01:41
@MikaelMayer MikaelMayer enabled auto-merge March 27, 2026 01:41
Copy link
Copy Markdown
Contributor

@joscoh joscoh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the CI change should be a separate PR, but I am fine to merge if others are.

Copy link
Copy Markdown
Contributor

@aqjune-aws aqjune-aws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving based on the fact that Thanh approved in the past

@MikaelMayer MikaelMayer added this pull request to the merge queue Mar 27, 2026
Merged via the queue into main with commit 41ae0c1 Mar 27, 2026
15 checks passed
@MikaelMayer MikaelMayer deleted the fix/pin-any-precondition branch March 27, 2026 03:17
olivier-aws pushed a commit that referenced this pull request Mar 30, 2026
## Problem

`Any_to_bool` only accepted `bool`, `none`, `string`, and `int` in its
precondition and body (returning `false` for everything else). This
caused a soundness issue:

After `if results:`, the solver knows `results` must satisfy
`Any_to_bool`'s precondition and its body must return `true`. Since Dict
and List were not handled, the solver could prove that `results` is
never a Dict or List. Then `PIn`'s precondition (`requires isfrom_Dict
|| isfrom_ListAny`) was provably unsatisfiable:

```
❌ always false and is reachable from declaration entry
```

This is a false positive — in Python, non-empty dicts and lists are
truthy.

## Fix

Add `isfrom_Dict` and `isfrom_ListAny` to `Any_to_bool`'s precondition
and body: empty dict/list → `false`, non-empty → `true`.

## Additional changes

- Add `# strata-args:` directive for per-file flags in test Python files
- Deprecate `pyAnalyze` (non-Laurel) in CI — default to
`pyAnalyzeLaurel`

## Test

`test_pin_any.py`: `def test_in_on_any(results: Any): if results: assert
'key' in results`

- **Before**: `assert_assert(124)_calls_PIn_0: ❌ always false and is
reachable`
- **After**: `assert_assert(124)_calls_PIn_0: ❓ unknown`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants