Detect type aliasing cycles

This commit is contained in:
Eugen Wissner 2025-03-30 17:09:22 +02:00
parent 0658d07e97
commit f2e2da4a34
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
10 changed files with 102 additions and 44 deletions

View File

@ -264,18 +264,8 @@ namespace elna::boot
delete m_body;
}
return_declaration::return_declaration(std::shared_ptr<type_expression> type)
: type(type)
{
}
return_declaration::return_declaration(std::monostate)
: no_return(true)
{
}
procedure_type_expression::procedure_type_expression(const struct position position,
return_declaration return_type)
return_declaration<std::shared_ptr<type_expression>> return_type)
: type_expression(position), return_type(return_type)
{
}

View File

@ -144,7 +144,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::statement *>> statements;
%type <elna::boot::procedure_definition *> procedure_definition;
%type <std::pair<std::vector<std::string>, std::shared_ptr<elna::boot::procedure_type_expression>>> procedure_heading;
%type <elna::boot::return_declaration> return_declaration;
%type <elna::boot::procedure_type_expression::return_t> return_declaration;
%type <std::vector<elna::boot::procedure_definition *>> procedure_definitions procedure_part;
%type <elna::boot::type_definition *> type_definition;
%type <std::vector<elna::boot::type_definition *>> type_definitions type_part;
@ -197,8 +197,8 @@ identifier_definitions:
| identifier_definition { $$.emplace_back(std::move($1)); }
return_declaration:
/* proper procedure */ {}
| "->" "!" { $$ = boot::return_declaration(std::monostate{}); }
| "->" type_expression { $$ = boot::return_declaration($2); }
| "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); }
| "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); }
procedure_heading:
"(" formal_parameters ")" return_declaration
{

View File

@ -142,8 +142,28 @@ namespace elna::boot
this->current_type = type(result_type);
}
void declaration_visitor::visit(procedure_type_expression *)
void declaration_visitor::visit(procedure_type_expression *type_expression)
{
std::shared_ptr<procedure_type> result_type;
if (type_expression->return_type.no_return)
{
result_type = std::make_shared<procedure_type>(procedure_type::return_t(std::monostate{}));
}
else if (type_expression->return_type.proper_type != nullptr)
{
type_expression->return_type.proper_type->accept(this);
result_type = std::make_shared<procedure_type>(procedure_type::return_t(this->current_type));
}
else
{
result_type = std::make_shared<procedure_type>(procedure_type::return_t());
}
for (auto& parameter : type_expression->parameters)
{
parameter->accept(this);
result_type->parameters.push_back(this->current_type);
}
}
void declaration_visitor::visit(variable_declaration *declaration)
@ -161,9 +181,9 @@ namespace elna::boot
{
heading_parameter->accept(this);
}
if (definition->heading().return_type.type != nullptr)
if (definition->heading().return_type.proper_type != nullptr)
{
definition->heading().return_type.type->accept(this);
definition->heading().return_type.proper_type->accept(this);
}
if (definition->body != nullptr)
{

View File

@ -135,6 +135,11 @@ namespace elna::boot
return *this;
}
bool type::operator==(const std::nullptr_t&)
{
return empty();
}
type::~type()
{
switch (tag)
@ -265,6 +270,11 @@ namespace elna::boot
{
}
procedure_type::procedure_type(return_t return_type)
: return_type(return_type)
{
}
info::~info()
{
}

View File

@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see
#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna1.h"
#include <algorithm>
#include "ggc.h"
#include "function.h"
#include "cgraph.h"
@ -37,7 +39,7 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
tree get_inner_alias(const boot::type& type, std::shared_ptr<symbol_table> symbols,
std::unordered_map<std::string, tree>& unresolved)
std::unordered_map<std::string, tree>& unresolved, std::vector<std::string>& path)
{
if (auto reference = type.get<boot::primitive_type>())
{
@ -57,7 +59,7 @@ namespace elna::gcc
}
else if (auto reference = type.get<boot::pointer_type>())
{
return build_global_pointer_type(get_inner_alias(reference->base, symbols, unresolved));
return build_global_pointer_type(get_inner_alias(reference->base, symbols, unresolved, path));
}
else if (auto reference = type.get<boot::array_type>())
{
@ -65,19 +67,27 @@ namespace elna::gcc
tree upper_bound = build_int_cst_type(integer_type_node, reference->size);
tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound);
return build_array_type(get_inner_alias(reference->base, symbols, unresolved), range_type);
return build_array_type(get_inner_alias(reference->base, symbols, unresolved, path), range_type);
}
else if (auto reference = type.get<boot::alias_type>())
{
return handle_symbol(reference->name, reference, symbols, unresolved);
return handle_symbol(reference->name, reference, symbols, unresolved, path);
}
return error_mark_node;
}
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved)
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved,
std::vector<std::string>& path)
{
auto looked_up = get_inner_alias(reference->reference, symbols, unresolved);
path.push_back(symbol_name);
std::vector<std::string>::const_iterator head_peace = std::cbegin(path);
if (std::find(std::next(head_peace), std::cend(path), *head_peace) != std::cend(path))
{
return error_mark_node;
}
auto looked_up = get_inner_alias(reference->reference, symbols, unresolved, path);
unresolved.insert({ symbol_name, looked_up });
@ -97,7 +107,8 @@ namespace elna::gcc
{
for (auto& [symbol_name, symbol_info] : declaration_visitor.unresolved)
{
handle_symbol(symbol_name, symbol_info, symbols, unresolved);
std::vector<std::string> type_path;
handle_symbol(symbol_name, symbol_info, symbols, unresolved, type_path);
}
}
return std::move(declaration_visitor.errors());
@ -803,9 +814,9 @@ namespace elna::gcc
}
tree return_type = void_type_node;
if (type.return_type.type != nullptr)
if (type.return_type.proper_type != nullptr)
{
type.return_type.type->accept(this);
type.return_type.proper_type->accept(this);
return_type = this->current_expression;
}
this->current_expression = NULL_TREE;

