diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-10-03 00:46:51 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-10-03 00:46:51 +0200 |
| commit | 128a564be05afb1cb2ec76884a81c2bd536899fd (patch) | |
| tree | 12bcd5e877aaff326edb253c6b53a3e8b762d699 | |
| parent | c8c9cb33f400031a697b4cad7ccc6b8555776e4f (diff) | |
| download | elna-128a564be05afb1cb2ec76884a81c2bd536899fd.tar.gz | |
Replace noreturn type with an attribute
| -rw-r--r-- | boot/ast.cc | 12 | ||||
| -rw-r--r-- | boot/lexer.ll | 3 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 76 | ||||
| -rw-r--r-- | boot/parser.yy | 10 | ||||
| -rw-r--r-- | boot/symbol.cc | 23 | ||||
| -rw-r--r-- | boot/type_check.cc | 37 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 6 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 6 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 87 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 24 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 9 | ||||
| -rw-r--r-- | source/cstdlib.elna | 2 | ||||
| -rw-r--r-- | testsuite/compilable/noreturn_procedure.elna | 6 | ||||
| -rw-r--r-- | testsuite/fail_compilation/noreturn_return.elna | 6 |
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. |
