aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-10-03 00:46:51 +0200
committerEugen Wissner <belka@caraus.de>2026-10-03 00:46:51 +0200
commit128a564be05afb1cb2ec76884a81c2bd536899fd (patch)
tree12bcd5e877aaff326edb253c6b53a3e8b762d699
parentc8c9cb33f400031a697b4cad7ccc6b8555776e4f (diff)
downloadelna-128a564be05afb1cb2ec76884a81c2bd536899fd.tar.gz
Replace noreturn type with an attribute
-rw-r--r--boot/ast.cc12
-rw-r--r--boot/lexer.ll3
-rw-r--r--boot/name_analysis.cc76
-rw-r--r--boot/parser.yy10
-rw-r--r--boot/symbol.cc23
-rw-r--r--boot/type_check.cc37
-rw-r--r--gcc/gcc/elna-builtins.cc6
-rw-r--r--include/elna/boot/ast.h6
-rw-r--r--include/elna/boot/name_analysis.h87
-rw-r--r--include/elna/boot/result.h24
-rw-r--r--include/elna/boot/symbol.h9
-rw-r--r--source/cstdlib.elna2
-rw-r--r--testsuite/compilable/noreturn_procedure.elna6
-rw-r--r--testsuite/fail_compilation/noreturn_return.elna6
14 files changed, 146 insertions, 161 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 87d6313..bdc9089 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -237,9 +237,9 @@ namespace elna::boot
{
parameter.second->accept(this);
}
- if (declaration->heading().return_type.proper_type != nullptr)
+ if (declaration->heading().return_type != nullptr)
{
- declaration->heading().return_type.proper_type->accept(this);
+ declaration->heading().return_type->accept(this);
}
if (declaration->body.has_value())
{
@@ -394,9 +394,9 @@ namespace elna::boot
{
field.second->accept(this);
}
- if (expression->return_type.proper_type != nullptr)
+ if (expression->return_type != nullptr)
{
- expression->return_type.proper_type->accept(this);
+ expression->return_type->accept(this);
}
}
@@ -949,14 +949,14 @@ namespace elna::boot
}
procedure_type_expression::procedure_type_expression(const source_position position,
- std::vector<field_declaration>&& parameters, return_t return_type)
+ std::vector<field_declaration>&& parameters, type_expression *return_type)
: node(position), return_type(return_type), parameters(std::move(parameters))
{
}
procedure_type_expression::~procedure_type_expression()
{
- delete return_type.proper_type;
+ delete return_type;
}
void procedure_type_expression::accept(parser_visitor *visitor)
diff --git a/boot/lexer.ll b/boot/lexer.ll
index 6fa1904..4452956 100644
--- a/boot/lexer.ll
+++ b/boot/lexer.ll
@@ -476,9 +476,6 @@ module {
@ {
return yy::parser::make_AT(this->location);
}
-! {
- return yy::parser::make_EXCLAMATION(this->location);
-}
-> {
return yy::parser::make_ARROW(this->location);
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 20f08ee..1a87927 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -90,6 +90,8 @@ namespace elna::boot
return "Attribute '" + this->name + "' takes no arguments";
case missing_parameter:
return "Attribute '" + this->name + "' requires at least one parameter";
+ case return_type:
+ return "Attribute '" + this->name + "' cannot be used with a return type";
default:
__builtin_unreachable();
}
@@ -317,53 +319,6 @@ namespace elna::boot
{
}
- procedure_type::return_t resolving_visitor::build_return_type(
- const procedure_type_expression::return_t& return_type)
- {
- if (return_type.no_return)
- {
- return procedure_type::return_t(std::monostate{});
- }
- else if (return_type.proper_type != nullptr)
- {
- return procedure_type::return_t(resolve_type(*return_type.proper_type));
- }
- else
- {
- return procedure_type::return_t();
- }
- }
-
- std::pair<procedure_type, std::vector<std::string>> resolving_visitor::build_procedure(
- procedure_type_expression& expression, const bool variadic_supported)
- {
- const procedure_type::return_t result_return = build_return_type(expression.return_type);
- std::pair<procedure_type, std::vector<std::string>> result_type{
- procedure_type(result_return), std::vector<std::string>()
- };
- for (const auto& [parameter_names, parameters_type] : expression.parameters)
- {
- const type parameter_type = resolve_type(*parameters_type);
-
- for (const auto& parameter_name : parameter_names)
- {
- evaluate_attributes<>(parameter_name.attributes);
- result_type.first.parameters.push_back(parameter_type);
- result_type.second.push_back(parameter_name.name());
- }
- }
- result_type.first.variadic = variadic_supported
- ? evaluate_attributes<attribute_kind::varargs>(expression.attributes).variadic
- : evaluate_attributes<>(expression.attributes).variadic;
-
- if (result_type.first.variadic && result_type.first.parameters.empty())
- {
- add_error<attribute_error>(expression.position(), "varargs",
- attribute_error::kind::missing_parameter);
- }
- return result_type;
- }
-
std::optional<attribute_kind> resolving_visitor::parse_attribute(const std::string& name)
{
if (name == "aligned")
@@ -374,6 +329,10 @@ namespace elna::boot
{
return attribute_kind::varargs;
}
+ if (name == "noreturn")
+ {
+ return attribute_kind::noreturn;
+ }
return std::nullopt;
}
@@ -676,16 +635,15 @@ namespace elna::boot
ordered_map<field_origin> field_names;
collect_field_names(result_type->base, field_names);
result_type->fields = build_composite_type(expression->fields, field_names, type(result_type));
- result_type->alignment =
- evaluate_attributes<attribute_kind::aligned>(expression->attributes).alignment;
+ result_type->alignment = evaluate_attributes<attribute_kind::aligned>(expression->attributes).alignment;
this->current_type = type(result_type);
}
void resolving_visitor::visit(procedure_type_expression *expression)
{
- std::shared_ptr<procedure_type> const result_type =
- std::make_shared<procedure_type>(std::move(build_procedure(*expression, true).first));
+ const std::shared_ptr<procedure_type> result_type =
+ std::make_shared<procedure_type>(build_procedure<attribute_kind::varargs>(*expression).heading);
this->current_type = type(result_type);
}
@@ -851,7 +809,7 @@ namespace elna::boot
call->callable().accept(this);
if (const auto procedure = call->callable().type_decoration.get<procedure_type>())
{
- call->type_decoration = procedure->return_type.proper_type;
+ call->type_decoration = procedure->return_type;
}
visit_all(this, call->arguments);
this->current_type = type();
@@ -1290,10 +1248,10 @@ namespace elna::boot
return false;
}
}
- if (!link->return_type.proper_type.empty())
+ if (!link->return_type.empty())
{
alias_path.resize(saved_path);
- return find_alias_cycle(being_resolved, link->return_type.proper_type, alias_path, state);
+ return find_alias_cycle(being_resolved, link->return_type, alias_path, state);
}
return true;
}
@@ -1328,8 +1286,7 @@ namespace elna::boot
if (unit->entry_point.has_value())
{
const type word8_primitive = lookup_primitive_type("Word8");
- const procedure_type::return_t result_return = procedure_type::return_t(word8_primitive);
- auto heading = procedure_type(result_return);
+ auto heading = procedure_type(word8_primitive);
std::shared_ptr<procedure_info> info;
if (unit->parameters.size() > 1)
@@ -1451,8 +1408,9 @@ namespace elna::boot
// is why the body sees them too.
const std::shared_ptr<symbol_table> scope = this->bag.enter();
const std::vector<type> type_parameters = enter_parameters(declaration->parameters);
- auto [heading, parameter_names] =
- build_procedure(declaration->heading(), !declaration->body.has_value());
+ auto [heading, parameter_names, noreturn] = !declaration->body.has_value()
+ ? build_procedure<attribute_kind::varargs, attribute_kind::noreturn>(declaration->heading())
+ : build_procedure<attribute_kind::noreturn>(declaration->heading());
std::shared_ptr<procedure_info> info_object;
if (declaration->body.has_value())
@@ -1480,6 +1438,8 @@ namespace elna::boot
info_object->exported = declaration->identifier.exported();
info_object->position.emplace(declaration->position());
info_object->file = this->module_file;
+ info_object->noreturn = noreturn;
+
if (!this->bag.enter(declaration->identifier.name(), info_object))
{
const std::shared_ptr<info> original = this->bag.lookup(declaration->identifier.name());
diff --git a/boot/parser.yy b/boot/parser.yy
index cc73aef..04c07a8 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -91,9 +91,7 @@ along with GCC; see the file COPYING3. If not see
%token <bool> BOOLEAN
%token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]"
LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" ATTRIBUTE_OPEN "#{"
-%token ASSIGNMENT ":="
- EXCLAMATION "!" ARROW "->"
- AT "@" HAT "^"
+%token ASSIGNMENT ":=" ARROW "->" AT "@" HAT "^"
COLON ":" SEMICOLON ";" DOT "." COMMA ","
%token PROGRAM "program" MODULE "module"
CAST "cast"
@@ -135,7 +133,7 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::switch_case>> switch_cases;
%type <elna::boot::variable_declaration *> variable_declaration;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations;
-%type <elna::boot::type_expression *> type_expression;
+%type <elna::boot::type_expression *> type_expression return_declaration;
%type <std::vector<elna::boot::type_expression *>> type_expressions;
%type <elna::boot::expression *> expression operand simple_expression procedure_return unary_expression;
%type <elna::boot::binary_expression *> binary_expression;
@@ -146,7 +144,6 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::statement *>> statements statement_part optional_begin_statements;
%type <elna::boot::procedure_declaration *> procedure_declaration;
%type <elna::boot::procedure_type_expression *> procedure_heading;
-%type <elna::boot::procedure_type_expression::return_t> return_declaration;
%type <elna::boot::type_declaration *> type_declaration;
%type <std::vector<elna::boot::declaration *>> type_declarations sections section;
%type <std::pair<std::vector<elna::boot::import_declaration *>, std::vector<elna::boot::declaration *>>>
@@ -274,8 +271,7 @@ identifier_definitions:
| attributed_identifier { $$.emplace_back(std::move(*$1)); }
return_declaration:
%empty {}
- | "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); }
- | "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); }
+ | "->" type_expression { $$ = $2; }
procedure_heading:
"(" field_declarations ")" attributes return_declaration
{
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 54cb595..2293275 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -199,13 +199,9 @@ namespace elna::boot
{
result += " #{varargs}";
}
- if (payload->return_type.no_return)
+ else if (!payload->return_type.empty())
{
- result += " -> !";
- }
- else if (!payload->return_type.proper_type.empty())
- {
- result += " -> " + payload->return_type.proper_type.to_string();
+ result += " -> " + payload->return_type.to_string();
}
return result;
}
@@ -272,7 +268,7 @@ namespace elna::boot
{
}
- procedure_type::procedure_type(return_t return_type)
+ procedure_type::procedure_type(type return_type)
: return_type(std::move(return_type))
{
}
@@ -414,14 +410,14 @@ namespace elna::boot
const type boolean = type(std::make_shared<primitive_type>("Bool", target.bool_properties));
result->enter("Bool", std::make_shared<type_info>(boolean));
- procedure_type assert_symbol{ procedure_type::return_t() };
+ procedure_type assert_symbol{};
assert_symbol.parameters.push_back(boolean);
std::shared_ptr<procedure_info> const assert_info = std::make_shared<procedure_info>(assert_symbol,
std::vector<std::string>{ "condition" });
result->enter("assert", assert_info);
const type load_parameter = type(std::make_shared<parameter_type>("T"));
- procedure_type volatile_load_symbol{ procedure_type::return_t(load_parameter) };
+ procedure_type volatile_load_symbol{ load_parameter };
volatile_load_symbol.parameters.emplace_back(std::make_shared<pointer_type>(load_parameter));
std::shared_ptr<procedure_info> const volatile_load_info =
std::make_shared<procedure_info>(volatile_load_symbol, std::vector<std::string>{ "source" });
@@ -429,7 +425,7 @@ namespace elna::boot
result->enter("volatile_load", volatile_load_info);
const type store_parameter = type(std::make_shared<parameter_type>("T"));
- procedure_type volatile_store_symbol{ procedure_type::return_t() };
+ procedure_type volatile_store_symbol{};
volatile_store_symbol.parameters.emplace_back(std::make_shared<pointer_type>(store_parameter));
volatile_store_symbol.parameters.push_back(store_parameter);
std::shared_ptr<procedure_info> const volatile_store_info =
@@ -626,12 +622,11 @@ namespace elna::boot
}
else if (const auto procedure = subject.get<procedure_type>())
{
- procedure_type::return_t result_return = procedure->return_type;
+ type result_return = procedure->return_type;
- if (!result_return.no_return && !result_return.proper_type.empty())
+ if (!result_return.empty())
{
- result_return = procedure_type::return_t(
- substitute(result_return.proper_type, parameters, arguments));
+ result_return = substitute(result_return, parameters, arguments);
}
const std::shared_ptr<procedure_type> result = std::make_shared<procedure_type>(std::move(result_return));
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 15a01dc..ea0bcd5 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -413,8 +413,8 @@ namespace elna::boot
return opaque;
}
}
- return !procedure->return_type.proper_type.empty()
- ? find_opaque_type(procedure->return_type.proper_type)
+ return !procedure->return_type.empty()
+ ? find_opaque_type(procedure->return_type)
: std::nullopt;
}
return std::nullopt;
@@ -723,18 +723,18 @@ namespace elna::boot
}
++parameter_index;
}
- if (declaration->heading().return_type.proper_type != nullptr)
+ if (declaration->heading().return_type != nullptr)
{
- if (auto opaque = find_opaque_type(this->current_procedure->symbol.return_type.proper_type))
+ if (auto opaque = find_opaque_type(this->current_procedure->symbol.return_type))
{
add_error<type_requirement_error>(
- declaration->heading().return_type.proper_type->position(),
+ declaration->heading().return_type->position(),
opaque.value(), type_requirement_error::kind::opaque_return);
}
- else if (auto found = find_zero_sized(this->current_procedure->symbol.return_type.proper_type, this->target))
+ else if (auto found = find_zero_sized(this->current_procedure->symbol.return_type, this->target))
{
add_error<type_requirement_error>(
- declaration->heading().return_type.proper_type->position(),
+ declaration->heading().return_type->position(),
found.value(), type_requirement_error::kind::zero_sized);
}
}
@@ -750,7 +750,7 @@ namespace elna::boot
void type_analysis_visitor::check_return(const procedure_body& body,
const source_position position, const std::string& name)
{
- const type return_type = this->current_procedure->symbol.return_type.proper_type;
+ const type return_type = this->current_procedure->symbol.return_type;
if (body.return_expression == nullptr)
{
@@ -990,18 +990,17 @@ namespace elna::boot
parameter, type_requirement_error::kind::zero_sized);
}
}
- if (!procedure->return_type.proper_type.empty())
+ if (!procedure->return_type.empty())
{
- if (const auto opaque = find_opaque_type(procedure->return_type.proper_type))
+ if (const auto opaque = find_opaque_type(procedure->return_type))
{
add_error<type_requirement_error>(declaration->position(),
opaque.value(), type_requirement_error::kind::opaque_return);
}
- else if (has_zero_size(procedure->return_type.proper_type, this->target))
+ else if (has_zero_size(procedure->return_type, this->target))
{
add_error<type_requirement_error>(declaration->position(),
- procedure->return_type.proper_type,
- type_requirement_error::kind::zero_sized);
+ procedure->return_type, type_requirement_error::kind::zero_sized);
}
}
}
@@ -1079,8 +1078,8 @@ namespace elna::boot
return true;
}
}
- return !procedure->return_type.proper_type.empty()
- && occurs_in(parameter, procedure->return_type.proper_type);
+ return !procedure->return_type.empty()
+ && occurs_in(parameter, procedure->return_type);
}
else if (const auto instance = subject.get<instantiated_type>())
{
@@ -1206,10 +1205,8 @@ namespace elna::boot
return false;
}
}
- return left_procedure->return_type.proper_type.empty()
- || bind_arguments(parameters, bindings,
- left_procedure->return_type.proper_type,
- right_procedure->return_type.proper_type);
+ return left_procedure->return_type.empty()
+ || bind_arguments(parameters, bindings, left_procedure->return_type, right_procedure->return_type);
}
return true;
}
@@ -1424,7 +1421,7 @@ namespace elna::boot
// just bound.
if (const auto substituted = callable_type.get<procedure_type>())
{
- call->type_decoration = substituted->return_type.proper_type;
+ call->type_decoration = substituted->return_type;
}
}
}
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index ee6856d..4a50967 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -176,9 +176,9 @@ namespace elna::gcc
}
tree return_type = void_type_node;
- if (!procedure.return_type.proper_type.empty())
+ if (!procedure.return_type.empty())
{
- return_type = get_inner_alias(procedure.return_type.proper_type, symbols);
+ return_type = get_inner_alias(procedure.return_type, symbols);
}
if (procedure.variadic)
{
@@ -344,7 +344,7 @@ namespace elna::gcc
get_identifier(name.c_str()), declaration_type);
symbols->enter(name, fndecl);
- if (info.symbol.return_type.no_return)
+ if (info.noreturn)
{
TREE_THIS_VOLATILE(fndecl) = 1;
}
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 8c0ba05..fba8ad4 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -668,15 +668,13 @@ namespace elna::boot
class procedure_type_expression : public type_expression
{
public:
- using return_t = return_declaration<type_expression *>;
-
- const return_t return_type;
+ type_expression *const return_type;
const std::vector<field_declaration> parameters;
/// Attributes attached to the procedure type.
std::vector<attribute> attributes;
procedure_type_expression(const source_position position,
- std::vector<field_declaration>&& parameters, return_t return_type = return_t());
+ std::vector<field_declaration>&& parameters, type_expression *return_type = nullptr);
~procedure_type_expression() override;
void accept(parser_visitor *visitor) override;
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index c726a4f..4e13df1 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -75,7 +75,8 @@ namespace elna::boot
enum class attribute_kind
{
aligned,
- varargs
+ varargs,
+ noreturn
};
/**
@@ -93,7 +94,8 @@ namespace elna::boot
not_constant,
invalid_alignment,
unexpected_arguments,
- missing_parameter
+ missing_parameter,
+ return_type
};
attribute_error(const source_position position, const std::string& name, kind attribute_kind);
@@ -198,6 +200,26 @@ namespace elna::boot
void visit(procedure_declaration *) override;
};
+ /*
+ * What the attributes attached to a declaration decide.
+ */
+ struct attribute_values
+ {
+ std::optional<std::size_t> alignment;
+ std::optional<source_position> variadic;
+ std::optional<source_position> noreturn;
+ };
+
+ /**
+ * Information collected from the procedure declaration.
+ */
+ struct procedure_signature
+ {
+ procedure_type heading;
+ std::vector<std::string> parameter_names;
+ bool noreturn;
+ };
+
/**
* Shared name resolution machinery for the declaration and the body passes.
*
@@ -217,20 +239,46 @@ namespace elna::boot
resolving_visitor(symbol_bag& bag, const target_info& target,
const std::filesystem::path& module_path);
- procedure_type::return_t build_return_type(
- const procedure_type_expression::return_t& return_type);
- std::pair<procedure_type, std::vector<std::string>> build_procedure(
- procedure_type_expression& expression, bool variadic_supported);
+ template<attribute_kind... supported>
+ procedure_signature build_procedure(procedure_type_expression& expression)
+ {
+ const type result_return = expression.return_type == nullptr
+ ? type()
+ : resolve_type(*expression.return_type);
+ const attribute_values attributes_result = evaluate_attributes<supported...>(expression.attributes);
+ procedure_signature result_type{
+ .heading = procedure_type(result_return),
+ .parameter_names = std::vector<std::string>(),
+ .noreturn = attributes_result.noreturn.has_value()
+ };
+ for (const auto& [parameter_names, parameters_type] : expression.parameters)
+ {
+ const type parameter_type = resolve_type(*parameters_type);
+
+ for (const auto& parameter_name : parameter_names)
+ {
+ evaluate_attributes<>(parameter_name.attributes);
+ result_type.heading.parameters.push_back(parameter_type);
+ result_type.parameter_names.push_back(parameter_name.name());
+ }
+ }
+ if (attributes_result.variadic.has_value() && result_type.heading.parameters.empty())
+ {
+ add_error<attribute_error>(attributes_result.variadic.value(), "varargs",
+ attribute_error::kind::missing_parameter);
+ }
+ if (attributes_result.noreturn.has_value() && expression.return_type != nullptr)
+ {
+ add_error<attribute_error>(attributes_result.noreturn.value(), "noreturn",
+ attribute_error::kind::return_type);
+ }
+ result_type.heading.variadic = attributes_result.variadic.has_value();
+
+ return result_type;
+ }
+
ordered_map<field_info> build_composite_type(const std::vector<field_declaration>& fields,
ordered_map<field_origin>& field_names, const type& aggregate);
- /*
- * What the attributes attached to a declaration decide.
- */
- struct attribute_values
- {
- std::optional<std::size_t> alignment;
- bool variadic{ false };
- };
static std::optional<attribute_kind> parse_attribute(const std::string& name);
/*
* Reads the attributes attached to a declaration, reporting the ones
@@ -268,13 +316,18 @@ namespace elna::boot
{
add_error<attribute_error>(position, name, attribute_error::kind::unexpected_arguments);
}
- else if (result.variadic)
+ else if ((result.variadic && written_kind.value() == attribute_kind::varargs)
+ || (result.noreturn && written_kind.value() == attribute_kind::noreturn))
{
add_error<attribute_error>(position, name, attribute_error::kind::duplicate);
}
- else
+ else if (written_kind.value() == attribute_kind::varargs)
+ {
+ result.variadic = std::optional(position);
+ }
+ else if (written_kind.value() == attribute_kind::noreturn)
{
- result.variadic = true;
+ result.noreturn = std::optional(position);
}
}
return result;
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index d0a550a..c7b79d3 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -161,30 +161,6 @@ namespace elna::boot
bool has_errors() const;
};
- /**
- * Tags a procedure type as never returning.
- */
- template<typename T>
- struct return_declaration
- {
- return_declaration() = default;
-
- explicit return_declaration(T type)
- : proper_type(std::move(type))
- {
- }
-
- explicit return_declaration(std::monostate)
- : no_return(true)
- {
- }
-
- bool operator==(const return_declaration&) const = default;
-
- T proper_type{};
- bool no_return{ false };
- };
-
struct identifier
{
identifier(const std::string& name, const source_position& position);
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 38aee08..fd56d98 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -197,15 +197,13 @@ namespace elna::boot
struct procedure_type
{
- using return_t = return_declaration<type>;
-
std::vector<type> parameters;
- const return_t return_type;
+ const type return_type;
/// Whether further arguments may follow the parameters, as with a C
/// variadic function.
bool variadic{ false };
- explicit procedure_type(return_t return_type = return_t());
+ explicit procedure_type(type return_type = type());
};
struct enumeration_type
@@ -454,6 +452,9 @@ namespace elna::boot
/// plain procedure.
std::vector<type> parameters;
+ /// Annotated as a noreturn procedure.
+ bool noreturn{ false };
+
/**
* Constructs procedure symbol information.
*
diff --git a/source/cstdlib.elna b/source/cstdlib.elna
index 9b858e7..51f8ea0 100644
--- a/source/cstdlib.elna
+++ b/source/cstdlib.elna
@@ -18,7 +18,7 @@ extern
proc atoi*(str: ^Word8) -> Int
extern
-proc exit*(code: Int) -> !
+proc exit*(code: Int) #{noreturn}
extern
end.
diff --git a/testsuite/compilable/noreturn_procedure.elna b/testsuite/compilable/noreturn_procedure.elna
new file mode 100644
index 0000000..3604f0f
--- /dev/null
+++ b/testsuite/compilable/noreturn_procedure.elna
@@ -0,0 +1,6 @@
+module
+
+proc exit() #{noreturn}
+return
+
+end.
diff --git a/testsuite/fail_compilation/noreturn_return.elna b/testsuite/fail_compilation/noreturn_return.elna
new file mode 100644
index 0000000..7ce50aa
--- /dev/null
+++ b/testsuite/fail_compilation/noreturn_return.elna
@@ -0,0 +1,6 @@
+module
+
+proc die() #{noreturn} -> Int (* @Error Attribute 'noreturn' cannot be used with a return type *)
+extern
+
+end.