diff --git a/executable_semantics/ast/member.h b/executable_semantics/ast/member.h index 9053cc91df51..5aa926b89f65 100644 --- a/executable_semantics/ast/member.h +++ b/executable_semantics/ast/member.h @@ -50,7 +50,9 @@ class Member : public AstNode { class FieldMember : public Member { public: FieldMember(SourceLocation source_loc, Nonnull binding) - : Member(AstNodeKind::FieldMember, source_loc), binding_(binding) {} + : Member(AstNodeKind::FieldMember, source_loc), binding_(binding) { + CHECK(binding->name() != AnonymousName); + } static auto classof(const AstNode* node) -> bool { return InheritsFromFieldMember(node->kind()); @@ -60,9 +62,6 @@ class FieldMember : public Member { 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 binding_; }; diff --git a/executable_semantics/ast/pattern.cpp b/executable_semantics/ast/pattern.cpp index fd62f16487b6..bd4d51882121 100644 --- a/executable_semantics/ast/pattern.cpp +++ b/executable_semantics/ast/pattern.cpp @@ -26,12 +26,7 @@ void Pattern::Print(llvm::raw_ostream& out) const { break; case PatternKind::BindingPattern: { const auto& binding = cast(*this); - if (binding.name().has_value()) { - out << *binding.name(); - } else { - out << "_"; - } - out << ": " << binding.type(); + out << binding.name() << ": " << binding.type(); break; } case PatternKind::TuplePattern: { diff --git a/executable_semantics/ast/pattern.h b/executable_semantics/ast/pattern.h index 4ab3f04d9fcb..bd6bb51ae8f4 100644 --- a/executable_semantics/ast/pattern.h +++ b/executable_semantics/ast/pattern.h @@ -101,7 +101,7 @@ class BindingPattern : public Pattern { public: using ImplementsCarbonNamedEntity = void; - BindingPattern(SourceLocation source_loc, std::optional name, + BindingPattern(SourceLocation source_loc, std::string name, Nonnull type) : Pattern(AstNodeKind::BindingPattern, source_loc), name_(std::move(name)), @@ -111,8 +111,10 @@ class BindingPattern : public Pattern { return InheritsFromBindingPattern(node->kind()); } - // The name this pattern binds, if any. - auto name() const -> const std::optional& { return name_; } + // The name this pattern binds, if any. If equal to AnonymousName, indicates + // that this BindingPattern does not bind a name, which in turn means it + // should not be used as a NamedEntity. + auto name() const -> const std::string& { return name_; } // The pattern specifying the type of values that this pattern matches. auto type() const -> const Pattern& { return *type_; } @@ -121,7 +123,7 @@ class BindingPattern : public Pattern { auto value_category() const -> ValueCategory { return ValueCategory::Var; } private: - std::optional name_; + std::string name_; Nonnull type_; }; diff --git a/executable_semantics/ast/statement.cpp b/executable_semantics/ast/statement.cpp index 82e741cb3ff6..cb3a0317074c 100644 --- a/executable_semantics/ast/statement.cpp +++ b/executable_semantics/ast/statement.cpp @@ -100,7 +100,7 @@ void Statement::PrintDepth(int depth, llvm::raw_ostream& out) const { } case StatementKind::Continuation: { const auto& cont = cast(*this); - out << "continuation " << cont.continuation_variable() << " "; + out << "continuation " << cont.name() << " "; if (depth < 0 || depth > 1) { out << "\n"; } diff --git a/executable_semantics/ast/statement.h b/executable_semantics/ast/statement.h index ef4780eaeb89..46815d8d72e6 100644 --- a/executable_semantics/ast/statement.h +++ b/executable_semantics/ast/statement.h @@ -314,19 +314,17 @@ class Continuation : public Statement { public: using ImplementsCarbonNamedEntity = void; - Continuation(SourceLocation source_loc, std::string continuation_variable, + Continuation(SourceLocation source_loc, std::string name, Nonnull body) : Statement(AstNodeKind::Continuation, source_loc), - continuation_variable_(std::move(continuation_variable)), + name_(std::move(name)), body_(body) {} static auto classof(const AstNode* node) -> bool { return InheritsFromContinuation(node->kind()); } - auto continuation_variable() const -> const std::string& { - return continuation_variable_; - } + auto name() const -> const std::string& { return name_; } auto body() const -> const Block& { return *body_; } auto body() -> Block& { return *body_; } @@ -348,7 +346,7 @@ class Continuation : public Statement { auto value_category() const -> ValueCategory { return ValueCategory::Var; } private: - std::string continuation_variable_; + std::string name_; Nonnull body_; std::optional> static_type_; }; diff --git a/executable_semantics/ast/static_scope.h b/executable_semantics/ast/static_scope.h index a7dc9710e59b..27753d52cb0f 100644 --- a/executable_semantics/ast/static_scope.h +++ b/executable_semantics/ast/static_scope.h @@ -11,6 +11,7 @@ #include #include +#include "common/check.h" #include "executable_semantics/ast/ast_node.h" #include "executable_semantics/ast/source_location.h" #include "executable_semantics/ast/value_category.h" @@ -20,6 +21,9 @@ namespace Carbon { class Value; +// The placeholder name exposed by anonymous NamedEntities. +static constexpr std::string_view AnonymousName = "_"; + // True if NodeType::ImplementsCarbonNamedEntity is valid and names a type, // indicating that NodeType implements the NamedEntity interface, which means // it must define the following methods, with contracts as documented. @@ -29,6 +33,10 @@ class Value; // Returns the value category of an IdentifierExpression that names *this. auto value_category() const -> ValueCategory; + + // Returns the name of an IdentifierExpression that names *this. If *this + // is anonymous, returns AnonymousName. + auto name() const -> std::string_view; #endif // NodeType must be derived from AstNode. // @@ -45,6 +53,7 @@ static constexpr bool // NodeType implements the NamedEntity interface. class NamedEntityView { public: + // REQUIRES: node->name() != AnonymousName template >> // NOLINTNEXTLINE(google-explicit-constructor) @@ -53,12 +62,18 @@ class NamedEntityView { // and using std::function to encapsulate the ability to call // the derived class's methods. : base_(node), + name_([](const AstNode& base) -> std::string_view { + return llvm::cast(base).name(); + }), static_type_([](const AstNode& base) -> const Value& { return llvm::cast(base).static_type(); }), value_category_([](const AstNode& base) -> ValueCategory { return llvm::cast(base).value_category(); - }) {} + }) { + CHECK(node->name() != AnonymousName) + << "Entity with no name used as NamedEntity: " << *node; + } NamedEntityView(const NamedEntityView&) = default; NamedEntityView(NamedEntityView&&) = default; @@ -68,6 +83,9 @@ class NamedEntityView { // Returns `node` as an instance of the base class AstNode. auto base() const -> const AstNode& { return *base_; } + // Returns node->name() + auto name() const -> std::string_view { return name_(*base_); } + // Returns node->static_type() auto static_type() const -> const Value& { return static_type_(*base_); } @@ -76,18 +94,24 @@ class NamedEntityView { return value_category_(*base_); } - friend auto operator==(const NamedEntityView& lhs, - const NamedEntityView& rhs) { + friend auto operator==(const NamedEntityView& lhs, const NamedEntityView& rhs) + -> bool { return lhs.base_ == rhs.base_; } - friend auto operator!=(const NamedEntityView& lhs, - const NamedEntityView& rhs) { + friend auto operator!=(const NamedEntityView& lhs, const NamedEntityView& rhs) + -> bool { return lhs.base_ != rhs.base_; } + friend auto operator<(const NamedEntityView& lhs, const NamedEntityView& rhs) + -> bool { + return std::less<>()(lhs.base_, rhs.base_); + } + private: Nonnull base_; + std::function name_; std::function static_type_; std::function value_category_; }; diff --git a/executable_semantics/interpreter/heap.cpp b/executable_semantics/interpreter/heap.cpp index 8bb50772590a..54180f007918 100644 --- a/executable_semantics/interpreter/heap.cpp +++ b/executable_semantics/interpreter/heap.cpp @@ -46,8 +46,8 @@ void Heap::Deallocate(AllocationId allocation) { if (alive_[allocation.index_]) { alive_[allocation.index_] = false; } else { - FATAL_RUNTIME_ERROR_NO_LINE() << "deallocating an already dead value: " - << *values_[allocation.index_]; + FATAL() << "deallocating an already dead value: " + << *values_[allocation.index_]; } } diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 1c78bd69f08d..68a84844a13b 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -88,7 +88,7 @@ auto Interpreter::EvalPrim(Operator op, return arena_->New(cast(*args[0]).value() || cast(*args[1]).value()); case Operator::Eq: - return arena_->New(ValueEqual(args[0], args[1], source_loc)); + return arena_->New(ValueEqual(args[0], args[1])); case Operator::Ptr: return arena_->New(args[0]); case Operator::Deref: @@ -125,7 +125,7 @@ void Interpreter::InitEnv(const Declaration& d, Env* env) { const Expression& type_expression = cast(binding.type()).expression(); auto type = InterpExp(Env(arena_), &type_expression); - fields.push_back({.name = *binding.name(), .value = type}); + fields.push_back({.name = binding.name(), .value = type}); break; } } @@ -158,7 +158,7 @@ void Interpreter::InitEnv(const Declaration& d, Env* env) { Nonnull v = Convert(InterpExp(*env, &var.initializer()), &var.static_type()); AllocationId a = heap_.AllocateValue(v); - env->Set(*var.binding().name(), a); + env->Set(var.binding().name(), a); break; } } @@ -189,9 +189,9 @@ auto Interpreter::PatternMatch(Nonnull p, Nonnull v, case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*p); Env values(arena_); - if (placeholder.name().has_value()) { + if (placeholder.named_entity().has_value()) { AllocationId a = heap_.AllocateValue(v); - values.Set(*placeholder.name(), a); + values.Set(std::string(placeholder.named_entity()->name()), a); } return values; } @@ -283,7 +283,7 @@ auto Interpreter::PatternMatch(Nonnull p, Nonnull v, // on the typechecker to ensure that `v` is a type. return Env(arena_); default: - if (ValueEqual(p, v, source_loc)) { + if (ValueEqual(p, v)) { return Env(arena_); } else { return std::nullopt; @@ -680,11 +680,11 @@ void Interpreter::StepPattern() { } case PatternKind::BindingPattern: { const auto& binding = cast(pattern); - if (act.pos() == 0) { - return todo_.Spawn(std::make_unique(&binding.type())); + if (binding.name() != AnonymousName) { + return todo_.FinishAction( + arena_->New(&binding)); } else { - return todo_.FinishAction(arena_->New( - binding.name(), act.results()[0])); + return todo_.FinishAction(arena_->New()); } } case PatternKind::TuplePattern: { @@ -926,9 +926,8 @@ void Interpreter::StepStmt() { AllocationId continuation_address = heap_.AllocateValue(arena_->New(fragment)); // Bind the continuation object to the continuation variable - todo_.CurrentScope().AddLocal( - cast(stmt).continuation_variable(), - continuation_address); + todo_.CurrentScope().AddLocal(cast(stmt).name(), + continuation_address); return todo_.FinishAction(); } case StatementKind::Run: { diff --git a/executable_semantics/interpreter/resolve_names.cpp b/executable_semantics/interpreter/resolve_names.cpp index 6e2cd7439631..0068fa069233 100644 --- a/executable_semantics/interpreter/resolve_names.cpp +++ b/executable_semantics/interpreter/resolve_names.cpp @@ -28,8 +28,8 @@ static void AddExposedNames(const Member& member, switch (member.kind()) { case MemberKind::FieldMember: { const auto& field = cast(member); - if (field.binding().name().has_value()) { - enclosing_scope.Add(*field.binding().name(), &field.binding()); + if (field.binding().name() != AnonymousName) { + enclosing_scope.Add(field.binding().name(), &field.binding()); } break; } @@ -56,8 +56,8 @@ static void AddExposedNames(const Declaration& declaration, } case DeclarationKind::VariableDeclaration: auto& var = cast(declaration); - if (var.binding().name().has_value()) { - enclosing_scope.Add(*(var.binding().name()), &var.binding()); + if (var.binding().name() != AnonymousName) { + enclosing_scope.Add(var.binding().name(), &var.binding()); } return; } @@ -158,8 +158,8 @@ static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope) { case PatternKind::BindingPattern: { auto& binding = cast(pattern); ResolveNames(binding.type(), enclosing_scope); - if (binding.name().has_value()) { - enclosing_scope.Add(*binding.name(), &binding); + if (binding.name() != AnonymousName) { + enclosing_scope.Add(binding.name(), &binding); } break; } @@ -241,7 +241,7 @@ static void ResolveNames(Statement& statement, StaticScope& enclosing_scope) { } case StatementKind::Continuation: { auto& continuation = cast(statement); - enclosing_scope.Add(continuation.continuation_variable(), &continuation); + enclosing_scope.Add(continuation.name(), &continuation); StaticScope continuation_scope; continuation_scope.AddParent(&enclosing_scope); ResolveNames(cast(statement).body(), continuation_scope); diff --git a/executable_semantics/interpreter/type_checker.cpp b/executable_semantics/interpreter/type_checker.cpp index 748ec7ebc037..5d335ddf1ed7 100644 --- a/executable_semantics/interpreter/type_checker.cpp +++ b/executable_semantics/interpreter/type_checker.cpp @@ -1051,13 +1051,13 @@ void TypeChecker::TypeCheckClassDeclaration( switch (m->kind()) { case MemberKind::FieldMember: { BindingPattern& binding = cast(*m).binding(); - if (!binding.name().has_value()) { + if (binding.name() == AnonymousName) { FATAL_COMPILATION_ERROR(binding.source_loc()) << "Struct members must have names"; } TypeCheckPattern(&binding, ct_top, std::nullopt); fields.push_back( - {.name = *binding.name(), .value = &binding.static_type()}); + {.name = binding.name(), .value = &binding.static_type()}); break; } } diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 8789b1045a9d..0ca55b6a449e 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -139,12 +139,13 @@ void Value::Print(llvm::raw_ostream& out) const { } case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*this); - if (placeholder.name().has_value()) { - out << *placeholder.name(); + out << "Placeholder<"; + if (placeholder.named_entity().has_value()) { + out << (*placeholder.named_entity()).name(); } else { out << "_"; } - out << ": " << placeholder.type(); + out << ">"; break; } case Value::Kind::AlternativeValue: { @@ -374,8 +375,7 @@ auto TypeEqual(Nonnull t1, Nonnull t2) -> bool { // Returns true if the two values are equal and returns false otherwise. // // This function implements the `==` operator of Carbon. -auto ValueEqual(Nonnull v1, Nonnull v2, - SourceLocation source_loc) -> bool { +auto ValueEqual(Nonnull v1, Nonnull v2) -> bool { if (v1->kind() != v2->kind()) { return false; } @@ -401,7 +401,7 @@ auto ValueEqual(Nonnull v1, Nonnull v2, return false; } for (size_t i = 0; i < elements1.size(); ++i) { - if (!ValueEqual(elements1[i], elements2[i], source_loc)) { + if (!ValueEqual(elements1[i], elements2[i])) { return false; } } @@ -414,7 +414,7 @@ auto ValueEqual(Nonnull v1, Nonnull v2, for (size_t i = 0; i < struct_v1.elements().size(); ++i) { CHECK(struct_v1.elements()[i].name == struct_v2.elements()[i].name); if (!ValueEqual(struct_v1.elements()[i].value, - struct_v2.elements()[i].value, source_loc)) { + struct_v2.elements()[i].value)) { return false; } } diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index 0d6b707fa8f0..636cdb9fafee 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -285,23 +285,24 @@ class TupleValue : public Value { // A binding placeholder value. class BindingPlaceholderValue : public Value { public: - // nullopt represents the `_` placeholder. - BindingPlaceholderValue(std::optional name, - Nonnull type) + // Represents the `_` placeholder. + explicit BindingPlaceholderValue() : Value(Kind::BindingPlaceholderValue) {} + + // Represents a named placeholder. + explicit BindingPlaceholderValue(NamedEntityView named_entity) : Value(Kind::BindingPlaceholderValue), - name_(std::move(name)), - type_(type) {} + named_entity_(std::move(named_entity)) {} static auto classof(const Value* value) -> bool { return value->kind() == Kind::BindingPlaceholderValue; } - auto name() const -> const std::optional& { return name_; } - auto type() const -> const Value& { return *type_; } + auto named_entity() const -> const std::optional& { + return named_entity_; + } private: - std::optional name_; - Nonnull type_; + std::optional named_entity_; }; // The int type. @@ -601,8 +602,7 @@ class TypeOfChoiceType : public Value { }; auto TypeEqual(Nonnull t1, Nonnull t2) -> bool; -auto ValueEqual(Nonnull v1, Nonnull v2, - SourceLocation source_loc) -> bool; +auto ValueEqual(Nonnull v1, Nonnull v2) -> bool; } // namespace Carbon diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index d534977b018e..ff85994f0917 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -121,7 +121,7 @@ %type > struct_type_literal %type > struct_type_literal_contents %type > tuple -%type > binding_lhs +%type binding_lhs %type > variable_declaration %type > member %type >> member_list @@ -476,7 +476,7 @@ non_expression_pattern: ; binding_lhs: identifier { $$ = $1; } -| UNDERSCORE { $$ = std::nullopt; } +| UNDERSCORE { $$ = AnonymousName; } ; paren_pattern: paren_pattern_base { $$ = PatternFromParenContents(arena, context.source_loc(), $1); } @@ -536,7 +536,7 @@ clause: | DEFAULT DOUBLE_ARROW statement { $$ = Match::Clause(arena->New( - context.source_loc(), std::nullopt, + context.source_loc(), std::string(AnonymousName), arena->New(context.source_loc())), $3); }