Store named constant values in the AST (#1011)

This enables us to stop using `Env` in the typechecker. As a byproduct, this commit also restructures the interpreter to handle run-time global initialization as part of ordinary execution, using the Action stack.
This commit is contained in:
Geoff Romer
2022-01-13 11:07:28 -08:00
committed by GitHub
parent d88f95ad31
commit 461e178273
15 changed files with 309 additions and 242 deletions
+44
View File
@@ -108,11 +108,22 @@ class GenericBinding : public AstNode {
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
Nonnull<Expression*> type_;
std::optional<Nonnull<const Value*>> static_type_;
std::optional<Nonnull<const Value*>> constant_value_;
};
// The syntactic representation of a function declaration's return type.
@@ -232,6 +243,16 @@ class FunctionDeclaration : public Declaration {
auto body() -> std::optional<Nonnull<Block*>> { return body_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
@@ -239,6 +260,7 @@ class FunctionDeclaration : public Declaration {
Nonnull<TuplePattern*> param_pattern_;
ReturnTerm return_term_;
std::optional<Nonnull<Block*>> body_;
std::optional<Nonnull<const Value*>> constant_value_;
};
class ClassDeclaration : public Declaration {
@@ -259,10 +281,21 @@ class ClassDeclaration : public Declaration {
auto members() const -> llvm::ArrayRef<Nonnull<Member*>> { return members_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
std::vector<Nonnull<Member*>> members_;
std::optional<Nonnull<const Value*>> constant_value_;
};
class AlternativeSignature : public AstNode {
@@ -312,10 +345,21 @@ class ChoiceDeclaration : public Declaration {
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
std::vector<Nonnull<AlternativeSignature*>> alternatives_;
std::optional<Nonnull<const Value*>> constant_value_;
};
// Global variable definition implements the Declaration concept.
+5
View File
@@ -61,6 +61,7 @@ class Pattern : public AstNode {
auto has_static_type() const -> bool { return static_type_.has_value(); }
// The value of this pattern. Cannot be called before typechecking.
// TODO rename to avoid confusion with BindingPattern::constant_value
auto value() const -> const Value& { return **value_; }
// Sets the value of this pattern. Can only be called once, during
@@ -122,6 +123,10 @@ class BindingPattern : public Pattern {
auto value_category() const -> ValueCategory { return ValueCategory::Var; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
private:
std::string name_;
Nonnull<Pattern*> type_;
+3
View File
@@ -344,6 +344,9 @@ class Continuation : public Statement {
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto value_category() const -> ValueCategory { return ValueCategory::Var; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
private:
std::string name_;
+16 -1
View File
@@ -37,6 +37,10 @@ static constexpr std::string_view AnonymousName = "_";
// Returns the name of an IdentifierExpression that names *this. If *this
// is anonymous, returns AnonymousName.
auto name() const -> std::string_view;
// If *this names a compile-time constant whose value is known, returns that
// value. Otherwise returns std::nullopt.
auto constant_value() const -> std::optional<Nonnull<const Value*>>;
*/
// NodeType must be derived from AstNode.
//
@@ -70,7 +74,11 @@ class NamedEntityView {
}),
value_category_([](const AstNode& base) -> ValueCategory {
return llvm::cast<NodeType>(base).value_category();
}) {
}),
constant_value_(
[](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
return llvm::cast<NodeType>(base).constant_value();
}) {
CHECK(node->name() != AnonymousName)
<< "Entity with no name used as NamedEntity: " << *node;
}
@@ -94,6 +102,11 @@ class NamedEntityView {
return value_category_(*base_);
}
// Returns node->constant_value()
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_(*base_);
}
friend auto operator==(const NamedEntityView& lhs, const NamedEntityView& rhs)
-> bool {
return lhs.base_ == rhs.base_;
@@ -114,6 +127,8 @@ class NamedEntityView {
std::function<std::string_view(const AstNode&)> name_;
std::function<const Value&(const AstNode&)> static_type_;
std::function<ValueCategory(const AstNode&)> value_category_;
std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
constant_value_;
};
// Maps the names visible in a given scope to the entities they name.
+1
View File
@@ -130,6 +130,7 @@ cc_library(
":stack",
"//common:check",
"//common:ostream",
"//executable_semantics/ast",
"//executable_semantics/ast:declaration",
"//executable_semantics/ast:expression",
"//executable_semantics/ast:pattern",
@@ -55,6 +55,9 @@ void Action::Print(llvm::raw_ostream& out) const {
case Action::Kind::StatementAction:
cast<StatementAction>(*this).statement().PrintDepth(1, out);
break;
case Action::Kind::DeclarationAction:
cast<DeclarationAction>(*this).declaration().Print(out);
break;
case Action::Kind::ScopeAction:
out << "ScopeAction";
}
+19
View File
@@ -65,6 +65,7 @@ class Action {
ExpressionAction,
PatternAction,
StatementAction,
DeclarationAction,
ScopeAction,
};
@@ -203,6 +204,24 @@ class StatementAction : public Action {
Nonnull<const Statement*> statement_;
};
// Action which implements the run-time effects of executing a Declaration.
// Does not produce a result.
class DeclarationAction : public Action {
public:
explicit DeclarationAction(Nonnull<const Declaration*> declaration)
: Action(Kind::DeclarationAction), declaration_(declaration) {}
static auto classof(const Action* action) -> bool {
return action->kind() == Kind::DeclarationAction;
}
// The Declaration this Action executes.
auto declaration() const -> const Declaration& { return *declaration_; }
private:
Nonnull<const Declaration*> declaration_;
};
// Action which does nothing except introduce a new scope into the action
// stack. This is useful when a distinct scope doesn't otherwise have an
// Action it can naturally be associated with. ScopeActions are not associated
@@ -17,10 +17,10 @@ void ActionStack::Print(llvm::raw_ostream& out) const {
}
}
void ActionStack::Start(std::unique_ptr<Action> action, Scope scope) {
void ActionStack::Start(std::unique_ptr<Action> action) {
result_ = std::nullopt;
CHECK(todo_.IsEmpty());
todo_ = {};
todo_.Push(std::make_unique<ScopeAction>(std::move(scope)));
todo_.Push(std::move(action));
}
@@ -30,7 +30,7 @@ auto ActionStack::CurrentScope() const -> Scope& {
return *action->scope();
}
}
FATAL() << "No current scope";
return globals_;
}
void ActionStack::FinishAction() {
@@ -39,12 +39,12 @@ void ActionStack::FinishAction() {
case Action::Kind::ExpressionAction:
case Action::Kind::LValAction:
case Action::Kind::PatternAction:
FATAL() << "This kind of action must produce a result.";
FATAL() << "This kind of action must produce a result: " << *act;
case Action::Kind::ScopeAction:
FATAL() << "ScopeAction at top of stack";
case Action::Kind::StatementAction:
case Action::Kind::DeclarationAction:
PopScopes();
CHECK(!IsEmpty());
}
}
@@ -52,7 +52,8 @@ void ActionStack::FinishAction(Nonnull<const Value*> result) {
std::unique_ptr<Action> act = todo_.Pop();
switch (act->kind()) {
case Action::Kind::StatementAction:
FATAL() << "Statements cannot produce results.";
case Action::Kind::DeclarationAction:
FATAL() << "This kind of Action cannot produce results: " << *act;
case Action::Kind::ScopeAction:
FATAL() << "ScopeAction at top of stack";
case Action::Kind::ExpressionAction:
@@ -19,14 +19,17 @@ namespace Carbon {
class ActionStack {
public:
// Constructs an empty ActionStack
ActionStack() = default;
explicit ActionStack(Scope globals) : globals_(std::move(globals)) {}
void Print(llvm::raw_ostream& out) const;
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
// Starts execution with `action` at the top of the stack, in the given scope.
// `action` must be an `ExpressionAction` or `PatternAction`.
void Start(std::unique_ptr<Action> action, Scope scope);
// Returns an Env containing the currently-defined global variables.
auto GlobalEnv() const -> Env { return globals_.values(); }
// Starts execution with `action` at the top of the stack. Cannot be called
// when IsEmpty() is false.
void Start(std::unique_ptr<Action> action);
// True if the stack is empty.
auto IsEmpty() const -> bool { return todo_.IsEmpty(); }
@@ -97,6 +100,7 @@ class ActionStack {
// TODO: consider defining a non-nullable unique_ptr-like type to use here.
Stack<std::unique_ptr<Action>> todo_;
std::optional<Nonnull<const Value*>> result_;
mutable Scope globals_;
};
} // namespace Carbon
@@ -41,8 +41,7 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
}
llvm::outs() << "********** starting execution **********\n";
}
int result =
Interpreter(arena, trace).InterpProgram(ast.declarations, *ast.main_call);
int result = Interpreter(arena, trace).InterpProgram(ast);
llvm::outs() << "result: " << result << "\n";
}
+63 -103
View File
@@ -96,80 +96,6 @@ auto Interpreter::EvalPrim(Operator op,
}
}
void Interpreter::InitEnv(const Declaration& d, Env* env) {
switch (d.kind()) {
case DeclarationKind::FunctionDeclaration: {
const auto& func_def = cast<FunctionDeclaration>(d);
Env new_env = *env;
// Bring the deduced parameters into scope.
for (Nonnull<const GenericBinding*> deduced :
func_def.deduced_parameters()) {
AllocationId a =
heap_.AllocateValue(arena_->New<VariableType>(deduced));
new_env.Set(deduced->name(), a);
}
Nonnull<const FunctionValue*> f = arena_->New<FunctionValue>(&func_def);
AllocationId a = heap_.AllocateValue(f);
env->Set(func_def.name(), a);
break;
}
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(d);
std::vector<NamedValue> fields;
std::vector<NamedValue> methods;
for (Nonnull<const Member*> m : class_decl.members()) {
switch (m->kind()) {
case MemberKind::FieldMember: {
const BindingPattern& binding = cast<FieldMember>(*m).binding();
const Expression& type_expression =
cast<ExpressionPattern>(binding.type()).expression();
auto type = InterpExp(Env(arena_), &type_expression);
fields.push_back({.name = binding.name(), .value = type});
break;
}
}
}
auto st = arena_->New<NominalClassType>(
class_decl.name(), std::move(fields), std::move(methods));
AllocationId a = heap_.AllocateValue(st);
env->Set(class_decl.name(), a);
break;
}
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(d);
std::vector<NamedValue> alts;
for (Nonnull<const AlternativeSignature*> alternative :
choice.alternatives()) {
auto t = InterpExp(Env(arena_), &alternative->signature());
alts.push_back({.name = alternative->name(), .value = t});
}
auto ct = arena_->New<ChoiceType>(choice.name(), std::move(alts));
AllocationId a = heap_.AllocateValue(ct);
env->Set(choice.name(), a);
break;
}
case DeclarationKind::VariableDeclaration: {
const auto& var = cast<VariableDeclaration>(d);
// Adds an entry in `globals` mapping the variable's name to the
// result of evaluating the initializer.
Nonnull<const Value*> v =
Convert(InterpExp(*env, &var.initializer()), &var.static_type());
AllocationId a = heap_.AllocateValue(v);
env->Set(var.binding().name(), a);
break;
}
}
}
void Interpreter::InitGlobals(llvm::ArrayRef<Nonnull<Declaration*>> fs) {
for (const auto d : fs) {
InitEnv(*d, &globals_);
}
}
auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
-> Nonnull<const Value*> {
@@ -521,6 +447,11 @@ void Interpreter::StepExp() {
<< "Identifier '" << exp << "' at " << exp.source_loc()
<< " was not resolved";
// { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
if (std::optional<Nonnull<const Value*>> value =
ident.named_entity().constant_value();
value.has_value()) {
return todo_.FinishAction(*value);
}
Address pointer = GetFromEnv(exp.source_loc(), ident.name());
return todo_.FinishAction(heap_.Read(pointer, exp.source_loc()));
}
@@ -579,7 +510,7 @@ void Interpreter::StepExp() {
exp.source_loc());
CHECK(matches.has_value())
<< "internal error in call_function, pattern match failed";
Scope new_scope(globals_, &heap_);
Scope new_scope(todo_.GlobalEnv(), &heap_);
for (const auto& [name, value] : *matches) {
new_scope.AddLocal(name, value);
}
@@ -948,6 +879,32 @@ void Interpreter::StepStmt() {
}
}
void Interpreter::StepDeclaration() {
Action& act = todo_.CurrentAction();
const Declaration& decl = cast<DeclarationAction>(act).declaration();
if (trace_) {
llvm::outs() << "--- step declaration (" << decl.source_loc() << ") --->\n";
}
switch (decl.kind()) {
case DeclarationKind::VariableDeclaration: {
const auto& var_decl = cast<VariableDeclaration>(decl);
if (act.pos() == 0) {
return todo_.Spawn(
std::make_unique<ExpressionAction>(&var_decl.initializer()));
} else {
todo_.CurrentScope().AddLocal(var_decl.binding().name(),
heap_.AllocateValue(act.results()[0]));
return todo_.FinishAction();
}
}
case DeclarationKind::FunctionDeclaration:
case DeclarationKind::ClassDeclaration:
case DeclarationKind::ChoiceDeclaration:
// These declarations have no run-time effects.
return todo_.FinishAction();
}
}
// State transition.
void Interpreter::Step() {
Action& act = todo_.CurrentAction();
@@ -964,62 +921,65 @@ void Interpreter::Step() {
case Action::Kind::StatementAction:
StepStmt();
break;
case Action::Kind::DeclarationAction:
StepDeclaration();
break;
case Action::Kind::ScopeAction:
FATAL() << "ScopeAction escaped ActionStack";
} // switch
}
auto Interpreter::ExecuteAction(std::unique_ptr<Action> action, Env values,
bool trace_steps) -> Nonnull<const Value*> {
todo_.Start(std::move(action), Scope(values, &heap_));
void Interpreter::RunAllSteps(bool trace_steps) {
while (!todo_.IsEmpty()) {
Step();
if (trace_steps) {
PrintState(llvm::outs());
}
}
// Clean up any remaining suspended continuations.
for (Nonnull<ContinuationValue::StackFragment*> fragment : stack_fragments_) {
fragment->Clear();
}
return todo_.result();
}
auto Interpreter::InterpProgram(llvm::ArrayRef<Nonnull<Declaration*>> fs,
Nonnull<const Expression*> call_main) -> int {
// Check that the interpreter is in a clean state.
CHECK(globals_.IsEmpty());
CHECK(todo_.IsEmpty());
auto Interpreter::InterpProgram(const AST& ast) -> int {
if (trace_) {
llvm::outs() << "********** initializing globals **********\n";
}
InitGlobals(fs);
for (Nonnull<Declaration*> declaration : ast.declarations) {
todo_.Start(std::make_unique<DeclarationAction>(declaration));
RunAllSteps(trace_);
}
if (trace_) {
llvm::outs() << "********** calling main function **********\n";
PrintState(llvm::outs());
}
return cast<IntValue>(
*ExecuteAction(std::make_unique<ExpressionAction>(call_main),
globals_, trace_))
.value();
todo_.Start(std::make_unique<ExpressionAction>(*ast.main_call));
RunAllSteps(trace_);
// Clean up any remaining suspended continuations.
for (Nonnull<ContinuationValue::StackFragment*> fragment : stack_fragments_) {
fragment->Clear();
}
return cast<IntValue>(*todo_.result()).value();
}
auto Interpreter::InterpExp(Env values, Nonnull<const Expression*> e)
auto Interpreter::RunCompileTimeAction(std::unique_ptr<Action> action)
-> Nonnull<const Value*> {
return ExecuteAction(std::make_unique<ExpressionAction>(e), values,
/*trace_steps=*/false);
todo_.Start(std::move(action));
RunAllSteps(/*trace_steps=*/false);
CHECK(stack_fragments_.empty());
return todo_.result();
}
auto Interpreter::InterpPattern(Env values, Nonnull<const Pattern*> p)
auto Interpreter::InterpExp(Nonnull<const Expression*> e)
-> Nonnull<const Value*> {
return ExecuteAction(std::make_unique<PatternAction>(p), values,
/*trace_steps=*/false);
return RunCompileTimeAction(std::make_unique<ExpressionAction>(e));
}
auto Interpreter::InterpPattern(Nonnull<const Pattern*> p)
-> Nonnull<const Value*> {
return RunCompileTimeAction(std::make_unique<PatternAction>(p));
}
} // namespace Carbon
+21 -21
View File
@@ -10,6 +10,7 @@
#include <vector>
#include "common/ostream.h"
#include "executable_semantics/ast/ast.h"
#include "executable_semantics/ast/declaration.h"
#include "executable_semantics/ast/expression.h"
#include "executable_semantics/ast/pattern.h"
@@ -24,19 +25,19 @@ namespace Carbon {
class Interpreter {
public:
explicit Interpreter(Nonnull<Arena*> arena, bool trace)
: arena_(arena), globals_(arena), heap_(arena), trace_(trace) {}
: arena_(arena),
heap_(arena),
todo_(Scope(Env(arena_), &heap_)),
trace_(trace) {}
// Interpret the whole program.
auto InterpProgram(llvm::ArrayRef<Nonnull<Declaration*>> fs,
Nonnull<const Expression*> call_main) -> int;
auto InterpProgram(const AST& ast) -> int;
// Interpret an expression at compile-time.
auto InterpExp(Env values, Nonnull<const Expression*> e)
-> Nonnull<const Value*>;
auto InterpExp(Nonnull<const Expression*> e) -> Nonnull<const Value*>;
// Interpret a pattern at compile-time.
auto InterpPattern(Env values, Nonnull<const Pattern*> p)
-> Nonnull<const Value*>;
auto InterpPattern(Nonnull<const Pattern*> p) -> Nonnull<const Value*>;
// Attempts to match `v` against the pattern `p`. If matching succeeds,
// returns the bindings of pattern variables to their matched values.
@@ -48,7 +49,6 @@ class Interpreter {
return heap_.AllocateValue(v);
}
void InitEnv(const Declaration& d, Env* env);
void PrintEnv(Env values, llvm::raw_ostream& out);
private:
@@ -62,12 +62,17 @@ class Interpreter {
void StepPattern();
// State transition for statements.
void StepStmt();
// State transition for declarations.
void StepDeclaration();
void InitGlobals(llvm::ArrayRef<Nonnull<Declaration*>> fs);
auto CurrentEnv() -> Env;
auto GetFromEnv(SourceLocation source_loc, const std::string& name)
-> Address;
// Calls Step() repeatedly until there are no steps left to execute. Produces
// trace output if trace_steps is true.
void RunAllSteps(bool trace_steps);
auto CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
-> Nonnull<const Value*>;
@@ -82,22 +87,17 @@ class Interpreter {
void PrintState(llvm::raw_ostream& out);
// Runs `action` in a scope consisting of `values`, and returns the result.
// `action` must produce a result. In other words, it must not be a
// StatementAction or ScopeAction.
//
// TODO: consider whether to use this->trace_ rather than a separate
// trace_steps parameter.
auto ExecuteAction(std::unique_ptr<Action> action, Env values,
bool trace_steps) -> Nonnull<const Value*>;
// Runs `action` in an environment where the given constants are defined, and
// returns the result. `action` must produce a result. In other words, it must
// not be a StatementAction, ScopeAction, or DeclarationAction. Can only be
// called at compile time (before InterpProgram), and while `todo_` is empty.
auto RunCompileTimeAction(std::unique_ptr<Action> action)
-> Nonnull<const Value*>;
Nonnull<Arena*> arena_;
// Globally-defined entities, such as functions, structs, or choices.
Env globals_;
ActionStack todo_;
Heap heap_;
ActionStack todo_;
// The underlying states of continuation values. All StackFragments created
// during execution are tracked here, in order to safely deallocate the
@@ -390,23 +390,22 @@ auto TypeChecker::Substitute(
}
}
void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
if (trace_) {
llvm::outs() << "checking expression " << *e << "\nvalues: ";
interpreter_.PrintEnv(values, llvm::outs());
llvm::outs() << "checking expression " << *e << "\nconstants: ";
PrintConstants(llvm::outs());
llvm::outs() << "\n";
}
switch (e->kind()) {
case ExpressionKind::IndexExpression: {
auto& index = cast<IndexExpression>(*e);
TypeCheckExp(&index.aggregate(), values);
TypeCheckExp(&index.aggregate());
const Value& aggregate_type = index.aggregate().static_type();
switch (aggregate_type.kind()) {
case Value::Kind::TupleValue: {
const auto& tuple_type = cast<TupleValue>(aggregate_type);
int i =
cast<IntValue>(*interpreter_.InterpExp(values, &index.offset()))
.value();
cast<IntValue>(*interpreter_.InterpExp(&index.offset())).value();
if (i < 0 || i >= static_cast<int>(tuple_type.elements().size())) {
FATAL_COMPILATION_ERROR(e->source_loc())
<< "index " << i << " is out of range for type " << tuple_type;
@@ -422,7 +421,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
case ExpressionKind::TupleLiteral: {
std::vector<Nonnull<const Value*>> arg_types;
for (auto& arg : cast<TupleLiteral>(*e).fields()) {
TypeCheckExp(arg, values);
TypeCheckExp(arg);
arg_types.push_back(&arg->static_type());
}
SetStaticType(e, arena_->New<TupleValue>(std::move(arg_types)));
@@ -432,7 +431,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
case ExpressionKind::StructLiteral: {
std::vector<NamedValue> arg_types;
for (auto& arg : cast<StructLiteral>(*e).fields()) {
TypeCheckExp(&arg.expression(), values);
TypeCheckExp(&arg.expression());
arg_types.push_back({arg.name(), &arg.expression().static_type()});
}
SetStaticType(e, arena_->New<StructType>(std::move(arg_types)));
@@ -442,9 +441,9 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
case ExpressionKind::StructTypeLiteral: {
auto& struct_type = cast<StructTypeLiteral>(*e);
for (auto& arg : struct_type.fields()) {
TypeCheckExp(&arg.expression(), values);
TypeCheckExp(&arg.expression());
ExpectIsConcreteType(arg.expression().source_loc(),
interpreter_.InterpExp(values, &arg.expression()));
interpreter_.InterpExp(&arg.expression()));
}
if (struct_type.fields().empty()) {
// `{}` is the type of `{}`, just as `()` is the type of `()`.
@@ -460,7 +459,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
}
case ExpressionKind::FieldAccessExpression: {
auto& access = cast<FieldAccessExpression>(*e);
TypeCheckExp(&access.aggregate(), values);
TypeCheckExp(&access.aggregate());
const Value& aggregate_type = access.aggregate().static_type();
switch (aggregate_type.kind()) {
case Value::Kind::StructType: {
@@ -549,7 +548,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
auto& op = cast<PrimitiveOperatorExpression>(*e);
std::vector<Nonnull<const Value*>> ts;
for (Nonnull<Expression*> argument : op.arguments()) {
TypeCheckExp(argument, values);
TypeCheckExp(argument);
ts.push_back(&argument->static_type());
}
switch (op.op()) {
@@ -624,11 +623,11 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
}
case ExpressionKind::CallExpression: {
auto& call = cast<CallExpression>(*e);
TypeCheckExp(&call.function(), values);
TypeCheckExp(&call.function());
switch (call.function().static_type().kind()) {
case Value::Kind::FunctionType: {
const auto& fun_t = cast<FunctionType>(call.function().static_type());
TypeCheckExp(&call.argument(), values);
TypeCheckExp(&call.argument());
Nonnull<const Value*> parameters = &fun_t.parameters();
Nonnull<const Value*> return_type = &fun_t.return_type();
if (!fun_t.deduced().empty()) {
@@ -668,9 +667,9 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
case ExpressionKind::FunctionTypeLiteral: {
auto& fn = cast<FunctionTypeLiteral>(*e);
ExpectIsConcreteType(fn.parameter().source_loc(),
interpreter_.InterpExp(values, &fn.parameter()));
interpreter_.InterpExp(&fn.parameter()));
ExpectIsConcreteType(fn.return_type().source_loc(),
interpreter_.InterpExp(values, &fn.return_type()));
interpreter_.InterpExp(&fn.return_type()));
SetStaticType(&fn, arena_->New<TypeType>());
fn.set_value_category(ValueCategory::Let);
return;
@@ -681,7 +680,7 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
return;
case ExpressionKind::IntrinsicExpression: {
auto& intrinsic_exp = cast<IntrinsicExpression>(*e);
TypeCheckExp(&intrinsic_exp.args(), values);
TypeCheckExp(&intrinsic_exp.args());
switch (cast<IntrinsicExpression>(*e).intrinsic()) {
case IntrinsicExpression::Intrinsic::Print:
if (intrinsic_exp.args().fields().size() != 1) {
@@ -710,15 +709,14 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e, Env values) {
}
void TypeChecker::TypeCheckPattern(
Nonnull<Pattern*> p, Env values,
std::optional<Nonnull<const Value*>> expected) {
Nonnull<Pattern*> p, std::optional<Nonnull<const Value*>> expected) {
if (trace_) {
llvm::outs() << "checking pattern " << *p;
if (expected) {
llvm::outs() << ", expecting " << **expected;
}
llvm::outs() << "\nvalues: ";
interpreter_.PrintEnv(values, llvm::outs());
llvm::outs() << "\nconstants: ";
PrintConstants(llvm::outs());
llvm::outs() << "\n";
}
switch (p->kind()) {
@@ -728,9 +726,8 @@ void TypeChecker::TypeCheckPattern(
}
case PatternKind::BindingPattern: {
auto& binding = cast<BindingPattern>(*p);
TypeCheckPattern(&binding.type(), values, std::nullopt);
Nonnull<const Value*> type =
interpreter_.InterpPattern(values, &binding.type());
TypeCheckPattern(&binding.type(), std::nullopt);
Nonnull<const Value*> type = interpreter_.InterpPattern(&binding.type());
if (expected) {
if (IsConcreteType(type)) {
ExpectType(p->source_loc(), "name binding", type, *expected);
@@ -742,14 +739,12 @@ void TypeChecker::TypeCheckPattern(
<< "Type pattern '" << *type << "' does not match actual type '"
<< **expected << "'";
}
CHECK(values->begin() == values->end())
<< "Name bindings within type patterns are unsupported";
type = *expected;
}
}
ExpectIsConcreteType(binding.source_loc(), type);
SetStaticType(&binding, type);
SetValue(&binding, interpreter_.InterpPattern(values, &binding));
SetValue(&binding, interpreter_.InterpPattern(&binding));
return;
}
case PatternKind::TuplePattern: {
@@ -769,16 +764,16 @@ void TypeChecker::TypeCheckPattern(
if (expected) {
expected_field_type = cast<TupleValue>(**expected).elements()[i];
}
TypeCheckPattern(field, values, expected_field_type);
TypeCheckPattern(field, expected_field_type);
field_types.push_back(&field->static_type());
}
SetStaticType(&tuple, arena_->New<TupleValue>(std::move(field_types)));
SetValue(&tuple, interpreter_.InterpPattern(values, &tuple));
SetValue(&tuple, interpreter_.InterpPattern(&tuple));
return;
}
case PatternKind::AlternativePattern: {
auto& alternative = cast<AlternativePattern>(*p);
TypeCheckExp(&alternative.choice_type(), values);
TypeCheckExp(&alternative.choice_type());
if (alternative.choice_type().static_type().kind() !=
Value::Kind::TypeOfChoiceType) {
FATAL_COMPILATION_ERROR(alternative.source_loc())
@@ -799,41 +794,40 @@ void TypeChecker::TypeCheckPattern(
<< "'" << alternative.alternative_name()
<< "' is not an alternative of " << choice_type;
}
TypeCheckPattern(&alternative.arguments(), values, *parameter_types);
TypeCheckPattern(&alternative.arguments(), *parameter_types);
SetStaticType(&alternative, &choice_type);
SetValue(&alternative, interpreter_.InterpPattern(values, &alternative));
SetValue(&alternative, interpreter_.InterpPattern(&alternative));
return;
}
case PatternKind::ExpressionPattern: {
auto& expression = cast<ExpressionPattern>(*p).expression();
TypeCheckExp(&expression, values);
TypeCheckExp(&expression);
SetStaticType(p, &expression.static_type());
SetValue(p, interpreter_.InterpPattern(values, p));
SetValue(p, interpreter_.InterpPattern(p));
return;
}
}
}
void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s, Env values) {
void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s) {
switch (s->kind()) {
case StatementKind::Match: {
auto& match = cast<Match>(*s);
TypeCheckExp(&match.expression(), values);
TypeCheckExp(&match.expression());
std::vector<Match::Clause> new_clauses;
for (auto& clause : match.clauses()) {
TypeCheckPattern(&clause.pattern(), values,
&match.expression().static_type());
TypeCheckStmt(&clause.statement(), values);
TypeCheckPattern(&clause.pattern(), &match.expression().static_type());
TypeCheckStmt(&clause.statement());
}
return;
}
case StatementKind::While: {
auto& while_stmt = cast<While>(*s);
TypeCheckExp(&while_stmt.condition(), values);
TypeCheckExp(&while_stmt.condition());
ExpectType(s->source_loc(), "condition of `while`",
arena_->New<BoolType>(),
&while_stmt.condition().static_type());
TypeCheckStmt(&while_stmt.body(), values);
TypeCheckStmt(&while_stmt.body());
return;
}
case StatementKind::Break:
@@ -842,21 +836,21 @@ void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s, Env values) {
case StatementKind::Block: {
auto& block = cast<Block>(*s);
for (auto* block_statement : block.statements()) {
TypeCheckStmt(block_statement, values);
TypeCheckStmt(block_statement);
}
return;
}
case StatementKind::VariableDefinition: {
auto& var = cast<VariableDefinition>(*s);
TypeCheckExp(&var.init(), values);
TypeCheckExp(&var.init());
const Value& rhs_ty = var.init().static_type();
TypeCheckPattern(&var.pattern(), values, &rhs_ty);
TypeCheckPattern(&var.pattern(), &rhs_ty);
return;
}
case StatementKind::Assign: {
auto& assign = cast<Assign>(*s);
TypeCheckExp(&assign.rhs(), values);
TypeCheckExp(&assign.lhs(), values);
TypeCheckExp(&assign.rhs());
TypeCheckExp(&assign.lhs());
ExpectType(s->source_loc(), "assign", &assign.lhs().static_type(),
&assign.rhs().static_type());
if (assign.lhs().value_category() != ValueCategory::Var) {
@@ -866,23 +860,23 @@ void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s, Env values) {
return;
}
case StatementKind::ExpressionStatement: {
TypeCheckExp(&cast<ExpressionStatement>(*s).expression(), values);
TypeCheckExp(&cast<ExpressionStatement>(*s).expression());
return;
}
case StatementKind::If: {
auto& if_stmt = cast<If>(*s);
TypeCheckExp(&if_stmt.condition(), values);
TypeCheckExp(&if_stmt.condition());
ExpectType(s->source_loc(), "condition of `if`", arena_->New<BoolType>(),
&if_stmt.condition().static_type());
TypeCheckStmt(&if_stmt.then_block(), values);
TypeCheckStmt(&if_stmt.then_block());
if (if_stmt.else_block()) {
TypeCheckStmt(*if_stmt.else_block(), values);
TypeCheckStmt(*if_stmt.else_block());
}
return;
}
case StatementKind::Return: {
auto& ret = cast<Return>(*s);
TypeCheckExp(&ret.expression(), values);
TypeCheckExp(&ret.expression());
ReturnTerm& return_term = ret.function().return_term();
if (return_term.is_auto()) {
SetStaticType(&return_term, &ret.expression().static_type());
@@ -894,13 +888,13 @@ void TypeChecker::TypeCheckStmt(Nonnull<Statement*> s, Env values) {
}
case StatementKind::Continuation: {
auto& cont = cast<Continuation>(*s);
TypeCheckStmt(&cont.body(), values);
TypeCheckStmt(&cont.body());
SetStaticType(&cont, arena_->New<ContinuationType>());
return;
}
case StatementKind::Run: {
auto& run = cast<Run>(*s);
TypeCheckExp(&run.argument(), values);
TypeCheckExp(&run.argument());
ExpectType(s->source_loc(), "argument of `run`",
arena_->New<ContinuationType>(),
&run.argument().static_type());
@@ -988,17 +982,16 @@ void TypeChecker::ExpectReturnOnAllPaths(
// TODO: Add checking to function definitions to ensure that
// all deduced type parameters will be deduced.
void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
Env values, bool check_body) {
bool check_body) {
// Bring the deduced parameters into scope
for (Nonnull<GenericBinding*> deduced : f->deduced_parameters()) {
TypeCheckExp(&deduced->type(), values);
TypeCheckExp(&deduced->type());
// auto t = interpreter_.InterpExp(values, deduced.type);
SetStaticType(deduced, arena_->New<VariableType>(deduced));
AllocationId a = interpreter_.AllocateValue(&deduced->static_type());
values.Set(deduced->name(), a);
SetConstantValue(deduced, &deduced->static_type());
}
// Type check the parameter pattern
TypeCheckPattern(&f->param_pattern(), values, std::nullopt);
TypeCheckPattern(&f->param_pattern(), std::nullopt);
// Evaluate the return type, if we can do so without examining the body.
if (std::optional<Nonnull<Expression*>> return_expression =
@@ -1006,9 +999,9 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
return_expression.has_value()) {
// We ignore the return value because return type expressions can't bring
// new types into scope.
TypeCheckExp(*return_expression, values);
TypeCheckExp(*return_expression);
SetStaticType(&f->return_term(),
interpreter_.InterpExp(values, *return_expression));
interpreter_.InterpExp(*return_expression));
} else if (f->return_term().is_omitted()) {
SetStaticType(&f->return_term(), TupleValue::Empty());
} else {
@@ -1021,7 +1014,7 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
}
if (f->body().has_value() && check_body) {
TypeCheckStmt(*f->body(), values);
TypeCheckStmt(*f->body());
if (!f->return_term().is_omitted()) {
ExpectReturnOnAllPaths(f->body(), f->source_loc());
}
@@ -1044,7 +1037,7 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
}
void TypeChecker::TypeCheckClassDeclaration(
Nonnull<ClassDeclaration*> class_decl, Env ct_top) {
Nonnull<ClassDeclaration*> class_decl) {
std::vector<NamedValue> fields;
std::vector<NamedValue> methods;
for (Nonnull<Member*> m : class_decl->members()) {
@@ -1055,7 +1048,7 @@ void TypeChecker::TypeCheckClassDeclaration(
FATAL_COMPILATION_ERROR(binding.source_loc())
<< "Struct members must have names";
}
TypeCheckPattern(&binding, ct_top, std::nullopt);
TypeCheckPattern(&binding, std::nullopt);
fields.push_back(
{.name = binding.name(), .value = &binding.static_type()});
break;
@@ -1068,12 +1061,12 @@ void TypeChecker::TypeCheckClassDeclaration(
class_decl->name(), std::move(fields), std::move(methods))));
}
void TypeChecker::TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
Env ct_top) {
void TypeChecker::TypeCheckChoiceDeclaration(
Nonnull<ChoiceDeclaration*> choice) {
std::vector<NamedValue> alternatives;
for (Nonnull<AlternativeSignature*> alternative : choice->alternatives()) {
TypeCheckExp(&alternative->signature(), ct_top);
auto signature = interpreter_.InterpExp(ct_top, &alternative->signature());
TypeCheckExp(&alternative->signature());
auto signature = interpreter_.InterpExp(&alternative->signature());
alternatives.push_back({.name = alternative->name(), .value = signature});
}
auto ct = arena_->New<ChoiceType>(choice->name(), std::move(alternatives));
@@ -1081,35 +1074,33 @@ void TypeChecker::TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
}
void TypeChecker::TypeCheck(AST& ast) {
Env values(arena_);
for (Nonnull<Declaration*> declaration : ast.declarations) {
TopLevel(declaration, &values);
TopLevel(declaration);
}
for (Nonnull<Declaration*> decl : ast.declarations) {
TypeCheckDeclaration(decl, values);
TypeCheckDeclaration(decl);
}
TypeCheckExp(*ast.main_call, values);
TypeCheckExp(*ast.main_call);
}
void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
const Env& values) {
void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d) {
switch (d->kind()) {
case DeclarationKind::FunctionDeclaration:
TypeCheckFunctionDeclaration(&cast<FunctionDeclaration>(*d), values,
TypeCheckFunctionDeclaration(&cast<FunctionDeclaration>(*d),
/*check_body=*/true);
return;
case DeclarationKind::ClassDeclaration:
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), values);
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d));
return;
case DeclarationKind::ChoiceDeclaration:
TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d), values);
TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d));
return;
case DeclarationKind::VariableDeclaration: {
auto& var = cast<VariableDeclaration>(*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.
TypeCheckExp(&var.initializer(), values);
TypeCheckExp(&var.initializer());
const auto* binding_type =
dyn_cast<ExpressionPattern>(&var.binding().type());
if (binding_type == nullptr) {
@@ -1118,7 +1109,7 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
<< "Type of a top-level variable must be an expression.";
}
Nonnull<const Value*> declared_type =
interpreter_.InterpExp(values, &binding_type->expression());
interpreter_.InterpExp(&binding_type->expression());
SetStaticType(&var, declared_type);
ExpectType(var.source_loc(), "initializer of variable", declared_type,
&var.initializer().static_type());
@@ -1127,33 +1118,28 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
}
}
void TypeChecker::TopLevel(Nonnull<Declaration*> d, Nonnull<Env*> values) {
void TypeChecker::TopLevel(Nonnull<Declaration*> d) {
switch (d->kind()) {
case DeclarationKind::FunctionDeclaration: {
auto& func_def = cast<FunctionDeclaration>(*d);
TypeCheckFunctionDeclaration(&func_def, *values,
/*check_body=*/false);
interpreter_.InitEnv(*d, values);
TypeCheckFunctionDeclaration(&func_def, /*check_body=*/false);
SetConstantValue(&func_def, arena_->New<FunctionValue>(&func_def));
break;
}
case DeclarationKind::ClassDeclaration: {
auto& class_decl = cast<ClassDeclaration>(*d);
TypeCheckClassDeclaration(&class_decl, *values);
TypeCheckClassDeclaration(&class_decl);
const auto& type = cast<TypeOfClassType>(class_decl.static_type());
const NominalClassType& value = type.class_type();
AllocationId a = interpreter_.AllocateValue(&value);
values->Set(class_decl.name(), a); // Is this obsolete?
SetConstantValue(&class_decl, &type.class_type());
break;
}
case DeclarationKind::ChoiceDeclaration: {
auto& choice = cast<ChoiceDeclaration>(*d);
TypeCheckChoiceDeclaration(&choice, *values);
TypeCheckChoiceDeclaration(&choice);
const auto& type = cast<TypeOfChoiceType>(choice.static_type());
const ChoiceType& value = type.choice_type();
AllocationId a = interpreter_.AllocateValue(&value);
values->Set(choice.name(), a); // Is this obsolete?
SetConstantValue(&choice, &type.choice_type());
break;
}
@@ -1163,13 +1149,33 @@ void TypeChecker::TopLevel(Nonnull<Declaration*> d, Nonnull<Env*> values) {
// compile-time symbol table.
Expression& type =
cast<ExpressionPattern>(var.binding().type()).expression();
TypeCheckPattern(&var.binding(), *values, std::nullopt);
Nonnull<const Value*> declared_type =
interpreter_.InterpExp(*values, &type);
TypeCheckPattern(&var.binding(), std::nullopt);
Nonnull<const Value*> declared_type = interpreter_.InterpExp(&type);
SetStaticType(&var, declared_type);
break;
}
}
}
template <typename T>
void TypeChecker::SetConstantValue(Nonnull<T*> named_entity,
Nonnull<const Value*> value) {
std::optional<Nonnull<const Value*>> old_value =
named_entity->constant_value();
if (old_value.has_value()) {
CHECK(ValueEqual(*old_value, value));
} else {
named_entity->set_constant_value(value);
CHECK(constants_.insert(named_entity).second);
}
}
void TypeChecker::PrintConstants(llvm::raw_ostream& out) {
llvm::ListSeparator sep;
for (const auto& named_entity : constants_) {
out << sep << named_entity.name() << ": "
<< **named_entity.constant_value();
}
}
} // namespace Carbon
+17 -10
View File
@@ -42,39 +42,37 @@ class TypeChecker {
//
// `values` maps variable names to their compile-time values. It is not
// directly used in this function but is passed to InterExp.
void TypeCheckExp(Nonnull<Expression*> e, Env values);
void TypeCheckExp(Nonnull<Expression*> e);
// Equivalent to TypeCheckExp, but operates on the AST rooted at `p`.
//
// `expected` is the type that this pattern is expected to have, if the
// surrounding context gives us that information. Otherwise, it is
// nullopt.
void TypeCheckPattern(Nonnull<Pattern*> p, Env values,
void TypeCheckPattern(Nonnull<Pattern*> p,
std::optional<Nonnull<const Value*>> expected);
// Equivalent to TypeCheckExp, but operates on the AST rooted at `d`.
void TypeCheckDeclaration(Nonnull<Declaration*> d, const Env& values);
void TypeCheckDeclaration(Nonnull<Declaration*> d);
// Equivalent to TypeCheckExp, but operates on the AST rooted at `s`.
//
// REQUIRES: f.return_term().has_static_type() || f.return_term().is_auto(),
// where `f` is nearest enclosing FunctionDeclaration of `s`.
void TypeCheckStmt(Nonnull<Statement*> s, Env values);
void TypeCheckStmt(Nonnull<Statement*> s);
// Equivalent to TypeCheckExp, but operates on the AST rooted at `f`,
// and may not traverse f->body() if `check_body` is false.
void TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f, Env values,
void TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
bool check_body);
// Equivalent to TypeCheckExp, but operates on the AST rooted at class_decl.
void TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
Env ct_top);
void TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl);
// Equivalent to TypeCheckExp, but operates on the AST rooted at choice_decl.
void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
Env ct_top);
void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice);
void TopLevel(Nonnull<Declaration*> d, Nonnull<Env*> values);
void TopLevel(Nonnull<Declaration*> d);
// Verifies that opt_stmt holds a statement, and it is structurally impossible
// for control flow to leave that statement except via a `return`.
@@ -90,8 +88,17 @@ class TypeChecker {
Nonnull<const Value*>>& dict,
Nonnull<const Value*> type) -> Nonnull<const Value*>;
// Sets named_entity.constant_value() to `value`. Can be called multiple
// times on the same named_entity, so long as it is always called with
// the same value.
template <typename T>
void SetConstantValue(Nonnull<T*> named_entity, Nonnull<const Value*> value);
void PrintConstants(llvm::raw_ostream& out);
Nonnull<Arena*> arena_;
Interpreter interpreter_;
std::set<NamedEntityView> constants_;
bool trace_;
};
+2 -2
View File
@@ -13,7 +13,7 @@
// CHECK: fn Print (format_str: String) {
// CHECK: ********** type checking **********
// CHECK: checking pattern (format_str: String)
// CHECK: values: Print: fun<Print>
// CHECK: constants: Main: fun<Main>, Print: fun<Print>
// CHECK: ********** type checking complete **********
// CHECK: fn Print (format_str: String) {
// CHECK: ********** starting execution **********
@@ -21,7 +21,7 @@
// CHECK: ********** calling main function **********
// CHECK: {
// CHECK: stack:
// CHECK: heap: fun<Print>, fun<Main>
// CHECK: heap:
// CHECK: }
// CHECK: --- step exp Main() (<Main()>:0) --->
// CHECK: result: 0