diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-12 00:18:11 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-12 00:18:11 +0200 |
| commit | eb1d068beeaaa420b558595751295eec35ad0c3c (patch) | |
| tree | a10aadd88ba358726573f7324ff6f9c489ebad77 | |
| parent | bd2b80d6ac582b9f0bcd96c86d4b79bfdb64eca1 (diff) | |
| download | elna-eb1d068beeaaa420b558595751295eec35ad0c3c.tar.gz | |
Support variadic extern procedures
| -rw-r--r-- | boot/lexer.ll | 3 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 112 | ||||
| -rw-r--r-- | boot/parser.yy | 48 | ||||
| -rw-r--r-- | boot/symbol.cc | 8 | ||||
| -rw-r--r-- | boot/type_check.cc | 44 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 8 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 25 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 9 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 77 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 2 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 3 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 8 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 3 | ||||
| -rw-r--r-- | testsuite/fail_compilation/aligned_attribute.elna | 8 | ||||
| -rw-r--r-- | testsuite/fail_compilation/varargs_argument.elna | 12 | ||||
| -rw-r--r-- | testsuite/fail_compilation/varargs_attribute.elna | 8 | ||||
| -rw-r--r-- | testsuite/runnable/aligned_attribute.elna | 8 | ||||
| -rw-r--r-- | testsuite/runnable/varargs.elna | 16 |
18 files changed, 292 insertions, 110 deletions
diff --git a/boot/lexer.ll b/boot/lexer.ll index 17fe0ca..e8dde7c 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -184,6 +184,9 @@ program { #\[ { return yy::parser::make_GENERIC_OPEN(this->location); } +#\{ { + return yy::parser::make_ATTRIBUTE_OPEN(this->location); +} (0|{NATURAL})u|{HEXADECIMAL} { if (auto result = parse_integer<std::uint64_t>(yytext, 0)) { diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index caff18b..4a1a0ca 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -69,17 +69,21 @@ namespace elna::boot { using enum kind; case unknown: - return "Attribute '#" + this->name + "' not declared"; + return "Attribute '" + this->name + "' not declared"; case duplicate: - return "Attribute '#" + this->name + "' specified more than once"; + return "Attribute '" + this->name + "' specified more than once"; case unsupported: - return "Attribute '#" + this->name + "' is not supported in this position"; + return "Attribute '" + this->name + "' is not supported in this position"; case argument_count: - return "Attribute '#" + this->name + "' takes exactly one argument"; + return "Attribute '" + this->name + "' takes exactly one argument"; case not_constant: - return "Attribute '#" + this->name + "' requires a constant expression"; + return "Attribute '" + this->name + "' requires a constant expression"; case invalid_alignment: return "Alignment must be a power of two"; + case unexpected_arguments: + return "Attribute '" + this->name + "' takes no arguments"; + case missing_parameter: + return "Attribute '" + this->name + "' requires at least one parameter"; default: __builtin_unreachable(); } @@ -320,7 +324,7 @@ namespace elna::boot } std::pair<procedure_type, std::vector<std::string>> resolving_visitor::build_procedure( - procedure_type_expression& expression) + 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{ @@ -332,65 +336,70 @@ namespace elna::boot for (const auto& parameter_name : parameter_names) { - evaluate_alignment(parameter_name.attributes, false); + 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<std::size_t> resolving_visitor::evaluate_alignment( - const std::vector<attribute>& attributes, const bool supported) + std::optional<attribute_kind> resolving_visitor::parse_attribute(const std::string& name) { - std::optional<std::size_t> result; + if (name == "aligned") + { + return attribute_kind::aligned; + } + if (name == "varargs") + { + return attribute_kind::varargs; + } + return std::nullopt; + } - for (const attribute& written : attributes) + void resolving_visitor::evaluate_alignment(const attribute& written, std::optional<std::size_t>& alignment) + { + if (written.arguments().size() != 1) { - if (written.name().name() != "aligned") - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::unknown); - } - else if (!supported) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::unsupported); - } - else if (written.arguments().size() != 1) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::argument_count); - } - else if (result.has_value()) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::duplicate); - } - else if (auto value = this->constant_evaluator.evaluate_index(*written.arguments().front())) + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::argument_count); + } + else if (alignment.has_value()) + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::duplicate); + } + else if (auto value = this->constant_evaluator.evaluate_index(*written.arguments().front())) + { + if (value.value() == 0 || (value.value() & (value.value() - 1)) != 0) { - if (value.value() == 0 || (value.value() & (value.value() - 1)) != 0) - { - add_error<attribute_error>(written.arguments().front()->position(), - written.name().name(), attribute_error::kind::invalid_alignment); - } - else - { - result = value; - } + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::invalid_alignment); } else { - add_error<attribute_error>(written.arguments().front()->position(), - written.name().name(), attribute_error::kind::not_constant); + alignment = value; } } - return result; + else + { + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::not_constant); + } } ordered_map<field_info> resolving_visitor::build_composite_type( const std::vector<field_declaration>& fields, - ordered_map<field_origin>& field_names, const type& aggregate, const bool supported) + ordered_map<field_origin>& field_names, const type& aggregate) { ordered_map<field_info> result; @@ -401,7 +410,7 @@ namespace elna::boot for (const identifier_definition& field_name : field.first) { const std::optional<std::size_t> alignment = - evaluate_alignment(field_name.attributes, supported); + evaluate_attributes<attribute_kind::aligned>(field_name.attributes).alignment; auto [existing, inserted] = field_names.insert(field_name.name(), field_origin{ .position = field.second->position(), .base_type = type() }); @@ -522,7 +531,8 @@ namespace elna::boot auto variable_symbol = register_variable(variable_identifier.name(), variable_type, declaration->position(), declaration->is_extern); variable_symbol->exported = variable_identifier.exported(); - variable_symbol->alignment = evaluate_alignment(variable_identifier.attributes, true); + variable_symbol->alignment = + evaluate_attributes<attribute_kind::aligned>(variable_identifier.attributes).alignment; variable_symbol->value = computed; } } @@ -690,7 +700,8 @@ 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_alignment(expression->attributes, true); + result_type->alignment = + evaluate_attributes<attribute_kind::aligned>(expression->attributes).alignment; this->current_type = type(result_type); } @@ -698,7 +709,7 @@ namespace elna::boot 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).first)); + std::make_shared<procedure_type>(std::move(build_procedure(*expression, true).first)); this->current_type = type(result_type); } @@ -1418,7 +1429,8 @@ 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()); + auto [heading, parameter_names] = + build_procedure(declaration->heading(), !declaration->body.has_value()); std::shared_ptr<procedure_info> info; if (declaration->body.has_value()) diff --git a/boot/parser.yy b/boot/parser.yy index 21ff827..cc0a46c 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -88,7 +88,7 @@ along with GCC; see the file COPYING3. If not see %token <std::string> STRING %token <bool> BOOLEAN %token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]" - LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" + LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" ATTRIBUTE_OPEN "#{" %token ASSIGNMENT ":=" EXCLAMATION "!" ARROW "->" AT "@" HAT "^" @@ -190,9 +190,9 @@ identifier_definition: IDENTIFIER "*" { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), true); } | IDENTIFIER { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), false); } attribute: - TRAIT "(" required_expressions ")" + "#{" IDENTIFIER optional_expressions "}" { - $$ = std::make_unique<boot::attribute>(boot::identifier($1, boot::make_position(@1)), $3); + $$ = std::make_unique<boot::attribute>(boot::identifier($2, boot::make_position(@2)), $3); } attributes: %empty {} @@ -202,17 +202,10 @@ attributes: $$.emplace($$.cbegin(), std::move(*$1)); } attributed_identifier: - attributes identifier_definition + identifier_definition attributes { - $$ = $2; - $$->attributes = $1; - - // An empty rule is located where the previous token ended, which - // would push the declaration's position back over it. - if ($$->attributes.empty()) - { - @$ = @2; - } + $$ = $1; + $$->attributes = $2; } identifier_definitions: attributed_identifier "," identifier_definitions @@ -225,8 +218,11 @@ return_declaration: %empty {} | "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } | "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); } -procedure_heading: "(" field_declarations ")" return_declaration - { $$ = std::make_unique<boot::procedure_type_expression>(boot::make_position(@$), $2, $4); } +procedure_heading: "(" field_declarations ")" attributes return_declaration + { + $$ = std::make_unique<boot::procedure_type_expression>(boot::make_position(@$), $2, $5); + $$->attributes = $4; + } procedure_declaration: "proc" identifier_definition generic_parameters procedure_heading procedure_body { @@ -616,33 +612,21 @@ type_expression: { $$ = new boot::pointer_type_expression(boot::make_position(@$), $2); } - | attributes "record" field_declarations "end" + | "record" attributes field_declarations "end" { - std::vector<boot::attribute> written = $1; - - if (written.empty()) - { - @$ = @2 + @4; - } auto *record = new boot::record_type_expression(boot::make_position(@$), $3); - record->attributes = std::move(written); + record->attributes = $2; $$ = record; } - | attributes "record" "(" name_reference ")" field_declarations "end" + | "record" "(" name_reference ")" attributes field_declarations "end" { - std::vector<boot::attribute> written = $1; - - if (written.empty()) - { - @$ = @2 + @7; - } - boot::named_expression *reference = $4; + boot::named_expression *reference = $3; auto *record = new boot::record_type_expression(boot::make_position(@$), $6, boot::identifier(reference->name, reference->position()), std::move(reference->arguments)); - record->attributes = std::move(written); + record->attributes = $5; delete reference; $$ = record; } diff --git a/boot/symbol.cc b/boot/symbol.cc index 0814217..466715b 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -117,7 +117,8 @@ namespace elna::boot auto right_procedure = resolved_that.get<procedure_type>(); return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type - && left_procedure->parameters == right_procedure->parameters; + && left_procedure->parameters == right_procedure->parameters + && left_procedure->variadic == right_procedure->variadic; } if (auto left_parameter = resolved_this.get<parameter_type>()) { @@ -190,6 +191,10 @@ namespace elna::boot { std::string result = "proc(" + join(payload->parameters) + ")"; + if (payload->variadic) + { + result += " #{varargs}"; + } if (payload->return_type.no_return) { result += " -> !"; @@ -578,6 +583,7 @@ namespace elna::boot } auto result = std::make_shared<procedure_type>(std::move(result_return)); + result->variadic = procedure->variadic; result->parameters.reserve(procedure->parameters.size()); for (const type& parameter_type : procedure->parameters) { diff --git a/boot/type_check.cc b/boot/type_check.cc index 8847693..5cdd2a2 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/evaluator.h" #include "elna/boot/name_analysis.h" +#include <climits> #include <ranges> #include <utility> @@ -286,6 +287,9 @@ namespace elna::boot case not_addressable: return "Expression of type '" + this->actual.to_string() + "' is not addressable"; + case variadic_argument: + return "Type '" + this->actual.to_string() + + "' cannot be passed as a variadic argument"; default: __builtin_unreachable(); } @@ -1337,6 +1341,34 @@ namespace elna::boot procedure.parameters, bindings); } + type type_analysis_visitor::promote_variadic_argument(const expression& argument) + { + const type resolved = resolve_underlying_type(argument.type_decoration); + + if (resolved.get<array_type>() != nullptr || resolved.get<slice_type>() != nullptr) + { + add_error<type_requirement_error>(argument.position(), argument.type_decoration, + type_requirement_error::kind::variadic_argument); + return type(); + } + if (is_primitive_type(resolved, "Single")) + { + return this->bag.lookup("Double")->is_type()->symbol; + } + if (is_discrete_type(resolved)) + { + const std::optional<type_properties> properties = get_type_properties(resolved, this->target); + + if (properties.has_value() && properties->size < this->target.c_int_properties.size) + { + const std::size_t bit_size = this->target.c_int_properties.size * CHAR_BIT; + + return this->bag.lookup("Int" + std::to_string(bit_size))->is_type()->symbol; + } + } + return argument.type_decoration; + } + void type_analysis_visitor::visit(procedure_call *call) { const named_expression *reference = call->callable().is_named(); @@ -1388,7 +1420,17 @@ namespace elna::boot ++argument_iterator; ++type_iterator; } - if (call->arguments.size() != procedure->parameters.size()) + while (procedure->variadic && argument_iterator != std::cend(call->arguments)) + { + if (inferred == nullptr) + { + (*argument_iterator)->accept(this); + } + call->variadic_types.push_back(promote_variadic_argument(**argument_iterator)); + ++argument_iterator; + } + if (call->arguments.size() < procedure->parameters.size() + || (!procedure->variadic && call->arguments.size() > procedure->parameters.size())) { if (auto *procedure_name = call->callable().is_named()) { diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 2dc1147..a8e54c8 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -59,7 +59,8 @@ namespace elna::gcc .size = static_cast<std::size_t>( (TYPE_PRECISION(elna_bool_type_node) + BITS_PER_UNIT - 1) / BITS_PER_UNIT), .alignment = TYPE_ALIGN_UNIT(elna_bool_type_node) - } + }, + .c_int_properties = get_host_numeric_properties(integer_type_node) }; return info; } @@ -176,6 +177,11 @@ namespace elna::gcc { return_type = get_inner_alias(procedure.return_type.proper_type, symbols); } + if (procedure.variadic) + { + return build_varargs_function_type_array(return_type, + static_cast<int>(procedure.parameters.size()), parameter_types.data()); + } return build_function_type_array(return_type, static_cast<int>(procedure.parameters.size()), parameter_types.data()); } diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 8ab6762..8fd2ec8 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -43,16 +43,29 @@ namespace elna::gcc } void generic_visitor::build_procedure_call(location_t call_location, - tree procedure_address, const std::vector<boot::expression *>& arguments) + tree procedure_address, const std::vector<boot::expression *>& arguments, + const std::vector<boot::type>& variadic_types) { vec<tree, va_gc> *argument_trees = nullptr; tree symbol_type = TREE_TYPE(TREE_TYPE(procedure_address)); + const std::size_t fixed_count = arguments.size() - variadic_types.size(); vec_alloc(argument_trees, arguments.size()); - for (boot::expression *const argument : arguments) + for (std::size_t i = 0; i < arguments.size(); ++i) { - argument->accept(this); + arguments.at(i)->accept(this); this->current_expression = prepare_rvalue(this->current_expression); + + if (i >= fixed_count) + { + tree passed_type = get_inner_alias(variadic_types.at(i - fixed_count), this->symbols); + + if (passed_type != TREE_TYPE(this->current_expression) + && (INTEGRAL_TYPE_P(passed_type) || SCALAR_FLOAT_TYPE_P(passed_type))) + { + this->current_expression = fold_convert(passed_type, this->current_expression); + } + } argument_trees->quick_push(this->current_expression); } this->current_expression = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type), @@ -114,11 +127,13 @@ namespace elna::gcc { this->current_expression = build1(ADDR_EXPR, build_pointer_type(expression_type), this->current_expression); - build_procedure_call(call_location, this->current_expression, call->arguments); + build_procedure_call(call_location, this->current_expression, + call->arguments, call->variadic_types); } else if (POINTER_TYPE_P(expression_type) && TREE_CODE(TREE_TYPE(expression_type)) == FUNCTION_TYPE) { - build_procedure_call(call_location, this->current_expression, call->arguments); + build_procedure_call(call_location, this->current_expression, + call->arguments, call->variadic_types); } else { diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 76fb94e..8f5d863 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -374,7 +374,7 @@ namespace elna::boot const identifier& id() const; bool exported() const; /** - * \return Attributes written in front of the identifier. + * \return Attributes attached to the identifier. */ std::vector<attribute> attributes; @@ -501,7 +501,7 @@ namespace elna::boot * absent. */ std::vector<type_expression *> base_arguments; - /// Attributes written in front of the \c record keyword. + /// Attributes attached to the record itself. std::vector<attribute> attributes; record_type_expression(const source_position position, @@ -642,6 +642,8 @@ namespace elna::boot const return_t 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()); @@ -911,6 +913,9 @@ namespace elna::boot public: std::vector<expression *> arguments; + /// Types the arguments after the parameters of a variadic procedure + /// are passed as, set by the type checker. + std::vector<type> variadic_types; procedure_call(const source_position position, designator_expression *callable, std::vector<expression *>&& arguments); diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index f2f2813..73f0a66 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -67,6 +67,15 @@ namespace elna::boot }; /** + * Attributes the compiler knows. + */ + enum class attribute_kind + { + aligned, + varargs + }; + + /** * Error applying an attribute. */ class attribute_error final : public diagnostic @@ -79,7 +88,9 @@ namespace elna::boot unsupported, argument_count, not_constant, - invalid_alignment + invalid_alignment, + unexpected_arguments, + missing_parameter }; attribute_error(const source_position position, const std::string& name, kind attribute_kind); @@ -202,16 +213,66 @@ namespace elna::boot 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); + procedure_type_expression& expression, bool variadic_supported); ordered_map<field_info> build_composite_type(const std::vector<field_declaration>& fields, - ordered_map<field_origin>& field_names, const type& aggregate, - bool supported = true); + ordered_map<field_origin>& field_names, const type& aggregate); /* - * Reads the alignment out of the attributes written in front of a - * declaration, reporting the ones that do not belong there. + * What the attributes attached to a declaration decide. */ - std::optional<std::size_t> evaluate_alignment(const std::vector<attribute>& attributes, - bool supported); + 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 + * that do not belong there. The declaration accepts the attributes + * passed as template arguments. + */ + template<attribute_kind... supported> + attribute_values evaluate_attributes(const std::vector<attribute>& attributes) + { + constexpr auto is_supported = [](const attribute_kind checked) + { + return ((checked == supported) || ...); + }; + attribute_values result; + + for (const attribute& written : attributes) + { + const std::string& name = written.name().name(); + const source_position position = written.name().position(); + const std::optional<attribute_kind> written_kind = parse_attribute(name); + + if (!written_kind.has_value()) + { + add_error<attribute_error>(position, name, attribute_error::kind::unknown); + } + else if (!is_supported(written_kind.value())) + { + add_error<attribute_error>(position, name, attribute_error::kind::unsupported); + } + else if (written_kind.value() == attribute_kind::aligned) + { + evaluate_alignment(written, result.alignment); + } + else if (!written.arguments().empty()) + { + add_error<attribute_error>(position, name, attribute_error::kind::unexpected_arguments); + } + else if (result.variadic) + { + add_error<attribute_error>(position, name, attribute_error::kind::duplicate); + } + else + { + result.variadic = true; + } + } + return result; + } + void evaluate_alignment(const attribute& written, std::optional<std::size_t>& alignment); /* * Resolves a type expression, reporting names that denote something * other than a type. diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 6defcb8..a8ad73b 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -862,6 +862,8 @@ namespace elna::boot type_properties single_properties; type_properties double_properties; type_properties bool_properties; + /// C's \c int, the type narrower variadic arguments are promoted to. + type_properties c_int_properties; }; template<template<typename, typename> typename C, template<typename> typename Alloc = std::allocator> diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index e9c9a0c..1ca5deb 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -201,6 +201,9 @@ namespace elna::boot std::vector<type> parameters; const return_t 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()); }; diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index e948ada..5faec43 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -97,7 +97,8 @@ namespace elna::boot dereference_of_parameter, parameter_arithmetic, zero_sized, - not_addressable + not_addressable, + variadic_argument }; type_requirement_error(const source_position position, @@ -271,6 +272,11 @@ namespace elna::boot const procedure_info& procedure); void check_return(const procedure_body& body, const source_position position, const std::string& name); + /* + * Checks an argument passed after the parameters of a variadic procedure + * and returns the type it is passed as. + */ + type promote_variadic_argument(const expression& argument); protected: void visit_entry_point(unit *unit) override; diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index c0c23b1..eec6cb4 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -57,7 +57,8 @@ namespace elna::gcc void build_case_integral(boot::case_statement *statement, tree condition_expression); void build_case_general(boot::case_statement *statement, tree condition_expression); void build_procedure_call(location_t call_location, - tree procedure_address, const std::vector<boot::expression *>& arguments); + tree procedure_address, const std::vector<boot::expression *>& arguments, + const std::vector<boot::type>& variadic_types); bool build_builtin_procedures(boot::procedure_call *call); void build_assert_builtin(location_t call_location, const std::vector<boot::expression *>& arguments); diff --git a/testsuite/fail_compilation/aligned_attribute.elna b/testsuite/fail_compilation/aligned_attribute.elna index 3b5a56e..aab5513 100644 --- a/testsuite/fail_compilation/aligned_attribute.elna +++ b/testsuite/fail_compilation/aligned_attribute.elna @@ -1,15 +1,15 @@ type Uneven = record - #aligned(3) x: Word8 (* @Error Alignment must be a power of two *) + x #{aligned 3}: Word8 (* @Error Alignment must be a power of two *) end Twice = record - #aligned(4) #aligned(8) y: Word8 (* @Error Attribute '#aligned' specified more than once *) + y #{aligned 4} #{aligned 8}: Word8 (* @Error Attribute 'aligned' specified more than once *) end Unknown = record - #packed(1) z: Word8 (* @Error Attribute '#packed' not declared *) + z #{packed 1}: Word8 (* @Error Attribute 'packed' not declared *) end -proc takes(#aligned(8) p: Int) (* @Error Attribute '#aligned' is not supported in this position *) +proc takes(p #{aligned 8}: Int) (* @Error Attribute 'aligned' is not supported in this position *) begin return diff --git a/testsuite/fail_compilation/varargs_argument.elna b/testsuite/fail_compilation/varargs_argument.elna new file mode 100644 index 0000000..1325c29 --- /dev/null +++ b/testsuite/fail_compilation/varargs_argument.elna @@ -0,0 +1,12 @@ +var + written: Int32 + +proc printf(format: ^const Word8) #{varargs} -> Int32 +extern + +program() +begin + written := printf("%s".ptr, "text") (* @Error Type '\[\]const Word8' cannot be passed as a variadic argument *) +return 0u8 + +end. diff --git a/testsuite/fail_compilation/varargs_attribute.elna b/testsuite/fail_compilation/varargs_attribute.elna new file mode 100644 index 0000000..1c6d3e0 --- /dev/null +++ b/testsuite/fail_compilation/varargs_attribute.elna @@ -0,0 +1,8 @@ +proc with_body(x: Int) #{varargs} (* @Error Attribute 'varargs' is not supported in this position *) +begin +return + +proc no_parameters() #{varargs} -> Int32 (* @Error Attribute 'varargs' requires at least one parameter *) +extern + +end. diff --git a/testsuite/runnable/aligned_attribute.elna b/testsuite/runnable/aligned_attribute.elna index 330847e..45c0c37 100644 --- a/testsuite/runnable/aligned_attribute.elna +++ b/testsuite/runnable/aligned_attribute.elna @@ -5,20 +5,20 @@ type end Spread = record - a, #aligned(8) b: Word8 + a, b #{aligned 8}: Word8 end Packed = record a: Word8; - #aligned(1) wide: Word + wide #{aligned 1}: Word end - Over = #aligned(16) record + Over = record #{aligned 16} a: Word8 end var - #aligned(32) tuned: Word8 + tuned #{aligned 32}: Word8 spread: Spread first: ^Word8 second: ^Word8 diff --git a/testsuite/runnable/varargs.elna b/testsuite/runnable/varargs.elna new file mode 100644 index 0000000..feeb94b --- /dev/null +++ b/testsuite/runnable/varargs.elna @@ -0,0 +1,16 @@ +var + buffer: [8]Word8 + written: Int32 + +proc snprintf(buffer: ^Word8; size: Word; format: ^const Word8) #{varargs} -> Int32 +extern + +program() +begin + written := snprintf(@buffer[1], 8u, "%d %.1f".ptr, 200u8, 1.5f); + assert(written = 7i32); + assert(buffer[1] = '2'); + assert(buffer[7] = '5') +return 0u8 + +end. |
