Просмотр исходного кода

Implement static name resolution (#958)

This doesn't actually use the results of name resolution, but it does verify that they are present.
Also ensures that name resolution and type checking are applied to deduced function parameters and the implicit call to `Main()`.

Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
Geoff Romer 4 лет назад
Родитель
Сommit
17e0a1afb9

+ 2 - 0
executable_semantics/ast/ast.h

@@ -26,6 +26,8 @@ struct AST {
   std::vector<Nonnull<Declaration*>> declarations;
   // Names declared at the top level of the file.
   StaticScope static_scope;
+  // Synthesized call to `Main`. Injected after parsing.
+  std::optional<Nonnull<CallExpression*>> main_call;
 };
 
 }  // namespace Carbon

+ 11 - 0
executable_semantics/ast/declaration.h

@@ -91,6 +91,7 @@ struct GenericBinding : public virtual AstNode, public NamedEntity {
 
   auto name() const -> const std::string& { return name_; }
   auto type() const -> const Expression& { return *type_; }
+  auto type() -> Expression& { return *type_; }
 
  private:
   std::string name_;
@@ -201,6 +202,9 @@ class FunctionDeclaration : public Declaration {
       -> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
     return deduced_parameters_;
   }
+  auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
+    return deduced_parameters_;
+  }
   auto param_pattern() const -> const TuplePattern& { return *param_pattern_; }
   auto param_pattern() -> TuplePattern& { return *param_pattern_; }
   auto return_term() const -> const ReturnTerm& { return return_term_; }
