mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 11:01:05 +01:00
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:
committed by
GitHub
co-authored by
Jon Meow
Geoff Romer
parent
37a0e35b31
commit
210856dd57
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+5
-65
@@ -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_; }
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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, ¶m_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*>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
+25
@@ -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;
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user