mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 07:01:04 +01:00
Adds basic support for class functions and methods. (#1057)
* adding methods to the ast * pre commit stuff? * implementation of class functions * implemented methods * some cleanup * more cleanup * add newlines in test programs * pre-commit fixups * added include of return_term.h * a test of a method calling another method * replacing Member with Declaration * removing the member.h etc files * clarify a type annotation * update uses of FunctionDeclaration * remove ReturnTarget, no longer needed * more cleanup * more cleanup * yet more cleanup, playing with pre-commit * did a pre-commit run --all-files * fixed const issue * remove comment * checking dependencies in BUILD files and headers * pre-commit working now * refactor NominalClassType to just hold a pointer to the class declaration * remove Member from rtti * responding to Geoffreys review * change field_types to a non-member function
This commit is contained in:
@@ -149,6 +149,7 @@ cc_library(
|
||||
"//common:check",
|
||||
"//executable_semantics/ast",
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:return_term",
|
||||
"//executable_semantics/ast:statement",
|
||||
"//executable_semantics/common:error",
|
||||
"//executable_semantics/common:nonnull",
|
||||
@@ -165,8 +166,8 @@ cc_library(
|
||||
"//executable_semantics/ast",
|
||||
"//executable_semantics/ast:declaration",
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/ast:member",
|
||||
"//executable_semantics/ast:pattern",
|
||||
"//executable_semantics/ast:return_term",
|
||||
"//executable_semantics/ast:statement",
|
||||
"//executable_semantics/ast:static_scope",
|
||||
"//executable_semantics/common:arena",
|
||||
|
||||
@@ -22,7 +22,6 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
|
||||
for (const auto decl : ast.declarations) {
|
||||
llvm::outs() << *decl;
|
||||
}
|
||||
llvm::outs() << "********** type checking **********\n";
|
||||
}
|
||||
SourceLocation source_loc("<Main()>", 0);
|
||||
ast.main_call = arena->New<CallExpression>(
|
||||
@@ -30,8 +29,17 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
|
||||
arena->New<TupleLiteral>(source_loc));
|
||||
// Although name resolution is currently done once, generic programming
|
||||
// (particularly templates) may require more passes.
|
||||
if (trace) {
|
||||
llvm::outs() << "********** resolving names **********\n";
|
||||
}
|
||||
ResolveNames(ast);
|
||||
if (trace) {
|
||||
llvm::outs() << "********** resolving control flow **********\n";
|
||||
}
|
||||
ResolveControlFlow(ast);
|
||||
if (trace) {
|
||||
llvm::outs() << "********** type checking **********\n";
|
||||
}
|
||||
TypeChecker(arena, trace).TypeCheck(ast);
|
||||
if (trace) {
|
||||
llvm::outs() << "\n";
|
||||
|
||||
@@ -316,9 +316,11 @@ void Interpreter::StepLvalue() {
|
||||
}
|
||||
}
|
||||
case ExpressionKind::PrimitiveOperatorExpression: {
|
||||
const PrimitiveOperatorExpression& op = cast<PrimitiveOperatorExpression>(exp);
|
||||
const PrimitiveOperatorExpression& op =
|
||||
cast<PrimitiveOperatorExpression>(exp);
|
||||
if (op.op() != Operator::Deref) {
|
||||
FATAL() << "Can't treat primitive operator expression as lvalue: " << exp;
|
||||
FATAL() << "Can't treat primitive operator expression as lvalue: "
|
||||
<< exp;
|
||||
}
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
@@ -355,6 +357,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
switch (value->kind()) {
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LValue:
|
||||
case Value::Kind::BoolValue:
|
||||
@@ -573,6 +576,23 @@ void Interpreter::StepExp() {
|
||||
std::make_unique<StatementAction>(*function.body()),
|
||||
std::move(function_scope));
|
||||
}
|
||||
case Value::Kind::BoundMethodValue: {
|
||||
const BoundMethodValue& m =
|
||||
cast<BoundMethodValue>(*act.results()[0]);
|
||||
const FunctionDeclaration& method = m.declaration();
|
||||
Nonnull<const Value*> converted_args = Convert(
|
||||
act.results()[1], &method.param_pattern().static_type());
|
||||
RuntimeScope method_scope(&heap_);
|
||||
CHECK(PatternMatch(&method.me_pattern().value(), m.receiver(),
|
||||
exp.source_loc(), &method_scope));
|
||||
CHECK(PatternMatch(&method.param_pattern().value(), converted_args,
|
||||
exp.source_loc(), &method_scope));
|
||||
CHECK(method.body().has_value())
|
||||
<< "Calling a method that's missing a body";
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(*method.body()),
|
||||
std::move(method_scope));
|
||||
}
|
||||
default:
|
||||
FATAL_RUNTIME_ERROR(exp.source_loc())
|
||||
<< "in call, expected a function, not " << *act.results()[0];
|
||||
@@ -928,11 +948,15 @@ void Interpreter::StepDeclaration() {
|
||||
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()));
|
||||
if (var_decl.has_initializer()) {
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&var_decl.initializer()));
|
||||
} else {
|
||||
todo_.Initialize(&var_decl.binding(), act.results()[0]);
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
} else {
|
||||
todo_.Initialize(&var_decl.binding(), act.results()[0]);
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "executable_semantics/interpreter/resolve_control_flow.h"
|
||||
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/return_term.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@@ -110,16 +111,32 @@ static void ResolveControlFlow(Nonnull<Statement*> statement,
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveControlFlow(Nonnull<Declaration*> declaration) {
|
||||
switch (declaration->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
auto& function = cast<FunctionDeclaration>(*declaration);
|
||||
if (function.body().has_value()) {
|
||||
FunctionData data = {.declaration = &function};
|
||||
ResolveControlFlow(*function.body(), std::nullopt, &data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeclarationKind::ClassDeclaration: {
|
||||
auto& class_decl = cast<ClassDeclaration>(*declaration);
|
||||
for (Nonnull<Declaration*> member : class_decl.members()) {
|
||||
ResolveControlFlow(member);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveControlFlow(AST& ast) {
|
||||
for (auto declaration : ast.declarations) {
|
||||
if (declaration->kind() != DeclarationKind::FunctionDeclaration) {
|
||||
continue;
|
||||
}
|
||||
auto& function = cast<FunctionDeclaration>(*declaration);
|
||||
if (function.body().has_value()) {
|
||||
FunctionData data = {.declaration = &function};
|
||||
ResolveControlFlow(*function.body(), std::nullopt, &data);
|
||||
}
|
||||
ResolveControlFlow(declaration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/member.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "executable_semantics/ast/static_scope.h"
|
||||
@@ -21,20 +20,6 @@ namespace Carbon {
|
||||
// Adds the names exposed by the given AST node to enclosing_scope.
|
||||
static void AddExposedNames(const Declaration& declaration,
|
||||
StaticScope& enclosing_scope);
|
||||
static void AddExposedNames(const Member& member, StaticScope& enclosing_scope);
|
||||
|
||||
static void AddExposedNames(const Member& member,
|
||||
StaticScope& enclosing_scope) {
|
||||
switch (member.kind()) {
|
||||
case MemberKind::FieldMember: {
|
||||
const auto& field = cast<FieldMember>(member);
|
||||
if (field.binding().name() != AnonymousName) {
|
||||
enclosing_scope.Add(field.binding().name(), &field.binding());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddExposedNames(const Declaration& declaration,
|
||||
StaticScope& enclosing_scope) {
|
||||
@@ -77,7 +62,6 @@ static void ResolveNames(Expression& expression,
|
||||
const StaticScope& enclosing_scope);
|
||||
static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope);
|
||||
static void ResolveNames(Statement& statement, StaticScope& enclosing_scope);
|
||||
static void ResolveNames(Member& member, StaticScope& enclosing_scope);
|
||||
static void ResolveNames(Declaration& declaration,
|
||||
StaticScope& enclosing_scope);
|
||||
|
||||
@@ -257,13 +241,6 @@ static void ResolveNames(Statement& statement, StaticScope& enclosing_scope) {
|
||||
}
|
||||
}
|
||||
|
||||
static void ResolveNames(Member& member, StaticScope& enclosing_scope) {
|
||||
switch (member.kind()) {
|
||||
case MemberKind::FieldMember:
|
||||
ResolveNames(cast<FieldMember>(member).binding(), enclosing_scope);
|
||||
}
|
||||
}
|
||||
|
||||
static void ResolveNames(Declaration& declaration,
|
||||
StaticScope& enclosing_scope) {
|
||||
switch (declaration.kind()) {
|
||||
@@ -275,6 +252,9 @@ static void ResolveNames(Declaration& declaration,
|
||||
function_scope.Add(binding->name(), binding);
|
||||
ResolveNames(binding->type(), function_scope);
|
||||
}
|
||||
if (function.is_method()) {
|
||||
ResolveNames(function.me_pattern(), function_scope);
|
||||
}
|
||||
ResolveNames(function.param_pattern(), function_scope);
|
||||
if (function.return_term().type_expression().has_value()) {
|
||||
ResolveNames(**function.return_term().type_expression(),
|
||||
@@ -289,10 +269,11 @@ static void ResolveNames(Declaration& declaration,
|
||||
auto& class_decl = cast<ClassDeclaration>(declaration);
|
||||
StaticScope class_scope;
|
||||
class_scope.AddParent(&enclosing_scope);
|
||||
for (Nonnull<Member*> member : class_decl.members()) {
|
||||
class_scope.Add(class_decl.name(), &class_decl);
|
||||
for (Nonnull<Declaration*> member : class_decl.members()) {
|
||||
AddExposedNames(*member, class_scope);
|
||||
}
|
||||
for (Nonnull<Member*> member : class_decl.members()) {
|
||||
for (Nonnull<Declaration*> member : class_decl.members()) {
|
||||
ResolveNames(*member, class_scope);
|
||||
}
|
||||
break;
|
||||
@@ -316,7 +297,9 @@ static void ResolveNames(Declaration& declaration,
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
auto& var = cast<VariableDeclaration>(declaration);
|
||||
ResolveNames(var.binding(), enclosing_scope);
|
||||
ResolveNames(var.initializer(), enclosing_scope);
|
||||
if (var.has_initializer()) {
|
||||
ResolveNames(var.initializer(), enclosing_scope);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
|
||||
switch (value->kind()) {
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LValue:
|
||||
case Value::Kind::BoolValue:
|
||||
@@ -165,7 +166,7 @@ static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
|
||||
case Value::Kind::NominalClassType:
|
||||
return FieldTypesImplicitlyConvertible(
|
||||
cast<StructType>(*source).fields(),
|
||||
cast<NominalClassType>(*destination).fields());
|
||||
FieldTypes(cast<NominalClassType>(*destination)));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -314,6 +315,7 @@ void TypeChecker::ArgumentDeduction(
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LValue:
|
||||
case Value::Kind::StructValue:
|
||||
@@ -380,6 +382,7 @@ auto TypeChecker::Substitute(
|
||||
case Value::Kind::IntValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::FunctionValue:
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LValue:
|
||||
case Value::Kind::StructValue:
|
||||
@@ -480,25 +483,28 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const auto& t_class = cast<NominalClassType>(aggregate_type);
|
||||
// Search for a field
|
||||
for (auto& field : t_class.fields()) {
|
||||
if (access.field() == field.name) {
|
||||
SetStaticType(&access, field.value);
|
||||
access.set_value_category(access.aggregate().value_category());
|
||||
return;
|
||||
if (std::optional<Nonnull<const Declaration*>> member =
|
||||
t_class.FindMember(access.field());
|
||||
member.has_value()) {
|
||||
SetStaticType(&access, &(*member)->static_type());
|
||||
switch ((*member)->kind()) {
|
||||
case DeclarationKind::VariableDeclaration:
|
||||
access.set_value_category(access.aggregate().value_category());
|
||||
break;
|
||||
case DeclarationKind::FunctionDeclaration:
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
break;
|
||||
default:
|
||||
FATAL() << "member " << access.field()
|
||||
<< " is not a field or method";
|
||||
break;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
FATAL_COMPILATION_ERROR(e->source_loc())
|
||||
<< "class " << t_class.declaration().name()
|
||||
<< " does not have a field named " << access.field();
|
||||
}
|
||||
// Search for a method
|
||||
for (auto& method : t_class.methods()) {
|
||||
if (access.field() == method.name) {
|
||||
SetStaticType(&access, method.value);
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return;
|
||||
}
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(e->source_loc())
|
||||
<< "class " << t_class.name() << " does not have a field named "
|
||||
<< access.field();
|
||||
}
|
||||
case Value::Kind::TypeOfChoiceType: {
|
||||
const ChoiceType& choice =
|
||||
@@ -517,10 +523,36 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return;
|
||||
}
|
||||
case Value::Kind::TypeOfClassType: {
|
||||
const NominalClassType& class_type =
|
||||
cast<TypeOfClassType>(aggregate_type).class_type();
|
||||
if (std::optional<Nonnull<const Declaration*>> member =
|
||||
class_type.FindMember(access.field());
|
||||
member.has_value()) {
|
||||
switch ((*member)->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
const auto& func = cast<FunctionDeclaration>(*member);
|
||||
if (func->is_method()) {
|
||||
break;
|
||||
}
|
||||
SetStaticType(&access, &(*member)->static_type());
|
||||
access.set_value_category(ValueCategory::Let);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(access.source_loc())
|
||||
<< access.field() << " is not a class function";
|
||||
} else {
|
||||
FATAL_COMPILATION_ERROR(access.source_loc())
|
||||
<< class_type << " does not have a class function named "
|
||||
<< access.field();
|
||||
}
|
||||
}
|
||||
default:
|
||||
FATAL_COMPILATION_ERROR(e->source_loc())
|
||||
<< "field access, expected a struct\n"
|
||||
<< *e;
|
||||
<< "field access, unexpected " << aggregate_type << " in " << *e;
|
||||
}
|
||||
}
|
||||
case ExpressionKind::IdentifierExpression: {
|
||||
@@ -623,8 +655,9 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
|
||||
return;
|
||||
case Operator::AddressOf:
|
||||
if (op.arguments()[0]->value_category() != ValueCategory::Var) {
|
||||
FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc()) <<
|
||||
"Argument to " << ToString(op.op()) << " should be an lvalue.";
|
||||
FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc())
|
||||
<< "Argument to " << ToString(op.op())
|
||||
<< " should be an lvalue.";
|
||||
}
|
||||
SetStaticType(&op, arena_->New<PointerType>(ts[0]));
|
||||
op.set_value_category(ValueCategory::Let);
|
||||
@@ -1001,6 +1034,11 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
|
||||
SetStaticType(deduced, arena_->New<VariableType>(deduced));
|
||||
SetConstantValue(deduced, &deduced->static_type());
|
||||
}
|
||||
if (f->is_method()) {
|
||||
// Type check the receiver patter
|
||||
TypeCheckPattern(&f->me_pattern(), std::nullopt);
|
||||
}
|
||||
|
||||
// Type check the parameter pattern
|
||||
TypeCheckPattern(&f->param_pattern(), std::nullopt);
|
||||
|
||||
@@ -1044,32 +1082,32 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
|
||||
arena_->New<IntType>(), &f->return_term().static_type());
|
||||
// TODO: Check that main doesn't have any parameters.
|
||||
}
|
||||
SetConstantValue(f, arena_->New<FunctionValue>(f));
|
||||
return;
|
||||
}
|
||||
|
||||
void TypeChecker::TypeCheckClassDeclaration(
|
||||
Nonnull<ClassDeclaration*> class_decl) {
|
||||
std::vector<NamedValue> fields;
|
||||
std::vector<NamedValue> methods;
|
||||
for (Nonnull<Member*> m : class_decl->members()) {
|
||||
switch (m->kind()) {
|
||||
case MemberKind::FieldMember: {
|
||||
BindingPattern& binding = cast<FieldMember>(*m).binding();
|
||||
if (binding.name() == AnonymousName) {
|
||||
FATAL_COMPILATION_ERROR(binding.source_loc())
|
||||
<< "Struct members must have names";
|
||||
}
|
||||
TypeCheckPattern(&binding, std::nullopt);
|
||||
fields.push_back(
|
||||
{.name = binding.name(), .value = &binding.static_type()});
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The declarations of the members may refer to the class, so we
|
||||
// must set the constant value of the class and its static type
|
||||
// before we start processing the members.
|
||||
Nonnull<NominalClassType*> class_type =
|
||||
arena_->New<NominalClassType>(class_decl);
|
||||
SetConstantValue(class_decl, class_type);
|
||||
SetStaticType(class_decl, arena_->New<TypeOfClassType>(class_type));
|
||||
|
||||
// First pass: process the field, class function, and method
|
||||
// declarations but not the bodies of class functions or method
|
||||
// declarations.
|
||||
for (Nonnull<Declaration*> m : class_decl->members()) {
|
||||
DeclareDeclaration(m);
|
||||
}
|
||||
|
||||
// Second pass: type check the bodies of the class functions and
|
||||
// methods.
|
||||
for (Nonnull<Declaration*> m : class_decl->members()) {
|
||||
TypeCheckDeclaration(m);
|
||||
}
|
||||
SetStaticType(
|
||||
class_decl,
|
||||
arena_->New<TypeOfClassType>(arena_->New<NominalClassType>(
|
||||
class_decl->name(), std::move(fields), std::move(methods))));
|
||||
}
|
||||
|
||||
void TypeChecker::TypeCheckChoiceDeclaration(
|
||||
@@ -1081,12 +1119,13 @@ void TypeChecker::TypeCheckChoiceDeclaration(
|
||||
alternatives.push_back({.name = alternative->name(), .value = signature});
|
||||
}
|
||||
auto ct = arena_->New<ChoiceType>(choice->name(), std::move(alternatives));
|
||||
SetConstantValue(choice, ct);
|
||||
SetStaticType(choice, arena_->New<TypeOfChoiceType>(ct));
|
||||
}
|
||||
|
||||
void TypeChecker::TypeCheck(AST& ast) {
|
||||
for (Nonnull<Declaration*> declaration : ast.declarations) {
|
||||
TopLevel(declaration);
|
||||
DeclareDeclaration(declaration);
|
||||
}
|
||||
for (Nonnull<Declaration*> decl : ast.declarations) {
|
||||
TypeCheckDeclaration(decl);
|
||||
@@ -1111,7 +1150,9 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> 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());
|
||||
if (var.has_initializer()) {
|
||||
TypeCheckExp(&var.initializer());
|
||||
}
|
||||
const auto* binding_type =
|
||||
dyn_cast<ExpressionPattern>(&var.binding().type());
|
||||
if (binding_type == nullptr) {
|
||||
@@ -1122,35 +1163,32 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d) {
|
||||
Nonnull<const Value*> declared_type =
|
||||
InterpExp(&binding_type->expression(), arena_, trace_);
|
||||
SetStaticType(&var, declared_type);
|
||||
ExpectType(var.source_loc(), "initializer of variable", declared_type,
|
||||
&var.initializer().static_type());
|
||||
if (var.has_initializer()) {
|
||||
ExpectType(var.source_loc(), "initializer of variable", declared_type,
|
||||
&var.initializer().static_type());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TypeChecker::TopLevel(Nonnull<Declaration*> d) {
|
||||
void TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d) {
|
||||
switch (d->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
auto& func_def = cast<FunctionDeclaration>(*d);
|
||||
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);
|
||||
const auto& type = cast<TypeOfClassType>(class_decl.static_type());
|
||||
SetConstantValue(&class_decl, &type.class_type());
|
||||
break;
|
||||
}
|
||||
|
||||
case DeclarationKind::ChoiceDeclaration: {
|
||||
auto& choice = cast<ChoiceDeclaration>(*d);
|
||||
TypeCheckChoiceDeclaration(&choice);
|
||||
const auto& type = cast<TypeOfChoiceType>(choice.static_type());
|
||||
SetConstantValue(&choice, &type.choice_type());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,9 @@ class TypeChecker {
|
||||
// Equivalent to TypeCheckExp, but operates on the AST rooted at choice_decl.
|
||||
void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice);
|
||||
|
||||
void TopLevel(Nonnull<Declaration*> d);
|
||||
// Establish the type of the declaration without deeply checking
|
||||
// the declaration, such as checking the body of a function.
|
||||
void DeclareDeclaration(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`.
|
||||
|
||||
@@ -40,10 +40,27 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
return *field;
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const NominalClassValue& object = cast<NominalClassValue>(*v);
|
||||
// Look for a field
|
||||
std::optional<Nonnull<const Value*>> field =
|
||||
cast<StructValue>(cast<NominalClassValue>(*v).inits()).FindField(f);
|
||||
cast<StructValue>(object.inits()).FindField(f);
|
||||
if (field == std::nullopt) {
|
||||
FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v;
|
||||
// Look for a method in the object's class
|
||||
const NominalClassType& class_type =
|
||||
cast<NominalClassType>(object.type());
|
||||
std::optional<Nonnull<const FunctionValue*>> func =
|
||||
class_type.FindFunction(f);
|
||||
if (func == std::nullopt) {
|
||||
FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v
|
||||
<< " or its class " << class_type;
|
||||
} else if ((*func)->declaration().is_method()) {
|
||||
// Found a method. Turn it into a bound method.
|
||||
const FunctionValue& m = cast<FunctionValue>(**func);
|
||||
return arena->New<BoundMethodValue>(&m.declaration(), &object);
|
||||
} else {
|
||||
// Found a class function
|
||||
return *func;
|
||||
}
|
||||
}
|
||||
return *field;
|
||||
}
|
||||
@@ -55,6 +72,16 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
|
||||
}
|
||||
return arena->New<AlternativeConstructorValue>(f, choice.name());
|
||||
}
|
||||
case Value::Kind::NominalClassType: {
|
||||
const NominalClassType& class_type = cast<NominalClassType>(*v);
|
||||
std::optional<Nonnull<const FunctionValue*>> fun =
|
||||
class_type.FindFunction(f);
|
||||
if (fun == std::nullopt) {
|
||||
FATAL_RUNTIME_ERROR(source_loc)
|
||||
<< "class function " << f << " not in " << *v;
|
||||
}
|
||||
return *fun;
|
||||
}
|
||||
default:
|
||||
FATAL() << "field access not allowed for value " << *v;
|
||||
}
|
||||
@@ -158,7 +185,7 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
}
|
||||
case Value::Kind::NominalClassValue: {
|
||||
const auto& s = cast<NominalClassValue>(*this);
|
||||
out << cast<NominalClassType>(s.type()).name() << s.inits();
|
||||
out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
|
||||
break;
|
||||
}
|
||||
case Value::Kind::TupleValue: {
|
||||
@@ -179,6 +206,10 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
case Value::Kind::FunctionValue:
|
||||
out << "fun<" << cast<FunctionValue>(*this).declaration().name() << ">";
|
||||
break;
|
||||
case Value::Kind::BoundMethodValue:
|
||||
out << "bound_method<"
|
||||
<< cast<BoundMethodValue>(*this).declaration().name() << ">";
|
||||
break;
|
||||
case Value::Kind::PointerValue:
|
||||
out << "ptr<" << cast<PointerValue>(*this).address() << ">";
|
||||
break;
|
||||
@@ -230,9 +261,11 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
out << "}";
|
||||
break;
|
||||
}
|
||||
case Value::Kind::NominalClassType:
|
||||
out << "class " << cast<NominalClassType>(*this).name();
|
||||
case Value::Kind::NominalClassType: {
|
||||
const NominalClassType& class_type = cast<NominalClassType>(*this);
|
||||
out << "class " << class_type.declaration().name();
|
||||
break;
|
||||
}
|
||||
case Value::Kind::ChoiceType:
|
||||
out << "choice " << cast<ChoiceType>(*this).name();
|
||||
break;
|
||||
@@ -252,7 +285,8 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
out << "\"";
|
||||
break;
|
||||
case Value::Kind::TypeOfClassType:
|
||||
out << "typeof(" << cast<TypeOfClassType>(*this).class_type().name()
|
||||
out << "typeof("
|
||||
<< cast<TypeOfClassType>(*this).class_type().declaration().name()
|
||||
<< ")";
|
||||
break;
|
||||
case Value::Kind::TypeOfChoiceType:
|
||||
@@ -328,8 +362,8 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::NominalClassType:
|
||||
return cast<NominalClassType>(*t1).name() ==
|
||||
cast<NominalClassType>(*t2).name();
|
||||
return cast<NominalClassType>(*t1).declaration().name() ==
|
||||
cast<NominalClassType>(*t2).declaration().name();
|
||||
case Value::Kind::ChoiceType:
|
||||
return cast<ChoiceType>(*t1).name() == cast<ChoiceType>(*t2).name();
|
||||
case Value::Kind::TupleValue: {
|
||||
@@ -387,6 +421,15 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
|
||||
return body1.has_value() == body2.has_value() &&
|
||||
(!body1.has_value() || *body1 == *body2);
|
||||
}
|
||||
case Value::Kind::BoundMethodValue: {
|
||||
const BoundMethodValue& m1 = cast<BoundMethodValue>(*v1);
|
||||
const BoundMethodValue& m2 = cast<BoundMethodValue>(*v2);
|
||||
std::optional<Nonnull<const Statement*>> body1 = m1.declaration().body();
|
||||
std::optional<Nonnull<const Statement*>> body2 = m2.declaration().body();
|
||||
return ValueEqual(m1.receiver(), m2.receiver()) &&
|
||||
body1.has_value() == body2.has_value() &&
|
||||
(!body1.has_value() || *body1 == *body2);
|
||||
}
|
||||
case Value::Kind::TupleValue: {
|
||||
const std::vector<Nonnull<const Value*>>& elements1 =
|
||||
cast<TupleValue>(*v1).elements();
|
||||
@@ -455,4 +498,64 @@ auto ChoiceType::FindAlternative(std::string_view name) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto NominalClassType::FindFunction(const std::string& name) const
|
||||
-> std::optional<Nonnull<const FunctionValue*>> {
|
||||
for (const auto& member : declaration().members()) {
|
||||
switch (member->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
const auto& fun = cast<FunctionDeclaration>(*member);
|
||||
if (fun.name() == name) {
|
||||
return &cast<FunctionValue>(**fun.constant_value());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto FieldTypes(const NominalClassType& class_type) -> std::vector<NamedValue> {
|
||||
std::vector<NamedValue> field_types;
|
||||
for (Nonnull<Declaration*> m : class_type.declaration().members()) {
|
||||
switch (m->kind()) {
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const auto& var = cast<VariableDeclaration>(*m);
|
||||
field_types.push_back({.name = var.binding().name(),
|
||||
.value = &var.binding().static_type()});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return field_types;
|
||||
}
|
||||
|
||||
auto NominalClassType::FindMember(const std::string& name) const
|
||||
-> std::optional<Nonnull<const Declaration*>> {
|
||||
for (const auto& member : declaration().members()) {
|
||||
switch (member->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration: {
|
||||
const auto& fun = cast<FunctionDeclaration>(*member);
|
||||
if (fun.name() == name) {
|
||||
return &fun;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const auto& var = cast<VariableDeclaration>(*member);
|
||||
if (var.binding().name() == name) {
|
||||
return &var;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -36,6 +36,7 @@ class Value {
|
||||
enum class Kind {
|
||||
IntValue,
|
||||
FunctionValue,
|
||||
BoundMethodValue,
|
||||
PointerValue,
|
||||
LValue,
|
||||
BoolValue,
|
||||
@@ -135,6 +136,30 @@ class FunctionValue : public Value {
|
||||
Nonnull<const FunctionDeclaration*> declaration_;
|
||||
};
|
||||
|
||||
// A bound method value. It includes the receiver object.
|
||||
class BoundMethodValue : public Value {
|
||||
public:
|
||||
explicit BoundMethodValue(Nonnull<const FunctionDeclaration*> declaration,
|
||||
Nonnull<const Value*> receiver)
|
||||
: Value(Kind::BoundMethodValue),
|
||||
declaration_(declaration),
|
||||
receiver_(receiver) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::BoundMethodValue;
|
||||
}
|
||||
|
||||
auto declaration() const -> const FunctionDeclaration& {
|
||||
return *declaration_;
|
||||
}
|
||||
|
||||
auto receiver() const -> Nonnull<const Value*> { return receiver_; }
|
||||
|
||||
private:
|
||||
Nonnull<const FunctionDeclaration*> declaration_;
|
||||
Nonnull<const Value*> receiver_;
|
||||
};
|
||||
|
||||
// The value of a location in memory.
|
||||
class LValue : public Value {
|
||||
public:
|
||||
@@ -212,7 +237,7 @@ class StructValue : public Value {
|
||||
std::vector<NamedValue> elements_;
|
||||
};
|
||||
|
||||
// A value of a nominal class type.
|
||||
// A value of a nominal class type, i.e., an object.
|
||||
class NominalClassValue : public Value {
|
||||
public:
|
||||
NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
|
||||
@@ -227,7 +252,7 @@ class NominalClassValue : public Value {
|
||||
|
||||
private:
|
||||
Nonnull<const Value*> type_;
|
||||
Nonnull<const Value*> inits_;
|
||||
Nonnull<const Value*> inits_; // The initializing StructValue.
|
||||
};
|
||||
|
||||
// An alternative constructor value.
|
||||
@@ -429,27 +454,30 @@ class StructType : public Value {
|
||||
// A class type.
|
||||
class NominalClassType : public Value {
|
||||
public:
|
||||
NominalClassType(std::string name, std::vector<NamedValue> fields,
|
||||
std::vector<NamedValue> methods)
|
||||
: Value(Kind::NominalClassType),
|
||||
name_(std::move(name)),
|
||||
fields_(std::move(fields)),
|
||||
methods_(std::move(methods)) {}
|
||||
NominalClassType(Nonnull<const ClassDeclaration*> declaration)
|
||||
: Value(Kind::NominalClassType), declaration_(declaration) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::NominalClassType;
|
||||
}
|
||||
|
||||
auto name() const -> const std::string& { return name_; }
|
||||
auto fields() const -> llvm::ArrayRef<NamedValue> { return fields_; }
|
||||
auto methods() const -> llvm::ArrayRef<NamedValue> { return methods_; }
|
||||
auto declaration() const -> const ClassDeclaration& { return *declaration_; }
|
||||
|
||||
// Return the declaration of the member with the given name.
|
||||
auto FindMember(const std::string& name) const
|
||||
-> std::optional<Nonnull<const Declaration*>>;
|
||||
|
||||
// Returns the value of the function named `name` in this class, or
|
||||
// nullopt if there is no such function.
|
||||
auto FindFunction(const std::string& name) const
|
||||
-> std::optional<Nonnull<const FunctionValue*>>;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<NamedValue> fields_;
|
||||
std::vector<NamedValue> methods_;
|
||||
Nonnull<const ClassDeclaration*> declaration_;
|
||||
};
|
||||
|
||||
auto FieldTypes(const NominalClassType&) -> std::vector<NamedValue>;
|
||||
|
||||
// A choice type.
|
||||
class ChoiceType : public Value {
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user