From 102ea3ccaf342a01f0775d8c7e58f599c5768b20 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sun, 28 Feb 2021 11:29:38 -0800 Subject: [PATCH] Value semantics for Declaration. (#313) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change creates an unpleasant amount of boilerplate where `Declaration` is declared, in exchange for being able to—very pleasantly—treat it as a simple value that composes with other values everywhere it is used. Applying this technique broadly will pay off in code comprehensibility; once it has been done for all things being new'd, pointers disappear and references are only needed as an idiomatic approximation of inout. The unpleasant code grows only when new polymorphic operations are added, and then only a bit, and is an idiom whose details can readily be ignored once in place. The pleasant code pervades the codebase. Too bad we don't have existential types in C++ ;-) --- executable_semantics/ast/declaration.h | 90 +++++++++++++++---- .../interpreter/interpreter.cpp | 8 +- .../interpreter/interpreter.h | 2 +- .../interpreter/typecheck.cpp | 20 ++--- executable_semantics/interpreter/typecheck.h | 2 +- executable_semantics/syntax.ypp | 20 +++-- executable_semantics/syntax_helpers.cpp | 16 ++-- executable_semantics/syntax_helpers.h | 2 +- 8 files changed, 112 insertions(+), 48 deletions(-) diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index cc2940866dfb..91b8f2dc4ca3 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -24,42 +24,100 @@ using Env = AssocList; /// TODO:explain this. Also name it if necessary. Consult with jsiek. using ExecutionEnvironment = std::pair; -struct Declaration { - virtual void Print() const = 0; - virtual auto Name() const -> std::string = 0; - virtual auto TypeChecked(TypeEnv* env, Env* ct_env) const - -> const Declaration* = 0; - virtual void InitGlobals(Env*& globals) const = 0; - virtual auto TopLevel(ExecutionEnvironment&) const -> void = 0; +/// An existential AST declaration satisfying the Declaration concept. +class Declaration { + public: // ValueSemantic concept API. + Declaration(const Declaration& other) = default; + Declaration& operator=(const Declaration& other) = default; + + /// Constructs an instance equivalent to `d`, where `Model` satisfies the + /// Declaration concept. + template + Declaration(Model d) : box(std::make_shared>(d)) {} + + public: // Declaration concept API, in addition to ValueSemantic. + void Print() const { box->Print(); } + auto Name() const -> std::string { return box->Name(); } + auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration { + return box->TypeChecked(env, ct_env); + } + void InitGlobals(Env*& globals) const { return box->InitGlobals(globals); } + auto TopLevel(ExecutionEnvironment& e) const -> void { + return box->TopLevel(e); + } + + private: // types + /// A base class that erases the type of a `Boxed`, where `Content` + /// satisfies the Declaration concept. + struct Box { + protected: + Box() {} + + public: + Box(const Box& other) = delete; + Box& operator=(const Box& other) = delete; + + virtual ~Box() {} + virtual auto Print() const -> void = 0; + virtual auto Name() const -> std::string = 0; + virtual auto TypeChecked(TypeEnv* env, Env* ct_env) const + -> Declaration = 0; + virtual auto InitGlobals(Env*& globals) const -> void = 0; + virtual auto TopLevel(ExecutionEnvironment&) const -> void = 0; + }; + + /// The derived class that holds an instance of `Content` satisfying the + /// Declaration concept. + template + struct Boxed final : Box { + const Content content; + explicit Boxed(Content content) : Box(), content(content) {} + + auto Print() const -> void override { return content.Print(); } + auto Name() const -> std::string override { return content.Name(); } + auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration override { + return content.TypeChecked(env, ct_env); + } + auto InitGlobals(Env*& globals) const -> void override { + content.InitGlobals(globals); + } + auto TopLevel(ExecutionEnvironment& e) const -> void override { + content.TopLevel(e); + } + }; + + private: // data members + // Note: the pointee is const as long as we have no mutating methods. When + std::shared_ptr box; }; -struct FunctionDeclaration : Declaration { +struct FunctionDeclaration { const FunctionDefinition* definition; explicit FunctionDeclaration(const FunctionDefinition* definition) : definition(definition) {} - void Print() const; + auto Print() const -> void; auto Name() const -> std::string; - auto TypeChecked(TypeEnv* env, Env* ct_env) const -> const Declaration*; - void InitGlobals(Env*& globals) const; + auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration; + auto InitGlobals(Env*& globals) const -> void; auto TopLevel(ExecutionEnvironment&) const -> void; }; -struct StructDeclaration : Declaration { +struct StructDeclaration { StructDefinition definition; StructDeclaration(int line_num, std::string name, std::list* members) : definition{line_num, new std::string(name), members} {} void Print() const; auto Name() const -> std::string; - auto TypeChecked(TypeEnv* env, Env* ct_env) const -> const Declaration*; + auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration; void InitGlobals(Env*& globals) const; auto TopLevel(ExecutionEnvironment&) const -> void; }; -struct ChoiceDeclaration : Declaration { +struct ChoiceDeclaration { int line_num; - std::string name; + std::string name; std::list> alternatives; ChoiceDeclaration(int line_num, std::string name, @@ -68,7 +126,7 @@ struct ChoiceDeclaration : Declaration { void Print() const; auto Name() const -> std::string; - auto TypeChecked(TypeEnv* env, Env* ct_env) const -> const Declaration*; + auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration; void InitGlobals(Env*& globals) const; auto TopLevel(ExecutionEnvironment&) const -> void; }; diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index a0334c426dde..5aa6d30b9b55 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -253,10 +253,10 @@ auto EvalPrim(Operator op, const std::vector& args, int line_num) Env* globals; -void InitGlobals(std::list* fs) { +void InitGlobals(std::list* fs) { globals = nullptr; - for (auto d : *fs) { - d->InitGlobals(globals); + for (auto const& d : *fs) { + d.InitGlobals(globals); } } @@ -1321,7 +1321,7 @@ void Step() { } // Interpret the whole porogram. -auto InterpProgram(std::list* fs) -> int { +auto InterpProgram(std::list* fs) -> int { state = new State(); // Runtime state. std::cout << "********** initializing globals **********" << std::endl; InitGlobals(fs); diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index 9c7875a41919..c2bb9a09d6cc 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -52,7 +52,7 @@ auto ToInteger(Value* v) -> int; /***** Interpreters *****/ -auto InterpProgram(std::list* fs) -> int; +auto InterpProgram(std::list* fs) -> int; auto InterpExp(Env* env, Expression* e) -> Value*; } // namespace Carbon diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 38fd9aba380d..43abcd1aa44a 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -618,7 +618,7 @@ auto StructDeclaration::Name() const -> std::string { return *definition.name; } auto ChoiceDeclaration::Name() const -> std::string { return name; } auto StructDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const - -> const Declaration* { + -> Declaration { auto fields = new std::list(); for (auto& m : *definition.members) { if (m->tag == MemberKind::FieldMember) { @@ -626,28 +626,28 @@ auto StructDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const fields->push_back(m); } } - return new StructDeclaration(definition.line_num, *definition.name, fields); + return StructDeclaration(definition.line_num, *definition.name, fields); } auto FunctionDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const - -> const Declaration* { - return new FunctionDeclaration(TypeCheckFunDef(definition, env, ct_env)); + -> Declaration { + return FunctionDeclaration(TypeCheckFunDef(definition, env, ct_env)); } auto ChoiceDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const - -> const Declaration* { - return this; // TODO. + -> Declaration { + return *this; // TODO. } -auto TopLevel(std::list* fs) -> std::pair { +auto TopLevel(std::list* fs) -> std::pair { ExecutionEnvironment tops = {nullptr, nullptr}; bool found_main = false; - for (auto d : *fs) { - if (d->Name() == "main") { + for (auto const& d : *fs) { + if (d.Name() == "main") { found_main = true; } - d->TopLevel(tops); + d.TopLevel(tops); } if (found_main == false) { diff --git a/executable_semantics/interpreter/typecheck.h b/executable_semantics/interpreter/typecheck.h index a4494f168f54..c018a1d43bd3 100644 --- a/executable_semantics/interpreter/typecheck.h +++ b/executable_semantics/interpreter/typecheck.h @@ -45,7 +45,7 @@ auto TypeCheckStmt(Statement*, TypeEnv*, Env*, Value*) -> TCStatement; auto TypeCheckFunDef(struct FunctionDefinition*, TypeEnv*) -> struct FunctionDefinition*; -auto TopLevel(std::list* fs) -> std::pair; +auto TopLevel(std::list* fs) -> std::pair; void PrintErrorString(const std::string& s); diff --git a/executable_semantics/syntax.ypp b/executable_semantics/syntax.ypp index 5cb7ea61e6b9..6523a62eb51d 100644 --- a/executable_semantics/syntax.ypp +++ b/executable_semantics/syntax.ypp @@ -38,7 +38,7 @@ void yyerror(char* error) { Carbon::Statement* statement_list; Carbon::FunctionDefinition* function_definition; Carbon::Declaration* declaration; - std::list* declaration_list; + std::list* declaration_list; Carbon::Member* member; std::list* member_list; Carbon::ExpOrFieldList* field_list; @@ -312,21 +312,27 @@ alternative_list: ; declaration: function_definition - { $$ = new Carbon::FunctionDeclaration{$1}; } + { $$ = new Carbon::Declaration(Carbon::FunctionDeclaration{$1}); } | function_declaration - { $$ = new Carbon::FunctionDeclaration{$1}; } + { $$ = new Carbon::Declaration(Carbon::FunctionDeclaration{$1}); } | STRUCT identifier '{' member_list '}' - { $$ = new Carbon::StructDeclaration{yylineno, $2, $4}; } + { + $$ = new Carbon::Declaration( + Carbon::StructDeclaration{yylineno, $2, $4}); + } | CHOICE identifier '{' alternative_list '}' - { $$ = new Carbon::ChoiceDeclaration{yylineno, $2, std::list(*$4)}; } + { + $$ = new Carbon::Declaration( + Carbon::ChoiceDeclaration{yylineno, $2, std::list(*$4)}); + } ; declaration_list: // Empty - { $$ = new std::list(); } + { $$ = new std::list(); } | declaration declaration_list { $$ = $2; - $$->push_front($1); + $$->push_front(*$1); } ; %% diff --git a/executable_semantics/syntax_helpers.cpp b/executable_semantics/syntax_helpers.cpp index b5dcc0cc8c13..4b0b4c5fbc6e 100644 --- a/executable_semantics/syntax_helpers.cpp +++ b/executable_semantics/syntax_helpers.cpp @@ -18,24 +18,24 @@ void PrintSyntaxError(char* error, int line_num) { exit(-1); } -void ExecProgram(std::list* fs) { +void ExecProgram(std::list* fs) { std::cout << "********** source program **********" << std::endl; - for (const auto decl : *fs) { - decl->Print(); + for (const auto& decl : *fs) { + decl.Print(); } std::cout << "********** type checking **********" << std::endl; state = new State(); // Compile-time state. std::pair p = TopLevel(fs); TypeEnv* top = p.first; Env* ct_top = p.second; - std::list new_decls; - for (const auto i : *fs) { - new_decls.push_back(i->TypeChecked(top, ct_top)); + std::list new_decls; + for (const auto& decl : *fs) { + new_decls.push_back(decl.TypeChecked(top, ct_top)); } std::cout << std::endl; std::cout << "********** type checking complete **********" << std::endl; - for (const auto decl : new_decls) { - decl->Print(); + for (const auto& decl : new_decls) { + decl.Print(); } std::cout << "********** starting execution **********" << std::endl; int result = InterpProgram(&new_decls); diff --git a/executable_semantics/syntax_helpers.h b/executable_semantics/syntax_helpers.h index 734c1b2c1d63..0efc77449cd9 100644 --- a/executable_semantics/syntax_helpers.h +++ b/executable_semantics/syntax_helpers.h @@ -22,7 +22,7 @@ extern char* input_filename; void PrintSyntaxError(char* error, int line_num); // Runs the top-level declaration list. -void ExecProgram(std::list* fs); +void ExecProgram(std::list* fs); } // namespace Carbon