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:
Jeremy G. Siek
2022-04-20 10:18:01 -04:00
committed by GitHub
parent 958fb8e529
commit b784458aef
20 changed files with 893 additions and 386 deletions
+1
View File
@@ -56,3 +56,4 @@ abstract class Expression : AstNode;
class IfExpression : Expression;
class UnimplementedExpression : Expression;
class ArrayTypeLiteral : Expression;
class InstantiateImpl : Expression;
+21
View File
@@ -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();
}
+25
View File
@@ -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.
+6
View File
@@ -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;
}
+36 -9
View File
@@ -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.
+7 -8
View File
@@ -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;