View File

@ -21,7 +21,6 @@ along with GCC; see the file COPYING3. If not see
#include <memory>
#include <string>
#include <vector>
#include <variant>
#include "elna/boot/result.h"
namespace elna::boot
@ -316,30 +315,19 @@ namespace elna::boot
virtual ~constant_definition() override;
};
/**
* Tags a procedure type as never returning.
*/
struct return_declaration
{
return_declaration() = default;
explicit return_declaration(std::shared_ptr<type_expression> type);
explicit return_declaration(std::monostate);
std::shared_ptr<type_expression> type{ nullptr };
bool no_return{ false };
};
/**
* Procedure type.
*/
class procedure_type_expression : public type_expression
{
public:
const return_declaration return_type;
using return_t = return_declaration<std::shared_ptr<type_expression>>;
const return_t return_type;
std::vector<std::shared_ptr<type_expression>> parameters;
procedure_type_expression(const struct position position,
return_declaration return_type = return_declaration());
return_t return_type = return_t());
void accept(parser_visitor *visitor) override;
std::shared_ptr<procedure_type_expression> is_procedure() override;

View File

@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#include <string>
#include <deque>
#include <memory>
#include <variant>
namespace elna::boot
{
@ -79,4 +80,27 @@ namespace elna::boot
m_errors.emplace_back(std::move(new_error));
}
};
/**
* Tags a procedure type as never returning.
*/
template<typename T>
struct return_declaration
{
return_declaration() = default;
explicit return_declaration(T type)
: proper_type(type)
{
}
explicit return_declaration(std::monostate)
: no_return(true)
{
}
T proper_type{};
bool no_return{ false };
};
}

View File

@ -63,7 +63,7 @@ namespace elna::boot
void visit(type_definition *definition) override;
void visit(record_type_expression *type_expression) override;
void visit(union_type_expression *type_expression) override;
void visit(procedure_type_expression *) override;
void visit(procedure_type_expression *type_expression) override;
void visit(variable_declaration *declaration) override;
void visit(constant_definition *) override;

View File

@ -23,6 +23,8 @@ along with GCC; see the file COPYING3. If not see
#include <memory>
#include <vector>
#include "elna/boot/result.h"
namespace elna::boot
{
class alias_type;
@ -73,6 +75,8 @@ namespace elna::boot
type(type&& other);
type& operator=(type&& other);
bool operator==(const std::nullptr_t&);
~type();
template<typename T>
@ -123,6 +127,16 @@ namespace elna::boot
std::vector<type_field> fields;
};
struct procedure_type
{
using return_t = return_declaration<type>;
std::vector<type> parameters;
const return_t return_type;
procedure_type(return_t return_type = return_t());
};
class type_info;
class info : public std::enable_shared_from_this<info>

View File

@ -36,7 +36,8 @@ namespace elna::gcc
std::unique_ptr<boot::program>& ast, std::shared_ptr<boot::symbol_table> info_table,
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved);
tree handle_symbol(const std::string& symbol_name, std::shared_ptr<boot::alias_type> reference,
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved);
std::shared_ptr<symbol_table> symbols, std::unordered_map<std::string, tree>& unresolved,
std::vector<std::string>& path);
class generic_visitor final : public boot::parser_visitor