aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/name_analysis.cc20
-rw-r--r--boot/symbol.cc25
-rw-r--r--boot/type_check.cc19
-rw-r--r--include/elna/boot/symbol.h10
-rw-r--r--testsuite/fail_compilation/record_base_constructor_type_mismatch.elna14
-rw-r--r--testsuite/runnable/record_base_constructor.elna19
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.