Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial support for constraints formed by combining interfaces #1307

Merged
merged 14 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/fuzzing/carbon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ message PrimitiveOperatorExpression {
Or = 9;
Sub = 10;
Ptr = 11;
Combine = 12;
}
optional Operator op = 1;
repeated Expression arguments = 2;
Expand Down
4 changes: 4 additions & 0 deletions common/fuzzing/proto_to_carbon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ static auto PrimitiveOperatorToCarbon(
case Fuzzing::PrimitiveOperatorExpression::Or:
BinaryOperatorToCarbon(arg0, " or ", arg1, out);
break;

case Fuzzing::PrimitiveOperatorExpression::Combine:
BinaryOperatorToCarbon(arg0, " & ", arg1, out);
break;
}
out << ")";
}
Expand Down
1 change: 1 addition & 0 deletions explorer/ast/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ auto ToString(Operator op) -> std::string_view {
case Operator::Add:
return "+";
case Operator::AddressOf:
case Operator::Combine:
return "&";
case Operator::Neg:
case Operator::Sub:
Expand Down
24 changes: 21 additions & 3 deletions explorer/ast/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Carbon {
class Value;
class MemberName;
class VariableType;
class InterfaceType;
class ImplBinding;

class Expression : public AstNode {
Expand Down Expand Up @@ -111,6 +112,7 @@ enum class Operator {
Add,
AddressOf,
And,
Combine,
Deref,
Eq,
Mul,
Expand Down Expand Up @@ -168,6 +170,7 @@ class SimpleMemberAccessExpression : public Expression {
auto object() const -> const Expression& { return *object_; }
auto object() -> Expression& { return *object_; }
auto member() const -> const std::string& { return member_; }

// Returns true if the field is a method that has a "me" declaration in an
// AddrPattern.
auto is_field_addr_me_method() const -> bool {
Expand All @@ -180,21 +183,36 @@ class SimpleMemberAccessExpression : public Expression {
// If `object` has a generic type, returns the `ImplBinding` that
// identifies its witness table. Otherwise, returns `std::nullopt`. Should not
// be called before typechecking.
auto impl() const -> std::optional<Nonnull<const ImplBinding*>> {
auto impl() const -> std::optional<Nonnull<const Expression*>> {
return impl_;
}

// Can only be called once, during typechecking.
void set_impl(Nonnull<const ImplBinding*> impl) {
void set_impl(Nonnull<const Expression*> impl) {
CARBON_CHECK(!impl_.has_value());
impl_ = impl;
}

// If `object` is a constrained type parameter and `member` was found in an
// interface, returns that interface. Should not be called before
// typechecking.
auto found_in_interface() const
-> std::optional<Nonnull<const InterfaceType*>> {
return found_in_interface_;
}

// Can only be called once, during typechecking.
void set_found_in_interface(Nonnull<const InterfaceType*> interface) {
CARBON_CHECK(!found_in_interface_.has_value());
found_in_interface_ = interface;
}

private:
Nonnull<Expression*> object_;
std::string member_;
std::optional<Nonnull<const ImplBinding*>> impl_;
bool is_field_addr_me_method_ = false;
std::optional<Nonnull<const Expression*>> impl_;
std::optional<Nonnull<const InterfaceType*>> found_in_interface_;
};

// A compound member access expression of the form `object.(path)`.
Expand Down
2 changes: 2 additions & 0 deletions explorer/fuzzing/ast_to_proto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ static auto OperatorToProtoEnum(const Operator op)
return Fuzzing::PrimitiveOperatorExpression::Or;
case Operator::Sub:
return Fuzzing::PrimitiveOperatorExpression::Sub;
case Operator::Combine:
return Fuzzing::PrimitiveOperatorExpression::Combine;
}
}

Expand Down
68 changes: 54 additions & 14 deletions explorer/interpreter/impl_scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,78 @@
#include "llvm/Support/Casting.h"

using llvm::cast;
using llvm::dyn_cast;

namespace Carbon {

void ImplScope::Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
Nonnull<Expression*> impl) {
Add(iface, {}, type, {}, impl);
Nonnull<Expression*> impl,
const TypeChecker& type_checker) {
Add(iface, {}, type, {}, impl, type_checker);
}

void ImplScope::Add(Nonnull<const Value*> iface,
llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
Nonnull<const Value*> type,
llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
Nonnull<Expression*> impl) {
impls_.push_back({.interface = iface,
Nonnull<Expression*> impl_expr,
const TypeChecker& type_checker) {
if (auto* constraint = dyn_cast<ConstraintType>(iface)) {
BindingMap map;
map[constraint->self_binding()] = type;
for (size_t i = 0; i != constraint->impl_constraints().size(); ++i) {
ConstraintType::ImplConstraint impl = constraint->impl_constraints()[i];
Add(cast<InterfaceType>(type_checker.Substitute(map, impl.interface)),
deduced, type_checker.Substitute(map, impl.type), impl_bindings,
type_checker.MakeConstraintWitnessAccess(impl_expr, i), type_checker);
}
return;
}

impls_.push_back({.interface = cast<InterfaceType>(iface),
.deduced = deduced,
.type = type,
.impl_bindings = impl_bindings,
.impl = impl});
.impl = impl_expr});
}

void ImplScope::AddParent(Nonnull<const ImplScope*> parent) {
parent_scopes_.push_back(parent);
}

auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
Nonnull<const Value*> type, SourceLocation source_loc,
auto ImplScope::Resolve(Nonnull<const Value*> constraint_type,
Nonnull<const Value*> impl_type,
SourceLocation source_loc,
const TypeChecker& type_checker) const
-> ErrorOr<Nonnull<Expression*>> {
if (const auto* iface_type = dyn_cast<InterfaceType>(constraint_type)) {
return ResolveInterface(iface_type, impl_type, source_loc, type_checker);
}
if (const auto* constraint = dyn_cast<ConstraintType>(constraint_type)) {
std::vector<Nonnull<Expression*>> witnesses;
BindingMap map;
map[constraint->self_binding()] = impl_type;
for (auto impl : constraint->impl_constraints()) {
CARBON_ASSIGN_OR_RETURN(
Nonnull<Expression*> result,
ResolveInterface(
cast<InterfaceType>(type_checker.Substitute(map, impl.interface)),
type_checker.Substitute(map, impl.type), source_loc,
type_checker));
witnesses.push_back(result);
}
// TODO: Check satisfaction of same-type constraints.
return type_checker.MakeConstraintWitness(*constraint, std::move(witnesses),
source_loc);
}
CARBON_FATAL() << "expected a constraint, not " << *constraint_type;
}

auto ImplScope::ResolveInterface(Nonnull<const InterfaceType*> iface_type,
Nonnull<const Value*> type,
SourceLocation source_loc,
const TypeChecker& type_checker) const
-> ErrorOr<Nonnull<Expression*>> {
CARBON_ASSIGN_OR_RETURN(
std::optional<Nonnull<Expression*>> result,
TryResolve(iface_type, type, source_loc, *this, type_checker));
Expand All @@ -48,7 +92,7 @@ auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
return *result;
}

auto ImplScope::TryResolve(Nonnull<const Value*> iface_type,
auto ImplScope::TryResolve(Nonnull<const InterfaceType*> iface_type,
Nonnull<const Value*> type,
SourceLocation source_loc,
const ImplScope& original_scope,
Expand All @@ -73,20 +117,16 @@ auto ImplScope::TryResolve(Nonnull<const Value*> iface_type,
return result;
}

auto ImplScope::ResolveHere(Nonnull<const Value*> iface_type,
auto ImplScope::ResolveHere(Nonnull<const InterfaceType*> iface_type,
Nonnull<const Value*> impl_type,
SourceLocation source_loc,
const ImplScope& original_scope,
const TypeChecker& type_checker) const
-> ErrorOr<std::optional<Nonnull<Expression*>>> {
if (iface_type->kind() != Value::Kind::InterfaceType) {
CARBON_FATAL() << "expected an interface, not " << *iface_type;
}
const auto& iface = cast<InterfaceType>(*iface_type);
std::optional<Nonnull<Expression*>> result = std::nullopt;
for (const Impl& impl : impls_) {
std::optional<Nonnull<Expression*>> m = type_checker.MatchImpl(
iface, impl_type, impl, original_scope, source_loc);
*iface_type, impl_type, impl, original_scope, source_loc);
if (m.has_value()) {
if (result.has_value()) {
return CompilationError(source_loc)
Expand Down
25 changes: 17 additions & 8 deletions explorer/interpreter/impl_scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,23 @@ class ImplScope {
public:
// Associates `iface` and `type` with the `impl` in this scope.
void Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
Nonnull<Expression*> impl);
Nonnull<Expression*> impl, const TypeChecker& type_checker);
// For a parameterized impl, associates `iface` and `type`
// with the `impl` in this scope.
void Add(Nonnull<const Value*> iface,
llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
Nonnull<const Value*> type,
llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings,
Nonnull<Expression*> impl);
Nonnull<Expression*> impl, const TypeChecker& type_checker);

// Make `parent` a parent of this scope.
// REQUIRES: `parent` is not already a parent of this scope.
void AddParent(Nonnull<const ImplScope*> parent);

// Returns the associated impl for the given `iface` and `type` in
// Returns the associated impl for the given `constraint` and `type` in
// the ancestor graph of this scope, or reports a compilation error
// at `source_loc` there isn't exactly one matching impl.
auto Resolve(Nonnull<const Value*> iface, Nonnull<const Value*> type,
auto Resolve(Nonnull<const Value*> constraint, Nonnull<const Value*> type,
SourceLocation source_loc, const TypeChecker& type_checker) const
-> ErrorOr<Nonnull<Expression*>>;

Expand All @@ -73,22 +73,31 @@ class ImplScope {
// are non-empty. The former contains the type parameters and the
// later are impl bindings, that is, parameters for witnesses.
struct Impl {
Nonnull<const Value*> interface;
Nonnull<const InterfaceType*> interface;
std::vector<Nonnull<const GenericBinding*>> deduced;
Nonnull<const Value*> type;
std::vector<Nonnull<const ImplBinding*>> impl_bindings;
Nonnull<Expression*> impl;
};

private:
// Returns the associated impl for the given `iface` and `type` in
// the ancestor graph of this scope, or reports a compilation error
// at `source_loc` there isn't exactly one matching impl.
auto ResolveInterface(Nonnull<const InterfaceType*> iface,
Nonnull<const Value*> type, SourceLocation source_loc,
const TypeChecker& type_checker) const
-> ErrorOr<Nonnull<Expression*>>;

// Returns the associated impl for the given `iface` and `type` in
// the ancestor graph of this scope, returns std::nullopt if there
// is none, or reports a compilation error is there is not a most
// specific impl for the given `iface` and `type`.
// Use `original_scope` to satisfy requirements of any generic impl
// that matches `iface` and `type`.
auto TryResolve(Nonnull<const Value*> iface, Nonnull<const Value*> type,
SourceLocation source_loc, const ImplScope& original_scope,
auto TryResolve(Nonnull<const InterfaceType*> iface_type,
Nonnull<const Value*> type, SourceLocation source_loc,
const ImplScope& original_scope,
const TypeChecker& type_checker) const
-> ErrorOr<std::optional<Nonnull<Expression*>>>;

Expand All @@ -98,7 +107,7 @@ class ImplScope {
// given `iface` and `type`.
// Use `original_scope` to satisfy requirements of any generic impl
// that matches `iface` and `type`.
auto ResolveHere(Nonnull<const Value*> iface_type,
auto ResolveHere(Nonnull<const InterfaceType*> iface_type,
Nonnull<const Value*> impl_type, SourceLocation source_loc,
const ImplScope& original_scope,
const TypeChecker& type_checker) const
Expand Down
Loading