// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_EXPLORER_AST_VALUE_H_ #define CARBON_EXPLORER_AST_VALUE_H_ #include #include #include #include #include "common/ostream.h" #include "explorer/ast/address.h" #include "explorer/ast/bindings.h" #include "explorer/ast/declaration.h" #include "explorer/ast/element.h" #include "explorer/ast/element_path.h" #include "explorer/ast/expression_category.h" #include "explorer/ast/statement.h" #include "explorer/base/nonnull.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Compiler.h" namespace Carbon { class AssociatedConstant; class ChoiceType; class TupleValue; // A trait type that describes how to allocate an instance of `T` in an arena. // Returns the created object, which is not required to be of type `T`. template struct AllocateTrait { template static auto New(Nonnull arena, Args&&... args) -> Nonnull { return arena->New(std::forward(args)...); } }; using VTable = llvm::StringMap, int>>; // Returns a pointer to an empty VTable that will never be deallocated. // // Using this instead of `new VTable()` avoids unnecessary allocations, and // takes better advantage of Arena canonicalization when a VTable pointer is // used as a constructor argument. inline auto EmptyVTable() -> Nonnull { static Nonnull result = new VTable(); return result; } // Abstract base class of all AST nodes representing values. // // Value and its derived classes support LLVM-style RTTI, including // llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every // class derived from Value must provide a `classof` operation, and // every concrete derived class must have a corresponding enumerator // in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for // details. // // Arena's canonicalization support is enabled for Value and all derived types. // As a result, all Values must be immutable, and all their constructor // arguments must be copyable, equality-comparable, and hashable. See // Arena's documentation for details. class Value : public Printable { public: using EnableCanonicalizedAllocation = void; enum class Kind { #define CARBON_VALUE_KIND(kind) kind, #include "explorer/ast/value_kinds.def" }; Value(const Value&) = delete; auto operator=(const Value&) -> Value& = delete; // Call `f` on this value, cast to its most-derived type. `R` specifies the // expected return type of `f`. template auto Visit(F f) const -> R; void Print(llvm::raw_ostream& out) const; // Returns the sub-Value specified by `path`, which must be a valid element // path for *this. If the sub-Value is a method and its self_pattern is an // AddrPattern, then pass the LocationValue representing the receiver as // `me_value`, otherwise pass `*this`. auto GetElement(Nonnull arena, const ElementPath& path, SourceLocation source_loc, std::optional> me_value) const -> ErrorOr>; // Returns a copy of *this, but with the sub-Value specified by `path` // set to `field_value`. `path` must be a valid field path for *this. auto SetField(Nonnull arena, const ElementPath& path, Nonnull field_value, SourceLocation source_loc) const -> ErrorOr>; // Returns the enumerator corresponding to the most-derived type of this // object. auto kind() const -> Kind { return kind_; } protected: // Constructs a Value. `kind` must be the enumerator corresponding to the // most-derived type being constructed. explicit Value(Kind kind) : kind_(kind) {} private: const Kind kind_; }; // Returns whether the fully-resolved kind that this value will eventually have // is currently unknown, because it depends on a generic parameter. inline auto IsValueKindDependent(Nonnull type) -> bool { return type->kind() == Value::Kind::VariableType || type->kind() == Value::Kind::AssociatedConstant; } // Base class for types holding contextual information by which we can // determine whether values are equal. class EqualityContext { public: virtual auto VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool = 0; protected: virtual ~EqualityContext() = default; }; auto TypeEqual(Nonnull t1, Nonnull t2, std::optional> equality_ctx) -> bool; auto ValueEqual(Nonnull v1, Nonnull v2, std::optional> equality_ctx) -> bool; // Call the given `visitor` on all values nested within the given value, // including `value` itself, in a preorder traversal. Aborts and returns // `false` if `visitor` returns `false`, otherwise returns `true`. auto VisitNestedValues(Nonnull value, llvm::function_ref visitor) -> bool; // An integer value. class IntValue : public Value { public: explicit IntValue(int value) : Value(Kind::IntValue), value_(value) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::IntValue; } template auto Decompose(F f) const { return f(value_); } auto value() const -> int { return value_; } private: int value_; }; // A function or bound method value. class FunctionOrMethodValue : public Value { public: explicit FunctionOrMethodValue( Kind kind, Nonnull declaration, Nonnull bindings) : Value(kind), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::FunctionValue || value->kind() == Kind::BoundMethodValue; } auto declaration() const -> const FunctionDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } auto type_args() const -> const BindingMap& { return bindings_->args(); } auto witnesses() const -> const ImplWitnessMap& { return bindings_->witnesses(); } private: Nonnull declaration_; Nonnull bindings_; }; // A function value. class FunctionValue : public FunctionOrMethodValue { public: explicit FunctionValue(Nonnull declaration, Nonnull bindings) : FunctionOrMethodValue(Kind::FunctionValue, declaration, bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::FunctionValue; } template auto Decompose(F f) const { return f(&declaration(), &bindings()); } }; // A bound method value. It includes the receiver object. class BoundMethodValue : public FunctionOrMethodValue { public: explicit BoundMethodValue(Nonnull declaration, Nonnull receiver, Nonnull bindings) : FunctionOrMethodValue(Kind::BoundMethodValue, declaration, bindings), receiver_(receiver) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BoundMethodValue; } template auto Decompose(F f) const { return f(&declaration(), receiver_, &bindings()); } auto receiver() const -> Nonnull { return receiver_; } private: Nonnull receiver_; }; // A destructor value. class DestructorValue : public Value { public: explicit DestructorValue(Nonnull declaration) : Value(Kind::DestructorValue), declaration_(declaration) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::DestructorValue; } template auto Decompose(F f) const { return f(declaration_); } auto declaration() const -> const DestructorDeclaration& { return *declaration_; } private: Nonnull declaration_; }; // The value of a location in memory. class LocationValue : public Value { public: explicit LocationValue(Address value) : Value(Kind::LocationValue), value_(std::move(value)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::LocationValue; } template auto Decompose(F f) const { return f(value_); } auto address() const -> const Address& { return value_; } private: Address value_; }; // Contains the result of the evaluation of an expression, including a value, // the original expression category, and an optional address if available. class ExpressionResult { public: static auto Value(Nonnull v) -> ExpressionResult { return ExpressionResult(v, std::nullopt, ExpressionCategory::Value); } static auto Reference(Nonnull v, Address address) -> ExpressionResult { return ExpressionResult(v, std::move(address), ExpressionCategory::Reference); } static auto Initializing(Nonnull v, Address address) -> ExpressionResult { return ExpressionResult(v, std::move(address), ExpressionCategory::Initializing); } ExpressionResult(Nonnull v, std::optional
address, ExpressionCategory cat) : value_(v), address_(std::move(address)), expr_cat_(cat) {} auto value() const -> Nonnull { return value_; } auto address() const -> const std::optional
& { return address_; } auto expression_category() const -> ExpressionCategory { return expr_cat_; } private: Nonnull value_; std::optional
address_; ExpressionCategory expr_cat_; }; // Represents the result of the evaluation of a reference expression, and // holds the resulting `Value*` and its `Address`. class ReferenceExpressionValue : public Value { public: ReferenceExpressionValue(Nonnull value, Address address) : Value(Kind::ReferenceExpressionValue), value_(value), address_(std::move(address)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ReferenceExpressionValue; } template auto Decompose(F f) const { return f(value_, address_); } auto value() const -> Nonnull { return value_; } auto address() const -> const Address& { return address_; } private: Nonnull value_; Address address_; }; // A pointer value class PointerValue : public Value { public: explicit PointerValue(Address value) : Value(Kind::PointerValue), value_(std::move(value)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::PointerValue; } template auto Decompose(F f) const { return f(value_); } auto address() const -> const Address& { return value_; } private: Address value_; }; // A bool value. class BoolValue : public Value { public: explicit BoolValue(bool value) : Value(Kind::BoolValue), value_(value) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BoolValue; } template auto Decompose(F f) const { return f(value_); } auto value() const -> bool { return value_; } private: bool value_; }; // A value of a struct type. Note that the expression `{}` is a value of type // `{} as type`; the former is a `StructValue` and the latter is a // `StructType`. class StructValue : public Value { public: explicit StructValue(std::vector elements) : Value(Kind::StructValue), elements_(std::move(elements)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::StructValue; } template auto Decompose(F f) const { return f(elements_); } auto elements() const -> llvm::ArrayRef { return elements_; } // Returns the value of the field named `name` in this struct, or // nullopt if there is no such field. auto FindField(std::string_view name) const -> std::optional>; private: std::vector elements_; }; // A value of a nominal class type, i.e., an object. class NominalClassValue : public Value { public: static constexpr llvm::StringLiteral BaseField{"base"}; // Takes the class type, inits, an optional base, a pointer to a // NominalClassValue*, that must be common to all NominalClassValue of the // same object. The pointee is updated, when `NominalClassValue`s are // constructed, to point to the `NominalClassValue` corresponding to the // child-most class type. Sets *class_value_ptr = this, which corresponds to // the static type of the value matching its dynamic type. NominalClassValue(Nonnull type, Nonnull inits, std::optional> base, Nonnull class_value_ptr); static auto classof(const Value* value) -> bool { return value->kind() == Kind::NominalClassValue; } template auto Decompose(F f) const { return f(type_, inits_, base_, class_value_ptr_); } auto type() const -> const Value& { return *type_; } auto inits() const -> const Value& { return *inits_; } auto base() const -> std::optional> { return base_; } // Returns a pointer of pointer to the child-most class value. auto class_value_ptr() const -> Nonnull { return class_value_ptr_; } private: Nonnull type_; Nonnull inits_; // The initializing StructValue. std::optional> base_; Nonnull class_value_ptr_; }; // An alternative constructor value. class AlternativeConstructorValue : public Value { public: AlternativeConstructorValue(Nonnull choice, Nonnull alternative) : Value(Kind::AlternativeConstructorValue), choice_(choice), alternative_(alternative) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::AlternativeConstructorValue; } template auto Decompose(F f) const { return f(&choice(), &alternative()); } auto choice() const -> const ChoiceType& { return *choice_; } auto alternative() const -> const AlternativeSignature& { return *alternative_; } private: Nonnull choice_; Nonnull alternative_; }; // An alternative value. class AlternativeValue : public Value { public: AlternativeValue(Nonnull choice, Nonnull alternative, std::optional> argument) : Value(Kind::AlternativeValue), choice_(choice), alternative_(alternative), argument_(argument) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::AlternativeValue; } template auto Decompose(F f) const { return f(&choice(), &alternative(), argument_); } auto choice() const -> const ChoiceType& { return *choice_; } auto alternative() const -> const AlternativeSignature& { return *alternative_; } auto argument() const -> std::optional> { return argument_; } private: Nonnull choice_; Nonnull alternative_; std::optional> argument_; }; // Base class for tuple types and tuple values. These are the same other than // their type-of-type, but we separate them to make it easier to tell types and // values apart. class TupleValueBase : public Value { public: explicit TupleValueBase(Value::Kind kind, std::vector> elements) : Value(kind), elements_(std::move(elements)) {} auto elements() const -> llvm::ArrayRef> { return elements_; } static auto classof(const Value* value) -> bool { return value->kind() == Kind::TupleValue || value->kind() == Kind::TupleType; } template auto Decompose(F f) const { return f(elements_); } private: std::vector> elements_; }; // A tuple value. class TupleValue : public TupleValueBase { public: // An empty tuple. static auto Empty() -> Nonnull { static const TupleValue empty = TupleValue(std::vector>()); return static_cast>(&empty); } explicit TupleValue(std::vector> elements) : TupleValueBase(Kind::TupleValue, std::move(elements)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TupleValue; } }; // A tuple type. These values are produced by converting a tuple value // containing only types to type `type`. class TupleType : public TupleValueBase { public: // The unit type. static auto Empty() -> Nonnull { static const TupleType empty = TupleType(std::vector>()); return static_cast>(&empty); } explicit TupleType(std::vector> elements) : TupleValueBase(Kind::TupleType, std::move(elements)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TupleType; } }; // A binding placeholder value. class BindingPlaceholderValue : public Value { public: // Represents the `_` placeholder. explicit BindingPlaceholderValue() : Value(Kind::BindingPlaceholderValue) {} // Represents a named placeholder. explicit BindingPlaceholderValue(ValueNodeView value_node) : Value(Kind::BindingPlaceholderValue), value_node_(std::move(value_node)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BindingPlaceholderValue; } template auto Decompose(F f) const { return value_node_ ? f(*value_node_) : f(); } auto value_node() const -> const std::optional& { return value_node_; } private: std::optional value_node_; }; // Value for addr pattern class AddrValue : public Value { public: explicit AddrValue(Nonnull pattern) : Value(Kind::AddrValue), pattern_(pattern) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::AddrValue; } template auto Decompose(F f) const { return f(pattern_); } auto pattern() const -> const Value& { return *pattern_; } private: Nonnull pattern_; }; // Value for uninitialized local variables. class UninitializedValue : public Value { public: explicit UninitializedValue(Nonnull pattern) : Value(Kind::UninitializedValue), pattern_(pattern) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::UninitializedValue; } template auto Decompose(F f) const { return f(pattern_); } auto pattern() const -> const Value& { return *pattern_; } private: Nonnull pattern_; }; // The int type. class IntType : public Value { public: IntType() : Value(Kind::IntType) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::IntType; } template auto Decompose(F f) const { return f(); } }; // The bool type. class BoolType : public Value { public: BoolType() : Value(Kind::BoolType) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BoolType; } template auto Decompose(F f) const { return f(); } }; // A type type. class TypeType : public Value { public: TypeType() : Value(Kind::TypeType) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TypeType; } template auto Decompose(F f) const { return f(); } }; // A function type. class FunctionType : public Value { public: // An explicit function parameter that is a `:!` binding: // // fn MakeEmptyVector(T:! type) -> Vector(T); struct GenericParameter : public HashFromDecompose { template auto Decompose(F f) const { return f(index, binding); } size_t index; Nonnull binding; }; // For methods with unbound `self` parameters. struct MethodSelf : public HashFromDecompose { template auto Decompose(F f) const { return f(addr_self, self_type); } // True if `self` parameter uses an `addr` pattern. bool addr_self; // Type of `self` parameter. const Value* self_type; }; FunctionType(std::optional method_self, Nonnull parameters, Nonnull return_type) : FunctionType(method_self, parameters, {}, return_type, {}, {}, /*is_initializing=*/false) {} FunctionType(std::optional method_self, Nonnull parameters, std::vector generic_parameters, Nonnull return_type, std::vector> deduced_bindings, std::vector> impl_bindings, bool is_initializing) : Value(Kind::FunctionType), method_self_(method_self), parameters_(parameters), generic_parameters_(std::move(generic_parameters)), return_type_(return_type), deduced_bindings_(std::move(deduced_bindings)), impl_bindings_(std::move(impl_bindings)), is_initializing_(is_initializing) {} struct ExceptSelf : public HashFromDecompose { template auto Decompose(F f) const { return f(); } }; FunctionType(ExceptSelf, const FunctionType* clone) : FunctionType(std::nullopt, clone->parameters_, clone->generic_parameters_, clone->return_type_, clone->deduced_bindings_, clone->impl_bindings_, clone->is_initializing_) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::FunctionType; } template auto Decompose(F f) const { return f(method_self_, parameters_, generic_parameters_, return_type_, deduced_bindings_, impl_bindings_, is_initializing_); } // The type of the function parameter tuple. auto parameters() const -> const Value& { return *parameters_; } // Parameters that use a generic `:!` binding at the top level. auto generic_parameters() const -> llvm::ArrayRef { return generic_parameters_; } // The function return type. auto return_type() const -> const Value& { return *return_type_; } // All generic bindings in this function's signature that should be deduced // in a call. This excludes any generic parameters. auto deduced_bindings() const -> llvm::ArrayRef> { return deduced_bindings_; } // The bindings for the impl witness tables required by the // bounds on the type parameters of the generic function. auto impl_bindings() const -> llvm::ArrayRef> { return impl_bindings_; } // Return whether the function type is an initializing expression or not. auto is_initializing() const -> bool { return is_initializing_; } // Binding for the implicit `self` parameter, if this is an unbound method. auto method_self() const -> std::optional { return method_self_; } private: std::optional method_self_; Nonnull parameters_; std::vector generic_parameters_; Nonnull return_type_; std::vector> deduced_bindings_; std::vector> impl_bindings_; bool is_initializing_; }; // A pointer type. class PointerType : public Value { public: // Constructs a pointer type with the given pointee type. explicit PointerType(Nonnull pointee_type) : Value(Kind::PointerType), pointee_type_(pointee_type) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::PointerType; } template auto Decompose(F f) const { return f(pointee_type_); } auto pointee_type() const -> const Value& { return *pointee_type_; } private: Nonnull pointee_type_; }; // The `auto` type. class AutoType : public Value { public: AutoType() : Value(Kind::AutoType) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::AutoType; } template auto Decompose(F f) const { return f(); } }; // A struct type. class StructType : public Value { public: StructType() : StructType(std::vector{}) {} explicit StructType(std::vector fields) : Value(Kind::StructType), fields_(std::move(fields)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::StructType; } template auto Decompose(F f) const { return f(fields_); } auto fields() const -> llvm::ArrayRef { return fields_; } private: std::vector fields_; }; // A class type. class NominalClassType : public Value { public: explicit NominalClassType( Nonnull declaration, Nonnull bindings, std::optional> base, Nonnull class_vtable) : Value(Kind::NominalClassType), declaration_(declaration), bindings_(bindings), base_(base), vtable_(class_vtable), hierarchy_level_(base ? (*base)->hierarchy_level() + 1 : 0) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::NominalClassType; } template auto Decompose(F f) const { return f(declaration_, bindings_, base_, vtable_); } auto declaration() const -> const ClassDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } auto base() const -> std::optional> { return base_; } auto type_args() const -> const BindingMap& { return bindings_->args(); } // Witnesses for each of the class's impl bindings. auto witnesses() const -> const ImplWitnessMap& { return bindings_->witnesses(); } auto vtable() const -> const VTable& { return *vtable_; } // Returns how many levels from the top ancestor class it is. i.e. a class // with no base returns `0`, while a class with a `.base` and `.base.base` // returns `2`. auto hierarchy_level() const -> int { return hierarchy_level_; } // Returns whether this a parameterized class. That is, a class with // parameters and no corresponding arguments. auto IsParameterized() const -> bool { return declaration_->type_params().has_value() && type_args().empty(); } // Returns whether this class is, or inherits `other`. auto InheritsClass(Nonnull other) const -> bool; private: Nonnull declaration_; Nonnull bindings_ = Bindings::None(); const std::optional> base_; Nonnull vtable_; int hierarchy_level_; }; class MixinPseudoType : public Value { public: explicit MixinPseudoType(Nonnull declaration) : Value(Kind::MixinPseudoType), declaration_(declaration) { CARBON_CHECK(!declaration->params().has_value()) << "missing arguments for parameterized mixin type"; } explicit MixinPseudoType(Nonnull declaration, Nonnull bindings) : Value(Kind::MixinPseudoType), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::MixinPseudoType; } template auto Decompose(F f) const { return f(declaration_, bindings_); } auto declaration() const -> const MixinDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } auto args() const -> const BindingMap& { return bindings_->args(); } auto witnesses() const -> const ImplWitnessMap& { return bindings_->witnesses(); } auto FindFunction(const std::string_view& name) const -> std::optional>; private: Nonnull declaration_; Nonnull bindings_ = Bindings::None(); }; // Returns the value of the function named `name` in this class, or // nullopt if there is no such function. auto FindFunction(std::string_view name, llvm::ArrayRef> members) -> std::optional>; // Returns the value of the function named `name` in this class and its // parents, or nullopt if there is no such function. auto FindFunctionWithParents(std::string_view name, const ClassDeclaration& class_decl) -> std::optional>; // Return the declaration of the member with the given name. auto FindMember(std::string_view name, llvm::ArrayRef> members) -> std::optional>; // An interface type. class InterfaceType : public Value { public: explicit InterfaceType(Nonnull declaration) : Value(Kind::InterfaceType), declaration_(declaration) { CARBON_CHECK(!declaration->params().has_value()) << "missing arguments for parameterized interface type"; } explicit InterfaceType(Nonnull declaration, Nonnull bindings) : Value(Kind::InterfaceType), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::InterfaceType; } template auto Decompose(F f) const { return f(declaration_, bindings_); } auto declaration() const -> const InterfaceDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } auto args() const -> const BindingMap& { return bindings_->args(); } auto witnesses() const -> const ImplWitnessMap& { return bindings_->witnesses(); } private: Nonnull declaration_; Nonnull bindings_ = Bindings::None(); }; // A named constraint type. class NamedConstraintType : public Value { public: explicit NamedConstraintType( Nonnull declaration, Nonnull bindings) : Value(Kind::NamedConstraintType), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::NamedConstraintType; } template auto Decompose(F f) const { return f(declaration_, bindings_); } auto declaration() const -> const ConstraintDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } private: Nonnull declaration_; Nonnull bindings_ = Bindings::None(); }; // A constraint that requires implementation of an interface. struct ImplsConstraint : public HashFromDecompose { template auto Decompose(F f) const { return f(type, interface); } // The type that is required to implement the interface. Nonnull type; // The interface that is required to be implemented. Nonnull interface; }; // A constraint that requires an intrinsic property of a type. struct IntrinsicConstraint : public HashFromDecompose, public Printable { enum Kind { // `type` intrinsically implicitly converts to `parameters[0]`. // TODO: Split ImplicitAs into more specific constraints (such as // derived-to-base pointer conversions). ImplicitAs, }; explicit IntrinsicConstraint(Nonnull type, Kind kind, std::vector> arguments) : type(type), kind(kind), arguments(std::move(arguments)) {} template auto Decompose(F f) const { return f(type, kind, arguments); } // Print the intrinsic constraint. void Print(llvm::raw_ostream& out) const; // The type that is required to satisfy the intrinsic property. Nonnull type; // The kind of the intrinsic property. Kind kind; // Arguments for the intrinsic property. The meaning of these depends on // `kind`. std::vector> arguments; }; // A constraint that a collection of values are known to be the same. struct EqualityConstraint : public HashFromDecompose { template auto Decompose(F f) const { return f(values); } // Visit the values in this equality constraint that are a single step away // from the given value according to this equality constraint. That is: if // `value` is identical to a value in `values`, then call the visitor on all // values in `values` that are not identical to `value`. Otherwise, do not // call the visitor. // // Stops and returns `false` if any call to the visitor returns `false`, // otherwise returns `true`. auto VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool; std::vector> values; }; // A constraint indicating that access to an associated constant should be // replaced by another value. struct RewriteConstraint : public HashFromDecompose { template auto Decompose(F f) const { return f(constant, unconverted_replacement, unconverted_replacement_type, converted_replacement); } // The associated constant value that is rewritten. Nonnull constant; // The replacement in its original type. Nonnull unconverted_replacement; // The type of the replacement. Nonnull unconverted_replacement_type; // The replacement after conversion to the type of the associated constant. Nonnull converted_replacement; }; // A context in which we might look up a name. struct LookupContext : public HashFromDecompose { template auto Decompose(F f) const { return f(context); } Nonnull context; }; // A type-of-type for an unknown constrained type. // // These types are formed by the `&` operator that combines constraints and by // `where` expressions. // // A constraint has three main properties: // // * A collection of (type, interface) pairs for interfaces that are known to // be implemented by a type satisfying the constraint. // * A collection of (type, intrinsic) pairs for intrinsic properties that are // known to be satisfied by a type satisfying the constraint. // * A collection of sets of values, typically associated constants, that are // known to be the same. // * A collection of contexts in which member name lookups will be performed // for a type variable whose type is this constraint. // // Within these properties, the constrained type can be referred to with a // `VariableType` naming the `self_binding`. class ConstraintType : public Value { public: explicit ConstraintType( Nonnull self_binding, std::vector impls_constraints, std::vector intrinsic_constraints, std::vector equality_constraints, std::vector rewrite_constraints, std::vector lookup_contexts) : Value(Kind::ConstraintType), self_binding_(self_binding), impls_constraints_(std::move(impls_constraints)), intrinsic_constraints_(std::move(intrinsic_constraints)), equality_constraints_(std::move(equality_constraints)), rewrite_constraints_(std::move(rewrite_constraints)), lookup_contexts_(std::move(lookup_contexts)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ConstraintType; } template auto Decompose(F f) const { return f(self_binding_, impls_constraints_, intrinsic_constraints_, equality_constraints_, rewrite_constraints_, lookup_contexts_); } auto self_binding() const -> Nonnull { return self_binding_; } auto impls_constraints() const -> llvm::ArrayRef { return impls_constraints_; } auto intrinsic_constraints() const -> llvm::ArrayRef { return intrinsic_constraints_; } auto equality_constraints() const -> llvm::ArrayRef { return equality_constraints_; } auto rewrite_constraints() const -> llvm::ArrayRef { return rewrite_constraints_; } auto lookup_contexts() const -> llvm::ArrayRef { return lookup_contexts_; } // Visit the values in that are a single step away from the given value // according to equality constraints in this constraint type, that is, the // values `v` that are not identical to `value` but for which we have a // `value == v` equality constraint in this constraint type. // // Stops and returns `false` if any call to the visitor returns `false`, // otherwise returns `true`. auto VisitEqualValues( Nonnull value, llvm::function_ref)> visitor) const -> bool; private: Nonnull self_binding_; std::vector impls_constraints_; std::vector intrinsic_constraints_; std::vector equality_constraints_; std::vector rewrite_constraints_; std::vector lookup_contexts_; }; // A witness table. class Witness : public Value { protected: explicit Witness(Value::Kind kind) : Value(kind) {} public: static auto classof(const Value* value) -> bool { return value->kind() == Kind::ImplWitness || value->kind() == Kind::BindingWitness || value->kind() == Kind::ConstraintWitness || value->kind() == Kind::ConstraintImplWitness; } }; // The witness table for an impl. class ImplWitness : public Witness { public: // Construct a witness for an impl. explicit ImplWitness(Nonnull declaration, Nonnull bindings) : Witness(Kind::ImplWitness), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ImplWitness; } template auto Decompose(F f) const { return f(declaration_, bindings_); } auto declaration() const -> const ImplDeclaration& { return *declaration_; } auto bindings() const -> const Bindings& { return *bindings_; } auto type_args() const -> const BindingMap& { return bindings_->args(); } auto witnesses() const -> const ImplWitnessMap& { return bindings_->witnesses(); } private: Nonnull declaration_; Nonnull bindings_ = Bindings::None(); }; // The symbolic witness corresponding to an unresolved impl binding. class BindingWitness : public Witness { public: // Construct a witness for an impl binding. explicit BindingWitness(Nonnull binding) : Witness(Kind::BindingWitness), binding_(binding) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BindingWitness; } template auto Decompose(F f) const { return f(binding_); } auto binding() const -> Nonnull { return binding_; } private: Nonnull binding_; }; // A witness for a constraint type, expressed as a tuple of witnesses for the // individual impls constraints in the constraint type. class ConstraintWitness : public Witness { public: explicit ConstraintWitness(std::vector> witnesses) : Witness(Kind::ConstraintWitness), witnesses_(std::move(witnesses)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ConstraintWitness; } template auto Decompose(F f) const { return f(witnesses_); } auto witnesses() const -> llvm::ArrayRef> { return witnesses_; } private: std::vector> witnesses_; }; // A witness for an impls constraint in a constraint type, expressed in terms of // a symbolic witness for the constraint type. class ConstraintImplWitness : public Witness { public: // Make a witness for the given impls_constraint of the given `ConstraintType` // witness. If we're indexing into a known tuple of witnesses, pull out the // element. static auto Make(Nonnull arena, Nonnull witness, int index) -> Nonnull { CARBON_CHECK(!llvm::isa(witness)) << "impl witness has no components to access"; if (const auto* constraint_witness = llvm::dyn_cast(witness)) { return constraint_witness->witnesses()[index]; } return arena->New(witness, index); } explicit ConstraintImplWitness(Nonnull constraint_witness, int index) : Witness(Kind::ConstraintImplWitness), constraint_witness_(constraint_witness), index_(index) { CARBON_CHECK(!llvm::isa(constraint_witness)) << "should have resolved element from constraint witness"; } static auto classof(const Value* value) -> bool { return value->kind() == Kind::ConstraintImplWitness; } template auto Decompose(F f) const { return f(constraint_witness_, index_); } // Get the witness for the complete `ConstraintType`. auto constraint_witness() const -> Nonnull { return constraint_witness_; } // Get the index of the impls constraint within the constraint type. auto index() const -> int { return index_; } private: Nonnull constraint_witness_; int index_; }; // Allocate a `ConstraintImplWitness` using the custom `Make` function. template <> struct AllocateTrait { template static auto New(Nonnull arena, Args&&... args) -> Nonnull { return ConstraintImplWitness::Make(arena, std::forward(args)...); } }; // A choice type. class ChoiceType : public Value { public: ChoiceType(Nonnull declaration, Nonnull bindings) : Value(Kind::ChoiceType), declaration_(declaration), bindings_(bindings) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ChoiceType; } template auto Decompose(F f) const { return f(declaration_, bindings_); } auto bindings() const -> const Bindings& { return *bindings_; } auto type_args() const -> const BindingMap& { return bindings_->args(); } auto declaration() const -> const ChoiceDeclaration& { return *declaration_; } auto IsParameterized() const -> bool { return declaration_->type_params().has_value(); } private: Nonnull declaration_; Nonnull bindings_; }; // A variable type. class VariableType : public Value { public: explicit VariableType(Nonnull binding) : Value(Kind::VariableType), binding_(binding) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::VariableType; } template auto Decompose(F f) const { return f(binding_); } auto binding() const -> const GenericBinding& { return *binding_; } private: Nonnull binding_; }; // A name of an entity that has explicit parameters, such as a parameterized // class or interface. When arguments for those parameters are provided in a // call, the result will be a class type or interface type. class ParameterizedEntityName : public Value { public: explicit ParameterizedEntityName(Nonnull declaration, Nonnull params) : Value(Kind::ParameterizedEntityName), declaration_(declaration), params_(params) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::ParameterizedEntityName; } template auto Decompose(F f) const { return f(declaration_, params_); } auto declaration() const -> const Declaration& { return *declaration_; } auto params() const -> const TuplePattern& { return *params_; } private: Nonnull declaration_; Nonnull params_; }; // The name of a member of a class or interface. // // These values are used to represent the second operand of a compound member // access expression: `x.(A.B)`, and can also be the value of an alias // declaration, but cannot be used in most other contexts. class MemberName : public Value, public Printable { public: MemberName(std::optional> base_type, std::optional> interface, Nonnull member) : Value(Kind::MemberName), base_type_(base_type), interface_(interface), member_(member) { CARBON_CHECK(base_type || interface) << "member name must be in a type, an interface, or both"; } static auto classof(const Value* value) -> bool { return value->kind() == Kind::MemberName; } template auto Decompose(F f) const { return f(base_type_, interface_, member_); } // The type for which `name` is a member or a member of an `impl`. auto base_type() const -> std::optional> { return base_type_; } // The interface for which `name` is a member, if any. auto interface() const -> std::optional> { return interface_; } // The member. auto member() const -> const NamedElement& { return *member_; } // The name of the member. auto name() const -> std::string_view { return member().name(); } private: std::optional> base_type_; std::optional> interface_; Nonnull member_; }; // A symbolic value representing an associated constant. // // This is a value of the form `A.B` or `A.B.C` or similar, where `A` is a // `VariableType`. class AssociatedConstant : public Value { public: explicit AssociatedConstant( Nonnull base, Nonnull interface, Nonnull constant, Nonnull witness) : Value(Kind::AssociatedConstant), base_(base), interface_(interface), constant_(constant), witness_(witness) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::AssociatedConstant; } template auto Decompose(F f) const { return f(base_, interface_, constant_, witness_); } // The type for which we denote an associated constant. auto base() const -> const Value& { return *base_; } // The interface within which the constant was declared. auto interface() const -> const InterfaceType& { return *interface_; } // The associated constant whose value is being denoted. auto constant() const -> const AssociatedConstantDeclaration& { return *constant_; } // Witness within which the constant's value can be found. auto witness() const -> const Witness& { return *witness_; } private: Nonnull base_; Nonnull interface_; Nonnull constant_; Nonnull witness_; }; // The String type. class StringType : public Value { public: StringType() : Value(Kind::StringType) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::StringType; } template auto Decompose(F f) const { return f(); } }; // A string value. class StringValue : public Value { public: explicit StringValue(std::string value) : Value(Kind::StringValue), value_(std::move(value)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::StringValue; } template auto Decompose(F f) const { return f(value_); } auto value() const -> const std::string& { return value_; } private: std::string value_; }; class TypeOfMixinPseudoType : public Value { public: explicit TypeOfMixinPseudoType(Nonnull class_type) : Value(Kind::TypeOfMixinPseudoType), mixin_type_(class_type) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TypeOfMixinPseudoType; } template auto Decompose(F f) const { return f(mixin_type_); } auto mixin_type() const -> const MixinPseudoType& { return *mixin_type_; } private: Nonnull mixin_type_; }; // The type of an expression whose value is the name of a parameterized entity. // Such an expression can only be used as the operand of a call expression that // provides arguments for the parameters. class TypeOfParameterizedEntityName : public Value { public: explicit TypeOfParameterizedEntityName( Nonnull name) : Value(Kind::TypeOfParameterizedEntityName), name_(name) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TypeOfParameterizedEntityName; } template auto Decompose(F f) const { return f(name_); } auto name() const -> const ParameterizedEntityName& { return *name_; } private: Nonnull name_; }; // The type of a member name expression. // // This is used for member names that don't denote a specific object or value // until used on the right-hand side of a `.`, such as an instance method or // field name, or any member function in an interface. // // Such expressions can appear only as the target of an `alias` declaration or // as the member name in a compound member access. class TypeOfMemberName : public Value { public: explicit TypeOfMemberName(Nonnull member) : Value(Kind::TypeOfMemberName), member_(member) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TypeOfMemberName; } template auto Decompose(F f) const { return f(member_); } // TODO: consider removing this or moving it elsewhere in the AST, // since it's arguably part of the expression value rather than its type. auto member() const -> const NamedElement& { return *member_; } private: Nonnull member_; }; // The type of a namespace name. // // Such expressions can appear only as the target of an `alias` declaration or // as the left-hand side of a simple member access expression. class TypeOfNamespaceName : public Value { public: explicit TypeOfNamespaceName( Nonnull namespace_decl) : Value(Kind::TypeOfNamespaceName), namespace_decl_(namespace_decl) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::TypeOfNamespaceName; } template auto Decompose(F f) const { return f(namespace_decl_); } auto namespace_decl() const -> Nonnull { return namespace_decl_; } private: Nonnull namespace_decl_; }; // The type of a statically-sized array. // // Note that values of this type are represented as tuples. class StaticArrayType : public Value { public: // Constructs a statically-sized array type with the given element type and // size. StaticArrayType(Nonnull element_type, std::optional size) : Value(Kind::StaticArrayType), element_type_(element_type), size_(size) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::StaticArrayType; } template auto Decompose(F f) const { return f(element_type_, size_); } auto element_type() const -> const Value& { return *element_type_; } auto size() const -> size_t { CARBON_CHECK(has_size()); return *size_; } auto has_size() const -> bool { return size_.has_value(); } private: Nonnull element_type_; std::optional size_; }; template auto Value::Visit(F f) const -> R { switch (kind()) { #define CARBON_VALUE_KIND(kind) \ case Kind::kind: \ return f(static_cast(this)); #include "explorer/ast/value_kinds.def" } } } // namespace Carbon #endif // CARBON_EXPLORER_AST_VALUE_H_