// Copyright (C) 2020-2023 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #ifndef RUST_AST_TYPE_H #define RUST_AST_TYPE_H #include "rust-ast.h" #include "rust-path.h" namespace Rust { namespace AST { // definitions moved to rust-ast.h class TypeParamBound; class Lifetime; // A trait bound class TraitBound : public TypeParamBound { bool in_parens; bool opening_question_mark; // bool has_for_lifetimes; // LifetimeParams for_lifetimes; std::vector for_lifetimes; // inlined LifetimeParams TypePath type_path; Location locus; public: // Returns whether trait bound has "for" lifetimes bool has_for_lifetimes () const { return !for_lifetimes.empty (); } std::vector &get_for_lifetimes () { return for_lifetimes; } TraitBound (TypePath type_path, Location locus, bool in_parens = false, bool opening_question_mark = false, std::vector for_lifetimes = std::vector ()) : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()), in_parens (in_parens), opening_question_mark (opening_question_mark), for_lifetimes (std::move (for_lifetimes)), type_path (std::move (type_path)), locus (locus) {} TraitBound (NodeId id, TypePath type_path, Location locus, bool in_parens = false, bool opening_question_mark = false, std::vector for_lifetimes = std::vector ()) : TypeParamBound (id), in_parens (in_parens), opening_question_mark (opening_question_mark), for_lifetimes (std::move (for_lifetimes)), type_path (std::move (type_path)), locus (locus) {} std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: this mutable getter seems kinda dodgy TypePath &get_type_path () { return type_path; } const TypePath &get_type_path () const { return type_path; } bool is_in_parens () const { return in_parens; } bool has_opening_question_mark () const { return opening_question_mark; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ TraitBound *clone_type_param_bound_impl () const override { return new TraitBound (node_id, type_path, locus, in_parens, opening_question_mark, for_lifetimes); } }; // definition moved to rust-ast.h class TypeNoBounds; // An impl trait? Poor reference material here. class ImplTraitType : public Type { // TypeParamBounds type_param_bounds; // inlined form std::vector > type_param_bounds; Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ ImplTraitType *clone_type_impl () const override { return new ImplTraitType (*this); } public: ImplTraitType ( std::vector > type_param_bounds, Location locus) : type_param_bounds (std::move (type_param_bounds)), locus (locus) {} // copy constructor with vector clone ImplTraitType (ImplTraitType const &other) : locus (other.locus) { type_param_bounds.reserve (other.type_param_bounds.size ()); for (const auto &e : other.type_param_bounds) type_param_bounds.push_back (e->clone_type_param_bound ()); } // overloaded assignment operator to clone ImplTraitType &operator= (ImplTraitType const &other) { locus = other.locus; type_param_bounds.reserve (other.type_param_bounds.size ()); for (const auto &e : other.type_param_bounds) type_param_bounds.push_back (e->clone_type_param_bound ()); return *this; } // move constructors ImplTraitType (ImplTraitType &&other) = default; ImplTraitType &operator= (ImplTraitType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: mutable getter seems kinda dodgy std::vector > &get_type_param_bounds () { return type_param_bounds; } const std::vector > & get_type_param_bounds () const { return type_param_bounds; } }; // An opaque value of another type that implements a set of traits class TraitObjectType : public Type { bool has_dyn; std::vector > type_param_bounds; Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ TraitObjectType *clone_type_impl () const override { return new TraitObjectType (*this); } public: TraitObjectType ( std::vector > type_param_bounds, Location locus, bool is_dyn_dispatch) : has_dyn (is_dyn_dispatch), type_param_bounds (std::move (type_param_bounds)), locus (locus) {} // copy constructor with vector clone TraitObjectType (TraitObjectType const &other) : has_dyn (other.has_dyn), locus (other.locus) { type_param_bounds.reserve (other.type_param_bounds.size ()); for (const auto &e : other.type_param_bounds) type_param_bounds.push_back (e->clone_type_param_bound ()); } // overloaded assignment operator to clone TraitObjectType &operator= (TraitObjectType const &other) { has_dyn = other.has_dyn; locus = other.locus; type_param_bounds.reserve (other.type_param_bounds.size ()); for (const auto &e : other.type_param_bounds) type_param_bounds.push_back (e->clone_type_param_bound ()); return *this; } // move constructors TraitObjectType (TraitObjectType &&other) = default; TraitObjectType &operator= (TraitObjectType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; bool is_dyn () const { return has_dyn; } // TODO: mutable getter seems kinda dodgy std::vector > &get_type_param_bounds () { return type_param_bounds; } const std::vector > & get_type_param_bounds () const { return type_param_bounds; } }; // A type with parentheses around it, used to avoid ambiguity. class ParenthesisedType : public TypeNoBounds { std::unique_ptr type_in_parens; Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ ParenthesisedType *clone_type_no_bounds_impl () const override { return new ParenthesisedType (*this); } public: // Constructor uses Type pointer for polymorphism ParenthesisedType (std::unique_ptr type_inside_parens, Location locus) : type_in_parens (std::move (type_inside_parens)), locus (locus) {} /* Copy constructor uses custom deep copy method for type to preserve * polymorphism */ ParenthesisedType (ParenthesisedType const &other) : type_in_parens (other.type_in_parens->clone_type ()), locus (other.locus) {} // overload assignment operator to use custom clone method ParenthesisedType &operator= (ParenthesisedType const &other) { type_in_parens = other.type_in_parens->clone_type (); locus = other.locus; return *this; } // default move semantics ParenthesisedType (ParenthesisedType &&other) = default; ParenthesisedType &operator= (ParenthesisedType &&other) = default; std::string as_string () const override { return "(" + type_in_parens->as_string () + ")"; } // Creates a trait bound (clone of this one's trait bound) - HACK TraitBound *to_trait_bound (bool) const override { /* NOTE: obviously it is unknown whether the internal type is a trait bound * due to polymorphism, so just let the internal type handle it. As * parenthesised type, it must be in parentheses. */ return type_in_parens->to_trait_bound (true); } Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? std::unique_ptr &get_type_in_parens () { rust_assert (type_in_parens != nullptr); return type_in_parens; } }; // Impl trait with a single bound? Poor reference material here. class ImplTraitTypeOneBound : public TypeNoBounds { TraitBound trait_bound; Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override { return new ImplTraitTypeOneBound (*this); } public: ImplTraitTypeOneBound (TraitBound trait_bound, Location locus) : trait_bound (std::move (trait_bound)), locus (locus) {} std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? TraitBound &get_trait_bound () { // TODO: check to ensure invariants are met? return trait_bound; } }; /* A trait object with a single trait bound. The "trait bound" is really just * the trait. Basically like using an interface as a type in an OOP language. */ class TraitObjectTypeOneBound : public TypeNoBounds { bool has_dyn; TraitBound trait_bound; Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ TraitObjectTypeOneBound *clone_type_no_bounds_impl () const override { return new TraitObjectTypeOneBound (*this); } public: TraitObjectTypeOneBound (TraitBound trait_bound, Location locus, bool is_dyn_dispatch = false) : has_dyn (is_dyn_dispatch), trait_bound (std::move (trait_bound)), locus (locus) {} std::string as_string () const override; // Creates a trait bound (clone of this one's trait bound) - HACK TraitBound *to_trait_bound (bool) const override { /* NOTE: this assumes there is no dynamic dispatch specified- if there was, * this cloning would not be required as parsing is unambiguous. */ return new TraitBound (trait_bound); } Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? TraitBound &get_trait_bound () { // TODO: check to ensure invariants are met? return trait_bound; } bool is_dyn () const { return has_dyn; } }; class TypePath; // definition moved to "rust-path.h" /* A type consisting of the "product" of others (the tuple's elements) in a * specific order */ class TupleType : public TypeNoBounds { std::vector > elems; Location locus; public: // Returns whether the tuple type is the unit type, i.e. has no elements. bool is_unit_type () const { return elems.empty (); } TupleType (std::vector > elems, Location locus) : elems (std::move (elems)), locus (locus) {} // copy constructor with vector clone TupleType (TupleType const &other) : locus (other.locus) { elems.reserve (other.elems.size ()); for (const auto &e : other.elems) elems.push_back (e->clone_type ()); } // overloaded assignment operator to clone TupleType &operator= (TupleType const &other) { locus = other.locus; elems.reserve (other.elems.size ()); for (const auto &e : other.elems) elems.push_back (e->clone_type ()); return *this; } // move constructors TupleType (TupleType &&other) = default; TupleType &operator= (TupleType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: mutable getter seems kinda dodgy std::vector > &get_elems () { return elems; } const std::vector > &get_elems () const { return elems; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ TupleType *clone_type_no_bounds_impl () const override { return new TupleType (*this); } }; /* A type with no values, representing the result of computations that never * complete. Expressions of NeverType can be coerced into any other types. * Represented as "!". */ class NeverType : public TypeNoBounds { Location locus; protected: /* Use covariance to implement clone function as returning this object rather * than base */ NeverType *clone_type_no_bounds_impl () const override { return new NeverType (*this); } public: NeverType (Location locus) : locus (locus) {} std::string as_string () const override { return "! (never type)"; } Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; }; // A type consisting of a pointer without safety or liveness guarantees class RawPointerType : public TypeNoBounds { public: enum PointerType { MUT, CONST }; private: PointerType pointer_type; std::unique_ptr type; Location locus; public: // Returns whether the pointer is mutable or constant. PointerType get_pointer_type () const { return pointer_type; } // Constructor requires pointer for polymorphism reasons RawPointerType (PointerType pointer_type, std::unique_ptr type_no_bounds, Location locus) : pointer_type (pointer_type), type (std::move (type_no_bounds)), locus (locus) {} // Copy constructor calls custom polymorphic clone function RawPointerType (RawPointerType const &other) : pointer_type (other.pointer_type), type (other.type->clone_type_no_bounds ()), locus (other.locus) {} // overload assignment operator to use custom clone method RawPointerType &operator= (RawPointerType const &other) { pointer_type = other.pointer_type; type = other.type->clone_type_no_bounds (); locus = other.locus; return *this; } // default move semantics RawPointerType (RawPointerType &&other) = default; RawPointerType &operator= (RawPointerType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? std::unique_ptr &get_type_pointed_to () { rust_assert (type != nullptr); return type; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RawPointerType *clone_type_no_bounds_impl () const override { return new RawPointerType (*this); } }; // A type pointing to memory owned by another value class ReferenceType : public TypeNoBounds { // bool has_lifetime; // TODO: handle in lifetime or something? Lifetime lifetime; bool has_mut; std::unique_ptr type; Location locus; public: // Returns whether the reference is mutable or immutable. bool is_mut () const { return has_mut; } // Returns whether the reference has a lifetime. bool has_lifetime () const { return !lifetime.is_error (); } // Constructor ReferenceType (bool is_mut, std::unique_ptr type_no_bounds, Location locus, Lifetime lifetime = Lifetime::error ()) : lifetime (std::move (lifetime)), has_mut (is_mut), type (std::move (type_no_bounds)), locus (locus) {} // Copy constructor with custom clone method ReferenceType (ReferenceType const &other) : lifetime (other.lifetime), has_mut (other.has_mut), type (other.type->clone_type_no_bounds ()), locus (other.locus) {} // Operator overload assignment operator to custom clone the unique pointer ReferenceType &operator= (ReferenceType const &other) { lifetime = other.lifetime; has_mut = other.has_mut; type = other.type->clone_type_no_bounds (); locus = other.locus; return *this; } // move constructors ReferenceType (ReferenceType &&other) = default; ReferenceType &operator= (ReferenceType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? std::unique_ptr &get_type_referenced () { rust_assert (type != nullptr); return type; } bool get_has_mut () const { return has_mut; } Lifetime &get_lifetime () { return lifetime; } std::unique_ptr &get_base_type () { return type; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ReferenceType *clone_type_no_bounds_impl () const override { return new ReferenceType (*this); } }; // A fixed-size sequence of elements of a specified type class ArrayType : public TypeNoBounds { std::unique_ptr elem_type; std::unique_ptr size; Location locus; public: // Constructor requires pointers for polymorphism ArrayType (std::unique_ptr type, std::unique_ptr array_size, Location locus) : elem_type (std::move (type)), size (std::move (array_size)), locus (locus) {} // Copy constructor requires deep copies of both unique pointers ArrayType (ArrayType const &other) : elem_type (other.elem_type->clone_type ()), size (other.size->clone_expr ()), locus (other.locus) {} // Overload assignment operator to deep copy pointers ArrayType &operator= (ArrayType const &other) { elem_type = other.elem_type->clone_type (); size = other.size->clone_expr (); locus = other.locus; return *this; } // move constructors ArrayType (ArrayType &&other) = default; ArrayType &operator= (ArrayType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? std::unique_ptr &get_elem_type () { rust_assert (elem_type != nullptr); return elem_type; } // TODO: would a "vis_expr" be better? std::unique_ptr &get_size_expr () { rust_assert (size != nullptr); return size; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ArrayType *clone_type_no_bounds_impl () const override { return new ArrayType (*this); } }; /* A dynamically-sized type representing a "view" into a sequence of elements of * a type */ class SliceType : public TypeNoBounds { std::unique_ptr elem_type; Location locus; public: // Constructor requires pointer for polymorphism SliceType (std::unique_ptr type, Location locus) : elem_type (std::move (type)), locus (locus) {} // Copy constructor requires deep copy of Type smart pointer SliceType (SliceType const &other) : elem_type (other.elem_type->clone_type ()), locus (other.locus) {} // Overload assignment operator to deep copy SliceType &operator= (SliceType const &other) { elem_type = other.elem_type->clone_type (); locus = other.locus; return *this; } // move constructors SliceType (SliceType &&other) = default; SliceType &operator= (SliceType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: would a "vis_type" be better? std::unique_ptr &get_elem_type () { rust_assert (elem_type != nullptr); return elem_type; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ SliceType *clone_type_no_bounds_impl () const override { return new SliceType (*this); } }; /* Type used in generic arguments to explicitly request type inference (wildcard * pattern) */ class InferredType : public TypeNoBounds { Location locus; // e.g. Vec<_> = whatever protected: /* Use covariance to implement clone function as returning this object rather * than base */ InferredType *clone_type_no_bounds_impl () const override { return new InferredType (*this); } public: InferredType (Location locus) : locus (locus) {} std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; }; class QualifiedPathInType; // definition moved to "rust-path.h" // A possibly named param used in a BaseFunctionType struct MaybeNamedParam { public: enum ParamKind { UNNAMED, IDENTIFIER, WILDCARD }; private: std::vector outer_attrs; std::unique_ptr param_type; ParamKind param_kind; Identifier name; // technically, can be an identifier or '_' Location locus; public: MaybeNamedParam (Identifier name, ParamKind param_kind, std::unique_ptr param_type, std::vector outer_attrs, Location locus) : outer_attrs (std::move (outer_attrs)), param_type (std::move (param_type)), param_kind (param_kind), name (std::move (name)), locus (locus) {} // Copy constructor with clone MaybeNamedParam (MaybeNamedParam const &other) : outer_attrs (other.outer_attrs), param_kind (other.param_kind), name (other.name), locus (other.locus) { // guard to prevent null dereference if (other.param_type != nullptr) param_type = other.param_type->clone_type (); } ~MaybeNamedParam () = default; // Overloaded assignment operator with clone MaybeNamedParam &operator= (MaybeNamedParam const &other) { outer_attrs = other.outer_attrs; name = other.name; param_kind = other.param_kind; locus = other.locus; // guard to prevent null dereference if (other.param_type != nullptr) param_type = other.param_type->clone_type (); else param_type = nullptr; return *this; } // move constructors MaybeNamedParam (MaybeNamedParam &&other) = default; MaybeNamedParam &operator= (MaybeNamedParam &&other) = default; std::string as_string () const; // Returns whether the param is in an error state. bool is_error () const { return param_type == nullptr; } // Creates an error state param. static MaybeNamedParam create_error () { return MaybeNamedParam ("", UNNAMED, nullptr, {}, Location ()); } Location get_locus () const { return locus; } // TODO: this mutable getter seems really dodgy. Think up better way. std::vector &get_outer_attrs () { return outer_attrs; } const std::vector &get_outer_attrs () const { return outer_attrs; } // TODO: would a "vis_type" be better? std::unique_ptr &get_type () { rust_assert (param_type != nullptr); return param_type; } ParamKind get_param_kind () const { return param_kind; } Identifier get_name () const { return name; } }; /* A function pointer type - can be created via coercion from function items and * non-capturing closures. */ class BareFunctionType : public TypeNoBounds { // bool has_for_lifetimes; // ForLifetimes for_lifetimes; std::vector for_lifetimes; // inlined version FunctionQualifiers function_qualifiers; std::vector params; bool _is_variadic; std::vector variadic_attrs; // bool has_return_type; // BareFunctionReturnType return_type; std::unique_ptr return_type; // inlined version Location locus; public: // Whether a return type is defined with the function. bool has_return_type () const { return return_type != nullptr; } // Whether the function has ForLifetimes. bool has_for_lifetimes () const { return !for_lifetimes.empty (); } std::vector &get_for_lifetimes () { return for_lifetimes; } bool is_variadic () const { return _is_variadic; } std::vector &get_variadic_attr () { return variadic_attrs; }; const std::vector &get_variadic_attr () const { return variadic_attrs; }; BareFunctionType (std::vector lifetime_params, FunctionQualifiers qualifiers, std::vector named_params, bool is_variadic, std::vector variadic_attrs, std::unique_ptr type, Location locus) : for_lifetimes (std::move (lifetime_params)), function_qualifiers (std::move (qualifiers)), params (std::move (named_params)), _is_variadic (is_variadic), variadic_attrs (std::move (variadic_attrs)), return_type (std::move (type)), locus (locus) { if (!variadic_attrs.empty ()) is_variadic = true; } // Copy constructor with clone BareFunctionType (BareFunctionType const &other) : for_lifetimes (other.for_lifetimes), function_qualifiers (other.function_qualifiers), params (other.params), _is_variadic (other._is_variadic), variadic_attrs (other.variadic_attrs), locus (other.locus) { // guard to prevent null dereference if (other.return_type != nullptr) return_type = other.return_type->clone_type_no_bounds (); } // Overload assignment operator to deep copy BareFunctionType &operator= (BareFunctionType const &other) { for_lifetimes = other.for_lifetimes; function_qualifiers = other.function_qualifiers; params = other.params; _is_variadic = other._is_variadic; variadic_attrs = other.variadic_attrs; locus = other.locus; // guard to prevent null dereference if (other.return_type != nullptr) return_type = other.return_type->clone_type_no_bounds (); else return_type = nullptr; return *this; } // move constructors BareFunctionType (BareFunctionType &&other) = default; BareFunctionType &operator= (BareFunctionType &&other) = default; std::string as_string () const override; Location get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // TODO: this mutable getter seems kinda dodgy std::vector &get_function_params () { return params; } const std::vector &get_function_params () const { return params; } // TODO: would a "vis_type" be better? std::unique_ptr &get_return_type () { rust_assert (has_return_type ()); return return_type; } FunctionQualifiers &get_function_qualifiers () { return function_qualifiers; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ BareFunctionType *clone_type_no_bounds_impl () const override { return new BareFunctionType (*this); } }; // Forward decl - defined in rust-macro.h class MacroInvocation; /* TODO: possible types * struct type? * "enum" (tagged union) type? * C-like union type? * function item type? * closure expression types? * primitive types (bool, int, float, char, str (the slice)) * Although supposedly TypePaths are used to reference these types (including * primitives) */ /* FIXME: Incomplete spec references: * anonymous type parameters, aka "impl Trait in argument position" - impl then * trait bounds abstract return types, aka "impl Trait in return position" - * impl then trait bounds */ } // namespace AST } // namespace Rust #endif