Value semantics for Declaration. (#313)

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++ ;-)
This commit is contained in:
Dave Abrahams
2021-02-28 11:29:38 -08:00
committed by GitHub
parent 2afbfd6146
commit 102ea3ccaf
8 changed files with 112 additions and 48 deletions
+74 -16
View File
@@ -24,42 +24,100 @@ using Env = AssocList<std::string, Address>;
/// TODO:explain this. Also name it if necessary. Consult with jsiek.
using ExecutionEnvironment = std::pair<TypeEnv*, Env*>;
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 <class Model>
Declaration(Model d) : box(std::make_shared<Boxed<Model>>(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<Content>`, 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 <class Content>
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<const Box> 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<Member*>* 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<std::pair<std::string, Expression*>> 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;
};