diff --git a/eval/eval/ident_step.cc b/eval/eval/ident_step.cc index 81208414b..c1915c647 100644 --- a/eval/eval/ident_step.cc +++ b/eval/eval/ident_step.cc @@ -115,8 +115,10 @@ absl::Status SlotStep::Evaluate(ExecutionFrame* frame) const { if (frame->enable_missing_attribute_errors() && frame->attribute_utility().CheckForMissingAttribute(attribute_trail)) { + CEL_ASSIGN_OR_RETURN(std::string attribute, + attribute_trail.attribute().AsString()); frame->value_stack().Push(frame->value_factory().CreateErrorValue( - CreateMissingAttributeError(name_))); + CreateMissingAttributeError(std::move(attribute)))); return absl::OkStatus(); } diff --git a/eval/public/cel_options.h b/eval/public/cel_options.h index 80697c266..672324de1 100644 --- a/eval/public/cel_options.h +++ b/eval/public/cel_options.h @@ -139,7 +139,8 @@ struct InterpreterOptions { // // Note: In most cases enabling this option is safe, however to perform this // optimization overloads are not consulted for applicable calls. If you have - // overriden the default `matches` function you should not enable this option. + // overridden the default `matches` function you should not enable this + // option. bool enable_regex_precompilation = false; // Enable select optimization, replacing long select chains with a single @@ -158,7 +159,16 @@ struct InterpreterOptions { // enabling in an existing environment. bool enable_select_optimization = false; - // TODO(uncreated-issue/63): Do not use -- implementation in progress. + // Enable lazy cel.bind alias initialization. + // + // When enabled, cel.bind definition subexpression will be evaluated when the + // evaluator first requires the alias in the subexpression using the bound + // alias. When disabled, the cel.bind definition is eagerly evaluated. + // + // This prevents eagerly evaluating the definition subexpression if it is + // never used in the result subexpression of the cel.bind() macro. This allows + // for consistent behavior for CEL compiler optimized expressions that extract + // subexpressions to cel.bind calls. bool enable_lazy_bind_initialization = false; }; // LINT.ThenChange(//depot/google3/runtime/runtime_options.h) diff --git a/extensions/BUILD b/extensions/BUILD index 604989aca..53c52d935 100644 --- a/extensions/BUILD +++ b/extensions/BUILD @@ -133,15 +133,22 @@ cc_test( srcs = ["bindings_ext_test.cc"], deps = [ ":bindings_ext", + "//base:attributes", "//eval/public:activation", "//eval/public:builtin_func_registrar", "//eval/public:cel_expr_builder_factory", "//eval/public:cel_expression", + "//eval/public:cel_function", "//eval/public:cel_function_adapter", "//eval/public:cel_options", + "//eval/public:cel_value", + "//eval/public/structs:cel_proto_wrapper", "//internal:testing", "//parser", + "@com_google_absl//absl/strings:string_view", + "@com_google_cel_spec//proto/test/v1/proto2:test_all_types_cc_proto", "@com_google_googleapis//google/api/expr/v1alpha1:syntax_cc_proto", + "@com_google_protobuf//:protobuf", ], ) @@ -192,7 +199,6 @@ cc_library( "//runtime:runtime_options", "//runtime/internal:errors", "@com_google_absl//absl/algorithm:container", - "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/status", diff --git a/extensions/bindings_ext_test.cc b/extensions/bindings_ext_test.cc index 27deca9aa..c81cd77f6 100644 --- a/extensions/bindings_ext_test.cc +++ b/extensions/bindings_ext_test.cc @@ -20,33 +20,45 @@ #include #include "google/api/expr/v1alpha1/syntax.pb.h" +#include "absl/strings/string_view.h" +#include "base/attribute.h" #include "eval/public/activation.h" #include "eval/public/builtin_func_registrar.h" #include "eval/public/cel_expr_builder_factory.h" #include "eval/public/cel_expression.h" +#include "eval/public/cel_function.h" #include "eval/public/cel_function_adapter.h" #include "eval/public/cel_options.h" +#include "eval/public/cel_value.h" +#include "eval/public/structs/cel_proto_wrapper.h" #include "internal/testing.h" #include "parser/parser.h" +#include "proto/test/v1/proto2/test_all_types.pb.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/text_format.h" namespace cel::extensions { namespace { + +using ::google::api::expr::v1alpha1::CheckedExpr; using ::google::api::expr::v1alpha1::Expr; using ::google::api::expr::v1alpha1::ParsedExpr; using ::google::api::expr::v1alpha1::SourceInfo; - using ::google::api::expr::parser::ParseWithMacros; using ::google::api::expr::runtime::Activation; using ::google::api::expr::runtime::CelExpressionBuilder; using ::google::api::expr::runtime::CelFunction; using ::google::api::expr::runtime::CelFunctionDescriptor; +using ::google::api::expr::runtime::CelProtoWrapper; using ::google::api::expr::runtime::CelValue; using ::google::api::expr::runtime::CreateCelExpressionBuilder; using ::google::api::expr::runtime::FunctionAdapter; using ::google::api::expr::runtime::InterpreterOptions; using ::google::api::expr::runtime::RegisterBuiltinFunctions; - +using ::google::api::expr::runtime::UnknownProcessingOptions; +using ::google::api::expr::test::v1::proto2::NestedTestAllTypes; using ::google::protobuf::Arena; +using ::google::protobuf::TextFormat; using testing::HasSubstr; using cel::internal::IsOk; using cel::internal::StatusIs; @@ -153,5 +165,583 @@ INSTANTIATE_TEST_SUITE_P( /*constant_folding*/ testing::Bool(), /*lazy_bindings*/ testing::Bool())); +// Test bind expression with nested field selection. +// +// cel.bind(submsg, +// msg.child.child, +// (false) ? +// TestAllTypes{single_int64: -42}.single_int64 : +// submsg.payload.single_int64) +constexpr absl::string_view kFieldSelectTestExpr = R"pb( + reference_map: { + key: 4 + value: { name: "msg" } + } + reference_map: { + key: 8 + value: { overload_id: "conditional" } + } + reference_map: { + key: 9 + value: { name: "google.api.expr.test.v1.proto2.TestAllTypes" } + } + reference_map: { + key: 13 + value: { name: "submsg" } + } + reference_map: { + key: 18 + value: { name: "submsg" } + } + type_map: { + key: 4 + value: { message_type: "google.api.expr.test.v1.proto2.NestedTestAllTypes" } + } + type_map: { + key: 5 + value: { message_type: "google.api.expr.test.v1.proto2.NestedTestAllTypes" } + } + type_map: { + key: 6 + value: { message_type: "google.api.expr.test.v1.proto2.NestedTestAllTypes" } + } + type_map: { + key: 7 + value: { primitive: BOOL } + } + type_map: { + key: 8 + value: { primitive: INT64 } + } + type_map: { + key: 9 + value: { message_type: "google.api.expr.test.v1.proto2.TestAllTypes" } + } + type_map: { + key: 11 + value: { primitive: INT64 } + } + type_map: { + key: 12 + value: { primitive: INT64 } + } + type_map: { + key: 13 + value: { message_type: "google.api.expr.test.v1.proto2.NestedTestAllTypes" } + } + type_map: { + key: 14 + value: { message_type: "google.api.expr.test.v1.proto2.TestAllTypes" } + } + type_map: { + key: 15 + value: { primitive: INT64 } + } + type_map: { + key: 16 + value: { list_type: { elem_type: { dyn: {} } } } + } + type_map: { + key: 17 + value: { primitive: BOOL } + } + type_map: { + key: 18 + value: { message_type: "google.api.expr.test.v1.proto2.NestedTestAllTypes" } + } + type_map: { + key: 19 + value: { primitive: INT64 } + } + source_info: { + location: "" + line_offsets: 120 + positions: { key: 1 value: 0 } + positions: { key: 2 value: 8 } + positions: { key: 3 value: 9 } + positions: { key: 4 value: 17 } + positions: { key: 5 value: 20 } + positions: { key: 6 value: 26 } + positions: { key: 7 value: 35 } + positions: { key: 8 value: 42 } + positions: { key: 9 value: 56 } + positions: { key: 10 value: 69 } + positions: { key: 11 value: 71 } + positions: { key: 12 value: 75 } + positions: { key: 13 value: 91 } + positions: { key: 14 value: 97 } + positions: { key: 15 value: 105 } + positions: { key: 16 value: 8 } + positions: { key: 17 value: 8 } + positions: { key: 18 value: 8 } + positions: { key: 19 value: 8 } + macro_calls: { + key: 19 + value: { + call_expr: { + target: { + id: 1 + ident_expr: { name: "cel" } + } + function: "bind" + args: { + id: 3 + ident_expr: { name: "submsg" } + } + args: { + id: 6 + select_expr: { + operand: { + id: 5 + select_expr: { + operand: { + id: 4 + ident_expr: { name: "msg" } + } + field: "child" + } + } + field: "child" + } + } + args: { + id: 8 + call_expr: { + function: "_?_:_" + args: { + id: 7 + const_expr: { bool_value: false } + } + args: { + id: 12 + select_expr: { + operand: { + id: 9 + struct_expr: { + message_name: "google.api.expr.test.v1.proto2.TestAllTypes" + entries: { + id: 10 + field_key: "single_int64" + value: { + id: 11 + const_expr: { int64_value: -42 } + } + } + } + } + field: "single_int64" + } + } + args: { + id: 15 + select_expr: { + operand: { + id: 14 + select_expr: { + operand: { + id: 13 + ident_expr: { name: "submsg" } + } + field: "payload" + } + } + field: "single_int64" + } + } + } + } + } + } + } + } + expr: { + id: 19 + comprehension_expr: { + iter_var: "#unused" + iter_range: { + id: 16 + list_expr: {} + } + accu_var: "submsg" + accu_init: { + id: 6 + select_expr: { + operand: { + id: 5 + select_expr: { + operand: { + id: 4 + ident_expr: { name: "msg" } + } + field: "child" + } + } + field: "child" + } + } + loop_condition: { + id: 17 + const_expr: { bool_value: false } + } + loop_step: { + id: 18 + ident_expr: { name: "submsg" } + } + result: { + id: 8 + call_expr: { + function: "_?_:_" + args: { + id: 7 + const_expr: { bool_value: false } + } + args: { + id: 12 + select_expr: { + operand: { + id: 9 + struct_expr: { + message_name: "google.api.expr.test.v1.proto2.TestAllTypes" + entries: { + id: 10 + field_key: "single_int64" + value: { + id: 11 + const_expr: { int64_value: -42 } + } + } + } + } + field: "single_int64" + } + } + args: { + id: 15 + select_expr: { + operand: { + id: 14 + select_expr: { + operand: { + id: 13 + ident_expr: { name: "submsg" } + } + field: "payload" + } + } + field: "single_int64" + } + } + } + } + } + })pb"; + +class BindingsExtInteractionsTest + : public testing::TestWithParam> { + protected: + bool GetEnableLazyBinding() { return std::get<0>(GetParam()); } + bool GetEnableSelectOptimization() { return std::get<1>(GetParam()); } +}; + +TEST_P(BindingsExtInteractionsTest, SelectOptimization) { + CheckedExpr expr; + ASSERT_TRUE(TextFormat::ParseFromString(kFieldSelectTestExpr, &expr)); + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression(&expr)); + Arena arena; + Activation activation; + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsInt64()); + EXPECT_EQ(out.Int64OrDie(), 42); +} + +TEST_P(BindingsExtInteractionsTest, UnknownAttributesSelectOptimization) { + CheckedExpr expr; + ASSERT_TRUE(TextFormat::ParseFromString(kFieldSelectTestExpr, &expr)); + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.unknown_processing = UnknownProcessingOptions::kAttributeOnly; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression(&expr)); + Arena arena; + Activation activation; + activation.set_unknown_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsUnknownSet()); + EXPECT_THAT(out.UnknownSetOrDie()->unknown_attributes(), + testing::ElementsAre( + Attribute("msg", {AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("child")}))); +} + +TEST_P(BindingsExtInteractionsTest, + UnknownAttributeSelectOptimizationReturnValue) { + CheckedExpr expr; + ASSERT_TRUE(TextFormat::ParseFromString(kFieldSelectTestExpr, &expr)); + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.unknown_processing = UnknownProcessingOptions::kAttributeOnly; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression(&expr)); + Arena arena; + Activation activation; + activation.set_unknown_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("payload"), + AttributeQualifierPattern::OfString("single_int64")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsUnknownSet()) << out.DebugString(); + EXPECT_THAT(out.UnknownSetOrDie()->unknown_attributes(), + testing::ElementsAre(Attribute( + "msg", {AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("payload"), + AttributeQualifier::OfString("single_int64")}))); +} + +TEST_P(BindingsExtInteractionsTest, MissingAttributesSelectOptimization) { + CheckedExpr expr; + ASSERT_TRUE(TextFormat::ParseFromString(kFieldSelectTestExpr, &expr)); + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.enable_missing_attribute_errors = true; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression(&expr)); + Arena arena; + Activation activation; + activation.set_missing_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("payload"), + AttributeQualifierPattern::OfString("single_int64")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsError()) << out.DebugString(); + EXPECT_THAT(out.ErrorOrDie()->ToString(), + HasSubstr("msg.child.child.payload.single_int64")); +} + +TEST_P(BindingsExtInteractionsTest, UnknownAttribute) { + std::vector all_macros = Macro::AllMacros(); + std::vector bindings_macros = cel::extensions::bindings_macros(); + all_macros.insert(all_macros.end(), bindings_macros.begin(), + bindings_macros.end()); + ASSERT_OK_AND_ASSIGN(ParsedExpr expr, ParseWithMacros( + R"( + cel.bind( + x, + msg.child.payload.single_int64, + x < 42 || 1 == 1))", + all_macros)); + + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.unknown_processing = UnknownProcessingOptions::kAttributeOnly; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression( + &expr.expr(), &expr.source_info())); + Arena arena; + Activation activation; + activation.set_unknown_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("payload"), + AttributeQualifierPattern::OfString("single_int64")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsBool()) << out.DebugString(); + EXPECT_TRUE(out.BoolOrDie()); +} + +TEST_P(BindingsExtInteractionsTest, UnknownAttributeReturnValue) { + std::vector all_macros = Macro::AllMacros(); + std::vector bindings_macros = cel::extensions::bindings_macros(); + all_macros.insert(all_macros.end(), bindings_macros.begin(), + bindings_macros.end()); + ASSERT_OK_AND_ASSIGN(ParsedExpr expr, ParseWithMacros( + R"( + cel.bind( + x, + msg.child.payload.single_int64, + x))", + all_macros)); + + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.unknown_processing = UnknownProcessingOptions::kAttributeOnly; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression( + &expr.expr(), &expr.source_info())); + Arena arena; + Activation activation; + activation.set_unknown_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("payload"), + AttributeQualifierPattern::OfString("single_int64")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsUnknownSet()) << out.DebugString(); + EXPECT_THAT(out.UnknownSetOrDie()->unknown_attributes(), + testing::ElementsAre(Attribute( + "msg", {AttributeQualifier::OfString("child"), + AttributeQualifier::OfString("payload"), + AttributeQualifier::OfString("single_int64")}))); +} + +TEST_P(BindingsExtInteractionsTest, MissingAttribute) { + std::vector all_macros = Macro::AllMacros(); + std::vector bindings_macros = cel::extensions::bindings_macros(); + all_macros.insert(all_macros.end(), bindings_macros.begin(), + bindings_macros.end()); + ASSERT_OK_AND_ASSIGN(ParsedExpr expr, ParseWithMacros( + R"( + cel.bind( + x, + msg.child.payload.single_int64, + x < 42 || 1 == 2))", + all_macros)); + + InterpreterOptions options; + options.enable_empty_wrapper_null_unboxing = true; + options.enable_missing_attribute_errors = true; + options.enable_lazy_bind_initialization = GetEnableLazyBinding(); + options.enable_select_optimization = GetEnableSelectOptimization(); + std::unique_ptr builder = + CreateCelExpressionBuilder(options); + + ASSERT_OK(builder->GetRegistry()->Register(CreateBindFunction())); + + // Register builtins and configure the execution environment. + ASSERT_OK(RegisterBuiltinFunctions(builder->GetRegistry())); + + // Create CelExpression from AST (Expr object). + ASSERT_OK_AND_ASSIGN(auto cel_expr, builder->CreateExpression( + &expr.expr(), &expr.source_info())); + Arena arena; + Activation activation; + activation.set_missing_attribute_patterns({AttributePattern( + "msg", {AttributeQualifierPattern::OfString("child"), + AttributeQualifierPattern::OfString("payload"), + AttributeQualifierPattern::OfString("single_int64")})}); + + NestedTestAllTypes msg; + msg.mutable_child()->mutable_child()->mutable_payload()->set_single_int64(42); + + activation.InsertValue("msg", CelProtoWrapper::CreateMessage(&msg, &arena)); + + // Run evaluation. + ASSERT_OK_AND_ASSIGN(CelValue out, cel_expr->Evaluate(activation, &arena)); + ASSERT_TRUE(out.IsError()) << out.DebugString(); + EXPECT_THAT(out.ErrorOrDie()->ToString(), + HasSubstr("msg.child.payload.single_int64")); +} + +INSTANTIATE_TEST_SUITE_P(BindingsExtInteractionsTest, + BindingsExtInteractionsTest, + testing::Combine(testing::Bool(), testing::Bool())); + } // namespace } // namespace cel::extensions diff --git a/extensions/select_optimization.cc b/extensions/select_optimization.cc index 292bb7154..6dff8a8de 100644 --- a/extensions/select_optimization.cc +++ b/extensions/select_optimization.cc @@ -546,14 +546,12 @@ class RewriterImpl : public AstRewriterBase { class OptimizedSelectStep : public ExpressionStepBase { public: - OptimizedSelectStep(int expr_id, absl::optional attribute, - std::vector select_path, + OptimizedSelectStep(int expr_id, std::vector select_path, std::vector qualifiers, bool presence_test, bool enable_wrapper_type_null_unboxing, SelectOptimizationOptions options) : ExpressionStepBase(expr_id), - attribute_(std::move(attribute)), select_path_(std::move(select_path)), qualifiers_(std::move(qualifiers)), presence_test_(presence_test), @@ -618,11 +616,10 @@ absl::StatusOr>> CheckForMarkedAttributes( AttributeTrail OptimizedSelectStep::GetAttributeTrail( ExecutionFrame* frame) const { - if (attribute_.has_value()) { - return AttributeTrail(*attribute_); + const auto& attr = frame->value_stack().PeekAttribute(); + if (attr.empty()) { + return AttributeTrail(); } - - auto attr = frame->value_stack().PeekAttribute(); std::vector qualifiers = std::vector(attr.attribute().qualifier_path().begin(), attr.attribute().qualifier_path().end()); @@ -668,6 +665,14 @@ absl::Status OptimizedSelectStep::Evaluate(ExecutionFrame* frame) const { // variable names. constexpr size_t kStackInputs = 1; + // For now, we expect the operand to be top of stack. + const Handle& operand = frame->value_stack().Peek(); + + if (operand->Is() || operand->Is()) { + // Just forward the error which is already top of stack. + return absl::OkStatus(); + } + if (frame->enable_attribute_tracking()) { // Compute the attribute trail then check for any marked values. // When possible, this is computed at plan time based on the optimized @@ -684,13 +689,7 @@ absl::Status OptimizedSelectStep::Evaluate(ExecutionFrame* frame) const { } } - // Otherwise, we expect the operand to be top of stack. - const Handle& operand = frame->value_stack().Peek(); - if (operand->Is() || operand->Is()) { - // Just forward the error which is already top of stack. - return absl::OkStatus(); - } if (!operand->Is()) { return absl::InvalidArgumentError( @@ -756,7 +755,6 @@ absl::Status SelectOptimizer::OnPostVisit(PlannerContext& context, } const Expr& operand = node.call_expr().args()[0]; - absl::optional attribute; absl::string_view identifier; if (operand.has_ident_expr()) { identifier = operand.ident_expr().name(); @@ -778,10 +776,6 @@ absl::Status SelectOptimizer::OnPostVisit(PlannerContext& context, instruction)); } - if (!identifier.empty()) { - attribute.emplace(std::string(identifier), qualifiers); - } - // TODO(uncreated-issue/51): If the first argument is a string literal, the custom // step needs to handle variable lookup. google::api::expr::runtime::ExecutionPath path; @@ -797,9 +791,8 @@ absl::Status SelectOptimizer::OnPostVisit(PlannerContext& context, bool enable_wrapper_type_null_unboxing = context.options().enable_empty_wrapper_null_unboxing; path.push_back(std::make_unique( - node.id(), std::move(attribute), std::move(instructions), - std::move(qualifiers), presence_test, enable_wrapper_type_null_unboxing, - options_)); + node.id(), std::move(instructions), std::move(qualifiers), presence_test, + enable_wrapper_type_null_unboxing, options_)); return context.ReplaceSubplan(node, std::move(path)); } diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h index c69a53c0d..0e1df1ecc 100644 --- a/runtime/runtime_options.h +++ b/runtime/runtime_options.h @@ -129,7 +129,16 @@ struct RuntimeOptions { // cel representation of the proto defined default int64_t: 0. bool enable_empty_wrapper_null_unboxing = false; - // TODO(uncreated-issue/63): Do not use -- implementation in progress. + // Enable lazy cel.bind alias initialization. + // + // When enabled, cel.bind definition subexpression will be evaluated when the + // evaluator first requires the alias in the subexpression using the bound + // alias. When disabled, the cel.bind definition is eagerly evaluated. + // + // This prevents eagerly evaluating the definition subexpression if it is + // never used in the result subexpression of the cel.bind() macro. This allows + // for consistent behavior for CEL compiler optimized expressions that extract + // subexpressions to cel.bind calls. bool enable_lazy_bind_initialization = false; }; // LINT.ThenChange(//depot/google3/eval/public/cel_options.h)