mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Parameterized impl declarations (#1189)
* parameterized impls, first step * bug fixes, comments, etc. * added another test case, fix a bug in impl lookup * simplify tests, removing tuple stuff * don't create impl bindings for -bound implicit parameters * remove a redundant 'private' * CamelCase * add missing backtick * add a comment
This commit is contained in:
@@ -56,3 +56,4 @@ abstract class Expression : AstNode;
|
||||
class IfExpression : Expression;
|
||||
class UnimplementedExpression : Expression;
|
||||
class ArrayTypeLiteral : Expression;
|
||||
class InstantiateImpl : Expression;
|
||||
|
||||
@@ -211,6 +211,27 @@ void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
|
||||
}
|
||||
}
|
||||
|
||||
auto ImplDeclaration::Create(Nonnull<Arena*> arena, SourceLocation source_loc,
|
||||
ImplKind kind, Nonnull<Expression*> impl_type,
|
||||
Nonnull<Expression*> interface,
|
||||
std::vector<Nonnull<AstNode*>> deduced_params,
|
||||
std::vector<Nonnull<Declaration*>> members)
|
||||
-> ErrorOr<Nonnull<ImplDeclaration*>> {
|
||||
std::vector<Nonnull<GenericBinding*>> resolved_params;
|
||||
for (Nonnull<AstNode*> param : deduced_params) {
|
||||
switch (param->kind()) {
|
||||
case AstNodeKind::GenericBinding:
|
||||
resolved_params.push_back(&cast<GenericBinding>(*param));
|
||||
break;
|
||||
default:
|
||||
return CompilationError(source_loc)
|
||||
<< "illegal AST node in implicit parameter list of impl";
|
||||
}
|
||||
}
|
||||
return arena->New<ImplDeclaration>(source_loc, kind, impl_type, interface,
|
||||
resolved_params, members);
|
||||
}
|
||||
|
||||
void AlternativeSignature::Print(llvm::raw_ostream& out) const {
|
||||
out << "alt " << name() << " " << signature();
|
||||
}
|
||||
|
||||
@@ -321,14 +321,24 @@ class ImplDeclaration : public Declaration {
|
||||
public:
|
||||
using ImplementsCarbonValueNode = void;
|
||||
|
||||
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
|
||||
ImplKind kind, Nonnull<Expression*> impl_type,
|
||||
Nonnull<Expression*> interface,
|
||||
std::vector<Nonnull<AstNode*>> deduced_params,
|
||||
std::vector<Nonnull<Declaration*>> members)
|
||||
-> ErrorOr<Nonnull<ImplDeclaration*>>;
|
||||
|
||||
// Use `Create` instead.
|
||||
ImplDeclaration(SourceLocation source_loc, ImplKind kind,
|
||||
Nonnull<Expression*> impl_type,
|
||||
Nonnull<Expression*> interface,
|
||||
std::vector<Nonnull<GenericBinding*>> deduced_params,
|
||||
std::vector<Nonnull<Declaration*>> members)
|
||||
: Declaration(AstNodeKind::ImplDeclaration, source_loc),
|
||||
kind_(kind),
|
||||
impl_type_(impl_type),
|
||||
interface_(interface),
|
||||
deduced_parameters_(std::move(deduced_params)),
|
||||
members_(std::move(members)) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
@@ -347,17 +357,32 @@ class ImplDeclaration : public Declaration {
|
||||
auto interface_type() const -> Nonnull<const Value*> {
|
||||
return *interface_type_;
|
||||
}
|
||||
auto deduced_parameters() const
|
||||
-> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
|
||||
return deduced_parameters_;
|
||||
}
|
||||
auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
|
||||
return deduced_parameters_;
|
||||
}
|
||||
auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
|
||||
return members_;
|
||||
}
|
||||
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
|
||||
void set_impl_bindings(llvm::ArrayRef<Nonnull<const ImplBinding*>> imps) {
|
||||
impl_bindings_ = imps;
|
||||
}
|
||||
auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
|
||||
return impl_bindings_;
|
||||
}
|
||||
|
||||
private:
|
||||
ImplKind kind_;
|
||||
Nonnull<Expression*> impl_type_; // TODO: make this optional
|
||||
Nonnull<Expression*> interface_;
|
||||
std::optional<Nonnull<const Value*>> interface_type_;
|
||||
std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
|
||||
std::vector<Nonnull<Declaration*>> members_;
|
||||
std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
|
||||
};
|
||||
|
||||
// Return the name of a declaration, if it has one.
|
||||
|
||||
@@ -164,6 +164,11 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
<< if_expr.then_expression() << " else " << if_expr.else_expression();
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::InstantiateImpl: {
|
||||
const auto& inst_impl = cast<InstantiateImpl>(*this);
|
||||
out << "instantiate " << *inst_impl.generic_impl();
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::UnimplementedExpression: {
|
||||
const auto& unimplemented = cast<UnimplementedExpression>(*this);
|
||||
out << "UnimplementedExpression<" << unimplemented.label() << ">(";
|
||||
@@ -237,6 +242,7 @@ void Expression::PrintID(llvm::raw_ostream& out) const {
|
||||
case ExpressionKind::UnimplementedExpression:
|
||||
case ExpressionKind::FunctionTypeLiteral:
|
||||
case ExpressionKind::ArrayTypeLiteral:
|
||||
case ExpressionKind::InstantiateImpl:
|
||||
out << "...";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -364,6 +364,8 @@ class GenericBinding;
|
||||
using BindingMap =
|
||||
std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
|
||||
|
||||
using ImplExpMap = std::map<Nonnull<const ImplBinding*>, Nonnull<Expression*>>;
|
||||
|
||||
class CallExpression : public Expression {
|
||||
public:
|
||||
explicit CallExpression(SourceLocation source_loc,
|
||||
@@ -382,18 +384,14 @@ class CallExpression : public Expression {
|
||||
auto argument() const -> const Expression& { return *argument_; }
|
||||
auto argument() -> Expression& { return *argument_; }
|
||||
|
||||
// Maps each of `function`'s generic parameters to the AST node
|
||||
// that identifies the witness table for the corresponding argument.
|
||||
// Maps each of `function`'s impl bindings to an expression
|
||||
// that constructs a witness table.
|
||||
// Should not be called before typechecking, or if `function` is not
|
||||
// a generic function.
|
||||
auto impls() const
|
||||
-> const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& {
|
||||
return impls_;
|
||||
}
|
||||
auto impls() const -> const ImplExpMap& { return impls_; }
|
||||
|
||||
// Can only be called once, during typechecking.
|
||||
void set_impls(
|
||||
const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& impls) {
|
||||
void set_impls(const ImplExpMap& impls) {
|
||||
CHECK(impls_.empty());
|
||||
impls_ = impls;
|
||||
}
|
||||
@@ -407,7 +405,7 @@ class CallExpression : public Expression {
|
||||
private:
|
||||
Nonnull<Expression*> function_;
|
||||
Nonnull<Expression*> argument_;
|
||||
std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls_;
|
||||
ImplExpMap impls_;
|
||||
BindingMap deduced_args_;
|
||||
};
|
||||
|
||||
@@ -538,6 +536,35 @@ class IfExpression : public Expression {
|
||||
Nonnull<Expression*> else_expression_;
|
||||
};
|
||||
|
||||
// Instantiate a generic impl.
|
||||
class InstantiateImpl : public Expression {
|
||||
public:
|
||||
using ImplementsCarbonValueNode = void;
|
||||
|
||||
explicit InstantiateImpl(SourceLocation source_loc,
|
||||
Nonnull<Expression*> generic_impl,
|
||||
const BindingMap& type_args, const ImplExpMap& impls)
|
||||
: Expression(AstNodeKind::InstantiateImpl, source_loc),
|
||||
generic_impl_(generic_impl),
|
||||
type_args_(type_args),
|
||||
impls_(impls) {}
|
||||
|
||||
static auto classof(const AstNode* node) -> bool {
|
||||
return InheritsFromInstantiateImpl(node->kind());
|
||||
}
|
||||
auto generic_impl() const -> Nonnull<Expression*> { return generic_impl_; }
|
||||
auto type_args() const -> const BindingMap& { return type_args_; }
|
||||
|
||||
// Maps each of the impl bindings to an expression that constructs
|
||||
// the witness table for that impl.
|
||||
auto impls() const -> const ImplExpMap& { return impls_; }
|
||||
|
||||
private:
|
||||
Nonnull<Expression*> generic_impl_;
|
||||
BindingMap type_args_;
|
||||
ImplExpMap impls_;
|
||||
};
|
||||
|
||||
// An expression whose semantics have not been implemented. This can be used
|
||||
// as a placeholder during development, in order to implement and test parsing
|
||||
// of a new expression syntax without having to implement its semantics.
|
||||
|
||||
@@ -19,14 +19,13 @@ class Value;
|
||||
class Expression;
|
||||
class ImplBinding;
|
||||
|
||||
// The run-time counterpart of a `GenericBinding`.
|
||||
//
|
||||
// Once a generic binding has been declared, it can be used
|
||||
// in two different ways: as a compile-time constant with a
|
||||
// symbolic value (such as a `VariableType`), or as a run-time
|
||||
// variable with a concrete value that is stored on the stack.
|
||||
// An `ImplBinding` is used in contexts where the second
|
||||
// interpretation is intended.
|
||||
// `ImplBinding` plays the role of the parameter for passing witness
|
||||
// tables to a generic. However, unlike regular parameters
|
||||
// (`BindingPattern`) there is no explicit syntax that corresponds to
|
||||
// an `ImplBinding`, so they are not created during parsing. Instances
|
||||
// of `ImplBinding` are created during type checking, when processing
|
||||
// a type parameter (a `GenericBinding`), or an `is` requirement in
|
||||
// a `where` clause.
|
||||
class ImplBinding : public AstNode {
|
||||
public:
|
||||
using ImplementsCarbonValueNode = void;
|
||||
|
||||
Reference in New Issue
Block a user