diff options
| -rw-r--r-- | boot/name_analysis.cc | 20 | ||||
| -rw-r--r-- | boot/symbol.cc | 25 | ||||
| -rw-r--r-- | boot/type_check.cc | 19 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 10 | ||||
| -rw-r--r-- | testsuite/fail_compilation/record_base_constructor_type_mismatch.elna | 14 | ||||
| -rw-r--r-- | testsuite/runnable/record_base_constructor.elna | 19 |
6 files changed, 81 insertions, 26 deletions
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 620a32f..70b9031 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -385,23 +385,15 @@ namespace elna::boot type resolving_visitor::lookup_field(const type& composite_type, const std::string& field_name) { - const type resolved_type = resolve_underlying_type(composite_type); + type record_field = elna::boot::lookup_field(composite_type, field_name); - if (auto record = resolved_type.get<record_type>()) + if (!record_field.empty()) { - for (auto& field : record->fields) - { - if (field.first == field_name) - { - return field.second; - } - } - if (!record->base.empty()) - { - return lookup_field(record->base, field_name); - } + return record_field; } - else if (auto range_base = get_range_base_type(resolved_type)) + const type resolved_type = resolve_underlying_type(composite_type); + + if (auto range_base = get_range_base_type(resolved_type)) { if (auto field = lookup_pointer_like_field(field_name, range_base)) { diff --git a/boot/symbol.cc b/boot/symbol.cc index 4d6832d..0d847e1 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -176,11 +176,11 @@ namespace elna::boot if (payload->return_type.no_return) { - result += ": !"; + result += " -> !"; } else if (!payload->return_type.proper_type.empty()) { - result += ": " + payload->return_type.proper_type.to_string(); + result += " -> " + payload->return_type.proper_type.to_string(); } return result; } @@ -570,4 +570,25 @@ namespace elna::boot } return type(); } + + type lookup_field(const type& composite_type, const std::string& field_name) + { + const type resolved_type = resolve_underlying_type(composite_type); + + if (auto record = resolved_type.get<record_type>()) + { + for (const auto& [name, field_type] : record->fields) + { + if (name == field_name) + { + return field_type; + } + } + if (!record->base.empty()) + { + return lookup_field(record->base, field_name); + } + } + return type(); + } } diff --git a/boot/type_check.cc b/boot/type_check.cc index 8f7a30f..abee91f 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -911,17 +911,16 @@ namespace elna::boot } for (const field_initializer& initializer : expression->field_initializers) { - for (const auto& [field_name, field_type]: record->fields) + // An initializer for an unknown field is already reported in name + // analysis; only check the value type when the field was found. + const type field_type = lookup_field(expression->type_decoration, initializer.name()); + + if (!field_type.empty() + && !is_assignable_from(field_type, initializer.value().type_decoration)) { - if (field_name == initializer.name()) - { - if (!is_assignable_from(field_type, initializer.value().type_decoration)) - { - add_error<type_mismatch_error>( - initializer.value().position(), initializer.value().type_decoration, type_mismatch_error::expected_type{field_type}); - } - break; - } + add_error<type_mismatch_error>( + initializer.value().position(), initializer.value().type_decoration, + type_mismatch_error::expected_type{field_type}); } } } diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 2c47fcc..40cbbc6 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -667,4 +667,14 @@ namespace elna::boot * \return The base type, or an empty type. */ type get_range_base_type(const type& range); + + /** + * Finds the type of a record field by name, following the base records. + * + * \param composite_type The record type to search in. + * \param field_name The name of the field to find. + * \return The field type, or an empty type if the type is not a record + * or has no field with this name. + */ + type lookup_field(const type& composite_type, const std::string& field_name); } diff --git a/testsuite/fail_compilation/record_base_constructor_type_mismatch.elna b/testsuite/fail_compilation/record_base_constructor_type_mismatch.elna new file mode 100644 index 0000000..85c93c1 --- /dev/null +++ b/testsuite/fail_compilation/record_base_constructor_type_mismatch.elna @@ -0,0 +1,14 @@ +type + Base = record + a: Int + end + Child = record(Base) + b: Int + end + +var + c: Child + +begin + c := Child{a: "wrong", b: 9} (* @Error Expected type 'Int', but got '\[\]const Word8' *) +end. diff --git a/testsuite/runnable/record_base_constructor.elna b/testsuite/runnable/record_base_constructor.elna new file mode 100644 index 0000000..86da08a --- /dev/null +++ b/testsuite/runnable/record_base_constructor.elna @@ -0,0 +1,19 @@ +type + Base = record + a: Int + end + Child = record(Base) + b: Int + end + Grandchild = record(Child) + c: Int + end + +var + g: Grandchild := Grandchild{a: 3, b: 5, c: 7} + +begin + assert(g.a = 3); + assert(g.b = 5); + assert(g.c = 7) +end. |
