mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
Explorer: Add initial initializing expression support for variable declaration (#2907)
Add partial support for initializing expressions for variable declaration. 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. ## Functional changes * Initializing expressions initialize directly the provided storage when used to initialize a variable. * Allows initializing expressions to avoid a copy when using `[var|let] name: type = call_expression(...)` by initializing `name` in-place. * Support `returned var: ...` and `return <expr>` * Support nested initializing expressions ## Main implementation changes * Updated PatternMatch logic to handle expression categories * Updated `VariableDefinition` interpreter statement to allocate and pass a location to initializing expressions * Update statement actions to allow passing an allocation, used by return expr or returned var * Modified the RuntimeScope API to be one step closer to the memory model we want to have * Remove `GetAllocationId` and older `Bind` which don't apply * New set of tests to highlight those different situations * Added a new intrinsic to print the allocation stack (and make sure we behave correctly, beyond visible side effects) ## Next work * Dedicated `Action` to retrieve expression category information in the interpreter (https://github.com/carbon-language/carbon-lang/pull/2927) * Avoid copies when initializing value expression from reference expression and prevent mutations for the duration of the "pinning" (https://github.com/carbon-language/carbon-lang/pull/2927) * Avoid unnecessary copies from value expression to value expression, after ensuring that even value expression temporaries are registered for destruction. * Avoid unnecessary copies when binding function arguments
This commit is contained in:
@@ -86,6 +86,7 @@ class Address {
|
||||
// the Heap, so its implementation details are tied to the implementation
|
||||
// details of the Heap.
|
||||
friend class Heap;
|
||||
friend class RuntimeScope;
|
||||
|
||||
AllocationId allocation_;
|
||||
ElementPath element_path_;
|
||||
|
||||
@@ -31,6 +31,7 @@ auto IntrinsicExpression::FindIntrinsic(std::string_view name,
|
||||
{{"print", Intrinsic::Print},
|
||||
{"new", Intrinsic::Alloc},
|
||||
{"delete", Intrinsic::Dealloc},
|
||||
{"print_allocs", Intrinsic::PrintAllocs},
|
||||
{"rand", Intrinsic::Rand},
|
||||
{"implicit_as", Intrinsic::ImplicitAs},
|
||||
{"implicit_as_convert", Intrinsic::ImplicitAsConvert},
|
||||
@@ -62,6 +63,8 @@ auto IntrinsicExpression::name() const -> std::string_view {
|
||||
return "__intrinsic_new";
|
||||
case IntrinsicExpression::Intrinsic::Dealloc:
|
||||
return "__intrinsic_delete";
|
||||
case IntrinsicExpression::Intrinsic::PrintAllocs:
|
||||
return "__intrinsic_print_allocs";
|
||||
case IntrinsicExpression::Intrinsic::Rand:
|
||||
return "__intrinsic_rand";
|
||||
case IntrinsicExpression::Intrinsic::ImplicitAs:
|
||||
|
||||
@@ -858,6 +858,7 @@ class IntrinsicExpression : public RewritableMixin<Expression> {
|
||||
Print,
|
||||
Alloc,
|
||||
Dealloc,
|
||||
PrintAllocs,
|
||||
Rand,
|
||||
ImplicitAs,
|
||||
ImplicitAsConvert,
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_
|
||||
#define CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_
|
||||
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// The category of a Carbon expression indicates whether it evaluates
|
||||
@@ -18,6 +20,8 @@ enum class ExpressionCategory {
|
||||
Initializing,
|
||||
};
|
||||
|
||||
auto ExpressionCategoryToString(ExpressionCategory cat) -> llvm::StringRef;
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_EXPLORER_AST_EXPRESSION_CATEGORY_H_
|
||||
|
||||
@@ -1318,4 +1318,15 @@ auto NominalClassType::InheritsClass(Nonnull<const Value*> other) const
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ExpressionCategoryToString(ExpressionCategory cat) -> llvm::StringRef {
|
||||
switch (cat) {
|
||||
case ExpressionCategory::Value:
|
||||
return "value";
|
||||
case ExpressionCategory::Reference:
|
||||
return "reference";
|
||||
case ExpressionCategory::Initializing:
|
||||
return "initializing";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
+43
-4
@@ -16,6 +16,7 @@
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/element_path.h"
|
||||
#include "explorer/ast/expression_category.h"
|
||||
#include "explorer/ast/statement.h"
|
||||
#include "explorer/common/nonnull.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
@@ -95,6 +96,38 @@ 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 {
|
||||
@@ -631,19 +664,22 @@ class FunctionType : public Value {
|
||||
|
||||
FunctionType(Nonnull<const Value*> parameters,
|
||||
Nonnull<const Value*> return_type)
|
||||
: FunctionType(parameters, {}, return_type, {}, {}) {}
|
||||
: FunctionType(parameters, {}, return_type, {}, {},
|
||||
/*is_initializing=*/false) {}
|
||||
|
||||
FunctionType(Nonnull<const Value*> parameters,
|
||||
std::vector<GenericParameter> generic_parameters,
|
||||
Nonnull<const Value*> return_type,
|
||||
std::vector<Nonnull<const GenericBinding*>> deduced_bindings,
|
||||
std::vector<Nonnull<const ImplBinding*>> impl_bindings)
|
||||
std::vector<Nonnull<const ImplBinding*>> impl_bindings,
|
||||
bool is_initializing)
|
||||
: Value(Kind::FunctionType),
|
||||
parameters_(parameters),
|
||||
generic_parameters_(std::move(generic_parameters)),
|
||||
return_type_(return_type),
|
||||
deduced_bindings_(std::move(deduced_bindings)),
|
||||
impl_bindings_(std::move(impl_bindings)) {}
|
||||
impl_bindings_(std::move(impl_bindings)),
|
||||
is_initializing_(is_initializing) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::FunctionType;
|
||||
@@ -652,7 +688,7 @@ class FunctionType : public Value {
|
||||
template <typename F>
|
||||
auto Decompose(F f) const {
|
||||
return f(parameters_, generic_parameters_, return_type_, deduced_bindings_,
|
||||
impl_bindings_);
|
||||
impl_bindings_, is_initializing_);
|
||||
}
|
||||
|
||||
// The type of the function parameter tuple.
|
||||
@@ -674,6 +710,8 @@ class FunctionType : public Value {
|
||||
auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
|
||||
return impl_bindings_;
|
||||
}
|
||||
// Return whether the function type is an initializing expression or not.
|
||||
auto is_initializing() const -> bool { return is_initializing_; }
|
||||
|
||||
private:
|
||||
Nonnull<const Value*> parameters_;
|
||||
@@ -681,6 +719,7 @@ class FunctionType : public Value {
|
||||
Nonnull<const Value*> return_type_;
|
||||
std::vector<Nonnull<const GenericBinding*>> deduced_bindings_;
|
||||
std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
|
||||
bool is_initializing_;
|
||||
};
|
||||
|
||||
// A pointer type.
|
||||
|
||||
@@ -707,6 +707,9 @@ class Heap {
|
||||
fn Delete[T:! type, self: Self](p : T*) {
|
||||
__intrinsic_delete(p);
|
||||
}
|
||||
fn PrintAllocs[self: Self]() {
|
||||
__intrinsic_print_allocs();
|
||||
}
|
||||
}
|
||||
|
||||
var heap: Heap = {};
|
||||
|
||||
@@ -79,6 +79,7 @@ cc_library(
|
||||
deps = [
|
||||
":action",
|
||||
":heap_allocation_interface",
|
||||
"//common:check",
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"//explorer/common:error_builders",
|
||||
@@ -115,6 +116,7 @@ cc_library(
|
||||
"//common:error",
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"//explorer/ast:expression_category",
|
||||
"//explorer/common:arena",
|
||||
"//explorer/common:error_builders",
|
||||
"//explorer/common:source_location",
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/interpreter/stack.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@@ -44,32 +46,39 @@ void RuntimeScope::Print(llvm::raw_ostream& out) const {
|
||||
out << "}";
|
||||
}
|
||||
|
||||
void RuntimeScope::Bind(ValueNodeView value_node, Nonnull<const Value*> value) {
|
||||
void RuntimeScope::Bind(ValueNodeView value_node, Address address) {
|
||||
CARBON_CHECK(!value_node.constant_value().has_value());
|
||||
CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
|
||||
auto allocation_id = heap_->GetAllocationId(value);
|
||||
if (!allocation_id) {
|
||||
auto id = heap_->AllocateValue(value);
|
||||
auto [it, success] = locals_.insert(
|
||||
{value_node, heap_->arena().New<LocationValue>(Address(id))});
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
} else {
|
||||
auto [it, success] = locals_.insert(
|
||||
{value_node,
|
||||
heap_->arena().New<LocationValue>(Address(*allocation_id))});
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
}
|
||||
bool success =
|
||||
locals_.insert({value_node, heap_->arena().New<LocationValue>(address)})
|
||||
.second;
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
}
|
||||
|
||||
void RuntimeScope::Initialize(ValueNodeView value_node,
|
||||
Nonnull<const Value*> value) {
|
||||
void RuntimeScope::BindLifetimeToScope(Address address) {
|
||||
CARBON_CHECK(address.element_path_.IsEmpty())
|
||||
<< "Cannot extend lifetime of a specific sub-element";
|
||||
allocations_.push_back(address.allocation_);
|
||||
}
|
||||
|
||||
void RuntimeScope::BindValue(ValueNodeView value_node,
|
||||
Nonnull<const Value*> value) {
|
||||
CARBON_CHECK(!value_node.constant_value().has_value());
|
||||
CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
|
||||
bool success = locals_.insert({value_node, value}).second;
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
}
|
||||
|
||||
auto RuntimeScope::Initialize(ValueNodeView value_node,
|
||||
Nonnull<const Value*> value)
|
||||
-> Nonnull<const LocationValue*> {
|
||||
CARBON_CHECK(!value_node.constant_value().has_value());
|
||||
CARBON_CHECK(value->kind() != Value::Kind::LocationValue);
|
||||
allocations_.push_back(heap_->AllocateValue(value));
|
||||
auto [it, success] = locals_.insert(
|
||||
{value_node,
|
||||
heap_->arena().New<LocationValue>(Address(allocations_.back()))});
|
||||
const auto* location =
|
||||
heap_->arena().New<LocationValue>(Address(allocations_.back()));
|
||||
bool success = locals_.insert({value_node, location}).second;
|
||||
CARBON_CHECK(success) << "Duplicate definition of " << value_node.base();
|
||||
return location;
|
||||
}
|
||||
|
||||
void RuntimeScope::Merge(RuntimeScope other) {
|
||||
@@ -85,7 +94,7 @@ void RuntimeScope::Merge(RuntimeScope other) {
|
||||
}
|
||||
|
||||
auto RuntimeScope::Get(ValueNodeView value_node) const
|
||||
-> std::optional<Nonnull<const LocationValue*>> {
|
||||
-> std::optional<Nonnull<const Value*>> {
|
||||
auto it = locals_.find(value_node);
|
||||
if (it != locals_.end()) {
|
||||
return it->second;
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/address.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/ast/pattern.h"
|
||||
#include "explorer/ast/statement.h"
|
||||
@@ -45,30 +48,42 @@ class RuntimeScope {
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
|
||||
|
||||
// Binds `value` as the value of `value_node`.
|
||||
void Bind(ValueNodeView value_node, Nonnull<const Value*> value);
|
||||
|
||||
// Allocates storage for `value_node` in `heap`, and initializes it with
|
||||
// `value`.
|
||||
// TODO: Update existing callers to use Bind instead, where appropriate.
|
||||
void Initialize(ValueNodeView value_node, Nonnull<const Value*> value);
|
||||
auto Initialize(ValueNodeView value_node, Nonnull<const Value*> value)
|
||||
-> Nonnull<const LocationValue*>;
|
||||
|
||||
// Bind allocation lifetime to scope. Should only be called with unowned
|
||||
// allocations to avoid a double free.
|
||||
void BindLifetimeToScope(Address address);
|
||||
|
||||
// Binds location `address` of a reference value to `value_node` without
|
||||
// allocating local storage.
|
||||
void Bind(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.
|
||||
void BindValue(ValueNodeView value_node, Nonnull<const Value*> value);
|
||||
|
||||
// Transfers the names and allocations from `other` into *this. The two
|
||||
// scopes must not define the same name, and must be backed by the same Heap.
|
||||
void Merge(RuntimeScope other);
|
||||
|
||||
// Returns the local storage for value_node, if it has storage local to
|
||||
// this scope.
|
||||
// Given node `value_node`, returns:
|
||||
// - 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 LocationValue*>>;
|
||||
-> std::optional<Nonnull<const Value*>>;
|
||||
|
||||
// Returns the local values in created order
|
||||
// Returns the local values with allocation in created order.
|
||||
auto allocations() const -> const std::vector<AllocationId>& {
|
||||
return allocations_;
|
||||
}
|
||||
|
||||
private:
|
||||
llvm::MapVector<ValueNodeView, Nonnull<const LocationValue*>,
|
||||
llvm::MapVector<ValueNodeView, Nonnull<const Value*>,
|
||||
std::map<ValueNodeView, unsigned>>
|
||||
locals_;
|
||||
std::vector<AllocationId> allocations_;
|
||||
@@ -184,8 +199,12 @@ class LocationAction : public Action {
|
||||
// An Action which implements evaluation of an Expression to produce a `Value*`.
|
||||
class ExpressionAction : public Action {
|
||||
public:
|
||||
explicit ExpressionAction(Nonnull<const Expression*> expression)
|
||||
: Action(Kind::ExpressionAction), expression_(expression) {}
|
||||
explicit ExpressionAction(
|
||||
Nonnull<const Expression*> expression,
|
||||
std::optional<AllocationId> initialized_location = std::nullopt)
|
||||
: Action(Kind::ExpressionAction),
|
||||
expression_(expression),
|
||||
location_received_(initialized_location) {}
|
||||
|
||||
static auto classof(const Action* action) -> bool {
|
||||
return action->kind() == Kind::ExpressionAction;
|
||||
@@ -194,8 +213,14 @@ class ExpressionAction : public Action {
|
||||
// The Expression this Action evaluates.
|
||||
auto expression() const -> const Expression& { return *expression_; }
|
||||
|
||||
// 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_;
|
||||
};
|
||||
|
||||
// An Action which implements the Instantiation of Type. The result is expressed
|
||||
@@ -242,8 +267,11 @@ class WitnessAction : public Action {
|
||||
// result.
|
||||
class StatementAction : public Action {
|
||||
public:
|
||||
explicit StatementAction(Nonnull<const Statement*> statement)
|
||||
: Action(Kind::StatementAction), statement_(statement) {}
|
||||
explicit StatementAction(Nonnull<const Statement*> statement,
|
||||
std::optional<AllocationId> location_received)
|
||||
: Action(Kind::StatementAction),
|
||||
statement_(statement),
|
||||
location_received_(location_received) {}
|
||||
|
||||
static auto classof(const Action* action) -> bool {
|
||||
return action->kind() == Kind::StatementAction;
|
||||
@@ -252,8 +280,25 @@ class StatementAction : public Action {
|
||||
// The Statement this Action executes.
|
||||
auto statement() const -> const Statement& { return *statement_; }
|
||||
|
||||
// The location provided for the initializing expression, if any.
|
||||
auto location_received() const -> std::optional<AllocationId> {
|
||||
return location_received_;
|
||||
}
|
||||
|
||||
// Sets the location provided to an initializing expression.
|
||||
auto set_location_created(AllocationId location_created) {
|
||||
CARBON_CHECK(!location_created_) << "location created set twice";
|
||||
location_created_ = location_created;
|
||||
}
|
||||
// Returns the location provided to an initializing expression, if any.
|
||||
auto location_created() const -> std::optional<AllocationId> {
|
||||
return location_created_;
|
||||
}
|
||||
|
||||
private:
|
||||
Nonnull<const Statement*> statement_;
|
||||
std::optional<AllocationId> location_received_;
|
||||
std::optional<AllocationId> location_created_;
|
||||
};
|
||||
|
||||
// Action which implements the run-time effects of executing a Declaration.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "explorer/interpreter/heap.h"
|
||||
|
||||
#include "common/check.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@@ -51,23 +52,12 @@ auto Heap::Write(const Address& a, Nonnull<const Value*> v,
|
||||
return Success();
|
||||
}
|
||||
|
||||
auto Heap::GetAllocationId(Nonnull<const Value*> v) const
|
||||
-> std::optional<AllocationId> {
|
||||
auto iter = std::find(values_.begin(), values_.end(), v);
|
||||
if (iter != values_.end()) {
|
||||
auto index = iter - values_.begin();
|
||||
if (states_[index] == ValueState::Alive) {
|
||||
return AllocationId(index);
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) const
|
||||
-> ErrorOr<Success> {
|
||||
if (states_[allocation.index_] == ValueState::Dead) {
|
||||
if (states_[allocation.index_] == ValueState::Dead ||
|
||||
states_[allocation.index_] == ValueState::Discarded) {
|
||||
return ProgramError(source_loc)
|
||||
<< "undefined behavior: access to dead value "
|
||||
<< "undefined behavior: access to dead or discarded value "
|
||||
<< *values_[allocation.index_];
|
||||
}
|
||||
return Success();
|
||||
@@ -94,6 +84,19 @@ void Heap::Deallocate(AllocationId allocation) {
|
||||
|
||||
void Heap::Deallocate(const Address& a) { Deallocate(a.allocation_); }
|
||||
|
||||
auto Heap::is_initialized(AllocationId allocation) const -> bool {
|
||||
return states_[allocation.index_] != ValueState::Uninitialized;
|
||||
}
|
||||
|
||||
auto Heap::is_discarded(AllocationId allocation) const -> bool {
|
||||
return states_[allocation.index_] == ValueState::Discarded;
|
||||
}
|
||||
|
||||
void Heap::Discard(AllocationId allocation) {
|
||||
CARBON_CHECK(states_[allocation.index_] == ValueState::Uninitialized);
|
||||
states_[allocation.index_] = ValueState::Discarded;
|
||||
}
|
||||
|
||||
void Heap::Print(llvm::raw_ostream& out) const {
|
||||
llvm::ListSeparator sep;
|
||||
for (size_t i = 0; i < values_.size(); ++i) {
|
||||
|
||||
@@ -21,6 +21,7 @@ class Heap : public HeapAllocationInterface {
|
||||
public:
|
||||
enum class ValueState {
|
||||
Uninitialized,
|
||||
Discarded,
|
||||
Alive,
|
||||
Dead,
|
||||
};
|
||||
@@ -41,9 +42,6 @@ class Heap : public HeapAllocationInterface {
|
||||
auto Write(const Address& a, Nonnull<const Value*> v,
|
||||
SourceLocation source_loc) -> ErrorOr<Success>;
|
||||
|
||||
auto GetAllocationId(Nonnull<const Value*> v) const
|
||||
-> std::optional<AllocationId> 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;
|
||||
@@ -52,6 +50,13 @@ class Heap : public HeapAllocationInterface {
|
||||
void Deallocate(AllocationId allocation) override;
|
||||
void Deallocate(const Address& a);
|
||||
|
||||
// Marks this allocation, and all its sub-objects, as discarded.
|
||||
void Discard(AllocationId allocation);
|
||||
// Returns whether the given allocation was unused and discarded.
|
||||
auto is_discarded(AllocationId allocation) const -> bool;
|
||||
// Returns whether the given allocation was initialized.
|
||||
auto is_initialized(AllocationId allocation) const -> bool;
|
||||
|
||||
// Print all the values on the heap to the stream `out`.
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
|
||||
|
||||
@@ -30,11 +30,6 @@ class HeapAllocationInterface {
|
||||
// Returns the arena used to allocate the values in this heap.
|
||||
virtual auto arena() const -> Arena& = 0;
|
||||
|
||||
// Returns the ID of the first allocation that holds `v`, if one exists.
|
||||
// TODO: Find a way to remove this.
|
||||
virtual auto GetAllocationId(Nonnull<const Value*> v) const
|
||||
-> std::optional<AllocationId> = 0;
|
||||
|
||||
protected:
|
||||
HeapAllocationInterface() = default;
|
||||
virtual ~HeapAllocationInterface() = default;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/element.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/ast/expression_category.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/common/arena.h"
|
||||
#include "explorer/common/error_builders.h"
|
||||
@@ -169,7 +170,8 @@ class Interpreter {
|
||||
// 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, ImplWitnessMap&& witnesses)
|
||||
Nonnull<const Value*> arg, ImplWitnessMap&& witnesses,
|
||||
std::optional<AllocationId> location_received)
|
||||
-> ErrorOr<Success>;
|
||||
|
||||
auto CallDestructor(Nonnull<const DestructorDeclaration*> fun,
|
||||
@@ -291,46 +293,90 @@ auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
|
||||
return arena_->New<StructValue>(std::move(elements));
|
||||
}
|
||||
|
||||
auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
static auto InitializePlaceholderValue(
|
||||
const ValueNodeView& value_node, ExpressionResult v,
|
||||
std::optional<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());
|
||||
} 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());
|
||||
}
|
||||
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());
|
||||
} 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());
|
||||
} 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());
|
||||
}
|
||||
break;
|
||||
case ExpressionCategory::Initializing:
|
||||
CARBON_FATAL() << "Cannot pattern match an initializing expression";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
|
||||
SourceLocation source_loc,
|
||||
std::optional<Nonnull<RuntimeScope*>> bindings,
|
||||
BindingMap& generic_args, Nonnull<TraceStream*> trace_stream,
|
||||
Nonnull<Arena*> arena) -> bool {
|
||||
if (trace_stream->is_enabled()) {
|
||||
*trace_stream << "match pattern " << *p << "\nwith value " << *v << "\n";
|
||||
*trace_stream << "match pattern " << *p << "\nfrom "
|
||||
<< ExpressionCategoryToString(v.expression_category())
|
||||
<< " expression with value " << *v.value() << "\n";
|
||||
}
|
||||
switch (p->kind()) {
|
||||
case Value::Kind::BindingPlaceholderValue: {
|
||||
CARBON_CHECK(bindings.has_value());
|
||||
const auto& placeholder = cast<BindingPlaceholderValue>(*p);
|
||||
if (placeholder.value_node().has_value()) {
|
||||
(*bindings)->Initialize(*placeholder.value_node(), v);
|
||||
InitializePlaceholderValue(*placeholder.value_node(), v, bindings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::AddrValue: {
|
||||
const auto& addr = cast<AddrValue>(*p);
|
||||
CARBON_CHECK(v->kind() == Value::Kind::LocationValue);
|
||||
const auto& location = cast<LocationValue>(*v);
|
||||
CARBON_CHECK(v.value()->kind() == Value::Kind::LocationValue);
|
||||
const auto& location = cast<LocationValue>(*v.value());
|
||||
return PatternMatch(
|
||||
&addr.pattern(), arena->New<PointerValue>(location.address()),
|
||||
&addr.pattern(),
|
||||
ExpressionResult::Value(arena->New<PointerValue>(location.address())),
|
||||
source_loc, bindings, generic_args, trace_stream, arena);
|
||||
}
|
||||
case Value::Kind::VariableType: {
|
||||
const auto& var_type = cast<VariableType>(*p);
|
||||
generic_args[&var_type.binding()] = v;
|
||||
generic_args[&var_type.binding()] = v.value();
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue:
|
||||
switch (v->kind()) {
|
||||
switch (v.value()->kind()) {
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue: {
|
||||
const auto& p_tup = cast<TupleValueBase>(*p);
|
||||
const auto& v_tup = cast<TupleValueBase>(*v);
|
||||
const auto& v_tup = cast<TupleValueBase>(*v.value());
|
||||
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], v_tup.elements()[i],
|
||||
if (!PatternMatch(p_tup.elements()[i],
|
||||
ExpressionResult::Value(v_tup.elements()[i]),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
return false;
|
||||
@@ -341,7 +387,9 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
case Value::Kind::UninitializedValue: {
|
||||
const auto& p_tup = cast<TupleValueBase>(*p);
|
||||
for (const auto& ele : p_tup.elements()) {
|
||||
if (!PatternMatch(ele, arena->New<UninitializedValue>(ele),
|
||||
if (!PatternMatch(ele,
|
||||
ExpressionResult::Value(
|
||||
arena->New<UninitializedValue>(ele)),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
return false;
|
||||
@@ -350,28 +398,30 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL() << "expected a tuple value in pattern, not " << *v;
|
||||
CARBON_FATAL() << "expected a tuple value in pattern, not "
|
||||
<< *v.value();
|
||||
}
|
||||
case Value::Kind::StructValue: {
|
||||
const auto& p_struct = cast<StructValue>(*p);
|
||||
const auto& v_struct = cast<StructValue>(*v);
|
||||
const auto& v_struct = cast<StructValue>(*v.value());
|
||||
CARBON_CHECK(p_struct.elements().size() == v_struct.elements().size());
|
||||
for (size_t i = 0; i < p_struct.elements().size(); ++i) {
|
||||
CARBON_CHECK(p_struct.elements()[i].name ==
|
||||
v_struct.elements()[i].name);
|
||||
if (!PatternMatch(p_struct.elements()[i].value,
|
||||
v_struct.elements()[i].value, source_loc, bindings,
|
||||
generic_args, trace_stream, arena)) {
|
||||
ExpressionResult::Value(v_struct.elements()[i].value),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::AlternativeValue:
|
||||
switch (v->kind()) {
|
||||
switch (v.value()->kind()) {
|
||||
case Value::Kind::AlternativeValue: {
|
||||
const auto& p_alt = cast<AlternativeValue>(*p);
|
||||
const auto& v_alt = cast<AlternativeValue>(*v);
|
||||
const auto& v_alt = cast<AlternativeValue>(*v.value());
|
||||
if (&p_alt.alternative() != &v_alt.alternative()) {
|
||||
return false;
|
||||
}
|
||||
@@ -380,25 +430,30 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
if (!p_alt.argument().has_value()) {
|
||||
return true;
|
||||
}
|
||||
return PatternMatch(*p_alt.argument(), *v_alt.argument(), source_loc,
|
||||
bindings, generic_args, trace_stream, arena);
|
||||
return PatternMatch(
|
||||
*p_alt.argument(), ExpressionResult::Value(*v_alt.argument()),
|
||||
source_loc, bindings, generic_args, trace_stream, arena);
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL() << "expected a choice alternative in pattern, not "
|
||||
<< *v;
|
||||
<< *v.value();
|
||||
}
|
||||
case Value::Kind::UninitializedValue:
|
||||
CARBON_FATAL() << "uninitialized value is not allowed in pattern " << *v;
|
||||
CARBON_FATAL() << "uninitialized value is not allowed in pattern "
|
||||
<< *v.value();
|
||||
case Value::Kind::FunctionType:
|
||||
switch (v->kind()) {
|
||||
switch (v.value()->kind()) {
|
||||
case Value::Kind::FunctionType: {
|
||||
const auto& p_fn = cast<FunctionType>(*p);
|
||||
const auto& v_fn = cast<FunctionType>(*v);
|
||||
if (!PatternMatch(&p_fn.parameters(), &v_fn.parameters(), source_loc,
|
||||
bindings, generic_args, trace_stream, arena)) {
|
||||
const auto& v_fn = cast<FunctionType>(*v.value());
|
||||
if (!PatternMatch(&p_fn.parameters(),
|
||||
ExpressionResult::Value(&v_fn.parameters()),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
return false;
|
||||
}
|
||||
if (!PatternMatch(&p_fn.return_type(), &v_fn.return_type(),
|
||||
if (!PatternMatch(&p_fn.return_type(),
|
||||
ExpressionResult::Value(&v_fn.return_type()),
|
||||
source_loc, bindings, generic_args, trace_stream,
|
||||
arena)) {
|
||||
return false;
|
||||
@@ -410,16 +465,16 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
}
|
||||
case Value::Kind::AutoType:
|
||||
// `auto` matches any type, without binding any new names. We rely
|
||||
// on the typechecker to ensure that `v` is a type.
|
||||
// on the typechecker to ensure that `v.value()` is a type.
|
||||
return true;
|
||||
case Value::Kind::StaticArrayType: {
|
||||
switch (v->kind()) {
|
||||
switch (v.value()->kind()) {
|
||||
case Value::Kind::TupleType:
|
||||
case Value::Kind::TupleValue: {
|
||||
return true;
|
||||
}
|
||||
case Value::Kind::StaticArrayType: {
|
||||
const auto& v_arr = cast<StaticArrayType>(*v);
|
||||
const auto& v_arr = cast<StaticArrayType>(*v.value());
|
||||
return v_arr.has_size();
|
||||
}
|
||||
default:
|
||||
@@ -427,7 +482,7 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
|
||||
}
|
||||
}
|
||||
default:
|
||||
return ValueEqual(p, v, std::nullopt);
|
||||
return ValueEqual(p, v.value(), std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -993,13 +1048,18 @@ auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
|
||||
return ProgramError(fun->source_loc())
|
||||
<< "destructors currently don't support `addr self` bindings";
|
||||
}
|
||||
if (placeholder->value_node().has_value()) {
|
||||
method_scope.Bind(*placeholder->value_node(), receiver);
|
||||
if (auto& value_node = placeholder->value_node()) {
|
||||
if (value_node->expression_category() == ExpressionCategory::Value) {
|
||||
method_scope.BindValue(*placeholder->value_node(), receiver);
|
||||
} else {
|
||||
CARBON_FATAL()
|
||||
<< "TODO: [self addr: Self*] destructors not implemented yet";
|
||||
}
|
||||
}
|
||||
CARBON_CHECK(method.body().has_value())
|
||||
<< "Calling a method that's missing a body";
|
||||
|
||||
auto act = std::make_unique<StatementAction>(*method.body());
|
||||
auto act = std::make_unique<StatementAction>(*method.body(), std::nullopt);
|
||||
return todo_.Spawn(std::unique_ptr<Action>(std::move(act)),
|
||||
std::move(method_scope));
|
||||
}
|
||||
@@ -1007,7 +1067,9 @@ auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
|
||||
auto Interpreter::CallFunction(const CallExpression& call,
|
||||
Nonnull<const Value*> fun,
|
||||
Nonnull<const Value*> arg,
|
||||
ImplWitnessMap&& witnesses) -> ErrorOr<Success> {
|
||||
ImplWitnessMap&& witnesses,
|
||||
std::optional<AllocationId> location_received)
|
||||
-> ErrorOr<Success> {
|
||||
if (trace_stream_->is_enabled()) {
|
||||
*trace_stream_ << "calling function: " << *fun << "\n";
|
||||
}
|
||||
@@ -1039,31 +1101,26 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
for (const auto& [bind, val] : call.deduced_args()) {
|
||||
CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> inst_val,
|
||||
InstantiateType(val, call.source_loc()));
|
||||
binding_scope.Initialize(bind->original(), inst_val);
|
||||
binding_scope.BindValue(bind->original(), inst_val);
|
||||
}
|
||||
for (const auto& [impl_bind, witness] : witnesses) {
|
||||
binding_scope.Initialize(impl_bind->original(), witness);
|
||||
binding_scope.BindValue(impl_bind->original(), witness);
|
||||
}
|
||||
|
||||
// Bring the arguments that are determined by the function value into
|
||||
// scope. This includes the arguments for the class of which the function
|
||||
// is a member.
|
||||
for (const auto& [bind, val] : func_val->type_args()) {
|
||||
binding_scope.Initialize(bind->original(), val);
|
||||
binding_scope.BindValue(bind->original(), val);
|
||||
}
|
||||
for (const auto& [impl_bind, witness] : func_val->witnesses()) {
|
||||
binding_scope.Initialize(impl_bind->original(), witness);
|
||||
binding_scope.BindValue(impl_bind->original(), witness);
|
||||
}
|
||||
|
||||
// Enter the binding scope to make any deduced arguments visible before
|
||||
// we resolve the self type and parameter type.
|
||||
todo_.CurrentAction().StartScope(std::move(binding_scope));
|
||||
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
Nonnull<const Value*> converted_args,
|
||||
Convert(arg, &function.param_pattern().static_type(),
|
||||
call.source_loc()));
|
||||
|
||||
RuntimeScope function_scope(&heap_);
|
||||
BindingMap generic_args;
|
||||
|
||||
@@ -1073,23 +1130,36 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
const auto* self_pattern = &function.self_pattern().value();
|
||||
if (const auto* placeholder =
|
||||
dyn_cast<BindingPlaceholderValue>(self_pattern)) {
|
||||
// Immutable self with `[self: Self]`
|
||||
// TODO: move this logic into PatternMatch
|
||||
if (placeholder->value_node().has_value()) {
|
||||
function_scope.Bind(*placeholder->value_node(),
|
||||
method_val->receiver());
|
||||
function_scope.BindValue(*placeholder->value_node(),
|
||||
method_val->receiver());
|
||||
}
|
||||
} else {
|
||||
CARBON_CHECK(PatternMatch(self_pattern, method_val->receiver(),
|
||||
call.source_loc(), &function_scope,
|
||||
generic_args, trace_stream_, this->arena_));
|
||||
// Mutable self with `[addr self: Self*]`
|
||||
CARBON_CHECK(isa<AddrValue>(self_pattern));
|
||||
CARBON_CHECK(PatternMatch(
|
||||
self_pattern, ExpressionResult::Value(method_val->receiver()),
|
||||
call.source_loc(), &function_scope, generic_args, trace_stream_,
|
||||
this->arena_));
|
||||
}
|
||||
}
|
||||
|
||||
// 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(), converted_args, call.source_loc(),
|
||||
&function_scope, generic_args, trace_stream_, this->arena_));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(*function.body()),
|
||||
CARBON_CHECK(PatternMatch(&function.param_pattern().value(),
|
||||
ExpressionResult::Value(converted_args),
|
||||
call.source_loc(), &function_scope,
|
||||
generic_args, trace_stream_, this->arena_));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(*function.body(),
|
||||
location_received),
|
||||
std::move(function_scope));
|
||||
}
|
||||
case Value::Kind::ParameterizedEntityName: {
|
||||
@@ -1097,7 +1167,8 @@ auto Interpreter::CallFunction(const CallExpression& call,
|
||||
const Declaration& decl = name.declaration();
|
||||
RuntimeScope params_scope(&heap_);
|
||||
BindingMap generic_args;
|
||||
CARBON_CHECK(PatternMatch(&name.params().value(), arg, call.source_loc(),
|
||||
CARBON_CHECK(PatternMatch(&name.params().value(),
|
||||
ExpressionResult::Value(arg), call.source_loc(),
|
||||
¶ms_scope, generic_args, trace_stream_,
|
||||
this->arena_));
|
||||
Nonnull<const Bindings*> bindings =
|
||||
@@ -1227,8 +1298,8 @@ auto Interpreter::StepInstantiateType() -> ErrorOr<Success> {
|
||||
}
|
||||
|
||||
auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
Action& act = todo_.CurrentAction();
|
||||
const Expression& exp = cast<ExpressionAction>(act).expression();
|
||||
auto& act = cast<ExpressionAction>(todo_.CurrentAction());
|
||||
const Expression& exp = act.expression();
|
||||
if (trace_stream_->is_enabled()) {
|
||||
*trace_stream_ << "--- step exp " << exp << " ." << act.pos() << "."
|
||||
<< " (" << exp.source_loc() << ") --->\n";
|
||||
@@ -1587,7 +1658,7 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
}
|
||||
return CallFunction(call, act.results()[0], act.results()[1],
|
||||
std::move(witnesses));
|
||||
std::move(witnesses), act.location_received());
|
||||
} else if (act.pos() == 3 + static_cast<int>(num_witnesses)) {
|
||||
if (act.results().size() < 3 + num_witnesses) {
|
||||
// Control fell through without explicit return.
|
||||
@@ -1702,6 +1773,12 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
|
||||
}
|
||||
}
|
||||
}
|
||||
case IntrinsicExpression::Intrinsic::PrintAllocs: {
|
||||
CARBON_CHECK(args.empty());
|
||||
heap_.Print(*print_stream_);
|
||||
*print_stream_ << "\n";
|
||||
return todo_.FinishAction(TupleValue::Empty());
|
||||
}
|
||||
case IntrinsicExpression::Intrinsic::Rand: {
|
||||
CARBON_CHECK(args.size() == 2);
|
||||
const int64_t low = cast<IntValue>(*args[0]).value();
|
||||
@@ -1996,8 +2073,8 @@ auto Interpreter::StepWitness() -> ErrorOr<Success> {
|
||||
}
|
||||
|
||||
auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
Action& act = todo_.CurrentAction();
|
||||
const Statement& stmt = cast<StatementAction>(act).statement();
|
||||
auto& act = cast<StatementAction>(todo_.CurrentAction());
|
||||
const Statement& stmt = act.statement();
|
||||
if (trace_stream_->is_enabled()) {
|
||||
*trace_stream_ << "--- step stmt ";
|
||||
stmt.PrintDepth(1, trace_stream_->stream());
|
||||
@@ -2025,12 +2102,14 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
Nonnull<const Value*> val,
|
||||
Convert(act.results()[0], &c.pattern().static_type(),
|
||||
stmt.source_loc()));
|
||||
if (PatternMatch(&c.pattern().value(), val, stmt.source_loc(), &matches,
|
||||
generic_args, trace_stream_, this->arena_)) {
|
||||
if (PatternMatch(&c.pattern().value(), ExpressionResult::Value(val),
|
||||
stmt.source_loc(), &matches, generic_args,
|
||||
trace_stream_, this->arena_)) {
|
||||
// Ensure we don't process any more clauses.
|
||||
act.set_pos(match_stmt.clauses().size() + 1);
|
||||
todo_.MergeScope(std::move(matches));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(&c.statement()));
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(&c.statement(), std::nullopt));
|
||||
} else {
|
||||
return todo_.RunAgain();
|
||||
}
|
||||
@@ -2061,8 +2140,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
source_array->elements()[start_index]);
|
||||
act.ReplaceResult(CurrentIndexPosInResult,
|
||||
arena_->New<IntValue>(start_index + 1));
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(&cast<For>(stmt).body()));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
&cast<For>(stmt).body(), std::nullopt));
|
||||
}
|
||||
if (act.pos() >= 2) {
|
||||
auto current_index =
|
||||
@@ -2085,8 +2164,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
|
||||
act.ReplaceResult(CurrentIndexPosInResult,
|
||||
arena_->New<IntValue>(current_index + 1));
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(&cast<For>(stmt).body()));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
&cast<For>(stmt).body(), std::nullopt));
|
||||
}
|
||||
}
|
||||
return todo_.FinishAction();
|
||||
@@ -2109,8 +2188,8 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
if (cast<BoolValue>(*condition).value()) {
|
||||
// { {true :: (while ([]) s) :: C, E, F} :: S, H}
|
||||
// -> { { s :: (while (e) s) :: C, E, F } :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(&cast<While>(stmt).body()));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
&cast<While>(stmt).body(), std::nullopt));
|
||||
} else {
|
||||
// { {false :: (while ([]) s) :: C, E, F} :: S, H}
|
||||
// -> { { C, E, F } :: S, H}
|
||||
@@ -2142,37 +2221,90 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
}
|
||||
// Process the next statement in the block. The position will be
|
||||
// incremented as part of Spawn.
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(block.statements()[act.pos()]));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
block.statements()[act.pos()], act.location_received()));
|
||||
}
|
||||
case StatementKind::VariableDefinition: {
|
||||
const auto& definition = cast<VariableDefinition>(stmt);
|
||||
const auto* dest_type = &definition.pattern().static_type();
|
||||
const bool has_initializing_expr =
|
||||
definition.has_init() &&
|
||||
definition.init().kind() == ExpressionKind::CallExpression &&
|
||||
definition.init().expression_category() ==
|
||||
ExpressionCategory::Initializing;
|
||||
auto init_location = (act.location_received() && definition.is_returned())
|
||||
? act.location_received()
|
||||
: act.location_created();
|
||||
if (act.pos() == 0 && definition.has_init()) {
|
||||
// { {(var x = e) :: C, E, F} :: S, H}
|
||||
// -> { {e :: (var x = []) :: C, E, F} :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<ExpressionAction>(&definition.init()));
|
||||
if (has_initializing_expr && !init_location) {
|
||||
// Allocate storage for initializing expression.
|
||||
const auto allocation_id =
|
||||
heap_.AllocateValue(arena_->New<UninitializedValue>(
|
||||
&definition.init().static_type()));
|
||||
act.set_location_created(allocation_id);
|
||||
init_location = allocation_id;
|
||||
RuntimeScope scope(&heap_);
|
||||
scope.BindLifetimeToScope(Address(allocation_id));
|
||||
todo_.MergeScope(std::move(scope));
|
||||
}
|
||||
return todo_.Spawn(std::make_unique<ExpressionAction>(
|
||||
&definition.init(), init_location));
|
||||
} else {
|
||||
// { { v :: (x = []) :: C, E, F} :: S, H}
|
||||
// -> { { C, E(x := a), F} :: S, H(a := copy(v))}
|
||||
Nonnull<const Value*> p =
|
||||
&cast<VariableDefinition>(stmt).pattern().value();
|
||||
Nonnull<const Value*> p = &definition.pattern().value();
|
||||
Nonnull<const Value*> v;
|
||||
std::optional<Address> v_location;
|
||||
ExpressionCategory expr_category =
|
||||
definition.has_init() ? definition.init().expression_category()
|
||||
: ExpressionCategory::Value;
|
||||
if (definition.has_init()) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
v, Convert(act.results()[0], dest_type, stmt.source_loc()));
|
||||
if (has_initializing_expr && init_location &&
|
||||
heap_.is_initialized(*init_location)) {
|
||||
const auto address = Address(*init_location);
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
v, heap_.Read(address, definition.source_loc()));
|
||||
CARBON_CHECK(v == act.results()[0]);
|
||||
v_location = address;
|
||||
} else {
|
||||
// TODO: Prevent copies for Value expressions from Reference
|
||||
// expression, once able to prevent mutations.
|
||||
if (init_location && act.location_created()) {
|
||||
// Location provided to initializing expression was not used.
|
||||
heap_.Discard(*init_location);
|
||||
}
|
||||
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()));
|
||||
}
|
||||
} else {
|
||||
v = arena_->New<UninitializedValue>(p);
|
||||
}
|
||||
|
||||
RuntimeScope matches(&heap_);
|
||||
BindingMap generic_args;
|
||||
CARBON_CHECK(PatternMatch(p, v, stmt.source_loc(), &matches,
|
||||
generic_args, trace_stream_, this->arena_))
|
||||
<< stmt.source_loc()
|
||||
<< ": internal error in variable definition, match failed";
|
||||
todo_.MergeScope(std::move(matches));
|
||||
// If declaring a returned var, bind name to the location provided to
|
||||
// initializing expression, if any.
|
||||
RuntimeScope scope(&heap_);
|
||||
if (definition.is_returned() && init_location) {
|
||||
CARBON_CHECK(p->kind() == Value::Kind::BindingPlaceholderValue);
|
||||
const auto value_node =
|
||||
cast<BindingPlaceholderValue>(*p).value_node();
|
||||
CARBON_CHECK(value_node);
|
||||
const auto address = Address(*init_location);
|
||||
scope.Bind(*value_node, address);
|
||||
CARBON_RETURN_IF_ERROR(heap_.Write(address, v, stmt.source_loc()));
|
||||
} else {
|
||||
BindingMap generic_args;
|
||||
bool matched =
|
||||
PatternMatch(p, ExpressionResult(v, v_location, expr_category),
|
||||
stmt.source_loc(), &scope, generic_args,
|
||||
trace_stream_, this->arena_);
|
||||
CARBON_CHECK(matched)
|
||||
<< stmt.source_loc()
|
||||
<< ": internal error in variable definition, match failed";
|
||||
}
|
||||
todo_.MergeScope(std::move(scope));
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
}
|
||||
@@ -2239,14 +2371,14 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
// { {true :: if ([]) then_stmt else else_stmt :: C, E, F} ::
|
||||
// S, H}
|
||||
// -> { { then_stmt :: C, E, F } :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(&cast<If>(stmt).then_block()));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
&cast<If>(stmt).then_block(), std::nullopt));
|
||||
} else if (cast<If>(stmt).else_block()) {
|
||||
// { {false :: if ([]) then_stmt else else_stmt :: C, E, F} ::
|
||||
// S, H}
|
||||
// -> { { else_stmt :: C, E, F } :: S, H}
|
||||
return todo_.Spawn(
|
||||
std::make_unique<StatementAction>(*cast<If>(stmt).else_block()));
|
||||
return todo_.Spawn(std::make_unique<StatementAction>(
|
||||
*cast<If>(stmt).else_block(), std::nullopt));
|
||||
} else {
|
||||
return todo_.FinishAction();
|
||||
}
|
||||
@@ -2289,6 +2421,11 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
|
||||
Nonnull<const Value*> return_value,
|
||||
Convert(act.results()[0], &function.return_term().static_type(),
|
||||
stmt.source_loc()));
|
||||
// Write to initialized storage location, if any.
|
||||
if (const auto location = act.location_received()) {
|
||||
CARBON_RETURN_IF_ERROR(
|
||||
heap_.Write(Address(*location), return_value, stmt.source_loc()));
|
||||
}
|
||||
return todo_.UnwindPast(*function.body(), return_value);
|
||||
}
|
||||
}
|
||||
@@ -2433,6 +2570,10 @@ auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
|
||||
if (act.pos() < cleanup.allocations_count() * 2) {
|
||||
const size_t alloc_index = cleanup.allocations_count() - act.pos() / 2 - 1;
|
||||
auto allocation = act.scope()->allocations()[alloc_index];
|
||||
if (heap_.is_discarded(allocation)) {
|
||||
// Initializing expressions can generate discarded allocations.
|
||||
return todo_.RunAgain();
|
||||
}
|
||||
if (act.pos() % 2 == 0) {
|
||||
auto* location = arena_->New<LocationValue>(Address(allocation));
|
||||
auto value =
|
||||
|
||||
@@ -45,10 +45,12 @@ auto InterpExp(Nonnull<const Expression*> e, Nonnull<Arena*> arena,
|
||||
// The matches for generic variables in the pattern are output in
|
||||
// `generic_args`.
|
||||
// TODO: consider moving this to a separate header.
|
||||
[[nodiscard]] auto PatternMatch(
|
||||
Nonnull<const Value*> p, Nonnull<const Value*> v, SourceLocation source_loc,
|
||||
std::optional<Nonnull<RuntimeScope*>> bindings, BindingMap& generic_args,
|
||||
Nonnull<TraceStream*> trace_stream, Nonnull<Arena*> arena) -> bool;
|
||||
[[nodiscard]] auto PatternMatch(Nonnull<const Value*> p, ExpressionResult v,
|
||||
SourceLocation source_loc,
|
||||
std::optional<Nonnull<RuntimeScope*>> bindings,
|
||||
BindingMap& generic_args,
|
||||
Nonnull<TraceStream*> trace_stream,
|
||||
Nonnull<Arena*> arena) -> bool;
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "common/ostream.h"
|
||||
#include "explorer/ast/declaration.h"
|
||||
#include "explorer/ast/expression.h"
|
||||
#include "explorer/ast/pattern.h"
|
||||
#include "explorer/ast/value.h"
|
||||
#include "explorer/ast/value_transform.h"
|
||||
#include "explorer/common/arena.h"
|
||||
@@ -1116,9 +1117,9 @@ auto TypeChecker::GetBuiltinInterfaceType(SourceLocation source_loc,
|
||||
BindingMap binding_args;
|
||||
if (has_arguments) {
|
||||
TupleValue args(interface.arguments);
|
||||
if (!PatternMatch(&iface_decl->params().value()->value(), &args, source_loc,
|
||||
std::nullopt, binding_args, trace_stream_,
|
||||
this->arena_)) {
|
||||
if (!PatternMatch(&iface_decl->params().value()->value(),
|
||||
ExpressionResult::Value(&args), source_loc, std::nullopt,
|
||||
binding_args, trace_stream_, this->arena_)) {
|
||||
return bad_builtin();
|
||||
}
|
||||
}
|
||||
@@ -2431,7 +2432,8 @@ class TypeChecker::SubstituteTransform
|
||||
&fn_type->return_type()));
|
||||
return type_checker_->arena_->New<FunctionType>(
|
||||
param, std::move(generic_parameters), ret, std::move(deduced_bindings),
|
||||
std::move(subst_bindings).TakeImplBindings());
|
||||
std::move(subst_bindings).TakeImplBindings(),
|
||||
fn_type->is_initializing());
|
||||
}
|
||||
|
||||
// Substituting into a `ConstraintType` needs special handling if we replace
|
||||
@@ -3161,6 +3163,7 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
|
||||
access.set_found_in_interface(result.interface);
|
||||
access.set_is_type_access(!IsInstanceMember(&access.member()));
|
||||
access.set_static_type(inst_member_type);
|
||||
access.set_expression_category(ExpressionCategory::Value);
|
||||
|
||||
if (const auto* func_decl =
|
||||
dyn_cast<FunctionDeclaration>(result.member)) {
|
||||
@@ -3774,7 +3777,9 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
|
||||
Nonnull<const Value*> return_type,
|
||||
Substitute(call.bindings(), &fun_t.return_type()));
|
||||
call.set_static_type(return_type);
|
||||
call.set_expression_category(ExpressionCategory::Value);
|
||||
call.set_expression_category(fun_t.is_initializing()
|
||||
? ExpressionCategory::Initializing
|
||||
: ExpressionCategory::Value);
|
||||
return Success();
|
||||
}
|
||||
case Value::Kind::TypeOfParameterizedEntityName: {
|
||||
@@ -3912,6 +3917,15 @@ auto TypeChecker::TypeCheckExpImpl(Nonnull<Expression*> e,
|
||||
e->set_expression_category(ExpressionCategory::Value);
|
||||
return Success();
|
||||
}
|
||||
case IntrinsicExpression::Intrinsic::PrintAllocs: {
|
||||
if (!args.empty()) {
|
||||
return ProgramError(e->source_loc())
|
||||
<< "__intrinsic_print_allocs takes no arguments";
|
||||
}
|
||||
e->set_static_type(TupleType::Empty());
|
||||
e->set_expression_category(ExpressionCategory::Value);
|
||||
return Success();
|
||||
}
|
||||
case IntrinsicExpression::Intrinsic::Rand: {
|
||||
if (args.size() != 2) {
|
||||
return ProgramError(e->source_loc())
|
||||
@@ -4467,9 +4481,9 @@ auto TypeChecker::TypeCheckPattern(
|
||||
// this level rather than on the overall initializer.
|
||||
if (!IsNonDeduceableType(type)) {
|
||||
BindingMap generic_args;
|
||||
if (!PatternMatch(type, *expected, binding.type().source_loc(),
|
||||
std::nullopt, generic_args, trace_stream_,
|
||||
this->arena_)) {
|
||||
if (!PatternMatch(type, ExpressionResult::Value(*expected),
|
||||
binding.type().source_loc(), std::nullopt,
|
||||
generic_args, trace_stream_, this->arena_)) {
|
||||
return ProgramError(binding.type().source_loc())
|
||||
<< "type pattern '" << *type
|
||||
<< "' does not match actual type '" << **expected << "'";
|
||||
@@ -5112,7 +5126,7 @@ auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
|
||||
f->set_static_type(arena_->New<FunctionType>(
|
||||
&f->param_pattern().static_type(), std::move(generic_parameters),
|
||||
&f->return_term().static_type(), std::move(deduced_bindings),
|
||||
std::move(impl_bindings)));
|
||||
std::move(impl_bindings), /*is_initializing*/ true));
|
||||
switch (f->kind()) {
|
||||
case DeclarationKind::FunctionDeclaration:
|
||||
// TODO: Should we pass in the bindings from the enclosing scope?
|
||||
|
||||
@@ -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: 0: Heap{}, 1: 1, 2: 2
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
var a: i32 = 1;
|
||||
var b: i32 = 2;
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// CHECK:STDOUT: 0: Heap{}
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
fn Main() -> i32 {
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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: Destructor
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("Destructor");
|
||||
}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
let c: C = {};
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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{}
|
||||
// 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: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithValueExpressionBinding(c: C) {
|
||||
Print("Binding scope end");
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c_var: C = {};
|
||||
heap.PrintAllocs();
|
||||
|
||||
Print("Bind from c reference expression");
|
||||
CallWithValueExpressionBinding(c_var);
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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{}
|
||||
// CHECK:STDOUT: Bind from c value expression
|
||||
// CHECK:STDOUT: Binding scope end
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithValueExpressionBinding(c: C) {
|
||||
Print("Binding scope end");
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
let c_let: C = {};
|
||||
heap.PrintAllocs();
|
||||
|
||||
Print("Bind from c value expression");
|
||||
CallWithValueExpressionBinding(c_let);
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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: Initialize c from initializing expression (return <expr>)
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>, 2: C{}
|
||||
// CHECK:STDOUT: Object created, returning
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnExpression() -> C {
|
||||
var c: C = {};
|
||||
heap.PrintAllocs();
|
||||
Print("Object created, returning");
|
||||
return c;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnExpr() {
|
||||
Print("Initialize c from initializing expression (return <expr>)");
|
||||
let c: C = CallWithReturnExpression();
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnExpr();
|
||||
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: Initialize c from initializing expression (returned var)
|
||||
// CHECK:STDOUT: Entering call
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>
|
||||
// CHECK:STDOUT: Object created, returning
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar() -> C {
|
||||
Print("Entering call");
|
||||
heap.PrintAllocs();
|
||||
returned var c: C = {};
|
||||
Print("Object created, returning");
|
||||
return var;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnedVar() {
|
||||
Print("Initialize c from initializing expression (returned var)");
|
||||
let c: C = CallWithReturnedVar();
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnedVar();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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: Initialize c1 from initializing expression (returned var)
|
||||
// CHECK:STDOUT: Before nested init
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>
|
||||
// CHECK:STDOUT: Nested call return
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: First call return
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: Declaration scope
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar2() -> C {
|
||||
Print("Before nested init");
|
||||
heap.PrintAllocs();
|
||||
returned var c: C = {};
|
||||
Print("Nested call return");
|
||||
heap.PrintAllocs();
|
||||
return var;
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar() -> C {
|
||||
returned var c: C = CallWithReturnedVar2();
|
||||
Print("First call return");
|
||||
heap.PrintAllocs();
|
||||
return var;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnedVar() {
|
||||
Print("Initialize c1 from initializing expression (returned var)");
|
||||
let c: C = CallWithReturnedVar();
|
||||
Print("Declaration scope");
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnedVar();
|
||||
return 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{}
|
||||
// CHECK:STDOUT: Initialize c from reference expression
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn FromReferenceExpression() {
|
||||
var c_var: C = {};
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize c from reference expression");
|
||||
let c: C = c_var;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromReferenceExpression();
|
||||
return 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{}
|
||||
// CHECK:STDOUT: Initialize c from value expression
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn FromValueExpression() {
|
||||
let c_let: C = {};
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize c from value expression");
|
||||
let c: C = c_let;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromValueExpression();
|
||||
return 0;
|
||||
}
|
||||
+1
-1
@@ -9,6 +9,6 @@ package ExplorerTest api;
|
||||
fn Main() -> i32 {
|
||||
var p: i32* = heap.New(5);
|
||||
heap.Delete(p);
|
||||
// CHECK:STDERR: RUNTIME ERROR: fail_use_after_free.carbon:[[@LINE+1]]: undefined behavior: access to dead value 5
|
||||
// CHECK:STDERR: RUNTIME ERROR: fail_use_after_free.carbon:[[@LINE+1]]: undefined behavior: access to dead or discarded value 5
|
||||
return *p;
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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: Destructor
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("Destructor");
|
||||
}
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c: C = {};
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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{}
|
||||
// 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: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReferenceExpressionBinding(var c: C) {
|
||||
Print("Binding scope end");
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
var c_var: C = {};
|
||||
heap.PrintAllocs();
|
||||
|
||||
Print("Bind from c reference expression");
|
||||
CallWithReferenceExpressionBinding(c_var);
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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{}
|
||||
// CHECK:STDOUT: Bind from c value expression
|
||||
// CHECK:STDOUT: Binding scope end
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReferenceExpressionBinding(var c: C) {
|
||||
Print("Binding scope end");
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
let c_let: C = {};
|
||||
heap.PrintAllocs();
|
||||
|
||||
Print("Bind from c value expression");
|
||||
CallWithReferenceExpressionBinding(c_let);
|
||||
heap.PrintAllocs();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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: Initialize c from initializing expression (return <expr>)
|
||||
// CHECK:STDOUT: Entering call
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>
|
||||
// CHECK:STDOUT: Object created, returning
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: !!C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnExpression() -> C {
|
||||
Print("Entering call");
|
||||
heap.PrintAllocs();
|
||||
var c: C = {};
|
||||
Print("Object created, returning");
|
||||
heap.PrintAllocs();
|
||||
return c;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnExpr() {
|
||||
Print("Initialize c from initializing expression (return <expr>)");
|
||||
var c: C = CallWithReturnExpression();
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnExpr();
|
||||
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: Initialize c from initializing expression (returned var)
|
||||
// CHECK:STDOUT: Entering call
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>
|
||||
// CHECK:STDOUT: Object created, returning
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar() -> C {
|
||||
Print("Entering call");
|
||||
heap.PrintAllocs();
|
||||
returned var c: C = {};
|
||||
Print("Object created, returning");
|
||||
return var;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnedVar() {
|
||||
Print("Initialize c from initializing expression (returned var)");
|
||||
var c: C = CallWithReturnedVar();
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnedVar();
|
||||
return 0;
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// 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: Initialize c from initializing expression (returned var)
|
||||
// CHECK:STDOUT: Before nested init
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: !Uninit<class C>
|
||||
// CHECK:STDOUT: Nested call return
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: First call return
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: Declaration scope
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar2() -> C {
|
||||
Print("Before nested init");
|
||||
heap.PrintAllocs();
|
||||
returned var c: C = {};
|
||||
Print("Nested call return");
|
||||
heap.PrintAllocs();
|
||||
return var;
|
||||
}
|
||||
|
||||
fn CallWithReturnedVar() -> C {
|
||||
returned var c: C = CallWithReturnedVar2();
|
||||
Print("First call return");
|
||||
heap.PrintAllocs();
|
||||
return var;
|
||||
}
|
||||
|
||||
fn FromInitializingExpression_ReturnedVar() {
|
||||
Print("Initialize c from initializing expression (returned var)");
|
||||
var c: C = CallWithReturnedVar();
|
||||
Print("Declaration scope");
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromInitializingExpression_ReturnedVar();
|
||||
return 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{}
|
||||
// CHECK:STDOUT: Initialize c from reference expression
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn FromReferenceExpression() {
|
||||
var c_var: C = {};
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize c from reference expression");
|
||||
var c: C = c_var;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromReferenceExpression();
|
||||
return 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{}
|
||||
// CHECK:STDOUT: Initialize c from value expression
|
||||
// CHECK:STDOUT: 0: Heap{}, 1: C{}, 2: C{}
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: c destroyed
|
||||
// CHECK:STDOUT: result: 0
|
||||
|
||||
package ExplorerTest api;
|
||||
|
||||
class C {
|
||||
destructor[self: Self] {
|
||||
Print("c destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
fn FromValueExpression() {
|
||||
let c_let: C = {};
|
||||
heap.PrintAllocs();
|
||||
Print("Initialize c from value expression");
|
||||
var c: C = c_let;
|
||||
heap.PrintAllocs();
|
||||
}
|
||||
|
||||
fn Main() -> i32 {
|
||||
FromValueExpression();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user