// 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 EXECUTABLE_SEMANTICS_AST_DECLARATION_H_ #define EXECUTABLE_SEMANTICS_AST_DECLARATION_H_ #include #include #include #include "common/ostream.h" #include "executable_semantics/ast/member.h" #include "executable_semantics/ast/pattern.h" #include "executable_semantics/ast/source_location.h" #include "executable_semantics/ast/statement.h" #include "executable_semantics/ast/static_scope.h" #include "executable_semantics/ast/value_category.h" #include "executable_semantics/common/nonnull.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Compiler.h" namespace Carbon { // Abstract base class of all AST nodes representing patterns. // // Declaration and its derived classes support LLVM-style RTTI, including // llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every // class derived from Declaration 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 Declaration : public AstNode { public: ~Declaration() override = 0; Declaration(const Member&) = delete; auto operator=(const Member&) -> Declaration& = delete; void Print(llvm::raw_ostream& out) const override; static auto classof(const AstNode* node) -> bool { return InheritsFromDeclaration(node->kind()); } // Returns the enumerator corresponding to the most-derived type of this // object. auto kind() const -> DeclarationKind { return static_cast(root_kind()); } // The static type of the declared entity. Cannot be called before // typechecking. auto static_type() const -> const Value& { return **static_type_; } // Sets the static type of the declared entity. Can only be called once, // during typechecking. void set_static_type(Nonnull type) { static_type_ = type; } // Returns whether the static type 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_static_type() const -> bool { return static_type_.has_value(); } protected: // Constructs a Declaration representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being // constructed. Declaration(AstNodeKind kind, SourceLocation source_loc) : AstNode(kind, source_loc) {} private: std::optional> static_type_; }; // TODO: expand the kinds of things that can be deduced parameters. // For now, only generic parameters are supported. class GenericBinding : public AstNode { public: using ImplementsCarbonNamedEntity = void; GenericBinding(SourceLocation source_loc, std::string name, Nonnull type) : AstNode(AstNodeKind::GenericBinding, source_loc), name_(std::move(name)), type_(type) {} void Print(llvm::raw_ostream& out) const override; 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_; } // The static type of the binding. Cannot be called before typechecking. auto static_type() const -> const Value& { return **static_type_; } // Sets the static type of the binding. Can only be called once, during // typechecking. void set_static_type(Nonnull type) { static_type_ = type; } // Returns whether the static type 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_static_type() const -> bool { return static_type_.has_value(); } auto value_category() const -> ValueCategory { return ValueCategory::Let; } auto constant_value() const -> std::optional> { return constant_value_; } // Sets the value returned by constant_value(). Can only be called once, // during typechecking. void set_constant_value(Nonnull value) { CHECK(!constant_value_.has_value()); constant_value_ = value; } private: std::string name_; Nonnull type_; std::optional> static_type_; std::optional> constant_value_; }; // The syntactic representation of a function declaration's return type. // This syntax can take one of three forms: // - An _explicit_ term consists of `->` followed by a type expression. // - An _auto_ term consists of `-> auto`. // - An _omitted_ term consists of no tokens at all. // Each of these forms has a corresponding factory function. class ReturnTerm { public: ReturnTerm(const ReturnTerm&) = default; auto operator=(const ReturnTerm&) -> ReturnTerm& = default; // Represents an omitted return term at `source_loc`. static auto Omitted(SourceLocation source_loc) -> ReturnTerm { return ReturnTerm(ReturnKind::Omitted, source_loc); } // Represents an auto return term at `source_loc`. static auto Auto(SourceLocation source_loc) -> ReturnTerm { return ReturnTerm(ReturnKind::Auto, source_loc); } // Represents an explicit return term with the given type expression. static auto Explicit(Nonnull type_expression) -> ReturnTerm { return ReturnTerm(type_expression); } // Returns true if this represents an omitted return term. auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; } // Returns true if this represents an auto return term. auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; } // If this represents an explicit return term, returns the type expression. // Otherwise, returns nullopt. auto type_expression() const -> std::optional> { return type_expression_; } auto type_expression() -> std::optional> { return type_expression_; } // The static return type this term resolves to. Cannot be called before // typechecking. auto static_type() const -> const Value& { return **static_type_; } // Sets the value of static_type(). Can only be called once, during // typechecking. void set_static_type(Nonnull type) { static_type_ = type; } // Returns whether static_type() 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_static_type() const -> bool { return static_type_.has_value(); } auto source_loc() const -> SourceLocation { return source_loc_; } void Print(llvm::raw_ostream& out) const; LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } private: enum class ReturnKind { Omitted, Auto, Expression }; explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc) : kind_(kind), source_loc_(source_loc) { CHECK(kind != ReturnKind::Expression); } explicit ReturnTerm(Nonnull type_expression) : kind_(ReturnKind::Expression), type_expression_(type_expression), source_loc_(type_expression->source_loc()) {} ReturnKind kind_; std::optional> type_expression_; std::optional> static_type_; SourceLocation source_loc_; }; class FunctionDeclaration : public Declaration { public: using ImplementsCarbonNamedEntity = void; FunctionDeclaration(SourceLocation source_loc, std::string name, std::vector> deduced_params, Nonnull param_pattern, ReturnTerm return_term, std::optional> body) : Declaration(AstNodeKind::FunctionDeclaration, source_loc), name_(std::move(name)), deduced_parameters_(std::move(deduced_params)), param_pattern_(param_pattern), return_term_(return_term), body_(body) {} static auto classof(const AstNode* node) -> bool { return InheritsFromFunctionDeclaration(node->kind()); } void PrintDepth(int depth, llvm::raw_ostream& out) const; auto name() const -> const std::string& { return name_; } auto deduced_parameters() const -> llvm::ArrayRef> { return deduced_parameters_; } auto deduced_parameters() -> llvm::ArrayRef> { return deduced_parameters_; } auto param_pattern() const -> const TuplePattern& { return *param_pattern_; } auto param_pattern() -> TuplePattern& { return *param_pattern_; } auto return_term() const -> const ReturnTerm& { return return_term_; } auto return_term() -> ReturnTerm& { return return_term_; } auto body() const -> std::optional> { return body_; } auto body() -> std::optional> { return body_; } auto value_category() const -> ValueCategory { return ValueCategory::Let; } auto constant_value() const -> std::optional> { return constant_value_; } // Sets the value returned by constant_value(). Can only be called once, // during typechecking. void set_constant_value(Nonnull value) { CHECK(!constant_value_.has_value()); constant_value_ = value; } private: std::string name_; std::vector> deduced_parameters_; Nonnull param_pattern_; ReturnTerm return_term_; std::optional> body_; std::optional> constant_value_; }; class ClassDeclaration : public Declaration { public: using ImplementsCarbonNamedEntity = void; ClassDeclaration(SourceLocation source_loc, std::string name, std::vector> members) : Declaration(AstNodeKind::ClassDeclaration, source_loc), name_(std::move(name)), members_(std::move(members)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromClassDeclaration(node->kind()); } auto name() const -> const std::string& { return name_; } auto members() const -> llvm::ArrayRef> { return members_; } auto value_category() const -> ValueCategory { return ValueCategory::Let; } auto constant_value() const -> std::optional> { return constant_value_; } // Sets the value returned by constant_value(). Can only be called once, // during typechecking. void set_constant_value(Nonnull value) { CHECK(!constant_value_.has_value()); constant_value_ = value; } private: std::string name_; std::vector> members_; std::optional> constant_value_; }; class AlternativeSignature : public AstNode { public: AlternativeSignature(SourceLocation source_loc, std::string name, Nonnull signature) : AstNode(AstNodeKind::AlternativeSignature, source_loc), name_(std::move(name)), signature_(signature) {} void Print(llvm::raw_ostream& out) const override; static auto classof(const AstNode* node) -> bool { return InheritsFromAlternativeSignature(node->kind()); } auto name() const -> const std::string& { return name_; } auto signature() const -> const Expression& { return *signature_; } auto signature() -> Expression& { return *signature_; } private: std::string name_; Nonnull signature_; }; class ChoiceDeclaration : public Declaration { public: using ImplementsCarbonNamedEntity = void; ChoiceDeclaration(SourceLocation source_loc, std::string name, std::vector> alternatives) : Declaration(AstNodeKind::ChoiceDeclaration, source_loc), name_(std::move(name)), alternatives_(std::move(alternatives)) {} static auto classof(const AstNode* node) -> bool { return InheritsFromChoiceDeclaration(node->kind()); } auto name() const -> const std::string& { return name_; } auto alternatives() const -> llvm::ArrayRef> { return alternatives_; } auto alternatives() -> llvm::ArrayRef> { return alternatives_; } auto value_category() const -> ValueCategory { return ValueCategory::Let; } auto constant_value() const -> std::optional> { return constant_value_; } // Sets the value returned by constant_value(). Can only be called once, // during typechecking. void set_constant_value(Nonnull value) { CHECK(!constant_value_.has_value()); constant_value_ = value; } private: std::string name_; std::vector> alternatives_; std::optional> constant_value_; }; // Global variable definition implements the Declaration concept. class VariableDeclaration : public Declaration { public: VariableDeclaration(SourceLocation source_loc, Nonnull binding, Nonnull initializer) : Declaration(AstNodeKind::VariableDeclaration, source_loc), binding_(binding), initializer_(initializer) {} static auto classof(const AstNode* node) -> bool { return InheritsFromVariableDeclaration(node->kind()); } auto binding() const -> const BindingPattern& { return *binding_; } auto binding() -> BindingPattern& { return *binding_; } auto initializer() const -> const Expression& { return *initializer_; } auto initializer() -> Expression& { return *initializer_; } private: // TODO: split this into a non-optional name and a type, initialized by // a constructor that takes a BindingPattern and handles errors like a // missing name. Nonnull binding_; Nonnull initializer_; }; } // namespace Carbon #endif // EXECUTABLE_SEMANTICS_AST_DECLARATION_H_