Thanks for the patch!
I did see one issue in this here's a statement for repro:
CREATE TEMP TABLE outer_t (x int);
CREATE TEMP TABLE inner_t (y int NOT NULL);
INSERT INTO outer_t VALUES (NULL), (1);
INSERT INTO inner_t VALUES (2);
SELECT x
FROM outer_t
WHERE NOT (x = ANY('{}'::int[]))
AND x NOT IN (SELECT y FROM inner_t);
SQL semantics return only 1. With the patch, the planner
produces a hash anti-join and also returns the NULL row: the
first qual admits it, and the anti-join preserves it, whereas the
original NOT IN evaluates to NULL and filters it out.
This helper weakness also affects the existing inner-query
proof path, but the patch creates a new regression surface.
I've posted a likely fix in [1].
Apart from the above, your patch looked good to me.
Regards,
Ayush
[1]
PostgreSQL: [PATCH] SAOP nullability analysis below NOT/BooleanTest