Skip to content

Commit

Permalink
Support for templated impl declarations (#2700)
Browse files Browse the repository at this point in the history
The strategy that we use for now to support template instantiation is to check the impl declaration as if it were a generic, but to defer all checking of the impl definition until we see a use in which all template parameters have arguments. At that point, we clone the impl definition and type-check the whole thing, with constant values set on the template parameters corresponding to the given arguments.

No caching of template instantiations is performed yet; each time we form a reference to a template instantiation, we instantiate it afresh. We also don't implement the name lookup rule from #949 yet; lookups during template instantiation look only in the actual type and not in the constraint.

Depends on #2699
  • Loading branch information
zygoloid authored Mar 22, 2023
1 parent 4ca2e81 commit e0c9076
Show file tree
Hide file tree
Showing 19 changed files with 491 additions and 64 deletions.
17 changes: 11 additions & 6 deletions common/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class [[nodiscard]] Error {
: location_(std::move(other.location_)),
message_(std::move(other.message_)) {}

Error& operator=(Error&& other) noexcept {
auto operator=(Error&& other) noexcept -> Error& {
location_ = std::move(other.location_);
message_ = std::move(other.message_);
return *this;
Expand Down Expand Up @@ -122,7 +122,7 @@ class [[nodiscard]] ErrorOr {
}

private:
// Either an error message or
// Either an error message or a value.
std::variant<Error, T> val_;
};

Expand Down Expand Up @@ -171,15 +171,20 @@ class ErrorBuilder {
#define CARBON_MAKE_UNIQUE_NAME_IMPL(a, b, c) a##b##c
#define CARBON_MAKE_UNIQUE_NAME(a, b, c) CARBON_MAKE_UNIQUE_NAME_IMPL(a, b, c)

// Macro to prevent a top-level comma from being interpreted as a macro
// argument separator.
#define CARBON_PROTECT_COMMAS(...) __VA_ARGS__

#define CARBON_RETURN_IF_ERROR_IMPL(unique_name, expr) \
if (auto unique_name = (expr); /* NOLINT(bugprone-macro-parentheses) */ \
!(unique_name).ok()) { \
return std::move(unique_name).error(); \
}

#define CARBON_RETURN_IF_ERROR(expr) \
CARBON_RETURN_IF_ERROR_IMPL( \
CARBON_MAKE_UNIQUE_NAME(_llvm_error_line, __LINE__, __COUNTER__), expr)
#define CARBON_RETURN_IF_ERROR(expr) \
CARBON_RETURN_IF_ERROR_IMPL( \
CARBON_MAKE_UNIQUE_NAME(_llvm_error_line, __LINE__, __COUNTER__), \
CARBON_PROTECT_COMMAS(expr))

#define CARBON_ASSIGN_OR_RETURN_IMPL(unique_name, var, expr) \
auto unique_name = (expr); /* NOLINT(bugprone-macro-parentheses) */ \
Expand All @@ -191,6 +196,6 @@ class ErrorBuilder {
#define CARBON_ASSIGN_OR_RETURN(var, expr) \
CARBON_ASSIGN_OR_RETURN_IMPL( \
CARBON_MAKE_UNIQUE_NAME(_llvm_expected_line, __LINE__, __COUNTER__), \
var, expr)
CARBON_PROTECT_COMMAS(var), CARBON_PROTECT_COMMAS(expr))

#endif // CARBON_COMMON_ERROR_H_
5 changes: 5 additions & 0 deletions common/fuzzing/carbon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,13 @@ message BindingPattern {
}

message GenericBinding {
enum Kind {
Checked = 0;
Template = 1;
}
optional string name = 1;
optional Expression type = 2;
optional Kind kind = 3;
}

message TuplePattern {
Expand Down
7 changes: 7 additions & 0 deletions common/fuzzing/proto_to_carbon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ static auto BindingPatternToCarbon(const Fuzzing::BindingPattern& pattern,

static auto GenericBindingToCarbon(
const Fuzzing::GenericBinding& generic_binding, llvm::raw_ostream& out) {
switch (generic_binding.kind()) {
case Fuzzing::GenericBinding::Checked:
break;
case Fuzzing::GenericBinding::Template:
out << "template ";
break;
}
IdentifierToCarbon(generic_binding.name(), out);
out << ":! ";
ExpressionToCarbon(generic_binding.type(), out);
Expand Down
3 changes: 2 additions & 1 deletion explorer/ast/declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,8 @@ class ConstraintTypeDeclaration : public Declaration {
auto* self_type_ref = arena->New<IdentifierExpression>(
source_loc, std::string(name_.inner_name()));
self_type_ref->set_value_node(self_type_);
self_ = arena->New<GenericBinding>(source_loc, "Self", self_type_ref);
self_ = arena->New<GenericBinding>(source_loc, "Self", self_type_ref,
GenericBinding::BindingKind::Checked);
}

explicit ConstraintTypeDeclaration(CloneContext& context,
Expand Down
12 changes: 12 additions & 0 deletions explorer/ast/pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,17 @@ void Pattern::Print(llvm::raw_ostream& out) const {
}
case PatternKind::GenericBinding: {
const auto& binding = cast<GenericBinding>(*this);
switch (binding.binding_kind()) {
case GenericBinding::BindingKind::Checked:
break;
case GenericBinding::BindingKind::Template:
out << "template ";
break;
}
out << binding.name() << ":! " << binding.type();
if (auto value = binding.constant_value()) {
out << " = " << **value;
}
break;
}
case PatternKind::TuplePattern: {
Expand Down Expand Up @@ -177,6 +187,8 @@ GenericBinding::GenericBinding(CloneContext& context,
: Pattern(context, other),
name_(other.name_),
type_(context.Clone(other.type_)),
binding_kind_(other.binding_kind_),
template_value_(context.Clone(other.template_value_)),
symbolic_identity_(context.Clone(other.symbolic_identity_)),
impl_binding_(context.Clone(other.impl_binding_)),
original_(context.Remap(other.original_)),
Expand Down
28 changes: 24 additions & 4 deletions explorer/ast/pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,19 @@ class GenericBinding : public Pattern {
public:
using ImplementsCarbonValueNode = void;

GenericBinding(SourceLocation source_loc, std::string name,
Nonnull<Expression*> type)
enum class BindingKind {
// A checked generic binding, `T:! type`.
Checked,
// A template generic binding, `template T:! type`.
Template,
};

explicit GenericBinding(SourceLocation source_loc, std::string name,
Nonnull<Expression*> type, BindingKind binding_kind)
: Pattern(AstNodeKind::GenericBinding, source_loc),
name_(std::move(name)),
type_(type) {}
type_(type),
binding_kind_(binding_kind) {}

explicit GenericBinding(CloneContext& context, const GenericBinding& other);

Expand All @@ -285,6 +293,7 @@ class GenericBinding : public Pattern {
auto name() const -> const std::string& { return name_; }
auto type() const -> const Expression& { return *type_; }
auto type() -> Expression& { return *type_; }
auto binding_kind() const -> BindingKind { return binding_kind_; }

// The index of this binding, which is the number of bindings that are in
// scope at the point where this binding is declared.
Expand All @@ -299,7 +308,7 @@ class GenericBinding : public Pattern {
auto value_category() const -> ValueCategory { return ValueCategory::Let; }

auto constant_value() const -> std::optional<Nonnull<const Value*>> {
return std::nullopt;
return template_value_;
}

auto symbolic_identity() const -> std::optional<Nonnull<const Value*>> {
Expand All @@ -310,6 +319,15 @@ class GenericBinding : public Pattern {
symbolic_identity_ = value;
}

void set_template_value(Nonnull<const Value*> template_value) {
CARBON_CHECK(binding_kind_ == BindingKind::Template);
template_value_ = template_value;
}
auto has_template_value() const -> bool {
CARBON_CHECK(binding_kind_ == BindingKind::Template);
return template_value_.has_value();
}

// The impl binding associated with this type variable.
auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
return impl_binding_;
Expand Down Expand Up @@ -343,7 +361,9 @@ class GenericBinding : public Pattern {
private:
std::string name_;
Nonnull<Expression*> type_;
BindingKind binding_kind_;
std::optional<int> index_;
std::optional<Nonnull<const Value*>> template_value_;
std::optional<Nonnull<const Value*>> symbolic_identity_;
std::optional<Nonnull<const ImplBinding*>> impl_binding_;
std::optional<Nonnull<const GenericBinding*>> original_;
Expand Down
2 changes: 1 addition & 1 deletion explorer/data/prelude.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ __match_first {
// Pick up implicit conversions that are built into the compiler.
// TODO: Split these into individual categories and implement as many as we can
// in the prelude.
impl forall [U:! type, T:! __intrinsic_implicit_as(U)] T as ImplicitAs(U) {
impl forall [U:! type, template T:! __intrinsic_implicit_as(U)] T as ImplicitAs(U) {
fn Convert[self: Self]() -> U { return __intrinsic_implicit_as_convert(self, U); }
}

Expand Down
22 changes: 0 additions & 22 deletions explorer/fuzzing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -116,28 +116,6 @@ cc_test(
],
)

cc_test(
name = "clone_test",
srcs = ["clone_test.cpp"],
args = [
"$(locations //explorer:standard_libraries)",
"$(locations //explorer/testdata:carbon_files)",
],
data = [
"//explorer:standard_libraries",
"//explorer/testdata:carbon_files",
],
deps = [
":ast_to_proto_lib",
"//common/fuzzing:carbon_cc_proto",
"//explorer/ast",
"//explorer/syntax",
"@com_google_googletest//:gtest",
"@com_google_protobuf//:protobuf_headers",
"@llvm-project//llvm:Support",
],
)

cc_fuzz_test(
name = "explorer_fuzzer",
testonly = 1,
Expand Down
8 changes: 8 additions & 0 deletions explorer/fuzzing/ast_to_proto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@ static auto GenericBindingToProto(const GenericBinding& binding)
Fuzzing::GenericBinding binding_proto;
binding_proto.set_name(binding.name());
*binding_proto.mutable_type() = ExpressionToProto(binding.type());
switch (binding.binding_kind()) {
case GenericBinding::BindingKind::Checked:
binding_proto.set_kind(Fuzzing::GenericBinding::Checked);
break;
case GenericBinding::BindingKind::Template:
binding_proto.set_kind(Fuzzing::GenericBinding::Template);
break;
}
return binding_proto;
}

Expand Down
3 changes: 2 additions & 1 deletion explorer/interpreter/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1506,7 +1506,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
// it from here.
auto* self_binding = arena_->New<GenericBinding>(
exp.source_loc(), ".Self",
arena_->New<TypeTypeLiteral>(exp.source_loc()));
arena_->New<TypeTypeLiteral>(exp.source_loc()),
GenericBinding::BindingKind::Checked);
auto* self = arena_->New<VariableType>(self_binding);
auto* impl_binding = arena_->New<ImplBinding>(
exp.source_loc(), self_binding, std::nullopt);
Expand Down
Loading

0 comments on commit e0c9076

Please sign in to comment.