2323#include < iterator>
2424#include < limits>
2525#include < memory>
26+ #include < optional>
2627#include < stack>
2728#include < string>
2829#include < type_traits>
2930#include < utility>
3031#include < vector>
3132
3233#include " absl/algorithm/container.h"
33- #include " absl/base/attributes.h"
34- #include " absl/base/optimization.h"
3534#include " absl/container/flat_hash_map.h"
3635#include " absl/container/flat_hash_set.h"
3736#include " absl/container/node_hash_map.h"
4544#include " absl/strings/str_cat.h"
4645#include " absl/strings/string_view.h"
4746#include " absl/strings/strip.h"
48- #include " absl/types/optional.h"
4947#include " absl/types/span.h"
5048#include " absl/types/variant.h"
5149#include " base/ast.h"
5957#include " common/expr.h"
6058#include " common/kind.h"
6159#include " common/type.h"
60+ #include " common/type_spec_resolver.h"
6261#include " common/value.h"
6362#include " eval/compiler/check_ast_extensions.h"
6463#include " eval/compiler/flat_expr_builder_extensions.h"
@@ -528,7 +527,7 @@ class FlatExprVisitor : public cel::AstVisitor {
528527 FlatExprVisitor (
529528 const Resolver& resolver, const cel::RuntimeOptions& options,
530529 std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers,
531- const absl::flat_hash_map<int64_t , cel::Reference >& reference_map ,
530+ const absl::flat_hash_map<int64_t , cel::TypeSpec >& type_map ,
532531 const cel::TypeProvider& type_provider, IssueCollector& issue_collector,
533532 ProgramBuilder& program_builder, PlannerContext& extension_context,
534533 bool enable_optional_types)
@@ -538,6 +537,7 @@ class FlatExprVisitor : public cel::AstVisitor {
538537 resolved_select_expr_(nullptr ),
539538 options_(options),
540539 program_optimizers_(std::move(program_optimizers)),
540+ type_map_(type_map),
541541 issue_collector_(issue_collector),
542542 program_builder_(program_builder),
543543 extension_context_(extension_context),
@@ -606,6 +606,21 @@ class FlatExprVisitor : public cel::AstVisitor {
606606
607607 bool PlanRecursiveProgram () const { return max_recursion_depth_ > 0 ; }
608608
609+ void SetResolvedType (const cel::Expr& expr, cel::Type type) {
610+ resolved_types_[&expr] = std::move (type);
611+ }
612+
613+ std::optional<cel::Type> GetResolvedType (const cel::Expr* expr) const {
614+ if (expr == nullptr ) {
615+ return std::nullopt ;
616+ }
617+ auto it = resolved_types_.find (expr);
618+ if (it != resolved_types_.end ()) {
619+ return it->second ;
620+ }
621+ return std::nullopt ;
622+ }
623+
609624 void PreVisitExpr (const cel::Expr& expr) override {
610625 ValidateOrError (!absl::holds_alternative<cel::UnspecifiedExpr>(expr.kind ()),
611626 " Invalid empty expression" );
@@ -617,6 +632,10 @@ class FlatExprVisitor : public cel::AstVisitor {
617632 resume_from_suppressed_branch_ = &expr;
618633 }
619634
635+ if (options_.enable_typed_field_access ) {
636+ MaybeResolveType (expr);
637+ }
638+
620639 if (block_.has_value ()) {
621640 BlockInfo& block = *block_;
622641 if (block.in && block.bindings_set .contains (&expr)) {
@@ -977,6 +996,24 @@ class FlatExprVisitor : public cel::AstVisitor {
977996 }
978997
979998 StringValue field = cel::StringValue (select_expr.field ());
999+ std::optional<cel::StructType> struct_type;
1000+ std::optional<cel::StructTypeField> field_type;
1001+ if (options_.enable_typed_field_access ) {
1002+ std::optional<cel::Type> operand_type =
1003+ GetResolvedType (&select_expr.operand ());
1004+ if (operand_type.has_value () && operand_type->IsStruct ()) {
1005+ struct_type = operand_type->GetStruct ();
1006+ if (struct_type.has_value ()) {
1007+ auto field_lookup =
1008+ extension_context_.type_reflector ().FindStructTypeFieldByName (
1009+ *struct_type, select_expr.field ());
1010+ // Swallow error to fallback to duck typing behavior.
1011+ if (field_lookup.ok () && field_lookup->has_value ()) {
1012+ field_type = *std::move (field_lookup);
1013+ }
1014+ }
1015+ }
1016+ }
9801017 if (auto depth = RecursionEligible (); depth.has_value ()) {
9811018 auto deps = ExtractRecursiveDependencies ();
9821019 if (deps.size () != 1 ) {
@@ -994,6 +1031,13 @@ class FlatExprVisitor : public cel::AstVisitor {
9941031 return ;
9951032 }
9961033
1034+ if (field_type.has_value ()) {
1035+ AddStep (CreateTypedSelectStep (
1036+ std::move (field), *struct_type, *std::move (field_type),
1037+ select_expr.test_only (), expr.id (),
1038+ options_.enable_empty_wrapper_null_unboxing , enable_optional_types_));
1039+ return ;
1040+ }
9971041 AddStep (CreateSelectStep (
9981042 std::move (field), select_expr.test_only (), expr.id (),
9991043 options_.enable_empty_wrapper_null_unboxing , enable_optional_types_));
@@ -1921,6 +1965,8 @@ class FlatExprVisitor : public cel::AstVisitor {
19211965 CallHandlerResult HandleHeterogeneousEqualityIn (const cel::Expr& expr,
19221966 const cel::CallExpr& call);
19231967
1968+ void MaybeResolveType (const cel::Expr& expr);
1969+
19241970 const Resolver& resolver_;
19251971 const cel::TypeProvider& type_provider_;
19261972 absl::Status progress_status_;
@@ -1942,6 +1988,8 @@ class FlatExprVisitor : public cel::AstVisitor {
19421988 absl::flat_hash_set<const cel::Expr*> suppressed_branches_;
19431989 const cel::Expr* resume_from_suppressed_branch_ = nullptr ;
19441990 std::vector<std::unique_ptr<ProgramOptimizer>> program_optimizers_;
1991+ const absl::flat_hash_map<int64_t , cel::TypeSpec>& type_map_;
1992+ absl::flat_hash_map<const cel::Expr*, cel::Type> resolved_types_;
19451993 IssueCollector& issue_collector_;
19461994
19471995 ProgramBuilder& program_builder_;
@@ -2161,6 +2209,23 @@ FlatExprVisitor::HandleHeterogeneousEqualityIn(const cel::Expr& expr,
21612209 return CallHandlerResult::kIntercepted ;
21622210}
21632211
2212+ void FlatExprVisitor::MaybeResolveType (const cel::Expr& expr) {
2213+ // Try to resolve the type from the type map, but don't fail if it's not
2214+ // there. This permits cases where the runtime type is compatible but not
2215+ // the same as the type checked type.
2216+ auto it = type_map_.find (expr.id ());
2217+ if (it == type_map_.end ()) {
2218+ return ;
2219+ }
2220+ absl::StatusOr<cel::Type> type = cel::ConvertTypeSpecToType (
2221+ it->second , extension_context_.type_reflector (),
2222+ extension_context_.MutableArena ());
2223+ if (!type.ok ()) {
2224+ return ;
2225+ }
2226+ SetResolvedType (expr, *type);
2227+ }
2228+
21642229void LogicalCondVisitor::PreVisit (const cel::Expr* expr) {
21652230 visitor_->ValidateOrError (
21662231 !expr->call_expr ().has_target () && expr->call_expr ().args ().size () >= 2 ,
@@ -2561,8 +2626,8 @@ absl::StatusOr<FlatExpression> FlatExprBuilder::CreateExpressionImpl(
25612626 // These objects are expected to remain scoped to one build call -- references
25622627 // to them shouldn't be persisted in any part of the result expression.
25632628 FlatExprVisitor visitor (resolver, options_, std::move (optimizers),
2564- ast->reference_map (), GetTypeProvider (),
2565- issue_collector, program_builder, extension_context,
2629+ ast->type_map (), GetTypeProvider (), issue_collector ,
2630+ program_builder, extension_context,
25662631 enable_optional_types_);
25672632
25682633 if (options_.max_recursion_depth == -1 || options_.max_recursion_depth > 0 ) {
0 commit comments