Skip to content

Commit

Permalink
Fix #23 (#236)
Browse files Browse the repository at this point in the history
An exception parameter declaration for an EH block acts functionally like a function parameter, and a variable.  But, unlike function parameters or variables, they are not lexically initialized -- their initializations are performed by the runtime at the places where an exception is thrown.  There are arguments for reusing `ipr::Parameter` or `ipr::Var` to represent that.  This patch removes that ambiguity by introducing a distinct node class to represent EH parameters.  Most of the code is about capturing the nesting structure of EH handlers, region, and scope.

Fixes #23
  • Loading branch information
GabrielDosReis authored Apr 17, 2022
1 parent 781cdbd commit 802c786
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 45 deletions.
148 changes: 112 additions & 36 deletions include/ipr/impl
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,14 @@ namespace ipr::impl {
// --------------------------------------
// The Sequence<> interface admits various implementations.
// Here is the list of the implementations currently in use:
// (a) ref_sequence<>;
// (b) obj_sequence<>
// (c) obj_list<>;
// (d) empty_sequence<T>.
// (a) ref_sequence<T>
// (b) obj_sequence<T>
// (c) obj_list<T>
// (d) empty_sequence<T>
// Variants exist in form of
// (i) decl_sequence;
// (ii) singleton_ref<T>.
// (i) decl_sequence<T>
// (ii) singleton_ref<T>
// (iii) singleton_obj<T>

// -- When a class implements an interface, return that interface;
// -- otherwise, the type itself.
Expand Down Expand Up @@ -341,30 +342,57 @@ namespace ipr::impl {
// to keep track of their elements (even when they are empty).
template<class T>
struct empty_sequence : ipr::Sequence<T> {
int size() const final { return 0; }
using Index = typename Sequence<T>::Index;
Index size() const final { return 0; }

const T& get(int) const final
const T& get(Index) const final
{
throw std::domain_error("empty_sequence::get");
}
};

// --impl::singleton
// A singleton container (in Standard C++ sense), providing the backing store for
// the implementation of a singleton sequence. No allocator or other complications.
// Usual minimal requirements: begin(), end()
// Optionally: front().
// FIXME: It is a pity it takes this amount of code to provide that simple
// interface, in C++20.
template<typename T>
struct singleton {
using iterator = T*;
using const_iterator = const T*;
template<typename... Args>
explicit singleton(Args&&... args) : item{std::forward<Args>(args)...} { }
iterator begin() { return &item; }
const_iterator begin() const { return &item; }
iterator end() { return begin() + 1; }
const_iterator end() const { return begin() + 1; }
T& front() { return item; }
const T& front() const { return item; }
private:
T item;
};

// A sequence implementation for the case of a collection consisting of exactly one element.
template<typename T>
struct singleton : ipr::Sequence<T> {
using Index = typename Sequence<T>::Index;
struct singleton_obj : ipr::Sequence<projection<T>> {
using Index = typename Sequence<projection<T>>::Index;

template<typename... Args>
singleton(Args&&... args) : item{args...} { }
singleton_obj(Args&&... args) : seq{std::forward<Args>(args)...} { }
Index size() const final { return 1; }
const T& get(Index i) const final
const projection<T>& get(Index i) const final
{
if (i == 0)
return item;
return seq.front();
throw std::domain_error("singleton::get");
}
const T& element() const { return seq.front(); }
singleton<T>& backing_store() { return seq; }
const singleton<T>& backing_store() const { return seq; }
private:
T item;
impl::singleton<T> seq;
};
}

Expand Down Expand Up @@ -550,10 +578,9 @@ namespace ipr::impl {

typed_sequence() { }
explicit typed_sequence(const Seq& s) : seq(s) { }
const ipr::Sequence<ipr::Type>& operand() const final
{
return *this;
}
template<typename... Args>
explicit typed_sequence(Args&&... args) : seq(std::forward<Args>(args)...) { }
const ipr::Sequence<ipr::Type>& operand() const final { return *this; }
Index size() const final { return seq.size(); }
const ipr::Type& get(Index i) const final
{
Expand All @@ -575,7 +602,8 @@ namespace ipr::impl {
using Index = typename ipr::Sequence<ipr::Decl>::Index;
using Members = Seq<singleton_overload<Member>>;
typed_sequence<Members> decls;

template<typename... Args>
homogeneous_scope(Args&&... args) : decls{std::forward<Args>(args)...} { }
Index size() const final { return decls.size(); }
const projection<Member>& get(Index i) const final { return decls.seq.get(i); }
const ipr::Product& type() const final { return decls; }
Expand Down Expand Up @@ -609,6 +637,10 @@ namespace ipr::impl {
homogeneous_scope<Member, Seq> scope;

explicit homogeneous_region(const ipr::Region& p) : parent(p) { }
template<typename... Args>
explicit homogeneous_region(const ipr::Region& r, Args&&... args)
: parent{r}, scope{r, std::forward<Args>(args)...}
{ }
const ipr::Region& enclosing() const final { return parent; }
const ipr::Sequence<ipr::Expr>& body() const final { return scope; }
const ipr::Scope& bindings() const final { return scope; }
Expand All @@ -624,16 +656,12 @@ namespace ipr::impl {
Phases phases() const final { return f; }
};

// -- impl::Stmt implements the common operations of statements.
struct Stmt_common {
template<class S>
struct Stmt : S {
ipr::Unit_location unit_locus;
ipr::Source_location src_locus;
ref_sequence<ipr::Annotation> notes;
ref_sequence<ipr::Attribute> attrs;
};

template<class S>
struct Stmt : S, Stmt_common {
const ipr::Unit_location& unit_location() const final { return unit_locus; }
const ipr::Source_location& source_location() const final { return src_locus; }
const ipr::Sequence<ipr::Annotation>& annotation() const final { return notes; }
Expand Down Expand Up @@ -1933,7 +1961,7 @@ namespace ipr::impl {
single_using_declaration(const ipr::Scope_ref&, Designator::Mode);
const ipr::Sequence<Designator>& designators() const final { return what; }
private:
singleton<Designator> what;
singleton_obj<Designator> what;
};

struct Using_declaration : impl::Directive<ipr::Using_declaration, Phases::Elaboration> {
Expand Down Expand Up @@ -1963,37 +1991,86 @@ namespace ipr::impl {
using Expr_stmt = type_from_operand<Stmt<ipr::Expr_stmt>>;
using Empty_stmt = type_from_operand<Stmt<ipr::Empty_stmt>>;
using Goto = type_from_operand<Stmt<ipr::Goto>>;
using Handler = type_from_second<Stmt<ipr::Handler>>;
using If = Ternary<Stmt<Expr<ipr::If>>>;
using Labeled_stmt = type_from_second<Stmt<ipr::Labeled_stmt>>;
using Return = type_from_operand<Stmt<ipr::Return>>;
using Switch = Controlled_stmt<ipr::Switch>;
using While = Controlled_stmt<ipr::While>;

// -- impl::EH_parameter
struct EH_parameter : unique_decl<ipr::EH_parameter> {
EH_parameter(const ipr::Region&, const ipr::Name&, const ipr::Type&);
const ipr::Name& name() const final { return id; }
const ipr::Type& type() const final { return typing; }
const ipr::Region& home_region() const final { return home; }
const ipr::Region& lexical_region() const final { return home; }
private:
const ipr::Name& id;
const ipr::Type& typing;
const ipr::Region& home;
};

// -- impl::handler_block
// The block-statement body of a handler. It has no associated EH handlers of its own.
struct handler_block : impl::Stmt<impl::Expr<ipr::Block>> {
impl::Region lexical_region;
handler_block(const ipr::Region&);
const ipr::Region& region() const final { return lexical_region; }
const ipr::Sequence<ipr::Handler>& handlers() const final { return none; }

// The scope of declarations in this block
impl::Scope* scope() { return &lexical_region.scope; }
void add_stmt(const ipr::Expr& s)
{
lexical_region.expr_seq.push_back(&s);
}
private:
impl::empty_sequence<ipr::Handler> none;
};

// -- impl::eh_region
// The region containing the declaration of the exception parameter of a Handler.
// Like for function parameter region, this region encloses the region of the
// ipr::Block (body) associated with the Handler.
// Note: the enclosing() region of this eh_region is the same as the enclosing
// region() of the Block with which the Handler is associated.
struct eh_region : homogeneous_region<impl::EH_parameter, singleton_obj> {
using base = homogeneous_region<impl::EH_parameter, singleton_obj>;
using base::base;
const ipr::EH_parameter& parameter() const { return scope.decls.seq.element(); }
};

// -- impl::Handler
struct Handler : impl::Stmt<impl::Node<ipr::Handler>> {
Handler(const ipr::Region&, const ipr::Name&, const ipr::Type&);
const ipr::Type& type() const final { return block.type(); }
const ipr::EH_parameter& exception() const final { return eh.parameter(); }
const ipr::Block& body() const final { return block; }
impl::handler_block& body() { return block; }
private:
eh_region eh;
impl::handler_block block;
};

// A Block holds a heterogeneous region, suitable for
// recording the set of declarations appearing in that
// block. It also holds a sequence of handlers, when the
// block actually represents a C++ try-block.
struct Block : impl::Stmt<Expr<ipr::Block>> {
impl::Region lexical_region;
impl::ref_sequence<ipr::Handler> handler_seq;

explicit Block(const ipr::Region&);
const ipr::Region& region() const final { return lexical_region; }
const ipr::Sequence<ipr::Handler>& handlers() const final { return handler_seq; }

// The scope of declarations in this block
impl::Scope* scope() { return &lexical_region.scope; }

impl::Handler* new_handler(const ipr::Name&, const ipr::Type&);
void add_stmt(const ipr::Expr& s)
{
lexical_region.expr_seq.push_back(&s);
}

void add_handler(const ipr::Handler& h)
{
handler_seq.push_back(&h);
}
private:
impl::obj_list<impl::Handler> handler_seq;
};


Expand Down Expand Up @@ -2335,7 +2412,6 @@ namespace ipr::impl {
impl::If* make_if(const ipr::Expr&, const ipr::Stmt&);
impl::If* make_if(const ipr::Expr&, const ipr::Stmt&, const ipr::Stmt&);
impl::Switch* make_switch();
impl::Handler* make_handler(const ipr::Decl&, const ipr::Block&);
impl::Labeled_stmt* make_labeled_stmt(const ipr::Expr&,
const ipr::Stmt&);
impl::While* make_while();
Expand Down
21 changes: 17 additions & 4 deletions include/ipr/interface
Original file line number Diff line number Diff line change
Expand Up @@ -1551,10 +1551,14 @@ namespace ipr {

// -- Block --
// A Block is any sequence of general expressions bracketed by curly braces.
// A block may additionally have exception handlers, in which case it corresponds
// to Standard C++ syntax heavy try-block. Conventional, plain blocks carry
// no handlers.
struct Block : Category<Category_code::Block, Stmt> {
virtual const Region& region() const = 0;
const Sequence<Expr>& body() const { return region().body(); }
virtual const Sequence<Handler>& handlers() const = 0;
bool try_block() const { return handlers().size() == 0; }
};

// -- Ctor_body --
Expand Down Expand Up @@ -1643,10 +1647,9 @@ namespace ipr {
// This represents a catch-clause. Notice that there is no node
// for "try" as every Block is implicitly a try-block. Ideally, we
// should have every expression as a "try-block".
struct Handler : Binary<Category<Category_code::Handler, Stmt>,
const Decl&, const Block&> {
Arg1_type exception() const { return first(); }
Arg2_type body() const { return second(); }
struct Handler : Category<Category_code::Handler, Stmt> {
virtual const EH_parameter& exception() const = 0;
virtual const Block& body() const = 0;
};

// -- Substitution --
Expand Down Expand Up @@ -1772,6 +1775,15 @@ namespace ipr {
Optional<Expr> default_value() const { return initializer(); }
};

// -- EH_parameter --
// Parameter to an exception handler block. In many aspects, it functions
// like a function parameter, except its initialization is not lexical the
// way functional parameters are. Futhermore, they cannot have default values,
// and there is exactly one parameter per handler (unlike functions).
struct EH_parameter : Category<Category_code::EH_parameter, Decl> {
Optional<Expr> initializer() const final { return { }; }
};

// -- Fundecl --
// This node represents a function declaration. Notice that the
// exception specification is actually made part of the function type.
Expand Down Expand Up @@ -2066,6 +2078,7 @@ namespace ipr {
virtual void visit(const Parameter_list&);
virtual void visit(const Typedecl&);
virtual void visit(const Var&);
virtual void visit(const EH_parameter&);

virtual void visit(const Global_scope&);
virtual void visit(const Empty_stmt&);
Expand Down
1 change: 1 addition & 0 deletions include/ipr/node-category
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ Template, // ipr::Template
Parameter, // ipr::Parameter
Typedecl, // ipr::Typedecl
Var, // ipr::Var
EH_parameter, // ipr::EH_parameter

Unit, // ipr::Unit

Expand Down
1 change: 1 addition & 0 deletions include/ipr/synopsis
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ namespace ipr {
struct Parameter; // function or template parameter
struct Typedecl; // declaration for a type
struct Var; // variable declaration
struct EH_parameter; // exception handler block parameter

// ------------------------
// -- distinguished node --
Expand Down
23 changes: 18 additions & 5 deletions src/impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,19 @@ namespace ipr::impl {
:id{n}, typing{t}, pos{p}
{ }

// -- impl::EH_parameter
EH_parameter::EH_parameter(const ipr::Region& r, const ipr::Name& n, const ipr::Type& t)
: id{n}, typing{t}, home{r}
{ }

// -- impl::handler_block
handler_block::handler_block(const ipr::Region& r) : lexical_region{&r} { }

// -- impl::Handler
Handler::Handler(const ipr::Region& r, const ipr::Name& n, const ipr::Type& t)
: eh{r, n, t}, block{eh}
{ }

// --------------------
// -- impl::Typedecl --
// --------------------
Expand All @@ -532,6 +545,11 @@ namespace ipr::impl {
: lexical_region(&pr)
{ }

impl::Handler* Block::new_handler(const ipr::Name& n, const ipr::Type& t)
{
return handler_seq.push_back(lexical_region.enclosing(), n, t);
}

// ---------------
// -- impl::For --
// ---------------
Expand Down Expand Up @@ -665,11 +683,6 @@ namespace ipr::impl {
return switches.make();
}

impl::Handler*
stmt_factory::make_handler(const ipr::Decl& d, const ipr::Block& b) {
return handlers.make(d, b);
}

impl::Labeled_stmt*
stmt_factory::make_labeled_stmt(const ipr::Expr& l, const ipr::Stmt& s)
{
Expand Down
5 changes: 5 additions & 0 deletions src/traversal.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,11 @@ ipr::Visitor::visit(const Var& d)
visit(as<Decl>(d));
}

void ipr::Visitor::visit(const EH_parameter& d)
{
visit(as<Decl>(d));
}

//
// Similarly, the global namespace constant is visited as-if Namespace.
//
Expand Down

0 comments on commit 802c786

Please sign in to comment.