Pārlūkot izejas kodu

Use resolved names in typechecker (#974)

As a byproduct, replace NamedEntity with a type-erasing wrapper NamedEntityView, eliminate virtual inheritance from the AST, and eliminate interfaces from gen_rtti.
Geoff Romer 4 gadi atpakaļ
vecāks
revīzija
8e6c209a28

+ 1 - 0
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",

+ 0 - 38
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 <typename To>
-struct cast_convert_val<To, const Carbon::AstNode*, const Carbon::AstNode*> {
-  using ResultType = typename cast_retty<To, const Carbon::AstNode*>::ret_type;
-  static auto doit(const Carbon::AstNode* node) -> ResultType {
-    return dynamic_cast<ResultType>(node);
-  }
-};
-
-template <typename To>
-struct cast_convert_val<To, Carbon::AstNode*, Carbon::AstNode*> {
-  using ResultType = typename cast_retty<To, Carbon::AstNode*>::ret_type;
-  static auto doit(Carbon::AstNode* node) -> ResultType {
-    return dynamic_cast<ResultType>(node);
-  }
-};
-
-template <typename To>
-struct cast_convert_val<To, const Carbon::AstNode, const Carbon::AstNode> {
-  using ResultType = typename cast_retty<To, const Carbon::AstNode>::ret_type;
-  static auto doit(const Carbon::AstNode& node) -> ResultType {
-    return dynamic_cast<ResultType>(node);
-  }
-};
-
-template <typename To>
-struct cast_convert_val<To, Carbon::AstNode, Carbon::AstNode> {
-  using ResultType = typename cast_retty<To, Carbon::AstNode>::ret_type;
-  static auto doit(Carbon::AstNode& node) -> ResultType {
-    return dynamic_cast<ResultType>(node);
-  }
-};
-}  // namespace llvm
-
 #endif  // EXECUTABLE_SEMANTICS_AST_AST_NODE_H_

+ 6 - 7
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;

+ 20 - 11
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<Nonnull<const Value*>> 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<Expression*> 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<Nonnull<GenericBinding*>> deduced_params,
                       Nonnull<TuplePattern*> param_pattern,
                       ReturnTerm return_term,
                       std::optional<Nonnull<Block*>> 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<Nonnull<Block*>> body_;
 };
 
