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
+14 -16
View File
@@ -77,8 +77,8 @@ cc_library(
],
deps = [
":ast_node",
":member",
":pattern",
":return_term",
":source_location",
":statement",
":static_scope",
@@ -89,6 +89,18 @@ cc_library(
],
)
cc_library(
name = "return_term",
hdrs = ["return_term.h"],
deps = [
":source_location",
"//common:check",
"//common:ostream",
"//executable_semantics/common:nonnull",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "expression",
srcs = ["expression.cpp"],
@@ -119,20 +131,6 @@ cc_test(
],
)
cc_library(
name = "member",
srcs = ["member.cpp"],
hdrs = ["member.h"],
deps = [
":expression",
":pattern",
":source_location",
"//common:ostream",
"//executable_semantics/common:arena",
"@llvm-project//llvm:Support",
],
)
cc_library(
name = "library_name",
hdrs = ["library_name.h"],
@@ -186,7 +184,6 @@ cc_library(
":source_location",
":value_category",
"//common:check",
"//executable_semantics/common:arena",
"//executable_semantics/common:error",
"//executable_semantics/common:nonnull",
],
@@ -209,6 +206,7 @@ cc_library(
":ast_node",
":expression",
":pattern",
":return_term",
":source_location",
":static_scope",
":value_category",
-2
View File
@@ -50,5 +50,3 @@ abstract class Expression : AstNode;
class IdentifierExpression : Expression;
class IntrinsicExpression : Expression;
class UnimplementedExpression : Expression;
abstract class Member : AstNode;
class FieldMember : Member;
@@ -104,7 +104,7 @@ TEST(MatchesReturnTest, BasicUsage) {
TEST(MatchesFunctionDeclarationTest, BasicUsage) {
TuplePattern params(DummyLoc, {});
Block body(DummyLoc, {});
FunctionDeclaration decl(DummyLoc, "Foo", {}, &params,
FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, &params,
ReturnTerm::Omitted(DummyLoc), &body);
EXPECT_THAT(decl, MatchesFunctionDeclaration());
@@ -117,7 +117,7 @@ TEST(MatchesFunctionDeclarationTest, BasicUsage) {
EXPECT_THAT(decl,
Not(MatchesFunctionDeclaration().WithBody(MatchesLiteral(0))));
FunctionDeclaration forward_decl(DummyLoc, "Foo", {}, &params,
FunctionDeclaration forward_decl(DummyLoc, "Foo", {}, std::nullopt, &params,
ReturnTerm::Omitted(DummyLoc), std::nullopt);
EXPECT_THAT(forward_decl, MatchesFunctionDeclaration().WithName("Foo"));
EXPECT_THAT(forward_decl, Not(MatchesFunctionDeclaration().WithBody(_)));
@@ -150,7 +150,7 @@ TEST(MatchesUnimplementedExpressionTest, BasicUsage) {
TEST(ASTDeclarationsTest, BasicUsage) {
TuplePattern params(DummyLoc, {});
Block body(DummyLoc, {});
FunctionDeclaration decl(DummyLoc, "Foo", {}, &params,
FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, &params,
ReturnTerm::Omitted(DummyLoc), &body);
AST ast = {.declarations = {&decl}};
+31 -2
View File
@@ -22,7 +22,7 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(*this);
out << "class " << class_decl.name() << " {\n";
for (Nonnull<Member*> m : class_decl.members()) {
for (Nonnull<Declaration*> m : class_decl.members()) {
out << *m;
}
out << "}\n";
@@ -41,7 +41,11 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::VariableDeclaration: {
const auto& var = cast<VariableDeclaration>(*this);
out << "var " << var.binding() << " = " << var.initializer() << "\n";
out << "var " << var.binding();
if (var.has_initializer()) {
out << " = " << var.initializer();
}
out << ";\n";
break;
}
}
@@ -64,6 +68,31 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
}
}
// Look for the `me` parameter in the `deduced_parameters_`
// and put it in the `me_pattern_`.
void FunctionDeclaration::ResolveDeducedAndReceiver(
const std::vector<Nonnull<AstNode*>>& deduced_params) {
for (Nonnull<AstNode*> param : deduced_params) {
switch (param->kind()) {
case AstNodeKind::GenericBinding:
deduced_parameters_.push_back(&cast<GenericBinding>(*param));
break;
case AstNodeKind::BindingPattern: {
Nonnull<BindingPattern*> bp = &cast<BindingPattern>(*param);
if (me_pattern_.has_value() || bp->name() != "me") {
FATAL_COMPILATION_ERROR(source_loc())
<< "illegal binding pattern in implicit parameter list";
}
me_pattern_ = bp;
break;
}
default:
FATAL_COMPILATION_ERROR(source_loc())
<< "illegal AST node in implicit parameter list";
}
}
}
void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
out << "fn " << name_ << " ";
if (!deduced_parameters_.empty()) {
+27 -92
View File
@@ -10,8 +10,9 @@
#include <vector>
#include "common/ostream.h"
#include "executable_semantics/ast/member.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/return_term.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/statement.h"
#include "executable_semantics/ast/static_scope.h"
@@ -34,8 +35,8 @@ class Declaration : public AstNode {
public:
~Declaration() override = 0;
Declaration(const Member&) = delete;
auto operator=(const Member&) -> Declaration& = delete;
Declaration(const Declaration&) = delete;
auto operator=(const Declaration&) -> Declaration& = delete;
void Print(llvm::raw_ostream& out) const override;
@@ -126,100 +127,24 @@ class GenericBinding : public AstNode {
std::optional<Nonnull<const Value*>> constant_value_;
};
// The syntactic representation of a function declaration's return type.
// This syntax can take one of three forms:
// - An _explicit_ term consists of `->` followed by a type expression.
// - An _auto_ term consists of `-> auto`.
// - An _omitted_ term consists of no tokens at all.
// Each of these forms has a corresponding factory function.
class ReturnTerm {
public:
ReturnTerm(const ReturnTerm&) = default;
auto operator=(const ReturnTerm&) -> ReturnTerm& = default;
// Represents an omitted return term at `source_loc`.
static auto Omitted(SourceLocation source_loc) -> ReturnTerm {
return ReturnTerm(ReturnKind::Omitted, source_loc);
}
// Represents an auto return term at `source_loc`.
static auto Auto(SourceLocation source_loc) -> ReturnTerm {
return ReturnTerm(ReturnKind::Auto, source_loc);
}
// Represents an explicit return term with the given type expression.
static auto Explicit(Nonnull<Expression*> type_expression) -> ReturnTerm {
return ReturnTerm(type_expression);
}
// Returns true if this represents an omitted return term.
auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; }
// Returns true if this represents an auto return term.
auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; }
// If this represents an explicit return term, returns the type expression.
// Otherwise, returns nullopt.
auto type_expression() const -> std::optional<Nonnull<const Expression*>> {
return type_expression_;
}
auto type_expression() -> std::optional<Nonnull<Expression*>> {
return type_expression_;
}
// The static return type this term resolves to. Cannot be called before
// typechecking.
auto static_type() const -> const Value& { return **static_type_; }
// Sets the value of static_type(). Can only be called once, during
// typechecking.
void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
// Returns whether static_type() has been set. Should only be called
// during typechecking: before typechecking it's guaranteed to be false,
// and after typechecking it's guaranteed to be true.
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto source_loc() const -> SourceLocation { return source_loc_; }
void Print(llvm::raw_ostream& out) const;
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
private:
enum class ReturnKind { Omitted, Auto, Expression };
explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc)
: kind_(kind), source_loc_(source_loc) {
CHECK(kind != ReturnKind::Expression);
}
explicit ReturnTerm(Nonnull<Expression*> type_expression)
: kind_(ReturnKind::Expression),
type_expression_(type_expression),
source_loc_(type_expression->source_loc()) {}
ReturnKind kind_;
std::optional<Nonnull<Expression*>> type_expression_;
std::optional<Nonnull<const Value*>> static_type_;
SourceLocation source_loc_;
};
class FunctionDeclaration : public Declaration {
public:
using ImplementsCarbonNamedEntity = void;
FunctionDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<GenericBinding*>> deduced_params,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<BindingPattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: Declaration(AstNodeKind::FunctionDeclaration, source_loc),
name_(std::move(name)),
deduced_parameters_(std::move(deduced_params)),
me_pattern_(me_pattern),
param_pattern_(param_pattern),
return_term_(return_term),
body_(body) {}
body_(body) {
ResolveDeducedAndReceiver(deduced_params);
}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFunctionDeclaration(node->kind());
@@ -235,6 +160,8 @@ class FunctionDeclaration : public Declaration {
auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
return deduced_parameters_;
}
auto me_pattern() const -> const BindingPattern& { return **me_pattern_; }
auto me_pattern() -> BindingPattern& { return **me_pattern_; }
auto param_pattern() const -> const TuplePattern& { return *param_pattern_; }
auto param_pattern() -> TuplePattern& { return *param_pattern_; }
auto return_term() const -> const ReturnTerm& { return return_term_; }
@@ -254,9 +181,13 @@ class FunctionDeclaration : public Declaration {
constant_value_ = value;
}
bool is_method() const { return me_pattern_.has_value(); }
private:
void ResolveDeducedAndReceiver(const std::vector<Nonnull<AstNode*>>&);
std::string name_;
std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
std::optional<Nonnull<BindingPattern*>> me_pattern_;
Nonnull<TuplePattern*> param_pattern_;
ReturnTerm return_term_;
std::optional<Nonnull<Block*>> body_;
@@ -268,7 +199,7 @@ class ClassDeclaration : public Declaration {
using ImplementsCarbonNamedEntity = void;
ClassDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<Member*>> members)
std::vector<Nonnull<Declaration*>> members)
: Declaration(AstNodeKind::ClassDeclaration, source_loc),
name_(std::move(name)),
members_(std::move(members)) {}
@@ -278,7 +209,9 @@ class ClassDeclaration : public Declaration {
}
auto name() const -> const std::string& { return name_; }
auto members() const -> llvm::ArrayRef<Nonnull<Member*>> { return members_; }
auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
return members_;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
@@ -294,7 +227,7 @@ class ClassDeclaration : public Declaration {
private:
std::string name_;
std::vector<Nonnull<Member*>> members_;
std::vector<Nonnull<Declaration*>> members_;
std::optional<Nonnull<const Value*>> constant_value_;
};
@@ -367,7 +300,7 @@ class VariableDeclaration : public Declaration {
public:
VariableDeclaration(SourceLocation source_loc,
Nonnull<BindingPattern*> binding,
Nonnull<Expression*> initializer)
std::optional<Nonnull<Expression*>> initializer)
: Declaration(AstNodeKind::VariableDeclaration, source_loc),
binding_(binding),
initializer_(initializer) {}
@@ -378,15 +311,17 @@ class VariableDeclaration : public Declaration {
auto binding() const -> const BindingPattern& { return *binding_; }
auto binding() -> BindingPattern& { return *binding_; }
auto initializer() const -> const Expression& { return *initializer_; }
auto initializer() -> Expression& { return *initializer_; }
auto initializer() const -> const Expression& { return **initializer_; }
auto initializer() -> Expression& { return **initializer_; }
bool has_initializer() const { return initializer_.has_value(); }
private:
// TODO: split this into a non-optional name and a type, initialized by
// a constructor that takes a BindingPattern and handles errors like a
// missing name.
Nonnull<BindingPattern*> binding_;
Nonnull<Expression*> initializer_;
std::optional<Nonnull<Expression*>> initializer_;
};
} // namespace Carbon
-25
View File
@@ -1,25 +0,0 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "executable_semantics/ast/member.h"
#include "executable_semantics/common/arena.h"
#include "llvm/Support/Casting.h"
namespace Carbon {
using llvm::cast;
Member::~Member() = default;
void Member::Print(llvm::raw_ostream& out) const {
switch (kind()) {
case MemberKind::FieldMember:
const auto& field = cast<FieldMember>(*this);
out << "var " << field.binding() << ";\n";
break;
}
}
} // namespace Carbon
-70
View File
@@ -1,70 +0,0 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef EXECUTABLE_SEMANTICS_AST_MEMBER_H_
#define EXECUTABLE_SEMANTICS_AST_MEMBER_H_
#include <string>
#include "common/ostream.h"
#include "executable_semantics/ast/expression.h"
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/source_location.h"
#include "llvm/Support/Compiler.h"
namespace Carbon {
// Abstract base class of all AST nodes representing patterns.
//
// Member and its derived classes support LLVM-style RTTI, including
// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
// class derived from Member must provide a `classof` operation, and
// every concrete derived class must have a corresponding enumerator
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
// details.
class Member : public AstNode {
public:
~Member() override = 0;
Member(const Member&) = delete;
auto operator=(const Member&) -> Member& = delete;
void Print(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromMember(node->kind());
}
// Returns the enumerator corresponding to the most-derived type of this
// object.
auto kind() const -> MemberKind {
return static_cast<MemberKind>(root_kind());
}
protected:
Member(AstNodeKind kind, SourceLocation source_loc)
: AstNode(kind, source_loc) {}
};
class FieldMember : public Member {
public:
FieldMember(SourceLocation source_loc, Nonnull<BindingPattern*> binding)
: Member(AstNodeKind::FieldMember, source_loc), binding_(binding) {
CHECK(binding->name() != AnonymousName);
}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFieldMember(node->kind());
}
auto binding() const -> const BindingPattern& { return *binding_; }
auto binding() -> BindingPattern& { return *binding_; }
private:
Nonnull<BindingPattern*> binding_;
};
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_AST_MEMBER_H_
+1
View File
@@ -15,6 +15,7 @@
#include "executable_semantics/ast/expression.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/ast/value_category.h"
#include "llvm/ADT/ArrayRef.h"
namespace Carbon {
+102
View File
@@ -0,0 +1,102 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef EXECUTABLE_SEMANTICS_AST_RETURN_TERM_H_
#define EXECUTABLE_SEMANTICS_AST_RETURN_TERM_H_
#include <optional>
#include <utility>
#include "common/check.h"
#include "common/ostream.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/common/nonnull.h"
namespace Carbon {
class Value;
class Expression;
// The syntactic representation of a function declaration's return type.
// This syntax can take one of three forms:
// - An _explicit_ term consists of `->` followed by a type expression.
// - An _auto_ term consists of `-> auto`.
// - An _omitted_ term consists of no tokens at all.
// Each of these forms has a corresponding factory function.
class ReturnTerm {
public:
ReturnTerm(const ReturnTerm&) = default;
auto operator=(const ReturnTerm&) -> ReturnTerm& = default;
// Represents an omitted return term at `source_loc`.
static auto Omitted(SourceLocation source_loc) -> ReturnTerm {
return ReturnTerm(ReturnKind::Omitted, source_loc);
}
// Represents an auto return term at `source_loc`.
static auto Auto(SourceLocation source_loc) -> ReturnTerm {
return ReturnTerm(ReturnKind::Auto, source_loc);
}
// Represents an explicit return term with the given type expression.
static auto Explicit(Nonnull<Expression*> type_expression) -> ReturnTerm {
return ReturnTerm(type_expression);
}
// Returns true if this represents an omitted return term.
auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; }
// Returns true if this represents an auto return term.
auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; }
// If this represents an explicit return term, returns the type expression.
// Otherwise, returns nullopt.
auto type_expression() const -> std::optional<Nonnull<const Expression*>> {
return type_expression_;
}
auto type_expression() -> std::optional<Nonnull<Expression*>> {
return type_expression_;
}
// The static return type this term resolves to. Cannot be called before
// typechecking.
auto static_type() const -> const Value& { return **static_type_; }
// Sets the value of static_type(). Can only be called once, during
// typechecking.
void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
// Returns whether static_type() has been set. Should only be called
// during typechecking: before typechecking it's guaranteed to be false,
// and after typechecking it's guaranteed to be true.
auto has_static_type() const -> bool { return static_type_.has_value(); }
auto source_loc() const -> SourceLocation { return source_loc_; }
void Print(llvm::raw_ostream& out) const;
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
private:
enum class ReturnKind { Omitted, Auto, Expression };
explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc)
: kind_(kind), source_loc_(source_loc) {
CHECK(kind != ReturnKind::Expression);
}
explicit ReturnTerm(Nonnull<Expression*> type_expression)
: kind_(ReturnKind::Expression),
type_expression_(type_expression),
source_loc_(type_expression->source_loc()) {}
ReturnKind kind_;
std::optional<Nonnull<Expression*>> type_expression_;
std::optional<Nonnull<const Value*>> static_type_;
SourceLocation source_loc_;
};
} // namespace Carbon
#endif // EXECUTABLE_SEMANTICS_AST_RETURN_TERM_H_
+2
View File
@@ -9,8 +9,10 @@
#include <vector>
#include "common/ostream.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/expression.h"
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/return_term.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
#include "executable_semantics/ast/value_category.h"
+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:
+32 -24
View File
@@ -110,8 +110,8 @@
%type <std::vector<Nonnull<Statement*>>> statement_list
%type <Nonnull<Expression*>> expression
%type <Nonnull<GenericBinding*>> generic_binding
%type <std::vector<Nonnull<GenericBinding*>>> deduced_params
%type <std::vector<Nonnull<GenericBinding*>>> deduced_param_list
%type <std::vector<Nonnull<AstNode*>>> deduced_params
%type <std::vector<Nonnull<AstNode*>>> deduced_param_list
%type <Nonnull<Pattern*>> pattern
%type <Nonnull<Pattern*>> non_expression_pattern
%type <BisonWrap<ReturnTerm>> return_term
@@ -122,9 +122,8 @@
%type <std::vector<FieldInitializer>> struct_type_literal_contents
%type <Nonnull<TupleLiteral*>> tuple
%type <std::string> binding_lhs
%type <std::optional<Nonnull<BindingPattern*>>> receiver
%type <Nonnull<BindingPattern*>> variable_declaration
%type <Nonnull<Member*>> member
%type <std::vector<Nonnull<Member*>>> member_list
%type <ParenContents<Expression>> paren_expression_base
%type <ParenContents<Expression>> paren_expression_contents
%type <Nonnull<Pattern*>> paren_pattern
@@ -653,10 +652,10 @@ generic_binding:
;
deduced_param_list:
// Empty
{ $$ = std::vector<Nonnull<GenericBinding*>>(); }
{ $$ = std::vector<Nonnull<AstNode*>>(); }
| generic_binding
{
$$ = std::vector<Nonnull<GenericBinding*>>();
$$ = std::vector<Nonnull<AstNode*>>();
$$.push_back($1);
}
| generic_binding COMMA deduced_param_list
@@ -664,40 +663,44 @@ deduced_param_list:
$$ = $3;
$$.push_back($1);
}
| variable_declaration
{
$$ = std::vector<Nonnull<AstNode*>>();
$$.push_back($1);
}
| variable_declaration COMMA deduced_param_list
{
$$ = $3;
$$.push_back($1);
}
;
deduced_params:
// Empty
{ $$ = std::vector<Nonnull<GenericBinding*>>(); }
{ $$ = std::vector<Nonnull<AstNode*>>(); }
| LEFT_SQUARE_BRACKET deduced_param_list RIGHT_SQUARE_BRACKET
{ $$ = $2; }
;
receiver:
// Empty
{ $$ = std::nullopt; }
| LEFT_CURLY_BRACE variable_declaration RIGHT_CURLY_BRACE
{ $$ = $2; }
;
function_declaration:
FN identifier deduced_params maybe_empty_tuple_pattern return_term block
FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term block
{
$$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
$6);
$6, $7);
}
| FN identifier deduced_params maybe_empty_tuple_pattern return_term SEMICOLON
| FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term SEMICOLON
{
$$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
std::nullopt);
$6, std::nullopt);
}
;
variable_declaration: identifier COLON pattern
{ $$ = arena->New<BindingPattern>(context.source_loc(), $1, $3); }
;
member: VAR variable_declaration SEMICOLON
{ $$ = arena->New<FieldMember>(context.source_loc(), $2); }
;
member_list:
// Empty
{ $$ = {}; }
| member_list member
{
$$ = $1;
$$.push_back($2);
}
;
alternative:
identifier tuple
{ $$ = arena->New<AlternativeSignature>(context.source_loc(), $1, $2); }
@@ -728,10 +731,15 @@ alternative_list_contents:
declaration:
function_declaration
{ $$ = $1; }
| CLASS identifier LEFT_CURLY_BRACE member_list RIGHT_CURLY_BRACE
| CLASS identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ClassDeclaration>(context.source_loc(), $2, $4); }
| CHOICE identifier LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $4); }
| VAR variable_declaration SEMICOLON
{
$$ = arena->New<VariableDeclaration>(context.source_loc(), $2,
std::nullopt);
}
| VAR variable_declaration EQUAL expression SEMICOLON
{ $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4); }
;
+29
View File
@@ -0,0 +1,29 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
fn GetX[me: Point]() -> i32 {
return me.x;
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
var f: __Fn()-> i32 = p.GetX;
return f();
}
@@ -0,0 +1,27 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
return p.x;
}
@@ -0,0 +1,27 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
return p.Origin().x;
}
@@ -0,0 +1,27 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var f: __Fn()->Point = Point.Origin;
return f().x;
}
@@ -0,0 +1,30 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{not} %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/class/fail_method_from_class.carbon:29: GetX is not a class function
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
fn GetX[me: Point]() -> i32 {
return me.x;
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
return Point.GetX();
}
+31
View File
@@ -0,0 +1,31 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
fn GetX[me: Point]() -> i32 {
return me.x;
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
return p.GetX();
}
@@ -0,0 +1,35 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 0
package ExecutableSemanticsTest api;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
fn GetX[me: Point]() -> i32 {
return me.x;
}
fn GetXY[me: Point]() -> (i32, i32) {
return (me.GetX(), me.y);
}
var x: i32;
var y: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
return p.GetXY()[0];
}