mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 15:41:04 +01:00
Basic support for .Self within :! bindings and where expressions. (#1311)
`.Self` is modeled as a new kind of expression, `DotSelfExpression`. Name resolution associates each occurrence of a `DotSelfExpression` with a generic binding, much like for an `IdentifierExpression`. Both `:!` bindings and `where` expressions bring `.Self` into scope. The tentative intent is that if there are multiple `.Self`s in scope and they refer to different bindings, the result of using a `.Self` expression is an ambiguity error, but that is not implemented in this patch. Instead, like for `IdentifierExpression`s, we find the innermost enclosing definition of `.Self`. `.Foo` expressions are rewritten to `.Self.Foo`, but this isn't enough to make them do anything useful, because associated constants aren't supported in general yet.
This commit is contained in:
@@ -79,6 +79,10 @@ message IdentifierExpression {
|
||||
optional string name = 1;
|
||||
}
|
||||
|
||||
message DesignatorExpression {
|
||||
optional string name = 1;
|
||||
}
|
||||
|
||||
message IntrinsicExpression {
|
||||
enum Intrinsic {
|
||||
UnknownIntrinsic = 0;
|
||||
@@ -170,6 +174,7 @@ message Expression {
|
||||
ArrayTypeLiteral array_type_literal = 21;
|
||||
CompoundMemberAccessExpression compound_member_access = 22;
|
||||
WhereExpression where = 23;
|
||||
DesignatorExpression designator = 24;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -275,6 +275,13 @@ static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
|
||||
break;
|
||||
}
|
||||
|
||||
case Fuzzing::Expression::kDesignator: {
|
||||
const auto& designator = expression.designator();
|
||||
out << ".";
|
||||
IdentifierToCarbon(designator.name(), out);
|
||||
break;
|
||||
}
|
||||
|
||||
case Fuzzing::Expression::kIntrinsic: {
|
||||
const auto& intrinsic = expression.intrinsic();
|
||||
switch (intrinsic.intrinsic()) {
|
||||
|
||||
+4
-24
@@ -64,7 +64,6 @@ cc_test(
|
||||
":ast_test_matchers",
|
||||
":declaration",
|
||||
":expression",
|
||||
":pattern",
|
||||
":statement",
|
||||
"//common:gtest_main",
|
||||
"//explorer/common:arena",
|
||||
@@ -79,7 +78,7 @@ cc_library(
|
||||
],
|
||||
deps = [
|
||||
":ast_node",
|
||||
":pattern",
|
||||
":expression",
|
||||
":value_category",
|
||||
"//common:check",
|
||||
"//common:ostream",
|
||||
@@ -98,7 +97,7 @@ cc_library(
|
||||
deps = [
|
||||
":ast_node",
|
||||
":impl_binding",
|
||||
":pattern",
|
||||
":expression",
|
||||
":return_term",
|
||||
":statement",
|
||||
":static_scope",
|
||||
@@ -125,8 +124,8 @@ cc_library(
|
||||
|
||||
cc_library(
|
||||
name = "expression",
|
||||
srcs = ["expression.cpp"],
|
||||
hdrs = ["expression.h"],
|
||||
srcs = ["expression.cpp", "pattern.cpp"],
|
||||
hdrs = ["expression.h", "pattern.h"],
|
||||
deps = [
|
||||
":ast_node",
|
||||
":paren_contents",
|
||||
@@ -168,30 +167,12 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "pattern",
|
||||
srcs = ["pattern.cpp"],
|
||||
hdrs = ["pattern.h"],
|
||||
deps = [
|
||||
":ast_node",
|
||||
":expression",
|
||||
":static_scope",
|
||||
":value_category",
|
||||
"//common:ostream",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
"//explorer/common:source_location",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "pattern_test",
|
||||
srcs = ["pattern_test.cpp"],
|
||||
deps = [
|
||||
":expression",
|
||||
":paren_contents",
|
||||
":pattern",
|
||||
"//common:gtest_main",
|
||||
"//explorer/common:arena",
|
||||
"@com_google_googletest//:gtest",
|
||||
@@ -222,7 +203,6 @@ cc_library(
|
||||
deps = [
|
||||
":ast_node",
|
||||
":expression",
|
||||
":pattern",
|
||||
":return_term",
|
||||
":static_scope",
|
||||
":value_category",
|
||||
|
||||
@@ -57,6 +57,7 @@ abstract class Expression : AstNode;
|
||||
class TypeTypeLiteral : Expression;
|
||||
class ValueLiteral : Expression;
|
||||
class IdentifierExpression : Expression;
|
||||
class DotSelfExpression : Expression;
|
||||
class IntrinsicExpression : Expression;
|
||||
class IfExpression : Expression;
|
||||
class WhereExpression : Expression;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "explorer/ast/pattern.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@@ -184,7 +185,7 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
}
|
||||
case ExpressionKind::WhereExpression: {
|
||||
const auto& where = cast<WhereExpression>(*this);
|
||||
out << where.base() << " where ";
|
||||
out << where.self_binding().type() << " where ";
|
||||
llvm::ListSeparator sep(" and ");
|
||||
for (const WhereClause* clause : where.clauses()) {
|
||||
out << sep << *clause;
|
||||
@@ -213,6 +214,7 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::IdentifierExpression:
|
||||
case ExpressionKind::DotSelfExpression:
|
||||
case ExpressionKind::IntLiteral:
|
||||
case ExpressionKind::BoolLiteral:
|
||||
case ExpressionKind::BoolTypeLiteral:
|
||||
@@ -232,6 +234,9 @@ void Expression::PrintID(llvm::raw_ostream& out) const {
|
||||
case ExpressionKind::IdentifierExpression:
|
||||
out << cast<IdentifierExpression>(*this).name();
|
||||
break;
|
||||
case ExpressionKind::DotSelfExpression:
|
||||
out << ".Self";
|
||||
break;
|
||||
case ExpressionKind::IntLiteral:
|
||||
out << cast<IntLiteral>(*this).value();
|
||||
break;
|
||||
|
||||
@@ -28,6 +28,7 @@ class MemberName;
|
||||
class VariableType;
|
||||
class InterfaceType;
|
||||
class ImplBinding;
|
||||
class GenericBinding;
|
||||
|
||||
class Expression : public AstNode {
|
||||
public:
|
||||
@@ -154,6 +155,40 @@ class IdentifierExpression : public Expression {
|
||||
std::optional<ValueNodeView> value_node_;
|
||||
};
|
||||
|
||||
// A `.Self` expression within either a `:!` binding or a standalone `where`
|
||||
// expression.
|
||||
//
|
||||
// In a `:!` binding, the type of `.Self` is always `Type`. For example, in
|
||||
// `A:! AddableWith(.Self)`, the expression `.Self` refers to the same type as
|
||||
// `A`, but with type `Type`.
|
||||
//
|
||||
// In a `where` binding, the type of `.Self` is the constraint preceding the
|
||||
// `where` keyword. For example, in `Foo where .Result is Bar(.Self)`, the type
|
||||
// of `.Self` is `Foo`.
|
||||
class DotSelfExpression : public Expression {
|
||||
public:
|
||||
explicit DotSelfExpression(SourceLocation source_loc)
|
||||
: Expression(AstNodeKind::DotSelfExpression, source_loc) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
return InheritsFromDotSelfExpression(node->kind());
|
||||
}
|
||||
|
||||
// The self binding. Cannot be called before name resolution.
|
||||
auto self_binding() const -> const GenericBinding& { return **self_binding_; }
|
||||
auto self_binding() -> GenericBinding& { return **self_binding_; }
|
||||
|
||||
// Sets the self binding. Called only once, during name resolution.
|
||||
void set_self_binding(Nonnull<GenericBinding*> self_binding) {
|
||||
CARBON_CHECK(!self_binding_.has_value());
|
||||
self_binding_ = self_binding;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::optional<Nonnull<GenericBinding*>> self_binding_;
|
||||
};
|
||||
|
||||
class SimpleMemberAccessExpression : public Expression {
|
||||
public:
|
||||
explicit SimpleMemberAccessExpression(SourceLocation source_loc,
|
||||
@@ -741,20 +776,24 @@ class EqualsWhereClause : public WhereClause {
|
||||
};
|
||||
|
||||
// A `where` expression: `AddableWith(i32) where .Result == i32`.
|
||||
//
|
||||
// The first operand is rewritten to a generic binding, for example
|
||||
// `.Self:! AddableWith(i32)`, which may be used in the clauses.
|
||||
class WhereExpression : public Expression {
|
||||
public:
|
||||
explicit WhereExpression(SourceLocation source_loc, Nonnull<Expression*> base,
|
||||
explicit WhereExpression(SourceLocation source_loc,
|
||||
Nonnull<GenericBinding*> self_binding,
|
||||
std::vector<Nonnull<WhereClause*>> clauses)
|
||||
: Expression(AstNodeKind::WhereExpression, source_loc),
|
||||
base_(base),
|
||||
self_binding_(self_binding),
|
||||
clauses_(std::move(clauses)) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
return InheritsFromWhereExpression(node->kind());
|
||||
}
|
||||
|
||||
auto base() const -> const Expression& { return *base_; }
|
||||
auto base() -> Expression& { return *base_; }
|
||||
auto self_binding() const -> const GenericBinding& { return *self_binding_; }
|
||||
auto self_binding() -> GenericBinding& { return *self_binding_; }
|
||||
|
||||
auto clauses() const -> llvm::ArrayRef<Nonnull<const WhereClause*>> {
|
||||
return clauses_;
|
||||
@@ -762,7 +801,7 @@ class WhereExpression : public Expression {
|
||||
auto clauses() -> llvm::ArrayRef<Nonnull<WhereClause*>> { return clauses_; }
|
||||
|
||||
private:
|
||||
Nonnull<Expression*> base_;
|
||||
Nonnull<GenericBinding*> self_binding_;
|
||||
std::vector<Nonnull<WhereClause*>> clauses_;
|
||||
};
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ class Pattern : public AstNode {
|
||||
// and after typechecking it's guaranteed to be true.
|
||||
auto has_value() const -> bool { return value_.has_value(); }
|
||||
|
||||
// Determines whether the pattern has already been type-checked. Should
|
||||
// only be used by type-checking.
|
||||
auto is_type_checked() const -> bool {
|
||||
return static_type_.has_value() && value_.has_value();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Constructs a Pattern representing syntax at the given line number.
|
||||
// `kind` must be the enumerator corresponding to the most-derived type being
|
||||
@@ -277,12 +283,22 @@ class GenericBinding : public Pattern {
|
||||
// Set the original generic binding.
|
||||
void set_original(Nonnull<const GenericBinding*> orig) { original_ = orig; }
|
||||
|
||||
// Returns whether this binding has been named as a type within its own type
|
||||
// expression via `.Self`. Set by type-checking.
|
||||
auto named_as_type_via_dot_self() const -> bool {
|
||||
return named_as_type_via_dot_self_;
|
||||
}
|
||||
// Set that this binding was named as a type within its own type expression
|
||||
// via `.Self`. May only be called during type-checking.
|
||||
void set_named_as_type_via_dot_self() { named_as_type_via_dot_self_ = true; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
Nonnull<Expression*> type_;
|
||||
std::optional<Nonnull<const Value*>> symbolic_identity_;
|
||||
std::optional<Nonnull<const ImplBinding*>> impl_binding_;
|
||||
std::optional<Nonnull<const GenericBinding*>> original_;
|
||||
bool named_as_type_via_dot_self_ = false;
|
||||
};
|
||||
|
||||
// Converts paren_contents to a Pattern, interpreting the parentheses as
|
||||
|
||||
@@ -163,7 +163,7 @@ class StaticScope {
|
||||
|
||||
// Make `parent` a parent of this scope.
|
||||
// REQUIRES: `parent` is not already a parent of this scope.
|
||||
void AddParent(Nonnull<StaticScope*> parent) {
|
||||
void AddParent(Nonnull<const StaticScope*> parent) {
|
||||
parent_scopes_.push_back(parent);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ class StaticScope {
|
||||
std::unordered_map<std::string, Entry> declared_names_;
|
||||
|
||||
// A list of scopes used for name lookup within this scope.
|
||||
std::vector<Nonnull<StaticScope*>> parent_scopes_;
|
||||
std::vector<Nonnull<const StaticScope*>> parent_scopes_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
namespace Carbon {
|
||||
|
||||
using ::llvm::cast;
|
||||
using ::llvm::isa;
|
||||
|
||||
static auto ExpressionToProto(const Expression& expression)
|
||||
-> Fuzzing::Expression;
|
||||
@@ -108,6 +109,13 @@ static auto ExpressionToProto(const Expression& expression)
|
||||
case ExpressionKind::SimpleMemberAccessExpression: {
|
||||
const auto& simple_member_access =
|
||||
cast<SimpleMemberAccessExpression>(expression);
|
||||
if (isa<DotSelfExpression>(simple_member_access.object())) {
|
||||
// The parser rewrites `.Foo` into `.Self.Foo`. Undo this
|
||||
// transformation.
|
||||
auto* designator_proto = expression_proto.mutable_designator();
|
||||
designator_proto->set_name(simple_member_access.member());
|
||||
break;
|
||||
}
|
||||
auto* simple_member_access_proto =
|
||||
expression_proto.mutable_simple_member_access();
|
||||
simple_member_access_proto->set_field(simple_member_access.member());
|
||||
@@ -182,7 +190,8 @@ static auto ExpressionToProto(const Expression& expression)
|
||||
case ExpressionKind::WhereExpression: {
|
||||
const auto& where = cast<WhereExpression>(expression);
|
||||
auto* where_proto = expression_proto.mutable_where();
|
||||
*where_proto->mutable_base() = ExpressionToProto(where.base());
|
||||
*where_proto->mutable_base() =
|
||||
ExpressionToProto(where.self_binding().type());
|
||||
for (const WhereClause* where : where.clauses()) {
|
||||
Fuzzing::WhereClause clause_proto;
|
||||
switch (where->kind()) {
|
||||
@@ -208,6 +217,12 @@ static auto ExpressionToProto(const Expression& expression)
|
||||
break;
|
||||
}
|
||||
|
||||
case ExpressionKind::DotSelfExpression: {
|
||||
auto* designator_proto = expression_proto.mutable_designator();
|
||||
designator_proto->set_name("Self");
|
||||
break;
|
||||
}
|
||||
|
||||
case ExpressionKind::IntrinsicExpression: {
|
||||
const auto& intrinsic = cast<IntrinsicExpression>(expression);
|
||||
auto* intrinsic_proto = expression_proto.mutable_intrinsic();
|
||||
|
||||
@@ -26,7 +26,6 @@ cc_library(
|
||||
"//common:ostream",
|
||||
"//explorer/ast:declaration",
|
||||
"//explorer/ast:expression",
|
||||
"//explorer/ast:pattern",
|
||||
"//explorer/ast:statement",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
@@ -141,7 +140,6 @@ cc_library(
|
||||
"//explorer/ast",
|
||||
"//explorer/ast:declaration",
|
||||
"//explorer/ast:expression",
|
||||
"//explorer/ast:pattern",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
"@llvm-project//llvm:Support",
|
||||
@@ -174,7 +172,6 @@ cc_library(
|
||||
"//explorer/ast",
|
||||
"//explorer/ast:declaration",
|
||||
"//explorer/ast:expression",
|
||||
"//explorer/ast:pattern",
|
||||
"//explorer/ast:return_term",
|
||||
"//explorer/ast:statement",
|
||||
"//explorer/ast:static_scope",
|
||||
|
||||
@@ -408,6 +408,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
|
||||
case ExpressionKind::IntrinsicExpression:
|
||||
case ExpressionKind::IfExpression:
|
||||
case ExpressionKind::WhereExpression:
|
||||
case ExpressionKind::DotSelfExpression:
|
||||
case ExpressionKind::ArrayTypeLiteral:
|
||||
case ExpressionKind::InstantiateImpl:
|
||||
CARBON_FATAL() << "Can't treat expression as lvalue: " << exp;
|
||||
@@ -927,6 +928,14 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
return todo_.FinishAction(value);
|
||||
}
|
||||
case ExpressionKind::DotSelfExpression: {
|
||||
// `.Self` always symbolically resolves to the self binding, even if it's
|
||||
// not yet been type-checked.
|
||||
CARBON_CHECK(act.pos() == 0);
|
||||
const auto& dot_self = cast<DotSelfExpression>(exp);
|
||||
return todo_.FinishAction(
|
||||
arena_->New<VariableType>(&dot_self.self_binding()));
|
||||
}
|
||||
case ExpressionKind::IntLiteral:
|
||||
CARBON_CHECK(act.pos() == 0);
|
||||
// { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
|
||||
|
||||
@@ -166,6 +166,15 @@ static auto ResolveNames(Expression& expression,
|
||||
identifier.set_value_node(value_node);
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::DotSelfExpression: {
|
||||
auto& dot_self = cast<DotSelfExpression>(expression);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
const auto value_node,
|
||||
enclosing_scope.Resolve(".Self", dot_self.source_loc()));
|
||||
dot_self.set_self_binding(const_cast<GenericBinding*>(
|
||||
&cast<GenericBinding>(value_node.base())));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::IntrinsicExpression:
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(
|
||||
cast<IntrinsicExpression>(expression).args(), enclosing_scope));
|
||||
@@ -182,13 +191,14 @@ static auto ResolveNames(Expression& expression,
|
||||
}
|
||||
case ExpressionKind::WhereExpression: {
|
||||
auto& where = cast<WhereExpression>(expression);
|
||||
// TODO: Introduce `.Self` into scope?
|
||||
// StaticScope where_scope;
|
||||
// where_scope.AddParent(&enclosing_scope);
|
||||
// where_scope.Add(".Self", ???);
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(where.base(), enclosing_scope));
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
ResolveNames(where.self_binding().type(), enclosing_scope));
|
||||
// Introduce `.Self` into scope on the right of the `where` keyword.
|
||||
StaticScope where_scope;
|
||||
where_scope.AddParent(&enclosing_scope);
|
||||
CARBON_RETURN_IF_ERROR(where_scope.Add(".Self", &where.self_binding()));
|
||||
for (Nonnull<WhereClause*> clause : where.clauses()) {
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(*clause, enclosing_scope));
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(*clause, where_scope));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -253,7 +263,11 @@ static auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
|
||||
}
|
||||
case PatternKind::GenericBinding: {
|
||||
auto& binding = cast<GenericBinding>(pattern);
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), enclosing_scope));
|
||||
// `.Self` is in scope in the context of the type.
|
||||
StaticScope self_scope;
|
||||
self_scope.AddParent(&enclosing_scope);
|
||||
CARBON_RETURN_IF_ERROR(self_scope.Add(".Self", &binding));
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), self_scope));
|
||||
if (binding.name() != AnonymousName) {
|
||||
CARBON_RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
|
||||
}
|
||||
@@ -425,8 +439,7 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
|
||||
StaticScope function_scope;
|
||||
function_scope.AddParent(&enclosing_scope);
|
||||
for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(binding->type(), function_scope));
|
||||
CARBON_RETURN_IF_ERROR(function_scope.Add(binding->name(), binding));
|
||||
CARBON_RETURN_IF_ERROR(ResolveNames(*binding, function_scope));
|
||||
}
|
||||
if (function.is_method()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
|
||||
@@ -757,11 +757,6 @@ class ConstraintTypeBuilder {
|
||||
source_loc, ".Self", arena->New<TypeTypeLiteral>(source_loc))) {}
|
||||
ConstraintTypeBuilder(Nonnull<const GenericBinding*> self_binding)
|
||||
: self_binding_(self_binding) {}
|
||||
ConstraintTypeBuilder(Nonnull<const ConstraintType*> constraint)
|
||||
: self_binding_(constraint->self_binding()),
|
||||
impl_constraints_(constraint->impl_constraints()),
|
||||
equality_constraints_(constraint->equality_constraints()),
|
||||
lookup_contexts_(constraint->lookup_contexts()) {}
|
||||
|
||||
// Produce a type that refers to the `.Self` type of the constraint.
|
||||
auto MakeSelfType(Nonnull<Arena*> arena) const
|
||||
@@ -1090,8 +1085,6 @@ auto TypeChecker::MatchImpl(const InterfaceType& iface,
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: If the `interface` is a ConstraintType, for every impl_constraint,
|
||||
// try deduction against that.
|
||||
if (ErrorOr<Success> e = ArgumentDeduction(
|
||||
source_loc, "match", impl.deduced, deduced_args, impl.interface,
|
||||
&iface, /*allow_implicit_conversion=*/false, impl_scope);
|
||||
@@ -1259,6 +1252,8 @@ auto TypeChecker::DeduceCallBindings(
|
||||
impl_bindings, impl_scope, call.source_loc(), generic_bindings, impls));
|
||||
call.set_impls(impls);
|
||||
|
||||
// TODO: Ensure any equality constraints are satisfied.
|
||||
|
||||
// Convert the arguments to the parameter type.
|
||||
Nonnull<const Value*> param_type = Substitute(generic_bindings, params_type);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
@@ -1778,6 +1773,17 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
ident.set_value_category(ident.value_node().value_category());
|
||||
return Success();
|
||||
}
|
||||
case ExpressionKind::DotSelfExpression: {
|
||||
auto& dot_self = cast<DotSelfExpression>(*e);
|
||||
if (dot_self.self_binding().is_type_checked()) {
|
||||
dot_self.set_static_type(&dot_self.self_binding().static_type());
|
||||
} else {
|
||||
dot_self.set_static_type(arena_->New<TypeType>());
|
||||
dot_self.self_binding().set_named_as_type_via_dot_self();
|
||||
}
|
||||
dot_self.set_value_category(ValueCategory::Let);
|
||||
return Success();
|
||||
}
|
||||
case ExpressionKind::IntLiteral:
|
||||
e->set_value_category(ValueCategory::Let);
|
||||
e->set_static_type(arena_->New<IntType>());
|
||||
@@ -2074,28 +2080,38 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
||||
}
|
||||
case ExpressionKind::WhereExpression: {
|
||||
auto& where = cast<WhereExpression>(*e);
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckExp(&where.base(), impl_scope));
|
||||
ImplScope inner_impl_scope;
|
||||
inner_impl_scope.AddParent(&impl_scope);
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckPattern(&where.self_binding(),
|
||||
std::nullopt, inner_impl_scope,
|
||||
ValueCategory::Let));
|
||||
for (Nonnull<WhereClause*> clause : where.clauses()) {
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, impl_scope));
|
||||
CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, inner_impl_scope));
|
||||
}
|
||||
|
||||
const ConstraintType* base;
|
||||
const Value& base_type = where.base().static_type();
|
||||
if (auto* constraint_type_type =
|
||||
dyn_cast<TypeOfConstraintType>(&base_type)) {
|
||||
base = &constraint_type_type->constraint_type();
|
||||
} else if (auto* interface_type_type =
|
||||
dyn_cast<TypeOfInterfaceType>(&base_type)) {
|
||||
base = MakeConstraintForInterface(
|
||||
e->source_loc(), &interface_type_type->interface_type());
|
||||
std::optional<Nonnull<const ConstraintType*>> base;
|
||||
const Value& base_type = where.self_binding().static_type();
|
||||
if (auto* constraint_type = dyn_cast<ConstraintType>(&base_type)) {
|
||||
base = constraint_type;
|
||||
} else if (auto* interface_type = dyn_cast<InterfaceType>(&base_type)) {
|
||||
base = MakeConstraintForInterface(e->source_loc(), interface_type);
|
||||
} else if (isa<TypeType>(base_type)) {
|
||||
// Start with an unconstrained type.
|
||||
} else {
|
||||
return CompilationError(e->source_loc())
|
||||
<< "expected constraint as first operand of `where` expression, "
|
||||
<< "found " << base_type;
|
||||
}
|
||||
|
||||
// Start with the given constraint, if any.
|
||||
ConstraintTypeBuilder builder(&where.self_binding());
|
||||
if (base) {
|
||||
BindingMap map;
|
||||
map[(*base)->self_binding()] = builder.MakeSelfType(arena_);
|
||||
builder.Add(cast<ConstraintType>(Substitute(map, *base)));
|
||||
}
|
||||
|
||||
// Apply the `where` clauses.
|
||||
ConstraintTypeBuilder builder(base);
|
||||
for (Nonnull<const WhereClause*> clause : where.clauses()) {
|
||||
switch (clause->kind()) {
|
||||
case WhereClauseKind::IsWhereClause: {
|
||||
@@ -2340,6 +2356,11 @@ auto TypeChecker::TypeCheckPattern(
|
||||
<< binding;
|
||||
}
|
||||
binding.set_static_type(type);
|
||||
if (binding.named_as_type_via_dot_self() && !IsTypeOfType(type)) {
|
||||
return CompilationError(binding.type().source_loc())
|
||||
<< "`.Self` used in type of non-type binding `" << binding.name()
|
||||
<< "`";
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> val,
|
||||
InterpPattern(&binding, arena_, trace_stream_));
|
||||
binding.set_symbolic_identity(val);
|
||||
|
||||
@@ -81,7 +81,6 @@ cc_library(
|
||||
"//explorer/ast:declaration",
|
||||
"//explorer/ast:expression",
|
||||
"//explorer/ast:paren_contents",
|
||||
"//explorer/ast:pattern",
|
||||
"//explorer/ast:value_category",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
|
||||
@@ -301,6 +301,15 @@ api_or_impl:
|
||||
primary_expression:
|
||||
identifier
|
||||
{ $$ = arena->New<IdentifierExpression>(context.source_loc(), $1); }
|
||||
| designator
|
||||
{
|
||||
// `.Foo` is rewritten to `.Self.Foo`.
|
||||
$$ = arena->New<SimpleMemberAccessExpression>(
|
||||
context.source_loc(),
|
||||
arena->New<DotSelfExpression>(context.source_loc()), $1);
|
||||
}
|
||||
| PERIOD SELF
|
||||
{ $$ = arena->New<DotSelfExpression>(context.source_loc()); }
|
||||
| integer_literal
|
||||
{ $$ = arena->New<IntLiteral>(context.source_loc(), $1); }
|
||||
| string_literal
|
||||
@@ -544,7 +553,11 @@ where_clause_list:
|
||||
;
|
||||
where_expression:
|
||||
type_expression WHERE where_clause_list
|
||||
{ $$ = arena->New<WhereExpression>(context.source_loc(), $1, $3); }
|
||||
{
|
||||
auto* self =
|
||||
arena -> New<GenericBinding>(context.source_loc(), ".Self", $1);
|
||||
$$ = arena->New<WhereExpression>(context.source_loc(), self, $3);
|
||||
}
|
||||
;
|
||||
statement_expression:
|
||||
ref_deref_expression
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// RUN: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 12
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface SwizzleWith(T:! Type) {
|
||||
fn Op[me: Self](x: T) -> i32;
|
||||
}
|
||||
|
||||
impl i32 as SwizzleWith(i32) {
|
||||
fn Op[me: Self](x: Self) -> Self { return me * 10 + x; }
|
||||
}
|
||||
|
||||
fn F[T:! SwizzleWith(.Self)](v: T, w: T) -> i32 {
|
||||
return v.Op(w);
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var one: i32 = 1;
|
||||
var two: i32 = 2;
|
||||
return F(one, two);
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ impl i32 as I {}
|
||||
|
||||
fn F(A:! i32, B:! i32, C:! i32, D:! i32, E:! i32,
|
||||
T:! I where A == B and C == D and C == E and B == D) {
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where .Self:! Type is interface I, A:! i32 == B:! i32 == E:! i32 == C:! i32 == D:! i32
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where .Self:! I is interface I, A:! i32 == B:! i32 == E:! i32 == C:! i32 == D:! i32
|
||||
T.F();
|
||||
}
|
||||
|
||||
|
||||
@@ -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: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 12
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface A { fn F() -> i32; }
|
||||
interface B { fn F() -> i32; }
|
||||
|
||||
impl i32 as A { fn F() -> i32 { return 1; } }
|
||||
impl i32 as B { fn F() -> i32 { return 2; } }
|
||||
|
||||
fn GetA[T:! A](x: T) -> i32 { return x.F(); }
|
||||
fn GetAB[T:! B where .Self is A](x: T) -> i32 {
|
||||
// Note that there's no ambiguity in the use of the name F here.
|
||||
// `x.F` is `x.(B.F)` because the constraint only treats `B`, not
|
||||
// `A`, as a lookup context.
|
||||
return 10 * GetA(x) + x.F();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var v: i32 = 0;
|
||||
return GetAB(v);
|
||||
}
|
||||
@@ -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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface FrobWith(T:! Type) {}
|
||||
|
||||
fn F[T:! FrobWith(.Self)]
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_after_scope.carbon:[[@LINE+1]]: could not resolve '.Self'
|
||||
(U: .Self) {
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface FrobWith(T:! Type) {}
|
||||
|
||||
fn F[T:! FrobWith(.Self),
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_after_scope_2.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `U`
|
||||
U:! .Self]() {
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_not_in_scope.carbon:[[@LINE+1]]: could not resolve '.Self'
|
||||
fn Main() -> .Self {
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_infinite_self.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
|
||||
fn F[T:! .Self]() {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_infinite_self_ptr.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
|
||||
fn F[T:! .Self*]() {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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} %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class X(T:! Type) {}
|
||||
|
||||
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_nontype_self.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
|
||||
fn F[T:! X(.Self)](x: T) {}
|
||||
|
||||
fn Main() -> i32 { return 0; }
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 122
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface A { fn F() -> i32; }
|
||||
interface B { fn F() -> i32; }
|
||||
|
||||
impl i32 as A { fn F() -> i32 { return 1; } }
|
||||
impl i32 as B { fn F() -> i32 { return 2; } }
|
||||
|
||||
fn GetAB(T:! B where .Self is A) -> i32 {
|
||||
return 100 * T.(A.F)() + 10 * T.(B.F)() + T.F();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
return GetAB(i32);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// 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: %{explorer} %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
|
||||
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: %{explorer} %s
|
||||
// CHECK: result: 12
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface SwizzleWith(T:! Type) {
|
||||
fn Op[me: Self](x: T) -> i32;
|
||||
}
|
||||
|
||||
// TODO: There should be some way to write this that includes
|
||||
// `SwizzleWith(.Self)` in the list of lookup contexts.
|
||||
alias Swizzle = Type where .Self is SwizzleWith(.Self);
|
||||
|
||||
impl i32 as SwizzleWith(i32) {
|
||||
fn Op[me: Self](x: Self) -> Self { return me * 10 + x; }
|
||||
}
|
||||
|
||||
fn F[T:! Swizzle](v: T, w: T) -> i32 {
|
||||
return v.(SwizzleWith(T).Op)(w);
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var one: i32 = 1;
|
||||
var two: i32 = 2;
|
||||
return F(one, two);
|
||||
}
|
||||
Reference in New Issue
Block a user