From a47b9907b0eefcb6a2629037584e26dda08b0271 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Thu, 21 Aug 2025 11:27:32 -0700 Subject: [PATCH] Move AST metadata types from cel::ast_internal namespace to cel. Rename AST `Type` to `TypeSpec` to avoid collisions with the cel::Type representation. PiperOrigin-RevId: 797845083 --- common/ast/BUILD | 50 ++- common/ast/expr.cc | 137 ------ common/ast/expr.h | 841 +--------------------------------- common/ast/expr_test.cc | 260 ----------- common/ast/metadata.cc | 139 ++++++ common/ast/metadata.h | 868 ++++++++++++++++++++++++++++++++++++ common/ast/metadata_test.cc | 268 +++++++++++ common/ast_proto.cc | 2 +- 8 files changed, 1324 insertions(+), 1241 deletions(-) delete mode 100644 common/ast/expr.cc delete mode 100644 common/ast/expr_test.cc create mode 100644 common/ast/metadata.cc create mode 100644 common/ast/metadata.h create mode 100644 common/ast/metadata_test.cc diff --git a/common/ast/BUILD b/common/ast/BUILD index 68bfc72e4..07d4185ba 100644 --- a/common/ast/BUILD +++ b/common/ast/BUILD @@ -100,32 +100,11 @@ cc_test( cc_library( name = "expr", - srcs = ["expr.cc"], hdrs = [ "expr.h", ], deps = [ - "//common:constant", - "//common:expr", - "@com_google_absl//absl/base:no_destructor", - "@com_google_absl//absl/base:nullability", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/functional:overload", - "@com_google_absl//absl/types:optional", - "@com_google_absl//absl/types:variant", - ], -) - -cc_test( - name = "expr_test", - srcs = [ - "expr_test.cc", - ], - deps = [ - ":expr", - "//common:expr", - "//internal:testing", - "@com_google_absl//absl/types:variant", + ":metadata", ], ) @@ -146,3 +125,30 @@ cc_library( "@com_google_protobuf//:timestamp_cc_proto", ], ) + +cc_library( + name = "metadata", + srcs = ["metadata.cc"], + hdrs = ["metadata.h"], + deps = [ + "//common:constant", + "//common:expr", + "@com_google_absl//absl/base:no_destructor", + "@com_google_absl//absl/base:nullability", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:overload", + "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:variant", + ], +) + +cc_test( + name = "metadata_test", + srcs = ["metadata_test.cc"], + deps = [ + ":metadata", + "//common:expr", + "//internal:testing", + "@com_google_absl//absl/types:variant", + ], +) diff --git a/common/ast/expr.cc b/common/ast/expr.cc deleted file mode 100644 index d1767b142..000000000 --- a/common/ast/expr.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "common/ast/expr.h" - -#include -#include - -#include "absl/base/no_destructor.h" -#include "absl/functional/overload.h" -#include "absl/types/variant.h" - -namespace cel::ast_internal { - -namespace { - -const Type& default_type() { - static absl::NoDestructor type(TypeKind{UnspecifiedType()}); - return *type; -} - -TypeKind CopyImpl(const TypeKind& other) { - return absl::visit(absl::Overload( - [](const std::unique_ptr& other) -> TypeKind { - if (other == nullptr) { - return std::make_unique(); - } - return std::make_unique(*other); - }, - [](const auto& other) -> TypeKind { - // Other variants define copy ctor. - return other; - }), - other); -} - -} // namespace - -const Extension::Version& Extension::Version::DefaultInstance() { - static absl::NoDestructor instance; - return *instance; -} - -const Extension& Extension::DefaultInstance() { - static absl::NoDestructor instance; - return *instance; -} - -Extension::Extension(const Extension& other) - : id_(other.id_), - affected_components_(other.affected_components_), - version_(std::make_unique(*other.version_)) {} - -Extension& Extension::operator=(const Extension& other) { - id_ = other.id_; - affected_components_ = other.affected_components_; - version_ = std::make_unique(*other.version_); - return *this; -} - -const Type& ListType::elem_type() const { - if (elem_type_ != nullptr) { - return *elem_type_; - } - return default_type(); -} - -bool ListType::operator==(const ListType& other) const { - return elem_type() == other.elem_type(); -} - -const Type& MapType::key_type() const { - if (key_type_ != nullptr) { - return *key_type_; - } - return default_type(); -} - -const Type& MapType::value_type() const { - if (value_type_ != nullptr) { - return *value_type_; - } - return default_type(); -} - -bool MapType::operator==(const MapType& other) const { - return key_type() == other.key_type() && value_type() == other.value_type(); -} - -const Type& FunctionType::result_type() const { - if (result_type_ != nullptr) { - return *result_type_; - } - return default_type(); -} - -bool FunctionType::operator==(const FunctionType& other) const { - return result_type() == other.result_type() && arg_types_ == other.arg_types_; -} - -const Type& Type::type() const { - auto* value = absl::get_if>(&type_kind_); - if (value != nullptr) { - if (*value != nullptr) return **value; - } - return default_type(); -} - -Type::Type(const Type& other) : type_kind_(CopyImpl(other.type_kind_)) {} - -Type& Type::operator=(const Type& other) { - type_kind_ = CopyImpl(other.type_kind_); - return *this; -} - -FunctionType::FunctionType(const FunctionType& other) - : result_type_(std::make_unique(other.result_type())), - arg_types_(other.arg_types()) {} - -FunctionType& FunctionType::operator=(const FunctionType& other) { - result_type_ = std::make_unique(other.result_type()); - arg_types_ = other.arg_types(); - return *this; -} - -} // namespace cel::ast_internal diff --git a/common/ast/expr.h b/common/ast/expr.h index a3820c4a6..1e5ac45e5 100644 --- a/common/ast/expr.h +++ b/common/ast/expr.h @@ -17,830 +17,29 @@ #ifndef THIRD_PARTY_CEL_CPP_BASE_AST_INTERNAL_EXPR_H_ #define THIRD_PARTY_CEL_CPP_BASE_AST_INTERNAL_EXPR_H_ -#include -#include -#include -#include -#include - -#include "absl/base/nullability.h" -#include "absl/container/flat_hash_map.h" -#include "absl/types/optional.h" -#include "absl/types/variant.h" -#include "common/constant.h" -#include "common/expr.h" +#include "common/ast/metadata.h" namespace cel::ast_internal { - -// An extension that was requested for the source expression. -class Extension { - public: - // Version - class Version { - public: - Version() : major_(0), minor_(0) {} - Version(int64_t major, int64_t minor) : major_(major), minor_(minor) {} - - Version(const Version& other) = default; - Version(Version&& other) = default; - Version& operator=(const Version& other) = default; - Version& operator=(Version&& other) = default; - - static const Version& DefaultInstance(); - - // Major version changes indicate different required support level from - // the required components. - int64_t major() const { return major_; } - void set_major(int64_t val) { major_ = val; } - - // Minor version changes must not change the observed behavior from - // existing implementations, but may be provided informationally. - int64_t minor() const { return minor_; } - void set_minor(int64_t val) { minor_ = val; } - - bool operator==(const Version& other) const { - return major_ == other.major_ && minor_ == other.minor_; - } - - bool operator!=(const Version& other) const { return !operator==(other); } - - private: - int64_t major_; - int64_t minor_; - }; - - // CEL component specifier. - enum class Component { - // Unspecified, default. - kUnspecified, - // Parser. Converts a CEL string to an AST. - kParser, - // Type checker. Checks that references in an AST are defined and types - // agree. - kTypeChecker, - // Runtime. Evaluates a parsed and optionally checked CEL AST against a - // context. - kRuntime - }; - - static const Extension& DefaultInstance(); - - Extension() = default; - Extension(std::string id, std::unique_ptr version, - std::vector affected_components) - : id_(std::move(id)), - affected_components_(std::move(affected_components)), - version_(std::move(version)) {} - - Extension(const Extension& other); - Extension(Extension&& other) = default; - Extension& operator=(const Extension& other); - Extension& operator=(Extension&& other) = default; - - // Identifier for the extension. Example: constant_folding - const std::string& id() const { return id_; } - void set_id(std::string id) { id_ = std::move(id); } - - // If set, the listed components must understand the extension for the - // expression to evaluate correctly. - // - // This field has set semantics, repeated values should be deduplicated. - const std::vector& affected_components() const { - return affected_components_; - } - - std::vector& mutable_affected_components() { - return affected_components_; - } - - // Version info. May be skipped if it isn't meaningful for the extension. - // (for example constant_folding might always be v0.0). - const Version& version() const { - if (version_ == nullptr) { - return Version::DefaultInstance(); - } - return *version_; - } - - Version& mutable_version() { - if (version_ == nullptr) { - version_ = std::make_unique(); - } - return *version_; - } - - void set_version(std::unique_ptr version) { - version_ = std::move(version); - } - - bool operator==(const Extension& other) const { - return id_ == other.id_ && - affected_components_ == other.affected_components_ && - version() == other.version(); - } - - bool operator!=(const Extension& other) const { return !operator==(other); } - - private: - std::string id_; - std::vector affected_components_; - std::unique_ptr version_; -}; - -// Source information collected at parse time. -class SourceInfo { - public: - SourceInfo() = default; - SourceInfo(std::string syntax_version, std::string location, - std::vector line_offsets, - absl::flat_hash_map positions, - absl::flat_hash_map macro_calls, - std::vector extensions) - : syntax_version_(std::move(syntax_version)), - location_(std::move(location)), - line_offsets_(std::move(line_offsets)), - positions_(std::move(positions)), - macro_calls_(std::move(macro_calls)), - extensions_(std::move(extensions)) {} - - void set_syntax_version(std::string syntax_version) { - syntax_version_ = std::move(syntax_version); - } - - void set_location(std::string location) { location_ = std::move(location); } - - void set_line_offsets(std::vector line_offsets) { - line_offsets_ = std::move(line_offsets); - } - - void set_positions(absl::flat_hash_map positions) { - positions_ = std::move(positions); - } - - void set_macro_calls(absl::flat_hash_map macro_calls) { - macro_calls_ = std::move(macro_calls); - } - - const std::string& syntax_version() const { return syntax_version_; } - - const std::string& location() const { return location_; } - - const std::vector& line_offsets() const { return line_offsets_; } - - std::vector& mutable_line_offsets() { return line_offsets_; } - - const absl::flat_hash_map& positions() const { - return positions_; - } - - absl::flat_hash_map& mutable_positions() { - return positions_; - } - - const absl::flat_hash_map& macro_calls() const { - return macro_calls_; - } - - absl::flat_hash_map& mutable_macro_calls() { - return macro_calls_; - } - - bool operator==(const SourceInfo& other) const { - return syntax_version_ == other.syntax_version_ && - location_ == other.location_ && - line_offsets_ == other.line_offsets_ && - positions_ == other.positions_ && - macro_calls_ == other.macro_calls_ && - extensions_ == other.extensions_; - } - - bool operator!=(const SourceInfo& other) const { return !operator==(other); } - - const std::vector& extensions() const { return extensions_; } - - std::vector& mutable_extensions() { return extensions_; } - - private: - // The syntax version of the source, e.g. `cel1`. - std::string syntax_version_; - - // The location name. All position information attached to an expression is - // relative to this location. - // - // The location could be a file, UI element, or similar. For example, - // `acme/app/AnvilPolicy.cel`. - std::string location_; - - // Monotonically increasing list of code point offsets where newlines - // `\n` appear. - // - // The line number of a given position is the index `i` where for a given - // `id` the `line_offsets[i] < id_positions[id] < line_offsets[i+1]`. The - // column may be derivd from `id_positions[id] - line_offsets[i]`. - // - // TODO(uncreated-issue/14): clarify this documentation - std::vector line_offsets_; - - // A map from the parse node id (e.g. `Expr.id`) to the code point offset - // within source. - absl::flat_hash_map positions_; - - // A map from the parse node id where a macro replacement was made to the - // call `Expr` that resulted in a macro expansion. - // - // For example, `has(value.field)` is a function call that is replaced by a - // `test_only` field selection in the AST. Likewise, the call - // `list.exists(e, e > 10)` translates to a comprehension expression. The key - // in the map corresponds to the expression id of the expanded macro, and the - // value is the call `Expr` that was replaced. - absl::flat_hash_map macro_calls_; - - // A list of tags for extensions that were used while parsing or type checking - // the source expression. For example, optimizations that require special - // runtime support may be specified. - // - // These are used to check feature support between components in separate - // implementations. This can be used to either skip redundant work or - // report an error if the extension is unsupported. - std::vector extensions_; -}; - -// CEL primitive types. -enum class PrimitiveType { - // Unspecified type. - kPrimitiveTypeUnspecified = 0, - // Boolean type. - kBool = 1, - // Int64 type. - // - // Proto-based integer values are widened to int64. - kInt64 = 2, - // Uint64 type. - // - // Proto-based unsigned integer values are widened to uint64. - kUint64 = 3, - // Double type. - // - // Proto-based float values are widened to double values. - kDouble = 4, - // String type. - kString = 5, - // Bytes type. - kBytes = 6, -}; - -// Well-known protobuf types treated with first-class support in CEL. -// -// TODO(uncreated-issue/15): represent well-known via abstract types (or however) -// they will be named. -enum class WellKnownType { - // Unspecified type. - kWellKnownTypeUnspecified = 0, - // Well-known protobuf.Any type. - // - // Any types are a polymorphic message type. During type-checking they are - // treated like `DYN` types, but at runtime they are resolved to a specific - // message type specified at evaluation time. - kAny = 1, - // Well-known protobuf.Timestamp type, internally referenced as `timestamp`. - kTimestamp = 2, - // Well-known protobuf.Duration type, internally referenced as `duration`. - kDuration = 3, -}; - -class Type; - -// List type with typed elements, e.g. `list`. -class ListType { - public: - ListType() = default; - - ListType(const ListType& rhs) - : elem_type_(std::make_unique(rhs.elem_type())) {} - ListType& operator=(const ListType& rhs) { - elem_type_ = std::make_unique(rhs.elem_type()); - return *this; - } - ListType(ListType&& rhs) = default; - ListType& operator=(ListType&& rhs) = default; - - explicit ListType(std::unique_ptr elem_type) - : elem_type_(std::move(elem_type)) {} - - void set_elem_type(std::unique_ptr elem_type) { - elem_type_ = std::move(elem_type); - } - - bool has_elem_type() const { return elem_type_ != nullptr; } - - const Type& elem_type() const; - - Type& mutable_elem_type() { - if (elem_type_ == nullptr) { - elem_type_ = std::make_unique(); - } - return *elem_type_; - } - - bool operator==(const ListType& other) const; - - private: - std::unique_ptr elem_type_; -}; - -// Map type with parameterized key and value types, e.g. `map`. -class MapType { - public: - MapType() = default; - MapType(std::unique_ptr key_type, std::unique_ptr value_type) - : key_type_(std::move(key_type)), value_type_(std::move(value_type)) {} - - MapType(const MapType& rhs) - : key_type_(std::make_unique(rhs.key_type())), - value_type_(std::make_unique(rhs.value_type())) {} - MapType& operator=(const MapType& rhs) { - key_type_ = std::make_unique(rhs.key_type()); - value_type_ = std::make_unique(rhs.value_type()); - - return *this; - } - MapType(MapType&& rhs) = default; - MapType& operator=(MapType&& rhs) = default; - - void set_key_type(std::unique_ptr key_type) { - key_type_ = std::move(key_type); - } - - void set_value_type(std::unique_ptr value_type) { - value_type_ = std::move(value_type); - } - - bool has_key_type() const { return key_type_ != nullptr; } - - bool has_value_type() const { return value_type_ != nullptr; } - - const Type& key_type() const; - - const Type& value_type() const; - - bool operator==(const MapType& other) const; - - Type& mutable_key_type() { - if (key_type_ == nullptr) { - key_type_ = std::make_unique(); - } - return *key_type_; - } - - Type& mutable_value_type() { - if (value_type_ == nullptr) { - value_type_ = std::make_unique(); - } - return *value_type_; - } - - private: - // The type of the key. - std::unique_ptr key_type_; - - // The type of the value. - std::unique_ptr value_type_; -}; - -// Function type with result and arg types. -// -// (-- -// NOTE: function type represents a lambda-style argument to another function. -// Supported through macros, but not yet a first-class concept in CEL. -// --) -class FunctionType { - public: - FunctionType() = default; - FunctionType(std::unique_ptr result_type, std::vector arg_types); - - FunctionType(const FunctionType& other); - FunctionType& operator=(const FunctionType& other); - FunctionType(FunctionType&&) = default; - FunctionType& operator=(FunctionType&&) = default; - - void set_result_type(std::unique_ptr result_type) { - result_type_ = std::move(result_type); - } - - void set_arg_types(std::vector arg_types); - - bool has_result_type() const { return result_type_ != nullptr; } - - const Type& result_type() const; - - Type& mutable_result_type() { - if (result_type_ == nullptr) { - result_type_ = std::make_unique(); - } - return *result_type_; - } - - const std::vector& arg_types() const { return arg_types_; } - - std::vector& mutable_arg_types() { return arg_types_; } - - bool operator==(const FunctionType& other) const; - - private: - // Result type of the function. - std::unique_ptr result_type_; - - // Argument types of the function. - std::vector arg_types_; -}; - -// Application defined abstract type. -// -// TODO(uncreated-issue/15): decide on final naming for this. -class AbstractType { - public: - AbstractType() = default; - AbstractType(std::string name, std::vector parameter_types); - - void set_name(std::string name) { name_ = std::move(name); } - - void set_parameter_types(std::vector parameter_types); - - const std::string& name() const { return name_; } - - const std::vector& parameter_types() const { return parameter_types_; } - - std::vector& mutable_parameter_types() { return parameter_types_; } - - bool operator==(const AbstractType& other) const; - - private: - // The fully qualified name of this abstract type. - std::string name_; - - // Parameter types for this abstract type. - std::vector parameter_types_; -}; - -// Wrapper of a primitive type, e.g. `google.protobuf.Int64Value`. -class PrimitiveTypeWrapper { - public: - explicit PrimitiveTypeWrapper(PrimitiveType type) : type_(std::move(type)) {} - - void set_type(PrimitiveType type) { type_ = std::move(type); } - - const PrimitiveType& type() const { return type_; } - - PrimitiveType& mutable_type() { return type_; } - - bool operator==(const PrimitiveTypeWrapper& other) const { - return type_ == other.type_; - } - - private: - PrimitiveType type_; -}; - -// Protocol buffer message type. -// -// The `message_type` string specifies the qualified message type name. For -// example, `google.plus.Profile`. -class MessageType { - public: - MessageType() = default; - explicit MessageType(std::string type) : type_(std::move(type)) {} - - void set_type(std::string type) { type_ = std::move(type); } - - const std::string& type() const { return type_; } - - bool operator==(const MessageType& other) const { - return type_ == other.type_; - } - - private: - std::string type_; -}; - -// Type param type. -// -// The `type_param` string specifies the type parameter name, e.g. `list` -// would be a `list_type` whose element type was a `type_param` type -// named `E`. -class ParamType { - public: - ParamType() = default; - explicit ParamType(std::string type) : type_(std::move(type)) {} - - void set_type(std::string type) { type_ = std::move(type); } - - const std::string& type() const { return type_; } - - bool operator==(const ParamType& other) const { return type_ == other.type_; } - - private: - std::string type_; -}; - -// Error type. -// -// During type-checking if an expression is an error, its type is propagated -// as the `ERROR` type. This permits the type-checker to discover other -// errors present in the expression. -enum class ErrorType { kErrorTypeValue = 0 }; - -struct UnspecifiedType : public absl::monostate {}; - -struct DynamicType {}; - -inline bool operator==(const DynamicType&, const DynamicType&) { return true; } -inline bool operator!=(const DynamicType&, const DynamicType&) { return false; } - -struct NullType {}; -inline bool operator==(const NullType&, const NullType&) { return true; } -inline bool operator!=(const NullType&, const NullType&) { return false; } - -using TypeKind = - absl::variant, ErrorType, AbstractType>; - -// Analogous to cel::expr::Type. -// Represents a CEL type. -// -// TODO(uncreated-issue/15): align with value.proto -class Type { - public: - Type() = default; - explicit Type(TypeKind type_kind) : type_kind_(std::move(type_kind)) {} - - Type(const Type& other); - Type& operator=(const Type& other); - Type(Type&&) = default; - Type& operator=(Type&&) = default; - - void set_type_kind(TypeKind type_kind) { type_kind_ = std::move(type_kind); } - - const TypeKind& type_kind() const { return type_kind_; } - - TypeKind& mutable_type_kind() { return type_kind_; } - - bool has_dyn() const { - return absl::holds_alternative(type_kind_); - } - - bool has_null() const { - return absl::holds_alternative(type_kind_); - } - - bool has_primitive() const { - return absl::holds_alternative(type_kind_); - } - - bool has_wrapper() const { - return absl::holds_alternative(type_kind_); - } - - bool has_well_known() const { - return absl::holds_alternative(type_kind_); - } - - bool has_list_type() const { - return absl::holds_alternative(type_kind_); - } - - bool has_map_type() const { - return absl::holds_alternative(type_kind_); - } - - bool has_function() const { - return absl::holds_alternative(type_kind_); - } - - bool has_message_type() const { - return absl::holds_alternative(type_kind_); - } - - bool has_type_param() const { - return absl::holds_alternative(type_kind_); - } - - bool has_type() const { - return absl::holds_alternative>(type_kind_); - } - - bool has_error() const { - return absl::holds_alternative(type_kind_); - } - - bool has_abstract_type() const { - return absl::holds_alternative(type_kind_); - } - - NullType null() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - return {}; - } - - PrimitiveType primitive() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - return PrimitiveType::kPrimitiveTypeUnspecified; - } - - PrimitiveType wrapper() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return value->type(); - } - return PrimitiveType::kPrimitiveTypeUnspecified; - } - - WellKnownType well_known() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - return WellKnownType::kWellKnownTypeUnspecified; - } - - const ListType& list_type() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const ListType* default_list_type = new ListType(); - return *default_list_type; - } - - const MapType& map_type() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const MapType* default_map_type = new MapType(); - return *default_map_type; - } - - const FunctionType& function() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const FunctionType* default_function_type = new FunctionType(); - return *default_function_type; - } - - const MessageType& message_type() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const MessageType* default_message_type = new MessageType(); - return *default_message_type; - } - - const ParamType& type_param() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const ParamType* default_param_type = new ParamType(); - return *default_param_type; - } - - const Type& type() const; - - ErrorType error_type() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - return ErrorType::kErrorTypeValue; - } - - const AbstractType& abstract_type() const { - auto* value = absl::get_if(&type_kind_); - if (value != nullptr) { - return *value; - } - static const AbstractType* default_abstract_type = new AbstractType(); - return *default_abstract_type; - } - - bool operator==(const Type& other) const { - if (absl::holds_alternative>(type_kind_) && - absl::holds_alternative>(other.type_kind_)) { - const auto& self_type = absl::get>(type_kind_); - const auto& other_type = - absl::get>(other.type_kind_); - if (self_type == nullptr || other_type == nullptr) { - return self_type == other_type; - } - return *self_type == *other_type; - } - return type_kind_ == other.type_kind_; - } - - private: - TypeKind type_kind_; -}; - -// Describes a resolved reference to a declaration. -class Reference { - public: - Reference() = default; - - Reference(std::string name, std::vector overload_id, - Constant value) - : name_(std::move(name)), - overload_id_(std::move(overload_id)), - value_(std::move(value)) {} - - void set_name(std::string name) { name_ = std::move(name); } - - void set_overload_id(std::vector overload_id) { - overload_id_ = std::move(overload_id); - } - - void set_value(Constant value) { value_ = std::move(value); } - - const std::string& name() const { return name_; } - - const std::vector& overload_id() const { return overload_id_; } - - const Constant& value() const { - if (value_.has_value()) { - return value_.value(); - } - static const Constant* default_constant = new Constant; - return *default_constant; - } - - std::vector& mutable_overload_id() { return overload_id_; } - - Constant& mutable_value() { - if (!value_.has_value()) { - value_.emplace(); - } - return *value_; - } - - bool has_value() const { return value_.has_value(); } - - bool operator==(const Reference& other) const { - return name_ == other.name_ && overload_id_ == other.overload_id_ && - value() == other.value(); - } - - private: - // The fully qualified name of the declaration. - std::string name_; - // For references to functions, this is a list of `Overload.overload_id` - // values which match according to typing rules. - // - // If the list has more than one element, overload resolution among the - // presented candidates must happen at runtime because of dynamic types. The - // type checker attempts to narrow down this list as much as possible. - // - // Empty if this is not a reference to a [Decl.FunctionDecl][]. - std::vector overload_id_; - // For references to constants, this may contain the value of the - // constant if known at compile time. - absl::optional value_; -}; - -//////////////////////////////////////////////////////////////////////// -// Implementation details -//////////////////////////////////////////////////////////////////////// - -inline FunctionType::FunctionType(std::unique_ptr result_type, - std::vector arg_types) - : result_type_(std::move(result_type)), arg_types_(std::move(arg_types)) {} - -inline void FunctionType::set_arg_types(std::vector arg_types) { - arg_types_ = std::move(arg_types); -} - -inline AbstractType::AbstractType(std::string name, - std::vector parameter_types) - : name_(std::move(name)), parameter_types_(std::move(parameter_types)) {} - -inline void AbstractType::set_parameter_types( - std::vector parameter_types) { - parameter_types_ = std::move(parameter_types); -} - -inline bool AbstractType::operator==(const AbstractType& other) const { - return name_ == other.name_ && parameter_types_ == other.parameter_types_; -} +// Temporary aliases to allow for moving the metadata types. +using TypeKind = cel::TypeSpecKind; +using Type = cel::TypeSpec; +using Extension = cel::ExtensionSpec; +using ListType = cel::ListTypeSpec; +using MapType = cel::MapTypeSpec; +using FunctionType = cel::FunctionTypeSpec; +using AbstractType = cel::AbstractType; +using PrimitiveType = cel::PrimitiveType; +using PrimitiveTypeWrapper = cel::PrimitiveTypeWrapper; +using WellKnownType = cel::WellKnownTypeSpec; +using MessageType = cel::MessageTypeSpec; +using ParamType = cel::ParamTypeSpec; +using SourceInfo = cel::SourceInfo; +using ErrorType = cel::ErrorTypeSpec; +using DynamicType = cel::DynTypeSpec; +using NullType = cel::NullTypeSpec; +using Reference = cel::Reference; +using UnspecifiedType = cel::UnsetTypeSpec; } // namespace cel::ast_internal diff --git a/common/ast/expr_test.cc b/common/ast/expr_test.cc deleted file mode 100644 index fb62cbda7..000000000 --- a/common/ast/expr_test.cc +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "common/ast/expr.h" - -#include -#include -#include - -#include "absl/types/variant.h" -#include "common/expr.h" -#include "internal/testing.h" - -namespace cel { -namespace ast_internal { -namespace { - - -TEST(AstTest, ListTypeMutableConstruction) { - ListType type; - type.mutable_elem_type() = Type(PrimitiveType::kBool); - EXPECT_EQ(absl::get(type.elem_type().type_kind()), - PrimitiveType::kBool); -} - -TEST(AstTest, MapTypeMutableConstruction) { - MapType type; - type.mutable_key_type() = Type(PrimitiveType::kBool); - type.mutable_value_type() = Type(PrimitiveType::kBool); - EXPECT_EQ(absl::get(type.key_type().type_kind()), - PrimitiveType::kBool); - EXPECT_EQ(absl::get(type.value_type().type_kind()), - PrimitiveType::kBool); -} - -TEST(AstTest, MapTypeComparatorKeyType) { - MapType type; - type.mutable_key_type() = Type(PrimitiveType::kBool); - EXPECT_FALSE(type == MapType()); -} - -TEST(AstTest, MapTypeComparatorValueType) { - MapType type; - type.mutable_value_type() = Type(PrimitiveType::kBool); - EXPECT_FALSE(type == MapType()); -} - -TEST(AstTest, FunctionTypeMutableConstruction) { - FunctionType type; - type.mutable_result_type() = Type(PrimitiveType::kBool); - EXPECT_EQ(absl::get(type.result_type().type_kind()), - PrimitiveType::kBool); -} - -TEST(AstTest, FunctionTypeComparatorArgTypes) { - FunctionType type; - type.mutable_arg_types().emplace_back(Type()); - EXPECT_FALSE(type == FunctionType()); -} - -TEST(AstTest, ListTypeDefaults) { EXPECT_EQ(ListType().elem_type(), Type()); } - -TEST(AstTest, MapTypeDefaults) { - EXPECT_EQ(MapType().key_type(), Type()); - EXPECT_EQ(MapType().value_type(), Type()); -} - -TEST(AstTest, FunctionTypeDefaults) { - EXPECT_EQ(FunctionType().result_type(), Type()); -} - -TEST(AstTest, TypeDefaults) { - EXPECT_EQ(Type().null(), NullType()); - EXPECT_EQ(Type().primitive(), PrimitiveType::kPrimitiveTypeUnspecified); - EXPECT_EQ(Type().wrapper(), PrimitiveType::kPrimitiveTypeUnspecified); - EXPECT_EQ(Type().well_known(), WellKnownType::kWellKnownTypeUnspecified); - EXPECT_EQ(Type().list_type(), ListType()); - EXPECT_EQ(Type().map_type(), MapType()); - EXPECT_EQ(Type().function(), FunctionType()); - EXPECT_EQ(Type().message_type(), MessageType()); - EXPECT_EQ(Type().type_param(), ParamType()); - EXPECT_EQ(Type().type(), Type()); - EXPECT_EQ(Type().error_type(), ErrorType()); - EXPECT_EQ(Type().abstract_type(), AbstractType()); -} - -TEST(AstTest, TypeComparatorTest) { - Type type; - type.set_type_kind(std::make_unique(PrimitiveType::kBool)); - - EXPECT_TRUE(type == Type(std::make_unique(PrimitiveType::kBool))); - EXPECT_FALSE(type == Type(PrimitiveType::kBool)); - EXPECT_FALSE(type == Type(std::unique_ptr())); - EXPECT_FALSE(type == Type(std::make_unique(PrimitiveType::kInt64))); -} - -TEST(AstTest, ExprMutableConstruction) { - Expr expr; - expr.mutable_const_expr().set_bool_value(true); - ASSERT_TRUE(expr.has_const_expr()); - EXPECT_TRUE(expr.const_expr().bool_value()); - expr.mutable_ident_expr().set_name("expr"); - ASSERT_TRUE(expr.has_ident_expr()); - EXPECT_FALSE(expr.has_const_expr()); - EXPECT_EQ(expr.ident_expr().name(), "expr"); - expr.mutable_select_expr().set_field("field"); - ASSERT_TRUE(expr.has_select_expr()); - EXPECT_FALSE(expr.has_ident_expr()); - EXPECT_EQ(expr.select_expr().field(), "field"); - expr.mutable_call_expr().set_function("function"); - ASSERT_TRUE(expr.has_call_expr()); - EXPECT_FALSE(expr.has_select_expr()); - EXPECT_EQ(expr.call_expr().function(), "function"); - expr.mutable_list_expr(); - EXPECT_TRUE(expr.has_list_expr()); - EXPECT_FALSE(expr.has_call_expr()); - expr.mutable_struct_expr().set_name("name"); - ASSERT_TRUE(expr.has_struct_expr()); - EXPECT_EQ(expr.struct_expr().name(), "name"); - EXPECT_FALSE(expr.has_list_expr()); - expr.mutable_comprehension_expr().set_accu_var("accu_var"); - ASSERT_TRUE(expr.has_comprehension_expr()); - EXPECT_FALSE(expr.has_list_expr()); - EXPECT_EQ(expr.comprehension_expr().accu_var(), "accu_var"); -} - -TEST(AstTest, ReferenceConstantDefaultValue) { - Reference reference; - EXPECT_EQ(reference.value(), Constant()); -} - -TEST(AstTest, TypeCopyable) { - Type type = Type(PrimitiveType::kBool); - Type type2 = type; - EXPECT_TRUE(type2.has_primitive()); - EXPECT_EQ(type2, type); - - type = Type(ListType(std::make_unique(PrimitiveType::kBool))); - type2 = type; - EXPECT_TRUE(type2.has_list_type()); - EXPECT_EQ(type2, type); - - type = Type(MapType(std::make_unique(PrimitiveType::kBool), - std::make_unique(PrimitiveType::kBool))); - type2 = type; - EXPECT_TRUE(type2.has_map_type()); - EXPECT_EQ(type2, type); - - type = Type(FunctionType(std::make_unique(PrimitiveType::kBool), {})); - type2 = type; - EXPECT_TRUE(type2.has_function()); - EXPECT_EQ(type2, type); - - type = Type(AbstractType("optional", {Type(PrimitiveType::kBool)})); - type2 = type; - EXPECT_TRUE(type2.has_abstract_type()); - EXPECT_EQ(type2, type); -} - -TEST(AstTest, TypeMoveable) { - Type type = Type(PrimitiveType::kBool); - Type type2 = type; - Type type3 = std::move(type); - EXPECT_TRUE(type2.has_primitive()); - EXPECT_EQ(type2, type3); - - type = Type(ListType(std::make_unique(PrimitiveType::kBool))); - type2 = type; - type3 = std::move(type); - EXPECT_TRUE(type2.has_list_type()); - EXPECT_EQ(type2, type3); - - type = Type(MapType(std::make_unique(PrimitiveType::kBool), - std::make_unique(PrimitiveType::kBool))); - type2 = type; - type3 = std::move(type); - EXPECT_TRUE(type2.has_map_type()); - EXPECT_EQ(type2, type3); - - type = Type(FunctionType(std::make_unique(PrimitiveType::kBool), {})); - type2 = type; - type3 = std::move(type); - EXPECT_TRUE(type2.has_function()); - EXPECT_EQ(type2, type3); - - type = Type(AbstractType("optional", {Type(PrimitiveType::kBool)})); - type2 = type; - type3 = std::move(type); - EXPECT_TRUE(type2.has_abstract_type()); - EXPECT_EQ(type2, type3); -} - -TEST(AstTest, NestedTypeKindCopyAssignable) { - ListType list_type(std::make_unique(PrimitiveType::kBool)); - ListType list_type2; - list_type2 = list_type; - - EXPECT_EQ(list_type2, list_type); - - MapType map_type(std::make_unique(PrimitiveType::kBool), - std::make_unique(PrimitiveType::kBool)); - MapType map_type2; - map_type2 = map_type; - - AbstractType abstract_type( - "abstract", {Type(PrimitiveType::kBool), Type(PrimitiveType::kBool)}); - AbstractType abstract_type2; - abstract_type2 = abstract_type; - - EXPECT_EQ(abstract_type2, abstract_type); - - FunctionType function_type( - std::make_unique(PrimitiveType::kBool), - {Type(PrimitiveType::kBool), Type(PrimitiveType::kBool)}); - FunctionType function_type2; - function_type2 = function_type; - - EXPECT_EQ(function_type2, function_type); -} - -TEST(AstTest, ExtensionSupported) { - SourceInfo source_info; - - source_info.mutable_extensions().push_back( - Extension("constant_folding", nullptr, {})); - - EXPECT_EQ(source_info.extensions()[0], - Extension("constant_folding", nullptr, {})); -} - -TEST(AstTest, ExtensionEquality) { - Extension extension1("constant_folding", nullptr, {}); - - EXPECT_EQ(extension1, Extension("constant_folding", nullptr, {})); - - EXPECT_NE(extension1, - Extension("constant_folding", - std::make_unique(1, 0), {})); - EXPECT_NE(extension1, Extension("constant_folding", nullptr, - {Extension::Component::kRuntime})); - - EXPECT_EQ(extension1, - Extension("constant_folding", - std::make_unique(0, 0), {})); -} - -} // namespace -} // namespace ast_internal -} // namespace cel diff --git a/common/ast/metadata.cc b/common/ast/metadata.cc new file mode 100644 index 000000000..f744deb00 --- /dev/null +++ b/common/ast/metadata.cc @@ -0,0 +1,139 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/ast/metadata.h" + +#include +#include + +#include "absl/base/no_destructor.h" +#include "absl/functional/overload.h" +#include "absl/types/variant.h" + +namespace cel { + +namespace { + +const TypeSpec& DefaultTypeSpec() { + static absl::NoDestructor type(TypeSpecKind{UnsetTypeSpec()}); + return *type; +} + +TypeSpecKind CopyImpl(const TypeSpecKind& other) { + return absl::visit( + absl::Overload( + [](const std::unique_ptr& other) -> TypeSpecKind { + if (other == nullptr) { + return std::make_unique(); + } + return std::make_unique(*other); + }, + [](const auto& other) -> TypeSpecKind { + // Other variants define copy ctor. + return other; + }), + other); +} + +} // namespace + +const ExtensionSpec::Version& ExtensionSpec::Version::DefaultInstance() { + static absl::NoDestructor instance; + return *instance; +} + +const ExtensionSpec& ExtensionSpec::DefaultInstance() { + static absl::NoDestructor instance; + return *instance; +} + +ExtensionSpec::ExtensionSpec(const ExtensionSpec& other) + : id_(other.id_), + affected_components_(other.affected_components_), + version_(std::make_unique(*other.version_)) {} + +ExtensionSpec& ExtensionSpec::operator=(const ExtensionSpec& other) { + id_ = other.id_; + affected_components_ = other.affected_components_; + version_ = std::make_unique(*other.version_); + return *this; +} + +const TypeSpec& ListTypeSpec::elem_type() const { + if (elem_type_ != nullptr) { + return *elem_type_; + } + return DefaultTypeSpec(); +} + +bool ListTypeSpec::operator==(const ListTypeSpec& other) const { + return elem_type() == other.elem_type(); +} + +const TypeSpec& MapTypeSpec::key_type() const { + if (key_type_ != nullptr) { + return *key_type_; + } + return DefaultTypeSpec(); +} + +const TypeSpec& MapTypeSpec::value_type() const { + if (value_type_ != nullptr) { + return *value_type_; + } + return DefaultTypeSpec(); +} + +bool MapTypeSpec::operator==(const MapTypeSpec& other) const { + return key_type() == other.key_type() && value_type() == other.value_type(); +} + +const TypeSpec& FunctionTypeSpec::result_type() const { + if (result_type_ != nullptr) { + return *result_type_; + } + return DefaultTypeSpec(); +} + +bool FunctionTypeSpec::operator==(const FunctionTypeSpec& other) const { + return result_type() == other.result_type() && arg_types_ == other.arg_types_; +} + +const TypeSpec& TypeSpec::type() const { + auto* value = absl::get_if>(&type_kind_); + if (value != nullptr) { + if (*value != nullptr) return **value; + } + return DefaultTypeSpec(); +} + +TypeSpec::TypeSpec(const TypeSpec& other) + : type_kind_(CopyImpl(other.type_kind_)) {} + +TypeSpec& TypeSpec::operator=(const TypeSpec& other) { + type_kind_ = CopyImpl(other.type_kind_); + return *this; +} + +FunctionTypeSpec::FunctionTypeSpec(const FunctionTypeSpec& other) + : result_type_(std::make_unique(other.result_type())), + arg_types_(other.arg_types()) {} + +FunctionTypeSpec& FunctionTypeSpec::operator=(const FunctionTypeSpec& other) { + result_type_ = std::make_unique(other.result_type()); + arg_types_ = other.arg_types(); + return *this; +} + +} // namespace cel diff --git a/common/ast/metadata.h b/common/ast/metadata.h new file mode 100644 index 000000000..7e6d4d182 --- /dev/null +++ b/common/ast/metadata.h @@ -0,0 +1,868 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Type definitions for auxiliary structures in the AST. +// +// These are more direct equivalents to the public protobuf definitions. +#ifndef THIRD_PARTY_CEL_CPP_COMMON_AST_METADATA_H_ +#define THIRD_PARTY_CEL_CPP_COMMON_AST_METADATA_H_ + +#include +#include +#include +#include +#include + +#include "absl/base/nullability.h" +#include "absl/container/flat_hash_map.h" +#include "absl/types/optional.h" +#include "absl/types/variant.h" +#include "common/constant.h" +#include "common/expr.h" + +namespace cel { + +// An extension that was requested for the source expression. +class ExtensionSpec { + public: + // Version + class Version { + public: + Version() : major_(0), minor_(0) {} + Version(int64_t major, int64_t minor) : major_(major), minor_(minor) {} + + Version(const Version& other) = default; + Version(Version&& other) = default; + Version& operator=(const Version& other) = default; + Version& operator=(Version&& other) = default; + + static const Version& DefaultInstance(); + + // Major version changes indicate different required support level from + // the required components. + int64_t major() const { return major_; } + void set_major(int64_t val) { major_ = val; } + + // Minor version changes must not change the observed behavior from + // existing implementations, but may be provided informationally. + int64_t minor() const { return minor_; } + void set_minor(int64_t val) { minor_ = val; } + + bool operator==(const Version& other) const { + return major_ == other.major_ && minor_ == other.minor_; + } + + bool operator!=(const Version& other) const { return !operator==(other); } + + private: + int64_t major_; + int64_t minor_; + }; + + // CEL component specifier. + enum class Component { + // Unspecified, default. + kUnspecified, + // Parser. Converts a CEL string to an AST. + kParser, + // Type checker. Checks that references in an AST are defined and types + // agree. + kTypeChecker, + // Runtime. Evaluates a parsed and optionally checked CEL AST against a + // context. + kRuntime + }; + + static const ExtensionSpec& DefaultInstance(); + + ExtensionSpec() = default; + ExtensionSpec(std::string id, std::unique_ptr version, + std::vector affected_components) + : id_(std::move(id)), + affected_components_(std::move(affected_components)), + version_(std::move(version)) {} + + ExtensionSpec(const ExtensionSpec& other); + ExtensionSpec(ExtensionSpec&& other) = default; + ExtensionSpec& operator=(const ExtensionSpec& other); + ExtensionSpec& operator=(ExtensionSpec&& other) = default; + + // Identifier for the extension. Example: constant_folding + const std::string& id() const { return id_; } + void set_id(std::string id) { id_ = std::move(id); } + + // If set, the listed components must understand the extension for the + // expression to evaluate correctly. + // + // This field has set semantics, repeated values should be deduplicated. + const std::vector& affected_components() const { + return affected_components_; + } + + std::vector& mutable_affected_components() { + return affected_components_; + } + + // Version info. May be skipped if it isn't meaningful for the extension. + // (for example constant_folding might always be v0.0). + const Version& version() const { + if (version_ == nullptr) { + return Version::DefaultInstance(); + } + return *version_; + } + + Version& mutable_version() { + if (version_ == nullptr) { + version_ = std::make_unique(); + } + return *version_; + } + + void set_version(std::unique_ptr version) { + version_ = std::move(version); + } + + bool operator==(const ExtensionSpec& other) const { + return id_ == other.id_ && + affected_components_ == other.affected_components_ && + version() == other.version(); + } + + bool operator!=(const ExtensionSpec& other) const { + return !operator==(other); + } + + private: + std::string id_; + std::vector affected_components_; + std::unique_ptr version_; +}; + +// Source information collected at parse time. +class SourceInfo { + public: + SourceInfo() = default; + SourceInfo(std::string syntax_version, std::string location, + std::vector line_offsets, + absl::flat_hash_map positions, + absl::flat_hash_map macro_calls, + std::vector extensions) + : syntax_version_(std::move(syntax_version)), + location_(std::move(location)), + line_offsets_(std::move(line_offsets)), + positions_(std::move(positions)), + macro_calls_(std::move(macro_calls)), + extensions_(std::move(extensions)) {} + + void set_syntax_version(std::string syntax_version) { + syntax_version_ = std::move(syntax_version); + } + + void set_location(std::string location) { location_ = std::move(location); } + + void set_line_offsets(std::vector line_offsets) { + line_offsets_ = std::move(line_offsets); + } + + void set_positions(absl::flat_hash_map positions) { + positions_ = std::move(positions); + } + + void set_macro_calls(absl::flat_hash_map macro_calls) { + macro_calls_ = std::move(macro_calls); + } + + const std::string& syntax_version() const { return syntax_version_; } + + const std::string& location() const { return location_; } + + const std::vector& line_offsets() const { return line_offsets_; } + + std::vector& mutable_line_offsets() { return line_offsets_; } + + const absl::flat_hash_map& positions() const { + return positions_; + } + + absl::flat_hash_map& mutable_positions() { + return positions_; + } + + const absl::flat_hash_map& macro_calls() const { + return macro_calls_; + } + + absl::flat_hash_map& mutable_macro_calls() { + return macro_calls_; + } + + bool operator==(const SourceInfo& other) const { + return syntax_version_ == other.syntax_version_ && + location_ == other.location_ && + line_offsets_ == other.line_offsets_ && + positions_ == other.positions_ && + macro_calls_ == other.macro_calls_ && + extensions_ == other.extensions_; + } + + bool operator!=(const SourceInfo& other) const { return !operator==(other); } + + const std::vector& extensions() const { return extensions_; } + + std::vector& mutable_extensions() { return extensions_; } + + private: + // The syntax version of the source, e.g. `cel1`. + std::string syntax_version_; + + // The location name. All position information attached to an expression is + // relative to this location. + // + // The location could be a file, UI element, or similar. For example, + // `acme/app/AnvilPolicy.cel`. + std::string location_; + + // Monotonically increasing list of code point offsets where newlines + // `\n` appear. + // + // The line number of a given position is the index `i` where for a given + // `id` the `line_offsets[i] < id_positions[id] < line_offsets[i+1]`. The + // column may be derivd from `id_positions[id] - line_offsets[i]`. + // + // TODO(uncreated-issue/14): clarify this documentation + std::vector line_offsets_; + + // A map from the parse node id (e.g. `Expr.id`) to the code point offset + // within source. + absl::flat_hash_map positions_; + + // A map from the parse node id where a macro replacement was made to the + // call `Expr` that resulted in a macro expansion. + // + // For example, `has(value.field)` is a function call that is replaced by a + // `test_only` field selection in the AST. Likewise, the call + // `list.exists(e, e > 10)` translates to a comprehension expression. The key + // in the map corresponds to the expression id of the expanded macro, and the + // value is the call `Expr` that was replaced. + absl::flat_hash_map macro_calls_; + + // A list of tags for extensions that were used while parsing or type checking + // the source expression. For example, optimizations that require special + // runtime support may be specified. + // + // These are used to check feature support between components in separate + // implementations. This can be used to either skip redundant work or + // report an error if the extension is unsupported. + std::vector extensions_; +}; + +// CEL primitive types. +enum class PrimitiveType { + // Unspecified type. + kPrimitiveTypeUnspecified = 0, + // Boolean type. + kBool = 1, + // Int64 type. + // + // Proto-based integer values are widened to int64. + kInt64 = 2, + // Uint64 type. + // + // Proto-based unsigned integer values are widened to uint64. + kUint64 = 3, + // Double type. + // + // Proto-based float values are widened to double values. + kDouble = 4, + // String type. + kString = 5, + // Bytes type. + kBytes = 6, +}; + +// Well-known protobuf types treated with first-class support in CEL. +// +// TODO(uncreated-issue/15): represent well-known via abstract types (or however) +// they will be named. +enum class WellKnownTypeSpec { + // Unspecified type. + kWellKnownTypeUnspecified = 0, + // Well-known protobuf.Any type. + // + // Any types are a polymorphic message type. During type-checking they are + // treated like `DYN` types, but at runtime they are resolved to a specific + // message type specified at evaluation time. + kAny = 1, + // Well-known protobuf.Timestamp type, internally referenced as `timestamp`. + kTimestamp = 2, + // Well-known protobuf.Duration type, internally referenced as `duration`. + kDuration = 3, +}; + +// forward declare for recursive types. +class TypeSpec; + +// List type with typed elements, e.g. `list`. +class ListTypeSpec { + public: + ListTypeSpec() = default; + + ListTypeSpec(const ListTypeSpec& rhs) + : elem_type_(std::make_unique(rhs.elem_type())) {} + ListTypeSpec& operator=(const ListTypeSpec& rhs) { + elem_type_ = std::make_unique(rhs.elem_type()); + return *this; + } + ListTypeSpec(ListTypeSpec&& rhs) = default; + ListTypeSpec& operator=(ListTypeSpec&& rhs) = default; + + explicit ListTypeSpec(std::unique_ptr elem_type) + : elem_type_(std::move(elem_type)) {} + + void set_elem_type(std::unique_ptr elem_type) { + elem_type_ = std::move(elem_type); + } + + bool has_elem_type() const { return elem_type_ != nullptr; } + + const TypeSpec& elem_type() const; + + TypeSpec& mutable_elem_type() { + if (elem_type_ == nullptr) { + elem_type_ = std::make_unique(); + } + return *elem_type_; + } + + bool operator==(const ListTypeSpec& other) const; + + private: + std::unique_ptr elem_type_; +}; + +// Map type specifier with parameterized key and value types, e.g. +// `map`. +class MapTypeSpec { + public: + MapTypeSpec() = default; + MapTypeSpec(std::unique_ptr key_type, + std::unique_ptr value_type) + : key_type_(std::move(key_type)), value_type_(std::move(value_type)) {} + + MapTypeSpec(const MapTypeSpec& rhs) + : key_type_(std::make_unique(rhs.key_type())), + value_type_(std::make_unique(rhs.value_type())) {} + MapTypeSpec& operator=(const MapTypeSpec& rhs) { + key_type_ = std::make_unique(rhs.key_type()); + value_type_ = std::make_unique(rhs.value_type()); + + return *this; + } + MapTypeSpec(MapTypeSpec&& rhs) = default; + MapTypeSpec& operator=(MapTypeSpec&& rhs) = default; + + void set_key_type(std::unique_ptr key_type) { + key_type_ = std::move(key_type); + } + + void set_value_type(std::unique_ptr value_type) { + value_type_ = std::move(value_type); + } + + bool has_key_type() const { return key_type_ != nullptr; } + + bool has_value_type() const { return value_type_ != nullptr; } + + const TypeSpec& key_type() const; + + const TypeSpec& value_type() const; + + bool operator==(const MapTypeSpec& other) const; + + TypeSpec& mutable_key_type() { + if (key_type_ == nullptr) { + key_type_ = std::make_unique(); + } + return *key_type_; + } + + TypeSpec& mutable_value_type() { + if (value_type_ == nullptr) { + value_type_ = std::make_unique(); + } + return *value_type_; + } + + private: + // The type of the key. + std::unique_ptr key_type_; + + // The type of the value. + std::unique_ptr value_type_; +}; + +// Function type specifiers with result and arg types. +// +// NOTE: function type represents a lambda-style argument to another function. +// Supported through macros, but not yet a first-class concept in CEL. +class FunctionTypeSpec { + public: + FunctionTypeSpec() = default; + FunctionTypeSpec(std::unique_ptr result_type, + std::vector arg_types); + + FunctionTypeSpec(const FunctionTypeSpec& other); + FunctionTypeSpec& operator=(const FunctionTypeSpec& other); + FunctionTypeSpec(FunctionTypeSpec&&) = default; + FunctionTypeSpec& operator=(FunctionTypeSpec&&) = default; + + void set_result_type(std::unique_ptr result_type) { + result_type_ = std::move(result_type); + } + + void set_arg_types(std::vector arg_types); + + bool has_result_type() const { return result_type_ != nullptr; } + + const TypeSpec& result_type() const; + + TypeSpec& mutable_result_type() { + if (result_type_ == nullptr) { + result_type_ = std::make_unique(); + } + return *result_type_; + } + + const std::vector& arg_types() const { return arg_types_; } + + std::vector& mutable_arg_types() { return arg_types_; } + + bool operator==(const FunctionTypeSpec& other) const; + + private: + // Result type of the function. + std::unique_ptr result_type_; + + // Argument types of the function. + std::vector arg_types_; +}; + +// Application defined abstract type. +// +// Abstract types provide a name as an identifier for the application, and +// optionally one or more type parameters. +// +// For cel::Type representation, see OpaqueType. +class AbstractType { + public: + AbstractType() = default; + AbstractType(std::string name, std::vector parameter_types); + + void set_name(std::string name) { name_ = std::move(name); } + + void set_parameter_types(std::vector parameter_types); + + const std::string& name() const { return name_; } + + const std::vector& parameter_types() const { + return parameter_types_; + } + + std::vector& mutable_parameter_types() { return parameter_types_; } + + bool operator==(const AbstractType& other) const; + + private: + // The fully qualified name of this abstract type. + std::string name_; + + // Parameter types for this abstract type. + std::vector parameter_types_; +}; + +// Wrapper of a primitive type, e.g. `google.protobuf.Int64Value`. +class PrimitiveTypeWrapper { + public: + explicit PrimitiveTypeWrapper(PrimitiveType type) : type_(std::move(type)) {} + + void set_type(PrimitiveType type) { type_ = std::move(type); } + + const PrimitiveType& type() const { return type_; } + + PrimitiveType& mutable_type() { return type_; } + + bool operator==(const PrimitiveTypeWrapper& other) const { + return type_ == other.type_; + } + + private: + PrimitiveType type_; +}; + +// Protocol buffer message type specifier. +// +// The `message_type` string specifies the qualified message type name. For +// example, `google.plus.Profile`. This must be mapped to a google::protobuf::Descriptor +// for type checking. +class MessageTypeSpec { + public: + MessageTypeSpec() = default; + explicit MessageTypeSpec(std::string type) : type_(std::move(type)) {} + + void set_type(std::string type) { type_ = std::move(type); } + + const std::string& type() const { return type_; } + + bool operator==(const MessageTypeSpec& other) const { + return type_ == other.type_; + } + + private: + std::string type_; +}; + +// TypeSpec param type. +// +// The `type_param` string specifies the type parameter name, e.g. `list` +// would be a `list_type` whose element type was a `type_param` type +// named `E`. +class ParamTypeSpec { + public: + ParamTypeSpec() = default; + explicit ParamTypeSpec(std::string type) : type_(std::move(type)) {} + + void set_type(std::string type) { type_ = std::move(type); } + + const std::string& type() const { return type_; } + + bool operator==(const ParamTypeSpec& other) const { + return type_ == other.type_; + } + + private: + std::string type_; +}; + +// Error type specifier. +// +// During type-checking if an expression is an error, its type is propagated +// as the `ERROR` type. This permits the type-checker to discover other +// errors present in the expression. +enum class ErrorTypeSpec { kValue = 0 }; + +using UnsetTypeSpec = absl::monostate; + +struct DynTypeSpec {}; + +inline bool operator==(const DynTypeSpec&, const DynTypeSpec&) { return true; } +inline bool operator!=(const DynTypeSpec&, const DynTypeSpec&) { return false; } + +struct NullTypeSpec {}; +inline bool operator==(const NullTypeSpec&, const NullTypeSpec&) { + return true; +} +inline bool operator!=(const NullTypeSpec&, const NullTypeSpec&) { + return false; +} + +using TypeSpecKind = + absl::variant, ErrorTypeSpec, + AbstractType>; + +// Analogous to cel::expr::Type. +// Represents a CEL type. +// +// TODO(uncreated-issue/15): align with value.proto +class TypeSpec { + public: + TypeSpec() = default; + explicit TypeSpec(TypeSpecKind type_kind) + : type_kind_(std::move(type_kind)) {} + + TypeSpec(const TypeSpec& other); + TypeSpec& operator=(const TypeSpec& other); + TypeSpec(TypeSpec&&) = default; + TypeSpec& operator=(TypeSpec&&) = default; + + void set_type_kind(TypeSpecKind type_kind) { + type_kind_ = std::move(type_kind); + } + + const TypeSpecKind& type_kind() const { return type_kind_; } + + TypeSpecKind& mutable_type_kind() { return type_kind_; } + + bool has_dyn() const { + return absl::holds_alternative(type_kind_); + } + + bool has_null() const { + return absl::holds_alternative(type_kind_); + } + + bool has_primitive() const { + return absl::holds_alternative(type_kind_); + } + + bool has_wrapper() const { + return absl::holds_alternative(type_kind_); + } + + bool has_well_known() const { + return absl::holds_alternative(type_kind_); + } + + bool has_list_type() const { + return absl::holds_alternative(type_kind_); + } + + bool has_map_type() const { + return absl::holds_alternative(type_kind_); + } + + bool has_function() const { + return absl::holds_alternative(type_kind_); + } + + bool has_message_type() const { + return absl::holds_alternative(type_kind_); + } + + bool has_type_param() const { + return absl::holds_alternative(type_kind_); + } + + bool has_type() const { + return absl::holds_alternative>(type_kind_); + } + + bool has_error() const { + return absl::holds_alternative(type_kind_); + } + + bool has_abstract_type() const { + return absl::holds_alternative(type_kind_); + } + + NullTypeSpec null() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + return {}; + } + + PrimitiveType primitive() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + return PrimitiveType::kPrimitiveTypeUnspecified; + } + + PrimitiveType wrapper() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return value->type(); + } + return PrimitiveType::kPrimitiveTypeUnspecified; + } + + WellKnownTypeSpec well_known() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + return WellKnownTypeSpec::kWellKnownTypeUnspecified; + } + + const ListTypeSpec& list_type() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const ListTypeSpec* default_list_type = new ListTypeSpec(); + return *default_list_type; + } + + const MapTypeSpec& map_type() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const MapTypeSpec* default_map_type = new MapTypeSpec(); + return *default_map_type; + } + + const FunctionTypeSpec& function() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const FunctionTypeSpec* default_function_type = + new FunctionTypeSpec(); + return *default_function_type; + } + + const MessageTypeSpec& message_type() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const MessageTypeSpec* default_message_type = new MessageTypeSpec(); + return *default_message_type; + } + + const ParamTypeSpec& type_param() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const ParamTypeSpec* default_param_type = new ParamTypeSpec(); + return *default_param_type; + } + + const TypeSpec& type() const; + + ErrorTypeSpec error_type() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + return ErrorTypeSpec::kValue; + } + + const AbstractType& abstract_type() const { + auto* value = absl::get_if(&type_kind_); + if (value != nullptr) { + return *value; + } + static const AbstractType* default_abstract_type = new AbstractType(); + return *default_abstract_type; + } + + bool operator==(const TypeSpec& other) const { + if (absl::holds_alternative>(type_kind_) && + absl::holds_alternative>(other.type_kind_)) { + const auto& self_type = absl::get>(type_kind_); + const auto& other_type = + absl::get>(other.type_kind_); + if (self_type == nullptr || other_type == nullptr) { + return self_type == other_type; + } + return *self_type == *other_type; + } + return type_kind_ == other.type_kind_; + } + + private: + TypeSpecKind type_kind_; +}; + +// Describes a resolved reference to a declaration. +class Reference { + public: + Reference() = default; + + Reference(std::string name, std::vector overload_id, + Constant value) + : name_(std::move(name)), + overload_id_(std::move(overload_id)), + value_(std::move(value)) {} + + void set_name(std::string name) { name_ = std::move(name); } + + void set_overload_id(std::vector overload_id) { + overload_id_ = std::move(overload_id); + } + + void set_value(Constant value) { value_ = std::move(value); } + + const std::string& name() const { return name_; } + + const std::vector& overload_id() const { return overload_id_; } + + const Constant& value() const { + if (value_.has_value()) { + return value_.value(); + } + static const Constant* default_constant = new Constant; + return *default_constant; + } + + std::vector& mutable_overload_id() { return overload_id_; } + + Constant& mutable_value() { + if (!value_.has_value()) { + value_.emplace(); + } + return *value_; + } + + bool has_value() const { return value_.has_value(); } + + bool operator==(const Reference& other) const { + return name_ == other.name_ && overload_id_ == other.overload_id_ && + value() == other.value(); + } + + private: + // The fully qualified name of the declaration. + std::string name_; + // For references to functions, this is a list of `Overload.overload_id` + // values which match according to typing rules. + // + // If the list has more than one element, overload resolution among the + // presented candidates must happen at runtime because of dynamic types. The + // type checker attempts to narrow down this list as much as possible. + // + // Empty if this is not a reference to a [Decl.FunctionDecl][]. + std::vector overload_id_; + // For references to constants, this may contain the value of the + // constant if known at compile time. + absl::optional value_; +}; + +//////////////////////////////////////////////////////////////////////// +// Implementation details +//////////////////////////////////////////////////////////////////////// + +inline FunctionTypeSpec::FunctionTypeSpec(std::unique_ptr result_type, + std::vector arg_types) + : result_type_(std::move(result_type)), arg_types_(std::move(arg_types)) {} + +inline void FunctionTypeSpec::set_arg_types(std::vector arg_types) { + arg_types_ = std::move(arg_types); +} + +inline AbstractType::AbstractType(std::string name, + std::vector parameter_types) + : name_(std::move(name)), parameter_types_(std::move(parameter_types)) {} + +inline void AbstractType::set_parameter_types( + std::vector parameter_types) { + parameter_types_ = std::move(parameter_types); +} + +inline bool AbstractType::operator==(const AbstractType& other) const { + return name_ == other.name_ && parameter_types_ == other.parameter_types_; +} + +} // namespace cel + +#endif // THIRD_PARTY_CEL_CPP_COMMON_AST_METADATA_H_ diff --git a/common/ast/metadata_test.cc b/common/ast/metadata_test.cc new file mode 100644 index 000000000..4afb0d07d --- /dev/null +++ b/common/ast/metadata_test.cc @@ -0,0 +1,268 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/ast/metadata.h" + +#include +#include +#include + +#include "absl/types/variant.h" +#include "common/expr.h" +#include "internal/testing.h" + +namespace cel { +namespace { + +TEST(AstTest, ListTypeSpecMutableConstruction) { + ListTypeSpec type; + type.mutable_elem_type() = TypeSpec(PrimitiveType::kBool); + EXPECT_EQ(absl::get(type.elem_type().type_kind()), + PrimitiveType::kBool); +} + +TEST(AstTest, MapTypeSpecMutableConstruction) { + MapTypeSpec type; + type.mutable_key_type() = TypeSpec(PrimitiveType::kBool); + type.mutable_value_type() = TypeSpec(PrimitiveType::kBool); + EXPECT_EQ(absl::get(type.key_type().type_kind()), + PrimitiveType::kBool); + EXPECT_EQ(absl::get(type.value_type().type_kind()), + PrimitiveType::kBool); +} + +TEST(AstTest, MapTypeSpecComparatorKeyType) { + MapTypeSpec type; + type.mutable_key_type() = TypeSpec(PrimitiveType::kBool); + EXPECT_FALSE(type == MapTypeSpec()); +} + +TEST(AstTest, MapTypeSpecComparatorValueType) { + MapTypeSpec type; + type.mutable_value_type() = TypeSpec(PrimitiveType::kBool); + EXPECT_FALSE(type == MapTypeSpec()); +} + +TEST(AstTest, FunctionTypeSpecMutableConstruction) { + FunctionTypeSpec type; + type.mutable_result_type() = TypeSpec(PrimitiveType::kBool); + EXPECT_EQ(absl::get(type.result_type().type_kind()), + PrimitiveType::kBool); +} + +TEST(AstTest, FunctionTypeSpecComparatorArgTypes) { + FunctionTypeSpec type; + type.mutable_arg_types().emplace_back(TypeSpec()); + EXPECT_FALSE(type == FunctionTypeSpec()); +} + +TEST(AstTest, ListTypeSpecDefaults) { + EXPECT_EQ(ListTypeSpec().elem_type(), TypeSpec()); +} + +TEST(AstTest, MapTypeSpecDefaults) { + EXPECT_EQ(MapTypeSpec().key_type(), TypeSpec()); + EXPECT_EQ(MapTypeSpec().value_type(), TypeSpec()); +} + +TEST(AstTest, FunctionTypeSpecDefaults) { + EXPECT_EQ(FunctionTypeSpec().result_type(), TypeSpec()); +} + +TEST(AstTest, TypeDefaults) { + EXPECT_EQ(TypeSpec().null(), NullTypeSpec()); + EXPECT_EQ(TypeSpec().primitive(), PrimitiveType::kPrimitiveTypeUnspecified); + EXPECT_EQ(TypeSpec().wrapper(), PrimitiveType::kPrimitiveTypeUnspecified); + EXPECT_EQ(TypeSpec().well_known(), + WellKnownTypeSpec::kWellKnownTypeUnspecified); + EXPECT_EQ(TypeSpec().list_type(), ListTypeSpec()); + EXPECT_EQ(TypeSpec().map_type(), MapTypeSpec()); + EXPECT_EQ(TypeSpec().function(), FunctionTypeSpec()); + EXPECT_EQ(TypeSpec().message_type(), MessageTypeSpec()); + EXPECT_EQ(TypeSpec().type_param(), ParamTypeSpec()); + EXPECT_EQ(TypeSpec().type(), TypeSpec()); + EXPECT_EQ(TypeSpec().error_type(), ErrorTypeSpec()); + EXPECT_EQ(TypeSpec().abstract_type(), AbstractType()); +} + +TEST(AstTest, TypeComparatorTest) { + TypeSpec type; + type.set_type_kind(std::make_unique(PrimitiveType::kBool)); + + EXPECT_TRUE(type == + TypeSpec(std::make_unique(PrimitiveType::kBool))); + EXPECT_FALSE(type == TypeSpec(PrimitiveType::kBool)); + EXPECT_FALSE(type == TypeSpec(std::unique_ptr())); + EXPECT_FALSE(type == + TypeSpec(std::make_unique(PrimitiveType::kInt64))); +} + +TEST(AstTest, ExprMutableConstruction) { + Expr expr; + expr.mutable_const_expr().set_bool_value(true); + ASSERT_TRUE(expr.has_const_expr()); + EXPECT_TRUE(expr.const_expr().bool_value()); + expr.mutable_ident_expr().set_name("expr"); + ASSERT_TRUE(expr.has_ident_expr()); + EXPECT_FALSE(expr.has_const_expr()); + EXPECT_EQ(expr.ident_expr().name(), "expr"); + expr.mutable_select_expr().set_field("field"); + ASSERT_TRUE(expr.has_select_expr()); + EXPECT_FALSE(expr.has_ident_expr()); + EXPECT_EQ(expr.select_expr().field(), "field"); + expr.mutable_call_expr().set_function("function"); + ASSERT_TRUE(expr.has_call_expr()); + EXPECT_FALSE(expr.has_select_expr()); + EXPECT_EQ(expr.call_expr().function(), "function"); + expr.mutable_list_expr(); + EXPECT_TRUE(expr.has_list_expr()); + EXPECT_FALSE(expr.has_call_expr()); + expr.mutable_struct_expr().set_name("name"); + ASSERT_TRUE(expr.has_struct_expr()); + EXPECT_EQ(expr.struct_expr().name(), "name"); + EXPECT_FALSE(expr.has_list_expr()); + expr.mutable_comprehension_expr().set_accu_var("accu_var"); + ASSERT_TRUE(expr.has_comprehension_expr()); + EXPECT_FALSE(expr.has_list_expr()); + EXPECT_EQ(expr.comprehension_expr().accu_var(), "accu_var"); +} + +TEST(AstTest, ReferenceConstantDefaultValue) { + Reference reference; + EXPECT_EQ(reference.value(), Constant()); +} + +TEST(AstTest, TypeCopyable) { + TypeSpec type = TypeSpec(PrimitiveType::kBool); + TypeSpec type2 = type; + EXPECT_TRUE(type2.has_primitive()); + EXPECT_EQ(type2, type); + + type = + TypeSpec(ListTypeSpec(std::make_unique(PrimitiveType::kBool))); + type2 = type; + EXPECT_TRUE(type2.has_list_type()); + EXPECT_EQ(type2, type); + + type = + TypeSpec(MapTypeSpec(std::make_unique(PrimitiveType::kBool), + std::make_unique(PrimitiveType::kBool))); + type2 = type; + EXPECT_TRUE(type2.has_map_type()); + EXPECT_EQ(type2, type); + + type = TypeSpec( + FunctionTypeSpec(std::make_unique(PrimitiveType::kBool), {})); + type2 = type; + EXPECT_TRUE(type2.has_function()); + EXPECT_EQ(type2, type); + + type = TypeSpec(AbstractType("optional", {TypeSpec(PrimitiveType::kBool)})); + type2 = type; + EXPECT_TRUE(type2.has_abstract_type()); + EXPECT_EQ(type2, type); +} + +TEST(AstTest, TypeMoveable) { + TypeSpec type = TypeSpec(PrimitiveType::kBool); + TypeSpec type2 = type; + TypeSpec type3 = std::move(type); + EXPECT_TRUE(type2.has_primitive()); + EXPECT_EQ(type2, type3); + + type = + TypeSpec(ListTypeSpec(std::make_unique(PrimitiveType::kBool))); + type2 = type; + type3 = std::move(type); + EXPECT_TRUE(type2.has_list_type()); + EXPECT_EQ(type2, type3); + + type = + TypeSpec(MapTypeSpec(std::make_unique(PrimitiveType::kBool), + std::make_unique(PrimitiveType::kBool))); + type2 = type; + type3 = std::move(type); + EXPECT_TRUE(type2.has_map_type()); + EXPECT_EQ(type2, type3); + + type = TypeSpec( + FunctionTypeSpec(std::make_unique(PrimitiveType::kBool), {})); + type2 = type; + type3 = std::move(type); + EXPECT_TRUE(type2.has_function()); + EXPECT_EQ(type2, type3); + + type = TypeSpec(AbstractType("optional", {TypeSpec(PrimitiveType::kBool)})); + type2 = type; + type3 = std::move(type); + EXPECT_TRUE(type2.has_abstract_type()); + EXPECT_EQ(type2, type3); +} + +TEST(AstTest, NestedTypeKindCopyAssignable) { + ListTypeSpec list_type(std::make_unique(PrimitiveType::kBool)); + ListTypeSpec list_type2; + list_type2 = list_type; + + EXPECT_EQ(list_type2, list_type); + + MapTypeSpec map_type(std::make_unique(PrimitiveType::kBool), + std::make_unique(PrimitiveType::kBool)); + MapTypeSpec map_type2; + map_type2 = map_type; + + AbstractType abstract_type("abstract", {TypeSpec(PrimitiveType::kBool), + TypeSpec(PrimitiveType::kBool)}); + AbstractType abstract_type2; + abstract_type2 = abstract_type; + + EXPECT_EQ(abstract_type2, abstract_type); + + FunctionTypeSpec function_type( + std::make_unique(PrimitiveType::kBool), + {TypeSpec(PrimitiveType::kBool), TypeSpec(PrimitiveType::kBool)}); + FunctionTypeSpec function_type2; + function_type2 = function_type; + + EXPECT_EQ(function_type2, function_type); +} + +TEST(AstTest, ExtensionSupported) { + SourceInfo source_info; + + source_info.mutable_extensions().push_back( + ExtensionSpec("constant_folding", nullptr, {})); + + EXPECT_EQ(source_info.extensions()[0], + ExtensionSpec("constant_folding", nullptr, {})); +} + +TEST(AstTest, ExtensionSpecEquality) { + ExtensionSpec extension1("constant_folding", nullptr, {}); + + EXPECT_EQ(extension1, ExtensionSpec("constant_folding", nullptr, {})); + + EXPECT_NE(extension1, + ExtensionSpec("constant_folding", + std::make_unique(1, 0), {})); + EXPECT_NE(extension1, ExtensionSpec("constant_folding", nullptr, + {ExtensionSpec::Component::kRuntime})); + + EXPECT_EQ(extension1, + ExtensionSpec("constant_folding", + std::make_unique(0, 0), {})); +} + +} // namespace +} // namespace cel diff --git a/common/ast_proto.cc b/common/ast_proto.cc index 5fcf21f80..047a041ae 100644 --- a/common/ast_proto.cc +++ b/common/ast_proto.cc @@ -292,7 +292,7 @@ absl::StatusOr ConvertProtoTypeToNative( return Type(std::make_unique(*std::move(native_type))); } case cel::expr::Type::kError: - return Type(ErrorType::kErrorTypeValue); + return Type(ErrorType::kValue); case cel::expr::Type::kAbstractType: { auto native_abstract = ToNative(type.abstract_type()); if (!native_abstract.ok()) {