-class ClassDeclaration : public Declaration, public NamedEntity {
+class ClassDeclaration : public Declaration {
  public:
+  using ImplementsCarbonNamedEntity = void;
+
   ClassDeclaration(SourceLocation source_loc, std::string name,
                    std::vector<Nonnull<Member*>> 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<Nonnull<Member*>> members_;
 };
 
-class AlternativeSignature : public virtual AstNode {
+class AlternativeSignature : public AstNode {
  public:
   AlternativeSignature(SourceLocation source_loc, std::string name,
                        Nonnull<Expression*> signature)
@@ -274,11 +281,13 @@ class AlternativeSignature : public virtual AstNode {
   Nonnull<Expression*> signature_;
 };
 
-class ChoiceDeclaration : public Declaration, public NamedEntity {
+class ChoiceDeclaration : public Declaration {
  public:
+  using ImplementsCarbonNamedEntity = void;
+
   ChoiceDeclaration(SourceLocation source_loc, std::string name,
                     std::vector<Nonnull<AlternativeSignature*>> 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<BindingPattern*> binding,
                       Nonnull<Expression*> initializer)
-      : AstNode(AstNodeKind::VariableDeclaration, source_loc),
+      : Declaration(AstNodeKind::VariableDeclaration, source_loc),
         binding_(binding),
         initializer_(initializer) {}
 

+ 30 - 28
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<Nonnull<const Value*>> 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<const NamedEntity*> 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<Nonnull<const NamedEntity*>> named_entity_;
+  std::optional<NamedEntityView> named_entity_;
 };
 
 class FieldAccessExpression : public Expression {
@@ -156,7 +157,7 @@ class FieldAccessExpression : public Expression {
   explicit FieldAccessExpression(SourceLocation source_loc,
                                  Nonnull<Expression*> 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<Expression*> aggregate,
                            Nonnull<Expression*> 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<Nonnull<Expression*>> 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<FieldInitializer> 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<FieldInitializer> 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<Nonnull<Expression*>> 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<Expression*> function,
                           Nonnull<Expression*> 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<Expression*> parameter,
                                Nonnull<Expression*> 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<TupleLiteral*> 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 <typename... Children>
   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...);
   }

+ 4 - 3
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<BindingPattern*> 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());

+ 1 - 1
executable_semantics/ast/pattern.cpp

@@ -89,7 +89,7 @@ static auto RequireFieldAccess(Nonnull<Expression*> alternative)
 AlternativePattern::AlternativePattern(SourceLocation source_loc,
                                        Nonnull<Expression*> alternative,
                                        Nonnull<TuplePattern*> arguments)
-    : AstNode(AstNodeKind::AlternativePattern, source_loc),
+    : Pattern(AstNodeKind::AlternativePattern, source_loc),
       choice_type_(&RequireFieldAccess(alternative).aggregate()),
       alternative_name_(RequireFieldAccess(alternative).field()),
       arguments_(arguments) {}

+ 11 - 8
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<Nonnull<const Value*>> 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<std::string> name,
                  Nonnull<Pattern*> 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<Nonnull<Pattern*>> 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<Expression*> choice_type,
                      std::string alternative_name,
                      Nonnull<TuplePattern*> 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*> expression)
-      : AstNode(AstNodeKind::ExpressionPattern, expression->source_loc()),
+      : Pattern(AstNodeKind::ExpressionPattern, expression->source_loc()),
         expression_(expression) {}
 
   static auto classof(const AstNode* node) -> bool {

+ 19 - 16
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<Nonnull<Statement*>> 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*> 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<Expression*> lhs,
          Nonnull<Expression*> 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*> pattern,
                      Nonnull<Expression*> 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<Expression*> condition,
      Nonnull<Block*> then_block, std::optional<Nonnull<Block*>> 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<TupleLiteral>(source_loc), true) {}
   Return(SourceLocation source_loc, Nonnull<Expression*> 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<Expression*> condition,
         Nonnull<Block*> 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*> expression,
         std::vector<Clause> 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 <continuation_variable> {
 //       <body>
 //     }
-class Continuation : public Statement, public NamedEntity {
+class Continuation : public Statement {
  public:
+  using ImplementsCarbonNamedEntity = void;
+
   Continuation(SourceLocation source_loc, std::string continuation_variable,
                Nonnull<Block*> 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<Expression*> 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());

+ 10 - 13
executable_semantics/ast/static_scope.cpp

@@ -8,22 +8,18 @@
 
 namespace Carbon {
 
-NamedEntity::~NamedEntity() = default;
-
-void StaticScope::Add(std::string name, Nonnull<const NamedEntity*> 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<const NamedEntity*> {
-  std::optional<Nonnull<const NamedEntity*>> result =
-      TryResolve(name, source_loc);
+                          SourceLocation source_loc) const -> NamedEntityView {
+  std::optional<NamedEntityView> 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<Nonnull<const NamedEntity*>> {
+    -> std::optional<NamedEntityView> {
   auto it = declared_names_.find(name);
   if (it != declared_names_.end()) {
     return it->second;
   }
-  std::optional<Nonnull<const NamedEntity*>> result;
+  std::optional<NamedEntityView> result;
   for (Nonnull<const StaticScope*> 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;
   }

+ 56 - 10
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 <functional>
 #include <string>
 #include <unordered_map>
 #include <variant>
@@ -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 <typename T, typename = void>
+static constexpr bool ImplementsNamedEntity = false;
+
+template <typename T>
+static constexpr bool
+    ImplementsNamedEntity<T, typename T::ImplementsCarbonNamedEntity> = true;
+
+// Non-owning type-erased wrapper around a const NodeType* `node`, where
+// NodeType implements the NamedEntity interface.
+class NamedEntityView {
  public:
-  ~NamedEntity() override = 0;
+  template <typename NodeType,
+            typename = std::enable_if_t<ImplementsNamedEntity<NodeType>>>
+  // NOLINTNEXTLINE(google-explicit-constructor)
+  NamedEntityView(Nonnull<const NodeType*> 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<NodeType>(base).static_type();
+        }) {}
+
+  NamedEntityView(const NamedEntityView&) = default;
+  NamedEntityView(NamedEntityView&&) = default;
+  auto operator=(const NamedEntityView&) -> NamedEntityView& = default;
+  auto operator=(NamedEntityView&&) -> NamedEntityView& = default;
+
+  // Returns `node` as an instance of the base class AstNode.
+  auto base() const -> const AstNode& { return *base_; }
 
-  NamedEntity() = default;
+  // Returns node->static_type()
+  auto static_type() const -> const Value& { return static_type_(*base_); }
 
-  // TODO: This is unused, but is intended for casts after lookup.
-  auto kind() const -> NamedEntityKind {
-    return static_cast<NamedEntityKind>(root_kind());
+  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<const AstNode*> base_;
+  std::function<const Value&(const AstNode&)> 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<const NamedEntity*> 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<const NamedEntity*>;
+      -> 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<Nonnull<const NamedEntity*>>;
+      -> std::optional<NamedEntityView>;
 
   // Maps locally declared names to their entities.
-  std::unordered_map<std::string, Nonnull<const NamedEntity*>> declared_names_;
+  std::unordered_map<std::string, NamedEntityView> declared_names_;
 
   // A list of scopes used for name lookup within this scope.
   std::vector<Nonnull<StaticScope*>> parent_scopes_;

+ 37 - 94
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.
-
-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.
-
-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.
+# Background
+
+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.
+
 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;
+
+# Output
 
-For each non-concrete class `Foo`, the generated header file will contain
+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<prefix> \w*) \s*
                  class \s+
                  (?P<name> \w+)
                  (?: \s*:\s* (?P<parent> \w+)
-                   (?: , (?P<interfaces> .*) )?
                  )?
                  ;$"""
 
@@ -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};"

+ 14 - 12
executable_semantics/interpreter/type_checker.cpp

@@ -543,19 +543,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
     }
     case ExpressionKind::IdentifierExpression: {
       auto& ident = cast<IdentifierExpression>(*e);
-      CHECK(ident.has_named_entity()) << "Identifier '" << *e << "' at "
-                                      << e->source_loc() << " was not resolved";
-      std::optional<Nonnull<const Value*>> 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<FunctionDeclaration>(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);

+ 1 - 1
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;