diff --git a/executable_semantics/ast/BUILD b/executable_semantics/ast/BUILD index b4f284e7d12e..d36c2604fa8c 100644 --- a/executable_semantics/ast/BUILD +++ b/executable_semantics/ast/BUILD @@ -13,6 +13,7 @@ cc_library( deps = [ ":declaration", ":library_name", + ":static_scope", ], ) @@ -39,6 +40,7 @@ cc_library( ":pattern", ":source_location", ":statement", + ":static_scope", "//common:ostream", "//executable_semantics/common:nonnull", "@llvm-project//llvm:Support", @@ -101,6 +103,7 @@ cc_library( deps = [ ":expression", ":source_location", + ":static_scope", "//common:ostream", "//executable_semantics/common:arena", "//executable_semantics/common:error", @@ -119,10 +122,24 @@ cc_test( ], ) +cc_library( + name = "static_scope", + srcs = ["static_scope.cpp"], + hdrs = ["static_scope.h"], + deps = [ + ":source_location", + "//executable_semantics/common:arena", + "//executable_semantics/common:error", + ], +) + cc_library( name = "source_location", hdrs = ["source_location.h"], - deps = ["//common:ostream"], + deps = [ + "//common:ostream", + "//executable_semantics/common:nonnull", + ], ) cc_library( diff --git a/executable_semantics/ast/ast.h b/executable_semantics/ast/ast.h index 7d761e046368..80782cbe139d 100644 --- a/executable_semantics/ast/ast.h +++ b/executable_semantics/ast/ast.h @@ -9,6 +9,7 @@ #include "executable_semantics/ast/declaration.h" #include "executable_semantics/ast/library_name.h" +#include "executable_semantics/ast/static_scope.h" #include "executable_semantics/common/nonnull.h" namespace Carbon { @@ -23,6 +24,8 @@ struct AST { std::vector imports; // The file's ordered declarations. std::vector> declarations; + // Names declared at the top level of the file. + StaticScope static_scope; }; } // namespace Carbon diff --git a/executable_semantics/ast/class_definition.h b/executable_semantics/ast/class_definition.h index 9a28da7dad49..bc9a013357d3 100644 --- a/executable_semantics/ast/class_definition.h +++ b/executable_semantics/ast/class_definition.h @@ -10,9 +10,12 @@ #include "executable_semantics/ast/member.h" #include "executable_semantics/ast/source_location.h" +#include "executable_semantics/ast/static_scope.h" namespace Carbon { +class StaticScope; + class ClassDefinition { public: ClassDefinition(SourceLocation source_loc, std::string name, @@ -25,10 +28,15 @@ class ClassDefinition { auto name() const -> const std::string& { return name_; } auto members() const -> llvm::ArrayRef> { return members_; } + // Contains class members. + auto static_scope() const -> const StaticScope& { return static_scope_; } + auto static_scope() -> StaticScope& { return static_scope_; } + private: SourceLocation source_loc_; std::string name_; std::vector> members_; + StaticScope static_scope_; }; } // namespace Carbon diff --git a/executable_semantics/ast/declaration.cpp b/executable_semantics/ast/declaration.cpp index 6ba53fcfa8eb..46d75d7349a6 100644 --- a/executable_semantics/ast/declaration.cpp +++ b/executable_semantics/ast/declaration.cpp @@ -54,8 +54,8 @@ void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const { if (i != 0) { out << ", "; } - out << deduced.name << ":! "; - deduced.type->Print(out); + out << deduced.name() << ":! "; + deduced.type().Print(out); ++i; } out << "]"; diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index 38d49fe29543..0a623e98b167 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -15,12 +15,15 @@ #include "executable_semantics/ast/pattern.h" #include "executable_semantics/ast/source_location.h" #include "executable_semantics/ast/statement.h" +#include "executable_semantics/ast/static_scope.h" #include "executable_semantics/common/nonnull.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Compiler.h" namespace Carbon { +class StaticScope; + // Abstract base class of all AST nodes representing patterns. // // Declaration and its derived classes support LLVM-style RTTI, including @@ -29,7 +32,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 { +class Declaration : public NamedEntityInterface { public: enum class Kind { FunctionDeclaration, @@ -48,7 +51,11 @@ class Declaration { // object. auto kind() const -> Kind { return kind_; } - auto source_loc() const -> SourceLocation { return source_loc_; } + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::Declaration; + } + + auto source_loc() const -> SourceLocation override { return source_loc_; } // The static type of the declared entity. Cannot be called before // typechecking. @@ -78,9 +85,24 @@ class Declaration { // TODO: expand the kinds of things that can be deduced parameters. // For now, only generic parameters are supported. -struct GenericBinding { - std::string name; - Nonnull type; +struct GenericBinding : public NamedEntityInterface { + public: + GenericBinding(SourceLocation source_loc, std::string name, + Nonnull type) + : source_loc_(source_loc), name_(std::move(name)), type_(type) {} + + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::GenericBinding; + } + + auto source_loc() const -> SourceLocation override { return source_loc_; } + auto name() const -> const std::string& { return name_; } + auto type() const -> const Expression& { return *type_; } + + private: + SourceLocation source_loc_; + std::string name_; + Nonnull type_; }; class FunctionDeclaration : public Declaration { @@ -119,6 +141,10 @@ class FunctionDeclaration : public Declaration { auto body() const -> std::optional> { return body_; } auto body() -> std::optional> { return body_; } + // Only contains function parameters. Scoped variables are in the body. + auto static_scope() const -> const StaticScope& { return static_scope_; } + auto static_scope() -> StaticScope& { return static_scope_; } + private: std::string name_; std::vector deduced_parameters_; @@ -126,6 +152,7 @@ class FunctionDeclaration : public Declaration { Nonnull return_type_; bool is_omitted_return_type_; std::optional> body_; + StaticScope static_scope_; }; class ClassDeclaration : public Declaration { @@ -148,15 +175,24 @@ class ClassDeclaration : public Declaration { class ChoiceDeclaration : public Declaration { public: - class Alternative { + class Alternative : public NamedEntityInterface { public: - Alternative(std::string name, Nonnull signature) - : name_(std::move(name)), signature_(signature) {} + Alternative(SourceLocation source_loc, std::string name, + Nonnull signature) + : source_loc_(source_loc), + name_(std::move(name)), + signature_(signature) {} + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::ChoiceDeclarationAlternative; + } + + auto source_loc() const -> SourceLocation override { return source_loc_; } auto name() const -> const std::string& { return name_; } auto signature() const -> const Expression& { return *signature_; } private: + SourceLocation source_loc_; std::string name_; Nonnull signature_; }; @@ -176,9 +212,14 @@ class ChoiceDeclaration : public Declaration { return alternatives_; } + // Contains the alternatives. + auto static_scope() const -> const StaticScope& { return static_scope_; } + auto static_scope() -> StaticScope& { return static_scope_; } + private: std::string name_; std::vector alternatives_; + StaticScope static_scope_; }; // Global variable definition implements the Declaration concept. diff --git a/executable_semantics/ast/member.h b/executable_semantics/ast/member.h index 0a57500e3fb4..b52a0daec442 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 { +class Member : public NamedEntityInterface { public: enum class Kind { FieldMember }; @@ -37,7 +37,11 @@ class Member { // object. auto kind() const -> Kind { return kind_; } - auto source_loc() const -> SourceLocation { return source_loc_; } + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::Member; + } + + auto source_loc() const -> SourceLocation override { return source_loc_; } protected: // Constructs a Member representing syntax at the given line number. diff --git a/executable_semantics/ast/pattern.h b/executable_semantics/ast/pattern.h index a6917a7d48a1..7cb02b079513 100644 --- a/executable_semantics/ast/pattern.h +++ b/executable_semantics/ast/pattern.h @@ -12,6 +12,7 @@ #include "common/ostream.h" #include "executable_semantics/ast/expression.h" #include "executable_semantics/ast/source_location.h" +#include "executable_semantics/ast/static_scope.h" #include "llvm/ADT/ArrayRef.h" namespace Carbon { @@ -100,7 +101,7 @@ 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 { +class BindingPattern : public Pattern, public NamedEntityInterface { public: BindingPattern(SourceLocation source_loc, std::optional name, Nonnull type) @@ -108,6 +109,14 @@ class BindingPattern : public Pattern { name_(std::move(name)), type_(type) {} + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::BindingPattern; + } + + auto source_loc() const -> SourceLocation override { + return Pattern::source_loc(); + } + static auto classof(const Pattern* pattern) -> bool { return pattern->kind() == Kind::BindingPattern; } diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index bf6e391e2e6d..b0540e74648e 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -11,6 +11,7 @@ #include "executable_semantics/ast/expression.h" #include "executable_semantics/ast/pattern.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" @@ -18,6 +19,7 @@ namespace Carbon { class FunctionDeclaration; +class StaticScope; class Statement { public: @@ -75,8 +77,12 @@ class Block : public Statement { return statements_; } + auto static_scope() const -> const StaticScope& { return static_scope_; } + auto static_scope() -> StaticScope& { return static_scope_; } + private: std::vector> statements_; + StaticScope static_scope_; }; class ExpressionStatement : public Statement { @@ -293,9 +299,15 @@ class Match : public Statement { auto statement() const -> const Statement& { return *statement_; } auto statement() -> Statement& { return *statement_; } + // Contains names for the pattern and statement. Note that when the + // statement is a block, it gains its own scope. + auto static_scope() const -> const StaticScope& { return static_scope_; } + auto static_scope() -> StaticScope& { return static_scope_; } + private: Nonnull pattern_; Nonnull statement_; + StaticScope static_scope_; }; Match(SourceLocation source_loc, Nonnull expression, @@ -323,7 +335,7 @@ class Match : public Statement { // __continuation { // // } -class Continuation : public Statement { +class Continuation : public Statement, public NamedEntityInterface { public: Continuation(SourceLocation source_loc, std::string continuation_variable, Nonnull body) @@ -331,6 +343,14 @@ class Continuation : public Statement { continuation_variable_(std::move(continuation_variable)), body_(body) {} + auto named_entity_kind() const -> NamedEntityKind override { + return NamedEntityKind::Continuation; + } + + auto source_loc() const -> SourceLocation override { + return Statement::source_loc(); + } + static auto classof(const Statement* stmt) -> bool { return stmt->kind() == Kind::Continuation; } diff --git a/executable_semantics/ast/static_scope.cpp b/executable_semantics/ast/static_scope.cpp new file mode 100644 index 000000000000..78e62121dd8c --- /dev/null +++ b/executable_semantics/ast/static_scope.cpp @@ -0,0 +1,20 @@ +// 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 + +#include "executable_semantics/ast/static_scope.h" + +#include "executable_semantics/common/error.h" + +namespace Carbon { + +void StaticScope::Add(std::string name, + Nonnull entity) { + if (!declared_names_.insert({name, entity}).second) { + FATAL_COMPILATION_ERROR(entity->source_loc()) + << "Duplicate name `" << name << "` also found at " + << declared_names_[name]->source_loc(); + } +} + +} // namespace Carbon diff --git a/executable_semantics/ast/static_scope.h b/executable_semantics/ast/static_scope.h new file mode 100644 index 000000000000..ce9bdc05e7e2 --- /dev/null +++ b/executable_semantics/ast/static_scope.h @@ -0,0 +1,60 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_ +#define EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_ + +#include +#include +#include +#include + +#include "executable_semantics/ast/source_location.h" +#include "executable_semantics/common/nonnull.h" + +namespace Carbon { + +class NamedEntityInterface { + public: + enum class NamedEntityKind { + // Includes variable definitions and matching contexts. + BindingPattern, + // Used by entries in choices. + ChoiceDeclarationAlternative, + // Used by continuations. + Continuation, + // Includes choices, classes, and functions. Variables are handled through + // BindingPattern. + Declaration, + // Used by functions. + GenericBinding, + // Used by entries in classes. + Member, + }; + + virtual ~NamedEntityInterface() = default; + // TODO: This is unused, but is intended for casts after lookup. + virtual auto named_entity_kind() const -> NamedEntityKind = 0; + virtual auto source_loc() const -> SourceLocation = 0; +}; + +// 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. +class StaticScope { + public: + void Add(std::string name, Nonnull entity); + + private: + // Maps locally declared names to their entities. + std::unordered_map> + 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> parent_scopes_; +}; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_AST_STATIC_SCOPE_H_ diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index bc29b83543b4..52bc69821248 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -57,6 +57,7 @@ cc_library( deps = [ ":interpreter", ":resolve_control_flow", + ":resolve_names", ":type_checker", "//executable_semantics/ast", ], @@ -129,6 +130,17 @@ cc_library( ], ) +cc_library( + name = "resolve_names", + srcs = ["resolve_names.cpp"], + hdrs = ["resolve_names.h"], + deps = [ + "//common:check", + "//executable_semantics/ast", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "stack", hdrs = ["stack.h"], diff --git a/executable_semantics/interpreter/exec_program.cpp b/executable_semantics/interpreter/exec_program.cpp index 6dd6ea228c24..ee5deb61aac4 100644 --- a/executable_semantics/interpreter/exec_program.cpp +++ b/executable_semantics/interpreter/exec_program.cpp @@ -9,6 +9,7 @@ #include "executable_semantics/common/arena.h" #include "executable_semantics/interpreter/interpreter.h" #include "executable_semantics/interpreter/resolve_control_flow.h" +#include "executable_semantics/interpreter/resolve_names.h" #include "executable_semantics/interpreter/type_checker.h" namespace Carbon { @@ -45,6 +46,9 @@ void ExecProgram(Nonnull arena, AST ast, bool trace) { } llvm::outs() << "********** type checking **********\n"; } + // Although name resolution is currently done once, generic programming + // (particularly templates) may require more passes. + ResolveNames(arena, ast); ResolveControlFlow(ast); TypeChecker type_checker(arena, trace); TypeChecker::TypeCheckContext p = type_checker.TopLevel(&ast.declarations); diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 6a58eb7ddde3..9ba4f21cc1ff 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -117,8 +117,8 @@ void Interpreter::InitEnv(const Declaration& d, Env* env) { // Bring the deduced parameters into scope. for (const auto& deduced : func_def.deduced_parameters()) { AllocationId a = - heap_.AllocateValue(arena_->New(deduced.name)); - new_env.Set(deduced.name, a); + heap_.AllocateValue(arena_->New(deduced.name())); + new_env.Set(deduced.name(), a); } Nonnull f = arena_->New(&func_def); AllocationId a = heap_.AllocateValue(f); diff --git a/executable_semantics/interpreter/resolve_names.cpp b/executable_semantics/interpreter/resolve_names.cpp new file mode 100644 index 000000000000..f5c59b69f11c --- /dev/null +++ b/executable_semantics/interpreter/resolve_names.cpp @@ -0,0 +1,198 @@ +// 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 + +#include "executable_semantics/interpreter/resolve_names.h" + +#include "executable_semantics/ast/declaration.h" +#include "llvm/Support/Casting.h" + +using llvm::cast; + +namespace Carbon { + +namespace { + +// Populates names for a pattern. See PopulateNamesInDeclaration for overall +// flow. +void PopulateNamesInPattern(const Pattern& pattern, StaticScope& static_scope) { + switch (pattern.kind()) { + case Pattern::Kind::AlternativePattern: { + const auto& alt = cast(pattern); + PopulateNamesInPattern(alt.arguments(), static_scope); + break; + } + case Pattern::Kind::BindingPattern: { + const auto& binding = cast(pattern); + if (binding.name().has_value()) { + static_scope.Add(*binding.name(), &binding); + } + break; + } + case Pattern::Kind::TuplePattern: { + const auto& tuple = cast(pattern); + for (auto* field : tuple.fields()) { + PopulateNamesInPattern(*field, static_scope); + } + break; + } + case Pattern::Kind::AutoPattern: + case Pattern::Kind::ExpressionPattern: + // These don't add names. + break; + } +} + +// Populates names for a statement. See PopulateNamesInDeclaration for overall +// flow. +void PopulateNamesInStatement(Arena* arena, + std::optional> opt_statement, + StaticScope& static_scope) { + if (!opt_statement.has_value()) { + return; + } + Statement& statement = **opt_statement; + switch (statement.kind()) { + case Statement::Kind::Block: { + // Defines a new scope for names. + auto& block = cast(statement); + for (const auto& statement : block.statements()) { + PopulateNamesInStatement(arena, statement, block.static_scope()); + } + break; + } + case Statement::Kind::Continuation: { + // Defines a new name and contains a block. + auto& cont = cast(statement); + static_scope.Add(cont.continuation_variable(), &cont); + PopulateNamesInStatement(arena, &cont.body(), static_scope); + break; + } + case Statement::Kind::VariableDefinition: { + // Defines a new name. + const auto& var = cast(statement); + PopulateNamesInPattern(var.pattern(), static_scope); + break; + } + case Statement::Kind::If: { + // Contains blocks. + auto& if_stmt = cast(statement); + PopulateNamesInStatement(arena, &if_stmt.then_block(), static_scope); + PopulateNamesInStatement(arena, if_stmt.else_block(), static_scope); + break; + } + case Statement::Kind::While: { + // Contains a block. + auto& while_stmt = cast(statement); + PopulateNamesInStatement(arena, &while_stmt.body(), static_scope); + break; + } + case Statement::Kind::Match: { + // Contains blocks. + auto& match = cast(statement); + for (auto& clause : match.clauses()) { + PopulateNamesInPattern(clause.pattern(), clause.static_scope()); + PopulateNamesInStatement(arena, &clause.statement(), + clause.static_scope()); + } + break; + } + case Statement::Kind::Assign: + case Statement::Kind::Await: + case Statement::Kind::Break: + case Statement::Kind::Continue: + case Statement::Kind::ExpressionStatement: + case Statement::Kind::Return: + case Statement::Kind::Run: + // Neither contains names nor a scope. + break; + } +} + +// Populates names for a member. See PopulateNamesInDeclaration for overall +// flow. +void PopulateNamesInMember(Arena* arena, const Member& member, + StaticScope& static_scope) { + switch (member.kind()) { + case Member::Kind::FieldMember: { + const auto& field = cast(member); + if (field.binding().name().has_value()) { + static_scope.Add(*field.binding().name(), &member); + } + break; + } + } +} + +// Populates declared names at scoped boundaries, such as file-level or +// function bodies. This doesn't currently recurse into expressions, but +// likely will in the future in order to resolve names in lambdas. +void PopulateNamesInDeclaration(Arena* arena, Declaration& declaration, + StaticScope& static_scope) { + switch (declaration.kind()) { + case Declaration::Kind::FunctionDeclaration: { + auto& func = cast(declaration); + static_scope.Add(func.name(), &declaration); + for (const auto& param : func.deduced_parameters()) { + func.static_scope().Add(param.name(), ¶m); + } + PopulateNamesInPattern(func.param_pattern(), func.static_scope()); + PopulateNamesInStatement(arena, func.body(), static_scope); + break; + } + case Declaration::Kind::ClassDeclaration: { + auto& class_def = cast(declaration).definition(); + static_scope.Add(class_def.name(), &declaration); + for (auto* member : class_def.members()) { + PopulateNamesInMember(arena, *member, class_def.static_scope()); + } + break; + } + case Declaration::Kind::ChoiceDeclaration: { + auto& choice = cast(declaration); + static_scope.Add(choice.name(), &declaration); + for (auto& alt : choice.alternatives()) { + choice.static_scope().Add(alt.name(), &alt); + } + // Populate name into declared_names. + // Init the choice's declared_names, and populate it with the + // alternatives. + break; + } + case Declaration::Kind::VariableDeclaration: + auto& var = cast(declaration); + if (var.binding().name().has_value()) { + static_scope.Add(*(var.binding().name()), &var.binding()); + } + return; + } +} + +// TODO: ResolveNames for Expression, Member, Pattern, and Statement will be +// needed for recursion. + +// Recurses through a declaration to find and resolve IdentifierExpressions +// using declared_names. +void ResolveNamesInDeclaration(Declaration& declaration, + const StaticScope& static_scope) { + switch (declaration.kind()) { + case Declaration::Kind::FunctionDeclaration: + case Declaration::Kind::ClassDeclaration: + case Declaration::Kind::ChoiceDeclaration: + case Declaration::Kind::VariableDeclaration: + break; + } +} + +} // namespace + +void ResolveNames(Arena* arena, AST& ast) { + for (auto declaration : ast.declarations) { + PopulateNamesInDeclaration(arena, *declaration, ast.static_scope); + } + for (auto declaration : ast.declarations) { + ResolveNamesInDeclaration(*declaration, ast.static_scope); + } +} + +} // namespace Carbon diff --git a/executable_semantics/interpreter/resolve_names.h b/executable_semantics/interpreter/resolve_names.h new file mode 100644 index 000000000000..4624bac39fdd --- /dev/null +++ b/executable_semantics/interpreter/resolve_names.h @@ -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 + +#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_NAMES_H_ +#define EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_NAMES_H_ + +#include "executable_semantics/ast/ast.h" +#include "executable_semantics/common/arena.h" + +namespace Carbon { + +// Resolves names (IdentifierExpressions) in the AST. +void ResolveNames(Nonnull, AST& ast); + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_INTERPRETER_RESOLVE_CONTROL_FLOW_H_ diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 31982275d956..634edde6290b 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -666,10 +666,10 @@ auto TypeChecker::TypeCheckExp(Nonnull e, TypeEnv types, for (auto& deduced_param : fun_t.deduced()) { // TODO: change the following to a CHECK once the real checking // has been added to the type checking of function signatures. - if (!deduced_args.Get(deduced_param.name)) { + if (!deduced_args.Get(deduced_param.name())) { FATAL_COMPILATION_ERROR(e->source_loc()) << "could not deduce type argument for type parameter " - << deduced_param.name; + << deduced_param.name(); } } parameters = Substitute(deduced_args, parameters); @@ -1035,9 +1035,9 @@ auto TypeChecker::TypeCheckFunDef(FunctionDeclaration* f, TypeEnv types, // Bring the deduced parameters into scope for (const auto& deduced : f->deduced_parameters()) { // auto t = interpreter_.InterpExp(values, deduced.type); - types.Set(deduced.name, arena_->New(deduced.name)); - AllocationId a = interpreter_.AllocateValue(*types.Get(deduced.name)); - values.Set(deduced.name, a); + types.Set(deduced.name(), arena_->New(deduced.name())); + AllocationId a = interpreter_.AllocateValue(*types.Get(deduced.name())); + values.Set(deduced.name(), a); } // Type check the parameter pattern auto param_res = @@ -1076,9 +1076,9 @@ auto TypeChecker::TypeOfFunDef(TypeEnv types, Env values, // Bring the deduced parameters into scope for (const auto& deduced : fun_def->deduced_parameters()) { // auto t = interpreter_.InterpExp(values, deduced.type); - types.Set(deduced.name, arena_->New(deduced.name)); - AllocationId a = interpreter_.AllocateValue(*types.Get(deduced.name)); - values.Set(deduced.name, a); + types.Set(deduced.name(), arena_->New(deduced.name())); + AllocationId a = interpreter_.AllocateValue(*types.Get(deduced.name())); + values.Set(deduced.name(), a); } // Type check the parameter pattern TypeCheckPattern(&fun_def->param_pattern(), types, values, std::nullopt); diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 6db59e849d73..b09cbe596632 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -217,7 +217,7 @@ void Value::Print(llvm::raw_ostream& out) const { if (i != 0) { out << ", "; } - out << deduced.name << ":! " << *deduced.type; + out << deduced.name() << ":! " << deduced.type(); ++i; } out << "]"; diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 4bb7e6254228..01fefc7f4a67 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -108,7 +108,7 @@ %type > block %type >> statement_list %type > expression -%type generic_binding +%type > generic_binding %type > deduced_params %type > deduced_param_list %type > pattern @@ -632,7 +632,7 @@ return_type: ; generic_binding: identifier COLON_BANG expression - { $$ = GenericBinding({.name = std::move($1), .type = $3}); } + { $$ = GenericBinding(context.source_loc(), std::move($1), $3); } ; deduced_param_list: // Empty @@ -696,11 +696,12 @@ member_list: ; alternative: identifier tuple - { $$ = ChoiceDeclaration::Alternative($1, $2); } + { $$ = ChoiceDeclaration::Alternative(context.source_loc(), $1, $2); } | identifier { $$ = ChoiceDeclaration::Alternative( - $1, arena->New(context.source_loc())); + context.source_loc(), $1, + arena->New(context.source_loc())); } ; alternative_list: diff --git a/executable_semantics/testdata/name_lookup/fail_block_duplicate.carbon b/executable_semantics/testdata/name_lookup/fail_block_duplicate.carbon new file mode 100644 index 000000000000..7a6a970d60a4 --- /dev/null +++ b/executable_semantics/testdata/name_lookup/fail_block_duplicate.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_block_duplicate.carbon:16: Duplicate name `x` also found at {{.*}}/executable_semantics/testdata/name_lookup/fail_block_duplicate.carbon:15 + +package ExecutableSemanticsTest api; + +fn main() -> i32 { + var x: i32 = 0; + var x: i32 = 0; + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/fail_choice_duplicate.carbon b/executable_semantics/testdata/name_lookup/fail_choice_duplicate.carbon new file mode 100644 index 000000000000..3421fe6648a0 --- /dev/null +++ b/executable_semantics/testdata/name_lookup/fail_choice_duplicate.carbon @@ -0,0 +1,21 @@ +// 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_choice_duplicate.carbon:16: Duplicate name `None` also found at {{.*}}/executable_semantics/testdata/name_lookup/fail_choice_duplicate.carbon:15 + +package ExecutableSemanticsTest api; + +choice Ints { + None, + None, +} + +fn main() -> i32 { + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/fail_class_duplicate.carbon b/executable_semantics/testdata/name_lookup/fail_class_duplicate.carbon new file mode 100644 index 000000000000..8b7fa0daff5b --- /dev/null +++ b/executable_semantics/testdata/name_lookup/fail_class_duplicate.carbon @@ -0,0 +1,21 @@ +// 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_class_duplicate.carbon:16: Duplicate name `x` also found at {{.*}}/executable_semantics/testdata/name_lookup/fail_class_duplicate.carbon:15 + +package ExecutableSemanticsTest api; + +class Foo { + var x: i32; + var x: i32; +} + +fn main() -> i32 { + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/fail_global_duplicate.carbon b/executable_semantics/testdata/name_lookup/fail_global_duplicate.carbon new file mode 100644 index 000000000000..43d3953d92fa --- /dev/null +++ b/executable_semantics/testdata/name_lookup/fail_global_duplicate.carbon @@ -0,0 +1,19 @@ +// 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_global_duplicate.carbon:15: Duplicate name `x` also found at {{.*}}/executable_semantics/testdata/name_lookup/fail_global_duplicate.carbon:14 + +package ExecutableSemanticsTest api; + +var x: i32 = 0; +var x: i32 = 0; + +fn main() -> i32 { + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/fail_match_duplicate.carbon b/executable_semantics/testdata/name_lookup/fail_match_duplicate.carbon new file mode 100644 index 000000000000..208b7607ffb1 --- /dev/null +++ b/executable_semantics/testdata/name_lookup/fail_match_duplicate.carbon @@ -0,0 +1,20 @@ +// 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_match_duplicate.carbon:17: Duplicate name `x` also found at {{.*}}/executable_semantics/testdata/name_lookup/fail_match_duplicate.carbon:16 + +package ExecutableSemanticsTest api; + +fn main() -> i32 { + match (0) { + case x: i32 => + var x: i32 = 0; + } + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/global_shadow.carbon b/executable_semantics/testdata/name_lookup/global_shadow.carbon new file mode 100644 index 000000000000..1cdfe4d794da --- /dev/null +++ b/executable_semantics/testdata/name_lookup/global_shadow.carbon @@ -0,0 +1,19 @@ +// 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: executable_semantics %s 2>&1 | \ +// RUN: FileCheck --match-full-lines --allow-unused-prefixes=false %s +// RUN: executable_semantics --trace %s 2>&1 | \ +// RUN: FileCheck --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: executable_semantics %s +// CHECK: result: 0 + +package ExecutableSemanticsTest api; + +var x: i32 = 0; + +fn main() -> i32 { + var x: i32 = 0; + return 0; +} diff --git a/executable_semantics/testdata/name_lookup/match_shadow.carbon b/executable_semantics/testdata/name_lookup/match_shadow.carbon new file mode 100644 index 000000000000..7ee6c157c1db --- /dev/null +++ b/executable_semantics/testdata/name_lookup/match_shadow.carbon @@ -0,0 +1,23 @@ +// 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: executable_semantics %s 2>&1 | \ +// RUN: FileCheck --match-full-lines --allow-unused-prefixes=false %s +// RUN: executable_semantics --trace %s 2>&1 | \ +// RUN: FileCheck --match-full-lines --allow-unused-prefixes %s +// AUTOUPDATE: executable_semantics %s +// CHECK: result: 0 + +package ExecutableSemanticsTest api; + +var x: i32 = 0; + +fn main() -> i32 { + match (0) { + case x: i32 => { + var x: i32 = 0; + } + } + return 0; +}