Track the arguments and witnesses on values indirectly. (#1335)

This avoids the need to make copies of potentially large maps when
the same bindings are used in multiple values, such as when forming
a bound method value.
This commit is contained in:
Richard Smith
2022-06-21 08:48:48 -07:00
committed by GitHub
parent 8705e29d9f
commit a1be2a8a38
9 changed files with 203 additions and 108 deletions
+10
View File
@@ -89,6 +89,15 @@ cc_library(
],
)
cc_library(
name = "bindings",
srcs = ["bindings.cpp"],
hdrs = ["bindings.h"],
deps = [
"//explorer/common:nonnull",
],
)
cc_library(
name = "declaration",
srcs = ["declaration.cpp"],
@@ -153,6 +162,7 @@ cc_library(
hdrs = ["expression.h", "pattern.h"],
deps = [
":ast_node",
":bindings",
":member",
":paren_contents",
":static_scope",
+14
View File
@@ -0,0 +1,14 @@
// 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
#include "explorer/ast/bindings.h"
namespace Carbon {
auto Bindings::None() -> Nonnull<const Bindings*> {
static Nonnull<const Bindings*> bindings = new Bindings({}, {});
return bindings;
}
} // namespace Carbon
+57
View File
@@ -0,0 +1,57 @@
// 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
#ifndef CARBON_EXPLORER_AST_BINDINGS_H_
#define CARBON_EXPLORER_AST_BINDINGS_H_
#include <map>
#include "explorer/common/nonnull.h"
namespace Carbon {
class ImplBinding;
class GenericBinding;
class Value;
using BindingMap =
std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
using ImplWitnessMap =
std::map<Nonnull<const ImplBinding*>, Nonnull<const Value*>>;
// A set of evaluated bindings in some context, such as a function or class.
//
// These are shared by a context and all unparameterized entities within that
// context. For example, a class and the name of a method within that class
// will have the same set of bindings.
class Bindings {
public:
// Create an instantiated set of bindings for use during evaluation,
// containing both arguments and witnesses.
Bindings(BindingMap args, ImplWitnessMap witnesses)
: args_(args), witnesses_(witnesses) {}
enum NoWitnessesTag { NoWitnesses };
// Create a set of bindings for use during type-checking, containing only the
// arguments but not the corresponding witnesses.
Bindings(BindingMap args, NoWitnessesTag) : args_(args), witnesses_() {}
// Argument values corresponding to generic bindings.
auto args() const -> const BindingMap& { return args_; }
// Witnesses corresponding to impl bindings.
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
// An empty set of bindings.
static auto None() -> Nonnull<const Bindings*>;
private:
BindingMap args_;
ImplWitnessMap witnesses_;
};
} // namespace Carbon
#endif // CARBON_EXPLORER_AST_BINDINGS_H_
+1 -5
View File
@@ -13,6 +13,7 @@
#include "common/ostream.h"
#include "explorer/ast/ast_node.h"
#include "explorer/ast/bindings.h"
#include "explorer/ast/member.h"
#include "explorer/ast/paren_contents.h"
#include "explorer/ast/static_scope.h"
@@ -510,11 +511,6 @@ class PrimitiveOperatorExpression : public Expression {
std::vector<Nonnull<Expression*>> arguments_;
};
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 {
+1
View File
@@ -24,6 +24,7 @@ cc_library(
":stack",
"//common:check",
"//common:ostream",
"//explorer/ast:bindings",
"//explorer/ast:declaration",
"//explorer/ast:expression",
"//explorer/ast:member",
+42 -21
View File
@@ -109,10 +109,16 @@ class Interpreter {
auto InstantiateType(Nonnull<const Value*> type, SourceLocation source_loc)
-> ErrorOr<Nonnull<const Value*>>;
// Instantiate a set of bindings by replacing all type variables that occur
// within it by the current values of those variables.
auto InstantiateBindings(Nonnull<const Bindings*> bindings,
SourceLocation source_loc)
-> ErrorOr<Nonnull<const Bindings*>>;
// Call the function `fun` with the given `arg` and the `witnesses`
// for the function's impl bindings.
auto CallFunction(const CallExpression& call, Nonnull<const Value*> fun,
Nonnull<const Value*> arg, const ImplWitnessMap& witnesses)
Nonnull<const Value*> arg, ImplWitnessMap&& witnesses)
-> ErrorOr<Success>;
void PrintState(llvm::raw_ostream& out);
@@ -464,26 +470,38 @@ auto Interpreter::InstantiateType(Nonnull<const Value*> type,
}
case Value::Kind::NominalClassType: {
const auto& class_type = cast<NominalClassType>(*type);
BindingMap inst_type_args;
for (const auto& [ty_var, ty_arg] : class_type.type_args()) {
CARBON_ASSIGN_OR_RETURN(inst_type_args[ty_var],
InstantiateType(ty_arg, source_loc));
}
ImplWitnessMap witnesses = class_type.witnesses();
for (auto& [bind, witness] : witnesses) {
if (auto* sym = dyn_cast<SymbolicWitness>(witness)) {
CARBON_ASSIGN_OR_RETURN(witness,
EvalExpRecursively(&sym->impl_expression()));
}
}
return arena_->New<NominalClassType>(&class_type.declaration(),
inst_type_args, witnesses);
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Bindings*> bindings,
InstantiateBindings(&class_type.bindings(), source_loc));
return arena_->New<NominalClassType>(&class_type.declaration(), bindings);
}
default:
return type;
}
}
auto Interpreter::InstantiateBindings(Nonnull<const Bindings*> bindings,
SourceLocation source_loc)
-> ErrorOr<Nonnull<const Bindings*>> {
BindingMap args = bindings->args();
for (auto& [var, arg] : args) {
CARBON_ASSIGN_OR_RETURN(arg, InstantiateType(arg, source_loc));
}
ImplWitnessMap witnesses = bindings->witnesses();
for (auto& [bind, witness] : witnesses) {
if (auto* sym = dyn_cast<SymbolicWitness>(witness)) {
CARBON_ASSIGN_OR_RETURN(witness,
EvalExpRecursively(&sym->impl_expression()));
}
}
if (args == bindings->args() && witnesses == bindings->witnesses()) {
return bindings;
}
return arena_->New<Bindings>(std::move(args), std::move(witnesses));
}
auto Interpreter::Convert(Nonnull<const Value*> value,
Nonnull<const Value*> destination_type,
SourceLocation source_loc)
@@ -609,8 +627,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
auto Interpreter::CallFunction(const CallExpression& call,
Nonnull<const Value*> fun,
Nonnull<const Value*> arg,
const ImplWitnessMap& witnesses)
-> ErrorOr<Success> {
ImplWitnessMap&& witnesses) -> ErrorOr<Success> {
if (trace_stream_) {
**trace_stream_ << "calling function: " << *fun << "\n";
}
@@ -703,13 +720,15 @@ auto Interpreter::CallFunction(const CallExpression& call,
CARBON_CHECK(PatternMatch(&name.params().value(), arg, call.source_loc(),
&params_scope, generic_args, trace_stream_,
this->arena_));
Nonnull<const Bindings*> bindings =
arena_->New<Bindings>(std::move(generic_args), std::move(witnesses));
switch (decl.kind()) {
case DeclarationKind::ClassDeclaration:
return todo_.FinishAction(arena_->New<NominalClassType>(
&cast<ClassDeclaration>(decl), generic_args, witnesses));
&cast<ClassDeclaration>(decl), bindings));
case DeclarationKind::InterfaceDeclaration:
return todo_.FinishAction(arena_->New<InterfaceType>(
&cast<InterfaceDeclaration>(decl), generic_args, witnesses));
&cast<InterfaceDeclaration>(decl), bindings));
default:
CARBON_FATAL() << "unknown kind of ParameterizedEntityName " << decl;
}
@@ -751,7 +770,9 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
++i;
}
return todo_.FinishAction(arena_->New<ImplWitness>(
&generic_witness->declaration(), inst_impl.type_args(), witnesses));
&generic_witness->declaration(),
arena_->New<Bindings>(inst_impl.type_args(),
std::move(witnesses))));
}
}
case ExpressionKind::IndexExpression: {
@@ -1000,7 +1021,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
}
}
return CallFunction(call, act.results()[0], act.results()[1],
witnesses);
std::move(witnesses));
} else if (act.pos() == 3 + int(num_impls)) {
if (act.results().size() < 3 + num_impls) {
// Control fell through without explicit return.
+18 -11
View File
@@ -448,14 +448,17 @@ auto TypeChecker::GetBuiltinInterfaceType(SourceLocation source_loc,
if (has_parameters != has_arguments) {
return bad_builtin();
}
BindingMap bindings;
BindingMap binding_args;
if (has_arguments) {
TupleValue args(interface.arguments);
if (!PatternMatch(&iface_decl->params().value()->value(), &args, source_loc,
std::nullopt, bindings, trace_stream_, this->arena_)) {
std::nullopt, binding_args, trace_stream_,
this->arena_)) {
return bad_builtin();
}
}
Nonnull<const Bindings*> bindings =
arena_->New<Bindings>(std::move(binding_args), Bindings::NoWitnesses);
return arena_->New<InterfaceType>(iface_decl, bindings);
}
@@ -875,12 +878,13 @@ class ConstraintTypeBuilder {
auto TypeChecker::Substitute(
const std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>& dict,
Nonnull<const Value*> type) const -> Nonnull<const Value*> {
auto SubstituteIntoBindingMap = [&](const BindingMap& map) -> BindingMap {
auto SubstituteIntoBindings =
[&](const Bindings& bindings) -> Nonnull<const Bindings*> {
BindingMap result;
for (const auto& [name, value] : map) {
for (const auto& [name, value] : bindings.args()) {
result[name] = Substitute(dict, value);
}
return result;
return arena_->New<Bindings>(std::move(result), Bindings::NoWitnesses);
};
switch (type->kind()) {
@@ -971,14 +975,14 @@ auto TypeChecker::Substitute(
Nonnull<const NominalClassType*> new_class_type =
arena_->New<NominalClassType>(
&class_type.declaration(),
SubstituteIntoBindingMap(class_type.type_args()));
SubstituteIntoBindings(class_type.bindings()));
return new_class_type;
}
case Value::Kind::InterfaceType: {
const auto& iface_type = cast<InterfaceType>(*type);
Nonnull<const InterfaceType*> new_iface_type = arena_->New<InterfaceType>(
&iface_type.declaration(),
SubstituteIntoBindingMap(iface_type.args()));
SubstituteIntoBindings(iface_type.bindings()));
return new_iface_type;
}
case Value::Kind::ConstraintType: {
@@ -1975,13 +1979,15 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
CARBON_RETURN_IF_ERROR(DeduceCallBindings(
call, &param_name.params().static_type(), generic_parameters,
/*deduced_bindings=*/llvm::None, impl_bindings, impl_scope));
Nonnull<const Bindings*> bindings =
arena_->New<Bindings>(call.deduced_args(), Bindings::NoWitnesses);
const Declaration& decl = param_name.declaration();
switch (decl.kind()) {
case DeclarationKind::ClassDeclaration: {
Nonnull<NominalClassType*> inst_class_type =
arena_->New<NominalClassType>(&cast<ClassDeclaration>(decl),
call.deduced_args());
bindings);
call.set_static_type(
arena_->New<TypeOfClassType>(inst_class_type));
call.set_value_category(ValueCategory::Let);
@@ -1990,7 +1996,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
case DeclarationKind::InterfaceDeclaration: {
Nonnull<InterfaceType*> inst_iface_type =
arena_->New<InterfaceType>(&cast<InterfaceDeclaration>(decl),
call.deduced_args());
bindings);
call.set_static_type(
arena_->New<TypeOfInterfaceType>(inst_iface_type));
call.set_value_category(ValueCategory::Let);
@@ -2892,8 +2898,9 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
// above and/or by any enclosing generic classes.
generic_args[binding] = *binding->symbolic_identity();
}
Nonnull<NominalClassType*> self_type =
arena_->New<NominalClassType>(class_decl, generic_args);
Nonnull<NominalClassType*> self_type = arena_->New<NominalClassType>(
class_decl,
arena_->New<Bindings>(std::move(generic_args), Bindings::NoWitnesses));
SetConstantValue(self, self_type);
self->set_static_type(arena_->New<TypeOfClassType>(self_type));
+5 -10
View File
@@ -46,14 +46,12 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
if (fun_decl.is_method()) {
return arena->New<BoundMethodValue>(&fun_decl, v,
impl_witness->type_args(),
impl_witness->witnesses());
&impl_witness->bindings());
} else {
// Class function.
auto* fun = cast<FunctionValue>(*fun_decl.constant_value());
return arena->New<FunctionValue>(&fun->declaration(),
impl_witness->type_args(),
impl_witness->witnesses());
&impl_witness->bindings());
}
} else {
return CompilationError(source_loc)
@@ -101,13 +99,11 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
// Found a method. Turn it into a bound method.
const FunctionValue& m = cast<FunctionValue>(**func);
return arena->New<BoundMethodValue>(&m.declaration(), me_value,
class_type.type_args(),
class_type.witnesses());
&class_type.bindings());
} else {
// Found a class function
return arena->New<FunctionValue>(&(*func)->declaration(),
class_type.type_args(),
class_type.witnesses());
&class_type.bindings());
}
}
}
@@ -129,8 +125,7 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
<< "class function " << f << " not in " << *v;
}
return arena->New<FunctionValue>(&(*fun)->declaration(),
class_type.type_args(),
class_type.witnesses());
&class_type.bindings());
}
default:
CARBON_FATAL() << "field access not allowed for value " << *v;
+55 -61
View File
@@ -11,6 +11,7 @@
#include <vector>
#include "common/ostream.h"
#include "explorer/ast/bindings.h"
#include "explorer/ast/declaration.h"
#include "explorer/ast/member.h"
#include "explorer/ast/statement.h"
@@ -127,9 +128,6 @@ class IntValue : public Value {
int value_;
};
using ImplWitnessMap =
std::map<Nonnull<const ImplBinding*>, Nonnull<const Value*>>;
// A function value.
class FunctionValue : public Value {
public:
@@ -137,12 +135,10 @@ class FunctionValue : public Value {
: Value(Kind::FunctionValue), declaration_(declaration) {}
explicit FunctionValue(Nonnull<const FunctionDeclaration*> declaration,
const BindingMap& type_args,
const ImplWitnessMap& wits)
Nonnull<const Bindings*> bindings)
: Value(Kind::FunctionValue),
declaration_(declaration),
type_args_(type_args),
witnesses_(wits) {}
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::FunctionValue;
@@ -152,14 +148,17 @@ class FunctionValue : public Value {
return *declaration_;
}
auto type_args() const -> const BindingMap& { return type_args_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
auto type_args() const -> const BindingMap& { return bindings_->args(); }
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
private:
Nonnull<const FunctionDeclaration*> declaration_;
BindingMap type_args_;
ImplWitnessMap witnesses_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// A bound method value. It includes the receiver object.
@@ -173,13 +172,11 @@ class BoundMethodValue : public Value {
explicit BoundMethodValue(Nonnull<const FunctionDeclaration*> declaration,
Nonnull<const Value*> receiver,
const BindingMap& type_args,
const ImplWitnessMap& wits)
Nonnull<const Bindings*> bindings)
: Value(Kind::BoundMethodValue),
declaration_(declaration),
receiver_(receiver),
type_args_(type_args),
witnesses_(wits) {}
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::BoundMethodValue;
@@ -191,15 +188,18 @@ class BoundMethodValue : public Value {
auto receiver() const -> Nonnull<const Value*> { return receiver_; }
auto type_args() const -> const BindingMap& { return type_args_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
auto type_args() const -> const BindingMap& { return bindings_->args(); }
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
private:
Nonnull<const FunctionDeclaration*> declaration_;
Nonnull<const Value*> receiver_;
BindingMap type_args_;
ImplWitnessMap witnesses_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// The value of a location in memory.
@@ -549,40 +549,36 @@ class NominalClassType : public Value {
<< "missing arguments for parameterized class type";
}
// Construct a class type that represents the result of applying the
// given generic class to the `type_args`.
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration,
const BindingMap& type_args)
: Value(Kind::NominalClassType),
declaration_(declaration),
type_args_(type_args) {}
// Construct a fully instantiated generic class type to represent the
// run-time type of an object.
explicit NominalClassType(Nonnull<const ClassDeclaration*> declaration,
const BindingMap& type_args,
const ImplWitnessMap& wits)
Nonnull<const Bindings*> bindings)
: Value(Kind::NominalClassType),
declaration_(declaration),
type_args_(type_args),
witnesses_(wits) {}
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::NominalClassType;
}
auto declaration() const -> const ClassDeclaration& { return *declaration_; }
auto type_args() const -> const BindingMap& { return type_args_; }
// Maps each of the class's impl bindings to the witness table
// for the corresponding argument. Should only be called on a fully
// instantiated runtime type of a generic class.
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto type_args() const -> const BindingMap& { return bindings_->args(); }
// Witnesses for each of the class's impl bindings. These will not in general
// be set for class types that are only intended to be used within
// type-checking and not at runtime, such as in the static_type() of an
// expression or the type in a TypeOfClassType.
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
// Returns whether this a parameterized class. That is, a class with
// parameters and no corresponding arguments.
auto IsParameterized() const -> bool {
return declaration_->type_params().has_value() && type_args_.empty();
return declaration_->type_params().has_value() && type_args().empty();
}
// Returns the value of the function named `name` in this class, or
@@ -592,8 +588,7 @@ class NominalClassType : public Value {
private:
Nonnull<const ClassDeclaration*> declaration_;
BindingMap type_args_;
ImplWitnessMap witnesses_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// Return the declaration of the member with the given name.
@@ -602,7 +597,6 @@ auto FindMember(std::string_view name,
-> std::optional<Nonnull<const Declaration*>>;
// An interface type.
// TODO: Consider removing this once ConstraintType is ready.
class InterfaceType : public Value {
public:
explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration)
@@ -611,14 +605,10 @@ class InterfaceType : public Value {
<< "missing arguments for parameterized interface type";
}
explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration,
const BindingMap& args)
: Value(Kind::InterfaceType), declaration_(declaration), args_(args) {}
explicit InterfaceType(Nonnull<const InterfaceDeclaration*> declaration,
const BindingMap& args, const ImplWitnessMap& wits)
Nonnull<const Bindings*> bindings)
: Value(Kind::InterfaceType),
declaration_(declaration),
args_(args),
witnesses_(wits) {}
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::InterfaceType;
@@ -627,15 +617,18 @@ class InterfaceType : public Value {
auto declaration() const -> const InterfaceDeclaration& {
return *declaration_;
}
auto args() const -> const BindingMap& { return args_; }
// TODO: These aren't used for anything yet.
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto args() const -> const BindingMap& { return bindings_->args(); }
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
private:
Nonnull<const InterfaceDeclaration*> declaration_;
BindingMap args_;
ImplWitnessMap witnesses_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// A type-of-type for an unknown constrained type.
@@ -733,26 +726,27 @@ class ImplWitness : public Witness {
// Construct an instantiated generic impl.
explicit ImplWitness(Nonnull<const ImplDeclaration*> declaration,
const BindingMap& type_args, const ImplWitnessMap& wits)
Nonnull<const Bindings*> bindings)
: Witness(Kind::ImplWitness),
declaration_(declaration),
type_args_(type_args),
witnesses_(wits) {}
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::ImplWitness;
}
auto declaration() const -> const ImplDeclaration& { return *declaration_; }
auto type_args() const -> const BindingMap& { return type_args_; }
// Maps each of the impl's impl bindings to the witness table
// for the corresponding argument. Should only be called on a fully
// instantiated runtime type of a generic class.
auto witnesses() const -> const ImplWitnessMap& { return witnesses_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto type_args() const -> const BindingMap& { return bindings_->args(); }
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
private:
Nonnull<const ImplDeclaration*> declaration_;
BindingMap type_args_;
ImplWitnessMap witnesses_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// A witness table whose concrete value cannot be determined yet.