diff --git a/checker/src/test/java/dev/cel/checker/TypesTest.java b/checker/src/test/java/dev/cel/checker/TypesTest.java index a8ca2167e..786e50668 100644 --- a/checker/src/test/java/dev/cel/checker/TypesTest.java +++ b/checker/src/test/java/dev/cel/checker/TypesTest.java @@ -256,6 +256,66 @@ public void isAssignable_typeType_occursCheck_failsOnTransitiveCycle() { assertThat(result).isNull(); } + @Test + public void isAssignable_typeType_occursCheck_mapTypeParam_to_typeParam() { + Map subs = new HashMap<>(); + TypeParamType typeParamT = TypeParamType.create("T"); + CelType fromType = TypeType.create(MapType.create(SimpleType.STRING, typeParamT)); + CelType toType = TypeType.create(typeParamT); + + Map result = Types.isAssignable(subs, fromType, toType); + + assertThat(result).isNull(); + } + + @Test + public void isAssignable_typeType_occursCheck_typeParam_to_mapTypeParam() { + Map subs = new HashMap<>(); + TypeParamType typeParamT = TypeParamType.create("T"); + CelType fromType = TypeType.create(typeParamT); + CelType toType = TypeType.create(MapType.create(SimpleType.STRING, typeParamT)); + + Map result = Types.isAssignable(subs, fromType, toType); + + assertThat(result).isNull(); + } + + @Test + public void isAssignable_typeType_occursCheck_mapTypeParamInKey_to_typeParam() { + Map subs = new HashMap<>(); + TypeParamType typeParamT = TypeParamType.create("T"); + CelType fromType = TypeType.create(MapType.create(typeParamT, SimpleType.STRING)); + CelType toType = TypeType.create(typeParamT); + + Map result = Types.isAssignable(subs, fromType, toType); + + assertThat(result).isNull(); + } + + @Test + public void isAssignable_typeType_occursCheck_listTypeParam_to_typeParam() { + Map subs = new HashMap<>(); + TypeParamType typeParamT = TypeParamType.create("T"); + CelType fromType = TypeType.create(ListType.create(typeParamT)); + CelType toType = TypeType.create(typeParamT); + + Map result = Types.isAssignable(subs, fromType, toType); + + assertThat(result).isNull(); + } + + @Test + public void isAssignable_typeType_occursCheck_optionalTypeParam_to_typeParam() { + Map subs = new HashMap<>(); + TypeParamType typeParamT = TypeParamType.create("T"); + CelType fromType = TypeType.create(OptionalType.create(typeParamT)); + CelType toType = TypeType.create(typeParamT); + + Map result = Types.isAssignable(subs, fromType, toType); + + assertThat(result).isNull(); + } + @Test public void compiler_typeParamInTypeType_resolvesReturnTypeInt() throws Exception { TypeParamType typeParamT = TypeParamType.create("T"); diff --git a/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java b/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java index 650c01526..f594c6dc2 100644 --- a/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java +++ b/extensions/src/test/java/dev/cel/extensions/CelOptionalLibraryTest.java @@ -14,8 +14,10 @@ package dev.cel.extensions; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeFalse; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -35,6 +37,8 @@ import dev.cel.common.CelValidationException; import dev.cel.common.CelVarDecl; import dev.cel.common.ast.CelExpr; +import dev.cel.common.ast.CelExpr.CelList; +import dev.cel.common.types.CelKind; import dev.cel.common.types.CelType; import dev.cel.common.types.ListType; import dev.cel.common.types.MapType; @@ -1799,6 +1803,32 @@ public void optionalMessageCreation_fieldKeySetOnNonOptional_throws() { "Cannot initialize optional entry 'single_double_wrapper' from non-optional value foo"); } + @Test + @TestParameters("{expression: '[type([]), int, type(optional.none())]'}") + @TestParameters("{expression: '[type([]), type(optional.none()), int]'}") + @TestParameters("{expression: '[int, type([]), type(optional.none())]'}") + @TestParameters("{expression: '[int, type(optional.none()), type([])]'}") + @TestParameters("{expression: '[type(optional.none()), type([]), int]'}") + @TestParameters("{expression: '[type(optional.none()), int, type([])]'}") + public void listType_heterogeneousTypePermutations_resolvesToListDyn(String expression) + throws Exception { + assumeFalse(testMode.equals(TestMode.PLANNER_PARSE_ONLY)); + + Cel cel = newCelBuilder().build(); + + CelAbstractSyntaxTree ast = compile(cel, expression); + CelList list = ast.getExpr().listOrDefault(); + + assertThat(ast.getResultType()).isEqualTo(ListType.create(SimpleType.DYN)); + assertThat( + list.elements().stream() + .map(elem -> ast.getType(elem.id()).map(CelType::kind)) + .collect(toImmutableList())) + .containsExactly( + Optional.of(CelKind.TYPE), Optional.of(CelKind.TYPE), Optional.of(CelKind.TYPE)); + assertThat((List) cel.createProgram(ast).eval()).hasSize(3); + } + private CelAbstractSyntaxTree compile(CelCompiler compiler, String expression) throws CelValidationException { CelAbstractSyntaxTree ast = compiler.parse(expression).getAst();