2424import dev .cel .common .CelAbstractSyntaxTree ;
2525import dev .cel .common .CelMutableAst ;
2626import dev .cel .common .CelValidationException ;
27- import dev .cel .common .ast .CelConstant .Kind ;
2827import dev .cel .extensions .CelOptionalLibrary .Function ;
2928import dev .cel .optimizer .AstMutator ;
3029import dev .cel .optimizer .CelAstOptimizer ;
@@ -75,10 +74,14 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
7574 long lastOutputId = 0 ;
7675 for (CelCompiledMatch match : Lists .reverse (compiledRule .matches ())) {
7776 CelAbstractSyntaxTree conditionAst = match .condition ();
78- boolean isTriviallyTrue =
79- conditionAst .getExpr ().constantOrDefault ().getKind ().equals (Kind .BOOLEAN_VALUE )
80- && conditionAst .getExpr ().constant ().booleanValue ();
77+ // If the condition is trivially true, none of the matches in the rule causes the result
78+ // to become optional, and the rule is not the last match, then this will introduce
79+ // unreachable outputs or rules.
80+ boolean isTriviallyTrue = match .isConditionLiteral ();
81+
8182 switch (match .result ().kind ()) {
83+ // For the match's output, determine whether the output should be wrapped
84+ // into an optional value, a conditional, or both.
8285 case OUTPUT :
8386 OutputValue matchOutput = match .result ().output ();
8487 CelMutableAst outAst = CelMutableAst .fromCelAst (matchOutput .ast ());
@@ -107,21 +110,25 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
107110 lastOutputId = matchOutput .sourceId ();
108111 continue ;
109112 case RULE :
113+ // If the match has a nested rule, then compute the rule and whether it has
114+ // an optional return value.
110115 CelCompiledRule matchNestedRule = match .result ().rule ();
111116 RuleOptimizationResult nestedRule = optimizeRule (cel , matchNestedRule );
117+ boolean nestedHasOptional = matchNestedRule .hasOptionalOutput ();
112118 CelMutableAst nestedRuleAst = nestedRule .ast ();
113- if (isOptionalResult && !nestedRule . isOptionalResult () ) {
119+ if (isOptionalResult && !nestedHasOptional ) {
114120 nestedRuleAst =
115121 astMutator .newGlobalCall (Function .OPTIONAL_OF .getFunction (), nestedRuleAst );
116122 }
117- if (!isOptionalResult && nestedRule . isOptionalResult () ) {
123+ if (!isOptionalResult && nestedHasOptional ) {
118124 matchAst = astMutator .newGlobalCall (Function .OPTIONAL_OF .getFunction (), matchAst );
119125 isOptionalResult = true ;
120126 }
121- if (!isOptionalResult && !nestedRule .isOptionalResult ()) {
122- throw new IllegalArgumentException ("Subrule early terminates policy" );
123- }
124- if (isTriviallyTrue ) {
127+ // If either the nested rule or current condition output are optional then
128+ // use optional.or() to specify the combination of the first and second results
129+ // Note, the argument order is reversed due to the traversal of matches in
130+ // reverse order.
131+ if (isOptionalResult && isTriviallyTrue ) {
125132 matchAst = astMutator .newMemberCall (nestedRuleAst , Function .OR .getFunction (), matchAst );
126133 } else {
127134 matchAst =
@@ -131,6 +138,7 @@ private RuleOptimizationResult optimizeRule(Cel cel, CelCompiledRule compiledRul
131138 nestedRuleAst ,
132139 matchAst );
133140 }
141+
134142 assertComposedAstIsValid (
135143 cel ,
136144 matchAst ,
0 commit comments