mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 15:51:07 +01:00
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:
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user