mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +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:
@@ -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",
|
||||
|
||||
@@ -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", {}, ¶ms,
|
||||
FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, ¶ms,
|
||||
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", {}, ¶ms,
|
||||
FunctionDeclaration forward_decl(DummyLoc, "Foo", {}, std::nullopt, ¶ms,
|
||||
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", {}, ¶ms,
|
||||
FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, ¶ms,
|
||||
ReturnTerm::Omitted(DummyLoc), &body);
|
||||
AST ast = {.declarations = {&decl}};
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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 {
|
||||
|
||||
@@ -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_
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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); }
|
||||
;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
Reference in New Issue
Block a user