Feature destructor (#2116)

This commit is contained in:
pmqtt
2022-09-26 10:11:05 -07:00
committed by GitHub
parent c63ae9b05d
commit cfa295c0af
29 changed files with 827 additions and 82 deletions
+6
View File
@@ -340,6 +340,11 @@ message FunctionDeclaration {
optional BlockStatement body = 6;
}
message DestructorDeclaration {
optional Pattern me_pattern = 1;
optional BlockStatement body = 2;
}
message ClassDeclaration {
optional string name = 1;
repeated Declaration members = 2;
@@ -417,6 +422,7 @@ message Declaration {
LetDeclaration let = 8;
MixinDeclaration mixin = 9;
MixDeclaration mix = 10;
DestructorDeclaration destructor = 11;
}
}
+21
View File
@@ -677,6 +677,27 @@ static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
out << "var x: i32;";
break;
case Fuzzing::Declaration::kDestructor: {
const auto& function = declaration.destructor();
out << "destructor";
llvm::ListSeparator sep;
out << "[";
if (function.has_me_pattern()) {
// This is a class method.
out << sep;
PatternToCarbon(function.me_pattern(), out);
}
out << "]";
// Body is optional.
if (function.has_body()) {
out << "\n";
BlockStatementToCarbon(function.body(), out);
} else {
out << ";";
}
break;
}
case Fuzzing::Declaration::kFunction: {
const auto& function = declaration.function();
out << "fn ";
+3 -1
View File
@@ -13,7 +13,9 @@ abstract class Pattern : AstNode;
class AlternativePattern : Pattern;
class ExpressionPattern : Pattern;
abstract class Declaration : AstNode;
class FunctionDeclaration : Declaration;
abstract class CallableDeclaration : Declaration;
class FunctionDeclaration : CallableDeclaration;
class DestructorDeclaration : CallableDeclaration;
class SelfDeclaration : Declaration;
class ClassDeclaration : Declaration;
class MixinDeclaration : Declaration;
+46 -13
View File
@@ -38,7 +38,9 @@ void Declaration::Print(llvm::raw_ostream& out) const {
case DeclarationKind::FunctionDeclaration:
cast<FunctionDeclaration>(*this).PrintDepth(-1, out);
break;
case DeclarationKind::DestructorDeclaration:
cast<DestructorDeclaration>(*this).PrintDepth(-1, out);
break;
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(*this);
PrintID(out);
@@ -131,7 +133,9 @@ void Declaration::PrintID(llvm::raw_ostream& out) const {
case DeclarationKind::FunctionDeclaration:
out << "fn " << cast<FunctionDeclaration>(*this).name();
break;
case DeclarationKind::DestructorDeclaration:
out << cast<DestructorDeclaration>(*this).name();
break;
case DeclarationKind::ClassDeclaration: {
const auto& class_decl = cast<ClassDeclaration>(*this);
out << "class " << class_decl.name();
@@ -185,6 +189,8 @@ auto GetName(const Declaration& declaration)
switch (declaration.kind()) {
case DeclarationKind::FunctionDeclaration:
return cast<FunctionDeclaration>(declaration).name();
case DeclarationKind::DestructorDeclaration:
return cast<DestructorDeclaration>(declaration).name();
case DeclarationKind::ClassDeclaration:
return cast<ClassDeclaration>(declaration).name();
case DeclarationKind::MixinDeclaration: {
@@ -231,17 +237,13 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
}
}
auto FunctionDeclaration::Create(Nonnull<Arena*> arena,
SourceLocation source_loc, std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>> {
// Look for the `me` parameter in the `deduced_parameters`
// and put it in the `me_pattern`.
static auto MoveMeParameterToPattern(
SourceLocation source_loc, std::optional<Nonnull<Pattern*>>& me_pattern,
const std::vector<Nonnull<AstNode*>>& deduced_params)
-> ErrorOr<std::vector<Nonnull<GenericBinding*>>> {
std::vector<Nonnull<GenericBinding*>> resolved_params;
// Look for the `me` parameter in the `deduced_parameters`
// and put it in the `me_pattern`.
for (Nonnull<AstNode*> param : deduced_params) {
switch (param->kind()) {
case AstNodeKind::GenericBinding:
@@ -271,12 +273,43 @@ auto FunctionDeclaration::Create(Nonnull<Arena*> arena,
<< "illegal AST node in implicit parameter list";
}
}
return resolved_params;
}
auto DestructorDeclaration::CreateDestructor(
Nonnull<Arena*> arena, SourceLocation source_loc,
std::vector<Nonnull<AstNode*>> deduced_params,
Nonnull<TuplePattern*> param_pattern, ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<DestructorDeclaration*>> {
std::vector<Nonnull<GenericBinding*>> resolved_params;
std::optional<Nonnull<Pattern*>> me_pattern;
CARBON_ASSIGN_OR_RETURN(
resolved_params,
MoveMeParameterToPattern(source_loc, me_pattern, deduced_params));
return arena->New<DestructorDeclaration>(
source_loc, std::move(resolved_params), me_pattern, param_pattern,
return_term, body);
}
auto FunctionDeclaration::Create(Nonnull<Arena*> arena,
SourceLocation source_loc, std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>> {
std::vector<Nonnull<GenericBinding*>> resolved_params;
CARBON_ASSIGN_OR_RETURN(
resolved_params,
MoveMeParameterToPattern(source_loc, me_pattern, deduced_params));
return arena->New<FunctionDeclaration>(source_loc, name,
std::move(resolved_params), me_pattern,
param_pattern, return_term, body);
}
void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
void CallableDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
out << "fn " << name_ << " ";
if (!deduced_parameters_.empty()) {
out << "[";
+69 -19
View File
@@ -102,27 +102,15 @@ class Declaration : public AstNode {
std::optional<Nonnull<const Value*>> constant_value_;
};
class FunctionDeclaration : public Declaration {
class CallableDeclaration : public Declaration {
public:
using ImplementsCarbonValueNode = void;
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>>;
// Use `Create()` instead. This is public only so Arena::New() can call it.
FunctionDeclaration(SourceLocation source_loc, std::string name,
CallableDeclaration(AstNodeKind kind, SourceLocation loc, std::string name,
std::vector<Nonnull<GenericBinding*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: Declaration(AstNodeKind::FunctionDeclaration, source_loc),
: Declaration(kind, loc),
name_(std::move(name)),
deduced_parameters_(std::move(deduced_params)),
me_pattern_(me_pattern),
@@ -130,12 +118,9 @@ class FunctionDeclaration : public Declaration {
return_term_(return_term),
body_(body) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFunctionDeclaration(node->kind());
}
void PrintDepth(int depth, llvm::raw_ostream& out) const;
// TODO: Move name() and name_ to FunctionDeclaration
auto name() const -> const std::string& { return name_; }
auto deduced_parameters() const
-> llvm::ArrayRef<Nonnull<const GenericBinding*>> {
@@ -166,6 +151,62 @@ class FunctionDeclaration : public Declaration {
std::optional<Nonnull<Block*>> body_;
};
class FunctionDeclaration : public CallableDeclaration {
public:
using ImplementsCarbonValueNode = void;
static auto Create(Nonnull<Arena*> arena, SourceLocation source_loc,
std::string name,
std::vector<Nonnull<AstNode*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<FunctionDeclaration*>>;
// Use `Create()` instead. This is public only so Arena::New() can call it.
FunctionDeclaration(SourceLocation source_loc, std::string name,
std::vector<Nonnull<GenericBinding*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: CallableDeclaration(AstNodeKind::FunctionDeclaration, source_loc,
std::move(name), std::move(deduced_params),
me_pattern, param_pattern, return_term, body) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromFunctionDeclaration(node->kind());
}
};
class DestructorDeclaration : public CallableDeclaration {
public:
using ImplementsCarbonValueNode = void;
static auto CreateDestructor(Nonnull<Arena*> arena, SourceLocation source_loc,
std::vector<Nonnull<AstNode*>> deduced_params,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
-> ErrorOr<Nonnull<DestructorDeclaration*>>;
// Use `Create()` instead. This is public only so Arena::New() can call it.
DestructorDeclaration(SourceLocation source_loc,
std::vector<Nonnull<GenericBinding*>> deduced_params,
std::optional<Nonnull<Pattern*>> me_pattern,
Nonnull<TuplePattern*> param_pattern,
ReturnTerm return_term,
std::optional<Nonnull<Block*>> body)
: CallableDeclaration(AstNodeKind::DestructorDeclaration, source_loc,
"destructor", std::move(deduced_params), me_pattern,
param_pattern, return_term, body) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromDestructorDeclaration(node->kind());
}
};
class SelfDeclaration : public Declaration {
public:
using ImplementsCarbonValueNode = void;
@@ -222,6 +263,14 @@ class ClassDeclaration : public Declaration {
auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
return members_;
}
auto destructor() const -> std::optional<Nonnull<DestructorDeclaration*>> {
for (auto& x : members_) {
if (x->kind() == DeclarationKind::DestructorDeclaration) {
return llvm::cast<DestructorDeclaration>(x);
}
}
return std::nullopt;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
@@ -232,6 +281,7 @@ class ClassDeclaration : public Declaration {
std::optional<Nonnull<TuplePattern*>> type_params_;
std::optional<Nonnull<Expression*>> extends_;
std::vector<Nonnull<Declaration*>> members_;
std::optional<Nonnull<FunctionDeclaration*>> destructor_;
};
// EXPERIMENTAL MIXIN FEATURE
+5 -5
View File
@@ -22,7 +22,7 @@
namespace Carbon {
class FunctionDeclaration;
class CallableDeclaration;
class Statement : public AstNode {
public:
@@ -203,11 +203,11 @@ class Return : public Statement {
// Note that this function does not represent an edge in the tree
// structure of the AST: the return value is not a child of this node,
// but an ancestor.
auto function() const -> const FunctionDeclaration& { return **function_; }
auto function() -> FunctionDeclaration& { return **function_; }
auto function() const -> const CallableDeclaration& { return **function_; }
auto function() -> CallableDeclaration& { return **function_; }
// Can only be called once, by ResolveControlFlow.
void set_function(Nonnull<FunctionDeclaration*> function) {
void set_function(Nonnull<CallableDeclaration*> function) {
CARBON_CHECK(!function_.has_value());
function_ = function;
}
@@ -217,7 +217,7 @@ class Return : public Statement {
: Statement(node_kind, source_loc) {}
private:
std::optional<Nonnull<FunctionDeclaration*>> function_;
std::optional<Nonnull<CallableDeclaration*>> function_;
};
class ReturnVar : public Return {
+29
View File
@@ -566,6 +566,35 @@ static auto DeclarationToProto(const Declaration& declaration)
-> Fuzzing::Declaration {
Fuzzing::Declaration declaration_proto;
switch (declaration.kind()) {
case DeclarationKind::DestructorDeclaration: {
const auto& function = cast<DestructorDeclaration>(declaration);
auto* function_proto = declaration_proto.mutable_destructor();
if (function.is_method()) {
switch (function.me_pattern().kind()) {
case PatternKind::AddrPattern:
*function_proto->mutable_me_pattern() =
PatternToProto(cast<AddrPattern>(function.me_pattern()));
break;
case PatternKind::BindingPattern:
*function_proto->mutable_me_pattern() =
PatternToProto(cast<BindingPattern>(function.me_pattern()));
break;
default:
// Parser shouldn't allow me_pattern to be anything other than
// AddrPattern or BindingPattern
CARBON_FATAL() << "me_pattern in method declaration can be either "
"AddrPattern or BindingPattern. Actual pattern: "
<< function.me_pattern();
break;
}
}
if (function.body().has_value()) {
*function_proto->mutable_body() =
BlockStatementToProto(**function.body());
}
break;
}
case DeclarationKind::FunctionDeclaration: {
const auto& function = cast<FunctionDeclaration>(declaration);
auto* function_proto = declaration_proto.mutable_function();
+1
View File
@@ -40,6 +40,7 @@ cc_library(
deps = [
":action_and_value",
":stack",
"//common:error",
"//common:ostream",
"//explorer/ast",
"@llvm-project//llvm:Support",
+21 -5
View File
@@ -25,13 +25,15 @@ RuntimeScope::RuntimeScope(RuntimeScope&& other) noexcept
: locals_(std::move(other.locals_)),
// To transfer ownership of other.allocations_, we have to empty it out.
allocations_(std::exchange(other.allocations_, {})),
heap_(other.heap_) {}
heap_(other.heap_),
destructor_scope_(other.destructor_scope_) {}
auto RuntimeScope::operator=(RuntimeScope&& rhs) noexcept -> RuntimeScope& {
locals_ = std::move(rhs.locals_);
// To transfer ownership of rhs.allocations_, we have to empty it out.
allocations_ = std::exchange(rhs.allocations_, {});
heap_ = rhs.heap_;
destructor_scope_ = rhs.destructor_scope_;
return *this;
}
@@ -62,10 +64,11 @@ void RuntimeScope::Initialize(ValueNodeView value_node,
void RuntimeScope::Merge(RuntimeScope other) {
CARBON_CHECK(heap_ == other.heap_);
locals_.merge(other.locals_);
CARBON_CHECK(other.locals_.empty())
<< "Duplicate definition of " << other.locals_.size()
<< " names, including " << other.locals_.begin()->first.base();
for (auto& element : other.locals_) {
CARBON_CHECK(locals_.count(element.first) == 0)
<< "Duplicate definition of" << element.first;
locals_.insert(element);
}
allocations_.insert(allocations_.end(), other.allocations_.begin(),
other.allocations_.end());
other.allocations_.clear();
@@ -95,6 +98,16 @@ auto RuntimeScope::Capture(
return result;
}
void RuntimeScope::TransitState() {
if (destructor_scope_ == State::Normal) {
destructor_scope_ = State::Destructor;
} else if (destructor_scope_ == State::Destructor) {
destructor_scope_ = State::CleanUpped;
} else {
destructor_scope_ = State::CleanUpped;
}
}
void Action::Print(llvm::raw_ostream& out) const {
switch (kind()) {
case Action::Kind::LValAction:
@@ -119,6 +132,9 @@ void Action::Print(llvm::raw_ostream& out) const {
case Action::Kind::RecursiveAction:
out << "recursive";
break;
case Action::Kind::CleanUpAction:
out << "clean up";
break;
}
out << "." << pos_ << ".";
if (!results_.empty()) {
+52 -2
View File
@@ -5,7 +5,9 @@
#ifndef CARBON_EXPLORER_INTERPRETER_ACTION_H_
#define CARBON_EXPLORER_INTERPRETER_ACTION_H_
#include <list>
#include <map>
#include <tuple>
#include <vector>
#include "common/ostream.h"
@@ -16,6 +18,7 @@
#include "explorer/interpreter/heap_allocation_interface.h"
#include "explorer/interpreter/stack.h"
#include "explorer/interpreter/value.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Compiler.h"
namespace Carbon {
@@ -24,6 +27,12 @@ namespace Carbon {
// not compile-time constants.
class RuntimeScope {
public:
enum class State {
Normal = 0,
Destructor = 1,
CleanUpped = 2,
};
// Returns a RuntimeScope whose Get() operation for a given name returns the
// storage owned by the first entry in `scopes` that defines that name. This
// behavior is closely analogous to a `[&]` capture in C++, hence the name.
@@ -33,7 +42,8 @@ class RuntimeScope {
-> RuntimeScope;
// Constructs a RuntimeScope that allocates storage in `heap`.
explicit RuntimeScope(Nonnull<HeapAllocationInterface*> heap) : heap_(heap) {}
explicit RuntimeScope(Nonnull<HeapAllocationInterface*> heap)
: heap_(heap), destructor_scope_(State::Normal) {}
// Moving a RuntimeScope transfers ownership of its allocations.
RuntimeScope(RuntimeScope&&) noexcept;
@@ -58,10 +68,32 @@ class RuntimeScope {
auto Get(ValueNodeView value_node) const
-> std::optional<Nonnull<const LValue*>>;
// Returns the local values in created order
auto locals() const -> std::vector<Nonnull<const LValue*>> {
std::vector<Nonnull<const LValue*>> res;
for (auto& entry : locals_) {
res.push_back(entry.second);
}
return res;
}
// Return scope state
// Normal = Scope is not bind at the top of a destructor call
// Destructor = Scope is bind to a destructor call and was not cleaned up,
// CleanedUp = Scope is bind to a destructor call and is cleaned up
auto DestructionState() const -> State { return destructor_scope_; }
// Transit the state from Normal to Destructor
// Transit the state from Destructor to CleanUpped
void TransitState();
private:
std::map<ValueNodeView, Nonnull<const LValue*>> locals_;
llvm::MapVector<ValueNodeView, Nonnull<const LValue*>,
std::map<ValueNodeView, unsigned>>
locals_;
std::vector<AllocationId> allocations_;
Nonnull<HeapAllocationInterface*> heap_;
State destructor_scope_;
};
// An Action represents the current state of a self-contained computation,
@@ -86,6 +118,7 @@ class Action {
DeclarationAction,
ScopeAction,
RecursiveAction,
CleanUpAction,
};
Action(const Value&) = delete;
@@ -239,6 +272,23 @@ class DeclarationAction : public Action {
Nonnull<const Declaration*> declaration_;
};
class CleanupAction : public Action {
public:
explicit CleanupAction(RuntimeScope scope) : Action(Kind::CleanUpAction) {
locals_count_ = scope.locals().size();
StartScope(std::move(scope));
}
auto locals_count() const -> int { return locals_count_; }
static auto classof(const Action* action) -> bool {
return action->kind() == Kind::CleanUpAction;
}
private:
int locals_count_;
};
// Action which does nothing except introduce a new scope into the action
// stack. This is useful when a distinct scope doesn't otherwise have an
// Action it can naturally be associated with. ScopeActions are not associated
+82 -12
View File
@@ -4,6 +4,7 @@
#include "explorer/interpreter/action_stack.h"
#include "common/error.h"
#include "explorer/interpreter/action.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
@@ -126,8 +127,10 @@ void ActionStack::InitializeFragment(ContinuationValue::StackFragment& fragment,
}
auto ActionStack::FinishAction() -> ErrorOr<Success> {
std::stack<std::unique_ptr<Action>> scopes_to_destroy;
std::unique_ptr<Action> act = todo_.Pop();
switch (act->kind()) {
case Action::Kind::CleanUpAction:
case Action::Kind::ExpressionAction:
case Action::Kind::LValAction:
case Action::Kind::PatternAction:
@@ -136,16 +139,22 @@ auto ActionStack::FinishAction() -> ErrorOr<Success> {
CARBON_FATAL() << "ScopeAction at top of stack";
case Action::Kind::StatementAction:
case Action::Kind::DeclarationAction:
case Action::Kind::RecursiveAction:
PopScopes();
case Action::Kind::RecursiveAction: {
PopScopes(scopes_to_destroy);
break;
}
}
PushCleanUpAction(std::move(act));
PushCleanUpActions(std::move(scopes_to_destroy));
return Success();
}
auto ActionStack::FinishAction(Nonnull<const Value*> result)
-> ErrorOr<Success> {
std::stack<std::unique_ptr<Action>> scopes_to_destroy;
std::unique_ptr<Action> act = todo_.Pop();
switch (act->kind()) {
case Action::Kind::CleanUpAction:
case Action::Kind::StatementAction:
case Action::Kind::DeclarationAction:
case Action::Kind::RecursiveAction:
@@ -155,9 +164,12 @@ auto ActionStack::FinishAction(Nonnull<const Value*> result)
case Action::Kind::ExpressionAction:
case Action::Kind::LValAction:
case Action::Kind::PatternAction:
PopScopes();
PopScopes(scopes_to_destroy);
SetResult(result);
break;
}
PushCleanUpAction(std::move(act));
PushCleanUpActions(std::move(scopes_to_destroy));
return Success();
}
@@ -192,8 +204,9 @@ auto ActionStack::RunAgain() -> ErrorOr<Success> {
return Success();
}
auto ActionStack::UnwindTo(Nonnull<const Statement*> ast_node)
-> ErrorOr<Success> {
auto ActionStack::UnwindToWithCaptureScopesToDestroy(
Nonnull<const Statement*> ast_node) -> std::stack<std::unique_ptr<Action>> {
std::stack<std::unique_ptr<Action>> scopes_to_destroy;
while (true) {
if (const auto* statement_action =
llvm::dyn_cast<StatementAction>(todo_.Top().get());
@@ -201,23 +214,50 @@ auto ActionStack::UnwindTo(Nonnull<const Statement*> ast_node)
&statement_action->statement() == ast_node) {
break;
}
todo_.Pop();
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));
scopes_to_destroy.push(std::move(cleanup_action));
}
}
return scopes_to_destroy;
}
auto ActionStack::UnwindTo(Nonnull<const Statement*> ast_node)
-> ErrorOr<Success> {
std::stack<std::unique_ptr<Action>> scopes_to_destroy =
UnwindToWithCaptureScopesToDestroy(ast_node);
PushCleanUpActions(std::move(scopes_to_destroy));
return Success();
}
auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node)
-> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(UnwindTo(ast_node));
todo_.Pop();
PopScopes();
std::stack<std::unique_ptr<Action>> scopes_to_destroy =
UnwindPastWithCaptureScopesToDestroy(ast_node);
PushCleanUpActions(std::move(scopes_to_destroy));
return Success();
}
auto ActionStack::UnwindPastWithCaptureScopesToDestroy(
Nonnull<const Statement*> ast_node) -> std::stack<std::unique_ptr<Action>> {
std::stack<std::unique_ptr<Action>> scopes_to_destroy =
UnwindToWithCaptureScopesToDestroy(ast_node);
auto item = todo_.Pop();
scopes_to_destroy.push(std::move(item));
PopScopes(scopes_to_destroy);
return scopes_to_destroy;
}
auto ActionStack::UnwindPast(Nonnull<const Statement*> ast_node,
Nonnull<const Value*> result) -> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(UnwindPast(ast_node));
std::stack<std::unique_ptr<Action>> scopes_to_destroy =
UnwindPastWithCaptureScopesToDestroy(ast_node);
SetResult(result);
PushCleanUpActions(std::move(scopes_to_destroy));
return Success();
}
@@ -248,9 +288,17 @@ auto ActionStack::Suspend() -> ErrorOr<Success> {
return Success();
}
void ActionStack::PopScopes() {
void ActionStack::PopScopes(
std::stack<std::unique_ptr<Action>>& cleanup_stack) {
while (!todo_.IsEmpty() && llvm::isa<ScopeAction>(*todo_.Top())) {
todo_.Pop();
auto act = todo_.Pop();
if (act->scope()) {
if ((*act->scope()).DestructionState() <
RuntimeScope::State::CleanUpped) {
(*act->scope()).TransitState();
cleanup_stack.push(std::move(act));
}
}
}
}
@@ -262,4 +310,26 @@ void ActionStack::SetResult(Nonnull<const Value*> result) {
}
}
void ActionStack::PushCleanUpActions(
std::stack<std::unique_ptr<Action>> actions) {
while (!actions.empty()) {
auto& act = actions.top();
if (act->scope()) {
std::unique_ptr<Action> cleanup_action =
std::make_unique<CleanupAction>(std::move(*act->scope()));
todo_.Push(std::move(cleanup_action));
}
actions.pop();
}
}
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));
todo_.Push(std::move(cleanup_action));
}
}
} // namespace Carbon
+18 -1
View File
@@ -7,6 +7,7 @@
#include <memory>
#include <optional>
#include <stack>
#include "common/ostream.h"
#include "explorer/ast/statement.h"
@@ -118,15 +119,31 @@ class ActionStack {
// Suspends execution of the currently-executing continuation.
auto Suspend() -> ErrorOr<Success>;
void Pop() { todo_.Pop(); }
private:
// Pop any ScopeActions from the top of the stack, propagating results as
// needed, to restore the invariant that todo_.Top() is not a ScopeAction.
void PopScopes();
// Store the popped scope action into cleanup_stack, so that the destructor
// can be called for the variables
void PopScopes(std::stack<std::unique_ptr<Action>>& cleanup_stack);
// Set `result` as the result of the Action most recently removed from the
// stack.
void SetResult(Nonnull<const Value*> result);
auto UnwindToWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
-> std::stack<std::unique_ptr<Action>>;
auto UnwindPastWithCaptureScopesToDestroy(Nonnull<const Statement*> ast_node)
-> std::stack<std::unique_ptr<Action>>;
// Create CleanUpActions for all actions
void PushCleanUpActions(std::stack<std::unique_ptr<Action>> actions);
// Create and push a CleanUpAction on the stack
void PushCleanUpAction(std::unique_ptr<Action> act);
// TODO: consider defining a non-nullable unique_ptr-like type to use here.
Stack<std::unique_ptr<Action>> todo_;
std::optional<Nonnull<const Value*>> result_;
+79 -2
View File
@@ -4,6 +4,8 @@
#include "explorer/interpreter/interpreter.h"
#include <llvm/Support/raw_ostream.h>
#include <iterator>
#include <map>
#include <optional>
@@ -83,6 +85,8 @@ class Interpreter {
auto StepStmt() -> ErrorOr<Success>;
// State transition for declarations.
auto StepDeclaration() -> ErrorOr<Success>;
// State transition for object destruction.
auto StepCleanUp() -> ErrorOr<Success>;
auto CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
@@ -142,6 +146,9 @@ class Interpreter {
Nonnull<const Value*> arg, ImplWitnessMap&& witnesses)
-> ErrorOr<Success>;
auto CallDestructor(Nonnull<const DestructorDeclaration*> fun,
Nonnull<const Value*> receiver) -> ErrorOr<Success>;
void PrintState(llvm::raw_ostream& out);
Phase phase() const { return phase_; }
@@ -615,6 +622,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
@@ -739,6 +747,26 @@ 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;
CARBON_CHECK(PatternMatch(&method.me_pattern().value(), receiver,
fun->source_loc(), &method_scope, generic_args,
trace_stream_, this->arena_));
CARBON_CHECK(method.body().has_value())
<< "Calling a method that's missing a body";
auto act = std::make_unique<StatementAction>(*method.body());
method_scope.TransitState();
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,
@@ -1756,7 +1784,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
CARBON_ASSIGN_OR_RETURN(
value, heap_.Read(lvalue->address(), ret_var.source_loc()));
}
const FunctionDeclaration& function = cast<Return>(stmt).function();
const CallableDeclaration& function = cast<Return>(stmt).function();
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> return_value,
Convert(value, &function.return_term().static_type(),
@@ -1772,7 +1800,7 @@ auto Interpreter::StepStmt() -> ErrorOr<Success> {
} else {
// { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H}
// -> { {v :: C', E', F'} :: S, H}
const FunctionDeclaration& function = cast<Return>(stmt).function();
const CallableDeclaration& function = cast<Return>(stmt).function();
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> return_value,
Convert(act.results()[0], &function.return_term().static_type(),
@@ -1841,6 +1869,7 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
return todo_.FinishAction();
}
}
case DeclarationKind::DestructorDeclaration:
case DeclarationKind::FunctionDeclaration:
case DeclarationKind::ClassDeclaration:
case DeclarationKind::MixinDeclaration:
@@ -1856,6 +1885,51 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
}
}
auto Interpreter::StepCleanUp() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
CleanupAction& cleanup = cast<CleanupAction>(act);
if (act.pos() < cleanup.locals_count()) {
auto lvalue = act.scope()->locals()[cleanup.locals_count() - act.pos() - 1];
SourceLocation source_loc("destructor", 1);
auto value = heap_.Read(lvalue->address(), source_loc);
if (value.ok()) {
if (act.scope()->DestructionState() < RuntimeScope::State::CleanUpped) {
if (const auto* class_obj = dyn_cast<NominalClassValue>(*value)) {
const auto& class_type = cast<NominalClassType>(class_obj->type());
const auto& class_dec = class_type.declaration();
if (class_dec.destructor().has_value()) {
return CallDestructor(*class_dec.destructor(), class_obj);
}
}
} else {
if (const auto* class_obj = dyn_cast<NominalClassValue>(*value)) {
const auto& class_type = cast<NominalClassType>(class_obj->type());
const auto& class_dec = class_type.declaration();
const auto& class_members = class_dec.members();
for (const auto& member : class_members) {
if (const auto* var = dyn_cast<VariableDeclaration>(member)) {
const auto& type = var->static_type();
if (const auto* c_type = dyn_cast<NominalClassType>(&type)) {
auto& c_dec = c_type->declaration();
if (c_dec.destructor().has_value()) {
Address object = lvalue->address();
Address mem = object.SubobjectAddress(Member(var));
auto v = heap_.Read(mem, source_loc);
act.scope()->TransitState();
return CallDestructor(*c_dec.destructor(), *v);
}
}
}
}
}
act.scope()->TransitState();
}
}
}
todo_.Pop();
return Success();
}
// State transition.
auto Interpreter::Step() -> ErrorOr<Success> {
Action& act = todo_.CurrentAction();
@@ -1875,6 +1949,9 @@ auto Interpreter::Step() -> ErrorOr<Success> {
case Action::Kind::DeclarationAction:
CARBON_RETURN_IF_ERROR(StepDeclaration());
break;
case Action::Kind::CleanUpAction:
CARBON_RETURN_IF_ERROR(StepCleanUp());
break;
case Action::Kind::ScopeAction:
CARBON_FATAL() << "ScopeAction escaped ActionStack";
case Action::Kind::RecursiveAction:
@@ -18,7 +18,7 @@ namespace Carbon {
// Aggregate information about a function being analyzed.
struct FunctionData {
// The function declaration.
Nonnull<FunctionDeclaration*> declaration;
Nonnull<CallableDeclaration*> declaration;
// True if the function has a deduced return type, and we've already seen
// a `return` statement in its body.
@@ -72,6 +72,7 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
"signature.";
}
}
return Success();
}
case StatementKind::Break:
@@ -138,12 +139,13 @@ static auto ResolveControlFlow(Nonnull<Statement*> statement,
auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
switch (declaration->kind()) {
case DeclarationKind::DestructorDeclaration:
case DeclarationKind::FunctionDeclaration: {
auto& function = cast<FunctionDeclaration>(*declaration);
if (function.body().has_value()) {
FunctionData data = {.declaration = &function};
auto& callable = cast<CallableDeclaration>(*declaration);
if (callable.body().has_value()) {
FunctionData data = {.declaration = &callable};
CARBON_RETURN_IF_ERROR(
ResolveControlFlow(*function.body(), std::nullopt, &data));
ResolveControlFlow(*callable.body(), std::nullopt, &data));
}
break;
}
+11 -1
View File
@@ -33,6 +33,15 @@ static auto AddExposedNames(const Declaration& declaration,
// Nothing to do here
break;
}
case DeclarationKind::DestructorDeclaration: {
// TODO: Remove this code. With this code, it is possible to create not
// useful carbon code.
// Without this code, a Segfault is generated
auto& func = cast<DestructorDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(enclosing_scope.Add(
"destructor", &func, StaticScope::NameStatus::KnownButNotDeclared));
break;
}
case DeclarationKind::FunctionDeclaration: {
auto& func = cast<FunctionDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(enclosing_scope.Add(
@@ -531,8 +540,9 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
ResolveMemberNames(impl.members(), impl_scope, bodies));
break;
}
case DeclarationKind::DestructorDeclaration:
case DeclarationKind::FunctionDeclaration: {
auto& function = cast<FunctionDeclaration>(declaration);
auto& function = cast<CallableDeclaration>(declaration);
StaticScope function_scope;
function_scope.AddParent(&enclosing_scope);
enclosing_scope.MarkDeclared(function.name());
+5 -4
View File
@@ -284,11 +284,12 @@ static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
// Checks formed/unformed state intraprocedurally.
// Can be extended to an interprocedural analysis when a call graph is
// available.
case DeclarationKind::FunctionDeclaration: {
auto& function = cast<FunctionDeclaration>(*declaration);
if (function.body().has_value()) {
case DeclarationKind::FunctionDeclaration:
case DeclarationKind::DestructorDeclaration: {
auto& callable = cast<CallableDeclaration>(*declaration);
if (callable.body().has_value()) {
FlowFacts flow_facts;
CARBON_RETURN_IF_ERROR(ResolveUnformed(*function.body(), flow_facts,
CARBON_RETURN_IF_ERROR(ResolveUnformed(*callable.body(), flow_facts,
FlowFacts::ActionType::None));
}
break;
+32 -8
View File
@@ -167,6 +167,7 @@ static auto ExpectPointerType(SourceLocation source_loc,
static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::DestructorValue:
case Value::Kind::FunctionValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
@@ -228,6 +229,7 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
switch (value->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
@@ -871,6 +873,7 @@ auto TypeChecker::ArgumentDeduction(
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
@@ -1179,6 +1182,7 @@ auto TypeChecker::Substitute(
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
@@ -3214,7 +3218,7 @@ auto TypeChecker::ExpectReturnOnAllPaths(
// TODO: Add checking to function definitions to ensure that
// all deduced type parameters will be deduced.
auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
auto TypeChecker::DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
const ScopeInfo& scope_info)
-> ErrorOr<Success> {
if (trace_stream_) {
@@ -3287,7 +3291,20 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
f->set_static_type(arena_->New<FunctionType>(
&f->param_pattern().static_type(), generic_parameters,
&f->return_term().static_type(), deduced_bindings, impl_bindings));
SetConstantValue(f, arena_->New<FunctionValue>(f));
switch (f->kind()) {
case DeclarationKind::FunctionDeclaration:
SetConstantValue(
cast<FunctionDeclaration>(f),
arena_->New<FunctionValue>(cast<FunctionDeclaration>(f)));
break;
case DeclarationKind::DestructorDeclaration:
SetConstantValue(
cast<DestructorDeclaration>(f),
arena_->New<DestructorValue>(cast<DestructorDeclaration>(f)));
break;
default:
CARBON_FATAL() << "f is not a callable declaration";
}
if (f->name() == "Main") {
if (!f->return_term().type_expression().has_value()) {
@@ -3308,7 +3325,7 @@ auto TypeChecker::DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
return Success();
}
auto TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
auto TypeChecker::TypeCheckCallableDeclaration(Nonnull<CallableDeclaration*> f,
const ImplScope& impl_scope)
-> ErrorOr<Success> {
if (trace_stream_) {
@@ -4003,6 +4020,7 @@ static bool IsValidTypeForAliasTarget(Nonnull<const Value*> type) {
switch (type->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::DestructorValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
@@ -4110,9 +4128,10 @@ auto TypeChecker::TypeCheckDeclaration(
TypeCheckImplDeclaration(&cast<ImplDeclaration>(*d), impl_scope));
break;
}
case DeclarationKind::DestructorDeclaration:
case DeclarationKind::FunctionDeclaration:
CARBON_RETURN_IF_ERROR(TypeCheckFunctionDeclaration(
&cast<FunctionDeclaration>(*d), impl_scope));
CARBON_RETURN_IF_ERROR(TypeCheckCallableDeclaration(
&cast<CallableDeclaration>(*d), impl_scope));
return Success();
case DeclarationKind::ClassDeclaration:
CARBON_RETURN_IF_ERROR(
@@ -4181,11 +4200,16 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
break;
}
case DeclarationKind::FunctionDeclaration: {
auto& func_def = cast<FunctionDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareFunctionDeclaration(&func_def, scope_info));
auto& func_def = cast<CallableDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareCallableDeclaration(&func_def, scope_info));
break;
}
case DeclarationKind::DestructorDeclaration: {
auto& destructor_def = cast<CallableDeclaration>(*d);
CARBON_RETURN_IF_ERROR(
DeclareCallableDeclaration(&destructor_def, scope_info));
break;
}
case DeclarationKind::ClassDeclaration: {
auto& class_decl = cast<ClassDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareClassDeclaration(&class_decl, scope_info));
+2 -2
View File
@@ -192,7 +192,7 @@ class TypeChecker {
auto DeclareDeclaration(Nonnull<Declaration*> d, const ScopeInfo& scope_info)
-> ErrorOr<Success>;
auto DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
auto DeclareCallableDeclaration(Nonnull<CallableDeclaration*> f,
const ScopeInfo& scope_info)
-> ErrorOr<Success>;
@@ -275,7 +275,7 @@ class TypeChecker {
-> ErrorOr<Success>;
// Type check the body of the function.
auto TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
auto TypeCheckCallableDeclaration(Nonnull<CallableDeclaration*> f,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
+12 -2
View File
@@ -291,6 +291,13 @@ void Value::Print(llvm::raw_ostream& out) const {
case Value::Kind::BoolValue:
out << (cast<BoolValue>(*this).value() ? "true" : "false");
break;
case Value::Kind::DestructorValue: {
const DestructorValue& destructor = cast<DestructorValue>(*this);
out << "destructor [ ";
out << destructor.declaration().me_pattern();
out << " ]";
break;
}
case Value::Kind::FunctionValue: {
const FunctionValue& fun = cast<FunctionValue>(*this);
out << "fun<" << fun.declaration().name() << ">";
@@ -736,6 +743,7 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
}
case Value::Kind::IntValue:
case Value::Kind::BoolValue:
case Value::Kind::DestructorValue:
case Value::Kind::FunctionValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::StructValue:
@@ -789,6 +797,8 @@ auto ValueStructurallyEqual(
return body1.has_value() == body2.has_value() &&
(!body1.has_value() || *body1 == *body2);
}
case Value::Kind::DestructorValue:
return false;
case Value::Kind::BoundMethodValue: {
const auto& m1 = cast<BoundMethodValue>(*v1);
const auto& m2 = cast<BoundMethodValue>(*v2);
@@ -986,7 +996,7 @@ auto NominalClassType::FindFunction(std::string_view name) const
break;
}
case DeclarationKind::FunctionDeclaration: {
const auto& fun = cast<FunctionDeclaration>(*member);
const auto& fun = cast<CallableDeclaration>(*member);
if (fun.name() == name) {
return &cast<FunctionValue>(**fun.constant_value());
}
@@ -1014,7 +1024,7 @@ auto MixinPseudoType::FindFunction(const std::string_view& name) const
break;
}
case DeclarationKind::FunctionDeclaration: {
const auto& fun = cast<FunctionDeclaration>(*member);
const auto& fun = cast<CallableDeclaration>(*member);
if (fun.name() == name) {
return &cast<FunctionValue>(**fun.constant_value());
}
+19
View File
@@ -39,6 +39,7 @@ class Value {
enum class Kind {
IntValue,
FunctionValue,
DestructorValue,
BoundMethodValue,
PointerValue,
LValue,
@@ -186,6 +187,24 @@ class FunctionValue : public Value {
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// A destructor value.
class DestructorValue : public Value {
public:
explicit DestructorValue(Nonnull<const DestructorDeclaration*> declaration)
: Value(Kind::DestructorValue), declaration_(declaration) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::DestructorValue;
}
auto declaration() const -> const DestructorDeclaration& {
return *declaration_;
}
private:
Nonnull<const DestructorDeclaration*> declaration_;
};
// A bound method value. It includes the receiver object.
class BoundMethodValue : public Value {
public:
+2
View File
@@ -55,6 +55,7 @@ CONTINUATION "__continuation"
CONTINUATION_TYPE "__Continuation"
CONTINUE "continue"
DEFAULT "default"
DESTRUCTOR "destructor"
DOUBLE_ARROW "=>"
ELSE "else"
EQUAL "="
@@ -160,6 +161,7 @@ operand_start [(A-Za-z0-9_\"]
{CONTINUATION} { return CARBON_SIMPLE_TOKEN(CONTINUATION); }
{CONTINUE} { return CARBON_SIMPLE_TOKEN(CONTINUE); }
{DEFAULT} { return CARBON_SIMPLE_TOKEN(DEFAULT); }
{DESTRUCTOR} { return CARBON_SIMPLE_TOKEN(DESTRUCTOR); }
{DOUBLE_ARROW} { return CARBON_SIMPLE_TOKEN(DOUBLE_ARROW); }
{ELSE} { return CARBON_SIMPLE_TOKEN(ELSE); }
{EQUAL_EQUAL} { return CARBON_SIMPLE_TOKEN(EQUAL_EQUAL); }
+21
View File
@@ -108,6 +108,7 @@
%type <std::optional<Nonnull<Expression*>>> class_declaration_extends
%type <Nonnull<Declaration*>> declaration
%type <Nonnull<FunctionDeclaration*>> function_declaration
%type <Nonnull<DestructorDeclaration*>> destructor_declaration;
%type <Nonnull<MixDeclaration*>> mix_declaration
%type <Nonnull<AliasDeclaration*>> alias_declaration
%type <std::vector<Nonnull<Declaration*>>> declaration_list
@@ -223,6 +224,7 @@
CONTINUATION_TYPE
CONTINUE
DEFAULT
DESTRUCTOR
DOUBLE_ARROW
ELSE
EQUAL
@@ -1145,6 +1147,8 @@ class_declaration_extends:
declaration:
function_declaration
{ $$ = $1; }
| destructor_declaration
{ $$ = $1; }
| class_declaration_extensibility CLASS identifier type_params class_declaration_extends LEFT_CURLY_BRACE class_body RIGHT_CURLY_BRACE
{
$$ = arena->New<ClassDeclaration>(
@@ -1211,6 +1215,23 @@ impl_type:
{ $$ = arena->New<IdentifierExpression>(context.source_loc(), "Self"); }
| type_expression
;
destructor_declaration:
DESTRUCTOR deduced_params block
{
ErrorOr<DestructorDeclaration*> fn =
DestructorDeclaration::CreateDestructor(
arena, context.source_loc(), $2,
arena->New<TuplePattern>(context.source_loc(),
std::vector<Nonnull<Pattern*>>()),
ReturnTerm::Omitted(context.source_loc()), $3);
if (fn.ok()) {
$$ = *fn;
} else {
context.RecordSyntaxError(std::move(fn).error());
YYERROR;
}
}
;
declaration_list:
// Empty
{ $$ = {}; }
@@ -0,0 +1,45 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR A 1
// CHECK: DESTRUCTOR B 2
// CHECK: DESTRUCTOR A 3
// CHECK: DESTRUCTOR A 4
// CHECK: result: 1
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
}
fn Create(x: i32) -> A{
return {.n = x};
}
var n: i32;
}
class B{
destructor[me: Self]{
Print("DESTRUCTOR B {0}",me.n);
}
fn Create(x: i32) -> B{
return {.n = x, .a = A.Create(3) };
}
var n: i32;
var a: A;
}
fn Main() -> i32 {
var a: A = A.Create(4);
var b: B = B.Create(2);
var c: A = A.Create(1);
return 1;
}
@@ -0,0 +1,29 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR A 3
// CHECK: result: 1
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
if(me.n == 3){
return;
}
Print("DESTRUCTOR A: This message should not printed");
}
var n: i32;
}
fn Main() -> i32 {
var a: A = {.n = 3};
return 1;
}
@@ -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
//
// RUN: %{not} %{explorer} %s 2>&1 2>&1 | %{FileCheck} %s
// AUTOUPDATE: %{explorer} %s
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A1 {0}",me.n);
}
destructor[me: Self]{
Print("DESTRUCTOR A2 {0}",me.n);
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/destructor/fail_multiple_destructor_declaration.carbon:[[@LINE+1]]: Duplicate name `destructor` also found at {{.*}}/explorer/testdata/destructor/fail_multiple_destructor_declaration.carbon:13
}
var n: i32;
}
fn Main() -> i32 {
var a: A = {.n = 3};
return 1;
}
@@ -0,0 +1,43 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR C 1
// CHECK: DESTRUCTOR B 2
// CHECK: DESTRUCTOR A 3
// CHECK: result: 1
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
}
var n: i32;
}
class B{
destructor[me: Self]{
Print("DESTRUCTOR B {0}",me.n);
}
var n: i32;
}
class C{
destructor[me: Self]{
Print("DESTRUCTOR C {0}",me.n);
}
var n: i32;
}
fn Main() -> i32 {
var a: A = {.n = 3};
var b: B = {.n = 2};
var c: C = {.n = 1};
return 1;
}
+36
View File
@@ -0,0 +1,36 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR A 1
// CHECK: DESTRUCTOR A 2
// CHECK: DESTRUCTOR A 3
// CHECK: DESTRUCTOR A 1
// CHECK: DESTRUCTOR A 2
// CHECK: DESTRUCTOR A 3
// CHECK: result: 1
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
}
var n: i32;
}
fn Main() -> i32 {
var i: i32 = 0;
while( i < 2){
var a: A = {.n = 3};
var b: A = {.n = 2};
var c: A = {.n = 1};
i = i + 1;
}
return 1;
}
+58
View File
@@ -0,0 +1,58 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR A 1
// CHECK: DESTRUCTOR A 2
// CHECK: DESTRUCTOR A 3
// CHECK: DESTRUCTOR A 4
// CHECK: DESTRUCTOR A 5
// CHECK: DESTRUCTOR A 6
// CHECK: DESTRUCTOR A 7
// CHECK: DESTRUCTOR A 8
// CHECK: DESTRUCTOR A 6
// CHECK: DESTRUCTOR A 7
// CHECK: DESTRUCTOR A 8
// CHECK: result: 1
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
}
var n: i32;
}
fn Main() -> i32 {
var i: i32 = 0;
while(true){
var a: A = {.n = 4};
var b: A = {.n = 3};
var c: A = {.n = 2};
if(true){
var a: A = {.n = 1};
break;
}
}
i = 0;
while(i < 2 ){
var a: A = {.n = 8};
var b: A = {.n = 7};
var c: A = {.n = 6};
if(i == 0){
var a: A = {.n = 5};
i = i+1;
continue;
}
i = i+1;
}
return 1;
}
@@ -0,0 +1,49 @@
// 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
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: DESTRUCTOR A 1
// CHECK: DESTRUCTOR A 2
// CHECK: DESTRUCTOR A 3
// CHECK: DESTRUCTOR A 4
// CHECK: result: 2
package ExplorerTest api;
class A{
destructor[me: Self]{
Print("DESTRUCTOR A {0}",me.n);
}
var n: i32;
}
fn ident(x: bool)-> bool{
return x;
}
fn ident_i32(x: i32)-> i32{
return x;
}
// It should be enforced that different runtime scopes are on the stack.
//So that it can be tested that the wrong scopes are not removed from the stack.
fn Main() -> i32 {
var i: i32 = 0;
var d: A = {.n = 4};
if(ident(true)){
var a: A = {.n = 3};
if(true){
var b: A = {.n = 2};
ident_i32(2);
if(ident_i32(0) == 0){
var c: A = {.n = 1};
return 2;
}
}
}
return 1;
}