mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
Explorer: Prevent copies when initializing a let binding from reference expression (#2946)
Prevent copies when initializing value expression from reference expression. This is based on https://github.com/carbon-language/carbon-lang/pull/2006, which introduces expression categories, and how it is possible to convert to/from those different categories. Continuation of https://github.com/carbon-language/carbon-lang/pull/2907 ## Functional changes * Initializing a value expression from a reference expression takes its value without a copy * Reading from the value expression causes an error if the value changed from the time it was initialized * In this situation, prevents a copy both for variable definitions, and call parameter bindings ## Main implementation changes * Add new `ExpressionCategoryAction`, which evaluates an expression and returns an `ExpressionValue` containing its category and address (if any), in addition to the resulting `Value*` * `ExpressionAction`s now invokes `ExpressionCategoryAction` and unwraps the returned `ExpressionValue` * `RuntimeScope::BindAndPin` method, and corresponding when attempting to read a `value_node`. ## Next work * Avoid unnecessary copies from value expression to value expression, after ensuring that even value expression temporaries are registered for destruction (https://github.com/Pixep/carbon-lang/pull/9)
This commit is contained in:
@@ -115,6 +115,7 @@ class ElementPath {
|
||||
// another Value, so its implementation details are tied to the implementation
|
||||
// details of Value.
|
||||
friend class Value;
|
||||
friend class Heap;
|
||||
std::vector<Component> components_;
|
||||
};
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ struct NestedValueVisitor {
|
||||
auto Visit(ValueNodeView) -> bool { return true; }
|
||||
auto Visit(int) -> bool { return true; }
|
||||
auto Visit(Address) -> bool { return true; }
|
||||
auto Visit(ExpressionCategory) -> bool { return true; }
|
||||
auto Visit(const std::string&) -> bool { return true; }
|
||||
auto Visit(Nonnull<const NominalClassValue**>) -> bool {
|
||||
// This is the pointer to the most-derived value within a class value,
|
||||
@@ -567,6 +568,10 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
case Value::Kind::LocationValue:
|
||||
out << "lval<" << cast<LocationValue>(*this).address() << ">";
|
||||
break;
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
out << "ref_expr<" << cast<ReferenceExpressionValue>(*this).address()
|
||||
<< ">";
|
||||
break;
|
||||
case Value::Kind::BoolType:
|
||||
out << "bool";
|
||||
break;
|
||||
@@ -997,6 +1002,7 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
|
||||
case Value::Kind::StringValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BindingPlaceholderValue:
|
||||
case Value::Kind::AddrValue:
|
||||
case Value::Kind::UninitializedValue:
|
||||
@@ -1148,6 +1154,7 @@ auto ValueStructurallyEqual(
|
||||
case Value::Kind::AlternativeConstructorValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::UninitializedValue:
|
||||
case Value::Kind::MemberName:
|
||||
// TODO: support pointer comparisons once we have a clearer distinction
|
||||
|
||||
+58
-32
@@ -96,38 +96,6 @@ class Value {
|
||||
const Kind kind_;
|
||||
};
|
||||
|
||||
// Contains the result of the evaluation of an expression, including a value,
|
||||
// the original expression category, and an optional address if available.
|
||||
class ExpressionResult {
|
||||
public:
|
||||
static auto Value(Nonnull<const Carbon::Value*> v) -> ExpressionResult {
|
||||
return ExpressionResult(v, std::nullopt, ExpressionCategory::Value);
|
||||
}
|
||||
static auto Reference(Nonnull<const Carbon::Value*> v, Address address)
|
||||
-> ExpressionResult {
|
||||
return ExpressionResult(v, std::move(address),
|
||||
ExpressionCategory::Reference);
|
||||
}
|
||||
static auto Initializing(Nonnull<const Carbon::Value*> v, Address address)
|
||||
-> ExpressionResult {
|
||||
return ExpressionResult(v, std::move(address),
|
||||
ExpressionCategory::Initializing);
|
||||
}
|
||||
|
||||
ExpressionResult(Nonnull<const Carbon::Value*> v,
|
||||
std::optional<Address> address, ExpressionCategory cat)
|
||||
: value_(v), address_(std::move(address)), expr_cat_(cat) {}
|
||||
|
||||
auto value() const -> Nonnull<const Carbon::Value*> { return value_; }
|
||||
auto address() const -> const std::optional<Address>& { return address_; }
|
||||
auto expression_category() const -> ExpressionCategory { return expr_cat_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Carbon::Value*> value_;
|
||||
std::optional<Address> address_;
|
||||
ExpressionCategory expr_cat_;
|
||||
};
|
||||
|
||||
// Returns whether the fully-resolved kind that this value will eventually have
|
||||
// is currently unknown, because it depends on a generic parameter.
|
||||
inline auto IsValueKindDependent(Nonnull<const Value*> type) -> bool {
|
||||
@@ -296,6 +264,64 @@ class LocationValue : public Value {
|
||||
Address value_;
|
||||
};
|
||||
|
||||
// Contains the result of the evaluation of an expression, including a value,
|
||||
// the original expression category, and an optional address if available.
|
||||
class ExpressionResult {
|
||||
public:
|
||||
static auto Value(Nonnull<const Carbon::Value*> v) -> ExpressionResult {
|
||||
return ExpressionResult(v, std::nullopt, ExpressionCategory::Value);
|
||||
}
|
||||
static auto Reference(Nonnull<const Carbon::Value*> v, Address address)
|
||||
-> ExpressionResult {
|
||||
return ExpressionResult(v, std::move(address),
|
||||
ExpressionCategory::Reference);
|
||||
}
|
||||
static auto Initializing(Nonnull<const Carbon::Value*> v, Address address)
|
||||
-> ExpressionResult {
|
||||
return ExpressionResult(v, std::move(address),
|
||||
ExpressionCategory::Initializing);
|
||||
}
|
||||
|
||||
ExpressionResult(Nonnull<const Carbon::Value*> v,
|
||||
std::optional<Address> address, ExpressionCategory cat)
|
||||
: value_(v), address_(std::move(address)), expr_cat_(cat) {}
|
||||
|
||||
auto value() const -> Nonnull<const Carbon::Value*> { return value_; }
|
||||
auto address() const -> const std::optional<Address>& { return address_; }
|
||||
auto expression_category() const -> ExpressionCategory { return expr_cat_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Carbon::Value*> value_;
|
||||
std::optional<Address> address_;
|
||||
ExpressionCategory expr_cat_;
|
||||
};
|
||||
|
||||
// Represents the result of the evaluation of a reference expression, and
|
||||
// holds the resulting `Value*` and its `Address`.
|
||||
class ReferenceExpressionValue : public Value {
|
||||
public:
|
||||
ReferenceExpressionValue(Nonnull<const Value*> value, Address address)
|
||||
: Value(Kind::ReferenceExpressionValue),
|
||||
value_(value),
|
||||
address_(std::move(address)) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::ReferenceExpressionValue;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
auto Decompose(F f) const {
|
||||
return f(value_, address_);
|
||||
}
|
||||
|
||||
auto value() const -> Nonnull<const Value*> { return value_; }
|
||||
auto address() const -> const Address& { return address_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Value*> value_;
|
||||
Address address_;
|
||||
};
|
||||
|
||||
// A pointer value
|
||||
class PointerValue : public Value {
|
||||
public:
|
||||
|
||||
@@ -16,6 +16,7 @@ CARBON_VALUE_KIND(DestructorValue)
|
||||
CARBON_VALUE_KIND(BoundMethodValue)
|
||||
CARBON_VALUE_KIND(PointerValue)
|
||||
CARBON_VALUE_KIND(LocationValue)
|
||||
CARBON_VALUE_KIND(ReferenceExpressionValue)
|
||||
CARBON_VALUE_KIND(BoolValue)
|
||||
CARBON_VALUE_KIND(StructValue)
|
||||
CARBON_VALUE_KIND(NominalClassValue)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define CARBON_EXPLORER_AST_VALUE_TRANSFORM_H_
|
||||
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/expression_category.h"
|
||||
#include "explorer/ast/value.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -238,6 +239,8 @@ class ValueTransform : public TransformBase<Derived, ResultUnwrapper> {
|
||||
|
||||
auto operator()(Address addr) -> Address { return addr; }
|
||||
|
||||
auto operator()(ExpressionCategory cat) -> ExpressionCategory { return cat; }
|
||||
|
||||
auto operator()(ValueNodeView value_node) -> ValueNodeView {
|
||||
return value_node;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ cc_library(
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
"//explorer/common:nonnull",
|
||||
"//explorer/common:source_location",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
@@ -80,6 +81,7 @@ cc_library(
|
||||
":action",
|
||||
":heap_allocation_interface",
|
||||
"//common:check",
|
||||
"//common:error",
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"//explorer/common:error_builders",
|
||||
@@ -93,9 +95,11 @@ cc_library(
|
||||
name = "heap_allocation_interface",
|
||||
hdrs = ["heap_allocation_interface.h"],
|
||||
deps = [
|
||||
"//common:error",
|
||||
"//explorer/ast",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:nonnull",
|
||||
"//explorer/common:source_location",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -254,6 +258,7 @@ cc_library(
|
||||
deps = [
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"//explorer/ast:expression_category",
|
||||
"//explorer/common:nonnull",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
#include "explorer/interpreter/stack.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@@ -25,12 +27,14 @@ using llvm::cast;
|
||||
|
||||
RuntimeScope::RuntimeScope(RuntimeScope&& other) noexcept
|
||||
: locals_(std::move(other.locals_)),
|
||||
bound_values_(std::move(other.bound_values_)),
|
||||
// To transfer ownership of other.allocations_, we have to empty it out.
|
||||
allocations_(std::exchange(other.allocations_, {})),
|
||||
heap_(other.heap_) {}
|
||||
|
||||
auto RuntimeScope::operator=(RuntimeScope&& rhs) noexcept -> RuntimeScope& {
|
||||
locals_ = std::move(rhs.locals_);
|
||||
bound_values_ = std::move(rhs.bound_values_);
|
||||
// To transfer ownership of rhs.allocations_, we have to empty it out.
|
||||
allocations_ = std::exchange(rhs.allocations_, {});
|
||||
heap_ = rhs.heap_;
|
||||
@@ -54,6 +58,13 @@ void RuntimeScope::Bind(ValueNodeView value_node, Address address) {
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
}
|
||||
|
||||
void RuntimeScope::BindAndPin(ValueNodeView value_node, Address address) {
|
||||
Bind(value_node, address);
|
||||
bool success = bound_values_.insert(&value_node.base()).second;
|
||||
CARBON_CHECK(success) << "Duplicate pinned node for " << value_node.base();
|
||||
heap_->BindValueToReference(value_node, address);
|
||||
}
|
||||
|
||||
void RuntimeScope::BindLifetimeToScope(Address address) {
|
||||
CARBON_CHECK(address.element_path_.IsEmpty())
|
||||
<< "Cannot extend lifetime of a specific sub-element";
|
||||
@@ -84,23 +95,35 @@ auto RuntimeScope::Initialize(ValueNodeView value_node,
|
||||
void RuntimeScope::Merge(RuntimeScope other) {
|
||||
CARBON_CHECK(heap_ == other.heap_);
|
||||
for (auto& element : other.locals_) {
|
||||
CARBON_CHECK(locals_.count(element.first) == 0)
|
||||
<< "Duplicate definition of" << element.first;
|
||||
locals_.insert(element);
|
||||
bool success = locals_.insert(element).second;
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << element.first;
|
||||
}
|
||||
for (const auto* element : other.bound_values_) {
|
||||
bool success = bound_values_.insert(element).second;
|
||||
CARBON_CHECK(success) << "Duplicate bound value.";
|
||||
}
|
||||
allocations_.insert(allocations_.end(), other.allocations_.begin(),
|
||||
other.allocations_.end());
|
||||
other.allocations_.clear();
|
||||
}
|
||||
|
||||
auto RuntimeScope::Get(ValueNodeView value_node) const
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
auto RuntimeScope::Get(ValueNodeView value_node,
|
||||
SourceLocation source_loc) const
|
||||
-> ErrorOr<std::optional<Nonnull<const Value*>>> {
|
||||
auto it = locals_.find(value_node);
|
||||
if (it != locals_.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
if (it == locals_.end()) {
|
||||
return {std::nullopt};
|
||||
}
|
||||
if (bound_values_.contains(&value_node.base())) {
|
||||
// Check if the bound value is still alive.
|
||||
CARBON_CHECK(it->second->kind() == Value::Kind::LocationValue);
|
||||
if (!heap_->is_bound_value_alive(
|
||||
value_node, cast<LocationValue>(it->second)->address())) {
|
||||
return ProgramError(source_loc)
|
||||
<< "Reference has changed since this value was bound.";
|
||||
}
|
||||
}
|
||||
return {it->second};
|
||||
}
|
||||
|
||||
auto RuntimeScope::Capture(
|
||||
@@ -122,6 +145,9 @@ void Action::Print(llvm::raw_ostream& out) const {
|
||||
case Action::Kind::LocationAction:
|
||||
out << cast<LocationAction>(*this).expression() << " ";
|
||||
break;
|
||||
case Action::Kind::ValueExpressionAction:
|
||||
out << cast<ValueExpressionAction>(*this).expression() << " ";
|
||||
break;
|
||||
case Action::Kind::ExpressionAction:
|
||||
out << cast<ExpressionAction>(*this).expression() << " ";
|
||||
break;
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
#include "explorer/ast/pattern.h"
|
||||
#include "explorer/ast/statement.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
#include "explorer/interpreter/dictionary.h"
|
||||
#include "explorer/interpreter/heap_allocation_interface.h"
|
||||
#include "explorer/interpreter/stack.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
@@ -61,6 +63,10 @@ class RuntimeScope {
|
||||
// allocating local storage.
|
||||
void Bind(ValueNodeView value_node, Address address);
|
||||
|
||||
// Binds location `address` of a reference value to `value_node` without
|
||||
// allocating local storage, and pins the value, making it immutable.
|
||||
void BindAndPin(ValueNodeView value_node, Address address);
|
||||
|
||||
// Binds unlocated `value` to `value_node` without allocating local storage.
|
||||
// TODO: BindValue should pin the lifetime of `value` and make sure it isn't
|
||||
// mutated.
|
||||
@@ -74,8 +80,8 @@ class RuntimeScope {
|
||||
// - its `LocationValue*` if bound to a reference expression in this scope,
|
||||
// - a `Value*` if bound to a value expression in this scope, or
|
||||
// - `nullptr` if not bound.
|
||||
auto Get(ValueNodeView value_node) const
|
||||
-> std::optional<Nonnull<const Value*>>;
|
||||
auto Get(ValueNodeView value_node, SourceLocation source_loc) const
|
||||
-> ErrorOr<std::optional<Nonnull<const Value*>>>;
|
||||
|
||||
// Returns the local values with allocation in created order.
|
||||
auto allocations() const -> const std::vector<AllocationId>& {
|
||||
@@ -86,6 +92,7 @@ class RuntimeScope {
|
||||
llvm::MapVector<ValueNodeView, Nonnull<const Value*>,
|
||||
std::map<ValueNodeView, unsigned>>
|
||||
locals_;
|
||||
llvm::DenseSet<const AstNode*> bound_values_;
|
||||
std::vector<AllocationId> allocations_;
|
||||
Nonnull<HeapAllocationInterface*> heap_;
|
||||
};
|
||||
@@ -106,6 +113,7 @@ class Action {
|
||||
public:
|
||||
enum class Kind {
|
||||
LocationAction,
|
||||
ValueExpressionAction,
|
||||
ExpressionAction,
|
||||
WitnessAction,
|
||||
StatementAction,
|
||||
@@ -165,7 +173,9 @@ class Action {
|
||||
scope_ = std::move(scope);
|
||||
}
|
||||
|
||||
auto source_loc() -> std::optional<SourceLocation> { return source_loc_; }
|
||||
auto source_loc() const -> std::optional<SourceLocation> {
|
||||
return source_loc_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Constructs an Action. `kind` must be the enumerator corresponding to the
|
||||
@@ -202,17 +212,17 @@ class LocationAction : public Action {
|
||||
};
|
||||
|
||||
// An Action which implements evaluation of an Expression to produce a `Value*`.
|
||||
class ExpressionAction : public Action {
|
||||
class ValueExpressionAction : public Action {
|
||||
public:
|
||||
explicit ExpressionAction(
|
||||
explicit ValueExpressionAction(
|
||||
Nonnull<const Expression*> expression,
|
||||
std::optional<AllocationId> initialized_location = std::nullopt)
|
||||
: Action(expression->source_loc(), Kind::ExpressionAction),
|
||||
: Action(expression->source_loc(), Kind::ValueExpressionAction),
|
||||
expression_(expression),
|
||||
location_received_(initialized_location) {}
|
||||
|
||||
static auto classof(const Action* action) -> bool {
|
||||
return action->kind() == Kind::ExpressionAction;
|
||||
return action->kind() == Kind::ValueExpressionAction;
|
||||
}
|
||||
|
||||
// The Expression this Action evaluates.
|
||||
@@ -228,6 +238,44 @@ class ExpressionAction : public Action {
|
||||
std::optional<AllocationId> location_received_;
|
||||
};
|
||||
|
||||
// An Action which implements evaluation of a reference Expression to produce an
|
||||
// `ReferenceExpressionValue*`. The `preserve_nested_categories` flag can be
|
||||
// used to preserve values as `ReferenceExpressionValue` in nested value types,
|
||||
// such as tuples.
|
||||
class ExpressionAction : public Action {
|
||||
public:
|
||||
ExpressionAction(
|
||||
Nonnull<const Expression*> expression, bool preserve_nested_categories,
|
||||
std::optional<AllocationId> initialized_location = std::nullopt)
|
||||
: Action(expression->source_loc(), Kind::ExpressionAction),
|
||||
expression_(expression),
|
||||
location_received_(initialized_location),
|
||||
preserve_nested_categories_(preserve_nested_categories) {}
|
||||
|
||||
static auto classof(const Action* action) -> bool {
|
||||
return action->kind() == Kind::ExpressionAction;
|
||||
}
|
||||
|
||||
// The Expression this Action evaluates.
|
||||
auto expression() const -> const Expression& { return *expression_; }
|
||||
|
||||
// Returns whether direct descendent actions should preserve values as
|
||||
// `ReferenceExpressionValue*`s.
|
||||
auto preserve_nested_categories() const -> bool {
|
||||
return preserve_nested_categories_;
|
||||
}
|
||||
|
||||
// The location provided for the initializing expression, if any.
|
||||
auto location_received() const -> std::optional<AllocationId> {
|
||||
return location_received_;
|
||||
}
|
||||
|
||||
private:
|
||||
Nonnull<const Expression*> expression_;
|
||||
std::optional<AllocationId> location_received_;
|
||||
bool preserve_nested_categories_;
|
||||
};
|
||||
|
||||
// An Action which implements the Instantiation of Type. The result is expressed
|
||||
// as a Value.
|
||||
class TypeInstantiationAction : public Action {
|
||||
@@ -334,8 +382,8 @@ class DeclarationAction : public Action {
|
||||
// An Action which implements destroying all local allocations in a scope.
|
||||
class CleanUpAction : public Action {
|
||||
public:
|
||||
explicit CleanUpAction(RuntimeScope scope)
|
||||
: Action(std::nullopt, Kind::CleanUpAction),
|
||||
explicit CleanUpAction(RuntimeScope scope, SourceLocation source_loc)
|
||||
: Action(source_loc, Kind::CleanUpAction),
|
||||
allocations_count_(scope.allocations().size()) {
|
||||
StartScope(std::move(scope));
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ auto ActionStack::ValueOfNode(ValueNodeView value_node,
|
||||
// with that node. This will help keep unwanted dynamic-scoping behavior
|
||||
// from sneaking in.
|
||||
if (action->scope().has_value()) {
|
||||
std::optional<Nonnull<const Value*>> result =
|
||||
action->scope()->Get(value_node);
|
||||
CARBON_ASSIGN_OR_RETURN(auto result,
|
||||
action->scope()->Get(value_node, source_loc));
|
||||
if (result.has_value()) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (globals_.has_value()) {
|
||||
std::optional<Nonnull<const Value*>> result = globals_->Get(value_node);
|
||||
CARBON_ASSIGN_OR_RETURN(auto result, globals_->Get(value_node, source_loc));
|
||||
if (result.has_value()) {
|
||||
return *result;
|
||||
}
|
||||
@@ -110,6 +110,7 @@ enum class FinishActionKind {
|
||||
|
||||
static auto FinishActionKindFor(Action::Kind kind) -> FinishActionKind {
|
||||
switch (kind) {
|
||||
case Action::Kind::ValueExpressionAction:
|
||||
case Action::Kind::ExpressionAction:
|
||||
case Action::Kind::WitnessAction:
|
||||
case Action::Kind::LocationAction:
|
||||
@@ -207,8 +208,8 @@ auto ActionStack::UnwindToWithCaptureScopesToDestroy(
|
||||
auto item = todo_.Pop();
|
||||
auto& scope = item->scope();
|
||||
if (scope && item->kind() != Action::Kind::CleanUpAction) {
|
||||
std::unique_ptr<Action> cleanup_action =
|
||||
std::make_unique<CleanUpAction>(std::move(*scope));
|
||||
std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
|
||||
std::move(*scope), ast_node->source_loc());
|
||||
scopes_to_destroy.push(std::move(cleanup_action));
|
||||
}
|
||||
}
|
||||
@@ -274,8 +275,8 @@ void ActionStack::PushCleanUpActions(
|
||||
while (!actions.empty()) {
|
||||
auto& act = actions.top();
|
||||
if (act->scope()) {
|
||||
std::unique_ptr<Action> cleanup_action =
|
||||
std::make_unique<CleanUpAction>(std::move(*act->scope()));
|
||||
std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
|
||||
std::move(*act->scope()), SourceLocation("stack cleanup", 1));
|
||||
todo_.Push(std::move(cleanup_action));
|
||||
}
|
||||
actions.pop();
|
||||
@@ -285,8 +286,8 @@ void ActionStack::PushCleanUpActions(
|
||||
void ActionStack::PushCleanUpAction(std::unique_ptr<Action> act) {
|
||||
auto& scope = act->scope();
|
||||
if (scope && act->kind() != Action::Kind::CleanUpAction) {
|
||||
std::unique_ptr<Action> cleanup_action =
|
||||
std::make_unique<CleanUpAction>(std::move(*scope));
|
||||
std::unique_ptr<Action> cleanup_action = std::make_unique<CleanUpAction>(
|
||||
std::move(*scope), SourceLocation("stack cleanup", 1));
|
||||
todo_.Push(std::move(cleanup_action));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#include "explorer/interpreter/heap.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
@@ -24,6 +26,7 @@ auto Heap::AllocateValue(Nonnull<const Value*> v) -> AllocationId {
|
||||
} else {
|
||||
states_.push_back(ValueState::Alive);
|
||||
}
|
||||
bound_values_.push_back(llvm::DenseMap<const AstNode*, Address>{});
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -49,13 +52,25 @@ auto Heap::Write(const Address& a, Nonnull<const Value*> v,
|
||||
CARBON_ASSIGN_OR_RETURN(values_[a.allocation_.index_],
|
||||
values_[a.allocation_.index_]->SetField(
|
||||
arena_, a.element_path_, v, source_loc));
|
||||
auto& bound_values_map = bound_values_[a.allocation_.index_];
|
||||
// End lifetime of all values bound to this address and its subobjects.
|
||||
if (a.element_path_.IsEmpty()) {
|
||||
bound_values_map.clear();
|
||||
} else {
|
||||
for (auto value_it = bound_values_map.begin();
|
||||
value_it != bound_values_map.end(); ++value_it) {
|
||||
if (AddressesAreStrictlyNested(a, value_it->second)) {
|
||||
bound_values_map.erase(value_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
auto Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) const
|
||||
-> ErrorOr<Success> {
|
||||
if (states_[allocation.index_] == ValueState::Dead ||
|
||||
states_[allocation.index_] == ValueState::Discarded) {
|
||||
const auto state = states_[allocation.index_];
|
||||
if (state == ValueState::Dead || state == ValueState::Discarded) {
|
||||
return ProgramError(source_loc)
|
||||
<< "undefined behavior: access to dead or discarded value "
|
||||
<< *values_[allocation.index_];
|
||||
@@ -73,16 +88,19 @@ auto Heap::CheckInit(AllocationId allocation, SourceLocation source_loc) const
|
||||
return Success();
|
||||
}
|
||||
|
||||
void Heap::Deallocate(AllocationId allocation) {
|
||||
auto Heap::Deallocate(AllocationId allocation) -> ErrorOr<Success> {
|
||||
if (states_[allocation.index_] != ValueState::Dead) {
|
||||
states_[allocation.index_] = ValueState::Dead;
|
||||
} else {
|
||||
CARBON_FATAL() << "deallocating an already dead value: "
|
||||
<< *values_[allocation.index_];
|
||||
}
|
||||
return Success();
|
||||
}
|
||||
|
||||
void Heap::Deallocate(const Address& a) { Deallocate(a.allocation_); }
|
||||
auto Heap::Deallocate(const Address& a) -> ErrorOr<Success> {
|
||||
return Deallocate(a.allocation_);
|
||||
}
|
||||
|
||||
auto Heap::is_initialized(AllocationId allocation) const -> bool {
|
||||
return states_[allocation.index_] != ValueState::Uninitialized;
|
||||
@@ -97,6 +115,16 @@ void Heap::Discard(AllocationId allocation) {
|
||||
states_[allocation.index_] = ValueState::Discarded;
|
||||
}
|
||||
|
||||
void Heap::BindValueToReference(const ValueNodeView& node, const Address& a) {
|
||||
// Update mapped node ignoring any previous mapping.
|
||||
bound_values_[a.allocation_.index_].insert({&node.base(), a});
|
||||
}
|
||||
|
||||
auto Heap::is_bound_value_alive(const ValueNodeView& node,
|
||||
const Address& a) const -> bool {
|
||||
return bound_values_[a.allocation_.index_].contains(&node.base());
|
||||
}
|
||||
|
||||
void Heap::Print(llvm::raw_ostream& out) const {
|
||||
llvm::ListSeparator sep;
|
||||
for (size_t i = 0; i < values_.size(); ++i) {
|
||||
@@ -111,4 +139,41 @@ void Heap::Print(llvm::raw_ostream& out) const {
|
||||
}
|
||||
}
|
||||
|
||||
auto Heap::AddressesAreStrictlyNested(const Address& first,
|
||||
const Address& second) -> bool {
|
||||
if (first.allocation_.index_ != second.allocation_.index_) {
|
||||
return false;
|
||||
}
|
||||
return PathsAreStrictlyNested(first.element_path_, second.element_path_);
|
||||
}
|
||||
|
||||
auto Heap::PathsAreStrictlyNested(const ElementPath& first,
|
||||
const ElementPath& second) -> bool {
|
||||
for (size_t i = 0;
|
||||
i < std::min(first.components_.size(), second.components_.size()); ++i) {
|
||||
Nonnull<const Element*> element = first.components_[i].element();
|
||||
Nonnull<const Element*> other_element = second.components_[i].element();
|
||||
if (element->kind() != other_element->kind()) {
|
||||
return false;
|
||||
}
|
||||
switch (element->kind()) {
|
||||
case Carbon::ElementKind::NamedElement:
|
||||
if (!element->IsNamed(
|
||||
llvm::cast<NamedElement>(other_element)->name())) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Carbon::ElementKind::PositionalElement:
|
||||
if (llvm::cast<PositionalElement>(element)->index() !=
|
||||
llvm::cast<PositionalElement>(other_element)->index()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Carbon::ElementKind::BaseElement:
|
||||
// Nothing to test.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/address.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/ast/value_node.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
#include "explorer/interpreter/heap_allocation_interface.h"
|
||||
@@ -35,20 +36,27 @@ class Heap : public HeapAllocationInterface {
|
||||
// Returns the value at the given address in the heap after
|
||||
// checking that it is alive.
|
||||
auto Read(const Address& a, SourceLocation source_loc) const
|
||||
-> ErrorOr<Nonnull<const Value*>>;
|
||||
-> ErrorOr<Nonnull<const Value*>> override;
|
||||
|
||||
// Writes the given value at the address in the heap after
|
||||
// checking that the address is alive.
|
||||
auto Write(const Address& a, Nonnull<const Value*> v,
|
||||
SourceLocation source_loc) -> ErrorOr<Success>;
|
||||
SourceLocation source_loc) -> ErrorOr<Success> override;
|
||||
|
||||
// Returns whether the value bound at the given node is still alive.
|
||||
auto is_bound_value_alive(const ValueNodeView& node, const Address& a) const
|
||||
-> bool override;
|
||||
|
||||
void BindValueToReference(const ValueNodeView& node,
|
||||
const Address& a) override;
|
||||
|
||||
// Put the given value on the heap and mark its state.
|
||||
// Mark UninitializedValue as uninitialized and other values as alive.
|
||||
auto AllocateValue(Nonnull<const Value*> v) -> AllocationId override;
|
||||
|
||||
// Marks this allocation, and all of its sub-objects, as dead.
|
||||
void Deallocate(AllocationId allocation) override;
|
||||
void Deallocate(const Address& a);
|
||||
auto Deallocate(AllocationId allocation) -> ErrorOr<Success> override;
|
||||
auto Deallocate(const Address& a) -> ErrorOr<Success>;
|
||||
|
||||
// Marks this allocation, and all its sub-objects, as discarded.
|
||||
void Discard(AllocationId allocation);
|
||||
@@ -65,6 +73,17 @@ class Heap : public HeapAllocationInterface {
|
||||
auto arena() const -> Arena& override { return *arena_; }
|
||||
|
||||
private:
|
||||
// Returns whether the address have the same AllocationdId and their path
|
||||
// are strictly nested.
|
||||
static auto AddressesAreStrictlyNested(const Address& first,
|
||||
const Address& second) -> bool;
|
||||
|
||||
// Returns whether the provided paths are strictly nested. This checks the
|
||||
// name, index, and base element only, and might not valid if used to
|
||||
// compare paths based on a different AllocationId.
|
||||
static auto PathsAreStrictlyNested(const ElementPath& first,
|
||||
const ElementPath& second) -> bool;
|
||||
|
||||
// Signal an error if the allocation is no longer alive.
|
||||
auto CheckAlive(AllocationId allocation, SourceLocation source_loc) const
|
||||
-> ErrorOr<Success>;
|
||||
@@ -76,6 +95,7 @@ class Heap : public HeapAllocationInterface {
|
||||
Nonnull<Arena*> arena_;
|
||||
std::vector<Nonnull<const Value*>> values_;
|
||||
std::vector<ValueState> states_;
|
||||
std::vector<llvm::DenseMap<const AstNode*, Address>> bound_values_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
#ifndef CARBON_EXPLORER_INTERPRETER_HEAP_ALLOCATION_INTERFACE_H_
|
||||
#define CARBON_EXPLORER_INTERPRETER_HEAP_ALLOCATION_INTERFACE_H_
|
||||
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/address.h"
|
||||
#include "explorer/ast/value_node.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "explorer/common/source_location.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -21,15 +24,33 @@ class HeapAllocationInterface {
|
||||
auto operator=(const HeapAllocationInterface&)
|
||||
-> HeapAllocationInterface& = delete;
|
||||
|
||||
// Returns the value at the given address in the heap after
|
||||
// checking that it is alive.
|
||||
virtual auto Read(const Address& a, SourceLocation source_loc) const
|
||||
-> ErrorOr<Nonnull<const Value*>> = 0;
|
||||
|
||||
// Writes the given value at the address in the heap after
|
||||
// checking that the address is alive.
|
||||
virtual auto Write(const Address& a, Nonnull<const Value*> v,
|
||||
SourceLocation source_loc) -> ErrorOr<Success> = 0;
|
||||
|
||||
// Put the given value on the heap and mark it as alive.
|
||||
virtual auto AllocateValue(Nonnull<const Value*> v) -> AllocationId = 0;
|
||||
|
||||
// Marks this allocation, and all of its sub-objects, as dead.
|
||||
virtual void Deallocate(AllocationId allocation) = 0;
|
||||
virtual auto Deallocate(AllocationId allocation) -> ErrorOr<Success> = 0;
|
||||
|
||||
// Returns the arena used to allocate the values in this heap.
|
||||
virtual auto arena() const -> Arena& = 0;
|
||||
|
||||
// Binds a value node to a reference, and manages its lifetime.
|
||||
virtual void BindValueToReference(const ValueNodeView& node,
|
||||
const Address& a) = 0;
|
||||
|
||||
// Returns whether the value bound at the given node is still alive.
|
||||
virtual auto is_bound_value_alive(const ValueNodeView& node,
|
||||
const Address& a) const -> bool = 0;
|
||||
|
||||
protected:
|
||||
HeapAllocationInterface() = default;
|
||||
virtual ~HeapAllocationInterface() = default;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/error.h"
|
||||
#include "explorer/ast/address.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
@@ -86,6 +87,8 @@ class Interpreter {
|
||||
private:
|
||||
auto Step() -> ErrorOr<Success>;
|
||||
|
||||
// State transitions for expressions value generation.
|
||||
auto StepValueExp() -> ErrorOr<Success>;
|
||||
// State transitions for expressions.
|
||||
auto StepExp() -> ErrorOr<Success>;
|
||||
// State transitions for lvalues.
|
||||
@@ -260,8 +263,13 @@ auto Interpreter::EvalPrim(Operator op, Nonnull<const Value*> /*static_type*/,
|
||||
cast<BoolValue>(*args[1]).value());
|
||||
case Operator::Ptr:
|
||||
return arena_->New<PointerType>(args[0]);
|
||||
case Operator::Deref:
|
||||
return heap_.Read(cast<PointerValue>(*args[0]).address(), source_loc);
|
||||
case Operator::Deref: {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
const auto* value,
|
||||
heap_.Read(cast<PointerValue>(*args[0]).address(), source_loc));
|
||||
return arena_->New<ReferenceExpressionValue>(
|
||||
value, cast<PointerValue>(*args[0]).address());
|
||||
}
|
||||
case Operator::AddressOf:
|
||||
return arena_->New<PointerValue>(cast<LocationValue>(*args[0]).address());
|
||||
case Operator::As:
|
||||
@@ -294,38 +302,39 @@ auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
|
||||
return arena_->New<StructValue>(std::move(elements));
|
||||
}
|
||||
|
||||
static auto InitializePlaceholderValue(
|
||||
const ValueNodeView& value_node, ExpressionResult v,
|
||||
std::optional<Nonnull<RuntimeScope*>> bindings) {
|
||||
static auto InitializePlaceholderValue(const ValueNodeView& value_node,
|
||||
ExpressionResult v,
|
||||
Nonnull<RuntimeScope*> bindings) {
|
||||
switch (value_node.expression_category()) {
|
||||
case ExpressionCategory::Reference:
|
||||
if (v.expression_category() == ExpressionCategory::Value ||
|
||||
v.expression_category() == ExpressionCategory::Reference) {
|
||||
// Build by copying from value or reference expression.
|
||||
(*bindings)->Initialize(value_node, v.value());
|
||||
bindings->Initialize(value_node, v.value());
|
||||
} else {
|
||||
// Location initialized by initializing expression, bind node to
|
||||
// address.
|
||||
CARBON_CHECK(v.address())
|
||||
<< "Missing location from initializing expression";
|
||||
(*bindings)->Bind(value_node, *v.address());
|
||||
bindings->Bind(value_node, *v.address());
|
||||
}
|
||||
break;
|
||||
case ExpressionCategory::Value:
|
||||
if (v.expression_category() == ExpressionCategory::Value) {
|
||||
// TODO: Ensure value expressions of temporaries are registered as
|
||||
// allocation to allow us to reference it without the need for a copy.
|
||||
(*bindings)->Initialize(value_node, v.value());
|
||||
bindings->Initialize(value_node, v.value());
|
||||
} else if (v.expression_category() == ExpressionCategory::Reference) {
|
||||
// TODO: Prevent mutation, error on mutation, or copy
|
||||
// Bind the reference expression value directly.
|
||||
(*bindings)->BindValue(value_node, v.value());
|
||||
CARBON_CHECK(v.address())
|
||||
<< "Missing location from reference expression";
|
||||
bindings->BindAndPin(value_node, *v.address());
|
||||
} else {
|
||||
// Location initialized by initializing expression, bind node to
|
||||
// address.
|
||||
CARBON_CHECK(v.address())
|
||||
<< "Missing location from initializing expression";
|
||||
(*bindings)->Bind(value_node, *v.address());
|
||||
bindings->Bind(value_node, *v.address());
|
||||
}
|
||||
break;
|
||||
case ExpressionCategory::Initializing:
|
||||
@@ -344,12 +353,23 @@ auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
|
||||
<< ExpressionCategoryToString(v.expression_category())
|
||||
<< " expression with value " << *v.value() << "\n";
|
||||
}
|
||||
const auto make_expr_result =
|
||||
[](Nonnull<const Value*> v) -> ExpressionResult {
|
||||
if (const auto* expr_v = dyn_cast<ReferenceExpressionValue>(v)) {
|
||||
return ExpressionResult::Reference(expr_v->value(), expr_v->address());
|
||||
}
|
||||
return ExpressionResult::Value(v);
|
||||
};
|
||||
if (v.value()->kind() == Value::Kind::ReferenceExpressionValue) {
|
||||
return PatternMatch(p, make_expr_result(v.value()), source_loc, bindings,
|
||||
generic_args, trace_stream, arena);
|
||||
}
|
||||
switch (p->kind()) {
|
||||
case Value::Kind::BindingPlaceholderValue: {
|
||||
CARBON_CHECK(bindings.has_value());
|
||||
const auto& placeholder = cast<BindingPlaceholderValue>(*p);
|
||||
if (placeholder.value_node().has_value()) {
|
||||
InitializePlaceholderValue(*placeholder.value_node(), v, bindings);
|
||||
InitializePlaceholderValue(*placeholder.value_node(), v, *bindings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -377,9 +397,8 @@ auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
|
||||
CARBON_CHECK(p_tup.elements().size() == v_tup.elements().size());
|
||||
for (size_t i = 0; i < p_tup.elements().size(); ++i) {
|
||||
if (!PatternMatch(p_tup.elements()[i],
|
||||
ExpressionResult::Value(v_tup.elements()[i]),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
make_expr_result(v_tup.elements()[i]), source_loc,
|
||||
bindings, generic_args, trace_stream, arena)) {
|
||||
return false;
|
||||
}
|
||||
} // for
|
||||
@@ -575,7 +594,7 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
|
||||
&cast<IndexExpression>(exp).object()));
|
||||
|
||||
} else if (act.pos() == 1) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<IndexExpression>(exp).offset()));
|
||||
} else {
|
||||
// { v :: [][i] :: C, E, F} :: S, H}
|
||||
@@ -598,7 +617,7 @@ auto Interpreter::StepLocation() -> ErrorOr<Success> {
|
||||
}
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(op.arguments()[0]));
|
||||
std::make_unique<ValueExpressionAction>(op.arguments()[0]));
|
||||
} else {
|
||||
const auto& res = cast<PointerValue>(*act.results()[0]);
|
||||
return todo_.FinishAction(arena_->New<LocationValue>(res.address()));
|
||||
@@ -1030,6 +1049,17 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
|
||||
// parameterized types for pointers in function call parameters.
|
||||
return value;
|
||||
}
|
||||
case Value::Kind::ReferenceExpressionValue: {
|
||||
const auto* expr_value = cast<ReferenceExpressionValue>(value);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> converted,
|
||||
Convert(expr_value->value(), destination_type, source_loc));
|
||||
if (converted == expr_value->value()) {
|
||||
return expr_value;
|
||||
} else {
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,25 +1171,25 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
} else {
|
||||
// Mutable self with `[addr self: Self*]`
|
||||
CARBON_CHECK(isa<AddrValue>(self_pattern));
|
||||
CARBON_CHECK(PatternMatch(
|
||||
bool success = PatternMatch(
|
||||
self_pattern, ExpressionResult::Value(method_val->receiver()),
|
||||
call.source_loc(), &function_scope, generic_args, trace_stream_,
|
||||
this->arena_));
|
||||
this->arena_);
|
||||
CARBON_CHECK(success) << "Failed to bind addr self";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Preserve expression category to allow appropriate binding in
|
||||
// `PatternMatch`.
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> converted_args,
|
||||
Convert(arg, &function.param_pattern().static_type(),
|
||||
call.source_loc()));
|
||||
|
||||
// Bind the arguments to the parameters.
|
||||
CARBON_CHECK(PatternMatch(&function.param_pattern().value(),
|
||||
ExpressionResult::Value(converted_args),
|
||||
call.source_loc(), &function_scope,
|
||||
generic_args, trace_stream_, this->arena_));
|
||||
bool success = PatternMatch(&function.param_pattern().value(),
|
||||
ExpressionResult::Value(converted_args),
|
||||
call.source_loc(), &function_scope,
|
||||
generic_args, trace_stream_, this->arena_);
|
||||
CARBON_CHECK(success) << "Failed to bind arguments to parameters";
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(*function.body(),
|
||||
location_received),
|
||||
std::move(function_scope));
|
||||
@@ -1299,6 +1329,25 @@ auto Interpreter::StepInstantiateType() -> ErrorOr<Success> {
|
||||
}
|
||||
}
|
||||
|
||||
auto Interpreter::StepValueExp() -> ErrorOr<Success> {
|
||||
auto& act = cast<ValueExpressionAction>(todo_.CurrentAction());
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&act.expression(), /*preserve_nested_categories=*/false,
|
||||
act.location_received()));
|
||||
} else {
|
||||
CARBON_CHECK(act.results().size() == 1);
|
||||
if (const auto* expr_value =
|
||||
dyn_cast<ReferenceExpressionValue>(act.results()[0])) {
|
||||
// Unwrap the ExpressionAction to only keep the resulting
|
||||
// `Value*`.
|
||||
return todo_.FinishAction(expr_value->value());
|
||||
} else {
|
||||
return todo_.FinishAction(act.results()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
auto& act = cast<ExpressionAction>(todo_.CurrentAction());
|
||||
const Expression& exp = act.expression();
|
||||
@@ -1311,10 +1360,10 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
if (act.pos() == 0) {
|
||||
// { { e[i] :: C, E, F} :: S, H}
|
||||
// -> { { e :: [][i] :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<IndexExpression>(exp).object()));
|
||||
} else if (act.pos() == 1) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<IndexExpression>(exp).offset()));
|
||||
} else {
|
||||
// { { v :: [][i] :: C, E, F} :: S, H}
|
||||
@@ -1340,8 +1389,12 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
// H}
|
||||
// -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
|
||||
// H}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
cast<TupleLiteral>(exp).fields()[act.pos()]));
|
||||
const auto* field = cast<TupleLiteral>(exp).fields()[act.pos()];
|
||||
if (act.preserve_nested_categories()) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(field, false));
|
||||
} else {
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(field));
|
||||
}
|
||||
} else {
|
||||
return todo_.FinishAction(arena_->New<TupleValue>(act.results()));
|
||||
}
|
||||
@@ -1349,7 +1402,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
case ExpressionKind::StructLiteral: {
|
||||
const auto& literal = cast<StructLiteral>(exp);
|
||||
if (act.pos() < static_cast<int>(literal.fields().size())) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&literal.fields()[act.pos()].expression()));
|
||||
} else {
|
||||
return todo_.FinishAction(
|
||||
@@ -1359,7 +1412,9 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
case ExpressionKind::SimpleMemberAccessExpression: {
|
||||
const auto& access = cast<SimpleMemberAccessExpression>(exp);
|
||||
if (auto rewrite = access.rewritten_form()) {
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
|
||||
*rewrite, act.preserve_nested_categories(),
|
||||
act.location_received()));
|
||||
}
|
||||
if (act.pos() == 0) {
|
||||
// First, evaluate the first operand.
|
||||
@@ -1367,8 +1422,8 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<LocationAction>(&access.object()));
|
||||
} else {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&access.object()));
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&access.object(), /*preserve_nested_categories=*/false));
|
||||
}
|
||||
} else {
|
||||
if (auto constant_value = access.constant_value()) {
|
||||
@@ -1393,9 +1448,14 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
found_in_interface = cast<InterfaceType>(act.results().back());
|
||||
}
|
||||
std::optional<const Value*> type_result;
|
||||
const auto* result =
|
||||
act.results()[0]->kind() ==
|
||||
Value::Kind::ReferenceExpressionValue
|
||||
? cast<ReferenceExpressionValue>(act.results()[0])->value()
|
||||
: act.results()[0];
|
||||
if (!isa<InterfaceType, NamedConstraintType, ConstraintType>(
|
||||
act.results()[0])) {
|
||||
type_result = act.results()[0];
|
||||
result)) {
|
||||
type_result = result;
|
||||
}
|
||||
MemberName* member_name = arena_->New<MemberName>(
|
||||
type_result, found_in_interface, member_name_type->member());
|
||||
@@ -1441,21 +1501,37 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
ElementPath::Component member(&access.member(), found_in_interface,
|
||||
witness);
|
||||
const Value* aggregate;
|
||||
const Value* me_value;
|
||||
std::optional<Address> lhs_address;
|
||||
if (access.is_type_access()) {
|
||||
aggregate = act.results().back();
|
||||
} else if (const auto* location =
|
||||
dyn_cast<LocationValue>(act.results()[0])) {
|
||||
lhs_address = location->address();
|
||||
me_value = act.results()[0];
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
aggregate,
|
||||
this->heap_.Read(location->address(), exp.source_loc()));
|
||||
} else if (const auto* expr_value =
|
||||
dyn_cast<ReferenceExpressionValue>(
|
||||
act.results()[0])) {
|
||||
lhs_address = expr_value->address();
|
||||
aggregate = expr_value->value();
|
||||
me_value = aggregate;
|
||||
} else {
|
||||
aggregate = act.results()[0];
|
||||
me_value = aggregate;
|
||||
}
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> member_value,
|
||||
aggregate->GetElement(arena_, ElementPath(member),
|
||||
exp.source_loc(), act.results()[0]));
|
||||
return todo_.FinishAction(member_value);
|
||||
exp.source_loc(), me_value));
|
||||
if (lhs_address) {
|
||||
return todo_.FinishAction(arena_->New<ReferenceExpressionValue>(
|
||||
member_value, lhs_address->ElementAddress(member.element())));
|
||||
} else {
|
||||
return todo_.FinishAction(member_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1470,7 +1546,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
std::make_unique<LocationAction>(&access.object()));
|
||||
} else {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&access.object()));
|
||||
std::make_unique<ValueExpressionAction>(&access.object()));
|
||||
}
|
||||
} else {
|
||||
if (auto constant_value = access.constant_value()) {
|
||||
@@ -1563,7 +1639,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
const auto& access = cast<BaseAccessExpression>(exp);
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&access.object()));
|
||||
std::make_unique<ValueExpressionAction>(&access.object()));
|
||||
} else {
|
||||
ElementPath::Component base_elt(&access.element(), std::nullopt,
|
||||
std::nullopt);
|
||||
@@ -1584,6 +1660,10 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
if (const auto* location = dyn_cast<LocationValue>(value)) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
value, heap_.Read(location->address(), exp.source_loc()));
|
||||
if (ident.expression_category() == ExpressionCategory::Reference) {
|
||||
return todo_.FinishAction(arena_->New<ReferenceExpressionValue>(
|
||||
value, location->address()));
|
||||
}
|
||||
}
|
||||
return todo_.FinishAction(value);
|
||||
}
|
||||
@@ -1605,7 +1685,9 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
case ExpressionKind::OperatorExpression: {
|
||||
const auto& op = cast<OperatorExpression>(exp);
|
||||
if (auto rewrite = op.rewritten_form()) {
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
|
||||
*rewrite, act.preserve_nested_categories(),
|
||||
act.location_received()));
|
||||
}
|
||||
if (act.pos() != static_cast<int>(op.arguments().size())) {
|
||||
// { {v :: op(vs,[],e,es) :: C, E, F} :: S, H}
|
||||
@@ -1624,7 +1706,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
// No short-circuit, fall through to evaluate 2nd operand.
|
||||
}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(arg));
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(arg));
|
||||
} else {
|
||||
// { {v :: op(vs,[]) :: C, E, F} :: S, H}
|
||||
// -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H}
|
||||
@@ -1641,12 +1723,15 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
// { {e1(e2) :: C, E, F} :: S, H}
|
||||
// -> { {e1 :: [](e2) :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&call.function()));
|
||||
std::make_unique<ValueExpressionAction>(&call.function()));
|
||||
} else if (act.pos() == 1) {
|
||||
// { { v :: [](e) :: C, E, F} :: S, H}
|
||||
// -> { { e :: v([]) :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&call.argument()));
|
||||
bool preserve_nested_categories =
|
||||
(act.results()[0]->kind() !=
|
||||
Value::Kind::AlternativeConstructorValue);
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&call.argument(), preserve_nested_categories));
|
||||
} else if (num_witnesses > 0 &&
|
||||
act.pos() < 2 + static_cast<int>(num_witnesses)) {
|
||||
auto iter = call.witnesses().begin();
|
||||
@@ -1675,17 +1760,19 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
act.results()[2 + static_cast<int>(num_witnesses)]);
|
||||
}
|
||||
} else {
|
||||
CARBON_FATAL() << "in StepExp with Call pos " << act.pos();
|
||||
CARBON_FATAL() << "in StepValueExp with Call pos " << act.pos();
|
||||
}
|
||||
}
|
||||
case ExpressionKind::IntrinsicExpression: {
|
||||
const auto& intrinsic = cast<IntrinsicExpression>(exp);
|
||||
if (auto rewrite = intrinsic.rewritten_form()) {
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
|
||||
*rewrite, act.preserve_nested_categories(),
|
||||
act.location_received()));
|
||||
}
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&intrinsic.args()));
|
||||
std::make_unique<ValueExpressionAction>(&intrinsic.args()));
|
||||
}
|
||||
// { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}
|
||||
const auto& args = cast<TupleValue>(*act.results()[0]).elements();
|
||||
@@ -1767,7 +1854,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
return todo_.Spawn(std::make_unique<DestroyAction>(
|
||||
arena_->New<LocationValue>(obj_addr), child_class_value));
|
||||
} else {
|
||||
heap_.Deallocate(obj_addr);
|
||||
CARBON_RETURN_IF_ERROR(heap_.Deallocate(obj_addr));
|
||||
return todo_.FinishAction(TupleValue::Empty());
|
||||
}
|
||||
} else {
|
||||
@@ -1775,7 +1862,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
return todo_.Spawn(std::make_unique<DestroyAction>(
|
||||
arena_->New<LocationValue>(ptr->address()), pointee));
|
||||
} else {
|
||||
heap_.Deallocate(ptr->address());
|
||||
CARBON_RETURN_IF_ERROR(heap_.Deallocate(ptr->address()));
|
||||
return todo_.FinishAction(TupleValue::Empty());
|
||||
}
|
||||
}
|
||||
@@ -1971,10 +2058,10 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
const auto& if_expr = cast<IfExpression>(exp);
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&if_expr.condition()));
|
||||
std::make_unique<ValueExpressionAction>(&if_expr.condition()));
|
||||
} else if (act.pos() == 1) {
|
||||
const auto& condition = cast<BoolValue>(*act.results()[0]);
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
condition.value() ? &if_expr.then_expression()
|
||||
: &if_expr.else_expression()));
|
||||
} else {
|
||||
@@ -1985,15 +2072,18 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
case ExpressionKind::WhereExpression: {
|
||||
auto rewrite = cast<WhereExpression>(exp).rewritten_form();
|
||||
CARBON_CHECK(rewrite) << "where expression should be rewritten";
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
|
||||
*rewrite, act.preserve_nested_categories(), act.location_received()));
|
||||
}
|
||||
case ExpressionKind::BuiltinConvertExpression: {
|
||||
const auto& convert_expr = cast<BuiltinConvertExpression>(exp);
|
||||
if (auto rewrite = convert_expr.rewritten_form()) {
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.ReplaceWith(std::make_unique<ExpressionAction>(
|
||||
*rewrite, act.preserve_nested_categories(),
|
||||
act.location_received()));
|
||||
}
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
convert_expr.source_expression()));
|
||||
} else if (act.pos() == 1) {
|
||||
return todo_.Spawn(std::make_unique<TypeInstantiationAction>(
|
||||
@@ -2096,7 +2186,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
// -> { { e :: (match ([]) ...) :: C, E, F} :: S, H}
|
||||
act.StartScope(RuntimeScope(&heap_));
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&match_stmt.expression()));
|
||||
std::make_unique<ValueExpressionAction>(&match_stmt.expression()));
|
||||
} else {
|
||||
int clause_num = act.pos() - 1;
|
||||
if (clause_num >= static_cast<int>(match_stmt.clauses().size())) {
|
||||
@@ -2129,8 +2219,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
const auto* loop_var = &cast<BindingPlaceholderValue>(
|
||||
cast<For>(stmt).variable_declaration().value());
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&cast<For>(stmt).loop_target()));
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<For>(stmt).loop_target()));
|
||||
}
|
||||
if (act.pos() == 1) {
|
||||
const auto* source_array =
|
||||
@@ -2185,8 +2275,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
// { { (while (e) s) :: C, E, F} :: S, H}
|
||||
// -> { { e :: (while ([]) s) :: C, E, F} :: S, H}
|
||||
act.Clear();
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&cast<While>(stmt).condition()));
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<While>(stmt).condition()));
|
||||
} else {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> condition,
|
||||
@@ -2256,7 +2346,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
todo_.MergeScope(std::move(scope));
|
||||
}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&definition.init(), init_location));
|
||||
&definition.init(), /*preserve_nested_categories=*/false,
|
||||
init_location));
|
||||
} else {
|
||||
// { { v :: (x = []) :: C, E, F} :: S, H}
|
||||
// -> { { C, E(x := a), F} :: S, H(a := copy(v))}
|
||||
@@ -2267,13 +2358,26 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
definition.has_init() ? definition.init().expression_category()
|
||||
: ExpressionCategory::Value;
|
||||
if (definition.has_init()) {
|
||||
if (has_initializing_expr && init_location &&
|
||||
heap_.is_initialized(*init_location)) {
|
||||
const auto address = Address(*init_location);
|
||||
Nonnull<const Value*> result = act.results()[0];
|
||||
std::optional<Nonnull<const ReferenceExpressionValue*>> v_expr =
|
||||
(result->kind() == Value::Kind::ReferenceExpressionValue)
|
||||
? std::optional{cast<ReferenceExpressionValue>(result)}
|
||||
: std::nullopt;
|
||||
const auto init_location = act.location_created();
|
||||
v = v_expr ? (*v_expr)->value() : result;
|
||||
if (expr_category == ExpressionCategory::Reference) {
|
||||
CARBON_CHECK(v_expr) << "Expecting ReferenceExpressionValue from "
|
||||
"reference expression";
|
||||
v_location = (*v_expr)->address();
|
||||
CARBON_CHECK(v_location)
|
||||
<< "Expecting a valid address from reference expression";
|
||||
} else if (has_initializing_expr && init_location &&
|
||||
heap_.is_initialized(*init_location)) {
|
||||
// Bind even if a conversion is necessary.
|
||||
v_location = Address(*init_location);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
v, heap_.Read(address, definition.source_loc()));
|
||||
CARBON_CHECK(v == act.results()[0]);
|
||||
v_location = address;
|
||||
result, heap_.Read(*v_location, definition.source_loc()));
|
||||
CARBON_CHECK(v == result);
|
||||
} else {
|
||||
// TODO: Prevent copies for Value expressions from Reference
|
||||
// expression, once able to prevent mutations.
|
||||
@@ -2283,8 +2387,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
}
|
||||
expr_category = ExpressionCategory::Value;
|
||||
const auto* dest_type = &definition.pattern().static_type();
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
v, Convert(act.results()[0], dest_type, stmt.source_loc()));
|
||||
CARBON_ASSIGN_OR_RETURN(v,
|
||||
Convert(v, dest_type, stmt.source_loc()));
|
||||
}
|
||||
} else {
|
||||
v = arena_->New<UninitializedValue>(p);
|
||||
@@ -2319,7 +2423,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
if (act.pos() == 0) {
|
||||
// { {e :: C, E, F} :: S, H}
|
||||
// -> { {e :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<ExpressionStatement>(stmt).expression()));
|
||||
} else {
|
||||
return todo_.FinishAction();
|
||||
@@ -2328,7 +2432,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
const auto& assign = cast<Assign>(stmt);
|
||||
if (auto rewrite = assign.rewritten_form()) {
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(*rewrite));
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(*rewrite));
|
||||
} else {
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
@@ -2340,7 +2444,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
} else if (act.pos() == 1) {
|
||||
// { { a :: ([] = e) :: C, E, F} :: S, H}
|
||||
// -> { { e :: (a = []) :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(&assign.rhs()));
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ValueExpressionAction>(&assign.rhs()));
|
||||
} else {
|
||||
// { { v :: (a = []) :: C, E, F} :: S, H}
|
||||
// -> { { C, E, F} :: S, H(a := v)}
|
||||
@@ -2358,7 +2463,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
const auto& inc_dec = cast<IncrementDecrement>(stmt);
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(*inc_dec.rewritten_form()));
|
||||
std::make_unique<ValueExpressionAction>(*inc_dec.rewritten_form()));
|
||||
} else {
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
@@ -2367,8 +2472,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
if (act.pos() == 0) {
|
||||
// { {(if (e) then_stmt else else_stmt) :: C, E, F} :: S, H}
|
||||
// -> { { e :: (if ([]) then_stmt else else_stmt) :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&cast<If>(stmt).condition()));
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<If>(stmt).condition()));
|
||||
} else if (act.pos() == 1) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> condition,
|
||||
@@ -2418,7 +2523,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
if (act.pos() == 0) {
|
||||
// { {return e :: C, E, F} :: S, H}
|
||||
// -> { {e :: return [] :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
return todo_.Spawn(std::make_unique<ValueExpressionAction>(
|
||||
&cast<ReturnExpression>(stmt).expression()));
|
||||
} else {
|
||||
// { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H}
|
||||
@@ -2453,7 +2558,7 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
|
||||
if (var_decl.has_initializer()) {
|
||||
if (act.pos() == 0) {
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&var_decl.initializer()));
|
||||
std::make_unique<ValueExpressionAction>(&var_decl.initializer()));
|
||||
} else {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> v,
|
||||
@@ -2583,8 +2688,7 @@ auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
|
||||
}
|
||||
if (act.pos() % 2 == 0) {
|
||||
auto* location = arena_->New<LocationValue>(Address(allocation));
|
||||
auto value =
|
||||
heap_.Read(location->address(), SourceLocation("destructor", 1));
|
||||
auto value = heap_.Read(location->address(), *cleanup.source_loc());
|
||||
// Step over uninitialized values.
|
||||
if (value.ok()) {
|
||||
return todo_.Spawn(std::make_unique<DestroyAction>(location, *value));
|
||||
@@ -2592,7 +2696,7 @@ auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
|
||||
return todo_.RunAgain();
|
||||
}
|
||||
} else {
|
||||
heap_.Deallocate(allocation);
|
||||
CARBON_RETURN_IF_ERROR(heap_.Deallocate(allocation));
|
||||
return todo_.RunAgain();
|
||||
}
|
||||
}
|
||||
@@ -2621,6 +2725,9 @@ auto Interpreter::Step() -> ErrorOr<Success> {
|
||||
case Action::Kind::LocationAction:
|
||||
CARBON_RETURN_IF_ERROR(StepLocation());
|
||||
break;
|
||||
case Action::Kind::ValueExpressionAction:
|
||||
CARBON_RETURN_IF_ERROR(StepValueExp());
|
||||
break;
|
||||
case Action::Kind::ExpressionAction:
|
||||
CARBON_RETURN_IF_ERROR(StepExp());
|
||||
break;
|
||||
@@ -2688,7 +2795,7 @@ auto InterpProgram(const AST& ast, Nonnull<Arena*> arena,
|
||||
CARBON_CHECK(ast.main_call);
|
||||
set_file_ctx.update_source_loc(ast.main_call.value()->source_loc());
|
||||
CARBON_RETURN_IF_ERROR(interpreter.RunAllSteps(
|
||||
std::make_unique<ExpressionAction>(*ast.main_call)));
|
||||
std::make_unique<ValueExpressionAction>(*ast.main_call)));
|
||||
|
||||
return cast<IntValue>(*interpreter.result()).value();
|
||||
}
|
||||
@@ -2700,7 +2807,7 @@ auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
|
||||
Interpreter interpreter(Phase::CompileTime, arena, trace_stream,
|
||||
print_stream);
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
interpreter.RunAllSteps(std::make_unique<ExpressionAction>(e)));
|
||||
interpreter.RunAllSteps(std::make_unique<ValueExpressionAction>(e)));
|
||||
return interpreter.result();
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::TupleValue:
|
||||
case Value::Kind::StructValue:
|
||||
@@ -151,6 +152,7 @@ static auto IsType(Nonnull<const Value*> value) -> bool {
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::TupleValue:
|
||||
case Value::Kind::StructValue:
|
||||
@@ -220,6 +222,7 @@ static auto ExpectCompleteType(SourceLocation source_loc,
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::TupleValue:
|
||||
@@ -332,6 +335,7 @@ static auto TypeIsDeduceable(Nonnull<const Value*> type) -> bool {
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::TupleValue:
|
||||
case Value::Kind::StructValue:
|
||||
@@ -1534,6 +1538,7 @@ auto TypeChecker::ArgumentDeduction::Deduce(Nonnull<const Value*> param,
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::TupleValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
@@ -6254,6 +6259,7 @@ static auto IsValidTypeForAliasTarget(Nonnull<const Value*> type) -> bool {
|
||||
case Value::Kind::BoundMethodValue:
|
||||
case Value::Kind::PointerValue:
|
||||
case Value::Kind::LocationValue:
|
||||
case Value::Kind::ReferenceExpressionValue:
|
||||
case Value::Kind::BoolValue:
|
||||
case Value::Kind::StructValue:
|
||||
case Value::Kind::NominalClassValue:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <limits>
|
||||
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression_category.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
|
||||
@@ -76,6 +77,7 @@ struct TypeStructureBuilder {
|
||||
// Ignore values that can't contain holes.
|
||||
void Visit(int) {}
|
||||
void Visit(std::string_view) {}
|
||||
void Visit(ExpressionCategory) {}
|
||||
void Visit(Nonnull<const AstNode*>) {}
|
||||
void Visit(const ValueNodeView&) {}
|
||||
void Visit(const Address&) {}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var a: i32 = 1;
|
||||
let a_pinned: i32 = a;
|
||||
a = 2;
|
||||
// CHECK:STDERR: RUNTIME ERROR: fail_pinned_value_changed.carbon:[[@LINE+1]]: Reference has changed since this value was bound.
|
||||
Print("{0}", a_pinned);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
let original: i32 = 1;
|
||||
var a: i32 = original;
|
||||
let a_pinned: i32 = a;
|
||||
a = 2;
|
||||
a = original;
|
||||
// CHECK:STDERR: RUNTIME ERROR: fail_pinned_value_restored.carbon:[[@LINE+1]]: Reference has changed since this value was bound.
|
||||
Print("{0}", a_pinned);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
var i: i32;
|
||||
}
|
||||
|
||||
fn Foo(c: C) {
|
||||
Print("{0}", c.i);
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c: C = { .i = 1 };
|
||||
let pinned_c: C = c;
|
||||
c.i = 2;
|
||||
// CHECK:STDERR: RUNTIME ERROR: fail_pinned_value_subobject_changed.carbon:[[@LINE+1]]: Reference has changed since this value was bound.
|
||||
Foo(pinned_c);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: a: 2
|
||||
// CHECK:STDOUT: a_pinned2: 1
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var a: i32 = 1;
|
||||
let a_pinned: i32 = a;
|
||||
let a_pinned_copy: i32 = a_pinned;
|
||||
|
||||
// OK: Value unused after being mutated.
|
||||
a = 2;
|
||||
|
||||
Print("a: {0}", a);
|
||||
|
||||
// OK: Value was copied from `a_pinned`, and reflects previous value.
|
||||
// TODO: Avoid value->value copy when possible.
|
||||
Print("a_pinned2: {0}", a_pinned_copy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var a: i32 = 1;
|
||||
let a_pinned: i32 = a;
|
||||
let a_pinned2: i32 = a;
|
||||
// OK: Value unused after being mutated.
|
||||
a = 2;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: 1
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Foo(n: i32) {
|
||||
Print("{0}", n);
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var v: {.a: i32, .b: i32} = {.a = 1, .b = 2};
|
||||
let a: i32 = v.a;
|
||||
v.b = 3;
|
||||
Foo(a);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var a: i32 = 1;
|
||||
let a_pinned: i32 = a;
|
||||
// OK: Value unused after being mutated.
|
||||
a = 2;
|
||||
return 0;
|
||||
}
|
||||
@@ -6,8 +6,7 @@
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: Bind from c reference expression
|
||||
// CHECK:STDOUT: Binding scope end
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{}
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
|
||||
+1
-2
@@ -5,8 +5,7 @@
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: Initialize c from reference expression
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: ptr<Allocation(1)>
|
||||
// CHECK:STDOUT: Initialize c from reference expression from deferenced pointer
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: ptr<Allocation(1)>
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn FromReferenceExpressionDeref() {
|
||||
var c_var: C = {};
|
||||
var c_ref: C* = &c_var;
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize c from reference expression from deferenced pointer");
|
||||
let c: C = *c_ref;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromReferenceExpressionDeref();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{.d = D{}}
|
||||
// CHECK:STDOUT: Initialize d from reference expression from subobject
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{.d = D{}}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: d destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class D {
|
||||
destructor[self: Self] {
|
||||
Print("d destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
var d: D;
|
||||
}
|
||||
|
||||
fn FromReferenceExpressionDeref() {
|
||||
var c_var: C = {.d = {}};
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize d from reference expression from subobject");
|
||||
let d: D = c_var.d;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromReferenceExpressionDeref();
|
||||
return 0;
|
||||
}
|
||||
@@ -29,13 +29,21 @@
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory:{{ }}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: i32 .0. ## i32 .1.
|
||||
// CHECK:STDOUT: memory:{{ }}
|
||||
// CHECK:STDOUT: }
|
||||
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
interface TestInterface {}
|
||||
|
||||
// CHECK:STDOUT: --- step exp i32 .0. (full_trace.carbon:[[@LINE+49]]) --->
|
||||
// CHECK:STDOUT: --- step exp i32 .0. (full_trace.carbon:[[@LINE+53]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: i32 .1. {{[[][[]}}i32]]
|
||||
// CHECK:STDOUT: memory:{{ }}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory:{{ }}
|
||||
@@ -75,7 +83,7 @@ interface TestInterface {}
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step decl interface TestInterface .0. (full_trace.carbon:[[@LINE-42]]) --->
|
||||
// CHECK:STDOUT: --- step decl interface TestInterface .0. (full_trace.carbon:[[@LINE-46]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
@@ -86,7 +94,7 @@ interface TestInterface {}
|
||||
// CHECK:STDOUT: }
|
||||
fn Main() -> i32 {
|
||||
return 0;
|
||||
// CHECK:STDOUT: --- step decl fn Main .0. (full_trace.carbon:[[@LINE+82]]) --->
|
||||
// CHECK:STDOUT: --- step decl fn Main .0. (full_trace.carbon:[[@LINE+106]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
@@ -96,24 +104,36 @@ fn Main() -> i32 {
|
||||
// CHECK:STDOUT: stack:{{ }}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .0. ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp Main() .0. (<Main()>:0) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main .0. ## Main() .1.
|
||||
// CHECK:STDOUT: stack: Main .0. ## Main() .1. ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main .0. ## Main .1. ## Main() .1. ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp Main .0. (<Main()>:0) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .1. {{[[][[]}}fun<Main>]]
|
||||
// CHECK:STDOUT: stack: Main .1. {{[[][[]}}fun<Main>]] ## Main() .1. ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .1. {{[[][[]}}fun<Main>]] ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp Main() .1. (<Main()>:0) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: () .0. ## Main() .2. {{[[][[]}}fun<Main>]]
|
||||
// CHECK:STDOUT: stack: () .0. ## Main() .2. {{[[][[]}}fun<Main>]] ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp () .0. (<Main()>:0) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .2. {{[[][[]}}fun<Main>, ()]]
|
||||
// CHECK:STDOUT: stack: Main() .2. {{[[][[]}}fun<Main>, ()]] ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp Main() .2. (<Main()>:0) --->
|
||||
@@ -121,40 +141,52 @@ fn Main() -> i32 {
|
||||
// CHECK:STDOUT: match pattern ()
|
||||
// CHECK:STDOUT: from value expression with value ()
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: {return 0;} .0. ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {}
|
||||
// CHECK:STDOUT: stack: {return 0;} .0. ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step stmt {return 0;} .0. (full_trace.carbon:[[@LINE+44]]) --->
|
||||
// CHECK:STDOUT: --- step stmt {return 0;} .0. (full_trace.carbon:[[@LINE+56]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: return 0; .0. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {}
|
||||
// CHECK:STDOUT: stack: return 0; .0. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step stmt return 0; .0. (full_trace.carbon:[[@LINE-44]]) --->
|
||||
// CHECK:STDOUT: --- step stmt return 0; .0. (full_trace.carbon:[[@LINE-56]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: 0 .0. ## return 0; .1. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp 0 .0. (full_trace.carbon:[[@LINE-49]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: return 0; .1. {{[[][[]}}0]] ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {}
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step stmt return 0; .1. (full_trace.carbon:[[@LINE-54]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: clean up.0. {} ## clean up.0. {} ## Main() .3. {{[[][[]}}fun<Main>, (), 0]] {}
|
||||
// CHECK:STDOUT: stack: 0 .0. ## return 0; .1. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: clean up.0. {} ## Main() .3. {{[[][[]}}fun<Main>, (), 0]] {}
|
||||
// CHECK:STDOUT: stack: 0 .0. ## 0 .1. ## return 0; .1. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp 0 .0. (full_trace.carbon:[[@LINE-65]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: 0 .1. {{[[][[]}}0]] ## return 0; .1. ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .3. {{[[][[]}}fun<Main>, (), 0]] {}
|
||||
// CHECK:STDOUT: stack: return 0; .1. {{[[][[]}}0]] ## {return 0;} .1. {} ## .0. {} ## Main() .3. {{[[][[]}}fun<Main>, ()]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step stmt return 0; .1. (full_trace.carbon:[[@LINE-74]]) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: clean up.0. {} ## clean up.0. {} ## Main() .3. {{[[][[]}}fun<Main>, (), 0]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: clean up.0. {} ## Main() .3. {{[[][[]}}fun<Main>, (), 0]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .3. {{[[][[]}}fun<Main>, (), 0]] {} ## Main() .1.
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: --- step exp Main() .3. (<Main()>:0) --->
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: clean up.0. {}
|
||||
// CHECK:STDOUT: stack: clean up.0. {} ## Main() .1. {{[[][[]}}0]]
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
// CHECK:STDOUT: stack: Main() .1. {{[[][[]}}0]]
|
||||
// CHECK:STDOUT: memory: 0: Heap{}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: {
|
||||
|
||||
Reference in New Issue
Block a user