mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 19:01:07 +01:00
Feature destructor (#2116)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -40,6 +40,7 @@ cc_library(
|
||||
deps = [
|
||||
":action_and_value",
|
||||
":stack",
|
||||
"//common:error",
|
||||
"//common:ostream",
|
||||
"//explorer/ast",
|
||||
"@llvm-project//llvm:Support",
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user