diff --git a/checker/src/main/java/dev/cel/checker/Env.java b/checker/src/main/java/dev/cel/checker/Env.java index 97908dca3..dd1f92c2c 100644 --- a/checker/src/main/java/dev/cel/checker/Env.java +++ b/checker/src/main/java/dev/cel/checker/Env.java @@ -242,12 +242,12 @@ private static CelStandardDeclarations newStandardDeclarations(CelOptions celOpt } break; default: - if (!celOptions.enableHeterogeneousNumericComparisons() - && overload instanceof Comparison) { - Comparison comparison = (Comparison) overload; - if (comparison.isHeterogeneousComparison()) { - return false; - } + // Heterogeneous numeric comparisons require UnsignedLong at runtime so uint + // values do not share Long with int and dispatch to signed int64 overloads. + if (!celOptions.enableUnsignedLongs() + && overload instanceof Comparison + && ((Comparison) overload).isHeterogeneousComparison()) { + return false; } break; } diff --git a/checker/src/test/java/dev/cel/checker/CelStandardDeclarationsTest.java b/checker/src/test/java/dev/cel/checker/CelStandardDeclarationsTest.java index 867946fda..8c612b8e3 100644 --- a/checker/src/test/java/dev/cel/checker/CelStandardDeclarationsTest.java +++ b/checker/src/test/java/dev/cel/checker/CelStandardDeclarationsTest.java @@ -22,6 +22,7 @@ import com.google.testing.junit.testparameterinjector.TestParameters; import dev.cel.checker.CelStandardDeclarations.StandardFunction; import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.Arithmetic; +import dev.cel.checker.CelStandardDeclarations.StandardFunction.Overload.Comparison; import dev.cel.checker.CelStandardDeclarations.StandardIdentifier; import dev.cel.common.CelOptions; import dev.cel.common.CelValidationException; @@ -327,14 +328,36 @@ public void standardEnvironment_subsetEnvironment() throws Exception { @TestParameters("{expression: '2u <= 1'}") @TestParameters("{expression: '2u <= 1.0'}") @TestParameters("{expression: '1.0 <= 2u'}") - public void heterogeneousEqualityDisabled_mixedTypeComparisons_throws(String expression) { + public void standardEnvironment_excludeHeterogeneousComparisons_throws(String expression) { CelCompiler celCompiler = CelCompilerFactory.standardCelCompilerBuilder() - .setOptions(CelOptions.current().enableHeterogeneousNumericComparisons(false).build()) + .setStandardDeclarations( + CelStandardDeclarations.newBuilder() + .filterFunctions( + (function, overload) -> + !(overload instanceof Comparison + && ((Comparison) overload).isHeterogeneousComparison())) + .build()) + .build(); + CelValidationException e = + assertThrows(CelValidationException.class, () -> celCompiler.compile(expression).getAst()); + + assertThat(e).hasMessageThat().contains("found no matching overload for"); + } + + @Test + @TestParameters("{expression: '1 < 2.0'}") + @TestParameters("{expression: '1 < 2u'}") + @TestParameters("{expression: '2u < 1.0'}") + public void unsignedLongsDisabled_mixedTypeComparisons_throws(String expression) { + CelCompiler celCompiler = + CelCompilerFactory.standardCelCompilerBuilder() + .setOptions(CelOptions.current().enableUnsignedLongs(false).build()) .build(); CelValidationException e = assertThrows(CelValidationException.class, () -> celCompiler.compile(expression).getAst()); + assertThat(e).hasMessageThat().contains("found no matching overload for"); } diff --git a/common/src/main/java/dev/cel/common/CelOptions.java b/common/src/main/java/dev/cel/common/CelOptions.java index 632715fe5..31e05de2f 100644 --- a/common/src/main/java/dev/cel/common/CelOptions.java +++ b/common/src/main/java/dev/cel/common/CelOptions.java @@ -15,6 +15,7 @@ package dev.cel.common; import com.google.auto.value.AutoValue; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CheckReturnValue; import com.google.errorprone.annotations.Immutable; @@ -80,7 +81,13 @@ public enum ProtoUnsetFieldOptions { public abstract boolean enableTimestampEpoch(); - public abstract boolean enableHeterogeneousNumericComparisons(); + /** + * @deprecated Cross-type numeric comparisons are always enabled. + */ + @Deprecated + public boolean enableHeterogeneousNumericComparisons() { + return true; + } public abstract boolean enableNamespacedDeclarations(); @@ -148,7 +155,6 @@ public static Builder newBuilder() { .enableCompileTimeOverloadResolution(false) .enableHomogeneousLiterals(false) .enableTimestampEpoch(false) - .enableHeterogeneousNumericComparisons(false) .enableNamespacedDeclarations(true) .enableJsonFieldNames(false) // Evaluation options @@ -183,7 +189,6 @@ public static Builder current() { .enableUnsignedLongs(true) .enableRegexPartialMatch(true) .enableTimestampEpoch(true) - .enableHeterogeneousNumericComparisons(true) .errorOnDuplicateMapKeys(true) .evaluateCanonicalTypesToNativeValues(true) .errorOnIntWrap(true) @@ -320,20 +325,22 @@ public abstract static class Builder { public abstract Builder enableTimestampEpoch(boolean value); /** - * Enable the {@code less_equals_double_int64} and other cross-type numeric comparisons for - * {@code double}, {@code int64}, and {@code uint64} values. - * - *
This option is enabled by default in {@link CelOptions#current}. - * - *
TODO: Remove this feature once it has been auto-enabled.
- *
- * @deprecated This option is now enabled by default. If you are passing {@code true}, simply
- * remove this method call. If you are passing {@code false} to disable this feature, subset
- * the environment instead using {@code dev.cel.checker.CelStandardDeclarations} and {@code
+ * @deprecated Cross-type numeric comparisons are always enabled ({@code false} throws {@link
+ * IllegalArgumentException}). Remove calls passing {@code true}, or subset using {@code
+ * dev.cel.checker.CelStandardDeclarations} and {@code
* dev.cel.runtime.CelStandardFunctions}.
*/
@Deprecated
- public abstract Builder enableHeterogeneousNumericComparisons(boolean value);
+ @CanIgnoreReturnValue
+ public Builder enableHeterogeneousNumericComparisons(boolean value) {
+ if (!value) {
+ throw new IllegalArgumentException(
+ "Misconfigured CelOptions: enableHeterogeneousNumericComparisons cannot be disabled."
+ + " Subset the environment instead using CelStandardDeclarations and"
+ + " CelStandardFunctions.");
+ }
+ return this;
+ }
/**
* Enables the usage of namespaced functions and identifiers. This causes the type-checker to
diff --git a/common/src/test/java/dev/cel/common/CelOptionsTest.java b/common/src/test/java/dev/cel/common/CelOptionsTest.java
index d551f1d01..456e3dd56 100644
--- a/common/src/test/java/dev/cel/common/CelOptionsTest.java
+++ b/common/src/test/java/dev/cel/common/CelOptionsTest.java
@@ -15,6 +15,7 @@
package dev.cel.common;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,4 +38,29 @@ public void current_defaults() {
assertThat(CelOptions.current().build().enablePrattParser()).isFalse();
assertThat(CelOptions.current().build().enableIeee754ContainerEquality()).isTrue();
}
+
+ @Test
+ @SuppressWarnings("deprecation") // Testing deprecated compatibility methods
+ public void enableHeterogeneousNumericComparisons_true_isNoOp() {
+ CelOptions.Builder builder = CelOptions.newBuilder();
+
+ assertThat(builder.enableHeterogeneousNumericComparisons(true)).isSameInstanceAs(builder);
+ assertThat(CelOptions.DEFAULT.enableHeterogeneousNumericComparisons()).isTrue();
+ assertThat(CelOptions.LEGACY.enableHeterogeneousNumericComparisons()).isTrue();
+ }
+
+ @Test
+ @SuppressWarnings("deprecation") // Testing deprecated compatibility methods
+ public void enableHeterogeneousNumericComparisons_false_throws() {
+ CelOptions.Builder builder = CelOptions.DEFAULT.toBuilder();
+
+ IllegalArgumentException e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> builder.enableHeterogeneousNumericComparisons(false));
+
+ assertThat(e)
+ .hasMessageThat()
+ .contains("enableHeterogeneousNumericComparisons cannot be disabled");
+ }
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java
index 61e6ead60..e2da9de05 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeImpl.java
@@ -445,20 +445,11 @@ private static void assertAllowedCelOptions(CelOptions celOptions) {
throw new IllegalArgumentException(
prefix + "unwrapWellKnownTypesOnFunctionDispatch cannot be disabled.");
}
-
- // Disallowed options in favor of subsetting
- String subsettingError = "Subset the environment instead using setStandardFunctions method.";
-
if (!celOptions.enableTimestampEpoch()) {
- throw new IllegalArgumentException(
- prefix + "enableTimestampEpoch cannot be disabled. " + subsettingError);
- }
-
- if (!celOptions.enableHeterogeneousNumericComparisons()) {
throw new IllegalArgumentException(
prefix
- + "enableHeterogeneousNumericComparisons cannot be disabled. "
- + subsettingError);
+ + "enableTimestampEpoch cannot be disabled. Subset the environment instead using"
+ + " setStandardFunctions method.");
}
}
diff --git a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java
index a41ab9656..ea30dd942 100644
--- a/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java
+++ b/runtime/src/main/java/dev/cel/runtime/CelRuntimeLegacyImpl.java
@@ -409,7 +409,7 @@ private ImmutableSet