@@ -262,6 +266,7 @@ class AlternativeSignature : public virtual AstNode, public NamedEntity {
 
   auto name() const -> const std::string& { return name_; }
   auto signature() const -> const Expression& { return *signature_; }
+  auto signature() -> Expression& { return *signature_; }
 
  private:
   std::string name_;
@@ -285,6 +290,9 @@ class ChoiceDeclaration : public Declaration {
       -> llvm::ArrayRef<Nonnull<const AlternativeSignature*>> {
     return alternatives_;
   }
+  auto alternatives() -> llvm::ArrayRef<Nonnull<AlternativeSignature*>> {
+    return alternatives_;
+  }
 
   // Contains the alternatives.
   auto static_scope() const -> const StaticScope& { return static_scope_; }
@@ -297,6 +305,9 @@ class ChoiceDeclaration : public Declaration {
 };
 
 // Global variable definition implements the Declaration concept.
+//
+// TODO: this should not inherit from NamedEntity, because names should
+//   always resolve to the underlying binding, not the VariableDeclaration.
 class VariableDeclaration : public Declaration {
  public:
   VariableDeclaration(SourceLocation source_loc,

+ 18 - 0
executable_semantics/ast/expression.h

@@ -21,6 +21,7 @@
 namespace Carbon {
 
 class Value;
+class NamedEntity;
 
 class Expression : public virtual AstNode {
  public:
@@ -129,8 +130,25 @@ 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_; }
+
+  // Sets the value returned by named_entity. Can be called only once,
+  // during name resolution.
+  void set_named_entity(Nonnull<const NamedEntity*> named_entity) {
+    CHECK(!named_entity_.has_value());
+    named_entity_ = named_entity;
+  }
+
+  // Returns true if set_named_entity has been called. Should be used only
+  // for debugging purposes.
+  // TODO: remove this once we no longer need the CHECKs that use it.
+  auto has_named_entity() const -> bool { return named_entity_.has_value(); }
+
  private:
   std::string name_;
+  std::optional<Nonnull<const NamedEntity*>> named_entity_;
 };
 
 class FieldAccessExpression : public Expression {

+ 3 - 2
executable_semantics/ast/member.h

@@ -48,7 +48,7 @@ class Member : public virtual AstNode, public NamedEntity {
 
 class FieldMember : public Member {
  public:
-  FieldMember(SourceLocation source_loc, Nonnull<const BindingPattern*> binding)
+  FieldMember(SourceLocation source_loc, Nonnull<BindingPattern*> binding)
       : AstNode(AstNodeKind::FieldMember, source_loc), binding_(binding) {}
 
   static auto classof(const AstNode* node) -> bool {
@@ -56,12 +56,13 @@ class FieldMember : public Member {
   }
 
   auto binding() const -> const BindingPattern& { return *binding_; }
+  auto binding() -> BindingPattern& { return *binding_; }
 
  private:
   // TODO: split this into a non-optional name and a type, initialized by
   // a constructor that takes a BindingPattern and handles errors like a
   // missing name.
-  Nonnull<const BindingPattern*> binding_;
+  Nonnull<BindingPattern*> binding_;
 };
 
 }  // namespace Carbon

+ 33 - 0
executable_semantics/ast/static_scope.cpp

@@ -18,4 +18,37 @@ void StaticScope::Add(std::string name, Nonnull<const NamedEntity*> entity) {
   }
 }
 
+auto StaticScope::Resolve(const std::string& name,
+                          SourceLocation source_loc) const
+    -> Nonnull<const NamedEntity*> {
+  std::optional<Nonnull<const NamedEntity*>> result =
+      TryResolve(name, source_loc);
+  if (!result.has_value()) {
+    FATAL_COMPILATION_ERROR(source_loc)
+        << "'" << name << "' is not declared in this scope";
+  }
+  return *result;
+}
+
+auto StaticScope::TryResolve(const std::string& name,
+                             SourceLocation source_loc) const
+    -> std::optional<Nonnull<const NamedEntity*>> {
+  auto it = declared_names_.find(name);
+  if (it != declared_names_.end()) {
+    return it->second;
+  }
+  std::optional<Nonnull<const NamedEntity*>> 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();
+    }
+    result = parent_result;
+  }
+  return result;
+}
+
 }  // namespace Carbon

+ 23 - 3
executable_semantics/ast/static_scope.h

@@ -28,18 +28,38 @@ class NamedEntity : public virtual AstNode {
   }
 };
 
-// The set of declared names in a scope. This is not aware of child scopes, but
-// does include directions to parent or related scopes for lookup purposes.
+// Maps the names visible in a given scope to the entities they name.
+// A scope may have parent scopes, whose names will also be visible in the
+// child scope.
 class StaticScope {
  public:
+  // Defines `name` to be `entity` in this scope, or reports a compilation error
+  // if `name` is already defined in this scope.
   void Add(std::string name, Nonnull<const NamedEntity*> entity);
 
+  // Make `parent` a parent of this scope.
+  // REQUIRES: `parent` is not already a parent of this scope.
+  void AddParent(Nonnull<StaticScope*> parent) {
+    parent_scopes_.push_back(parent);
+  }
+
+  // Returns the nearest definition of `name` in the ancestor graph of this
+  // 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*>;
+
  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*>>;
+
   // Maps locally declared names to their entities.
   std::unordered_map<std::string, Nonnull<const NamedEntity*>> declared_names_;
 
   // A list of scopes used for name lookup within this scope.
-  // TODO: This is unused, but is intended for name lookup cross-scope.
   std::vector<Nonnull<StaticScope*>> parent_scopes_;
 };
 

+ 5 - 6
executable_semantics/interpreter/exec_program.cpp

@@ -24,6 +24,10 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
     }
     llvm::outs() << "********** type checking **********\n";
   }
+  SourceLocation source_loc("<Main()>", 0);
+  ast.main_call = arena->New<CallExpression>(
+      source_loc, arena->New<IdentifierExpression>(source_loc, "Main"),
+      arena->New<TupleLiteral>(source_loc));
   // Although name resolution is currently done once, generic programming
   // (particularly templates) may require more passes.
   ResolveNames(arena, ast);
@@ -37,13 +41,8 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
     }
     llvm::outs() << "********** starting execution **********\n";
   }
-
-  SourceLocation source_loc("<Main()>", 0);
-  Nonnull<Expression*> call_main = arena->New<CallExpression>(
-      source_loc, arena->New<IdentifierExpression>(source_loc, "Main"),
-      arena->New<TupleLiteral>(source_loc));
   int result =
