From c1148caf7d41c68c33a835e21227ccd38c96cc31 Mon Sep 17 00:00:00 2001 From: Jon Meow <46229924+jonmeow@users.noreply.github.com> Date: Fri, 6 Aug 2021 14:57:44 -0700 Subject: [PATCH] Switch Declaration to use inheritance+cast (#714) --- .../ast/abstract_syntax_tree.h | 2 +- executable_semantics/ast/declaration.cpp | 83 ++-------- executable_semantics/ast/declaration.h | 150 ++++++++++++------ .../interpreter/interpreter.cpp | 37 ++--- .../interpreter/interpreter.h | 2 +- .../interpreter/typecheck.cpp | 92 +++++------ executable_semantics/interpreter/typecheck.h | 4 +- executable_semantics/main.cpp | 2 +- executable_semantics/syntax/parser.ypp | 16 +- .../syntax/syntax_helpers.cpp | 18 +-- executable_semantics/syntax/syntax_helpers.h | 2 +- 11 files changed, 206 insertions(+), 202 deletions(-) diff --git a/executable_semantics/ast/abstract_syntax_tree.h b/executable_semantics/ast/abstract_syntax_tree.h index f89c65783118..a3cf42a6ebec 100644 --- a/executable_semantics/ast/abstract_syntax_tree.h +++ b/executable_semantics/ast/abstract_syntax_tree.h @@ -10,7 +10,7 @@ #include "executable_semantics/ast/declaration.h" namespace Carbon { -using AST = std::list; +using AST = std::list; } #endif // EXECUTABLE_SEMANTICS_AST_ABSTRACT_SYNTAX_TREE_H_ diff --git a/executable_semantics/ast/declaration.cpp b/executable_semantics/ast/declaration.cpp index 0f84cf1845fe..8c02873797de 100644 --- a/executable_semantics/ast/declaration.cpp +++ b/executable_semantics/ast/declaration.cpp @@ -4,72 +4,21 @@ #include "executable_semantics/ast/declaration.h" +#include "llvm/Support/Casting.h" + namespace Carbon { -auto Declaration::MakeFunctionDeclaration(FunctionDefinition definition) - -> const Declaration { - Declaration d; - d.value = FunctionDeclaration({.definition = definition}); - return d; -} - -auto Declaration::MakeStructDeclaration(int line_num, std::string name, - std::list members) - -> const Declaration { - Declaration d; - d.value = StructDeclaration( - {.definition = StructDefinition({.line_num = line_num, - .name = std::move(name), - .members = std::move(members)})}); - return d; -} - -auto Declaration::MakeChoiceDeclaration( - int line_num, std::string name, - std::list> alternatives) - -> const Declaration { - Declaration d; - d.value = ChoiceDeclaration({.line_num = line_num, - .name = std::move(name), - .alternatives = std::move(alternatives)}); - return d; -} - -auto Declaration::MakeVariableDeclaration(int source_location, - const BindingPattern* binding, - const Expression* initializer) - -> const Declaration { - Declaration d; - d.value = VariableDeclaration({.source_location = source_location, - .binding = binding, - .initializer = initializer}); - return d; -} - -auto Declaration::GetFunctionDeclaration() const -> const FunctionDeclaration& { - return std::get(value); -} - -auto Declaration::GetStructDeclaration() const -> const StructDeclaration& { - return std::get(value); -} - -auto Declaration::GetChoiceDeclaration() const -> const ChoiceDeclaration& { - return std::get(value); -} - -auto Declaration::GetVariableDeclaration() const -> const VariableDeclaration& { - return std::get(value); -} +using llvm::cast; void Declaration::Print(llvm::raw_ostream& out) const { - switch (tag()) { - case DeclarationKind::FunctionDeclaration: - out << GetFunctionDeclaration().definition; + switch (Tag()) { + case Kind::FunctionDeclaration: + out << cast(*this).Definition(); break; - case DeclarationKind::StructDeclaration: { - const StructDefinition& struct_def = GetStructDeclaration().definition; + case Kind::StructDeclaration: { + const StructDefinition& struct_def = + cast(*this).Definition(); out << "struct " << struct_def.name << " {\n"; for (Member* m : struct_def.members) { out << *m; @@ -78,19 +27,19 @@ void Declaration::Print(llvm::raw_ostream& out) const { break; } - case DeclarationKind::ChoiceDeclaration: { - const auto& choice = GetChoiceDeclaration(); - out << "choice " << choice.name << " {\n"; - for (const auto& [name, signature] : choice.alternatives) { + case Kind::ChoiceDeclaration: { + const auto& choice = cast(*this); + out << "choice " << choice.Name() << " {\n"; + for (const auto& [name, signature] : choice.Alternatives()) { out << "alt " << name << " " << *signature << ";\n"; } out << "}\n"; break; } - case DeclarationKind::VariableDeclaration: { - const auto& var = GetVariableDeclaration(); - out << "var " << *var.binding << " = " << *var.initializer << "\n"; + case Kind::VariableDeclaration: { + const auto& var = cast(*this); + out << "var " << *var.Binding() << " = " << *var.Initializer() << "\n"; break; } } diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index 63c2c996d29c..682b4ad110bc 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -31,34 +31,120 @@ struct TypeCheckContext { Env values; }; -enum class DeclarationKind { - FunctionDeclaration, - StructDeclaration, - ChoiceDeclaration, - VariableDeclaration, +// Abstract base class of all AST nodes representing patterns. +// +// Declaration and its derived classes support LLVM-style RTTI, including +// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every +// class derived from Declaration must provide a `classof` operation, and +// every concrete derived class must have a corresponding enumerator +// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for +// details. +class Declaration { + public: + enum class Kind { + FunctionDeclaration, + StructDeclaration, + ChoiceDeclaration, + VariableDeclaration, + }; + + Declaration(const Member&) = delete; + Declaration& operator=(const Member&) = delete; + + // Returns the enumerator corresponding to the most-derived type of this + // object. + auto Tag() const -> Kind { return tag; } + + auto LineNumber() const -> int { return line_num; } + + void Print(llvm::raw_ostream& out) const; + + protected: + // Constructs a Declaration representing syntax at the given line number. + // `tag` must be the enumerator corresponding to the most-derived type being + // constructed. + Declaration(Kind tag, int line_num) : tag(tag), line_num(line_num) {} + + private: + const Kind tag; + int line_num; }; -struct FunctionDeclaration { - static constexpr DeclarationKind Kind = DeclarationKind::FunctionDeclaration; +class FunctionDeclaration : public Declaration { + public: + FunctionDeclaration(FunctionDefinition definition) + : Declaration(Kind::FunctionDeclaration, definition.line_num), + definition(std::move(definition)) {} + + static auto classof(const Declaration* decl) -> bool { + return decl->Tag() == Kind::FunctionDeclaration; + } + + auto Definition() const -> const FunctionDefinition& { return definition; } + + private: FunctionDefinition definition; }; -struct StructDeclaration { - static constexpr DeclarationKind Kind = DeclarationKind::StructDeclaration; +class StructDeclaration : public Declaration { + public: + StructDeclaration(int line_num, std::string name, std::list members) + : Declaration(Kind::StructDeclaration, line_num), + definition({.line_num = line_num, + .name = std::move(name), + .members = std::move(members)}) {} + + static auto classof(const Declaration* decl) -> bool { + return decl->Tag() == Kind::StructDeclaration; + } + + auto Definition() const -> const StructDefinition& { return definition; } + + private: StructDefinition definition; }; -struct ChoiceDeclaration { - static constexpr DeclarationKind Kind = DeclarationKind::ChoiceDeclaration; - int line_num; +class ChoiceDeclaration : public Declaration { + public: + ChoiceDeclaration( + int line_num, std::string name, + std::list> alternatives) + : Declaration(Kind::ChoiceDeclaration, line_num), + name(std::move(name)), + alternatives(std::move(alternatives)) {} + + static auto classof(const Declaration* decl) -> bool { + return decl->Tag() == Kind::ChoiceDeclaration; + } + + auto Name() const -> const std::string& { return name; } + auto Alternatives() const + -> const std::list>& { + return alternatives; + } + + private: std::string name; std::list> alternatives; }; // Global variable definition implements the Declaration concept. -struct VariableDeclaration { - static constexpr DeclarationKind Kind = DeclarationKind::VariableDeclaration; - int source_location; +class VariableDeclaration : public Declaration { + public: + VariableDeclaration(int line_num, const BindingPattern* binding, + const Expression* initializer) + : Declaration(Kind::VariableDeclaration, line_num), + binding(binding), + initializer(initializer) {} + + static auto classof(const Declaration* decl) -> bool { + return decl->Tag() == Kind::VariableDeclaration; + } + + auto Binding() const -> const BindingPattern* { return binding; } + auto Initializer() const -> const Expression* { return initializer; } + + 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. @@ -66,40 +152,6 @@ struct VariableDeclaration { const Expression* initializer; }; -class Declaration { - public: - static auto MakeFunctionDeclaration(FunctionDefinition definition) - -> const Declaration; - static auto MakeStructDeclaration(int line_num, std::string name, - std::list members) - -> const Declaration; - static auto MakeChoiceDeclaration( - int line_num, std::string name, - std::list> alternatives) - -> const Declaration; - static auto MakeVariableDeclaration(int source_location, - const BindingPattern* binding, - const Expression* initializer) - -> const Declaration; - - auto GetFunctionDeclaration() const -> const FunctionDeclaration&; - auto GetStructDeclaration() const -> const StructDeclaration&; - auto GetChoiceDeclaration() const -> const ChoiceDeclaration&; - auto GetVariableDeclaration() const -> const VariableDeclaration&; - - void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - - inline auto tag() const -> DeclarationKind { - return std::visit([](const auto& t) { return t.Kind; }, value); - } - - private: - std::variant - value; -}; - } // namespace Carbon #endif // EXECUTABLE_SEMANTICS_AST_DECLARATION_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 8ef6b466e23c..399f4815dc1c 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -107,10 +107,10 @@ auto EvalPrim(Operator op, const std::vector& args, int line_num) static Env globals; void InitEnv(const Declaration& d, Env* env) { - switch (d.tag()) { - case DeclarationKind::FunctionDeclaration: { + switch (d.Tag()) { + case Declaration::Kind::FunctionDeclaration: { const FunctionDefinition& func_def = - d.GetFunctionDeclaration().definition; + cast(d).Definition(); Env new_env = *env; // Bring the deduced parameters into scope. for (const auto& deduced : func_def.deduced_parameters) { @@ -126,8 +126,9 @@ void InitEnv(const Declaration& d, Env* env) { break; } - case DeclarationKind::StructDeclaration: { - const StructDefinition& struct_def = d.GetStructDeclaration().definition; + case Declaration::Kind::StructDeclaration: { + const StructDefinition& struct_def = + cast(d).Definition(); VarValues fields; VarValues methods; for (const Member* m : struct_def.members) { @@ -149,34 +150,34 @@ void InitEnv(const Declaration& d, Env* env) { break; } - case DeclarationKind::ChoiceDeclaration: { - const auto& choice = d.GetChoiceDeclaration(); + case Declaration::Kind::ChoiceDeclaration: { + const auto& choice = cast(d); VarValues alts; - for (const auto& [name, signature] : choice.alternatives) { + for (const auto& [name, signature] : choice.Alternatives()) { auto t = InterpExp(Env(), signature); alts.push_back(make_pair(name, t)); } - auto ct = global_arena->New(choice.name, std::move(alts)); + auto ct = global_arena->New(choice.Name(), std::move(alts)); auto a = state->heap.AllocateValue(ct); - env->Set(choice.name, a); + env->Set(choice.Name(), a); break; } - case DeclarationKind::VariableDeclaration: { - const auto& var = d.GetVariableDeclaration(); + case Declaration::Kind::VariableDeclaration: { + const auto& var = cast(d); // Adds an entry in `globals` mapping the variable's name to the // result of evaluating the initializer. - auto v = InterpExp(*env, var.initializer); + auto v = InterpExp(*env, var.Initializer()); Address a = state->heap.AllocateValue(v); - env->Set(*var.binding->Name(), a); + env->Set(*var.Binding()->Name(), a); break; } } } -static void InitGlobals(std::list* fs) { - for (auto const& d : *fs) { - InitEnv(d, &globals); +static void InitGlobals(const std::list& fs) { + for (const auto* d : fs) { + InitEnv(*d, &globals); } } @@ -1167,7 +1168,7 @@ void Step() { } // Interpret the whole porogram. -auto InterpProgram(std::list* fs) -> int { +auto InterpProgram(const std::list& fs) -> int { state = global_arena->New(); // Runtime state. if (tracing_output) { llvm::outs() << "********** initializing globals **********\n"; diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index a5910aada93b..49c813c7e3c1 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -35,7 +35,7 @@ void PrintEnv(Env values); /***** Interpreters *****/ -auto InterpProgram(std::list* fs) -> int; +auto InterpProgram(const std::list& fs) -> int; auto InterpExp(Env values, const Expression* e) -> const Value*; auto InterpPattern(Env values, const Pattern* p) -> const Value*; diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index a3b13d1aeb63..3948df4c2ed5 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -905,15 +905,15 @@ auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top) } static auto GetName(const Declaration& d) -> const std::string& { - switch (d.tag()) { - case DeclarationKind::FunctionDeclaration: - return d.GetFunctionDeclaration().definition.name; - case DeclarationKind::StructDeclaration: - return d.GetStructDeclaration().definition.name; - case DeclarationKind::ChoiceDeclaration: - return d.GetChoiceDeclaration().name; - case DeclarationKind::VariableDeclaration: { - const BindingPattern* binding = d.GetVariableDeclaration().binding; + switch (d.Tag()) { + case Declaration::Kind::FunctionDeclaration: + return cast(d).Definition().name; + case Declaration::Kind::StructDeclaration: + return cast(d).Definition().name; + case Declaration::Kind::ChoiceDeclaration: + return cast(d).Name(); + case Declaration::Kind::VariableDeclaration: { + const BindingPattern* binding = cast(d).Binding(); if (!binding->Name().has_value()) { FATAL_COMPILATION_ERROR(binding->LineNumber()) << "Top-level variable declarations must have names"; @@ -924,14 +924,15 @@ static auto GetName(const Declaration& d) -> const std::string& { } auto MakeTypeChecked(const Declaration& d, const TypeEnv& types, - const Env& values) -> Declaration { - switch (d.tag()) { - case DeclarationKind::FunctionDeclaration: - return Declaration::MakeFunctionDeclaration(*TypeCheckFunDef( - &d.GetFunctionDeclaration().definition, types, values)); + const Env& values) -> const Declaration* { + switch (d.Tag()) { + case Declaration::Kind::FunctionDeclaration: + return global_arena->New(*TypeCheckFunDef( + &cast(d).Definition(), types, values)); - case DeclarationKind::StructDeclaration: { - const StructDefinition& struct_def = d.GetStructDeclaration().definition; + case Declaration::Kind::StructDeclaration: { + const StructDefinition& struct_def = + cast(d).Definition(); std::list fields; for (Member* m : struct_def.members) { switch (m->tag()) { @@ -941,49 +942,50 @@ auto MakeTypeChecked(const Declaration& d, const TypeEnv& types, break; } } - return Declaration::MakeStructDeclaration( + return global_arena->New( struct_def.line_num, struct_def.name, std::move(fields)); } - case DeclarationKind::ChoiceDeclaration: + case Declaration::Kind::ChoiceDeclaration: // TODO - return d; + return &d; - case DeclarationKind::VariableDeclaration: { - const auto& var = d.GetVariableDeclaration(); + case Declaration::Kind::VariableDeclaration: { + const auto& var = cast(d); // Signals a type error if the initializing expression does not have // the declared type of the variable, otherwise returns this // declaration with annotated types. TCExpression type_checked_initializer = - TypeCheckExp(var.initializer, types, values); + TypeCheckExp(var.Initializer(), types, values); const Expression* type = - dyn_cast(var.binding->Type())->Expression(); + dyn_cast(var.Binding()->Type())->Expression(); if (type == nullptr) { // TODO: consider adding support for `auto` - FATAL_COMPILATION_ERROR(var.source_location) + FATAL_COMPILATION_ERROR(var.LineNumber()) << "Type of a top-level variable must be an expression."; } const Value* declared_type = InterpExp(values, type); - ExpectType(var.source_location, "initializer of variable", declared_type, + ExpectType(var.LineNumber(), "initializer of variable", declared_type, type_checked_initializer.type); - return d; + return &d; } } } static void TopLevel(const Declaration& d, TypeCheckContext* tops) { - switch (d.tag()) { - case DeclarationKind::FunctionDeclaration: { + switch (d.Tag()) { + case Declaration::Kind::FunctionDeclaration: { const FunctionDefinition& func_def = - d.GetFunctionDeclaration().definition; + cast(d).Definition(); auto t = TypeOfFunDef(tops->types, tops->values, &func_def); tops->types.Set(func_def.name, t); InitEnv(d, &tops->values); break; } - case DeclarationKind::StructDeclaration: { - const StructDefinition& struct_def = d.GetStructDeclaration().definition; + case Declaration::Kind::StructDeclaration: { + const StructDefinition& struct_def = + cast(d).Definition(); auto st = TypeOfStructDef(&struct_def, tops->types, tops->values); Address a = state->heap.AllocateValue(st); tops->values.Set(struct_def.name, a); // Is this obsolete? @@ -999,42 +1001,42 @@ static void TopLevel(const Declaration& d, TypeCheckContext* tops) { break; } - case DeclarationKind::ChoiceDeclaration: { - const auto& choice = d.GetChoiceDeclaration(); + case Declaration::Kind::ChoiceDeclaration: { + const auto& choice = cast(d); VarValues alts; - for (const auto& [name, signature] : choice.alternatives) { + for (const auto& [name, signature] : choice.Alternatives()) { auto t = InterpExp(tops->values, signature); alts.push_back(std::make_pair(name, t)); } - auto ct = global_arena->New(choice.name, std::move(alts)); + auto ct = global_arena->New(choice.Name(), std::move(alts)); Address a = state->heap.AllocateValue(ct); - tops->values.Set(choice.name, a); // Is this obsolete? - tops->types.Set(choice.name, ct); + tops->values.Set(choice.Name(), a); // Is this obsolete? + tops->types.Set(choice.Name(), ct); break; } - case DeclarationKind::VariableDeclaration: { - const auto& var = d.GetVariableDeclaration(); + case Declaration::Kind::VariableDeclaration: { + const auto& var = cast(d); // Associate the variable name with it's declared type in the // compile-time symbol table. const Expression* type = - cast(var.binding->Type())->Expression(); + cast(var.Binding()->Type())->Expression(); const Value* declared_type = InterpExp(tops->values, type); - tops->types.Set(*var.binding->Name(), declared_type); + tops->types.Set(*var.Binding()->Name(), declared_type); break; } } } -auto TopLevel(std::list* fs) -> TypeCheckContext { +auto TopLevel(const std::list& fs) -> TypeCheckContext { TypeCheckContext tops; bool found_main = false; - for (auto const& d : *fs) { - if (GetName(d) == "main") { + for (auto const& d : fs) { + if (GetName(*d) == "main") { found_main = true; } - TopLevel(d, &tops); + TopLevel(*d, &tops); } if (found_main == false) { diff --git a/executable_semantics/interpreter/typecheck.h b/executable_semantics/interpreter/typecheck.h index afc7f77f6ee7..02649b3ea71a 100644 --- a/executable_semantics/interpreter/typecheck.h +++ b/executable_semantics/interpreter/typecheck.h @@ -51,8 +51,8 @@ auto TypeCheckFunDef(struct FunctionDefinition*, TypeEnv) -> struct FunctionDefinition*; auto MakeTypeChecked(const Declaration& decl, const TypeEnv& types, - const Env& values) -> Declaration; -auto TopLevel(std::list* fs) -> TypeCheckContext; + const Env& values) -> const Declaration*; +auto TopLevel(const std::list& fs) -> TypeCheckContext; } // namespace Carbon diff --git a/executable_semantics/main.cpp b/executable_semantics/main.cpp index 782b5b7a3bd9..30ca2d0529bd 100644 --- a/executable_semantics/main.cpp +++ b/executable_semantics/main.cpp @@ -39,5 +39,5 @@ int main(int argc, char* argv[]) { } // Typecheck and run the parsed program. - Carbon::ExecProgram(&std::get(ast_or_error)); + Carbon::ExecProgram(std::get(ast_or_error)); } diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 05584a1d04c3..3dd9780f6f13 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -91,10 +91,10 @@ void Carbon::Parser::error(const location_type&, const std::string& message) { %token identifier %token sized_type_literal %type designator -%type declaration +%type declaration %type function_declaration %type function_definition -%type > declaration_list +%type > declaration_list %type statement %type if_statement %type optional_else @@ -558,25 +558,25 @@ alternative_list: ; declaration: function_definition - { $$ = Declaration::MakeFunctionDeclaration(std::move($1)); } + { $$ = global_arena->New(std::move($1)); } | function_declaration - { $$ = Declaration::MakeFunctionDeclaration(std::move($1)); } + { $$ = global_arena->New(std::move($1)); } | STRUCT identifier "{" member_list "}" { - $$ = Declaration::MakeStructDeclaration(yylineno, $2, $4); + $$ = global_arena->New(yylineno, $2, $4); } | CHOICE identifier "{" alternative_list "}" { - $$ = Declaration::MakeChoiceDeclaration(yylineno, $2, $4); + $$ = global_arena->New(yylineno, $2, $4); } | VAR variable_declaration "=" expression ";" { - $$ = Declaration::MakeVariableDeclaration(yylineno, $2, $4); + $$ = global_arena->New(yylineno, $2, $4); } ; declaration_list: // Empty - { $$ = std::list(); } + { $$ = std::list(); } | declaration declaration_list { $$ = $2; diff --git a/executable_semantics/syntax/syntax_helpers.cpp b/executable_semantics/syntax/syntax_helpers.cpp index da8ae39eeba7..d4d084dfdf56 100644 --- a/executable_semantics/syntax/syntax_helpers.cpp +++ b/executable_semantics/syntax/syntax_helpers.cpp @@ -12,11 +12,11 @@ namespace Carbon { -void ExecProgram(std::list* fs) { +void ExecProgram(const std::list& fs) { if (tracing_output) { llvm::outs() << "********** source program **********\n"; - for (const auto& decl : *fs) { - llvm::outs() << decl; + for (const auto* decl : fs) { + llvm::outs() << *decl; } llvm::outs() << "********** type checking **********\n"; } @@ -24,19 +24,19 @@ void ExecProgram(std::list* fs) { TypeCheckContext p = TopLevel(fs); TypeEnv top = p.types; Env ct_top = p.values; - std::list new_decls; - for (const auto& decl : *fs) { - new_decls.push_back(MakeTypeChecked(decl, top, ct_top)); + std::list new_decls; + for (const auto* decl : fs) { + new_decls.push_back(MakeTypeChecked(*decl, top, ct_top)); } if (tracing_output) { llvm::outs() << "\n"; llvm::outs() << "********** type checking complete **********\n"; - for (const auto& decl : new_decls) { - llvm::outs() << decl; + for (const auto* decl : new_decls) { + llvm::outs() << *decl; } llvm::outs() << "********** starting execution **********\n"; } - int result = InterpProgram(&new_decls); + int result = InterpProgram(new_decls); llvm::outs() << "result: " << result << "\n"; } diff --git a/executable_semantics/syntax/syntax_helpers.h b/executable_semantics/syntax/syntax_helpers.h index c3916684fc2e..2e6c8284a345 100644 --- a/executable_semantics/syntax/syntax_helpers.h +++ b/executable_semantics/syntax/syntax_helpers.h @@ -16,7 +16,7 @@ namespace Carbon { // Runs the top-level declaration list. -void ExecProgram(std::list* fs); +void ExecProgram(const std::list& fs); } // namespace Carbon