Skip to content

fix(interpreter): propagate unknown/error operands through == and !=#865

Open
clmatt wants to merge 1 commit into
projectnessie:mainfrom
clmatt:fix/eq-ne-propagate-unknown
Open

fix(interpreter): propagate unknown/error operands through == and !=#865
clmatt wants to merge 1 commit into
projectnessie:mainfrom
clmatt:fix/eq-ne-propagate-unknown

Conversation

@clmatt

@clmatt clmatt commented Jun 9, 2026

Copy link
Copy Markdown

Using partial evaluate with unknown variables with == or != would incorrectly output true or false as opposed to unknown.

See the issue at #863.

For the corrected test, email is unknown, so email == "wiley@acme.co" (and therefore the whole expression) should evaluate to unknown, not false.

@CLAassistant

CLAassistant commented Jun 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@dimas-b dimas-b requested a review from snazy June 9, 2026 14:54
dimas-b
dimas-b previously approved these changes Jun 9, 2026

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the contribution, @clmatt ! Changes LGTM 👍

Let's see if @snazy has any comments.

void equalityWithUnknownOperandStaysUnknown() {
assertThat(evalWithUnknownStringX("x == 'foo'")).isInstanceOf(UnknownT.class);
assertThat(evalWithUnknownStringX("x != 'foo'")).isInstanceOf(UnknownT.class);
assertThat(evalWithUnknownStringX("false || x == 'foo'")).isInstanceOf(UnknownT.class);

@dimas-b dimas-b Jun 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

would it make sense to add a test case for false && x == 'foo' -> false?.. or is it covered elsewhere?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point! One sec

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

👍

EvalEq and EvalNe evaluated their operands and called lhs.equal(rhs)
directly, without first checking whether either operand was unknown or
error. Because UnknownT.equal(...) returns BoolT.False, any "==" against
an unknown collapsed to a concrete false (and "!=" to a concrete true)
during partial evaluation, instead of propagating the unknown.

The sibling EvalBinary (used by <, <=, >, >=) already guards with
isUnknownOrError, and cel-go guards its equality evaluators the same way.
Add the same guard to EvalEq and EvalNe so an undecidable equality is
deferred rather than resolved.

This fixes residual-AST / partial evaluation: a policy of the form
`... && claims.email == "x"` evaluated with `email` unknown now yields a
residual of the deferred comparison instead of collapsing the whole
expression to false. ResidualAst_Complex is updated to assert the
corrected (unknown-propagating) outcome, and a focused regression test,
InterpreterTest.equalityWithUnknownOperandStaysUnknown, is added.
@clmatt clmatt force-pushed the fix/eq-ne-propagate-unknown branch from b235d57 to 85ab10f Compare June 9, 2026 15:19
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.

3 participants