Generic classes (#1124)

* start of generic classes

* fix regressions

* class functions in interfaces

* access to class function on interface from class parameter

* more stuff working for generic classes

* fixing bugs

* update TypeEqual for generic classes

* fixing bugs and finding new ones

* disable unqualified access to members from other members for now

* minor edits

* bug fixes

* introduce compile_time_value to use in type checker instead of constant_value

* cleanup

* put a CHECK back in

* failure test cases for the new FATAL_COMPILATION_ERROR

* change a runtime FATAL into a FATAL_COMPILATION_ERROR

* Update executable_semantics/ast/declaration.h

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/action_stack.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/interpreter.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/interpreter.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/resolve_names.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* responses to reviews

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* rename compile_time_value to symbolic_identity

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/type_checker.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Jon Meow <jperkins@google.com>

* more edits from review

* Apply suggestions from code review

Co-authored-by: Geoff Romer <gromer@google.com>

* review responses

* Update executable_semantics/interpreter/interpreter.cpp

Co-authored-by: Geoff Romer <gromer@google.com>

* const impl_scope for TypeCheckChoiceDeclaration

* add some const

Co-authored-by: Jon Meow <jperkins@google.com>
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
Jeremy G. Siek
2022-03-29 13:09:41 -04:00
committed by GitHub
co-authored by Jon Meow Geoff Romer
parent 37a0e35b31
commit 210856dd57
41 changed files with 1597 additions and 401 deletions
+4 -4
View File
@@ -70,12 +70,13 @@ cc_test(
)
cc_library(
name = "generic_binding",
name = "impl_binding",
hdrs = [
"generic_binding.h",
"impl_binding.h",
],
deps = [
":ast_node",
":pattern",
":source_location",
":value_category",
"//common:check",
@@ -93,7 +94,7 @@ cc_library(
],
deps = [
":ast_node",
":generic_binding",
":impl_binding",
":pattern",
":return_term",
":source_location",
@@ -125,7 +126,6 @@ cc_library(
hdrs = ["expression.h"],
deps = [
":ast_node",
":generic_binding",
":paren_contents",
":source_location",
":static_scope",
+3
View File
@@ -43,7 +43,10 @@ class AstNode {
auto operator=(AstNode&&) -> AstNode& = delete;
virtual ~AstNode() = 0;
// Print the AST rooted at the node.
virtual void Print(llvm::raw_ostream& out) const = 0;
// Print identifying information about the node, such as it's name.
virtual void PrintID(llvm::raw_ostream& out) const = 0;
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
// Returns an enumerator specifying the concrete type of this node.
+1 -1
View File
@@ -7,6 +7,7 @@ abstract class Pattern : AstNode;
class AutoPattern : Pattern;
class VarPattern : Pattern;
class BindingPattern : Pattern;
class GenericBinding : Pattern;
class TuplePattern : Pattern;
class AlternativePattern : Pattern;
class ExpressionPattern : Pattern;
@@ -17,7 +18,6 @@ abstract class Declaration : AstNode;
class VariableDeclaration : Declaration;
class InterfaceDeclaration : Declaration;
class ImplDeclaration : Declaration;
class GenericBinding : AstNode;
class ImplBinding : AstNode;
class AlternativeSignature : AstNode;
abstract class Statement : AstNode;
+62 -13
View File
@@ -17,7 +17,8 @@ void Declaration::Print(llvm::raw_ostream& out) const {
switch (kind()) {
case DeclarationKind::InterfaceDeclaration: {
const auto& iface_decl = cast<InterfaceDeclaration>(*this);
out << "interface " << iface_decl.name() << " {\n";
PrintID(out);
out << " {\n";
for (Nonnull<Declaration*> m : iface_decl.members()) {
out << *m;
}
@@ -26,15 +27,8 @@ void Declaration::Print(llvm::raw_ostream& out) const {
}
case DeclarationKind::ImplDeclaration: {
const auto& impl_decl = cast<ImplDeclaration>(*this);
switch (impl_decl.kind()) {
case ImplKind::InternalImpl:
break;
case ImplKind::ExternalImpl:
out << "external ";
break;
}
out << "impl " << *impl_decl.impl_type() << " as "
<< impl_decl.interface() << " {\n";
PrintID(out);
out << " {\n";
for (Nonnull<Declaration*> m : impl_decl.members()) {
out << *m;
}
@@ -47,7 +41,11 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(*this);
out << "class " << class_decl.name() << " {\n";
PrintID(out);
if (class_decl.type_params().has_value()) {
out << **class_decl.type_params();
}
out << " {\n";
for (Nonnull<Declaration*> m : class_decl.members()) {
out << *m;
}
@@ -57,7 +55,8 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(*this);
out << "choice " << choice.name() << " {\n";
PrintID(out);
out << " {\n";
for (Nonnull<const AlternativeSignature*> alt : choice.alternatives()) {
out << *alt << ";\n";
}
@@ -67,7 +66,7 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::VariableDeclaration: {
const auto& var = cast<VariableDeclaration>(*this);
out << "var " << var.binding();
PrintID(out);
if (var.has_initializer()) {
out << " = " << var.initializer();
}
@@ -77,6 +76,50 @@ void Declaration::Print(llvm::raw_ostream& out) const {
}
}
void Declaration::PrintID(llvm::raw_ostream& out) const {
switch (kind()) {
case DeclarationKind::InterfaceDeclaration: {
const auto& iface_decl = cast<InterfaceDeclaration>(*this);
out << "interface" << iface_decl.name();
break;
}
case DeclarationKind::ImplDeclaration: {
const auto& impl_decl = cast<ImplDeclaration>(*this);
switch (impl_decl.kind()) {
case ImplKind::InternalImpl:
break;
case ImplKind::ExternalImpl:
out << "external ";
break;
}
out << "impl " << *impl_decl.impl_type() << " as "
<< impl_decl.interface();
break;
}
case DeclarationKind::FunctionDeclaration:
out << "fn " << cast<FunctionDeclaration>(*this).name();
break;
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(*this);
out << "class " << class_decl.name();
break;
}
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(*this);
out << "choice " << choice.name();
break;
}
case DeclarationKind::VariableDeclaration: {
const auto& var = cast<VariableDeclaration>(*this);
out << "var " << var.binding();
break;
}
}
}
auto GetName(const Declaration& declaration) -> std::optional<std::string> {
switch (declaration.kind()) {
case DeclarationKind::FunctionDeclaration:
@@ -98,6 +141,8 @@ void GenericBinding::Print(llvm::raw_ostream& out) const {
out << name() << ":! " << type();
}
void GenericBinding::PrintID(llvm::raw_ostream& out) const { out << name(); }
void ReturnTerm::Print(llvm::raw_ostream& out) const {
switch (kind_) {
case ReturnKind::Omitted:
@@ -170,4 +215,8 @@ void AlternativeSignature::Print(llvm::raw_ostream& out) const {
out << "alt " << name() << " " << signature();
}
void AlternativeSignature::PrintID(llvm::raw_ostream& out) const {
out << name();
}
} // namespace Carbon
+31 -54
View File
@@ -11,7 +11,7 @@
#include "common/ostream.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/generic_binding.h"
#include "executable_semantics/ast/impl_binding.h"
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/return_term.h"
#include "executable_semantics/ast/source_location.h"
@@ -40,6 +40,7 @@ class Declaration : public AstNode {
auto operator=(const Declaration&) -> Declaration& = delete;
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromDeclaration(node->kind());
@@ -67,6 +68,23 @@ class Declaration : public AstNode {
// and after typechecking it's guaranteed to be true.
auto has_static_type() const -> bool { return static_type_.has_value(); }
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
// See static_scope.h for API.
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// See static_scope.h for API.
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
protected:
// Constructs a Declaration representing syntax at the given line number.
// `kind` must be the enumerator corresponding to the most-derived type being
@@ -76,6 +94,7 @@ class Declaration : public AstNode {
private:
std::optional<Nonnull<const Value*>> static_type_;
std::optional<Nonnull<const Value*>> constant_value_;
};
class FunctionDeclaration : public Declaration {
@@ -130,16 +149,6 @@ class FunctionDeclaration : public Declaration {
auto body() -> std::optional<Nonnull<Block*>> { return body_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
auto is_method() const -> bool { return me_pattern_.has_value(); }
@@ -150,7 +159,6 @@ class FunctionDeclaration : public Declaration {
Nonnull<TuplePattern*> param_pattern_;
ReturnTerm return_term_;
std::optional<Nonnull<Block*>> body_;
std::optional<Nonnull<const Value*>> constant_value_;
};
class ClassDeclaration : public Declaration {
@@ -158,9 +166,11 @@ class ClassDeclaration : public Declaration {
using ImplementsCarbonValueNode = void;
ClassDeclaration(SourceLocation source_loc, std::string name,
std::optional<Nonnull<TuplePattern*>> type_params,
std::vector<Nonnull<Declaration*>> members)
: Declaration(AstNodeKind::ClassDeclaration, source_loc),
name_(std::move(name)),
type_params_(type_params),
members_(std::move(members)) {}
static auto classof(const AstNode* node) -> bool {
@@ -168,26 +178,23 @@ class ClassDeclaration : public Declaration {
}
auto name() const -> const std::string& { return name_; }
auto type_params() const -> std::optional<Nonnull<const TuplePattern*>> {
return type_params_;
}
auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
return type_params_;
}
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*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
std::optional<Nonnull<TuplePattern*>> type_params_;
std::vector<Nonnull<Declaration*>> members_;
std::optional<Nonnull<const Value*>> constant_value_;
};
class AlternativeSignature : public AstNode {
@@ -199,6 +206,7 @@ class AlternativeSignature : public AstNode {
signature_(signature) {}
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromAlternativeSignature(node->kind());
@@ -237,21 +245,10 @@ class ChoiceDeclaration : public Declaration {
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
std::vector<Nonnull<AlternativeSignature*>> alternatives_;
std::optional<Nonnull<const Value*>> constant_value_;
};
// Global variable definition implements the Declaration concept.
@@ -311,21 +308,10 @@ class InterfaceDeclaration : public Declaration {
auto self() -> Nonnull<GenericBinding*> { return self_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
private:
std::string name_;
std::vector<Nonnull<Declaration*>> members_;
std::optional<Nonnull<const Value*>> constant_value_;
Nonnull<GenericBinding*> self_;
};
@@ -364,14 +350,6 @@ class ImplDeclaration : public Declaration {
auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
return members_;
}
// Return the witness table for this impl.
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
@@ -380,7 +358,6 @@ class ImplDeclaration : public Declaration {
Nonnull<Expression*> interface_;
std::optional<Nonnull<const Value*>> interface_type_;
std::vector<Nonnull<Declaration*>> members_;
std::optional<Nonnull<const Value*>> constant_value_;
};
// Return the name of a declaration, if it has one.
+58 -29
View File
@@ -116,12 +116,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
PrintFields(out, cast<StructTypeLiteral>(*this).fields(), ": ");
out << "}";
break;
case ExpressionKind::IntLiteral:
out << cast<IntLiteral>(*this).value();
break;
case ExpressionKind::BoolLiteral:
out << (cast<BoolLiteral>(*this).value() ? "true" : "false");
break;
case ExpressionKind::PrimitiveOperatorExpression: {
out << "(";
const auto& op = cast<PrimitiveOperatorExpression>(*this);
@@ -142,9 +136,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
out << ")";
break;
}
case ExpressionKind::IdentifierExpression:
out << cast<IdentifierExpression>(*this).name();
break;
case ExpressionKind::CallExpression: {
const auto& call = cast<CallExpression>(*this);
out << call.function();
@@ -155,26 +146,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
}
break;
}
case ExpressionKind::BoolTypeLiteral:
out << "Bool";
break;
case ExpressionKind::IntTypeLiteral:
out << "i32";
break;
case ExpressionKind::StringLiteral:
out << "\"";
out.write_escaped(cast<StringLiteral>(*this).value());
out << "\"";
break;
case ExpressionKind::StringTypeLiteral:
out << "String";
break;
case ExpressionKind::TypeTypeLiteral:
out << "Type";
break;
case ExpressionKind::ContinuationTypeLiteral:
out << "Continuation";
break;
case ExpressionKind::FunctionTypeLiteral: {
const auto& fn = cast<FunctionTypeLiteral>(*this);
out << "fn " << fn.parameter() << " -> " << fn.return_type();
@@ -205,6 +176,64 @@ void Expression::Print(llvm::raw_ostream& out) const {
out << ")";
break;
}
case ExpressionKind::IdentifierExpression:
case ExpressionKind::IntLiteral:
case ExpressionKind::BoolLiteral:
case ExpressionKind::BoolTypeLiteral:
case ExpressionKind::IntTypeLiteral:
case ExpressionKind::StringLiteral:
case ExpressionKind::StringTypeLiteral:
case ExpressionKind::TypeTypeLiteral:
case ExpressionKind::ContinuationTypeLiteral:
PrintID(out);
break;
}
}
void Expression::PrintID(llvm::raw_ostream& out) const {
switch (kind()) {
case ExpressionKind::IdentifierExpression:
out << cast<IdentifierExpression>(*this).name();
break;
case ExpressionKind::IntLiteral:
out << cast<IntLiteral>(*this).value();
break;
case ExpressionKind::BoolLiteral:
out << (cast<BoolLiteral>(*this).value() ? "true" : "false");
break;
case ExpressionKind::BoolTypeLiteral:
out << "Bool";
break;
case ExpressionKind::IntTypeLiteral:
out << "i32";
break;
case ExpressionKind::StringLiteral:
out << "\"";
out.write_escaped(cast<StringLiteral>(*this).value());
out << "\"";
break;
case ExpressionKind::StringTypeLiteral:
out << "String";
break;
case ExpressionKind::TypeTypeLiteral:
out << "Type";
break;
case ExpressionKind::ContinuationTypeLiteral:
out << "Continuation";
break;
case ExpressionKind::IndexExpression:
case ExpressionKind::FieldAccessExpression:
case ExpressionKind::IfExpression:
case ExpressionKind::TupleLiteral:
case ExpressionKind::StructLiteral:
case ExpressionKind::StructTypeLiteral:
case ExpressionKind::CallExpression:
case ExpressionKind::PrimitiveOperatorExpression:
case ExpressionKind::IntrinsicExpression:
case ExpressionKind::UnimplementedExpression:
case ExpressionKind::FunctionTypeLiteral:
out << "...";
break;
}
}
+18 -3
View File
@@ -5,6 +5,7 @@
#ifndef EXECUTABLE_SEMANTICS_AST_EXPRESSION_H_
#define EXECUTABLE_SEMANTICS_AST_EXPRESSION_H_
#include <map>
#include <optional>
#include <string>
#include <variant>
@@ -12,7 +13,6 @@
#include "common/ostream.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/generic_binding.h"
#include "executable_semantics/ast/paren_contents.h"
#include "executable_semantics/ast/source_location.h"
#include "executable_semantics/ast/static_scope.h"
@@ -25,12 +25,14 @@ namespace Carbon {
class Value;
class VariableType;
class ImplBinding;
class Expression : public AstNode {
public:
~Expression() override = 0;
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) {
return InheritsFromExpression(node->kind());
@@ -43,7 +45,10 @@ class Expression : public AstNode {
}
// The static type of this expression. Cannot be called before typechecking.
auto static_type() const -> const Value& { return **static_type_; }
auto static_type() const -> const Value& {
CHECK(static_type_.has_value());
return **static_type_;
}
// Sets the static type of this expression. Can only be called once, during
// typechecking.
@@ -354,7 +359,10 @@ class PrimitiveOperatorExpression : public Expression {
std::vector<Nonnull<Expression*>> arguments_;
};
class ImplBinding;
class GenericBinding;
using BindingMap =
std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
class CallExpression : public Expression {
public:
@@ -390,10 +398,17 @@ class CallExpression : public Expression {
impls_ = impls;
}
auto deduced_args() const -> const BindingMap& { return deduced_args_; }
void set_deduced_args(const BindingMap& deduced_args) {
deduced_args_ = deduced_args;
}
private:
Nonnull<Expression*> function_;
Nonnull<Expression*> argument_;
std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls_;
BindingMap deduced_args_;
};
class FunctionTypeLiteral : public Expression {
@@ -10,6 +10,7 @@
#include "common/check.h"
#include "common/ostream.h"
#include "executable_semantics/ast/ast_node.h"
#include "executable_semantics/ast/pattern.h"
#include "executable_semantics/ast/value_category.h"
namespace Carbon {
@@ -18,71 +19,6 @@ class Value;
class Expression;
class ImplBinding;
// TODO: expand the kinds of things that can be deduced parameters.
// For now, only generic parameters are supported.
class GenericBinding : public AstNode {
public:
using ImplementsCarbonValueNode = void;
GenericBinding(SourceLocation source_loc, std::string name,
Nonnull<Expression*> type)
: AstNode(AstNodeKind::GenericBinding, source_loc),
name_(std::move(name)),
type_(type) {}
void Print(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromGenericBinding(node->kind());
}
auto name() const -> const std::string& { return name_; }
auto type() const -> const Expression& { return *type_; }
auto type() -> Expression& { return *type_; }
// The static type of the binding. Cannot be called before typechecking.
auto static_type() const -> const Value& { return **static_type_; }
// Sets the static type of the binding. Can only be called once, during
// typechecking.
void set_static_type(Nonnull<const Value*> type) {
CHECK(!static_type_.has_value());
static_type_ = type;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return constant_value_;
}
// Sets the value returned by constant_value(). Can only be called once,
// during typechecking.
void set_constant_value(Nonnull<const Value*> value) {
CHECK(!constant_value_.has_value());
constant_value_ = value;
}
// The impl binding associated with this type variable.
auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
return impl_binding_;
}
// Set the impl binding.
void set_impl_binding(Nonnull<const ImplBinding*> binding) {
CHECK(!impl_binding_.has_value());
impl_binding_ = binding;
}
private:
std::string name_;
Nonnull<Expression*> type_;
std::optional<Nonnull<const Value*>> static_type_;
std::optional<Nonnull<const Value*>> constant_value_;
std::optional<Nonnull<const ImplBinding*>> impl_binding_;
};
using BindingMap =
std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
// The run-time counterpart of a `GenericBinding`.
//
// Once a generic binding has been declared, it can be used
@@ -106,6 +42,7 @@ class ImplBinding : public AstNode {
return InheritsFromImplBinding(node->kind());
}
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
// The binding for the type variable.
auto type_var() const -> Nonnull<const GenericBinding*> { return type_var_; }
@@ -116,6 +53,9 @@ class ImplBinding : public AstNode {
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
// The static type of the impl. Cannot be called before typechecking.
auto static_type() const -> const Value& { return **static_type_; }
+40
View File
@@ -29,6 +29,11 @@ void Pattern::Print(llvm::raw_ostream& out) const {
out << binding.name() << ": " << binding.type();
break;
}
case PatternKind::GenericBinding: {
const auto& binding = cast<GenericBinding>(*this);
out << binding.name() << ":! " << binding.type();
break;
}
case PatternKind::TuplePattern: {
const auto& tuple = cast<TuplePattern>(*this);
out << "(";
@@ -54,6 +59,40 @@ void Pattern::Print(llvm::raw_ostream& out) const {
}
}
void Pattern::PrintID(llvm::raw_ostream& out) const {
switch (kind()) {
case PatternKind::AutoPattern:
out << "auto";
break;
case PatternKind::BindingPattern: {
const auto& binding = cast<BindingPattern>(*this);
out << binding.name();
break;
}
case PatternKind::GenericBinding: {
const auto& binding = cast<GenericBinding>(*this);
out << binding.name();
break;
}
case PatternKind::TuplePattern: {
out << "(...)";
break;
}
case PatternKind::AlternativePattern: {
const auto& alternative = cast<AlternativePattern>(*this);
out << alternative.choice_type() << "." << alternative.alternative_name()
<< "(...)";
break;
}
case PatternKind::VarPattern:
out << "var ...";
break;
case PatternKind::ExpressionPattern:
out << "...";
break;
}
}
// Equivalent to `GetBindings`, but stores its output in `bindings` instead of
// returning it.
static void GetBindingsImpl(
@@ -73,6 +112,7 @@ static void GetBindingsImpl(
return;
case PatternKind::AutoPattern:
case PatternKind::ExpressionPattern:
case PatternKind::GenericBinding:
return;
case PatternKind::VarPattern:
GetBindingsImpl(cast<VarPattern>(pattern).pattern(), bindings);
+60 -1
View File
@@ -38,6 +38,7 @@ class Pattern : public AstNode {
~Pattern() override = 0;
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromPattern(node->kind());
@@ -50,7 +51,10 @@ class Pattern : public AstNode {
}
// The static type of this pattern. Cannot be called before typechecking.
auto static_type() const -> const Value& { return **static_type_; }
auto static_type() const -> const Value& {
CHECK(static_type_.has_value());
return **static_type_;
}
// Sets the static type of this expression. Can only be called once, during
// typechecking.
@@ -168,6 +172,9 @@ class BindingPattern : public Pattern {
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
private:
std::string name_;
@@ -195,6 +202,58 @@ class TuplePattern : public Pattern {
std::vector<Nonnull<Pattern*>> fields_;
};
class GenericBinding : public Pattern {
public:
using ImplementsCarbonValueNode = void;
GenericBinding(SourceLocation source_loc, std::string name,
Nonnull<Expression*> type)
: Pattern(AstNodeKind::GenericBinding, source_loc),
name_(std::move(name)),
type_(type) {}
void Print(llvm::raw_ostream& out) const override;
void PrintID(llvm::raw_ostream& out) const override;
static auto classof(const AstNode* node) -> bool {
return InheritsFromGenericBinding(node->kind());
}
auto name() const -> const std::string& { return name_; }
auto type() const -> const Expression& { return *type_; }
auto type() -> Expression& { return *type_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return symbolic_identity_;
}
void set_symbolic_identity(Nonnull<const Value*> value) {
CHECK(!symbolic_identity_.has_value());
symbolic_identity_ = value;
}
// The impl binding associated with this type variable.
auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
return impl_binding_;
}
// Set the impl binding.
void set_impl_binding(Nonnull<const ImplBinding*> binding) {
CHECK(!impl_binding_.has_value());
impl_binding_ = binding;
}
private:
std::string name_;
Nonnull<Expression*> type_;
std::optional<Nonnull<const Value*>> symbolic_identity_;
std::optional<Nonnull<const ImplBinding*>> impl_binding_;
};
// Converts paren_contents to a Pattern, interpreting the parentheses as
// grouping if their contents permit that interpretation, or as forming a
// tuple otherwise.
+4
View File
@@ -29,6 +29,7 @@ class Statement : public AstNode {
~Statement() override = 0;
void Print(llvm::raw_ostream& out) const override { PrintDepth(-1, out); }
void PrintID(llvm::raw_ostream& out) const override { PrintDepth(1, out); }
void PrintDepth(int depth, llvm::raw_ostream& out) const;
static auto classof(const AstNode* node) {
@@ -350,6 +351,9 @@ class Continuation : public Statement {
auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
}
private:
std::string name_;
+22 -4
View File
@@ -38,15 +38,22 @@ static constexpr bool ImplementsValueNode = false;
with a value, such as declarations and bindings. The interface consists of
the following methods:
// Returns the constant associated with the node.
// This is called by the interpreter, not the type checker.
auto constant_value() const -> std::optional<Nonnull<const Value*>>;
// Returns the symbolic compile-time identity of the node.
// This is called by the type checker, not the interpreter.
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>>;
// Returns the static type of an IdentifierExpression that names *this.
auto static_type() const -> const Value&;
// Returns the value category of an IdentifierExpression that names *this.
auto value_category() const -> ValueCategory;
// Print the node for diagnostic or tracing purposes.
void Print(llvm::raw_ostream& out) const;
// Print the node's identity (e.g. its name).
void PrintID(llvm::raw_ostream& out) const;
*/
// TODO: consider turning the above documentation into real code, as sketched
@@ -70,9 +77,13 @@ class ValueNodeView {
[](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
return llvm::cast<NodeType>(base).constant_value();
}),
symbolic_identity_(
[](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
return llvm::cast<NodeType>(base).symbolic_identity();
}),
print_([](const AstNode& base, llvm::raw_ostream& out) -> void {
// TODO: change this to print a summary of the node
return llvm::cast<NodeType>(base).Print(out);
return llvm::cast<NodeType>(base).PrintID(out);
}),
static_type_([](const AstNode& base) -> const Value& {
return llvm::cast<NodeType>(base).static_type();
@@ -94,6 +105,11 @@ class ValueNodeView {
return constant_value_(*base_);
}
// Returns node->symbolic_identity()
auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
return symbolic_identity_(*base_);
}
void Print(llvm::raw_ostream& out) const { print_(*base_, out); }
// Returns node->static_type()
@@ -123,6 +139,8 @@ class ValueNodeView {
Nonnull<const AstNode*> base_;
std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
constant_value_;
std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
symbolic_identity_;
std::function<void(const AstNode&, llvm::raw_ostream&)> print_;
std::function<const Value&(const AstNode&)> static_type_;
std::function<ValueCategory(const AstNode&)> value_category_;
-1
View File
@@ -11,7 +11,6 @@ cc_library(
"//executable_semantics/ast",
"//executable_semantics/ast:declaration",
"//executable_semantics/ast:expression",
"//executable_semantics/ast:generic_binding",
"@llvm-project//llvm:Support",
],
)
+13 -9
View File
@@ -8,7 +8,6 @@
#include "executable_semantics/ast/declaration.h"
#include "executable_semantics/ast/expression.h"
#include "executable_semantics/ast/generic_binding.h"
#include "llvm/Support/Casting.h"
namespace Carbon {
@@ -240,6 +239,14 @@ static auto BindingPatternToProto(const BindingPattern& pattern)
return pattern_proto;
}
static auto GenericBindingToProto(const GenericBinding& binding)
-> Fuzzing::GenericBinding {
Fuzzing::GenericBinding binding_proto;
binding_proto.set_name(binding.name());
*binding_proto.mutable_type() = ExpressionToProto(binding.type());
return binding_proto;
}
static auto TuplePatternToProto(const TuplePattern& tuple_pattern)
-> Fuzzing::TuplePattern {
Fuzzing::TuplePattern tuple_pattern_proto;
@@ -252,6 +259,11 @@ static auto TuplePatternToProto(const TuplePattern& tuple_pattern)
static auto PatternToProto(const Pattern& pattern) -> Fuzzing::Pattern {
Fuzzing::Pattern pattern_proto;
switch (pattern.kind()) {
case PatternKind::GenericBinding: {
const auto& binding = cast<GenericBinding>(pattern);
*pattern_proto.mutable_generic_binding() = GenericBindingToProto(binding);
break;
}
case PatternKind::BindingPattern: {
const auto& binding = cast<BindingPattern>(pattern);
*pattern_proto.mutable_binding_pattern() = BindingPatternToProto(binding);
@@ -422,14 +434,6 @@ static auto ReturnTermToProto(const ReturnTerm& return_term)
return return_term_proto;
}
static auto GenericBindingToProto(const GenericBinding& binding)
-> Fuzzing::GenericBinding {
Fuzzing::GenericBinding binding_proto;
binding_proto.set_name(binding.name());
*binding_proto.mutable_type() = ExpressionToProto(binding.type());
return binding_proto;
}
static auto DeclarationToProto(const Declaration& declaration)
-> Fuzzing::Declaration {
Fuzzing::Declaration declaration_proto;
@@ -51,10 +51,11 @@ void ActionStack::Initialize(ValueNodeView value_node,
auto ActionStack::ValueOfNode(ValueNodeView value_node,
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>> {
if (std::optional<Nonnull<const Value*>> constant_value =
value_node.constant_value();
constant_value.has_value()) {
return *constant_value;
std::optional<const Value*> value = (phase_ == Phase::CompileTime)
? value_node.symbolic_identity()
: value_node.constant_value();
if (value.has_value()) {
return *value;
}
for (const std::unique_ptr<Action>& action : todo_) {
// TODO: have static name resolution identify the scope of value_node
@@ -15,16 +15,19 @@
namespace Carbon {
// Selects between compile-time and run-time behavior.
enum class Phase { CompileTime, RunTime };
// The stack of Actions currently being executed by the interpreter.
class ActionStack {
public:
// Constructs an empty compile-time ActionStack.
ActionStack() = default;
ActionStack() : phase_(Phase::CompileTime) {}
// Constructs an empty run-time ActionStack that allocates global variables
// on `heap`.
explicit ActionStack(Nonnull<HeapAllocationInterface*> heap)
: globals_(RuntimeScope(heap)) {}
: globals_(RuntimeScope(heap)), phase_(Phase::RunTime) {}
void Print(llvm::raw_ostream& out) const;
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
@@ -120,6 +123,7 @@ class ActionStack {
Stack<std::unique_ptr<Action>> todo_;
std::optional<Nonnull<const Value*>> result_;
std::optional<RuntimeScope> globals_;
Phase phase_;
};
} // namespace Carbon
@@ -6,6 +6,7 @@
#include "executable_semantics/common/error.h"
#include "executable_semantics/interpreter/value.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
using llvm::cast;
@@ -79,4 +80,17 @@ auto ImplScope::ResolveHere(Nonnull<const Value*> iface_type,
}
}
// TODO: Add indentation when printing the parents.
void ImplScope::Print(llvm::raw_ostream& out) const {
out << "impls: ";
llvm::ListSeparator sep;
for (Impl impl : impls_) {
out << sep << *(impl.type) << " as " << *(impl.interface);
}
out << "\n";
for (const Nonnull<const ImplScope*>& parent : parent_scopes_) {
out << *parent;
}
}
} // namespace Carbon
@@ -52,6 +52,8 @@ class ImplScope {
auto Resolve(Nonnull<const Value*> iface, Nonnull<const Value*> type,
SourceLocation source_loc) const -> ErrorOr<ValueNodeView>;
void Print(llvm::raw_ostream& out) const;
private:
auto TryResolve(Nonnull<const Value*> iface_type, Nonnull<const Value*> type,
SourceLocation source_loc) const
+234 -44
View File
@@ -29,9 +29,6 @@ using llvm::isa;
namespace Carbon {
// Selects between compile-time and run-time behavior.
enum class Phase { CompileTime, RunTime };
// Constructs an ActionStack suitable for the specified phase.
static auto MakeTodo(Phase phase, Nonnull<Heap*> heap) -> ActionStack {
switch (phase) {
@@ -54,7 +51,8 @@ class Interpreter {
: arena_(arena),
heap_(arena),
todo_(MakeTodo(phase, &heap_)),
trace_(trace) {}
trace_(trace),
phase_(phase) {}
~Interpreter();
@@ -90,11 +88,25 @@ class Interpreter {
// Returns the result of converting `value` to type `destination_type`.
auto Convert(Nonnull<const Value*> value,
Nonnull<const Value*> destination_type) const
-> Nonnull<const Value*>;
Nonnull<const Value*> destination_type,
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>>;
// Instantiate a type by replacing all type variables that occur inside the
// type by the current values of those variables.
//
// For example, suppose T=i32 and U=Bool. Then
// __Fn (Point(T)) -> Point(U)
// becomes
// __Fn (Point(i32)) -> Point(Bool)
auto InstantiateType(Nonnull<const Value*> type,
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>>;
void PrintState(llvm::raw_ostream& out);
Phase phase() const { return phase_; }
Nonnull<Arena*> arena_;
Heap heap_;
@@ -106,6 +118,7 @@ class Interpreter {
std::vector<Nonnull<ContinuationValue::StackFragment*>> stack_fragments_;
bool trace_;
Phase phase_;
};
Interpreter::~Interpreter() {
@@ -178,7 +191,8 @@ auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
SourceLocation source_loc,
std::optional<Nonnull<RuntimeScope*>> bindings) -> bool {
std::optional<Nonnull<RuntimeScope*>> bindings,
BindingMap& generic_args) -> bool {
switch (p->kind()) {
case Value::Kind::BindingPlaceholderValue: {
CHECK(bindings.has_value());
@@ -188,6 +202,11 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
}
return true;
}
case Value::Kind::VariableType: {
const auto& var_type = cast<VariableType>(*p);
generic_args[&var_type.binding()] = v;
return true;
}
case Value::Kind::TupleValue:
switch (v->kind()) {
case Value::Kind::TupleValue: {
@@ -196,7 +215,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
CHECK(p_tup.elements().size() == v_tup.elements().size());
for (size_t i = 0; i < p_tup.elements().size(); ++i) {
if (!PatternMatch(p_tup.elements()[i], v_tup.elements()[i],
source_loc, bindings)) {
source_loc, bindings, generic_args)) {
return false;
}
} // for
@@ -212,7 +231,8 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
for (size_t i = 0; i < p_struct.elements().size(); ++i) {
CHECK(p_struct.elements()[i].name == v_struct.elements()[i].name);
if (!PatternMatch(p_struct.elements()[i].value,
v_struct.elements()[i].value, source_loc, bindings)) {
v_struct.elements()[i].value, source_loc, bindings,
generic_args)) {
return false;
}
}
@@ -228,7 +248,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
return false;
}
return PatternMatch(&p_alt.argument(), &v_alt.argument(), source_loc,
bindings);
bindings, generic_args);
}
default:
FATAL() << "expected a choice alternative in pattern, not " << *v;
@@ -239,11 +259,11 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
const auto& p_fn = cast<FunctionType>(*p);
const auto& v_fn = cast<FunctionType>(*v);
if (!PatternMatch(&p_fn.parameters(), &v_fn.parameters(), source_loc,
bindings)) {
bindings, generic_args)) {
return false;
}
if (!PatternMatch(&p_fn.return_type(), &v_fn.return_type(),
source_loc, bindings)) {
source_loc, bindings, generic_args)) {
return false;
}
return true;
@@ -349,9 +369,79 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
}
}
auto Interpreter::InstantiateType(Nonnull<const Value*> type,
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>> {
if (trace_) {
llvm::outs() << "instantiating: " << *type << "\n";
}
switch (type->kind()) {
case Value::Kind::VariableType: {
if (trace_) {
llvm::outs() << "case VariableType\n";
}
ASSIGN_OR_RETURN(
Nonnull<const Value*> value,
todo_.ValueOfNode(&cast<VariableType>(*type).binding(), source_loc));
if (const auto* lvalue = dyn_cast<LValue>(value)) {
ASSIGN_OR_RETURN(value, heap_.Read(lvalue->address(), source_loc));
}
return value;
}
case Value::Kind::NominalClassType: {
if (trace_) {
llvm::outs() << "case NominalClassType\n";
}
const auto& class_type = cast<NominalClassType>(*type);
BindingMap inst_type_args;
for (const auto& [ty_var, ty_arg] : class_type.type_args()) {
ASSIGN_OR_RETURN(inst_type_args[ty_var],
InstantiateType(ty_arg, source_loc));
}
if (trace_) {
llvm::outs() << "finished instantiating ty_arg\n";
}
std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>> witnesses;
for (const auto& [bind, impl] : class_type.impls()) {
ASSIGN_OR_RETURN(Nonnull<const Value*> witness_addr,
todo_.ValueOfNode(impl, source_loc));
if (trace_) {
llvm::outs() << "witness_addr: " << *witness_addr << "\n";
}
// If the witness came directly from an `impl` declaration (via
// `constant_value`), then it is a `Witness`. If the witness
// came from the runtime scope, then the `Witness` got wrapped
// in an `LValue` because that's what
// `RuntimeScope::Initialize` does.
Nonnull<const Witness*> witness;
if (llvm::isa<Witness>(witness_addr)) {
witness = cast<Witness>(witness_addr);
} else if (llvm::isa<LValue>(witness_addr)) {
ASSIGN_OR_RETURN(
Nonnull<const Value*> witness_value,
heap_.Read(llvm::cast<LValue>(witness_addr)->address(),
source_loc));
witness = cast<Witness>(witness_value);
} else {
FATAL() << "expected a witness or LValue of a witness";
}
witnesses[bind] = witness;
}
if (trace_) {
llvm::outs() << "finished finding witnesses\n";
}
return arena_->New<NominalClassType>(&class_type.declaration(),
inst_type_args, witnesses);
}
default:
return type;
}
}
auto Interpreter::Convert(Nonnull<const Value*> value,
Nonnull<const Value*> destination_type) const
-> Nonnull<const Value*> {
Nonnull<const Value*> destination_type,
SourceLocation source_loc) const
-> ErrorOr<Nonnull<const Value*>> {
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
@@ -396,13 +486,19 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
destination_struct_type.fields()) {
std::optional<Nonnull<const Value*>> old_value =
struct_val.FindField(field_name);
new_elements.push_back(
{.name = field_name, .value = Convert(*old_value, field_type)});
ASSIGN_OR_RETURN(Nonnull<const Value*> val,
Convert(*old_value, field_type, source_loc));
new_elements.push_back({.name = field_name, .value = val});
}
return arena_->New<StructValue>(std::move(new_elements));
}
case Value::Kind::NominalClassType:
return arena_->New<NominalClassValue>(destination_type, value);
case Value::Kind::NominalClassType: {
// Instantiate the `destintation_type` to obtain the runtime
// type of the object.
ASSIGN_OR_RETURN(Nonnull<const Value*> inst_dest,
InstantiateType(destination_type, source_loc));
return arena_->New<NominalClassValue>(inst_dest, value);
}
default:
FATAL() << "Can't convert value " << *value << " to type "
<< *destination_type;
@@ -415,8 +511,11 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
destination_tuple_type->elements().size());
std::vector<Nonnull<const Value*>> new_elements;
for (size_t i = 0; i < tuple->elements().size(); ++i) {
new_elements.push_back(Convert(tuple->elements()[i],
destination_tuple_type->elements()[i]));
ASSIGN_OR_RETURN(
Nonnull<const Value*> val,
Convert(tuple->elements()[i], destination_tuple_type->elements()[i],
source_loc));
new_elements.push_back(val);
}
return arena_->New<TupleValue>(std::move(new_elements));
}
@@ -580,11 +679,27 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
alt.alt_name(), alt.choice_name(), act.results()[1]));
}
case Value::Kind::FunctionValue: {
const FunctionDeclaration& function =
cast<FunctionValue>(*act.results()[0]).declaration();
Nonnull<const Value*> converted_args = Convert(
act.results()[1], &function.param_pattern().static_type());
const FunctionValue& fun_val =
cast<FunctionValue>(*act.results()[0]);
const FunctionDeclaration& function = fun_val.declaration();
if (trace_) {
llvm::outs() << "*** call function " << function.name() << "\n";
}
ASSIGN_OR_RETURN(Nonnull<const Value*> converted_args,
Convert(act.results()[1],
&function.param_pattern().static_type(),
exp.source_loc()));
RuntimeScope function_scope(&heap_);
// Bring the class type arguments into scope.
for (const auto& [bind, val] : fun_val.type_args()) {
function_scope.Initialize(bind, val);
}
// Bring the deduced type arguments into scope.
for (const auto& [bind, val] :
cast<CallExpression>(exp).deduced_args()) {
function_scope.Initialize(bind, val);
}
// Bring the impl witness tables into scope.
for (const auto& [impl_bind, impl_node] :
cast<CallExpression>(exp).impls()) {
@@ -597,9 +712,13 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
}
function_scope.Initialize(impl_bind, witness);
}
for (const auto& [impl_bind, witness] : fun_val.witnesses()) {
function_scope.Initialize(impl_bind, witness);
}
BindingMap generic_args;
CHECK(PatternMatch(&function.param_pattern().value(),
converted_args, exp.source_loc(),
&function_scope));
&function_scope, generic_args));
CHECK(function.body().has_value())
<< "Calling a function that's missing a body";
return todo_.Spawn(
@@ -609,19 +728,75 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
case Value::Kind::BoundMethodValue: {
const auto& 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());
CHECK(method.is_method());
ASSIGN_OR_RETURN(
Nonnull<const Value*> converted_args,
Convert(act.results()[1], &method.param_pattern().static_type(),
exp.source_loc()));
RuntimeScope method_scope(&heap_);
BindingMap generic_args;
CHECK(PatternMatch(&method.me_pattern().value(), m.receiver(),
exp.source_loc(), &method_scope));
exp.source_loc(), &method_scope, generic_args));
CHECK(PatternMatch(&method.param_pattern().value(), converted_args,
exp.source_loc(), &method_scope));
exp.source_loc(), &method_scope, generic_args));
// Bring the class type arguments into scope.
for (const auto& [bind, val] : m.type_args()) {
method_scope.Initialize(bind, val);
}
// Bring the impl witness tables into scope.
for (const auto& [impl_bind, witness] : m.witnesses()) {
method_scope.Initialize(impl_bind, witness);
}
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));
}
case Value::Kind::NominalClassType: {
const NominalClassType& class_type =
cast<NominalClassType>(*act.results()[0]);
const ClassDeclaration& class_decl = class_type.declaration();
RuntimeScope type_params_scope(&heap_);
BindingMap generic_args;
if (class_decl.type_params().has_value()) {
CHECK(PatternMatch(&(*class_decl.type_params())->value(),
act.results()[1], exp.source_loc(),
&type_params_scope, generic_args));
switch (phase()) {
case Phase::RunTime: {
std::map<Nonnull<const ImplBinding*>, const Witness*>
witnesses;
for (const auto& [impl_bind, impl_node] :
cast<CallExpression>(exp).impls()) {
ASSIGN_OR_RETURN(
Nonnull<const Value*> witness,
todo_.ValueOfNode(impl_node, exp.source_loc()));
if (witness->kind() == Value::Kind::LValue) {
const LValue& lval = cast<LValue>(*witness);
ASSIGN_OR_RETURN(witness, heap_.Read(lval.address(),
exp.source_loc()));
}
witnesses[impl_bind] = &cast<Witness>(*witness);
}
Nonnull<NominalClassType*> inst_class =
arena_->New<NominalClassType>(&class_type.declaration(),
generic_args, witnesses);
return todo_.FinishAction(inst_class);
}
case Phase::CompileTime: {
Nonnull<NominalClassType*> inst_class =
arena_->New<NominalClassType>(
&class_type.declaration(), generic_args,
cast<CallExpression>(exp).impls());
return todo_.FinishAction(inst_class);
}
}
} else {
FATAL() << "instantiation of non-generic class " << class_type;
}
}
default:
return FATAL_RUNTIME_ERROR(exp.source_loc())
<< "in call, expected a function, not " << *act.results()[0];
@@ -735,6 +910,10 @@ auto Interpreter::StepPattern() -> ErrorOr<Success> {
return todo_.FinishAction(arena_->New<BindingPlaceholderValue>());
}
}
case PatternKind::GenericBinding: {
const auto& binding = cast<GenericBinding>(pattern);
return todo_.FinishAction(arena_->New<VariableType>(&binding));
}
case PatternKind::TuplePattern: {
const auto& tuple = cast<TuplePattern>(pattern);
if (act.pos() < static_cast<int>(tuple.fields().size())) {
@@ -805,9 +984,12 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
}
auto c = match_stmt.clauses()[clause_num];
RuntimeScope matches(&heap_);
if (PatternMatch(&c.pattern().value(),
Convert(act.results()[0], &c.pattern().static_type()),
stmt.source_loc(), &matches)) {
BindingMap generic_args;
ASSIGN_OR_RETURN(Nonnull<const Value*> val,
Convert(act.results()[0], &c.pattern().static_type(),
stmt.source_loc()));
if (PatternMatch(&c.pattern().value(), val, stmt.source_loc(), &matches,
generic_args)) {
// Ensure we don't process any more clauses.
act.set_pos(match_stmt.clauses().size() + 1);
todo_.MergeScope(std::move(matches));
@@ -825,8 +1007,9 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
return todo_.Spawn(
std::make_unique<ExpressionAction>(&cast<While>(stmt).condition()));
} else {
Nonnull<const Value*> condition =
Convert(act.results().back(), arena_->New<BoolType>());
ASSIGN_OR_RETURN(Nonnull<const Value*> condition,
Convert(act.results().back(), arena_->New<BoolType>(),
stmt.source_loc()));
if (cast<BoolValue>(*condition).value()) {
// { {true :: (while ([]) s) :: C, E, F} :: S, H}
// -> { { s :: (while (e) s) :: C, E, F } :: S, H}
@@ -876,13 +1059,16 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
} else {
// { { v :: (x = []) :: C, E, F} :: S, H}
// -> { { C, E(x := a), F} :: S, H(a := copy(v))}
Nonnull<const Value*> v =
Convert(act.results()[0], &definition.pattern().static_type());
ASSIGN_OR_RETURN(
Nonnull<const Value*> v,
Convert(act.results()[0], &definition.pattern().static_type(),
stmt.source_loc()));
Nonnull<const Value*> p =
&cast<VariableDefinition>(stmt).pattern().value();
RuntimeScope matches(&heap_);
CHECK(PatternMatch(p, v, stmt.source_loc(), &matches))
BindingMap generic_args;
CHECK(PatternMatch(p, v, stmt.source_loc(), &matches, generic_args))
<< stmt.source_loc()
<< ": internal error in variable definition, match failed";
todo_.MergeScope(std::move(matches));
@@ -912,8 +1098,9 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
// { { v :: (a = []) :: C, E, F} :: S, H}
// -> { { C, E, F} :: S, H(a := v)}
const auto& lval = cast<LValue>(*act.results()[0]);
Nonnull<const Value*> rval =
Convert(act.results()[1], &assign.lhs().static_type());
ASSIGN_OR_RETURN(Nonnull<const Value*> rval,
Convert(act.results()[1], &assign.lhs().static_type(),
stmt.source_loc()));
RETURN_IF_ERROR(heap_.Write(lval.address(), rval, stmt.source_loc()));
return todo_.FinishAction();
}
@@ -925,8 +1112,9 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
return todo_.Spawn(
std::make_unique<ExpressionAction>(&cast<If>(stmt).condition()));
} else if (act.pos() == 1) {
Nonnull<const Value*> condition =
Convert(act.results()[0], arena_->New<BoolType>());
ASSIGN_OR_RETURN(Nonnull<const Value*> condition,
Convert(act.results()[0], arena_->New<BoolType>(),
stmt.source_loc()));
if (cast<BoolValue>(*condition).value()) {
// { {true :: if ([]) then_stmt else else_stmt :: C, E, F} ::
// S, H}
@@ -955,9 +1143,11 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
// { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H}
// -> { {v :: C', E', F'} :: S, H}
const FunctionDeclaration& function = cast<Return>(stmt).function();
return todo_.UnwindPast(
*function.body(),
Convert(act.results()[0], &function.return_term().static_type()));
ASSIGN_OR_RETURN(
Nonnull<const Value*> return_value,
Convert(act.results()[0], &function.return_term().static_type(),
stmt.source_loc()));
return todo_.UnwindPast(*function.body(), return_value);
}
case StatementKind::Continuation: {
CHECK(act.pos() == 0);
@@ -44,12 +44,14 @@ auto InterpPattern(Nonnull<const Pattern*> p, Nonnull<Arena*> arena, bool trace)
// is not permitted to bind variables. **bindings may be modified even if the
// match is unsuccessful, so it should typically be created for the
// PatternMatch call and then merged into an existing scope on success.
// The matches for generic variables in the pattern are output in
// `generic_args`.
// TODO: consider moving this to a separate header.
[[nodiscard]] auto PatternMatch(Nonnull<const Value*> p,
Nonnull<const Value*> v,
SourceLocation source_loc,
std::optional<Nonnull<RuntimeScope*>> bindings)
-> bool;
std::optional<Nonnull<RuntimeScope*>> bindings,
BindingMap& generic_args) -> bool;
} // namespace Carbon
@@ -178,6 +178,14 @@ static auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
}
break;
}
case PatternKind::GenericBinding: {
auto& binding = cast<GenericBinding>(pattern);
RETURN_IF_ERROR(ResolveNames(binding.type(), enclosing_scope));
if (binding.name() != AnonymousName) {
RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
}
break;
}
case PatternKind::TuplePattern:
for (Nonnull<Pattern*> field : cast<TuplePattern>(pattern).fields()) {
RETURN_IF_ERROR(ResolveNames(*field, enclosing_scope));
@@ -315,8 +323,8 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
StaticScope function_scope;
function_scope.AddParent(&enclosing_scope);
for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
RETURN_IF_ERROR(function_scope.Add(binding->name(), binding));
RETURN_IF_ERROR(ResolveNames(binding->type(), function_scope));
RETURN_IF_ERROR(function_scope.Add(binding->name(), binding));
}
if (function.is_method()) {
RETURN_IF_ERROR(ResolveNames(function.me_pattern(), function_scope));
@@ -336,9 +344,17 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
StaticScope class_scope;
class_scope.AddParent(&enclosing_scope);
RETURN_IF_ERROR(class_scope.Add(class_decl.name(), &class_decl));
for (Nonnull<Declaration*> member : class_decl.members()) {
RETURN_IF_ERROR(AddExposedNames(*member, class_scope));
if (class_decl.type_params().has_value()) {
RETURN_IF_ERROR(ResolveNames(**class_decl.type_params(), class_scope));
}
// TODO: Disable unqualified access of members by other members for now.
// Put it back later, but in a way that turns unqualified accesses
// into qualified ones, so that generic classes and impls
// behave the in the right way. -Jeremy
// for (Nonnull<Declaration*> member : class_decl.members()) {
// AddExposedNames(*member, class_scope);
// }
for (Nonnull<Declaration*> member : class_decl.members()) {
RETURN_IF_ERROR(ResolveNames(*member, class_scope));
}
+365 -120
View File
@@ -122,16 +122,7 @@ auto TypeChecker::ExpectIsConcreteType(SourceLocation source_loc,
}
}
// Returns true if *source is implicitly convertible to *destination. *source
// and *destination must be concrete types.
static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
Nonnull<const Value*> destination) -> bool;
// Returns true if source_fields and destination_fields contain the same set
// of names, and each value in source_fields is implicitly convertible to
// the corresponding value in destination_fields. All values in both arguments
// must be types.
static auto FieldTypesImplicitlyConvertible(
auto TypeChecker::FieldTypesImplicitlyConvertible(
llvm::ArrayRef<NamedValue> source_fields,
llvm::ArrayRef<NamedValue> destination_fields) {
if (source_fields.size() != destination_fields.size()) {
@@ -150,8 +141,29 @@ static auto FieldTypesImplicitlyConvertible(
return true;
}
static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
Nonnull<const Value*> destination) -> bool {
auto TypeChecker::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);
Nonnull<const Value*> field_type =
Substitute(class_type.type_args(), &var.binding().static_type());
field_types.push_back(
{.name = var.binding().name(), .value = field_type});
break;
}
default:
break;
}
}
return field_types;
}
auto TypeChecker::IsImplicitlyConvertible(Nonnull<const Value*> source,
Nonnull<const Value*> destination)
-> bool {
CHECK(IsConcreteType(source));
CHECK(IsConcreteType(destination));
if (TypeEqual(source, destination)) {
@@ -172,34 +184,35 @@ static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
return false;
}
case Value::Kind::TupleValue:
switch (destination->kind()) {
case Value::Kind::TupleValue: {
const std::vector<Nonnull<const Value*>>& source_elements =
cast<TupleValue>(*source).elements();
const std::vector<Nonnull<const Value*>>& destination_elements =
cast<TupleValue>(*destination).elements();
if (source_elements.size() != destination_elements.size()) {
if (destination->kind() == Value::Kind::TupleValue) {
const std::vector<Nonnull<const Value*>>& source_elements =
cast<TupleValue>(*source).elements();
const std::vector<Nonnull<const Value*>>& destination_elements =
cast<TupleValue>(*destination).elements();
if (source_elements.size() != destination_elements.size()) {
return false;
}
for (size_t i = 0; i < source_elements.size(); ++i) {
if (!IsImplicitlyConvertible(source_elements[i],
destination_elements[i])) {
return false;
}
for (size_t i = 0; i < source_elements.size(); ++i) {
if (!IsImplicitlyConvertible(source_elements[i],
destination_elements[i])) {
return false;
}
}
return true;
}
default:
return false;
return true;
} else {
return false;
}
case Value::Kind::TypeType:
return destination->kind() == Value::Kind::InterfaceType;
default:
return false;
}
}
static auto ExpectType(SourceLocation source_loc, const std::string& context,
Nonnull<const Value*> expected,
Nonnull<const Value*> actual) -> ErrorOr<Success> {
auto TypeChecker::ExpectType(SourceLocation source_loc,
const std::string& context,
Nonnull<const Value*> expected,
Nonnull<const Value*> actual) -> ErrorOr<Success> {
if (!IsImplicitlyConvertible(actual, expected)) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in " << context << ": "
@@ -212,29 +225,29 @@ static auto ExpectType(SourceLocation source_loc, const std::string& context,
auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
BindingMap& deduced,
Nonnull<const Value*> param,
Nonnull<const Value*> arg)
Nonnull<const Value*> param_type,
Nonnull<const Value*> arg_type)
-> ErrorOr<Success> {
switch (param->kind()) {
switch (param_type->kind()) {
case Value::Kind::VariableType: {
const auto& var_type = cast<VariableType>(*param);
auto [it, success] = deduced.insert({&var_type.binding(), arg});
const auto& var_type = cast<VariableType>(*param_type);
auto [it, success] = deduced.insert({&var_type.binding(), arg_type});
if (!success) {
// TODO: can we allow implicit conversions here?
RETURN_IF_ERROR(
ExpectExactType(source_loc, "argument deduction", it->second, arg));
RETURN_IF_ERROR(ExpectExactType(source_loc, "argument deduction",
it->second, arg_type));
}
return Success();
}
case Value::Kind::TupleValue: {
if (arg->kind() != Value::Kind::TupleValue) {
if (arg_type->kind() != Value::Kind::TupleValue) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in argument deduction\n"
<< "expected: " << *param << "\n"
<< "actual: " << *arg;
<< "expected: " << *param_type << "\n"
<< "actual: " << *arg_type;
}
const auto& param_tup = cast<TupleValue>(*param);
const auto& arg_tup = cast<TupleValue>(*arg);
const auto& param_tup = cast<TupleValue>(*param_type);
const auto& arg_tup = cast<TupleValue>(*arg_type);
if (param_tup.elements().size() != arg_tup.elements().size()) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "mismatch in tuple sizes, expected "
@@ -249,14 +262,14 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
return Success();
}
case Value::Kind::StructType: {
if (arg->kind() != Value::Kind::StructType) {
if (arg_type->kind() != Value::Kind::StructType) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in argument deduction\n"
<< "expected: " << *param << "\n"
<< "actual: " << *arg;
<< "expected: " << *param_type << "\n"
<< "actual: " << *arg_type;
}
const auto& param_struct = cast<StructType>(*param);
const auto& arg_struct = cast<StructType>(*arg);
const auto& param_struct = cast<StructType>(*param_type);
const auto& arg_struct = cast<StructType>(*arg_type);
if (param_struct.fields().size() != arg_struct.fields().size()) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "mismatch in struct field counts, expected "
@@ -276,14 +289,14 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
return Success();
}
case Value::Kind::FunctionType: {
if (arg->kind() != Value::Kind::FunctionType) {
if (arg_type->kind() != Value::Kind::FunctionType) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in argument deduction\n"
<< "expected: " << *param << "\n"
<< "actual: " << *arg;
<< "expected: " << *param_type << "\n"
<< "actual: " << *arg_type;
}
const auto& param_fn = cast<FunctionType>(*param);
const auto& arg_fn = cast<FunctionType>(*arg);
const auto& param_fn = cast<FunctionType>(*param_type);
const auto& arg_fn = cast<FunctionType>(*arg_type);
// TODO: handle situation when arg has deduced parameters.
RETURN_IF_ERROR(ArgumentDeduction(
source_loc, deduced, &param_fn.parameters(), &arg_fn.parameters()));
@@ -292,23 +305,41 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
return Success();
}
case Value::Kind::PointerType: {
if (arg->kind() != Value::Kind::PointerType) {
if (arg_type->kind() != Value::Kind::PointerType) {
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in argument deduction\n"
<< "expected: " << *param << "\n"
<< "actual: " << *arg;
<< "expected: " << *param_type << "\n"
<< "actual: " << *arg_type;
}
return ArgumentDeduction(source_loc, deduced,
&cast<PointerType>(*param).type(),
&cast<PointerType>(*arg).type());
&cast<PointerType>(*param_type).type(),
&cast<PointerType>(*arg_type).type());
}
// Nothing to do in the case for `auto`.
case Value::Kind::AutoType: {
return Success();
}
case Value::Kind::NominalClassType: {
const auto& param_class_type = cast<NominalClassType>(*param_type);
if (arg_type->kind() == Value::Kind::NominalClassType) {
const auto& arg_class_type = cast<NominalClassType>(*arg_type);
if (param_class_type.declaration().name() ==
arg_class_type.declaration().name()) {
for (const auto& [ty, param_ty] : param_class_type.type_args()) {
RETURN_IF_ERROR(
ArgumentDeduction(source_loc, deduced, param_ty,
arg_class_type.type_args().at(ty)));
}
return Success();
}
}
return FATAL_COMPILATION_ERROR(source_loc)
<< "type error in argument deduction\n"
<< "expected: " << *param_type << "\n"
<< "actual: " << *arg_type;
}
// For the following cases, we check for type convertability.
case Value::Kind::ContinuationType:
case Value::Kind::NominalClassType:
case Value::Kind::InterfaceType:
case Value::Kind::ChoiceType:
case Value::Kind::IntType:
@@ -318,7 +349,7 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfChoiceType:
return ExpectType(source_loc, "argument deduction", param, arg);
return ExpectType(source_loc, "argument deduction", param_type, arg_type);
// The rest of these cases should never happen.
case Value::Kind::Witness:
case Value::Kind::IntValue:
@@ -334,7 +365,8 @@ auto TypeChecker::ArgumentDeduction(SourceLocation source_loc,
case Value::Kind::AlternativeConstructorValue:
case Value::Kind::ContinuationValue:
case Value::Kind::StringValue:
FATAL() << "In ArgumentDeduction: expected type, not value " << *param;
FATAL() << "In ArgumentDeduction: expected type, not value "
<< *param_type;
}
}
@@ -377,11 +409,24 @@ auto TypeChecker::Substitute(
return arena_->New<PointerType>(
Substitute(dict, &cast<PointerType>(*type).type()));
}
case Value::Kind::NominalClassType: {
const auto& class_type = cast<NominalClassType>(*type);
BindingMap type_args;
for (const auto& [name, value] : class_type.type_args()) {
type_args[name] = Substitute(dict, value);
}
Nonnull<const NominalClassType*> new_class_type =
arena_->New<NominalClassType>(&class_type.declaration(), type_args);
if (trace_) {
llvm::outs() << "substitution: " << class_type << " => "
<< *new_class_type << "\n";
}
return new_class_type;
}
case Value::Kind::AutoType:
case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::TypeType:
case Value::Kind::NominalClassType:
case Value::Kind::InterfaceType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
@@ -506,7 +551,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
if (std::optional<Nonnull<const Declaration*>> member =
FindMember(access.field(), t_class.declaration().members());
member.has_value()) {
access.set_static_type(&(*member)->static_type());
Nonnull<const Value*> field_type =
Substitute(t_class.type_args(), &(*member)->static_type());
access.set_static_type(field_type);
switch ((*member)->kind()) {
case DeclarationKind::VariableDeclaration:
access.set_value_category(access.aggregate().value_category());
@@ -554,7 +601,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
if (func->is_method()) {
break;
}
access.set_static_type(&(*member)->static_type());
Nonnull<const Value*> field_type = Substitute(
class_type.type_args(), &(*member)->static_type());
access.set_static_type(field_type);
access.set_value_category(ValueCategory::Let);
return Success();
}
@@ -570,7 +619,11 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
}
case Value::Kind::VariableType: {
const auto& var_type = cast<VariableType>(aggregate_type);
// This case handles access to a method on a receiver whose type
// is a type variable. For example, `x.foo` where the type of
// `x` is `T` and `foo` and `T` implements an interface that
// includes `foo`.
const VariableType& var_type = cast<VariableType>(aggregate_type);
const Value& typeof_var = var_type.binding().static_type();
switch (typeof_var.kind()) {
case Value::Kind::InterfaceType: {
@@ -586,6 +639,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
Nonnull<const Value*> inst_member_type =
Substitute(self_map, &member_type);
access.set_static_type(inst_member_type);
CHECK(var_type.binding().impl_binding().has_value());
access.set_impl(*var_type.binding().impl_binding());
return Success();
} else {
@@ -596,11 +650,37 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
break;
}
default:
break;
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "field access, unexpected " << aggregate_type
<< " of non-interface type " << typeof_var << " in " << *e;
}
break;
}
case Value::Kind::InterfaceType: {
// This case handles access to a class function from a type variable.
// If `T` is a type variable and `foo` is a class function in an
// interface implemented by `T`, then `T.foo` accesses the `foo` class
// function of `T`.
ASSIGN_OR_RETURN(Nonnull<const Value*> var_addr,
InterpExp(&access.aggregate(), arena_, trace_));
const VariableType& var_type = cast<VariableType>(*var_addr);
const InterfaceType& iface_type = cast<InterfaceType>(aggregate_type);
const InterfaceDeclaration& iface_decl = iface_type.declaration();
if (std::optional<Nonnull<const Declaration*>> member =
FindMember(access.field(), iface_decl.members());
member.has_value()) {
const Value& member_type = (*member)->static_type();
Nonnull<const Value*> inst_member_type =
Substitute({{iface_decl.self(), &var_type}}, &member_type);
access.set_static_type(inst_member_type);
CHECK(var_type.binding().impl_binding().has_value());
access.set_impl(*var_type.binding().impl_binding());
return Success();
} else {
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "field access, " << access.field() << " not in "
<< iface_decl.name();
}
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "field access, unexpected " << aggregate_type << " in "
<< *e;
break;
}
default:
@@ -724,30 +804,33 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
case ExpressionKind::CallExpression: {
auto& call = cast<CallExpression>(*e);
RETURN_IF_ERROR(TypeCheckExp(&call.function(), impl_scope));
RETURN_IF_ERROR(TypeCheckExp(&call.argument(), impl_scope));
switch (call.function().static_type().kind()) {
case Value::Kind::FunctionType: {
const auto& fun_t = cast<FunctionType>(call.function().static_type());
RETURN_IF_ERROR(TypeCheckExp(&call.argument(), impl_scope));
Nonnull<const Value*> parameters = &fun_t.parameters();
Nonnull<const Value*> return_type = &fun_t.return_type();
if (!fun_t.deduced().empty()) {
BindingMap deduced_args;
RETURN_IF_ERROR(ArgumentDeduction(e->source_loc(), deduced_args,
parameters,
BindingMap deduced_type_args;
RETURN_IF_ERROR(ArgumentDeduction(e->source_loc(),
deduced_type_args, parameters,
&call.argument().static_type()));
call.set_deduced_args(deduced_type_args);
for (Nonnull<const GenericBinding*> deduced_param :
fun_t.deduced()) {
// TODO: change the following to a CHECK once the real checking
// has been added to the type checking of function signatures.
if (auto it = deduced_args.find(deduced_param);
it == deduced_args.end()) {
if (auto it = deduced_type_args.find(deduced_param);
it == deduced_type_args.end()) {
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "could not deduce type argument for type parameter "
<< deduced_param->name();
<< deduced_param->name() << "\n"
<< "in " << call;
}
}
parameters = Substitute(deduced_args, parameters);
return_type = Substitute(deduced_args, return_type);
parameters = Substitute(deduced_type_args, parameters);
return_type = Substitute(deduced_type_args, return_type);
// Find impls for all the impl bindings of the function
std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls;
for (Nonnull<const ImplBinding*> impl_binding :
@@ -756,9 +839,10 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
case Value::Kind::InterfaceType: {
ASSIGN_OR_RETURN(
ValueNodeView impl,
impl_scope.Resolve(impl_binding->interface(),
deduced_args[impl_binding->type_var()],
e->source_loc()));
impl_scope.Resolve(
impl_binding->interface(),
deduced_type_args[impl_binding->type_var()],
e->source_loc()));
impls.emplace(impl_binding, impl);
break;
}
@@ -772,6 +856,8 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
call.set_impls(impls);
} else {
// No deduced parameters. Check that the argument types
// are convertible to the parameter types.
RETURN_IF_ERROR(ExpectType(e->source_loc(), "call", parameters,
&call.argument().static_type()));
}
@@ -779,10 +865,62 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
call.set_value_category(ValueCategory::Let);
return Success();
}
case Value::Kind::TypeOfClassType: {
// This case handles the application of a generic class to
// a type argument, such as Point(i32).
const ClassDeclaration& class_decl =
cast<TypeOfClassType>(call.function().static_type())
.class_type()
.declaration();
BindingMap generic_args;
if (class_decl.type_params().has_value()) {
if (trace_) {
llvm::outs() << "pattern matching type params and args ";
}
ASSIGN_OR_RETURN(Nonnull<const Value*> arg,
InterpExp(&call.argument(), arena_, trace_));
CHECK(PatternMatch(&(*class_decl.type_params())->value(), arg,
call.source_loc(), std::nullopt, generic_args));
} else {
return FATAL_COMPILATION_ERROR(call.source_loc())
<< "attempt to instantiate a non-generic class: " << *e;
}
// Find impls for all the impl bindings of the class.
std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls;
for (const auto& [binding, val] : generic_args) {
if (binding->impl_binding().has_value()) {
Nonnull<const ImplBinding*> impl_binding =
*binding->impl_binding();
switch (impl_binding->interface()->kind()) {
case Value::Kind::InterfaceType: {
ASSIGN_OR_RETURN(ValueNodeView impl,
impl_scope.Resolve(impl_binding->interface(),
generic_args[binding],
call.source_loc()));
impls.emplace(impl_binding, impl);
break;
}
case Value::Kind::TypeType:
break;
default:
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "unexpected type of deduced parameter "
<< *impl_binding->interface();
}
}
}
Nonnull<NominalClassType*> class_type =
arena_->New<NominalClassType>(&class_decl, generic_args, impls);
call.set_impls(impls);
call.set_static_type(class_type);
call.set_value_category(ValueCategory::Let);
return Success();
}
default: {
return FATAL_COMPILATION_ERROR(e->source_loc())
<< "in call, expected a function\n"
<< *e;
<< *e << "\nnot an operator of type "
<< call.function().static_type() << "\n";
}
}
break;
@@ -854,6 +992,41 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
}
void TypeChecker::AddPatternImpls(Nonnull<Pattern*> p, ImplScope& impl_scope) {
switch (p->kind()) {
case PatternKind::GenericBinding: {
auto& binding = cast<GenericBinding>(*p);
CHECK(binding.impl_binding().has_value());
Nonnull<const ImplBinding*> impl_binding = *binding.impl_binding();
impl_scope.Add(impl_binding->interface(),
*impl_binding->type_var()->symbolic_identity(),
impl_binding);
return;
}
case PatternKind::TuplePattern: {
auto& tuple = cast<TuplePattern>(*p);
for (Nonnull<Pattern*> field : tuple.fields()) {
AddPatternImpls(field, impl_scope);
}
return;
}
case PatternKind::AlternativePattern: {
auto& alternative = cast<AlternativePattern>(*p);
AddPatternImpls(&alternative.arguments(), impl_scope);
return;
}
case PatternKind::VarPattern: {
auto& var_pattern = cast<VarPattern>(*p);
AddPatternImpls(&var_pattern.pattern(), impl_scope);
return;
}
case PatternKind::ExpressionPattern:
case PatternKind::AutoPattern:
case PatternKind::BindingPattern:
return;
}
}
auto TypeChecker::TypeCheckPattern(
Nonnull<Pattern*> p, std::optional<Nonnull<const Value*>> expected,
const ImplScope& impl_scope, ValueCategory enclosing_value_category)
@@ -887,8 +1060,9 @@ auto TypeChecker::TypeCheckPattern(
RETURN_IF_ERROR(
ExpectType(p->source_loc(), "name binding", type, *expected));
} else {
BindingMap generic_args;
if (!PatternMatch(type, *expected, binding.type().source_loc(),
std::nullopt)) {
std::nullopt, generic_args)) {
return FATAL_COMPILATION_ERROR(binding.type().source_loc())
<< "Type pattern '" << *type
<< "' does not match actual type '" << **expected << "'";
@@ -907,6 +1081,27 @@ auto TypeChecker::TypeCheckPattern(
}
return Success();
}
case PatternKind::GenericBinding: {
auto& binding = cast<GenericBinding>(*p);
RETURN_IF_ERROR(TypeCheckExp(&binding.type(), impl_scope));
ASSIGN_OR_RETURN(Nonnull<const Value*> type,
InterpExp(&binding.type(), arena_, trace_));
if (expected) {
return FATAL_COMPILATION_ERROR(binding.type().source_loc())
<< "Generic binding may not occur in pattern with expected "
"type: "
<< binding;
}
binding.set_static_type(type);
ASSIGN_OR_RETURN(Nonnull<const Value*> val,
InterpPattern(&binding, arena_, trace_));
binding.set_symbolic_identity(val);
Nonnull<ImplBinding*> impl_binding = arena_->New<ImplBinding>(
binding.source_loc(), &binding, &binding.static_type());
binding.set_impl_binding(impl_binding);
SetValue(&binding, val);
return Success();
}
case PatternKind::TuplePattern: {
auto& tuple = cast<TuplePattern>(*p);
std::vector<Nonnull<const Value*>> field_types;
@@ -927,6 +1122,9 @@ auto TypeChecker::TypeCheckPattern(
}
RETURN_IF_ERROR(TypeCheckPattern(field, expected_field_type, impl_scope,
enclosing_value_category));
if (trace_)
llvm::outs() << "finished checking tuple pattern field " << *field
<< "\n";
field_types.push_back(&field->static_type());
}
tuple.set_static_type(arena_->New<TupleValue>(std::move(field_types)));
@@ -1183,28 +1381,19 @@ auto TypeChecker::ExpectReturnOnAllPaths(
// TODO: Add checking to function definitions to ensure that
// all deduced type parameters will be deduced.
auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
const ImplScope& enclosing_scope)
const ImplScope& impl_scope)
-> ErrorOr<Success> {
if (trace_) {
llvm::outs() << "** declaring function " << f->name() << "\n";
}
// Bring the deduced parameters into scope
for (Nonnull<GenericBinding*> deduced : f->deduced_parameters()) {
RETURN_IF_ERROR(TypeCheckExp(&deduced->type(), enclosing_scope));
SetConstantValue(deduced, arena_->New<VariableType>(deduced));
ASSIGN_OR_RETURN(Nonnull<const Value*> deduced_type,
RETURN_IF_ERROR(TypeCheckExp(&deduced->type(), impl_scope));
deduced->set_symbolic_identity(arena_->New<VariableType>(deduced));
ASSIGN_OR_RETURN(Nonnull<const Value*> type_of_type,
InterpExp(&deduced->type(), arena_, trace_));
deduced->set_static_type(deduced_type);
deduced->set_static_type(type_of_type);
}
// Type check the receiver pattern
if (f->is_method()) {
RETURN_IF_ERROR(TypeCheckPattern(&f->me_pattern(), std::nullopt,
enclosing_scope, ValueCategory::Let));
}
// Type check the parameter pattern
RETURN_IF_ERROR(TypeCheckPattern(&f->param_pattern(), std::nullopt,
enclosing_scope, ValueCategory::Let));
// Create the impl_bindings
std::vector<Nonnull<const ImplBinding*>> impl_bindings;
for (Nonnull<GenericBinding*> deduced : f->deduced_parameters()) {
@@ -1214,6 +1403,23 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
impl_binding->set_static_type(&deduced->static_type());
impl_bindings.push_back(impl_binding);
}
// Bring the impl bindings into scope.
ImplScope function_scope;
function_scope.AddParent(&impl_scope);
for (Nonnull<const ImplBinding*> impl_binding : impl_bindings) {
CHECK(impl_binding->type_var()->symbolic_identity().has_value());
function_scope.Add(impl_binding->interface(),
*impl_binding->type_var()->symbolic_identity(),
impl_binding);
}
// Type check the receiver pattern.
if (f->is_method()) {
RETURN_IF_ERROR(TypeCheckPattern(&f->me_pattern(), std::nullopt,
function_scope, ValueCategory::Let));
}
// Type check the parameter pattern.
RETURN_IF_ERROR(TypeCheckPattern(&f->param_pattern(), std::nullopt,
function_scope, ValueCategory::Let));
// Evaluate the return type, if we can do so without examining the body.
if (std::optional<Nonnull<Expression*>> return_expression =
@@ -1221,7 +1427,7 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
return_expression.has_value()) {
// We ignore the return value because return type expressions can't bring
// new types into scope.
RETURN_IF_ERROR(TypeCheckExp(*return_expression, enclosing_scope));
RETURN_IF_ERROR(TypeCheckExp(*return_expression, function_scope));
// Should we be doing SetConstantValue instead? -Jeremy
// And shouldn't the type of this be Type?
ASSIGN_OR_RETURN(Nonnull<const Value*> ret_type,
@@ -1235,15 +1441,7 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
return FATAL_COMPILATION_ERROR(f->return_term().source_loc())
<< "Function declaration has deduced return type but no body";
}
// Bring the impl bindings into scope
ImplScope function_scope;
function_scope.AddParent(&enclosing_scope);
for (Nonnull<const ImplBinding*> impl_binding : impl_bindings) {
function_scope.Add(impl_binding->interface(),
*impl_binding->type_var()->constant_value(),
impl_binding);
}
RETURN_IF_ERROR(TypeCheckStmt(*f->body(), enclosing_scope));
RETURN_IF_ERROR(TypeCheckStmt(*f->body(), function_scope));
if (!f->return_term().is_omitted()) {
RETURN_IF_ERROR(ExpectReturnOnAllPaths(f->body(), f->source_loc()));
}
@@ -1268,7 +1466,8 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
}
if (trace_) {
llvm::outs() << "** finished declaring function " << f->name() << "\n";
llvm::outs() << "** finished declaring function " << f->name()
<< " of type " << f->static_type() << "\n";
}
return Success();
}
@@ -1287,10 +1486,13 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
function_scope.AddParent(&impl_scope);
for (Nonnull<const ImplBinding*> impl_binding :
cast<FunctionType>(f->static_type()).impl_bindings()) {
CHECK(impl_binding->type_var()->symbolic_identity().has_value());
function_scope.Add(impl_binding->interface(),
*impl_binding->type_var()->constant_value(),
*impl_binding->type_var()->symbolic_identity(),
impl_binding);
}
if (trace_)
llvm::outs() << function_scope;
RETURN_IF_ERROR(TypeCheckStmt(*f->body(), function_scope));
if (!f->return_term().is_omitted()) {
RETURN_IF_ERROR(ExpectReturnOnAllPaths(f->body(), f->source_loc()));
@@ -1305,16 +1507,45 @@ auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
ImplScope& enclosing_scope)
-> ErrorOr<Success> {
// 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);
class_decl->set_static_type(arena_->New<TypeOfClassType>(class_type));
if (trace_) {
llvm::outs() << "** declaring class " << class_decl->name() << "\n";
}
if (class_decl->type_params().has_value()) {
ImplScope class_scope;
class_scope.AddParent(&enclosing_scope);
RETURN_IF_ERROR(TypeCheckPattern(*class_decl->type_params(), std::nullopt,
class_scope, ValueCategory::Let));
AddPatternImpls(*class_decl->type_params(), class_scope);
if (trace_) {
llvm::outs() << class_scope;
}
for (Nonnull<Declaration*> m : class_decl->members()) {
RETURN_IF_ERROR(DeclareDeclaration(m, enclosing_scope));
Nonnull<NominalClassType*> class_type =
arena_->New<NominalClassType>(class_decl);
SetConstantValue(class_decl, class_type);
class_decl->set_static_type(arena_->New<TypeOfClassType>(class_type));
for (Nonnull<Declaration*> m : class_decl->members()) {
RETURN_IF_ERROR(DeclareDeclaration(m, class_scope));
}
// TODO: when/how to bring impls in generic class into scope?
} else {
// 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);
class_decl->set_static_type(arena_->New<TypeOfClassType>(class_type));
for (Nonnull<Declaration*> m : class_decl->members()) {
RETURN_IF_ERROR(DeclareDeclaration(m, enclosing_scope));
}
}
if (trace_) {
llvm::outs() << "** finished declaring class " << class_decl->name()
<< "\n";
}
return Success();
}
@@ -1322,8 +1553,22 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
auto TypeChecker::TypeCheckClassDeclaration(
Nonnull<ClassDeclaration*> class_decl, const ImplScope& impl_scope)
-> ErrorOr<Success> {
if (trace_) {
llvm::outs() << "** checking class " << class_decl->name() << "\n";
}
ImplScope class_scope;
class_scope.AddParent(&impl_scope);
if (class_decl->type_params().has_value()) {
AddPatternImpls(*class_decl->type_params(), class_scope);
}
if (trace_) {
llvm::outs() << class_scope;
}
for (Nonnull<Declaration*> m : class_decl->members()) {
RETURN_IF_ERROR(TypeCheckDeclaration(m, impl_scope));
RETURN_IF_ERROR(TypeCheckDeclaration(m, class_scope));
}
if (trace_) {
llvm::outs() << "** finished checking class " << class_decl->name() << "\n";
}
return Success();
}
@@ -1339,7 +1584,7 @@ auto TypeChecker::DeclareInterfaceDeclaration(
RETURN_IF_ERROR(TypeCheckExp(&iface_decl->self()->type(), enclosing_scope));
iface_decl->self()->set_static_type(
arena_->New<VariableType>(iface_decl->self()));
SetConstantValue(iface_decl->self(), &iface_decl->self()->static_type());
iface_decl->self()->set_symbolic_identity(&iface_decl->self()->static_type());
for (Nonnull<Declaration*> m : iface_decl->members()) {
RETURN_IF_ERROR(DeclareDeclaration(m, enclosing_scope));
@@ -36,9 +36,9 @@ class TypeChecker {
// inside the argument type.
// The `deduced` parameter is an accumulator, that is, it holds the
// results so-far.
static auto ArgumentDeduction(SourceLocation source_loc, BindingMap& deduced,
Nonnull<const Value*> param,
Nonnull<const Value*> arg) -> ErrorOr<Success>;
auto ArgumentDeduction(SourceLocation source_loc, BindingMap& deduced,
Nonnull<const Value*> param_type,
Nonnull<const Value*> arg_type) -> ErrorOr<Success>;
// Traverses the AST rooted at `e`, populating the static_type() of all nodes
// and ensuring they follow Carbon's typing rules.
@@ -93,6 +93,9 @@ class TypeChecker {
const ImplScope& enclosing_scope)
-> ErrorOr<Success>;
// Add the impls from the pattern into the given `impl_scope`.
void AddPatternImpls(Nonnull<Pattern*> p, ImplScope& impl_scope);
// Checks the statements and (runtime) expressions within the
// declaration, such as the body of a function.
// Dispatches to one of the following functions.
@@ -135,6 +138,29 @@ class TypeChecker {
auto ExpectIsConcreteType(SourceLocation source_loc,
Nonnull<const Value*> value) -> ErrorOr<Success>;
// Returns the field names of the class together with their types.
auto FieldTypes(const NominalClassType& class_type)
-> std::vector<NamedValue>;
// Returns true if source_fields and destination_fields contain the same set
// of names, and each value in source_fields is implicitly convertible to
// the corresponding value in destination_fields. All values in both arguments
// must be types.
auto FieldTypesImplicitlyConvertible(
llvm::ArrayRef<NamedValue> source_fields,
llvm::ArrayRef<NamedValue> destination_fields);
// Returns true if *source is implicitly convertible to *destination. *source
// and *destination must be concrete types.
auto IsImplicitlyConvertible(Nonnull<const Value*> source,
Nonnull<const Value*> destination) -> bool;
// Check whether `actual` is implicitly convertible to `expected`
// and halt with a fatal compilation error if it is not.
auto ExpectType(SourceLocation source_loc, const std::string& context,
Nonnull<const Value*> expected, Nonnull<const Value*> actual)
-> ErrorOr<Success>;
auto Substitute(const std::map<Nonnull<const GenericBinding*>,
Nonnull<const Value*>>& dict,
Nonnull<const Value*> type) -> Nonnull<const Value*>;
+63 -30
View File
@@ -42,7 +42,12 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
FindMember(f, witness->declaration().members());
mem_decl.has_value()) {
const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
return arena->New<BoundMethodValue>(&fun_decl, v);
if (fun_decl.is_method()) {
return arena->New<BoundMethodValue>(&fun_decl, v);
} else {
// Class function.
return *fun_decl.constant_value();
}
} else {
return FATAL_COMPILATION_ERROR(source_loc)
<< "member " << f << " not in " << *witness;
@@ -67,7 +72,9 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
// Look for a field
std::optional<Nonnull<const Value*>> field =
cast<StructValue>(object.inits()).FindField(f);
if (field == std::nullopt) {
if (field.has_value()) {
return *field;
} else {
// Look for a method in the object's class
const auto& class_type = cast<NominalClassType>(object.type());
std::optional<Nonnull<const FunctionValue*>> func =
@@ -78,14 +85,18 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
<< class_type;
} else if ((*func)->declaration().is_method()) {
// Found a method. Turn it into a bound method.
const auto& m = cast<FunctionValue>(**func);
return arena->New<BoundMethodValue>(&m.declaration(), &object);
const FunctionValue& m = cast<FunctionValue>(**func);
return arena->New<BoundMethodValue>(&m.declaration(), &object,
class_type.type_args(),
class_type.witnesses());
} else {
// Found a class function
return *func;
Nonnull<const FunctionValue*> fun = arena->New<FunctionValue>(
&(*func)->declaration(), class_type.type_args(),
class_type.witnesses());
return fun;
}
}
return *field;
}
case Value::Kind::ChoiceType: {
const auto& choice = cast<ChoiceType>(*v);
@@ -96,14 +107,17 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
return arena->New<AlternativeConstructorValue>(f, choice.name());
}
case Value::Kind::NominalClassType: {
const auto& class_type = cast<NominalClassType>(*v);
// Access a class function.
const NominalClassType& class_type = cast<NominalClassType>(*v);
std::optional<Nonnull<const FunctionValue*>> fun =
class_type.FindFunction(f);
if (fun == std::nullopt) {
return FATAL_RUNTIME_ERROR(source_loc)
<< "class function " << f << " not in " << *v;
}
return *fun;
return arena->New<FunctionValue>(&(*fun)->declaration(),
class_type.type_args(),
class_type.witnesses());
}
default:
FATAL() << "field access not allowed for value " << *v;
@@ -293,6 +307,28 @@ void Value::Print(llvm::raw_ostream& out) const {
case Value::Kind::NominalClassType: {
const auto& class_type = cast<NominalClassType>(*this);
out << "class " << class_type.declaration().name();
if (!class_type.type_args().empty()) {
out << "(";
llvm::ListSeparator sep;
for (const auto& [bind, val] : class_type.type_args()) {
out << sep << bind->name() << " = " << *val;
}
out << ")";
}
if (!class_type.impls().empty()) {
out << " impls ";
llvm::ListSeparator sep;
for (const auto& [impl_bind, impl] : class_type.impls()) {
out << sep << impl;
}
}
if (!class_type.witnesses().empty()) {
out << " witnesses ";
llvm::ListSeparator sep;
for (const auto& [impl_bind, witness] : class_type.witnesses()) {
out << sep << *witness;
}
}
break;
}
case Value::Kind::InterfaceType: {
@@ -302,7 +338,7 @@ void Value::Print(llvm::raw_ostream& out) const {
}
case Value::Kind::Witness: {
const auto& witness = cast<Witness>(*this);
out << "impl " << *witness.declaration().impl_type() << " as "
out << "witness " << *witness.declaration().impl_type() << " as "
<< witness.declaration().interface();
break;
}
@@ -310,7 +346,7 @@ void Value::Print(llvm::raw_ostream& out) const {
out << "choice " << cast<ChoiceType>(*this).name();
break;
case Value::Kind::VariableType:
out << cast<VariableType>(*this).binding().name();
out << cast<VariableType>(*this).binding();
break;
case Value::Kind::ContinuationValue: {
out << cast<ContinuationValue>(*this).stack();
@@ -410,8 +446,18 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
return true;
}
case Value::Kind::NominalClassType:
return cast<NominalClassType>(*t1).declaration().name() ==
cast<NominalClassType>(*t2).declaration().name();
if (cast<NominalClassType>(*t1).declaration().name() !=
cast<NominalClassType>(*t2).declaration().name()) {
return false;
}
for (const auto& [ty_var1, ty1] :
cast<NominalClassType>(*t1).type_args()) {
if (!TypeEqual(ty1,
cast<NominalClassType>(*t2).type_args().at(ty_var1))) {
return false;
}
}
return true;
case Value::Kind::InterfaceType:
return cast<InterfaceType>(*t1).declaration().name() ==
cast<InterfaceType>(*t2).declaration().name();
@@ -591,23 +637,6 @@ auto NominalClassType::FindFunction(const std::string& name) const
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 FindMember(const std::string& name,
llvm::ArrayRef<Nonnull<Declaration*>> members)
-> std::optional<Nonnull<const Declaration*>> {
@@ -623,7 +652,11 @@ auto FindMember(const std::string& name,
}
void ImplBinding::Print(llvm::raw_ostream& out) const {
out << "impl " << *type_var_ << " as " << *iface_;
out << "impl binding " << *type_var_ << " as " << *iface_;
}
void ImplBinding::PrintID(llvm::raw_ostream& out) const {
out << *type_var_ << " as " << *iface_;
}
} // namespace Carbon
+93 -1
View File
@@ -129,6 +129,15 @@ class FunctionValue : public Value {
explicit FunctionValue(Nonnull<const FunctionDeclaration*> declaration)
: Value(Kind::FunctionValue), declaration_(declaration) {}
explicit FunctionValue(Nonnull<const FunctionDeclaration*> declaration,
const BindingMap& type_args,
const std::map<Nonnull<const ImplBinding*>,
Nonnull<const Witness*>>& wits)
: Value(Kind::FunctionValue),
declaration_(declaration),
type_args_(type_args),
witnesses_(wits) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::FunctionValue;
}
@@ -137,8 +146,17 @@ class FunctionValue : public Value {
return *declaration_;
}
auto type_args() const -> const BindingMap& { return type_args_; }
auto witnesses() const
-> const std::map<Nonnull<const ImplBinding*>, const Witness*>& {
return witnesses_;
}
private:
Nonnull<const FunctionDeclaration*> declaration_;
BindingMap type_args_;
std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>> witnesses_;
};
// A bound method value. It includes the receiver object.
@@ -150,6 +168,17 @@ class BoundMethodValue : public Value {
declaration_(declaration),
receiver_(receiver) {}
explicit BoundMethodValue(Nonnull<const FunctionDeclaration*> declaration,
Nonnull<const Value*> receiver,
const BindingMap& type_args,
const std::map<Nonnull<const ImplBinding*>,
Nonnull<const Witness*>>& wits)
: Value(Kind::BoundMethodValue),
declaration_(declaration),
receiver_(receiver),
type_args_(type_args),
witnesses_(wits) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::BoundMethodValue;
}
@@ -160,9 +189,18 @@ class BoundMethodValue : public Value {
auto receiver() const -> Nonnull<const Value*> { return receiver_; }
auto type_args() const -> const BindingMap& { return type_args_; }
auto witnesses() const
-> const std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>>& {
return witnesses_;
}
private:
Nonnull<const FunctionDeclaration*> declaration_;
Nonnull<const Value*> receiver_;
BindingMap type_args_;
std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>> witnesses_;
};
// The value of a location in memory.
@@ -465,16 +503,68 @@ class StructType : public Value {
};
// A class type.
// TODO: Consider splitting this class into several classes.
class NominalClassType : public Value {
public:
// Construct a non-generic class type or a generic class type that has
// not yet been applied to type arguments.
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration)
: Value(Kind::NominalClassType), declaration_(declaration) {}
// Construct a class type that represents the result of applying the
// given generic class to the `type_args`.
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration,
const BindingMap& type_args)
: Value(Kind::NominalClassType),
declaration_(declaration),
type_args_(type_args) {}
// Construct a class type that represents the result of applying the
// given generic class to the `type_args` and that records the result of the
// compile-time search for any required impls.
explicit NominalClassType(
Nonnull<const ClassDeclaration*> declaration, const BindingMap& type_args,
const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& impls)
: Value(Kind::NominalClassType),
declaration_(declaration),
type_args_(type_args),
impls_(impls) {}
// Construct a fully instantiated generic class type to represent the
// run-time type of an object.
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration,
const BindingMap& type_args,
const std::map<Nonnull<const ImplBinding*>,
Nonnull<const Witness*>>& wits)
: Value(Kind::NominalClassType),
declaration_(declaration),
type_args_(type_args),
witnesses_(wits) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::NominalClassType;
}
auto declaration() const -> const ClassDeclaration& { return *declaration_; }
auto type_args() const -> const BindingMap& { return type_args_; }
// Maps each of the class's generic parameters to the AST node that
// identifies the witness table for the corresponding argument.
// Should not be called on 1) a non-generic class, 2) a generic-class
// that is not instantiated, or 3) a fully instantiated runtime type
// of a generic class.
auto impls() const
-> const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& {
return impls_;
}
// Maps each of the class's generic parameters to the witness table
// for the corresponding argument. Should only be called on a fully
// instantiated runtime type of a generic class.
auto witnesses() const
-> const std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>>& {
return witnesses_;
}
// Returns the value of the function named `name` in this class, or
// nullopt if there is no such function.
@@ -483,9 +573,11 @@ class NominalClassType : public Value {
private:
Nonnull<const ClassDeclaration*> declaration_;
BindingMap type_args_;
std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls_;
std::map<Nonnull<const ImplBinding*>, Nonnull<const Witness*>> witnesses_;
};
auto FieldTypes(const NominalClassType&) -> std::vector<NamedValue>;
// Return the declaration of the member with the given name.
auto FindMember(const std::string& name,
llvm::ArrayRef<Nonnull<Declaration*>> members)
+11 -2
View File
@@ -159,6 +159,7 @@
%type <Nonnull<Pattern*>> paren_pattern
%type <Nonnull<TuplePattern*>> tuple_pattern
%type <Nonnull<TuplePattern*>> maybe_empty_tuple_pattern
%type <std::optional<Nonnull<TuplePattern*>>> type_params
%type <ParenContents<Pattern>> paren_pattern_base
%type <ParenContents<Pattern>> paren_pattern_contents
%type <Nonnull<AlternativeSignature*>> alternative
@@ -580,6 +581,8 @@ non_expression_pattern:
$$ = arena->New<BindingPattern>(context.source_loc(), $1, $3,
std::nullopt);
}
| binding_lhs COLON_BANG expression
{ $$ = arena->New<GenericBinding>(context.source_loc(), $1, $3); }
| paren_pattern
{ $$ = $1; }
| postfix_expression tuple_pattern
@@ -868,11 +871,17 @@ alternative_list_contents:
$$.push_back(std::move($3));
}
;
type_params:
// Empty
{ $$ = std::nullopt; }
| tuple_pattern
{ $$ = $1; }
;
declaration:
function_declaration
{ $$ = $1; }
| CLASS identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ClassDeclaration>(context.source_loc(), $2, $4); }
| CLASS identifier type_params LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ClassDeclaration>(context.source_loc(), $2, $3, $5); }
| CHOICE identifier LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $4); }
| VAR variable_declaration SEMICOLON
@@ -0,0 +1,41 @@
// 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;
interface Number {
fn Zero() -> Self;
fn Add[me: Self](other: Self) -> Self;
}
class Point(T:! Number) {
fn Origin() -> Point(T) {
return {.x = T.Zero(), .y = T.Zero()};
}
fn SumXY(p: Point(T)) -> T {
return p.x.Add(p.y);
}
fn SumFn() -> (__Fn(Point(T)) -> T) {
return Point(T).SumXY;
}
var x: T;
var y: T;
}
external impl i32 as Number {
fn Zero() -> i32 { return 0; }
fn Add[me: i32](other: i32) -> i32 { return me + other; }
}
fn Main() -> i32 {
var p: Point(i32) = Point(i32).Origin();
return p.SumFn()(p);
}
@@ -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: %{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/generic_class/fail_argument_deduction.carbon:26: type error in argument deduction
package ExecutableSemanticsTest api;
class Point(T:! Type) {
var x: T;
var y: T;
}
fn FirstOfTwoPoints[T:! Type](a: Point(T), b: Point(T)) -> Point(T) {
return a;
}
fn Main() -> i32 {
var p: Point(i32) = {.x = 0, .y = 1};
var q: Point(Bool) = {.x = true, .y = false};
return FirstOfTwoPoints(p, q).x;
}
@@ -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: %{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/generic_class/fail_bad_parameter_type.carbon:16: unexpected type of deduced parameter i32
package ExecutableSemanticsTest api;
class Point(T:! i32) {
fn Origin(zero: T) -> Point(T) {
return {.x = zero, .y = zero};
}
fn GetX[me: Point(T)]() -> T {
return me.x;
}
var x: T;
var y: T;
}
fn Main() -> i32 {
var p: Point(i32) = Point(i32).Origin(0);
return p.GetX();
}
@@ -0,0 +1,20 @@
// 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/generic_class/fail_field_access_on_generic.carbon:15: field access, unexpected T:! Type of non-interface type Type in a.x
package ExecutableSemanticsTest api;
fn BadFieldAccess[T:! Type](a: T) -> T {
return a.x;
}
fn Main() -> i32 {
return BadFieldAccess(0);
}
@@ -0,0 +1,22 @@
// 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/generic_class/fail_generic_in_pattern.carbon:17: Generic binding may not occur in pattern with expected type: T:! i32
package ExecutableSemanticsTest api;
fn Main() -> i32 {
var t: auto = 5;
match (t) {
case T:! i32 =>
return 0;
default =>
return 1;
}
}
@@ -0,0 +1,25 @@
// 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/generic_class/fail_instantiate_non_generic.carbon:23: attempt to instantiate a non-generic class: Point(i32)
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(i32) = Point.Origin();
return 0;
}
@@ -0,0 +1,23 @@
// 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/generic_class/fail_point_equal.carbon:21: type error in name binding: 'class Point(T = i32)' is not implicitly convertible to 'class Point(T = Bool)'
package ExecutableSemanticsTest api;
class Point(T:! Type) {
var x: T;
var y: T;
}
fn Main() -> i32 {
var p: Point(i32) = {.x = 0, .y = 0};
var q: Point(Bool) = p;
return 0;
}
@@ -0,0 +1,39 @@
// 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;
interface Number {
fn Zero() -> Self;
fn Add[me: Self](other: Self) -> Self;
}
class Point(T:! Number) {
fn Origin() -> Point(T) {
return {.x = T.Zero(), .y = T.Zero()};
}
var x: T;
var y: T;
}
external impl i32 as Number {
fn Zero() -> i32 { return 0; }
fn Add[me: i32](other: i32) -> i32 { return me + other; }
}
fn SumXY[U:! Number](p: Point(U)) -> U {
return p.Origin().x.Add(p.y);
}
fn Main() -> i32 {
var p: Point(i32) = {.x = 0, .y = 0};
return SumXY(p);
}
@@ -0,0 +1,44 @@
// 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;
interface Number {
fn Zero() -> Self;
fn Add[me: Self](other: Self) -> Self;
}
class Point(T:! Number) {
var x: T;
var y: T;
}
fn Origin[U :! Number](other: U) -> Point(U) {
return {.x = U.Zero(), .y = U.Zero()};
}
fn Clone[U :! Number](other: Point(U)) -> Point(U) {
return {.x = other.x, .y = other.y};
}
fn SumXY[U :! Number](other: Point(U)) -> U {
return other.x.Add(other.y);
}
external impl i32 as Number {
fn Zero() -> i32 { return 0; }
fn Add[me: i32](other: i32) -> i32 { return me + other; }
}
fn Main() -> i32 {
var p: Point(i32) = Origin(0);
return SumXY(Clone(p));
}
@@ -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(T:! Type) {
fn Origin(zero: T) -> Point(T) {
return {.x = zero, .y = zero};
}
fn GetX[me: Point(T)]() -> T {
return me.x;
}
var x: T;
var y: T;
}
fn Main() -> i32 {
var p: Point(i32) = Point(i32).Origin(0);
return p.GetX();
}
@@ -0,0 +1,41 @@
// 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;
interface Number {
fn Zero() -> Self;
fn Add[me: Self](other: Self) -> Self;
}
class Point(T:! Number) {
fn Origin() -> Point(T) {
return {.x = T.Zero(), .y = T.Zero()};
}
fn Clone[me: Point(T)]() -> Point(T) {
return {.x = me.x, .y = me.y};
}
fn SumXY[me: Point(T)]() -> T {
return me.x.Add(me.y);
}
var x: T;
var y: T;
}
external impl i32 as Number {
fn Zero() -> i32 { return 0; }
fn Add[me: i32](other: i32) -> i32 { return me + other; }
}
fn Main() -> i32 {
var p: Point(i32) = Point(i32).Origin();
return p.Clone().SumXY();
}
@@ -9,7 +9,7 @@
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/generic_function/fail_not_addable.carbon:17: type error in addition(1)
// CHECK: expected: i32
// CHECK: actual: T
// CHECK: actual: T:! Type
package ExecutableSemanticsTest api;
@@ -0,0 +1,44 @@
// 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;
interface Vector {
fn Zero() -> Self;
fn Add[me: Self](b: Self) -> Self;
fn Scale[me: Self](v: i32) -> Self;
}
class Point {
var x: i32;
var y: i32;
impl Point as Vector {
fn Zero() -> Point {
return {.x = 0, .y = 0};
}
fn Add[me: Point](b: Point) -> Point {
return {.x = me.x + b.x, .y = me.y + b.y};
}
fn Scale[me: Point](v: i32) -> Point {
return {.x = me.x * v, .y = me.y * v};
}
}
}
fn AddAndScaleGeneric[T:! Vector](a: T, s: i32) -> T {
return a.Add(T.Zero()).Scale(s);
}
fn Main() -> i32 {
var a: Point = {.x = 2, .y = 1};
var p: Point = AddAndScaleGeneric(a, 5);
return p.x - 10;
}
@@ -0,0 +1,36 @@
// 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/interface/fail_interface_missing_member.carbon:19: field access, Scale not in Vector
package ExecutableSemanticsTest api;
interface Vector {
fn Add[me: Self](b: Self) -> Self;
}
fn ScaleGeneric[T:! Vector](a: T, s: i32) -> T {
return a.Scale(s);
}
class Point {
var x: i32;
var y: i32;
impl Point as Vector {
fn Add[me: Point](b: Point) -> Point {
return {.x = me.x + b.x, .y = me.y + b.y};
}
}
}
fn Main() -> i32 {
var a: Point = {.x = 3, .y = 1};
var b: Point = ScaleGeneric(a, 2);
return b.x - 6;
}