-      Interpreter(arena, trace).InterpProgram(ast.declarations, call_main);
+      Interpreter(arena, trace).InterpProgram(ast.declarations, *ast.main_call);
   llvm::outs() << "result: " << result << "\n";
 }
 

+ 6 - 0
executable_semantics/interpreter/interpreter.cpp

@@ -302,6 +302,9 @@ void Interpreter::StepLvalue() {
     case ExpressionKind::IdentifierExpression: {
       //    { {x :: C, E, F} :: S, H}
       // -> { {E(x) :: C, E, F} :: S, H}
+      CHECK(cast<IdentifierExpression>(exp).has_named_entity())
+          << "Identifier '" << exp << "' at " << exp.source_loc()
+          << " was not resolved";
       Address pointer =
           GetFromEnv(exp.source_loc(), cast<IdentifierExpression>(exp).name());
       Nonnull<const Value*> v = arena_->New<LValue>(pointer);
@@ -512,6 +515,9 @@ void Interpreter::StepExp() {
     case ExpressionKind::IdentifierExpression: {
       CHECK(act.pos() == 0);
       const auto& ident = cast<IdentifierExpression>(exp);
+      CHECK(ident.has_named_entity())
+          << "Identifier '" << exp << "' at " << exp.source_loc()
+          << " was not resolved";
       // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
       Address pointer = GetFromEnv(exp.source_loc(), ident.name());
       return todo_.FinishAction(heap_.Read(pointer, exp.source_loc()));

+ 219 - 8
executable_semantics/interpreter/resolve_names.cpp

@@ -56,6 +56,7 @@ void PopulateNamesInStatement(Arena* arena,
     case StatementKind::Block: {
       // Defines a new scope for names.
       auto& block = cast<Block>(statement);
+      block.static_scope().AddParent(&static_scope);
       for (const auto& statement : block.statements()) {
         PopulateNamesInStatement(arena, statement, block.static_scope());
       }
@@ -91,6 +92,7 @@ void PopulateNamesInStatement(Arena* arena,
       // Contains blocks.
       auto& match = cast<Match>(statement);
       for (auto& clause : match.clauses()) {
+        clause.static_scope().AddParent(&static_scope);
         PopulateNamesInPattern(clause.pattern(), clause.static_scope());
         PopulateNamesInStatement(arena, &clause.statement(),
                                  clause.static_scope());
@@ -132,16 +134,18 @@ void PopulateNamesInDeclaration(Arena* arena, Declaration& declaration,
   switch (declaration.kind()) {
     case DeclarationKind::FunctionDeclaration: {
       auto& func = cast<FunctionDeclaration>(declaration);
+      func.static_scope().AddParent(&static_scope);
       static_scope.Add(func.name(), &declaration);
       for (Nonnull<const GenericBinding*> param : func.deduced_parameters()) {
         func.static_scope().Add(param->name(), param);
       }
       PopulateNamesInPattern(func.param_pattern(), func.static_scope());
-      PopulateNamesInStatement(arena, func.body(), static_scope);
+      PopulateNamesInStatement(arena, func.body(), func.static_scope());
       break;
     }
     case DeclarationKind::ClassDeclaration: {
       auto& class_decl = cast<ClassDeclaration>(declaration);
+      class_decl.static_scope().AddParent(&static_scope);
       static_scope.Add(class_decl.name(), &declaration);
       for (auto* member : class_decl.members()) {
         PopulateNamesInMember(arena, *member, class_decl.static_scope());
@@ -150,6 +154,7 @@ void PopulateNamesInDeclaration(Arena* arena, Declaration& declaration,
     }
     case DeclarationKind::ChoiceDeclaration: {
       auto& choice = cast<ChoiceDeclaration>(declaration);
+      choice.static_scope().AddParent(&static_scope);
       static_scope.Add(choice.name(), &declaration);
       for (Nonnull<const AlternativeSignature*> alt : choice.alternatives()) {
         choice.static_scope().Add(alt->name(), alt);
@@ -168,19 +173,222 @@ void PopulateNamesInDeclaration(Arena* arena, Declaration& declaration,
   }
 }
 
-// TODO: ResolveNames for Expression, Member, Pattern, and Statement will be
-// needed for recursion.
+// Populates the named_entity member of all IdentifierExpressions in the
+// subtree rooted at `expression`, whose nearest enclosing scope is
+// `enclosing_scope`.
+static void ResolveNamesInExpression(Expression& expression,
+                                     const StaticScope& enclosing_scope) {
+  switch (expression.kind()) {
+    case ExpressionKind::CallExpression: {
+      auto& call = cast<CallExpression>(expression);
+      ResolveNamesInExpression(call.function(), enclosing_scope);
+      ResolveNamesInExpression(call.argument(), enclosing_scope);
+      break;
+    }
+    case ExpressionKind::FunctionTypeLiteral: {
+      auto& fun_type = cast<FunctionTypeLiteral>(expression);
+      ResolveNamesInExpression(fun_type.parameter(), enclosing_scope);
+      ResolveNamesInExpression(fun_type.return_type(), enclosing_scope);
+      break;
+    }
+    case ExpressionKind::FieldAccessExpression:
+      ResolveNamesInExpression(
+          cast<FieldAccessExpression>(expression).aggregate(), enclosing_scope);
+      break;
+    case ExpressionKind::IndexExpression: {
+      auto& index = cast<IndexExpression>(expression);
+      ResolveNamesInExpression(index.aggregate(), enclosing_scope);
+      ResolveNamesInExpression(index.offset(), enclosing_scope);
+      break;
+    }
+    case ExpressionKind::PrimitiveOperatorExpression:
+      for (Nonnull<Expression*> operand :
+           cast<PrimitiveOperatorExpression>(expression).arguments()) {
+        ResolveNamesInExpression(*operand, enclosing_scope);
+      }
+      break;
+    case ExpressionKind::TupleLiteral:
+      for (Nonnull<Expression*> field :
+           cast<TupleLiteral>(expression).fields()) {
+        ResolveNamesInExpression(*field, enclosing_scope);
+      }
+      break;
+    case ExpressionKind::StructLiteral:
+      for (FieldInitializer& init : cast<StructLiteral>(expression).fields()) {
+        ResolveNamesInExpression(init.expression(), enclosing_scope);
+      }
+      break;
+    case ExpressionKind::StructTypeLiteral:
+      for (FieldInitializer& init :
+           cast<StructTypeLiteral>(expression).fields()) {
+        ResolveNamesInExpression(init.expression(), enclosing_scope);
+      }
+      break;
+    case ExpressionKind::IdentifierExpression: {
+      auto& identifier = cast<IdentifierExpression>(expression);
+      identifier.set_named_entity(
+          enclosing_scope.Resolve(identifier.name(), identifier.source_loc()));
+      break;
+    }
+    case ExpressionKind::IntrinsicExpression:
+      // FIXME implement this once #955 is merged
+    case ExpressionKind::BoolTypeLiteral:
+    case ExpressionKind::BoolLiteral:
+    case ExpressionKind::IntTypeLiteral:
+    case ExpressionKind::ContinuationTypeLiteral:
+    case ExpressionKind::IntLiteral:
+    case ExpressionKind::StringLiteral:
+    case ExpressionKind::StringTypeLiteral:
+    case ExpressionKind::TypeTypeLiteral:
+      break;
+  }
+}
+
+// Equivalent to ResolveNamesInExpression, but operates on patterns.
+static void ResolveNamesInPattern(Pattern& pattern,
+                                  const StaticScope& enclosing_scope) {
+  switch (pattern.kind()) {
+    case PatternKind::BindingPattern:
+      ResolveNamesInPattern(cast<BindingPattern>(pattern).type(),
+                            enclosing_scope);
+      break;
+    case PatternKind::TuplePattern:
+      for (Nonnull<Pattern*> field : cast<TuplePattern>(pattern).fields()) {
+        ResolveNamesInPattern(*field, enclosing_scope);
+      }
+      break;
+    case PatternKind::AlternativePattern: {
+      auto& alternative = cast<AlternativePattern>(pattern);
+      ResolveNamesInExpression(alternative.choice_type(), enclosing_scope);
+      ResolveNamesInPattern(alternative.arguments(), enclosing_scope);
+      break;
+    }
+    case PatternKind::ExpressionPattern:
+      ResolveNamesInExpression(cast<ExpressionPattern>(pattern).expression(),
+                               enclosing_scope);
+      break;
+    case PatternKind::AutoPattern:
+      break;
+  }
+}
+
+// Equivalent to ResolveNamesInExpression, but operates on statements.
+static void ResolveNamesInStatement(Statement& statement,
+                                    const StaticScope& enclosing_scope) {
+  switch (statement.kind()) {
+    case StatementKind::ExpressionStatement:
+      ResolveNamesInExpression(
+          cast<ExpressionStatement>(statement).expression(), enclosing_scope);
+      break;
+    case StatementKind::Assign: {
+      auto& assign = cast<Assign>(statement);
+      ResolveNamesInExpression(assign.lhs(), enclosing_scope);
+      ResolveNamesInExpression(assign.rhs(), enclosing_scope);
+      break;
+    }
+    case StatementKind::VariableDefinition: {
+      auto& def = cast<VariableDefinition>(statement);
+      ResolveNamesInPattern(def.pattern(), enclosing_scope);
+      ResolveNamesInExpression(def.init(), enclosing_scope);
+      break;
+    }
+    case StatementKind::If: {
+      auto& if_stmt = cast<If>(statement);
+      ResolveNamesInExpression(if_stmt.condition(), enclosing_scope);
+      ResolveNamesInStatement(if_stmt.then_block(), enclosing_scope);
+      if (if_stmt.else_block().has_value()) {
+        ResolveNamesInStatement(**if_stmt.else_block(), enclosing_scope);
+      }
+      break;
+    }
+    case StatementKind::Return:
+      ResolveNamesInExpression(cast<Return>(statement).expression(),
+                               enclosing_scope);
+      break;
+    case StatementKind::Block: {
+      // TODO: this will resolve usages to declarations that occur later in
+      //   the block. Figure out how to avoid that.
+      auto& block = cast<Block>(statement);
+      for (Nonnull<Statement*> sub_statement : block.statements()) {
+        ResolveNamesInStatement(*sub_statement, block.static_scope());
+      }
+      break;
+    }
+    case StatementKind::While: {
+      auto& while_stmt = cast<While>(statement);
+      ResolveNamesInExpression(while_stmt.condition(), enclosing_scope);
+      ResolveNamesInStatement(while_stmt.body(), enclosing_scope);
+      break;
+    }
+    case StatementKind::Match: {
+      auto& match = cast<Match>(statement);
+      ResolveNamesInExpression(match.expression(), enclosing_scope);
+      for (Match::Clause& clause : match.clauses()) {
+        ResolveNamesInPattern(clause.pattern(), clause.static_scope());
+        ResolveNamesInStatement(clause.statement(), clause.static_scope());
+      }
+      break;
+    }
+    case StatementKind::Continuation:
+      ResolveNamesInStatement(cast<Continuation>(statement).body(),
+                              enclosing_scope);
+      break;
+    case StatementKind::Run:
+      ResolveNamesInExpression(cast<Run>(statement).argument(),
+                               enclosing_scope);
+      break;
+    case StatementKind::Await:
+    case StatementKind::Break:
+    case StatementKind::Continue:
+      break;
+  }
+}
 
 // Recurses through a declaration to find and resolve IdentifierExpressions
 // using declared_names.
 void ResolveNamesInDeclaration(Declaration& declaration,
-                               const StaticScope& static_scope) {
+                               const StaticScope& enclosing_scope) {
   switch (declaration.kind()) {
-    case DeclarationKind::FunctionDeclaration:
-    case DeclarationKind::ClassDeclaration:
-    case DeclarationKind::ChoiceDeclaration:
-    case DeclarationKind::VariableDeclaration:
+    case DeclarationKind::FunctionDeclaration: {
+      auto& function = cast<FunctionDeclaration>(declaration);
+      for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
+        ResolveNamesInExpression(binding->type(), function.static_scope());
+      }
+      ResolveNamesInPattern(function.param_pattern(), function.static_scope());
+      if (function.return_term().type_expression().has_value()) {
+        ResolveNamesInExpression(**function.return_term().type_expression(),
+                                 function.static_scope());
+      }
+      if (function.body().has_value()) {
+        ResolveNamesInStatement(**function.body(), function.static_scope());
+      }
       break;
+    }
+    case DeclarationKind::ClassDeclaration: {
+      auto& class_decl = cast<ClassDeclaration>(declaration);
+      for (Nonnull<Member*> member : class_decl.members()) {
+        switch (member->kind()) {
+          case MemberKind::FieldMember:
+            ResolveNamesInPattern(cast<FieldMember>(member)->binding(),
+                                  class_decl.static_scope());
+        }
+      }
+      break;
+    }
+    case DeclarationKind::ChoiceDeclaration: {
+      auto& choice = cast<ChoiceDeclaration>(declaration);
+      for (Nonnull<AlternativeSignature*> alternative : choice.alternatives()) {
+        ResolveNamesInExpression(alternative->signature(),
+                                 choice.static_scope());
+      }
+      break;
+    }
+    case DeclarationKind::VariableDeclaration: {
+      auto& var = cast<VariableDeclaration>(declaration);
+      ResolveNamesInPattern(var.binding(), enclosing_scope);
+      ResolveNamesInExpression(var.initializer(), enclosing_scope);
+      break;
+    }
   }
 }
 
@@ -193,6 +401,9 @@ void ResolveNames(Arena* arena, AST& ast) {
   for (auto declaration : ast.declarations) {
     ResolveNamesInDeclaration(*declaration, ast.static_scope);
   }
+  if (ast.main_call.has_value()) {
+    ResolveNamesInExpression(**ast.main_call, ast.static_scope);
+  }
 }
 
 }  // namespace Carbon

+ 7 - 1
executable_semantics/interpreter/type_checker.cpp

@@ -569,6 +569,8 @@ 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);
@@ -1053,7 +1055,8 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
                                                TypeEnv types, Env values,
                                                bool check_body) -> TCResult {
   // Bring the deduced parameters into scope
-  for (Nonnull<const GenericBinding*> deduced : f->deduced_parameters()) {
+  for (Nonnull<GenericBinding*> deduced : f->deduced_parameters()) {
+    TypeCheckExp(&deduced->type(), types, values);
     // auto t = interpreter_.InterpExp(values, deduced.type);
     types.Set(deduced->name(), arena_->New<VariableType>(deduced->name()));
     AllocationId a = interpreter_.AllocateValue(*types.Get(deduced->name()));
@@ -1160,6 +1163,9 @@ void TypeChecker::TypeCheck(AST& ast) {
   for (const auto decl : ast.declarations) {
     TypeCheckDeclaration(decl, top, ct_top);
   }
+  if (ast.main_call.has_value()) {
+    TypeCheckExp(*ast.main_call, p.types, p.values);
+  }
 }
 
 void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,

+ 2 - 2
executable_semantics/interpreter/value.h

@@ -335,11 +335,11 @@ class TypeType : public Value {
 // A function type.
 class FunctionType : public Value {
  public:
-  FunctionType(std::vector<Nonnull<const GenericBinding*>> deduced,
+  FunctionType(llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
                Nonnull<const Value*> parameters,
                Nonnull<const Value*> return_type)
       : Value(Kind::FunctionType),
-        deduced_(std::move(deduced)),
+        deduced_(deduced),
         parameters_(parameters),
         return_type_(return_type) {}
 

+ 18 - 0
executable_semantics/testdata/name_lookup/fail_use_before_declare.carbon

@@ -0,0 +1,18 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: not executable_semantics %s 2>&1 | \
+// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
+// 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/name_lookup/fail_use_before_declare.carbon:15: could not find `x`
+
+package ExecutableSemanticsTest api;
+
+fn Main() -> i32 {
+  x = 0;
+  var x: i32 = 0;
+  return 0;
+}