Skip to content

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

@clmatt

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions