diff options
| -rw-r--r-- | boot/type_check.cc | 18 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 19 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 72 | ||||
| -rw-r--r-- | gcc/gcc/elna-module-loader.cc | 8 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 39 | ||||
| -rw-r--r-- | include/elna/boot/dependency.h | 16 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 4 | ||||
| -rw-r--r-- | include/elna/gcc/elna-builtins.h | 2 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 2 | ||||
| -rw-r--r-- | include/elna/gcc/elna-module-loader.h | 5 | ||||
| -rw-r--r-- | include/elna/gcc/elna-tree.h | 2 | ||||
| -rw-r--r-- | testsuite/fail_compilation/assign_void_call.elna | 9 | ||||
| -rw-r--r-- | testsuite/fail_compilation/compare_unrelated_records.elna | 17 | ||||
| -rw-r--r-- | testsuite/fail_compilation/proc_type_without_parameters.elna | 9 | ||||
| -rw-r--r-- | testsuite/fail_compilation/record-base-cycle-indirect.elna | 13 | ||||
| -rw-r--r-- | testsuite/runnable/case_constant_label.elna | 13 | ||||
| -rw-r--r-- | testsuite/runnable/exported_variable/helper.elna | 4 | ||||
| -rw-r--r-- | testsuite/runnable/exported_variable/sut.elna | 5 | ||||
| -rw-r--r-- | testsuite/runnable/generic_pointer_arithmetic.elna | 13 |
19 files changed, 174 insertions, 96 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc index 5b0547b..068d958 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -127,6 +127,11 @@ namespace elna::boot if constexpr (std::is_same_v<T, expected_type>) { + if (this->actual.empty()) + { + return "Expected type '" + payload.value.to_string() + + "', but the expression has no value"; + } return "Expected type '" + payload.value.to_string() + "', but got '" + this->actual.to_string() + "'"; } @@ -381,14 +386,7 @@ namespace elna::boot bool type_analysis_visitor::is_equality_compatible(const type& left, const type& right) { - auto resolved_left = resolve_underlying_type(left); - auto resolved_right = resolve_underlying_type(right); - - return resolved_left == resolved_right - || (is_primitive_type(resolved_left, "Pointer") && is_any_pointer_type(resolved_right)) - || (is_any_pointer_type(resolved_left) && is_primitive_type(resolved_right, "Pointer")) - || (resolved_left.get<slice_type>() && resolved_right.get<slice_type>()) - || (resolved_left.get<record_type>() && resolved_right.get<record_type>()); + return is_assignable_from(left, right) || is_assignable_from(right, left); } void type_analysis_visitor::visit_and_validate_condition(expression& condition) @@ -465,7 +463,7 @@ namespace elna::boot if (assignee_ptr == nullptr || assignment_ptr == nullptr) { - return verdict::reject; + return verdict::pass; } auto assignee_pointee_const = resolve_aliases(assignee_ptr->base).get<constant_type>(); auto assignment_pointee_const = resolve_aliases(assignment_ptr->base).get<constant_type>(); @@ -678,7 +676,7 @@ namespace elna::boot found.value(), type_requirement_error::kind::zero_sized); } } - if (declaration->initializer == nullptr || has_errors()) + if (declaration->initializer == nullptr) { return; } diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 18dcc52..23315e5 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -16,6 +16,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "elna/gcc/elna-builtins.h" +#include "elna/gcc/elna-diagnostic.h" #include "elna/gcc/elna1.h" #include "stor-layout.h" #include "stringpool.h" @@ -304,7 +305,11 @@ namespace elna::gcc const std::shared_ptr<symbol_table>& symbols) { tree declaration_type = gcc::build_procedure_type(info.symbol, symbols); - tree fndecl = build_fn_decl(name.c_str(), declaration_type); + const location_t location = info.position.has_value() + ? get_location(&info.position.value()) + : UNKNOWN_LOCATION; + tree fndecl = build_decl(location, FUNCTION_DECL, + get_identifier(name.c_str()), declaration_type); symbols->enter(name, fndecl); if (info.symbol.return_type.no_return) @@ -330,8 +335,11 @@ namespace elna::gcc function_args_iter_next(¶meter_type); } DECL_ARGUMENTS(fndecl) = argument_chain; + // Elna has no exceptions. + TREE_NOTHROW(fndecl) = 1; DECL_EXTERNAL(fndecl) = static_cast<unsigned>(info.is_extern()); - TREE_PUBLIC(fndecl) = static_cast<unsigned>(info.exported); + // An extern procedure has C linkage whether or not Elna re-exports the name. + TREE_PUBLIC(fndecl) = static_cast<unsigned>(info.exported || info.is_extern()); } tree declare_variable(const std::string& name, const boot::variable_info& info, @@ -361,7 +369,7 @@ namespace elna::gcc } void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table, - const std::shared_ptr<symbol_table>& symbols) + const std::shared_ptr<symbol_table>& symbols, const bool imported) { for (auto& [symbol_name, symbol_info] : *info_table) { @@ -371,7 +379,10 @@ namespace elna::gcc } else if (auto variable_info = symbol_info->is_variable()) { - declare_variable(symbol_name, *variable_info, symbols); + tree declaration_tree = declare_variable(symbol_name, *variable_info, symbols); + + DECL_EXTERNAL(declaration_tree) = + static_cast<unsigned>(imported || variable_info->is_extern); } else if (auto procedure_info = symbol_info->is_procedure()) { diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index e2d50b4..fb68136 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -176,8 +176,7 @@ namespace elna::gcc for (const boot::field_initializer& initializer : expression->field_initializers) { - tree field_decl = find_field_by_name(get_location(&expression->position()), - record_type, initializer.name()); + tree field_decl = find_field_by_name(record_type, initializer.name()); initializer.value().accept(this); // NOLINTNEXTLINE(misc-const-correctness) CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, this->current_expression); @@ -474,8 +473,6 @@ namespace elna::gcc DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); DECL_SAVED_TREE(fndecl) = mapping; - DECL_PRESERVE_P(fndecl) = 1; - dump_function(TDI_original, fndecl); pop_cfun(); @@ -720,8 +717,7 @@ namespace elna::gcc if (lang_hooks.decls.global_bindings_p()) { - TREE_STATIC(declaration_tree) = - static_cast<unsigned>(!variable_identifier.exported() && !declaration->is_extern); + TREE_STATIC(declaration_tree) = static_cast<unsigned>(!declaration->is_extern); varpool_node::get_create(declaration_tree); varpool_node::finalize_decl(declaration_tree); } @@ -741,14 +737,9 @@ namespace elna::gcc { tree symbol = this->symbols->lookup(expression->name); - if (symbol == NULL_TREE) - { - this->current_expression = error_mark_node; - } - else - { - this->current_expression = symbol; - } + // Name analysis rejects an undeclared name. + gcc_assert(symbol != NULL_TREE); + this->current_expression = symbol; } void generic_visitor::visit(boot::array_access_expression *expression) @@ -813,15 +804,12 @@ namespace elna::gcc } else { - tree field_declaration = find_field_by_name(expression_location, + tree field_declaration = find_field_by_name( TREE_TYPE(this->current_expression), expression->field().name()); - if (field_declaration != error_mark_node) - { - this->current_expression = build3_loc(expression_location, COMPONENT_REF, - TREE_TYPE(field_declaration), this->current_expression, - field_declaration, NULL_TREE); - } + this->current_expression = build3_loc(expression_location, COMPONENT_REF, + TREE_TYPE(field_declaration), this->current_expression, + field_declaration, NULL_TREE); } } @@ -866,10 +854,7 @@ namespace elna::gcc } result = make_if_branch(statement->branch(), result); - if (result != error_mark_node) - { - append_statement(result); - } + append_statement(result); this->current_expression = NULL_TREE; } @@ -1010,16 +995,13 @@ namespace elna::gcc } result = make_if_branch(statement->branch(), result, goto_check); - if (result != error_mark_node) - { - tree check_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, - void_type_node, while_check_label); - tree end_label_expr = build1(LABEL_EXPR, void_type_node, while_end_label); + tree check_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, + void_type_node, while_check_label); + tree end_label_expr = build1(LABEL_EXPR, void_type_node, while_end_label); - append_statement(check_label_expr); - append_statement(result); - append_statement(end_label_expr); - } + append_statement(check_label_expr); + append_statement(result); + append_statement(end_label_expr); this->current_expression = NULL_TREE; } @@ -1077,7 +1059,7 @@ namespace elna::gcc case_label->accept(this); const location_t case_location = get_location(&case_label->position()); - assert_constant(case_location); + assert_constant(); tree case_label_declaration = create_artificial_label(case_location); tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration); @@ -1130,10 +1112,7 @@ namespace elna::gcc for (boot::expression *const case_label : case_block.labels) { case_label->accept(this); - if (!assert_constant(get_location(&case_label->position()))) - { - continue; - } + assert_constant(); tree label_comparison = build_equality_comparison(get_location(&case_label->position()), condition_value, this->current_expression, statement->condition().type_decoration, EQ_EXPR); @@ -1153,19 +1132,12 @@ namespace elna::gcc } } - bool generic_visitor::assert_constant(location_t expression_location) + void generic_visitor::assert_constant() { tree constant_expression = extract_constant(this->current_expression); - if (constant_expression == NULL_TREE) - { - error_at(expression_location, "Expected a constant expression"); - this->current_expression = error_mark_node; - } - else - { - this->current_expression = constant_expression; - } - return this->current_expression != error_mark_node; + // Type checking rejects a case label that is not a constant expression. + gcc_assert(constant_expression != NULL_TREE); + this->current_expression = constant_expression; } } diff --git a/gcc/gcc/elna-module-loader.cc b/gcc/gcc/elna-module-loader.cc index 8827a1e..5852bbe 100644 --- a/gcc/gcc/elna-module-loader.cc +++ b/gcc/gcc/elna-module-loader.cc @@ -78,9 +78,9 @@ namespace elna::gcc } void module_loader::finalize(const std::filesystem::path&, - const std::shared_ptr<boot::symbol_table>& module_scope) const + const std::shared_ptr<boot::symbol_table>& module_scope, const bool imported) const { - rewrite_symbol_table(module_scope, this->symbols); + rewrite_symbol_table(module_scope, this->symbols, imported); } void compile_files(const char *const *filenames, unsigned int count) @@ -99,9 +99,9 @@ namespace elna::gcc report_errors(diagnostics); } - if (result.value != nullptr) + if (result.value != nullptr && result.errors.empty()) { - linemap_add(line_table, LC_ENTER, 0, key.native().c_str(), 1); + linemap_add(line_table, LC_ENTER, 0, ggc_strdup(key.native().c_str()), 1); generic_visitor visitor{ state.custom, state.find(key)->second, target }; result.value->accept(&visitor); linemap_add(line_table, LC_LEAVE, 0, nullptr, 0); diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index cddb407..7aa4012 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -97,6 +97,14 @@ namespace elna::gcc return field_declaration; } + // The generic Pointer has no pointee size; it advances byte by byte. + static tree pointer_stride(tree pointer_type, tree offset_type) + { + return pointer_type == elna_pointer_type_node + ? size_one_node + : fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type))); + } + tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right, location_t operation_location) { @@ -124,9 +132,7 @@ namespace elna::gcc { return error_mark_node; } - tree size_exp = pointer_type == elna_pointer_type_node - ? size_one_node - : fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer)))); + tree size_exp = pointer_stride(pointer_type, TREE_TYPE(offset)); offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp); offset = fold_convert(size_type_node, offset); @@ -139,7 +145,7 @@ namespace elna::gcc { tree pointer_type = left_type; tree offset_type = right_type; - tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type))); + tree size_exp = pointer_stride(pointer_type, offset_type); tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp); convert_expression = fold_convert(size_type_node, convert_expression); @@ -156,13 +162,11 @@ namespace elna::gcc gcc_unreachable(); } - tree find_field_by_name(location_t, tree type, const std::string& field_name) + tree find_field_by_name(tree type, const std::string& field_name) { - // NOLINTNEXTLINE(readability-simplify-boolean-expr) - if (type == error_mark_node || !RECORD_OR_UNION_TYPE_P(type)) - { - return error_mark_node; - } + // Name analysis rejects a field the record does not have. + gcc_assert(RECORD_OR_UNION_TYPE_P(type)); + for (tree field = TYPE_FIELDS(type); field != NULL_TREE; field = TREE_CHAIN(field)) { if (field_name == IDENTIFIER_POINTER(DECL_NAME(field))) @@ -170,7 +174,7 @@ namespace elna::gcc return field; } } - return error_mark_node; + gcc_unreachable(); } tree build_static_array_type(tree type, const std::uint64_t size) @@ -233,8 +237,13 @@ namespace elna::gcc tree extract_constant(tree expression) { const int code = TREE_CODE(expression); + // A mutable variable can have a constant initializer too, so readonly is + // what marks one whose value is known at compile time. + const bool constant_variable = code == VAR_DECL && TREE_READONLY(expression) + && DECL_INITIAL(expression) != NULL_TREE + && TREE_CONSTANT(DECL_INITIAL(expression)); - if (code == CONST_DECL) + if (code == CONST_DECL || constant_variable) { return DECL_INITIAL(expression); } @@ -333,11 +342,7 @@ namespace elna::gcc for (const auto& [field_name, field_value] : fields) { - tree field_decl = find_field_by_name(UNKNOWN_LOCATION, type, field_name); - if (field_decl == error_mark_node) - { - return NULL_TREE; - } + tree field_decl = find_field_by_name(type, field_name); tree value_tree = constant_to_tree(field_value, symbols, TREE_TYPE(field_decl)); if (value_tree == NULL_TREE) { diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index 18df64c..9bc1adf 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -174,7 +174,7 @@ namespace elna::boot { { loader.resolve(relative, position, key) } -> std::same_as<std::filesystem::path>; { loader.read(key) } -> std::same_as<read_result>; - loader.finalize(key, module_scope); + loader.finalize(key, module_scope, true); }; /** @@ -217,6 +217,9 @@ namespace elna::boot * their canonical paths to detect circular imports. * \param loader Host-side module loader. * \param target Target machine information. + * \param imported Whether \p key is being compiled because another module + * imports it. False for the module the caller asked for; set when + * recursing into an import, and passed on to \c finalize. * * \return Abstract syntax tree of the module itself and the * diagnostics of this module and all its imports, grouped by @@ -224,7 +227,7 @@ namespace elna::boot */ template<module_loader Loader> dependency compile(const std::filesystem::path& key, Loader& loader, - const target_info& target) + const target_info& target, const bool imported = false) { auto cached = this->cache.find(key); if (cached != this->cache.cend()) @@ -257,7 +260,7 @@ namespace elna::boot module_path.stem().string())); continue; } - dependency sub = this->compile(module_path, loader, target); + dependency sub = this->compile(module_path, loader, target, true); for (auto& diagnostics : sub.errors) { result.append(std::move(diagnostics.module), std::move(diagnostics.errors())); @@ -273,12 +276,11 @@ namespace elna::boot result.append(key, std::move(analysis.errors)); } this->cache.insert({ key, analysis.value }); - loader.finalize(key, analysis.value.leave()); - this->in_progress.erase(key); - if (failed) + if (!failed) { - tree.reset(); + loader.finalize(key, analysis.value.leave(), imported); } + this->in_progress.erase(key); result.value = std::move(tree); return result; } diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index eacc072..d37484a 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -263,6 +263,10 @@ namespace elna::boot template<has_to_string T> std::string join(const std::vector<T>& identifiers, std::string_view delimiter = ", ") { + if (identifiers.empty()) + { + return std::string(); + } return std::accumulate(std::next(identifiers.begin()), identifiers.end(), identifiers.front().to_string(), [delimiter](const std::string& accumulator, const T& next) -> std::string { diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h index 75b409d..10a2732 100644 --- a/include/elna/gcc/elna-builtins.h +++ b/include/elna/gcc/elna-builtins.h @@ -33,7 +33,7 @@ namespace elna::gcc std::shared_ptr<symbol_table> builtin_symbol_table(); void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table, - const std::shared_ptr<symbol_table>& symbols); + const std::shared_ptr<symbol_table>& symbols, bool imported); tree handle_symbol(const std::string& symbol_name, const std::shared_ptr<boot::alias_type>& reference, const std::shared_ptr<symbol_table>& symbols); diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index fd54ba5..c0c23b1 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -62,7 +62,7 @@ namespace elna::gcc void build_assert_builtin(location_t call_location, const std::vector<boot::expression *>& arguments); void visit_statements(const std::vector<boot::statement *>& statements); - bool assert_constant(location_t expression_location); + void assert_constant(); tree declare_local_variable(const boot::identifier& name, const boot::variable_info& info, tree initial_value); diff --git a/include/elna/gcc/elna-module-loader.h b/include/elna/gcc/elna-module-loader.h index 2b46fc5..5f5aab6 100644 --- a/include/elna/gcc/elna-module-loader.h +++ b/include/elna/gcc/elna-module-loader.h @@ -78,9 +78,12 @@ namespace elna::gcc * * \param key Resolved module path. * \param module_scope Analyzed module scope. + * \param imported Whether the module is being registered because another + * module imports it, rather than being the translation unit itself. + * Its variables are then declarations instead of definitions. */ void finalize(const std::filesystem::path& key, - const std::shared_ptr<boot::symbol_table>& module_scope) const; + const std::shared_ptr<boot::symbol_table>& module_scope, bool imported) const; }; /** diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index f201e64..a3a9106 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -55,7 +55,7 @@ namespace elna::gcc tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right, location_t operation_location); tree build_field(location_t location, tree record_type, const std::string& name, tree type); - tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name); + tree find_field_by_name(tree type, const std::string& field_name); tree build_static_array_type(tree type, const std::uint64_t size); tree build_slice(tree slice_type, tree ptr, tree length); tree build_enumeration_type(const std::vector<std::string>& members); diff --git a/testsuite/fail_compilation/assign_void_call.elna b/testsuite/fail_compilation/assign_void_call.elna new file mode 100644 index 0000000..d18b1c8 --- /dev/null +++ b/testsuite/fail_compilation/assign_void_call.elna @@ -0,0 +1,9 @@ +var + x: Int + +proc v() +return + +begin + x := v() (* @Error Expected type 'Int', but the expression has no value *) +end. diff --git a/testsuite/fail_compilation/compare_unrelated_records.elna b/testsuite/fail_compilation/compare_unrelated_records.elna new file mode 100644 index 0000000..71eaeda --- /dev/null +++ b/testsuite/fail_compilation/compare_unrelated_records.elna @@ -0,0 +1,17 @@ +type + A = record + x: Int + end + B = record + y: Int; + z: Int + end + +var + a: A + b: B + ok: Bool + +begin + ok := b = a (* @Error Invalid operands of type 'B' and 'A' for operator = *) +end. diff --git a/testsuite/fail_compilation/proc_type_without_parameters.elna b/testsuite/fail_compilation/proc_type_without_parameters.elna new file mode 100644 index 0000000..92514f6 --- /dev/null +++ b/testsuite/fail_compilation/proc_type_without_parameters.elna @@ -0,0 +1,9 @@ +var + x: Int + +proc f() +return + +begin + x := f (* @Error Expected type 'Int', but got 'proc\(\)' *) +end. diff --git a/testsuite/fail_compilation/record-base-cycle-indirect.elna b/testsuite/fail_compilation/record-base-cycle-indirect.elna new file mode 100644 index 0000000..412e6ea --- /dev/null +++ b/testsuite/fail_compilation/record-base-cycle-indirect.elna @@ -0,0 +1,13 @@ +type + A = record(B) + x: Int + end + B = record(A) (* @Error Type declaration forms a cycle: A -> B -> A *) + y: Int + end + +var + v: A + +begin +end. diff --git a/testsuite/runnable/case_constant_label.elna b/testsuite/runnable/case_constant_label.elna new file mode 100644 index 0000000..5d55e9f --- /dev/null +++ b/testsuite/runnable/case_constant_label.elna @@ -0,0 +1,13 @@ +var + base: const Int := 2 + limit: const Int := base + x: Int := 2 + matched: Bool := false + +begin + case x of + 1: assert(false) + | limit: matched := true + end; + assert(matched) +end. diff --git a/testsuite/runnable/exported_variable/helper.elna b/testsuite/runnable/exported_variable/helper.elna new file mode 100644 index 0000000..23924be --- /dev/null +++ b/testsuite/runnable/exported_variable/helper.elna @@ -0,0 +1,4 @@ +var + Shared*: Int := 42 + +end. diff --git a/testsuite/runnable/exported_variable/sut.elna b/testsuite/runnable/exported_variable/sut.elna new file mode 100644 index 0000000..52fb291 --- /dev/null +++ b/testsuite/runnable/exported_variable/sut.elna @@ -0,0 +1,5 @@ +import helper + +begin + assert(Shared = 42) +end. diff --git a/testsuite/runnable/generic_pointer_arithmetic.elna b/testsuite/runnable/generic_pointer_arithmetic.elna new file mode 100644 index 0000000..c737c7d --- /dev/null +++ b/testsuite/runnable/generic_pointer_arithmetic.elna @@ -0,0 +1,13 @@ +var + base: [4]Int + element: ^Int + first: Pointer + second: Pointer + +begin + element := base.ptr; + first := element; + second := first + 1; + assert(cast(second : Word) - cast(first : Word) = 1u); + assert(second - 1 = first) +end. |
