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:
Jeremy G. Siek
2022-02-05 12:30:13 -05:00
committed by GitHub
parent 4479c55305
commit ac0b810bf3
27 changed files with 739 additions and 351 deletions
+2 -1
View File
@@ -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`.
+111 -8
View File
@@ -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
+42 -14
View File
@@ -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: