Skip to content

Partial evaluation behaves incorrectly with _==_ / _!=_ when one operand is unknown. #863

Description

@clmatt

During partial evaluation, the equality operators discard unknown operands and return a concrete result. Specifically an expression like false || x == 'foo' with x unknown returns concrete false when using partial eval, claiming the expression is false for every possible value of x, which is not true. All other operatorsI tested (>, >=, in, &&/|| absorption comprehensions, function dispatch) propagate unknowns correctly.

Reproducing (I tested with 0.6.0):

import java.util.Map;
  import org.projectnessie.cel.Env;
  import org.projectnessie.cel.EnvOption;
  import org.projectnessie.cel.EvalOption;
  import org.projectnessie.cel.Program;
  import org.projectnessie.cel.ProgramOption;
  import org.projectnessie.cel.checker.Decls;
  import org.projectnessie.cel.common.types.ref.Val;
  import org.projectnessie.cel.interpreter.Activation;
  import org.projectnessie.cel.interpreter.AttributePattern;

  public class Repro {
    static Env env;

    static Val partialEval(String expr) {
      Env.AstIssuesTuple parsed = env.parse(expr);
      if (parsed.hasIssues()) throw new IllegalStateException(parsed.getIssues().toString());
      Env.AstIssuesTuple checked = env.check(parsed.getAst());
      if (checked.hasIssues()) throw new IllegalStateException(checked.getIssues().toString());
      Program prg =
          env.program(checked.getAst(), ProgramOption.evalOptions(EvalOption.OptPartialEval));
      Activation.PartialActivation vars =
          Activation.newPartialActivation(
              Map.of(),
              AttributePattern.newAttributePattern("x"),
              AttributePattern.newAttributePattern("y"));
      return prg.eval(vars).getVal();
    }

    public static void main(String[] args) {
      env =
          Env.newEnv(
              EnvOption.declarations(
                  Decls.newVar("x", Decls.String), Decls.newVar("y", Decls.Int)));
      System.out.println("x == 'foo'           -> " + partialEval("x == 'foo'"));
      System.out.println("x != 'foo'           -> " + partialEval("x != 'foo'"));
      System.out.println("y > 5                -> " + partialEval("y > 5"));
      System.out.println("y >= 5               -> " + partialEval("y >= 5"));
      System.out.println("x in ['foo']         -> " + partialEval("x in ['foo']"));
      System.out.println("false || x == 'foo'  -> " + partialEval("false || x == 'foo'"));
    }
  }

Output with both x and y unknown:

  x == 'foo'              -> bool{false}        // expected: unknown{...}
  x != 'foo'               -> bool{true}         // expected: unknown{...}
  y > 5                     -> unknown{1}       // correct
  y >= 5                   -> unknown{1}       // correct
  x in ['foo']             -> unknown{1}      // correct
  false || x == 'foo'  -> bool{false}       // expected: unknown{...}  

The fix doesn't seem too bad, I'm happy to put in a PR, but I wanted to check that I'm not missing something.

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