// 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_PATTERN_H_ #define CARBON_EXPLORER_AST_PATTERN_H_ #include #include #include #include "common/ostream.h" #include "explorer/ast/ast_node.h" #include "explorer/ast/ast_rtti.h" #include "explorer/ast/clone_context.h" #include "explorer/ast/expression.h" #include "explorer/ast/expression_category.h" #include "explorer/ast/value_node.h" #include "explorer/common/source_location.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLFunctionalExtras.h" namespace Carbon { class Value; // Abstract base class of all AST nodes representing patterns. // // Pattern and its derived classes support LLVM-style RTTI, including // llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every // class derived from Pattern 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. class Pattern : public AstNode { public: explicit Pattern(CloneContext& context, const Pattern& other) : AstNode(context, other), static_type_(context.Clone(other.static_type_)), value_(context.Clone(other.value_)) {} Pattern(const Pattern&) = delete; auto operator=(const Pattern&) -> Pattern& = delete; ~Pattern() override = 0; void Print(llvm::raw_ostream& out) const override; void PrintID(llvm::raw_ostream& out) const override; static auto classof(const AstNode* node) -> bool { return InheritsFromPattern(node->kind()); } // Returns the enumerator corresponding to the most-derived type of this // object. auto kind() const -> PatternKind { return static_cast(root_kind()); } // The static type of this pattern. Cannot be called before typechecking. auto static_type() const -> const Value& { CARBON_CHECK(static_type_.has_value()); return **static_type_; } auto has_static_type() const -> bool { return static_type_.has_value(); } // Sets the static type of this expression. Can only be called once, during // typechecking. void set_static_type(Nonnull type) { CARBON_CHECK(!static_type_.has_value()); static_type_ = type; } // The value of this pattern. Cannot be called before typechecking. // TODO: Rename to avoid confusion with BindingPattern::constant_value auto value() const -> const Value& { return **value_; } // Sets the value of this pattern. Can only be called once, during // typechecking. void set_value(Nonnull value) { CARBON_CHECK(!value_) << "set_value called more than once"; value_ = value; } // Returns whether the value has been set. Should only be called // during typechecking: before typechecking it's guaranteed to be false, // and after typechecking it's guaranteed to be true. auto has_value() const -> bool { return value_.has_value(); } // Determines whether the pattern has already been type-checked. Should // only be used by type-checking. auto is_type_checked() const -> bool { return static_type_.has_value() && value_.has_value(); } protected: // Constructs a Pattern representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being // constructed. Pattern(AstNodeKind kind, SourceLocation source_loc) : AstNode(kind, source_loc) {} private: std::optional> static_type_; std::optional> value_; }; // Call the given `visitor` on all patterns nested within the given pattern, // including `pattern` itself. Aborts and returns `false` if `visitor` returns // `false`, otherwise returns `true`. auto VisitNestedPatterns(const Pattern& pattern, llvm::function_ref visitor) -> bool; inline auto VisitNestedPatterns(Pattern& pattern, llvm::function_ref visitor) -> bool { // The non-const version is is implemented in terms of the const version. The // const_cast is safe because every pattern reachable through a non-const // pattern is also non-const. const Pattern& const_pattern = pattern; return VisitNestedPatterns(const_pattern, [&](const Pattern& inner) { return visitor(const_cast(inner)); }); } // A pattern consisting of the `auto` keyword. class AutoPattern : public Pattern { public: explicit AutoPattern(SourceLocation source_loc) : Pattern(AstNodeKind::AutoPattern, source_loc) {} explicit AutoPattern(CloneContext& context, const AutoPattern& other) : Pattern(context, other) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAutoPattern(node->kind()); } }; class VarPattern : public Pattern { public: explicit VarPattern(SourceLocation source_loc, Nonnull pattern) : Pattern(AstNodeKind::VarPattern, source_loc), pattern_(pattern) {} explicit VarPattern(CloneContext& context, const VarPattern& other) : Pattern(context, other), pattern_(context.Clone(other.pattern_)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromVarPattern(node->kind()); } auto pattern() const -> const Pattern& { return *pattern_; } auto pattern() -> Pattern& { return *pattern_; } auto expression_category() const -> ExpressionCategory { return ExpressionCategory::Reference; } private: Nonnull pattern_; }; // A pattern that matches a value of a specified type, and optionally binds // a name to it. class BindingPattern : public Pattern { public: using ImplementsCarbonValueNode = void; BindingPattern(SourceLocation source_loc, std::string name, Nonnull type, std::optional expression_category) : Pattern(AstNodeKind::BindingPattern, source_loc), name_(std::move(name)), type_(type), expression_category_(expression_category) {} explicit BindingPattern(CloneContext& context, const BindingPattern& other) : Pattern(context, other), name_(other.name_), type_(context.Clone(other.type_)), expression_category_(other.expression_category_) {} static auto classof(const AstNode* node) -> bool { return InheritsFromBindingPattern(node->kind()); } // The name this pattern binds, if any. If equal to AnonymousName, indicates // that this BindingPattern does not bind a name, which in turn means it // should not be used as a ValueNode. auto name() const -> const std::string& { return name_; } // The pattern specifying the type of values that this pattern matches. auto type() const -> const Pattern& { return *type_; } auto type() -> Pattern& { return *type_; } // Returns the value category of this pattern. Can only be called after // typechecking. auto expression_category() const -> ExpressionCategory { return expression_category_.value(); } // Returns whether the value category has been set. Should only be called // during typechecking. auto has_expression_category() const -> bool { return expression_category_.has_value(); } // Sets the value category of the variable being bound. Can only be called // once during typechecking void set_expression_category(ExpressionCategory vc) { CARBON_CHECK(!expression_category_.has_value()); expression_category_ = vc; } auto constant_value() const -> std::optional> { return std::nullopt; } auto symbolic_identity() const -> std::optional> { return std::nullopt; } private: std::string name_; Nonnull type_; std::optional expression_category_; }; class AddrPattern : public Pattern { public: explicit AddrPattern(SourceLocation source_loc, Nonnull binding) : Pattern(AstNodeKind::AddrPattern, source_loc), binding_(binding) {} explicit AddrPattern(CloneContext& context, const AddrPattern& other) : Pattern(context, other), binding_(context.Clone(other.binding_)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAddrPattern(node->kind()); } auto binding() const -> const BindingPattern& { return *binding_; } auto binding() -> BindingPattern& { return *binding_; } private: Nonnull binding_; }; // A pattern that matches a tuple value field-wise. class TuplePattern : public Pattern { public: TuplePattern(SourceLocation source_loc, std::vector> fields) : Pattern(AstNodeKind::TuplePattern, source_loc), fields_(std::move(fields)) {} explicit TuplePattern(CloneContext& context, const TuplePattern& other) : Pattern(context, other), fields_(context.Clone(other.fields_)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromTuplePattern(node->kind()); } auto fields() const -> llvm::ArrayRef> { return fields_; } auto fields() -> llvm::ArrayRef> { return fields_; } private: std::vector> fields_; }; class GenericBinding : public Pattern { public: using ImplementsCarbonValueNode = void; enum class BindingKind { // A checked generic binding, `T:! type`. Checked, // A template generic binding, `template T:! type`. Template, }; explicit GenericBinding(SourceLocation source_loc, std::string name, Nonnull type, BindingKind binding_kind) : Pattern(AstNodeKind::GenericBinding, source_loc), name_(std::move(name)), type_(type), binding_kind_(binding_kind) {} explicit GenericBinding(CloneContext& context, const GenericBinding& other); static auto classof(const AstNode* node) -> bool { return InheritsFromGenericBinding(node->kind()); } auto name() const -> const std::string& { return name_; } auto type() const -> const Expression& { return *type_; } auto type() -> Expression& { return *type_; } auto binding_kind() const -> BindingKind { return binding_kind_; } // The index of this binding, which is the number of bindings that are in // scope at the point where this binding is declared. auto index() const -> int { return *index_; } // Set the index of this binding. Should be called only during type-checking. void set_index(int index) { CARBON_CHECK(!index_) << "should only set depth and index once"; index_ = index; } auto expression_category() const -> ExpressionCategory { return ExpressionCategory::Value; } auto constant_value() const -> std::optional> { return template_value_; } auto symbolic_identity() const -> std::optional> { return symbolic_identity_; } void set_symbolic_identity(Nonnull value) { CARBON_CHECK(!symbolic_identity_.has_value()); symbolic_identity_ = value; } void set_template_value(Nonnull template_value) { CARBON_CHECK(binding_kind_ == BindingKind::Template); template_value_ = template_value; } auto has_template_value() const -> bool { CARBON_CHECK(binding_kind_ == BindingKind::Template); return template_value_.has_value(); } // The impl binding associated with this type variable. auto impl_binding() const -> std::optional> { return impl_binding_; } // Set the impl binding. void set_impl_binding(Nonnull binding) { CARBON_CHECK(!impl_binding_.has_value()); impl_binding_ = binding; } // Return the original generic binding. auto original() const -> Nonnull { if (original_.has_value()) { return *original_; } else { return this; } } // Set the original generic binding. void set_original(Nonnull orig) { original_ = orig; } // Returns whether this binding has been named as a type within its own type // expression via `.Self`. Set by type-checking. auto named_as_type_via_dot_self() const -> bool { return named_as_type_via_dot_self_; } // Set that this binding was named as a type within its own type expression // via `.Self`. May only be called during type-checking. void set_named_as_type_via_dot_self() { named_as_type_via_dot_self_ = true; } private: std::string name_; Nonnull type_; BindingKind binding_kind_; std::optional index_; std::optional> template_value_; std::optional> symbolic_identity_; std::optional> impl_binding_; std::optional> original_; bool named_as_type_via_dot_self_ = false; }; // Converts paren_contents to a Pattern, interpreting the parentheses as // grouping if their contents permit that interpretation, or as forming a // tuple otherwise. auto PatternFromParenContents(Nonnull arena, SourceLocation source_loc, const ParenContents& paren_contents) -> Nonnull; // Converts paren_contents to a TuplePattern, interpreting the parentheses as // forming a tuple. auto TuplePatternFromParenContents(Nonnull arena, SourceLocation source_loc, const ParenContents& paren_contents) -> Nonnull; // Converts `contents` to ParenContents by replacing each Expression // with an ExpressionPattern. auto ParenExpressionToParenPattern(Nonnull arena, const ParenContents& contents) -> ParenContents; // A pattern that matches an alternative of a choice type. class AlternativePattern : public Pattern { public: // Constructs an AlternativePattern that matches the alternative specified // by `alternative`, if its arguments match `arguments`. static auto Create(Nonnull arena, SourceLocation source_loc, Nonnull alternative, Nonnull arguments) -> ErrorOr> { CARBON_ASSIGN_OR_RETURN( Nonnull member_access, RequireSimpleMemberAccess(alternative)); return arena->New(source_loc, &member_access->object(), member_access->member_name(), arguments); } // Constructs an AlternativePattern that matches a value of the type // specified by choice_type if it represents an alternative named // alternative_name, and its arguments match `arguments`. AlternativePattern(SourceLocation source_loc, Nonnull choice_type, std::string alternative_name, Nonnull arguments) : Pattern(AstNodeKind::AlternativePattern, source_loc), choice_type_(choice_type), alternative_name_(std::move(alternative_name)), arguments_(arguments) {} explicit AlternativePattern(CloneContext& context, const AlternativePattern& other) : Pattern(context, other), choice_type_(context.Clone(other.choice_type_)), alternative_name_(other.alternative_name_), arguments_(context.Clone(other.arguments_)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAlternativePattern(node->kind()); } auto choice_type() const -> const Expression& { return *choice_type_; } auto choice_type() -> Expression& { return *choice_type_; } auto alternative_name() const -> const std::string& { return alternative_name_; } auto arguments() const -> const TuplePattern& { return *arguments_; } auto arguments() -> TuplePattern& { return *arguments_; } private: static auto RequireSimpleMemberAccess(Nonnull alternative) -> ErrorOr>; Nonnull choice_type_; std::string alternative_name_; Nonnull arguments_; }; // A pattern that matches a value if it is equal to the value of a given // expression. class ExpressionPattern : public Pattern { public: explicit ExpressionPattern(Nonnull expression) : Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()), expression_(expression) {} explicit ExpressionPattern(CloneContext& context, const ExpressionPattern& other) : Pattern(context, other), expression_(context.Clone(other.expression_)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromExpressionPattern(node->kind()); } auto expression() const -> const Expression& { return *expression_; } auto expression() -> Expression& { return *expression_; } private: Nonnull expression_; }; } // namespace Carbon #endif // CARBON_EXPLORER_AST_PATTERN_H_