diff --git a/executable_semantics/ast/BUILD b/executable_semantics/ast/BUILD index 31482625f19a..b5c0c91fb1f6 100644 --- a/executable_semantics/ast/BUILD +++ b/executable_semantics/ast/BUILD @@ -93,6 +93,7 @@ cc_library( deps = [ ":ast_node", ":paren_contents", + ":static_scope", "//common:indirect_value", "//common:ostream", "//executable_semantics/common:arena", diff --git a/executable_semantics/ast/ast_node.h b/executable_semantics/ast/ast_node.h index f26f82dcb67e..1757b8b341a6 100644 --- a/executable_semantics/ast/ast_node.h +++ b/executable_semantics/ast/ast_node.h @@ -35,8 +35,6 @@ namespace Carbon { // The definitions of `InheritsFromFoo` and `FooKind` are generated from // ast_rtti.txt, and are implicitly provided by this header. // -// When inheriting from this class, the inheritance must me marked `virtual`. -// // TODO: To support generic traversal, add children() method, and ensure that // all AstNodes are reachable from a root AstNode. class AstNode { @@ -75,40 +73,4 @@ class AstNode { } // namespace Carbon -// Ensure that LLVM casts from AstNode use dynamic_cast, because static_cast -// doesn't work with a virtual base class. -namespace llvm { -template -struct cast_convert_val { - using ResultType = typename cast_retty::ret_type; - static auto doit(const Carbon::AstNode* node) -> ResultType { - return dynamic_cast(node); - } -}; - -template -struct cast_convert_val { - using ResultType = typename cast_retty::ret_type; - static auto doit(Carbon::AstNode* node) -> ResultType { - return dynamic_cast(node); - } -}; - -template -struct cast_convert_val { - using ResultType = typename cast_retty::ret_type; - static auto doit(const Carbon::AstNode& node) -> ResultType { - return dynamic_cast(node); - } -}; - -template -struct cast_convert_val { - using ResultType = typename cast_retty::ret_type; - static auto doit(Carbon::AstNode& node) -> ResultType { - return dynamic_cast(node); - } -}; -} // namespace llvm - #endif // EXECUTABLE_SEMANTICS_AST_AST_NODE_H_ diff --git a/executable_semantics/ast/ast_rtti.txt b/executable_semantics/ast/ast_rtti.txt index 8604dd0dacad..4782f766cc26 100644 --- a/executable_semantics/ast/ast_rtti.txt +++ b/executable_semantics/ast/ast_rtti.txt @@ -3,19 +3,18 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception root class AstNode; -interface class NamedEntity : AstNode; abstract class Pattern : AstNode; class AutoPattern : Pattern; - class BindingPattern : Pattern, NamedEntity; + class BindingPattern : Pattern; class TuplePattern : Pattern; class AlternativePattern : Pattern; class ExpressionPattern : Pattern; abstract class Declaration : AstNode; - class FunctionDeclaration : Declaration, NamedEntity; - class ClassDeclaration : Declaration, NamedEntity; - class ChoiceDeclaration : Declaration, NamedEntity; + class FunctionDeclaration : Declaration; + class ClassDeclaration : Declaration; + class ChoiceDeclaration : Declaration; class VariableDeclaration : Declaration; -class GenericBinding : AstNode, NamedEntity; +class GenericBinding : AstNode; class AlternativeSignature : AstNode; abstract class Statement : AstNode; class ExpressionStatement : Statement; @@ -28,7 +27,7 @@ abstract class Statement : AstNode; class Break : Statement; class Continue : Statement; class Match : Statement; - class Continuation : Statement, NamedEntity; + class Continuation : Statement; class Run : Statement; class Await : Statement; abstract class Expression : AstNode; diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index 259f48904b45..a0c6e19aab92 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -29,7 +29,7 @@ namespace Carbon { // every concrete derived class must have a corresponding enumerator // in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for // details. -class Declaration : public virtual AstNode { +class Declaration : public AstNode { public: ~Declaration() override = 0; @@ -65,7 +65,8 @@ class Declaration : public virtual AstNode { // Constructs a Declaration representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being // constructed. - Declaration() = default; + Declaration(AstNodeKind kind, SourceLocation source_loc) + : AstNode(kind, source_loc) {} private: std::optional> static_type_; @@ -73,8 +74,10 @@ class Declaration : public virtual AstNode { // TODO: expand the kinds of things that can be deduced parameters. // For now, only generic parameters are supported. -struct GenericBinding : public virtual AstNode, public NamedEntity { +class GenericBinding : public AstNode { public: + using ImplementsCarbonNamedEntity = void; + GenericBinding(SourceLocation source_loc, std::string name, Nonnull type) : AstNode(AstNodeKind::GenericBinding, source_loc), @@ -188,14 +191,16 @@ class ReturnTerm { SourceLocation source_loc_; }; -class FunctionDeclaration : public Declaration, public NamedEntity { +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) - : AstNode(AstNodeKind::FunctionDeclaration, source_loc), + : Declaration(AstNodeKind::FunctionDeclaration, source_loc), name_(std::move(name)), deduced_parameters_(std::move(deduced_params)), param_pattern_(param_pattern), @@ -231,11 +236,13 @@ class FunctionDeclaration : public Declaration, public NamedEntity { std::optional> body_; }; -class ClassDeclaration : public Declaration, public NamedEntity { +class ClassDeclaration : public Declaration { public: + using ImplementsCarbonNamedEntity = void; + ClassDeclaration(SourceLocation source_loc, std::string name, std::vector> members) - : AstNode(AstNodeKind::ClassDeclaration, source_loc), + : Declaration(AstNodeKind::ClassDeclaration, source_loc), name_(std::move(name)), members_(std::move(members)) {} @@ -251,7 +258,7 @@ class ClassDeclaration : public Declaration, public NamedEntity { std::vector> members_; }; -class AlternativeSignature : public virtual AstNode { +class AlternativeSignature : public AstNode { public: AlternativeSignature(SourceLocation source_loc, std::string name, Nonnull signature) @@ -274,11 +281,13 @@ class AlternativeSignature : public virtual AstNode { Nonnull signature_; }; -class ChoiceDeclaration : public Declaration, public NamedEntity { +class ChoiceDeclaration : public Declaration { public: + using ImplementsCarbonNamedEntity = void; + ChoiceDeclaration(SourceLocation source_loc, std::string name, std::vector> alternatives) - : AstNode(AstNodeKind::ChoiceDeclaration, source_loc), + : Declaration(AstNodeKind::ChoiceDeclaration, source_loc), name_(std::move(name)), alternatives_(std::move(alternatives)) {} @@ -306,7 +315,7 @@ class VariableDeclaration : public Declaration { VariableDeclaration(SourceLocation source_loc, Nonnull binding, Nonnull initializer) - : AstNode(AstNodeKind::VariableDeclaration, source_loc), + : Declaration(AstNodeKind::VariableDeclaration, source_loc), binding_(binding), initializer_(initializer) {} diff --git a/executable_semantics/ast/expression.h b/executable_semantics/ast/expression.h index b4c4bd59eed1..1a4fdc329b40 100644 --- a/executable_semantics/ast/expression.h +++ b/executable_semantics/ast/expression.h @@ -14,6 +14,7 @@ #include "executable_semantics/ast/ast_node.h" #include "executable_semantics/ast/paren_contents.h" #include "executable_semantics/ast/source_location.h" +#include "executable_semantics/ast/static_scope.h" #include "executable_semantics/common/arena.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Compiler.h" @@ -21,9 +22,8 @@ namespace Carbon { class Value; -class NamedEntity; -class Expression : public virtual AstNode { +class Expression : public AstNode { public: // The value category of a Carbon expression indicates whether it evaluates // to a variable or a value. A variable can be mutated, and can have its @@ -76,7 +76,8 @@ class Expression : public virtual AstNode { // Constructs an Expression representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being // constructed. - Expression() = default; + Expression(AstNodeKind kind, SourceLocation source_loc) + : AstNode(kind, source_loc) {} private: std::optional> static_type_; @@ -121,7 +122,7 @@ auto ToString(Operator op) -> std::string_view; class IdentifierExpression : public Expression { public: explicit IdentifierExpression(SourceLocation source_loc, std::string name) - : AstNode(AstNodeKind::IdentifierExpression, source_loc), + : Expression(AstNodeKind::IdentifierExpression, source_loc), name_(std::move(name)) {} static auto classof(const AstNode* node) -> bool { @@ -130,15 +131,15 @@ class IdentifierExpression : public Expression { auto name() const -> const std::string& { return name_; } - // Returns the NamedEntity this identifier refers to. Cannot be called before - // name resolution. - auto named_entity() const -> const NamedEntity& { return **named_entity_; } + // Returns the NamedEntityView this identifier refers to. Cannot be called + // before name resolution. + auto named_entity() const -> const NamedEntityView& { return *named_entity_; } // Sets the value returned by named_entity. Can be called only once, // during name resolution. - void set_named_entity(Nonnull named_entity) { + void set_named_entity(NamedEntityView named_entity) { CHECK(!named_entity_.has_value()); - named_entity_ = named_entity; + named_entity_ = std::move(named_entity); } // Returns true if set_named_entity has been called. Should be used only @@ -148,7 +149,7 @@ class IdentifierExpression : public Expression { private: std::string name_; - std::optional> named_entity_; + std::optional named_entity_; }; class FieldAccessExpression : public Expression { @@ -156,7 +157,7 @@ class FieldAccessExpression : public Expression { explicit FieldAccessExpression(SourceLocation source_loc, Nonnull aggregate, std::string field) - : AstNode(AstNodeKind::FieldAccessExpression, source_loc), + : Expression(AstNodeKind::FieldAccessExpression, source_loc), aggregate_(aggregate), field_(std::move(field)) {} @@ -178,7 +179,7 @@ class IndexExpression : public Expression { explicit IndexExpression(SourceLocation source_loc, Nonnull aggregate, Nonnull offset) - : AstNode(AstNodeKind::IndexExpression, source_loc), + : Expression(AstNodeKind::IndexExpression, source_loc), aggregate_(aggregate), offset_(offset) {} @@ -199,7 +200,7 @@ class IndexExpression : public Expression { class IntLiteral : public Expression { public: explicit IntLiteral(SourceLocation source_loc, int value) - : AstNode(AstNodeKind::IntLiteral, source_loc), value_(value) {} + : Expression(AstNodeKind::IntLiteral, source_loc), value_(value) {} static auto classof(const AstNode* node) -> bool { return InheritsFromIntLiteral(node->kind()); @@ -214,7 +215,7 @@ class IntLiteral : public Expression { class BoolLiteral : public Expression { public: explicit BoolLiteral(SourceLocation source_loc, bool value) - : AstNode(AstNodeKind::BoolLiteral, source_loc), value_(value) {} + : Expression(AstNodeKind::BoolLiteral, source_loc), value_(value) {} static auto classof(const AstNode* node) -> bool { return InheritsFromBoolLiteral(node->kind()); @@ -229,7 +230,7 @@ class BoolLiteral : public Expression { class StringLiteral : public Expression { public: explicit StringLiteral(SourceLocation source_loc, std::string value) - : AstNode(AstNodeKind::StringLiteral, source_loc), + : Expression(AstNodeKind::StringLiteral, source_loc), value_(std::move(value)) {} static auto classof(const AstNode* node) -> bool { @@ -245,7 +246,7 @@ class StringLiteral : public Expression { class StringTypeLiteral : public Expression { public: explicit StringTypeLiteral(SourceLocation source_loc) - : AstNode(AstNodeKind::StringTypeLiteral, source_loc) {} + : Expression(AstNodeKind::StringTypeLiteral, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromStringTypeLiteral(node->kind()); @@ -259,7 +260,7 @@ class TupleLiteral : public Expression { explicit TupleLiteral(SourceLocation source_loc, std::vector> fields) - : AstNode(AstNodeKind::TupleLiteral, source_loc), + : Expression(AstNodeKind::TupleLiteral, source_loc), fields_(std::move(fields)) {} static auto classof(const AstNode* node) -> bool { @@ -285,7 +286,8 @@ class StructLiteral : public Expression { public: explicit StructLiteral(SourceLocation loc, std::vector fields) - : AstNode(AstNodeKind::StructLiteral, loc), fields_(std::move(fields)) { + : Expression(AstNodeKind::StructLiteral, loc), + fields_(std::move(fields)) { CHECK(!fields_.empty()) << "`{}` is represented as a StructTypeLiteral, not a StructLiteral."; } @@ -311,7 +313,7 @@ class StructTypeLiteral : public Expression { explicit StructTypeLiteral(SourceLocation loc, std::vector fields) - : AstNode(AstNodeKind::StructTypeLiteral, loc), + : Expression(AstNodeKind::StructTypeLiteral, loc), fields_(std::move(fields)) {} static auto classof(const AstNode* node) -> bool { @@ -330,7 +332,7 @@ class PrimitiveOperatorExpression : public Expression { explicit PrimitiveOperatorExpression( SourceLocation source_loc, Operator op, std::vector> arguments) - : AstNode(AstNodeKind::PrimitiveOperatorExpression, source_loc), + : Expression(AstNodeKind::PrimitiveOperatorExpression, source_loc), op_(op), arguments_(std::move(arguments)) {} @@ -356,7 +358,7 @@ class CallExpression : public Expression { explicit CallExpression(SourceLocation source_loc, Nonnull function, Nonnull argument) - : AstNode(AstNodeKind::CallExpression, source_loc), + : Expression(AstNodeKind::CallExpression, source_loc), function_(function), argument_(argument) {} @@ -379,7 +381,7 @@ class FunctionTypeLiteral : public Expression { explicit FunctionTypeLiteral(SourceLocation source_loc, Nonnull parameter, Nonnull return_type) - : AstNode(AstNodeKind::FunctionTypeLiteral, source_loc), + : Expression(AstNodeKind::FunctionTypeLiteral, source_loc), parameter_(parameter), return_type_(return_type) {} @@ -400,7 +402,7 @@ class FunctionTypeLiteral : public Expression { class BoolTypeLiteral : public Expression { public: explicit BoolTypeLiteral(SourceLocation source_loc) - : AstNode(AstNodeKind::BoolTypeLiteral, source_loc) {} + : Expression(AstNodeKind::BoolTypeLiteral, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromBoolTypeLiteral(node->kind()); @@ -410,7 +412,7 @@ class BoolTypeLiteral : public Expression { class IntTypeLiteral : public Expression { public: explicit IntTypeLiteral(SourceLocation source_loc) - : AstNode(AstNodeKind::IntTypeLiteral, source_loc) {} + : Expression(AstNodeKind::IntTypeLiteral, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromIntTypeLiteral(node->kind()); @@ -420,7 +422,7 @@ class IntTypeLiteral : public Expression { class ContinuationTypeLiteral : public Expression { public: explicit ContinuationTypeLiteral(SourceLocation source_loc) - : AstNode(AstNodeKind::ContinuationTypeLiteral, source_loc) {} + : Expression(AstNodeKind::ContinuationTypeLiteral, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromContinuationTypeLiteral(node->kind()); @@ -430,7 +432,7 @@ class ContinuationTypeLiteral : public Expression { class TypeTypeLiteral : public Expression { public: explicit TypeTypeLiteral(SourceLocation source_loc) - : AstNode(AstNodeKind::TypeTypeLiteral, source_loc) {} + : Expression(AstNodeKind::TypeTypeLiteral, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromTypeTypeLiteral(node->kind()); @@ -446,7 +448,7 @@ class IntrinsicExpression : public Expression { explicit IntrinsicExpression(std::string_view intrinsic_name, Nonnull args, SourceLocation source_loc) - : AstNode(AstNodeKind::IntrinsicExpression, source_loc), + : Expression(AstNodeKind::IntrinsicExpression, source_loc), intrinsic_(FindIntrinsic(intrinsic_name, source_loc)), args_(args) {} @@ -480,7 +482,7 @@ class UnimplementedExpression : public Expression { template UnimplementedExpression(SourceLocation source_loc, std::string label, Children... children) - : AstNode(AstNodeKind::UnimplementedExpression, source_loc), + : Expression(AstNodeKind::UnimplementedExpression, source_loc), label_(std::move(label)) { AddChildren(children...); } diff --git a/executable_semantics/ast/member.h b/executable_semantics/ast/member.h index c9f8e6a54028..9053cc91df51 100644 --- a/executable_semantics/ast/member.h +++ b/executable_semantics/ast/member.h @@ -23,7 +23,7 @@ namespace Carbon { // every concrete derived class must have a corresponding enumerator // in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for // details. -class Member : public virtual AstNode { +class Member : public AstNode { public: ~Member() override = 0; @@ -43,13 +43,14 @@ class Member : public virtual AstNode { } protected: - Member() = default; + Member(AstNodeKind kind, SourceLocation source_loc) + : AstNode(kind, source_loc) {} }; class FieldMember : public Member { public: FieldMember(SourceLocation source_loc, Nonnull binding) - : AstNode(AstNodeKind::FieldMember, source_loc), binding_(binding) {} + : Member(AstNodeKind::FieldMember, source_loc), binding_(binding) {} static auto classof(const AstNode* node) -> bool { return InheritsFromFieldMember(node->kind()); diff --git a/executable_semantics/ast/pattern.cpp b/executable_semantics/ast/pattern.cpp index 6e5495443efe..fd62f16487b6 100644 --- a/executable_semantics/ast/pattern.cpp +++ b/executable_semantics/ast/pattern.cpp @@ -89,7 +89,7 @@ static auto RequireFieldAccess(Nonnull alternative) AlternativePattern::AlternativePattern(SourceLocation source_loc, Nonnull alternative, Nonnull arguments) - : AstNode(AstNodeKind::AlternativePattern, source_loc), + : Pattern(AstNodeKind::AlternativePattern, source_loc), choice_type_(&RequireFieldAccess(alternative).aggregate()), alternative_name_(RequireFieldAccess(alternative).field()), arguments_(arguments) {} diff --git a/executable_semantics/ast/pattern.h b/executable_semantics/ast/pattern.h index 7ef8d8d9a6ba..68c383adef00 100644 --- a/executable_semantics/ast/pattern.h +++ b/executable_semantics/ast/pattern.h @@ -29,7 +29,7 @@ class Value; // every concrete derived class must have a corresponding enumerator // in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for // details. -class Pattern : public virtual AstNode { +class Pattern : public AstNode { public: Pattern(const Pattern&) = delete; auto operator=(const Pattern&) -> Pattern& = delete; @@ -76,7 +76,8 @@ class Pattern : public virtual AstNode { // Constructs a Pattern representing syntax at the given line number. // `kind` must be the enumerator corresponding to the most-derived type being // constructed. - Pattern() = default; + Pattern(AstNodeKind kind, SourceLocation source_loc) + : AstNode(kind, source_loc) {} private: std::optional> static_type_; @@ -87,7 +88,7 @@ class Pattern : public virtual AstNode { class AutoPattern : public Pattern { public: explicit AutoPattern(SourceLocation source_loc) - : AstNode(AstNodeKind::AutoPattern, source_loc) {} + : Pattern(AstNodeKind::AutoPattern, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAutoPattern(node->kind()); @@ -96,11 +97,13 @@ class AutoPattern : public Pattern { // A pattern that matches a value of a specified type, and optionally binds // a name to it. -class BindingPattern : public Pattern, public NamedEntity { +class BindingPattern : public Pattern { public: + using ImplementsCarbonNamedEntity = void; + BindingPattern(SourceLocation source_loc, std::optional name, Nonnull type) - : AstNode(AstNodeKind::BindingPattern, source_loc), + : Pattern(AstNodeKind::BindingPattern, source_loc), name_(std::move(name)), type_(type) {} @@ -124,7 +127,7 @@ class BindingPattern : public Pattern, public NamedEntity { class TuplePattern : public Pattern { public: TuplePattern(SourceLocation source_loc, std::vector> fields) - : AstNode(AstNodeKind::TuplePattern, source_loc), + : Pattern(AstNodeKind::TuplePattern, source_loc), fields_(std::move(fields)) {} static auto classof(const AstNode* node) -> bool { @@ -170,7 +173,7 @@ class AlternativePattern : public Pattern { Nonnull choice_type, std::string alternative_name, Nonnull arguments) - : AstNode(AstNodeKind::AlternativePattern, source_loc), + : Pattern(AstNodeKind::AlternativePattern, source_loc), choice_type_(choice_type), alternative_name_(std::move(alternative_name)), arguments_(arguments) {} @@ -204,7 +207,7 @@ class AlternativePattern : public Pattern { class ExpressionPattern : public Pattern { public: explicit ExpressionPattern(Nonnull expression) - : AstNode(AstNodeKind::ExpressionPattern, expression->source_loc()), + : Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()), expression_(expression) {} static auto classof(const AstNode* node) -> bool { diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index ae5e5fc0f38f..c1f1b72e475a 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -21,7 +21,7 @@ namespace Carbon { class FunctionDeclaration; -class Statement : public virtual AstNode { +class Statement : public AstNode { public: ~Statement() override = 0; @@ -39,13 +39,14 @@ class Statement : public virtual AstNode { } protected: - Statement() = default; + Statement(AstNodeKind kind, SourceLocation source_loc) + : AstNode(kind, source_loc) {} }; class Block : public Statement { public: Block(SourceLocation source_loc, std::vector> statements) - : AstNode(AstNodeKind::Block, source_loc), + : Statement(AstNodeKind::Block, source_loc), statements_(std::move(statements)) {} static auto classof(const AstNode* node) -> bool { @@ -67,7 +68,7 @@ class ExpressionStatement : public Statement { public: ExpressionStatement(SourceLocation source_loc, Nonnull expression) - : AstNode(AstNodeKind::ExpressionStatement, source_loc), + : Statement(AstNodeKind::ExpressionStatement, source_loc), expression_(expression) {} static auto classof(const AstNode* node) -> bool { @@ -85,7 +86,7 @@ class Assign : public Statement { public: Assign(SourceLocation source_loc, Nonnull lhs, Nonnull rhs) - : AstNode(AstNodeKind::Assign, source_loc), lhs_(lhs), rhs_(rhs) {} + : Statement(AstNodeKind::Assign, source_loc), lhs_(lhs), rhs_(rhs) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAssign(node->kind()); @@ -105,7 +106,7 @@ class VariableDefinition : public Statement { public: VariableDefinition(SourceLocation source_loc, Nonnull pattern, Nonnull init) - : AstNode(AstNodeKind::VariableDefinition, source_loc), + : Statement(AstNodeKind::VariableDefinition, source_loc), pattern_(pattern), init_(init) {} @@ -127,7 +128,7 @@ class If : public Statement { public: If(SourceLocation source_loc, Nonnull condition, Nonnull then_block, std::optional> else_block) - : AstNode(AstNodeKind::If, source_loc), + : Statement(AstNodeKind::If, source_loc), condition_(condition), then_block_(then_block), else_block_(else_block) {} @@ -157,7 +158,7 @@ class Return : public Statement { : Return(source_loc, arena->New(source_loc), true) {} Return(SourceLocation source_loc, Nonnull expression, bool is_omitted_expression) - : AstNode(AstNodeKind::Return, source_loc), + : Statement(AstNodeKind::Return, source_loc), expression_(expression), is_omitted_expression_(is_omitted_expression) {} @@ -194,7 +195,7 @@ class While : public Statement { public: While(SourceLocation source_loc, Nonnull condition, Nonnull body) - : AstNode(AstNodeKind::While, source_loc), + : Statement(AstNodeKind::While, source_loc), condition_(condition), body_(body) {} @@ -215,7 +216,7 @@ class While : public Statement { class Break : public Statement { public: explicit Break(SourceLocation source_loc) - : AstNode(AstNodeKind::Break, source_loc) {} + : Statement(AstNodeKind::Break, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromBreak(node->kind()); @@ -242,7 +243,7 @@ class Break : public Statement { class Continue : public Statement { public: explicit Continue(SourceLocation source_loc) - : AstNode(AstNodeKind::Continue, source_loc) {} + : Statement(AstNodeKind::Continue, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromContinue(node->kind()); @@ -285,7 +286,7 @@ class Match : public Statement { Match(SourceLocation source_loc, Nonnull expression, std::vector clauses) - : AstNode(AstNodeKind::Match, source_loc), + : Statement(AstNodeKind::Match, source_loc), expression_(expression), clauses_(std::move(clauses)) {} @@ -308,11 +309,13 @@ class Match : public Statement { // __continuation { // // } -class Continuation : public Statement, public NamedEntity { +class Continuation : public Statement { public: + using ImplementsCarbonNamedEntity = void; + Continuation(SourceLocation source_loc, std::string continuation_variable, Nonnull body) - : AstNode(AstNodeKind::Continuation, source_loc), + : Statement(AstNodeKind::Continuation, source_loc), continuation_variable_(std::move(continuation_variable)), body_(body) {} @@ -353,7 +356,7 @@ class Continuation : public Statement, public NamedEntity { class Run : public Statement { public: Run(SourceLocation source_loc, Nonnull argument) - : AstNode(AstNodeKind::Run, source_loc), argument_(argument) {} + : Statement(AstNodeKind::Run, source_loc), argument_(argument) {} static auto classof(const AstNode* node) -> bool { return InheritsFromRun(node->kind()); @@ -372,7 +375,7 @@ class Run : public Statement { class Await : public Statement { public: explicit Await(SourceLocation source_loc) - : AstNode(AstNodeKind::Await, source_loc) {} + : Statement(AstNodeKind::Await, source_loc) {} static auto classof(const AstNode* node) -> bool { return InheritsFromAwait(node->kind()); diff --git a/executable_semantics/ast/static_scope.cpp b/executable_semantics/ast/static_scope.cpp index 9db5ba1f76d0..28cbfbdae644 100644 --- a/executable_semantics/ast/static_scope.cpp +++ b/executable_semantics/ast/static_scope.cpp @@ -8,22 +8,18 @@ namespace Carbon { -NamedEntity::~NamedEntity() = default; - -void StaticScope::Add(std::string name, Nonnull entity) { +void StaticScope::Add(std::string name, NamedEntityView entity) { auto [it, success] = declared_names_.insert({name, entity}); if (!success && it->second != entity) { - FATAL_COMPILATION_ERROR(entity->source_loc()) + FATAL_COMPILATION_ERROR(entity.base().source_loc()) << "Duplicate name `" << name << "` also found at " - << it->second->source_loc(); + << it->second.base().source_loc(); } } auto StaticScope::Resolve(const std::string& name, - SourceLocation source_loc) const - -> Nonnull { - std::optional> result = - TryResolve(name, source_loc); + SourceLocation source_loc) const -> NamedEntityView { + std::optional result = TryResolve(name, source_loc); if (!result.has_value()) { FATAL_COMPILATION_ERROR(source_loc) << "could not resolve '" << name << "'"; } @@ -32,19 +28,20 @@ auto StaticScope::Resolve(const std::string& name, auto StaticScope::TryResolve(const std::string& name, SourceLocation source_loc) const - -> std::optional> { + -> std::optional { auto it = declared_names_.find(name); if (it != declared_names_.end()) { return it->second; } - std::optional> result; + std::optional result; for (Nonnull parent : parent_scopes_) { auto parent_result = parent->TryResolve(name, source_loc); if (parent_result.has_value() && result.has_value() && *parent_result != *result) { FATAL_COMPILATION_ERROR(source_loc) - << "'" << name << "' is ambiguous between " << (*result)->source_loc() - << " and " << (*parent_result)->source_loc(); + << "'" << name << "' is ambiguous between " + << result->base().source_loc() << " and " + << parent_result->base().source_loc(); } result = parent_result; } diff --git a/executable_semantics/ast/static_scope.h b/executable_semantics/ast/static_scope.h index 791d5b906b7b..24fde837a85d 100644 --- a/executable_semantics/ast/static_scope.h +++ b/executable_semantics/ast/static_scope.h @@ -5,6 +5,7 @@ #ifndef EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_ #define EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_ +#include #include #include #include @@ -16,16 +17,61 @@ namespace Carbon { -class NamedEntity : public virtual AstNode { +class Value; + +// True if NodeType::ImplementsCarbonNamedEntity is valid and names a type, +// indicating that NodeType implements the NamedEntity interface. This imposes +// the following requirements on NodeType, where `node` is a const instance of +// NodeType: +// +// - NodeType is derived from AstNode. +// - node.static_type() is well-formed and has type const Value&. +template +static constexpr bool ImplementsNamedEntity = false; + +template +static constexpr bool + ImplementsNamedEntity = true; + +// Non-owning type-erased wrapper around a const NodeType* `node`, where +// NodeType implements the NamedEntity interface. +class NamedEntityView { public: - ~NamedEntity() override = 0; + template >> + // NOLINTNEXTLINE(google-explicit-constructor) + NamedEntityView(Nonnull node) + // Type-erase NodeType, retaining a pointer to the base class AstNode + // and using std::function to encapsulate the ability to call + // the derived class's static_type. + : base_(node), static_type_([](const AstNode& base) -> const Value& { + return llvm::cast(base).static_type(); + }) {} - NamedEntity() = default; + NamedEntityView(const NamedEntityView&) = default; + NamedEntityView(NamedEntityView&&) = default; + auto operator=(const NamedEntityView&) -> NamedEntityView& = default; + auto operator=(NamedEntityView&&) -> NamedEntityView& = default; - // TODO: This is unused, but is intended for casts after lookup. - auto kind() const -> NamedEntityKind { - return static_cast(root_kind()); + // Returns `node` as an instance of the base class AstNode. + auto base() const -> const AstNode& { return *base_; } + + // Returns node->static_type() + auto static_type() const -> const Value& { return static_type_(*base_); } + + friend auto operator==(const NamedEntityView& lhs, + const NamedEntityView& rhs) { + return lhs.base_ == rhs.base_; } + + friend auto operator!=(const NamedEntityView& lhs, + const NamedEntityView& rhs) { + return lhs.base_ != rhs.base_; + } + + private: + Nonnull base_; + std::function static_type_; }; // Maps the names visible in a given scope to the entities they name. @@ -35,7 +81,7 @@ class StaticScope { public: // Defines `name` to be `entity` in this scope, or reports a compilation error // if `name` is already defined to be a different entity in this scope. - void Add(std::string name, Nonnull entity); + void Add(std::string name, NamedEntityView entity); // Make `parent` a parent of this scope. // REQUIRES: `parent` is not already a parent of this scope. @@ -47,17 +93,17 @@ class StaticScope { // scope, or reports a compilation error at `source_loc` there isn't exactly // one such definition. auto Resolve(const std::string& name, SourceLocation source_loc) const - -> Nonnull; + -> NamedEntityView; private: // Equivalent to Resolve, but returns `nullopt` instead of raising an error // if no definition can be found. Still raises a compilation error if more // than one definition is found. auto TryResolve(const std::string& name, SourceLocation source_loc) const - -> std::optional>; + -> std::optional; // Maps locally declared names to their entities. - std::unordered_map> declared_names_; + std::unordered_map declared_names_; // A list of scopes used for name lookup within this scope. std::vector> parent_scopes_; diff --git a/executable_semantics/gen_rtti.py b/executable_semantics/gen_rtti.py index c88986a7de14..abb264cbfbe9 100755 --- a/executable_semantics/gen_rtti.py +++ b/executable_semantics/gen_rtti.py @@ -2,42 +2,44 @@ """Generates C++ header to support LLVM-style RTTI for a class hierarchy. -Takes as input a file describing the class hierarchy which can consist of -four different kinds of classes: a *root* class is the base of a class -hierarchy, meaning that it doesn't inherit from any other class. *Abstract* and -*interface* classes are non-root classes that cannot be instantiated, and -*concrete* classes are classes that can be instantiated. +# Background -A non-root class C must inherit from exactly one parent, which can be a root or -abstract class, and can also inherit from any number of interfaces, but each -interface's parent must be an ancestor of C. +A C++ class hierarchy supported by this script consists of *abstract* classes, +which can be inherited from but can't be instantiated, and *concrete* classes, +which can be instantiated but can't be inherited from. Classes can inherit from +at most one other class in the hierarchy; a class that doesn't inherit from +any other class is called a *root* class, and it cannot be concrete. + +# Input format + +This script's input file declares every class in the hierarchy, and specifies +the parent of each non-root class. The input file consists of comment lines +starting with `#`, whitespace lines, and one `;`-terminated line for each class. +The core of a line is `class` followed by the class name. `class` can be +prefixed with `root` or `abstract` to specify the corresponding kind of class; +if there is no prefix, the class is concrete. If the class is not a root class, +the name is followed by `:` and then the name of the class it inherits from. A +class cannot inherit from a class defined later in the file. -The input file consists of comment lines starting with `#`, whitespace lines, -and one `;`-terminated line for each class. The core of a line is `class` -followed by the class name. `class` can be prefixed with `root`, `abstract`, -or `interface` to specify the corresponding kind of class; if there is no -prefix, the class is concrete. If the class is not a root class, the name is -followed by `:` and then a comma-separated list of the names of the classes -it inherits from. The first entry in the list is the parent, and the others -are interfaces. A class cannot inherit from classes defined later in the file. For example: root class R; abstract class A : R; -interface class I : R; -abstract class B : R, I; +abstract class B : R; class C : A; class D : B; -class E : A, I; +class E : A; -For each non-concrete class `Foo`, the generated header file will contain +# Output + +For each abstract class `Foo`, the generated header file will contain `enum class FooKind`, which has an enumerator for each concrete class derived from `Foo`, with a name that matches the concrete class name. For each non-root class `Foo` whose root class is `Root`, the generated header -file will also contain a function `bool InheritsFromFoo(RootKind kind)`, -which returns true if the value of `kind` corresponds to a class that is -derived from `Foo`. This function can be used to implement `Foo::classof`. +file will also contain a function `bool InheritsFromFoo(RootKind kind)`, which +returns true if the value of `kind` corresponds to a class that is derived from +`Foo`. This function can be used to implement `Foo::classof`. All enumerators that represent the same concrete class will have the same numeric value, so you can use `static_cast` to convert between the enum types @@ -64,11 +66,9 @@ class Class: Attributes set at construction: name: The class name. - kind: The class kind (root, abstract, interface, or concrete) + kind: The class kind (root, abstract, or concrete) ancestors: A list of Class objects representing the class's ancestors, starting with the root and ending with the current class's parent. - interfaces: A list of Class objects representing the interfaces the class - inherits from. _children: A list of Class objects representing the classes that are derived directly from this one. @@ -78,18 +78,15 @@ class Class: id_range (ROOT and ABSTRACT only): A pair such that a Class object `c` represents a concrete class derived from `self` if and only if c.id >= self.id_range[0] and c.id < self.id_range[1]. - leaf_ids (INTERFACE only): A set containing the IDs of all concrete - classes derived from this interface. leaves (ROOT only): A list of all concrete classes derived from this one, indexed by their IDs. """ - Kind = enum.Enum("Kind", "ROOT ABSTRACT INTERFACE CONCRETE") + Kind = enum.Enum("Kind", "ROOT ABSTRACT CONCRETE") - def __init__(self, name, kind, parent, interfaces): + def __init__(self, name, kind, parent): self.name = name self.kind = kind - self.interfaces = interfaces assert (parent is None) == (kind == Class.Kind.ROOT) if parent is None: @@ -102,8 +99,6 @@ class Class: self.id_range = None elif self.kind == Class.Kind.ABSTRACT: self.id_range = None - elif self.kind == Class.Kind.INTERFACE: - self.leaf_ids = set() else: self.id = None @@ -113,9 +108,6 @@ class Class: if parent: parent._children.append(self) - for interface in self.interfaces: - interface._children.append(self) - def Parent(self): """Returns this Class's parent.""" return self.ancestors[-1] @@ -130,9 +122,9 @@ class Class: def _RegisterLeaf(self, leaf): """Records that `leaf` is derived from self. - Also recursively updates the parent and interfaces of self. leaf.id must - already be populated, and leaves must be registered in order of ID. This - operation is idempotent.""" + Also recursively updates the parent of self. leaf.id must already be + populated, and leaves must be registered in order of ID. This operation + is idempotent.""" already_visited = False if self.kind == Class.Kind.ROOT: if leaf.id == len(self.leaves): @@ -155,17 +147,9 @@ class Class: assert self.id_range[1] == leaf.id + 1 already_visited = True - elif self.kind == Class.Kind.INTERFACE: - if leaf.id in self.leaf_ids: - already_visited = True - else: - self.leaf_ids.add(leaf.id) - if not already_visited: if self.kind != Class.Kind.ROOT: self.Parent()._RegisterLeaf(leaf) - for interface in self.interfaces: - interface._RegisterLeaf(leaf) def Finalize(self): """Populates additional attributes for `self` and derived Classes. @@ -176,7 +160,7 @@ class Class: if self.kind == Class.Kind.CONCRETE: self.id = len(self.Root().leaves) self._RegisterLeaf(self) - elif self.kind in [Class.Kind.ROOT, Class.Kind.ABSTRACT]: + else: for child in self._children: child.Finalize() @@ -185,7 +169,6 @@ _LINE_PATTERN = r"""(?P \w*) \s* class \s+ (?P \w+) (?: \s*:\s* (?P \w+) - (?: , (?P .*) )? )? ;$""" @@ -210,8 +193,6 @@ def main(): kind = Class.Kind.ROOT elif prefix == "abstract": kind = Class.Kind.ABSTRACT - elif prefix == "interface": - kind = Class.Kind.INTERFACE else: sys.exit(f"Unrecognized class prefix '{prefix}' on line {line_num}") @@ -225,36 +206,14 @@ def main(): sys.exit(f"Unknown class '{parent_name}' on line {line_num}") if parent.kind == Class.Kind.CONCRETE: sys.exit(f"{parent.name} cannot be a parent on line {line_num}") - elif parent.kind == Class.Kind.INTERFACE: - if kind != Class.Kind.INTERFACE: - sys.exit( - "Interface cannot be parent of non-interface on" - + f" line {line_num}" - ) else: if kind != Class.Kind.ROOT: sys.exit( f"Non-root class must have a parent on line {line_num}" ) - interfaces = [] - if match_result.group("interfaces"): - for unstripped_name in match_result.group("interfaces").split(","): - interface_name = unstripped_name.strip() - interface = classes[interface_name] - if not interface: - sys.exit( - f"Unknown class '{interface_name}' on line {line_num}" - ) - if interface.kind != Class.Kind.INTERFACE: - sys.exit( - f"'{interface_name}' used as interface on" - + f" line {line_num}" - ) - interfaces.append(interface) - classes[match_result.group("name")] = Class( - match_result.group("name"), kind, parent, interfaces + match_result.group("name"), kind, parent ) for node in classes.values(): @@ -275,16 +234,13 @@ def main(): for node in classes.values(): if node.kind != Class.Kind.CONCRETE: - if node.kind == Class.Kind.INTERFACE: - ids = sorted(node.leaf_ids) - else: - ids = range(node.id_range[0], node.id_range[1]) + ids = range(node.id_range[0], node.id_range[1]) print(f"enum class {node.name}Kind {{") for id in ids: print(f" {node.Root().leaves[id].name} = {id},") print("};\n") - if node.kind != Class.Kind.ROOT: + if node.kind in [Class.Kind.ABSTRACT, Class.Kind.CONCRETE]: print( f"inline bool InheritsFrom{node.name}({node.Root().name}Kind" + " kind) {" @@ -305,19 +261,6 @@ def main(): + f"::{range_end}" ) print(" ;") - elif node.kind == Class.Kind.INTERFACE: - print(" switch(kind) {") - is_empty = True - for id in sorted(node.leaf_ids): - print( - f" case {node.Root().name}Kind::" - + f"{node.Root().leaves[id].name}:" - ) - is_empty = False - if not is_empty: - print(" return true;") - print(" default:") - print(" return false;\n }") elif node.kind == Class.Kind.CONCRETE: print( f" return kind == {node.Root().name}Kind::{node.name};" diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 757f4f9ccae2..e029cd091c76 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -543,19 +543,21 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, } case ExpressionKind::IdentifierExpression: { auto& ident = cast(*e); - CHECK(ident.has_named_entity()) << "Identifier '" << *e << "' at " - << e->source_loc() << " was not resolved"; - std::optional> type = types.Get(ident.name()); - if (type) { - SetStaticType(&ident, *type); - // TODO: this should depend on what entity this name resolves to, but - // we don't have access to that information yet. - ident.set_value_category(Expression::ValueCategory::Var); - return TCResult(types); - } else { - FATAL_COMPILATION_ERROR(e->source_loc()) - << "could not find `" << ident.name() << "`"; + if (ident.named_entity().base().kind() == + AstNodeKind::FunctionDeclaration) { + const auto& function = + cast(ident.named_entity().base()); + if (!function.has_static_type()) { + CHECK(function.return_term().is_auto()); + FATAL_COMPILATION_ERROR(ident.source_loc()) + << "Function calls itself, but has a deduced return type"; + } } + SetStaticType(&ident, &ident.named_entity().static_type()); + // TODO: this should depend on what entity this name resolves to, but + // we don't have access to that information yet. + ident.set_value_category(Expression::ValueCategory::Var); + return TCResult(types); } case ExpressionKind::IntLiteral: e->set_value_category(Expression::ValueCategory::Let); diff --git a/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon b/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon index eff8895f72e8..163c9161c54f 100644 --- a/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon +++ b/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon @@ -7,7 +7,7 @@ // RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \ // RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s // AUTOUPDATE: %{executable_semantics} %s -// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon:18: could not find `Recurse` +// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/function/auto_return/fail_direct_recurse.carbon:18: Function calls itself, but has a deduced return type package ExecutableSemanticsTest api;