Add addr self binding support in destructors (#3117)

Refactors `CallDestructor` and `CallFunction` to both call a new method
`BindSelfIfPresent`, which includes support for binding `addr self` if
specified. Fixes the associated unit test.

Closes #2802.
This commit is contained in:
Lucile Rose Nihlen
2023-08-29 20:46:39 +00:00
committed by GitHub
parent 82860c4573
commit af0710ea16
3 changed files with 75 additions and 55 deletions
+70 -54
View File
@@ -182,8 +182,17 @@ class Interpreter {
std::optional<AllocationId> location_received)
-> ErrorOr<Success>;
// Call the destructor method in `fun`, with any self argument bound to
// `receiver`.
auto CallDestructor(Nonnull<const DestructorDeclaration*> fun,
Nonnull<const Value*> receiver) -> ErrorOr<Success>;
ExpressionResult receiver) -> ErrorOr<Success>;
// If the given method or destructor `decl` has a self argument, bind it to
// `receiver`.
void BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
ExpressionResult receiver, RuntimeScope& method_scope,
BindingMap& generic_args,
const SourceLocation& source_location);
auto phase() const -> Phase { return phase_; }
@@ -848,39 +857,6 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
}
}
auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
Nonnull<const Value*> receiver)
-> ErrorOr<Success> {
const DestructorDeclaration& method = *fun;
CARBON_CHECK(method.is_method());
RuntimeScope method_scope(&heap_);
BindingMap generic_args;
// TODO: move this logic into PatternMatch, and call it here.
const auto* p = &method.self_pattern().value();
const auto* placeholder = dyn_cast<BindingPlaceholderValue>(p);
if (!placeholder) {
// TODO: Fix this, probably merging logic with CallFunction.
// https://github.com/carbon-language/carbon-lang/issues/2802
return ProgramError(fun->source_loc())
<< "destructors currently don't support `addr self` bindings";
}
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(), std::nullopt);
return todo_.Spawn(std::unique_ptr<Action>(std::move(act)),
std::move(method_scope));
}
auto Interpreter::CallFunction(const CallExpression& call,
Nonnull<const Value*> fun,
Nonnull<const Value*> arg,
@@ -941,25 +917,9 @@ auto Interpreter::CallFunction(const CallExpression& call,
// Bind the receiver to the `self` parameter, if there is one.
if (const auto* method_val = dyn_cast<BoundMethodValue>(func_val)) {
CARBON_CHECK(function.is_method());
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.BindValue(*placeholder->value_node(),
method_val->receiver());
}
} else {
// Mutable self with `[addr self: Self*]`
CARBON_CHECK(isa<AddrValue>(self_pattern));
bool success = PatternMatch(
self_pattern, ExpressionResult::Value(method_val->receiver()),
call.source_loc(), &function_scope, generic_args, trace_stream_,
this->arena_);
CARBON_CHECK(success) << "Failed to bind addr self";
}
BindSelfIfPresent(&function,
ExpressionResult::Value(method_val->receiver()),
function_scope, generic_args, call.source_loc());
}
CARBON_ASSIGN_OR_RETURN(
@@ -1013,6 +973,60 @@ auto Interpreter::CallFunction(const CallExpression& call,
}
}
auto Interpreter::CallDestructor(Nonnull<const DestructorDeclaration*> fun,
ExpressionResult receiver)
-> ErrorOr<Success> {
const DestructorDeclaration& method = *fun;
CARBON_CHECK(method.is_method());
RuntimeScope method_scope(&heap_);
BindingMap generic_args;
BindSelfIfPresent(fun, receiver, method_scope, generic_args,
SourceLocation::DiagnosticsIgnored());
CARBON_CHECK(method.body().has_value())
<< "Calling a method that's missing a 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));
}
void Interpreter::BindSelfIfPresent(Nonnull<const CallableDeclaration*> decl,
ExpressionResult receiver,
RuntimeScope& method_scope,
BindingMap& generic_args,
const SourceLocation& source_location) {
CARBON_CHECK(decl->is_method());
const auto* self_pattern = &decl->self_pattern().value();
if (const auto* placeholder =
dyn_cast<BindingPlaceholderValue>(self_pattern)) {
// Immutable self with `[self: Self]`
if (placeholder->value_node().has_value()) {
bool success =
PatternMatch(placeholder, receiver, source_location, &method_scope,
generic_args, trace_stream_, this->arena_);
CARBON_CHECK(success) << "Failed to bind self";
}
} else {
// Mutable self with `[addr self: Self*]`
CARBON_CHECK(isa<AddrValue>(self_pattern));
ExpressionResult v = receiver;
// See if we need to make a LocationValue from the address provided in the
// ExpressionResult
if (receiver.value()->kind() != Value::Kind::LocationValue) {
CARBON_CHECK(receiver.expression_category() ==
ExpressionCategory::Reference);
CARBON_CHECK(receiver.address().has_value());
v = ExpressionResult::Value(
arena_->New<LocationValue>(receiver.address().value()));
}
bool success = PatternMatch(self_pattern, v, source_location, &method_scope,
generic_args, trace_stream_, this->arena_);
CARBON_CHECK(success) << "Failed to bind addr self";
}
}
// Returns true if the format string is okay to pass to formatv. This only
// supports `{{` and `{N}` as special syntax.
static auto ValidateFormatString(SourceLocation source_loc,
@@ -2395,7 +2409,9 @@ auto Interpreter::StepDestroy() -> ErrorOr<Success> {
if (act.pos() == 0) {
// Run the destructor, if there is one.
if (auto destructor = class_decl.destructor()) {
return CallDestructor(*destructor, class_obj);
return CallDestructor(
*destructor, ExpressionResult::Reference(
class_obj, destroy_act.location()->address()));
} else {
return todo_.RunAgain();
}
@@ -10,7 +10,6 @@ class A {
destructor[addr self: Self*] {
self->n += 1;
Print("DESTRUCTOR A {0}", self->n);
// CHECK:STDERR: RUNTIME ERROR: fail_addr.carbon:[[@LINE+1]]: destructors currently don't support `addr self` bindings
}
var n: i32;
@@ -20,3 +19,6 @@ fn Main() -> i32 {
var a: A = {.n = 2};
return 0;
}
// CHECK:STDOUT: DESTRUCTOR A 3
// CHECK:STDOUT: result: 0
+2
View File
@@ -109,6 +109,8 @@ fn Main() -> i32 {
// CHECK:STDOUT: <[] stack-pop: ValueExpressionAction pos: 1 `{}.(interface ImplicitAs(T = class T).Convert)` results: [`bound_method<Convert>`] (phase_execution.carbon:10)
// CHECK:STDOUT: ->> step ExpressionAction pos: 1 `{}.(interface ImplicitAs(T = class T).Convert)()` results: [`bound_method<Convert>`] scope: [] (phase_execution.carbon:10) --->
// CHECK:STDOUT: -() calling function: bound_method<Convert>
// CHECK:STDOUT: === match pattern `Placeholder<self>`
// CHECK:STDOUT: from value expression with value `{}`
// CHECK:STDOUT: === match pattern `()`
// CHECK:STDOUT: from value expression with value `()`
// CHECK:STDOUT: >[] stack-push: ScopeAction pos: 0 scope: [`self: Self`: `{}`] (None)